## Copyright (C) 2021 Andrius Štikonas
## This file is part of stage0.
##
## stage0 is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## stage0 is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with stage0.  If not, see <http://www.gnu.org/licenses/>.

## ELF Header
# :ELF_base ; (0x600000)
7F 45 4C 46        ## e_ident[EI_MAG0-3] ELF's magic number

02                 ## e_ident[EI_CLASS] Indicating 64 bit
01                 ## e_ident[EI_DATA] Indicating little endianness
01                 ## e_ident[EI_VERSION] Indicating original elf

03                 ## e_ident[EI_OSABI] Set at 3 for Linux
00                 ## e_ident[EI_ABIVERSION] Ignored for Statically linked executables

00 00 00 00 00 00 00 ## e_ident[EI_PAD]
02 00              ## e_type Indicating Executable
F3 00              ## e_machine Indicating RISC-V
01 00 00 00        ## e_version Indicating original elf

78 00 60 00 00 00 00 00 ## e_entry Address of the entry point (Number of bytes this header is + Base Address) # TODO
40 00 00 00 00 00 00 00 ## e_phoff Address of program header table
00 00 00 00 00 00 00 00 ## e_shoff Address of section header table

00 00 00 00        ## e_flags
40 00              ## e_ehsize Indicating our 64 Byte header

38 00              ## e_phentsize size of a program header table
01 00              ## e_phnum number of entries in program table

00 00              ## e_shentsize size of a section header table
00 00              ## e_shnum number of entries in section table

00 00              ## e_shstrndx index of the section names

## Program Header
#:ELF_program_headers
01 00 00 00             ## p_type
07 00 00 00             ## Flags
00 00 00 00 00 00 00 00 ## p_offset

00 00 60 00 00 00 00 00 ## p_vaddr
00 00 60 00 00 00 00 00 ## p_physaddr

C1 04 00 00 00 00 00 00 ## p_filesz
C1 04 00 00 00 00 00 00 ## p_memsz

01 00 00 00 00 00 00 00 ## Required alignment

#:ELF_text

; Register use:
; s2: input fd
; s3: output fd
; s4: toggle
; s5: hold
; s6: ip
; s7: tempword
; s8: shiftregister

; Our main function
# :_start ; (0x0600078)

    03 36 01 01     # RD_A2 RS1_SP !16 LD               ; Input file name

    ; Initialize globals
    13 0A F0 FF     # RD_S4 !-1 ADDI                    ; Toggle
    93 0A 00 00     # RD_S5 ADDI                        ; Hold
    13 0B 00 00     # RD_S6 ADDI                        ; Instruction Pointer

    ; Open input file and store FD in s2
    93 08 80 03     # RD_A7 !56 ADDI                    ; sys_openat
    13 05 C0 F9     # RD_A0 !-100 ADDI                  ; AT_FDCWD
    93 05 06 00     # RD_A1 RS1_A2 MV                   ; file name
    13 06 00 00     # RD_A2 ADDI                        ; read only
    73 00 00 00     # ECALL                             ; syscall
    63 4C 05 40     # RS1_A0 @Fail BLTZ                 ; Error opening file
                    # +1048
    13 09 05 00     # RD_S2 RS1_A0 MV                   ; Save fd in for later

    ; Set default FD for output file to stdout
    93 09 10 00     # RD_S3 !1 ADDI

    ; If we only have 2 arguments, don't use the third (it's not set)
    93 02 20 00     # RD_T0 !2 ADDI
    03 35 01 00     # RD_A0 RS1_SP LD                   ; Get number of the args
    63 42 55 40     # RS1_A0 RS2_T0 @Fail BLT           ; No input file provided
                    # +1028B
    63 00 55 02     # RS1_A0 RS2_T0 @after_open BEQ     ; No output file provided. Use stdout
                    # +32B

    ; Open output file and store the FD in s3
    93 08 80 03     # RD_A7 !56 ADDI                    ; sys_openat
    13 05 C0 F9     # RD_A0 !-100 ADDI                  ; AT_FDCWD
    83 35 81 01     # RD_A1 RS1_SP !24 LD               ; Output file (argument 3)
    13 06 10 24     # RD_A2 !00001101 ADDI              ; decimal 577
    ; O_TRUNC   00001000
    ; O_CREAT   00000100
    ; O_WRONLY  00000001
    ; OCTAL!
    93 06 00 1C     # RD_A3 !00700 ADDI                 ; Set read, write, execute permission on user
    ; S_IRWXU  00700
    ; OCTAL!
    73 00 00 00     # ECALL                             ; syscall
    93 09 05 00     # RD_S3 RS1_A0 MV                   ; Save fd in for later

# :after_open ; (0x06000D4)
    EF 00 40 03     # RD_RA $First_pass JAL             ; First pass

    ; Rewind input file
    93 08 E0 03     # RD_A7 !62 ADDI                    ; sys_lseek
    13 05 09 00     # RD_A0 RS1_S2 MV                   ; Input file descriptor
    93 05 00 00     # RD_A1 MV                          ; Set offset to zero
    13 06 00 00     # RD_A2 MV                          ; Set whence to zero
    73 00 00 00     # ECALL                             ; syscall

    ; Initialize globals
    13 0A F0 FF     # RD_S4 !-1 ADDI                    ; Toggle
    93 0A 00 00     # RD_S5 ADDI                        ; Hold
    13 0B 00 00     # RD_S6 ADDI                        ; Instruction Pointer
    93 0B 00 00     # RD_S7 ADDI                        ; tempword
    13 0C 00 00     # RD_S8 ADDI                        ; Shift register

    EF 00 00 07     # RD_RA $Second_pass JAL            ; Now do the second pass
                    # +112B

    6F 00 40 3A     # $Done JAL                         ; We are done
                    # +392B

; First pass loop to determine addresses of labels
# :First_pass ; (0x0600108)
    13 01 81 FF     # RD_SP RS1_SP !-8 ADDI             ; Allocate stack
    23 30 11 00     # RS1_SP RS2_RA SD                  ; protect ra

# :First_pass_loop ; (0x0600110)
    EF 00 C0 2D     # RD_RA $Read_byte JAL              ; Get another byte
                    # +732B

    ; Deal with EOF
    13 03 C0 FF     # RD_T1 !-4 ADDI
    63 06 65 04 # RS1_A0 RS2_T1 @First_pass_done BEQ
                    # +76B

    ; Check for :
    13 03 A0 03     # RD_T1 !0x3a ADDI
    63 14 65 00     # RS1_A0 RS2_T1 @First_pass_0 BNE
                    # +8B
    EF 00 C0 32     # RD_RA $StoreLabel JAL             ; Store this label
                    # +812B

# :First_pass_0 ; (0x0600128)
    ; Check for !
    13 03 10 02     # RD_T1 !0x21 ADDI
    63 08 65 02     # RS1_A0 RS2_T1 @Throwaway_token BEQ
                    # +48B

    ; Check for @
    13 03 00 04     # RD_T1 !0x40 ADDI
    63 04 65 02     # RS1_A0 RS2_T1 @Throwaway_token BEQ
                    # +40B

    ; Check for $
    13 03 40 02     # RD_T1 !0x24 ADDI
    63 00 65 02     # RS1_A0 RS2_T1 @Throwaway_token BEQ
                    # +32B

    ; Check for ~
    13 03 E0 07     # RD_T1 !0x7e ADDI
    63 0C 65 00     # RS1_A0 RS2_T1 @Throwaway_token BEQ
                    # +24B

    93 05 F0 FF     # RD_A1 !-1 ADDI                    ; write = false
    EF 00 C0 19     # RD_RA $DoByte JAL                 ; Deal with everything else
                    # +412B

    13 03 C0 FF     # RD_T1 !-4 ADDI                    ; Deal with EOF
    63 08 65 00     # RS1_A0 RS2_T1 @First_pass_done BEQ
                    # +16B

    6F F0 9F FB     # $First_pass_loop JAL              ; Keep looping
                    # -72B

# :Throwaway_token ; (0x060015C)
    ; Deal with Pointer to label
    EF 00 00 29     # RD_RA $Read_byte JAL              ; Drop the char
                    # +656B
    6F F0 1F FB     # $First_pass_loop JAL              ; Loop again
                    # -80B

