Slide 1

Slide 1 text

MAKING NES GAMES
 IN ASSEMBLY Kevin Zurawel (@kzurawel)

Slide 2

Slide 2 text

WHAT IS THE NES?

Slide 3

Slide 3 text

NINTENDO ENTERTAINMENT SYSTEM (1985)

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

Photo by Eckhard Pecher, CC-BY 2.5

Slide 8

Slide 8 text

RADAR SCOPE (1980)

Slide 9

Slide 9 text

arcadeblogger.com/2016/05/13/arcade-factory/

Slide 10

Slide 10 text

MISSILE COMMAND (ATARI, 1980) Total materials cost: $871.00 + assembly (~$2575 in 2017)

Slide 11

Slide 11 text

DONKEY KONG (1981)

Slide 12

Slide 12 text

FAMICOM (1983)

Slide 13

Slide 13 text

NINTENDO ENTERTAINMENT SYSTEM (1985)

Slide 14

Slide 14 text

WHAT DO I NEED TO GET STARTED?

Slide 15

Slide 15 text

➤ Compiler / Linker (ca65 / ld65) ➤ CHR editor (NES Screen Tool) ➤ Emulator (FCEUX Win32)

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

INSTALL NES SCREEN TOOL ➤Download from http://shiru.untergrund.net/files/nesst.zip ➤Unzip ➤Windows: open NESst.exe ➤Mac/Linux: wine NESst.exe

Slide 19

Slide 19 text

INSTALL FCEUX (WIN32) ➤Download from https://sourceforge.net/projects/fceultra/ files/Binaries/2.2.2/fceux-2.2.2-win32.zip/download ➤Windows: open fceux.exe ➤Mac/Linux: wine fceux.exe

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

NES 101:
 HOW DO NES GAMES WORK?

Slide 22

Slide 22 text

+

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

2A03 2C02 https://133fsb.wordpress.com/2009/11/28/restoring-a-nice-famiclone-part-2/

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

CPU
 (6502) Picture Processing Unit
 (PPU) CHR-ROM PRG-ROM NES HARDWARE CARTRIDGE RAM
 (2KB)

Slide 28

Slide 28 text

NES 201:
 UNDER THE HOOD

Slide 29

Slide 29 text

“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”

Slide 30

Slide 30 text

¥9800 (~$40)

Slide 31

Slide 31 text

1975

Slide 32

Slide 32 text

No content

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

FIRST, A LITTLE BIT OF MATH (just a little!)

Slide 35

Slide 35 text

BITS & BYTES BIT 0 1 Two possible values

Slide 36

Slide 36 text

BITS & BYTES TWO BITS 00 01 10 11 Four possible values

Slide 37

Slide 37 text

BITS & BYTES BYTE (EIGHT BITS) 00000000 00000001 00000010 00000011 00000100 00000101 00000110 … 256 possible values

Slide 38

Slide 38 text

BITS & BYTES BYTE (EIGHT BITS) 11001011 256 possible values Bit 7 Bit 0

Slide 39

Slide 39 text

HEX HEXADECIMAL (HEX) DIGIT 0 1 2 3 4 5 6 7 8 9 A B C D E F 16 possible values

Slide 40

Slide 40 text

HEX TWO HEX DIGITS = ONE BYTE 00 01 02 03 04 05 06 07 08 09 0A 0B 0C … 256 possible values

Slide 41

Slide 41 text

REPRESENTING NUMBERS IN ASSEMBLY %01101010 ; BINARY $6A ; HEX 106 ; DECIMAL

Slide 42

Slide 42 text

16-BIT VALUES 16 BITS = 2 BYTES = 4 HEX DIGITS 00110001 11010110 “High” byte “Low” byte $31 D6 65,536 possible values

Slide 43

Slide 43 text

WHAT IS “ASSEMBLY?”

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

.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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

Assembler (ca65) LDA $8024 A9 24 80 (object file)

Slide 48

Slide 48 text

Linker (ld65) header.o main.o joypad.o graphics.chr game.nes

Slide 49

Slide 49 text

NES 202:
 6502 ASSEMBLY

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

MORE REGISTERS ADDRESS BUS PROGRAM COUNTER (PC) 16 BITS 16 BITS

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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?)

Slide 55

