Writing NES games! with assembly!!

Writing NES games! with assembly!!

Talk given at !!Con 2017.

I’d like to take you on a stroll down memory lane and dig into the internals of the Nintendo Entertainment System (NES) to figure out how it works. While we’re there, we’ll see how to build a game for the NES using 6502 assembly with the help of a few modern tools. We’ll gain a new respect for ’80s developers and an appreciation for the high-level languages we have today!

85b03650a2ec5235376b0b983a49511a?s=128

Christian Joudrey

May 06, 2017
Tweet

Transcript

  1. None
  2. None
  3. None
  4. None
  5. None
  6. None
  7. PRG ROM CHR ROM

  8. chr rom

  9. None
  10. None
  11. None
  12. None
  13. PRG ROM CHR ROM battery RAM

  14. Milestone 1

  15. NO WAY! OMG omg!! it works!!! is it normal that

    we are this excited about displaying a ball?
  16. NO WAY! OMG omg!! it works!!! is it normal that

    we are this excited about displaying a ball?
  17. NO WAY! OMG omg!! it works!!! is it normal that

    we are this excited about displaying a ball?
  18. NO WAY! OMG omg!! it works!!! is it normal that

    we are this excited about displaying a ball?
  19. NO WAY! OMG omg!! it works!!! is it normal that

    we are this excited about displaying a ball?
  20. None
  21. 00008000: 20 8e 10 40 2c 02 20 2c 02

    20 10 fb 8a 95 00 9d 00008010: 00 01 9d 00 03 9d 00 04 9d 00 05 9d 00 06 9d 00 00008020: 07 e8 d0 e9 a9 ff 9d 00 02 e8 d0 fa 2c 02 20 10 00008030: fb 4c b6 80 a9 01 85 02 a9 01 85 03 a9 70 8d 03 00008040: 02 a9 70 8d 00 02 a9 00 8d 02 02 a9 01 8d 01 02 00008050: 60 e6 00 e6 01 a5 00 8d 03 02 a5 01 8d 00 02 60 00008060: a9 01 8d 16 40 85 05 4a 8d 16 40 ad 16 40 29 03 00008070: c9 01 26 04 ad 17 40 29 03 c9 01 26 05 90 ec 60 00008080: a5 05 85 07 a5 04 85 06 20 70 80 a5 05 85 09 a5 00008090: 04 85 08 20 70 80 a2 01 b5 04 d5 08 f0 04 b5 06 000080a0: 95 04 ca 10 f3 60 a9 3f 8d 06 20 a9 00 8d 06 20 000080b0: a9 0f 8d 07 20 a9 3f 8d 06 20 a9 11 8d 06 20 a9 000080c0: 30 8d 07 20 a9 0f 8d 07 20 8d 07 20 20 44 80 a9 000080d0: 18 8d 01 20 a9 80 8d 00 20 a9 01 85 0e 20 90 80 000080e0: a5 04 f0 03 20 00 81 20 61 80 20 25 81 4c e9 80 000080f0: a5 04 29 0c f0 0c a5 04 29 08 f0 03 e6 03 60 c6 00008100: 03 60 a5 04 29 03 f0 0b a5 04 29 02 f0 03 c6 02 00008110: 60 e6 02 60 60 e6 11 a5 11 d0 fc 60 48 8a 48 98 00008120: 48 a5 0e f0 0a a9 00 8d 03 20 a9 02 8d 14 40 a5 00008130: 0f f0 08 2c 02 20 20 24 81 c6 0f a5 10 f0 17 a5 00008140: 0b 8d 01 20 a5 0a 8d 00 20 2c 02 20 a5 0c 8d 05 00008150: 20 a5 0d 8d 05 20 a9 00 85 11 68 a8 68 aa 68 40
  22. cc65 toolchain asm asm asm ca65 ld65 asm asm obj

    ines
  23. 69 70 main: 71 ; Set universal background color ($3F00)

    to black ($0F) 72 ; Set PPU address to $3F00 73 lda #$3F 74 sta PPUADDR 75 lda #$00 76 sta PPUADDR 77 ; Write $0F to that PPU address 78 lda #$0F 79 sta PPUDATA 80 81 ; Set sprite palette 0 ($3F11-$3F13) to white ($30), black ($0F), black ($0F) 82 ; Set PPU address to $3F11 83 lda #$3F 84 sta PPUADDR 85 lda #$11 86 sta PPUADDR 87 ; Write $30, $0F, $0F to PPU 88 lda #$30 89 sta PPUDATA ; PPUADDR will increase by 1 each time we write to it 90 lda #$0F 91 sta PPUDATA 92 sta PPUDATA 93 94 jsr ballSetup 95 96 ; Enable sprite and background rendering 97 lda #%00011000 98 sta PPUMASK 99 100 ; Enable NMI 101 lda #%10000000 102 sta PPUCTRL 103
  24. 6502 assembly LDA STA memory ADC SBC INC DEC arithmetics

    AND ORA CMP compare BCC BCS BEQ BMI BNE BPL branch
  25. 6502 assembly LDA STA memory ADC SBC INC DEC arithmetics

    AND ORA CMP compare BCC BCS BEQ BMI BNE BPL branch
  26. 6502 assembly LDA STA memory ADC SBC INC DEC arithmetics

    AND ORA CMP compare BCC BCS BEQ BMI BNE BPL branch
  27. 6502 assembly LDA STA memory ADC SBC INC DEC arithmetics

    AND ORA CMP compare BCC BCS BEQ BMI BNE BPL branch
  28. 6502 assembly LDA STA memory ADC SBC INC DEC arithmetics

    AND ORA CMP compare BCC BCS BEQ BMI BNE BPL branch
  29. $0600: LDA $05 $0602: CMP #$08 $0604: BEQ $060b $0606:

    INC $05 $0608: JMP $0600 $060b: RTS PC
  30. Milestone 2 !

  31. Milestone 2 !

  32. picture processing unit

  33. 256px 240px

  34. None
  35. 00 01 02 03 04 05 06 07 08 09

    0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F nes palette
  36. None
  37. None
  38. None
  39. y tile attrs x 23 01 00 01 64 x

  40. PPU registers $2000-$2007

  41. ! Milestone 3 " " "

  42. OMG! Wait... Is that real? yeah!!! NO WAY!! yeah!!!

  43. OMG! Wait... Is that real? yeah!!! NO WAY!! yeah!!!

  44. OMG! Wait... Is that real? yeah!!! NO WAY!! yeah!!!

  45. OMG! Wait... Is that real? yeah!!! NO WAY!! yeah!!!

  46. OMG! Wait... Is that real? yeah!!! NO WAY!! yeah!!!

  47. OMG! Wait... Is that real? yeah!!! NO WAY!! yeah!!!

  48. video frame vertical blank

  49. video frame vertical blank

  50. nmi interrupt

  51. loop: INC $05 JMP loop nmi: ; Do stuff RTI

    PC
  52. loop: INC $05 JMP loop nmi: ; Do stuff RTI

    PC
  53. loop: INC $05 JMP loop nmi: ; Do stuff RTI

    PC
  54. loop: INC $05 JMP loop nmi: ; Do stuff RTI

    PC
  55. loop: INC $05 JMP loop nmi: ; Do stuff RTI

    PC
  56. loop: INC $05 JMP loop nmi: ; Do stuff RTI

    PC
  57. loop: INC $05 JMP loop nmi: ; Do stuff RTI

    PC
  58. loop: INC $05 JMP loop nmi: ; Do stuff RTI

    PC
  59. loop: INC $05 JMP loop nmi: ; Do stuff RTI

    PC
  60. Milestone 4 # # "

  61. controllers $4016-$4017

  62. None
  63. 0 0 0 0 0 0 0 0 a B

    se st
  64. $ 1 0 0 0 0 0 0 0 a

    B se st
  65. $ $ 1 0 0 0 0 0 0 1

    a B se st
  66. LDA controller1 AND #%10000000 BEQ notPressed
 ; A is pressed

    notPressed: RTS
  67. LDA controller1 AND #%10000000 BEQ notPressed
 ; A is pressed

    notPressed: RTS 1 0 0 0 0 0 0 0 a B se st
  68. Milestone 5 # # " % & "

  69. wine + fceux

  70. None
  71. lua scripting

  72. emu.frameadvance() memory.readbyte(0x0000) memory.writebyte(0x0001, 0x30)

  73. emu.registerafter(function() ballX = memory.readbyte(0x0000) ballY = memory.readbyte(0x0001) gui.text( 10, 30,

    string.format('Ball: %02x,%02x', ballX, ballY) ) end)
  74. None
  75. '

  76. ' ( lua

  77. ! )

  78. function TestBall:test_ball_moves_down() memory.writebyte(ballYAddress, 0x30) memory.writebyte(ballYDirection, 0x01) emu.frameadvance() ballY = memory.readbyte(ballYAddress)

    luaunit.assertEquals(ballY, 0x31) end
  79. None
  80. None
  81. None
  82. Read the source code when in doubt

  83. None
  84. NExt steps

  85. @cjoudrey