# :First_pass_done ; (0x0600164)
    83 30 01 00     # RD_RA RS1_SP LD                   ; restore ra
    13 01 81 00     # RD_SP RS1_SP !8 ADDI              ; deallocate stack
    67 80 00 00     # RS1_RA JALR                       ; return

# :Second_pass ; (0x0600170)
    13 01 81 FF     # RD_SP RS1_SP !-8 ADDI             ; Allocate stack
    23 30 11 00     # RS1_SP RS2_RA SD                  ; protect ra

# :Second_pass_loop ; (0x0600178)
    EF 00 40 27     # RD_RA $Read_byte JAL              ; Read another byte
                    # +628B

    ; Deal with EOF
    13 03 C0 FF     # RD_T1 !-4 ADDI                    ; Deal with EOF
    63 0E 65 14     # RS1_A0 RS2_T1 @Second_pass_done BEQ
                    # +348B

    ; Drop the label
    13 03 A0 03     # RD_T1 !0x3a ADDI
    63 16 65 00     # RS1_A0 RS2_T1 @Second_pass_0 BNE
                    # +12B

    EF 00 00 26     # RD_RA $Read_byte JAL              ; Read the label
                    # +608B
    6F F0 9F FE     # $Second_pass_loop JAL             ; Continue looping
                    # -24B

# :Second_pass_0 ; (0x0600194)
    ; Check for !
    13 03 10 02     # RD_T1 !0x21 ADDI
    63 08 65 02     # RS1_A0 RS2_T1 @UpdateShiftRegister BEQ
                    # +48B

    ; Check for @
    13 03 00 04     # RD_T1 !0x40 ADDI
    63 04 65 02     # RS1_A0 RS2_T1 @UpdateShiftRegister BEQ
                    # +40B

    ; Check for $
    13 03 40 02     # RD_T1 !0x24 ADDI
    63 00 65 02     # RS1_A0 RS2_T1 @UpdateShiftRegister BEQ
                    # +32B

    ; Check for ~
    13 03 E0 07     # RD_T1 !0x7e ADDI
    63 0C 65 00     # RS1_A0 RS2_T1 @UpdateShiftRegister BEQ
                    # +24B

    ; Deal with everything else
    93 05 00 00     # RD_A1 MV                          ; write = true
    EF 00 00 13     # RD_RA $DoByte JAL                 ; Process our char
                    # +304B

    # Deal with EOF
    13 03 C0 FF     # RD_T1 !-4 ADDI
    63 0E 65 10     # RS1_A0 RS2_T1 @Second_pass_done BEQ ; We are done
                    # +284B

    6F F0 5F FB     # $Second_pass_loop JAL             ; continue looping
                    # -76B

# :UpdateShiftRegister ; (0x06001C8)
    93 05 05 00     # RD_A1 RS1_A0 MV                   ; Store label prefix
    EF 00 C0 25     # RD_RA $Get_table_target JAL       ; Get target
                    # +604B
    03 35 05 00     # RD_A0 RS1_A0 LD                   ; Dereference pointer
    33 05 65 41     # RD_A0 RS1_A0 RS2_S6 SUB           ; target - ip

    ; Check for !
    13 03 10 02     # RD_T1 !0x21 ADDI
    63 80 65 02     # RS1_A1 RS2_T1 @UpdateShiftRegister_I BEQ
                    # +32B

    ; Check for @
    13 03 00 04     # RD_T1 !0x40 ADDI
    63 8A 65 02     # RS1_A1 RS2_T1 @UpdateShiftRegister_B BEQ
                    # +52B

    ; Check for $
    13 03 40 02     # RD_T1 !0x24 ADDI
    63 8A 65 06     # RS1_A1 RS2_T1 @UpdateShiftRegister_J BEQ
                    # +116B

    ; Check for ~
    13 03 E0 07     # RD_T1 !0x7e ADDI
    63 88 65 0A     # RS1_A1 RS2_T1 @UpdateShiftRegister_U BEQ
                    # +176B

    6F F0 1F F8     # $Second_pass_loop JAL             ; continue looping
                    # -128B

# :UpdateShiftRegister_I ; (0x06001FC)
    ; Corresponds to RISC-V I format
    13 05 45 00     # RD_A0 RS1_A0 !4 ADDI              ; add 4 due to this being 2nd part of AUIPC combo

    37 13 00 00     # RD_T1 ~0xfff LUI                  ; load higher bits
    1B 03 F3 FF     # RD_T1 RS1_T1 !0xfff ADDIW
    33 73 65 00     # RD_T1 RS1_A0 RS2_T1 AND           ; (value & 0xfff)
    93 1B 43 01     # RD_S7 RS1_T1 RS2_X20 SLLI         ; tempword = (value & 0xfff) << 20
    33 4C 7C 01     # RD_S8 RS1_S8 RS2_S7 XOR           ; shiftregister = shiftregister ^ tempword

    6F F0 5F F6     # $Second_pass_loop JAL             ; continue looping
                    # -156B

# :UpdateShiftRegister_B ; (0x0600218)
    ; Corresponds to RISC-V B format

    ; tempword = ((value & 0x1e) << 7)            ; imm[4:1]
    ;          | ((value & 0x7e0) << (31 - 11))   ; imm[10:5]
    ;          | ((value & 0x800) >> 4)           ; imm[11]
    ;          | ((value & 0x1000) << (31 - 12))  ; imm[12]

    13 03 E0 01     # RD_T1 !0x1e ADDI
    33 73 65 00     # RD_T1 RS1_A0 RS2_T1 AND           ; value & 0x1e
    93 12 73 00     # RD_T0 RS1_T1 RS2_X7 SLLI          ; tempword = (value & 0x1e) << 7

    13 03 00 7E     # RD_T1 !0x7e0 ADDI
    33 73 65 00     # RD_T1 RS1_A0 RS2_T1 AND           ; value & 0x7e0
    13 13 43 01     # RD_T1 RS1_T1 RS2_X20 SLLI         ; (value & 0x7e0) << (31 - 11)
    B3 E2 62 00     # RD_T0 RS1_T0 RS2_T1 OR            ; logical or with the previous expression

    37 13 00 00     # RD_T1 ~0x800 LUI                  ; load higher bits
    1B 03 03 80     # RD_T1 RS1_T1 !0x800 ADDIW
    33 73 65 00     # RD_T1 RS1_A0 RS2_T1 AND           ; value & 0x800
    13 53 43 00     # RD_T1 RS1_T1 RS2_X4 SRLI          ; (value & 0x800) >> 4
    B3 E2 62 00     # RD_T0 RS1_T0 RS2_T1 OR            ; logical or with the previous expression

    37 13 00 00     # RD_T1 ~0x1000 LUI                 ; load higher bits
    33 73 65 00     # RD_T1 RS1_A0 RS2_T1 AND           ; value & 0x1000
    13 13 33 01     # RD_T1 RS1_T1 RS2_X19 SLLI         ; (value & 0x1000) << (31 - 12)
    B3 EB 62 00     # RD_S7 RS1_T0 RS2_T1 OR            ; logical or with the previous expression

    33 4C 7C 01     # RD_S8 RS1_S8 RS2_S7 XOR           ; shiftregister = shiftregister ^ tempword

    6F F0 DF F1     # $Second_pass_loop JAL             ; continue looping
                    # -228B

# :UpdateShiftRegister_J ; (0x0600260)
    ; Corresponds to RISC-V J format

    ; tempword = ((value & 0x7fe) << (30 - 10))    ; imm[10:1]
    ;          | ((value & 0x800) << (20 - 11))    ; imm[11]
    ;          | ((value & 0xff000))               ; imm[19:12]
    ;          | ((value & 0x100000) << (31 - 20)) ; imm[20]

    13 03 E0 7F     # RD_T1 !0x7fe ADDI
    33 73 65 00     # RD_T1 RS1_A0 RS2_T1 AND           ; value & 0x7fe
    93 12 43 01     # RD_T0 RS1_T1 RS2_X20 SLLI         ; tempword = (value & 0x7fe) << 20

    37 13 00 00     # RD_T1 ~0x800 LUI                  ; load higher bits
    1B 03 03 80     # RD_T1 RS1_T1 !0x800 ADDIW
    33 73 65 00     # RD_T1 RS1_A0 RS2_T1 AND           ; value & 0x800
    13 13 93 00     # RD_T1 RS1_T1 RS2_X9 SLLI          ; (value & 0x800) << (20 - 11)
    B3 E2 62 00     # RD_T0 RS1_T0 RS2_T1 OR            ; logical or with the previous expression

    37 F3 0F 00     # RD_T1 ~0xff000 LUI                ; load higher bits
    33 73 65 00     # RD_T1 RS1_A0 RS2_T1 AND           ; value & 0xff000
    B3 E2 62 00     # RD_T0 RS1_T0 RS2_T1 OR            ; logical or with the previous expression

    37 03 10 00     # RD_T1 ~0x100000 LUI               ; load higher bits
    33 73 65 00     # RD_T1 RS1_A0 RS2_T1 AND           ; value & 0x100000
    13 13 B3 00     # RD_T1 RS1_T1 RS2_X11 SLLI         ; (value & 0x100000) << (31 - 20)
    B3 EB 62 00     # RD_S7 RS1_T0 RS2_T1 OR            ; logical or with the previous expression

    33 4C 7C 01     # RD_S8 RS1_S8 RS2_S7 XOR           ; shiftregister = shiftregister ^ tempword

    6F F0 9F ED     # $Second_pass_loop JAL             ; continue looping
                    # -296B

# :UpdateShiftRegister_U ; (0x06002A4)
    ; Corresponds to RISC-V U format
    ; if value is 0x800 or more we have to add 11-th bit (0x1000) to compensate for signed extension

    B7 12 00 00     # RD_T0 ~0x800 LUI                  ; load higher bits
    9B 82 02 80     # RD_T0 RS1_T0 !0x800 ADDIW
    37 13 00 00     # RD_T1 ~0xfff LUI                  ; load higher bits
    1B 03 F3 FF     # RD_T1 RS1_T1 !0xfff ADDIW

    ; We are outside 31-bit that ~ can normally load
    B7 03 10 00     # RD_T2 ~0x100000 LUI               ; load 0xfffff000
    9B 83 F3 FF     # RD_T2 RS1_T2 !-1 ADDIW            ; load 0xfffff000
    93 93 C3 00     # RD_T2 RS1_T2 RS2_X12 SLLI         ; load 0xfffff000
    33 73 65 00     # RD_T1 RS1_A0 RS2_T1 AND           ; value & 0xfff
    B3 7B 75 00     # RD_S7 RS1_A0 RS2_T2 AND           ; value & 0xfffff000
    63 46 53 00     # RS1_T1 RS2_T0 @UpdateShiftRegister_U_small BLT
                    # +12B

    # Deal with sign extension: add 0x1000
    B7 12 00 00     # RD_T0 ~0x1000 LUI                 ; load higher bits
    BB 8B 72 01     # RD_S7 RS1_T0 RS2_S7 ADDW          ; (value & 0xfffff000) + 0x1000

# :UpdateShiftRegister_U_small ; (0x06002D4)
    33 4C 7C 01     # RD_S8 RS1_S8 RS2_S7 XOR           ; shiftregister = shiftregister ^ tempword

    6F F0 1F EA     # $Second_pass_loop JAL             ; continue looping
                    # -352B

# :Second_pass_done ; (0x06002DC)
    83 30 01 00     # RD_RA RS1_SP LD                   ; restore ra
    13 01 81 00     # RD_SP RS1_SP !8 ADDI              ; deallocate stack
    67 80 00 00     # RS1_RA JALR                       ; return


