## 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 . # 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