Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Intro to NES Assembly (RevolutionConf 2017)

Intro to NES Assembly (RevolutionConf 2017)

The Nintendo Entertainment System (NES) defined the look and feel of video games in the late 80's and early 90's. It was designed around the MOS Technologies 6502 CPU, a cheap microprocessor developed in 1975 and still manufactured today. The NES' impressive library of games was written using 6502 assembly language. Compared to assembly languages for modern processors, 6502 assembly is relatively simple, with only 53 possible instructions, one accumulator, two index registers, and a 256-byte stack. Combined with the NES' custom "Picture Processing Unit" (PPU) and highly optimized memory layout, these simple foundations allow for an incredibly diverse range of game experiences, and developers continually pushed the bounds of what the system was thought capable of until its commercial demise in 1995.
We will explore the design of the NES' hardware and memory layout, focusing on the system's graphical capabilities and limitations. We'll take a brief look at writing assembly code for the 6502 and put this into practice with techniques common to many types of games: metatile ("map") compression, collision detection, and random number generation. Finally, we'll discuss what a modern toolkit for NES assembly development looks like, and how you can get started developing your own NES games.

Kevin Zurawel

June 01, 2017
Tweet

More Decks by Kevin Zurawel

Other Decks in Technology

Transcript

  1. “The product had to be priced so that parents would

    buy it. No matter how great the games, if mothers thought they were too expensive the machine would never take off. -Osamu Katayama, “Japanese Business into the 21st Century”
  2. BITS & BYTES BYTE (EIGHT BITS) 00000000 00000001 00000010 00000011

    00000100 00000101 00000110 … 256 possible values
  3. HEX HEXADECIMAL (HEX) DIGIT 0 1 2 3 4 5

    6 7 8 9 A B C D E F 16 possible values
  4. HEX TWO HEX DIGITS = ONE BYTE 00 01 02

    03 04 05 06 07 08 09 0A 0B 0C … 256 possible values
  5. 16-BIT VALUES 16 BITS = 2 BYTES = 4 HEX

    DIGITS 00110001 11010110 “High” byte “Low” byte $31 D6 65,536 possible values
  6. MACHINE CODE E1 80 20 76 F9 C8 A9 7F

    8D 00 02 20 80 F9 E1 80 20 84 F9 60 A9 55 85 78 A9 FF 85 01 24 01 A0 11 A2 23 A9 00 A5 78 F0 10 30 0E C9 55 D0 0A C0 11 D0 06 E0 23 50 02 F0 04 A9 76 85 00 A9 46 24 01 85 78 F0 0A 10 08 50 06 A5 78 C9 46 F0 04 A9 77 85 00 A9 55 85 78 24 01 A9 11 A2 23 A0 00 A4 78 F0 10 30 0E C0 55 D0 0A C9 11 D0 06 E0 23 50 02 F0 04 A9 78 85 00 A0 46 24 01 84 78 F0 0A 10 08 50 06 A4 78 C0 46 F0 04 A9 79 85 00 24 01 A9
  7. ASSEMBLY LDX #$00 STX PPUCTRL STX PPUMASK STX $4010 DEX

    TXS BIT PPUSTATUS BIT SNDCHN LDA #$40
  8. 6502 INSTRUCTION SET ➤ ADC Add with Carry ➤ AND

    And with accumulator ➤ ASL Arithmetic Shift Left ➤ BCC Branch on Carry Clear ➤ BCS Branch on Carry Set ➤ BEQ Branch on result Equals zero ➤ BIT Bit Test ➤ BMI Branch on result Minus ➤ BNE Branch on result Not Equal zero ➤ BPL Branch on result Plus
 ➤ BRK Break ➤ BVC Branch on Overflow Clear ➤ BVS Branch on Overflow Set ➤ CLC Clear Carry flag ➤ CLD Clear Decimal flag ➤ CLI Clear Interrupt disable flag ➤ CLV Clear Overflow flag ➤ CMP Compare with accumulator ➤ CPX Compare with X register ➤ CPY Compare with Y register ➤ DEC Decrement memory ➤ DEX Decrement X register ➤ DEY Decrement Y register ➤ EOR Exclusive Or with accumulator ➤ INC Increment memory ➤ INX Increment X register ➤ INY Increment Y register ➤ JMP Jump to new location ➤ JSR Jump and Save Return address ➤ LDA Load Accumulator with value ➤ LDX Load X register with value ➤ LDY Load Y register with value ➤ LSR Logical Shift Right ➤ NOP No Operation ➤ ORA "OR" with Accumulator ➤ PHA Push Accumulator to stack ➤ PHP Push Processor status to stack ➤ PLA Pull Accumulator from stack ➤ PLP Pull Processor status from stack ➤ ROL Rotate bits Left ➤ RTI Return from Interrupt ➤ RTS Return from Subroutine ➤ SBC Subtract with Carry borrowing ➤ SEC Set Carry flag ➤ SED Set Decimal mode ➤ SEI Set Interrupt disable flag ➤ STA Store Accumulator in memory ➤ STX Store X register in memory ➤ STY Store Y register in memory ➤ TAX Transfer Accumulator to X register ➤ TAY Transfer Accumulator to Y register ➤ TSX Transfer Stack pointer to X register ➤ TXA Transfer X register to Accumulator ➤ TXS Transfer X register to Stack pointer ➤ TYA Transfer Y register to Accumulator
  9. DONKEY KONG (FAMICOM, 1983) Programmer: Course Designer: Original Game Designer:

    Music & Sound Effects: Producer: Toshihiko Nakago Kenta Usui Shigeru Miyamoto Yukio Taneoka Masayuki Uemura
  10. SUPER MARIO BROS. (FAMICOM, 1985) Producer/Director/Designer: Assistant Director/Designer: Sound &

    Music: Programmer: Programmer: Shigeru Miyamoto Takashi Tezuka Koji Kondo Toshihiko Nakago Kazuaki Morita
  11. +

  12. 1 Background / Transparency color
 + 24 additional colors (3

    colors per palette x 8 palettes) at any given time
  13. $24 $24 $24 $24 $24 $24 $24 $24 $24 $24

    $24 $24 $16 $0A $1B $12 $18 $24 $24 $24 $24 $24 $24 $24 $00 $00 $04 $02 $05 $00 $24 $24 $2E $29 $00 $05 $24 $24 $24 $24 $24 $24 $24 $24 $24 $24 $24 $24
  14. %01100011 Bits 0-1, top left: %11 = palette 3 Bits

    2-3, top right: %00 = palette 0 Bits 4-5, bottom left: %10 = palette 2 Bits 6-7, bottom right: %01 = palette 1
  15. FOUR BYTES PER SPRITE ➤ Y position of top-left corner

    (0-255) ➤ Tile number (0-255) ➤ Attributes
 (palette, flip vertical/horizontal, priority) ➤ X position of top-left corner (0-255)
  16. REGISTERS ACCUMULATOR (A) INDEX REGISTER (X) 8 BITS 8 BITS

    8 BITS INDEX REGISTER (Y) Temporary storage / can do math Temporary storage / used to make loops
  17. CPU MEMORY MAP (64KB) $0000 $FFFF $8000 INTERRUPT
 VECTORS PRG-ROM

    FROM CARTRIDGE
 (CODE YOU WRITE) ZEROPAGE
 RAM STACK OAM (SPRITE)
 BUFFER EMPTY SPACE, MEMORY-MAPPED
 I/O SAVE/
 WORK
 RAM
 (OPTIONAL) $6000 $0200 $0100 $FFFA
  18. LOADING AND STORING DATA LDA $8000 Load Accumulator with data

    stored at $8000 LDA #$80 Load Accumulator with actual hex value 80
  19. LOADING AND STORING DATA LDX #%01101001 Load X register with

    binary value 01101001 LDY #32 Load Y register with the decimal value 32
  20. LOADING AND STORING DATA STX $A0 Store value of X

    register at address $A0
 (in fast zeropage RAM) STA $0200,x Store Accumulator value at $0200 + value of X
 (“indexed addressing”)
  21. INCREMENTING AND DECREMENTING INX Increment X register INY Increment Y

    register DEX Decrement X register DEY Decrement Y register
  22. BEQ label BRANCHING, OR: HOW ASSEMBLY DOES IF/THEN Branch to

    label if last result equals zero Branch to label if last result did not equal zero BNE label
  23. A FULL EXAMPLE: ZEROING OUT ZERO-PAGE RAM LDA #$00 LDX

    #$00 clear_zeropage: STA $00,x INX BNE clear_zeropage
  24. $FFFA $FFFB $FFFC $FFFD $FFFE $FFFF { { { Address

    of
 NMI
 handler Address of
 RES
 handler Address of
 IRQ
 handler
  25. RESET (RES) VECTOR Initializes the system at power-on / reset:

    ➤ Set all RAM to zero ➤ Turn on PPU ➤ Set up APU (audio)
  26. Game loop NMI handler mainloop: ; play music ; do

    physics ; manage state JMP mainloop nmi_handler: ; read controllers ; update sprites ; re-draw screen RTI +
  27. 14 screens (name tables) x 960 bytes / screen 13,440

    bytes x 32 stages 430,080 bytes (!!!)
  28. ;level 1–1 
 L_GroundArea6: .db $50, $21
 .db $07, $81,

    $47, $24, $57, $00, $63, $01, $77, $01
 .db $c9, $71, $68, $f2, $e7, $73, $97, $fb, $06, $83 
 .db $5c, $01, $d7, $22, $e7, $00, $03, $a7, $6c, $02 
 .db $b3, $22, $e3, $01, $e7, $07, $47, $a0, $57, $06 
 .db $a7, $01, $d3, $00, $d7, $01, $07, $81, $67, $20 
 .db $93, $22, $03, $a3, $1c, $61, $17, $21, $6f, $33 
 .db $c7, $63, $d8, $62, $e9, $61, $fa, $60, $4f, $b3 
 .db $87, $63, $9c, $01, $b7, $63, $c8, $62, $d9, $61 
 .db $ea, $60, $39, $f1, $87, $21, $a7, $01, $b7, $20 
 .db $39, $f1, $5f, $38, $6d, $c1, $af, $26
 .db $fd 101 bytes "I AM ERROR", p.131
  29. ;level 1–1 
 L_GroundArea6: .db $50, $21
 .db $07, $81,

    $47, $24, $57, $00, $63, $01, $77, $01
 .db $c9, $71, $68, $f2, $e7, $73, $97, $fb, $06, $83 
 .db $5c, $01, $d7, $22, $e7, $00, $03, $a7, $6c, $02 
 .db $b3, $22, $e3, $01, $e7, $07, $47, $a0, $57, $06 
 .db $a7, $01, $d3, $00, $d7, $01, $07, $81, $67, $20 
 .db $93, $22, $03, $a3, $1c, $61, $17, $21, $6f, $33 
 .db $c7, $63, $d8, $62, $e9, $61, $fa, $60, $4f, $b3 
 .db $87, $63, $9c, $01, $b7, $63, $c8, $62, $d9, $61 
 .db $ea, $60, $39, $f1, $87, $21, $a7, $01, $b7, $20 
 .db $39, $f1, $5f, $38, $6d, $c1, $af, $26
 .db $fd 101 bytes Header Footer Byte pairs (screen location, metatile) "I AM ERROR", p.131
  30. Byte pair Column/Row Description $07, $81 0, 7 (new page)

    small obj "?" block w/ coin $47, $24 4, 7 large obj row of bricks, length 4 $57, $00 5, 7 small obj "?" block w/ power-up item $63, $01 6, 3 small obj "?" block w/ coin "I AM ERROR", p.133
  31. 3 name tables x 960 bytes / name table ——————————

    2,880 bytes (reused across multiple levels) + 101 bytes (world 1-1 object data) —————————— 2,981 bytes
  32. AE D0 38 8A ED 60 DB 72 5C 59

    27 D8 0A 4A F4 34 08 A9 C3 96 56 3B F1 55 F8 6B 31 EF 6D 28 AC 41 68 1E 2A C1 E5 8F 50 F5 3E 7B B7 4C 14 39 12 CD B2 62 8B 82 3C BA 63 85 3A 17 B8 2E B5 BE 20 CB 46 51 2C CF 03 78 53 97 06 69 EB 77 86 E6 EA 74 0C 21 E2 40 D4 5A 3D C7 2B 94 D5 8C 44 FD EE D2 43 00 BB FA C6 1D 98 A0 D3 54 5F 5E DC A8 00 AF 93 A1 E1 6C 04 DE B6 D7 36 16 C5 C8 C4 E4 0F 02 AB E8 33 99 73 11 6A 09 67 F3 FF A2 DF 32 0E 1F 0D 90 25 64 75 B3 65 2F C9 B0 DA 5D 9F EC 29 CE E3 F0 91 7A 58 45 24 1C 47 A4 89 18 2D CC BD 6F 80 F6 81 22 E9 07 70 FB DD AD 35 A6 61 B4 A3 FE B1 30 4B 15 48 6E 4F 5B 13 9C 83 92 01 C2 19 7F 1A 1B 71 B9 3F 4E 9B BF 9E 87 0B 10 57 F2 26 79 9A 05 C0 E0 F7 4D 7D CA 52 9D F9 BC AA FC 8D 7E D1 A5 42 E7 D6 76 A7 84 8E 66 7C 23 88 37 49 D9
  33. point x > rect left && point x < rect

    right && point y > rect top && point y < rect bottom FAST COLLISION DETECTION - POINT VS. RECTANGLE .
  34. THINGS WE DIDN’T TALK ABOUT ➤ Reading the controllers ➤

    Audio (music, sound effects) ➤ Hardware scrolling ➤ Mapper chips (MMC1, MMC3, etc.) & bank switching ➤ Alternative input devices (Zapper, etc.) ➤ Battery-backed save RAM ➤ “Sprite 0 hit” / raster effects
  35. BOOKS ➤ Altice, Nathan. “I AM ERROR”. MIT Press, 2015.

    ➤ Zaks, Rodnay. "Programming the 6502". Sybex, 1978. ➤ Mansfield, Richard. "Machine Language for Beginners". Compute! Publications, 1983.
 (available at http://www.atariarchives.org/mlb/) ➤ Zaks, Rodnay. "Advanced 6502 Programming". Sybex, 1982.
  36. WEBSITES ➤ TEXTFILES, http://web.textfiles.com/games/ ➤ PinEight (NROM template and more),


    https://pineight.com/nes/ ➤ http://nesdev.com (wiki and forums) ➤ http://www.6502.org and http://visual6502.org ➤ NintendoAge - The Brewery forum,
 http://nintendoage.com/forum/categories.cfm?catid=22
  37. TOOLS ➤ cc65 compiler suite (ca65 assembler and ld65 linker):


    http://cc65.github.io/cc65/ ➤ NES Screen Tool:
 https://shiru.untergrund.net/software.shtml ➤ FCEUX (w/ debugger on Windows):
 http://www.fceux.com/web/download.html ➤ Nestopia (cross-platform emulator):
 http://nestopia.sourceforge.net/
  38. GAME-SPECIFIC RESOURCES ➤ “A Comprehensive Super Mario Bros. Disassembly”, 


    https://gist.github.com/1wErt3r/4048722 ➤ “TASVideos: Final Fantasy”,
 http://tasvideos.org/GameResources/NES/FinalFantasy1.html ➤ “Retro Internals: Contra”,
 http://tomorrowcorporation.com/posts/retro-game-internals ➤“Programming M.C. Kids”,
 http://games.greggman.com/game/programming_m_c__kids/