hexdump for MIPS emulator: mars

I translated the RISC-V assembly hex dump into MIPS assembly. Because of the fact that my RISC-V simulator, rars, is a fork of the MIPS simulator, mars, I just had to do some slight changes in register names and system calls because they are mostly compatible. In fact the main thing is that the MIPS registers must all be prefixed with $. It took a few hours to do.

Theoretically, it also could be made to be compatible with spim, however I haven’t learned how command line arguments are passed using spim.

#hexdump for MIPS emulator: mars
.data
title: .asciiz "hexdump program in MIPS assembly language\n\n"

# test string of integer for input
test_int: .asciiz "10011101001110011110011"
hex_message: .asciiz "Hex Dump of File: "
file_message_yes: .asciiz "The file is open.\n"
file_message_no: .asciiz "The file could not be opened.\n"
file_data: .byte '?':16
           .byte 0
space_three: .asciiz "   "

#this is the location in memory where digits are written to by the putint function
int_string: .byte '?':32
int_newline: .byte 10,0
radix: .byte 2
int_width: .byte 4

argc: .word 0
argv: .word 0

.text
main:

# at the beginning of the program a0 has the number of arguments
# so we will save it in the argc variable
la $t1,argc
sw $a0,0($t1)

# at the beginning of the program a1 has a pointer to the argument strings
# so we save it because we may need a1 for system calls
la $t1,argv
sw $a1,0($t1)

#Now that the argument data is stored away, we can access it even if it is overwritten.
#For example, the putstring function uses a0 for system call number 4, which prints a string

la $s0,title
jal putstring

li $t0,16    #change radix
la $t1,radix
sb $t0,0($t1)

li $t0,1    #change width
la $t1,int_width
sb $t0,0($t1)


# next, we load argc from the memory so we can display the number of arguments
la $t1,argc
lw $s0,0($t1)
#jal putint

beq $s0,$zero,exit # if the number of arguments is zero, exit the program because nothing else to print

# this section processes the filename and opens the file from the first argument

jal next_argument
#jal putstring
move $s7,$s0 #save the filename in register s7 so we can use it any time

li $v0,13 # open file call number
move $a0,$s7  # copy filename for the open call
li $a1,0    # read only access for the file we will open (rars does not support read+write mode)
syscall

move $s0,$v0
#jal putspace
#jal putint

blt $s0,$zero,file_error # branch if argc is not equal to zero

move $s6,$s0 # save the file handle in register s6
la $s0,file_message_yes
#jal putstring

jal hexdump

j exit

file_error:

la $s0,file_message_no
jal putstring


j exit

exit:
li $v0, 10     # exit syscall
syscall








# this is the hexdump function

hexdump:
addi $sp,$sp,-4
sw $ra,0($sp)

la $s0,hex_message
jal putstring
move $s0,$s7
jal putstring
jal putline


li $t0,0    #disable automatic newlines after putint
la $t1,int_newline
sb $t0,0($t1)

li, $s5,0 # we will use s5 register as current offset



hex_read_row:
li $v0,14        # read system call
move $a0,$s6        # file handle
la $a1,file_data # where to store data
li $a2,16        # how many bytes to read
syscall           # a0 will have number of bytes read after this call

move $s3,$v0 #save v0 to s3 to keep count of how many bytes read
move $s2,$v0 #save v0 to s2 to keep count of how many bytes read

beq $v0,$zero,hexdump_end

li $s0,8    #change width
la $s1,int_width
sb $s0,0($s1)

move $s0,$s5
add $s5,$s5,$s3
jal putint
jal putspace

li $s0,2    #change width to 2 for the bytes printed this row
la $s1,int_width
sb $s0,0($s1)

la $s1,file_data
hex_row_print:
lb $s0,0($s1)
jal putint
jal putspace
addi $s1,$s1,1

addi $s2,$s2,-1
bne $s2,$zero,hex_row_print

#pad the row with extra spaces

move $t2,$s3
li $t3,16
extra_row_space:
beq $t2,$t3,extra_row_space_complete
la $s0,space_three
jal putstring
addi $t2,$t2,1
j extra_row_space
extra_row_space_complete:

#now the hex form of the bytes are printed
#we will filter the text form and also print it

li $s2,0
la $s1,file_data
char_filter:
lb $s0,0($s1)

#if char is below 0x20 or above 0x7E, it is outside the range of printable characters

li $t5,0x20
blt $s0,$t5,not_printable
li $t5,0x7E
bgt $s0,$t5,not_printable

j next_char_index

not_printable:
li $s0,'.'
sb $s0,0($s1)

next_char_index:
addi $s1,$s1,1
addi $s2,$s2,1
blt $s2,$s3,char_filter

li $s0,0
sb $s0,0($s1)   #terminate string with a zero

la $s0,file_data
jal putstring


