### translator.asm ## 378->SPIM-ifier ## hw3 CSE 378 ### notes: ## $s7 - filehandle ## $t9, s4 - loop counter .data inputFile: .asciiz "378.binary" outputFile: .asciiz "spim.asm" deadStr: .asciiz "der wer an error\n" magic378: .ascii "378 " line: .ascii "line" globlmain: .ascii "\n\n\t.text\n\t.globl main\nmain:\n\tmove\t$t9, $sp\n" .ascii "\tli\t$t8, 0\n\n" dotdata: .ascii "\t.data\nretstr:\t.asciiz\t\"\nProgram exited with code" .ascii " \"\ndatastart: " dotword: .ascii "\n\t.word\t" dotdatajt: .ascii "\n\njt:\t" addcode: .ascii "\taddi\t$t0, $sp, 8\n\tbgt\t$t0, $t9, error\n\tlw\t$t0," .ascii " 0($sp)\n\tlw\t$t1, 4($sp)\n\taddi\t$sp, $sp, 4\n\tadd" .ascii "\t$t0, $t0, $t1\n\tsw\t$t0, 0($sp)\n" subcode: .ascii "\taddi\t$t0, $sp, 8\n\tbgt\t$t0, $t9, error\n\tlw\t$t0," .ascii " 0($sp)\n\tlw\t$t1, 4($sp)\n\taddi\t$sp, $sp, 4\n\tsub" .ascii "\t$t0, $t1, $t0\n\tsw\t$t0, 0($sp)\n" multcode: .ascii "\taddi\t$t0, $sp, 8\n\tbgt\t$t0, $t9, error\n\tlw\t$t0," .ascii " 0($sp)\n\tlw\t$t1, 4($sp)\n\taddi\t$sp, $sp, 4\n\tmul" .ascii "\t$t0, $t1, $t0\n\tsw\t$t0, 0($sp)\n" bgtzcode: .ascii "\taddi\t$t0, $sp, 4\n\tbgt\t$t0, $t9, error\n\tlw\t$t0," .ascii " 0($sp)\n\taddi\t$sp, $sp, 4\n\tbgtz\t$t0, " bezcode: .ascii "\taddi\t$t0, $sp, 4\n\tbgt\t$t0, $t9, error\n\tlw\t$t0," .ascii " 0($sp)\n\taddi\t$sp, $sp, 4\n\tbeqz\t$t0, " jumpcode: .ascii "\taddi\t$t0, $sp, 4\n\tbgt\t$t0, $t9, error\n\tlw\t$t0," .ascii " 0($sp)\n\taddi\t$sp, $sp, 4\n\tlw\t$t1, jt($t0)\n\tjr" .ascii "\t$t1\n" loadcode: .ascii "\taddi\t$t0, $sp, 4\n\tbgt\t$t0, $t9, error\n\tlw\t$t0," .ascii " 0($sp)\n\tlw\t$t1, datastart($t0)\n\tsw\t$t1, 0($sp)\n" storcode: .ascii "\taddi\t$t0, $sp, 8\n\tbgt\t$t0, $t9, error\n\tlw\t$t0," .ascii " 0($sp)\n\tlw\t$t1, 4($sp)\n\tsw\t$t1, datastart($t0)\n" .ascii "\taddi\t$sp, $sp, 8\n" intcode0: .ascii "\tj\tend\n" intcode1: .ascii "\taddi\t$t0, $sp, 4\n\tbgt\t$t0, $t9, error\n\tlw\t$a0," .ascii " 0($sp)\n\tli\t$v0, 1\n\tsyscall\n\taddi\t$sp, $sp, 4\n" intcode2: .ascii "\taddi\t$sp, $sp, -4\n\tli\t$v0, 5\n\tsyscall\n\tsw" .ascii "\t$v0, 0($sp)\n" clercode: .ascii "\tmove\t$sp, $t9\n" pushcode1: .ascii "\taddi\t$sp, $sp, -4\n\tli\t$t0, -" pushcode2: .ascii "\n\tsw\t$t0, 0($sp)\n" endcode: .ascii "\nend:\tmove\t$sp, $t9\n\tla\t$a0, retstr\n\tli\t$v0, " .ascii "4\n\tsyscall\n\tmove\t$a0, $t8\n\tli\t$v0, 1\n\tsyscal" .ascii "l\n\tjr\t$ra\nerror:\tli\t$t8, 1\n\tj\tend\n" .align 2 jt: .word die # op code 0x0 not implemented .word opadd .word opsub .word opmult .word die # op code 0x4 not implemented .word opbgtz .word opbez .word opjump .word opload .word opstor .word opint .word die # op code 0xB not implemented .word die # op code 0xC not implemented .word opcler .word oppush intjt: .word opint0 opint1 opint2 .text .globl main main: addi $sp, $sp, -20 # space for $ra + 16 bytes of inputFile header sw $ra, 16($sp) # save return address ### Open the binary 378 file, read in the header. ### Read rest of file into memory, then open the output file ### ## open inputFile for read in binary mode la $a0, inputFile # file name string li $a1, 0x8000 # _O_BINARY file mode li $a2, 0 # no perms needed li $v0, 11 # open syscall syscall # bltz $v0, die # exit if error move $s7, $v0 # remember input file handle ## read in the first 16 bytes move $a0, $s7 # move in file descriptor add $a1, $sp, $0 # data goes into stack li $a2, 0x10 # get 16 bytes li $v0, 12 # read syscall syscall # ## is this a 378 binary? lw $t0, 0($sp) # first byte of file ulw $t1, magic378($0) # technically a string, so maybe unaligned bne $t0, $t1, die # test is NOT actually endian-dependant ## it is, so "parse" the header lw $s0, 4($sp) # datasize lw $s1, 8($sp) # codesize lw $s2, 12($sp) # IPC ## ask the OS for some memory and put the file there addu $a0, $s0, $s1 # need s0+s1 bytes of memory li $v0, 9 # sbrk syscall syscall # move $a0, $s7 # move in file descriptor move $a1, $v0 # memory space from sbrk addu $a2, $s0, $s1 # read s0+s1 bytes li $v0, 12 # read syscall syscall # move $s5, $a1 # s5 is start of data add $s6, $s5, $s0 # s6 is start of code move $a0, $s7 # don't need the input file anymore li $v0, 14 # close syscall (need to free up the fd) syscall # ## get us an output file la $a0, outputFile # file name string li $a1, 0x4302 # _O_TEXT | _O_TRUNC | _O_CREAT | _O_RDWR li $a2,0x180 # _S_IREAD | _S_IWRITE li $v0, 11 # open syscall syscall # bltz $v0, die # exit if error move $s7, $v0 # remember output file handle ### Print .data section ### Includes data from the 378 binary and the jump table ### ## print the data section to the output file move $a0, $s7 # output file descriptor la $a1, dotdata # string to write li $a2, 63 # 63 bytes of string li $v0, 13 # write syscall syscall # add $s4, $0, $0 # $s4 is loop counter ## print .data wloop: la $a1, dotword # string to write li $a2, 8 # 8 bytes worth of string li $v0, 13 # write syscall syscall # ## load the data word, convert to hex string add $t3, $s5, $s4 # address of the current data word lw $a0, 0($t3) # the current data word addi $a1, $sp, 0 # address to store hex string jal hexconv # ## print hex string to file move $a0, $s7 # output file descriptor move $a1, $sp # start writing at stack pointer li $a2, 10 # '0x' + 8 digit hex number li $v0, 13 # write syscall syscall # write syscall addi $s4, $s4, 4 # increment by word size blt $s4, $s0, wloop # loop while in data section ## print jumptable la $a1, dotdatajt # '\n\njt:\t' li $a2, 6 # 6 bytes li $v0, 13 # write syscall syscall # li $s4, 0 # instruction counter ulw $t0, line($0) # load string 'line' into $t0 sw $t0, 0($sp) # put at bottom of stack ## print '.word ' to file jtloop: la $a1, dotword # string to write li $a2, 8 # 8 bytes of string li $v0, 13 # write syscall syscall # ## print a label move $a0, $s4 # going to convert this to a hex string addi $a1, $sp, 4 # start hex string after 'line' jal hexconv # ## print 'line0x
' to file move $a0, $s7 # output file descriptor move $a1, $sp # start at bottom of stack li $a2, 14 # 'line' + '0x' + 8 hex digits li $v0, 13 # write syscall syscall # addi $s4, $s4, 4 # increment loop counter blt $s4, $s1, jtloop # loop to end of code ($s1 is codesize) ### Write body of output file. Jump to the appropriate op label for ### each instruction in the input file ### move $a0, $s7 # output file descriptor la $a1, globlmain # li $a2, 55 # li $v0, 13 # write syscall syscall # ## manufacture a push(IPC) & jump there move $a0, $s2 # pass IPC as first arg jal oppush # jal opjump # li $s4, 0 # instruction counter ulw $t0, line($0) # load string 'line' into $t8 sw $t0, 0($sp) # put at bottom of stack li $t0, 58 # ascii ':' sb $t0, 14($sp) # li $t0, 10 # ascii LF sb $t0, 15($sp) # codelp: move $a0, $s4 # going to convert this to a hex string addi $a1, $sp, 4 # start hex string after 'line' jal hexconv # ## print 'line0x
' to file move $a0, $s7 # output file descriptor move $a1, $sp # start at bottom of stack li $a2, 16 # 'line' + '0x' + 8 hex digits + ":\t" li $v0, 13 # write syscall syscall # ## now find out what the instruction is and jump to its label add $t3, $s4, $s6 # get address to current instruction lw $t4, 0($t3) # load instruction li $t3, 0xFFFFFFF # mask and $a0, $t4, $t3 # argument is lower 28 bits of instruction srl $t4, $t4, 28 # isolate op code sll $t4, $t4, 2 # multiply by 4 (next instruction) lw $t3, jt($t4) # load address from jp table jal $t3 # addi $s4, $s4, 4 # increment loop counter blt $s4, $s1, codelp # loop to end of code ($s1 is codesize) ## print end string move $a0, $s7 # output file descriptor la $a1, endcode # load string li $a2, 123 # 123 bytes worth of code li $v0, 13 # write_syscall syscall # ### ### ### ### Subroutines ### endOfProg: move $a0, $s7 # don't need the output file anymore li $v0, 14 # close syscall syscall # lw $ra, 16($sp) # restore return address addi $sp, $sp, 4 # deallocate stack jr $ra # return die: la $a0, deadStr li $v0, 4 syscall li $v0, 10 syscall ### Takes word in $a0, converts to hex string, and writes that to $a1 hexconv: ## start with the string "0x" li $t0, 0x30 # ascii '0' sb $t0, 0($a1) # li $t0, 0x78 # ascii 'x' sb $t0, 1($a1) # addi $t0, $a1, 2 # go to 3rd byte li $t9, 28 # shift amount (nybble counter) li $t1, 9 # 9 hexlp: srl $t2, $a0, $t9 # shift 4 bits to beginning of word andi $t2, $t2, 0xF # isolate the 4 bits bgt $t2, $t1, hexch # for hex digits a-f addi $t2, $t2, 0x30 # convert to character j hexwr # hexch: addi $t2, $t2, 55 # convert to character hexwr: sb $t2, 0($t0) # store char in stack addi $t0, $t0, 1 # increment char position addi $t9, $t9, -4 # decrement shift value bgez $t9, hexlp # loop until whole word has been processed jr $ra # return ### ### opcode implementations ### opadd: move $a0, $s7 # output file descriptor la $a1, addcode # use string in .data li $a2, 124 # 124 bytes of string li $v0, 13 # write syscall syscall # jr $ra opsub: move $a0, $s7 # output file descriptor la $a1, subcode # use string in .data li $a2, 124 # 124 bytes of string li $v0, 13 # write syscall syscall # jr $ra opmult: move $a0, $s7 # output file descriptor la $a1, multcode # use string in .data li $a2, 124 # 124 bytes of string li $v0, 13 # write syscall syscall # jr $ra opbgtz: move $t8, $ra # store returning address addi $a1, $sp, 4 # put hex string at sp + 4 jal hexconv # convert a0 to string li $t0, 10 # ascii LF lbu $t1, 14($sp) # store value at sp + 14 sb $t0, 14($sp) # store LF in stack move $a0, $s7 # output file dsecriptor la $a1, bgtzcode # code for bgtz li $a2, 84 # 84 chars li $v0, 13 # write syscall syscall # move $a1, $sp # start printing from bottom of stack li $a2, 15 # 'line' + '0x' + 8 hex digits + '\n' li $v0, 13 # write syscall syscall # sb $t1, 14($sp) # restore value in stack jr $t8 opbez: move $t8, $ra # save returning address addi $a1, $sp, 4 # put hex string at sp + 4 jal hexconv # convert a0 to string li $t0, 10 # ascii LF lbu $t1, 14($sp) # store value at sp + 14 sb $t0, 14($sp) # store LF in stack move $a0, $s7 # output file dsecriptor la $a1, bezcode # code for bez li $a2, 84 # 84 chars li $v0, 13 # write syscall syscall # move $a1, $sp # start printing from bottom of stack li $a2, 15 # 'line' + '0x' + 8 hex digits + '\n' li $v0, 13 # write syscall syscall # sb $t1, 14($sp) # restore value in stack jr $t8 opjump: move $a0, $s7 # output file descriptor la $a1, jumpcode # code for jump li $a2, 98 # 98 chars li $v0, 13 # write syscall syscall # jr $ra # opload: move $a0, $s7 # output file descriptor la $a1, loadcode # use string in .data li $a2, 95 # 95 bytes of string li $v0, 13 # write syscall syscall # jr $ra opstor: move $a0, $s7 # output file descriptor la $a1, storcode # use string in .data li $a2, 113 # 113 bytes of string li $v0, 13 # write syscall syscall # jr $ra opint: sll $a0, $a0, 2 # multiply by 4 lw $t0, intjt($a0) # address of opint jr $t0 # opint0: la $a1, intcode0 # basically just "b end\n" li $a2, 7 # 7 chars j opintd # skip opint1 and opint2 opint1: la $a1, intcode1 # print int code li $a2, 93 # 93 chars j opintd # skip opint2 opint2: la $a1, intcode2 # read int code li $a2, 55 # 55 chars opintd: move $a0, $s7 # output file descriptor li $v0, 13 # write syscall syscall # jr $ra # opcler: move $a0, $s7 # output file descriptor la $a1, clercode # use string in .data li $a2, 15 # 15 bytes of string li $v0, 13 # write syscall syscall # jr $ra # oppush: ## sign extend $a0 sll $a0, $a0, 4 # shift left by 4 sra $a0, $a0, 4 # shift back preserving sign move $t0, $a0 # save argument ## print first half of push code move $t8, $ra # move $a0, $s7 # output file descriptor la $a1, pushcode1 # use string in .data li $a2, 28 # 28 bytes of string slt $t7, $v1, $0 # if v1 is neg, set to 1 add $a2, $a2, $t7 # 28 + 1 if v1 is negative li $v0, 13 # write syscall syscall # ## print value to push in hex move $a0, $t0 # move for hex convert addi $a1, $sp, 4 # start string at sp + 4 jal hexconv # convert to hex string move $a0, $s7 # output file li $a2, 10 # word is 10 bytes long li $v0, 13 # write syscall syscall # ## print second half of push code la $a1, pushcode2 # use string in .data li $a2, 17 # 17 bytes of string li $v0, 13 # write syscall syscall # jr $t8 #