Bitwise IO/MIPS Assembly

From Rosetta Code
    .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)