jal putline

j hex_read_row

hexdump_end:
lw $ra,0($sp)
addi $sp,$sp,4
jr $ra








# this function gets the next command line argument and returns it in s0
# it also decrements the argc variable so that it can be checked for 0 to exit the program if needed by the main program

next_argument:

la $t1,argv
lw $t0,0($t1) #load the string pointer located in argv into t0 register
lw $s0,0($t0) #load the data being pointed to by t0 into s0 for displaying the string
addi $t0,$t0,4 #add 4 to the pointer
sw $t0,0($t1)  #store the pointer so it will be loaded at the next string if the loop continues

# load the number of arguments from memory, subtract 1, store back to memory
# then use to compare and loop if nonzero
la $t1,argc
lw $t0,0($t1)

addi $t0,$t0,-1
sw $t0,0($t1)

jr $ra



putline:
li $v0,11
li $a0,10
syscall
jr $ra

putspace:
li $v0,11
li $a0,' '
syscall
jr $ra





putstring:
li $v0,4      # load immediate, v0 = 4 (4 is print string system call)
move $a0,$s0  # load address of string to print into a0
syscall
jr $ra


#this is the intstr function, the ultimate integer to string conversion function
#just like the Intel Assembly version, it can convert an integer into a string
#radixes 2 to 36 are supported. Digits higher than 9 will be capital letters

intstr:

la $t1,int_newline # load target index address of lowest digit
addi $t1,$t1,-1

lb $t2,radix     # load value of radix into $t2
lb $t4,int_width # load value of int_width into $t4
li $t3,1         # load current number of digits, always 1

digits_start:

divu $s0,$s0,$t2 # $s0=$s0/$t2 (divide s0 by the radix value in $t2)
mfhi $t0        # $t0=remainder of the previous division
blt $t0,10,decimal_digit
bge $t0,10,hexadecimal_digit

decimal_digit: # we go here if it is only a digit 0 to 9
addi $t0,$t0,'0'
j save_digit

hexadecimal_digit:
addi $t0,$t0,-10
addi $t0,$t0,'A'

save_digit:
sb $t0,($t1) # store byte from $t0 at address $t1
beq $s0,$0,intstr_end
addi $t1,$t1,-1
addi $t3,$t3,1
j digits_start

intstr_end:

li $t0,'0'
prefix_zeros:
bge $t3,$t4,end_zeros
addi $t1,$t1,-1
sb $t0,($t1) # store byte from $t0 at address $t1
addi $t3,$t3,1
j prefix_zeros
end_zeros:

move $s0,$t1

jr $ra

#this function calls intstr to convert the $s0 register into a string
#then it uses a system call to print the string
#it also uses the stack to save the value of $s0 and $ra (return address)

putint:
addi $sp,$sp,-8
sw $ra,0($sp)
sw $s0,4($sp)
jal intstr
#print string
li $v0,4      # load immediate, v0 = 4 (4 is print string system call)
move $a0,$s0  # load address of string to print into a0
syscall
lw $ra,0($sp)
lw $s0,4($sp)
addi $sp,$sp,8
jr $ra




strint:

move $t1,$s0 # copy string address from $s0 to $t1
li $s0,0

lb $t2,radix     # load value of radix into $t2

read_strint:
lb $t0,($t1)
addi $t1,$t1,1
beq $t0,0,strint_end

#if char is below '0' or above '9', it is outside the range of these and is not a digit
blt $t0,'0',not_digit
bgt $t0,'9',not_digit

#but if it is a digit, then correct and process the character
is_digit:
and $t0,$t0,0xF
j process_char

not_digit:
#it isn't a digit, but it could be perhaps and alphabet character
#which is a digit in a higher base

#if char is below 'A' or above 'Z', it is outside the range of these and is not capital letter
blt $t0,'A',not_upper
bgt $t0,'Z',not_upper

is_upper:

sub $t0,$t0,'A'
addi $t0,$t0,10
j process_char

not_upper:

#if char is below 'a' or above 'z', it is outside the range of these and is not lowercase letter
blt $t0,'a',not_lower
bgt $t0,'z',not_lower

is_lower:

sub $t0,$t0,'a'
addi $t0,$t0,10
j process_char

not_lower:

#if we have reached this point, result invalid and end function
#this is only reached if the byte was not a valid digit or alphabet character
j strint_end

process_char:

bgt $t0,$t2 strint_end #;if this value is above or equal to radix, it is too high despite being a valid digit/alpha

mul $s0,$s0,$t2 # multiply $s0 by the radix
add $s0,$s0,$t0     # add the correct value of this digit

j read_strint # jump back and continue the loop if nothing has exited it

strint_end:

jr $ra

To assemble and run this source, this command will do:

java -jar ~/Mars4_5.jar main.s pa testfile

Comments

Leave a comment