⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cstart.htm

📁 这是一个TC2.0下的对X86平台进行ROM运行的开发工具,包括源代码和文档说明。包括重写了TC的启动代码和一个代码定位工具。
💻 HTM
📖 第 1 页 / 共 3 页
字号:
<!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 Software</H2></CENTER>
<CENTER><B>Copyright Brian Brown, 1989-1995</B></CENTER>
<A HREF="default.htm"><IMG SRC="images/menu.gif" ALT="menu"></A>
<A HREF="swparta.htm"><IMG SRC="images/previous.gif" ALT="prev"></A><BR>
<HR>
<B>INTRODUCTION</B><BR>
Target systems are generally TURNKEY, which means that when the computer 
is powered on the software automatically runs without user intervention.
<P>
This implies that there is possibly no operating system present, rather, 
the software contains all the routines necessary for correct operation of 
the system.
<P>
In designing software for these types of applications, it is important 
to design the software in such a way as to perform the necessary hardware 
device initializations, and to handle shutdowns and error situations 
gracefully.
<P>
<HR>
<B>HARDWARE CONSIDERATIONS</B><BR>
The processor, upon a reset or power on situation, gets the start address 
of the program from a RESET VECTOR.
<P>
The software stored in ROM must contain this vector. Other tasks the 
software must do is,
<UL>
<LI>test the RAM, ROM and CPU
<LI>initialize any hardware peripheral chips
<LI>test error/interrupt circuitry and exception handlers
</UL>
<P>
<HR>
<B>TESTING ROM,RAM and CPU</B><BR>
Testing the ROM can be achieved by a simple checksum loop. A spare 
location in the ROM can be used to store its checksum.
<P>
When the target system is turned on, it computes its own ROM checksum 
then compares it with that stored in ROM. If these checksums are the 
same, the program can then continue.
<PRE>
checkrom:       xor     ax, ax                  ; clear ax
		mov     ds, 0f000h              ; start at location 0 in EPROM
		mov     si, 0                   ;    offset 0
		mov     cx, 0ffffh              ;    number of characters in checksum
chklp1:         add     ax, ds:[si]             ; add character to total
		inc     si                      ; point to next character
		loop    chklp1
		mov     si, chksum              ; now compare with rom chksum
		cmp     ax, ds:[si]
		jne     rombad
next:
		org     0ffffh                  ;top of 32k eprom 27C256
chksum:         db      04h                     ; calculated checksum

</PRE>
Testing RAM can be done by writing values to the RAM areas and then 
verifying that the read-back value is the same.
<P>
It is vital that these routines are written in such a way that they do 
not rely upon the use of a STACK, ie, using CALLS, as a RAM error will 
cause the system to crash upon return from the test module.
<P>
The best patterns to use are AA and 55 as these test for flawed bits 
within each byte.
<P>
In most systems it will be necessary to initialize the dynamic RAM refresh 
subsystem before testing the RAM. The IBM-PC uses a timer counter chip 
(8239, channel 0), tied to a direct memory access chip (8253) to refresh 
the dynamic RAM by pseudo-reads.
<P>
RAM chips normally go bit faulty, in that one or more bits fail to 
assume the correct logic state. Using the patterns AA and 55 test for 
these faulty bits. There are more comprehensive memory tests available,
<UL>        
<LI>walking ones
<LI>walking zeros
<LI>checker board
</UL>
<P>
Which test is used is dependent upon how critical the application is. 
More comprehensive tests can take long periods to complete.
<PRE>
ramtest:        mov     ds, 0h                  ; RAM start 00000h
		mov     cx, 4000h               ; test 16k RAM
		mov     si, 0
		mov     dx, 0AA55h
ramlp1:         mov     al, dh
		mov     byte ptr ds:[si], al            ; write value
		cmp     al, byte ptr ds:[si]            ; read back and compare
		jne     ramerr
		mov     al, dl
		mov     byte ptr ds:[si], al            ; write value
		cmp     al, byte ptr ds:[si]            ; read back and compare
		jne     ramerr
		inc     si
		loop    ramlp1

</PRE>
Testing the CPU can be performed by loading the registers with values 
and doing known calculations, then testing the register result against 
the expected result (using the condition code register).
<PRE>
	cputest:        cli                                     ; disable interrupts
			mov     ah, 0d5h                        ; set sf, cf, zf, and af
			sahf
			jnc     cpuerr                          ; test carry
			jnz     cpuerr                          ; test zero
			jnp     cpuerr                          ; test parity
			jns     cpuerr                          ; test sign
			lahf                                    ; transfer flags to ah
			mov     cl, 5                           ; test auxillary carry
			shr     ah, cl                          ; rotate ah
			jnc     cpuerr
			mov     al, 40h                         ; test overflow flag
			shl     al, 1
			jno     cpuerr
			xor     ah, ah
			sahf                                    ; clear sf, cf, zf, pf
			jc      cpuerr
			jz      cpuerr
			js      cpuerr
			jp      cpuerr
			lahf
			mov     cl, 5
			shr     ah, cl
			jc      cpuerr
			shl     al, 1
			jo      cpuerr
	regtest:        mov     ax, 0ffffh
			mov     ds, ax
			mov     bx, ds
			mov     es, bx
			mov     cx, es
			mov     ss, cx
			mov     dx, ss
			mov     sp, dx
			mov     bp, sp
			mov     si, bp
			mov     di, si
			or      ax, di
			jnz     cpuerr

