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