Slide 55 text

THE CARRY FLAG $a9 +$10 ———— $b9
 
 $90 -$19 ———— $77
 
 (clear carry) (set carry) (carry clear) (carry set)

Slide 56

Slide 56 text

THE CARRY FLAG $a9 +$90 ———— $39
 
 $90 -$a9 ———— $e7
 
 (carry set) (clear carry) (set carry) (carry clear)

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

SEVEN GROUPS OF OPCODES

Slide 59

Slide 59 text

1. MOVING DATA AROUND
 (10 OPCODES)

Slide 60

Slide 60 text

LDA LDX LDY STA STX STY TXA TAX TYA TAY

Slide 61

Slide 61 text

LOADING DATA LDA
 Load accumulator LDX
 Load X register LDY
 Load Y register

Slide 62

Slide 62 text

STORING DATA STA
 Store accumulator
 at memory
 address STX
 Store X register
 at memory
 address STY
 Store Y register
 at memory
 address

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

6502 ADDRESSING MODES

Slide 65

Slide 65 text

ABSOLUTE MODE Load the contents of a memory address
 into accumulator LDA $8000

Slide 66

Slide 66 text

ZERO-PAGE MODE Load the contents of a memory address
 from zero-page RAM into accumulator LDA $80

Slide 67

Slide 67 text

IMMEDIATE MODE Load a given value into accumulator
 (not a memory address) LDA #$80

Slide 68

Slide 68 text

INDEXED MODE Load the contents of
 (a memory address + an index register)
 into accumulator LDA $0200,x

Slide 69

Slide 69 text

MOVING DATA AROUND: AN EXAMPLE LDA #$10 TAX STX $8000

Slide 70

Slide 70 text

2. ARITHMETIC
 (4 OPCODES)

Slide 71

Slide 71 text

CLC ADC SEC SBC

Slide 72

Slide 72 text

ADDITION CLC Clear carry flag ADC Add, with carry

Slide 73

Slide 73 text

ADDITION: AN EXAMPLE LDA $8000 CLC ADC #$2c

Slide 74

Slide 74 text

SUBTRACTION SEC Set carry flag SBC Subtract, with carry

Slide 75

Slide 75 text

SUBTRACTION: AN EXAMPLE LDA $8000 SEC SBC #$2c

Slide 76

Slide 76 text

3. COMPARING AND BRANCHING
 (7 OPCODES)

Slide 77

Slide 77 text

CMP CPX CPY BEQ BNE BCS BCC

Slide 78

Slide 78 text

CMP Compare operand with accumulator (subtract operand from accumulator, set zero and carry flags appropriately)

Slide 79

Slide 79 text

LDA #$80 CMP #$60 behind the scenes, subtract $60 from $80: SEC SBC #$60 result: #$20 Zero flag: 0 Carry flag: 1

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

LDA #$80 CMP #$A0 Subtract $A0 from $80: SEC SBC #$A0 result: #$E0 Zero flag: 0 Carry flag: 0

Slide 82

Slide 82 text

MAKING COMPARISONS CPX Compare operand
 with X register CPY Compare operand
 with Y register

Slide 83

Slide 83 text

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)

Slide 84

Slide 84 text

LDA #$00 do_stuff: CLC ADC #$10 CMP #$80 BNE do_stuff

Slide 85

Slide 85 text

BRANCHING ON THE CARRY FLAG BCS Branch if
 carry flag set BCC Branch if
 carry flag cleared

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

4. LOOP HELPERS
 (6 OPCODES)

Slide 88

Slide 88 text

INX DEX INY DEY INC DEC

Slide 89

Slide 89 text

INCREMENTING REGISTERS INX Increment X register by one INY Increment Y register by one

Slide 90

Slide 90 text

DECREMENTING REGISTERS DEX Decrement X register by one DEY Decrement Y register by one

Slide 91

Slide 91 text

INCREMENTING AND DECREMENTING MEMORY INC Increment memory address by one DEC Decrement memory address by one

Slide 92

Slide 92 text

A FULL EXAMPLE: ZEROING OUT ZERO-PAGE RAM LDA #$00 TAX clear_zeropage: STA $00,x INX BNE clear_zeropage

Slide 93

Slide 93 text

MORE TO COME LATER…

Slide 94

Slide 94 text

LET’S ACTUALLY
 MAKE SOMETHING! (finally)

Slide 95

Slide 95 text

OUR GOAL:

Slide 96

Slide 96 text

INTERRUPT VECTORS or:
 "What Happens When You Turn It On?"

Slide 97

Slide 97 text

No content

Slide 98

Slide 98 text

$FFFA $FFFB $FFFC $FFFD $FFFE $FFFF { { { Address of
 NMI
 handler Address of
 RES
 handler Address of
 IRQ
 handler

Slide 99

Slide 99 text

INTERRUPT VECTORS ➤ NMI handler: code that runs once per frame ➤ RES handler: code that runs at power-on / reset ➤ IRQ handler: generally not used

Slide 100

Slide 100 text

PALETTES or: “Where Do Colors Come From?”

Slide 101

Slide 101 text

No content

Slide 102

Slide 102 text

No content

Slide 103

Slide 103 text

1 Background / Transparency color
 + 24 additional colors (3 colors per palette x 8 palettes) at any given time

Slide 104

Slide 104 text

MEMORY-MAPPED I/O

Slide 105

Slide 105 text

“ -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.

Slide 106

Slide 106 text

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

Slide 107

Slide 107 text

MEMORY ADDRESSES FOR WRITING TO THE PPU PPUSTATUS $2002 PPUADDR $2006 PPUDATA $2007

Slide 108

Slide 108 text

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

Slide 109

Slide 109 text

PPU MEMORY MAP (16KB) $0000 $2000 Pattern Tables (2x) from CHR-ROM Name and Attribute Tables
 (backgrounds) (4x) $3F00-$3F20 $3FFF Palettes
 (8x) $3000

Slide 110

Slide 110 text

PUTTING IT ALL TOGETHER or: “Your First NES Game”

Slide 111

Slide 111 text

WHAT WE NEED TO DO ➤ Boot up the NES ➤ Write palettes to the PPU ➤ Loop forever

Slide 112

Slide 112 text

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

Slide 113

Slide 113 text

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; }

Slide 114

Slide 114 text

EX01/SRC/CONSTANTS.ASM PPUCTRL = $2000 PPUMASK = $2001 PPUSTATUS = $2002 PPUADDR = $2006 PPUDATA = $2007

Slide 115

Slide 115 text

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

Slide 116

Slide 116 text

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)

Slide 117

Slide 117 text

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

Slide 118

Slide 118 text

EX01/SRC/MAIN.ASM - MAIN CODE LOOP, FINISHED forever: ; do nothing, forever JMP forever .endproc

Slide 119

Slide 119 text

EX01/SRC/MAIN.ASM - WRAPPING THINGS UP .segment "VECTORS" .addr nmi_handler, reset_handler, irq_handler .segment "CHR" .incbin "blank.chr"

Slide 120

Slide 120 text

BUILDING AND RUNNING
 ASSEMBLY CODE

Slide 121

Slide 121 text

ca65 src/main.asm -I src -o main.o Assembler “main” file to assemble Includes directory output filename

Slide 122

Slide 122 text

ld65 main.o -C nes.cfg -o ex01.nes Linker file(s) to link Config file to use output filename

Slide 123

Slide 123 text

open FCEUX, load “ex01.nes”

Slide 124

Slide 124 text

YOUR TURN! Change the color
 that is displayed on screen

Slide 125

Slide 125 text

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

Slide 126

Slide 126 text

DEBUGGING or:
 "It's not working! What happened?"

Slide 127

Slide 127 text

DEBUGGING WITH FCEUX

Slide 128

Slide 128 text

DEBUGGING WITH FCEUX

Slide 129

Slide 129 text

DEBUGGING WITH FCEUX

Slide 130

Slide 130 text

DEBUGGING WITH FCEUX

Slide 131

Slide 131 text

NOW FOR SOMETHING
 A BIT MORE INTERESTING (drawing things!)

Slide 132

Slide 132 text

OUR GOAL:

Slide 133

Slide 133 text

DRAWING GRAPHICS

Slide 134

Slide 134 text

256x240 pixels = 61,440 pixels ÷ 4 pixels / byte = 15,360 bytes
 per screen

Slide 135

Slide 135 text

BOOTSTRAPPING GRAPHICS 8x8-pixel "tile"
 as basic unit 1 screen = 32x30 tiles (960 bytes)

Slide 136

Slide 136 text

PATTERN TABLES ("TILESETS") Sprite (foreground) tiles Background tiles

Slide 137

Slide 137 text

No content

Slide 138

Slide 138 text

OBJECT ACCESS MEMORY (OAM) or: “Wee sprites!”

Slide 139

Slide 139 text

CPU
 (6502) Picture Processing Unit
 (PPU) RAM
 (2KB) OAM (256 bytes)

Slide 140

Slide 140 text

256 bytes OAM ÷ 4 bytes / sprite ——————— 64 sprites (max)

Slide 141

Slide 141 text

No content

Slide 142

Slide 142 text

metatiles

Slide 143

Slide 143 text

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)

Slide 144

Slide 144 text

THE NMI VECTOR

Slide 145

Slide 145 text

NON-MASKABLE INTERRUPT (NMI) VECTOR Runs once per frame, during VBlank

Slide 146

Slide 146 text

WHAT IS “VBLANK”?

Slide 147

Slide 147 text

http://www.circuitstoday.com/crt-cathode-ray-tube

Slide 148

Slide 148 text

VBlank

Slide 149

Slide 149 text

NON-MASKABLE INTERRUPT (NMI) VECTOR Runs once per frame, during VBlank IT WILL BE YOUR BEST FRIEND

Slide 150

Slide 150 text

Game loop NMI handler mainloop: ; play music ; do physics ; manage state JMP mainloop nmi_handler: ; read controllers ; update sprites ; re-draw screen RTI +

Slide 151

Slide 151 text

PATTERN TABLES ("TILESETS") Sprite (foreground) tiles Background tiles

Slide 152

Slide 152 text

NES SCREEN TOOL - OPENING SPRITES.CHR

Slide 153

Slide 153 text

PUTTING IT ALL TOGETHER

Slide 154

Slide 154 text

WHAT WE NEED TO DO ➤Store the data for our sprites somewhere ➤Use the NMI handler to copy that sprite data to the PPU

Slide 155

Slide 155 text

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

Slide 156

Slide 156 text

EX02/SRC/MAIN.ASM - NMI HANDLER .proc nmi_handler LDA #$00 STA OAMADDR LDA #$02 STA OAMDMA RTI .endproc

Slide 157

Slide 157 text

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”:

Slide 158

Slide 158 text

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

Slide 159

Slide 159 text

EX02/SRC/MAIN.ASM - ONE MORE THING… inside .proc main: LDA #%10010000 ; turn on NMIs, ; sprites use first ; pattern table STA PPUCTRL

Slide 160

Slide 160 text

➤ ca65 src/main.asm -I src -o main.o ➤ ld65 main.o -C nes.cfg -o ex02.nes ➤ open FCEUX, load “ex02.nes”

Slide 161

Slide 161 text

YOUR TURN! Change the color of the ball OR Draw a spaceship instead of the ball

Slide 162

Slide 162 text

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)

Slide 163

Slide 163 text

ADD SOME RAZZLE-DAZZLE (moving our sprites around)

Slide 164

Slide 164 text

OUR GOAL:

Slide 165

Slide 165 text

ZEROPAGE RAM

Slide 166

Slide 166 text

ZEROPAGE: THE PLACE TO STORE FREQUENTLY-CHANGING VARIABLES ➤ Sprite positions ➤ Sprite velocities ➤ Score ➤ Number of lives ➤ Time remaining

Slide 167

Slide 167 text

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

Slide 168

Slide 168 text

ZERO-PAGE MODE Load the contents of a memory address
 in zero-page RAM
 into accumulator LDA $80

Slide 169

Slide 169 text

SEVEN GROUPS OF OPCODES: 5. JUMPS AND SUBROUTINES
 (7 OPCODES)

Slide 170

Slide 170 text

JMP JSR RTS PHA PLA PHP PLP

Slide 171

Slide 171 text

JUMPING JMP Jump (set program counter)
 to a new memory address

Slide 172

Slide 172 text

; start the game JMP main

Slide 173

Slide 173 text

SUBROUTINES JSR Jump to Subroutine RTS Return from Subroutine

Slide 174

