Bitwise IO/MIPS Assembly
<lang mips> .data
- the following are used by the subroutines
bitbuf: .byte 0, 0 cumulus: .word 0 rbitbuf: .byte 0, 0 rfree: .word 0
- these are instead for testing
outbuf: .space 16 abuf: .space 16
txt: .asciiz "This is a test"
.text
main:
# ---- encode txt to buf ----- la $s0, txt la $s1, outbuf
encloop:
lbu $a0, ($s0) beq $a0, $0, txtend li $a1, 7 move $a2, $s1 jal bits_write addu $s0, $s0, 1 move $s1, $v1 b encloop
txtend:
move $a1, $s1 jal bits_flush
move $s1, $v0 # update buf, will be used as "terminator" la $s0, outbuf
# ---- write buffer
wrtchar:
beq $s0, $s1, wrtend # while $s0 < $s1 { lb $a0, ($s0) li $v0, 11 syscall # print_char($a0) addi $s0, $s0, 1 # inc $s0 b wrtchar # }
wrtend:
# ---- decode from buffer in bufferb---- # $s1 is still our end marker la $s3, outbuf # prev. output becomes input li $s7, 7 # commodity la $s4, abuf # current output buf
decloop:
bge $s3, $s1, decend # while $s3(input) < $s1(limit) move $a0, $0 # clean acc. move $a1, $s7 # 7 bits move $a2, $s3 # from input buf jal bits_read # read 7 bits into acc. move $s3, $v0 # update input buf sb $v1, ($s4) addi $s4, $s4, 1 # write char and update outp buf ptr b decloop # end while
decend:
# lets output abuf; $s4 points to end of abuf, # we need to mark it with 0 to print it as a str sb $0, ($s4) la $a0, abuf li $v0, 4 syscall # print_string(abuf)
li $v0, 10 # exit syscall
- read_bits
- $a0 acc.
- $a1 how many (max 32)
- $a2 input buffer
- > $v1 (new acc)
- > $t0 num of read bits or less (failure)
- > $v0 updated input buffer
bits_read:
sub $sp, $sp, 20 # local vars sw $ra, 0($sp) # save regs sw $s0, 4($sp) sw $s1, 8($sp) sw $s2, 12($sp) sw $s3, 16($sp)
move $s3, $a2 # $s3 input buf move $s0, $a0 # $s0 acc move $s1, $a1 # $s1 n move $s2, $0 # rbit = 0 li $t0, 32 bgt $a1, $t0, brexit # if n > 32 then return
brloop:
ble $s1, $0, brexit # while n > 0 (if <= 0 break) move $a0, $s0 move $a1, $s3 jal read_bit # read 1 bit into acc. move $s3, $v0 # update input buf li $t1, 1 bne $t0, $t1, brexit # exit if not read move $s0, $v1 # get updated acc sub $s1, $s1, 1 # n-- addi $s2, $s2, 1 # rbit++ b brloop
brexit:
move $t0, $s2 # currently read bits move $v1, $s0 # new accumulator move $v0, $s3 # updated input ptr lw $ra, 0($sp) # restore regs lw $s0, 4($sp) lw $s1, 8($sp) lw $s2, 12($sp) lw $s3, 16($sp) addu $sp, $sp, 20 # and free stack jr $ra # return
- get last (maybe incomplete) accumulated byte
- $a0 accumulator
- >$v0 free storage
bits_getlast:
lw $t0, rfree li $t1, 8 sub $t1, $t1, $t0 # sochar - rfree sllv $a0, $a0, $t1 # *d << (8 - rfree) lbu $t2, rbitbuf srlv $t2, $t2, $t0 # rbitbuf >> rfree sb $0, rbitbuf # rbitbuf = 0 sw $0, rfree # rfree = 0 move $v0, $t1 jr $ra # return sochar - rfree
- read a single bit
- $a0 accumulator
- $a1 input buffer
- > $v1 new accumulator
- > $t0 bit read (1 or not 1; in this impl, reading
- can't fail unless input buffer is an invalid ptr
- > $v0 updated input buffer
read_bit:
sub $sp, $sp, 20 # local stack, sw $s0, 0($sp) # save regs sw $s1, 4($sp) sw $s2, 8($sp) sw $s3, 16($sp)
move $s0, $a0 # save args move $s3, $a1 # $s0 = acc, $s3 = inbuf
lw $s1, rfree # preload rfree and lbu $s2, rbitbuf # rbitbuf
bne $s1, $0, skipif # if rfree == 0 then
lbu $s2, ($s3) # rbitbuf = nextchar() addi $s3, $s3, 1 # update it inp buf
li $s1, 8 # rfree = 8
skipif:
sll $s0, $s0, 1 # *d << 1 srl $t0, $s2, 7 # rbitbuf >> (8-1) andi $t0, $t0, 1 # & 1 or $s0, $s0, $t0 # | *d sll $s2, $s2, 1 # rbitbuf << 1 sub $s1, $s1, 1 # rfree--
sw $s1, rfree sb $s2, rbitbuf # update rfree, rbitbuf li $t0, 1 # $t0 returns 1
move $v1, $s0 # return the acc. in $v1 move $v0, $s3 # and updated inp buf in $v0 lw $s0, 0($sp) # restore regs lw $s1, 4($sp) lw $s2, 8($sp) lw $s3, 16($sp) add $sp, $sp, 20 # .. and stack jr $ra # return($v0, $v1)
- bits_write; write upto 32 bits
- $a0 holds the bits (right aligned)
- $a1 how many bits are significative
- $a2 dest buf
- > $v1 updated buf ptr
- > $v0 written bits (-1 on error)
bits_write:
sub $sp, $sp, 28 # some local storage sw $fp, 20($sp) move $fp, $sp sw $s1, 4($fp) # save regs and values sw $s0, 0($fp) sw $s2, 8($fp) sw $s3, 12($fp) sw $ra, 16($fp) sw $s4, 24($fp)
move $s4, $a2
move $s1, $a1 # n is $s1 li $t0, 32 bgt $a1, $t0, error # n > 32 => return(-1) sub $t0, $t0, $a1 # 32 - n sllv $s0, $a0, $t0 # d << (32-n) move $s3, $0
loop:
blez $s1, exit0 # if n <= 0 then break sub $s1, $s1, 1 # n-- addu $s3, $s3, 1 # wbit++ move $a0, $s0 rol $a0, $a0, 1 # place "last" bit in first pos. move $a1, $s4 jal appendbit # append it sll $s0, $s0, 1 # dpad << 1 move $s4, $v1 # update buf ptr b loop # loop
error:
li $v0, -1 b exit
exit0:
move $v0, $s3
exit:
lw $s1, 4($fp) # restore regs lw $s0, 0($fp) lw $s2, 8($fp) lw $s3, 12($fp) move $v1, $s4 lw $s4, 24($fp) lw $ra, 16($fp) move $sp, $fp # and release stack lw $fp, 20($sp) addi $sp, $sp, 28 jr $ra # return wbit, ptr to byte beyond last # written
- flush bits
- $a1 output buf
- >$v0 adv. ptr to buf.
bits_flush:
li $t0, 8 lw $t1, cumulus sub $t0, $t0, $t1 # 8 - cumulus lbu $a0, bitbuf sllv $a0, $a0, $t0 # bitbuf <<= (8 - cumulus) sb $a0, ($a1) # write to buf addi $v0, $a1, 1 # inc buf ptr sb $0, bitbuf # bitbuf := 0 sw $0, cumulus # cumulus := 0 jr $ra
- add a single bit.
- $a0 holds the bit (as LSB i.e. "rightmost bit")
- $a1 output buf
- > $v0 number of written bits (always 1)
- > $v1 incremented buf ptr
appendbit:
sub $sp, $sp, 16 # save s-regs on stack sw $s0, 0($sp) sw $s1, 4($sp) sw $s2, 8($sp) sw $s3, 12($sp)
move $s3, $a1 # save buf ptr
move $s0, $a0 # copy argument lw $s1, cumulus # get cumulus lbu $s2, bitbuf # preload bitbuf li $t1, 8 bne $s1, $t1, mroom # if cumulus = 8 then sb $s2, ($s3) # write bitbuf to buf add $s3, $s3, 1 # inc buf. move $s2, $0 # bitbuf := 0 move $s1, $0 # cumulus := 0
mroom: # endif
sll $s2, $s2, 1 # bitbuf <<= 1 andi $s0, $s0, 1 # arg & 1 or $s2, $s2, $s0 # bitbuf |= arg sb $s2, bitbuf # store bitbuf addi $s1, $s1, 1 # cumulus++ sw $s1, cumulus # store cumulus
lw $s0, 0($sp) # restore regs lw $s1, 4($sp) lw $s2, 8($sp) move $v1, $s3 lw $s3, 12($sp) addi $sp, $sp, 16 # restore stack li $v0, 1 jr $ra # return(1)</lang>