; DoByte function
; Receives:
;   character in a0
;   bool write in a1
; Does not return anything
# :DoByte ; (0x06002E8)
    13 01 81 FF     # RD_SP RS1_SP !-8 ADDI             ; Allocate stack
    23 30 11 00     # RS1_SP RS2_RA SD                  ; protect ra

    EF 00 00 05     # RD_RA $hex JAL                    ; Process hex, store it in a6
                    # +80B

    63 40 08 04     # RS1_A6 @DoByte_Done BLTZ          ; Deal with EOF and unrecognized characters
                    # +64B

    63 1A 0A 02     # RS1_S4 @DoByte_NotToggle BNEZ     ; Check if toggle is set
                    # +56B

    ; toggle = true
    63 92 05 02     # RS1_A1 @DoByte_1 BNEZ             ; check if we have to write
                    # +36B

    ; write = true
    ; We calculate (hold * 16) + hex(c) ^ sr_nextb()
    ; First, calculate new shiftregister
    93 02 F0 0F     # RD_T0 !0xff ADDI
    B3 72 5C 00     # RD_T0 RS1_S8 RS2_T0 AND           ; sr_nextb = shiftregister & 0xff
    13 5C 8C 00     # RD_S8 RS1_S8 RS2_X8 SRLI          ; shiftregister >> 8

    B3 C2 02 01     # RD_T0 RS1_T0 RS2_A6 XOR           ; hex(c) ^ sr_nextb
    13 93 4A 00     # RD_T1 RS1_S5 RS2_X4 SLLI          ; hold << 4
    33 85 62 00     # RD_A0 RS1_T0 RS2_T1 ADD           ; (hold << 4) + hex(c) ^ sr_nextb()
    EF 00 40 15     # RD_RA $fputc JAL                  ; print it
                    # +340B
    63 0C 05 18     # RS1_A0 @Fail BEQZ                 ; Fail if nothing was written
                    # +408B

# :DoByte_1 ; (0x0600320)
    13 0B 1B 00     # RD_S6 RS1_S6 !1 ADDI              ; Increment IP
    93 0A 00 00     # RD_S5 MV                          ; hold = 0
    6F 00 80 00     # $DoByte_FlipToggle JAL            ; return
                    # +8B

# :DoByte_NotToggle ; (0x060032C)
    93 0A 08 00     # RD_S5 RS1_A6 MV                   ; hold = hex(c)

# :DoByte_FlipToggle ; (0x0600330)
    13 4A FA FF     # RD_S4 RS1_S4 NOT                  ; Flip the toggle

# :DoByte_Done ; (0x0600334)
    83 30 01 00     # RD_RA RS1_SP LD                   ; restore ra
    13 01 81 00     # RD_SP RS1_SP !8 ADDI              ; deallocate stack
    67 80 00 00     # RS1_RA JALR                       ; return

; Convert ASCII hex characters into binary representation, e.g. 'a' -> 0xA
; Receives:
;   character in a0
; Returns:
;   a6 with character's hex value.
# :hex ; (0x0600340)
    13 01 01 FF     # RD_SP RS1_SP !-16 ADDI            ; Allocate stack
    23 30 11 00     # RS1_SP RS2_RA SD                  ; protect ra
    23 34 B1 00     # RS1_SP RS2_A1 @8 SD               ; protect a1

    ; Deal with EOF
    13 03 C0 FF     # RD_T1 !-4 ADDI
    63 06 65 08     # RS1_A0 RS2_T1 @hex_return BEQ
                    # +140B

    ; deal with line comments starting with #
    13 03 30 02     # RD_T1 !0x23 ADDI
    63 06 65 06     # RS1_A0 RS2_T1 @ascii_comment BEQ ; a0 eq to '#'
                    # +108B

    ; deal with line comments starting with ;
    13 03 B0 03     # RD_T1 !0x3b ADDI
    63 02 65 06     # RS1_A0 RS2_T1 @ascii_comment BEQ  ; a0 eq to ';'
                    # +100B

    ; deal all ascii less than 0
    13 03 00 03     # RD_T1 !0x30 ADDI
    63 4A 65 04     # RS1_A0 RS2_T1 @ascii_other BLT
                    # +84B

    ; deal with 0-9
    13 03 A0 03     # RD_T1 !0x3a ADDI
    63 44 65 02     # RS1_A0 RS2_T1 @ascii_num BLT
                    # +40B

    ; deal with all ascii less than A
    13 03 10 04     # RD_T1 !0x41 ADDI
    63 42 65 04     # RS1_A0 RS2_T1 @ascii_other BLT
                    # +68B

    ; deal with A-F
    13 03 70 04     # RD_T1 !0x47 ADDI
    63 48 65 02     # RS1_A0 RS2_T1 @ascii_high BLT
                    # +48B

    ; deal with all ascii less than a
    13 03 10 06     # RD_T1 !0x61 ADDI
    63 4A 65 02     # RS1_A0 RS2_T1 @ascii_other BLT
                    # +52B

    ; deal with a-f
    13 03 70 06     # RD_T1 !0x67 ADDI
    63 4A 65 00     # RS1_A0 RS2_T1 @ascii_low BLT
                    # +20B

    ; The rest that remains needs to be ignored
    6F 00 80 02     # $ascii_other JAL
                    # +40B

# :ascii_num ; (0x0600398)
    13 03 00 03     # RD_T1 !0x30 ADDI                  ; '0' -> 0
    33 08 65 40     # RD_A6 RS1_A0 RS2_T1 SUB
    6F 00 C0 03     # $hex_return JAL                   ; return
                    # +60B
# :ascii_low ; (0x06003A4)
    13 03 70 05     # RD_T1 !0x57 ADDI                  ; 'a' -> 0xA
    33 08 65 40     # RD_A6 RS1_A0 RS2_T1 SUB
    6F 00 00 03     # $hex_return JAL                   ; return
                    # +48B
# :ascii_high ; (0x06003B0)
    13 03 70 03     # RD_T1 !0x37 ADDI                  ; 'A' -> 0xA
    33 08 65 40     # RD_A6 RS1_A0 RS2_T1 SUB
    6F 00 40 02 # $hex_return JAL                       ; return
                    # +36B
# :ascii_other ; (0x06003BC)
    13 08 F0 FF     # RD_A6 !-1 ADDI                    ; Return -1
    6F 00 C0 01     # $hex_return JAL                   ; return
                    # +28B
# :ascii_comment ; (0x06003C4)                          ; Read the comment until newline
    EF 00 80 02     # RD_RA $Read_byte JAL
                    # +40B
    13 03 D0 00     # RD_T1 !0xd ADDI                   ; CR
    63 06 65 00     # RS1_A0 RS2_T1 @ascii_comment_cr BEQ
                    # +12B
    13 03 A0 00     # RD_T1 !0xa ADDI                   ; LF
    E3 18 65 FE     # RS1_A0 RS2_T1 @ascii_comment BNE  ; Keep reading comment
                    # -16B
# :ascii_comment_cr ; (0x06003D8)
    13 08 F0 FF     # RD_A6 !-1 ADDI                    ; Return -1
# :hex_return ; (0x06003DC)
    83 30 01 00     # RD_RA RS1_SP LD                   ; restore ra
    83 35 81 00     # RD_A1 RS1_SP !8 LD                ; restore a1
    13 01 01 01     # RD_SP RS1_SP !16 ADDI             ; Deallocate stack
    67 80 00 00     # RS1_RA JALR                       ; return

; Read byte into a0
# :Read_byte ; (0x06003EC)
    13 01 01 FF     # RD_SP RS1_SP !-16 ADDI            ; Allocate stack
    23 34 B1 00     # RS1_SP RS2_A1 @8 SD               ; protect a1

    93 08 F0 03     # RD_A7 !63 ADDI                    ; sys_read
    13 05 09 00     # RD_A0 RS1_S2 MV                   ; File descriptor
    93 05 01 00     # RD_A1 RS1_SP MV                   ; Get stack address for buffer
    13 00 00 00     # NOP                               ; no-op
    13 06 10 00     # RD_A2 !1 ADDI                     ; Size of what we want to read
    73 00 00 00     # ECALL                             ; syscall

    63 06 05 00     # RS1_A0 @Read_byte_1 BEQZ          ; Deal with EOF
                    # +12B
    03 85 05 00     # RD_A0 RS1_A1 LB                   ; Dereference pointer

    6F 00 80 00     # $Read_byte_done JAL               ; return
                    # +8B

# :Read_byte_1 ; (0x0600418)
    13 05 C0 FF     # RD_A0 !-4 ADDI                    ; Put EOF in a0
# :Read_byte_done ; (0x060041C)
    83 35 81 00     # RD_A1 RS1_SP !8 LD                ; restore a1
    13 01 01 01     # RD_SP RS1_SP !16 ADDI             ; Deallocate stack
    67 80 00 00     # RS1_RA JALR                       ; return

; Reads a byte and calculates table address
; Returns a pointer in a0
# :Get_table_target ; (0x0600428)
    13 01 81 FF     # RD_SP RS1_SP !-8 ADDI             ; Allocate stack
    23 30 11 00     # RS1_SP RS2_RA SD                  ; protect ra

    EF F0 DF FB     # RD_RA $Read_byte JAL              ; Get single char label
                    # -68B
    13 15 35 00     # RD_A0 RS1_A0 RS2_X3 SLLI          ; Each label in table takes 8 bytes to store
    97 02 00 00     # RD_T0 ~table AUIPC                ; Load address of table
    93 82 82 08     # RD_T0 RS1_T0 !table ADDI          ; into register t0
                    # +136B
    33 05 55 00     # RD_A0 RS1_A0 RS2_T0 ADD           ; Calculate offset

    83 30 01 00     # RD_RA RS1_SP LD                   ; restore ra
    13 01 81 00     # RD_SP RS1_SP !8 ADDI              ; deallocate stack
    67 80 00 00     # RS1_RA JALR                       ; return

# :StoreLabel ; (0x0600450)
    13 01 81 FF     # RD_SP RS1_SP !-8 ADDI             ; Allocate stack
    23 30 11 00     # RS1_SP RS2_RA SD                  ; protect ra

    EF F0 1F FD     # RD_RA $Get_table_target JAL
                    # -48B
    23 30 65 01     # RS1_A0 RS2_S6 SD                  ; Store ip into table target

    83 30 01 00     # RD_RA RS1_SP LD                   ; restore ra
    13 01 81 00     # RD_SP RS1_SP !8 ADDI              ; deallocate stack
    67 80 00 00     # RS1_RA JALR                       ; return

; fputc function
; Receives CHAR in a0
; Writes and returns number of bytes written in a0
# :fputc ; (0x060046C)
    13 01 01 FE     # RD_SP RS1_SP !-32 ADDI            ; allocate stack
    23 30 A1 00     # RS1_SP RS2_A0 SD                  ; protect a0
    23 34 11 00     # RS1_SP RS2_RA @8 SD               ; protect ra
    23 38 B1 00     # RS1_SP RS2_A1 @16 SD              ; protect a1
    23 3C C1 00     # RS1_SP RS2_A2 @24 SD              ; protect a2

    93 08 00 04     # RD_A7 !64 ADDI                    ; sys_write
    13 85 09 00     # RD_A0 RS1_S3 MV                   ; write to output
    93 05 01 00     # RD_A1 RS1_SP MV                   ; Get stack address
    13 06 10 00     # RD_A2 !1 ADDI                     ; write 1 character
    73 00 00 00     # ECALL                             ; syscall

    83 30 81 00     # RD_RA RS1_SP !8 LD                ; restore ra
    83 35 01 01     # RD_A1 RS1_SP !16 LD               ; restore a1
    03 36 81 01     # RD_A2 RS1_SP !24 LD               ; restore a2
    13 01 01 02     # RD_SP RS1_SP !32 ADDI             ; Deallocate stack
    67 80 00 00     # RS1_RA JALR                       ; return

# :Done ; (0x06004A8)
    ; Terminate program with 0 return code
    93 08 D0 05     # RD_A7 !93 ADDI                    ; sys_exit
    13 05 00 00     # RD_A0 MV                          ; Return code 0
    73 00 00 00     # ECALL                             ; exit(0)
# :Fail ; (0x06004B4)
    ; Terminate program with 1 return code
    93 08 D0 05     # RD_A7 !93 ADDI                    ; sys_exit
    13 05 10 00     # RD_A0 !1 ADDI                     ; Return code 1
    73 00 00 00     # ECALL                             ; exit(1)
# PROGRAM END

# :table; (0x06004C0)
    00