Slide 174 text

JSR draw_sprite
 ;store current program counter
 ;on stack
 ;set program counter to new
 ;memory address(draw_sprite)

Slide 175

Slide 175 text

draw_sprite:
 ; do sprite-drawing stuff
 RTS
 ; read memory address from
 ; stack and jump back there

Slide 176

Slide 176 text

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

Slide 177

Slide 177 text

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

Slide 178

Slide 178 text

PLP ; restore status PLA TAY ; restore Y PLA TAX ; restore X PLA ; restore A RTS ; return

Slide 179

Slide 179 text

PUTTING IT ALL TOGETHER

Slide 180

Slide 180 text

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

Slide 181

Slide 181 text

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

Slide 182

Slide 182 text

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

Slide 183

Slide 183 text

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

Slide 184

Slide 184 text

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

Slide 185

Slide 185 text

move_sprite_up: LDA sprite_y SEC SBC #$01 STA sprite_y vertical_movement_done: EX03/SRC/MAIN.ASM - ADD UPDATE_SPRITE_POSITION SUBROUTINE, CONTINUED

Slide 186

Slide 186 text

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

Slide 187

Slide 187 text

➤ ca65 src/main.asm -I src -o main.o ➤ ld65 main.o -C nes.cfg -o ex03.nes ➤ open FCEUX, load “ex03.nes”

Slide 188

Slide 188 text

YOUR TURN! Change the start position, 
 direction,
 and speed of the ball

Slide 189

Slide 189 text

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

Slide 190

Slide 190 text

LET’S HIT THINGS (adding collision detection)

Slide 191

Slide 191 text

OUR GOAL:

Slide 192

Slide 192 text

BRANCHING IN-DEPTH

Slide 193

Slide 193 text

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

Slide 194

Slide 194 text

CMP CMP #$20 “Subtract #$20 from accumulator,
 set processor status flags,
 keep accumulator at previous value” NV--DIZC

Slide 195

Slide 195 text

REFRESHER: SUBTRACTION LDA #$30 SEC SBC #$20

Slide 196

Slide 196 text

BRANCHING ON CARRY FLAG Accumulator >= CMP argument? Carry flag still set Accumulator < CMP argument? Carry flag cleared

Slide 197

Slide 197 text

PUTTING IT ALL TOGETHER

Slide 198

Slide 198 text

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

Slide 199

Slide 199 text

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:

Slide 200

Slide 200 text

EX04/SRC/MAIN.ASM - UPDATE NMI HANDLER JSR process_collisions JSR update_sprite_position JSR draw_sprite

Slide 201

Slide 201 text

➤ ca65 src/main.asm -I src -o main.o ➤ ld65 main.o -C nes.cfg -o ex04.nes ➤ open FCEUX, load “ex04.nes”

Slide 202

Slide 202 text

YOUR TURN! Make the ball bounce
 off of an invisible wall

Slide 203

Slide 203 text

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)

Slide 204

Slide 204 text

MAKING IT INTERACTIVE (dealing with controller input)

Slide 205

Slide 205 text

OUR GOAL:

Slide 206

Slide 206 text

READING THE CONTROLLER

Slide 207

Slide 207 text

8 7 6 5 4 3 2 1

Slide 208

Slide 208 text

Photo by flickr user phossil, CC BY-NC-ND 2.0

Slide 209

Slide 209 text

Photo by flickr user phossil, CC BY-NC-ND 2.0 Ground Power Clock Latch Data 1 Data 2 Data 3

Slide 210

Slide 210 text

“parallel-in,
 serial-out
 shift register”

Slide 211

Slide 211 text

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

Slide 212

Slide 212 text

SEVEN GROUPS OF OPCODES:
 
 6. LOGICAL FILTERS
 (3 OPCODES)

Slide 213

Slide 213 text

AND ORA EOR

Slide 214

Slide 214 text

LOGICAL FILTERS AND
 Logical AND with
 accumulator ORA
 Logical OR with
 accumulator EOR
 Logical XOR with
 accumulator

Slide 215

Slide 215 text

LOGICAL AND - FILTER BITS LDA #%01010101 AND #%00001111 result: %00000101

Slide 216

Slide 216 text

LOGICAL OR - TURN BITS ON LDA #%01010101 ORA #%00001111 result: %01011111

