📄 pcrom.htm
字号:
<!doctype html public "-//IETF//DTD HTML//EN">
<HTML>
<HEAD>
<TITLE>EMBEDDED SYSTEMS</TITLE>
<META NAME="GENERATOR" CONTENT="Internet Assistant for Word 1.00">
<META NAME="AUTHOR" CONTENT="Brian Brown">
</HEAD>
<BODY>
<CENTER><H2>EMBEDDED CODE GENERATION FOR IBM-PCS</H2></CENTER>
<CENTER><B>Copyright Brian Brown, 1989-1995</B></CENTER>
<A HREF="default.htm"><IMG SRC="images/menu.gif" ALT="menu"></A>
<HR>
The introduction of low cost PC clones has popularized their use
for tasks other than personal computing. These PC's offer
advantages such as
<UL>
<LI>speed
<LI>memory
<LI>low cost
<LI>quick development time over traditional strategies for embedded<br>
control systems.
</UL>
Using PC's in embedded systems does pose significant problems however.
The software generated for the target system adheres to two basic principles.
<P>
First, it is normally standalone, in that the operating system is
not present.
<P>
Secondly, the target software is position dependent, residing within
physical memory at specific address locations. This second requirement
also means that any static data required by the program must also be
stored in non-volatile memory (Eprom) and copied to RAM before the
main program starts.
<P>
The first requirement of not relying upon an operating system severely
limits the choice of development software. The advent of utilities to
convert standard generated .EXE files, converting them to position
dependent code has certainly helped.
<P>
How-ever, the downfall occurs in that the library routines used by
the mainstream compilers are not rommable. The routines use DOS to
perform most functions, and as DOS is not present, any call to these
functions will result in a system crash.
<P>
The second requirement for position dependent code has been alleviated
by special utilities. These covert a .EXE file, and assign physical
addresses to the various segments of the program. The resultant file
is then copied to an Eprom programmer where the code is transferred
into a non-volatile memory chip. The chip is then mounted on the target
system.
<P>
ROMLIB.ASM is the source code for a large or small memory model rom
code interface for C Compilers. It has been tested with the following
C Compilers,
<UL>
<LI> TurboC V1.00, 1.5
<LI> Microsoft C V4.00
<LI> Intel C86 V1.3
</UL>
The source code does not use DOS to perform any function, thus is
completely rommable. The rom code interface comprises the following files.
<UL>
<LI><A HREF="sw/romlib/romlib.asm">ROMLIB.ASM</A>, assembler source for rom
code routines
<LI><A HREF="sw/romlib/romlib.mac">ROMLIB.MAC</A>, assembler macro file
used to determine offsets for parameters accepted by routines
<LI><A HREF="sw/romlib/romlib.h">ROMLIB.H</A>, prototype header file for
C compilers
<LI><A HREF="sw/romlib/romdemo.c">ROMDEMO.C</A>, turboc demonstration
program
<LI><A HREF="sw/romlib/romdemo.cfg">ROMDEMO.CFG</A>, configuration file
specifying addresses for each program segment
<LI><A HREF="sw/romlib/romdemo.prj">ROMDEMO.PRJ</A>, project file
</UL>
Many clones have empty ROM sockets on the motherboard. These empty
sockets house the BASIC chips on true PC machines. On a PC clone,
these sockets are usually mapped as follows.
<UL>
<LI> FE00 ROM BIOS
<LI> FC00
<LI> FA00
<LI> F800
<LI> F600
<LI> F400
</UL>
The empty sockets on the motherboard provide an ideal solution for
producing embedded code. The solution is to dump the program into
Eprom, place it into the appropriate slot, and when the PC is turned
on, the program in Eprom will take over.
<P>
Well, life is not that simple. Whether the code stored in these Eproms
ever gets to execute depends upon a lot of things. Most clones will
generate an int18h instruction if the boot from the floppy drive was
unsuccessful (when the enter key is pressed, some may require more
than one enter key press!). This is due because most clones generate
an int19h instruction to boot from floppy, and when this is unsuccessful,
the type int18h instruction is executed, forcing a jump to F600:0000
<P>
If the Eproms are placed into the correct sockets, the code should now
be executing. Lifting the lid off the clone should reveal the ROM BIOS
chip, and about four or five empty sockets (latest clones only have one
empty socket, which takes up the memory space of the four or five empty
ones on earlier machines). The socket which is the FOURTH empty one
furthest away from the BIOS chip is that which will be executed first
(it maps to F600:0000).
<P>
Having identified where to place the first Eprom on the motherboard,
the next task is to generate some code to try everything out. Problems
again arise, as a utility is needed to locate the program to the correct
physical addresses in memory. Another problem is that in using library
routines supplied with common compilers invariably call DOS routines.
As DOS will not be loaded when the program starts up, the use of functions
like printf() and puts() are forbidden (they will cause a system crash).
<P>
This means that you need to write your own routines to perform I/O,
which are not reliant upon DOS being present. As a short cut, because the
ROM BIOS chip is still present, some of the routines in that chip can be
used. A runtime interface which adheres to the above rules is called
ROMLIB and will be used in the examples which follow.
<P>
A good article on romcode generation using turboc, written by R Naro,
was published in Dr Dobbs, Dec 1987. The article develops a locate utility
which converts .EXE files to position dependent code. The following
examples use the locate utility to demonstrate an example rommable
program written in turboc.
<P>
<HR>
<B>PREPARING THE ROMLIB OBJECT CODE INTERFACE ROUTINES</B><BR>
The file <A HREF="sw/romlib/romlib.asm">ROMLIB.ASM</A> supports both small
and large memory models, as well as an option for saving the DI and SI
registers on entry to procedures (if you intend to use register variables in
your C programs).
<P>
The following command line assembles the romcode interface for a
large memory model and register variables.
<PRE>
MASM romlib;
</PRE>
Note that the defaults are a large memory model with the DI and SI
registers saved. If you do not intend to use register based variables in
your C program, then the NREGISTER option should be specified as follows,
<PRE>
MASM /DNREGISTER romlib;
</PRE>
This will create an object file which will be linked together with the
C program, providing the runtime support needed.
<P>
<HR>
<B>COMPILING THE PROGRAM ROMDEMO.C</B><BR>
Using the turboc compiler, compile the file
<A HREF="sw/romlib/romdemo.c">ROMDEMO.C</A> to object format, using a large
memory model, unsigned chars and register variables on.
<P>
<HR>
<B>COMBINING THE OBJECT FILES USING TLINK</B><BR>
The tlink utility will combine the object files into a single executable
program. DO NOT try to run this program on your machine under DOS. It is
designed to fit in specific memory locations.
<PRE>
TLINK /m tc romdemo romlib, romdemo, romdemo
</PRE>
This combines the startup code from R Naros article (tc) with the
object files <i>romdemo</i> and <i>romlib</i>, creating <i>romdemo.exe</i>
and a map file name <i>romdemo.map</i>.
<P>
<HR>
<B>DECIDING WHERE TO PLACE THE CODE</B><BR>
When the locate utility (Dr Dobbs Dec 1987, R Naro) is executed, it
searches for a configuration file which specifies where the various
program segments are to reside. As DOS is not present, and assuming
that the PC we have has 640k of RAM, then it is an easy task of
assigning addresses to the various segments.
<P>
Obviously, the code segment must start at F600 so as to intercept with
the int18h instruction upon an unsuccessful cold boot. The data segment
and stack segment can go anywhere except low memory (ie, 0000 which is
used to store all the interrupt vectors).
<P>
The supplied file <A HREF="sw/romlib/romdemo.cfg"<i>ROMDEMO.CFG</i></A>
details the addresses assigned to the program segments, which are,
<PRE>
CODE F600
DATA 6000
STACK 7000
</PRE>
This instructs the locate utility to copy the initialized data variables
into a CONSTant segment, which is part of the Eprom. The startup code
copies these variables into RAM when the program begins.
<P>
To locate the program, generating a file suitable for transfer into Eprom,
type the command
<PRE>
LOCATE ROMDEMO
</PRE>
This creates <i>ROMDEMO.HEX</i>, an Intel hex format file which can be
downloaded into an Eprom programmer for burning into Eprom. Many companies
will be able to do this for you, at relatively low cost. The Eprom we used
in the demonstration was a type 2764 (8k x 8 bits), and the code started at
offset 0 in the Eprom.
<P>
Once the code is in the Eprom, insert it into the empty slot associated
with the location F600 (ensure the computer is turned off, preferably use
anti-static precautions, and ensure its the right way round).
<P>
Turn the computer on, ensuring there is no system diskette in the disk
drive (this assumes there is also no hard disk). Once the 'no system disk,
press any key to re-boot' message is displayed, press the enter key. The
computer then executes the int18h instruction, and execution branches to
the code stored in the Eprom.
<P>
<HR>
<B>ROMLIB - A DESCRIPTION OF THE VARIOUS RUNTIME ROMMABLE ROUTINES</B><BR>
<PRE>
<B>BOOT from disk</B> ipl_load()
This function generates an int19h instruction, which will cause a cold
boot from diskette. Its prototype is
void ipl_load( void );
example usage,
ipl_load(); /* boot from disk drive */
<B>JUMP to Basic ROM</B> basic_rom()
This function generates an int18h instruction, causing a jump to
location F600:0000. Its prototype is
void basic_rom( void );
example usage,
basic_rom(); /* execute program stored in Eprom */
<B>INITIALIZE Serial Card</B> ser_init()
This function initializes the serial card. Its prototype is
void ser_init( unsigned char parameters, int comport );
where comport specifies COM1 (0) or COM2 (1)
parameters specifies
bit 7,6,5 baud rate (111=9600 baud)
bit 4,3 parity (00=N,01=Odd,11=Even)
bit 2 stops (0=1,1=2stops)
bit 1,0 word size (10=7,11=8bits)
example usage,
ser_init( (unsigned char) 0xe3, 0);
/* initialize com1 to 9600,n,8,1 */
<B>READ CHARACTER FROM SERIAL PORT</B> serial_in()
This function returns an integer which represents the character read from
the specified serial port. It uses the rom bios function int16h. The
prototype is
int serial_in( int comport );
example usage,
int comport = 0;
unsigned char ch;
ch = serial_in( comport );
<B>WRITING A CHARACTER TO A SERIAL PORT</B> serial_out()
This function writes the character out to the specified com port. It
uses the rom bios function int16h. The function prototype is,
void serial_out( char ch, int comport );
example usage,
int comport = 0;
char ch = 'A';
serial_out( ch, comport );
<B>READ SERIAL PORT STATUS</B> serial_status()
This function returns the status of the Modem Control and Line Control
registers. The MCR status is returned in AL, and the LCR status in AH.
The rom bios function int16h is used. The prototype is,
int serial_status( int comport );
example usage,
int temp, comport = 0, mcrstatus, lcrstatus;
temp = serial_status( comport );
mcrstatus = temp & 0xff;
lcrstatus = (temp & 0xff00) >> 8;
<B>OUTPUTTING A CHARACTER TO THE PRINTER</B> prn_out()
This function outputs a character to the specified printer. It uses the
rom bios function int17h. Its prototype is
void prn_out( char byte, int printer );
where printer is a value between 0 and 3 (LPT1 = 0).
example usage,
int printer = 0;
char *message = "Hello there.";
while( *message++ )
prn_out( *message, printer );
<B>READ PRINTER STATUS</B> prn_status()
This function returns the status of the specified printer. It uses the
rom bios function int17h. Its prototype is
int prn_status( int printer );
The return status is returned in the AH register
0x80 = busy 0x40 = acknowledge 0x20 = out of paper
0x10 = on-line 0x08 = I/O error 0x01 = time out
example usage,
#define out_of_paper 0x20
int status_lpt1, printer = 0;
status_lpt1 = (prn_status( printer ) >> 8);
if( status_lpt1 == out_of_paper ) {
printstr("Sorry, printer is out of paper.");
beep();
}
else
.....
<B>INITIALIZE PRINTER PORT</B> prn_init()
This function initializes the specified printer port. It uses the rom
bios function int17h. The prototype is,
int prn_init( int printer );
and returns a printer status byte in AH. See the prn_status() function
call for a description of the status bits.
example usage,
#define on_line 0x10
int printer = 0, status;
status = (prn_init(printer) >> 8);
if( status != on_line ) {
printstr("Sorry, printer is off_line.");
beep();
}
<B>TEST FOR KEYBOARD CHARACTER READY</B> kbhit()
This function returns non-zero if a character is available from the
keyboard, otherwise it returns zero. If a character is available, the
function kbgetch() is used to retrieve the character. The function uses
int6h and its prototype is
int kbhit( void );
example usage,
int key;
while( kbhit() == 0)
;
key = kbgetch() & 0xff;
<B>READ A CHARACTER FROM THE KEYBOARD</B> kbgetch()
This function returns an integer which contains the ASCII code in AL,
and the keyscan code in AH. It uses int16h and the prototype is
int kbgetch( void );
example usage;
int key;
key = kbgetch();
if( (key & 0xff ) == 0) /* is it a function or special key */
key = key >> 8;
else
key = key & 0xff; /* no, its ascii */
<B>GET KEYBOARD SHIFT STATUS</B> kbstatus()
This function returns the status of the caps, shift, alt, ctrl, numlock
and scroll-lock keys. It uses the rom bios function int16h, and the
prototype is
int kbstatus( void );
The return integer has the following values.
0x80 = insert on 0x40 = caps toggled
0x20 = num lock togled 0x10 = scroll lock toggled
0x08 = alt key pressed 0x04 = ctrl key pressed
0x02 = left shift down 0x01 = right shift down
example usage,
#define INS 0x80
int keystatus;
keystatus = kbstatus();
if( (keystatus & INS) == INS)
beep();
<B>READ SEGMENT REGISTER VALUES</B> segread()
This function copies the values of the DS, ES, SS and CS registers
into the structure pointed to by sregs. The structure definition is
included in the file ronlib.h. The prototype is
void segread( struct SREGS *sregs );
example usage,
struct SREGS sregs;
segread( &sregs );
<B>READ A BYTE FROM A PORT</B> inportb()
This function reads a byte from an input port. Its prototype is,
int inport( int port_number );
example usage,
int value, port = 0x3ff;
value = inportb( port );
<B>WRITE A BYTE TO A PORT</B> outportb()
This function writes a character out to an output port. The prototype is
void outportb( int port, char byte );
example usage,
int port = 0x3d9;
outportb( port, '9' );
<B>SOFTWARE GENERATED INTERRUPTS</B> int86()
This function generates a software interrupt of the type specified.
It accepts a structure (defined in romlib.h) which represents the
programming model of the CPU. Before executing the interrupt, the
function copies the value of each pseudo register into the cpu's
registers. On exit, the function copies the cpu registers back into
the pseudo registers. The prototype is
void int86(int intnum,struct REGS *inregs,struct REGS *outregs );
example usage,
struct REGS regs;
int video_page, video_columns, video_mode;
regs.h.ah = 15; /* get video status */
int86( 0x10, &regs, &regs );
video_page = regs.h.bh;
video_columns = regs.h.ah;
video_mode = regs.h.al;
<B>WRITE A BYTE TO MEMORY</B> pokeb()
This function writes a byte to the specified absolute address. The
prototype is,
void pokeb(unsigned segment,unsigned offset,unsigned char byte );
example usage,
unsigned int segment = 0xb000, offset = 0;
unsigned char value = 'A';
pokeb( segment, offset, byte );
<B>READ A BYTE FROM MEMORY</B> peekb()
This function returns a character from memory. The prototype is
char peekb( unsigned segment, offset );
example usage;
unsigned int segment = 0xb000, offset = 0;
unsigned char value;
value = peekb( segment, offset );
<B>EQUIPMENT DETERMINATION</B> biosequip()
This function returns the equipment settings for the attatched hardware.
It uses int11h and the prototype is
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -