-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathmain.S
4028 lines (3382 loc) · 79 KB
/
main.S
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
;------------------------------------------------------------------------;
; rv51 - A RISC-V emulator for the 8051 microcontroller. ;
; Copyright (C) 2020-2023 Forest Crossman <[email protected]> ;
; ;
; This program 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. ;
; ;
; This program 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 this program. If not, see <https://www.gnu.org/licenses/>. ;
;------------------------------------------------------------------------;
; Rules and conventions:
; - R0 is the IRAM destination pointer.
; - R1 is the IRAM source pointer.
; - All multi-byte values are little-endian.
; - A, B, and C can be clobbered by any function.
; - DPTR points to the next RV instruction in CODE/PMEM.
; - RV's PC = 0x80000000 + (DPTR - #rv_code)
; - Instruction execution functions are each responsible for extracting
; their parameters from their instructions.
; - Only the "Fetch instruction" code can modify instruction_[0-3].
; - Only the "set_dst_to_rd_iram_addr" and "init" functions can modify
; R0.
; - Only the "set_src_to_rs1_iram_addr" and "set_src_to_rs2_iram_addr"
; functions can modify R1.
; - Only the "rv_calc_curr_pc" and "rv_calc_next_pc" functions can
; modify rv_pc_[0-1].
rv_ram_base = 0x00000000
rv_rom_base = 0x80000000
rv_sfr_base = 0xC0000000
rv_hyp_base = 0xE0000000
; CSR addresses
rv_csr_addr_mvendorid = 0xF11
rv_csr_addr_marchid = 0xF12
rv_csr_addr_mimpid = 0xF13
rv_csr_addr_mhartid = 0xF14
rv_csr_addr_mstatus = 0x300
rv_csr_addr_misa = 0x301
rv_csr_addr_mie = 0x304
rv_csr_addr_mtvec = 0x305
rv_csr_addr_mcounteren = 0x306
rv_csr_addr_mscratch = 0x340
rv_csr_addr_mepc = 0x341
rv_csr_addr_mcause = 0x342
rv_csr_addr_mip = 0x344
rv_csr_addr_minstret = 0xB02
rv_csr_addr_mcountinhibit = 0x320
; CSR read-only values
rv_csr_val_mvendorid = 0x00000000
rv_csr_val_marchid = 0x00000000
rv_csr_val_mimpid = 0x00000000
rv_csr_val_mhartid = 0x00000000
rv_csr_val_misa = ((1 << 30) | (1 << 12) | (1 << 8))
; Global variables
instruction_0 = 0x20
instruction_1 = 0x21
instruction_2 = 0x22
instruction_3 = 0x23
rd = 0x24
funct3 = 0x25
rs1 = 0x26
rs2 = 0x27
funct7 = 0x2C
misc = 0x2D
misc_bit0__new_interrupt = (((misc - 0x20) * 8) + 0)
misc_bit1__rv_in_interrupt = (((misc - 0x20) * 8) + 1)
immediate_0 = 0x28
immediate_1 = 0x29
immediate_2 = 0x2A
immediate_3 = 0x2B
rd_dat0 = 0x30
rd_dat1 = 0x31
rd_dat2 = 0x32
rd_dat3 = 0x33
rs1_dat0 = 0x34
rs1_dat1 = 0x35
rs1_dat2 = 0x36
rs1_dat3 = 0x37
rs2_dat0 = 0x38
rs2_dat1 = 0x39
rs2_dat2 = 0x3A
rs2_dat3 = 0x3B
rv_pc_0 = 0x3C
rv_pc_1 = 0x3D
curr_pc_dptr_0 = 0x3E
curr_pc_dptr_1 = 0x3F
rv_csr_mepc_0 = 0x40
rv_csr_mepc_1 = 0x41
rv_csr_mtvec_0 = 0x42
rv_csr_mtvec_1 = 0x43
rv_csr_mcause = 0x44
rv_csr_mip_mie = 0x45
rv_csr_mscratch_0 = 0x48
rv_csr_mscratch_1 = 0x49
rv_csr_mscratch_2 = 0x4A
rv_csr_mscratch_3 = 0x4B
stack_base = 0x4C
rv_x0 = 0x80
.area XSEG (XDATA)
.area PSEG (PAG,XDATA)
.area HOME (ABS,CODE)
.org 0x0000
reset:
ljmp init
.org 0x0003
ljmp isr_eint0
.org 0x000b
ljmp isr_timer0
.org 0x0013
ljmp isr_eint1
.org 0x001b
ljmp isr_timer1
.org 0x0023
ljmp isr_serial
sjmp .
init:
; Set stack pointer to scratch pad area.
mov sp, #(stack_base-1)
; Use register bank zero.
anl psw, #(~((1 << 4) | (1 << 3)))
; Use dptr as the RV's PC, offset by #rv_code.
mov dptr, #(rv_code)
; Zero all emulator state, including RISC-V CSRs and registers.
; - CSRs must be cleared since most of the set-by-hardware-only
; registers are assumed to be zero on reset, and we can't
; otherwise clear them with CSR operations.
; - x0 must be cleared since we don't treat reads from it as a
; special case.
; - Clearing x1-x31 isn't strictly necessary, but doing this
; here means the RISC-V code won't have to (slowly) zero
; registers when it starts, resulting in a slightly faster
; RISC-V application startup time.
; - Clearing the stack also isn't necessary, but there's not
; much reason to avoid doing so other than a slight speedup in
; startup time.
mov R0, #0x01
zero_rv_regs:
mov @R0, #0
inc R0
cjne R0, #0, zero_rv_regs
emu_loop:
; Save the DPTR to the current instruction
; NOTE: This must always happen first.
mov curr_pc_dptr_0, dpl
mov curr_pc_dptr_1, dph
fetch_instruction:
; Fetch instruction
clr a
movc a, @a+dptr
mov instruction_0, a
inc dptr
clr a
movc a, @a+dptr
mov instruction_1, a
inc dptr
clr a
movc a, @a+dptr
mov instruction_2, a
inc dptr
clr a
movc a, @a+dptr
mov instruction_3, a
inc dptr
; Decode/execute instruction
lcall decode_opcode
; If we're in an interrupt, don't check for new interrupts.
jb misc_bit1__rv_in_interrupt, emu_loop
; Check if an interrupt was triggered.
jbc misc_bit0__new_interrupt, 1$
; Next instruction
sjmp emu_loop
1$:
; Handle the interrupt.
lcall interrupt_controller
; Next instruction
sjmp emu_loop
; We should never get here.
busy_loop:
sjmp .
decode_opcode:
; Extract the opcode from the first byte of the instruction.
mov a, instruction_0
anl a, #0x7F
mov R2, a
; Opcode dispatch.
; Divide valid opcodes into a rough binary tree to reduce
; the average number of instructions executed.
jb a.6, decode_1XXXXXX
decode_0XXXXXX:
jb a.5, decode_01XXXXX
decode_00XXXXX:
; LOAD, MISC-MEM, OP-IMM, AUIPC
cjne R2, #0x03, decode_0 ; LOAD
ljmp exec_load
decode_0:
cjne R2, #0x0F, decode_1 ; MISC-MEM
; FENCE instructions are a no-op because this virtual core is
; entirely in-order, meaning all memory operations are already
; guaranteed to finish before the instructions that follow them.
ret
decode_1:
cjne R2, #0x13, decode_2 ; OP-IMM
ljmp exec_op_imm
decode_2:
cjne R2, #0x17, decode_10 ; AUIPC
ljmp exec_auipc
decode_01XXXXX:
; STORE, OP, LUI
cjne R2, #0x23, decode_3 ; STORE
ljmp exec_store
decode_3:
cjne R2, #0x33, decode_4 ; OP
ljmp exec_op
decode_4:
cjne R2, #0x37, decode_10 ; LUI
ljmp exec_lui
decode_1XXXXXX:
jnb a.2, decode_1XXX0XX
decode_1XXX1XX:
; JAL, JALR
cjne R2, #0x6F, decode_5 ; JAL
ljmp exec_jal
decode_5:
cjne R2, #0x67, decode_10 ; JALR
lcall extract_funct3
; funct3 is now also in A
jnz decode_10
ljmp exec_jalr
decode_1XXX0XX:
; BRANCH, SYSTEM
cjne R2, #0x63, decode_7 ; BRANCH
ljmp exec_branch
decode_7:
cjne R2, #0x73, decode_10 ; SYSTEM
lcall extract_funct3
; funct3 is now also in A
jnz exec_csr_op
mov a, instruction_2
cjne a, #0x00, 1$
mov a, instruction_3
cjne a, #0x00, .
sjmp exec_ecall
1$:
cjne a, #0x10, 2$
mov a, instruction_3
cjne a, #0x00, .
sjmp exec_ebreak
2$:
cjne a, #0x20, .
mov a, instruction_3
cjne a, #0x30, .
; Fallthrough to MRET
exec_mret:
; Set the RISC-V PC to the value in mepc.
mov a, rv_csr_mepc_0
add a, #(rv_code)
mov dpl, a
mov a, rv_csr_mepc_1
addc a, #(rv_code >> 8)
mov dph, a
; Mark that we're no longer in an interrupt.
clr misc_bit1__rv_in_interrupt
ret
exec_ebreak:
; Save the exception reason ("Breakpoint") in the mcause CSR.
mov rv_csr_mcause, #((0 << 7) | 3)
; Prepare the jump to the trap handler.
ljmp prepare_jump_to_synchronous_trap
exec_csr_op:
ljmp exec_csr_op_impl
decode_10:
; Unrecognized instruction or execution function didn't "ret".
sjmp .
exec_ecall:
mov R1, #(rv_x0 + (4 * 17)) ; Syscall number is in register a7 (x17).
mov a, @R1
; Only directly handle the "exit" syscall.
cjne a, #93, exec_ecall_exception
mov R1, #(rv_x0 + (4 * 10)) ; Exit retval is in register a0 (x10).
mov a, @R1
; Write the retval to the serial buffer, in case the debugger
; supports it.
mov SBUF, a
; Loop forever so the debugger can see the retval preserved in
; the RV registers, too.
sjmp .
exec_ecall_exception:
; Save the exception reason ("Environment call from M-mode") in
; the mcause CSR.
mov rv_csr_mcause, #((0 << 7) | 11)
; Prepare the jump to the trap handler.
ljmp prepare_jump_to_synchronous_trap
exec_load:
lcall extract_rd
; Return early if we're writing the zero register.
mov a, rd
jnz exec_load_rd_not_zero
ret
exec_load_rd_not_zero:
lcall extract_funct3
; Bit 2 of funct3 indicates that we should zero-extend instead of sign-extend.
inst_funct3_bit_2__zero_extend = (((instruction_0 - 0x20) * 8) + 14)
lcall extract_rs1
lcall extract_imm_11_0
; Load rs1 into R1.
lcall set_src_to_rs1_iram_addr
; Read the base address byte by byte, adding the immediate to it
; and storing the results in rs1_dat[0-3].
mov a, @R1
add a, immediate_0
mov rs1_dat0, a
inc R1
mov a, @R1
addc a, immediate_1
mov rs1_dat1, a
inc R1
; The upper bytes are used to access different address spaces.
; e.g., XDATA, CODE, SFR, hypercall registers, etc.
mov a, @R1
addc a, immediate_2
mov rs1_dat2, a
inc R1
mov a, @R1
addc a, immediate_3
mov rs1_dat3, a
; Now that we have the final virtual source address, we need to
; convert that into a real dptr. Push the current dptr to the
; stack, then set the new dptr from the lowest two bytes of the
; source address.
push dph
push dpl
mov dpl, rs1_dat0
mov dph, rs1_dat1
; The data to LOAD gets stored in rd, so load rd into R0.
lcall set_dst_to_rd_iram_addr
; Check what address space we're reading from.
clr c
mov a, rs1_dat3
subb a, #((rv_rom_base - rv_ram_base) >> 24)
jc exec_load_lt_80_xdata
subb a, #((rv_sfr_base - rv_rom_base) >> 24)
jc exec_load_lt_C0_code
subb a, #((rv_hyp_base - rv_sfr_base) >> 24)
jc exec_load_lt_E0_sfr
ljmp exec_load_ge_E0_hyp
exec_load_lt_80_xdata:
; We're always reading at least one byte. Use R2 to store the
; byte for sign extension.
movx a, @dptr
mov @R0, a
mov R2, a
; Jump to the sign extension if we're done. Otherwise, continue.
mov a, funct3
anl a, #0x03
jnz 1$
ljmp exec_load_sign_extend_B
1$:
dec funct3
; Read the next byte of the 16-bit word. Use R2 to store the
; byte for sign extension.
inc dptr
movx a, @dptr
inc R0
mov @R0, a
mov R2, a
; Jump to the the sign extension if we're done. Otherwise,
; continue.
mov a, funct3
anl a, #0x03
jnz 2$
ljmp exec_load_sign_extend_H
2$:
; Read the next two bytes of the 32-bit word.
inc dptr
movx a, @dptr
inc R0
mov @R0, a
inc dptr
movx a, @dptr
inc R0
mov @R0, a
; We just finished loading a 32-bit word, so we don't do sign
; extension. Just jump to the end.
ljmp exec_load_done
exec_load_lt_C0_code:
; To get the real CODE address, add the virtual address to
; rv_code, then store the result in dptr.
mov a, dpl
add a, #(rv_code)
mov dpl, a
mov a, dph
addc a, #(rv_code >> 8)
mov dph, a
; We're always reading at least one byte. Use R2 to store the
; byte for sign extension.
clr a
movc a, @a+dptr
mov @R0, a
mov R2, a
; Jump to the sign extension if we're done. Otherwise, continue.
mov a, funct3
anl a, #0x03
jz exec_load_sign_extend_B
dec funct3
; Read the next byte of the 16-bit word. Use R2 to store the
; byte for sign extension.
inc dptr
clr a
movc a, @a+dptr
inc R0
mov @R0, a
mov R2, a
; Jump to the the sign extension if we're done. Otherwise,
; continue.
mov a, funct3
anl a, #0x03
jz exec_load_sign_extend_H
; Read the next two bytes of the 32-bit word.
inc dptr
clr a
movc a, @a+dptr
inc R0
mov @R0, a
inc dptr
clr a
movc a, @a+dptr
inc R0
mov @R0, a
; We just finished loading a 32-bit word, so we don't do sign
; extension. Just jump to the end.
sjmp exec_load_done
exec_load_lt_E0_sfr:
; We're always reading at least one byte. Use R2 to store the
; byte for sign extension.
mov R3, rs1_dat0
lcall sfr_byte_read
mov @R0, a
mov R2, a
; Jump to the sign extension if we're done. Otherwise, continue.
mov a, funct3
anl a, #0x03
jz exec_load_sign_extend_B
dec funct3
; Read the next byte of the 16-bit word. Use R2 to store the
; byte for sign extension.
inc R3
lcall sfr_byte_read
inc R0
mov @R0, a
mov R2, a
; Jump to the the sign extension if we're done. Otherwise,
; continue.
mov a, funct3
anl a, #0x03
jz exec_load_sign_extend_H
; Read the next two bytes of the 32-bit word.
inc R3
lcall sfr_byte_read
inc R0
mov @R0, a
inc R3
lcall sfr_byte_read
inc R0
mov @R0, a
; We just finished loading a 32-bit word, so we don't do sign
; extension. Just jump to the end.
sjmp exec_load_done
exec_load_ge_E0_hyp:
; Unimplemented--return zero for now.
mov @R0, #0
inc R0
mov @R0, #0
inc R0
mov @R0, #0
inc R0
mov @R0, #0
sjmp exec_load_done
exec_load_sign_extend_B:
; Extend the sign for three bytes.
; Jump if we should zero extend instead of sign extend.
jb inst_funct3_bit_2__zero_extend, exec_load_zero_extend_B
; Check if the sign is positive or negative.
mov a, R2
jnb ACC.7, exec_load_zero_extend_B
; Sign was negative. Sign extend for three bytes.
inc R0
mov @R0, #0xFF
exec_load_sign_extend_H_final:
; Sign extend for two bytes.
inc R0
mov @R0, #0xFF
inc R0
mov @R0, #0xFF
; Done extending, jump to the end.
sjmp exec_load_done
exec_load_sign_extend_H:
; Extend the sign for two bytes.
; Jump if we should zero extend instead of sign extend.
jb inst_funct3_bit_2__zero_extend, exec_load_zero_extend_H
; Check if the sign is positive or negative.
mov a, R2
jnb ACC.7, exec_load_zero_extend_H
; Sign was negative.
sjmp exec_load_sign_extend_H_final
exec_load_zero_extend_B:
; Zero extend for three bytes.
inc R0
mov @R0, #0x00
exec_load_zero_extend_H:
; Zero extend for two bytes.
inc R0
mov @R0, #0x00
inc R0
mov @R0, #0x00
; Done extending, and we're at the end already so there's no
; need to jump.
exec_load_done:
; Restore dptr.
pop dpl
pop dph
ret
.macro sfr_read_case sfr_addr
mov a, sfr_addr
ret
.endm
sfr_byte_read:
; The address to read from is stored in R3. The result is
; returned in A. R7 is used as a scratch register.
mov a, R3
; Rotate the address left by one, into the carry bit. This does
; three things:
; 1. It subtracts 0x80 from the address.
; 2. The address-offset-by-0x80 is multiplied by 2.
; 3. The carry bit is set if the address is >= 0x80, and
; cleared otherwise.
clr c
rlc a
jc sfr_read_addr_ge_0x80
; Address is < 0x80, so return zero.
clr a
ret
sfr_read_addr_ge_0x80:
; Address is >= 0x80, and (address - 0x80) * 2 is in the A register.
; Save A, which is (address - 0x80) * 2, to R7.
mov R7, a
; Rotate A right by one, dividing it by two to make it
; (address - 0x80).
rr a
; Add R7 to A to make it (address - 0x80) * 3, with the carry
; bit set to the high bit.
add a, R7
; If the carry bit isn't set, that means the address is < 0xD6.
jnc sfr_read_addr_lt_0xD6
; If the carry bit is set, then the address is >= 0xD6.
; When the address is 0xD6, A is 2, so subtract two from the
; constant sfr_read_jump_table_hi to avoid having to subtract
; two from A. ((0xD6 - 0x80) * 3) & 0xFF == 2
mov dptr, #(sfr_read_jump_table_hi - 2)
jmp @a+dptr
sfr_read_addr_lt_0xD6:
; Address is < 0xD6.
mov dptr, #sfr_read_jump_table_lo
jmp @a+dptr
sfr_read_jump_table_lo:
sfr_read_case 0x80
sfr_read_case 0x81
sfr_read_case 0x82
sfr_read_case 0x83
sfr_read_case 0x84
sfr_read_case 0x85
sfr_read_case 0x86
sfr_read_case 0x87
sfr_read_case 0x88
sfr_read_case 0x89
sfr_read_case 0x8A
sfr_read_case 0x8B
sfr_read_case 0x8C
sfr_read_case 0x8D
sfr_read_case 0x8E
sfr_read_case 0x8F
sfr_read_case 0x90
sfr_read_case 0x91
sfr_read_case 0x92
sfr_read_case 0x93
sfr_read_case 0x94
sfr_read_case 0x95
sfr_read_case 0x96
sfr_read_case 0x97
sfr_read_case 0x98
sfr_read_case 0x99
sfr_read_case 0x9A
sfr_read_case 0x9B
sfr_read_case 0x9C
sfr_read_case 0x9D
sfr_read_case 0x9E
sfr_read_case 0x9F
sfr_read_case 0xA0
sfr_read_case 0xA1
sfr_read_case 0xA2
sfr_read_case 0xA3
sfr_read_case 0xA4
sfr_read_case 0xA5
sfr_read_case 0xA6
sfr_read_case 0xA7
sfr_read_case 0xA8
sfr_read_case 0xA9
sfr_read_case 0xAA
sfr_read_case 0xAB
sfr_read_case 0xAC
sfr_read_case 0xAD
sfr_read_case 0xAE
sfr_read_case 0xAF
sfr_read_case 0xB0
sfr_read_case 0xB1
sfr_read_case 0xB2
sfr_read_case 0xB3
sfr_read_case 0xB4
sfr_read_case 0xB5
sfr_read_case 0xB6
sfr_read_case 0xB7
sfr_read_case 0xB8
sfr_read_case 0xB9
sfr_read_case 0xBA
sfr_read_case 0xBB
sfr_read_case 0xBC
sfr_read_case 0xBD
sfr_read_case 0xBE
sfr_read_case 0xBF
sfr_read_case 0xC0
sfr_read_case 0xC1
sfr_read_case 0xC2
sfr_read_case 0xC3
sfr_read_case 0xC4
sfr_read_case 0xC5
sfr_read_case 0xC6
sfr_read_case 0xC7
sfr_read_case 0xC8
sfr_read_case 0xC9
sfr_read_case 0xCA
sfr_read_case 0xCB
sfr_read_case 0xCC
sfr_read_case 0xCD
sfr_read_case 0xCE
sfr_read_case 0xCF
sfr_read_case 0xD0
sfr_read_case 0xD1
sfr_read_case 0xD2
sfr_read_case 0xD3
sfr_read_case 0xD4
sfr_read_case 0xD5
sfr_read_jump_table_hi:
sfr_read_case 0xD6
sfr_read_case 0xD7
sfr_read_case 0xD8
sfr_read_case 0xD9
sfr_read_case 0xDA
sfr_read_case 0xDB
sfr_read_case 0xDC
sfr_read_case 0xDD
sfr_read_case 0xDE
sfr_read_case 0xDF
sfr_read_case 0xE0
sfr_read_case 0xE1
sfr_read_case 0xE2
sfr_read_case 0xE3
sfr_read_case 0xE4
sfr_read_case 0xE5
sfr_read_case 0xE6
sfr_read_case 0xE7
sfr_read_case 0xE8
sfr_read_case 0xE9
sfr_read_case 0xEA
sfr_read_case 0xEB
sfr_read_case 0xEC
sfr_read_case 0xED
sfr_read_case 0xEE
sfr_read_case 0xEF
sfr_read_case 0xF0
sfr_read_case 0xF1
sfr_read_case 0xF2
sfr_read_case 0xF3
sfr_read_case 0xF4
sfr_read_case 0xF5
sfr_read_case 0xF6
sfr_read_case 0xF7
sfr_read_case 0xF8
sfr_read_case 0xF9
sfr_read_case 0xFA
sfr_read_case 0xFB
sfr_read_case 0xFC
sfr_read_case 0xFD
sfr_read_case 0xFE
sfr_read_case 0xFF
exec_branch:
; Skip extracting funct3--instead use the bits directly from the
; instruction.
inst_funct3_bit_0__set_if_negated = (((instruction_0 - 0x20) * 8) + 12)
inst_funct3_bit_1__set_if_unsigned_cleared_if_signed = (((instruction_0 - 0x20) * 8) + 13)
inst_funct3_bit_2__set_if_lt_ge_cleared_if_eq = (((instruction_0 - 0x20) * 8) + 14)
; Extract rs1 and rs2.
lcall extract_rs1
lcall extract_rs2
; Load rs1 into R1.
lcall set_src_to_rs1_iram_addr
; Read rs1 into immediate_[0-3].
mov immediate_0, @R1
inc R1
mov immediate_1, @R1
inc R1
mov immediate_2, @R1
inc R1
mov immediate_3, @R1
; Load rs2 into R1.
lcall set_src_to_rs2_iram_addr
; Read rs2 into rd, funct3, rs1, rs2.
; DO NOT USE THESE AGAIN IN THIS FUNCTION EXCEPT FOR COMPARISONS!
mov rd, @R1
inc R1
mov funct3, @R1
inc R1
mov rs1, @R1
inc R1
mov rs2, @R1
; Determine if we need to do an equality comparison or not.
jb inst_funct3_bit_2__set_if_lt_ge_cleared_if_eq, branch_check_lt_ge
; We're checking EQ/NE, not LT/GE.
mov a, immediate_0
xrl a, rd
jnz branch_eq_ne_result_not_equal
mov a, immediate_1
xrl a, funct3
jnz branch_eq_ne_result_not_equal
mov a, immediate_2
xrl a, rs1
jnz branch_eq_ne_result_not_equal
mov a, immediate_3
xrl a, rs2
jnz branch_eq_ne_result_not_equal
; rs1 == rs2. If the negated bit is cleared, that means we take the branch on EQ.
jnb inst_funct3_bit_0__set_if_negated, branch_take
; Don't take the branch when rs1 == rs2 and the bit is set.
ret
branch_eq_ne_result_not_equal:
; rs1 != rs2. If the negated bit is set, that means we take the branch on NE.
jb inst_funct3_bit_0__set_if_negated, branch_take
; Don't take the branch when rs1 != rs2 and the bit is cleared.
ret
branch_check_lt_ge:
; We're checking LT/GE, not EQ/NE.
; Determine if we need to do a signed comparison or not.
jb inst_funct3_bit_1__set_if_unsigned_cleared_if_signed, branch_check_lt_ge_unsigned
; We're doing a signed comparison.
; if rs1 is negative (sign bit set) and rs2 is positive (sign bit cleared), rs1 < rs2.
; Jump if rs1 is negative.
jb (((immediate_0 - 0x20) * 8) + 31), branch_rs1_is_negative_rs2_is_unknown
; rs1 is positive.
; if rs1 is positive (sign bit cleared) and rs2 is negative (sign bit set), rs1 > rs2.
; Jump if rs2 is negative.
jb (((rd - 0x20) * 8) + 31), branch_lt_ge_result_greater_than_or_equal_to
; rs2 is positive.
; if rs1 is positive (sign bit cleared) and rs2 is positive
; (sign bit cleared), we just do an unsigned comparison.
sjmp branch_check_lt_ge_unsigned
branch_rs1_is_negative_rs2_is_unknown:
; rs1 is negative.
; Jump if rs2 is negative.
jb (((rd - 0x20) * 8) + 31), branch_rs1_and_rs2_are_both_negative
; rs2 is positive.
; rs1 < rs2.
sjmp branch_lt_ge_result_less_than
branch_rs1_and_rs2_are_both_negative:
; rs1 is negative and rs2 is negative. Since they're the same sign, we
; can just treat them like they're unsigned and do an unsigned
; comparison.
; Fall through here.
branch_check_lt_ge_unsigned:
; We're doing an unsigned comparison.
; Compare rs1 and rs2 byte-by-byte, from most significant to
; least significant, until a difference is found. If the carry
; bit is set, then rs1 < rs2. If the carry bit is not set and
; the result is non-zero, then rs1 > rs2.
clr c
mov a, immediate_3
subb a, rs2
jc branch_lt_ge_result_less_than
jnz branch_lt_ge_result_greater_than_or_equal_to
mov a, immediate_2
subb a, rs1
jc branch_lt_ge_result_less_than
jnz branch_lt_ge_result_greater_than_or_equal_to
mov a, immediate_1
subb a, funct3
jc branch_lt_ge_result_less_than
jnz branch_lt_ge_result_greater_than_or_equal_to
mov a, immediate_0
subb a, rd
jc branch_lt_ge_result_less_than
branch_lt_ge_result_greater_than_or_equal_to:
; rs1 >= rs2. If the negated bit is set, that means we take the branch on GE.
jb inst_funct3_bit_0__set_if_negated, branch_take
; Don't take the branch when rs1 >= rs2 and the bit is cleared.
ret
branch_lt_ge_result_less_than:
; rs1 < rs2. If the negated bit is cleared, that means we take the branch on LT.
jnb inst_funct3_bit_0__set_if_negated, branch_take
; Don't take the branch when rs1 < rs2 and the bit is set.
ret
branch_take:
; Load the immediate and take the branch.
lcall extract_imm_12_10_5_4_1_11
ljmp jump_to_current_PC_plus_immediate
exec_jalr:
lcall extract_rd
lcall extract_rs1
lcall extract_imm_11_0
; Read the jump address first, and if necessary write the link
; register second. It's necessary to perform these steps in that
; order to prevent overwriting the jump target when the source
; and destination registers are the same.
; Load rs1 into R1.
lcall set_src_to_rs1_iram_addr
; Read the base address byte by byte, adding the immediate to
; it, clearing the lowest bit, and storing the results in
; rs1_dat[0-1]. This is the virtual jump address.
mov a, @R1
add a, immediate_0
anl a, #0xFE
mov rs1_dat0, a
inc R1
mov a, @R1
addc a, immediate_1
mov rs1_dat1, a
; We can't jump anywhere but CODE memory, so just ignore the upper
; two bytes of rs1.
; TODO: Use immediate_2 to support paged CODE memory.
; Skip writing the link register when rd is zero.