# This is a shell archive. Remove anything before this line, # then unpack it by saving it in a file and typing "sh file". # # Wrapped by Douglas W. Jones # # This archive contains README. See that for details. # LANG=""; export LANG PATH=/bin:/usr/bin:/usr/sbin:/usr/ccs/bin:$PATH; export PATH EXIT_STATUS=0 echo x - README cat >README <<'@EOF' Distribution bundle for the Hawk emulator Author: Douglas W. Jones, University of Iowa, Iowa City, IA 52242, USA Copyright: All of the files included here may be redistributed freely so long as the original author is given complete credit for his work. README -- this file hello.a -- a hello-world demo program using the monitor link -- a UNIX shell script to link Hawk object files monitor.h -- the interface definition for monitor.a monitor.a -- a minimal run-time system for the Hawk emulator Assuming you have the SMAL assembler, the hawk.macs include file and the Hawk interpreter installed on your system, the following sequence of shell commands will build the demo: > smal monitor.a -- makes monitor.o, monitor.l > smal hello.a -- makes hello.o, hello.l > link hello.o -- makes link.o, link.l; implicitly uses monitor.o > hawk link.o The .l files created by the steps above hold listings; if there are no errors, they may be disregarded. Once the hawk emulator is run with the final object file, it will wait for the run command (r) and then it will run the program. @EOF chmod 600 README echo x - hello.a sed 's/^@//' >hello.a <<'@EOF' TITLE Hello World Program, by D. Jones, Aug 15 2008 USE "hawk.macs" ; this program demonstrates use of the ; support library included in the Hawk monitor. ; this is a main program, so it has to set up the stack S START USE "hawk.macs" USE "monitor.h" EXT UNUSED ; the program starts here! START: LIL R2,UNUSED ; setup stack LIL R1,DSPINI JSRS R1,R1 ; call dspini() LIL R3,HELLO LIL R1,DSPST JSRS R1,R1 ; call dspst(hello) LIL R1,EXIT JSRS R1,R1 ; call exit() HELLO: ASCII "Hello world!",0 END @EOF echo x - link sed 's/^@//' >link <<'@EOF' #!/bin/sh # sh script -- linker for the Hawk, using the smal assembler # link [parameters] a b c d e ... # link -help -- gives more documentation # where is the SMAL assembler executable? # Where is monitor.o stored? smal="smal" usepath="/space/jones/hawk/" if [ $# -lt 1 ] then echo "link requires parameters" echo "link -help is available" exit 1 fi if [ -help = $1 ] then echo "link [parameters] a b c d e ... links SMAL object and library files named a, b, c, d and e into a new loadable file called link.o no additional parameters are required, but the following are allowed:" echo " -r nnnn -- start relocatable code at nnnn (a hex address) -c nnnn -- start commons at nnnn (a hex address) -m -- suppress linking with standard monitor -o ffff -- direct output to file ffff.o instead of link.o " exit 1 fi # defaults for linkage origin=1000 common=10000 monitor=monitor.o outfile=link # parse options -- loop exits on first non-option while [ $# -gt 0 ] do case $1 in -c) if [ $# -gt 1 ] then common=$2 shift shift else echo "link -c nnn requires a hex number nnn" echo "link -help explains options" exit 1 fi ;; -r) if [ $# -gt 1 ] then origin=$2 shift shift else echo "link -r nnn requires a hex number nnn" echo "link -help explains options" exit 1 fi ;; -o) if [ $# -gt 1 ] then outfile=$2 shift shift else echo "link -o fff requires a file name fff" echo "link -help explains options" exit 1 fi ;; -m) monitor="" shift ;; -*) echo "link $1 option unknown" echo "link -help explains options" exit 1 ;; *) break ;; esac done if [ $# -lt 1 ] then echo "link found no object files" echo "link -help explains options" exit 1 fi # build linker control file header echo TITLE "smal $*" > ${outfile}.a echo ".=#${origin}" >> ${outfile}.a echo "C=#${common}" >> ${outfile}.a if [ -n ${monitor} ] then echo "USE \"${monitor}\"" >> ${outfile}.a fi # build linker control file body while [ $# -gt 0 ] do case $1 in -*) rm ${outfile}.a echo "link $1 unexpected option" echo "link -help explains command format" exit 1 ;; *) if [ -r $1 ] then echo USE \"$1\" >> ${outfile}.a shift elif [ -r ${usepath}$1 ] then echo USE \"${usepath}$1\" >> ${outfile}.a shift else echo "link $1 unreadable object file" echo "link -help is available" exit 1 fi ;; esac done # build linker control file trailer echo RUNUSED=C >> ${outfile}.a echo RUNAVAIL=#20000 >> ${outfile}.a # actually do the linkage ${smal} -L ${outfile}.a rm ${outfile}.a @EOF chmod +x link echo x - monitor.h sed 's/^@//' >monitor.h <<'@EOF' ; monitor.h ; Standard interface definitons for the Hawk monitor. ; Linkage conventions: ; R1 -- used for linkage by all support routines. ; R2 -- typically used internally as an activation record pointer. ; R3 -- typically the first parameter and function return value ; R4 -- typically the second parameter, if there is one. ; This file allocates one word pointing to each monitor function. ALIGN 4 ;------------------------------- ; linkage the one universal monitor service EXT EXIT ; terminate application ; touches nothing! ;------------------------------- ; linkage to monitor display management routines EXT DSPINI ; initialize display ; returns R3=columns (X size of display) ; returns R4=lines (Y size of display) EXT DSPAT ; set display text coordinates ; takes R3=X R4=Y (origin in top left) ; wipes out R2-7 EXT DSPCH ; output one character ; takes R3=character ; wipes out R4-5, increments X EXT DSPST ; output one null terminated string ; takes R3=string pointer ; wipes out R3-7, increments X EXT DSPHX ; output integer in hexadecimal ; takes R3=integer to print ; wipes out R3-7, increments X EXT DSPDEC ; output signed integer in decimal EXT DSPDECU ; output unsigned integer in decimal ; takes R3=integer to print ; takes R4=field width ; wipes out R3-6, increments X ;------------------------------- ; linkage to monitor keyboard interface EXT KBGETC ; get a character from the keyboard ; returns R3=character ; wipes out R4 EXT KBGETS ; get null-terminated string from keyboard ; takes R3=pointer to string buffer ; wipes out R3-7 ;------------------------------- ; arithmetic routines EXT MUL ; multiply two signed integers ; takes R3=multiplicand R4=multiplier ; returns R3=product ; wipes out R4-6 EXT DIVU ; divide two unsigned integers ; takes R3=dividend R4=divisor ; returns R3=quotient R4=remainder ; wipes out R5-6 @EOF echo x - monitor.a sed 's/^@//' >monitor.a <<'@EOF' TITLE Hawk Monitor and Standard Library ;USE "/group/22c018/hawk.macs" USE "hawk.macs" MACRO ALLOC =n . = . + n ENDMAC ; trap vector RESTARTTRAP = #00 BUSTRAP = #10 INSTRTRAP = #20 ; memory mapped display interface DISPBASE = #FF000000 DISPCOLS = 4 DISPTEXT = #100 ; keyboard interface KBDBASE = #FF100000 KBDDATA = 0 KBDSTAT = 4 ; 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. COMMON TRAPBUF,TRAPLIM ; trap save area LCSAVE=. .=TRAPBUF MASV: ALLOC 4 ; saved trap memory address PCSV: ALLOC 4 ; saved trap program counter PSSV: ALLOC 4 ; saved trap psw R1SV: ALLOC 4 ; saved register 1 R2SV: ALLOC 4 ; saved register 2 R3SV: ALLOC 4 ; saved register 3 R4SV: ALLOC 4 ; saved register 4 R5SV: ALLOC 4 ; saved register 5 R6SV: ALLOC 4 ; saved register 6 R7SV: ALLOC 4 ; saved register 7 ALIGN 4 STKBAS: ALLOC 16 ; monitor stack (not much depth needed) TRAPLIM: .=LCSAVE . = RESTARTTRAP CPUSET R1,TSV ; 2 LOAD R1,R5SVP ; 4 STORES R5,R1 ; 2 = 16 locations total LEA R5,RESMSG ; 4 JUMP TCOM ; 4 . = BUSTRAP CPUSET R1,TSV ; 2 LOAD R1,R5SVP ; 4 STORES R5,R1 ; 2 = 16 locations total LEA R5,BUSMSG ; 4 JUMP TCOM ; 4 . = INSTRTRAP CPUSET R1,TSV ; 2 LOAD R1,R5SVP ; 4 STORES R5,R1 ; 2 = 16 locations total LEA R5,INSMSG ; 4 JUMP TCOM ; 4 ALIGN 4 R5SVP: W R5SV ; pointer to R5 save loc PCSVP: W PCSV ; pointer to PC save loc MASVP: W MASV ; pointer to MA save loc PSSVP: W PSSV ; pointer to PS save loc TRAPBUP:W TRAPBUF ; pointer to whole save area STKBASP:W STKBAS ; pointer to base of stack RESMSG: ASCII "Restart Trap. ",0 INSMSG: ASCII "Instruction Trap.",0 BUSMSG: ASCII "Bus Trap. ",0 ALIGN 2 TCOM: ; common code shared by all traps ; come here with user's R1 in TSV, R5 in R5SV ; trap message in R5 LOAD R1,TRAPBUP ; setup for indexing STORE R2,R1,R2SV-TRAPBUF STORE R3,R1,R3SV-TRAPBUF STORE R4,R1,R4SV-TRAPBUF ; R5 already saved STORE R6,R1,R6SV-TRAPBUF STORE R7,R1,R7SV-TRAPBUF CPUGET R2,TPC CPUGET R3,TSV ; 2 CPUGET R4,TMA STORE R2,R1,PCSV-TRAPBUF STORE R3,R1,R1SV-TRAPBUF STORE R4,R1,MASV-TRAPBUF CPUGET R2,PSW STORE R2,R1,PSSV-TRAPBUF ; the following trap service code simply prints ; diagnostic output and terminates! LOAD R2,STKBASP ; setup for output JSR R1,DSPINI ; wipes out R3-4 MOVE R3,R5 ; output trap name JSR R1,DSPST ; wipes out R3-7 LEA R3,MSGPC ; output "PC =" JSR R1,DSPST ; wipes out R3-7 LOAD R4,PCSVP LOADS R3,R4 ; output PC value JSR R1,DSPHX ; wipes out R3-7 LEA R3,MSGXX ; output trailer JSR R1,DSPST ; wipes out R3-7 LIS R3,17 LIS R4,1 ; moveto (17,1) JSR R1,DSPAT ; wipes out R3-7 LEA R3,MSGMA ; output "MA =" JSR R1,DSPST ; wipes out R3-7 LIL R4,MASVP LOADS R3,R4 ; output MA value JSR R1,DSPHX ; wipes out R3-7 LEA R3,MSGXX ; output trailer JSR R1,DSPST ; wipes out R3-7 LIS R3,17 LIS R4,2 ; moveto (17,2) JSR R1,DSPAT ; wipes out R3-7 LEA R3,MSGPS ; output "PS =" JSR R1,DSPST ; wipes out R3-7 LIL R4,PSSVP LOADS R3,R4 ; output MA value JSR R1,DSPHX ; wipes out R3-7 LEA R3,MSGXX ; output trailer JSR R1,DSPST ; wipes out R3-7 LOAD R7,TRAPBUP ; setup to restore registers LOAD R1,R7,PSSV-TRAPBUF CPUSET R1,PSW LOAD R1,R7,R1SV-TRAPBUF LOAD R2,R7,R2SV-TRAPBUF LOAD R3,R7,R3SV-TRAPBUF LOAD R4,R7,R4SV-TRAPBUF LOAD R5,R7,R5SV-TRAPBUF LOAD R6,R7,R6SV-TRAPBUF LOAD R7,R7,R7SV-TRAPBUF JUMP 0000 MSGPC: ASCII " Trap PC = #",0 MSGMA: ASCII " Trap MA = #",0 MSGPS: ASCII " Trap PSW= #",0 MSGXX: ASCII " ",0 ; 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 wipes; 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! COMMON DSPPTR,4 ; pointer to output in Video RAM ALIGN 4 DSPPTP: W DSPPTR ; pointer to the pointer! ALIGN 2 ;------------------------ INT DSPINI DSPINI: ; initialize for display ; wipes out R3-4 ; does not use R2 LIW R3,DISPBASE+DISPTEXT-1 LOAD R4,DSPPTP STORES R3,R4 JUMPS R1 ; return ;------------------------ INT DSPAT ; AR format indexed using R2: ; 0 ; return address DSPAAR = 4 ; total size DSPAT: ; move to X=R3, Y=R4 on display ; wipes out R3-7 MOVE R7,R3 ; set aside X coord STORES R1,R2 ; push old return address ADDSI R2,DSPAAR LIW R3,DISPBASE+DISPCOLS LOADS R3,R3 ; get columns times y coord JSR R1,TIMES; wipes out R4-6 ADD R3,R3,R7; add x coord LIW R5,DISPBASE+DISPTEXT-1 ADD R3,R3,R5; add display base (less 1) LOAD R4,DSPPTP STORES R3,R4 ; done! ADDSI R2,-DSPAAR LOADS R1,R2 ; restore return address JUMPS R1 ;------------------------ INT DSPCH DSPCH: ; output char in R3 ; wipes out R4-5 ; does not use R2 LOAD R4,DSPPTP LOADS R5,R4 ; get display pointer ADDSI R5,1 STORES R5,R4 ; save updated pointer LOADS R4,R5 STUFFB R4,R3,R5 STORES R4,R5 ; update display JUMPS R1 ; return ;------------------------ INT DSPST DSPST: ; output string pointed to by R3 ; wipes out R3-7 ; does not use R2 MOVE R7,R3 MOVE R6,R1 DSPSTL: LOADS R4,R7 ; get a character to display EXTB R3,R4,R7 BZS DSPSTQ ; quit loop if null JSR R1,DSPCH ; wipes out R4-5 ADDSI R7,1 ; advance to next char BR DSPSTL DSPSTQ: MOVE R1,R6 JUMPS R1 ;------------------------ INT DSPHX ; AR format indexed using R2: ; 0 ; return address DSPHAR = 4 ; total size DSPHX: ; output hex number in R3 ; wipes out R3-7 STORES R1,R2 ; push old return address ADDSI R2,DSPHAR MOVE R7,R3 LIS R6,8 ; initialize loop counter DSPHXL: MOVE R3,R7 ; loop top SRU R3,12 SRU R3,16 ; get one digit in place LIS R4,#F AND R3,R4 ; mask off surplus bits ADDI R3,'0' ; make an ASCII digit CMPI R3,'9' ; see if should make a letter BLE DSPHXN ADDI R3,'A'-('9'+1) DSPHXN: JSR R1,DSPCH ; wipes out R4-5 SL R7,4 ; advance to next digit ADDSI R6,-1 ; decrement loop counter BGT DSPHXL ADDSI R2,-DSPHAR LOADS R1,R2 ; restore return address JUMPS R1 ;------------------------ INT DSPDEC ; AR format, indexed using R2: ; 0 ; return address DSPDRM = 4 ; remainder after division by 10 DSPDFW = 8 ; field width DSPDAR = 12 ; total size DSPDEC: ; output R3 in decimal ; uses R4 as field width ; wipes out R3-6 STORES R1,R2 ; save ret addr in ar ADDSI R4,-1 ; decrement and save field width STORE R4,R2,DSPDFW LIS R4,10 JSR R1,DIVIDE; divide number by 10 (wipe out R5-6) STORE R4,R2,DSPDRM TESTR R3 ; check quotient after save remainder BZS DSPDBL ; if zero, we have final digit LOAD R4,R2,DSPDFW ADDI R2,DSPDFW ; setup for recursion JSR R1,DSPDEC ; recursive call ADDI R2,-DSPDFW; (note that field width not needed) DSPDQT: LOAD R3,R2,DSPDRM ADDI R3,'0' ; convert remainder to ASCII JSR R1,DSPCH; output it (wipe out R4-5) LOADS R1,R2 ; recover ret addr from ar JUMPS R1 ; return ; by rights, the following else clause should be inside ; the above, but it's a bit more efficient to put it here DSPDBL: LOAD R6,R2,DSPDFW LIS R3,' ' DSPDLP: ADDSI R6,-1 ; decrement BLT DSPDQT ; quit if nothing left JSR R1,DSPCH; output blank (wipe out R4-5) BR DSPDLP ;------------------------------- INT KBGETC KBGETC: ; get char from keyboard ; return char in R3 ; wipe out R4 ; does not use R2 LIW R4,KBDBASE+KBDSTAT KBPOLL: ; loop awaiting input LOADSCC R3,R4 ; test status BZS KBPOLL LIW R4,KBDBASE+KBDDATA LOADS R3,R4 ; get char JUMPS R1 ; return ;------------------------------- INT KBGETS KBGETS: ; get string from keyboard ; expects R3 - pointer to string ; wipes out R3-7 ; does not use R2 MOVE R7,R1 ; save return addr MOVE R6,R3 ; set aside string pointer KBGSLP: JSR R1,KBGETC ; get a char CMPI R3," " ; see if printable BLT KBGSNP LOADS R5,R6 STUFFB R5,R3,R6 ; store character in string STORES R5,R6 ADDSI R6,1 ; advance string pointer JSR R1,DSPCH ; echo the char (wipe out R4-5) BR KBGSLP ; get next char! KBGSNP: CMPI R3,8#12 ; see if LF key BEQ KBGSQT CMPI R3,8#15 ; see if CR key BEQ KBGSQT CMPI R3,8#10 ; see if BS key BNE KBGSLP ; ignore char if not LOAD R4,DSPPTP LOADS R5,R4 ; get display pointer ADDSI R5,-1 STORES R5,R4 ; update display pointer ADDSI R5,1 ; now go back and erase a char LIS R4," " ; now go back and erase a char LOADS R3,R5 STUFFB R3,R4,R5 STORES R3,R5 ; erase char from display ADDSI R6,-1 ; also backup string pointer BR KBGSLP ; get next char KBGSQT: LOADS R5,R6 STUFFB R5,R0,R6 ; store null at end-string STORES R5,R6 JUMPS R7 ; return ;------------------------ INT MUL MUL: ; R3 = R3 * R4 (signed!) ; wipes out R4-6 ; does not use R2 MOVE R5,R3 ; set aside multiplicand CLR R3 SL R4,1 ; shift multiplier left 1 BCR .+4 ; if sign bit was 1 SUB R3,R0,R5; subtract multiplicand from accumulator LIS R6,31 ; load loop counter TIMLP: SL R3,1 ; \ SL R4,1 ; \ BCR .+4 ; / 31 more multiply steps ADD R3,R3,R5; / ADDSI R6,-1 ; count BGT TIMLP ; iterate JUMPS R1 ;------------------------ INT DIVU DIVU: ; R3 = R3 / R4; R4 = R3 mod R4 (unsigned!) ; wipes out R5-6 ; does not use R2 MOVE R5,R4 ; set aside divisor CLR R4 ; clear remainder LIS R6,32 ; load loop counter DIVLP: SL R3,1 ; shift dividend/quotient ROL R4 ; putting high bit into remainder CMP R4,R5 ; see if divisor divides remainder BLTU .+6 ; if not, skip SUB R4,R4,R5; if so, take away divisor ADDSI R3,1 ; and add a bit to the quotient ADDSI R6,-1 ; count BGT DIVLP ; iterate JUMPS R1 @EOF if [ $EXIT_STATUS -eq 1 ];then exit 1 fi exit 0