boot2

Playing with the boostrap
git clone https://git.ryansepassi.com/git/boot2.git
Log | Files | Refs | README

hex2.hex1 (39968B)


      1 ## Copyright (C) 2021 Andrius Štikonas
      2 ## This file is part of stage0.
      3 ##
      4 ## stage0 is free software: you can redistribute it and/or modify
      5 ## it under the terms of the GNU General Public License as published by
      6 ## the Free Software Foundation, either version 3 of the License, or
      7 ## (at your option) any later version.
      8 ##
      9 ## stage0 is distributed in the hope that it will be useful,
     10 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12 ## GNU General Public License for more details.
     13 ##
     14 ## You should have received a copy of the GNU General Public License
     15 ## along with stage0.  If not, see <http://www.gnu.org/licenses/>.
     16 
     17 ## ELF Header
     18 
     19 #:ELF_base
     20 7F 45 4C 46        ## e_ident[EI_MAG0-3] ELF's magic number
     21 
     22 01                 ## e_ident[EI_CLASS] Indicating 32 bit
     23 01                 ## e_ident[EI_DATA] Indicating little endianness
     24 01                 ## e_ident[EI_VERSION] Indicating original elf
     25 
     26 03                 ## e_ident[EI_OSABI] Set at 3 because FreeBSD is strict
     27 00                 ## e_ident[EI_ABIVERSION] Set at 0 because none cares
     28 
     29 00 00 00 00 00 00 00 ## e_ident[EI_PAD]
     30 02 00              ## e_type Indicating Executable
     31 F3 00              ## e_machine Indicating RISC-V
     32 01 00 00 00        ## e_version Indicating original elf
     33 
     34 54 00 60 00        ## e_entry Address of the entry point (Number of bytes this header is + Base Address)
     35 34 00 00 00        ## e_phoff Address of program header table
     36 00 00 00 00        ## e_shoff Address of section header table
     37 
     38 00 00 00 00        ## e_flags
     39 34 00              ## e_ehsize Indicating our 52 Byte header
     40 
     41 20 00              ## e_phentsize size of a program header table
     42 01 00              ## e_phnum number of entries in program table
     43 
     44 00 00              ## e_shentsize size of a section header table
     45 00 00              ## e_shnum number of entries in section table
     46 
     47 00 00              ## e_shstrndx index of the section names
     48 
     49 ## Program Header
     50 #:ELF_program_headers
     51 01 00 00 00        ## ph_type: PT-LOAD = 1
     52 00 00 00 00        ## p_offset
     53 
     54 00 00 60 00        ## ph_vaddr
     55 00 00 60 00        ## ph_physaddr
     56 
     57 D0 07 00 00        ## p_filesz
     58 D0 07 00 00        ## p_memsz
     59 
     60 07 00 00 00        ## ph_flags: PF-X|PF-W|PF-R = 7
     61 01 00 00 00        ## ph_align
     62 
     63 #:ELF_text
     64 ; Register use:
     65 ; s1: jump table
     66 ; s2: input fd
     67 ; s3: output fd
     68 ; s4: toggle
     69 ; s5: hold
     70 ; s6: ip
     71 ; s7: tempword
     72 ; s8: shiftregister
     73 ; s9: malloc pointer
     74 ; s10: updates
     75 
     76 ; Struct format: (size 12)
     77 ; next => 0                      ; Next element in linked list
     78 ; target => 4                    ; Target (ip)
     79 ; name => 8                      ; Label name
     80 
     81 ; Our main function
     82 #:_start
     83     03 26 81 00     # rd_a2 rs1_sp !8  lw               ; Input file name
     84 
     85     ; Initialize globals
     86     13 0A F0 FF     # rd_s4 !-1 addi                    ; Toggle
     87     93 0A 00 00     # rd_s5 addi                        ; Hold
     88     37 0B 60 00     # rd_s6 ~0x600000 lui               ; Instruction Pointer
     89 
     90     ; Open input file and store FD in s2
     91     93 08 80 03     # rd_a7 !56 addi                    ; sys_openat
     92     13 05 C0 F9     # rd_a0 !-100 addi                  ; AT_FDCWD
     93     93 05 06 00     # rd_a1 rs1_a2 mv                   ; file name
     94     13 06 00 00     # rd_a2 addi                        ; read only
     95     73 00 00 00     # ecall                             ; syscall
     96     @F 63 40 05 00  # rs1_a0 @Fail bltz                 ; Error opening file
     97     13 09 05 00     # rd_s2 rs1_a0 mv                   ; Save fd in for later
     98 
     99     ; Set default FD for output file to stdout
    100     93 09 10 00     # rd_s3 !1 addi
    101 
    102     ; If we only have 2 arguments, don't use the third (it's not set)
    103     93 02 20 00     # rd_t0 !2 addi
    104     03 25 01 00     # rd_a0 rs1_sp lw                   ; Get number of the args
    105     @F 63 40 55 00  # rs1_a0 rs2_t0 @Fail blt           ; No input file provided
    106     @a 63 00 55 00  # rs1_a0 rs2_t0 @after_open beq     ; No output file provided. Use stdout
    107 
    108     ; Open output file and store the FD in s3
    109     93 08 80 03     # rd_a7 !56 addi                    ; sys_openat
    110     13 05 C0 F9     # rd_a0 !-100 addi                  ; AT_FDCWD
    111     83 25 C1 00     # rd_a1 rs1_sp !12 lw               ; Output file (argument 3)
    112     13 06 10 24     # rd_a2 !00001101 addi              ; decimal 577
    113     ; O_TRUNC   00001000
    114     ; O_CREAT   00000100
    115     ; O_WRONLY  00000001
    116     ; OCTAL!
    117     93 06 00 1C     # rd_a3 !00700 addi                 ; Set read, write, execute permission on user
    118     ; S_IRWXU  00700
    119     ; OCTAL!
    120     73 00 00 00     # ecall                             ; syscall
    121     93 09 05 00     # rd_s3 rs1_a0 mv                   ; Save fd in for later
    122 
    123 :a ;after_open
    124     ; Prepare heap memory
    125     93 08 60 0D     # rd_a7 !214 addi                   ; sys_brk
    126     13 05 00 00     # rd_a0 addi                        ; Get current brk
    127     73 00 00 00     # ecall                             ; syscall
    128     93 0C 05 00     # rd_s9 rs1_a0 addi                 ; Set our malloc pointer
    129 
    130     B7 05 10 00     # rd_a1 ~0x100000 lui
    131     33 05 B5 00     # rd_a0 rs1_a0 rs2_a1 add           ; Request the 1 MiB
    132     93 08 60 0D     # rd_a7 !214 addi                   ; sys_brk
    133     73 00 00 00     # ecall                             ; syscall
    134 
    135     $C EF 00 00 00  # rd_ra $ClearScratch jal           ; Zero scratch
    136     $f EF 00 00 00  # rd_ra $First_pass jal             ; First pass
    137 
    138     ; Rewind input file
    139     93 08 E0 03     # rd_a7 !62 addi                    ; sys_llseek
    140     13 05 09 00     # rd_a0 rs1_s2 mv                   ; Input file descriptor
    141     93 05 00 00     # rd_a1 mv                          ; Set offset to zero
    142     13 06 00 00     # rd_a2 mv                          ; Set offset to zero
    143     93 06 00 00     # rd_a3 mv                          ; Set result pointer to zero
    144     13 07 00 00     # rd_a4 mv                          ; Set whence to zero
    145     73 00 00 00     # ecall                             ; syscall
    146 
    147     ; Initialize globals
    148     13 0A F0 FF     # rd_s4 !-1 addi                    ; Toggle
    149     93 0A 00 00     # rd_s5 addi                        ; Hold
    150     37 0B 60 00     # rd_s6 ~0x600000 lui               ; Instruction Pointer
    151     93 0B 00 00     # rd_s7 addi                        ; tempword
    152     13 0C 00 00     # rd_s8 addi                        ; Shift register
    153 
    154     $X EF 00 00 00  # rd_ra $Second_pass jal            ; Now do the second pass
    155 
    156     ; Terminate program with 0 return code
    157     93 08 D0 05     # rd_a7 !93 addi                    ; sys_exit
    158     13 05 00 00     # rd_a0 mv                          ; Return code 0
    159     73 00 00 00     # ecall                             ; exit(0)
    160 
    161 ; First pass loop to determine addresses of labels
    162 :f ;First_pass
    163     13 01 C1 FF     # rd_sp rs1_sp !-4 addi             ; Allocate stack
    164     23 20 11 00     # rs1_sp rs2_ra sw                  ; protect ra
    165 
    166 :1 ;First_pass_loop
    167     $R EF 00 00 00  # rd_ra $Read_byte jal              ; Get another byte
    168 
    169     ; Deal with EOF
    170     13 03 C0 FF     # rd_t1 !-4 addi
    171     @3 63 00 65 00  # rs1_a0 rs2_t1 @First_pass_done beq
    172 
    173     ; Check for :
    174     13 03 A0 03     # rd_t1 !0x3A addi
    175     @L 63 00 65 00  # rs1_a0 rs2_t1 @StoreLabel beq ; Store this label
    176 
    177     ; Check for .
    178     13 03 E0 02     # rd_t1 !0x2E addi
    179     @w 63 00 65 00  # rs1_a0 rs2_t1 @First_pass_UpdateWord beq
    180 
    181     ; Check for %
    182     13 03 50 02     # rd_t1 !0x25 addi
    183     @p 63 00 65 00  # rs1_a0 rs2_t1 @First_pass_pointer beq
    184 
    185     ; Check for &
    186     13 03 60 02     # rd_t1 !0x26 addi
    187     @p 63 00 65 00  # rs1_a0 rs2_t1 @First_pass_pointer beq
    188 
    189     ; Check for !
    190     13 03 10 02     # rd_t1 !0x21 addi
    191     @T 63 00 65 00  # rs1_a0 rs2_t1 @Throwaway_token beq
    192 
    193     ; Check for @
    194     13 03 00 04     # rd_t1 !0x40 addi
    195     @T 63 00 65 00  # rs1_a0 rs2_t1 @Throwaway_token beq
    196 
    197     ; Check for $
    198     13 03 40 02     # rd_t1 !0x24 addi
    199     @T 63 00 65 00  # rs1_a0 rs2_t1 @Throwaway_token beq
    200 
    201     ; Check for ~
    202     13 03 E0 07     # rd_t1 !0x7E addi
    203     @T 63 00 65 00  # rs1_a0 rs2_t1 @Throwaway_token beq
    204 
    205     ; Check for <
    206     13 03 C0 03     # rd_t1 !0x3C addi
    207     93 05 F0 FF     # rd_a1 !-1 addi                    ; write = false
    208     @A 63 00 65 00  # rs1_a0 rs2_t1 @PadToAlign beq
    209 
    210     93 05 F0 FF     # rd_a1 !-1 addi                    ; write = false
    211     13 06 F0 FF     # rd_a2 !-1 addi                    ; update = false
    212     $D EF 00 00 00  # rd_ra $DoByte jal                 ; Deal with everything else
    213 
    214     13 03 C0 FF     # rd_t1 !-4 addi                    ; Deal with EOF
    215     @3 63 00 65 00  # rs1_a0 rs2_t1 @First_pass_done beq
    216 
    217     $1 6F 00 00 00  # $First_pass_loop jal              ; Keep looping
    218 
    219 :T ;Throwaway_token
    220     ~s 97 05 00 00  # rd_a1 ~scratch auipc
    221     !s 93 85 05 00  # rd_a1 rs1_a1 !scratch addi        ; get scratch
    222     $c EF 00 00 00  # rd_ra $consume_token jal          ; Read token
    223     $C EF 00 00 00  # rd_ra $ClearScratch jal           ; Throw away token
    224     $1 6F 00 00 00  # $First_pass_loop jal              ; Loop again
    225 
    226 :p ;First_pass_pointer
    227     13 0B 4B 00     # rd_s6 rs1_s6 !4 addi              ; Update ip
    228     ; Deal with Pointer to label
    229     ~s 97 05 00 00  # rd_a1 ~scratch auipc
    230     !s 93 85 05 00  # rd_a1 rs1_a1 !scratch addi        ; Using scratch
    231     $c EF 00 00 00  # rd_ra $consume_token jal          ; Read token
    232     $C EF 00 00 00  # rd_ra $ClearScratch jal           ; Throw away token
    233     13 03 E0 03     # rd_t1 !0x3E addi                  ; Check for '>'
    234     @1 63 10 65 00  # rs1_a0 rs2_t1 @First_pass_loop bne ; Loop again
    235 
    236     ; Deal with %label>label case
    237     ~s 97 05 00 00  # rd_a1 ~scratch auipc
    238     !s 93 85 05 00  # rd_a1 rs1_a1 !scratch addi        ; Using scratch
    239     $c EF 00 00 00  # rd_ra $consume_token jal          ; Read token
    240     $C EF 00 00 00  # rd_ra $ClearScratch jal           ; Throw away token
    241     $1 6F 00 00 00  # $First_pass_loop jal ; Loop again
    242 
    243 :w ;First_pass_UpdateWord
    244     13 0D 00 00     # rd_s10 addi                       ; updates = 0
    245     93 0B 00 00     # rd_s7 addi                        ; tempword = 0
    246     93 07 40 00     # rd_a5 !4 addi                     ; a5 = 4
    247 :4 ;First_pass_UpdateWord_loop
    248     $R EF 00 00 00  # rd_ra $Read_byte jal              ; Read another byte into a0
    249 
    250     93 05 F0 FF     # rd_a1 !-1 addi                    ; write = false
    251     13 06 00 00     # rd_a2 addi                        ; update = true
    252     $D EF 00 00 00  # rd_ra $DoByte jal                 ; Process byte
    253     @4 63 40 FD 00  # rs1_s10 rs2_a5 @First_pass_UpdateWord_loop blt ; loop 4 times
    254 
    255     13 0B CB FF     # rd_s6 rs1_s6 !-4 addi             ; ip = ip - 4
    256 
    257     $1 6F 00 00 00  # $First_pass_loop jal              ; Loop again
    258 
    259 :3 ;First_pass_done
    260     83 20 01 00     # rd_ra rs1_sp lw                   ; restore ra
    261     13 01 41 00     # rd_sp rs1_sp !4 addi              ; deallocate stack
    262     67 80 00 00     # rs1_ra jalr                       ; return
    263 
    264 :X ;Second_pass
    265     13 01 C1 FF     # rd_sp rs1_sp !-4 addi             ; Allocate stack
    266     23 20 11 00     # rs1_sp rs2_ra sw                  ; protect ra
    267 
    268 :5 ;Second_pass_loop
    269     $R EF 00 00 00  # rd_ra $Read_byte jal              ; Read another byte
    270 
    271     ; Deal with EOF
    272     13 03 C0 FF     # rd_t1 !-4 addi                    ; Deal with EOF
    273     @6 63 00 65 00  # rs1_a0 rs2_t1 @Second_pass_done beq
    274 
    275     ; Drop the label
    276     13 03 A0 03     # rd_t1 !0x3A addi
    277     @7 63 10 65 00  # rs1_a0 rs2_t1 @Second_pass_0 bne
    278 
    279     ~s 97 05 00 00  # rd_a1 ~scratch auipc
    280     !s 93 85 05 00  # rd_a1 rs1_a1 !scratch addi        ; Using scratch
    281     $c EF 00 00 00  # rd_ra $consume_token jal          ; Read the label
    282     $C EF 00 00 00  # rd_ra $ClearScratch jal           ; Throw away token
    283 
    284     $5 6F 00 00 00  # $Second_pass_loop jal             ; Continue looping
    285 
    286 :7 ;Second_pass_0
    287     ; Check for .
    288     13 03 E0 02     # rd_t1 !0x2E addi
    289     @8 63 00 65 00  # rs1_a0 rs2_t1 @Second_pass_UpdateWord beq
    290 
    291     ; Check for %
    292     13 03 50 02     # rd_t1 !0x25 addi
    293     @S 63 00 65 00  # rs1_a0 rs2_t1 @StorePointer beq
    294 
    295     ; Check for &
    296     13 03 60 02     # rd_t1 !0x26 addi
    297     @S 63 00 65 00  # rs1_a0 rs2_t1 @StorePointer beq
    298 
    299     ; Check for !
    300     13 03 10 02     # rd_t1 !0x21 addi
    301     @Y 63 00 65 00  # rs1_a0 rs2_t1 @UpdateShiftRegister beq
    302 
    303     ; Check for @
    304     13 03 00 04     # rd_t1 !0x40 addi
    305     @Y 63 00 65 00  # rs1_a0 rs2_t1 @UpdateShiftRegister beq
    306 
    307     ; Check for $
    308     13 03 40 02     # rd_t1 !0x24 addi
    309     @Y 63 00 65 00  # rs1_a0 rs2_t1 @UpdateShiftRegister beq
    310 
    311     ; Check for ~
    312     13 03 E0 07     # rd_t1 !0x7E addi
    313     @Y 63 00 65 00  # rs1_a0 rs2_t1 @UpdateShiftRegister beq
    314 
    315     ; Check for <
    316     13 03 C0 03     # rd_t1 !0x3C addi
    317     93 05 00 00     # rd_a1 addi                        ; write = true
    318     @A 63 00 65 00  # rs1_a0 rs2_t1 @PadToAlign beq
    319 
    320     ; Deal with everything else
    321     93 05 00 00     # rd_a1 addi                        ; write = true
    322     13 06 F0 FF     # rd_a2 !-1 addi                    ; update = false
    323     $D EF 00 00 00  # rd_ra $DoByte jal                 ; Process our char
    324 
    325     # Deal with EOF
    326     13 03 C0 FF     # rd_t1 !-4 addi
    327     @6 63 00 65 00  # rs1_a0 rs2_t1 @Second_pass_done beq ; We are done
    328 
    329     $5 6F 00 00 00  # $Second_pass_loop jal ; continue looping
    330 
    331 :8 ;Second_pass_UpdateWord
    332     13 0D 00 00     # rd_s10 addi                       ; updates = 0
    333     93 0B 00 00     # rd_s7 addi                        ; tempword = 0
    334     93 07 40 00     # rd_a5 !4 addi                     ; a5 = 4
    335 
    336 :9 ;Second_pass_UpdateWord_loop
    337     $R EF 00 00 00  # rd_ra $Read_byte jal       ; Read another byte into a0
    338 
    339     93 05 F0 FF     # rd_a1 !-1 addi                    ; write = false
    340     13 06 00 00     # rd_a2 addi                        ; update = true
    341     $D EF 00 00 00  # rd_ra $DoByte jal             ; Process our char
    342     @9 63 40 FD 00  # rs1_s10 rs2_a5 @Second_pass_UpdateWord_loop blt ; loop 4 times
    343 
    344     13 85 0B 00     # rd_a0 rs1_s7 mv                   ; tempword
    345     $d 6F 00 00 00  # $UpdateShiftRegister_DOT jal ; UpdateShiftRegister('.', tempword)
    346 
    347 :Y ;UpdateShiftRegister
    348     13 06 05 00     # rd_a2 rs1_a0 mv                   ; Store label prefix
    349     ~s 97 05 00 00  # rd_a1 ~scratch auipc
    350     !s 93 85 05 00  # rd_a1 rs1_a1 !scratch addi        ; Get scratch
    351     $C EF 00 00 00  # rd_ra $ClearScratch jal           ; Clear scratch
    352     $c EF 00 00 00  # rd_ra $consume_token jal          ; Read token
    353     $G EF 00 00 00  # rd_ra $GetTarget jal              ; Get target
    354     03 25 05 00     # rd_a0 rs1_a0 lw                   ; Dereference pointer
    355     33 05 65 41     # rd_a0 rs1_a0 rs2_s6 sub           ; target - ip
    356 
    357     ; Check for !
    358     13 03 10 02     # rd_t1 !0x21 addi
    359     @I 63 00 66 00  # rs1_a2 rs2_t1 @UpdateShiftRegister_I beq
    360 
    361     ; Check for @
    362     13 03 00 04     # rd_t1 !0x40 addi
    363     @B 63 00 66 00  # rs1_a2 rs2_t1 @UpdateShiftRegister_B beq
    364 
    365     ; Check for $
    366     13 03 40 02     # rd_t1 !0x24 addi
    367     @J 63 00 66 00  # rs1_a2 rs2_t1 @UpdateShiftRegister_J beq
    368 
    369     ; Check for ~
    370     13 03 E0 07     # rd_t1 !0x7E addi
    371     @U 63 00 66 00  # rs1_a2 rs2_t1 @UpdateShiftRegister_U beq
    372 
    373     $5 6F 00 00 00  # $Second_pass_loop jal ; continue looping
    374 
    375 :d ;UpdateShiftRegister_DOT
    376     ; . before instruction means it has to be added to the final word
    377 
    378     ; swap = (((value >> 24) & 0xff) |
    379     ;        ((value << 8) & 0xff0000) |
    380     ;        ((value >> 8) & 0xff00) |
    381     ;        ((value << 24) & 0xff000000))
    382 
    383     93 53 85 01     # rd_t2 rs1_a0 rs2_x24 srli         ; value >> 24
    384     13 03 F0 0F     # rd_t1 !0xFF addi                  ; t1 = 0xff
    385     B3 72 73 00     # rd_t0 rs1_t1 rs2_t2 and           ; (value >> 24) & 0xff
    386 
    387     93 13 85 00     # rd_t2 rs1_a0 rs2_x8 slli          ; value << 8
    388     37 03 FF 00     # rd_t1 ~0xFF0000 lui               ; t1 = 0xff0000
    389     B3 73 73 00     # rd_t2 rs1_t1 rs2_t2 and           ; (value << 8) & 0xff0000
    390     B3 E2 72 00     # rd_t0 rs1_t0 rs2_t2 or            ; logical or with the previous expression
    391 
    392     93 53 85 00     # rd_t2 rs1_a0 rs2_x8 srli          ; value >> 8
    393     37 03 01 00     # rd_t1 ~0xFF00 lui                 ; t1 = 0xff00
    394     13 03 03 F0     # rd_t1 rs1_t1 !0xFF00 addi         ; t1 = 0xff00
    395     B3 73 73 00     # rd_t2 rs1_t1 rs2_t2 and           ; (value << 8) & 0xff00
    396     B3 E2 72 00     # rd_t0 rs1_t0 rs2_t2 or            ; logical or with the previous expression
    397 
    398     93 13 85 01     # rd_t2 rs1_a0 rs2_x24 slli         ; value << 24
    399     13 03 F0 0F     # rd_t1 !0xFF addi
    400     13 13 83 01     # rd_t1 rs1_t1 rs2_x24 slli         ; t1 = 0xff000000
    401     B3 73 73 00     # rd_t2 rs1_t1 rs2_t2 and           ; (value << 24) & 0xff000000
    402     B3 E2 72 00     # rd_t0 rs1_t0 rs2_t2 or            ; swap
    403 
    404     33 4C 5C 00     # rd_s8 rs1_s8 rs2_t0 xor           ; shiftregister = shiftregister ^ swap
    405 
    406     13 0B CB FF     # rd_s6 rs1_s6 !-4 addi             ; ip = ip - 4
    407     $5 6F 00 00 00  # $Second_pass_loop jal             ; continue looping
    408 
    409 :I ;UpdateShiftRegister_I
    410     ; Corresponds to RISC-V I format
    411     13 05 45 00     # rd_a0 rs1_a0 !4 addi              ; add 4 due to this being 2nd part of auipc combo
    412 
    413     37 13 00 00     # rd_t1 ~0xFFF lui                  ; load higher bits
    414     13 03 F3 FF     # rd_t1 rs1_t1 !0xFFF addi
    415     33 73 65 00     # rd_t1 rs1_a0 rs2_t1 and           ; (value & 0xfff)
    416     93 1B 43 01     # rd_s7 rs1_t1 rs2_x20 slli         ; tempword = (value & 0xfff) << 20
    417     33 4C 7C 01     # rd_s8 rs1_s8 rs2_s7 xor           ; shiftregister = shiftregister ^ tempword
    418 
    419     $5 6F 00 00 00  # $Second_pass_loop jal             ; continue looping
    420 
    421 :B ;UpdateShiftRegister_B
    422     ; Corresponds to RISC-V B format
    423 
    424     ; tempword = ((value & 0x1e) << 7)            ; imm[4:1]
    425     ;          | ((value & 0x7e0) << (31 - 11))   ; imm[10:5]
    426     ;          | ((value & 0x800) >> 4)           ; imm[11]
    427     ;          | ((value & 0x1000) << (31 - 12))  ; imm[12]
    428 
    429     13 03 E0 01     # rd_t1 !0x1E addi
    430     33 73 65 00     # rd_t1 rs1_a0 rs2_t1 and           ; value & 0x1e
    431     93 12 73 00     # rd_t0 rs1_t1 rs2_x7 slli          ; tempword = (value & 0x1e) << 7
    432 
    433     13 03 00 7E     # rd_t1 !0x7E0 addi
    434     33 73 65 00     # rd_t1 rs1_a0 rs2_t1 and           ; value & 0x7e0
    435     13 13 43 01     # rd_t1 rs1_t1 rs2_x20 slli         ; (value & 0x7e0) << (31 - 11)
    436     B3 E2 62 00     # rd_t0 rs1_t0 rs2_t1 or            ; logical or with the previous expression
    437 
    438     37 13 00 00     # rd_t1 ~0x800 lui                  ; load higher bits
    439     13 03 03 80     # rd_t1 rs1_t1 !0x800 addi
    440     33 73 65 00     # rd_t1 rs1_a0 rs2_t1 and           ; value & 0x800
    441     13 53 43 00     # rd_t1 rs1_t1 rs2_x4 srli          ; (value & 0x800) >> 4
    442     B3 E2 62 00     # rd_t0 rs1_t0 rs2_t1 or            ; logical or with the previous expression
    443 
    444     37 13 00 00     # rd_t1 ~0x1000 lui                 ; load higher bits
    445     33 73 65 00     # rd_t1 rs1_a0 rs2_t1 and           ; value & 0x1000
    446     13 13 33 01     # rd_t1 rs1_t1 rs2_x19 slli         ; (value & 0x1000) << (31 - 12)
    447     B3 EB 62 00     # rd_s7 rs1_t0 rs2_t1 or            ; logical or with the previous expression
    448 
    449     33 4C 7C 01     # rd_s8 rs1_s8 rs2_s7 xor           ; shiftregister = shiftregister ^ tempword
    450 
    451     $5 6F 00 00 00  # $Second_pass_loop jal             ; continue looping
    452 
    453 :J ;UpdateShiftRegister_J
    454     ; Corresponds to RISC-V J format
    455 
    456     ; tempword = ((value & 0x7fe) << (30 - 10))    ; imm[10:1]
    457     ;          | ((value & 0x800) << (20 - 11))    ; imm[11]
    458     ;          | ((value & 0xff000))               ; imm[19:12]
    459     ;          | ((value & 0x100000) << (31 - 20)) ; imm[20]
    460 
    461     13 03 E0 7F     # rd_t1 !0x7FE addi
    462     33 73 65 00     # rd_t1 rs1_a0 rs2_t1 and           ; value & 0x7fe
    463     93 12 43 01     # rd_t0 rs1_t1 rs2_x20 slli         ; tempword = (value & 0x7fe) << 20
    464 
    465     37 13 00 00     # rd_t1 ~0x800 lui                  ; load higher bits
    466     13 03 03 80     # rd_t1 rs1_t1 !0x800 addi
    467     33 73 65 00     # rd_t1 rs1_a0 rs2_t1 and           ; value & 0x800
    468     13 13 93 00     # rd_t1 rs1_t1 rs2_x9 slli          ; (value & 0x800) << (20 - 11)
    469     B3 E2 62 00     # rd_t0 rs1_t0 rs2_t1 or            ; logical or with the previous expression
    470 
    471     37 F3 0F 00     # rd_t1 ~0xFF000 lui                ; load higher bits
    472     33 73 65 00     # rd_t1 rs1_a0 rs2_t1 and           ; value & 0xff000
    473     B3 E2 62 00     # rd_t0 rs1_t0 rs2_t1 or            ; logical or with the previous expression
    474 
    475     37 03 10 00     # rd_t1 ~0x100000 lui               ; load higher bits
    476     33 73 65 00     # rd_t1 rs1_a0 rs2_t1 and           ; value & 0x100000
    477     13 13 B3 00     # rd_t1 rs1_t1 rs2_x11 slli         ; (value & 0x100000) << (31 - 20)
    478     B3 EB 62 00     # rd_s7 rs1_t0 rs2_t1 or            ; logical or with the previous expression
    479 
    480     33 4C 7C 01     # rd_s8 rs1_s8 rs2_s7 xor           ; shiftregister = shiftregister ^ tempword
    481 
    482     $5 6F 00 00 00  # $Second_pass_loop jal             ; continue looping
    483 
    484 :U ;UpdateShiftRegister_U
    485     ; Corresponds to RISC-V U format
    486     ; if value is 0x800 or more we have to add 11-th bit (0x1000) to compensate for signed extension
    487 
    488     B7 12 00 00     # rd_t0 ~0x800 lui                  ; load higher bits
    489     93 82 02 80     # rd_t0 rs1_t0 !0x800 addi
    490     37 13 00 00     # rd_t1 ~0xFFF lui                  ; load higher bits
    491     13 03 F3 FF     # rd_t1 rs1_t1 !0xFFF addi
    492 
    493     ; We are outside 31-bit that ~ can normally load
    494     B7 03 10 00     # rd_t2 ~0x100000 lui               ; load 0xfffff000
    495     93 83 F3 FF     # rd_t2 rs1_t2 !-1 addi             ; load 0xfffff000
    496     93 93 C3 00     # rd_t2 rs1_t2 rs2_x12 slli         ; load 0xfffff000
    497     33 73 65 00     # rd_t1 rs1_a0 rs2_t1 and           ; value & 0xfff
    498     B3 7B 75 00     # rd_s7 rs1_a0 rs2_t2 and           ; value & 0xfffff000
    499     @u 63 40 53 00  # rs1_t1 rs2_t0 @UpdateShiftRegister_U_small blt
    500 
    501     # Deal with sign extension: add 0x1000
    502     B7 12 00 00     # rd_t0 ~0x1000 lui                 ; load higher bits
    503     B3 8B 72 01     # rd_s7 rs1_t0 rs2_s7 add           ; (value & 0xfffff000) + 0x1000
    504 
    505 :u ;UpdateShiftRegister_U_small
    506     33 4C 7C 01     # rd_s8 rs1_s8 rs2_s7 xor           ; shiftregister = shiftregister ^ tempword
    507 
    508     $5 6F 00 00 00  # $Second_pass_loop jal             ; continue looping
    509 
    510 :S ;StorePointer
    511     13 0B 4B 00     # rd_s6 rs1_s6 !4 addi              ; update ip
    512     13 06 05 00     # rd_a2 rs1_a0 mv                   ; Store label prefix
    513 
    514     ~s 97 05 00 00  # rd_a1 ~scratch auipc
    515     !s 93 85 05 00  # rd_a1 rs1_a1 !scratch addi        ; Get scratch
    516     $C EF 00 00 00  # rd_ra $ClearScratch jal           ; clear scratch
    517     $c EF 00 00 00  # rd_ra $consume_token jal          ; Read token
    518     93 07 05 00     # rd_a5 rs1_a0 mv                   ; save char
    519     $G EF 00 00 00  # rd_ra $GetTarget jal              ; Get target
    520     83 25 05 00     # rd_a1 rs1_a0 lw                   ; Dereference pointer
    521 
    522     ; If char is > then change relative base address to ip
    523     13 03 E0 03     # rd_t1 !0x3E addi                  ; t1 = 0x3e
    524     @P 63 00 F3 00  # rs1_t1 rs2_a5 @StorePointer_1 beq
    525 
    526     ; Check for &
    527     13 03 60 02     # rd_t1 !0x26 addi
    528     @0 63 00 66 00  # rs1_a2 rs2_t1 @StorePointer_0 beq
    529 
    530     ; Check for %
    531     13 03 50 02     # rd_t1 !0x25 addi
    532     @F 63 10 66 00  # rs1_a2 rs2_t1 @Fail bne
    533     B3 85 65 41     # rd_a1 rs1_a1 rs2_s6 sub           ; displacement = target - ip
    534 
    535 :0 ;StorePointer_0
    536     ; Output pointer
    537     93 07 40 00     # rd_a5 !4 addi                     ; number of bytes
    538 :l ;StorePointer_loop
    539     13 D3 85 00     # rd_t1 rs1_a1 rs2_x8 srli          ; value / 256
    540     13 15 83 00     # rd_a0 rs1_t1 rs2_x8 slli
    541     33 85 A5 40     # rd_a0 rs1_a1 rs2_a0 sub           ; byte = value % 256
    542 
    543     93 05 03 00     # rd_a1 rs1_t1 mv                   ; value = value / 256
    544     $t EF 00 00 00  # rd_ra $fputc jal                  ; write value
    545     93 87 F7 FF     # rd_a5 rs1_a5 !-1 addi             ; decrease number of bytes to write
    546     @l 63 90 07 00  # rs1_a5 @StorePointer_loop bnez    ; continue looping
    547 
    548     $5 6F 00 00 00  # $Second_pass_loop jal             ; Continue looping
    549 
    550 :P ;StorePointer_1
    551     13 86 05 00     # rd_a2 rs1_a1 mv                   ; save target
    552     ~s 97 05 00 00  # rd_a1 ~scratch auipc
    553     !s 93 85 05 00  # rd_a1 rs1_a1 !scratch addi        ; Get scratch
    554     $C EF 00 00 00  # rd_ra $ClearScratch jal           ; clear scratch
    555     $c EF 00 00 00  # rd_ra $consume_token jal          ; consume token
    556     $G EF 00 00 00  # rd_ra $GetTarget jal              ; Get target
    557     83 25 05 00     # rd_a1 rs1_a0 lw                   ; Dereference pointer
    558     B3 05 B6 40     # rd_a1 rs1_a2 rs2_a1 sub           ; displacement = target - ip
    559 
    560     $0 6F 00 00 00  # $StorePointer_0 jal               ; Continue looping
    561 
    562 :6 ;Second_pass_done
    563     83 20 01 00     # rd_ra rs1_sp lw                   ; restore ra
    564     13 01 41 00     # rd_sp rs1_sp !4 addi              ; deallocate stack
    565     67 80 00 00     # rs1_ra jalr                       ; return
    566 
    567 ; Pad with zeros to align to word size
    568 ;   bool write in a1
    569 :A ;PadToAlign
    570     13 03 10 00     # rd_t1 !1 addi                     ; t1 = 1
    571     33 75 6B 00     # rd_a0 rs1_s6 rs2_t1 and           ; ip & 0x1
    572     @b 63 10 65 00  # rs1_a0 rs2_t1 @PadToAlign_1 bne   ; check if ip & 0x1 == 1
    573     33 0B 6B 00     # rd_s6 rs1_s6 rs2_t1 add           ; ip = ip + 1
    574 
    575     @b 63 90 05 00  # rs1_a1 @PadToAlign_1 bnez         ; check if we have to write
    576     13 05 00 00     # rd_a0 mv                          ; a0 = 0
    577     $t EF 00 00 00  # rd_ra $fputc jal                  ; write 0
    578 
    579 :b ;PadToAlign_1
    580     13 03 20 00     # rd_t1 !2 addi                     ; t1 = 2
    581     33 75 6B 00     # rd_a0 rs1_s6 rs2_t1 and           ; ip & 0x1
    582     @e 63 10 65 00  # rs1_a0 rs2_t1 @PadToAlign_2 bne   ; check if ip & 0x2 == 2
    583     33 0B 6B 00     # rd_s6 rs1_s6 rs2_t1 add           ; ip = ip + 2
    584 
    585     @e 63 90 05 00  # rs1_a1 @PadToAlign_2 bnez         ; check if we have to write
    586     13 05 00 00     # rd_a0 mv                          ; a0 = 0
    587     $t EF 00 00 00  # rd_ra $fputc jal                  ; write 0
    588     13 05 00 00     # rd_a0 mv                          ; a0 = 0
    589     $t EF 00 00 00  # rd_ra $fputc jal                  ; write 0
    590 
    591 :e ;PadToAlign_2
    592     @5 63 80 05 00  # rs1_a1 @Second_pass_loop beqz     ; return to Second_pass
    593     $1 6F 00 00 00  # $First_pass_loop jal              ; return to First_pass
    594 
    595 ; Zero scratch area
    596 :C ;ClearScratch
    597     13 01 41 FF     # rd_sp rs1_sp !-12 addi            ; Allocate stack
    598     23 20 11 00     # rs1_sp rs2_ra SD                  ; protect ra
    599     23 22 A1 00     # rs1_sp rs2_a0 @4 SD               ; protect a0
    600     23 24 B1 00     # rs1_sp rs2_a1 @8 SD               ; protect a1
    601 
    602     ~s 17 05 00 00  # rd_a0 ~scratch auipc
    603     !s 13 05 05 00  # rd_a0 rs1_a0 !scratch addi        ; Find where our scratch area is
    604 
    605 :g ;ClearScratch_loop
    606     83 05 05 00     # rd_a1 rs1_a0 lb                   ; Read current byte: s[i]
    607     23 00 05 00     # rs1_a0 sb                         ; Write zero: s[i] = 0
    608     13 05 15 00     # rd_a0 rs1_a0 !1 addi              ; Increment: i = i + 1
    609     @g 63 90 05 00  # rs1_a1 @ClearScratch_loop bnez    ; Keep looping
    610 
    611     83 20 01 00     # rd_ra rs1_sp lw                   ; restore ra
    612     03 25 41 00     # rd_a0 rs1_sp !4 lw                ; restore a0
    613     83 25 81 00     # rd_a1 rs1_sp !8 lw                ; restore a1
    614     13 01 C1 00     # rd_sp rs1_sp !12 addi             ; Deallocate stack
    615     67 80 00 00     # rs1_ra jalr                       ; return
    616 
    617 ; Receives pointer in a1
    618 ; Writes our token and updates pointer in a1
    619 :c ;consume_token
    620     13 01 C1 FF     # rd_sp rs1_sp !-4 addi             ; Allocate stack
    621     23 20 11 00     # rs1_sp rs2_ra sw                  ; protect ra
    622 
    623 :h ;consume_token_0
    624     $R EF 00 00 00  # rd_ra $Read_byte jal              ; Read byte into a0
    625 
    626     ; Check for \t
    627     13 03 90 00     # rd_t1 !0x09 addi
    628     @j 63 00 65 00  # rs1_a0 rs2_t1 @consume_token_done beq
    629 
    630     ; Check for \n
    631     13 03 A0 00     # rd_t1 !0x0A addi
    632     @j 63 00 65 00  # rs1_a0 rs2_t1 @consume_token_done beq
    633 
    634     ; Check for ' '
    635     13 03 00 02     # rd_t1 !0x20 addi
    636     @j 63 00 65 00  # rs1_a0 rs2_t1 @consume_token_done beq
    637 
    638     ; Check for >
    639     13 03 E0 03     # rd_t1 !0x3E addi
    640     @j 63 00 65 00  # rs1_a0 rs2_t1 @consume_token_done beq
    641 
    642     23 80 A5 00     # rs1_a1 rs2_a0 sb                  ; Store char
    643     93 85 15 00     # rd_a1 rs1_a1 !1 addi              ; Point to next spot
    644     $h 6F 00 00 00  # $consume_token_0 jal ; Continue looping
    645 
    646 :j ;consume_token_done
    647     23 A0 05 00     # rs1_a1 sw                         ; Pad with nulls
    648     93 85 45 00     # rd_a1 rs1_a1 !4 addi              ; Update the pointer
    649 
    650     83 20 01 00     # rd_ra rs1_sp lw                   ; restore ra
    651     13 01 41 00     # rd_sp rs1_sp !4 addi              ; deallocate stack
    652     67 80 00 00     # rs1_ra jalr                       ; return
    653 
    654 ; DoByte function
    655 ; Receives:
    656 ;   character in a0
    657 ;   bool write in a1
    658 ;   bool update in a2
    659 ; Does not return anything
    660 :D ;DoByte
    661     13 01 81 FF     # rd_sp rs1_sp !-8 addi             ; Allocate stack
    662     23 20 11 00     # rs1_sp rs2_ra sw                  ; protect ra
    663     23 22 01 01     # rs1_sp rs2_a6 @4 sw               ; protect a6
    664 
    665     $H EF 00 00 00  # rd_ra $hex jal                    ; Process hex, store it in a6
    666 
    667     @k 63 40 08 00  # rs1_a6 @DoByte_Done bltz          ; Deal with EOF and unrecognized characters
    668 
    669     @2 63 10 0A 00  # rs1_s4 @DoByte_NotToggle bnez     ; Check if toggle is set
    670 
    671     ; toggle = true
    672     @m 63 90 05 00  # rs1_a1 @DoByte_1 bnez             ; check if we have to write
    673 
    674     ; write = true
    675     ; We calculate (hold * 16) + hex(c) ^ sr_nextb()
    676     ; First, calculate new shiftregister
    677     93 02 F0 0F     # rd_t0 !0xFF addi
    678     B3 72 5C 00     # rd_t0 rs1_s8 rs2_t0 and           ; sr_nextb = shiftregister & 0xff
    679     13 5C 8C 00     # rd_s8 rs1_s8 rs2_x8 srli          ; shiftregister >> 8
    680 
    681     B3 C2 02 01     # rd_t0 rs1_t0 rs2_a6 xor           ; hex(c) ^ sr_nextb
    682     13 93 4A 00     # rd_t1 rs1_s5 rs2_x4 slli          ; hold << 4
    683     33 85 62 00     # rd_a0 rs1_t0 rs2_t1 add           ; (hold << 4) + hex(c) ^ sr_nextb()
    684     $t EF 00 00 00  # rd_ra $fputc jal                  ; print it
    685     @F 63 00 05 00  # rs1_a0 @Fail beqz                 ; Fail if nothing was written
    686 
    687 :m ;DoByte_1
    688     13 0B 1B 00     # rd_s6 rs1_s6 !1 addi              ; Increment IP
    689     @o 63 00 06 00  # rs1_a2 @DoByte_2 beqz             ; check if we have to update
    690 :n ;DoByte_2b
    691     93 0A 00 00     # rd_s5 mv                          ; hold = 0
    692     $q 6F 00 00 00  # $DoByte_FlipToggle jal            ; return
    693 
    694 :2 ;DoByte_NotToggle
    695     93 0A 08 00     # rd_s5 rs1_a6 mv                   ; hold = hex(c)
    696 
    697 :q ;DoByte_FlipToggle
    698     13 4A FA FF     # rd_s4 rs1_s4 not                  ; Flip the toggle
    699 
    700 :k ;DoByte_Done
    701     83 20 01 00     # rd_ra rs1_sp lw                   ; restore ra
    702     03 28 41 00     # rd_a6 rs1_sp !4 lw                ; restore a6
    703     13 01 81 00     # rd_sp rs1_sp !8 addi              ; Deallocate stack
    704     67 80 00 00     # rs1_ra jalr                       ; return
    705 
    706 :o ;DoByte_2
    707     13 93 4A 00     # rd_t1 rs1_s5 rs2_x4 slli          ; hold * 16
    708     B3 0A 03 01     # rd_s5 rs1_t1 rs2_a6 add           ; hold = hold * 16 + hex(c)
    709     13 93 8B 00     # rd_t1 rs1_s7 rs2_x8 slli          ; tempword << 8
    710     B3 4B 53 01     # rd_s7 rs1_t1 rs2_s5 xor           ; tempword = (tempword << 8) ^ hold
    711     13 0D 1D 00     # rd_s10 rs1_s10 !1 addi            ; updates = updates + 1
    712     $n 6F 00 00 00  # $DoByte_2b jal
    713 
    714 ; Convert ASCII hex characters into binary representation, e.g. 'a' -> 0xA
    715 ; Receives:
    716 ;   character in a0
    717 ; Returns:
    718 ;   a6 with character's hex value.
    719 :H ;hex
    720     13 01 81 FF     # rd_sp rs1_sp !-8 addi             ; Allocate stack
    721     23 20 11 00     # rs1_sp rs2_ra sw                  ; protect ra
    722     23 22 B1 00     # rs1_sp rs2_a1 @4 sw               ; protect a1
    723 
    724     ; Deal with EOF
    725     13 03 C0 FF     # rd_t1 !-4 addi
    726     @r 63 00 65 00  # rs1_a0 rs2_t1 @hex_return beq
    727 
    728     ; deal with line comments starting with #
    729     13 03 30 02     # rd_t1 !0x23 addi
    730     @x 63 00 65 00  # rs1_a0 rs2_t1 @ascii_comment beq  ; a0 eq to '#'
    731 
    732     ; deal with line comments starting with ;
    733     13 03 B0 03     # rd_t1 !0x3B addi
    734     @x 63 00 65 00  # rs1_a0 rs2_t1 @ascii_comment beq  ; a0 eq to ';'
    735 
    736     ; deal all ascii less than 0
    737     13 03 00 03     # rd_t1 !0x30 addi
    738     @y 63 40 65 00  # rs1_a0 rs2_t1 @ascii_other blt
    739 
    740     ; deal with 0-9
    741     13 03 A0 03     # rd_t1 !0x3A addi
    742     @N 63 40 65 00  # rs1_a0 rs2_t1 @ascii_num blt
    743 
    744     ; deal with all ascii less than A
    745     13 03 10 04     # rd_t1 !0x41 addi
    746     @y 63 40 65 00  # rs1_a0 rs2_t1 @ascii_other blt
    747 
    748     ; deal with A-F
    749     13 03 70 04     # rd_t1 !0x47 addi
    750     @z 63 40 65 00  # rs1_a0 rs2_t1 @ascii_high blt
    751 
    752     ; deal with all ascii less than a
    753     13 03 10 06     # rd_t1 !0x61 addi
    754     @y 63 40 65 00  # rs1_a0 rs2_t1 @ascii_other blt
    755 
    756     ; deal with a-f
    757     13 03 70 06     # rd_t1 !0x67 addi
    758     @Z 63 40 65 00  # rs1_a0 rs2_t1 @ascii_low blt
    759 
    760     ; The rest that remains needs to be ignored
    761     $y 6F 00 00 00  # $ascii_other jal
    762 
    763 :N ;ascii_num
    764     13 03 00 03     # rd_t1 !0x30 addi                  ; '0' -> 0
    765     33 08 65 40     # rd_a6 rs1_a0 rs2_t1 sub
    766     $r 6F 00 00 00  # $hex_return jal                   ; return
    767 :Z ;ascii_low
    768     13 03 70 05     # rd_t1 !0x57 addi                  ; 'a' -> 0xA
    769     33 08 65 40     # rd_a6 rs1_a0 rs2_t1 sub
    770     $r 6F 00 00 00  # $hex_return jal                   ; return
    771 :z ;ascii_high
    772     13 03 70 03     # rd_t1 !0x37 addi                  ; 'A' -> 0xA
    773     33 08 65 40     # rd_a6 rs1_a0 rs2_t1 sub
    774     $r 6F 00 00 00 # $hex_return jal                    ; return
    775 :y ;ascii_other
    776     13 08 F0 FF     # rd_a6 !-1 addi                    ; Return -1
    777     $r 6F 00 00 00  # $hex_return jal                   ; return
    778 :x ;ascii_comment                        ; Read the comment until newline
    779     $R EF 00 00 00  # rd_ra $Read_byte jal
    780     13 03 D0 00     # rd_t1 !0xD addi                   ; CR
    781     @E 63 00 65 00  # rs1_a0 rs2_t1 @ascii_comment_cr beq
    782     13 03 A0 00     # rd_t1 !0xA addi                   ; LF
    783     @x 63 10 65 00  # rs1_a0 rs2_t1 @ascii_comment bne  ; Keep reading comment
    784 :E ;ascii_comment_cr
    785     13 08 F0 FF     # rd_a6 !-1 addi                    ; Return -1
    786 :r ;hex_return
    787     83 20 01 00     # rd_ra rs1_sp lw                   ; restore ra
    788     83 25 41 00     # rd_a1 rs1_sp !4 lw                ; restore a1
    789     13 01 81 00     # rd_sp rs1_sp !8 addi              ; Deallocate stack
    790     67 80 00 00     # rs1_ra jalr                       ; return
    791 
    792 ; Read byte into a0
    793 :R ;Read_byte
    794     13 01 41 FF     # rd_sp rs1_sp !-12 addi            ; Allocate stack
    795     23 22 B1 00     # rs1_sp rs2_a1 @4 sw               ; protect a1
    796     23 24 C1 00     # rs1_sp rs2_a2 @8 sw               ; protect a2
    797 
    798     93 08 F0 03     # rd_a7 !63 addi                    ; sys_read
    799     13 05 09 00     # rd_a0 rs1_s2 mv                   ; File descriptor
    800     93 05 01 00     # rd_a1 rs1_sp mv                   ; Get stack address for buffer
    801     13 06 10 00     # rd_a2 !1 addi                     ; Size of what we want to read
    802     73 00 00 00     # ecall                             ; syscall
    803 
    804     @K 63 00 05 00  # rs1_a0 @Read_byte_1 beqz          ; Deal with EOF
    805     03 C5 05 00     # rd_a0 rs1_a1 lbu                  ; return char in a0
    806 
    807     $M 6F 00 00 00  # $Read_byte_done jal               ; return
    808 
    809 :K ;Read_byte_1
    810     13 05 C0 FF     # rd_a0 !-4 addi                    ; Put EOF in a0
    811 :M ;Read_byte_done
    812     83 25 41 00     # rd_a1 rs1_sp !4 lw                ; restore a1
    813     03 26 81 00     # rd_a2 rs1_sp !8 lw                ; restore a2
    814     13 01 C1 00     # rd_sp rs1_sp !12 addi             ; Deallocate stack
    815     67 80 00 00     # rs1_ra jalr                       ; return
    816 
    817 ; Find a label matching pointer in scratch
    818 ; Returns a pointer in a0
    819 :G ;GetTarget
    820     13 01 C1 FF     # rd_sp rs1_sp !-4 addi             ; Allocate stack
    821     23 20 11 00     # rs1_sp rs2_ra sw                  ; protect ra
    822 
    823     93 82 04 00     # rd_t0 rs1_s1 mv                   ; grab jump_table
    824 
    825 :O ;GetTarget_loop_0
    826     ; Compare the strings
    827     ~s 17 03 00 00  # rd_t1 ~scratch auipc
    828     !s 13 03 03 00  # rd_t1 rs1_t1 !scratch addi        ; reset scratch
    829     83 A3 82 00     # rd_t2 rs1_t0 !8 lw                ; I->name
    830 :Q ;GetTarget_loop
    831     83 CE 03 00     # rd_t4 rs1_t2 lbu                  ; I->name[i]
    832     03 4E 03 00     # rd_t3 rs1_t1 lbu                  ; scratch[i]
    833     @v 63 10 DE 01  # rs1_t3 rs2_t4 @GetTarget_miss bne ; strings don't match
    834 
    835     ; Look at the next char
    836     13 03 13 00     # rd_t1 rs1_t1 !1 addi
    837     93 83 13 00     # rd_t2 rs1_t2 !1 addi
    838     @Q 63 90 0E 00  # rs1_t4 @GetTarget_loop bnez       ; Loop until zero (end of string)
    839     $V 6F 00 00 00  # $GetTarget_done jal   ; We have a match
    840 
    841 :v ;GetTarget_miss
    842     83 A2 02 00     # rd_t0 rs1_t0 lw                   ; I = I->next
    843     @F 63 80 02 00  # rs1_t0 @Fail beqz                 ; Abort, no match found
    844 
    845     $O 6F 00 00 00  # $GetTarget_loop_0 jal             ; Try another label
    846 
    847 :V ;GetTarget_done
    848     13 85 42 00     # rd_a0 rs1_t0 !4 addi              ; Get target address
    849 
    850     83 20 01 00     # rd_ra rs1_sp lw                   ; restore ra
    851     13 01 41 00     # rd_sp rs1_sp !4 addi              ; deallocate stack
    852     67 80 00 00     # rs1_ra jalr                       ; return
    853 
    854 :L ;StoreLabel
    855     13 01 C1 FF     # rd_sp rs1_sp !-4 addi             ; Allocate stack
    856     23 20 11 00     # rs1_sp rs2_ra sw                  ; protect ra
    857 
    858     13 85 0C 00     # rd_a0 rs1_s9 mv                   ; struct entry
    859     93 8C 8C 01     # rd_s9 rs1_s9 !24 addi             ; calloc
    860     23 22 65 01     # rs1_a0 rs2_s6 @4 sw               ; entry->target = ip
    861     23 20 95 00     # rs1_a0 rs2_s1 sw                  ; entry->next = jump_table
    862     93 04 05 00     # rd_s1 rs1_a0 mv                   ; jump_table = entry
    863     23 24 95 01     # rs1_a0 rs2_s9 @8 sw               ; entry->name = token
    864     93 85 0C 00     # rd_a1 rs1_s9 mv                   ; Write after struct
    865     $c EF 00 00 00  # rd_ra $consume_token jal          ; Collect string
    866     93 8C 05 00     # rd_s9 rs1_a1 mv                   ; update HEAP
    867 
    868     83 20 01 00     # rd_ra rs1_sp lw                   ; restore ra
    869     13 01 41 00     # rd_sp rs1_sp !4 addi              ; deallocate stack
    870     $1 6F 00 00 00  # $First_pass_loop jal              ; return
    871 
    872 ; fputc function
    873 ; Receives CHAR in a0
    874 ; Writes and returns number of bytes written in a0
    875 :t ;fputc
    876     13 01 01 FF     # rd_sp rs1_sp !-16 addi            ; allocate stack
    877     23 20 A1 00     # rs1_sp rs2_a0 sw                  ; protect a0
    878     23 22 11 00     # rs1_sp rs2_ra @4 sw               ; protect ra
    879     23 24 B1 00     # rs1_sp rs2_a1 @8 sw               ; protect a1
    880     23 26 C1 00     # rs1_sp rs2_a2 @12 sw              ; protect a2
    881 
    882     93 08 00 04     # rd_a7 !64 addi                    ; sys_write
    883     13 85 09 00     # rd_a0 rs1_s3 mv                   ; write to output
    884     93 05 01 00     # rd_a1 rs1_sp mv                   ; Get stack address
    885     13 06 10 00     # rd_a2 !1 addi                     ; write 1 character
    886     73 00 00 00     # ecall                             ; syscall
    887 
    888     83 20 41 00     # rd_ra rs1_sp !4 lw                ; restore ra
    889     83 25 81 00     # rd_a1 rs1_sp !8 lw                ; restore a1
    890     03 26 C1 00     # rd_a2 rs1_sp !12 lw               ; restore a2
    891     13 01 01 01     # rd_sp rs1_sp !16 addi             ; Deallocate stack
    892     67 80 00 00     # rs1_ra jalr                       ; return
    893 
    894 :F ;Fail
    895     ; Terminate program with 1 return code
    896     93 08 D0 05     # rd_a7 !93 addi                    ; sys_exit
    897     13 05 10 00     # rd_a0 !1 addi                     ; Return code 1
    898     73 00 00 00     # ecall                             ; exit(1)
    899 # PROGRAM END
    900 
    901 :s ;scratch
    902     00 00 00 00
    903 
    904 #:ELF_end