📄 cstart.htm
字号:
</PRE>
The purpose of the BSSEND segment is to provide a means of
determining how long _BSS is. As BSSEND occurs after _BSS,
then zeroing everything from _BSS to _BSSEND will ensure that
all of _BSS is zeroed out!
<P>
<HR>
<B>INITIALIZATION OF THE _DATA SEGMENT</B><BR>
The _DATA segment must be copied to RAM before execution of _main()
begins.
<PRE>
_DATA segment para public 'DATA'
_DATA ends
_DATAEND segment para public 'DATAEND'
public enddata
enddata label byte
_DATAEND ends
DGROUP group _DATA, _DATAEND, _BSS, _BSSEND
_TEXT segment byte public 'CODE'
_TEXT ends
_TEXTEND segment para public 'CODEEND'
public codeend
db 16 dup (?)
codeend label byte
_TEXTEND ends
CGROUP group _TEXT, _TEXTEND
_TEXT segment
assume CS:_TEXT, DS:DGROUP, ES:DGROUP, SS:_STACK
start: cli ; disable interrupts
... ; initialize _STACK
... ; initialise _BSS
mov ax, seg DGROUP
mov es, ax ; point ES to _DATA
mov cx, offset DGROUP:enddata
mov si, 0
mov di, 0
assume ds:CGROUP:_TEXTEND
mov ax, seg _TEXTEND:codeend
inc ax
mov ds, ax ; point DS to _CONST
rep movsb ; copy _CONST to _DATA
push es ; point DS to _DATA
pop ds
sti ; enable interrupts
call _main
jmp start
_TEXT ends
</PRE>
The option chosen by the <A HREF="sw/turboc/locate.exe">LOCATE</A> utility
is to duplicate the _DATA segment into a segment called _CONST.
This is then attached to the end of the _CODE segment and stored in EPROM.
<P>
The startup code then copies this _CONST segment to the address reserved in
RAM for _DATA. It figures out the start address of the _CONST segment using
the dummy _TEXTEND segment, and the length of the _DATA segment using the
dummy segment _DATAEND.
<P>
<HR>
<B>ROMCODE AND THE TURBO C COMPILER</B><BR>
A sample startup code, written in assembler is
<PRE>
<A HREF="sw/turboc/tcf600.asm">; tcstart.asm, for f600:0000</A>
extrn _main:far
_text segment byte public 'CODE'
_text ends
_textend segment para public 'CODEEND'
_textend ends
_data segment para public 'DATA'
_data ends
_dataend segment para public 'DATAEND'
_dataend ends
_bss segment para public 'BSS'
_bss ends
_bssend segment byte public 'BSSEND'
_bssend ends
_stack segment para stack 'STACK'
_stack ends
DGROUP group _DATA, _DATAEND, _BSS, _BSSEND
CGROUP group _TEXT, _TEXTEND
_TEXT segment
assume CS:CGROUP, DS:DGROUP, ES:DGROUP, SS:_STACK
start: cli ; disable interrupts
mov ax, _STACK ; initialize stack
mov ss, ax
mov ax, offset stackend
mov sp, ax
mov ax, _BSS
mov es, ax
mov cx, offset DGROUP:enddata ; calculate length of _BSS
mov ax, offset DGROUP:endbss
sub ax, cx
mov cx, ax
mov di, 0
mov ax, 0
rep stosb ; write to ES:DI
mov ax, seg DGROUP
mov es, ax ; point ES to _DATA
mov cx, offset DGROUP:enddata
mov si, 0
mov di, 0
assume ds:CGROUP:_TEXTEND
mov ax, seg _TEXTEND:codeend
inc ax
mov ds, ax ; point DS to _CONST
rep movsb ; copy _CONST to _DATA
push es ; point DS to _DATA
pop ds
sti ; enable interrupts
call _main
jmp start
_TEXT ends
_TEXTEND segment
public codeend
db 16 dup (?)
codeend label byte
_TEXTEND ends
_STACK segment
db 1024 dup ('STACK');
stackend label word
_STACK ends
_DATAEND segment
public enddata
enddata label byte
_DATAEND ends
_BSSEND segment
public endbss
endbss label byte
_BSSEND ends
end
</PRE>
The C source is compiled using the command line version of the
compiler (tcc), then linked (using tlink) with the startup code
and any user supplied libraries or object files
(<A HREF="sw/turboc/doit.bat">doit.bat</A>).
<PRE>
tasm /mx tcstart
tasm /mx <A HREF="sw/turboc/tclib.asm">tclib</A>
tcc -a- -c -f- -G- -K -B -ml -M -N- -O- -r- -v- -y- -Z- -S -O- %1.c
tlink /m tcstart %1 tclib, %1, %1
locate %1
<A HREF="sw/turboc/hexbin2.exe">hexbin2</A> %1.hex %1.bin i f600
</PRE>
The LOCATE utility (developed by R Naro, Dr Dobbs, Dec 1987) is
then used to process the .EXE file and assign physical addresses
to each of the segments. This creates a .HEX file which is then
used to burn an EPROM. The locate utility uses a special configuration
file which specifies where the segments are to be located.
<P>
<HR>
<B>ROMCODE AND THE IC86 INTEL C COMPILER</B><BR>
The ic86 compiler generates the following segments using a LARGE
memory model,
<PRE>
filename_CODE
filename_DATA
filename_CONST ; if using the RAM directive
STACK
</PRE>
It does not prefix variable names, segments or function names with
an underscore. If the compiler option ROM is specified, all constants
and string literals are placed into the filename_CODE segment.
<P>
The startup code declares the segments which are used to arrange
them in the correct order in physical memory. Initialized constants
are placed in the FILENAME_CONST segment (when using RAM, else they
are placed in the filename_CODE if using the ROM directive).
<P>
All other data is placed into FILENAME_DATA. This creates a small
problem in referencing data, so it is best to local variables and
pass them as parameters to functions which access them (ie, do
not use GLOBAL variables!).
<P>
Initialized global variables should be avoided if designing
programs for ROM. The compiler generates an error if it encounters
initialized data, eg,
<PRE>
static int y = 7;
static int z;
</PRE>
compiled with the command line
<PRE>
ic86 file.c LARGE ROM
</PRE>
will generate an error for the initialization of the variable y.
As the DATA segment will be placed in RAM, any initial values will
be lost.
<P>
The compiler also inserts code to initialize the DS register to
the FILENAME_DATA segment. The following code will be used with
the IC86 compiler.
<PRE>
<A HREF="sw/intel/c86start.asm">; c86start.asm</A>
name c86start
extrn main : far
STACK segment para stack 'STACK'
public top
db 1024 dup ('STACK')
top label word
STACK ends
CODE segment byte public 'CODE'
assume cs:CODE, SS:STACK
start proc far
entry: cli
mov ax, STACK
mov ss, ax
mov ax, offset top
mov sp, ax
sti
call main
jmp entry
start endp
CODE ends
end
</PRE>
The command line sequence to generate the file is,
<PRE>
asm86 c86start.asm
ic86 <A HREF="sw/intel/test.c">test.c</A> LARGE
link86 c86start.obj,test.obj,c86clib.obj to test.lnk nobind
loc86 test.lnk order(classes(stack,data,code)) &
>> order(segments(STACK,TEST_DATA,CODE,TEST_CODE)) &
>> ad(sm(stack(10000h),test_data(20000h))),(code(0f6000h)))
oh86 test to test.hex
</PRE>
The segment maps look like,
<PRE>
C Program c86start
+---------+ +---------+
| |FILENAME_CODE |+++++++++|STACK
| | |+++++++++|
| | |+++++++++|
+---------+ +---------+
| |FILENAME_DATA |/////////|CODE
| | |/////////|
+---------+ +---------+
|+++++++++|STACK
+---------+
</PRE>
Combined segments after linking and locating are,
<PRE>
+---------+
|+++++++++|STACK (10000h)
|+++++++++|
|+++++++++|
+---------+
| |FILENAME_DATA (20000h)
| |
+---------+
| |
| |
+---------+
|/////////|CODE (0F6000h)
|/////////|
+---------+
| |FILENAME_CODE
| |
| |
+---------+
+---------+
</PRE>
<P>
<HR>
<B>GENERATING ROMCODE FOR PC/XT AT F600:0000 USING TURBO-C</B><BR>
There is an empty ROM slot on some PC/XT compatibles. This slot is
normally used for BASIC on IBM machines. It is memory mapped to
segment 0F600h. Using this ROM space has the following advantages,
<UL>
<LI>no memory board required
<LI>can use BIOS routines for keyboard, video, disk etc
<LI>simple to use
</UL>
<P>
When the PC is powered up, it first attempts to boot from floppy
disk. If this is unsuccessful, it reads the keyboard then generates
an int18h instruction. This normally performs the following instruction,
<PRE>
JMP F600:0000
</PRE>
However, generating code to fit into this empty space requires
special libraries or run time routines. Compilers like TC and
MSC have libraries which use DOS in order to perform many functions.
Since our turnkey system does not have DOS loaded, any systems we
design using these libraries will certainly result in a system crash.
<P>
To overcome this, we shall design our own library of routines which
emulate functions found in TC. These routines will look like
functions in C, but will be written in assembler. First, lets look
at creating a header file for these calls.
<P>
<PRE>
<A HREF="sw/turboc/crom.h">
/* CROM.H, designed by SE2, 1990 for EMBEDDED CODE */</A>
struct eightbit
{
unsigned char al, ah, bl, bh, cl, ch, dl, dh;
};
struct sixteenbit
{
unsigned int ax, bx, cx, dx, si, di, cflag;
};
union REGS
{
struct sixteenbit x;
struct eightbit h;
};
/* function prototypes follow */
extern void outportb( unsigned int, char);
extern char inportb( unsigned int );
extern void int86( int, union REGS *, union REGS * );
extern void enable( void );
extern void disable( void );
extern void setvect( int, void (*ptr)( void ) );
</PRE>
The _int86 call provides a method of interfacing to the existing
ROM BIOS routines, which are accessed via software interrupt calls.
<P>
The library code, stored in TCLIB.ASM, looks like,
<PRE>
<A HREF="sw/turboc/tclib.asm">
; TCLIB.ASM, library routine for int86() calls from EMBEDDED CODE</A>
; designed by SE2, 1990
; works with CROM.H
; turboc code must be compiled using
; tcc -a- -c -f- -G- -K -B -ml -M -N- -O- -r- -v- -y- -Z- -S -O- %1.c
public _enable
public _disable
public _setvect
public _int86
public _inportb
public _outportb
CODE segment para PUBLIC 'CODE'
assume cs:CODE
name tclib
; void enable( void ) ;
; enable interrupts
_enable proc far
cli
ret
_enable endp
; void disable( void );
; disable interrupts
_disable proc far
sti
ret
_disable endp
; void setvect( int vetcnumber, void (*ptr)( void ) );
; set interrupt vector to function code
_setvect proc far
push bp ;acess local vars inside functions
mov bp,sp ;use bp to acess passed parameters
push ax
push bx
push dx
push es
mov al, byte ptr [bp+6] ; int number
mov ah, 0
rol ax, 1
rol ax, 1
mov bx, ax
xor ax, ax
mov es, ax
mov dx, [bp+10] ; segment
mov es:[bx+2], dx
mov dx, [bp+8] ; offset
mov es:[bx], dx
pop es
pop dx
pop bx
pop ax
pop bp
ret
_setvect endp
; void int86( int intnumber, union REGS *, union REGS *);
_int86 proc far
push bp ;acess local vars inside functions
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -