| ## Copyright (C) 2026 Christian Westrom
|
| ## 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/>.
|
|
|
| # hex0 - A minimal hex loader for RISC-V (bare metal, QEMU virt)
|
| # Reads hex digits from UART, converts pairs to bytes, stores in memory.
|
| # On Ctrl-D, jumps to the loaded code.
|
|
|
| ; Register use:
|
| ; s0: holder (first nibble)
|
| ; s1: toggle (1 = first nibble, 0 = second nibble)
|
| ; s2: pointer to code buffer
|
|
|
| # _start:
|
| # Setup stack pointer
|
| 17 31 00 00 # auipc sp, 0x3
|
| 13 01 01 18 # addi sp, sp, 384
|
| # Setup code buffer pointer
|
| 17 19 00 00 # auipc s2, 0x1
|
| 13 09 89 17 # addi s2, s2, 376
|
| # Initialize toggle and holder
|
| 93 04 10 00 # addi s1, x0, 1
|
| 13 04 00 00 # addi s0, x0, 0
|
|
|
| # loop:
|
| EF 00 80 04 # jal ra, read_char
|
| # Check for Ctrl-D
|
| 93 02 40 00 # addi t0, x0, 4
|
| 63 16 55 00 # bne a0, t0, process_input
|
| EF 00 00 0E # jal ra, execute_code
|
| 6F F0 1F FF # jal x0, loop
|
|
|
| # process_input:
|
| EF 00 40 05 # jal ra, hex
|
| E3 44 05 FE # blt a0, x0, loop
|
| 63 88 04 00 # beq s1, x0, process_second_nibble
|
| # Process first nibble
|
| 13 74 F5 00 # andi s0, a0, 0xF
|
| 93 04 00 00 # addi s1, x0, 0
|
| 6F F0 9F FD # jal x0, loop
|
|
|
| # process_second_nibble:
|
| 13 14 44 00 # slli s0, s0, 4
|
| 13 75 F5 00 # andi a0, a0, 0xF
|
| 33 05 A4 00 # add a0, s0, a0
|
| 93 04 10 00 # addi s1, x0, 1
|
| # Write out byte
|
| 23 00 A9 00 # sb a0, 0(s2)
|
| # Update our pointer
|
| 13 09 19 00 # addi s2, s2, 1
|
| 6F F0 DF FB # jal x0, loop
|
|
|
| # read_char:
|
| B7 02 00 10 # lui t0, 0x10000
|
| 9B 82 52 00 # addiw t0, t0, 5
|
| # poll_rx:
|
| 03 85 02 00 # lb a0, 0(t0)
|
| 13 75 15 00 # andi a0, a0, 1
|
| E3 0C 05 FE # beq a0, x0, poll_rx
|
| B7 02 00 10 # lui t0, 0x10000
|
| 03 85 02 00 # lb a0, 0(t0)
|
| 67 80 00 00 # jalr x0, ra, 0
|
|
|
| # hex:
|
| # Deal with line comments starting with #
|
| 93 02 30 02 # addi t0, x0, 0x23
|
| 63 00 55 06 # beq a0, t0, ascii_comment
|
| # Deal with line comments starting with ;
|
| 93 02 B0 03 # addi t0, x0, 0x3B
|
| 63 0C 55 04 # beq a0, t0, ascii_comment
|
| # Deal with all ASCII less than '0'
|
| 93 02 00 03 # addi t0, x0, 0x30
|
| 63 44 55 04 # blt a0, t0, ascii_other
|
| # Deal with '0' through '9'
|
| 93 02 A0 03 # addi t0, x0, 0x3A
|
| 63 44 55 02 # blt a0, t0, ascii_num
|
| # Deal with all ASCII less than 'A'
|
| 93 02 10 04 # addi t0, x0, 0x41
|
| 63 4C 55 02 # blt a0, t0, ascii_other
|
| # Deal with 'A' through 'F'
|
| 93 02 70 04 # addi t0, x0, 0x47
|
| 63 44 55 02 # blt a0, t0, ascii_high
|
| # Deal with all ASCII less than 'a'
|
| 93 02 10 06 # addi t0, x0, 0x61
|
| 63 44 55 02 # blt a0, t0, ascii_other
|
| # Deal with 'a' through 'f'
|
| 93 02 70 06 # addi t0, x0, 0x67
|
| 63 48 55 00 # blt a0, t0, ascii_low
|
| # Deal with everything else
|
| 6F 00 C0 01 # jal x0, ascii_other
|
|
|
| # ascii_num:
|
| 13 05 05 FD # addi a0, a0, -48
|
| 67 80 00 00 # jalr x0, ra, 0
|
|
|
| # ascii_low:
|
| 13 05 95 FA # addi a0, a0, -87
|
| 67 80 00 00 # jalr x0, ra, 0
|
|
|
| # ascii_high:
|
| 13 05 95 FC # addi a0, a0, -55
|
| 67 80 00 00 # jalr x0, ra, 0
|
|
|
| # ascii_other:
|
| 13 05 F0 FF # addi a0, x0, -1
|
| 67 80 00 00 # jalr x0, ra, 0
|
|
|
| # ascii_comment:
|
| 13 01 01 FF # addi sp, sp, -16
|
| 23 30 11 00 # sd ra, 0(sp)
|
| EF F0 5F F7 # jal ra, read_char
|
| 93 02 D0 00 # addi t0, x0, 0xD
|
| E3 18 55 FE # bne a0, t0, ascii_comment
|
| 83 30 01 00 # ld ra, 0(sp)
|
| 13 01 01 01 # addi sp, sp, 16
|
| 6F F0 DF FD # jal x0, ascii_other
|
|
|
| # execute_code:
|
| # Zero all registers before jump
|
| 13 05 00 00 # addi a0, x0, 0
|
| 93 05 00 00 # addi a1, x0, 0
|
| 13 06 00 00 # addi a2, x0, 0
|
| 93 06 00 00 # addi a3, x0, 0
|
| 13 07 00 00 # addi a4, x0, 0
|
| 93 07 00 00 # addi a5, x0, 0
|
| 13 08 00 00 # addi a6, x0, 0
|
| 93 08 00 00 # addi a7, x0, 0
|
| 93 02 00 00 # addi t0, x0, 0
|
| 13 03 00 00 # addi t1, x0, 0
|
| 93 03 00 00 # addi t2, x0, 0
|
| 13 0E 00 00 # addi t3, x0, 0
|
| 93 0E 00 00 # addi t4, x0, 0
|
| 13 0F 00 00 # addi t5, x0, 0
|
| 93 0F 00 00 # addi t6, x0, 0
|
| 13 04 00 00 # addi s0, x0, 0
|
| 93 04 00 00 # addi s1, x0, 0
|
| 93 09 00 00 # addi s3, x0, 0
|
| 13 0A 00 00 # addi s4, x0, 0
|
| 93 0A 00 00 # addi s5, x0, 0
|
| 13 0B 00 00 # addi s6, x0, 0
|
| 93 0B 00 00 # addi s7, x0, 0
|
| 13 0C 00 00 # addi s8, x0, 0
|
| 93 0C 00 00 # addi s9, x0, 0
|
| 13 0D 00 00 # addi s10, x0, 0
|
| 93 0D 00 00 # addi s11, x0, 0
|
| # Load the code that we input by hand
|
| 97 12 00 00 # auipc t0, 0x1
|
| 93 82 42 01 # addi t0, t0, 20
|
| 67 80 02 00 # jalr x0, t0, 0
|
|
|
| # done:
|
| 6F 00 00 00 # jal x0, done
|