hex2.hex1 (39968B)
1 ## Copyright (C) 2021 Andrius Štikonas 2 ## This file is part of stage0. 3 ## 4 ## stage0 is free software: you can redistribute it and/or modify 5 ## it under the terms of the GNU General Public License as published by 6 ## the Free Software Foundation, either version 3 of the License, or 7 ## (at your option) any later version. 8 ## 9 ## stage0 is distributed in the hope that it will be useful, 10 ## but WITHOUT ANY WARRANTY; without even the implied warranty of 11 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 ## GNU General Public License for more details. 13 ## 14 ## You should have received a copy of the GNU General Public License 15 ## along with stage0. If not, see <http://www.gnu.org/licenses/>. 16 17 ## ELF Header 18 19 #:ELF_base 20 7F 45 4C 46 ## e_ident[EI_MAG0-3] ELF's magic number 21 22 01 ## e_ident[EI_CLASS] Indicating 32 bit 23 01 ## e_ident[EI_DATA] Indicating little endianness 24 01 ## e_ident[EI_VERSION] Indicating original elf 25 26 03 ## e_ident[EI_OSABI] Set at 3 because FreeBSD is strict 27 00 ## e_ident[EI_ABIVERSION] Set at 0 because none cares 28 29 00 00 00 00 00 00 00 ## e_ident[EI_PAD] 30 02 00 ## e_type Indicating Executable 31 F3 00 ## e_machine Indicating RISC-V 32 01 00 00 00 ## e_version Indicating original elf 33 34 54 00 60 00 ## e_entry Address of the entry point (Number of bytes this header is + Base Address) 35 34 00 00 00 ## e_phoff Address of program header table 36 00 00 00 00 ## e_shoff Address of section header table 37 38 00 00 00 00 ## e_flags 39 34 00 ## e_ehsize Indicating our 52 Byte header 40 41 20 00 ## e_phentsize size of a program header table 42 01 00 ## e_phnum number of entries in program table 43 44 00 00 ## e_shentsize size of a section header table 45 00 00 ## e_shnum number of entries in section table 46 47 00 00 ## e_shstrndx index of the section names 48 49 ## Program Header 50 #:ELF_program_headers 51 01 00 00 00 ## ph_type: PT-LOAD = 1 52 00 00 00 00 ## p_offset 53 54 00 00 60 00 ## ph_vaddr 55 00 00 60 00 ## ph_physaddr 56 57 D0 07 00 00 ## p_filesz 58 D0 07 00 00 ## p_memsz 59 60 07 00 00 00 ## ph_flags: PF-X|PF-W|PF-R = 7 61 01 00 00 00 ## ph_align 62 63 #:ELF_text 64 ; Register use: 65 ; s1: jump table 66 ; s2: input fd 67 ; s3: output fd 68 ; s4: toggle 69 ; s5: hold 70 ; s6: ip 71 ; s7: tempword 72 ; s8: shiftregister 73 ; s9: malloc pointer 74 ; s10: updates 75 76 ; Struct format: (size 12) 77 ; next => 0 ; Next element in linked list 78 ; target => 4 ; Target (ip) 79 ; name => 8 ; Label name 80 81 ; Our main function 82 #:_start 83 03 26 81 00 # rd_a2 rs1_sp !8 lw ; Input file name 84 85 ; Initialize globals 86 13 0A F0 FF # rd_s4 !-1 addi ; Toggle 87 93 0A 00 00 # rd_s5 addi ; Hold 88 37 0B 60 00 # rd_s6 ~0x600000 lui ; Instruction Pointer 89 90 ; Open input file and store FD in s2 91 93 08 80 03 # rd_a7 !56 addi ; sys_openat 92 13 05 C0 F9 # rd_a0 !-100 addi ; AT_FDCWD 93 93 05 06 00 # rd_a1 rs1_a2 mv ; file name 94 13 06 00 00 # rd_a2 addi ; read only 95 73 00 00 00 # ecall ; syscall 96 @F 63 40 05 00 # rs1_a0 @Fail bltz ; Error opening file 97 13 09 05 00 # rd_s2 rs1_a0 mv ; Save fd in for later 98 99 ; Set default FD for output file to stdout 100 93 09 10 00 # rd_s3 !1 addi 101 102 ; If we only have 2 arguments, don't use the third (it's not set) 103 93 02 20 00 # rd_t0 !2 addi 104 03 25 01 00 # rd_a0 rs1_sp lw ; Get number of the args 105 @F 63 40 55 00 # rs1_a0 rs2_t0 @Fail blt ; No input file provided 106 @a 63 00 55 00 # rs1_a0 rs2_t0 @after_open beq ; No output file provided. Use stdout 107 108 ; Open output file and store the FD in s3 109 93 08 80 03 # rd_a7 !56 addi ; sys_openat 110 13 05 C0 F9 # rd_a0 !-100 addi ; AT_FDCWD 111 83 25 C1 00 # rd_a1 rs1_sp !12 lw ; Output file (argument 3) 112 13 06 10 24 # rd_a2 !00001101 addi ; decimal 577 113 ; O_TRUNC 00001000 114 ; O_CREAT 00000100 115 ; O_WRONLY 00000001 116 ; OCTAL! 117 93 06 00 1C # rd_a3 !00700 addi ; Set read, write, execute permission on user 118 ; S_IRWXU 00700 119 ; OCTAL! 120 73 00 00 00 # ecall ; syscall 121 93 09 05 00 # rd_s3 rs1_a0 mv ; Save fd in for later 122 123 :a ;after_open 124 ; Prepare heap memory 125 93 08 60 0D # rd_a7 !214 addi ; sys_brk 126 13 05 00 00 # rd_a0 addi ; Get current brk 127 73 00 00 00 # ecall ; syscall 128 93 0C 05 00 # rd_s9 rs1_a0 addi ; Set our malloc pointer 129 130 B7 05 10 00 # rd_a1 ~0x100000 lui 131 33 05 B5 00 # rd_a0 rs1_a0 rs2_a1 add ; Request the 1 MiB 132 93 08 60 0D # rd_a7 !214 addi ; sys_brk 133 73 00 00 00 # ecall ; syscall 134 135 $C EF 00 00 00 # rd_ra $ClearScratch jal ; Zero scratch 136 $f EF 00 00 00 # rd_ra $First_pass jal ; First pass 137 138 ; Rewind input file 139 93 08 E0 03 # rd_a7 !62 addi ; sys_llseek 140 13 05 09 00 # rd_a0 rs1_s2 mv ; Input file descriptor 141 93 05 00 00 # rd_a1 mv ; Set offset to zero 142 13 06 00 00 # rd_a2 mv ; Set offset to zero 143 93 06 00 00 # rd_a3 mv ; Set result pointer to zero 144 13 07 00 00 # rd_a4 mv ; Set whence to zero 145 73 00 00 00 # ecall ; syscall 146 147 ; Initialize globals 148 13 0A F0 FF # rd_s4 !-1 addi ; Toggle 149 93 0A 00 00 # rd_s5 addi ; Hold 150 37 0B 60 00 # rd_s6 ~0x600000 lui ; Instruction Pointer 151 93 0B 00 00 # rd_s7 addi ; tempword 152 13 0C 00 00 # rd_s8 addi ; Shift register 153 154 $X EF 00 00 00 # rd_ra $Second_pass jal ; Now do the second pass 155 156 ; Terminate program with 0 return code 157 93 08 D0 05 # rd_a7 !93 addi ; sys_exit 158 13 05 00 00 # rd_a0 mv ; Return code 0 159 73 00 00 00 # ecall ; exit(0) 160 161 ; First pass loop to determine addresses of labels 162 :f ;First_pass 163 13 01 C1 FF # rd_sp rs1_sp !-4 addi ; Allocate stack 164 23 20 11 00 # rs1_sp rs2_ra sw ; protect ra 165 166 :1 ;First_pass_loop 167 $R EF 00 00 00 # rd_ra $Read_byte jal ; Get another byte 168 169 ; Deal with EOF 170 13 03 C0 FF # rd_t1 !-4 addi 171 @3 63 00 65 00 # rs1_a0 rs2_t1 @First_pass_done beq 172 173 ; Check for : 174 13 03 A0 03 # rd_t1 !0x3A addi 175 @L 63 00 65 00 # rs1_a0 rs2_t1 @StoreLabel beq ; Store this label 176 177 ; Check for . 178 13 03 E0 02 # rd_t1 !0x2E addi 179 @w 63 00 65 00 # rs1_a0 rs2_t1 @First_pass_UpdateWord beq 180 181 ; Check for % 182 13 03 50 02 # rd_t1 !0x25 addi 183 @p 63 00 65 00 # rs1_a0 rs2_t1 @First_pass_pointer beq 184 185 ; Check for & 186 13 03 60 02 # rd_t1 !0x26 addi 187 @p 63 00 65 00 # rs1_a0 rs2_t1 @First_pass_pointer beq 188 189 ; Check for ! 190 13 03 10 02 # rd_t1 !0x21 addi 191 @T 63 00 65 00 # rs1_a0 rs2_t1 @Throwaway_token beq 192 193 ; Check for @ 194 13 03 00 04 # rd_t1 !0x40 addi 195 @T 63 00 65 00 # rs1_a0 rs2_t1 @Throwaway_token beq 196 197 ; Check for $ 198 13 03 40 02 # rd_t1 !0x24 addi 199 @T 63 00 65 00 # rs1_a0 rs2_t1 @Throwaway_token beq 200 201 ; Check for ~ 202 13 03 E0 07 # rd_t1 !0x7E addi 203 @T 63 00 65 00 # rs1_a0 rs2_t1 @Throwaway_token beq 204 205 ; Check for < 206 13 03 C0 03 # rd_t1 !0x3C addi 207 93 05 F0 FF # rd_a1 !-1 addi ; write = false 208 @A 63 00 65 00 # rs1_a0 rs2_t1 @PadToAlign beq 209 210 93 05 F0 FF # rd_a1 !-1 addi ; write = false 211 13 06 F0 FF # rd_a2 !-1 addi ; update = false 212 $D EF 00 00 00 # rd_ra $DoByte jal ; Deal with everything else 213 214 13 03 C0 FF # rd_t1 !-4 addi ; Deal with EOF 215 @3 63 00 65 00 # rs1_a0 rs2_t1 @First_pass_done beq 216 217 $1 6F 00 00 00 # $First_pass_loop jal ; Keep looping 218 219 :T ;Throwaway_token 220 ~s 97 05 00 00 # rd_a1 ~scratch auipc 221 !s 93 85 05 00 # rd_a1 rs1_a1 !scratch addi ; get scratch 222 $c EF 00 00 00 # rd_ra $consume_token jal ; Read token 223 $C EF 00 00 00 # rd_ra $ClearScratch jal ; Throw away token 224 $1 6F 00 00 00 # $First_pass_loop jal ; Loop again 225 226 :p ;First_pass_pointer 227 13 0B 4B 00 # rd_s6 rs1_s6 !4 addi ; Update ip 228 ; Deal with Pointer to label 229 ~s 97 05 00 00 # rd_a1 ~scratch auipc 230 !s 93 85 05 00 # rd_a1 rs1_a1 !scratch addi ; Using scratch 231 $c EF 00 00 00 # rd_ra $consume_token jal ; Read token 232 $C EF 00 00 00 # rd_ra $ClearScratch jal ; Throw away token 233 13 03 E0 03 # rd_t1 !0x3E addi ; Check for '>' 234 @1 63 10 65 00 # rs1_a0 rs2_t1 @First_pass_loop bne ; Loop again 235 236 ; Deal with %label>label case 237 ~s 97 05 00 00 # rd_a1 ~scratch auipc 238 !s 93 85 05 00 # rd_a1 rs1_a1 !scratch addi ; Using scratch 239 $c EF 00 00 00 # rd_ra $consume_token jal ; Read token 240 $C EF 00 00 00 # rd_ra $ClearScratch jal ; Throw away token 241 $1 6F 00 00 00 # $First_pass_loop jal ; Loop again 242 243 :w ;First_pass_UpdateWord 244 13 0D 00 00 # rd_s10 addi ; updates = 0 245 93 0B 00 00 # rd_s7 addi ; tempword = 0 246 93 07 40 00 # rd_a5 !4 addi ; a5 = 4 247 :4 ;First_pass_UpdateWord_loop 248 $R EF 00 00 00 # rd_ra $Read_byte jal ; Read another byte into a0 249 250 93 05 F0 FF # rd_a1 !-1 addi ; write = false 251 13 06 00 00 # rd_a2 addi ; update = true 252 $D EF 00 00 00 # rd_ra $DoByte jal ; Process byte 253 @4 63 40 FD 00 # rs1_s10 rs2_a5 @First_pass_UpdateWord_loop blt ; loop 4 times 254 255 13 0B CB FF # rd_s6 rs1_s6 !-4 addi ; ip = ip - 4 256 257 $1 6F 00 00 00 # $First_pass_loop jal ; Loop again 258 259 :3 ;First_pass_done 260 83 20 01 00 # rd_ra rs1_sp lw ; restore ra 261 13 01 41 00 # rd_sp rs1_sp !4 addi ; deallocate stack 262 67 80 00 00 # rs1_ra jalr ; return 263 264 :X ;Second_pass 265 13 01 C1 FF # rd_sp rs1_sp !-4 addi ; Allocate stack 266 23 20 11 00 # rs1_sp rs2_ra sw ; protect ra 267 268 :5 ;Second_pass_loop 269 $R EF 00 00 00 # rd_ra $Read_byte jal ; Read another byte 270 271 ; Deal with EOF 272 13 03 C0 FF # rd_t1 !-4 addi ; Deal with EOF 273 @6 63 00 65 00 # rs1_a0 rs2_t1 @Second_pass_done beq 274 275 ; Drop the label 276 13 03 A0 03 # rd_t1 !0x3A addi 277 @7 63 10 65 00 # rs1_a0 rs2_t1 @Second_pass_0 bne 278 279 ~s 97 05 00 00 # rd_a1 ~scratch auipc 280 !s 93 85 05 00 # rd_a1 rs1_a1 !scratch addi ; Using scratch 281 $c EF 00 00 00 # rd_ra $consume_token jal ; Read the label 282 $C EF 00 00 00 # rd_ra $ClearScratch jal ; Throw away token 283 284 $5 6F 00 00 00 # $Second_pass_loop jal ; Continue looping 285 286 :7 ;Second_pass_0 287 ; Check for . 288 13 03 E0 02 # rd_t1 !0x2E addi 289 @8 63 00 65 00 # rs1_a0 rs2_t1 @Second_pass_UpdateWord beq 290 291 ; Check for % 292 13 03 50 02 # rd_t1 !0x25 addi 293 @S 63 00 65 00 # rs1_a0 rs2_t1 @StorePointer beq 294 295 ; Check for & 296 13 03 60 02 # rd_t1 !0x26 addi 297 @S 63 00 65 00 # rs1_a0 rs2_t1 @StorePointer beq 298 299 ; Check for ! 300 13 03 10 02 # rd_t1 !0x21 addi 301 @Y 63 00 65 00 # rs1_a0 rs2_t1 @UpdateShiftRegister beq 302 303 ; Check for @ 304 13 03 00 04 # rd_t1 !0x40 addi 305 @Y 63 00 65 00 # rs1_a0 rs2_t1 @UpdateShiftRegister beq 306 307 ; Check for $ 308 13 03 40 02 # rd_t1 !0x24 addi 309 @Y 63 00 65 00 # rs1_a0 rs2_t1 @UpdateShiftRegister beq 310 311 ; Check for ~ 312 13 03 E0 07 # rd_t1 !0x7E addi 313 @Y 63 00 65 00 # rs1_a0 rs2_t1 @UpdateShiftRegister beq 314 315 ; Check for < 316 13 03 C0 03 # rd_t1 !0x3C addi 317 93 05 00 00 # rd_a1 addi ; write = true 318 @A 63 00 65 00 # rs1_a0 rs2_t1 @PadToAlign beq 319 320 ; Deal with everything else 321 93 05 00 00 # rd_a1 addi ; write = true 322 13 06 F0 FF # rd_a2 !-1 addi ; update = false 323 $D EF 00 00 00 # rd_ra $DoByte jal ; Process our char 324 325 # Deal with EOF 326 13 03 C0 FF # rd_t1 !-4 addi 327 @6 63 00 65 00 # rs1_a0 rs2_t1 @Second_pass_done beq ; We are done 328 329 $5 6F 00 00 00 # $Second_pass_loop jal ; continue looping 330 331 :8 ;Second_pass_UpdateWord 332 13 0D 00 00 # rd_s10 addi ; updates = 0 333 93 0B 00 00 # rd_s7 addi ; tempword = 0 334 93 07 40 00 # rd_a5 !4 addi ; a5 = 4 335 336 :9 ;Second_pass_UpdateWord_loop 337 $R EF 00 00 00 # rd_ra $Read_byte jal ; Read another byte into a0 338 339 93 05 F0 FF # rd_a1 !-1 addi ; write = false 340 13 06 00 00 # rd_a2 addi ; update = true 341 $D EF 00 00 00 # rd_ra $DoByte jal ; Process our char 342 @9 63 40 FD 00 # rs1_s10 rs2_a5 @Second_pass_UpdateWord_loop blt ; loop 4 times 343 344 13 85 0B 00 # rd_a0 rs1_s7 mv ; tempword 345 $d 6F 00 00 00 # $UpdateShiftRegister_DOT jal ; UpdateShiftRegister('.', tempword) 346 347 :Y ;UpdateShiftRegister 348 13 06 05 00 # rd_a2 rs1_a0 mv ; Store label prefix 349 ~s 97 05 00 00 # rd_a1 ~scratch auipc 350 !s 93 85 05 00 # rd_a1 rs1_a1 !scratch addi ; Get scratch 351 $C EF 00 00 00 # rd_ra $ClearScratch jal ; Clear scratch 352 $c EF 00 00 00 # rd_ra $consume_token jal ; Read token 353 $G EF 00 00 00 # rd_ra $GetTarget jal ; Get target 354 03 25 05 00 # rd_a0 rs1_a0 lw ; Dereference pointer 355 33 05 65 41 # rd_a0 rs1_a0 rs2_s6 sub ; target - ip 356 357 ; Check for ! 358 13 03 10 02 # rd_t1 !0x21 addi 359 @I 63 00 66 00 # rs1_a2 rs2_t1 @UpdateShiftRegister_I beq 360 361 ; Check for @ 362 13 03 00 04 # rd_t1 !0x40 addi 363 @B 63 00 66 00 # rs1_a2 rs2_t1 @UpdateShiftRegister_B beq 364 365 ; Check for $ 366 13 03 40 02 # rd_t1 !0x24 addi 367 @J 63 00 66 00 # rs1_a2 rs2_t1 @UpdateShiftRegister_J beq 368 369 ; Check for ~ 370 13 03 E0 07 # rd_t1 !0x7E addi 371 @U 63 00 66 00 # rs1_a2 rs2_t1 @UpdateShiftRegister_U beq 372 373 $5 6F 00 00 00 # $Second_pass_loop jal ; continue looping 374 375 :d ;UpdateShiftRegister_DOT 376 ; . before instruction means it has to be added to the final word 377 378 ; swap = (((value >> 24) & 0xff) | 379 ; ((value << 8) & 0xff0000) | 380 ; ((value >> 8) & 0xff00) | 381 ; ((value << 24) & 0xff000000)) 382 383 93 53 85 01 # rd_t2 rs1_a0 rs2_x24 srli ; value >> 24 384 13 03 F0 0F # rd_t1 !0xFF addi ; t1 = 0xff 385 B3 72 73 00 # rd_t0 rs1_t1 rs2_t2 and ; (value >> 24) & 0xff 386 387 93 13 85 00 # rd_t2 rs1_a0 rs2_x8 slli ; value << 8 388 37 03 FF 00 # rd_t1 ~0xFF0000 lui ; t1 = 0xff0000 389 B3 73 73 00 # rd_t2 rs1_t1 rs2_t2 and ; (value << 8) & 0xff0000 390 B3 E2 72 00 # rd_t0 rs1_t0 rs2_t2 or ; logical or with the previous expression 391 392 93 53 85 00 # rd_t2 rs1_a0 rs2_x8 srli ; value >> 8 393 37 03 01 00 # rd_t1 ~0xFF00 lui ; t1 = 0xff00 394 13 03 03 F0 # rd_t1 rs1_t1 !0xFF00 addi ; t1 = 0xff00 395 B3 73 73 00 # rd_t2 rs1_t1 rs2_t2 and ; (value << 8) & 0xff00 396 B3 E2 72 00 # rd_t0 rs1_t0 rs2_t2 or ; logical or with the previous expression 397 398 93 13 85 01 # rd_t2 rs1_a0 rs2_x24 slli ; value << 24 399 13 03 F0 0F # rd_t1 !0xFF addi 400 13 13 83 01 # rd_t1 rs1_t1 rs2_x24 slli ; t1 = 0xff000000 401 B3 73 73 00 # rd_t2 rs1_t1 rs2_t2 and ; (value << 24) & 0xff000000 402 B3 E2 72 00 # rd_t0 rs1_t0 rs2_t2 or ; swap 403 404 33 4C 5C 00 # rd_s8 rs1_s8 rs2_t0 xor ; shiftregister = shiftregister ^ swap 405 406 13 0B CB FF # rd_s6 rs1_s6 !-4 addi ; ip = ip - 4 407 $5 6F 00 00 00 # $Second_pass_loop jal ; continue looping 408 409 :I ;UpdateShiftRegister_I 410 ; Corresponds to RISC-V I format 411 13 05 45 00 # rd_a0 rs1_a0 !4 addi ; add 4 due to this being 2nd part of auipc combo 412 413 37 13 00 00 # rd_t1 ~0xFFF lui ; load higher bits 414 13 03 F3 FF # rd_t1 rs1_t1 !0xFFF addi 415 33 73 65 00 # rd_t1 rs1_a0 rs2_t1 and ; (value & 0xfff) 416 93 1B 43 01 # rd_s7 rs1_t1 rs2_x20 slli ; tempword = (value & 0xfff) << 20 417 33 4C 7C 01 # rd_s8 rs1_s8 rs2_s7 xor ; shiftregister = shiftregister ^ tempword 418 419 $5 6F 00 00 00 # $Second_pass_loop jal ; continue looping 420 421 :B ;UpdateShiftRegister_B 422 ; Corresponds to RISC-V B format 423 424 ; tempword = ((value & 0x1e) << 7) ; imm[4:1] 425 ; | ((value & 0x7e0) << (31 - 11)) ; imm[10:5] 426 ; | ((value & 0x800) >> 4) ; imm[11] 427 ; | ((value & 0x1000) << (31 - 12)) ; imm[12] 428 429 13 03 E0 01 # rd_t1 !0x1E addi 430 33 73 65 00 # rd_t1 rs1_a0 rs2_t1 and ; value & 0x1e 431 93 12 73 00 # rd_t0 rs1_t1 rs2_x7 slli ; tempword = (value & 0x1e) << 7 432 433 13 03 00 7E # rd_t1 !0x7E0 addi 434 33 73 65 00 # rd_t1 rs1_a0 rs2_t1 and ; value & 0x7e0 435 13 13 43 01 # rd_t1 rs1_t1 rs2_x20 slli ; (value & 0x7e0) << (31 - 11) 436 B3 E2 62 00 # rd_t0 rs1_t0 rs2_t1 or ; logical or with the previous expression 437 438 37 13 00 00 # rd_t1 ~0x800 lui ; load higher bits 439 13 03 03 80 # rd_t1 rs1_t1 !0x800 addi 440 33 73 65 00 # rd_t1 rs1_a0 rs2_t1 and ; value & 0x800 441 13 53 43 00 # rd_t1 rs1_t1 rs2_x4 srli ; (value & 0x800) >> 4 442 B3 E2 62 00 # rd_t0 rs1_t0 rs2_t1 or ; logical or with the previous expression 443 444 37 13 00 00 # rd_t1 ~0x1000 lui ; load higher bits 445 33 73 65 00 # rd_t1 rs1_a0 rs2_t1 and ; value & 0x1000 446 13 13 33 01 # rd_t1 rs1_t1 rs2_x19 slli ; (value & 0x1000) << (31 - 12) 447 B3 EB 62 00 # rd_s7 rs1_t0 rs2_t1 or ; logical or with the previous expression 448 449 33 4C 7C 01 # rd_s8 rs1_s8 rs2_s7 xor ; shiftregister = shiftregister ^ tempword 450 451 $5 6F 00 00 00 # $Second_pass_loop jal ; continue looping 452 453 :J ;UpdateShiftRegister_J 454 ; Corresponds to RISC-V J format 455 456 ; tempword = ((value & 0x7fe) << (30 - 10)) ; imm[10:1] 457 ; | ((value & 0x800) << (20 - 11)) ; imm[11] 458 ; | ((value & 0xff000)) ; imm[19:12] 459 ; | ((value & 0x100000) << (31 - 20)) ; imm[20] 460 461 13 03 E0 7F # rd_t1 !0x7FE addi 462 33 73 65 00 # rd_t1 rs1_a0 rs2_t1 and ; value & 0x7fe 463 93 12 43 01 # rd_t0 rs1_t1 rs2_x20 slli ; tempword = (value & 0x7fe) << 20 464 465 37 13 00 00 # rd_t1 ~0x800 lui ; load higher bits 466 13 03 03 80 # rd_t1 rs1_t1 !0x800 addi 467 33 73 65 00 # rd_t1 rs1_a0 rs2_t1 and ; value & 0x800 468 13 13 93 00 # rd_t1 rs1_t1 rs2_x9 slli ; (value & 0x800) << (20 - 11) 469 B3 E2 62 00 # rd_t0 rs1_t0 rs2_t1 or ; logical or with the previous expression 470 471 37 F3 0F 00 # rd_t1 ~0xFF000 lui ; load higher bits 472 33 73 65 00 # rd_t1 rs1_a0 rs2_t1 and ; value & 0xff000 473 B3 E2 62 00 # rd_t0 rs1_t0 rs2_t1 or ; logical or with the previous expression 474 475 37 03 10 00 # rd_t1 ~0x100000 lui ; load higher bits 476 33 73 65 00 # rd_t1 rs1_a0 rs2_t1 and ; value & 0x100000 477 13 13 B3 00 # rd_t1 rs1_t1 rs2_x11 slli ; (value & 0x100000) << (31 - 20) 478 B3 EB 62 00 # rd_s7 rs1_t0 rs2_t1 or ; logical or with the previous expression 479 480 33 4C 7C 01 # rd_s8 rs1_s8 rs2_s7 xor ; shiftregister = shiftregister ^ tempword 481 482 $5 6F 00 00 00 # $Second_pass_loop jal ; continue looping 483 484 :U ;UpdateShiftRegister_U 485 ; Corresponds to RISC-V U format 486 ; if value is 0x800 or more we have to add 11-th bit (0x1000) to compensate for signed extension 487 488 B7 12 00 00 # rd_t0 ~0x800 lui ; load higher bits 489 93 82 02 80 # rd_t0 rs1_t0 !0x800 addi 490 37 13 00 00 # rd_t1 ~0xFFF lui ; load higher bits 491 13 03 F3 FF # rd_t1 rs1_t1 !0xFFF addi 492 493 ; We are outside 31-bit that ~ can normally load 494 B7 03 10 00 # rd_t2 ~0x100000 lui ; load 0xfffff000 495 93 83 F3 FF # rd_t2 rs1_t2 !-1 addi ; load 0xfffff000 496 93 93 C3 00 # rd_t2 rs1_t2 rs2_x12 slli ; load 0xfffff000 497 33 73 65 00 # rd_t1 rs1_a0 rs2_t1 and ; value & 0xfff 498 B3 7B 75 00 # rd_s7 rs1_a0 rs2_t2 and ; value & 0xfffff000 499 @u 63 40 53 00 # rs1_t1 rs2_t0 @UpdateShiftRegister_U_small blt 500 501 # Deal with sign extension: add 0x1000 502 B7 12 00 00 # rd_t0 ~0x1000 lui ; load higher bits 503 B3 8B 72 01 # rd_s7 rs1_t0 rs2_s7 add ; (value & 0xfffff000) + 0x1000 504 505 :u ;UpdateShiftRegister_U_small 506 33 4C 7C 01 # rd_s8 rs1_s8 rs2_s7 xor ; shiftregister = shiftregister ^ tempword 507 508 $5 6F 00 00 00 # $Second_pass_loop jal ; continue looping 509 510 :S ;StorePointer 511 13 0B 4B 00 # rd_s6 rs1_s6 !4 addi ; update ip 512 13 06 05 00 # rd_a2 rs1_a0 mv ; Store label prefix 513 514 ~s 97 05 00 00 # rd_a1 ~scratch auipc 515 !s 93 85 05 00 # rd_a1 rs1_a1 !scratch addi ; Get scratch 516 $C EF 00 00 00 # rd_ra $ClearScratch jal ; clear scratch 517 $c EF 00 00 00 # rd_ra $consume_token jal ; Read token 518 93 07 05 00 # rd_a5 rs1_a0 mv ; save char 519 $G EF 00 00 00 # rd_ra $GetTarget jal ; Get target 520 83 25 05 00 # rd_a1 rs1_a0 lw ; Dereference pointer 521 522 ; If char is > then change relative base address to ip 523 13 03 E0 03 # rd_t1 !0x3E addi ; t1 = 0x3e 524 @P 63 00 F3 00 # rs1_t1 rs2_a5 @StorePointer_1 beq 525 526 ; Check for & 527 13 03 60 02 # rd_t1 !0x26 addi 528 @0 63 00 66 00 # rs1_a2 rs2_t1 @StorePointer_0 beq 529 530 ; Check for % 531 13 03 50 02 # rd_t1 !0x25 addi 532 @F 63 10 66 00 # rs1_a2 rs2_t1 @Fail bne 533 B3 85 65 41 # rd_a1 rs1_a1 rs2_s6 sub ; displacement = target - ip 534 535 :0 ;StorePointer_0 536 ; Output pointer 537 93 07 40 00 # rd_a5 !4 addi ; number of bytes 538 :l ;StorePointer_loop 539 13 D3 85 00 # rd_t1 rs1_a1 rs2_x8 srli ; value / 256 540 13 15 83 00 # rd_a0 rs1_t1 rs2_x8 slli 541 33 85 A5 40 # rd_a0 rs1_a1 rs2_a0 sub ; byte = value % 256 542 543 93 05 03 00 # rd_a1 rs1_t1 mv ; value = value / 256 544 $t EF 00 00 00 # rd_ra $fputc jal ; write value 545 93 87 F7 FF # rd_a5 rs1_a5 !-1 addi ; decrease number of bytes to write 546 @l 63 90 07 00 # rs1_a5 @StorePointer_loop bnez ; continue looping 547 548 $5 6F 00 00 00 # $Second_pass_loop jal ; Continue looping 549 550 :P ;StorePointer_1 551 13 86 05 00 # rd_a2 rs1_a1 mv ; save target 552 ~s 97 05 00 00 # rd_a1 ~scratch auipc 553 !s 93 85 05 00 # rd_a1 rs1_a1 !scratch addi ; Get scratch 554 $C EF 00 00 00 # rd_ra $ClearScratch jal ; clear scratch 555 $c EF 00 00 00 # rd_ra $consume_token jal ; consume token 556 $G EF 00 00 00 # rd_ra $GetTarget jal ; Get target 557 83 25 05 00 # rd_a1 rs1_a0 lw ; Dereference pointer 558 B3 05 B6 40 # rd_a1 rs1_a2 rs2_a1 sub ; displacement = target - ip 559 560 $0 6F 00 00 00 # $StorePointer_0 jal ; Continue looping 561 562 :6 ;Second_pass_done 563 83 20 01 00 # rd_ra rs1_sp lw ; restore ra 564 13 01 41 00 # rd_sp rs1_sp !4 addi ; deallocate stack 565 67 80 00 00 # rs1_ra jalr ; return 566 567 ; Pad with zeros to align to word size 568 ; bool write in a1 569 :A ;PadToAlign 570 13 03 10 00 # rd_t1 !1 addi ; t1 = 1 571 33 75 6B 00 # rd_a0 rs1_s6 rs2_t1 and ; ip & 0x1 572 @b 63 10 65 00 # rs1_a0 rs2_t1 @PadToAlign_1 bne ; check if ip & 0x1 == 1 573 33 0B 6B 00 # rd_s6 rs1_s6 rs2_t1 add ; ip = ip + 1 574 575 @b 63 90 05 00 # rs1_a1 @PadToAlign_1 bnez ; check if we have to write 576 13 05 00 00 # rd_a0 mv ; a0 = 0 577 $t EF 00 00 00 # rd_ra $fputc jal ; write 0 578 579 :b ;PadToAlign_1 580 13 03 20 00 # rd_t1 !2 addi ; t1 = 2 581 33 75 6B 00 # rd_a0 rs1_s6 rs2_t1 and ; ip & 0x1 582 @e 63 10 65 00 # rs1_a0 rs2_t1 @PadToAlign_2 bne ; check if ip & 0x2 == 2 583 33 0B 6B 00 # rd_s6 rs1_s6 rs2_t1 add ; ip = ip + 2 584 585 @e 63 90 05 00 # rs1_a1 @PadToAlign_2 bnez ; check if we have to write 586 13 05 00 00 # rd_a0 mv ; a0 = 0 587 $t EF 00 00 00 # rd_ra $fputc jal ; write 0 588 13 05 00 00 # rd_a0 mv ; a0 = 0 589 $t EF 00 00 00 # rd_ra $fputc jal ; write 0 590 591 :e ;PadToAlign_2 592 @5 63 80 05 00 # rs1_a1 @Second_pass_loop beqz ; return to Second_pass 593 $1 6F 00 00 00 # $First_pass_loop jal ; return to First_pass 594 595 ; Zero scratch area 596 :C ;ClearScratch 597 13 01 41 FF # rd_sp rs1_sp !-12 addi ; Allocate stack 598 23 20 11 00 # rs1_sp rs2_ra SD ; protect ra 599 23 22 A1 00 # rs1_sp rs2_a0 @4 SD ; protect a0 600 23 24 B1 00 # rs1_sp rs2_a1 @8 SD ; protect a1 601 602 ~s 17 05 00 00 # rd_a0 ~scratch auipc 603 !s 13 05 05 00 # rd_a0 rs1_a0 !scratch addi ; Find where our scratch area is 604 605 :g ;ClearScratch_loop 606 83 05 05 00 # rd_a1 rs1_a0 lb ; Read current byte: s[i] 607 23 00 05 00 # rs1_a0 sb ; Write zero: s[i] = 0 608 13 05 15 00 # rd_a0 rs1_a0 !1 addi ; Increment: i = i + 1 609 @g 63 90 05 00 # rs1_a1 @ClearScratch_loop bnez ; Keep looping 610 611 83 20 01 00 # rd_ra rs1_sp lw ; restore ra 612 03 25 41 00 # rd_a0 rs1_sp !4 lw ; restore a0 613 83 25 81 00 # rd_a1 rs1_sp !8 lw ; restore a1 614 13 01 C1 00 # rd_sp rs1_sp !12 addi ; Deallocate stack 615 67 80 00 00 # rs1_ra jalr ; return 616 617 ; Receives pointer in a1 618 ; Writes our token and updates pointer in a1 619 :c ;consume_token 620 13 01 C1 FF # rd_sp rs1_sp !-4 addi ; Allocate stack 621 23 20 11 00 # rs1_sp rs2_ra sw ; protect ra 622 623 :h ;consume_token_0 624 $R EF 00 00 00 # rd_ra $Read_byte jal ; Read byte into a0 625 626 ; Check for \t 627 13 03 90 00 # rd_t1 !0x09 addi 628 @j 63 00 65 00 # rs1_a0 rs2_t1 @consume_token_done beq 629 630 ; Check for \n 631 13 03 A0 00 # rd_t1 !0x0A addi 632 @j 63 00 65 00 # rs1_a0 rs2_t1 @consume_token_done beq 633 634 ; Check for ' ' 635 13 03 00 02 # rd_t1 !0x20 addi 636 @j 63 00 65 00 # rs1_a0 rs2_t1 @consume_token_done beq 637 638 ; Check for > 639 13 03 E0 03 # rd_t1 !0x3E addi 640 @j 63 00 65 00 # rs1_a0 rs2_t1 @consume_token_done beq 641 642 23 80 A5 00 # rs1_a1 rs2_a0 sb ; Store char 643 93 85 15 00 # rd_a1 rs1_a1 !1 addi ; Point to next spot 644 $h 6F 00 00 00 # $consume_token_0 jal ; Continue looping 645 646 :j ;consume_token_done 647 23 A0 05 00 # rs1_a1 sw ; Pad with nulls 648 93 85 45 00 # rd_a1 rs1_a1 !4 addi ; Update the pointer 649 650 83 20 01 00 # rd_ra rs1_sp lw ; restore ra 651 13 01 41 00 # rd_sp rs1_sp !4 addi ; deallocate stack 652 67 80 00 00 # rs1_ra jalr ; return 653 654 ; DoByte function 655 ; Receives: 656 ; character in a0 657 ; bool write in a1 658 ; bool update in a2 659 ; Does not return anything 660 :D ;DoByte 661 13 01 81 FF # rd_sp rs1_sp !-8 addi ; Allocate stack 662 23 20 11 00 # rs1_sp rs2_ra sw ; protect ra 663 23 22 01 01 # rs1_sp rs2_a6 @4 sw ; protect a6 664 665 $H EF 00 00 00 # rd_ra $hex jal ; Process hex, store it in a6 666 667 @k 63 40 08 00 # rs1_a6 @DoByte_Done bltz ; Deal with EOF and unrecognized characters 668 669 @2 63 10 0A 00 # rs1_s4 @DoByte_NotToggle bnez ; Check if toggle is set 670 671 ; toggle = true 672 @m 63 90 05 00 # rs1_a1 @DoByte_1 bnez ; check if we have to write 673 674 ; write = true 675 ; We calculate (hold * 16) + hex(c) ^ sr_nextb() 676 ; First, calculate new shiftregister 677 93 02 F0 0F # rd_t0 !0xFF addi 678 B3 72 5C 00 # rd_t0 rs1_s8 rs2_t0 and ; sr_nextb = shiftregister & 0xff 679 13 5C 8C 00 # rd_s8 rs1_s8 rs2_x8 srli ; shiftregister >> 8 680 681 B3 C2 02 01 # rd_t0 rs1_t0 rs2_a6 xor ; hex(c) ^ sr_nextb 682 13 93 4A 00 # rd_t1 rs1_s5 rs2_x4 slli ; hold << 4 683 33 85 62 00 # rd_a0 rs1_t0 rs2_t1 add ; (hold << 4) + hex(c) ^ sr_nextb() 684 $t EF 00 00 00 # rd_ra $fputc jal ; print it 685 @F 63 00 05 00 # rs1_a0 @Fail beqz ; Fail if nothing was written 686 687 :m ;DoByte_1 688 13 0B 1B 00 # rd_s6 rs1_s6 !1 addi ; Increment IP 689 @o 63 00 06 00 # rs1_a2 @DoByte_2 beqz ; check if we have to update 690 :n ;DoByte_2b 691 93 0A 00 00 # rd_s5 mv ; hold = 0 692 $q 6F 00 00 00 # $DoByte_FlipToggle jal ; return 693 694 :2 ;DoByte_NotToggle 695 93 0A 08 00 # rd_s5 rs1_a6 mv ; hold = hex(c) 696 697 :q ;DoByte_FlipToggle 698 13 4A FA FF # rd_s4 rs1_s4 not ; Flip the toggle 699 700 :k ;DoByte_Done 701 83 20 01 00 # rd_ra rs1_sp lw ; restore ra 702 03 28 41 00 # rd_a6 rs1_sp !4 lw ; restore a6 703 13 01 81 00 # rd_sp rs1_sp !8 addi ; Deallocate stack 704 67 80 00 00 # rs1_ra jalr ; return 705 706 :o ;DoByte_2 707 13 93 4A 00 # rd_t1 rs1_s5 rs2_x4 slli ; hold * 16 708 B3 0A 03 01 # rd_s5 rs1_t1 rs2_a6 add ; hold = hold * 16 + hex(c) 709 13 93 8B 00 # rd_t1 rs1_s7 rs2_x8 slli ; tempword << 8 710 B3 4B 53 01 # rd_s7 rs1_t1 rs2_s5 xor ; tempword = (tempword << 8) ^ hold 711 13 0D 1D 00 # rd_s10 rs1_s10 !1 addi ; updates = updates + 1 712 $n 6F 00 00 00 # $DoByte_2b jal 713 714 ; Convert ASCII hex characters into binary representation, e.g. 'a' -> 0xA 715 ; Receives: 716 ; character in a0 717 ; Returns: 718 ; a6 with character's hex value. 719 :H ;hex 720 13 01 81 FF # rd_sp rs1_sp !-8 addi ; Allocate stack 721 23 20 11 00 # rs1_sp rs2_ra sw ; protect ra 722 23 22 B1 00 # rs1_sp rs2_a1 @4 sw ; protect a1 723 724 ; Deal with EOF 725 13 03 C0 FF # rd_t1 !-4 addi 726 @r 63 00 65 00 # rs1_a0 rs2_t1 @hex_return beq 727 728 ; deal with line comments starting with # 729 13 03 30 02 # rd_t1 !0x23 addi 730 @x 63 00 65 00 # rs1_a0 rs2_t1 @ascii_comment beq ; a0 eq to '#' 731 732 ; deal with line comments starting with ; 733 13 03 B0 03 # rd_t1 !0x3B addi 734 @x 63 00 65 00 # rs1_a0 rs2_t1 @ascii_comment beq ; a0 eq to ';' 735 736 ; deal all ascii less than 0 737 13 03 00 03 # rd_t1 !0x30 addi 738 @y 63 40 65 00 # rs1_a0 rs2_t1 @ascii_other blt 739 740 ; deal with 0-9 741 13 03 A0 03 # rd_t1 !0x3A addi 742 @N 63 40 65 00 # rs1_a0 rs2_t1 @ascii_num blt 743 744 ; deal with all ascii less than A 745 13 03 10 04 # rd_t1 !0x41 addi 746 @y 63 40 65 00 # rs1_a0 rs2_t1 @ascii_other blt 747 748 ; deal with A-F 749 13 03 70 04 # rd_t1 !0x47 addi 750 @z 63 40 65 00 # rs1_a0 rs2_t1 @ascii_high blt 751 752 ; deal with all ascii less than a 753 13 03 10 06 # rd_t1 !0x61 addi 754 @y 63 40 65 00 # rs1_a0 rs2_t1 @ascii_other blt 755 756 ; deal with a-f 757 13 03 70 06 # rd_t1 !0x67 addi 758 @Z 63 40 65 00 # rs1_a0 rs2_t1 @ascii_low blt 759 760 ; The rest that remains needs to be ignored 761 $y 6F 00 00 00 # $ascii_other jal 762 763 :N ;ascii_num 764 13 03 00 03 # rd_t1 !0x30 addi ; '0' -> 0 765 33 08 65 40 # rd_a6 rs1_a0 rs2_t1 sub 766 $r 6F 00 00 00 # $hex_return jal ; return 767 :Z ;ascii_low 768 13 03 70 05 # rd_t1 !0x57 addi ; 'a' -> 0xA 769 33 08 65 40 # rd_a6 rs1_a0 rs2_t1 sub 770 $r 6F 00 00 00 # $hex_return jal ; return 771 :z ;ascii_high 772 13 03 70 03 # rd_t1 !0x37 addi ; 'A' -> 0xA 773 33 08 65 40 # rd_a6 rs1_a0 rs2_t1 sub 774 $r 6F 00 00 00 # $hex_return jal ; return 775 :y ;ascii_other 776 13 08 F0 FF # rd_a6 !-1 addi ; Return -1 777 $r 6F 00 00 00 # $hex_return jal ; return 778 :x ;ascii_comment ; Read the comment until newline 779 $R EF 00 00 00 # rd_ra $Read_byte jal 780 13 03 D0 00 # rd_t1 !0xD addi ; CR 781 @E 63 00 65 00 # rs1_a0 rs2_t1 @ascii_comment_cr beq 782 13 03 A0 00 # rd_t1 !0xA addi ; LF 783 @x 63 10 65 00 # rs1_a0 rs2_t1 @ascii_comment bne ; Keep reading comment 784 :E ;ascii_comment_cr 785 13 08 F0 FF # rd_a6 !-1 addi ; Return -1 786 :r ;hex_return 787 83 20 01 00 # rd_ra rs1_sp lw ; restore ra 788 83 25 41 00 # rd_a1 rs1_sp !4 lw ; restore a1 789 13 01 81 00 # rd_sp rs1_sp !8 addi ; Deallocate stack 790 67 80 00 00 # rs1_ra jalr ; return 791 792 ; Read byte into a0 793 :R ;Read_byte 794 13 01 41 FF # rd_sp rs1_sp !-12 addi ; Allocate stack 795 23 22 B1 00 # rs1_sp rs2_a1 @4 sw ; protect a1 796 23 24 C1 00 # rs1_sp rs2_a2 @8 sw ; protect a2 797 798 93 08 F0 03 # rd_a7 !63 addi ; sys_read 799 13 05 09 00 # rd_a0 rs1_s2 mv ; File descriptor 800 93 05 01 00 # rd_a1 rs1_sp mv ; Get stack address for buffer 801 13 06 10 00 # rd_a2 !1 addi ; Size of what we want to read 802 73 00 00 00 # ecall ; syscall 803 804 @K 63 00 05 00 # rs1_a0 @Read_byte_1 beqz ; Deal with EOF 805 03 C5 05 00 # rd_a0 rs1_a1 lbu ; return char in a0 806 807 $M 6F 00 00 00 # $Read_byte_done jal ; return 808 809 :K ;Read_byte_1 810 13 05 C0 FF # rd_a0 !-4 addi ; Put EOF in a0 811 :M ;Read_byte_done 812 83 25 41 00 # rd_a1 rs1_sp !4 lw ; restore a1 813 03 26 81 00 # rd_a2 rs1_sp !8 lw ; restore a2 814 13 01 C1 00 # rd_sp rs1_sp !12 addi ; Deallocate stack 815 67 80 00 00 # rs1_ra jalr ; return 816 817 ; Find a label matching pointer in scratch 818 ; Returns a pointer in a0 819 :G ;GetTarget 820 13 01 C1 FF # rd_sp rs1_sp !-4 addi ; Allocate stack 821 23 20 11 00 # rs1_sp rs2_ra sw ; protect ra 822 823 93 82 04 00 # rd_t0 rs1_s1 mv ; grab jump_table 824 825 :O ;GetTarget_loop_0 826 ; Compare the strings 827 ~s 17 03 00 00 # rd_t1 ~scratch auipc 828 !s 13 03 03 00 # rd_t1 rs1_t1 !scratch addi ; reset scratch 829 83 A3 82 00 # rd_t2 rs1_t0 !8 lw ; I->name 830 :Q ;GetTarget_loop 831 83 CE 03 00 # rd_t4 rs1_t2 lbu ; I->name[i] 832 03 4E 03 00 # rd_t3 rs1_t1 lbu ; scratch[i] 833 @v 63 10 DE 01 # rs1_t3 rs2_t4 @GetTarget_miss bne ; strings don't match 834 835 ; Look at the next char 836 13 03 13 00 # rd_t1 rs1_t1 !1 addi 837 93 83 13 00 # rd_t2 rs1_t2 !1 addi 838 @Q 63 90 0E 00 # rs1_t4 @GetTarget_loop bnez ; Loop until zero (end of string) 839 $V 6F 00 00 00 # $GetTarget_done jal ; We have a match 840 841 :v ;GetTarget_miss 842 83 A2 02 00 # rd_t0 rs1_t0 lw ; I = I->next 843 @F 63 80 02 00 # rs1_t0 @Fail beqz ; Abort, no match found 844 845 $O 6F 00 00 00 # $GetTarget_loop_0 jal ; Try another label 846 847 :V ;GetTarget_done 848 13 85 42 00 # rd_a0 rs1_t0 !4 addi ; Get target address 849 850 83 20 01 00 # rd_ra rs1_sp lw ; restore ra 851 13 01 41 00 # rd_sp rs1_sp !4 addi ; deallocate stack 852 67 80 00 00 # rs1_ra jalr ; return 853 854 :L ;StoreLabel 855 13 01 C1 FF # rd_sp rs1_sp !-4 addi ; Allocate stack 856 23 20 11 00 # rs1_sp rs2_ra sw ; protect ra 857 858 13 85 0C 00 # rd_a0 rs1_s9 mv ; struct entry 859 93 8C 8C 01 # rd_s9 rs1_s9 !24 addi ; calloc 860 23 22 65 01 # rs1_a0 rs2_s6 @4 sw ; entry->target = ip 861 23 20 95 00 # rs1_a0 rs2_s1 sw ; entry->next = jump_table 862 93 04 05 00 # rd_s1 rs1_a0 mv ; jump_table = entry 863 23 24 95 01 # rs1_a0 rs2_s9 @8 sw ; entry->name = token 864 93 85 0C 00 # rd_a1 rs1_s9 mv ; Write after struct 865 $c EF 00 00 00 # rd_ra $consume_token jal ; Collect string 866 93 8C 05 00 # rd_s9 rs1_a1 mv ; update HEAP 867 868 83 20 01 00 # rd_ra rs1_sp lw ; restore ra 869 13 01 41 00 # rd_sp rs1_sp !4 addi ; deallocate stack 870 $1 6F 00 00 00 # $First_pass_loop jal ; return 871 872 ; fputc function 873 ; Receives CHAR in a0 874 ; Writes and returns number of bytes written in a0 875 :t ;fputc 876 13 01 01 FF # rd_sp rs1_sp !-16 addi ; allocate stack 877 23 20 A1 00 # rs1_sp rs2_a0 sw ; protect a0 878 23 22 11 00 # rs1_sp rs2_ra @4 sw ; protect ra 879 23 24 B1 00 # rs1_sp rs2_a1 @8 sw ; protect a1 880 23 26 C1 00 # rs1_sp rs2_a2 @12 sw ; protect a2 881 882 93 08 00 04 # rd_a7 !64 addi ; sys_write 883 13 85 09 00 # rd_a0 rs1_s3 mv ; write to output 884 93 05 01 00 # rd_a1 rs1_sp mv ; Get stack address 885 13 06 10 00 # rd_a2 !1 addi ; write 1 character 886 73 00 00 00 # ecall ; syscall 887 888 83 20 41 00 # rd_ra rs1_sp !4 lw ; restore ra 889 83 25 81 00 # rd_a1 rs1_sp !8 lw ; restore a1 890 03 26 C1 00 # rd_a2 rs1_sp !12 lw ; restore a2 891 13 01 01 01 # rd_sp rs1_sp !16 addi ; Deallocate stack 892 67 80 00 00 # rs1_ra jalr ; return 893 894 :F ;Fail 895 ; Terminate program with 1 return code 896 93 08 D0 05 # rd_a7 !93 addi ; sys_exit 897 13 05 10 00 # rd_a0 !1 addi ; Return code 1 898 73 00 00 00 # ecall ; exit(1) 899 # PROGRAM END 900 901 :s ;scratch 902 00 00 00 00 903 904 #:ELF_end