Advertisement
Masterjun

SNES JIT 65816 example

Dec 3rd, 2024 (edited)
43
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
LLVM 4.92 KB | None | 0 0
  1. ; The 65816 SNES code that is being JIT compiled:
  2.  
  3. ; $00:8000: LDA #$5A
  4. ; $00:8002: LDX #$08
  5. ; loop:
  6. ; $00:8004: STA $0003,x
  7. ; $00:8007: DEX
  8. ; $00:8008: CPX #$01
  9. ; $00:800A: BNE loop
  10. ; $00:800C: STP
  11.  
  12. ; Which basically just writes 7 bytes of 0x5A into memory at $0005-$000B
  13.  
  14. ;--------------------------------
  15.  
  16. ; The following functions are the result of the JIT compilation, which ran like this:
  17. ; Generate ADDR_008000_MX
  18. ; Run      ADDR_008000_MX | Initial LDA+LDX and 1st loop
  19. ;
  20. ; Generate ADDR_008004_MX
  21. ; Run      ADDR_008004_MX | 2nd loop
  22. ; Run      ADDR_008004_MX | 3rd loop
  23. ; Run      ADDR_008004_MX | 4th loop
  24. ; Run      ADDR_008004_MX | 5th loop
  25. ; Run      ADDR_008004_MX | 6th loop
  26. ; Run      ADDR_008004_MX | 7th loop
  27. ;
  28. ; Generate ADDR_00800C_MX
  29. ; Run      ADDR_00800C_MX | STP (stop)
  30.  
  31. ;--------------------------------
  32.  
  33. ; This function runs the first two instructions and the first loop until the branch.
  34. ; We can optimize things, because the first loop always runs with A=0x5A and X=0x08.
  35. ; Other things are optimized too, e.g. LDA, LDX and DEX all affect the N and Z flags of the P register,
  36. ; but those computations are all removed because their results are not needed before CPX overwrites them all.
  37. define void @ADDR_008000_MX(ptr nocapture %0) local_unnamed_addr #0 {
  38. entry:
  39.   %mem = getelementptr i8, ptr %0, i64 32      ;\ Prepare pointers
  40.   %ptrA = getelementptr i8, ptr %0, i64 4      ;|
  41.   %ptrX = getelementptr i8, ptr %0, i64 6      ;|
  42.   %ptrP = getelementptr i8, ptr %0, i64 12     ;|
  43.   %ptrDB = getelementptr i8, ptr %0, i64 16    ;/
  44.   %1 = load i8, ptr %ptrP, align 1             ; Load P
  45.   %2 = load i8, ptr %ptrDB, align 1            ; Load DB
  46.   %3 = zext i8 %2 to i64                       ;\ Prepare DB for indexing
  47.   %4 = shl nuw nsw i64 %3, 16                  ;/
  48.   %5 = or i64 %4, 11                           ; $0003,x and X equals 8 in this first loop, so hardcoded $0011 with DB bank
  49.   %6 = getelementptr i8, ptr %mem, i64 %5      ; Prepare memory pointer
  50.   store i8 90, ptr %6, align 1                 ; Execute STA of STA $0003,x | A is always 0x5A=90 here, that's why we can hardcode the value
  51.   %7 = and i8 %1, 124                          ;\ In P (flag register): reset N,Z and set C
  52.   %P8 = or i8 %7, 1                            ;/
  53.   store i16 -32764, ptr %0, align 2            ; Write 0x8004 to PC and quit block because we reached a branch instruction
  54.   store i8 90, ptr %ptrA, align 1              ; Write 0x5A to A
  55.   store i8 7, ptr %ptrX, align 1               ; Write 0x07 to X
  56.   store i8 %P8, ptr %ptrP, align 1             ; Write changed P
  57.   ret void
  58. }
  59.  
  60. ; This function runs the loop.
  61. ; Since A and X can be anything here, we can't optimize things so much.
  62. define void @ADDR_008004_MX(ptr nocapture %0) local_unnamed_addr #0 {
  63. entry:
  64.   %mem = getelementptr i8, ptr %0, i64 32       ;\ Prepare pointers
  65.   %ptrA = getelementptr i8, ptr %0, i64 4       ;|
  66.   %ptrX = getelementptr i8, ptr %0, i64 6       ;|
  67.   %ptrP = getelementptr i8, ptr %0, i64 12      ;|
  68.   %ptrDB = getelementptr i8, ptr %0, i64 16     ;/
  69.   %1 = load i8, ptr %ptrA, align 1              ; Load A
  70.   %2 = load i8, ptr %ptrX, align 1              ; Load X
  71.   %3 = load i8, ptr %ptrP, align 1              ; Load P
  72.   %4 = load i8, ptr %ptrDB, align 1             ; Load DB
  73.   %5 = zext i8 %4 to i64                        ;\ Prepare DB for indexing
  74.   %6 = shl nuw nsw i64 %5, 16                   ;/
  75.   %7 = zext i8 %2 to i64                        ; Prepare X for indexing
  76.   %8 = add nuw nsw i64 %7, 3                    ; Add 3 to X
  77.   %9 = or i64 %8, %6                            ; Compute $0003,x
  78.   %10 = getelementptr i8, ptr %mem, i64 %9      ; Prepare memory pointer
  79.   store i8 %1, ptr %10, align 1                 ; Execute STA of STA $0003,x
  80.   %11 = add i8 %2, -1                           ; Execute DEX
  81.   %12 = and i8 %3, 124                          ; Reset flags N,Z and C
  82.   %13 = add i8 %2, -2                           ;\ X-2 == 0, which is the combination of DEX and CPX #$01
  83.   %14 = icmp eq i8 %13, 0                       ;/
  84.   %15 = select i1 %14, i8 2, i8 0               ; If the loop is done, set Z
  85.   %16 = and i8 %13, -128                        ;\ Build the flag register P
  86.   %17 = or i8 %12, %16                          ;|
  87.   %18 = icmp sge i8 %11, %13                    ;|
  88.   %19 = zext i1 %18 to i8                       ;|
  89.   %20 = or i8 %17, %19                          ;|
  90.   %P4 = or i8 %20, %15                          ;/
  91.   %21 = select i1 %14, i16 -32756, i16 -32764   ; Jump to $800C if loop ended, if loop continues jump back to $8004
  92.   store i16 %21, ptr %0, align 2                ; Write PC
  93.   store i8 %11, ptr %ptrX, align 1              ; Write X
  94.   store i8 %P4, ptr %ptrP, align 1              ; Write P
  95.   ret void
  96. }
  97.  
  98. define void @ADDR_00800C_MX(ptr nocapture writeonly %0) local_unnamed_addr #0 {
  99. entry:
  100.   store i16 -32755, ptr %0, align 2             ; Write $800D to PC
  101.   ret void
  102. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement