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

NES Assembly Workshop

Kevin Zurawel
September 26, 2017

NES Assembly Workshop

Presented at Strange Loop, September 28, 2017.

Kevin Zurawel

September 26, 2017
Tweet

More Decks by Kevin Zurawel

Other Decks in Programming

Transcript

  1. ➤ Compiler / Linker (ca65 / ld65) ➤ CHR editor

    (NES Screen Tool) ➤ Emulator (FCEUX Win32)
  2. ARE YOU ON MAC/LINUX? INSTALL WINE Mac Homebrew: brew cask

    install xquartz;
 brew install wine Mac direct install: use .pkg from
 https://dl.winehq.org/wine-builds/macosx/download.html Linux: use your distribution’s package manager
  3. INSTALL CC65 COMPILER SUITE ➤ Windows:
 download “Windows Snapshot” from

    https://github.com/cc65/cc65 ➤ Mac Homebrew:
 brew install cc65 ➤ Mac direct install:
 git clone https://github.com/cc65/cc65.git; cd cc65
 make
 export prefix=/usr/local; sudo make install ➤ Linux:
 use your distribution’s package manager, or:
 git clone https://github.com/cc65/cc65.git; cd cc65
 make; sudo make avail
  4. DOWNLOAD WORKSHOP SOURCE CODE ➤ On GitHub: https://bit.ly/nes2017 ➤ git

    clone https://github.com/kzurawel/ nesworkshop.git ➤ or: download https://github.com/kzurawel/ nesworkshop/archive/master.zip
  5. +

  6. “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”
  7. BITS & BYTES BYTE (EIGHT BITS) 00000000 00000001 00000010 00000011

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

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

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

    DIGITS 00110001 11010110 “High” byte “Low” byte $31 D6 65,536 possible values
  11. 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
  12. .proc main LDX PPUSTATUS LDX #$3f STX PPUADDR LDX #$00

    STX PPUADDR copy_palettes: LDA palettes,x STA PPUDATA INX CPX #$20 ; 32 colors total BNE copy_palettes Opcode Operand Comment Directive Label
  13. 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
  14. 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
  15. 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
  16. THE PROCESSOR STATUS REGISTER NV--DIZC Carry flag
 (did last op


    cause a carry?) Zero flag
 (was last result zero?) Interrupt flag
 (ignore for now) Decimal flag
 (NES does not support) Sign overflow flag
 (Negative number < -128) Negative flag
 (did last result
 have “1” in bit 7?)
  17. THE CARRY FLAG $a9 +$10 ———— $b9
 
 $90 -$19

    ———— $77
 
 (clear carry) (set carry) (carry clear) (carry set)
  18. THE CARRY FLAG $a9 +$90 ———— $39
 
 $90 -$a9

    ———— $e7
 
 (carry set) (clear carry) (set carry) (carry clear)
  19. THE CARRY FLAG, IN SUMMARY ➤ Adding with result <

    256 ? carry clear ➤ Adding with result > 255 ($ff)? carry set ➤ Subtracting with result >= 0 ? carry set ➤ Subtracting with result < 0 ? carry clear
  20. STORING DATA STA
 Store accumulator
 at memory
 address STX
 Store

    X register
 at memory
 address STY
 Store Y register
 at memory
 address
  21. TRANSFERRING DATA TXA
 Copy X register
 to accumulator TAX
 Copy

    accumulator
 to X register TYA
 Copy Y register
 to accumulator TAY
 Copy Y register
 to accumulator
  22. ZERO-PAGE MODE Load the contents of a memory address
 from

    zero-page RAM into accumulator LDA $80
  23. INDEXED MODE Load the contents of
 (a memory address +

    an index register)
 into accumulator LDA $0200,x
  24. LDA #$80 CMP #$60 behind the scenes, subtract $60 from

    $80: SEC SBC #$60 result: #$20 Zero flag: 0 Carry flag: 1
  25. LDA #$80 CMP #$80 Subtract $80 from $80: SEC SBC

    #$80 result: #$00 Zero flag: 1 Carry flag: 1
  26. LDA #$80 CMP #$A0 Subtract $A0 from $80: SEC SBC

    #$A0 result: #$E0 Zero flag: 0 Carry flag: 0
  27. BRANCHING ON THE ZERO FLAG BEQ Branch if
 zero flag

    set (last result
 EQuals zero) BNE Branch if
 zero flag cleared (last result
 Not Equal to zero)
  28. BRANCHING ON THE CARRY FLAG BCS Branch if
 carry flag

    set BCC Branch if
 carry flag cleared
  29. LDA $0a CMP #$80 BCC smaller_than_80 BEQ equal_to_80 ; if

    you get here, A > $80 equal_to_80: ; if you get here, A == $80 smaller_than_80: ; otherwise, A < $80
  30. A FULL EXAMPLE: ZEROING OUT ZERO-PAGE RAM LDA #$00 TAX

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

    of
 NMI
 handler Address of
 RES
 handler Address of
 IRQ
 handler
  32. INTERRUPT VECTORS ➤ NMI handler: code that runs once per

    frame ➤ RES handler: code that runs at power-on / reset ➤ IRQ handler: generally not used
  33. 1 Background / Transparency color
 + 24 additional colors (3

    colors per palette x 8 palettes) at any given time
  34. “ -Wikipedia Memory-mapped I/O uses the same address space to

    address both memory and I/O devices. …Thus, the CPU instructions used to access the memory can also be used for accessing devices.
  35. CPU MEMORY MAP (64KB) $0000 $FFFF $8000 INTERRUPT
 VECTORS PRG-ROM

    FROM CARTRIDGE
 (CODE YOU WRITE) ZEROPAGE
 RAM STACK OAM (SPRITE)
 BUFFER MEMORY MAPPED
 I/O SAVE/
 WORK
 RAM
 (OPTIONAL) $6000 $0200 $0100 $FFFA $2000
  36. WRITING TO THE PPU ➤ Read from PPUSTATUS to reset

    address latch ➤ Write high byte of PPU address to PPUADDR ➤ Write low byte of PPU address to PPUADDR ➤ Write data to PPUDATA byte-by-byte
  37. PPU MEMORY MAP (16KB) $0000 $2000 Pattern Tables (2x) from

    CHR-ROM Name and Attribute Tables
 (backgrounds) (4x) $3F00-$3F20 $3FFF Palettes
 (8x) $3000
  38. WHAT WE NEED TO DO ➤ Boot up the NES

    ➤ Write palettes to the PPU ➤ Loop forever
  39. EX01/SRC/HEADER.ASM .segment "HEADER" .byte "NES", $1a .byte $01 .byte $01

    .byte %00000001 .byte %00000000 .byte $00 .byte $00 iNES was an early NES emulator developed by Marat Fayzullin. Its most lasting contribution to the NES scene was its popularization of the iNES ROM file format and mapper numbering system. http://wiki.nesdev.com/w/index.php/INES
  40. EX01/NES.CFG (CONFIGURATION FOR LINKER) SEGMENTS { HEADER: load=HEADER, type=ro, align=$10;

    ZEROPAGE: load=ZEROPAGE, type=zp; BSS: load=RAM, type=bss, define=yes, align=$0100; DMC: load=ROM, type=ro, align=64, optional=yes; CODE: load=ROM, type=ro, align=$0100; RODATA: load=ROM, type=ro, align=$0100; VECTORS: load=ROM, type=ro, start=$FFFA; CHR: load=CHRROM, type=ro, align=16, optional=yes; }
  41. EX01/SRC/MAIN.ASM - PALETTE STORAGE .segment "RODATA" palettes: .byte $21, $00,

    $10, $30 .byte $21, $01, $0f, $31 .byte $21, $06, $16, $26 .byte $21, $09, $19, $29
  42. EX01/SRC/MAIN.ASM - MAIN CODE LOOP .proc main LDX PPUSTATUS ;

    reset PPUADDR latch LDX #$3f STX PPUADDR LDX #$00 STX PPUADDR ; send writes to $3f00 ; (palette ram)
  43. EX01/SRC/MAIN.ASM - MAIN CODE LOOP, CONTINUED copy_palettes: LDA palettes,x ;

    use indexed addressing ; into palette storage STA PPUDATA ; write to PPU INX CPX #$20 ; have we copied 32 values? BNE copy_palettes ; if no, repeat
  44. ca65 src/main.asm -I src -o main.o Assembler “main” file to

    assemble Includes directory output filename
  45. NES (NTSC) COLOR PALETTE $00 $10 $20 $30 $01 $11

    $21 $31 $02 $12 $22 $32 $03 $13 $23 $33 $04 $14 $24 $34 $05 $15 $25 $35 $06 $16 $26 $36 $07 $17 $27 $37 $08 $18 $28 $38 $09 $19 $29 $39 $0A $1A $2A $3A $0B $1B $2B $3B $0C $1C $2C $3C $0F $0F $2D $3D
  46. FOUR BYTES PER SPRITE ➤ Y position of top-left corner

    (0-255) ➤ Tile number (0-255) ➤ Attributes
 (flip vertical/horizontal, priority, palette) ➤ X position of top-left corner (0-255)
  47. Game loop NMI handler mainloop: ; play music ; do

    physics ; manage state JMP mainloop nmi_handler: ; read controllers ; update sprites ; re-draw screen RTI +
  48. WHAT WE NEED TO DO ➤Store the data for our

    sprites somewhere ➤Use the NMI handler to copy that sprite data to the PPU
  49. EX02/SRC/CONSTANTS.ASM - NEW MMIO ADDRESSES OAMADDR = $2003 OAMDMA =

    $4014 Set address to write to
 within OAM buffer Set page of memory to copy
 into OAM buffer and copy it
  50. EX02/SRC/MAIN.ASM - SPRITE DATA sprites: .byte $70, $04, %00000000, $70

    .byte $70, $04, %01000000, $78 .byte $78, $04, %10000000, $70 .byte $78, $04, %11000000, $78 within .segment “RODATA”:
  51. EX02/SRC/MAIN.ASM - COPYING OVER SPRITE DATA LDX #$00 ; set

    X register to zero set_up_sprites: LDA sprites,x ; load next byte STA $0200,x ; copy to $0200 + x INX CPX #$10 ; have we copied 16 values? BNE set_up_sprites ; if no, repeat
  52. EX02/SRC/MAIN.ASM - ONE MORE THING… inside .proc main: LDA #%10010000

    ; turn on NMIs, ; sprites use first ; pattern table STA PPUCTRL
  53. ➤ ca65 src/main.asm -I src -o main.o ➤ ld65 main.o

    -C nes.cfg -o ex02.nes ➤ open FCEUX, load “ex02.nes”
  54. YOUR TURN! Change the color of the ball OR Draw

    a spaceship instead of the ball
  55. SPRITE DATA CHEAT SHEET ➤ Y position (0-255) ➤ Tile

    number (0-255) ➤ Attributes ➤ X position (0-255) # % 7 6 5 4 3 2 1 0 7: Flip sprite vertically 6: Flip sprite horizontally 5: Sprite priority
 (1 = behind background) 4-2: not used 1-0: Palette for sprite
 (00, 01, 10, 11)
  56. ZEROPAGE: THE PLACE TO STORE FREQUENTLY-CHANGING VARIABLES ➤ Sprite positions

    ➤ Sprite velocities ➤ Score ➤ Number of lives ➤ Time remaining
  57. CPU MEMORY MAP (64KB) $0000 $FFFF $8000 INTERRUPT
 VECTORS PRG-ROM

    FROM CARTRIDGE
 (CODE YOU WRITE) ZEROPAGE
 RAM STACK OAM (SPRITE)
 BUFFER MEMORY MAPPED
 I/O SAVE/
 WORK
 RAM
 (OPTIONAL) $6000 $0200 $0100 $FFFA $2000
  58. ZERO-PAGE MODE Load the contents of a memory address
 in

    zero-page RAM
 into accumulator LDA $80
  59. SAVING REGISTER DATA BEFORE A SUBROUTINE PHA
 Push accumulator
 onto

    stack PLA
 Pull accumulator
 from stack PHP
 Push program status
 register onto stack PLP
 Pull program status
 register from stack
  60. JSR draw_sprites
 draw_sprites: PHA ; store A on stack TXA

    PHA ; store X on stack TYA PHA ; store Y on stack PHP ; store status
  61. PLP ; restore status PLA TAY ; restore Y PLA

    TAX ; restore X PLA ; restore A RTS ; return
  62. WHAT WE NEED TO DO ➤ Remove hard-coded sprite info

    from RODATA ➤ Store our metatile’s location info in zeropage RAM ➤ Write a subroutine to draw the metatile based on that data ➤ Write a subroutine to update (move) the metatile ➤ Call both subroutines in the NMI handler
  63. EX03/SRC/MAIN.ASM - RESERVE SPACE IN ZEROPAGE .segment "ZEROPAGE" sprite_x: .res

    1 sprite_y: .res 1 sprite_v: .res 1 sprite_h: .res 1
  64. EX03/SRC/MAIN.ASM - INITIALIZE ZEROPAGE VARIABLES .proc main LDA #$70 STA

    sprite_x LDA #$30 STA sprite_y LDA #$01 STA sprite_v STA sprite_h
  65. EX03/SRC/MAIN.ASM - ADD DRAW_SPRITE SUBROUTINE .proc draw_sprite ; skipping register

    storage LDA sprite_y STA $0200 STA $0204 CLC ADC #$08 STA $0208 STA $020c
  66. LDA sprite_v BEQ move_sprite_up LDA sprite_y CLC ADC #$01 STA

    sprite_y JMP vertical_movement_done EX03/SRC/MAIN.ASM - ADD UPDATE_SPRITE_POSITION SUBROUTINE
  67. EX03/SRC/MAIN.ASM - UPDATE NMI HANDLER .proc nmi_handler LDA #$00 ;

    draw SOMETHING first, STA OAMADDR ; in case we run out LDA #$02 ; of vblank time, STA OAMDMA ; then update positions JSR update_sprite_position JSR draw_sprite RTI .endproc
  68. ➤ ca65 src/main.asm -I src -o main.o ➤ ld65 main.o

    -C nes.cfg -o ex03.nes ➤ open FCEUX, load “ex03.nes”
  69. SPRITE MOVEMENT CHEAT SHEET Sprite start position:
 set sprite_x /

    sprite_y at beginning of .proc main Sprite direction: set sprite_h / sprite_v at beginning of .proc main Sprite speed: change amount added / subtracted in .proc update_sprite_position
  70. CMP CMP #$20 “Subtract #$20 from accumulator,
 set processor status

    flags,
 keep accumulator at previous value”
  71. CMP CMP #$20 “Subtract #$20 from accumulator,
 set processor status

    flags,
 keep accumulator at previous value” NV--DIZC
  72. BRANCHING ON CARRY FLAG Accumulator >= CMP argument? Carry flag

    still set Accumulator < CMP argument? Carry flag cleared
  73. WHAT WE NEED TO DO ➤ Add a subroutine to

    detect and process collisions ➤ Update sprite movement direction on collision ➤ Call our subroutine inside our NMI handler
  74. EX04/SRC/MAIN.ASM - PROCESS_COLLISIONS SUBROUTINE LDA sprite_x CMP #$ec ; is

    sprite_x >= #$ec? BCC check_left_edge ; if no, branch LDA #$00 ; yes, update direction STA sprite_h JMP horizontal_check_done check_left_edge:
  75. ➤ ca65 src/main.asm -I src -o main.o ➤ ld65 main.o

    -C nes.cfg -o ex04.nes ➤ open FCEUX, load “ex04.nes”
  76. COLLISIONS CHEAT SHEET Change position of walls: Update $04 /

    $ec for left / right, $08 / $d8 for top / bottom (hint: halfway point in each direction is $80)
  77. READING FROM THE CONTROLLER ➤ Write to an MMIO address

    to “latch” the buttons
 (stop listening for new input) ➤ Make eight reads to an MMIO address,
 one for each button - bit zero will be “1”
 for pressed, “0” for not-pressed
  78. LOGICAL FILTERS AND
 Logical AND with
 accumulator ORA
 Logical OR

    with
 accumulator EOR
 Logical XOR with
 accumulator
  79. WHAT WE NEED TO DO ➤ Add a metatile for

    the paddle / subroutine to draw it ➤ Add a subroutine to read the controller ➤ Add a subroutine to update the paddle’s position based on 
 controller read ➤ Add a subroutine to handle ball/paddle collisions
  80. EX05/SRC/CONSTANTS.ASM - NEW MMIO ADDRESSES AND SOME CONSTANTS BTN_RIGHT =

    %00000001 BTN_LEFT = %00000010 BTN_DOWN = %00000100 BTN_UP = %00001000 BTN_START = %00010000 BTN_SELECT = %00100000 BTN_B = %01000000 BTN_A = %10000000 CONT_PORT_1 = $4016 CONT_PORT_2 = $4017
  81. EX05/SRC/MAIN.ASM - NMI HANDLER ADDITIONS JSR process_collisions JSR update_sprite_position JSR

    draw_sprite JSR read_controller JSR update_paddle_position JSR draw_paddle
  82. EX05/SRC/MAIN.ASM - READ_CONTROLLER - SETUP ; write a 1, then

    a 0, ; to CONT_PORT_1 ; to latch button states LDA #$01 STA CONT_PORT_1 LDA #$00 STA CONT_PORT_1
  83. EX05/SRC/MAIN.ASM - READ_CONTROLLER - MAIN LOOP get_buttons: LDA CONT_PORT_1 ;

    read controller LSR A ; push bit into carry ROL controller1 ; move carry to zeropage BCC get_buttons ; continue until "1 in carry
  84. EX05/SRC/MAIN.ASM - PROCESS_COLLISIONS - UPDATES LDA sprite_y CMP #$c8 ;

    y pos of paddle BCC check_top_edge JSR check_paddle_collision JMP vertical_check_done
  85. EX05/SRC/MAIN.ASM - CHECK_PADDLE_COLLISION - LEFT SIDE OF PADDLE LDA sprite_x

    CMP paddle_x ; is sprite_x >= paddle_x? BCC no_collision ; yes
  86. EX05/SRC/MAIN.ASM - CHECK_PADDLE_COLLISION - RIGHT SIDE OF PADDLE CLC ;

    A = sprite_x ADC #$10 ; sprite is 2 tiles wide STA temp_storage LDA paddle_x CLC ADC #$20 ; paddle is 4 tiles wide CMP temp_storage BCC no_collision
  87. ➤ ca65 src/main.asm -I src -o main.o ➤ ld65 main.o

    -C nes.cfg -o ex05.nes ➤ open FCEUX, load “ex05.nes”
  88. CONTROLLER CHEAT SHEET Check if A is pressed: LDA controller1

    AND #BTN_A BEQ not_a_pressed ;“A pressed” code not_a_pressed: ; continue
  89. $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
  90. WRITING TO NAMETABLES ➤Wait for VBlank to avoid glitches ➤Read

    from PPUSTATUS to reset latch ➤Write high byte of nametable address to PPUADDR ➤Write low byte of nametable address to PPUADDR ➤Write data, one byte at a time, to PPUDATA
  91. %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
  92. WRITING TO SCROLL REGISTERS ➤ At the end of the

    NMI handler… ➤ Write X scroll (0-255 pixels) to PPUSCROLL ➤ Write Y scroll (0-255 pixels) to PPUSCROLL ➤ Update base nametable, write to PPUCTRL
  93. PPUCTRL BIT FLAGS # % 7 6 5 4 3

    2 1 0 Generate NMIs?
 0: no, 1: yes not used
 (always 0) Sprite size
 (0: 8x8, 1: 8x16) Background pattern
 table (0: A, 1: B) Sprite pattern
 table (0: A, 1: B) Address increment
 (0: 1, 1: 32) Base nametable address
 00: $2000, 01: $2400,
 10: $2800, 11: $2C00
  94. WHAT WE NEED TO DO ➤ Add background object bytes

    to RODATA ➤ Add a subroutine to write them to nametables ➤ Write an attribute table for each nametable ➤ Update scroll registers in the NMI handler
  95. EX06/SRC/MAIN.ASM - ADDING PATTERNS TO .RODATA goodluck: .byte $0a, $12,

    $12, $07, $00, $0f, $18, $06, $0e havefun: .byte $0b, $04, $19, $08, $00, $09, $18, $11 pong: .byte $13, $12, $11, $0a
  96. EX06/SRC/MAIN.ASM - .PROC DRAW_BACKGROUNDS ; set PPU address LDX PPUSTATUS

    LDX #$26 STX PPUADDR LDX #$c4 STX PPUADDR LDX #$00 draw_havefun: LDA havefun, x STA PPUDATA INX CPX #$08 BNE draw_havefun
  97. PPU MEMORY MAP (16KB) $0000 $2000 Pattern Tables (2x) from

    CHR-ROM Name and Attribute Tables
 (backgrounds) (4x) $3F00-$3F20 $3FFF Palettes
 (8x) $3000
  98. EX06/SRC/MAIN.ASM - DRAW_BACKGROUNDS - WRITE ATTRIBUTE TABLES ; set PPU

    address LDA PPUSTATUS LDA #$23 STA PPUADDR LDA #$c0 STA PPUADDR LDA #%01010101 LDX #$00 write_attribute_table: STA PPUDATA INX CPX #$40 BNE write_attribute_table
  99. EX06/SRC/MAIN.ASM - .PROC MAIN - CALL DRAW_BACKGROUNDS vblankwait: BIT PPUSTATUS

    BPL vblankwait JSR draw_backgrounds vblankwait2: BIT PPUSTATUS BPL vblankwait2
  100. EX06/SRC/MAIN.ASM - NMI HANDLER - SET SCROLL REGISTERS AND PPUCTRL

    LDA scroll_x ; horizontal scroll STA PPUSCROLL LDA #$00 ; vertical scroll STA PPUSCROLL LDA scroll_table ; set PPUCTRL STA PPUCTRL
  101. PPUCTRL BIT FLAGS # % 7 6 5 4 3

    2 1 0 Generate NMIs?
 0: no, 1: yes not used
 (always 0) Sprite size
 (0: 8x8, 1: 8x16) Background pattern
 table (0: A, 1: B) Sprite pattern
 table (0: A, 1: B) Address increment
 (0: 1, 1: 32) Base nametable address
 00: $2000, 01: $2400,
 10: $2800, 11: $2C00
  102. EX06/SRC/MAIN.ASM - NMI HANDLER - UPDATE SCROLL_X LDA scroll_x CLC

    ADC #$01 STA scroll_x CMP #$00 ; did we wrap BNE no_wrap ; to a new table?
  103. EX06/SRC/MAIN.ASM - NMI HANDLER - UPDATE SCROLL_TABLE LDA scroll_table ;

    get current PPUCTRL CMP #%10010000 ; compare to table $2000 BEQ first_nametable LDA #%10010000 ; $2400 now, so write STA scroll_table ; $2000 table to PPUCTRL JMP no_wrap first_nametable: LDA #%10010001 ; $2000 now, so write STA scroll_table ; $2400 table to PPUCTRL
  104. ➤ ca65 src/main.asm -I src -o main.o ➤ ld65 main.o

    -C nes.cfg -o ex06.nes ➤ open FCEUX, load “ex06.nes”
  105. YOUR TURN! (EXTRA CHALLENGE!) Change which palette the star is

    drawn with
 (change attribute tables)
  106. NAMETABLE / ATTRIBUTE TABLE CHEAT SHEET Nametable 1/2: $2000/$2400 Attribute

    1/2: $23c0/$27c0 $23c0 - $23c7 $23c8 - $23cf $23d0 - $23d7 $23d8 - $23df $23e0 - $23e7 $23e8 - $23ef $23f0 - $23f7 $23f8 - $23ff %76543210 10 32 54 76
  107. THINGS WE DIDN’T TALK ABOUT ➤ Audio (music, sound effects)

    ➤ Mapper chips (MMC1, MMC3, etc.) ➤ Raster effects (scrolling mid-scanline) ➤ “Sprite 0 hit” (partial screen scroll) ➤ Battery-backed save RAM ➤ Alternative input devices (Zapper, etc.)
  108. 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.
  109. WEBSITES ➤ http://nesdev.com (wiki and forums) ➤ PinEight (NROM template

    and more),
 https://pineight.com/nes/ ➤ http://www.6502.org and http://visual6502.org ➤ TEXTFILES, http://web.textfiles.com/games/ ➤ NintendoAge - The Brewery forum,
 http://nintendoage.com/forum/categories.cfm?catid=22
  110. 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/