Slide 217

Slide 217 text

LOGICAL EXCLUSIVE-OR: FLIP BITS TO THEIR OPPOSITE LDA #%01010101 EOR #%00001111 result: %01011010

Slide 218

Slide 218 text

SEVEN GROUPS OF OPCODES:
 
 7. BIT SHIFTING
 (4 OPCODES)

Slide 219

Slide 219 text

ASL LSR ROL ROR

Slide 220

Slide 220 text

SHIFTING LSR Logical Shift Right ASL Arithmetic Shift Left

Slide 221

Slide 221 text

SHIFTING LEFT LDA #%00001111 ASL A result: %00011110
 carry flag: 0

Slide 222

Slide 222 text

SHIFTING LEFT carry 1 1 1 1 0 0 0 0 0

Slide 223

Slide 223 text

SHIFTING LEFT carry 1 1 1 0 0 0 0 0 1

Slide 224

Slide 224 text

SHIFTING RIGHT LDA #%00001111 LSR A result: %00000111
 carry flag: 1

Slide 225

Slide 225 text

SHIFTING RIGHT carry 1 1 1 1 1 1 1 1 0

Slide 226

Slide 226 text

SHIFTING RIGHT carry 0 1 1 1 1 1 1 1 1

Slide 227

Slide 227 text

ROTATING ROL Rotate Left ROR Rotate Right

Slide 228

Slide 228 text

ROTATING LEFT LDA #%00001111 ROL A result: %00011111, carry flag: 0 carry flag: 1

Slide 229

Slide 229 text

ROTATING LEFT carry 1 0 1 0 1 0 1 0 0

Slide 230

Slide 230 text

ROTATING LEFT carry 0 1 0 1 0 1 0 0 1

Slide 231

Slide 231 text

ROTATING LEFT carry 1 0 1 0 1 0 0 1 0

Slide 232

Slide 232 text

ROTATING RIGHT LDA #%00001111 ROR A result: %10000111, carry flag: 1 carry flag: 1

Slide 233

Slide 233 text

PUTTING IT ALL TOGETHER

Slide 234

Slide 234 text

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

Slide 235

Slide 235 text

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

Slide 236

Slide 236 text

EX05/SRC/MAIN.ASM - MORE ZEROPAGE VARIABLES paddle_x: .res 1 paddle_y: .res 1 controller1: .res 1 temp_storage: .res 1

Slide 237

Slide 237 text

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

Slide 238

Slide 238 text

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

Slide 239

Slide 239 text

EX05/SRC/MAIN.ASM - READ_CONTROLLER - SETUP LDA #$01 STA controller1 ; move the '1' until done ; (ring counter)

Slide 240

Slide 240 text

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

Slide 241

Slide 241 text

EX05/SRC/MAIN.ASM - UPDATE_PADDLE_POSITION LDA controller1 AND #BTN_LEFT BEQ not_left_pressed (BTN_LEFT = %00000010)

Slide 242

Slide 242 text

EX05/SRC/MAIN.ASM - UPDATE_PADDLE_POSITION - LEFT PRESSED LDA paddle_x SEC SBC #$02 STA paddle_x JMP done_with_controller

Slide 243

Slide 243 text

EX05/SRC/MAIN.ASM - UPDATE_PADDLE_POSITION - NOT LEFT PRESSED not_left_pressed: LDA controller1 AND #BTN_RIGHT BEQ done_with_controller

Slide 244

Slide 244 text

EX05/SRC/MAIN.ASM - UPDATE_PADDLE_POSITION - RIGHT PRESSED LDA paddle_x CLC ADC #$02 STA paddle_x done_with_controller:

Slide 245

Slide 245 text

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

Slide 246

Slide 246 text

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

Slide 247

Slide 247 text

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

Slide 248

Slide 248 text

EX05/SRC/MAIN.ASM - CHECK_PADDLE_COLLISION - COLLISION! LDA #$00 ; bounce STA sprite_v no_collision:

Slide 249

Slide 249 text

➤ ca65 src/main.asm -I src -o main.o ➤ ld65 main.o -C nes.cfg -o ex05.nes ➤ open FCEUX, load “ex05.nes”

Slide 250

Slide 250 text

