## Copyright (C) 2021 Ekaitz Zarraga
## 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/>.

; Register use:
; s2: input fd
; s3: output fd
; s4: toggle
; s5: hold

; Uses top of the stack as i/o buffer

## ELF Header
#:ELF_base
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)
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

88 01 00 00 00 00 00 00 ## p_filesz
88 01 00 00 00 00 00 00 ## p_memsz

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

# :_start ; (0x0600078)
    03 36 01 01    # RD_A2 RS1_SP !16 LD      ; Input file name

    ; 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          ; input file
    13 06 00 00    # RD_A2 MV                 ; read only
    73 00 00 00    # ECALL
    13 09 05 00    # RD_S2 RS1_A0 MV          ; Save fd in for later

    ; Open output file and store the FD in s3
    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 !577 ADDI          ; octal 00001101
    ; O_TRUNC   00001000
    ; O_CREAT   00000100
    ; O_WRONLY  00000001
    ; OCTAL!

    93 06 00 1C    # RD_A3 !448 ADDI          ; Set read, write, execute permission on user
    ; S_IRWXU  00700
    ; OCTAL!

    73 00 00 00    # ECALL
    93 09 05 00    # RD_S3 RS1_A0 MV          ; Save fd in for later

# :next_byte ; (0x06000AC)
    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          ; Buffer
    13 06 10 00    # RD_A2 !1 ADDI            ; Size of what we want to read (set for all subsequent syscalls)
    73 00 00 00    # ECALL

    ; If the file ended (0 bytes read) terminate
    63 00 05 0C    # RS1_A0 @terminate BEQZ
                   # +192B

    ; Check if it's a comment
    03 05 01 00    # RD_A0 RS1_SP LB
    93 02 30 02    # RD_T0 !0x23 ADDI
    63 08 55 00    # RS1_A0 RS2_T0 @loop BEQ  ; a0 eq to '#'
                   # +16B
    93 02 B0 03    # RD_T0 !0x3B ADDI
    63 04 55 00    # RS1_A0 RS2_T0 @loop BEQ  ; a0 eq to ';'
                   # +8B
    6F 00 80 02    # $not_comment JAL
                   # +40B
# :loop ; (0x06000DC)
        13 05 09 00    # RD_A0 RS1_S2 MV   ; File descriptor
        73 00 00 00    # ECALL             ; sys_read

        ; If the file ended (0 bytes read) terminate
        63 0E 05 08    # RS1_A0 @terminate BEQZ
                       # +156B
        ; Check if read byte is the end of the comment,
        ; in the case it is continue processing
        03 05 01 00     # RD_A0 RS1_SP LB
        93 02 A0 00     # RD_T0 !0xA ADDI
        E3 0E 55 FA     # RS1_A0 RS2_T0 @next_byte BEQ ; a0 eq to \n
                        # -68B
        93 02 D0 00     # RD_T0 !0xD ADDI
        E3 0A 55 FA     # RS1_A0 RS2_T0 @next_byte BEQ ; a0 eq to \r
                        # -76B
    6F F0 1F FE    # $loop JAL
# :not_comment ; (0x0600100)
    ; Check if it's a hex character:
    ; in the case it's not, ignores and reads next byte
    03 05 01 00    # RD_A0 RS1_SP LB

    ; Is it between '0' and '9'?
    93 02 00 03    # RD_T0 !48 ADDI           ; '0' character
    63 4A 55 00    # RS1_A0 RS2_T0 @uppercase_alpha BLT
                   # +20B
    93 02 90 03    # RD_T0 !57 ADDI           ; '9' character
    63 C6 A2 00    # RS1_T0 RS2_A0 @uppercase_alpha BLT
                   # +12B
    13 05 05 FD    # RD_A0 RS1_A0 !-48 ADDI
    6F 00 00 03    # $hex_read JAL
                   # +48B
# :uppercase_alpha ; (0x060011C)
    ; Is it between 'A' and 'F'?
    93 02 10 04    # RD_T0 !65 ADDI           ; 'A' character
    63 4A 55 00    # RS1_A0 RS2_T0 @lowercase_alpha BLT
                   # +20B
    93 02 60 04    # RD_T0 !70 ADDI           ; 'F' character
    63 C6 A2 00    # RS1_T0 RS2_A0 @lowercase_alpha BLT
                   # +12B
    13 05 95 FC    # RD_A0 RS1_A0 !-55 ADDI
    6F 00 80 01    # $hex_read JAL
                   # +24B
# :lowercase_alpha
    ; Is it between 'a' and 'f'?
    93 02 10 06    # RD_T0 !97 ADDI           ; 'a' character
    E3 4A 55 F6    # RS1_A0 RS2_T0 @next_byte BLT ; Not hex, continue reading
                   # -140B
    93 02 60 06    # RD_T0 !102 ADDI          ; 'f' character
    E3 C6 A2 F6    # RS1_T0 RS2_A0 @next_byte BLT ; Not hex, continue reading
                   # -148B
    13 05 95 FA    # RD_A0 RS1_A0 !-87 ADDI
# :hex_read ; (0x0600148)
    ; END check hex -- leaves the half byte in a0

    63 18 0A 00    # RS1_S4 @combine BNEZ     ; if toggle != 0 -> combine
                   # +16B
    ; Toggle == 0, we need to prepare for later
    93 0A 05 00    # RD_S5 RS1_A0 MV          ; Load hold

    13 0A 10 00    # RD_S4 !1 ADDI            ; Set toggle
    6F F0 9F F5    # $next_byte JAL           ; Read next byte
                   # -168B

# :combine ; (0x0600158)
    ; Combine half bytes
    93 95 4A 00    # RD_A1 RS1_S5 RS2_X4 SLLI ; Shift logical left 4 times
    33 05 B5 00    # RD_A0 RS1_A0 RS2_A1 ADD  ; Combine two half bytes
    ; Leaves the full byte in a0

    ; Values are combined, now we write in the file
    23 00 A1 00    # RS1_SP RS2_A0 SB         ; Store prepared byte in buffer
    93 08 00 04    # RD_A7 !64 ADDI           ; sys_write
    13 85 09 00    # RD_A0 RS1_S3 MV          ; file descriptor (stdout)
    93 05 01 00    # RD_A1 RS1_SP MV          ; string address
    73 00 00 00    # ECALL

    ; Update globals
    13 0A 00 00    # RD_S4 MV                 ; Clear toggle
    93 0A 00 00    # RD_S5 MV                 ; Clear hold

    ; Read next byte
    6F F0 1F F3    # $next_byte JAL
                   # -208B

# :terminate  ; (0x600180)
    ; Terminate program with 0 return code
    93 08 D0 05    # RD_A7 !93 ADDI           ; sys_exit
    73 00 00 00    # ECALL
# PROGRAM END

# :ELF_end ; (0x600188)
