dnl (c)INRIA 1999 Christophe.Lavarenne@inria.fr dnl $Id: 386.m4x,v 1.1 2000/04/17 12:59:18 lavarenn Exp $ divert(-1) # SynDEx v5.1 generic executive kernel for i80386, to be compiled under DJGPP ifdef(`syndex.m4x_already_included',,`errprint( __file__:__line__: m4: syndex.m4x must be included before __file__! )m4exit(1)') ifdef( __file__`_already_included',`error_(`already included')', `define(__file__`_already_included')') # ---------------------------------------------------------------------------- # This file defines all TARGET LANGUAGE DEPENDENT macros (prefixed by `basic') # which customize the generic executive macros defined in the syndex.m4x file. # SynDEx generates for each processor one macro-executive file starting with a # `processor_' macro taking as argument the processor type, which is stored in # the `processorType_' macro, and used to include the processorType_.m4x file. # Application specific macros may be conditionned by `processorType_'=`386' # to generate different codes for different target processor types; # they may also be conditionned by `lang_' which may be defined # identically by different processorType_.m4x files: define(`lang_',`asm386') ########## # COMMENTS # comment{Comments with {balanced} curly braces}comment # Assembler-style till-end-of-line comments: # The TARGET LANGUAGE DEPENDENT start-comment character (# for As) is given # as 3rd argument of the patsubst macro in the following definition: #define(`comment_',`changequote({,})patsubst({{$1}},{^},{# })changequote') # default C-style multi-line comments are ok with GNU as ############ # DATA TYPES # -------- # typedef_(name, size) defines a new type with its size in address units: typedef_(`bool', 1) # MUST be defined typedef_(`char', 1) # SHOULD be defined typedef_(`int', 4) # SHOULD be defined typedef_(`float', 4) # SHOULD be defined typedef_(`double', 8) # SHOULD be defined ################### # MEMORY ALLOCATION # Data buffers for temporary signals, delays, windows and constants, # are located in the uninitialized .bss section (or in another unitialized # section named "syndexN", N being the "memBank" last optional macro argument) # and are referenced by the symbolic "label" given to the macros. # ----------- # basicAlloc_(label, memoryBank) # Only one memory bank on i80386, allocated in multiples of 4 bytes # to keep addresses aligned to fasten memory accesses: define(`basicAlloc_', `_(.lcomm $1,eval($1_size_*$1_type_()_size_+3&~3))') # ----------- # basicAlias_(newLabel,oldLabel[,offset=0]) # Makes an equivalence between newLabel and oldLabel+charOffset define(`basicAlias_', `_($1 = ifelse($3,,`$2',`($2+$3*$2_type_()_size_)'))') ################## # MEMORY TRANSFERS # ---------- # basicCopy_(destLabel,srceLabel,size) define(`basicCopy_', `pushdef(`size_',`eval($3*$2_type_()_size_)')dnl ifelse(dnl copy memory range. size_,1, `_(movb $2,%al; movb %al,$1)', dnl copy one byte. size_,2, `_(movw $2,%ax; movw %ax,$1)', dnl copy one word. size_,4, `_(movl $2,%eax; movl %eax,$1)', dnl copy one long. `_(movl `$'$1,%edi; movl `$'$2,%esi; )ifelse(dnl copy several: eval(size_&1),1, `_(movl `$'size_,%ecx; rep; movsb)', dnl bytes. eval(size_&3),2, `_(movl `$'size_/2,%ecx; rep; movsw)', dnl words. `_(movl `$'size_/4,%ecx; rep; movsl)' dnl longs. )')popdef(`size_')') #################### # CONTROL STRUCTURES # -------- # basicIf_(bool, tagforElseOrEndif) define(`basicIf_', `dnl _(testb `$'1,$1)dnl _(jz Forward_$2)') # ----------- # basicIfnot_(bool, tagForElseOrEndif) define(`basicIfnot_',`dnl _(testb `$'1,$1) _(jnz Forward_$2)') # ---------- # basicElse_(tagFromIf, tagForEndif) define(`basicElse_', `dnl _(jmp Forward_$2)dnl _(Forward_$1:)') # ----------- # basicEndif_(tagFromIfOrElse) define(`basicEndif_', `dnl _(Forward_$1:)') # ---------- # basicLoop_(tagForEndloop) define(`basicLoop_', `ifdef(`NBITERATIONS', `dnl _(jmp Forward_$1 `# fixed number of iterations')dnl _(.int NBITERATIONS `# iterations counter')')dnl _(Backward_$1:)') # ------------- # basicEndloop_(tagFromLoop) define(`basicEndloop_', `ifdef(`NBITERATIONS', `dnl _(Forward_$1: `# fixed number of iterations')dnl _(decl Backward_$1-4)dnl _(jge Backward_$1)', `dnl _(jmp Backward_$1 `# infinite loop')')') ############### # SYNCHRONIZERS # To synchronize main (priority=0) and communication sequences (priority=1). # semaphores_(...) ; allocate and initialize named semaphores n/0 def(`semaphores_', `number_($*)dnl _(.lcomm sem_,eval($#+3&~3) `# each semaphore is one byte initially null')') define(`number_',`ifelse($1,,,`_($1=decr($#))`'number_(shift($*))')') # Pre0_(s) ; priority 1->0 precedence def(`Pre0_', `dnl _(movb `$'1,sem_+$1 `# set semaphore $1')') # Suc0_(s) ; priority 0<-1 precedence def(`Suc0_', `dnl (bit-test-and-reset could be used on 386) _(0: testb `$'1,sem_+$1 `# test semaphore $1:')dnl _(jz 0b `# until set by Pre0_($1) on interrupt;')dnl _(movb `$'0,sem_+$1 `# reset it.')') # Pre1_(s) ; priority 0->1 or 1->1 precedence def(`Pre1_', `dnl _(call Suc_$1_ `# label Suc_$1_ defined by Suc1_($1)')') # Suc1_(s) ; priority 1<-0 or 1<-1 precedence def(`Suc1_', `dnl _(Suc_$1_:)dnl _(xorb `$'1,sem_+$1 `# change and test semaphore $1:')dnl _(jz 0f `# if null (2nd pass) continue;')dnl _(ret `# otherwise (1st pass) return.')dnl _(0:)') ############### # MAIN sequence # ---------- # basicMain_() ############################# # These definitions are inserted at the very beginning of the generated file, # undiverted by the `processor_' macro after generating file header comments: divert(1) _(.lcomm lock_bss_start,0 `# all code and data are DPMI-locked,')dnl _(lock_code_start: `# for realtime execution cannot afford any swap')dnl divert(-1) define(`basicMain_', `dnl _(_main: .global _main `# for link with C runtime boot')dnl _(pushl %ebp `# save EBP usually used by C runtime library')dnl _(movl %esp,%ebp `# EBP:{0:oldEBP, 4:return, 8:argc, 12:argv}')dnl _(`# void _go32_dpmi_lock_code(void *lockaddr, unsigned long locksize);')dnl Ccall_(void, _go32_dpmi_lock_code, const lock_code_start, const lock_code_end-lock_code_start)dnl _(`# void _go32_dpmi_lock_data(void *lockaddr, unsigned long locksize);')dnl Ccall_(void, _go32_dpmi_lock_data, const lock_bss_start, const lock_bss_end-lock_bss_start)dnl ') # ------------- # basicEndmain_() define(`basicEndmain_', `dnl _(popl %ebp `# restore EBP usually used by C runtime library')dnl _(xor %eax,%eax `# return null=SUCCES code')dnl _(ret `# end of main') _(lock_code_end: `# all code and data are DPMI-locked,')dnl _(.lcomm lock_bss_end,0 `# for realtime execution cannot afford any swap')dnl ') # ------------------------ # spawn_thread_(mediaName) ; spawn the pseudo-parallel execution of a com.seq. def(`spawn_thread_', `dnl _(call thread_$1_ `# label defined by thread_($1)')') # ----------------------- # basicThread_(mediaName) define(`basicThread_', `dnl _(thread_$1_: `# --------- start of Media $1 thread ----------')') # -------------------------- # basicEndthread_(mediaName) define(`basicEndthread_', `dnl _(.lcomm $1_empty_,4)dnl _(movb `$'1,$1_empty_ `# set semaphore $1_empty_')dnl _(ret `# return after CALL of `Pre1_' or of comINTshared_')dnl _(`# ------------- end of Media $1 thread ----------------')') # -------------------------- # wait_endthread_(mediaName) ; wait for com.seq. thread to terminate def(`wait_endthread_', `dnl _(0: testb `$'1,$1_empty_ `# test semaphore $1_empty_:')dnl _(jz 0b `# until set by endthread on interrupt;')') ifelse(` ******ICI ###################### # CHRONOMETRIC LOGGING dnl GetTime_() ; read the PC real-time clock define(`GetTime_', `# Read PC timer, result in EAX: xorl %eax,%eax # clear MSW outb %al,`$'0x43 inb `$'0x40,%al # Read timer LSB value movb %al,%ah inb `$'0x40,%al # Read timer MSB value xchgb %al,%ah negw %ax # timer is decrementing') define(`GetTime_', `# Read PC timer@1,193,180 ticks/sec, result in EDX:EAX call _uclock # this takes about 155E-7 sec on a Pentium@100MHz') define(`CvtTime_', `# PC ticks at 1.19318 MHz, C40 ticks at 10 MHz movl `$'10000000,%ebx ; mull %ebx movl `$'1193180,%ebx ; divl %ebx') dnl Chronos_(size) ; size=number of (label,date) records def(`Chronos_', ` .data Chrono__: # chronometric (label,date) records buffer .int Chrono_base_ # current-record pointer, initialized Chrono_base_: # buffer base address .fill $1,2+4,0 # buffer body, all labels null Chrono_limit_: # buffer end address .int 0 # temp for `Chrono_store_' .text ChronoLap__: # AX= label movl Chrono__,%edi # EDI= current chrono record address stosw # store label GetTime_() # EAX= date (current time) stosl # store date cmpl `$'Chrono_limit_,%edi jnz 0f # if end of Chrono_limit_ reached: movl `$'Chrono_base_,%edi # wrap pointer to Chrono_base_ 0: movl %edi,Chrono__ # store back next record address ret') # ChronoLap_(label) def(`ChronoLap_', `dnl _(movw `$'$1,%ax)dnl _(call ChronoLap__)') # Chrono_() is there anything to initialize on 386? def(`Chrono_ini_', `pushtag(`Chrono') # first call takes care of initializations: GetTime_') # endChrono_() def(`endChrono_', `poptag(`Chrono') ifdef(`ChronoMulti', ` IOB=0x200 # TDMB410 motherboard I/O ports base address RXSTAT=IOB+0x02 # RO byte, 0=empty, otherwise 0FFh TXSTAT=IOB+0x03 # RO byte, 0=full, otherwise 0FFh RXDATA=IOB+0x10 # RO word, may be read when RXSTAT!=0 TXDATA=IOB+0x12 # WO word, may be written when TXSTAT!=0 #**** set offset from descendant to local time movw `$'TXDATA,%dx # send ready signal to C40 movw `$'(1193180 & 0xFFFF),%ax outw %ax,%dx movw `$'(1193180 >> 16),%ax outw %ax,%dx call Lgetd_ # wait for dated ping from C40 movl %eax,%ebx # EBX= C40 ping date(lo) GetTime_() # EAX= PC timer date when ping received CvtTime_() # convert into tenth of microseconds subl %eax,%ebx # EBX= dated pong = C40ping-PCtimer movw `$'TXDATA,%dx movl %ebx,%eax # return dated pong to C40 outw %ax,%dx # bits0-15 shrl `$'16,%eax outw %ax,%dx # bits16-31') #**** dump oldest part from current to Chrono_limit_ movl Chrono__,%esi # ESI= oldest record pointer cmpw `$'0,(%esi) # label non-null if pointer wrapped jz 1f # null if buffer not full of records 0:call Chrono_store_ # (see Chrono_store_ hereunder) cmpl `$'Chrono_limit_,%esi jnz 0b #**** dump newest part from Chrono_base_ to current 1:movl `$'Chrono_base_,%esi 2:cmpl Chrono__,%esi jz Ldescendants_ call Chrono_store_ # (see Chrono_store_ hereunder) jmp 2b #**** subroutines Chrono_store_ and Chrono_dstore_ Chrono_store_: # ESI+=4, use EAX,ECX xorl %eax,%eax lodsw # ESI=(label,date) record pointer movl %eax,%ecx # ECX=label lodsl # EAX=date CvtTime_() # convert into tenth of microseconds Chrono_dstore_: # EAX=date ECX=label xchgl Chrono_limit_,%eax negl %eax addl Chrono_limit_,%eax # time elapsed since last record pushl %eax pushl Chrono_limit_ pushl %ecx define(`args_', 3) printf_(`%6d %9d %9d') ret ifdef(`ChronoMulti', ` #**** subroutine Lgetd_ Lgetd_: # wait for data word from C40, use DX, return EAX call_args_(`kbhit') #allow loop exit with control-break movw `$'RXSTAT,%dx inb %dx,%al orb %al,%al jz Lgetd_ # until RX non empty movw `$'RXDATA,%dx xorl %eax,%eax inw %dx,%ax # bits0-15 movl %eax,%ebx inw %dx,%ax # bits16-31 shll `$'16,%eax orl %ebx,%eax ret #**** forward descendant dumps Ldescendants_: call Lgetd_ # wait for EAX=label from C40 movl %eax,%ecx # ECX=label jecxz 3f # until null label = end of dump pushl %ecx call Lgetd_ # EAX=date popl %ecx call Chrono_dstore_ jmp Ldescendants_ 3:', ` Ldescendants_: xorl %ecx,%ecx') call Chrono_dstore_ # last chrono labelled zero') ******JUSQUICI') ################## # Subroutine calls # For interfacing C functions with parameters passed by the stack. # Change these macros for other parameter passing models. # ------ # Cdecl_ generates the declaration of an separately C compiled function: # $1: return # $2: C function name (without the underscore prefix added by the assembler) # $n>2: argument (with `*' if passed by address) # Example m4 declaration: # Cdecl_(int,my_fun,int,int*,float) # | .global _my_fun ; int my_fun(int, int*, float); def(`Cdecl_', `dnl _(`.global _$2 # $1 $2('shift(shift($*))`);')') # ------ # Ccall_ mimics the ANSI C declaration of a separately compiled function: # $1: return and