## 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) # This file is generated from baremetal/hex0.hex0. # Regenerate it with: just annotate_hex0 # Reads hex digits from UART, converts pairs to bytes, stores in memory. # On Ctrl-D, jumps to the loaded code. # Audited by Christian Westrom: 2026-03-15, 08:53 UTC ; Register use: ; s0: holder (first nibble) ; s1: toggle (1 = first nibble, 0 = second nibble) ; s2: pointer to code buffer # _start: (0x0) # Setup stack pointer with 0x1903 # Combined intent at 0x0000-0x0004: auipc/addi builds pc-relative sp = 0x3190 (PC + 0x3000 + 0x190) # Instruction at 0x0000: auipc sp, 0x3 # Hex (LE): 17 31 00 00 → Word: 0x00003117 # Binary: 00000000 00000000 00110001 00010111 # Fields: [imm[31:12]=0x3] [rd=2(sp)] [opcode=0x17] 17 31 00 00 # Instruction at 0x0004: addi sp, sp, 0x190 # Hex (LE): 13 01 01 19 → Word: 0x19010113 # Binary: 00011001 00000001 00000001 00010011 # Fields: [imm[11:0]=0x190] [rs1=2(sp)] [f3=0b000] [rd=2(sp)] [opcode=0x13] 13 01 01 19 # Setup code buffer pointer # Combined intent at 0x0008-0x000C: auipc/addi builds pc-relative s2 = 0x1190 (PC + 0x1000 + 0x188) # Instruction at 0x0008: auipc s2, 0x1 # Hex (LE): 17 19 00 00 → Word: 0x00001917 # Binary: 00000000 00000000 00011001 00010111 # Fields: [imm[31:12]=0x1] [rd=18(s2)] [opcode=0x17] 17 19 00 00 # Instruction at 0x000C: addi s2, s2, 0x188 # Hex (LE): 13 09 89 18 → Word: 0x18890913 # Binary: 00011000 10001001 00001001 00010011 # Fields: [imm[11:0]=0x188] [rs1=18(s2)] [f3=0b000] [rd=18(s2)] [opcode=0x13] 13 09 89 18 # Initialize toggle # Instruction at 0x0010: addi s1, x0, 1 # Hex (LE): 93 04 10 00 → Word: 0x00100493 # Binary: 00000000 00010000 00000100 10010011 # Fields: [imm[11:0]=0x1] [rs1=0(x0)] [f3=0b000] [rd=9(s1)] [opcode=0x13] 93 04 10 00 # Initialize holder # Instruction at 0x0014: addi s0, x0, 0 # Hex (LE): 13 04 00 00 → Word: 0x00000413 # Binary: 00000000 00000000 00000100 00010011 # Fields: [imm[11:0]=0x0] [rs1=0(x0)] [f3=0b000] [rd=8(s0)] [opcode=0x13] 13 04 00 00 # main: (0x18) # Read a character # Instruction at 0x0018: jal ra, 0x0060 # Hex (LE): EF 00 80 04 → Word: 0x048000EF # Binary: 00000100 10000000 00000000 11101111 # Fields: [imm[20]=0b0] [imm[10:1]=0b0000100100] [imm[11]=0b0] [imm[19:12]=0x0] [rd=1(ra)] [opcode=0x6F] EF 00 80 04 # Check for Ctrl-D (0x4) # Load 0x4 into t0 # Instruction at 0x001C: addi t0, x0, 4 # Hex (LE): 93 02 40 00 → Word: 0x00400293 # Binary: 00000000 01000000 00000010 10010011 # Fields: [imm[11:0]=0x4] [rs1=0(x0)] [f3=0b000] [rd=5(t0)] [opcode=0x13] 93 02 40 00 # If we did not get 0x4, start processing input # Instruction at 0x0020: bne a0, t0, 0x002C # Hex (LE): 63 16 55 00 → Word: 0x00551663 # Binary: 00000000 01010101 00010110 01100011 # Fields: [imm[12]=0b0] [imm[10:5]=0b000000] [rs2=5(t0)] [rs1=10(a0)] [f3=0b001] [imm[4:1]=0b0110] [imm[11]=0b0] [opcode=0x63] 63 16 55 00 # If we're here, we did get 0x4, so we can go execute the code # Instruction at 0x0024: jal ra, 0x010C # Hex (LE): EF 00 80 0E → Word: 0x0E8000EF # Binary: 00001110 10000000 00000000 11101111 # Fields: [imm[20]=0b0] [imm[10:1]=0b0001110100] [imm[11]=0b0] [imm[19:12]=0x0] [rd=1(ra)] [opcode=0x6F] EF 00 80 0E # Otherwise, go back to the top of the main loop to read characters # Instruction at 0x0028: jal x0, 0x0018 # Hex (LE): 6F F0 1F FF → Word: 0xFF1FF06F # Binary: 11111111 00011111 11110000 01101111 # Fields: [imm[20]=0b1] [imm[10:1]=0b1111111000] [imm[11]=0b1] [imm[19:12]=0xFF] [rd=0(x0)] [opcode=0x6F] 6F F0 1F FF # process_input: (0x2C) # The first thing we do is go to the hex processing routine # Instruction at 0x002C: jal ra, 0x0080 # Hex (LE): EF 00 40 05 → Word: 0x054000EF # Binary: 00000101 01000000 00000000 11101111 # Fields: [imm[20]=0b0] [imm[10:1]=0b0000101010] [imm[11]=0b0] [imm[19:12]=0x0] [rd=1(ra)] [opcode=0x6F] EF 00 40 05 # If a0 comes back less than 0 from the hex routine, go back to the main loop # Instruction at 0x0030: blt a0, x0, 0x0018 # Hex (LE): E3 44 05 FE → Word: 0xFE0544E3 # Binary: 11111110 00000101 01000100 11100011 # Fields: [imm[12]=0b1] [imm[10:5]=0b111111] [rs2=0(x0)] [rs1=10(a0)] [f3=0b100] [imm[4:1]=0b0100] [imm[11]=0b1] [opcode=0x63] E3 44 05 FE # Else, if s1 has been set to zero, we process the second nibble # Instruction at 0x0034: beq s1, x0, 0x0044 # Hex (LE): 63 88 04 00 → Word: 0x00048863 # Binary: 00000000 00000100 10001000 01100011 # Fields: [imm[12]=0b0] [imm[10:5]=0b000000] [rs2=0(x0)] [rs1=9(s1)] [f3=0b000] [imm[4:1]=0b1000] [imm[11]=0b0] [opcode=0x63] 63 88 04 00 # In any case, we extract the first nibble from the lower 4 bits by using a mask # Instruction at 0x0038: andi s0, a0, 0xF # Hex (LE): 13 74 F5 00 → Word: 0x00F57413 # Binary: 00000000 11110101 01110100 00010011 # Fields: [imm[11:0]=0xF] [rs1=10(a0)] [f3=0b111] [rd=8(s0)] [opcode=0x13] 13 74 F5 00 # We then reset s1 back to zero to prepare to go back to the main loop # Instruction at 0x003C: addi s1, x0, 0 # Hex (LE): 93 04 00 00 → Word: 0x00000493 # Binary: 00000000 00000000 00000100 10010011 # Fields: [imm[11:0]=0x0] [rs1=0(x0)] [f3=0b000] [rd=9(s1)] [opcode=0x13] 93 04 00 00 # Processing one hex is done # Instruction at 0x0040: jal x0, 0x0018 # Hex (LE): 6F F0 9F FD → Word: 0xFD9FF06F # Binary: 11111101 10011111 11110000 01101111 # Fields: [imm[20]=0b1] [imm[10:1]=0b1111101100] [imm[11]=0b1] [imm[19:12]=0xFF] [rd=0(x0)] [opcode=0x6F] 6F F0 9F FD # process_second_nibble: (0x44) # We shift the first nibble by 4 # Instruction at 0x0044: slli s0, s0, 4 # Hex (LE): 13 14 44 00 → Word: 0x00441413 # Binary: 00000000 01000100 00010100 00010011 # Fields: [imm[11:0]=0x4] [rs1=8(s0)] [f3=0b001] [rd=8(s0)] [opcode=0x13] 13 14 44 00 # Then we extract the lower for bits from a0 # Instruction at 0x0048: andi a0, a0, 0xF # Hex (LE): 13 75 F5 00 → Word: 0x00F57513 # Binary: 00000000 11110101 01110101 00010011 # Fields: [imm[11:0]=0xF] [rs1=10(a0)] [f3=0b111] [rd=10(a0)] [opcode=0x13] 13 75 F5 00 # We combine the first (shifted) and second nibbles # Instruction at 0x004C: add a0, s0, a0 # Hex (LE): 33 05 A4 00 → Word: 0x00A40533 # Binary: 00000000 10100100 00000101 00110011 # Fields: [funct7=0b0000000] [rs2=10(a0)] [rs1=8(s0)] [f3=0b000] [rd=10(a0)] [opcode=0x33] 33 05 A4 00 # Now that we've processed the second nibble we'll flag it in s1 # Instruction at 0x0050: addi s1, x0, 1 # Hex (LE): 93 04 10 00 → Word: 0x00100493 # Binary: 00000000 00010000 00000100 10010011 # Fields: [imm[11:0]=0x1] [rs1=0(x0)] [f3=0b000] [rd=9(s1)] [opcode=0x13] 93 04 10 00 # Write out the byte to the buffer # Instruction at 0x0054: sb a0, 0(s2) # Hex (LE): 23 00 A9 00 → Word: 0x00A90023 # Binary: 00000000 10101001 00000000 00100011 # Fields: [imm[11:5]=0b0000000] [rs2=10(a0)] [rs1=18(s2)] [f3=0b000] [imm[4:0]=0b00000] [opcode=0x23] 23 00 A9 00 # Update the pointer to the buffer # Instruction at 0x0058: addi s2, s2, 1 # Hex (LE): 13 09 19 00 → Word: 0x00190913 # Binary: 00000000 00011001 00001001 00010011 # Fields: [imm[11:0]=0x1] [rs1=18(s2)] [f3=0b000] [rd=18(s2)] [opcode=0x13] 13 09 19 00 # Jump back to the main loop # Instruction at 0x005C: jal x0, 0x0018 # Hex (LE): 6F F0 DF FB → Word: 0xFBDFF06F # Binary: 11111011 11011111 11110000 01101111 # Fields: [imm[20]=0b1] [imm[10:1]=0b1111011110] [imm[11]=0b1] [imm[19:12]=0xFF] [rd=0(x0)] [opcode=0x6F] 6F F0 DF FB # read_char: (0x60) # Load UART RX into t0 # Combined intent at 0x0060-0x0064: lui/addiw builds t0 = 0x10000005 (0x10000000 + 5) # Instruction at 0x0060: lui t0, 0x10000 # Hex (LE): B7 02 00 10 → Word: 0x100002B7 # Binary: 00010000 00000000 00000010 10110111 # Fields: [imm[31:12]=0x10000] [rd=5(t0)] [opcode=0x37] B7 02 00 10 # Instruction at 0x0064: addiw t0, t0, 5 # Hex (LE): 9B 82 52 00 → Word: 0x0052829B # Binary: 00000000 01010010 10000010 10011011 # Fields: [imm[11:0]=0x5] [rs1=5(t0)] [f3=0b000] [rd=5(t0)] [opcode=0x1B] 9B 82 52 00 # poll_rx: (0x68) # Load a byte from RX to a0 # Instruction at 0x0068: lb a0, 0(t0) # Hex (LE): 03 85 02 00 → Word: 0x00028503 # Binary: 00000000 00000010 10000101 00000011 # Fields: [imm[11:0]=0x0] [rs1=5(t0)] [f3=0b000] [rd=10(a0)] [opcode=0x03] 03 85 02 00 # Check if a0 is odd # Instruction at 0x006C: andi a0, a0, 0x1 # Hex (LE): 13 75 15 00 → Word: 0x00157513 # Binary: 00000000 00010101 01110101 00010011 # Fields: [imm[11:0]=0x1] [rs1=10(a0)] [f3=0b111] [rd=10(a0)] [opcode=0x13] 13 75 15 00 # If a0 is 0 then RX is not ready so jump back # Instruction at 0x0070: beq a0, x0, 0x0068 # Hex (LE): E3 0C 05 FE → Word: 0xFE050CE3 # Binary: 11111110 00000101 00001100 11100011 # Fields: [imm[12]=0b1] [imm[10:5]=0b111111] [rs2=0(x0)] [rs1=10(a0)] [f3=0b000] [imm[4:1]=0b1100] [imm[11]=0b1] [opcode=0x63] E3 0C 05 FE # Set t0 to 0x10000000 (UART data register) # Instruction at 0x0074: lui t0, 0x10000 # Hex (LE): B7 02 00 10 → Word: 0x100002B7 # Binary: 00010000 00000000 00000010 10110111 # Fields: [imm[31:12]=0x10000] [rd=5(t0)] [opcode=0x37] B7 02 00 10 # Load a byte from the data register # Instruction at 0x0078: lb a0, 0(t0) # Hex (LE): 03 85 02 00 → Word: 0x00028503 # Binary: 00000000 00000010 10000101 00000011 # Fields: [imm[11:0]=0x0] [rs1=5(t0)] [f3=0b000] [rd=10(a0)] [opcode=0x03] 03 85 02 00 # Jump back to address in ra # Instruction at 0x007C: jalr x0, ra, 0 # Hex (LE): 67 80 00 00 → Word: 0x00008067 # Binary: 00000000 00000000 10000000 01100111 # Fields: [imm[11:0]=0x0] [rs1=1(ra)] [f3=0b000] [rd=0(x0)] [opcode=0x67] 67 80 00 00 # hex: (0x80) # Deal with line comments starting with # # Instruction at 0x0080: addi t0, x0, 0x23 # Hex (LE): 93 02 30 02 → Word: 0x02300293 # Binary: 00000010 00110000 00000010 10010011 # Fields: [imm[11:0]=0x23] [rs1=0(x0)] [f3=0b000] [rd=5(t0)] [opcode=0x13] 93 02 30 02 # Instruction at 0x0084: beq a0, t0, 0x00E4 # Hex (LE): 63 00 55 06 → Word: 0x06550063 # Binary: 00000110 01010101 00000000 01100011 # Fields: [imm[12]=0b0] [imm[10:5]=0b000011] [rs2=5(t0)] [rs1=10(a0)] [f3=0b000] [imm[4:1]=0b0000] [imm[11]=0b0] [opcode=0x63] 63 00 55 06 # Deal with line comments starting with ; # Instruction at 0x0088: addi t0, x0, 0x3B # Hex (LE): 93 02 B0 03 → Word: 0x03B00293 # Binary: 00000011 10110000 00000010 10010011 # Fields: [imm[11:0]=0x3B] [rs1=0(x0)] [f3=0b000] [rd=5(t0)] [opcode=0x13] 93 02 B0 03 # Instruction at 0x008C: beq a0, t0, 0x00E4 # Hex (LE): 63 0C 55 04 → Word: 0x04550C63 # Binary: 00000100 01010101 00001100 01100011 # Fields: [imm[12]=0b0] [imm[10:5]=0b000010] [rs2=5(t0)] [rs1=10(a0)] [f3=0b000] [imm[4:1]=0b1100] [imm[11]=0b0] [opcode=0x63] 63 0C 55 04 # Deal with all ASCII less than '0' # Instruction at 0x0090: addi t0, x0, 0x30 # Hex (LE): 93 02 00 03 → Word: 0x03000293 # Binary: 00000011 00000000 00000010 10010011 # Fields: [imm[11:0]=0x30] [rs1=0(x0)] [f3=0b000] [rd=5(t0)] [opcode=0x13] 93 02 00 03 # Instruction at 0x0094: blt a0, t0, 0x00DC # Hex (LE): 63 44 55 04 → Word: 0x04554463 # Binary: 00000100 01010101 01000100 01100011 # Fields: [imm[12]=0b0] [imm[10:5]=0b000010] [rs2=5(t0)] [rs1=10(a0)] [f3=0b100] [imm[4:1]=0b0100] [imm[11]=0b0] [opcode=0x63] 63 44 55 04 # Deal with '0' through '9' # Instruction at 0x0098: addi t0, x0, 0x3A # Hex (LE): 93 02 A0 03 → Word: 0x03A00293 # Binary: 00000011 10100000 00000010 10010011 # Fields: [imm[11:0]=0x3A] [rs1=0(x0)] [f3=0b000] [rd=5(t0)] [opcode=0x13] 93 02 A0 03 # Instruction at 0x009C: blt a0, t0, 0x00C4 # Hex (LE): 63 44 55 02 → Word: 0x02554463 # Binary: 00000010 01010101 01000100 01100011 # Fields: [imm[12]=0b0] [imm[10:5]=0b000001] [rs2=5(t0)] [rs1=10(a0)] [f3=0b100] [imm[4:1]=0b0100] [imm[11]=0b0] [opcode=0x63] 63 44 55 02 # Deal with all ASCII less than 'A' # Instruction at 0x00A0: addi t0, x0, 0x41 # Hex (LE): 93 02 10 04 → Word: 0x04100293 # Binary: 00000100 00010000 00000010 10010011 # Fields: [imm[11:0]=0x41] [rs1=0(x0)] [f3=0b000] [rd=5(t0)] [opcode=0x13] 93 02 10 04 # Instruction at 0x00A4: blt a0, t0, 0x00DC # Hex (LE): 63 4C 55 02 → Word: 0x02554C63 # Binary: 00000010 01010101 01001100 01100011 # Fields: [imm[12]=0b0] [imm[10:5]=0b000001] [rs2=5(t0)] [rs1=10(a0)] [f3=0b100] [imm[4:1]=0b1100] [imm[11]=0b0] [opcode=0x63] 63 4C 55 02 # Deal with 'A' through 'F' # Instruction at 0x00A8: addi t0, x0, 0x47 # Hex (LE): 93 02 70 04 → Word: 0x04700293 # Binary: 00000100 01110000 00000010 10010011 # Fields: [imm[11:0]=0x47] [rs1=0(x0)] [f3=0b000] [rd=5(t0)] [opcode=0x13] 93 02 70 04 # Instruction at 0x00AC: blt a0, t0, 0x00D4 # Hex (LE): 63 44 55 02 → Word: 0x02554463 # Binary: 00000010 01010101 01000100 01100011 # Fields: [imm[12]=0b0] [imm[10:5]=0b000001] [rs2=5(t0)] [rs1=10(a0)] [f3=0b100] [imm[4:1]=0b0100] [imm[11]=0b0] [opcode=0x63] 63 44 55 02 # Deal with all ASCII less than 'a' # Instruction at 0x00B0: addi t0, x0, 0x61 # Hex (LE): 93 02 10 06 → Word: 0x06100293 # Binary: 00000110 00010000 00000010 10010011 # Fields: [imm[11:0]=0x61] [rs1=0(x0)] [f3=0b000] [rd=5(t0)] [opcode=0x13] 93 02 10 06 # Instruction at 0x00B4: blt a0, t0, 0x00DC # Hex (LE): 63 44 55 02 → Word: 0x02554463 # Binary: 00000010 01010101 01000100 01100011 # Fields: [imm[12]=0b0] [imm[10:5]=0b000001] [rs2=5(t0)] [rs1=10(a0)] [f3=0b100] [imm[4:1]=0b0100] [imm[11]=0b0] [opcode=0x63] 63 44 55 02 # Deal with 'a' through 'f' # Instruction at 0x00B8: addi t0, x0, 0x67 # Hex (LE): 93 02 70 06 → Word: 0x06700293 # Binary: 00000110 01110000 00000010 10010011 # Fields: [imm[11:0]=0x67] [rs1=0(x0)] [f3=0b000] [rd=5(t0)] [opcode=0x13] 93 02 70 06 # Instruction at 0x00BC: blt a0, t0, 0x00CC # Hex (LE): 63 48 55 00 → Word: 0x00554863 # Binary: 00000000 01010101 01001000 01100011 # Fields: [imm[12]=0b0] [imm[10:5]=0b000000] [rs2=5(t0)] [rs1=10(a0)] [f3=0b100] [imm[4:1]=0b1000] [imm[11]=0b0] [opcode=0x63] 63 48 55 00 # Deal with everything else # Instruction at 0x00C0: jal x0, 0x00DC # Hex (LE): 6F 00 C0 01 → Word: 0x01C0006F # Binary: 00000001 11000000 00000000 01101111 # Fields: [imm[20]=0b0] [imm[10:1]=0b0000001110] [imm[11]=0b0] [imm[19:12]=0x0] [rd=0(x0)] [opcode=0x6F] 6F 00 C0 01 # ascii_num: (0xC4) # Numbers are passed back as is # Instruction at 0x00C4: addi a0, a0, -48 # Hex (LE): 13 05 05 FD → Word: 0xFD050513 # Binary: 11111101 00000101 00000101 00010011 # Fields: [imm[11:0]=0xFD0] [rs1=10(a0)] [f3=0b000] [rd=10(a0)] [opcode=0x13] 13 05 05 FD # Instruction at 0x00C8: jalr x0, ra, 0 # Hex (LE): 67 80 00 00 → Word: 0x00008067 # Binary: 00000000 00000000 10000000 01100111 # Fields: [imm[11:0]=0x0] [rs1=1(ra)] [f3=0b000] [rd=0(x0)] [opcode=0x67] 67 80 00 00 # ascii_low: (0xCC) # Simple ascii arithmetic. Lowercase 'a' through 'f' -87 gives us the hex number we want # Instruction at 0x00CC: addi a0, a0, -87 # Hex (LE): 13 05 95 FA → Word: 0xFA950513 # Binary: 11111010 10010101 00000101 00010011 # Fields: [imm[11:0]=0xFA9] [rs1=10(a0)] [f3=0b000] [rd=10(a0)] [opcode=0x13] 13 05 95 FA # Instruction at 0x00D0: jalr x0, ra, 0 # Hex (LE): 67 80 00 00 → Word: 0x00008067 # Binary: 00000000 00000000 10000000 01100111 # Fields: [imm[11:0]=0x0] [rs1=1(ra)] [f3=0b000] [rd=0(x0)] [opcode=0x67] 67 80 00 00 # ascii_high: (0xD4) # Simple ascii arithmetic. Uppercase 'A' through 'F' -55 gives us the hex number we want # Instruction at 0x00D4: addi a0, a0, -55 # Hex (LE): 13 05 95 FC → Word: 0xFC950513 # Binary: 11111100 10010101 00000101 00010011 # Fields: [imm[11:0]=0xFC9] [rs1=10(a0)] [f3=0b000] [rd=10(a0)] [opcode=0x13] 13 05 95 FC # Instruction at 0x00D8: jalr x0, ra, 0 # Hex (LE): 67 80 00 00 → Word: 0x00008067 # Binary: 00000000 00000000 10000000 01100111 # Fields: [imm[11:0]=0x0] [rs1=1(ra)] [f3=0b000] [rd=0(x0)] [opcode=0x67] 67 80 00 00 # ignore_byte: (0xDC) # Returns -1 to signal the caller to discard this character. # s0 (holder) and s1 (toggle) are NOT modified here or in ascii_comment, # so nibble state is preserved across ignored characters and comments. # # Behavioral note: non-hex characters (including whitespace, newlines, and # comments) are transparent to nibble pairing. This means hex digits CAN be # split across a comment or newline and will still combine into one byte. # Example: "A # comment\nB" produces byte 0xAB. # This matches the hex0 spec (non-hex = ignored), but may be surprising. # # Also: if Ctrl-D arrives while s1==0 (a first nibble was stored but no # second arrived), the partial nibble in s0 is silently discarded. # Instruction at 0x00DC: addi a0, x0, -1 # Hex (LE): 13 05 F0 FF → Word: 0xFFF00513 # Binary: 11111111 11110000 00000101 00010011 # Fields: [imm[11:0]=0xFFF] [rs1=0(x0)] [f3=0b000] [rd=10(a0)] [opcode=0x13] 13 05 F0 FF # Instruction at 0x00E0: jalr x0, ra, 0 # Hex (LE): 67 80 00 00 → Word: 0x00008067 # Binary: 00000000 00000000 10000000 01100111 # Fields: [imm[11:0]=0x0] [rs1=1(ra)] [f3=0b000] [rd=0(x0)] [opcode=0x67] 67 80 00 00 # ascii_comment: (0xE4) # We're going to loop multiple times, so we'll allocate 16 bytes of stack space # Instruction at 0x00E4: addi sp, sp, -16 # Hex (LE): 13 01 01 FF → Word: 0xFF010113 # Binary: 11111111 00000001 00000001 00010011 # Fields: [imm[11:0]=0xFF0] [rs1=2(sp)] [f3=0b000] [rd=2(sp)] [opcode=0x13] 13 01 01 FF # Then we'll save the return address before reading more characters # Instruction at 0x00E8: sd ra, 0(sp) # Hex (LE): 23 30 11 00 → Word: 0x00113023 # Binary: 00000000 00010001 00110000 00100011 # Fields: [imm[11:5]=0b0000000] [rs2=1(ra)] [rs1=2(sp)] [f3=0b011] [imm[4:0]=0b00000] [opcode=0x23] 23 30 11 00 # ascii_comment_loop: (0xEC) # This loop will read characters until we're done with comments # Instruction at 0x00EC: jal ra, 0x0060 # Hex (LE): EF F0 5F F7 → Word: 0xF75FF0EF # Binary: 11110111 01011111 11110000 11101111 # Fields: [imm[20]=0b1] [imm[10:1]=0b1110111010] [imm[11]=0b1] [imm[19:12]=0xFF] [rd=1(ra)] [opcode=0x6F] EF F0 5F F7 # Set t0 to carriage return # Instruction at 0x00F0: addi t0, x0, 0xD # Hex (LE): 93 02 D0 00 → Word: 0x00D00293 # Binary: 00000000 11010000 00000010 10010011 # Fields: [imm[11:0]=0xD] [rs1=0(x0)] [f3=0b000] [rd=5(t0)] [opcode=0x13] 93 02 D0 00 # Check if we got carriage return we're done # Instruction at 0x00F4: beq a0, t0, 0x0100 # Hex (LE): 63 06 55 00 → Word: 0x00550663 # Binary: 00000000 01010101 00000110 01100011 # Fields: [imm[12]=0b0] [imm[10:5]=0b000000] [rs2=5(t0)] [rs1=10(a0)] [f3=0b000] [imm[4:1]=0b0110] [imm[11]=0b0] [opcode=0x63] 63 06 55 00 # Set t0 to newline # Instruction at 0x00F8: addi t0, x0, 0xA # Hex (LE): 93 02 A0 00 → Word: 0x00A00293 # Binary: 00000000 10100000 00000010 10010011 # Fields: [imm[11:0]=0xA] [rs1=0(x0)] [f3=0b000] [rd=5(t0)] [opcode=0x13] 93 02 A0 00 # Loop back if we didn't get a newline # Instruction at 0x00FC: bne a0, t0, 0x00EC # Hex (LE): E3 18 55 FE → Word: 0xFE5518E3 # Binary: 11111110 01010101 00011000 11100011 # Fields: [imm[12]=0b1] [imm[10:5]=0b111111] [rs2=5(t0)] [rs1=10(a0)] [f3=0b001] [imm[4:1]=0b1000] [imm[11]=0b1] [opcode=0x63] E3 18 55 FE # ascii_comment_done: (0x100) # We're here if we got \r or \n, so we'll restore the return address # Instruction at 0x0100: ld ra, 0(sp) # Hex (LE): 83 30 01 00 → Word: 0x00013083 # Binary: 00000000 00000001 00110000 10000011 # Fields: [imm[11:0]=0x0] [rs1=2(sp)] [f3=0b011] [rd=1(ra)] [opcode=0x03] 83 30 01 00 # Deallocate the 16 bytes of stack space # Instruction at 0x0104: addi sp, sp, 0x10 # Hex (LE): 13 01 01 01 → Word: 0x01010113 # Binary: 00000001 00000001 00000001 00010011 # Fields: [imm[11:0]=0x10] [rs1=2(sp)] [f3=0b000] [rd=2(sp)] [opcode=0x13] 13 01 01 01 # Jump back to ignore_byte, which returns -1 to ignore as it's not program bytes # Instruction at 0x0108: jal x0, 0x00DC # Hex (LE): 6F F0 5F FD → Word: 0xFD5FF06F # Binary: 11111101 01011111 11110000 01101111 # Fields: [imm[20]=0b1] [imm[10:1]=0b1111101010] [imm[11]=0b1] [imm[19:12]=0xFF] [rd=0(x0)] [opcode=0x6F] 6F F0 5F FD # execute_code: (0x10C) # Zero all registers before jump (except sp and code location) # Instruction at 0x010C: addi a0, x0, 0 # Hex (LE): 13 05 00 00 → Word: 0x00000513 # Binary: 00000000 00000000 00000101 00010011 # Fields: [imm[11:0]=0x0] [rs1=0(x0)] [f3=0b000] [rd=10(a0)] [opcode=0x13] 13 05 00 00 # Instruction at 0x0110: addi a1, x0, 0 # Hex (LE): 93 05 00 00 → Word: 0x00000593 # Binary: 00000000 00000000 00000101 10010011 # Fields: [imm[11:0]=0x0] [rs1=0(x0)] [f3=0b000] [rd=11(a1)] [opcode=0x13] 93 05 00 00 # Instruction at 0x0114: addi a2, x0, 0 # Hex (LE): 13 06 00 00 → Word: 0x00000613 # Binary: 00000000 00000000 00000110 00010011 # Fields: [imm[11:0]=0x0] [rs1=0(x0)] [f3=0b000] [rd=12(a2)] [opcode=0x13] 13 06 00 00 # Instruction at 0x0118: addi a3, x0, 0 # Hex (LE): 93 06 00 00 → Word: 0x00000693 # Binary: 00000000 00000000 00000110 10010011 # Fields: [imm[11:0]=0x0] [rs1=0(x0)] [f3=0b000] [rd=13(a3)] [opcode=0x13] 93 06 00 00 # Instruction at 0x011C: addi a4, x0, 0 # Hex (LE): 13 07 00 00 → Word: 0x00000713 # Binary: 00000000 00000000 00000111 00010011 # Fields: [imm[11:0]=0x0] [rs1=0(x0)] [f3=0b000] [rd=14(a4)] [opcode=0x13] 13 07 00 00 # Instruction at 0x0120: addi a5, x0, 0 # Hex (LE): 93 07 00 00 → Word: 0x00000793 # Binary: 00000000 00000000 00000111 10010011 # Fields: [imm[11:0]=0x0] [rs1=0(x0)] [f3=0b000] [rd=15(a5)] [opcode=0x13] 93 07 00 00 # Instruction at 0x0124: addi a6, x0, 0 # Hex (LE): 13 08 00 00 → Word: 0x00000813 # Binary: 00000000 00000000 00001000 00010011 # Fields: [imm[11:0]=0x0] [rs1=0(x0)] [f3=0b000] [rd=16(a6)] [opcode=0x13] 13 08 00 00 # Instruction at 0x0128: addi a7, x0, 0 # Hex (LE): 93 08 00 00 → Word: 0x00000893 # Binary: 00000000 00000000 00001000 10010011 # Fields: [imm[11:0]=0x0] [rs1=0(x0)] [f3=0b000] [rd=17(a7)] [opcode=0x13] 93 08 00 00 # Instruction at 0x012C: addi t0, x0, 0 # Hex (LE): 93 02 00 00 → Word: 0x00000293 # Binary: 00000000 00000000 00000010 10010011 # Fields: [imm[11:0]=0x0] [rs1=0(x0)] [f3=0b000] [rd=5(t0)] [opcode=0x13] 93 02 00 00 # Instruction at 0x0130: addi t1, x0, 0 # Hex (LE): 13 03 00 00 → Word: 0x00000313 # Binary: 00000000 00000000 00000011 00010011 # Fields: [imm[11:0]=0x0] [rs1=0(x0)] [f3=0b000] [rd=6(t1)] [opcode=0x13] 13 03 00 00 # Instruction at 0x0134: addi t2, x0, 0 # Hex (LE): 93 03 00 00 → Word: 0x00000393 # Binary: 00000000 00000000 00000011 10010011 # Fields: [imm[11:0]=0x0] [rs1=0(x0)] [f3=0b000] [rd=7(t2)] [opcode=0x13] 93 03 00 00 # Instruction at 0x0138: addi t3, x0, 0 # Hex (LE): 13 0E 00 00 → Word: 0x00000E13 # Binary: 00000000 00000000 00001110 00010011 # Fields: [imm[11:0]=0x0] [rs1=0(x0)] [f3=0b000] [rd=28(t3)] [opcode=0x13] 13 0E 00 00 # Instruction at 0x013C: addi t4, x0, 0 # Hex (LE): 93 0E 00 00 → Word: 0x00000E93 # Binary: 00000000 00000000 00001110 10010011 # Fields: [imm[11:0]=0x0] [rs1=0(x0)] [f3=0b000] [rd=29(t4)] [opcode=0x13] 93 0E 00 00 # Instruction at 0x0140: addi t5, x0, 0 # Hex (LE): 13 0F 00 00 → Word: 0x00000F13 # Binary: 00000000 00000000 00001111 00010011 # Fields: [imm[11:0]=0x0] [rs1=0(x0)] [f3=0b000] [rd=30(t5)] [opcode=0x13] 13 0F 00 00 # Instruction at 0x0144: addi t6, x0, 0 # Hex (LE): 93 0F 00 00 → Word: 0x00000F93 # Binary: 00000000 00000000 00001111 10010011 # Fields: [imm[11:0]=0x0] [rs1=0(x0)] [f3=0b000] [rd=31(t6)] [opcode=0x13] 93 0F 00 00 # Instruction at 0x0148: addi s0, x0, 0 # Hex (LE): 13 04 00 00 → Word: 0x00000413 # Binary: 00000000 00000000 00000100 00010011 # Fields: [imm[11:0]=0x0] [rs1=0(x0)] [f3=0b000] [rd=8(s0)] [opcode=0x13] 13 04 00 00 # Instruction at 0x014C: addi s1, x0, 0 # Hex (LE): 93 04 00 00 → Word: 0x00000493 # Binary: 00000000 00000000 00000100 10010011 # Fields: [imm[11:0]=0x0] [rs1=0(x0)] [f3=0b000] [rd=9(s1)] [opcode=0x13] 93 04 00 00 # s2 already points to CODE_BASE, keep it # Instruction at 0x0150: addi s3, x0, 0 # Hex (LE): 93 09 00 00 → Word: 0x00000993 # Binary: 00000000 00000000 00001001 10010011 # Fields: [imm[11:0]=0x0] [rs1=0(x0)] [f3=0b000] [rd=19(s3)] [opcode=0x13] 93 09 00 00 # Instruction at 0x0154: addi s4, x0, 0 # Hex (LE): 13 0A 00 00 → Word: 0x00000A13 # Binary: 00000000 00000000 00001010 00010011 # Fields: [imm[11:0]=0x0] [rs1=0(x0)] [f3=0b000] [rd=20(s4)] [opcode=0x13] 13 0A 00 00 # Instruction at 0x0158: addi s5, x0, 0 # Hex (LE): 93 0A 00 00 → Word: 0x00000A93 # Binary: 00000000 00000000 00001010 10010011 # Fields: [imm[11:0]=0x0] [rs1=0(x0)] [f3=0b000] [rd=21(s5)] [opcode=0x13] 93 0A 00 00 # Instruction at 0x015C: addi s6, x0, 0 # Hex (LE): 13 0B 00 00 → Word: 0x00000B13 # Binary: 00000000 00000000 00001011 00010011 # Fields: [imm[11:0]=0x0] [rs1=0(x0)] [f3=0b000] [rd=22(s6)] [opcode=0x13] 13 0B 00 00 # Instruction at 0x0160: addi s7, x0, 0 # Hex (LE): 93 0B 00 00 → Word: 0x00000B93 # Binary: 00000000 00000000 00001011 10010011 # Fields: [imm[11:0]=0x0] [rs1=0(x0)] [f3=0b000] [rd=23(s7)] [opcode=0x13] 93 0B 00 00 # Instruction at 0x0164: addi s8, x0, 0 # Hex (LE): 13 0C 00 00 → Word: 0x00000C13 # Binary: 00000000 00000000 00001100 00010011 # Fields: [imm[11:0]=0x0] [rs1=0(x0)] [f3=0b000] [rd=24(s8)] [opcode=0x13] 13 0C 00 00 # Instruction at 0x0168: addi s9, x0, 0 # Hex (LE): 93 0C 00 00 → Word: 0x00000C93 # Binary: 00000000 00000000 00001100 10010011 # Fields: [imm[11:0]=0x0] [rs1=0(x0)] [f3=0b000] [rd=25(s9)] [opcode=0x13] 93 0C 00 00 # Instruction at 0x016C: addi s10, x0, 0 # Hex (LE): 13 0D 00 00 → Word: 0x00000D13 # Binary: 00000000 00000000 00001101 00010011 # Fields: [imm[11:0]=0x0] [rs1=0(x0)] [f3=0b000] [rd=26(s10)] [opcode=0x13] 13 0D 00 00 # Instruction at 0x0170: addi s11, x0, 0 # Hex (LE): 93 0D 00 00 → Word: 0x00000D93 # Binary: 00000000 00000000 00001101 10010011 # Fields: [imm[11:0]=0x0] [rs1=0(x0)] [f3=0b000] [rd=27(s11)] [opcode=0x13] 93 0D 00 00 # Jump to CODE_BASE (0x1190) using PC-relative addressing # auipc captures the current PC (0x174) and adds one 4KB page -> 0x1174 # Combined intent at 0x0174-0x0178: auipc/addi builds pc-relative t0 = 0x1190 (PC + 0x1000 + 0x1C) # Instruction at 0x0174: auipc t0, 0x1 # Hex (LE): 97 12 00 00 → Word: 0x00001297 # Binary: 00000000 00000000 00010010 10010111 # Fields: [imm[31:12]=0x1] [rd=5(t0)] [opcode=0x17] 97 12 00 00 # Adds 0x1C to bridge from 0x1174 to CODE_BASE (0x1190) # Instruction at 0x0178: addi t0, t0, 0x1C # Hex (LE): 93 82 C2 01 → Word: 0x01C28293 # Binary: 00000001 11000010 10000010 10010011 # Fields: [imm[11:0]=0x1C] [rs1=5(t0)] [f3=0b000] [rd=5(t0)] [opcode=0x13] 93 82 C2 01 # Jump to CODE_BASE, discard return address # Instruction at 0x017C: jalr x0, t0, 0 # Hex (LE): 67 80 02 00 → Word: 0x00028067 # Binary: 00000000 00000010 10000000 01100111 # Fields: [imm[11:0]=0x0] [rs1=5(t0)] [f3=0b000] [rd=0(x0)] [opcode=0x67] 67 80 02 00 # done: (0x180) # Infinite loop # Instruction at 0x0180: jal x0, 0x0180 # Hex (LE): 6F 00 00 00 → Word: 0x0000006F # Binary: 00000000 00000000 00000000 01101111 # Fields: [imm[20]=0b0] [imm[10:1]=0b0000000000] [imm[11]=0b0] [imm[19:12]=0x0] [rd=0(x0)] [opcode=0x6F] 6F 00 00 00