TITLE "monitor.a -- Hawk Monitor and Library" ; by: Douglas W. Jones ; rewritten: July 2014 ; library routines for malloc, free, strcat, strcpy added, Sept 2019 ; library routines for ftoi (float to int) and strcmp added, Nov 2019 ; support for installing exception handlers for traps added, Jan 2024 ; requires full Hawk instruction set ; except does not use floating point coprocessor USE "hawk.h" USE "ascii.h" NULL = 0; ; ---------------------------------- ; static memory allocation support MACRO ALLOC =n . = . + n ENDMAC ; ---------------------------------- ; machine configuration ; trap vector RESTARTTRAP = #00 BUSTRAP = #10 INSTRTRAP = #20 PRIVTRAP = #30 MMUTRAP = #40 COTRAP = #50 ; interrupt vector ;IRQ0 = #80 ; dangerous -- CPU hang with default hardware IRQ1 = #90 ; recommended for high speed devices IRQ2 = #A0 IRQ3 = #B0 ; recommended for medium speed devices IRQ4 = #C0 IRQ5 = #D0 IRQ6 = #E0 IRQ7 = #F0 ; recommended for low speed devices POSTTRAP = #100 ; location beyond the trap vector ; memory mapped display interface DISPBASE = #FF000000 DISPLINES = 0 DISPCOLS = 4 DISPTEXT = #100 ; keyboard interface KBDBASE = #FF100000 KBDDATA = 0 KBDSTAT = 4 ; ---------------------------------- ; trap handler framework ; the following code intercepts all traps and interrupts, ; saves registers in the trap save area. Note that it is ; essential that trap service routines that intend to return ; to the user not cause traps themselves. ; traps have a default fatal error message ; to override the default, set the indicated exception pointer ; to point to an exception data structure, as defined in exceptions.h COMMON SYSEXCEPT,EXCEPS; exceptions thrown by various traps LC = . . = SYSEXCEPT EXBUSTR:W NULL ; thrown by bus traps EXINSTR:W NULL ; thrown by instruction traps EXEPRIV:W NULL ; exception thrown by privileg violation traps EXECOPR:W NULL ; exception thrown by coprocessor traps EXCEPS = . . = LC INT EXBUSTR,EXINSTR,EXEPRIV,EXECOPR ; fields of exception data structure, lifted from exception.h EXHAND = 0 ; one word, the address of the handler EXAR = 4 ; one word, the handler's activation record EXOLD = 8 ; one word, pointer to previous exception record ; save area structure (displacements from start of area) LC = . . = 0 PSWSV: ALLOC 4 TMASV: ALLOC 4 PCSV: ALLOC 4 R1SV: ALLOC 4 R2SV: ALLOC 4 R3SV: ALLOC 4 R4SV: ALLOC 4 R5SV: ALLOC 4 R6SV: ALLOC 4 R7SV: ALLOC 4 R8SV: ALLOC 4 R9SV: ALLOC 4 RASV: ALLOC 4 RBSV: ALLOC 4 RCSV: ALLOC 4 RDSV: ALLOC 4 RESV: ALLOC 4 RFSV: ALLOC 4 SVSIZE: ALLOC 0 . = LC COMMON SVFATAL,SVSIZE ; savearea for fatal traps SUBTITLE "Trap vector code" ; ---------------------------------- . = RESTARTTRAP ; do { --special case, start or restart EXT UNUSED ; -- needed only for restart EXT MAIN ; -- needed only for restart LIL R2,UNUSED ; 4 -- set up the stack JSR R1,DSPINI ; 4 dspini() LIL R1,MAIN ; 4 JSRS R1,R1 ; 2 main(columns, lines) BR RESTARTTRAP ; 2 } forever . = BUSTRAP CPUSET R2,TSV ; 2 LIL R2,SVFATAL+R5SV ; 4 STORES R5,R2 ; 2 savearea.r5sv = oldR5 LEA R5,BUSTRM ; 4 -- parameter, bus trap message JUMP TRAPCON ; 4 . = INSTRTRAP CPUSET R2,TSV ; 2 LIL R2,SVFATAL+R5SV ; 4 STORES R5,R2 ; 2 savearea.r5sv = oldR5 LEA R5,INSTTRM ; 4 -- parameter, instruction trap message JUMP TRAPCON ; 4 . = PRIVTRAP CPUSET R2,TSV ; 2 LIL R2,SVFATAL+R5SV ; 4 STORES R5,R2 ; 2 savearea.r5sv = oldR5 LEA R5,PRIVTRM ; 4 -- parameter, privilege violation message JUMP TRAPCON ; 4 . = MMUTRAP CPUSET R2,TSV ; 2 LIL R2,SVFATAL+R5SV ; 4 STORES R5,R2 ; 2 savearea.r5sv = oldR5 LEA R5,MMUTRM ; 4 -- parameter, mmu trap message JUMP TRAPCON ; 4 . = COTRAP CPUSET R2,TSV ; 2 LIL R2,SVFATAL+R5SV ; 4 STORES R5,R2 ; 2 savearea.r5sv = oldR5 LEA R5,COPTRM ; 4 -- parameter, coprocessor trap message JUMP TRAPCON ; 4 . = IRQ1 CPUSET R2,TSV ; 2 LIL R2,SVFATAL+R5SV ; 4 STORES R5,R2 ; 2 savearea.r5sv = oldR5 LEA R5,IRQ1TRM ; 4 -- parameter, IRQ 1 message JUMP TRAPCON ; 4 . = IRQ2 CPUSET R2,TSV ; 2 LIL R2,SVFATAL+R5SV ; 4 STORES R5,R2 ; 2 savearea.r5sv = oldR5 LEA R5,IRQ2TRM ; 4 -- parameter, IRQ 2 message JUMP TRAPCON ; 4 . = IRQ6 CPUSET R2,TSV ; 2 LIL R2,SVFATAL+R5SV ; 4 STORES R5,R2 ; 2 savearea.r5sv = oldR5 LEA R5,IRQ6TRM ; 4 -- parameter, IRQ 6 message JUMP TRAPCON ; 4 . = IRQ7 CPUSET R2,TSV ; 2 LIL R2,SVFATAL+R5SV ; 4 STORES R5,R2 ; 2 savearea.r5sv = oldR5 LEA R5,IRQ7TRM ; 4 -- parameter, IRQ 7 message JUMP TRAPCON ; 4 . = POSTTRAP SUBTITLE "Generic fatal trap continuation" ; ---------------------------------- TRAPCON:; expects: oldR5 saved in savearea->r5sv ; oldR2 saved in TSV ; R2 -- points to savearea->r5sv ; R5 -- m, pointer to exception pointer, message to print, etc. ; all other registers still hold user values ; including PSW, TMA and TPC ; first, fix R2 so it points to the start of the savearea LEA R2,R2,PSWSV-R5SV ; now, save a few registers STORE R1,R2,R1SV ; savearea->r1sv = oldR1 CPUGET R1,TSV STORE R3,R2,R3SV ; savearea->r3sv = oldR3 STORE R1,R2,R2SV ; savearea->r2sv = TSV = oldR2 ; take a break from saving registers to save other stuff CPUGET R1,PSW CPUGET R3,TMA STORE R1,R2,PSWSV ; savearea->pswsv = oldPSW CPUGET R1,TPC STORE R3,R2,TMASV ; savearea->tmasv = TMA STORE R1,R2,PCSV ; savearea->pcsv = TPC = oldPC ; finish saving registers STORE R4,R2,R4SV ; savearea->r4sv = oldR4 STORE R6,R2,R6SV ; savearea->r6sv = oldR6 STORE R7,R2,R7SV ; savearea->r7sv = oldR7 ; here: (R1-R7, PSW, TMA and TPC) all saved in savearea ; R2 -- points to R8SV (used as base of small stack) ; R5 -- m, pointer to exception pointer, message to print, etc. ; can now use existing monitor calling conventions ; is there an exception handler installed for this trap? LOADSCC R4,R5 ; get address of exception pointer BZS NOEXE ; null means no hope, user can't arm exception LOADSCC R3,R4 ; get address of exception block BZS NOEXE ; null means no hope, user didn't arm this one ; throw the exception pointed to by R3, like exceptions.h except ; must change registers in save area LOAD R1,R3,EXAR STORE R1,R2,R2SV ; savedR2 = R3->EXAR LOAD R1,R3,EXHAND STORE R1,R2,PCSV ; savedPC = R3->EXHAND LOAD R1,R3,EXOLD STORES R1,R4 ; deinstall exception BR DONEXE ; the following trap service code simply prints ; here: R2 -- points to R8SV (used as base of small stack) ; R5 -- m, pointer to exception pointer (that was not armed) ; which preceeds the null-terminated trap name ; which preceeds non-null byte if TMA to be printed ; diagnostic output and terminates! NOEXE: ADDI R2,R2,R8SV ; -- push activation record (trap save area) LEA R3,R5,4 ; -- parameter m advance over exception pointer JSR R1,PUTSTR ; x = putstr( m ) ADDSI R3,1 ; x++ -- advance over null LOADS R4,R3 EXTB R3,R4,R3 ; printtma = m[xx] CPUSET R3,TSV ; -- save printtma in TSV (rare use of it!) LEA R3,MSGPC ; -- parameter (address of string) JSR R1,PUTSTR ; putstr( "PC =" ) -- uses R3-7 LOAD R3,R2,PCSV-R8SV ; -- parameter pcsv LIS R4,8 ; -- parameter 8 JSR R1,PUTHEX ; puthex( pcsv, 8 ) -- uses R3-7 LEA R3,MSGXX ; -- parameter (address of string) JSR R1,PUTSTR ; putstr( " " ) -- uses R3-7 CPUGET R3,TSV ; -- recover printtma in TSV (rare use of it!) TESTR R3 BEQ NOTMA ; if (printtma) { LEA R3,MSGMA ; -- parameter (address of string) JSR R1,PUTSTR ; putstr( "MA =" ) -- uses R3-7 LOAD R3,R2,TMASV-R8SV SR R3,2 SL R3,2 ; -- parameter tmasv & ~3 (actual address) LIS R4,8 ; -- parameter 8 JSR R1,PUTHEX ; puthex( tmasv, 8 ) -- uses R3-7 LOAD R3,R2,TMASV-R8SV TRUNC R3,2 ; -- tmasv & 3 (trap cause) LEA R4,TCAUSES ADDSL R3,R4,2 LOADS R3,R3 ; -- parameter tcauses[tmasv & 3] JSR R1,PUTSTR ; putstr( tcauses[tmasv & 3] ) -- uses R3-7 NOTMA: ; } ADDI R2,R2,-R8SV ; -- pop activation record STORE R0,R2,PCSV ; return to location zero (HALT or restart) ; here: (R1-R7, PSW, TMA and TPC) all saved in savearea ; R2 -- points to R8SV ; First, restore (R3-R7) to prepare for restart DONEXE: LOAD R7,R2,R7SV ; R7 = savearea.r7sv LOAD R6,R2,R6SV ; R6 = savearea.r6sv LOAD R5,R2,R5SV ; R5 = savearea.r5sv LOAD R4,R2,R4SV ; R4 = savearea.r4sv LOAD R3,R2,R3SV ; R3 = savearea.r3sv ; Then, stop to restore (TPC, PSW) before restoring (R1-R2) LOAD R1,R2,PSWSV CPUSET R1,PSW ; PSW = savearea->pswsv LOAD R1,R2,PCSV CPUSET R1,TPC ; TPC = savearea->pcsv LOAD R1,R2,R1SV ; R1 = savearea.r1sv LOAD R2,R2,R2SV ; R2 = savearea.r2sv RTT ; -- done! ; TRM things begin with a word-aligned pointer to an exception pointer ; followed by a message to print if the exception pointer is null SHOWTMA = 1 NOSHOW = 0 ALIGN 4 BUSTRM: W EXBUSTR ASCII "",LF,"Bus Trap. ",0,SHOWTMA ALIGN 4 INSTTRM:W EXINSTR ASCII "",LF,"Instruction Trap.",0,NOSHOW ALIGN 4 PRIVTRM:W EXEPRIV ASCII "",LF,"Privelege Trap. ",0,NOSHOW ALIGN 4 MMUTRM: W NULL ; MMU traps don't turn into exceptions ASCII "",LF,"MMU Trap. ",0,SHOWTMA ALIGN 4 COPTRM: W EXECOPR ASCII "",LF,"Coprocessor Trap.",0,NOSHOW ALIGN 4 IRQ1TRM:W NULL ; interrupts don't turn into exceptions ASCII "",LF,"Interrupt 1. ",0,NOSHOW ALIGN 4 IRQ2TRM:W NULL ASCII "",LF,"Interrupt 2. ",0,NOSHOW ALIGN 4 IRQ6TRM:W NULL ASCII "",LF,"Interrupt 3-6. ",0,NOSHOW ALIGN 4 IRQ7TRM:W NULL ASCII "",LF,"Interrupt 7. ",0,NOSHOW ; messages used by default fatal trap handler MSGPC: ASCII " Trap PC = #",0 MSGMA: ASCII "",LF,HT,HT," Trap MA = #",0 MSGXX: ASCII " ",0 MSGINV: ASCII " invalid addr ",0 ; 00 interpretation of TMA low bits MSGNEX: ASCII " cannot fetch ",0 ; 01 MSGROM: ASCII " cannot STORE ",0 ; 10 MSGWOM: ASCII " cannot LOAD ",0 ; 11 ALIGN 4 TCAUSES:W MSGINV,MSGNEX,MSGROM,MSGWOM SUBTITLE "Standard Library" ; ---------------------------------- ; All support procedures are linked through R1. ; on procedure entry, R2 is the frame pointer. ; the stack frame grows up. Each support proc ; documents the registers it uses; the caller ; must save these if they are valuable. ; All output procedures use DSPPTR. This ; pointer points to the most recent character ; output in Video RAM. It is incremented before ; use! ; DSPCOM structure (displacements from start of area) LC = . . = 0 DSPPTR: ALLOC 4 ; points to location in Video RAM for next char DSPTXT: ALLOC 4 ; address of first word of Video RAM DSPEND: ALLOC 4 ; address just beyond end of Video RAM DSPROWS:ALLOC 4 ; display rows (copied from device interface) DSPCOLS:ALLOC 4 ; display columns (copied from device interface) DSPSIZE:ALLOC 0 COMMON DSPCOM,DSPSIZE ; description of display memory . = LC TERMINFO= DSPCOM+DSPROWS INT TERMINFO TERMSIZE = DSPCOM + DSPROWS; INT TERMSIZE ; export terminal size {ROWS,COLS} ALIGN 4 ; ---------------------------------- INT EXIT EXIT = 0 ; terminate application ; no parameters, does nothing ; ---------------------------------- INT DSPINI ; (re)initialize for display output ; activation record ;RETAD = 0 ; return address ARSIZE = 4 ; total size DSPINI: ; expects: nothing ; uses R3-7 STORES R1,R2 ADDSI R2,ARSIZE LIW R7,DISPBASE ; -- use R7 so it survives TIMESU LEA R3,R7,DISPTEXT ; -- initial value of dspptr LIL R5,DSPCOM STORES R3,R5;DSPPTR ; dspptr = dispbase+disptext STORE R3,R5,DSPTXT ; dsptxt = dispbase+disptext LOAD R3,R7,DISPCOLS ; R3 = display columns STORE R3,R5,DSPCOLS ; -- copy to dspcom LOAD R4,R7,DISPLINES ; R4 = display lines STORE R4,R5,DSPROWS ; -- copy to dspcom JSR R1,TIMESU ; prod = timesu( lines, columns ) ADD R3,R3,R7 ADDI R3,R3,DISPTEXT LIL R4,DSPCOM+DSPEND STORES R3,R4 ; dspend = prod + (dispbase+disptext) ADDSI R2,-ARSIZE LOADS R1,R2 JUMPS R1 ; return SUBTITLE "PUTAT" ; ---------------------------------- INT PUTAT ; set location on display ; activation record ;RETAD = 0 ; return address ARSIZE = 4 ; total size PUTAT: ; expects: R3 -- x coordinate ; R4 -- y coordinate ; uses: R3-7 STORES R1,R2 ; -- push return address ADDSI R2,ARSIZE MOVE R7,R3 ; -- set aside x LIL R3,DSPCOM+DSPCOLS LOADS R3,R3 ; -- parameter, dspcols JSR R1,TIMESU ; prod = times( y, dspcols ), uses R4-6 ADD R3,R3,R7 ; offset = prod + x LIL R5,DSPCOM LOAD R6,R5,DSPTXT ADD R3,R3,R6 ; addr = dsptxt + offset LOAD R4,R5,DSPEND CMP R3,R4 BGEU DSPATER ; if (dspptr >= dspend) error STORES R3,R5 ; dspptr = addr ADDSI R2,-ARSIZE LOADS R1,R2 ; -- pop return address JUMPS R1 DSPATER: LEA R3,DSPATM1 JSR R1,PUTSTR ; putstr( dspatm1 ) LOAD R3,R2,-ARSIZE ; -- parameter retad LIS R4,8 ; -- parameter 8 JSR R1,PUTHEX ; puthex( retad, 8 ) LEA R3,DSPATM2 JSR R1,PUTSTR ; putstr( dspatm2 ) LIS R3,0 JUMPS R3 ; halt DSPATM1:ASCII "",LF,"PUTAT called from ",0 DSPATM2:ASCII " with off-screen coordinates ",0 ALIGN 2 SUBTITLE "PUTCHAR" ; ---------------------------------- INT PUTCHAR ; output char to display PUTCHAR:; expects: R3 -- char to output ; uses: R4-5 LIL R4,DSPCOM LOADS R5,R4;DSPPTR ; -- dspptr in R5 CMPI R3,' ' BLT PUTCHCC ; if ch<' ' go handle control chars LOAD R4,R4,DSPEND CMP R5,R4 BLTU PUTCHOK ; if (dspptr >= dspend) { -- scroll PUTCHSC: ; -- ch in R3, dspend in R4 ;R3SV = 0 R6SAVE = 4 R7SAVE = 8 ; -- local AR structure STORES R3,R2 STORE R6,R2,R6SAVE STORE R7,R2,R7SAVE LIL R5,DSPCOM LOAD R6,R5,DSPCOLS LOAD R5,R5,DSPTXT ; dest = dsptxt ADD R6,R5,R6 ; src = dest + dispcols PUTCHS1: ; do { -- copy middle lines up a line LOADS R3,R6 EXTB R3,R3,R6 ; t = *src LOADS R7,R5 STUFFB R7,R3,R5 STORES R7,R5 ; *dst = t ADDSI R6,1 ; src++ ADDSI R5,1 ; dst++ CMP R6,R4 BLTU PUTCHS1 ; } while (src < dspend) LIS R3,' ' ; t = ' ' PUTCHS2: ; do { -- blank out bottom line LOADS R7,R5 STUFFB R7,R3,R5 STORES R7,R5 ; *dst = t ADDSI R5,1 ; dst++ CMP R5,R4 BLTU PUTCHS2 ; } while (dst < dspend) LIL R5,DSPCOM ; -- dsppos back a line LOAD R6,R5,DSPCOLS LOADS R5,R5;DSPPTR SUB R5,R5,R6 ; dspptr = dspptr - dispcols LOAD R7,R2,R7SAVE ; restore R3,R6-7 LOAD R6,R2,R6SAVE LOADS R3,R2 PUTCHOK: ; } -- ch in R3, dspptr in R5 LOADS R4,R5 STUFFB R4,R3,R5 STORES R4,R5 ; display[ dspptr ] = char ADDSI R5,1 ; dspptr = dspptr + 1 LIL R4,DSPCOM STORES R5,R4;DSPPTR ; -- dspptr back to mem JUMPS R1 ; normal return ; PUTCH special case to handle control chars PUTCHCC: ; -- R3 = ch, R4 = &dspcom, R5 = dspptr ;RETAD = 0 R6SAVE = 4 ARSIZE = 8 ; -- local AR structure STORES R1,R2 ; -- save RETAD STORE R6,R2,R6SAVE ; -- save R6 LOAD R6,R4,DSPTXT CMPI R3,BS ; dispatch control chars BEQ PUTCHBS CMPI R3,HT BEQ PUTCHHT CMPI R3,LF BEQ PUTCHLF CMPI R3,VT BEQ PUTCHVT CMPI R3,CR BEQ PUTCHCR CMPI R3,FF BEQ PUTCHFF BR PUTCHQT PUTCHBS: ; if (ch = BS) { -- R5 = dspptr, R6 = dsptxt ADDSI R5,-1 ; dspptr = dspptr - 1 -- R4 = &dspcom CMP R5,R6 BGE PUTCHQT ; if (dspptr < disptext) -- stick at top left MOVE R5,R6 ; dspptr = dsptxt BR PUTCHQT ; } PUTCHHT: ; if (ch = HT) { -- R5 = dspptr, R6 = dsptxt SUB R3,R5,R6 ; offset = dspptr - dsptxt -- R4 = &dspcom LOAD R4,R4,DSPCOLS JSR R1,DIVIDEU ; curcol = offset % dspcols TRUNC R4,3 LIS R3,8 SUB R3,R3,R4 ; spaces = 8 - (curcol % 8) LIL R4,DSPCOM LOADS R5,R4;DSPPTR ; -- dspptr in R5 LIS R4,' ' PUTCHH2: ; do { -- blanks to tab stop LOADS R6,R5 STUFFB R6,R4,R5 STORES R6,R5 ; *dspptr = t ADDSI R5,+1 ; dspptr++ ADDSI R3,-1 ; spaces-- BZR PUTCHH2 ; } while (spaces != 0) LIL R4,DSPCOM ; -- &dspptr restored BR PUTCHQT ; } PUTCHLF: ; if (ch = LF) { -- R5 = dspptr, R6 = dsptxt SUB R3,R5,R6 ; offset = dspptr - dsptxt -- R4 = &dspcom LOAD R4,R4,DSPCOLS JSR R1,DIVIDEU ; curcol = offset % dispcols LIL R3,DSPCOM LOAD R3,R3,DSPCOLS SUB R3,R3,R4 ; increment = dspcols - curcol LIL R4,DSPCOM LOADS R5,R4;DSPPTR ; -- recover dspptr in R5, &dspptr in R4 ADD R5,R5,R3 ; dspptr = dspptr + increment BR PUTCHQT ; } PUTCHVT: ; if (ch = VT) { -- R5 = dspptr, R6 = dsptxt LOAD R3,R4,DSPCOLS ; -- R4 = &dspcom ADD R5,R5,R3 ; dspptr = dspptr + dispcols BR PUTCHQT ; } PUTCHCR: ; if (ch = CR) { -- R5 = dspptr, R6 = dsptxt SUB R3,R5,R6 ; offset = dspptr - dsptxt -- R4 = &dspcom LOAD R4,R4,DSPCOLS JSR R1,DIVIDEU MOVE R3,R4 ; curcol = offset % dispcols LIL R4,DSPCOM LOADS R5,R4;DSPPTR ; -- recover dspptr in R5, &dspptr in R4 SUB R5,R5,R3 ; dspptr = dspptr - curcol BR PUTCHQT ; } PUTCHFF: ; if (ch = FF) { -- R5 = dspptr, R6 = dsptxt MOVE R5,R6 ; dspptr = dsptxt -- R4 = &dspptr LIW R3,' ' ; -- a word holding 4 blanks LOAD R6,R4,DSPEND PUTCHFL: ; do { -- R5 = dspptr, R6 = dspend STORES R3,R5 ; *dspptr = ' ' ADDSI R5,4 ; dspptr++ CMP R5,R6 BLTU PUTCHFL ; } while (dspptr < dspend) LOAD R5,R4,DSPTXT ;BR PUTCHQT ; } PUTCHQT: ; -- dspptr in R5, &dspptr in R4 STORES R5,R4;DSPPTR ; -- put dspptr back in memory LOAD R6,R2,R6SAVE ; -- restore R6 LOADS PC,R2 ; return SUBTITLE "PUTSTR" ; ---------------------------------- INT PUTSTR ; output string to display PUTSTR: ; expects: R3 -- s, pointer to string to output ; returns: R3 -- pointer to null at end of s ; uses: R6 -- saved return address ; R7 -- string pointer ; R3-5 -- used for each call to putchar MOVE R6,R1 ; -- save return address MOVE R7,R3 ; -- move s PUTSTRL: ; loop { LOADS R4,R7 EXTB R3,R4,R7 ; ch = *s BZS PUTSTRQ ; if (ch == NUL) break JSR R1,PUTCHAR ; putchar( ch ) -- wipes R4-5 ADDSI R7,1 ; s = s + 1 BR PUTSTRL PUTSTRQ: ; } MOVE R3,R7 JUMPS R6 ; return SUBTITLE "PUTS" ; ---------------------------------- INT PUTS ; output string to display with appended LF ; activation record ;RETAD = 0 ; return address ARSIZE = 4 ; total size PUTS: ; expects: R3 -- s, pointer to string to output ; uses: R4-7 STORES R1,R2 ADDSI R2,ARSIZE JSR R1,PUTSTR ; use putstr( s ) LIS R3,LF JSR R1,PUTCHAR ; putchar( '\n' ) ADDSI R2,-ARSIZE LOADS PC,R2 ; return SUBTITLE "PUTHEX" ; ---------------------------------- INT PUTHEX ; output hex number ; activation record ;RETAD = 0 ; return address ARSIZE = 4 ; total size PUTHEX: ; expects: R3 -- n, number to output ; R4 -- w, field width ; uses: R7 -- copy of n ; R6 -- i, loop counter ; R3-5 -- used for each call to putchar STORES R1,R2 ; -- push return address ADDSI R2,ARSIZE MOVE R7,R3 ; -- move n MOVE R6,R4 ; i = w PUTHXS: ; loop { -- output extra leading zeros CMPI R6,8 BLEU PUTHXT ; if (i <= 8) break LIS R3,'0' JSR R1,PUTCHAR ; putchar( '0' ) -- wipes R4-5 ADDSI R6,-1 ; i = i - 1; BR PUTHXS ; } PUTHXT: MOVE R4,R6 ; t = i; -- field width now <= 8 LIS R6,8 ; i = 8; PUTHXU: ; loop { -- discard leading zeros CMP R4,R6 BGEU PUTHXL ; if (t >= i) break MOVE R3,R7 SRU R3,12 SRU R3,16 ; digit = n >>> 28 BZR PUTHXL ; if (digit != 0) break ADDSI R6,-1 ; i-- -- account for discarded digits SL R7,4 ; n << 4 -- shift next digit into place BR PUTHXU ; } PUTHXL: ; loop { -- output digits MOVE R3,R7 SRU R3,12 SRU R3,16 ; digit = n >>> 28 LIS R1,'0' ADD R3,R3,R1 ; ch = digit + '0' -- convert to ASCII LIS R1,'9' CMP R3,R1 BLE PUTHXN ; if (ch > '9') { ; -- convert digits above 9 to A-F range LIS R1,'A'-('9'+1) ADD R3,R3,R1 ; ch = ch + 'A' - ('9' + 1) PUTHXN: ; } JSR R1,PUTCHAR ; putchar( ch ) -- wipes R4-5 SL R7,4 ; n << 4 -- shift next digit into place ADDSI R6,-1 ; i = i - 1 BGT PUTHXL ; } while (i > 0) ADDSI R2,-ARSIZE LOADS PC,R2 ; return SUBTITLE "PUTDEC and PUTDECU" ; ---------------------------------- INT PUTDEC ; output signed decimal number PUTDEC: ; expects: R3 - n, the number to output ; R4 - w, field width ; uses: R5 - s, the sign ; R6 ?? ; R7 ?? LIS R5,0 ; s = 0 TESTR R3 BNR PDECRB ; if (n < 0) NEG R3,R3 ; n = -n LIS R5,'-' ; s = '-' BR PDECRB ; endif ; pdecrb( n, w, s ) ; ---------------------------------- INT PUTDECU ; output unsigned decimal number PUTDECU:; expects: R3 - n, the number to output ; R4 - w, field width ; uses: R5 - s, the sign ; R6 ?? ; R7 ?? LIS R5,0 ; s = 0 ; pdecrb( n, w, s ) ; ---------------------------------- ; PDECRB ; actual putdec recursive body ; activation record ;RETAD = 0 WR = 4 ; field width/remainder S = 8 ; sign ARSIZE = 12 PDECRB: ; expects: R3 - n, the number to output ; R4 - w, field width ; R5 - s, the sign, either 0 or '-' ; uses R6 - used by divideu() ; R7 ?? STORES R1,R2 ; -- save return address ADDSI R4,-1 ; w = w - 1 STORE R4,R2,WR ; -- save w STORE R5,R2,S ; -- save s ADDI R2,R2,ARSIZE LIS R4,10 ; -- parameter JSR R1,DIVIDEU ; (q, r) = divideu( n, 10 ) -- wipe out R5-6 TESTR R3 BZS PDECBL ; if (q != 0) -- else clause is after return LOAD R5,R2,S-ARSIZE ; -- restore s LOAD R6,R2,WR-ARSIZE ; -- partially restore w STORE R4,R2,WR-ARSIZE ; -- save r MOVE R4,R6 ; -- finish restoring w JSR R1,PDECRB ; pdecrb( q, w, s ) PDECQT: ; } endif LOAD R3,R2,WR-ARSIZE ; -- restore r LIS R4,'0' ADD R3,R3,R4 ; -- parameter r + '0' JSR R1,PUTCHAR ; putchar( r + '0' ) -- wipes R4,R5 ADDI R2,R2,-ARSIZE LOADS PC,R2 ; return ; by rights, the following else clause should be inside ; the above, but it's a bit more efficient to put it here PDECBL: ; else { LOADCC R3,R2,S-ARSIZE ; -- restore and test s LOAD R6,R2,WR-ARSIZE ; -- partially restore w STORE R4,R2,WR-ARSIZE ; -- save r BZS PDECNS ; if (s != 0) { ADDSI R6,-1 ; w = w - 1 PDECNS: ; } PDECLP: ; loop { ADDSI R6,-1 ; w = w - 1; BLT PDECPS ; if (w < 0) break LIS R3,' ' ; -- parameter ' ' JSR R1,PUTCHAR ; putchar( ' ' ) -- wipes R4,R5 BR PDECLP ; } PDECPS: LOADCC R3,R2,S-ARSIZE BZS PDECQT ; if (s != 0) { JSR R1,PUTCHAR ; putchar( s ) -- wipes R4,R5 BR PDECQT ; } ; } SUBTITLE "GETCHAR" ; ---------------------------------- INT GETCHAR ; get char from keyboard GETCHAR:; returns: R3 -- ch, the character from keyboard ; uses: R4 -- scratch used for addressing LIW R4,KBDBASE GETCLP: ; loop { -- poll keyboard LOADCC R3,R4,KBDSTAT ; -- test status BZS GETCLP ; } until ( kbdstat != 0 ) LOAD R3,R4,KBDDATA ; -- get ch JUMPS R1 ; return ch SUBTITLE "GETS" ; ---------------------------------- INT GETS ; get string from keyboard GETS: ; expects: R3 -- s, pointer to string ; uses: R3 -- ch, the most recent character ; R4 -- scratch ; R5 -- scratch ; R6 -- s, working copy ; R7 -- saved return address ; really buggy code, no length limit (bug inherited from C). ; but also, multiple BS cann overwrites before start of buffer! MOVE R7,R1 ; -- save return addr MOVE R6,R3 ; -- move s GETSLP: ; loop { JSR R1,GETCHAR ; ch = getchar() -- wipes out R4, R5 CMPI R3,' ' BLT GETSNP ; if (ch >= ' ') { LOADS R5,R6 STUFFB R5,R3,R6 ; -- stuff character into string STORES R5,R6 ; *s = ch ADDSI R6,1 ; s++ -- advance string pointer ; -- echo character JSR R1,PUTCHAR ; putchar( ch ) -- wipes out R4, R5 BR GETSLP ; -- continue GETSNP: ; } else { -- nonprinting CMPI R3,LF BEQ GETSQT ; if (ch == LF) break CMPI R3,CR BEQ GETSQT ; if (ch == CR) break CMPI R3,BS BNE GETSLP ; if (ch == BS) { LIS R3,BS JSR R1,PUTCHAR ; putchar( BS ) LIS R3,' ' JSR R1,PUTCHAR ; putchar( ' ' ) LIS R3,BS JSR R1,PUTCHAR ; putchar( BS ) -- R4, R5 wiped out ADDSI R6,-1 ; s-- BR GETSLP ; } -- continue ; } -- continue GETSQT: ; } LOADS R5,R6 STUFFB R5,R0,R6 ; store null at end-string STORES R5,R6 JUMPS R7 ; return SUBTITLE "TIMES" ; ---------------------------------- INT TIMES ; signed multiply TIMES: ; expects: R3 -- cand, signed 32-bit multiplicand ; R4 -- ier, signed 32-bit multiplier ; returns: R3 -- prod, the low 32 bits of the product ; uses: R4 -- ier, incrementally destroyed ; R5 -- cand ; R6 -- i, the loop counter MOVE R5,R3 ; -- move cand CLR R3 ; prod = 0 ; -- start of special multiply step SL R4,1 ; ier = ier * 2 BCR TMSSKP ; if (ier was negative) { SUB R3,R0,R5 ; prod = prod - icand TMSSKP: ; } -- end of multiply step LIS R6,31 ; i = 31 TMSLLP: ; do { ; -- start of normal multiply step SL R3,1 ; prod = prod * 2 SL R4,1 ; ier = ier * 2 BCR TMSLCN ; if (high bit was one) { ADD R3,R3,R5 ; prod = prod + icand TMSLCN: ; } -- end of multiply step ADDSI R6,-1 ; i = i - 1 BGT TMSLLP ; } until (i = 0) JUMPS R1 ; return prod SUBTITLE "TIMESU" ; ---------------------------------- INT TIMESU ; unsigned multiply TIMESU: ; expects: R3 -- cand, unsigned 32-bit multiplicand ; R4 -- ier, unsigned 32-bit multiplier ; returns: R3 -- prod, the low 32 bits of the product ; uses: R4 -- ier, incrementally destroyed ; R5 -- cand ; R6 -- i, the loop counter MOVE R5,R3 ; move cand CLR R3 ; prod = 0 LIS R6,16 ; i = 16 TMULLP: ; do { ; -- start of first multiply step SL R3,1 ; prod = prod * 2 SL R4,1 ; ier = ier * 2 BCR TMULC1 ; if (high bit was one) { ADD R3,R3,R5 ; prod = prod + icand TMULC1: ; -- end of multiply step ; -- start of second multiply step SL R3,1 ; prod = prod * 2 SL R4,1 ; ier = ier * 2 BCR TMULC2 ; if (high bit was one) { ADD R3,R3,R5 ; prod = prod + icand TMULC2: ; } -- end of multiply step ADDSI R6,-1 ; i = i - 1 BGT TMULLP ; } until (i = 0) JUMPS R1 ; return prod SUBTITLE "DIVIDEU" ; ---------------------------------- INT DIVIDEU ; unsigned divide DIVIDEU:; expects: R3 -- idend, unsigned 32-bit dividend ; R4 -- isor, unsigned 32-bit divisor ; returns: R3 -- quo, unsigned 32-bit quotient ; R4 -- rem, unsigned 32-bit remaineder ; uses R5 -- isor ; R6 -- i, loop counter MOVE R5,R4 ; -- move isor CLR R4 ; rem = 0 LIS R6,32 ; i = 32 DIVULP: ; do { ; -- start of divide step SL R3,1 ROL R4 ; rem/quo = rem/quo << 1 (64-bit shift) CMP R4,R5 BLTU DIVUC ; if ( rem >= isor ) { SUB R4,R4,R5 ; rem = rem - isor ADDSI R3,1 ; quo = quo + 1 -- low bit was zero DIVUC: ; } -- end of divide step ADDSI R6,-1 ; i = i - 1 BGT DIVULP ; } while (i > 0) JUMPS R1 ; return rem/quo SUBTITLE "RANDOM NUMBERS" ; ---------------------------------- INT RAND COMMON RANDNEXT,4 LC = . . = RANDNEXT W 17 . = LC RETAD = 0; ARSIZE = 4; RAND: ; returns R3, random 0..32767 STORES R1,R2 ADDSI R2,ARSIZE LIL R3,RANDNEXT LOADS R3,R3 ; -- parameter next LIW R4,1103515245 ; -- parameter constant JSR R1,TIMESU ADDI R3,R3,12345 LIL R4,RANDNEXT STORES R3,R4 ; next = next * 1103515245 + 12345 SRU R3,16 TRUNC R3,15 ADDSI R2,-ARSIZE LOADS PC,R2 ; return (next/65536) % 32768 SUBTITLE "HEAP MANAGEMENT" ; ---------------------------------- ; boundary tag structure ; ___________________________________________________________ _ _ ; |___________________________________________________________|_|_| ; | a |x|u| ; ; a = below, address of next lower block in heap, or null if no such block ; x = ignored; ; u = inuse, block in use if 1, free if 0 HEAPTSIZ= 4 ; tag is 4 bytes ; initial heap: EXT UNAVAIL ; linker sets to addr beyond RAM HEAPHEAD= UNAVAIL-4 ; address of head tag on heap LC = . . = HEAPHEAD W NULL+1 ; the only tag on the heap, block in use . = LC SUBTITLE "MALLOC" ; ---------------------------------- INT MALLOC ; allocate memory MALLOC: ; expects: R3 -- size, unsigned size of memory block ; returns: R3 -- pointer to size bytes, or NULL if none ; uses: R4 -- high, addr of tag above current candidate block ; R5 -- low, addr of next tag below high ; R6 -- lower, addr of next tag below high ; R7 -- new, addr of candidate for new block ADDSI R3,HEAPTSIZ+3 ; -- allow space for a tag in the block LIS R4,#FFFFFFFC ; -- mask to round up to nearest word AND R3,R4 ; size = (size + heaptsiz + 3) & mask LIL R4,HEAPHEAD ; high = heaphead -- topmost tag on heap MLCLOOP: ; for (;;) { -- hunt tag list for big free block ; -- assert high is word aligned pointer LOADS R5,R4 ; low = *high -- assert high is aligned CMPI R5,4 ; -- clever check for NULL without aligning BGEU MLCONT ; if (high->below == NULL) { -- end of heap SUB R7,R4,R3 ; new = high - size -- new bottommost tag MLCGROW:; uses: R4 -- high ; -- (come here to grow the heap and return) ; R5 -- *high ; R7 -- new ADDI R6,R2,1024 ; -- 1024 byte margin twixt stack, heap CMP R7,R6 BGEU MLCELSE ; if (new < SP+1024) { LIS R3,NULL ; -- put return value in place JUMPS R1 ; return NULL MLCELSE: ; } else { TRUNC R5,1 ; -- preserve old tag bit from high tag ADD R5,R5,R7 STORES R5,R4 ; high->below = new LIS R4,1 ; new->below = NULL STORES R4,R7 ; new->inuse = 1 ADDI R3,R7,HEAPTSIZ ; -- put return value in place JUMPS R1 ; return new + sizeof( heap tag ) ; } ; -- control never falls through here! MLCONT: ; uses: R4 -- high ; } -- high->below != NULL ; R5 -- *high ; -- R4 word aligned, r5 holds high->inuse CMP R4,R5 ; -- check for heap corruption BLE MLCBUG ; if (high <= low) heap is corrupt BITTST R5,1 BBS MLCBUG ; if (high->unused == 1) heap is corrupt LOADS R6,R5 ; lower = *low (Hawk ignores low bits 0-1) CMP R5,R6 ; -- check for heap corruption BLE MLCBUG ; if (low <= lower) heap is corrupt BITTST R6,1 BBS MLCBUG ; if (low->unused == 1) heap is corrupt BITTST R6,0 BBR MLCNDIF ; if (low->inuse == 1) { -- low is in use LIS R4,#FFFFFFFC ; -- align AND R4,R5 ; high = low -- walk on down tag list BR MLCLOOP ; continue; MLCNDIF: ; } ; -- *low is free block, *lower is below it SUB R7,R4,R3 ; new = high - size; -- might not fit MLCINLP:; R3 = size ; R4 = high (aligned) ; R5 = low (non aligned because it holds xxx->inuse) low->inuse = 0 ; R7 = new (aligned) LIS R6,#FFFFFFFC AND R6,R5 ; -- word aligned low CMP R7,R6 ; BGEU MLCINLQ ; while (new < low) { -- try merging free blks LOADS R6,R5 ; lower = low->below; CMPI R6,4 ; -- clever check for null without aligning BLTU MLCGROW ; if (lower == NULL) break -- heap ended ; -- above code cheats to grow heap LOADS R5,R6 ; -- lower->inuse BITTST R5,0 BBR MLCMRGE ; if (lower->inuse) { -- can't merge LIS R4,#FFFFFFFC AND R4,R6 ; high = lower -- walk on down list BR MLCLOOP ; continue outer loop MLCMRGE: ; } ; R3 = size ; R4 = high (aligned) ; R5 = wiped ; R6 = lower (aligned because low->inuse == 0) ; R7 = new (aligned) ; -- low->inuse == lower->inuse == 0, merge LOADS R5,R4 TRUNC R5,1 ; -- preserve old high->inuse ADD R5,R5,R6 ; -- no tag interference cause it's free STORES R5,R4 ; high->below = lower MOVE R5,R6 ; low = lower BR MLCINLP MLCINLQ: ; } ; R3 = size ; R4 = high (aligned) ; R5 = wiped ; R6 = low (aligned) ; R7 = new (aligned) ; -- assert new >= low, low->inuse = 0 ADDSI R6,HEAPTSIZ ; limit = low + sizeof( heap tag ) CMP R7,R6 BLTU MLCNOSP ; if (new >= limit) { -- split the block LOADS R5,R4 TRUNC R5,1 ; -- preserve old high->inuse ADD R5,R5,R7 STORES R5,R4 ; high->below = new ADDSI R6,1-HEAPTSIZ ; new->inuse = 1 (and undo temp limit) STORES R6,R7 ; new->below = low (may make 0-word blk) ADDI R3,R7,HEAPTSIZ ; -- put return value in place JUMPS R1 ; return new + sizeof( heap tag ) MLCNOSP: ; } ; -- no split possible, return low ADDSI R6,-HEAPTSIZ ; -- undo creation of temp variable limit LOADS R5,R6 ; -- assert low->inuse = 0 ADDSI R5,1 STORES R5,R6 ; low->inuse = 1 ADDI R3,R6,HEAPTSIZ ; new = low JUMPS R1 ; return new + sizeof( heap tag ) ; } /* end of loop walking down list */ ; -- note, loop iterates from embedded continues ; malloc needs an activation record only for error messages RETAD = 0 ; return address ARSIZE = 4 MLCBUG: ; -- report heap tags out of order STORES R1,R2 ; -- report heap tags out of order ADDSI R2,ARSIZE LEA R3,MLCM1 JSR R1,PUTSTR; putstr( m1 ) LOAD R3,R2,RETAD-ARSIZE LIS R4,8 JSR R1,PUTHEX ; puthex( retad, 8 ) LEA R3,MLCM2 JSR R1,PUTSTR ; putstr( m2 ) LIS R1,0 JUMPS R1 ; halt! MLCM1: ASCII "",LF,"MALLOC() called from ",0 MLCM2: ASCII "; when heap corrupted ",0 ALIGN 2 SUBTITLE "FREE" ; ---------------------------------- INT FREE ; deallocate memory FREE: ; expects: R3 -- old points to block to data within deallocated block ; returns: nothing ; R4 -- temp LIL R4,HEAPHEAD CMP R3,R4 BGEU FREEHIGH ; if (old >= freehigh) go gripe ADDSI R3,-HEAPTSIZ ; old = old - sizeof( heap tag ) CMP R3,R2 BLTU FREELOW ; if (old < SP) go gripe LOADS R4,R3 BITTST R4,0 BBR FREEFREE ; if (old->inuse == 0) go gripe ADDSI R4,-1 ; old->inuse == 0 -- block marked as free STORES R4,R3 JUMPS R1 ; return ; free needs an activation record only for error messages RETAD = 0 ; return address PTR = 4 ; user's pointer MSG = 8 ; pointer to final part of message ARSIZE = 12 FREELOW: LOAD R4,FREELM ADDSI R3,HEAPTSIZ ; -- recover user's block address BR FREEBUG FREEHIGH: LOAD R4,FREEHM BR FREEBUG FREEFREE: LOAD R4,FREEFM ADDSI R3,HEAPTSIZ ; -- recover user's block address FREEBUG: STORES R1,R2 ; -- save registers STORE R3,R2,PTR STORE R4,R2,MSG ADDI R2,R2,ARSIZE LEA R3,FREEM1 JSR R1,PUTSTR ; putstr( freem1 ) LOAD R3,R2,PTR-ARSIZE; -- parameter ptr LIS R4,8 ; -- parameter 8 JSR R1,PUTHEX ; puthex( ptr, 8 ) LEA R3,FREEM2 JSR R1,PUTSTR ; putstr( freem2 ) LOAD R3,R2,RETAD-ARSIZE LIS R4,8 ; -- parameter 8 JSR R1,PUTHEX ; puthex( retad, 8 ) LOAD R3,R2,MSG-ARSIZE; -- parameter msg JSR R1,PUTSTR ; putstr( msg ) ADDI R2,R2,-ARSIZE ; -- put AR back to where it was at call LIS R3,0 JUMPS R3 ; halt! FREEM1: ASCII "",LF,"FREE( ",0 FREEM2: ASCII " ) called from ",0 FREELM: ASCII "; block is below heap ",0 FREEHM: ASCII "; block is above heap ",0 FREEFM: ASCII "; block is already free ",0 ALIGN 2 SUBTITLE "STRING LIBRARY -- STRLEN" ; ---------------------------------- INT STRLEN ; receiving sequence assumptions for STRLEN (SLN) STRLEN: ; expects: R3 -- src, the address of the string ; uses: R4 -- ch, the character *src ; R5 -- start, the starting address of src ; returns: R3 -- len, length of the string src MOVE R5,R3 ; start = src LOADSCC R4,R3 ; ch = *src -- the whole word BTRUNC R3,2 ; select (src & 3) { BR SLNB0 ; -- branch table 0 (byte number) BR SLNB1 ; -- 1 BR SLNB2 ; -- 2 BR SLNB3 ; -- 3 SLNB1: ; case 1 EXTB R0,R4,R3 ; -- finish ch = *src BZS SLNQT ; if (ch == NUL) break ADDSI R3,1 ; src = src + 1 SLNB2: ; case 2 EXTB R0,R4,R3 ; -- finish ch = *src BZS SLNQT ; if (ch == NUL) break ADDSI R3,1 ; src = src + 1 SLNB3: ; case 3 EXTB R0,R4,R3 ; -- finish ch = *src BZS SLNQT ; if (ch == NUL) break ADDSI R3,1 ; src = src + 1 LOADSCC R4,R3 ; ch = *src -- the whole word SLNB0: ; case 0 -- src divisible by 4 ; -- note ch=*src, LOADSCC set C bit BCS SLNLX ; if no byte in this word is zero { SLNLP: ; do { ADDSI R3,4 ; src = src + 4 LOADSCC R4,R3 ; ch = *src -- the whole word BCR SLNLP ; while no byte in this word is zero SLNLX: ; } -- some byte in R4 is zero EXTB R0,R4,R3 ; -- finish ch = *src BZS SLNQT ; if (ch == NUL) break ADDSI R3,1 ; src = src + 1 EXTB R0,R4,R3 ; -- finish ch = *src BZS SLNQT ; if (ch == NUL) break ADDSI R3,1 ; src = src + 1 EXTB R0,R4,R3 ; -- finish ch = *src BZS SLNQT ; if (ch == NUL) break ADDSI R3,1 ; src = src + 1 SLNQT: ; } -- assert *src == NUL SUB R3,R3,R5 ; len = src - start JUMPS R1 ; return len SUBTITLE "STRCPY" ; ---------------------------------- INT STRCPY ; activation record format for STRCPY string copy routine RETVAL = 0 ; return value ; receiving sequence and assumptions STRCPY: ; expects: R3 -- dst, the destination string address ; R4 -- src, the source address ; uses: R5 -- the word being copied ; R6 -- the character being copied ; R7 -- temp for stuffing bytes ; returns: R3 -- original dst, unchanged STORES R3,R2 ; retval = dst LOADSCC R5,R4 ; ch = *src -- the whole word (C set if any NUL) BTRUNC R4,2 ; select (src & 3) { BR STCS0 ; -- branch table 0 (byte number) BR STCS1 ; -- 1 BR STCS2 ; -- 2 BR STCS3 ; -- 3 STCS1: ; case 1 EXTB R6,R5,R4 ; -- finish ch = *src for byte 1 BZS STCQT ; if (ch == NUL) go plant trailing nul ADDSI R4,1 ; src++ LOADS R7,R3 STUFFB R7,R6,R3 STORES R7,R3 ; *dst = ch; ADDSI R3,1 ; dst ++ STCS2: ; case 2 EXTB R6,R5,R4 ; -- finish ch = *src for byte 2 BZS STCQT ; if (ch == NUL) go plant trailing nul ADDSI R4,1 ; src++ LOADS R7,R3 STUFFB R7,R6,R3 STORES R7,R3 ; *dst = ch; ADDSI R3,1 ; dst ++ STCS3: ; case 3 EXTB R6,R5,R4 ; -- finish ch = *src for byte 3 BZS STCQT ; if (ch == NUL) go plant trailing nul ADDSI R4,1 ; src++ LOADS R7,R3 STUFFB R7,R6,R3 STORES R7,R3 ; *dst = ch; ADDSI R3,1 ; dst ++ LOADSCC R5,R4 ; ch = *src -- the next word (C set if NUL) STCS0: ; case 4 ; } BCS STCSUFX ; -- if any byte is NUL, go copy final bytes ; -- assert src & 00 = 00, word aligned ; here: R3 -- dst ; R4 -- src ; R5 -- *src ; R6 -- byte or halfword extracted ; R7 -- free BTRUNC R3,2 ; select (dst & 3) { BR STCD0 ; -- branch table 0 (byte number) BR STCD1 ; -- 1 BR STCD2 ; -- 2 BR STCD3 ; -- 3 STCD0: ; case 0 -- can do entire loop copying words ; loop { STORES R5,R3 ; *dst = ch (4 of them) ADDSI R4,4 ; src = src + 4 ADDSI R3,4 ; dst = dst + 4 LOADSCC R5,R4 ; ch (4 of them) = *src BCR STCD0 ; } until some byte is null BR STCSUFX ; go finish copying STCD2: ; case 2 -- can copy halfwords ; loop { EXTH R6,R5,R4 ; -- finish getting ch (2 bytes) LOADS R7,R3 STUFFH R7,R6,R3 STORES R7,R3 ; *dst = ch (2 of them) ADDSI R4,2 ; src = src + 4 ADDSI R3,2 ; dst = dst + 4 LOADSCC R5,R4 ; ch (4 of them) = *src BCR STCD2 ; } until some byte is null STCSUFX: STCD1: ; case 1 -- off by one byte STCD3: ; case 3 -- off by one byte EXTB R6,R5,R4 ; -- finish getting ch (1 bytes) STCLP: ; loop { LOADS R7,R3 STUFFB R7,R6,R3 STORES R7,R3 ; *dst = ch (just 1) ADDSI R4,1 ; src = src + 4 ADDSI R3,1 ; dst = dst + 4 LOADS R5,R4 ; ch (4 of them) = *src EXTB R6,R5,R4 ; -- finish getting ch (1 byte) BZR STCLP ; } until have null in hand STCQT: ; } ; -- stuff trailing NUL LOADS R7,R3 STUFFB R7,R0,R3 STORES R7,R3 ; *dst = NUL LOADS R3,R2 JUMPS R1 ; return retval SUBTITLE "STRNCPY" ; ---------------------------------- INT STRNCPY ; activation record format for STRNCPY string copy routine RETVAL = 0 ; return value (NOT return address!) R8SAVE = 4 ; save R8 ; receiving sequence and assumptions STRNCPY:; expects: R3 -- dst, the destination string address ; R4 -- src, the source address ; R5 -- limit on length copied ; uses: R6 -- the character being copied ; R7 -- temp for stuffing bytes ; R8 -- the word being copied ; returns: R3 -- original dst, unchanged TESTR R5 BLE STNCQQ ; if (len <= 0) go return ; -- assert len > 0, at least one char to copy STORES R3,R2 ; retval = dst STORE R8,R2,R8SAVE ; save R8 LOADSCC R8,R4 ; ch = *src -- the whole word (C set if any NUL) BTRUNC R4,2 ; select (src & 3) { BR STNCS0 ; -- branch table 0 (byte number) BR STNCS1 ; -- 1 BR STNCS2 ; -- 2 BR STNCS3 ; -- 3 STNCS1: ; case 1 EXTB R6,R8,R4 ; -- finish ch = *src for byte 1 BZS STNCQT ; if (ch == NUL) go plant trailing nul ADDSI R4,1 ; src++ LOADS R7,R3 STUFFB R7,R6,R3 STORES R7,R3 ; *dst = ch ADDSI R5,-1 ; len = len - 1 BLE STNCQR ; if (len <= 0) go return and restore regs ADDSI R3,1 STNCS2: ; case 2 EXTB R6,R8,R4 ; -- finish ch = *src for byte 2 BZS STNCQT ; if (ch == NUL) go plant trailing nul ADDSI R4,1 ; src++ LOADS R7,R3 STUFFB R7,R6,R3 STORES R7,R3 ; *dst = ch ADDSI R5,-1 ; len = len - 1 BLE STNCQR ; if (len <= 0) go return and restore regs ADDSI R3,1 STNCS3: ; case 3 EXTB R6,R8,R4 ; -- finish ch = *src for byte 3 BZS STNCQT ; if (ch == NUL) go plant trailing nul ADDSI R4,1 ; src++ LOADS R7,R3 STUFFB R7,R6,R3 STORES R7,R3 ; *dst = ch ADDSI R5,-1 ; len = len - 1 BLE STNCQR ; if (len <= 0) go return and restore regs ADDSI R3,1 LOADSCC R8,R4 ; ch = *src -- the next word (C set if NUL) STNCS0: ; case 4 ; } BCS STNCSUFX ; -- if byte is NUL, go copy final bytes ; -- assert src & 00 = 00, word aligned ; here: R3 -- dst ; R4 -- src ; R6 -- byte or halfword extracted ; R7 -- free ; R8 -- *src BTRUNC R3,2 ; select (dst & 3) { BR STNCD0 ; -- branch table 0 (byte number) BR STNCD1 ; -- 1 BR STNCD2 ; -- 2 BR STNCD3 ; -- 3 STNCD0: ; case 0 -- can do entire loop copying words ; loop { ADDSI R5,-4 ; len = len - 4 BLE STNCQ0 ; if (len <= 0) go finish byte by byte STORES R8,R3 ; *dst = ch (4 of them) ADDSI R4,4 ; src = src + 4 ADDSI R3,4 ; dst = dst + 4 LOADSCC R8,R4 ; ch (4 of them) = *src BCR STNCD0 ; } until some byte is null BR STNCSUFX ; go finish copying STNCD2: ; case 2 -- can copy halfwords ; loop { ADDSI R5,-2 ; len = len - 2 BLE STNCQ2 ; if (len <= 0) go finish byte by byte EXTH R6,R8,R4 ; -- finish getting ch (2 bytes) LOADS R7,R3 STUFFH R7,R6,R3 STORES R7,R3 ; *dst = ch (2 of them) ADDSI R4,2 ; src = src + 4 ADDSI R3,2 ; dst = dst + 4 LOADSCC R8,R4 ; ch (4 of them) = *src BCR STNCD2 ; } until some byte is null BR STNCSUFX ; go finish copying STNCQ0: ADDSI R5,2 ; len = len + 2 -- undo premature decrement STNCQ2: ADDSI R5,2 ; len = len + 2 STNCSUFX: STNCD1: ; case 1 -- off by one byte STNCD3: ; case 3 -- off by one byte EXTB R6,R8,R4 ; -- finish getting ch (1 bytes) STNCLP: ; loop { LOADS R7,R3 STUFFB R7,R6,R3 STORES R7,R3 ; *dst = ch (just 1) ADDSI R5,-1 ; len = len - 1 BLE STNCQR ; if (len <= 0) go return and restore regs ADDSI R4,1 ; src = src + 4 ADDSI R3,1 ; dst = dst + 4 LOADS R8,R4 ; ch (4 of them) = *src EXTB R6,R8,R4 ; -- finish getting ch (1 byte) BZR STNCLP ; } until have null in hand STNCQT: ; } ; -- stuff trailing NUL LOADS R7,R3 STUFFB R7,R0,R3 STORES R7,R3 ; *dst = NUL STNCQR: ; -- come here to restore registers and return LOAD R8,R2,R8SAVE ; recover R8 LOADS R3,R2 STNCQQ: ; -- come here when can't even plant NUL JUMPS R1 ; return retval SUBTITLE "STRCAT" ; ---------------------------------- INT STRCAT ; activation record format for STRCAT string concatenation routine RETAD = 0 DST = 4 SRC = 8 ARSIZE = 12 ; receiving sequence and assumptions STRCAT: ; expects: R3 -- dst, the destination string address ; R4 -- src, the source address ; returns: R3 -- dst, unchanged STORES R1,R2 STORE R3,R2,DST STORE R4,R2,SRC ADDI R2,R2,ARSIZE JSR R1,STRLEN ; tmp = strlen( dst ); LOAD R4,R2,DST-ARSIZE ADD R3,R3,R4 LOAD R4,R2,SRC-ARSIZE JSR R1,STRCPY ; strcpy( dst + tmp, src ); ADDI R2,R2,-ARSIZE LOAD R3,R2,DST LOADS PC,R2 ; return dst SUBTITLE "STRNCAT" ; ---------------------------------- INT STRNCAT ; activation record format for STRCAT string concatenation routine RETAD = 0 DST = 4 SRC = 8 N = 12 ARSIZE = 16 ; receiving sequence and assumptions STRNCAT:; expects: R3 -- dst, the destination string address ; R4 -- src, the source address ; R5 -- n, limit on bytes to add to string ; returns: R3 -- dst, unchanged STORES R1,R2 STORE R3,R2,DST STORE R4,R2,SRC STORE R5,R2,N ADDI R2,R2,ARSIZE JSR R1,STRLEN ; tmp = strlen( dst ); LOAD R4,R2,DST-ARSIZE ADD R3,R3,R4 LOAD R4,R2,SRC-ARSIZE LOAD R5,R2,N-ARSIZE JSR R1,STRNCPY ; strcpy( dst + tmp, src, n ); ADDI R2,R2,-ARSIZE LOAD R3,R2,DST LOADS PC,R2 ; return dst SUBTITLE "STRCMP" ; ---------------------------------- INT STRCMP ; receiving sequence and assumptions STRCMP: ; expects: R3 -- s1, a string address ; R4 -- s2, a string address ; uses: R5 -- the word being inspected and the character ch2 from s2 ; R6 -- the character ch1 from s1 ; returns: R3 -- result, 0 if s1=s2, <0 if s10 if s1>s2 STRCLP: ; loop { LOADS R5,R3 EXTB R6,R5,R3 ; ch1 = *s1 LOADS R5,R4 EXTB R5,R5,R4 ; ch2 = *s2 SUB R5,R6,R5 ; res = ch1 - ch2 BZR STRCQT ; if (res != 0) break TESTR R6 BZS STRCQT ; if (ch1 == 0) break (implies also ch2 == 0) ADDSI R3,1 ; s1++ ADDSI R4,1 ; s2++ BR STRCLP STRCQT: ; } MOVE R3,R5 JUMPS R1 ; return res SUBTITLE "FLOATING POINT SUPPORT -- FTOI" ; ---------------------------------- INT FTOI EXP0 = #7F ; IEEE representation of the exponent 0 FTOI: ; float to int conversion ; expects: R3 = f, floating point number ; returns: R3 = i, the truncated integer equivalent ; R3 = #80000000 if overflow ; uses: R3 = mant, mantissa of f ; R4 = exp, exponent of f becomes shift count ; R5 = sign, of f MOVE R4,R3 SL R4,1 ; -- clip sign bit from exponent SRU R4,12 SRU R4,12 ; exp = (f >> 23) & #FF CMPI R4,EXP0 BGE FTOIELS1 ; if (exp < 0) { -- exp < 0 LIS R3,0 ; i = 0 -- value < 1, truncates to 0 BR FTOIEND FTOIELS1: ; } else { -- exp >= 0 MOVE R5,R3 ; sign = i; ADDI R4,R4,-(EXP0+30); -- exp - 30 NEG R4,R4 ; shift = 30 - exp BGE FTOIELS2 ; if (shift < 0) { SUB R3,R3,R3 ; -- zero with C = 1 ADJUST R3,CMSB ; i = #80000000 -- indicates overflow BR FTOIEND FTOIELS2: ; } else { SL R3,9 ; -- clip off the entire exponent SRU R3,1 ; mant = (i << 8) & #7FFFFFFF CMP R1,R1 ; -- set the carry bit for use as hidden bit ADJUST R3,CMSB ; -- put the hidden bit in place SRU R3,1 ; mant = (mant + #80000000) >> 1 ; -- mant now in form 01.xxxxxxxx... TESTR R5 BGE FTOIEND2 ; if (sign < 0) { NEG R3,R3 ; mant = -mant; FTOIEND2: ; } -- mant now in 2's complement ; -- shift now from 0 to 30 BITTST R4,4 BBR FTOIEND3 SR R3,16 ; if (shift & #10) mant = mant >> 16 FTOIEND3: BITTST R4,3 BBR FTOIEND4 SR R3,8 ; if (shift & #08) mant = mant >> 8 FTOIEND4: BITTST R4,2 BBR FTOIEND5 SR R3,4 ; if (shift & #04) mant = mant >> 4 FTOIEND5: BITTST R4,1 BBR FTOIEND6 SR R3,2 ; if (shift & #02) mant = mant >> 2 FTOIEND6: BITTST R4,0 BBR FTOIEND7 SR R3,1 ; if (shift & #01) mant = mant >> 1 FTOIEND7: ; i = mant ; } FTOIEND: ; } JUMPS R1 ; return i END