YOUR TURN! Change ball direction when player presses “A” or “B”

Slide 251

Slide 251 text

CONTROLLER CHEAT SHEET Check if A is pressed: LDA controller1 AND #BTN_A BEQ not_a_pressed ;“A pressed” code not_a_pressed: ; continue

Slide 252

Slide 252 text

SETTING THE SCENE (backgrounds and scrolling)

Slide 253

Slide 253 text

OUR GOAL:

Slide 254

Slide 254 text

REMEMBER: 8x8-pixel "tile"
 as basic unit 1 screen = 32x30 tiles (960 bytes)

Slide 255

Slide 255 text

NAME TABLES

Slide 256

Slide 256 text

NAME TABLES Screen 1 ($2000) Screen 2 ($2400) Screen 3 ($2800) Screen 4 ($2C00)

Slide 257

Slide 257 text

¥9800 (~$40)

Slide 258

Slide 258 text

VERTICAL MIRRORING (HORIZONTAL SCROLLING) Image by 
 Damian Yerrick
 (tepples)

Slide 259

Slide 259 text

HORIZONTAL MIRRORING (VERTICAL SCROLLING) Image by 
 Damian Yerrick
 (tepples)

Slide 260

Slide 260 text

$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

Slide 261

Slide 261 text

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

Slide 262

Slide 262 text

ATTRIBUTE TABLES

Slide 263

Slide 263 text

Name table (960 bytes) Attribute table (64 bytes)

Slide 264

Slide 264 text

%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

Slide 265

Slide 265 text

metatiles

Slide 266

Slide 266 text

SNAKE RATTLE ’N ROLL (NES, 1990)

Slide 267

Slide 267 text

SCROLL REGISTERS

Slide 268

Slide 268 text

http://wiki.nesdev.com/w/index.php/PPU_scrolling

Slide 269

Slide 269 text

No content

Slide 270

Slide 270 text

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

Slide 271

Slide 271 text

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

Slide 272

Slide 272 text

PPUCTRL ADDRESS INCREMENT

Slide 273

Slide 273 text

PUTTING IT ALL TOGETHER

Slide 274

Slide 274 text

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

Slide 275

Slide 275 text

No content

Slide 276

Slide 276 text

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

Slide 277

Slide 277 text

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

Slide 278

Slide 278 text

PPU MEMORY MAP (16KB) $0000 $2000 Pattern Tables (2x) from CHR-ROM Name and Attribute Tables
 (backgrounds) (4x) $3F00-$3F20 $3FFF Palettes
 (8x) $3000

Slide 279

Slide 279 text

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

Slide 280

Slide 280 text

EX06/SRC/MAIN.ASM - .PROC MAIN - CALL DRAW_BACKGROUNDS vblankwait: BIT PPUSTATUS BPL vblankwait JSR draw_backgrounds vblankwait2: BIT PPUSTATUS BPL vblankwait2

Slide 281

Slide 281 text

EX06/SRC/MAIN.ASM - ZEROPAGE - ADD SCROLL VARIABLES scroll_x: .res 1 scroll_table: .res 1

Slide 282

Slide 282 text

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

Slide 283

Slide 283 text

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

Slide 284

Slide 284 text

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?

Slide 285

Slide 285 text

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

Slide 286

Slide 286 text

➤ ca65 src/main.asm -I src -o main.o ➤ ld65 main.o -C nes.cfg -o ex06.nes ➤ open FCEUX, load “ex06.nes”

Slide 287

Slide 287 text

YOUR TURN! Add a star
 to the background!

Slide 288

Slide 288 text

YOUR TURN! (EXTRA CHALLENGE!) Change which palette the star is drawn with
 (change attribute tables)

Slide 289

Slide 289 text

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

Slide 290

Slide 290 text

BUT WAIT!
 THERE’S MORE!

Slide 291

Slide 291 text

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.)

Slide 292

Slide 292 text

FURTHER RESOURCES or: "It's dangerous to go alone! Take this!"

Slide 293

Slide 293 text

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.

Slide 294

Slide 294 text

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

Slide 295

Slide 295 text

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/

Slide 296

Slide 296 text

BIT.LY/NES-WORKSHOP TWITTER: @KZURAWEL EMAIL: KZURAWEL@GMAIL.COM