</PRE>
Modern processors such as the iAPX386 perform self tests at power on, 
with EAX holding 00000000h to indicate success. Register DX holds the 
Component and ID Revision numbers of the 386 chip.
<P>
The preferred order of testing is CPU, ROM then RAM.
<P>
In large memory systems, a parity generator is probably used. This 
requires either 0's or 1's to be written to the memory in order for 
the parity comparator system to work properly (the PC requires 0's).
<P>
The parity system in the PC is connected to the NMI interrupt line, 
so this may require a dummy service routine whilst the parity system 
is being initialized.
<P>
Normally, the NMI is disabled by writing to port A0h.
<PRE>
	nmioff:         mov     al, 0
			out     0a0h, al

</PRE>
<HR>
<B>INITIALIZE HARDWARE PERIPHERAL CHIPS</B><BR>
Typical hardware devices are
<UL>        
<LI>Parallel interface chips (8255)
<LI>Serial interface chips (8250)
<LI>Priority interrupt controllers (8259)
<LI>Real time clocks etc
</UL>
<P>
The first step is obtaining data sheets for each of the chips then 
writing initialization routines as HLL algorithms. These can then 
be compiled or converted to assembler.
<P>
Determine the order in which the devices must be initialized, system 
chips like PIC's may need to done first. PROBABLY, the PIC and timers 
would be disabled till all the other hardware is initialized, at which 
time they would be re-activated.
<P>
You can't have interrupts being generated whilst setting up the system! 
The hardware should also be tested using dummy interrupts. Serial 
systems can be tested using LOOP BACK MODE, parallel systems for 
handshake signals. 
<P>
Spare bits on parallel ports could be used for system checkout.
<P>
<HR>
<B>INTERRUPTS/EXCEPTION HANDLING, ERROR RECOVERY</B><BR>
Interrupts can be tested using a dummy interrupt routine, the address 
of which is placed into the associated vector location. An interrupt 
is then forced and the dummy routine checks to see if the interrupt 
call worked.
<PRE>
; irq vectors are located in RAM and initialised at power-up

	org     0004h           ; irq 1
dw              dummyint

	CSEG
dummyint:       mov     ax, 0h
		iret

testint:                mov     ax, 0ffh
		swi     1
		cmp     ax, 0
		jne     interr
		ret

</PRE>
Unexpected interrupts can also be tested for by initialising all 
interrupt vectors to the dummy interrupt handler, and waiting for 
a small delay.
<PRE>
testall:        mov     ax, 0ffh
		sti                             ; enable interrupts
		sub     cx, cx
tstlp1:         loop    tstlp1                  ; wait for 1 second
tstlp2:         loop    tstlp2
		cmp     ax, 0ffh                ; any interrupts occur?
		jne     interr

</PRE>
Exception handling deals with the occurrence of unexpected 
hardware/software errors. These could be interrupts, overflows/wrap 
around, divide by zero, power down etc. All possible foreseeable 
exceptions should be catered for by software, or disabled by the 
use of hardware. This could be simple IRET statements.
<P>
Some systems use watchdog timers to prevent software runaway. A watchdog 
timer is programmed to generate an interrupt at regular intervals (say 
every 20milliseconds). The timer is loaded with a count value and 
decrements by one for each system clock period (or a divisor thereof). 
The main software periodically resets the timer (every 15ms) to prevent 
the interrupt occurring.
<PRE>
	main()
	{
	    reset_timer(1000);
	    for( ; ; ) {
		.......
		.......
		reset_timer(1000);
		.......
		.......
		reset_timer(1000);
	    }
	}

</PRE>
When the software hangs, the timer will not be reset, thus generating an 
interrupt. The service routine will then either recover, restart, or 
shut the system down.
<P>
Error recovery means handling exceptions in such a way as to continue 
processing with little or no degrading of performance. It is best to 
incorporate several layers of error recovery so that a system is said to 
'degrade gracefully'.
<P>
It should not fail instantly. Certain techniques used could be switching 
to an alternate RAM area, using timeouts and retrying (for parallel and 
serial ports), performing calculations etc.
<PRE>
	retry
	re-initialize then retry
	abort

</PRE>
<HR>
<B>WARM-RESTARTS AND BATTERY BACKED UP VARIABLES</B>><BR>
Often, a running program may crash. At this resetting the target 
system would re-initialise the RAM and hardware, and make debugging 
difficult.
<P>
It is easy to add simple warm restart capability to the target system 
by using a variable located in RAM. At power-up, this variable is 
tested for a specific value, and if set, this indicates a warm-start 
is required.
<PRE>
	warmboot:       cmp     warmbootflag, 1234h
			je      warmstart
			mov     warmbootflag, 1234h
	warmstart:

</PRE>
<P>
<HR>
<B>SOFTWARE INITIALIZATION AND C PROGRAMS</B><BR>
Divide memory into segments holding CODE, DATA and STACK areas. On 
startup, the DATA segment should be zeroed out and any initialized 
variables have their values copied from ROM into RAM.
<P>
The processor stack registers is initialized to the STACK 
segment (SS and SP), and the processor data registers to the 
DATA segment (ES and DS).
<P>
Some compilers have special pre-processor directives to simplify 
segment placement, eg, the CC09 supports #DATA, #CODE and #CONST, 
which specify segment types to the assembler/linker utilities.
<P>
<HR>
<B>THE STRUCTURE OF C PROGRAMS</B><BR>
A typical C program comprises the following segments
<PRE>
	_TEXT   Code
	_STACK  Stack
	_BSS    Un-initialized data, eg, static int i;
	_DATA   Initialized data, eg,    static int y = 3;

</PRE>
NOTE: The IC86 compiler does not use underscores or the _BSS segment, 
whereas both TurboC and MSC do. Standard convention is to prefix 
underscores to variable and function names (except for the IC86 compiler).
<P>
The two data segments are combined forming a single concatenated 
data segment called DGROUP. The C compiler inserts code to zero 
out the _BSS segment before jumping to _main. Because the _BSS segment 
contains zero's, it does not need to be stored in ROM.
<P>
The _DATA segment, containing initialized values, will need to be 
appended at the end of the _CODE segment, and stored in ROM. During 
startup, the _DATA segment will be copied to RAM, then the DS 
register will be switched to the RAM area.
<P>
In creating rommable code, it is necessary to write your own 
startup code which replaces that of the C compiler (TurboC uses 
C0x.obj). The basic structure of the startup code is,
<UL>
<LI>initialize the DS and SS registers
<LI>zero out the _BSS segment
<LI>copy initialized data from ROM into RAM
<LI>switch DS register from ROM to RAM
<LI>jump to main
</UL>
<P>
The startup code is normally written in assembler, so that direct 
control over segment placement can be achieved.
<P>
<HR>
<B>INITIALIZATION OF THE _STACK SEGMENT</B><BR>
The first thing the startup code will do is initialize the 
stack registers to point to the _STACK segment.
<PRE>
; startup.asm

_STACK          segment         para stack 'STACK'
		db              1024 dup ('STACK');
stackend                label           word
_STACK  ends

_TEXT           segment byte public 'CODE'
		assume CS:_TEXT, DS:_DATA, ES:_BSS, SS:_STACK
start:          cli                ; disable interrupts
		mov     ax, _STACK ; initialize stack
		mov     ss, ax
		mov     ax, offset stackend
		mov     sp, ax
		...
		...
		sti
		call    _main
		jmp     start
_TEXT           ends


</PRE>
<HR>
<B>INITIALIZING THE _BSS SEGMENT</B><BR>
The entire _BSS segment will be zeroed out by the following code portion.
<PRE>
; startup.asm

_BSS            segment para public 'BSS'
_BSS            ends

_BSSEND         segment         byte public 'BSSEND'
		public          endbss
endbss          label           byte
_BSSEND         ends

DGROUP  group   _BSS, _BSSEND

_TEXT           segment byte public 'CODE'
		assume CS:_TEXT, DS:DGROUP, ES:DGROUP, SS:_STACK
start:          cli             ; disable interrupts
		...             ; initialize stack
		mov     ax, seg _BSS
		mov     es, ax
		mov     cx, offset DGROUP:endbss
		mov     di, 0
		mov     ax, 0
		rep     stosb   ; write to ES:DI
		...
		sti
		call    _main
		jmp     start
_TEXT           ends


⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -