├── .devcontainer └── devcontainer.json ├── .github └── dependabot.yml ├── .gitignore ├── .vscode ├── settings.json └── tasks.json ├── README.md ├── chapters ├── chapter1 │ └── 1 │ │ └── chapter1_1.asm ├── chapter10 │ ├── 1 │ │ ├── chapter10_1.asm │ │ ├── gameData.asm │ │ ├── gameTanks.asm │ │ └── gameVariables.asm │ ├── 2 │ │ ├── chapter10_2.asm │ │ ├── gameData.asm │ │ ├── gameTanks.asm │ │ └── gameVariables.asm │ └── 3 │ │ ├── chapter10_3.asm │ │ ├── gameData.asm │ │ ├── gameTanks.asm │ │ └── gameVariables.asm ├── chapter11 │ ├── 1 │ │ ├── chapter11_1.asm │ │ ├── gameData.asm │ │ ├── gameFlow.asm │ │ ├── gameTanks.asm │ │ └── gameVariables.asm │ ├── 2 │ │ ├── chapter11_2.asm │ │ ├── gameData.asm │ │ ├── gameFlow.asm │ │ ├── gameTanks.asm │ │ └── gameVariables.asm │ └── 3 │ │ ├── chapter11_3.asm │ │ ├── gameData.asm │ │ ├── gameFlow.asm │ │ ├── gameTanks.asm │ │ └── gameVariables.asm ├── chapter12 │ └── 1 │ │ ├── chapter12_1.asm │ │ ├── gameData.asm │ │ ├── gameFlow.asm │ │ ├── gameTanks.asm │ │ └── gameVariables.asm ├── chapter13 │ └── 1 │ │ ├── chapter13_1.asm │ │ ├── gameData.asm │ │ ├── gameFlow.asm │ │ ├── gameTanks.asm │ │ └── gameVariables.asm ├── chapter2 │ └── 1 │ │ └── chapter2_1.asm ├── chapter6 │ └── 1 │ │ ├── chapter6_1.asm │ │ └── gameData.asm ├── chapter7 │ ├── 1 │ │ ├── chapter7_1.asm │ │ └── gameData.asm │ ├── 2 │ │ ├── chapter7_2.asm │ │ └── gameData.asm │ └── 3 │ │ ├── chapter7_3.asm │ │ └── gameData.asm ├── chapter8 │ ├── 1 │ │ ├── chapter8_1.asm │ │ └── gameData.asm │ ├── 2 │ │ ├── chapter8_2.asm │ │ ├── gameData.asm │ │ └── gameTanks.asm │ ├── 3 │ │ ├── chapter8_3.asm │ │ ├── gameData.asm │ │ ├── gameTanks.asm │ │ └── gameVariables.asm │ └── 4 │ │ ├── chapter8_4.asm │ │ ├── gameData.asm │ │ ├── gameTanks.asm │ │ └── gameVariables.asm ├── chapter9 │ ├── 1 │ │ ├── chapter9_1.asm │ │ ├── gameData.asm │ │ ├── gameTanks.asm │ │ └── gameVariables.asm │ ├── 2 │ │ ├── chapter9_2.asm │ │ ├── gameData.asm │ │ ├── gameTanks.asm │ │ └── gameVariables.asm │ ├── 3 │ │ ├── chapter9_3.asm │ │ ├── gameData.asm │ │ ├── gameTanks.asm │ │ └── gameVariables.asm │ └── 4 │ │ ├── chapter9_4.asm │ │ ├── gameData.asm │ │ ├── gameTanks.asm │ │ └── gameVariables.asm ├── content │ ├── music.fms │ ├── music.s │ ├── sfx.fms │ ├── sfx.s │ ├── tankrace.nss │ ├── tankracebackground.chr │ ├── tankracebackground.pal │ ├── tankracedesertleft.nam │ ├── tankracedesertright.nam │ ├── tankracesprites.chr │ ├── tankracesprites.pal │ ├── tankracetitleleft.nam │ ├── tankracetitleright.nam │ ├── test.nss │ ├── testbackground.chr │ ├── testbackground.nam │ └── testbackground.pal └── library │ ├── famistudio_ca65.s │ ├── libDefines.asm │ ├── libInput.asm │ ├── libMath.asm │ ├── libScreen.asm │ ├── libSound.asm │ ├── libSprite.asm │ └── libVariables.asm ├── nes.cfg ├── tankrace.nes └── tools ├── cc65 ├── bin │ ├── ar65 │ ├── ca65 │ ├── cc65 │ ├── chrcvt65 │ ├── cl65 │ ├── co65 │ ├── da65 │ ├── grc65 │ ├── ld65 │ ├── od65 │ ├── sim65 │ └── sp65 └── lib │ └── nes.lib └── lbl2nl ├── lbl2nl ├── lbl2nl.cpp └── readme /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/ubuntu 3 | { 4 | "name": "Ubuntu", 5 | // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile 6 | "image": "mcr.microsoft.com/devcontainers/base:jammy", 7 | 8 | // Features to add to the dev container. More info: https://containers.dev/features. 9 | "features": { "ghcr.io/devcontainers/features/desktop-lite:1": { "noVncVersion": "1.2.0" } }, 10 | "containerEnv": { "XDG_RUNTIME_DIR": "/tmp/runtime-vscode" }, 11 | 12 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 13 | "forwardPorts": [6080], "portsAttributes": { "6080": { "label": "emulator" } }, 14 | 15 | // Use 'postCreateCommand' to run commands after the container is created. 16 | // Update package lists and install FCEUX 17 | "postCreateCommand": "sudo apt update && sudo apt install -y fceux", 18 | 19 | // Configure tool-specific properties. 20 | "customizations": { "vscode": { "extensions": [ "tlgkccampbell.code-ca65" ] } } 21 | } -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for more information: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | # https://containers.dev/guide/dependabot 6 | 7 | version: 2 8 | updates: 9 | - package-ecosystem: "devcontainers" 10 | directory: "/" 11 | schedule: 12 | interval: weekly 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Don't check these into git 2 | *.nes 3 | *.lbl 4 | *.nes.0.nl 5 | *._* 6 | ~* 7 | .DS_Store 8 | !tankrace.nes -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "explorer.compactFolders": false, 3 | "workbench.editorAssociations": 4 | { 5 | "*.chr": "hexEditor.hexedit", 6 | "*.nam": "hexEditor.hexedit", 7 | "*.map": "hexEditor.hexedit", 8 | "*.pal": "hexEditor.hexedit" 9 | }, 10 | "editor.tabSize": 2 11 | } 12 | 13 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Assembler - RetroGameDev", 6 | "type": "shell", 7 | "presentation": { 8 | "echo": true, // Show output of the assembler task 9 | "showReuseMessage": false, 10 | "clear": true // Clear terminal before running this task 11 | }, 12 | "command": "./tools/cc65/bin/cl65", // Command to compile the assembly code 13 | "args": [ 14 | "-t", "nes", // Target platform is NES 15 | "-g", "${file}", // Source file 16 | "-C", "nes.cfg", // Configuration file for memory layout 17 | "-Ln", "${fileDirname}${pathSeparator}${fileBasenameNoExtension}.lbl", // Output label file 18 | "-o", "${fileDirname}${pathSeparator}${fileBasenameNoExtension}.nes" // Output NES ROM 19 | ] 20 | }, 21 | { 22 | "label": "Symbols", 23 | "type": "shell", 24 | "presentation": { 25 | "echo": true, // Show output of the symbol generation task 26 | "showReuseMessage": false, 27 | "clear": false // Do not clear terminal 28 | }, 29 | "command": "./tools/lbl2nl/lbl2nl", // Command to generate the NES symbol file from the .lbl file 30 | "args": [ 31 | "${fileDirname}${pathSeparator}${fileBasenameNoExtension}.lbl", // Input label file 32 | "${fileDirname}${pathSeparator}${fileBasenameNoExtension}.nes.0.nl" // Output symbol file for the emulator 33 | ], 34 | "dependsOn": ["Assembler - RetroGameDev"] // Ensure this task runs after the assembler task 35 | }, 36 | { 37 | "label": "Delete .lbl file", 38 | "type": "shell", 39 | "command": "rm", // Command to remove the file 40 | "args": [ 41 | "${fileDirname}${pathSeparator}${fileBasenameNoExtension}.lbl" // Specify the label file to delete 42 | ], 43 | "presentation": { 44 | "echo": true, // Show output when the file is deleted 45 | "showReuseMessage": false, 46 | "clear": false // Keep the previous terminal output visible 47 | }, 48 | "dependsOn": ["Symbols"] // Ensure this task runs after the symbol generation task 49 | }, 50 | { 51 | "label": "Emulator", 52 | "type": "shell", 53 | "presentation": { 54 | "echo": true, // Show output of the emulator task 55 | "showReuseMessage": false, 56 | "clear": false // Do not clear terminal 57 | }, 58 | "command": "/usr/games/fceux", // Command to run the NES ROM in the emulator 59 | "args": [ 60 | "${fileDirname}${pathSeparator}${fileBasenameNoExtension}.nes" // Specify the NES ROM to run 61 | ], 62 | "dependsOn": ["Delete .lbl file"], // Ensure this task runs after the .lbl file is deleted 63 | "group": { 64 | "kind": "build", // Group this task under "build" so it can be run as a default task 65 | "isDefault": true 66 | } 67 | } 68 | ] 69 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RetroGameDev NES Edition 2 | 3 | Welcome to the **NES Edition** repository! This project is part of the **RetroGameDev** series, and teaches how to develop retro games for the Nintendo Entertainment System (NES). Happy coding and enjoy building your NES tank racing game! 4 | 5 | ## Documentation 6 | 7 | - [ca65](https://cc65.github.io/doc/ca65.html) 8 | - [Codespaces](https://docs.github.com/en/codespaces) 9 | - [FamiStudio](https://famistudio.org/doc/) 10 | - [FCEUX](https://fceux.com/web/documentation.html) 11 | - [Git](https://git-scm.com/doc) 12 | - [VS Code](https://code.visualstudio.com/docs) -------------------------------------------------------------------------------- /chapters/chapter1/1/chapter1_1.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition chapter1_1 3 | ;=============================================================================== 4 | ; NES ROM Header 5 | .segment "HEADER" 6 | .byte "NES" ; 'NES' identifier - tells the system it's a valid NES ROM 7 | .byte $1a ; Control byte for NES compatibility 8 | .byte 2 ; Number of 16KB PRG-ROM banks (2 x 16KB = 32KB of code) 9 | .byte 1 ; Number of 8KB CHR-ROM banks (1 x 8KB = 8KB of graphics) 10 | .byte 1 | (0 << 4) ; Vertical mirroring 11 | .byte 0 & $f0 ; Using mapper 0 (no extra memory mappers) 12 | .byte 0,0,0,0,0,0,0,0 ; Padding bytes to complete the header 13 | 14 | ;=============================================================================== 15 | ; Zero Page Segment 16 | .segment "ZEROPAGE" 17 | ; Zero Page memory is quicker to access, making it ideal for variables that are 18 | ; frequently used in your program. 19 | .include "../../library/libVariables.asm" ; Include variables (defined in external file) 20 | 21 | ;=============================================================================== 22 | ; RAM Segment 23 | .segment "RAM" 24 | ; Defines a RAM segment to avoid assembler warnings in chapters not using FamiStudio. 25 | 26 | ;=============================================================================== 27 | ; Code Segment 28 | .segment "CODE" 29 | ; Include essential library functions and constants 30 | .include "../../library/libDefines.asm" ; Include color codes and other constants 31 | .include "../../library/libScreen.asm" ; Include functions for managing the screen 32 | 33 | ;=============================================================================== 34 | ; Game Initialization 35 | gameMainInit: 36 | LIBSCREEN_INIT ; Initialize the screen and PPU settings 37 | LIBSCREEN_SETBACKGROUNDCOLOR_V BLUE ; Set background color to BLUE 38 | ; Note: You can uncomment the line below to set the background to CYAN instead 39 | ; jsr gameMainSetBackgroundColor ; Calls subroutine that sets background to CYAN 40 | LIBSCREEN_ENABLEPPU ; Enable PPU rendering (sprites, background) 41 | ; No 'rts' (return) here, so the code will flow directly into the main game loop 42 | 43 | ;=============================================================================== 44 | ; Main Game Update Loop 45 | gameMainUpdate: 46 | lda bFrameReady ; Load the frame-ready flag 47 | beq gameMainUpdate ; If frame not ready (bFrameReady = 0), loop and wait 48 | 49 | ; Game logic would go here (currently empty for this chapter) 50 | 51 | lda #0 ; Reset the bFrameReady flag back to 0 52 | sta bFrameReady ; This prevents the game logic from running more than once per frame 53 | jmp gameMainUpdate ; Infinite loop - returns to the beginning of the update loop 54 | 55 | ;=============================================================================== 56 | ; NMI (Non-Maskable Interrupt) Handler 57 | gameMainNMI: 58 | lda #1 ; Set bFrameReady flag to 1 to signal the game update loop 59 | sta bFrameReady ; Store the flag in memory 60 | rti ; Return from interrupt - graphics update happens after this 61 | 62 | ;=============================================================================== 63 | ; Set Background Color Subroutine 64 | gameMainSetBackgroundColor: 65 | LIBSCREEN_SETPPUADDRESS_A BGPALETTE ; Set PPU address to background palette 66 | LIBSCREEN_SETPPUDATA_V CYAN ; Set the first color entry in the palette to CYAN 67 | rts ; Return from subroutine 68 | 69 | ;=============================================================================== 70 | ; Character Segment (CHR-ROM) 71 | .segment "CHARS" 72 | ; ROM chars start at PPU address $0000 73 | ; This segment will hold sprite and tile graphics. Currently empty, but will be 74 | ; filled with graphic data in later chapters. 75 | 76 | ;=============================================================================== 77 | ; Interrupt Vectors 78 | .segment "VECTORS" 79 | ; This segment defines what happens during specific events: 80 | .word gameMainNMI ; NMI (VBlank) - triggered every frame to handle graphics 81 | .word gameMainInit ; Reset - called when the game is first started 82 | .word 0 ; IRQ - not used in this simple project -------------------------------------------------------------------------------- /chapters/chapter10/1/chapter10_1.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition chapter10_1 3 | ;=============================================================================== 4 | ; NES ROM Header 5 | .segment "HEADER" 6 | .byte "NES" ; 'NES' identifier - tells the system it's a valid NES ROM 7 | .byte $1a ; Control byte for NES compatibility 8 | .byte 2 ; Number of 16KB PRG-ROM banks (2 x 16KB = 32KB of code) 9 | .byte 1 ; Number of 8KB CHR-ROM banks (1 x 8KB = 8KB of graphics) 10 | .byte 1 | (0 << 4) ; Vertical mirroring 11 | .byte 0 & $f0 ; Using mapper 0 (no extra memory mappers) 12 | .byte 0,0,0,0,0,0,0,0 ; Padding bytes to complete the header 13 | 14 | ;=============================================================================== 15 | ; Zero Page Segment 16 | .segment "ZEROPAGE" 17 | ; The zero page is a special area of memory that's faster for the CPU to access. 18 | ; Variables 19 | .include "../../library/libVariables.asm" 20 | .include "gameVariables.asm" 21 | 22 | ;=============================================================================== 23 | ; RAM Segment 24 | .segment "RAM" 25 | ; Defines a RAM segment to avoid assembler warnings in chapters not using FamiStudio. 26 | 27 | ;=============================================================================== 28 | .segment "CODE" 29 | ; Include library code and definitions 30 | .include "../../library/libDefines.asm" ; This file includes constants like color codes 31 | .include "../../library/libInput.asm" ; This file includes functions to handle player input 32 | .include "../../library/libMath.asm" ; This file includes math-related functions 33 | .include "../../library/libScreen.asm" ; This file includes functions to manage the screen 34 | .include "../../library/libSprite.asm" ; This file includes functions to manage sprites 35 | 36 | ; Include game code 37 | .include "gameTanks.asm" 38 | 39 | ;=============================================================================== 40 | ; Game Initialization 41 | gameMainInit: 42 | LIBSCREEN_INIT ; Initialize the screen and PPU 43 | 44 | ; Load background & sprite palettes 45 | LIBSCREEN_LOADPALETTE_AA BGPALETTE, PaletteBG 46 | LIBSCREEN_LOADPALETTE_AA SPPALETTE, PaletteSP 47 | 48 | ; Load screen data to the left nametable 49 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE0, DesertLeft 50 | 51 | ; Load screen data to the right nametable 52 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE1, DesertRight 53 | 54 | LIBSPRITE_INIT ; Initialize sprites 55 | LIBSCREEN_SETSPRITESIZE8x16 ; Set sprites to 8x16 pixel mode for larger character sizes 56 | LIBSCREEN_LEFT8PIXELSSPRITESDISABLE ; Disable sprite rendering in the leftmost 8 pixels of the screen 57 | jsr gameDataSpritesInit ; Initialize sprite data for the game 58 | LIBSCREEN_ENABLEPPU ; Enable PPU rendering (sprites, background) 59 | ; note no rts here so that code flows straight into gameMainUpdate 60 | 61 | ;=============================================================================== 62 | ; gameMain Game Update Loop 63 | gameMainUpdate: 64 | lda bFrameReady ; Load the bFrameReady flag 65 | beq gameMainUpdate ; If it's 0, keep waiting (loop) 66 | 67 | ; game code 68 | LIBINPUT_UPDATE ; update input 69 | jsr gameTanksUpdate ; update the tanks 70 | LIBSCREEN_WAITSPRITE0 ; wait for sprite 0 hit 71 | jsr gameMainUpdateBottomScroll 72 | 73 | lda #0 ; Reset bFrameReady to 0 74 | sta bFrameReady 75 | jmp gameMainUpdate ; Infinite loop 76 | 77 | ;=============================================================================== 78 | ; NMI (Vertical Blank) Interrupt Handler 79 | gameMainNMI: 80 | LIBSPRITE_UPDATE ; update sprites oam 81 | jsr gameMainUpdateTopScroll 82 | 83 | lda #1 ; Set bFrameReady flag to 1 84 | sta bFrameReady 85 | rti ; Return from interrupt (used to update graphics once per frame) 86 | 87 | ;=============================================================================== 88 | 89 | gameMainUpdateBottomScroll: 90 | ; Set the scroll for the bottom of the screen 91 | LIBSCREEN_SETSCROLL_AA wBottomScrollX+1, wBottomScrollY+1 92 | rts 93 | 94 | ;=============================================================================== 95 | 96 | gameMainUpdateTopScroll: 97 | ; Set the scroll for the top of the screen 98 | LIBSCREEN_SETSCROLL_AA wTopScrollX+1, wTopScrollY+1 99 | rts 100 | 101 | ;=============================================================================== 102 | ; Data 103 | .include "gameData.asm" 104 | 105 | ;=============================================================================== 106 | ; Character Segment (CHR-ROM) 107 | .segment "CHARS" 108 | ; ROM chars start at PPU address $0000 109 | .incbin "../../content/tankracebackground.chr" 110 | .incbin "../../content/tankracesprites.chr" 111 | 112 | ;=============================================================================== 113 | ; Interrupt Vectors 114 | .segment "VECTORS" ; This section tells the NES where to jump on specific events. 115 | .word gameMainNMI ; NMI (Non-Maskable Interrupt) - triggers once per frame, jump to vblank 116 | .word gameMainInit ; Reset - when the system starts, jump to gameMainInit 117 | .word 0 ; IRQ - Not used in this project -------------------------------------------------------------------------------- /chapters/chapter10/1/gameData.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition gameData 3 | ;=============================================================================== 4 | ; Constants 5 | Sprite0_X = 8 6 | Sprite0_Y = 162 7 | PL1_X = 32 8 | PL1_Y = 187 9 | CPU_X = 32 10 | CPU_Y = 152 11 | HUD_X = 28 12 | HUD_Y = 5 13 | HUD_WIDTH = 32 14 | 15 | ;=============================================================================== 16 | ; Palette Data 17 | PaletteBG: 18 | .incbin "../../content/tankracebackground.pal" 19 | 20 | PaletteSP: 21 | .incbin "../../content/tankracesprites.pal" 22 | 23 | ;=============================================================================== 24 | ; Screen Data 25 | DesertLeft: 26 | .incbin "../../content/tankracedesertleft.nam" 27 | 28 | DesertRight: 29 | .incbin "../../content/tankracedesertright.nam" 30 | 31 | ;=============================================================================== 32 | ; Sprite Data 33 | 34 | bSpriteTiles: 35 | /* 00-00 */ .byte $FE ; Sprite 0 36 | /* 01-11 */ .byte $02, $04, $06, $08, $0a, $20, $22, $24, $26, $28, $2A ; PL1 Tank 37 | /* 12-22 */ .byte $02, $04, $06, $08, $0a, $20, $22, $24, $26, $28, $2A ; CPU Tank 38 | /* 23-24 */ .byte $12, $14 ; CPU Label 39 | /* 25-26 */ .byte $0E, $10 ; PL1 Label 40 | /* 27-31 */ .byte $16, $16, $16, $16, $16 ; PL1 Power bar 41 | /* 32-38 */ .byte $1A, $18, $18, $18, $18, $18, $1A ; HUD bar 42 | /* 39-40 */ .byte $1C, $1C ; HUD markers 43 | /* end */ .byte $FF ; end flag to terminate init loop 44 | 45 | bSpritePalettes: 46 | /* 00-00 */ .byte $00 ; Sprite 0 47 | /* 01-11 */ .byte $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01 ; PL1 Tank 48 | /* 12-22 */ .byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 ; CPU Tank 49 | /* 23-24 */ .byte $02, $02 ; CPU Label 50 | /* 25-26 */ .byte $02, $02 ; PL1 Label 51 | /* 27-31 */ .byte $03, $03, $03, $03, $03 ; PL1 Power bar 52 | /* 32-38 */ .byte $01, $00, $00, $00, $00, $00, $02 ; HUD bar 53 | /* 39-40 */ .byte $00, $01 ; HUD markers 54 | 55 | bSpriteXPos: 56 | /* 00-00 */ .byte Sprite0_X ; Sprite 0 57 | /* 01-11 */ .byte PL1_X+8, PL1_X+16, PL1_X+24, PL1_X+32, PL1_X+40, PL1_X, PL1_X+8, PL1_X+16, PL1_X+24, PL1_X+32, PL1_X+40 ; PL1 Tank 58 | /* 12-22 */ .byte CPU_X+8, CPU_X+16, CPU_X+24, CPU_X+32, CPU_X+40, CPU_X, CPU_X+8, CPU_X+16, CPU_X+24, CPU_X+32, CPU_X+40 ; CPU Tank 59 | /* 23-24 */ .byte CPU_X+16, CPU_X+24 ; CPU Label 60 | /* 25-26 */ .byte PL1_X+16, PL1_X+24 ; PL1 Label 61 | /* 27-31 */ .byte PL1_X+3, PL1_X+11, PL1_X+19, PL1_X+27, PL1_X+35 ; PL1 Power bar 62 | /* 32-38 */ .byte HUD_X, HUD_X+(HUD_WIDTH*1), HUD_X+(HUD_WIDTH*2), HUD_X+(HUD_WIDTH*3), HUD_X+(HUD_WIDTH*4), HUD_X+(HUD_WIDTH*5), HUD_X+(HUD_WIDTH*6) ; HUD bar 63 | /* 39-40 */ .byte HUD_X, HUD_X ; HUD markers 64 | 65 | bSpriteYPos: 66 | /* 00-00 */ .byte Sprite0_Y ; Sprite 0 67 | /* 01-11 */ .byte PL1_Y, PL1_Y, PL1_Y, PL1_Y, PL1_Y, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16 ; PL1 Tank 68 | /* 12-22 */ .byte CPU_Y, CPU_Y, CPU_Y, CPU_Y, CPU_Y, CPU_Y+16, CPU_Y+16, CPU_Y+16, CPU_Y+16, CPU_Y+16, CPU_Y+16 ; CPU Tank 69 | /* 23-24 */ .byte CPU_X, CPU_Y ; CPU Label 70 | /* 25-26 */ .byte PL1_Y, PL1_Y ; PL1 Label 71 | /* 27-31 */ .byte PL1_Y+30, PL1_Y+30, PL1_Y+30, PL1_Y+30, PL1_Y+30 ; PL1 Power bar 72 | /* 32-38 */ .byte HUD_Y, HUD_Y, HUD_Y, HUD_Y, HUD_Y, HUD_Y, HUD_Y ; HUD bar 73 | /* 39-40 */ .byte HUD_Y+3, HUD_Y+12 ; HUD markers 74 | 75 | bSpriteXOffset: 76 | /* 00-00 */ .byte 0 ; Not used 77 | /* 01-11 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; Not used 78 | /* 12-22 */ .byte 8, 16, 24, 32, 40, 0, 8, 16, 24, 32, 40 ; CPU Tank X Offsets 79 | /* 23-24 */ .byte 16, 24 ; CPU Label X Offsets 80 | 81 | bSpriteYOffset: 82 | /* 00-00 */ .byte 0 ; Not used 83 | /* 01-11 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; Not used 84 | /* 12-22 */ .byte 0, 0, 0, 0, 0, 16, 16, 16, 16, 16, 16 ; CPU Tank Y Offsets 85 | /* 23-24 */ .byte 0, 0 ; CPU Label Y Offsets 86 | 87 | ;=============================================================================== 88 | 89 | gameDataSpritesInit: 90 | ldx #0 ; Initialize X register to 0 (starting index for sprites) 91 | sprite_loop: 92 | stx bLibTemp1 ; Store sprite index in bLibTemp1 (used for sprite number) 93 | 94 | ; Set sprite pattern tile 95 | lda bSpriteTiles, x ; Load sprite tile from array based on X index 96 | cmp #$FF ; Check if tile is the terminator ($FF) 97 | beq sprite_done ; If it's $FF, exit the loop 98 | sta bLibTemp2 ; Store tile in bLibTemp2 for later use 99 | LIBSPRITE_SETFRAME8x16_AAV bLibTemp1, bLibTemp2, 1 ; Set the sprite frame to 8x16 mode 100 | 101 | ; Set sprite palette 102 | lda bSpritePalettes, x ; Load sprite palette from array based on X index 103 | sta bLibTemp2 ; Store palette in bLibTemp2 104 | LIBSPRITE_SETPALETTE_AA bLibTemp1, bLibTemp2 ; Set sprite palette 105 | 106 | ; Set sprite X and Y position 107 | lda bSpriteXPos, x ; Load X position from array 108 | sta bLibTemp2 ; Store X position in bLibTemp2 109 | lda bSpriteYPos, x ; Load Y position from array 110 | sta bLibTemp3 ; Store Y position in bLibTemp3 111 | LIBSPRITE_SETPOSITION_AAA bLibTemp1, bLibTemp2, bLibTemp3 ; Set sprite position (X, Y) 112 | 113 | inx ; Increment X to move to the next sprite 114 | jmp sprite_loop ; Repeat the loop for the next sprite 115 | 116 | sprite_done: 117 | rts ; Return from subroutine -------------------------------------------------------------------------------- /chapters/chapter10/1/gameVariables.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition - gameVariables 3 | ;=============================================================================== 4 | ; Variables 5 | 6 | bLeftPressRequired: .byte 0 7 | bPL1Acceleration: .byte 0 8 | wPL1Velocity: .word 0 9 | wCPUAcceleration: .word 0 10 | wCPUVelocity: .word 0 11 | wCPUTankXPos: .word 0 ; 1st byte = subpixels, 2nd byte = pixels 12 | .byte 0 ; 3rd byte = nametable 13 | wCPUTankScreenXPos: .word 0 ; 1st byte = subpixels, 2nd byte = pixels 14 | .byte 0 ; 3rd byte = nametable -------------------------------------------------------------------------------- /chapters/chapter10/2/chapter10_2.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition chapter10_2 3 | ;=============================================================================== 4 | ; NES ROM Header 5 | .segment "HEADER" 6 | .byte "NES" ; 'NES' identifier - tells the system it's a valid NES ROM 7 | .byte $1a ; Control byte for NES compatibility 8 | .byte 2 ; Number of 16KB PRG-ROM banks (2 x 16KB = 32KB of code) 9 | .byte 1 ; Number of 8KB CHR-ROM banks (1 x 8KB = 8KB of graphics) 10 | .byte 1 | (0 << 4) ; Vertical mirroring 11 | .byte 0 & $f0 ; Using mapper 0 (no extra memory mappers) 12 | .byte 0,0,0,0,0,0,0,0 ; Padding bytes to complete the header 13 | 14 | ;=============================================================================== 15 | ; Zero Page Segment 16 | .segment "ZEROPAGE" 17 | ; The zero page is a special area of memory that's faster for the CPU to access. 18 | ; Variables 19 | .include "../../library/libVariables.asm" 20 | .include "gameVariables.asm" 21 | 22 | ;=============================================================================== 23 | ; RAM Segment 24 | .segment "RAM" 25 | ; Defines a RAM segment to avoid assembler warnings in chapters not using FamiStudio. 26 | 27 | ;=============================================================================== 28 | .segment "CODE" 29 | ; Include library code and definitions 30 | .include "../../library/libDefines.asm" ; This file includes constants like color codes 31 | .include "../../library/libInput.asm" ; This file includes functions to handle player input 32 | .include "../../library/libMath.asm" ; This file includes math-related functions 33 | .include "../../library/libScreen.asm" ; This file includes functions to manage the screen 34 | .include "../../library/libSprite.asm" ; This file includes functions to manage sprites 35 | 36 | ; Include game code 37 | .include "gameTanks.asm" 38 | 39 | ;=============================================================================== 40 | ; Game Initialization 41 | gameMainInit: 42 | LIBSCREEN_INIT ; Initialize the screen and PPU 43 | 44 | ; Load background & sprite palettes 45 | LIBSCREEN_LOADPALETTE_AA BGPALETTE, PaletteBG 46 | LIBSCREEN_LOADPALETTE_AA SPPALETTE, PaletteSP 47 | 48 | ; Load screen data to the left nametable 49 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE0, DesertLeft 50 | 51 | ; Load screen data to the right nametable 52 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE1, DesertRight 53 | 54 | LIBSPRITE_INIT ; Initialize sprites 55 | LIBSCREEN_SETSPRITESIZE8x16 ; Set sprites to 8x16 pixel mode for larger character sizes 56 | LIBSCREEN_LEFT8PIXELSSPRITESDISABLE ; Disable sprite rendering in the leftmost 8 pixels of the screen 57 | jsr gameDataSpritesInit ; Initialize sprite data for the game 58 | LIBSCREEN_ENABLEPPU ; Enable PPU rendering (sprites, background) 59 | ; note no rts here so that code flows straight into gameMainUpdate 60 | 61 | ;=============================================================================== 62 | ; gameMain Game Update Loop 63 | gameMainUpdate: 64 | lda bFrameReady ; Load the bFrameReady flag 65 | beq gameMainUpdate ; If it's 0, keep waiting (loop) 66 | 67 | ; game code 68 | LIBINPUT_UPDATE ; update input 69 | jsr gameTanksUpdate ; update the tanks 70 | LIBSCREEN_WAITSPRITE0 ; wait for sprite 0 hit 71 | jsr gameMainUpdateBottomScroll 72 | 73 | lda #0 ; Reset bFrameReady to 0 74 | sta bFrameReady 75 | jmp gameMainUpdate ; Infinite loop 76 | 77 | ;=============================================================================== 78 | ; NMI (Vertical Blank) Interrupt Handler 79 | gameMainNMI: 80 | LIBSPRITE_UPDATE ; update sprites oam 81 | jsr gameMainUpdateTopScroll 82 | 83 | lda #1 ; Set bFrameReady flag to 1 84 | sta bFrameReady 85 | rti ; Return from interrupt (used to update graphics once per frame) 86 | 87 | ;=============================================================================== 88 | 89 | gameMainUpdateBottomScroll: 90 | ; Set the scroll for the bottom of the screen 91 | LIBSCREEN_SETSCROLL_AA wBottomScrollX+1, wBottomScrollY+1 92 | rts 93 | 94 | ;=============================================================================== 95 | 96 | gameMainUpdateTopScroll: 97 | ; Set the scroll for the top of the screen 98 | LIBSCREEN_SETSCROLL_AA wTopScrollX+1, wTopScrollY+1 99 | rts 100 | 101 | ;=============================================================================== 102 | ; Data 103 | .include "gameData.asm" 104 | 105 | ;=============================================================================== 106 | ; Character Segment (CHR-ROM) 107 | .segment "CHARS" 108 | ; ROM chars start at PPU address $0000 109 | .incbin "../../content/tankracebackground.chr" 110 | .incbin "../../content/tankracesprites.chr" 111 | 112 | ;=============================================================================== 113 | ; Interrupt Vectors 114 | .segment "VECTORS" ; This section tells the NES where to jump on specific events. 115 | .word gameMainNMI ; NMI (Non-Maskable Interrupt) - triggers once per frame, jump to vblank 116 | .word gameMainInit ; Reset - when the system starts, jump to gameMainInit 117 | .word 0 ; IRQ - Not used in this project -------------------------------------------------------------------------------- /chapters/chapter10/2/gameData.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition gameData 3 | ;=============================================================================== 4 | ; Constants 5 | Sprite0_X = 8 6 | Sprite0_Y = 162 7 | PL1_X = 32 8 | PL1_Y = 187 9 | CPU_X = 32 10 | CPU_Y = 152 11 | HUD_X = 28 12 | HUD_Y = 5 13 | HUD_WIDTH = 32 14 | 15 | ;=============================================================================== 16 | ; Palette Data 17 | PaletteBG: 18 | .incbin "../../content/tankracebackground.pal" 19 | 20 | PaletteSP: 21 | .incbin "../../content/tankracesprites.pal" 22 | 23 | ;=============================================================================== 24 | ; Screen Data 25 | DesertLeft: 26 | .incbin "../../content/tankracedesertleft.nam" 27 | 28 | DesertRight: 29 | .incbin "../../content/tankracedesertright.nam" 30 | 31 | ;=============================================================================== 32 | ; Sprite Data 33 | 34 | bSpriteTiles: 35 | /* 00-00 */ .byte $FE ; Sprite 0 36 | /* 01-11 */ .byte $02, $04, $06, $08, $0a, $20, $22, $24, $26, $28, $2A ; PL1 Tank 37 | /* 12-22 */ .byte $02, $04, $06, $08, $0a, $20, $22, $24, $26, $28, $2A ; CPU Tank 38 | /* 23-24 */ .byte $12, $14 ; CPU Label 39 | /* 25-26 */ .byte $0E, $10 ; PL1 Label 40 | /* 27-31 */ .byte $16, $16, $16, $16, $16 ; PL1 Power bar 41 | /* 32-38 */ .byte $1A, $18, $18, $18, $18, $18, $1A ; HUD bar 42 | /* 39-40 */ .byte $1C, $1C ; HUD markers 43 | /* end */ .byte $FF ; end flag to terminate init loop 44 | 45 | bSpritePalettes: 46 | /* 00-00 */ .byte $00 ; Sprite 0 47 | /* 01-11 */ .byte $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01 ; PL1 Tank 48 | /* 12-22 */ .byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 ; CPU Tank 49 | /* 23-24 */ .byte $02, $02 ; CPU Label 50 | /* 25-26 */ .byte $02, $02 ; PL1 Label 51 | /* 27-31 */ .byte $03, $03, $03, $03, $03 ; PL1 Power bar 52 | /* 32-38 */ .byte $01, $00, $00, $00, $00, $00, $02 ; HUD bar 53 | /* 39-40 */ .byte $00, $01 ; HUD markers 54 | 55 | bSpriteXPos: 56 | /* 00-00 */ .byte Sprite0_X ; Sprite 0 57 | /* 01-11 */ .byte PL1_X+8, PL1_X+16, PL1_X+24, PL1_X+32, PL1_X+40, PL1_X, PL1_X+8, PL1_X+16, PL1_X+24, PL1_X+32, PL1_X+40 ; PL1 Tank 58 | /* 12-22 */ .byte CPU_X+8, CPU_X+16, CPU_X+24, CPU_X+32, CPU_X+40, CPU_X, CPU_X+8, CPU_X+16, CPU_X+24, CPU_X+32, CPU_X+40 ; CPU Tank 59 | /* 23-24 */ .byte CPU_X+16, CPU_X+24 ; CPU Label 60 | /* 25-26 */ .byte PL1_X+16, PL1_X+24 ; PL1 Label 61 | /* 27-31 */ .byte PL1_X+3, PL1_X+11, PL1_X+19, PL1_X+27, PL1_X+35 ; PL1 Power bar 62 | /* 32-38 */ .byte HUD_X, HUD_X+(HUD_WIDTH*1), HUD_X+(HUD_WIDTH*2), HUD_X+(HUD_WIDTH*3), HUD_X+(HUD_WIDTH*4), HUD_X+(HUD_WIDTH*5), HUD_X+(HUD_WIDTH*6) ; HUD bar 63 | /* 39-40 */ .byte HUD_X, HUD_X ; HUD markers 64 | 65 | bSpriteYPos: 66 | /* 00-00 */ .byte Sprite0_Y ; Sprite 0 67 | /* 01-11 */ .byte PL1_Y, PL1_Y, PL1_Y, PL1_Y, PL1_Y, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16 ; PL1 Tank 68 | /* 12-22 */ .byte CPU_Y, CPU_Y, CPU_Y, CPU_Y, CPU_Y, CPU_Y+16, CPU_Y+16, CPU_Y+16, CPU_Y+16, CPU_Y+16, CPU_Y+16 ; CPU Tank 69 | /* 23-24 */ .byte CPU_X, CPU_Y ; CPU Label 70 | /* 25-26 */ .byte PL1_Y, PL1_Y ; PL1 Label 71 | /* 27-31 */ .byte PL1_Y+30, PL1_Y+30, PL1_Y+30, PL1_Y+30, PL1_Y+30 ; PL1 Power bar 72 | /* 32-38 */ .byte HUD_Y, HUD_Y, HUD_Y, HUD_Y, HUD_Y, HUD_Y, HUD_Y ; HUD bar 73 | /* 39-40 */ .byte HUD_Y+3, HUD_Y+12 ; HUD markers 74 | 75 | bSpriteXOffset: 76 | /* 00-00 */ .byte 0 ; Not used 77 | /* 01-11 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; Not used 78 | /* 12-22 */ .byte 8, 16, 24, 32, 40, 0, 8, 16, 24, 32, 40 ; CPU Tank X Offsets 79 | /* 23-24 */ .byte 16, 24 ; CPU Label X Offsets 80 | 81 | bSpriteYOffset: 82 | /* 00-00 */ .byte 0 ; Not used 83 | /* 01-11 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; Not used 84 | /* 12-22 */ .byte 0, 0, 0, 0, 0, 16, 16, 16, 16, 16, 16 ; CPU Tank Y Offsets 85 | /* 23-24 */ .byte 0, 0 ; CPU Label Y Offsets 86 | 87 | ;=============================================================================== 88 | 89 | gameDataSpritesInit: 90 | ldx #0 ; Initialize X register to 0 (starting index for sprites) 91 | sprite_loop: 92 | stx bLibTemp1 ; Store sprite index in bLibTemp1 (used for sprite number) 93 | 94 | ; Set sprite pattern tile 95 | lda bSpriteTiles, x ; Load sprite tile from array based on X index 96 | cmp #$FF ; Check if tile is the terminator ($FF) 97 | beq sprite_done ; If it's $FF, exit the loop 98 | sta bLibTemp2 ; Store tile in bLibTemp2 for later use 99 | LIBSPRITE_SETFRAME8x16_AAV bLibTemp1, bLibTemp2, 1 ; Set the sprite frame to 8x16 mode 100 | 101 | ; Set sprite palette 102 | lda bSpritePalettes, x ; Load sprite palette from array based on X index 103 | sta bLibTemp2 ; Store palette in bLibTemp2 104 | LIBSPRITE_SETPALETTE_AA bLibTemp1, bLibTemp2 ; Set sprite palette 105 | 106 | ; Set sprite X and Y position 107 | lda bSpriteXPos, x ; Load X position from array 108 | sta bLibTemp2 ; Store X position in bLibTemp2 109 | lda bSpriteYPos, x ; Load Y position from array 110 | sta bLibTemp3 ; Store Y position in bLibTemp3 111 | LIBSPRITE_SETPOSITION_AAA bLibTemp1, bLibTemp2, bLibTemp3 ; Set sprite position (X, Y) 112 | 113 | inx ; Increment X to move to the next sprite 114 | jmp sprite_loop ; Repeat the loop for the next sprite 115 | 116 | sprite_done: 117 | rts ; Return from subroutine -------------------------------------------------------------------------------- /chapters/chapter10/2/gameVariables.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition - gameVariables 3 | ;=============================================================================== 4 | ; Variables 5 | 6 | bLeftPressRequired: .byte 0 7 | bPL1Acceleration: .byte 0 8 | wPL1Velocity: .word 0 9 | wCPUAcceleration: .word 0 10 | wCPUVelocity: .word 0 11 | wCPUTankXPos: .word 0 ; 1st byte = subpixels, 2nd byte = pixels 12 | .byte 0 ; 3rd byte = nametable 13 | wCPUTankScreenXPos: .word 0 ; 1st byte = subpixels, 2nd byte = pixels 14 | .byte 0 ; 3rd byte = nametable -------------------------------------------------------------------------------- /chapters/chapter10/3/chapter10_3.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition chapter10_3 3 | ;=============================================================================== 4 | ; NES ROM Header 5 | .segment "HEADER" 6 | .byte "NES" ; 'NES' identifier - tells the system it's a valid NES ROM 7 | .byte $1a ; Control byte for NES compatibility 8 | .byte 2 ; Number of 16KB PRG-ROM banks (2 x 16KB = 32KB of code) 9 | .byte 1 ; Number of 8KB CHR-ROM banks (1 x 8KB = 8KB of graphics) 10 | .byte 1 | (0 << 4) ; Vertical mirroring 11 | .byte 0 & $f0 ; Using mapper 0 (no extra memory mappers) 12 | .byte 0,0,0,0,0,0,0,0 ; Padding bytes to complete the header 13 | 14 | ;=============================================================================== 15 | ; Zero Page Segment 16 | .segment "ZEROPAGE" 17 | ; The zero page is a special area of memory that's faster for the CPU to access. 18 | ; Variables 19 | .include "../../library/libVariables.asm" 20 | .include "gameVariables.asm" 21 | 22 | ;=============================================================================== 23 | ; RAM Segment 24 | .segment "RAM" 25 | ; Defines a RAM segment to avoid assembler warnings in chapters not using FamiStudio. 26 | 27 | ;=============================================================================== 28 | .segment "CODE" 29 | ; Include library code and definitions 30 | .include "../../library/libDefines.asm" ; This file includes constants like color codes 31 | .include "../../library/libInput.asm" ; This file includes functions to handle player input 32 | .include "../../library/libMath.asm" ; This file includes math-related functions 33 | .include "../../library/libScreen.asm" ; This file includes functions to manage the screen 34 | .include "../../library/libSprite.asm" ; This file includes functions to manage sprites 35 | 36 | ; Include game code 37 | .include "gameTanks.asm" 38 | 39 | ;=============================================================================== 40 | ; Game Initialization 41 | gameMainInit: 42 | LIBSCREEN_INIT ; Initialize the screen and PPU 43 | 44 | ; Load background & sprite palettes 45 | LIBSCREEN_LOADPALETTE_AA BGPALETTE, PaletteBG 46 | LIBSCREEN_LOADPALETTE_AA SPPALETTE, PaletteSP 47 | 48 | ; Load screen data to the left nametable 49 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE0, DesertLeft 50 | 51 | ; Load screen data to the right nametable 52 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE1, DesertRight 53 | 54 | LIBSPRITE_INIT ; Initialize sprites 55 | LIBSCREEN_SETSPRITESIZE8x16 ; Set sprites to 8x16 pixel mode for larger character sizes 56 | LIBSCREEN_LEFT8PIXELSSPRITESDISABLE ; Disable sprite rendering in the leftmost 8 pixels of the screen 57 | jsr gameDataSpritesInit ; Initialize sprite data for the game 58 | LIBSCREEN_ENABLEPPU ; Enable PPU rendering (sprites, background) 59 | ; note no rts here so that code flows straight into gameMainUpdate 60 | 61 | ;=============================================================================== 62 | ; gameMain Game Update Loop 63 | gameMainUpdate: 64 | lda bFrameReady ; Load the bFrameReady flag 65 | beq gameMainUpdate ; If it's 0, keep waiting (loop) 66 | 67 | ; game code 68 | LIBINPUT_UPDATE ; update input 69 | jsr gameTanksUpdate ; update the tanks 70 | LIBSCREEN_WAITSPRITE0 ; wait for sprite 0 hit 71 | jsr gameMainUpdateBottomScroll 72 | 73 | lda #0 ; Reset bFrameReady to 0 74 | sta bFrameReady 75 | jmp gameMainUpdate ; Infinite loop 76 | 77 | ;=============================================================================== 78 | ; NMI (Vertical Blank) Interrupt Handler 79 | gameMainNMI: 80 | LIBSPRITE_UPDATE ; update sprites oam 81 | jsr gameMainUpdateTopScroll 82 | 83 | lda #1 ; Set bFrameReady flag to 1 84 | sta bFrameReady 85 | rti ; Return from interrupt (used to update graphics once per frame) 86 | 87 | ;=============================================================================== 88 | 89 | gameMainUpdateBottomScroll: 90 | ; Set the scroll for the bottom of the screen 91 | LIBSCREEN_SETSCROLL_AA wBottomScrollX+1, wBottomScrollY+1 92 | rts 93 | 94 | ;=============================================================================== 95 | 96 | gameMainUpdateTopScroll: 97 | ; Set the scroll for the top of the screen 98 | LIBSCREEN_SETSCROLL_AA wTopScrollX+1, wTopScrollY+1 99 | rts 100 | 101 | ;=============================================================================== 102 | ; Data 103 | .include "gameData.asm" 104 | 105 | ;=============================================================================== 106 | ; Character Segment (CHR-ROM) 107 | .segment "CHARS" 108 | ; ROM chars start at PPU address $0000 109 | .incbin "../../content/tankracebackground.chr" 110 | .incbin "../../content/tankracesprites.chr" 111 | 112 | ;=============================================================================== 113 | ; Interrupt Vectors 114 | .segment "VECTORS" ; This section tells the NES where to jump on specific events. 115 | .word gameMainNMI ; NMI (Non-Maskable Interrupt) - triggers once per frame, jump to vblank 116 | .word gameMainInit ; Reset - when the system starts, jump to gameMainInit 117 | .word 0 ; IRQ - Not used in this project -------------------------------------------------------------------------------- /chapters/chapter10/3/gameData.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition gameData 3 | ;=============================================================================== 4 | ; Constants 5 | Sprite0_X = 8 6 | Sprite0_Y = 162 7 | PL1_X = 32 8 | PL1_Y = 187 9 | CPU_X = 32 10 | CPU_Y = 152 11 | HUD_X = 28 12 | HUD_Y = 5 13 | HUD_WIDTH = 32 14 | 15 | ;=============================================================================== 16 | ; Palette Data 17 | PaletteBG: 18 | .incbin "../../content/tankracebackground.pal" 19 | 20 | PaletteSP: 21 | .incbin "../../content/tankracesprites.pal" 22 | 23 | ;=============================================================================== 24 | ; Screen Data 25 | DesertLeft: 26 | .incbin "../../content/tankracedesertleft.nam" 27 | 28 | DesertRight: 29 | .incbin "../../content/tankracedesertright.nam" 30 | 31 | ;=============================================================================== 32 | ; Sprite Data 33 | 34 | bSpriteTiles: 35 | /* 00-00 */ .byte $FE ; Sprite 0 36 | /* 01-11 */ .byte $02, $04, $06, $08, $0a, $20, $22, $24, $26, $28, $2A ; PL1 Tank 37 | /* 12-22 */ .byte $02, $04, $06, $08, $0a, $20, $22, $24, $26, $28, $2A ; CPU Tank 38 | /* 23-24 */ .byte $12, $14 ; CPU Label 39 | /* 25-26 */ .byte $0E, $10 ; PL1 Label 40 | /* 27-31 */ .byte $16, $16, $16, $16, $16 ; PL1 Power bar 41 | /* 32-38 */ .byte $1A, $18, $18, $18, $18, $18, $1A ; HUD bar 42 | /* 39-40 */ .byte $1C, $1C ; HUD markers 43 | /* end */ .byte $FF ; end flag to terminate init loop 44 | 45 | bSpritePalettes: 46 | /* 00-00 */ .byte $00 ; Sprite 0 47 | /* 01-11 */ .byte $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01 ; PL1 Tank 48 | /* 12-22 */ .byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 ; CPU Tank 49 | /* 23-24 */ .byte $02, $02 ; CPU Label 50 | /* 25-26 */ .byte $02, $02 ; PL1 Label 51 | /* 27-31 */ .byte $03, $03, $03, $03, $03 ; PL1 Power bar 52 | /* 32-38 */ .byte $01, $00, $00, $00, $00, $00, $02 ; HUD bar 53 | /* 39-40 */ .byte $00, $01 ; HUD markers 54 | 55 | bSpriteXPos: 56 | /* 00-00 */ .byte Sprite0_X ; Sprite 0 57 | /* 01-11 */ .byte PL1_X+8, PL1_X+16, PL1_X+24, PL1_X+32, PL1_X+40, PL1_X, PL1_X+8, PL1_X+16, PL1_X+24, PL1_X+32, PL1_X+40 ; PL1 Tank 58 | /* 12-22 */ .byte CPU_X+8, CPU_X+16, CPU_X+24, CPU_X+32, CPU_X+40, CPU_X, CPU_X+8, CPU_X+16, CPU_X+24, CPU_X+32, CPU_X+40 ; CPU Tank 59 | /* 23-24 */ .byte CPU_X+16, CPU_X+24 ; CPU Label 60 | /* 25-26 */ .byte PL1_X+16, PL1_X+24 ; PL1 Label 61 | /* 27-31 */ .byte PL1_X+3, PL1_X+11, PL1_X+19, PL1_X+27, PL1_X+35 ; PL1 Power bar 62 | /* 32-38 */ .byte HUD_X, HUD_X+(HUD_WIDTH*1), HUD_X+(HUD_WIDTH*2), HUD_X+(HUD_WIDTH*3), HUD_X+(HUD_WIDTH*4), HUD_X+(HUD_WIDTH*5), HUD_X+(HUD_WIDTH*6) ; HUD bar 63 | /* 39-40 */ .byte HUD_X, HUD_X ; HUD markers 64 | 65 | bSpriteYPos: 66 | /* 00-00 */ .byte Sprite0_Y ; Sprite 0 67 | /* 01-11 */ .byte PL1_Y, PL1_Y, PL1_Y, PL1_Y, PL1_Y, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16 ; PL1 Tank 68 | /* 12-22 */ .byte CPU_Y, CPU_Y, CPU_Y, CPU_Y, CPU_Y, CPU_Y+16, CPU_Y+16, CPU_Y+16, CPU_Y+16, CPU_Y+16, CPU_Y+16 ; CPU Tank 69 | /* 23-24 */ .byte CPU_X, CPU_Y ; CPU Label 70 | /* 25-26 */ .byte PL1_Y, PL1_Y ; PL1 Label 71 | /* 27-31 */ .byte PL1_Y+30, PL1_Y+30, PL1_Y+30, PL1_Y+30, PL1_Y+30 ; PL1 Power bar 72 | /* 32-38 */ .byte HUD_Y, HUD_Y, HUD_Y, HUD_Y, HUD_Y, HUD_Y, HUD_Y ; HUD bar 73 | /* 39-40 */ .byte HUD_Y+3, HUD_Y+12 ; HUD markers 74 | 75 | bSpriteXOffset: 76 | /* 00-00 */ .byte 0 ; Not used 77 | /* 01-11 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; Not used 78 | /* 12-22 */ .byte 8, 16, 24, 32, 40, 0, 8, 16, 24, 32, 40 ; CPU Tank X Offsets 79 | /* 23-24 */ .byte 16, 24 ; CPU Label X Offsets 80 | 81 | bSpriteYOffset: 82 | /* 00-00 */ .byte 0 ; Not used 83 | /* 01-11 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; Not used 84 | /* 12-22 */ .byte 0, 0, 0, 0, 0, 16, 16, 16, 16, 16, 16 ; CPU Tank Y Offsets 85 | /* 23-24 */ .byte 0, 0 ; CPU Label Y Offsets 86 | 87 | ;=============================================================================== 88 | 89 | gameDataSpritesInit: 90 | ldx #0 ; Initialize X register to 0 (starting index for sprites) 91 | sprite_loop: 92 | stx bLibTemp1 ; Store sprite index in bLibTemp1 (used for sprite number) 93 | 94 | ; Set sprite pattern tile 95 | lda bSpriteTiles, x ; Load sprite tile from array based on X index 96 | cmp #$FF ; Check if tile is the terminator ($FF) 97 | beq sprite_done ; If it's $FF, exit the loop 98 | sta bLibTemp2 ; Store tile in bLibTemp2 for later use 99 | LIBSPRITE_SETFRAME8x16_AAV bLibTemp1, bLibTemp2, 1 ; Set the sprite frame to 8x16 mode 100 | 101 | ; Set sprite palette 102 | lda bSpritePalettes, x ; Load sprite palette from array based on X index 103 | sta bLibTemp2 ; Store palette in bLibTemp2 104 | LIBSPRITE_SETPALETTE_AA bLibTemp1, bLibTemp2 ; Set sprite palette 105 | 106 | ; Set sprite X and Y position 107 | lda bSpriteXPos, x ; Load X position from array 108 | sta bLibTemp2 ; Store X position in bLibTemp2 109 | lda bSpriteYPos, x ; Load Y position from array 110 | sta bLibTemp3 ; Store Y position in bLibTemp3 111 | LIBSPRITE_SETPOSITION_AAA bLibTemp1, bLibTemp2, bLibTemp3 ; Set sprite position (X, Y) 112 | 113 | inx ; Increment X to move to the next sprite 114 | jmp sprite_loop ; Repeat the loop for the next sprite 115 | 116 | sprite_done: 117 | rts ; Return from subroutine -------------------------------------------------------------------------------- /chapters/chapter10/3/gameVariables.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition - gameVariables 3 | ;=============================================================================== 4 | ; Variables 5 | 6 | bLeftPressRequired: .byte 0 7 | bPL1Acceleration: .byte 0 8 | wPL1Velocity: .word 0 9 | wCPUAcceleration: .word 0 10 | wCPUVelocity: .word 0 11 | wCPUTankXPos: .word 0 ; 1st byte = subpixels, 2nd byte = pixels 12 | .byte 0 ; 3rd byte = nametable 13 | wCPUTankScreenXPos: .word 0 ; 1st byte = subpixels, 2nd byte = pixels 14 | .byte 0 ; 3rd byte = nametable -------------------------------------------------------------------------------- /chapters/chapter11/1/chapter11_1.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition chapter11_1 3 | ;=============================================================================== 4 | ; NES ROM Header 5 | .segment "HEADER" 6 | .byte "NES" ; 'NES' identifier - tells the system it's a valid NES ROM 7 | .byte $1a ; Control byte for NES compatibility 8 | .byte 2 ; Number of 16KB PRG-ROM banks (2 x 16KB = 32KB of code) 9 | .byte 1 ; Number of 8KB CHR-ROM banks (1 x 8KB = 8KB of graphics) 10 | .byte 1 | (0 << 4) ; Vertical mirroring 11 | .byte 0 & $f0 ; Using mapper 0 (no extra memory mappers) 12 | .byte 0,0,0,0,0,0,0,0 ; Padding bytes to complete the header 13 | 14 | ;=============================================================================== 15 | ; Zero Page Segment 16 | .segment "ZEROPAGE" 17 | ; The zero page is a special area of memory that's faster for the CPU to access. 18 | ; Variables 19 | .include "../../library/libVariables.asm" 20 | .include "gameVariables.asm" 21 | 22 | ;=============================================================================== 23 | ; RAM Segment 24 | .segment "RAM" 25 | ; Defines a RAM segment to avoid assembler warnings in chapters not using FamiStudio. 26 | 27 | ;=============================================================================== 28 | .segment "CODE" 29 | ; Include library code and definitions 30 | .include "../../library/libDefines.asm" ; This file includes constants like color codes 31 | .include "../../library/libInput.asm" ; This file includes functions to handle player input 32 | .include "../../library/libMath.asm" ; This file includes math-related functions 33 | .include "../../library/libScreen.asm" ; This file includes functions to manage the screen 34 | .include "../../library/libSprite.asm" ; This file includes functions to manage sprites 35 | 36 | ; Include game code 37 | .include "gameTanks.asm" 38 | .include "gameFlow.asm" 39 | 40 | ;=============================================================================== 41 | ; Game Initialization 42 | gameMainInit: 43 | LIBSCREEN_INIT ; Initialize the screen and PPU 44 | LIBSPRITE_INIT ; Initialize sprites 45 | LIBSCREEN_SETSPRITESIZE8x16 ; Set sprites to 8x16 pixel mode for larger character sizes 46 | LIBSCREEN_LEFT8PIXELSSPRITESDISABLE ; Disable sprite rendering in the leftmost 8 pixels of the screen 47 | jsr gameFlowInit ; Initialize game flow for the game 48 | ; note no rts here so that code flows straight into gameMainUpdate 49 | 50 | ;=============================================================================== 51 | ; gameMain Game Update Loop 52 | gameMainUpdate: 53 | lda bFrameReady ; Load the bFrameReady flag 54 | beq gameMainUpdate ; If it's 0, keep waiting (loop) 55 | 56 | ; game code 57 | LIBINPUT_UPDATE ; update input 58 | jsr gameFlowUpdate ; Update the game flow subroutines 59 | LIBSCREEN_WAITSPRITE0 ; wait for sprite 0 hit 60 | jsr gameMainUpdateBottomScroll 61 | 62 | lda #0 ; Reset bFrameReady to 0 63 | sta bFrameReady 64 | jmp gameMainUpdate ; Infinite loop 65 | 66 | ;=============================================================================== 67 | ; NMI (Vertical Blank) Interrupt Handler 68 | gameMainNMI: 69 | LIBSPRITE_UPDATE ; update sprites oam 70 | jsr gameMainUpdateTopScroll 71 | 72 | lda #1 ; Set bFrameReady flag to 1 73 | sta bFrameReady 74 | rti ; Return from interrupt (used to update graphics once per frame) 75 | 76 | ;=============================================================================== 77 | 78 | gameMainUpdateBottomScroll: 79 | ; Set the scroll for the bottom of the screen 80 | LIBSCREEN_SETSCROLL_AA wBottomScrollX+1, wBottomScrollY+1 81 | rts 82 | 83 | ;=============================================================================== 84 | 85 | gameMainUpdateTopScroll: 86 | ; Set the scroll for the top of the screen 87 | LIBSCREEN_SETSCROLL_AA wTopScrollX+1, wTopScrollY+1 88 | rts 89 | 90 | ;=============================================================================== 91 | ; Data 92 | .include "gameData.asm" 93 | 94 | ;=============================================================================== 95 | ; Character Segment (CHR-ROM) 96 | .segment "CHARS" 97 | ; ROM chars start at PPU address $0000 98 | .incbin "../../content/tankracebackground.chr" 99 | .incbin "../../content/tankracesprites.chr" 100 | 101 | ;=============================================================================== 102 | ; Interrupt Vectors 103 | .segment "VECTORS" ; This section tells the NES where to jump on specific events. 104 | .word gameMainNMI ; NMI (Non-Maskable Interrupt) - triggers once per frame, jump to vblank 105 | .word gameMainInit ; Reset - when the system starts, jump to gameMainInit 106 | .word 0 ; IRQ - Not used in this project -------------------------------------------------------------------------------- /chapters/chapter11/1/gameFlow.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition - gameFlow 3 | ;=============================================================================== 4 | ; Constants 5 | 6 | FlowStateStart = 0 7 | FlowStateGame = 1 8 | FlowPressA_X = 100 9 | FlowPressA_Y = 120 10 | 11 | ;=============================================================================== 12 | ; Jump Tables 13 | 14 | gameFlowJumpTable: 15 | .word gameFlowUpdateStart 16 | .word gameFlowUpdateGame 17 | 18 | ;=============================================================================== 19 | ; Subroutines 20 | 21 | gameFlowInit: 22 | jsr gameFlowLoadGame 23 | jsr gameDataSpritesInit 24 | 25 | lda #255 26 | sta bGoCountDown ; set the countdown to max 255 27 | rts 28 | 29 | ;=============================================================================== 30 | 31 | gameFlowUpdate: 32 | lda bFlowState ; Get the current state into A 33 | asl ; Multiply by 2 34 | tay ; Copy A to Y 35 | lda gameFlowJumpTable,y ; Lookup low byte 36 | sta bLibTemp1 ; Store in a temporary variable 37 | lda gameFlowJumpTable+1,y ; Lookup high byte 38 | sta bLibTemp2 ; Store in temporary variable+1 39 | jmp (bLibTemp1) ; Indirect jump to subroutine 40 | 41 | ;=============================================================================== 42 | 43 | gameFlowUpdateStart: 44 | 45 | lda bGoCountDown 46 | 47 | cmp #250 48 | bne :+ 49 | LIBSPRITE_SETFRAME8x16_VVV 60, $6A, 1 ; $6A = 5 50 | : 51 | cmp #200 52 | bne :+ 53 | LIBSPRITE_SETFRAME8x16_VVV 60, $68, 1 ; $68 = 4 54 | : 55 | cmp #150 56 | bne :+ 57 | LIBSPRITE_SETFRAME8x16_VVV 60, $66, 1 ; $66 = 3 58 | : 59 | cmp #100 60 | bne :+ 61 | LIBSPRITE_SETFRAME8x16_VVV 60, $64, 1 ; $64 = 2 62 | : 63 | cmp #50 64 | bne :+ 65 | LIBSPRITE_SETFRAME8x16_VVV 60, $62, 1 ; $62 = 1 66 | : 67 | cmp #0 68 | bne :+ 69 | 70 | ; move all sprite y's for level # to $FF 71 | LIBSPRITE_SETPOSITIONY_VV 54, $FF 72 | LIBSPRITE_SETPOSITIONY_VV 55, $FF 73 | LIBSPRITE_SETPOSITIONY_VV 56, $FF 74 | LIBSPRITE_SETPOSITIONY_VV 57, $FF 75 | LIBSPRITE_SETPOSITIONY_VV 58, $FF 76 | LIBSPRITE_SETPOSITIONY_VV 59, $FF 77 | LIBSPRITE_SETPOSITIONY_VV 60, $FF 78 | 79 | ; move to game state 80 | lda #FlowStateGame 81 | sta bFlowState 82 | 83 | lda #0 84 | sta wTopScrollX 85 | sta wTopScrollX+1 86 | sta wTopScrollX+2 87 | sta wBottomScrollX 88 | sta wBottomScrollX+1 89 | sta wBottomScrollX+2 90 | : 91 | ; update start state here 92 | dec bGoCountDown 93 | 94 | rts 95 | 96 | ;=============================================================================== 97 | 98 | gameFlowLoadGame: 99 | LIBSCREEN_DISABLEPPU 100 | 101 | ; Load background & sprite palettes 102 | LIBSCREEN_LOADPALETTE_AA BGPALETTE, PaletteBG 103 | LIBSCREEN_LOADPALETTE_AA SPPALETTE, PaletteSP 104 | 105 | ; Load screen data to the left nametable 106 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE0, DesertLeft 107 | 108 | ; Load screen data to the right nametable 109 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE1, DesertRight 110 | 111 | LIBSCREEN_ENABLEPPU 112 | rts 113 | 114 | ;=============================================================================== 115 | 116 | gameFlowUpdateGame: 117 | ; update game state here 118 | jsr gameTanksUpdate 119 | rts -------------------------------------------------------------------------------- /chapters/chapter11/1/gameVariables.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition - gameVariables 3 | ;=============================================================================== 4 | ; Variables 5 | 6 | bLeftPressRequired: .byte 0 7 | bPL1Acceleration: .byte 0 8 | wPL1Velocity: .word 0 9 | wCPUAcceleration: .word 0 10 | wCPUVelocity: .word 0 11 | wCPUTankXPos: .word 0 ; 1st byte = subpixels, 2nd byte = pixels 12 | .byte 0 ; 3rd byte = nametable 13 | wCPUTankScreenXPos: .word 0 ; 1st byte = subpixels, 2nd byte = pixels 14 | .byte 0 ; 3rd byte = nametable 15 | bFlowState: .byte 0 16 | bGoCountDown: .byte 0 -------------------------------------------------------------------------------- /chapters/chapter11/2/chapter11_2.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition chapter11_2 3 | ;=============================================================================== 4 | ; NES ROM Header 5 | .segment "HEADER" 6 | .byte "NES" ; 'NES' identifier - tells the system it's a valid NES ROM 7 | .byte $1a ; Control byte for NES compatibility 8 | .byte 2 ; Number of 16KB PRG-ROM banks (2 x 16KB = 32KB of code) 9 | .byte 1 ; Number of 8KB CHR-ROM banks (1 x 8KB = 8KB of graphics) 10 | .byte 1 | (0 << 4) ; Vertical mirroring 11 | .byte 0 & $f0 ; Using mapper 0 (no extra memory mappers) 12 | .byte 0,0,0,0,0,0,0,0 ; Padding bytes to complete the header 13 | 14 | ;=============================================================================== 15 | ; Zero Page Segment 16 | .segment "ZEROPAGE" 17 | ; The zero page is a special area of memory that's faster for the CPU to access. 18 | ; Variables 19 | .include "../../library/libVariables.asm" 20 | .include "gameVariables.asm" 21 | 22 | ;=============================================================================== 23 | ; RAM Segment 24 | .segment "RAM" 25 | ; Defines a RAM segment to avoid assembler warnings in chapters not using FamiStudio. 26 | 27 | ;=============================================================================== 28 | .segment "CODE" 29 | ; Include library code and definitions 30 | .include "../../library/libDefines.asm" ; This file includes constants like color codes 31 | .include "../../library/libInput.asm" ; This file includes functions to handle player input 32 | .include "../../library/libMath.asm" ; This file includes math-related functions 33 | .include "../../library/libScreen.asm" ; This file includes functions to manage the screen 34 | .include "../../library/libSprite.asm" ; This file includes functions to manage sprites 35 | 36 | ; Include game code 37 | .include "gameTanks.asm" 38 | .include "gameFlow.asm" 39 | 40 | ;=============================================================================== 41 | ; Game Initialization 42 | gameMainInit: 43 | LIBSCREEN_INIT ; Initialize the screen and PPU 44 | LIBSPRITE_INIT ; Initialize sprites 45 | LIBSCREEN_SETSPRITESIZE8x16 ; Set sprites to 8x16 pixel mode for larger character sizes 46 | LIBSCREEN_LEFT8PIXELSSPRITESDISABLE ; Disable sprite rendering in the leftmost 8 pixels of the screen 47 | jsr gameFlowInit ; Initialize game flow for the game 48 | ; note no rts here so that code flows straight into gameMainUpdate 49 | 50 | ;=============================================================================== 51 | ; gameMain Game Update Loop 52 | gameMainUpdate: 53 | lda bFrameReady ; Load the bFrameReady flag 54 | beq gameMainUpdate ; If it's 0, keep waiting (loop) 55 | 56 | ; game code 57 | LIBINPUT_UPDATE ; update input 58 | jsr gameFlowUpdate ; Update the game flow subroutines 59 | LIBSCREEN_WAITSPRITE0 ; wait for sprite 0 hit 60 | jsr gameMainUpdateBottomScroll 61 | 62 | lda #0 ; Reset bFrameReady to 0 63 | sta bFrameReady 64 | jmp gameMainUpdate ; Infinite loop 65 | 66 | ;=============================================================================== 67 | ; NMI (Vertical Blank) Interrupt Handler 68 | gameMainNMI: 69 | LIBSPRITE_UPDATE ; update sprites oam 70 | jsr gameMainUpdateTopScroll 71 | 72 | lda #1 ; Set bFrameReady flag to 1 73 | sta bFrameReady 74 | rti ; Return from interrupt (used to update graphics once per frame) 75 | 76 | ;=============================================================================== 77 | 78 | gameMainUpdateBottomScroll: 79 | ; Set the scroll for the bottom of the screen 80 | LIBSCREEN_SETSCROLL_AA wBottomScrollX+1, wBottomScrollY+1 81 | rts 82 | 83 | ;=============================================================================== 84 | 85 | gameMainUpdateTopScroll: 86 | ; Set the scroll for the top of the screen 87 | LIBSCREEN_SETSCROLL_AA wTopScrollX+1, wTopScrollY+1 88 | rts 89 | 90 | ;=============================================================================== 91 | ; Data 92 | .include "gameData.asm" 93 | 94 | ;=============================================================================== 95 | ; Character Segment (CHR-ROM) 96 | .segment "CHARS" 97 | ; ROM chars start at PPU address $0000 98 | .incbin "../../content/tankracebackground.chr" 99 | .incbin "../../content/tankracesprites.chr" 100 | 101 | ;=============================================================================== 102 | ; Interrupt Vectors 103 | .segment "VECTORS" ; This section tells the NES where to jump on specific events. 104 | .word gameMainNMI ; NMI (Non-Maskable Interrupt) - triggers once per frame, jump to vblank 105 | .word gameMainInit ; Reset - when the system starts, jump to gameMainInit 106 | .word 0 ; IRQ - Not used in this project -------------------------------------------------------------------------------- /chapters/chapter11/2/gameFlow.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition - gameFlow 3 | ;=============================================================================== 4 | ; Constants 5 | 6 | FlowStateStart = 0 7 | FlowStateGame = 1 8 | FlowStatePause = 2 9 | FlowPressA_X = 100 10 | FlowPressA_Y = 120 11 | 12 | ;=============================================================================== 13 | ; Jump Tables 14 | 15 | gameFlowJumpTable: 16 | .word gameFlowUpdateStart 17 | .word gameFlowUpdateGame 18 | .word gameFlowUpdatePause 19 | 20 | ;=============================================================================== 21 | ; Subroutines 22 | 23 | gameFlowInit: 24 | jsr gameFlowLoadGame 25 | jsr gameDataSpritesInit 26 | 27 | lda #255 28 | sta bGoCountDown ; set the countdown to max 255 29 | rts 30 | 31 | ;=============================================================================== 32 | 33 | gameFlowUpdate: 34 | lda bFlowState ; Get the current state into A 35 | asl ; Multiply by 2 36 | tay ; Copy A to Y 37 | lda gameFlowJumpTable,y ; Lookup low byte 38 | sta bLibTemp1 ; Store in a temporary variable 39 | lda gameFlowJumpTable+1,y ; Lookup high byte 40 | sta bLibTemp2 ; Store in temporary variable+1 41 | jmp (bLibTemp1) ; Indirect jump to subroutine 42 | 43 | ;=============================================================================== 44 | 45 | gameFlowUpdateStart: 46 | 47 | lda bGoCountDown 48 | 49 | cmp #250 50 | bne :+ 51 | LIBSPRITE_SETFRAME8x16_VVV 60, $6A, 1 ; $6A = 5 52 | : 53 | cmp #200 54 | bne :+ 55 | LIBSPRITE_SETFRAME8x16_VVV 60, $68, 1 ; $68 = 4 56 | : 57 | cmp #150 58 | bne :+ 59 | LIBSPRITE_SETFRAME8x16_VVV 60, $66, 1 ; $66 = 3 60 | : 61 | cmp #100 62 | bne :+ 63 | LIBSPRITE_SETFRAME8x16_VVV 60, $64, 1 ; $64 = 2 64 | : 65 | cmp #50 66 | bne :+ 67 | LIBSPRITE_SETFRAME8x16_VVV 60, $62, 1 ; $62 = 1 68 | : 69 | cmp #0 70 | bne :+ 71 | 72 | ; move all sprite y's for level # to $FF 73 | LIBSPRITE_SETPOSITIONY_VV 54, $FF 74 | LIBSPRITE_SETPOSITIONY_VV 55, $FF 75 | LIBSPRITE_SETPOSITIONY_VV 56, $FF 76 | LIBSPRITE_SETPOSITIONY_VV 57, $FF 77 | LIBSPRITE_SETPOSITIONY_VV 58, $FF 78 | LIBSPRITE_SETPOSITIONY_VV 59, $FF 79 | LIBSPRITE_SETPOSITIONY_VV 60, $FF 80 | 81 | ; move to game state 82 | lda #FlowStateGame 83 | sta bFlowState 84 | 85 | lda #0 86 | sta wTopScrollX 87 | sta wTopScrollX+1 88 | sta wTopScrollX+2 89 | sta wBottomScrollX 90 | sta wBottomScrollX+1 91 | sta wBottomScrollX+2 92 | : 93 | ; update start state here 94 | dec bGoCountDown 95 | 96 | rts 97 | 98 | ;=============================================================================== 99 | 100 | gameFlowLoadGame: 101 | LIBSCREEN_DISABLEPPU 102 | 103 | ; Load background & sprite palettes 104 | LIBSCREEN_LOADPALETTE_AA BGPALETTE, PaletteBG 105 | LIBSCREEN_LOADPALETTE_AA SPPALETTE, PaletteSP 106 | 107 | ; Load screen data to the left nametable 108 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE0, DesertLeft 109 | 110 | ; Load screen data to the right nametable 111 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE1, DesertRight 112 | 113 | LIBSCREEN_ENABLEPPU 114 | rts 115 | 116 | ;=============================================================================== 117 | 118 | gameFlowUpdateGame: 119 | LIBINPUT_GETBLIP_V GameportSelectMask 120 | beq :+ 121 | 122 | ; move to pause state 123 | lda #FlowStatePause 124 | sta bFlowState 125 | rts 126 | : 127 | ; update game state here 128 | jsr gameTanksUpdate 129 | rts 130 | 131 | ;=============================================================================== 132 | 133 | gameFlowUpdatePause: 134 | LIBINPUT_GETBLIP_V GameportSelectMask 135 | beq :+ 136 | 137 | ; move to game state 138 | lda #FlowStateGame 139 | sta bFlowState 140 | rts 141 | : 142 | rts 143 | -------------------------------------------------------------------------------- /chapters/chapter11/2/gameVariables.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition - gameVariables 3 | ;=============================================================================== 4 | ; Variables 5 | 6 | bLeftPressRequired: .byte 0 7 | bPL1Acceleration: .byte 0 8 | wPL1Velocity: .word 0 9 | wCPUAcceleration: .word 0 10 | wCPUVelocity: .word 0 11 | wCPUTankXPos: .word 0 ; 1st byte = subpixels, 2nd byte = pixels 12 | .byte 0 ; 3rd byte = nametable 13 | wCPUTankScreenXPos: .word 0 ; 1st byte = subpixels, 2nd byte = pixels 14 | .byte 0 ; 3rd byte = nametable 15 | bFlowState: .byte 0 16 | bGoCountDown: .byte 0 -------------------------------------------------------------------------------- /chapters/chapter11/3/chapter11_3.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition chapter11_3 3 | ;=============================================================================== 4 | ; NES ROM Header 5 | .segment "HEADER" 6 | .byte "NES" ; 'NES' identifier - tells the system it's a valid NES ROM 7 | .byte $1a ; Control byte for NES compatibility 8 | .byte 2 ; Number of 16KB PRG-ROM banks (2 x 16KB = 32KB of code) 9 | .byte 1 ; Number of 8KB CHR-ROM banks (1 x 8KB = 8KB of graphics) 10 | .byte 1 | (0 << 4) ; Vertical mirroring 11 | .byte 0 & $f0 ; Using mapper 0 (no extra memory mappers) 12 | .byte 0,0,0,0,0,0,0,0 ; Padding bytes to complete the header 13 | 14 | ;=============================================================================== 15 | ; Zero Page Segment 16 | .segment "ZEROPAGE" 17 | ; The zero page is a special area of memory that's faster for the CPU to access. 18 | ; Variables 19 | .include "../../library/libVariables.asm" 20 | .include "gameVariables.asm" 21 | 22 | ;=============================================================================== 23 | ; RAM Segment 24 | .segment "RAM" 25 | ; Defines a RAM segment to avoid assembler warnings in chapters not using FamiStudio. 26 | 27 | ;=============================================================================== 28 | .segment "CODE" 29 | ; Include library code and definitions 30 | .include "../../library/libDefines.asm" ; This file includes constants like color codes 31 | .include "../../library/libInput.asm" ; This file includes functions to handle player input 32 | .include "../../library/libMath.asm" ; This file includes math-related functions 33 | .include "../../library/libScreen.asm" ; This file includes functions to manage the screen 34 | .include "../../library/libSprite.asm" ; This file includes functions to manage sprites 35 | 36 | ; Include game code 37 | .include "gameTanks.asm" 38 | .include "gameFlow.asm" 39 | 40 | ;=============================================================================== 41 | ; Game Initialization 42 | gameMainInit: 43 | LIBSCREEN_INIT ; Initialize the screen and PPU 44 | LIBSPRITE_INIT ; Initialize sprites 45 | LIBSCREEN_SETSPRITESIZE8x16 ; Set sprites to 8x16 pixel mode for larger character sizes 46 | LIBSCREEN_LEFT8PIXELSSPRITESDISABLE ; Disable sprite rendering in the leftmost 8 pixels of the screen 47 | jsr gameFlowInit ; Initialize game flow for the game 48 | ; note no rts here so that code flows straight into gameMainUpdate 49 | 50 | ;=============================================================================== 51 | ; gameMain Game Update Loop 52 | gameMainUpdate: 53 | lda bFrameReady ; Load the bFrameReady flag 54 | beq gameMainUpdate ; If it's 0, keep waiting (loop) 55 | 56 | ; game code 57 | LIBINPUT_UPDATE ; update input 58 | jsr gameFlowUpdate ; Update the game flow subroutines 59 | LIBSCREEN_WAITSPRITE0 ; wait for sprite 0 hit 60 | jsr gameMainUpdateBottomScroll 61 | 62 | lda #0 ; Reset bFrameReady to 0 63 | sta bFrameReady 64 | jmp gameMainUpdate ; Infinite loop 65 | 66 | ;=============================================================================== 67 | ; NMI (Vertical Blank) Interrupt Handler 68 | gameMainNMI: 69 | LIBSPRITE_UPDATE ; update sprites oam 70 | jsr gameMainUpdateTopScroll 71 | 72 | lda #1 ; Set bFrameReady flag to 1 73 | sta bFrameReady 74 | rti ; Return from interrupt (used to update graphics once per frame) 75 | 76 | ;=============================================================================== 77 | 78 | gameMainUpdateBottomScroll: 79 | ; Set the scroll for the bottom of the screen 80 | LIBSCREEN_SETSCROLL_AA wBottomScrollX+1, wBottomScrollY+1 81 | rts 82 | 83 | ;=============================================================================== 84 | 85 | gameMainUpdateTopScroll: 86 | ; Set the scroll for the top of the screen 87 | LIBSCREEN_SETSCROLL_AA wTopScrollX+1, wTopScrollY+1 88 | rts 89 | 90 | ;=============================================================================== 91 | ; Data 92 | .include "gameData.asm" 93 | 94 | ;=============================================================================== 95 | ; Character Segment (CHR-ROM) 96 | .segment "CHARS" 97 | ; ROM chars start at PPU address $0000 98 | .incbin "../../content/tankracebackground.chr" 99 | .incbin "../../content/tankracesprites.chr" 100 | 101 | ;=============================================================================== 102 | ; Interrupt Vectors 103 | .segment "VECTORS" ; This section tells the NES where to jump on specific events. 104 | .word gameMainNMI ; NMI (Non-Maskable Interrupt) - triggers once per frame, jump to vblank 105 | .word gameMainInit ; Reset - when the system starts, jump to gameMainInit 106 | .word 0 ; IRQ - Not used in this project -------------------------------------------------------------------------------- /chapters/chapter11/3/gameVariables.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition - gameVariables 3 | ;=============================================================================== 4 | ; Variables 5 | 6 | bLeftPressRequired: .byte 0 7 | bPL1Acceleration: .byte 0 8 | wPL1Velocity: .word 0 9 | wCPUAcceleration: .word 0 10 | wCPUVelocity: .word 0 11 | wCPUTankXPos: .word 0 ; 1st byte = subpixels, 2nd byte = pixels 12 | .byte 0 ; 3rd byte = nametable 13 | wCPUTankScreenXPos: .word 0 ; 1st byte = subpixels, 2nd byte = pixels 14 | .byte 0 ; 3rd byte = nametable 15 | bFlowState: .byte 0 16 | bGoCountDown: .byte 0 -------------------------------------------------------------------------------- /chapters/chapter12/1/chapter12_1.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition chapter12_1 3 | ;=============================================================================== 4 | ; NES ROM Header 5 | .segment "HEADER" 6 | .byte "NES" ; 'NES' identifier - tells the system it's a valid NES ROM 7 | .byte $1a ; Control byte for NES compatibility 8 | .byte 2 ; Number of 16KB PRG-ROM banks (2 x 16KB = 32KB of code) 9 | .byte 1 ; Number of 8KB CHR-ROM banks (1 x 8KB = 8KB of graphics) 10 | .byte 1 | (0 << 4) ; Vertical mirroring 11 | .byte 0 & $f0 ; Using mapper 0 (no extra memory mappers) 12 | .byte 0,0,0,0,0,0,0,0 ; Padding bytes to complete the header 13 | 14 | ;=============================================================================== 15 | ; Zero Page Segment 16 | .segment "ZEROPAGE" 17 | ; The zero page is a special area of memory that's faster for the CPU to access. 18 | ; Variables 19 | .include "../../library/libVariables.asm" 20 | .include "gameVariables.asm" 21 | 22 | ;=============================================================================== 23 | .segment "CODE" 24 | ; Include library code and definitions 25 | .include "../../library/libDefines.asm" ; This file includes constants like color codes 26 | .include "../../library/libInput.asm" ; This file includes functions to handle player input 27 | .include "../../library/libMath.asm" ; This file includes math-related functions 28 | .include "../../library/libScreen.asm" ; This file includes functions to manage the screen 29 | .include "../../library/libSprite.asm" ; This file includes functions to manage sprites 30 | .include "../../library/libSound.asm" ; This file includes functions to manage music & sfx 31 | 32 | ; Include game code 33 | .include "gameTanks.asm" 34 | .include "gameFlow.asm" 35 | 36 | ;=============================================================================== 37 | ; Game Initialization 38 | gameMainInit: 39 | LIBSCREEN_INIT ; Initialize the screen and PPU 40 | LIBSPRITE_INIT ; Initialize sprites 41 | LIBSCREEN_SETSPRITESIZE8x16 ; Set sprites to 8x16 pixel mode for larger character sizes 42 | LIBSCREEN_LEFT8PIXELSSPRITESDISABLE ; Disable sprite rendering in the leftmost 8 pixels of the screen 43 | LIBSOUND_INIT music_data_music, sounds ; Initialize the music and sound effects for the game 44 | jsr gameFlowInit ; Initialize game flow for the game 45 | ; note no rts here so that code flows straight into gameMainUpdate 46 | 47 | ;=============================================================================== 48 | ; gameMain Game Update Loop 49 | gameMainUpdate: 50 | lda bFrameReady ; Load the bFrameReady flag 51 | beq gameMainUpdate ; If it's 0, keep waiting (loop) 52 | 53 | ; game code 54 | LIBINPUT_UPDATE ; update input 55 | jsr gameFlowUpdate ; Update the game flow subroutines 56 | LIBSCREEN_WAITSPRITE0 ; wait for sprite 0 hit 57 | jsr gameMainUpdateBottomScroll 58 | 59 | lda #0 ; Reset bFrameReady to 0 60 | sta bFrameReady 61 | jmp gameMainUpdate ; Infinite loop 62 | 63 | ;=============================================================================== 64 | ; NMI (Vertical Blank) Interrupt Handler 65 | gameMainNMI: 66 | LIBSPRITE_UPDATE ; update sprites oam 67 | 68 | jsr gameMainUpdateTopScroll 69 | 70 | jsr famistudio_update ; update the sound engine 71 | 72 | lda #1 ; Set bFrameReady flag to 1 73 | sta bFrameReady 74 | rti ; Return from interrupt (used to update graphics once per frame) 75 | 76 | ;=============================================================================== 77 | 78 | gameMainUpdateBottomScroll: 79 | ; Set the scroll for the bottom of the screen 80 | LIBSCREEN_SETSCROLL_AA wBottomScrollX+1, wBottomScrollY+1 81 | rts 82 | 83 | ;=============================================================================== 84 | 85 | gameMainUpdateTopScroll: 86 | ; Set the scroll for the top of the screen 87 | LIBSCREEN_SETSCROLL_AA wTopScrollX+1, wTopScrollY+1 88 | rts 89 | 90 | ;=============================================================================== 91 | ; Data 92 | .include "gameData.asm" 93 | 94 | ;=============================================================================== 95 | ; Character Segment (CHR-ROM) 96 | .segment "CHARS" 97 | ; ROM chars start at PPU address $0000 98 | .incbin "../../content/tankracebackground.chr" 99 | .incbin "../../content/tankracesprites.chr" 100 | 101 | ;=============================================================================== 102 | ; Interrupt Vectors 103 | .segment "VECTORS" ; This section tells the NES where to jump on specific events. 104 | .word gameMainNMI ; NMI (Non-Maskable Interrupt) - triggers once per frame, jump to vblank 105 | .word gameMainInit ; Reset - when the system starts, jump to gameMainInit 106 | .word 0 ; IRQ - Not used in this project -------------------------------------------------------------------------------- /chapters/chapter12/1/gameVariables.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition - gameVariables 3 | ;=============================================================================== 4 | ; Variables 5 | 6 | bLeftPressRequired: .byte 0 7 | bPL1Acceleration: .byte 0 8 | wPL1Velocity: .word 0 9 | wCPUAcceleration: .word 0 10 | wCPUVelocity: .word 0 11 | wCPUTankXPos: .word 0 ; 1st byte = subpixels, 2nd byte = pixels 12 | .byte 0 ; 3rd byte = nametable 13 | wCPUTankScreenXPos: .word 0 ; 1st byte = subpixels, 2nd byte = pixels 14 | .byte 0 ; 3rd byte = nametable 15 | bFlowState: .byte 0 16 | bGoCountDown: .byte 0 17 | bTankSFX: .byte 0 -------------------------------------------------------------------------------- /chapters/chapter13/1/chapter13_1.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition chapter13_1 3 | ;=============================================================================== 4 | ; NES ROM Header 5 | .segment "HEADER" 6 | .byte "NES" ; 'NES' identifier - tells the system it's a valid NES ROM 7 | .byte $1a ; Control byte for NES compatibility 8 | .byte 2 ; Number of 16KB PRG-ROM banks (2 x 16KB = 32KB of code) 9 | .byte 1 ; Number of 8KB CHR-ROM banks (1 x 8KB = 8KB of graphics) 10 | .byte 1 | (0 << 4) ; Vertical mirroring 11 | .byte 0 & $f0 ; Using mapper 0 (no extra memory mappers) 12 | .byte 0,0,0,0,0,0,0,0 ; Padding bytes to complete the header 13 | 14 | ;=============================================================================== 15 | ; Zero Page Segment 16 | .segment "ZEROPAGE" 17 | ; The zero page is a special area of memory that's faster for the CPU to access. 18 | ; Variables 19 | .include "../../library/libVariables.asm" 20 | .include "gameVariables.asm" 21 | 22 | ;=============================================================================== 23 | .segment "CODE" 24 | ; Include library code and definitions 25 | .include "../../library/libDefines.asm" ; This file includes constants like color codes 26 | .include "../../library/libInput.asm" ; This file includes functions to handle player input 27 | .include "../../library/libMath.asm" ; This file includes math-related functions 28 | .include "../../library/libScreen.asm" ; This file includes functions to manage the screen 29 | .include "../../library/libSprite.asm" ; This file includes functions to manage sprites 30 | .include "../../library/libSound.asm" ; This file includes functions to manage music & sfx 31 | 32 | ; Include game code 33 | .include "gameTanks.asm" 34 | .include "gameFlow.asm" 35 | 36 | ;=============================================================================== 37 | ; Game Initialization 38 | gameMainInit: 39 | LIBSCREEN_INIT ; Initialize the screen and PPU 40 | LIBSPRITE_INIT ; Initialize sprites 41 | LIBSCREEN_SETSPRITESIZE8x16 ; Set sprites to 8x16 pixel mode for larger character sizes 42 | LIBSCREEN_LEFT8PIXELSSPRITESDISABLE ; Disable sprite rendering in the leftmost 8 pixels of the screen 43 | LIBSOUND_INIT music_data_music, sounds ; Initialize the music and sound effects for the game 44 | jsr gameFlowInit ; Initialize game flow for the game 45 | ; note no rts here so that code flows straight into gameMainUpdate 46 | 47 | ;=============================================================================== 48 | ; gameMain Game Update Loop 49 | gameMainUpdate: 50 | lda bFrameReady ; Load the bFrameReady flag 51 | beq gameMainUpdate ; If it's 0, keep waiting (loop) 52 | 53 | ; game code 54 | LIBINPUT_UPDATE ; update input 55 | jsr gameFlowUpdate ; Update the game flow subroutines 56 | LIBSCREEN_WAITSPRITE0 ; wait for sprite 0 hit 57 | jsr gameMainUpdateBottomScroll 58 | 59 | lda #0 ; Reset bFrameReady to 0 60 | sta bFrameReady 61 | jmp gameMainUpdate ; Infinite loop 62 | 63 | ;=============================================================================== 64 | ; NMI (Vertical Blank) Interrupt Handler 65 | gameMainNMI: 66 | LIBSPRITE_UPDATE ; update sprites oam 67 | 68 | jsr gameMainUpdateTopScroll 69 | 70 | jsr famistudio_update ; update the sound engine 71 | 72 | lda #1 ; Set bFrameReady flag to 1 73 | sta bFrameReady 74 | rti ; Return from interrupt (used to update graphics once per frame) 75 | 76 | ;=============================================================================== 77 | 78 | gameMainUpdateBottomScroll: 79 | ; Set the scroll for the bottom of the screen 80 | LIBSCREEN_SETSCROLL_AA wBottomScrollX+1, wBottomScrollY+1 81 | rts 82 | 83 | ;=============================================================================== 84 | 85 | gameMainUpdateTopScroll: 86 | ; Set the scroll for the top of the screen 87 | LIBSCREEN_SETSCROLL_AA wTopScrollX+1, wTopScrollY+1 88 | rts 89 | 90 | ;=============================================================================== 91 | ; Data 92 | .include "gameData.asm" 93 | 94 | ;=============================================================================== 95 | ; Character Segment (CHR-ROM) 96 | .segment "CHARS" 97 | ; ROM chars start at PPU address $0000 98 | .incbin "../../content/tankracebackground.chr" 99 | .incbin "../../content/tankracesprites.chr" 100 | 101 | ;=============================================================================== 102 | ; Interrupt Vectors 103 | .segment "VECTORS" ; This section tells the NES where to jump on specific events. 104 | .word gameMainNMI ; NMI (Non-Maskable Interrupt) - triggers once per frame, jump to vblank 105 | .word gameMainInit ; Reset - when the system starts, jump to gameMainInit 106 | .word 0 ; IRQ - Not used in this project -------------------------------------------------------------------------------- /chapters/chapter13/1/gameVariables.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition - gameVariables 3 | ;=============================================================================== 4 | ; Variables 5 | 6 | bLeftPressRequired: .byte 0 7 | bPL1Acceleration: .byte 0 8 | wPL1Velocity: .word 0 9 | wCPUAcceleration: .word 0 10 | wCPUVelocity: .word 0 11 | wCPUTankXPos: .word 0 ; 1st byte = subpixels, 2nd byte = pixels 12 | .byte 0 ; 3rd byte = nametable 13 | wCPUTankScreenXPos: .word 0 ; 1st byte = subpixels, 2nd byte = pixels 14 | .byte 0 ; 3rd byte = nametable 15 | bFlowState: .byte 0 16 | bGoCountDown: .byte 0 17 | bTankSFX: .byte 0 -------------------------------------------------------------------------------- /chapters/chapter2/1/chapter2_1.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition chapter2_1 3 | ;=============================================================================== 4 | ; NES ROM Header 5 | .segment "HEADER" 6 | .byte "NES" ; 'NES' identifier - tells the system it's a valid NES ROM 7 | .byte $1a ; Control byte for NES compatibility 8 | .byte 2 ; Number of 16KB PRG-ROM banks (2 x 16KB = 32KB of code) 9 | .byte 1 ; Number of 8KB CHR-ROM banks (1 x 8KB = 8KB of graphics) 10 | .byte 1 | (0 << 4) ; Vertical mirroring 11 | .byte 0 & $f0 ; Using mapper 0 (no extra memory mappers) 12 | .byte 0,0,0,0,0,0,0,0 ; Padding bytes to complete the header 13 | 14 | ;=============================================================================== 15 | ; Zero Page Segment 16 | .segment "ZEROPAGE" 17 | result: .byte 0 ; Temporary result storage 18 | 19 | ;=============================================================================== 20 | ; RAM Segment 21 | .segment "RAM" 22 | ; Defines a RAM segment to avoid assembler warnings in chapters not using FamiStudio. 23 | 24 | ;=============================================================================== 25 | ; Code Segment 26 | .segment "CODE" 27 | 28 | ; Macro definition 29 | .macro IncrementMacro address 30 | lda address ; Load value from address 31 | clc ; Clear carry before addition 32 | adc #$01 ; Add 1 to the value 33 | sta address ; Store result back in address 34 | .endmacro 35 | 36 | gameMainInit: 37 | sei ; Disable interrupts for stable environment 38 | cld ; Clear decimal mode 39 | 40 | ; Load, Store, Arithmetic, and Stack 41 | lda #$05 ; Load 5 into A 42 | sta result ; Store A into result (Zero Page) 43 | pha ; Push A onto stack 44 | lda #$10 ; Load 16 into A 45 | adc result ; Add result (5) to A 46 | pla ; Pull previous value (5) from stack into A 47 | 48 | ; Branching and Loop 49 | ldx #$03 ; Loop 3 times 50 | loopStart: 51 | jsr IncrementSubroutine ; Call subroutine 52 | dex ; Decrement X 53 | bne loopStart ; Repeat until X = 0 54 | 55 | ; Addressing Modes and Macros 56 | lda #$12 ; Immediate mode: Load 18 57 | sta $0200 ; Absolute mode: Store at $0200 58 | ldx #$02 ; Load 2 into X 59 | lda $0200, X ; Indexed mode: Load from $0202 60 | IncrementMacro result ; Use macro to increment result 61 | 62 | infiniteLoop: 63 | jmp infiniteLoop ; Loop indefinitely to allow inspection 64 | 65 | IncrementSubroutine: 66 | lda result ; Load current result 67 | clc ; Clear carry 68 | adc #$02 ; Add 2 to result 69 | sta result ; Store updated result 70 | rts ; Return from subroutine 71 | 72 | ;=============================================================================== 73 | 74 | .segment "CHARS" 75 | .segment "VECTORS" 76 | .word 0 77 | .word gameMainInit 78 | .word 0 -------------------------------------------------------------------------------- /chapters/chapter6/1/chapter6_1.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition chapter6_1 3 | ;=============================================================================== 4 | ; NES ROM Header 5 | .segment "HEADER" 6 | .byte "NES" ; 'NES' identifier - tells the system it's a valid NES ROM 7 | .byte $1a ; Control byte for NES compatibility 8 | .byte 2 ; Number of 16KB PRG-ROM banks (2 x 16KB = 32KB of code) 9 | .byte 1 ; Number of 8KB CHR-ROM banks (1 x 8KB = 8KB of graphics) 10 | .byte 1 | (0 << 4) ; Vertical mirroring 11 | .byte 0 & $f0 ; Using mapper 0 (no extra memory mappers) 12 | .byte 0,0,0,0,0,0,0,0 ; Padding bytes to complete the header 13 | 14 | ;=============================================================================== 15 | ; Zero Page Segment 16 | .segment "ZEROPAGE" 17 | ; Zero Page is used for variables that require quick access. 18 | .include "../../library/libVariables.asm" ; Include variable declarations from the library 19 | 20 | ;=============================================================================== 21 | ; RAM Segment 22 | .segment "RAM" 23 | ; Defines a RAM segment to avoid assembler warnings in chapters not using FamiStudio. 24 | 25 | ;=============================================================================== 26 | ; Code Segment 27 | .segment "CODE" 28 | ; Include necessary library code for handling screen functions 29 | .include "../../library/libDefines.asm" ; Includes constants like color codes 30 | .include "../../library/libScreen.asm" ; Includes screen management functions 31 | 32 | ;=============================================================================== 33 | ; Game Initialization 34 | gameMainInit: 35 | LIBSCREEN_INIT ; Initialize the screen and PPU settings 36 | 37 | ; Load background palette 38 | LIBSCREEN_LOADPALETTE_AA BGPALETTE, Palette 39 | 40 | ; Load screen data to the left nametable 41 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE0, Screen 42 | 43 | LIBSCREEN_ENABLEPPU ; Enable PPU rendering (sprites, background) 44 | ; No 'rts' (return) here, so the code will flow directly into the main game loop 45 | 46 | ;=============================================================================== 47 | ; Main Game Update Loop 48 | gameMainUpdate: 49 | lda bFrameReady ; Load the frame-ready flag 50 | beq gameMainUpdate ; If frame not ready (bFrameReady = 0), loop and wait 51 | 52 | ; Game logic would go here (currently empty for this section) 53 | 54 | lda #0 ; Reset the bFrameReady flag back to 0 55 | sta bFrameReady ; This prevents the game logic from running more than once per frame 56 | jmp gameMainUpdate ; Infinite loop - returns to the beginning of the update loop 57 | 58 | ;=============================================================================== 59 | ; NMI (Non-Maskable Interrupt) Handler 60 | gameMainNMI: 61 | lda #1 ; Set bFrameReady flag to 1 to signal the game update loop 62 | sta bFrameReady ; Store the flag in memory 63 | rti ; Return from interrupt - graphics update happens after this 64 | 65 | ;=============================================================================== 66 | ; Data Segment 67 | .include "gameData.asm" ; Include external game data (background map, palettes, etc.) 68 | 69 | ;=============================================================================== 70 | ; Character Segment (CHR-ROM) 71 | .segment "CHARS" 72 | ; ROM chars start at PPU address $0000 73 | ; This section will hold sprite and tile graphics. Currently, it loads a test screen 74 | .incbin "../../content/testbackground.chr" 75 | 76 | ;=============================================================================== 77 | ; Interrupt Vectors 78 | .segment "VECTORS" 79 | ; This segment defines what happens during specific events: 80 | .word gameMainNMI ; NMI (VBlank) - triggered every frame to handle graphics 81 | .word gameMainInit ; Reset - called when the game is first started 82 | .word 0 ; IRQ - not used in this simple project -------------------------------------------------------------------------------- /chapters/chapter6/1/gameData.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition gameData 3 | ;=============================================================================== 4 | ; Palette Data 5 | Palette: 6 | .incbin "../../content/testbackground.pal" 7 | 8 | ;=============================================================================== 9 | ; Screen Data 10 | Screen: 11 | .incbin "../../content/testbackground.nam" -------------------------------------------------------------------------------- /chapters/chapter7/1/chapter7_1.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition chapter7_1 3 | ;=============================================================================== 4 | ; NES ROM Header 5 | .segment "HEADER" 6 | .byte "NES" ; 'NES' identifier - tells the system it's a valid NES ROM 7 | .byte $1a ; Control byte for NES compatibility 8 | .byte 2 ; Number of 16KB PRG-ROM banks (2 x 16KB = 32KB of code) 9 | .byte 1 ; Number of 8KB CHR-ROM banks (1 x 8KB = 8KB of graphics) 10 | .byte 1 | (0 << 4) ; Vertical mirroring 11 | .byte 0 & $f0 ; Using mapper 0 (no extra memory mappers) 12 | .byte 0,0,0,0,0,0,0,0 ; Padding bytes to complete the header 13 | 14 | ;=============================================================================== 15 | ; Zero Page Segment 16 | .segment "ZEROPAGE" 17 | ; Zero Page is used for variables that require quick access. 18 | .include "../../library/libVariables.asm" ; Include variable declarations from the library 19 | 20 | ;=============================================================================== 21 | ; RAM Segment 22 | .segment "RAM" 23 | ; Defines a RAM segment to avoid assembler warnings in chapters not using FamiStudio. 24 | 25 | ;=============================================================================== 26 | ; Code Segment 27 | .segment "CODE" 28 | ; Include necessary library code 29 | .include "../../library/libDefines.asm" ; Includes constants like color codes 30 | .include "../../library/libScreen.asm" ; Includes screen management functions 31 | 32 | ;=============================================================================== 33 | ; Game Initialization 34 | gameMainInit: 35 | LIBSCREEN_INIT ; Initialize the screen and PPU settings 36 | 37 | ; Load background palette 38 | LIBSCREEN_LOADPALETTE_AA BGPALETTE, PaletteBG 39 | 40 | ; Load screen data to the left nametable 41 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE0, DesertLeft 42 | 43 | LIBSCREEN_ENABLEPPU ; Enable PPU rendering (sprites, background) 44 | ; No 'rts' (return) here, so the code will flow directly into the main game loop 45 | 46 | ;=============================================================================== 47 | ; Main Game Update Loop 48 | gameMainUpdate: 49 | lda bFrameReady ; Load the frame-ready flag 50 | beq gameMainUpdate ; If frame not ready (bFrameReady = 0), loop and wait 51 | 52 | ; Game logic would go here (currently empty for this section) 53 | 54 | lda #0 ; Reset the bFrameReady flag back to 0 55 | sta bFrameReady ; This prevents the game logic from running more than once per frame 56 | jmp gameMainUpdate ; Infinite loop - returns to the beginning of the update loop 57 | 58 | ;=============================================================================== 59 | ; NMI (Non-Maskable Interrupt) Handler 60 | gameMainNMI: 61 | lda #1 ; Set bFrameReady flag to 1 to signal the game update loop 62 | sta bFrameReady ; Store the flag in memory 63 | rti ; Return from interrupt - graphics update happens after this 64 | 65 | ;=============================================================================== 66 | ; Data Segment 67 | .include "gameData.asm" ; Include external game data (background map, palettes, etc.) 68 | 69 | ;=============================================================================== 70 | ; Character Segment (CHR-ROM) 71 | .segment "CHARS" 72 | ; ROM chars start at PPU address $0000 73 | .incbin "../../content/tankracebackground.chr" 74 | 75 | ;=============================================================================== 76 | ; Interrupt Vectors 77 | .segment "VECTORS" 78 | ; This segment defines what happens during specific events: 79 | .word gameMainNMI ; NMI (VBlank) - triggered every frame to handle graphics 80 | .word gameMainInit ; Reset - called when the game is first started 81 | .word 0 ; IRQ - not used in this simple project -------------------------------------------------------------------------------- /chapters/chapter7/1/gameData.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition gameData 3 | ;=============================================================================== 4 | ; Palette Data 5 | PaletteBG: 6 | .incbin "../../content/tankracebackground.pal" 7 | 8 | ;=============================================================================== 9 | ; Screen Data 10 | DesertLeft: 11 | .incbin "../../content/tankracedesertleft.nam" 12 | -------------------------------------------------------------------------------- /chapters/chapter7/2/chapter7_2.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition chapter7_2 3 | ;=============================================================================== 4 | ; NES ROM Header 5 | .segment "HEADER" 6 | .byte "NES" ; 'NES' identifier - tells the system it's a valid NES ROM 7 | .byte $1a ; Control byte for NES compatibility 8 | .byte 2 ; Number of 16KB PRG-ROM banks (2 x 16KB = 32KB of code) 9 | .byte 1 ; Number of 8KB CHR-ROM banks (1 x 8KB = 8KB of graphics) 10 | .byte 1 | (0 << 4) ; Vertical mirroring 11 | .byte 0 & $f0 ; Using mapper 0 (no extra memory mappers) 12 | .byte 0,0,0,0,0,0,0,0 ; Padding bytes to complete the header 13 | 14 | ;=============================================================================== 15 | ; Zero Page Segment 16 | .segment "ZEROPAGE" 17 | ; Variables stored in zero page for faster access 18 | .include "../../library/libVariables.asm" 19 | 20 | ;=============================================================================== 21 | ; RAM Segment 22 | .segment "RAM" 23 | ; Defines a RAM segment to avoid assembler warnings in chapters not using FamiStudio. 24 | 25 | ;=============================================================================== 26 | ; Code Segment 27 | .segment "CODE" 28 | ; Include necessary library code for handling screen, math, and sprites 29 | .include "../../library/libDefines.asm" ; Includes constants like color codes 30 | .include "../../library/libMath.asm" ; Includes math-related functions 31 | .include "../../library/libScreen.asm" ; Includes screen management functions 32 | 33 | ;=============================================================================== 34 | ; Game Initialization 35 | gameMainInit: 36 | LIBSCREEN_INIT ; Initialize the screen and PPU settings 37 | 38 | ; Load background palette 39 | LIBSCREEN_LOADPALETTE_AA BGPALETTE, PaletteBG 40 | 41 | ; Load screen data to the left nametable 42 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE0, DesertLeft 43 | 44 | ; Load screen data to the right nametable 45 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE1, DesertRight 46 | 47 | LIBSCREEN_ENABLEPPU ; Enable PPU rendering (sprites, background) 48 | ; No 'rts' (return) here, so the code will flow directly into the main game loop 49 | 50 | ;=============================================================================== 51 | ; Main Game Update Loop 52 | gameMainUpdate: 53 | lda bFrameReady ; Load the frame-ready flag 54 | beq gameMainUpdate ; If frame not ready (bFrameReady = 0), loop and wait 55 | 56 | ; Game logic goes here (currently empty for this section) 57 | 58 | lda #0 ; Reset the bFrameReady flag back to 0 59 | sta bFrameReady ; This prevents the game logic from running more than once per frame 60 | jmp gameMainUpdate ; Infinite loop - returns to the beginning of the update loop 61 | 62 | ;=============================================================================== 63 | ; NMI (Non-Maskable Interrupt) Handler 64 | gameMainNMI: 65 | jsr gameMainUpdateBottomScroll ; Update bottom scroll 66 | 67 | lda #1 ; Set bFrameReady flag to 1 to signal the game update loop 68 | sta bFrameReady ; Store the flag in memory 69 | rti ; Return from interrupt - graphics update happens after this 70 | 71 | ;=============================================================================== 72 | ; Update Bottom Scroll Subroutine 73 | gameMainUpdateBottomScroll: 74 | ; Add 100 subpixels to the horizontal scroll position every frame 75 | LIBMATH_ADD8TO24_AVA wBottomScrollX, 100, wBottomScrollX 76 | 77 | ; Set the bottom scroll for the screen (based on the current scroll values) 78 | LIBSCREEN_SETSCROLL_AA wBottomScrollX+1, wBottomScrollY+1 79 | rts ; Return from subroutine 80 | 81 | ;=============================================================================== 82 | ; Data Segment 83 | .include "gameData.asm" ; Include external game data (background map, palettes, etc.) 84 | 85 | ;=============================================================================== 86 | ; Character Segment (CHR-ROM) 87 | .segment "CHARS" 88 | ; ROM chars start at PPU address $0000 89 | .incbin "../../content/tankracebackground.chr" 90 | 91 | ;=============================================================================== 92 | ; Interrupt Vectors 93 | .segment "VECTORS" 94 | ; This segment defines what happens during specific events: 95 | .word gameMainNMI ; NMI (VBlank) - triggered every frame to handle graphics 96 | .word gameMainInit ; Reset - called when the game is first started 97 | .word 0 ; IRQ - not used in this simple project 98 | -------------------------------------------------------------------------------- /chapters/chapter7/2/gameData.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition gameData 3 | ;=============================================================================== 4 | ; Palette Data 5 | PaletteBG: 6 | .incbin "../../content/tankracebackground.pal" 7 | 8 | ;=============================================================================== 9 | ; Screen Data 10 | DesertLeft: 11 | .incbin "../../content/tankracedesertleft.nam" 12 | 13 | DesertRight: 14 | .incbin "../../content/tankracedesertright.nam" -------------------------------------------------------------------------------- /chapters/chapter7/3/chapter7_3.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition chapter7_3 3 | ;=============================================================================== 4 | ; NES ROM Header 5 | .segment "HEADER" 6 | .byte "NES" ; 'NES' identifier - tells the system it's a valid NES ROM 7 | .byte $1a ; Control byte for NES compatibility 8 | .byte 2 ; Number of 16KB PRG-ROM banks (2 x 16KB = 32KB of code) 9 | .byte 1 ; Number of 8KB CHR-ROM banks (1 x 8KB = 8KB of graphics) 10 | .byte 1 | (0 << 4) ; Vertical mirroring 11 | .byte 0 & $f0 ; Using mapper 0 (no extra memory mappers) 12 | .byte 0,0,0,0,0,0,0,0 ; Padding bytes to complete the header 13 | 14 | ;=============================================================================== 15 | ; Zero Page Segment 16 | .segment "ZEROPAGE" 17 | ; Variables stored in zero page for faster access 18 | .include "../../library/libVariables.asm" 19 | 20 | ;=============================================================================== 21 | ; RAM Segment 22 | .segment "RAM" 23 | ; Defines a RAM segment to avoid assembler warnings in chapters not using FamiStudio. 24 | 25 | ;=============================================================================== 26 | ; Code Segment 27 | .segment "CODE" 28 | ; Include necessary library code for handling screen, math, and sprites 29 | .include "../../library/libDefines.asm" ; Includes constants like color codes 30 | .include "../../library/libMath.asm" ; Includes math-related functions 31 | .include "../../library/libScreen.asm" ; Includes screen management functions 32 | .include "../../library/libSprite.asm" ; Includes sprite management functions 33 | 34 | ;=============================================================================== 35 | ; Game Initialization 36 | gameMainInit: 37 | LIBSCREEN_INIT ; Initialize the screen and PPU settings 38 | 39 | ; Load background & sprite palettes 40 | LIBSCREEN_LOADPALETTE_AA BGPALETTE, PaletteBG 41 | LIBSCREEN_LOADPALETTE_AA SPPALETTE, PaletteSP 42 | 43 | ; Load screen data to the left nametable 44 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE0, DesertLeft 45 | 46 | ; Load screen data to the right nametable 47 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE1, DesertRight 48 | 49 | LIBSPRITE_INIT ; Initialize sprites 50 | LIBSCREEN_SETSPRITESIZE8x16 ; Set sprites to 8x16 pixel mode for larger character sizes 51 | jsr gameDataSpritesInit ; Initialize sprite data for the game 52 | 53 | LIBSCREEN_ENABLEPPU ; Enable PPU rendering (sprites, background) 54 | ; No 'rts' (return) here, so the code will flow directly into the main game loop 55 | 56 | ;=============================================================================== 57 | ; Main Game Update Loop 58 | gameMainUpdate: 59 | lda bFrameReady ; Load the frame-ready flag 60 | beq gameMainUpdate ; If frame not ready (bFrameReady = 0), loop and wait 61 | 62 | ; Game logic goes here (currently waiting for sprite 0 hit) 63 | LIBSCREEN_WAITSPRITE0 ; Wait for sprite 0 hit (sync game events) 64 | jsr gameMainUpdateBottomScroll ; Update bottom scroll values 65 | 66 | lda #0 ; Reset the bFrameReady flag back to 0 67 | sta bFrameReady ; Prevents game logic from running more than once per frame 68 | jmp gameMainUpdate ; Infinite loop - returns to the beginning of the update loop 69 | 70 | ;=============================================================================== 71 | ; NMI (Non-Maskable Interrupt) Handler 72 | gameMainNMI: 73 | LIBSPRITE_UPDATE ; Update sprites in OAM (Object Attribute Memory) 74 | jsr gameMainUpdateTopScroll ; Update top scroll values 75 | 76 | lda #1 ; Set bFrameReady flag to 1 to signal the game update loop 77 | sta bFrameReady ; Store the flag in memory 78 | rti ; Return from interrupt - graphics update happens after this 79 | 80 | ;=============================================================================== 81 | ; Bottom Scroll Update Subroutine 82 | gameMainUpdateBottomScroll: 83 | ; Add 100 subpixels to the horizontal scroll position every frame 84 | LIBMATH_ADD8TO24_AVA wBottomScrollX, 100, wBottomScrollX 85 | 86 | ; Set the bottom scroll for the screen (based on the current scroll values) 87 | LIBSCREEN_SETSCROLL_AA wBottomScrollX+1, wBottomScrollY+1 88 | rts ; Return from subroutine 89 | 90 | ;=============================================================================== 91 | ; Top Scroll Update Subroutine 92 | gameMainUpdateTopScroll: 93 | ; Add 50 subpixels to the horizontal scroll position every frame (half the speed of the bottom) 94 | LIBMATH_ADD8TO24_AVA wTopScrollX, 50, wTopScrollX 95 | 96 | ; Set the top scroll for the screen (based on the current scroll values) 97 | LIBSCREEN_SETSCROLL_AA wTopScrollX+1, wTopScrollY+1 98 | rts ; Return from subroutine 99 | 100 | ;=============================================================================== 101 | ; Data Segment 102 | .include "gameData.asm" ; Include external game data (background map, palettes, etc.) 103 | 104 | ;=============================================================================== 105 | ; Character Segment (CHR-ROM) 106 | .segment "CHARS" 107 | ; ROM chars start at PPU address $0000 108 | .incbin "../../content/tankracebackground.chr" 109 | .incbin "../../content/tankracesprites.chr" 110 | 111 | ;=============================================================================== 112 | ; Interrupt Vectors 113 | .segment "VECTORS" 114 | ; This segment defines what happens during specific events: 115 | .word gameMainNMI ; NMI (VBlank) - triggered every frame to handle graphics 116 | .word gameMainInit ; Reset - called when the game is first started 117 | .word 0 ; IRQ - not used in this simple project 118 | -------------------------------------------------------------------------------- /chapters/chapter7/3/gameData.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition gameData 3 | ;=============================================================================== 4 | ; Constants 5 | Sprite0_X = 8 6 | Sprite0_Y = 162 ; Y position for Sprite 0 on the screen 7 | 8 | ;=============================================================================== 9 | ; Palette Data 10 | PaletteBG: 11 | .incbin "../../content/tankracebackground.pal" 12 | 13 | PaletteSP: 14 | .incbin "../../content/tankracesprites.pal" 15 | 16 | ;=============================================================================== 17 | ; Screen Data 18 | DesertLeft: 19 | .incbin "../../content/tankracedesertleft.nam" 20 | 21 | DesertRight: 22 | .incbin "../../content/tankracedesertright.nam" 23 | 24 | ;=============================================================================== 25 | ; Sprite Data 26 | 27 | bSpriteTiles: 28 | /* 00-00 */ .byte $FE ; Sprite 0 tile ID 29 | /* End flag */ .byte $FF ; End of sprite tiles array (terminates init loop) 30 | 31 | bSpritePalettes: 32 | /* 00-00 */ .byte $00 ; Palette for Sprite 0 33 | 34 | bSpriteXPos: 35 | /* 00-00 */ .byte Sprite0_X ; X position for Sprite 0 36 | 37 | bSpriteYPos: 38 | /* 00-00 */ .byte Sprite0_Y ; Y position for Sprite 0 39 | 40 | ;=============================================================================== 41 | ; Sprite Initialization Subroutine 42 | 43 | gameDataSpritesInit: 44 | ldx #0 ; Initialize X register to 0 (starting index for sprites) 45 | 46 | sprite_loop: 47 | stx bLibTemp1 ; Store sprite index in bLibTemp1 (used for sprite number) 48 | 49 | ; Set sprite pattern tile 50 | lda bSpriteTiles, x ; Load sprite tile from array based on X index 51 | cmp #$FF ; Check if tile is the terminator ($FF) 52 | beq sprite_done ; If it's $FF, exit the loop 53 | sta bLibTemp2 ; Store tile in bLibTemp2 for later use 54 | LIBSPRITE_SETFRAME8x16_AAV bLibTemp1, bLibTemp2, 1 ; Set the sprite frame to 8x16 mode 55 | 56 | ; Set sprite palette 57 | lda bSpritePalettes, x ; Load sprite palette from array based on X index 58 | sta bLibTemp2 ; Store palette in bLibTemp2 59 | LIBSPRITE_SETPALETTE_AA bLibTemp1, bLibTemp2 ; Set sprite palette 60 | 61 | ; Set sprite X and Y position 62 | lda bSpriteXPos, x ; Load X position from array 63 | sta bLibTemp2 ; Store X position in bLibTemp2 64 | lda bSpriteYPos, x ; Load Y position from array 65 | sta bLibTemp3 ; Store Y position in bLibTemp3 66 | LIBSPRITE_SETPOSITION_AAA bLibTemp1, bLibTemp2, bLibTemp3 ; Set sprite position (X, Y) 67 | 68 | inx ; Increment X to move to the next sprite 69 | jmp sprite_loop ; Repeat the loop for the next sprite 70 | 71 | sprite_done: 72 | rts ; Return from subroutine 73 | -------------------------------------------------------------------------------- /chapters/chapter8/1/gameData.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition gameData 3 | ;=============================================================================== 4 | ; Constants 5 | Sprite0_X = 0 6 | Sprite0_Y = 162 7 | PL1_X = 32 8 | PL1_Y = 187 ; X and Y positions for Player 1's tank 9 | 10 | ;=============================================================================== 11 | ; Palette Data 12 | PaletteBG: 13 | .incbin "../../content/tankracebackground.pal" 14 | 15 | PaletteSP: 16 | .incbin "../../content/tankracesprites.pal" 17 | 18 | ;=============================================================================== 19 | ; Screen Data 20 | DesertLeft: 21 | .incbin "../../content/tankracedesertleft.nam" 22 | 23 | DesertRight: 24 | .incbin "../../content/tankracedesertright.nam" 25 | 26 | ;=============================================================================== 27 | ; Sprite Data 28 | 29 | bSpriteTiles: 30 | /* 00-00 */ .byte $FE ; Sprite 0 tile ID (sprite 0 is a simple placeholder) 31 | /* 01-11 */ .byte $02, $04, $06, $08, $0a, $20, $22, $24, $26, $28, $2A ; PL1 Tank tiles 32 | /* End flag */ .byte $FF ; End of sprite tiles array (terminates init loop) 33 | 34 | bSpritePalettes: 35 | /* 00-00 */ .byte $00 ; Palette for Sprite 0 36 | /* 01-11 */ .byte $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01 ; Palette for Player 1's tank 37 | 38 | bSpriteXPos: 39 | /* 00-00 */ .byte Sprite0_X ; X position for Sprite 0 40 | /* 01-11 */ .byte PL1_X+8, PL1_X+16, PL1_X+24, PL1_X+32, PL1_X+40, PL1_X, PL1_X+8, PL1_X+16, PL1_X+24, PL1_X+32, PL1_X+40 ; X positions for PL1 tank sprites 41 | 42 | bSpriteYPos: 43 | /* 00-00 */ .byte Sprite0_Y ; Y position for Sprite 0 44 | /* 01-11 */ .byte PL1_Y, PL1_Y, PL1_Y, PL1_Y, PL1_Y, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16 ; Y positions for PL1 tank sprites 45 | 46 | ;=============================================================================== 47 | ; Sprite Initialization Subroutine 48 | 49 | gameDataSpritesInit: 50 | ldx #0 ; Initialize X register to 0 (starting index for sprites) 51 | 52 | sprite_loop: 53 | stx bLibTemp1 ; Store sprite index in bLibTemp1 (used for sprite number) 54 | 55 | ; Set sprite pattern tile 56 | lda bSpriteTiles, x ; Load sprite tile from array based on X index 57 | cmp #$FF ; Check if tile is the terminator ($FF) 58 | beq sprite_done ; If it's $FF, exit the loop 59 | sta bLibTemp2 ; Store tile in bLibTemp2 for later use 60 | LIBSPRITE_SETFRAME8x16_AAV bLibTemp1, bLibTemp2, 1 ; Set sprite frame (8x16) 61 | 62 | ; Set sprite palette 63 | lda bSpritePalettes, x ; Load sprite palette from array based on X index 64 | sta bLibTemp2 ; Store palette in bLibTemp2 65 | LIBSPRITE_SETPALETTE_AA bLibTemp1, bLibTemp2 ; Set sprite palette 66 | 67 | ; Set sprite X and Y positions 68 | lda bSpriteXPos, x ; Load X position from array 69 | sta bLibTemp2 ; Store X position in bLibTemp2 70 | lda bSpriteYPos, x ; Load Y position from array 71 | sta bLibTemp3 ; Store Y position in bLibTemp3 72 | LIBSPRITE_SETPOSITION_AAA bLibTemp1, bLibTemp2, bLibTemp3 ; Set sprite position (X, Y) 73 | 74 | inx ; Increment X register (move to next sprite) 75 | jmp sprite_loop ; Repeat loop for the next sprite 76 | 77 | sprite_done: 78 | rts ; Return from subroutine 79 | -------------------------------------------------------------------------------- /chapters/chapter8/2/chapter8_2.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition chapter8_2 3 | ;=============================================================================== 4 | ; NES ROM Header 5 | .segment "HEADER" 6 | .byte "NES" ; 'NES' identifier - tells the system it's a valid NES ROM 7 | .byte $1a ; Control byte for NES compatibility 8 | .byte 2 ; Number of 16KB PRG-ROM banks (2 x 16KB = 32KB of code) 9 | .byte 1 ; Number of 8KB CHR-ROM banks (1 x 8KB = 8KB of graphics) 10 | .byte 1 | (0 << 4) ; Vertical mirroring 11 | .byte 0 & $f0 ; Using mapper 0 (no extra memory mappers) 12 | .byte 0,0,0,0,0,0,0,0 ; Padding bytes to complete the header 13 | 14 | ;=============================================================================== 15 | ; Zero Page Segment 16 | .segment "ZEROPAGE" 17 | ; Variables stored in zero page for faster access 18 | .include "../../library/libVariables.asm" 19 | 20 | ;=============================================================================== 21 | ; RAM Segment 22 | .segment "RAM" 23 | ; Defines a RAM segment to avoid assembler warnings in chapters not using FamiStudio. 24 | 25 | ;=============================================================================== 26 | ; Code Segment 27 | .segment "CODE" 28 | ; Include necessary library code for handling input, math, screen, and sprites 29 | .include "../../library/libDefines.asm" ; Includes constants like color codes 30 | .include "../../library/libInput.asm" ; Includes player input handling 31 | .include "../../library/libMath.asm" ; Includes math-related functions 32 | .include "../../library/libScreen.asm" ; Includes screen management functions 33 | .include "../../library/libSprite.asm" ; Includes sprite management functions 34 | 35 | ; Include game-specific code 36 | .include "gameTanks.asm" 37 | 38 | ;=============================================================================== 39 | ; Game Initialization 40 | gameMainInit: 41 | LIBSCREEN_INIT ; Initialize the screen and PPU settings 42 | 43 | ; Load background & sprite palettes 44 | LIBSCREEN_LOADPALETTE_AA BGPALETTE, PaletteBG 45 | LIBSCREEN_LOADPALETTE_AA SPPALETTE, PaletteSP 46 | 47 | ; Load screen data to the left nametable 48 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE0, DesertLeft 49 | 50 | ; Load screen data to the right nametable 51 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE1, DesertRight 52 | 53 | LIBSPRITE_INIT ; Initialize sprites 54 | LIBSCREEN_SETSPRITESIZE8x16 ; Set sprites to 8x16 pixel mode for larger character sizes 55 | LIBSCREEN_LEFT8PIXELSSPRITESDISABLE ; Disable sprite rendering in the leftmost 8 pixels of the screen 56 | jsr gameDataSpritesInit ; Initialize sprite data for the game 57 | LIBSCREEN_ENABLEPPU ; Enable PPU rendering (sprites, background) 58 | ; No 'rts' (return) here, so the code will flow directly into the main game loop 59 | 60 | ;=============================================================================== 61 | ; Main Game Update Loop 62 | gameMainUpdate: 63 | lda bFrameReady ; Load the frame-ready flag 64 | beq gameMainUpdate ; If frame not ready (bFrameReady = 0), loop and wait 65 | 66 | ; Player input and tank update logic 67 | LIBINPUT_UPDATE ; Update player input (controller) 68 | jsr gameTanksUpdate ; Update the tank based on player input 69 | LIBSCREEN_WAITSPRITE0 ; Wait for sprite 0 hit (sync game events) 70 | jsr gameMainUpdateBottomScroll ; Update bottom scroll values 71 | 72 | lda #0 ; Reset the bFrameReady flag back to 0 73 | sta bFrameReady ; Prevents game logic from running more than once per frame 74 | jmp gameMainUpdate ; Infinite loop - returns to the beginning of the update loop 75 | 76 | ;=============================================================================== 77 | ; NMI (Non-Maskable Interrupt) Handler 78 | gameMainNMI: 79 | LIBSPRITE_UPDATE ; Update sprites in OAM (Object Attribute Memory) 80 | jsr gameMainUpdateTopScroll ; Update top scroll values 81 | 82 | lda #1 ; Set bFrameReady flag to 1 to signal the game update loop 83 | sta bFrameReady ; Store the flag in memory 84 | rti ; Return from interrupt - graphics update happens after this 85 | 86 | ;=============================================================================== 87 | ; Bottom Scroll Update Subroutine 88 | gameMainUpdateBottomScroll: 89 | ; Set the bottom scroll for the screen (based on the current scroll values) 90 | LIBSCREEN_SETSCROLL_AA wBottomScrollX+1, wBottomScrollY+1 91 | rts ; Return from subroutine 92 | 93 | ;=============================================================================== 94 | ; Top Scroll Update Subroutine 95 | gameMainUpdateTopScroll: 96 | ; Set the top scroll for the screen (based on the current scroll values) 97 | LIBSCREEN_SETSCROLL_AA wTopScrollX+1, wTopScrollY+1 98 | rts ; Return from subroutine 99 | 100 | ;=============================================================================== 101 | ; Data Segment 102 | .include "gameData.asm" ; Include external game data (background map, palettes, etc.) 103 | 104 | ;=============================================================================== 105 | ; Character Segment (CHR-ROM) 106 | .segment "CHARS" 107 | ; ROM chars start at PPU address $0000 108 | .incbin "../../content/tankracebackground.chr" 109 | .incbin "../../content/tankracesprites.chr" 110 | 111 | ;=============================================================================== 112 | ; Interrupt Vectors 113 | .segment "VECTORS" 114 | ; This segment defines what happens during specific events: 115 | .word gameMainNMI ; NMI (VBlank) - triggered every frame to handle graphics 116 | .word gameMainInit ; Reset - called when the game is first started 117 | .word 0 ; IRQ - not used in this simple project 118 | -------------------------------------------------------------------------------- /chapters/chapter8/2/gameData.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition gameData 3 | ;=============================================================================== 4 | ; Constants 5 | Sprite0_X = 0 6 | Sprite0_Y = 162 7 | PL1_X = 32 8 | PL1_Y = 187 ; X and Y positions for Player 1's tank 9 | 10 | ;=============================================================================== 11 | ; Palette Data 12 | PaletteBG: 13 | .incbin "../../content/tankracebackground.pal" 14 | 15 | PaletteSP: 16 | .incbin "../../content/tankracesprites.pal" 17 | 18 | ;=============================================================================== 19 | ; Screen Data 20 | DesertLeft: 21 | .incbin "../../content/tankracedesertleft.nam" 22 | 23 | DesertRight: 24 | .incbin "../../content/tankracedesertright.nam" 25 | 26 | ;=============================================================================== 27 | ; Sprite Data 28 | 29 | bSpriteTiles: 30 | /* 00-00 */ .byte $FE ; Sprite 0 tile ID (sprite 0 is a simple placeholder) 31 | /* 01-11 */ .byte $02, $04, $06, $08, $0a, $20, $22, $24, $26, $28, $2A ; PL1 Tank tiles 32 | /* End flag */ .byte $FF ; End of sprite tiles array (terminates init loop) 33 | 34 | bSpritePalettes: 35 | /* 00-00 */ .byte $00 ; Palette for Sprite 0 36 | /* 01-11 */ .byte $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01 ; Palette for Player 1's tank 37 | 38 | bSpriteXPos: 39 | /* 00-00 */ .byte Sprite0_X ; X position for Sprite 0 40 | /* 01-11 */ .byte PL1_X+8, PL1_X+16, PL1_X+24, PL1_X+32, PL1_X+40, PL1_X, PL1_X+8, PL1_X+16, PL1_X+24, PL1_X+32, PL1_X+40 ; X positions for PL1 tank sprites 41 | 42 | bSpriteYPos: 43 | /* 00-00 */ .byte Sprite0_Y ; Y position for Sprite 0 44 | /* 01-11 */ .byte PL1_Y, PL1_Y, PL1_Y, PL1_Y, PL1_Y, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16 ; Y positions for PL1 tank sprites 45 | 46 | ;=============================================================================== 47 | ; Sprite Initialization Subroutine 48 | 49 | gameDataSpritesInit: 50 | ldx #0 ; Initialize X register to 0 (starting index for sprites) 51 | 52 | sprite_loop: 53 | stx bLibTemp1 ; Store sprite index in bLibTemp1 (used for sprite number) 54 | 55 | ; Set sprite pattern tile 56 | lda bSpriteTiles, x ; Load sprite tile from array based on X index 57 | cmp #$FF ; Check if tile is the terminator ($FF) 58 | beq sprite_done ; If it's $FF, exit the loop 59 | sta bLibTemp2 ; Store tile in bLibTemp2 for later use 60 | LIBSPRITE_SETFRAME8x16_AAV bLibTemp1, bLibTemp2, 1 ; Set sprite frame (8x16) 61 | 62 | ; Set sprite palette 63 | lda bSpritePalettes, x ; Load sprite palette from array based on X index 64 | sta bLibTemp2 ; Store palette in bLibTemp2 65 | LIBSPRITE_SETPALETTE_AA bLibTemp1, bLibTemp2 ; Set sprite palette 66 | 67 | ; Set sprite X and Y positions 68 | lda bSpriteXPos, x ; Load X position from array 69 | sta bLibTemp2 ; Store X position in bLibTemp2 70 | lda bSpriteYPos, x ; Load Y position from array 71 | sta bLibTemp3 ; Store Y position in bLibTemp3 72 | LIBSPRITE_SETPOSITION_AAA bLibTemp1, bLibTemp2, bLibTemp3 ; Set sprite position (X, Y) 73 | 74 | inx ; Increment X register (move to next sprite) 75 | jmp sprite_loop ; Repeat loop for the next sprite 76 | 77 | sprite_done: 78 | rts ; Return from subroutine 79 | -------------------------------------------------------------------------------- /chapters/chapter8/2/gameTanks.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition - gameTanks 3 | ;=============================================================================== 4 | ; Main Tank Update Subroutine 5 | gameTanksUpdate: 6 | jsr gameTanksUpdatePositionPL1 ; Update the position of Player 1's tank 7 | rts ; Return from subroutine 8 | 9 | ;=============================================================================== 10 | ; Player 1 Tank Position Update 11 | ; This subroutine checks for input to move Player 1's tank and updates the 12 | ; screen scroll values accordingly. 13 | gameTanksUpdatePositionPL1: 14 | LIBINPUT_GET_V(GameportRightMask) ; Check if the right D-pad button is pressed 15 | beq no_movement ; If not pressed, skip position update 16 | 17 | ; If the player pressed right, update the tank position and scroll 18 | 19 | ; Update scroll variables 20 | ; 24-bit number for scroll positions (split into subpixel, pixel, and nametable) 21 | ; 8 bits for subpixel, 8 bits for pixel, and 1 bit for nametable (X scrolling) 22 | 23 | ; Add 50 subpixels per frame for the top scroll 24 | LIBMATH_ADD8TO24_AVA wTopScrollX, 50, wTopScrollX 25 | 26 | ; Add 100 subpixels per frame for the bottom scroll 27 | LIBMATH_ADD8TO24_AVA wBottomScrollX, 100, wBottomScrollX 28 | 29 | no_movement: 30 | rts ; Return from subroutine 31 | -------------------------------------------------------------------------------- /chapters/chapter8/3/chapter8_3.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition chapter8_3 3 | ;=============================================================================== 4 | ; NES ROM Header 5 | .segment "HEADER" 6 | .byte "NES" ; 'NES' identifier - tells the system it's a valid NES ROM 7 | .byte $1a ; Control byte for NES compatibility 8 | .byte 2 ; Number of 16KB PRG-ROM banks (2 x 16KB = 32KB of code) 9 | .byte 1 ; Number of 8KB CHR-ROM banks (1 x 8KB = 8KB of graphics) 10 | .byte 1 | (0 << 4) ; Vertical mirroring 11 | .byte 0 & $f0 ; Using mapper 0 (no extra memory mappers) 12 | .byte 0,0,0,0,0,0,0,0 ; Padding bytes to complete the header 13 | 14 | ;=============================================================================== 15 | ; Zero Page Segment 16 | .segment "ZEROPAGE" 17 | ; The zero page is a special area of memory that's faster for the CPU to access. 18 | ; Variables 19 | .include "../../library/libVariables.asm" 20 | .include "gameVariables.asm" 21 | 22 | ;=============================================================================== 23 | ; RAM Segment 24 | .segment "RAM" 25 | ; Defines a RAM segment to avoid assembler warnings in chapters not using FamiStudio. 26 | 27 | ;=============================================================================== 28 | .segment "CODE" 29 | ; Include library code and definitions 30 | .include "../../library/libDefines.asm" ; This file includes constants like color codes 31 | .include "../../library/libInput.asm" ; This file includes functions to handle player input 32 | .include "../../library/libMath.asm" ; This file includes math-related functions 33 | .include "../../library/libScreen.asm" ; This file includes functions to manage the screen 34 | .include "../../library/libSprite.asm" ; This file includes functions to manage sprites 35 | 36 | ; Include game code 37 | .include "gameTanks.asm" 38 | 39 | ;=============================================================================== 40 | ; Game Initialization 41 | gameMainInit: 42 | LIBSCREEN_INIT ; Initialize the screen and PPU 43 | 44 | ; Load background & sprite palettes 45 | LIBSCREEN_LOADPALETTE_AA BGPALETTE, PaletteBG 46 | LIBSCREEN_LOADPALETTE_AA SPPALETTE, PaletteSP 47 | 48 | ; Load screen data to the left nametable 49 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE0, DesertLeft 50 | 51 | ; Load screen data to the right nametable 52 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE1, DesertRight 53 | 54 | LIBSPRITE_INIT ; Initialize sprites 55 | LIBSCREEN_SETSPRITESIZE8x16 ; Set sprites to 8x16 pixel mode for larger character sizes 56 | LIBSCREEN_LEFT8PIXELSSPRITESDISABLE ; Disable sprite rendering in the leftmost 8 pixels of the screen 57 | jsr gameDataSpritesInit ; Initialize sprite data for the game 58 | LIBSCREEN_ENABLEPPU ; Enable PPU rendering (sprites, background) 59 | ; note no rts here so that code flows straight into gameMainUpdate 60 | 61 | ;=============================================================================== 62 | ; gameMain Game Update Loop 63 | gameMainUpdate: 64 | lda bFrameReady ; Load the bFrameReady flag 65 | beq gameMainUpdate ; If it's 0, keep waiting (loop) 66 | 67 | ; game code 68 | LIBINPUT_UPDATE ; update input 69 | jsr gameTanksUpdate ; update the tanks 70 | LIBSCREEN_WAITSPRITE0 ; wait for sprite 0 hit 71 | jsr gameMainUpdateBottomScroll 72 | 73 | lda #0 ; Reset bFrameReady to 0 74 | sta bFrameReady 75 | jmp gameMainUpdate ; Infinite loop 76 | 77 | ;=============================================================================== 78 | ; NMI (Vertical Blank) Interrupt Handler 79 | gameMainNMI: 80 | LIBSPRITE_UPDATE ; update sprites oam 81 | jsr gameMainUpdateTopScroll 82 | 83 | lda #1 ; Set bFrameReady flag to 1 84 | sta bFrameReady 85 | rti ; Return from interrupt (used to update graphics once per frame) 86 | 87 | ;=============================================================================== 88 | 89 | gameMainUpdateBottomScroll: 90 | ; Set the scroll for the bottom of the screen 91 | LIBSCREEN_SETSCROLL_AA wBottomScrollX+1, wBottomScrollY+1 92 | rts 93 | 94 | ;=============================================================================== 95 | 96 | gameMainUpdateTopScroll: 97 | ; Set the scroll for the top of the screen 98 | LIBSCREEN_SETSCROLL_AA wTopScrollX+1, wTopScrollY+1 99 | rts 100 | 101 | ;=============================================================================== 102 | ; Data 103 | .include "gameData.asm" 104 | 105 | ;=============================================================================== 106 | ; Character Segment (CHR-ROM) 107 | .segment "CHARS" 108 | ; ROM chars start at PPU address $0000 109 | .incbin "../../content/tankracebackground.chr" 110 | .incbin "../../content/tankracesprites.chr" 111 | 112 | ;=============================================================================== 113 | ; Interrupt Vectors 114 | .segment "VECTORS" ; This section tells the NES where to jump on specific events. 115 | .word gameMainNMI ; NMI (Non-Maskable Interrupt) - triggers once per frame, jump to vblank 116 | .word gameMainInit ; Reset - when the system starts, jump to gameMainInit 117 | .word 0 ; IRQ - Not used in this project -------------------------------------------------------------------------------- /chapters/chapter8/3/gameData.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition gameData 3 | ;=============================================================================== 4 | ; Constants 5 | Sprite0_X = 0 6 | Sprite0_Y = 162 7 | PL1_X = 32 8 | PL1_Y = 187 9 | 10 | ;=============================================================================== 11 | ; Palette Data 12 | PaletteBG: 13 | .incbin "../../content/tankracebackground.pal" 14 | 15 | PaletteSP: 16 | .incbin "../../content/tankracesprites.pal" 17 | 18 | ;=============================================================================== 19 | ; Screen Data 20 | DesertLeft: 21 | .incbin "../../content/tankracedesertleft.nam" 22 | 23 | DesertRight: 24 | .incbin "../../content/tankracedesertright.nam" 25 | 26 | ;=============================================================================== 27 | ; Sprite Data 28 | 29 | bSpriteTiles: 30 | /* 00-00 */ .byte $FE ; Sprite 0 31 | /* 01-11 */ .byte $02, $04, $06, $08, $0a, $20, $22, $24, $26, $28, $2A ; PL1 Tank 32 | /* end */ .byte $FF ; end flag to terminate init loop 33 | 34 | bSpritePalettes: 35 | /* 00-00 */ .byte $00 ; Sprite 0 36 | /* 01-11 */ .byte $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01 ; PL1 Tank 37 | 38 | bSpriteXPos: 39 | /* 00-00 */ .byte Sprite0_X ; Sprite 0 40 | /* 01-11 */ .byte PL1_X+8, PL1_X+16, PL1_X+24, PL1_X+32, PL1_X+40, PL1_X, PL1_X+8, PL1_X+16, PL1_X+24, PL1_X+32, PL1_X+40 ; PL1 Tank 41 | 42 | bSpriteYPos: 43 | /* 00-00 */ .byte Sprite0_Y ; Sprite 0 44 | /* 01-11 */ .byte PL1_Y, PL1_Y, PL1_Y, PL1_Y, PL1_Y, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16 ; PL1 Tank 45 | 46 | ;=============================================================================== 47 | 48 | gameDataSpritesInit: 49 | ldx #0 ; Initialize X register to 0 (starting index for sprites) 50 | sprite_loop: 51 | stx bLibTemp1 ; Store sprite index in bLibTemp1 (used for sprite number) 52 | 53 | ; Set sprite pattern tile 54 | lda bSpriteTiles, x ; Load sprite tile from array based on X index 55 | cmp #$FF ; Check if tile is the terminator ($FF) 56 | beq sprite_done ; If it's $FF, exit the loop 57 | sta bLibTemp2 ; Store tile in bLibTemp2 for later use 58 | LIBSPRITE_SETFRAME8x16_AAV bLibTemp1, bLibTemp2, 1 ; Set the sprite frame to 8x16 mode 59 | 60 | ; Set sprite palette 61 | lda bSpritePalettes, x ; Load sprite palette from array based on X index 62 | sta bLibTemp2 ; Store palette in bLibTemp2 63 | LIBSPRITE_SETPALETTE_AA bLibTemp1, bLibTemp2 ; Set sprite palette 64 | 65 | ; Set sprite X and Y position 66 | lda bSpriteXPos, x ; Load X position from array 67 | sta bLibTemp2 ; Store X position in bLibTemp2 68 | lda bSpriteYPos, x ; Load Y position from array 69 | sta bLibTemp3 ; Store Y position in bLibTemp3 70 | LIBSPRITE_SETPOSITION_AAA bLibTemp1, bLibTemp2, bLibTemp3 ; Set sprite position (X, Y) 71 | 72 | inx ; Increment X to move to the next sprite 73 | jmp sprite_loop ; Repeat the loop for the next sprite 74 | 75 | sprite_done: 76 | rts ; Return from subroutine -------------------------------------------------------------------------------- /chapters/chapter8/3/gameTanks.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition - gameTanks 3 | ;=============================================================================== 4 | ; Constants 5 | 6 | ButtonLeft = GameportBMask 7 | ButtonRight = GameportAMask 8 | ;ButtonLeft = GameportLeftMask 9 | ;ButtonRight = GameportRightMask 10 | TankPower = 20 11 | TankFriction = 1 12 | PL1TankMaxSpeed = 125 13 | 14 | ;=============================================================================== 15 | ; Subroutines 16 | 17 | gameTanksUpdate: 18 | jsr gameTanksUpdatePositionPL1 19 | rts 20 | 21 | ;=============================================================================== 22 | 23 | gameTanksUpdatePositionPL1: 24 | ; Initialize Player 1 acceleration to 0 25 | lda #0 26 | sta bPL1Acceleration 27 | 28 | ; Check if left press is required 29 | lda bLeftPressRequired 30 | beq check_right_press ; Skip to right press check if not required 31 | 32 | ; Left press required 33 | LIBINPUT_GETBLIP_V ButtonLeft ; Check for left input 34 | beq handle_input_end ; Skip if left not pressed 35 | ; Left was pressed 36 | lda #0 ; Set left press not required 37 | sta bLeftPressRequired 38 | lda #TankPower ; Load tank power value 39 | sta bPL1Acceleration ; Apply acceleration based on TankPower 40 | jmp handle_input_end ; Skip the right press handling 41 | 42 | check_right_press: 43 | ; Check if right press is required 44 | LIBINPUT_GETBLIP_V ButtonRight ; Check for right input 45 | beq handle_input_end ; Skip if right not pressed 46 | ; Right was pressed 47 | lda #1 ; Set left press required again 48 | sta bLeftPressRequired 49 | lda #TankPower ; Load tank power value 50 | sta bPL1Acceleration ; Apply acceleration based on TankPower 51 | 52 | handle_input_end: 53 | 54 | ; Update Player 1 Velocity 55 | LIBMATH_ADD8TO16_AAA wPL1Velocity, bPL1Acceleration, wPL1Velocity ; Add acceleration to velocity 56 | LIBMATH_MIN16BIT_AV wPL1Velocity, PL1TankMaxSpeed ; Clamp velocity to MaxSpeed 57 | 58 | ; Apply Friction to Velocity 59 | LIBMATH_MAX16BIT_AV wPL1Velocity, TankFriction ; Ensure velocity doesn't go below 0 60 | LIBMATH_SUB8FROM16_AVA wPL1Velocity, TankFriction, wPL1Velocity ; Subtract friction 61 | 62 | ; Update Bottom Scroll (Player 1's position) 63 | LIBMATH_ADD16TO24_AAA wBottomScrollX, wPL1Velocity, wBottomScrollX ; Add velocity to bottom scroll position 64 | 65 | ; Update Top Scroll (Half speed of bottom scroll) 66 | lda wPL1Velocity 67 | sta wLibTemp1 68 | lda wPL1Velocity+1 69 | sta wLibTemp1+1 70 | 71 | ; Divide velocity by 2 72 | lsr wLibTemp1+1 ; Shift right the most significant byte 73 | ror wLibTemp1 ; Rotate right the least significant byte 74 | 75 | LIBMATH_ADD16TO24_AAA wTopScrollX, wLibTemp1, wTopScrollX ; Add halved velocity to top scroll 76 | 77 | rts ; Return from subroutine -------------------------------------------------------------------------------- /chapters/chapter8/3/gameVariables.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition - gameVariables 3 | ;=============================================================================== 4 | ; Variables 5 | 6 | bLeftPressRequired: .byte 0 7 | bPL1Acceleration: .byte 0 8 | wPL1Velocity: .word 0 -------------------------------------------------------------------------------- /chapters/chapter8/4/chapter8_4.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition chapter8_4 3 | ;=============================================================================== 4 | ; NES ROM Header 5 | .segment "HEADER" 6 | .byte "NES" ; 'NES' identifier - tells the system it's a valid NES ROM 7 | .byte $1a ; Control byte for NES compatibility 8 | .byte 2 ; Number of 16KB PRG-ROM banks (2 x 16KB = 32KB of code) 9 | .byte 1 ; Number of 8KB CHR-ROM banks (1 x 8KB = 8KB of graphics) 10 | .byte 1 | (0 << 4) ; Vertical mirroring 11 | .byte 0 & $f0 ; Using mapper 0 (no extra memory mappers) 12 | .byte 0,0,0,0,0,0,0,0 ; Padding bytes to complete the header 13 | 14 | ;=============================================================================== 15 | ; Zero Page Segment 16 | .segment "ZEROPAGE" 17 | ; The zero page is a special area of memory that's faster for the CPU to access. 18 | ; Variables 19 | .include "../../library/libVariables.asm" 20 | .include "gameVariables.asm" 21 | 22 | ;=============================================================================== 23 | ; RAM Segment 24 | .segment "RAM" 25 | ; Defines a RAM segment to avoid assembler warnings in chapters not using FamiStudio. 26 | 27 | ;=============================================================================== 28 | .segment "CODE" 29 | ; Include library code and definitions 30 | .include "../../library/libDefines.asm" ; This file includes constants like color codes 31 | .include "../../library/libInput.asm" ; This file includes functions to handle player input 32 | .include "../../library/libMath.asm" ; This file includes math-related functions 33 | .include "../../library/libScreen.asm" ; This file includes functions to manage the screen 34 | .include "../../library/libSprite.asm" ; This file includes functions to manage sprites 35 | 36 | ; Include game code 37 | .include "gameTanks.asm" 38 | 39 | ;=============================================================================== 40 | ; Game Initialization 41 | gameMainInit: 42 | LIBSCREEN_INIT ; Initialize the screen and PPU 43 | 44 | ; Load background & sprite palettes 45 | LIBSCREEN_LOADPALETTE_AA BGPALETTE, PaletteBG 46 | LIBSCREEN_LOADPALETTE_AA SPPALETTE, PaletteSP 47 | 48 | ; Load screen data to the left nametable 49 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE0, DesertLeft 50 | 51 | ; Load screen data to the right nametable 52 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE1, DesertRight 53 | 54 | LIBSPRITE_INIT ; Initialize sprites 55 | LIBSCREEN_SETSPRITESIZE8x16 ; Set sprites to 8x16 pixel mode for larger character sizes 56 | LIBSCREEN_LEFT8PIXELSSPRITESDISABLE ; Disable sprite rendering in the leftmost 8 pixels of the screen 57 | jsr gameDataSpritesInit ; Initialize sprite data for the game 58 | LIBSCREEN_ENABLEPPU ; Enable PPU rendering (sprites, background) 59 | ; note no rts here so that code flows straight into gameMainUpdate 60 | 61 | ;=============================================================================== 62 | ; gameMain Game Update Loop 63 | gameMainUpdate: 64 | lda bFrameReady ; Load the bFrameReady flag 65 | beq gameMainUpdate ; If it's 0, keep waiting (loop) 66 | 67 | ; game code 68 | LIBINPUT_UPDATE ; update input 69 | jsr gameTanksUpdate ; update the tanks 70 | LIBSCREEN_WAITSPRITE0 ; wait for sprite 0 hit 71 | jsr gameMainUpdateBottomScroll 72 | 73 | lda #0 ; Reset bFrameReady to 0 74 | sta bFrameReady 75 | jmp gameMainUpdate ; Infinite loop 76 | 77 | ;=============================================================================== 78 | ; NMI (Vertical Blank) Interrupt Handler 79 | gameMainNMI: 80 | LIBSPRITE_UPDATE ; update sprites oam 81 | jsr gameMainUpdateTopScroll 82 | 83 | lda #1 ; Set bFrameReady flag to 1 84 | sta bFrameReady 85 | rti ; Return from interrupt (used to update graphics once per frame) 86 | 87 | ;=============================================================================== 88 | 89 | gameMainUpdateBottomScroll: 90 | ; Set the scroll for the bottom of the screen 91 | LIBSCREEN_SETSCROLL_AA wBottomScrollX+1, wBottomScrollY+1 92 | rts 93 | 94 | ;=============================================================================== 95 | 96 | gameMainUpdateTopScroll: 97 | ; Set the scroll for the top of the screen 98 | LIBSCREEN_SETSCROLL_AA wTopScrollX+1, wTopScrollY+1 99 | rts 100 | 101 | ;=============================================================================== 102 | ; Data 103 | .include "gameData.asm" 104 | 105 | ;=============================================================================== 106 | ; Character Segment (CHR-ROM) 107 | .segment "CHARS" 108 | ; ROM chars start at PPU address $0000 109 | .incbin "../../content/tankracebackground.chr" 110 | .incbin "../../content/tankracesprites.chr" 111 | 112 | ;=============================================================================== 113 | ; Interrupt Vectors 114 | .segment "VECTORS" ; This section tells the NES where to jump on specific events. 115 | .word gameMainNMI ; NMI (Non-Maskable Interrupt) - triggers once per frame, jump to vblank 116 | .word gameMainInit ; Reset - when the system starts, jump to gameMainInit 117 | .word 0 ; IRQ - Not used in this project -------------------------------------------------------------------------------- /chapters/chapter8/4/gameData.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition gameData 3 | ;=============================================================================== 4 | ; Constants 5 | Sprite0_X = 0 6 | Sprite0_Y = 162 7 | PL1_X = 32 8 | PL1_Y = 187 9 | 10 | ;=============================================================================== 11 | ; Palette Data 12 | PaletteBG: 13 | .incbin "../../content/tankracebackground.pal" 14 | 15 | PaletteSP: 16 | .incbin "../../content/tankracesprites.pal" 17 | 18 | ;=============================================================================== 19 | ; Screen Data 20 | DesertLeft: 21 | .incbin "../../content/tankracedesertleft.nam" 22 | 23 | DesertRight: 24 | .incbin "../../content/tankracedesertright.nam" 25 | 26 | ;=============================================================================== 27 | ; Sprite Data 28 | 29 | bSpriteTiles: 30 | /* 00-00 */ .byte $FE ; Sprite 0 31 | /* 01-11 */ .byte $02, $04, $06, $08, $0a, $20, $22, $24, $26, $28, $2A ; PL1 Tank 32 | /* end */ .byte $FF ; end flag to terminate init loop 33 | 34 | bSpritePalettes: 35 | /* 00-00 */ .byte $00 ; Sprite 0 36 | /* 01-11 */ .byte $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01 ; PL1 Tank 37 | 38 | bSpriteXPos: 39 | /* 00-00 */ .byte Sprite0_X ; Sprite 0 40 | /* 01-11 */ .byte PL1_X+8, PL1_X+16, PL1_X+24, PL1_X+32, PL1_X+40, PL1_X, PL1_X+8, PL1_X+16, PL1_X+24, PL1_X+32, PL1_X+40 ; PL1 Tank 41 | 42 | bSpriteYPos: 43 | /* 00-00 */ .byte Sprite0_Y ; Sprite 0 44 | /* 01-11 */ .byte PL1_Y, PL1_Y, PL1_Y, PL1_Y, PL1_Y, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16 ; PL1 Tank 45 | 46 | ;=============================================================================== 47 | 48 | gameDataSpritesInit: 49 | ldx #0 ; Initialize X register to 0 (starting index for sprites) 50 | sprite_loop: 51 | stx bLibTemp1 ; Store sprite index in bLibTemp1 (used for sprite number) 52 | 53 | ; Set sprite pattern tile 54 | lda bSpriteTiles, x ; Load sprite tile from array based on X index 55 | cmp #$FF ; Check if tile is the terminator ($FF) 56 | beq sprite_done ; If it's $FF, exit the loop 57 | sta bLibTemp2 ; Store tile in bLibTemp2 for later use 58 | LIBSPRITE_SETFRAME8x16_AAV bLibTemp1, bLibTemp2, 1 ; Set the sprite frame to 8x16 mode 59 | 60 | ; Set sprite palette 61 | lda bSpritePalettes, x ; Load sprite palette from array based on X index 62 | sta bLibTemp2 ; Store palette in bLibTemp2 63 | LIBSPRITE_SETPALETTE_AA bLibTemp1, bLibTemp2 ; Set sprite palette 64 | 65 | ; Set sprite X and Y position 66 | lda bSpriteXPos, x ; Load X position from array 67 | sta bLibTemp2 ; Store X position in bLibTemp2 68 | lda bSpriteYPos, x ; Load Y position from array 69 | sta bLibTemp3 ; Store Y position in bLibTemp3 70 | LIBSPRITE_SETPOSITION_AAA bLibTemp1, bLibTemp2, bLibTemp3 ; Set sprite position (X, Y) 71 | 72 | inx ; Increment X to move to the next sprite 73 | jmp sprite_loop ; Repeat the loop for the next sprite 74 | 75 | sprite_done: 76 | rts ; Return from subroutine -------------------------------------------------------------------------------- /chapters/chapter8/4/gameTanks.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition - gameTanks 3 | ;=============================================================================== 4 | ; Constants 5 | 6 | ButtonLeft = GameportBMask 7 | ButtonRight = GameportAMask 8 | ;ButtonLeft = GameportLeftMask 9 | ;ButtonRight = GameportRightMask 10 | TankPower = 20 11 | TankFriction = 1 12 | PL1TankMaxSpeed = 125 13 | PL1TankStartSprite = 1 14 | 15 | ;=============================================================================== 16 | ; Subroutines 17 | 18 | gameTanksUpdate: 19 | jsr gameTanksUpdatePositionPL1 20 | jsr gameTanksUpdateAnimationPL1 21 | rts 22 | 23 | ;=============================================================================== 24 | 25 | gameTanksUpdatePositionPL1: 26 | ; Initialize Player 1 acceleration to 0 27 | lda #0 28 | sta bPL1Acceleration 29 | 30 | ; Check if left press is required 31 | lda bLeftPressRequired 32 | beq check_right_press ; Skip to right press check if not required 33 | 34 | ; Left press required 35 | LIBINPUT_GETBLIP_V ButtonLeft ; Check for left input 36 | beq handle_input_end ; Skip if left not pressed 37 | ; Left was pressed 38 | lda #0 ; Set left press not required 39 | sta bLeftPressRequired 40 | lda #TankPower ; Load tank power value 41 | sta bPL1Acceleration ; Apply acceleration based on TankPower 42 | jmp handle_input_end ; Skip the right press handling 43 | 44 | check_right_press: 45 | ; Check if right press is required 46 | LIBINPUT_GETBLIP_V ButtonRight ; Check for right input 47 | beq handle_input_end ; Skip if right not pressed 48 | ; Right was pressed 49 | lda #1 ; Set left press required again 50 | sta bLeftPressRequired 51 | lda #TankPower ; Load tank power value 52 | sta bPL1Acceleration ; Apply acceleration based on TankPower 53 | 54 | handle_input_end: 55 | 56 | ; Update Player 1 Velocity 57 | LIBMATH_ADD8TO16_AAA wPL1Velocity, bPL1Acceleration, wPL1Velocity ; Add acceleration to velocity 58 | LIBMATH_MIN16BIT_AV wPL1Velocity, PL1TankMaxSpeed ; Clamp velocity to MaxSpeed 59 | 60 | ; Apply Friction to Velocity 61 | LIBMATH_MAX16BIT_AV wPL1Velocity, TankFriction ; Ensure velocity doesn't go below 0 62 | LIBMATH_SUB8FROM16_AVA wPL1Velocity, TankFriction, wPL1Velocity ; Subtract friction 63 | 64 | ; Update Bottom Scroll (Player 1's position) 65 | LIBMATH_ADD16TO24_AAA wBottomScrollX, wPL1Velocity, wBottomScrollX ; Add velocity to bottom scroll position 66 | 67 | ; Update Top Scroll (Half speed of bottom scroll) 68 | lda wPL1Velocity 69 | sta wLibTemp1 70 | lda wPL1Velocity+1 71 | sta wLibTemp1+1 72 | 73 | ; Divide velocity by 2 74 | lsr wLibTemp1+1 ; Shift right the most significant byte 75 | ror wLibTemp1 ; Rotate right the least significant byte 76 | 77 | LIBMATH_ADD16TO24_AAA wTopScrollX, wLibTemp1, wTopScrollX ; Add halved velocity to top scroll 78 | 79 | rts ; Return from subroutine 80 | 81 | ;=============================================================================== 82 | 83 | gameTanksUpdateAnimationPL1: 84 | LIBSPRITE_SETFRAME8x16_VVV PL1TankStartSprite+0, $02, 1 85 | LIBSPRITE_SETFRAME8x16_VVV PL1TankStartSprite+6, $22, 1 86 | LIBSPRITE_SETFRAME8x16_VVV PL1TankStartSprite+7, $24, 1 87 | LIBSPRITE_SETFRAME8x16_VVV PL1TankStartSprite+8, $26, 1 88 | LIBSPRITE_SETFRAME8x16_VVV PL1TankStartSprite+9, $28, 1 89 | lda wBottomScrollX+1 90 | and #%00000010 91 | beq :++ 92 | 93 | lda wPL1Velocity 94 | beq :+ 95 | ; only set arial to not be upright if velocity > 0 96 | LIBSPRITE_SETFRAME8x16_VVV PL1TankStartSprite+0, $0C, 1 97 | : 98 | LIBSPRITE_SETFRAME8x16_VVV PL1TankStartSprite+6, $2C, 1 99 | LIBSPRITE_SETFRAME8x16_VVV PL1TankStartSprite+7, $2E, 1 100 | LIBSPRITE_SETFRAME8x16_VVV PL1TankStartSprite+8, $30, 1 101 | LIBSPRITE_SETFRAME8x16_VVV PL1TankStartSprite+9, $32, 1 102 | : 103 | rts -------------------------------------------------------------------------------- /chapters/chapter8/4/gameVariables.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition - gameVariables 3 | ;=============================================================================== 4 | ; Variables 5 | 6 | bLeftPressRequired: .byte 0 7 | bPL1Acceleration: .byte 0 8 | wPL1Velocity: .word 0 -------------------------------------------------------------------------------- /chapters/chapter9/1/chapter9_1.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition chapter9_1 3 | ;=============================================================================== 4 | ; NES ROM Header 5 | .segment "HEADER" 6 | .byte "NES" ; 'NES' identifier - tells the system it's a valid NES ROM 7 | .byte $1a ; Control byte for NES compatibility 8 | .byte 2 ; Number of 16KB PRG-ROM banks (2 x 16KB = 32KB of code) 9 | .byte 1 ; Number of 8KB CHR-ROM banks (1 x 8KB = 8KB of graphics) 10 | .byte 1 | (0 << 4) ; Vertical mirroring 11 | .byte 0 & $f0 ; Using mapper 0 (no extra memory mappers) 12 | .byte 0,0,0,0,0,0,0,0 ; Padding bytes to complete the header 13 | 14 | ;=============================================================================== 15 | ; Zero Page Segment 16 | .segment "ZEROPAGE" 17 | ; The zero page is a special area of memory that's faster for the CPU to access. 18 | ; Variables 19 | .include "../../library/libVariables.asm" 20 | .include "gameVariables.asm" 21 | 22 | ;=============================================================================== 23 | ; RAM Segment 24 | .segment "RAM" 25 | ; Defines a RAM segment to avoid assembler warnings in chapters not using FamiStudio. 26 | 27 | ;=============================================================================== 28 | .segment "CODE" 29 | ; Include library code and definitions 30 | .include "../../library/libDefines.asm" ; This file includes constants like color codes 31 | .include "../../library/libInput.asm" ; This file includes functions to handle player input 32 | .include "../../library/libMath.asm" ; This file includes math-related functions 33 | .include "../../library/libScreen.asm" ; This file includes functions to manage the screen 34 | .include "../../library/libSprite.asm" ; This file includes functions to manage sprites 35 | 36 | ; Include game code 37 | .include "gameTanks.asm" 38 | 39 | ;=============================================================================== 40 | ; Game Initialization 41 | gameMainInit: 42 | LIBSCREEN_INIT ; Initialize the screen and PPU 43 | 44 | ; Load background & sprite palettes 45 | LIBSCREEN_LOADPALETTE_AA BGPALETTE, PaletteBG 46 | LIBSCREEN_LOADPALETTE_AA SPPALETTE, PaletteSP 47 | 48 | ; Load screen data to the left nametable 49 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE0, DesertLeft 50 | 51 | ; Load screen data to the right nametable 52 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE1, DesertRight 53 | 54 | LIBSPRITE_INIT ; Initialize sprites 55 | LIBSCREEN_SETSPRITESIZE8x16 ; Set sprites to 8x16 pixel mode for larger character sizes 56 | LIBSCREEN_LEFT8PIXELSSPRITESDISABLE ; Disable sprite rendering in the leftmost 8 pixels of the screen 57 | jsr gameDataSpritesInit ; Initialize sprite data for the game 58 | LIBSCREEN_ENABLEPPU ; Enable PPU rendering (sprites, background) 59 | ; note no rts here so that code flows straight into gameMainUpdate 60 | 61 | ;=============================================================================== 62 | ; gameMain Game Update Loop 63 | gameMainUpdate: 64 | lda bFrameReady ; Load the bFrameReady flag 65 | beq gameMainUpdate ; If it's 0, keep waiting (loop) 66 | 67 | ; game code 68 | LIBINPUT_UPDATE ; update input 69 | jsr gameTanksUpdate ; update the tanks 70 | LIBSCREEN_WAITSPRITE0 ; wait for sprite 0 hit 71 | jsr gameMainUpdateBottomScroll 72 | 73 | lda #0 ; Reset bFrameReady to 0 74 | sta bFrameReady 75 | jmp gameMainUpdate ; Infinite loop 76 | 77 | ;=============================================================================== 78 | ; NMI (Vertical Blank) Interrupt Handler 79 | gameMainNMI: 80 | LIBSPRITE_UPDATE ; update sprites oam 81 | jsr gameMainUpdateTopScroll 82 | 83 | lda #1 ; Set bFrameReady flag to 1 84 | sta bFrameReady 85 | rti ; Return from interrupt (used to update graphics once per frame) 86 | 87 | ;=============================================================================== 88 | 89 | gameMainUpdateBottomScroll: 90 | ; Set the scroll for the bottom of the screen 91 | LIBSCREEN_SETSCROLL_AA wBottomScrollX+1, wBottomScrollY+1 92 | rts 93 | 94 | ;=============================================================================== 95 | 96 | gameMainUpdateTopScroll: 97 | ; Set the scroll for the top of the screen 98 | LIBSCREEN_SETSCROLL_AA wTopScrollX+1, wTopScrollY+1 99 | rts 100 | 101 | ;=============================================================================== 102 | ; Data 103 | .include "gameData.asm" 104 | 105 | ;=============================================================================== 106 | ; Character Segment (CHR-ROM) 107 | .segment "CHARS" 108 | ; ROM chars start at PPU address $0000 109 | .incbin "../../content/tankracebackground.chr" 110 | .incbin "../../content/tankracesprites.chr" 111 | 112 | ;=============================================================================== 113 | ; Interrupt Vectors 114 | .segment "VECTORS" ; This section tells the NES where to jump on specific events. 115 | .word gameMainNMI ; NMI (Non-Maskable Interrupt) - triggers once per frame, jump to vblank 116 | .word gameMainInit ; Reset - when the system starts, jump to gameMainInit 117 | .word 0 ; IRQ - Not used in this project -------------------------------------------------------------------------------- /chapters/chapter9/1/gameData.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition gameData 3 | ;=============================================================================== 4 | ; Constants 5 | Sprite0_X = 0 6 | Sprite0_Y = 162 7 | PL1_X = 32 8 | PL1_Y = 187 9 | CPU_X = 32 10 | CPU_Y = 151 11 | 12 | ;=============================================================================== 13 | ; Palette Data 14 | PaletteBG: 15 | .incbin "../../content/tankracebackground.pal" 16 | 17 | PaletteSP: 18 | .incbin "../../content/tankracesprites.pal" 19 | 20 | ;=============================================================================== 21 | ; Screen Data 22 | DesertLeft: 23 | .incbin "../../content/tankracedesertleft.nam" 24 | 25 | DesertRight: 26 | .incbin "../../content/tankracedesertright.nam" 27 | 28 | ;=============================================================================== 29 | ; Sprite Data 30 | 31 | bSpriteTiles: 32 | /* 00-00 */ .byte $FE ; Sprite 0 33 | /* 01-11 */ .byte $02, $04, $06, $08, $0a, $20, $22, $24, $26, $28, $2A ; PL1 Tank 34 | /* 12-22 */ .byte $02, $04, $06, $08, $0a, $20, $22, $24, $26, $28, $2A ; CPU Tank 35 | /* end */ .byte $FF ; end flag to terminate init loop 36 | 37 | bSpritePalettes: 38 | /* 00-00 */ .byte $00 ; Sprite 0 39 | /* 01-11 */ .byte $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01 ; PL1 Tank 40 | /* 12-22 */ .byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 ; CPU Tank 41 | 42 | bSpriteXPos: 43 | /* 00-00 */ .byte Sprite0_X ; Sprite 0 44 | /* 01-11 */ .byte PL1_X+8, PL1_X+16, PL1_X+24, PL1_X+32, PL1_X+40, PL1_X, PL1_X+8, PL1_X+16, PL1_X+24, PL1_X+32, PL1_X+40 ; PL1 Tank 45 | /* 12-22 */ .byte CPU_X+8, CPU_X+16, CPU_X+24, CPU_X+32, CPU_X+40, CPU_X, CPU_X+8, CPU_X+16, CPU_X+24, CPU_X+32, CPU_X+40 ; CPU Tank 46 | 47 | bSpriteYPos: 48 | /* 00-00 */ .byte Sprite0_Y ; Sprite 0 49 | /* 01-11 */ .byte PL1_Y, PL1_Y, PL1_Y, PL1_Y, PL1_Y, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16 ; PL1 Tank 50 | /* 12-22 */ .byte CPU_Y, CPU_Y, CPU_Y, CPU_Y, CPU_Y, CPU_Y+16, CPU_Y+16, CPU_Y+16, CPU_Y+16, CPU_Y+16, CPU_Y+16 ; CPU Tank 51 | 52 | ;=============================================================================== 53 | 54 | gameDataSpritesInit: 55 | ldx #0 ; Initialize X register to 0 (starting index for sprites) 56 | sprite_loop: 57 | stx bLibTemp1 ; Store sprite index in bLibTemp1 (used for sprite number) 58 | 59 | ; Set sprite pattern tile 60 | lda bSpriteTiles, x ; Load sprite tile from array based on X index 61 | cmp #$FF ; Check if tile is the terminator ($FF) 62 | beq sprite_done ; If it's $FF, exit the loop 63 | sta bLibTemp2 ; Store tile in bLibTemp2 for later use 64 | LIBSPRITE_SETFRAME8x16_AAV bLibTemp1, bLibTemp2, 1 ; Set the sprite frame to 8x16 mode 65 | 66 | ; Set sprite palette 67 | lda bSpritePalettes, x ; Load sprite palette from array based on X index 68 | sta bLibTemp2 ; Store palette in bLibTemp2 69 | LIBSPRITE_SETPALETTE_AA bLibTemp1, bLibTemp2 ; Set sprite palette 70 | 71 | ; Set sprite X and Y position 72 | lda bSpriteXPos, x ; Load X position from array 73 | sta bLibTemp2 ; Store X position in bLibTemp2 74 | lda bSpriteYPos, x ; Load Y position from array 75 | sta bLibTemp3 ; Store Y position in bLibTemp3 76 | LIBSPRITE_SETPOSITION_AAA bLibTemp1, bLibTemp2, bLibTemp3 ; Set sprite position (X, Y) 77 | 78 | inx ; Increment X to move to the next sprite 79 | jmp sprite_loop ; Repeat the loop for the next sprite 80 | 81 | sprite_done: 82 | rts ; Return from subroutine -------------------------------------------------------------------------------- /chapters/chapter9/1/gameTanks.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition - gameTanks 3 | ;=============================================================================== 4 | ; Constants 5 | 6 | ButtonLeft = GameportBMask 7 | ButtonRight = GameportAMask 8 | ;ButtonLeft = GameportLeftMask 9 | ;ButtonRight = GameportRightMask 10 | TankPower = 20 11 | TankFriction = 1 12 | PL1TankMaxSpeed = 125 13 | PL1TankStartSprite = 1 14 | 15 | ;=============================================================================== 16 | ; Subroutines 17 | 18 | gameTanksUpdate: 19 | jsr gameTanksUpdatePositionPL1 20 | jsr gameTanksUpdateAnimationPL1 21 | rts 22 | 23 | ;=============================================================================== 24 | 25 | gameTanksUpdatePositionPL1: 26 | ; Initialize Player 1 acceleration to 0 27 | lda #0 28 | sta bPL1Acceleration 29 | 30 | ; Check if left press is required 31 | lda bLeftPressRequired 32 | beq check_right_press ; Skip to right press check if not required 33 | 34 | ; Left press required 35 | LIBINPUT_GETBLIP_V ButtonLeft ; Check for left input 36 | beq handle_input_end ; Skip if left not pressed 37 | ; Left was pressed 38 | lda #0 ; Set left press not required 39 | sta bLeftPressRequired 40 | lda #TankPower ; Load tank power value 41 | sta bPL1Acceleration ; Apply acceleration based on TankPower 42 | jmp handle_input_end ; Skip the right press handling 43 | 44 | check_right_press: 45 | ; Check if right press is required 46 | LIBINPUT_GETBLIP_V ButtonRight ; Check for right input 47 | beq handle_input_end ; Skip if right not pressed 48 | ; Right was pressed 49 | lda #1 ; Set left press required again 50 | sta bLeftPressRequired 51 | lda #TankPower ; Load tank power value 52 | sta bPL1Acceleration ; Apply acceleration based on TankPower 53 | 54 | handle_input_end: 55 | 56 | ; Update Player 1 Velocity 57 | LIBMATH_ADD8TO16_AAA wPL1Velocity, bPL1Acceleration, wPL1Velocity ; Add acceleration to velocity 58 | LIBMATH_MIN16BIT_AV wPL1Velocity, PL1TankMaxSpeed ; Clamp velocity to MaxSpeed 59 | 60 | ; Apply Friction to Velocity 61 | LIBMATH_MAX16BIT_AV wPL1Velocity, TankFriction ; Ensure velocity doesn't go below 0 62 | LIBMATH_SUB8FROM16_AVA wPL1Velocity, TankFriction, wPL1Velocity ; Subtract friction 63 | 64 | ; Update Bottom Scroll (Player 1's position) 65 | LIBMATH_ADD16TO24_AAA wBottomScrollX, wPL1Velocity, wBottomScrollX ; Add velocity to bottom scroll position 66 | 67 | ; Update Top Scroll (Half speed of bottom scroll) 68 | lda wPL1Velocity 69 | sta wLibTemp1 70 | lda wPL1Velocity+1 71 | sta wLibTemp1+1 72 | 73 | ; Divide velocity by 2 74 | lsr wLibTemp1+1 ; Shift right the most significant byte 75 | ror wLibTemp1 ; Rotate right the least significant byte 76 | 77 | LIBMATH_ADD16TO24_AAA wTopScrollX, wLibTemp1, wTopScrollX ; Add halved velocity to top scroll 78 | 79 | rts ; Return from subroutine 80 | 81 | ;=============================================================================== 82 | 83 | gameTanksUpdateAnimationPL1: 84 | LIBSPRITE_SETFRAME8x16_VVV PL1TankStartSprite+0, $02, 1 85 | LIBSPRITE_SETFRAME8x16_VVV PL1TankStartSprite+6, $22, 1 86 | LIBSPRITE_SETFRAME8x16_VVV PL1TankStartSprite+7, $24, 1 87 | LIBSPRITE_SETFRAME8x16_VVV PL1TankStartSprite+8, $26, 1 88 | LIBSPRITE_SETFRAME8x16_VVV PL1TankStartSprite+9, $28, 1 89 | lda wBottomScrollX+1 90 | and #%00000010 91 | beq :++ 92 | 93 | lda wPL1Velocity 94 | beq :+ 95 | ; only set arial to not be upright if velocity > 0 96 | LIBSPRITE_SETFRAME8x16_VVV PL1TankStartSprite+0, $0C, 1 97 | : 98 | LIBSPRITE_SETFRAME8x16_VVV PL1TankStartSprite+6, $2C, 1 99 | LIBSPRITE_SETFRAME8x16_VVV PL1TankStartSprite+7, $2E, 1 100 | LIBSPRITE_SETFRAME8x16_VVV PL1TankStartSprite+8, $30, 1 101 | LIBSPRITE_SETFRAME8x16_VVV PL1TankStartSprite+9, $32, 1 102 | : 103 | rts -------------------------------------------------------------------------------- /chapters/chapter9/1/gameVariables.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition - gameVariables 3 | ;=============================================================================== 4 | ; Variables 5 | 6 | bLeftPressRequired: .byte 0 7 | bPL1Acceleration: .byte 0 8 | wPL1Velocity: .word 0 -------------------------------------------------------------------------------- /chapters/chapter9/2/chapter9_2.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition chapter9_2 3 | ;=============================================================================== 4 | ; NES ROM Header 5 | .segment "HEADER" 6 | .byte "NES" ; 'NES' identifier - tells the system it's a valid NES ROM 7 | .byte $1a ; Control byte for NES compatibility 8 | .byte 2 ; Number of 16KB PRG-ROM banks (2 x 16KB = 32KB of code) 9 | .byte 1 ; Number of 8KB CHR-ROM banks (1 x 8KB = 8KB of graphics) 10 | .byte 1 | (0 << 4) ; Vertical mirroring 11 | .byte 0 & $f0 ; Using mapper 0 (no extra memory mappers) 12 | .byte 0,0,0,0,0,0,0,0 ; Padding bytes to complete the header 13 | 14 | ;=============================================================================== 15 | ; Zero Page Segment 16 | .segment "ZEROPAGE" 17 | ; The zero page is a special area of memory that's faster for the CPU to access. 18 | ; Variables 19 | .include "../../library/libVariables.asm" 20 | .include "gameVariables.asm" 21 | 22 | ;=============================================================================== 23 | ; RAM Segment 24 | .segment "RAM" 25 | ; Defines a RAM segment to avoid assembler warnings in chapters not using FamiStudio. 26 | 27 | ;=============================================================================== 28 | .segment "CODE" 29 | ; Include library code and definitions 30 | .include "../../library/libDefines.asm" ; This file includes constants like color codes 31 | .include "../../library/libInput.asm" ; This file includes functions to handle player input 32 | .include "../../library/libMath.asm" ; This file includes math-related functions 33 | .include "../../library/libScreen.asm" ; This file includes functions to manage the screen 34 | .include "../../library/libSprite.asm" ; This file includes functions to manage sprites 35 | 36 | ; Include game code 37 | .include "gameTanks.asm" 38 | 39 | ;=============================================================================== 40 | ; Game Initialization 41 | gameMainInit: 42 | LIBSCREEN_INIT ; Initialize the screen and PPU 43 | 44 | ; Load background & sprite palettes 45 | LIBSCREEN_LOADPALETTE_AA BGPALETTE, PaletteBG 46 | LIBSCREEN_LOADPALETTE_AA SPPALETTE, PaletteSP 47 | 48 | ; Load screen data to the left nametable 49 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE0, DesertLeft 50 | 51 | ; Load screen data to the right nametable 52 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE1, DesertRight 53 | 54 | LIBSPRITE_INIT ; Initialize sprites 55 | LIBSCREEN_SETSPRITESIZE8x16 ; Set sprites to 8x16 pixel mode for larger character sizes 56 | LIBSCREEN_LEFT8PIXELSSPRITESDISABLE ; Disable sprite rendering in the leftmost 8 pixels of the screen 57 | jsr gameDataSpritesInit ; Initialize sprite data for the game 58 | LIBSCREEN_ENABLEPPU ; Enable PPU rendering (sprites, background) 59 | ; note no rts here so that code flows straight into gameMainUpdate 60 | 61 | ;=============================================================================== 62 | ; gameMain Game Update Loop 63 | gameMainUpdate: 64 | lda bFrameReady ; Load the bFrameReady flag 65 | beq gameMainUpdate ; If it's 0, keep waiting (loop) 66 | 67 | ; game code 68 | LIBINPUT_UPDATE ; update input 69 | jsr gameTanksUpdate ; update the tanks 70 | LIBSCREEN_WAITSPRITE0 ; wait for sprite 0 hit 71 | jsr gameMainUpdateBottomScroll 72 | 73 | lda #0 ; Reset bFrameReady to 0 74 | sta bFrameReady 75 | jmp gameMainUpdate ; Infinite loop 76 | 77 | ;=============================================================================== 78 | ; NMI (Vertical Blank) Interrupt Handler 79 | gameMainNMI: 80 | LIBSPRITE_UPDATE ; update sprites oam 81 | jsr gameMainUpdateTopScroll 82 | 83 | lda #1 ; Set bFrameReady flag to 1 84 | sta bFrameReady 85 | rti ; Return from interrupt (used to update graphics once per frame) 86 | 87 | ;=============================================================================== 88 | 89 | gameMainUpdateBottomScroll: 90 | ; Set the scroll for the bottom of the screen 91 | LIBSCREEN_SETSCROLL_AA wBottomScrollX+1, wBottomScrollY+1 92 | rts 93 | 94 | ;=============================================================================== 95 | 96 | gameMainUpdateTopScroll: 97 | ; Set the scroll for the top of the screen 98 | LIBSCREEN_SETSCROLL_AA wTopScrollX+1, wTopScrollY+1 99 | rts 100 | 101 | ;=============================================================================== 102 | ; Data 103 | .include "gameData.asm" 104 | 105 | ;=============================================================================== 106 | ; Character Segment (CHR-ROM) 107 | .segment "CHARS" 108 | ; ROM chars start at PPU address $0000 109 | .incbin "../../content/tankracebackground.chr" 110 | .incbin "../../content/tankracesprites.chr" 111 | 112 | ;=============================================================================== 113 | ; Interrupt Vectors 114 | .segment "VECTORS" ; This section tells the NES where to jump on specific events. 115 | .word gameMainNMI ; NMI (Non-Maskable Interrupt) - triggers once per frame, jump to vblank 116 | .word gameMainInit ; Reset - when the system starts, jump to gameMainInit 117 | .word 0 ; IRQ - Not used in this project -------------------------------------------------------------------------------- /chapters/chapter9/2/gameData.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition gameData 3 | ;=============================================================================== 4 | ; Constants 5 | Sprite0_X = 0 6 | Sprite0_Y = 162 7 | PL1_X = 32 8 | PL1_Y = 187 9 | CPU_X = 32 10 | CPU_Y = 151 11 | 12 | ;=============================================================================== 13 | ; Palette Data 14 | PaletteBG: 15 | .incbin "../../content/tankracebackground.pal" 16 | 17 | PaletteSP: 18 | .incbin "../../content/tankracesprites.pal" 19 | 20 | ;=============================================================================== 21 | ; Screen Data 22 | DesertLeft: 23 | .incbin "../../content/tankracedesertleft.nam" 24 | 25 | DesertRight: 26 | .incbin "../../content/tankracedesertright.nam" 27 | 28 | ;=============================================================================== 29 | ; Sprite Data 30 | 31 | bSpriteTiles: 32 | /* 00-00 */ .byte $FE ; Sprite 0 33 | /* 01-11 */ .byte $02, $04, $06, $08, $0a, $20, $22, $24, $26, $28, $2A ; PL1 Tank 34 | /* 12-22 */ .byte $02, $04, $06, $08, $0a, $20, $22, $24, $26, $28, $2A ; CPU Tank 35 | /* end */ .byte $FF ; end flag to terminate init loop 36 | 37 | bSpritePalettes: 38 | /* 00-00 */ .byte $00 ; Sprite 0 39 | /* 01-11 */ .byte $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01 ; PL1 Tank 40 | /* 12-22 */ .byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 ; CPU Tank 41 | 42 | bSpriteXPos: 43 | /* 00-00 */ .byte Sprite0_X ; Sprite 0 44 | /* 01-11 */ .byte PL1_X+8, PL1_X+16, PL1_X+24, PL1_X+32, PL1_X+40, PL1_X, PL1_X+8, PL1_X+16, PL1_X+24, PL1_X+32, PL1_X+40 ; PL1 Tank 45 | /* 12-22 */ .byte CPU_X+8, CPU_X+16, CPU_X+24, CPU_X+32, CPU_X+40, CPU_X, CPU_X+8, CPU_X+16, CPU_X+24, CPU_X+32, CPU_X+40 ; CPU Tank 46 | 47 | bSpriteYPos: 48 | /* 00-00 */ .byte Sprite0_Y ; Sprite 0 49 | /* 01-11 */ .byte PL1_Y, PL1_Y, PL1_Y, PL1_Y, PL1_Y, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16 ; PL1 Tank 50 | /* 12-22 */ .byte CPU_Y, CPU_Y, CPU_Y, CPU_Y, CPU_Y, CPU_Y+16, CPU_Y+16, CPU_Y+16, CPU_Y+16, CPU_Y+16, CPU_Y+16 ; CPU Tank 51 | 52 | bSpriteXOffset: 53 | /* 00-00 */ .byte 0 ; Not used 54 | /* 01-11 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; Not used 55 | /* 12-22 */ .byte 8, 16, 24, 32, 40, 0, 8, 16, 24, 32, 40 ; CPU Tank X Offsets 56 | 57 | bSpriteYOffset: 58 | /* 00-00 */ .byte 0 ; Not used 59 | /* 01-11 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; Not used 60 | /* 12-22 */ .byte 0, 0, 0, 0, 0, 16, 16, 16, 16, 16, 16 ; CPU Tank Y Offsets 61 | 62 | ;=============================================================================== 63 | 64 | gameDataSpritesInit: 65 | ldx #0 ; Initialize X register to 0 (starting index for sprites) 66 | sprite_loop: 67 | stx bLibTemp1 ; Store sprite index in bLibTemp1 (used for sprite number) 68 | 69 | ; Set sprite pattern tile 70 | lda bSpriteTiles, x ; Load sprite tile from array based on X index 71 | cmp #$FF ; Check if tile is the terminator ($FF) 72 | beq sprite_done ; If it's $FF, exit the loop 73 | sta bLibTemp2 ; Store tile in bLibTemp2 for later use 74 | LIBSPRITE_SETFRAME8x16_AAV bLibTemp1, bLibTemp2, 1 ; Set the sprite frame to 8x16 mode 75 | 76 | ; Set sprite palette 77 | lda bSpritePalettes, x ; Load sprite palette from array based on X index 78 | sta bLibTemp2 ; Store palette in bLibTemp2 79 | LIBSPRITE_SETPALETTE_AA bLibTemp1, bLibTemp2 ; Set sprite palette 80 | 81 | ; Set sprite X and Y position 82 | lda bSpriteXPos, x ; Load X position from array 83 | sta bLibTemp2 ; Store X position in bLibTemp2 84 | lda bSpriteYPos, x ; Load Y position from array 85 | sta bLibTemp3 ; Store Y position in bLibTemp3 86 | LIBSPRITE_SETPOSITION_AAA bLibTemp1, bLibTemp2, bLibTemp3 ; Set sprite position (X, Y) 87 | 88 | inx ; Increment X to move to the next sprite 89 | jmp sprite_loop ; Repeat the loop for the next sprite 90 | 91 | sprite_done: 92 | rts ; Return from subroutine -------------------------------------------------------------------------------- /chapters/chapter9/2/gameVariables.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition - gameVariables 3 | ;=============================================================================== 4 | ; Variables 5 | 6 | bLeftPressRequired: .byte 0 7 | bPL1Acceleration: .byte 0 8 | wPL1Velocity: .word 0 9 | wCPUAcceleration: .word 0 10 | wCPUVelocity: .word 0 11 | wCPUTankXPos: .word 0 ; 1st byte = subpixels, 2nd byte = pixels 12 | .byte 0 ; 3rd byte = nametable 13 | wCPUTankScreenXPos: .word 0 ; 1st byte = subpixels, 2nd byte = pixels 14 | .byte 0 ; 3rd byte = nametable -------------------------------------------------------------------------------- /chapters/chapter9/3/chapter9_3.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition chapter9_3 3 | ;=============================================================================== 4 | ; NES ROM Header 5 | .segment "HEADER" 6 | .byte "NES" ; 'NES' identifier - tells the system it's a valid NES ROM 7 | .byte $1a ; Control byte for NES compatibility 8 | .byte 2 ; Number of 16KB PRG-ROM banks (2 x 16KB = 32KB of code) 9 | .byte 1 ; Number of 8KB CHR-ROM banks (1 x 8KB = 8KB of graphics) 10 | .byte 1 | (0 << 4) ; Vertical mirroring 11 | .byte 0 & $f0 ; Using mapper 0 (no extra memory mappers) 12 | .byte 0,0,0,0,0,0,0,0 ; Padding bytes to complete the header 13 | 14 | ;=============================================================================== 15 | ; Zero Page Segment 16 | .segment "ZEROPAGE" 17 | ; The zero page is a special area of memory that's faster for the CPU to access. 18 | ; Variables 19 | .include "../../library/libVariables.asm" 20 | .include "gameVariables.asm" 21 | 22 | ;=============================================================================== 23 | ; RAM Segment 24 | .segment "RAM" 25 | ; Defines a RAM segment to avoid assembler warnings in chapters not using FamiStudio. 26 | 27 | ;=============================================================================== 28 | .segment "CODE" 29 | ; Include library code and definitions 30 | .include "../../library/libDefines.asm" ; This file includes constants like color codes 31 | .include "../../library/libInput.asm" ; This file includes functions to handle player input 32 | .include "../../library/libMath.asm" ; This file includes math-related functions 33 | .include "../../library/libScreen.asm" ; This file includes functions to manage the screen 34 | .include "../../library/libSprite.asm" ; This file includes functions to manage sprites 35 | 36 | ; Include game code 37 | .include "gameTanks.asm" 38 | 39 | ;=============================================================================== 40 | ; Game Initialization 41 | gameMainInit: 42 | LIBSCREEN_INIT ; Initialize the screen and PPU 43 | 44 | ; Load background & sprite palettes 45 | LIBSCREEN_LOADPALETTE_AA BGPALETTE, PaletteBG 46 | LIBSCREEN_LOADPALETTE_AA SPPALETTE, PaletteSP 47 | 48 | ; Load screen data to the left nametable 49 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE0, DesertLeft 50 | 51 | ; Load screen data to the right nametable 52 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE1, DesertRight 53 | 54 | LIBSPRITE_INIT ; Initialize sprites 55 | LIBSCREEN_SETSPRITESIZE8x16 ; Set sprites to 8x16 pixel mode for larger character sizes 56 | LIBSCREEN_LEFT8PIXELSSPRITESDISABLE ; Disable sprite rendering in the leftmost 8 pixels of the screen 57 | jsr gameDataSpritesInit ; Initialize sprite data for the game 58 | LIBSCREEN_ENABLEPPU ; Enable PPU rendering (sprites, background) 59 | ; note no rts here so that code flows straight into gameMainUpdate 60 | 61 | ;=============================================================================== 62 | ; gameMain Game Update Loop 63 | gameMainUpdate: 64 | lda bFrameReady ; Load the bFrameReady flag 65 | beq gameMainUpdate ; If it's 0, keep waiting (loop) 66 | 67 | ; game code 68 | LIBINPUT_UPDATE ; update input 69 | jsr gameTanksUpdate ; update the tanks 70 | LIBSCREEN_WAITSPRITE0 ; wait for sprite 0 hit 71 | jsr gameMainUpdateBottomScroll 72 | 73 | lda #0 ; Reset bFrameReady to 0 74 | sta bFrameReady 75 | jmp gameMainUpdate ; Infinite loop 76 | 77 | ;=============================================================================== 78 | ; NMI (Vertical Blank) Interrupt Handler 79 | gameMainNMI: 80 | LIBSPRITE_UPDATE ; update sprites oam 81 | jsr gameMainUpdateTopScroll 82 | 83 | lda #1 ; Set bFrameReady flag to 1 84 | sta bFrameReady 85 | rti ; Return from interrupt (used to update graphics once per frame) 86 | 87 | ;=============================================================================== 88 | 89 | gameMainUpdateBottomScroll: 90 | ; Set the scroll for the bottom of the screen 91 | LIBSCREEN_SETSCROLL_AA wBottomScrollX+1, wBottomScrollY+1 92 | rts 93 | 94 | ;=============================================================================== 95 | 96 | gameMainUpdateTopScroll: 97 | ; Set the scroll for the top of the screen 98 | LIBSCREEN_SETSCROLL_AA wTopScrollX+1, wTopScrollY+1 99 | rts 100 | 101 | ;=============================================================================== 102 | ; Data 103 | .include "gameData.asm" 104 | 105 | ;=============================================================================== 106 | ; Character Segment (CHR-ROM) 107 | .segment "CHARS" 108 | ; ROM chars start at PPU address $0000 109 | .incbin "../../content/tankracebackground.chr" 110 | .incbin "../../content/tankracesprites.chr" 111 | 112 | ;=============================================================================== 113 | ; Interrupt Vectors 114 | .segment "VECTORS" ; This section tells the NES where to jump on specific events. 115 | .word gameMainNMI ; NMI (Non-Maskable Interrupt) - triggers once per frame, jump to vblank 116 | .word gameMainInit ; Reset - when the system starts, jump to gameMainInit 117 | .word 0 ; IRQ - Not used in this project -------------------------------------------------------------------------------- /chapters/chapter9/3/gameData.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition gameData 3 | ;=============================================================================== 4 | ; Constants 5 | Sprite0_X = 0 6 | Sprite0_Y = 162 7 | PL1_X = 32 8 | PL1_Y = 187 9 | CPU_X = 32 10 | CPU_Y = 151 11 | 12 | ;=============================================================================== 13 | ; Palette Data 14 | PaletteBG: 15 | .incbin "../../content/tankracebackground.pal" 16 | 17 | PaletteSP: 18 | .incbin "../../content/tankracesprites.pal" 19 | 20 | ;=============================================================================== 21 | ; Screen Data 22 | DesertLeft: 23 | .incbin "../../content/tankracedesertleft.nam" 24 | 25 | DesertRight: 26 | .incbin "../../content/tankracedesertright.nam" 27 | 28 | ;=============================================================================== 29 | ; Sprite Data 30 | 31 | bSpriteTiles: 32 | /* 00-00 */ .byte $FE ; Sprite 0 33 | /* 01-11 */ .byte $02, $04, $06, $08, $0a, $20, $22, $24, $26, $28, $2A ; PL1 Tank 34 | /* 12-22 */ .byte $02, $04, $06, $08, $0a, $20, $22, $24, $26, $28, $2A ; CPU Tank 35 | /* end */ .byte $FF ; end flag to terminate init loop 36 | 37 | bSpritePalettes: 38 | /* 00-00 */ .byte $00 ; Sprite 0 39 | /* 01-11 */ .byte $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01 ; PL1 Tank 40 | /* 12-22 */ .byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 ; CPU Tank 41 | 42 | bSpriteXPos: 43 | /* 00-00 */ .byte Sprite0_X ; Sprite 0 44 | /* 01-11 */ .byte PL1_X+8, PL1_X+16, PL1_X+24, PL1_X+32, PL1_X+40, PL1_X, PL1_X+8, PL1_X+16, PL1_X+24, PL1_X+32, PL1_X+40 ; PL1 Tank 45 | /* 12-22 */ .byte CPU_X+8, CPU_X+16, CPU_X+24, CPU_X+32, CPU_X+40, CPU_X, CPU_X+8, CPU_X+16, CPU_X+24, CPU_X+32, CPU_X+40 ; CPU Tank 46 | 47 | bSpriteYPos: 48 | /* 00-00 */ .byte Sprite0_Y ; Sprite 0 49 | /* 01-11 */ .byte PL1_Y, PL1_Y, PL1_Y, PL1_Y, PL1_Y, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16 ; PL1 Tank 50 | /* 12-22 */ .byte CPU_Y, CPU_Y, CPU_Y, CPU_Y, CPU_Y, CPU_Y+16, CPU_Y+16, CPU_Y+16, CPU_Y+16, CPU_Y+16, CPU_Y+16 ; CPU Tank 51 | 52 | bSpriteXOffset: 53 | /* 00-00 */ .byte 0 ; Not used 54 | /* 01-11 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; Not used 55 | /* 12-22 */ .byte 8, 16, 24, 32, 40, 0, 8, 16, 24, 32, 40 ; CPU Tank X Offsets 56 | 57 | bSpriteYOffset: 58 | /* 00-00 */ .byte 0 ; Not used 59 | /* 01-11 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; Not used 60 | /* 12-22 */ .byte 0, 0, 0, 0, 0, 16, 16, 16, 16, 16, 16 ; CPU Tank Y Offsets 61 | 62 | ;=============================================================================== 63 | 64 | gameDataSpritesInit: 65 | ldx #0 ; Initialize X register to 0 (starting index for sprites) 66 | sprite_loop: 67 | stx bLibTemp1 ; Store sprite index in bLibTemp1 (used for sprite number) 68 | 69 | ; Set sprite pattern tile 70 | lda bSpriteTiles, x ; Load sprite tile from array based on X index 71 | cmp #$FF ; Check if tile is the terminator ($FF) 72 | beq sprite_done ; If it's $FF, exit the loop 73 | sta bLibTemp2 ; Store tile in bLibTemp2 for later use 74 | LIBSPRITE_SETFRAME8x16_AAV bLibTemp1, bLibTemp2, 1 ; Set the sprite frame to 8x16 mode 75 | 76 | ; Set sprite palette 77 | lda bSpritePalettes, x ; Load sprite palette from array based on X index 78 | sta bLibTemp2 ; Store palette in bLibTemp2 79 | LIBSPRITE_SETPALETTE_AA bLibTemp1, bLibTemp2 ; Set sprite palette 80 | 81 | ; Set sprite X and Y position 82 | lda bSpriteXPos, x ; Load X position from array 83 | sta bLibTemp2 ; Store X position in bLibTemp2 84 | lda bSpriteYPos, x ; Load Y position from array 85 | sta bLibTemp3 ; Store Y position in bLibTemp3 86 | LIBSPRITE_SETPOSITION_AAA bLibTemp1, bLibTemp2, bLibTemp3 ; Set sprite position (X, Y) 87 | 88 | inx ; Increment X to move to the next sprite 89 | jmp sprite_loop ; Repeat the loop for the next sprite 90 | 91 | sprite_done: 92 | rts ; Return from subroutine -------------------------------------------------------------------------------- /chapters/chapter9/3/gameVariables.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition - gameVariables 3 | ;=============================================================================== 4 | ; Variables 5 | 6 | bLeftPressRequired: .byte 0 7 | bPL1Acceleration: .byte 0 8 | wPL1Velocity: .word 0 9 | wCPUAcceleration: .word 0 10 | wCPUVelocity: .word 0 11 | wCPUTankXPos: .word 0 ; 1st byte = subpixels, 2nd byte = pixels 12 | .byte 0 ; 3rd byte = nametable 13 | wCPUTankScreenXPos: .word 0 ; 1st byte = subpixels, 2nd byte = pixels 14 | .byte 0 ; 3rd byte = nametable -------------------------------------------------------------------------------- /chapters/chapter9/4/chapter9_4.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition chapter9_4 3 | ;=============================================================================== 4 | ; NES ROM Header 5 | .segment "HEADER" 6 | .byte "NES" ; 'NES' identifier - tells the system it's a valid NES ROM 7 | .byte $1a ; Control byte for NES compatibility 8 | .byte 2 ; Number of 16KB PRG-ROM banks (2 x 16KB = 32KB of code) 9 | .byte 1 ; Number of 8KB CHR-ROM banks (1 x 8KB = 8KB of graphics) 10 | .byte 1 | (0 << 4) ; Vertical mirroring 11 | .byte 0 & $f0 ; Using mapper 0 (no extra memory mappers) 12 | .byte 0,0,0,0,0,0,0,0 ; Padding bytes to complete the header 13 | 14 | ;=============================================================================== 15 | ; Zero Page Segment 16 | .segment "ZEROPAGE" 17 | ; The zero page is a special area of memory that's faster for the CPU to access. 18 | ; Variables 19 | .include "../../library/libVariables.asm" 20 | .include "gameVariables.asm" 21 | 22 | ;=============================================================================== 23 | ; RAM Segment 24 | .segment "RAM" 25 | ; Defines a RAM segment to avoid assembler warnings in chapters not using FamiStudio. 26 | 27 | ;=============================================================================== 28 | .segment "CODE" 29 | ; Include library code and definitions 30 | .include "../../library/libDefines.asm" ; This file includes constants like color codes 31 | .include "../../library/libInput.asm" ; This file includes functions to handle player input 32 | .include "../../library/libMath.asm" ; This file includes math-related functions 33 | .include "../../library/libScreen.asm" ; This file includes functions to manage the screen 34 | .include "../../library/libSprite.asm" ; This file includes functions to manage sprites 35 | 36 | ; Include game code 37 | .include "gameTanks.asm" 38 | 39 | ;=============================================================================== 40 | ; Game Initialization 41 | gameMainInit: 42 | LIBSCREEN_INIT ; Initialize the screen and PPU 43 | 44 | ; Load background & sprite palettes 45 | LIBSCREEN_LOADPALETTE_AA BGPALETTE, PaletteBG 46 | LIBSCREEN_LOADPALETTE_AA SPPALETTE, PaletteSP 47 | 48 | ; Load screen data to the left nametable 49 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE0, DesertLeft 50 | 51 | ; Load screen data to the right nametable 52 | LIBSCREEN_LOADNAMETABLE_AA NAMETABLE1, DesertRight 53 | 54 | LIBSPRITE_INIT ; Initialize sprites 55 | LIBSCREEN_SETSPRITESIZE8x16 ; Set sprites to 8x16 pixel mode for larger character sizes 56 | LIBSCREEN_LEFT8PIXELSSPRITESDISABLE ; Disable sprite rendering in the leftmost 8 pixels of the screen 57 | jsr gameDataSpritesInit ; Initialize sprite data for the game 58 | LIBSCREEN_ENABLEPPU ; Enable PPU rendering (sprites, background) 59 | ; note no rts here so that code flows straight into gameMainUpdate 60 | 61 | ;=============================================================================== 62 | ; gameMain Game Update Loop 63 | gameMainUpdate: 64 | lda bFrameReady ; Load the bFrameReady flag 65 | beq gameMainUpdate ; If it's 0, keep waiting (loop) 66 | 67 | ; game code 68 | LIBINPUT_UPDATE ; update input 69 | jsr gameTanksUpdate ; update the tanks 70 | LIBSCREEN_WAITSPRITE0 ; wait for sprite 0 hit 71 | jsr gameMainUpdateBottomScroll 72 | 73 | lda #0 ; Reset bFrameReady to 0 74 | sta bFrameReady 75 | jmp gameMainUpdate ; Infinite loop 76 | 77 | ;=============================================================================== 78 | ; NMI (Vertical Blank) Interrupt Handler 79 | gameMainNMI: 80 | LIBSPRITE_UPDATE ; update sprites oam 81 | jsr gameMainUpdateTopScroll 82 | 83 | lda #1 ; Set bFrameReady flag to 1 84 | sta bFrameReady 85 | rti ; Return from interrupt (used to update graphics once per frame) 86 | 87 | ;=============================================================================== 88 | 89 | gameMainUpdateBottomScroll: 90 | ; Set the scroll for the bottom of the screen 91 | LIBSCREEN_SETSCROLL_AA wBottomScrollX+1, wBottomScrollY+1 92 | rts 93 | 94 | ;=============================================================================== 95 | 96 | gameMainUpdateTopScroll: 97 | ; Set the scroll for the top of the screen 98 | LIBSCREEN_SETSCROLL_AA wTopScrollX+1, wTopScrollY+1 99 | rts 100 | 101 | ;=============================================================================== 102 | ; Data 103 | .include "gameData.asm" 104 | 105 | ;=============================================================================== 106 | ; Character Segment (CHR-ROM) 107 | .segment "CHARS" 108 | ; ROM chars start at PPU address $0000 109 | .incbin "../../content/tankracebackground.chr" 110 | .incbin "../../content/tankracesprites.chr" 111 | 112 | ;=============================================================================== 113 | ; Interrupt Vectors 114 | .segment "VECTORS" ; This section tells the NES where to jump on specific events. 115 | .word gameMainNMI ; NMI (Non-Maskable Interrupt) - triggers once per frame, jump to vblank 116 | .word gameMainInit ; Reset - when the system starts, jump to gameMainInit 117 | .word 0 ; IRQ - Not used in this project -------------------------------------------------------------------------------- /chapters/chapter9/4/gameData.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition gameData 3 | ;=============================================================================== 4 | ; Constants 5 | Sprite0_X = 0 6 | Sprite0_Y = 162 7 | PL1_X = 32 8 | PL1_Y = 187 9 | CPU_X = 32 10 | CPU_Y = 151 11 | 12 | ;=============================================================================== 13 | ; Palette Data 14 | PaletteBG: 15 | .incbin "../../content/tankracebackground.pal" 16 | 17 | PaletteSP: 18 | .incbin "../../content/tankracesprites.pal" 19 | 20 | ;=============================================================================== 21 | ; Screen Data 22 | DesertLeft: 23 | .incbin "../../content/tankracedesertleft.nam" 24 | 25 | DesertRight: 26 | .incbin "../../content/tankracedesertright.nam" 27 | 28 | ;=============================================================================== 29 | ; Sprite Data 30 | 31 | bSpriteTiles: 32 | /* 00-00 */ .byte $FE ; Sprite 0 33 | /* 01-11 */ .byte $02, $04, $06, $08, $0a, $20, $22, $24, $26, $28, $2A ; PL1 Tank 34 | /* 12-22 */ .byte $02, $04, $06, $08, $0a, $20, $22, $24, $26, $28, $2A ; CPU Tank 35 | /* end */ .byte $FF ; end flag to terminate init loop 36 | 37 | bSpritePalettes: 38 | /* 00-00 */ .byte $00 ; Sprite 0 39 | /* 01-11 */ .byte $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01 ; PL1 Tank 40 | /* 12-22 */ .byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 ; CPU Tank 41 | 42 | bSpriteXPos: 43 | /* 00-00 */ .byte Sprite0_X ; Sprite 0 44 | /* 01-11 */ .byte PL1_X+8, PL1_X+16, PL1_X+24, PL1_X+32, PL1_X+40, PL1_X, PL1_X+8, PL1_X+16, PL1_X+24, PL1_X+32, PL1_X+40 ; PL1 Tank 45 | /* 12-22 */ .byte CPU_X+8, CPU_X+16, CPU_X+24, CPU_X+32, CPU_X+40, CPU_X, CPU_X+8, CPU_X+16, CPU_X+24, CPU_X+32, CPU_X+40 ; CPU Tank 46 | 47 | bSpriteYPos: 48 | /* 00-00 */ .byte Sprite0_Y ; Sprite 0 49 | /* 01-11 */ .byte PL1_Y, PL1_Y, PL1_Y, PL1_Y, PL1_Y, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16, PL1_Y+16 ; PL1 Tank 50 | /* 12-22 */ .byte CPU_Y, CPU_Y, CPU_Y, CPU_Y, CPU_Y, CPU_Y+16, CPU_Y+16, CPU_Y+16, CPU_Y+16, CPU_Y+16, CPU_Y+16 ; CPU Tank 51 | 52 | bSpriteXOffset: 53 | /* 00-00 */ .byte 0 ; Not used 54 | /* 01-11 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; Not used 55 | /* 12-22 */ .byte 8, 16, 24, 32, 40, 0, 8, 16, 24, 32, 40 ; CPU Tank X Offsets 56 | 57 | bSpriteYOffset: 58 | /* 00-00 */ .byte 0 ; Not used 59 | /* 01-11 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; Not used 60 | /* 12-22 */ .byte 0, 0, 0, 0, 0, 16, 16, 16, 16, 16, 16 ; CPU Tank Y Offsets 61 | 62 | ;=============================================================================== 63 | 64 | gameDataSpritesInit: 65 | ldx #0 ; Initialize X register to 0 (starting index for sprites) 66 | sprite_loop: 67 | stx bLibTemp1 ; Store sprite index in bLibTemp1 (used for sprite number) 68 | 69 | ; Set sprite pattern tile 70 | lda bSpriteTiles, x ; Load sprite tile from array based on X index 71 | cmp #$FF ; Check if tile is the terminator ($FF) 72 | beq sprite_done ; If it's $FF, exit the loop 73 | sta bLibTemp2 ; Store tile in bLibTemp2 for later use 74 | LIBSPRITE_SETFRAME8x16_AAV bLibTemp1, bLibTemp2, 1 ; Set the sprite frame to 8x16 mode 75 | 76 | ; Set sprite palette 77 | lda bSpritePalettes, x ; Load sprite palette from array based on X index 78 | sta bLibTemp2 ; Store palette in bLibTemp2 79 | LIBSPRITE_SETPALETTE_AA bLibTemp1, bLibTemp2 ; Set sprite palette 80 | 81 | ; Set sprite X and Y position 82 | lda bSpriteXPos, x ; Load X position from array 83 | sta bLibTemp2 ; Store X position in bLibTemp2 84 | lda bSpriteYPos, x ; Load Y position from array 85 | sta bLibTemp3 ; Store Y position in bLibTemp3 86 | LIBSPRITE_SETPOSITION_AAA bLibTemp1, bLibTemp2, bLibTemp3 ; Set sprite position (X, Y) 87 | 88 | inx ; Increment X to move to the next sprite 89 | jmp sprite_loop ; Repeat the loop for the next sprite 90 | 91 | sprite_done: 92 | rts ; Return from subroutine -------------------------------------------------------------------------------- /chapters/chapter9/4/gameVariables.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition - gameVariables 3 | ;=============================================================================== 4 | ; Variables 5 | 6 | bLeftPressRequired: .byte 0 7 | bPL1Acceleration: .byte 0 8 | wPL1Velocity: .word 0 9 | wCPUAcceleration: .word 0 10 | wCPUVelocity: .word 0 11 | wCPUTankXPos: .word 0 ; 1st byte = subpixels, 2nd byte = pixels 12 | .byte 0 ; 3rd byte = nametable 13 | wCPUTankScreenXPos: .word 0 ; 1st byte = subpixels, 2nd byte = pixels 14 | .byte 0 ; 3rd byte = nametable -------------------------------------------------------------------------------- /chapters/content/music.fms: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retrogamedev/nesedition/d70ef377d0dada706e717c92647c236d53136210/chapters/content/music.fms -------------------------------------------------------------------------------- /chapters/content/sfx.fms: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retrogamedev/nesedition/d70ef377d0dada706e717c92647c236d53136210/chapters/content/sfx.fms -------------------------------------------------------------------------------- /chapters/content/sfx.s: -------------------------------------------------------------------------------- 1 | ; This file is for the FamiStudio Sound Engine and was generated by FamiStudio 2 | 3 | 4 | .if FAMISTUDIO_CFG_C_BINDINGS 5 | .export _sounds=sounds 6 | .endif 7 | 8 | sounds: 9 | .word @ntsc 10 | .word @ntsc 11 | @ntsc: 12 | .word @sfx_ntsc_button 13 | .word @sfx_ntsc_countdown 14 | .word @sfx_ntsc_countdown_end 15 | .word @sfx_ntsc_winner 16 | .word @sfx_ntsc_loser 17 | .word @sfx_ntsc_tank 18 | 19 | @sfx_ntsc_button: 20 | .byte $87,$7e,$88,$00,$86,$8f,$89,$f0,$02,$00 21 | @sfx_ntsc_countdown: 22 | .byte $81,$fd,$82,$00,$80,$3f,$89,$f0,$07,$00 23 | @sfx_ntsc_countdown_end: 24 | .byte $81,$fd,$82,$00,$80,$3f,$89,$f0,$28,$00 25 | @sfx_ntsc_winner: 26 | .byte $89,$f0,$02,$87,$7e,$88,$00,$86,$8f,$03,$86,$80,$05,$86,$8f,$30 27 | .byte $00 28 | @sfx_ntsc_loser: 29 | .byte $89,$f0,$02,$81,$fb,$82,$01,$80,$3f,$06,$80,$30,$07,$80,$3f,$28 30 | .byte $00 31 | @sfx_ntsc_tank: 32 | .byte $84,$e0,$85,$07,$83,$37,$89,$f0,$07,$84,$02,$06,$00 33 | 34 | .export sounds 35 | -------------------------------------------------------------------------------- /chapters/content/tankracebackground.chr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retrogamedev/nesedition/d70ef377d0dada706e717c92647c236d53136210/chapters/content/tankracebackground.chr -------------------------------------------------------------------------------- /chapters/content/tankracebackground.pal: -------------------------------------------------------------------------------- 1 |  '1 187 ' -------------------------------------------------------------------------------- /chapters/content/tankracedesertleft.nam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retrogamedev/nesedition/d70ef377d0dada706e717c92647c236d53136210/chapters/content/tankracedesertleft.nam -------------------------------------------------------------------------------- /chapters/content/tankracedesertright.nam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retrogamedev/nesedition/d70ef377d0dada706e717c92647c236d53136210/chapters/content/tankracedesertright.nam -------------------------------------------------------------------------------- /chapters/content/tankracesprites.chr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retrogamedev/nesedition/d70ef377d0dada706e717c92647c236d53136210/chapters/content/tankracesprites.chr -------------------------------------------------------------------------------- /chapters/content/tankracesprites.pal: -------------------------------------------------------------------------------- 1 |  2 |   -------------------------------------------------------------------------------- /chapters/content/tankracetitleleft.nam: -------------------------------------------------------------------------------- 1 | } 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /chapters/content/tankracetitleright.nam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retrogamedev/nesedition/d70ef377d0dada706e717c92647c236d53136210/chapters/content/tankracetitleright.nam -------------------------------------------------------------------------------- /chapters/content/testbackground.chr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retrogamedev/nesedition/d70ef377d0dada706e717c92647c236d53136210/chapters/content/testbackground.chr -------------------------------------------------------------------------------- /chapters/content/testbackground.nam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retrogamedev/nesedition/d70ef377d0dada706e717c92647c236d53136210/chapters/content/testbackground.nam -------------------------------------------------------------------------------- /chapters/content/testbackground.pal: -------------------------------------------------------------------------------- 1 | 0 !2' ) -------------------------------------------------------------------------------- /chapters/library/libDefines.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition libDefines 3 | ;=============================================================================== 4 | .feature c_comments ; Enable the use of C-style comments in the assembler 5 | ; Constants 6 | 7 | ; Colors (PPU color codes) 8 | BLACK = 14 ; Color code for black (background or sprite) 9 | WHITE = 48 ; Color code for white 10 | RED = 22 ; Color code for red 11 | CYAN = 28 ; Color code for cyan 12 | PURPLE = 36 ; Color code for purple 13 | GREEN = 43 ; Color code for green 14 | BLUE = 17 ; Color code for blue 15 | YELLOW = 41 ; Color code for yellow 16 | GRAY = 16 ; Color code for gray 17 | 18 | ; OAM (Object Attribute Memory) Bit Masks for sprite properties 19 | OAM_PALETTE = %00000011 ; Bits 0-1: Sprite palette selection (values 0-3) 20 | OAM_BEHIND = %00100000 ; Bit 5: Sprite behind the background flag (0 = in front, 1 = behind) 21 | OAM_FLIP_H = %01000000 ; Bit 6: Horizontal flip (0 = normal, 1 = flipped) 22 | OAM_FLIP_V = %10000000 ; Bit 7: Vertical flip (0 = normal, 1 = flipped) 23 | 24 | ; CPU Memory Locations 25 | ; $0000-$00FF: 256 bytes Zero Page (Used in libVariables.asm & gameVariables.asm) 26 | SPRITERAM = $0200 ; $0200-$02FF: 256 bytes of Sprite RAM (buffer for OAM data, copied during VBlank) 27 | ; $0300-$07FF: 1280 bytes free CPU RAM for general use 28 | ; ==================================== 29 | ; CPU RAM is 2KB in total 30 | 31 | ; PPU Registers (Memory-mapped I/O addresses) 32 | PPUCTRL = $2000 ; PPU Control register: Controls NMI enable, sprite size, background/sprite tile select, etc. 33 | PPUMASK = $2001 ; PPU Mask register: Enables/disables rendering, grayscale, and visibility of sprites/backgrounds. 34 | PPUSTATUS = $2002 ; PPU Status register: VBlank flag, sprite 0 hit, sprite overflow status. 35 | OAMADDR = $2003 ; OAM Address register: Set OAM address for sprite data reads/writes. 36 | OAMDATA = $2004 ; OAM Data register: Read/write sprite data to/from OAM. 37 | PPUSCROLL = $2005 ; PPU Scroll register: Fine X/Y scrolling (two consecutive writes - X first, then Y). 38 | PPUADDR = $2006 ; PPU Address register: Set VRAM address for read/write operations (two consecutive writes - MSB first, then LSB). 39 | PPUDATA = $2007 ; PPU Data register: Read/write data to/from VRAM at the address set by PPUADDR. 40 | OAMDMA = $4014 ; OAM DMA: DMA transfer of 256 bytes from CPU RAM to OAM (usually for sprite data). 41 | 42 | 43 | ; APU (Audio Processing Unit) Registers 44 | DMCFREQ = $4010 ; DMC (Delta Modulation Channel) Frequency Control. 45 | APUFRAME = $4017 ; APU Frame Counter: Controls frame interrupts and sequence for audio timing. 46 | 47 | ; JoyPad Registers (Controller input) 48 | JOYPAD1 = $4016 ; Player 1 input register: Write to latch inputs, read to get Player 1's controller state. 49 | JOYPAD2 = $4017 ; Player 2 input register: Read-only for Player 2's controller input. 50 | 51 | ; PPU Memory Locations (Nametable and Attribute Table addresses) 52 | 53 | ; Nametables (960 bytes each, arranged in a 2x2 grid for scrolling) 54 | NAMETABLE0 = $2000 ; Nametable 0 (top-left) 55 | NAMETABLE1 = $2400 ; Nametable 1 (top-right) 56 | NAMETABLE2 = $2800 ; Nametable 2 (bottom-left) 57 | NAMETABLE3 = $2C00 ; Nametable 3 (bottom-right) 58 | 59 | ; Attribute tables (64 bytes each, managing 16x16 pixel attribute blocks) 60 | ATTRTABLE0 = $23C0 ; Attribute table for Nametable 0 61 | ATTRTABLE1 = $27C0 ; Attribute table for Nametable 1 62 | ATTRTABLE2 = $2BC0 ; Attribute table for Nametable 2 63 | ATTRTABLE3 = $2FC0 ; Attribute table for Nametable 3 64 | 65 | ; Palette Memory Locations (Background and Sprite palettes) 66 | BGPALETTE = $3F00 ; Background palette memory start address. 67 | SPPALETTE = $3F10 ; Sprite palette memory start address. 68 | 69 | ; FamiStudio Sound Engine Configuration 70 | .define FAMISTUDIO_CA65_ZP_SEGMENT ZEROPAGE ; FamiStudio uses the Zero Page segment. 71 | .define FAMISTUDIO_CA65_RAM_SEGMENT RAM ; Define RAM segment for FamiStudio data. 72 | .define FAMISTUDIO_CA65_CODE_SEGMENT CODE ; Define Code segment for FamiStudio. 73 | 74 | FAMISTUDIO_CFG_EXTERNAL = 1 ; Enable external sound engine support. 75 | FAMISTUDIO_CFG_DPCM_SUPPORT = 1 ; Enable support for DPCM (Delta Pulse Code Modulation) samples. 76 | FAMISTUDIO_CFG_SFX_SUPPORT = 1 ; Enable support for sound effects. 77 | FAMISTUDIO_CFG_SFX_STREAMS = 2 ; Number of sound effect channels. 78 | FAMISTUDIO_CFG_EQUALIZER = 1 ; Enable equalizer effect. 79 | FAMISTUDIO_USE_VOLUME_TRACK = 1 ; Enable volume effects. 80 | FAMISTUDIO_USE_PITCH_TRACK = 1 ; Enable pitch effects. 81 | FAMISTUDIO_USE_VOLUME_SLIDES = 1 ; Enable volume slides. 82 | FAMISTUDIO_USE_SLIDE_NOTES = 1 ; Enable sliding notes (pitch bends). 83 | FAMISTUDIO_USE_VIBRATO = 1 ; Enable vibrato effect. 84 | FAMISTUDIO_USE_ARPEGGIO = 1 ; Enable arpeggio effect. 85 | FAMISTUDIO_CFG_SMOOTH_VIBRATO = 1 ; Enable smooth vibrato transitions. 86 | FAMISTUDIO_USE_RELEASE_NOTES = 1 ; Enable support for release notes (sustain/release). 87 | FAMISTUDIO_DPCM_OFF = $e000 ; DPCM sample memory starting address. 88 | FAMISTUDIO_EXP_VRC6 = 1 ; Enable support for VRC6 expansion audio (Konami expansion chip). 89 | FAMISTUDIO_USE_DUTYCYCLE_EFFECT = 1 ; Enable duty cycle effect for sound manipulation. -------------------------------------------------------------------------------- /chapters/library/libInput.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition libInput 3 | ;=============================================================================== 4 | ; Constants 5 | 6 | ; Port Masks (for player input buttons) 7 | GameportRightMask = %00000001 ; Right D-pad 8 | GameportLeftMask = %00000010 ; Left D-pad 9 | GameportDownMask = %00000100 ; Down D-pad 10 | GameportUpMask = %00001000 ; Up D-pad 11 | GameportStartMask = %00010000 ; Start button 12 | GameportSelectMask = %00100000 ; Select button 13 | GameportBMask = %01000000 ; B button (fire) 14 | GameportAMask = %10000000 ; A button (jump/confirm) 15 | 16 | ;=============================================================================== 17 | ; Macros 18 | 19 | .macro LIBINPUT_GETANY 20 | lda bLibButtonsLast ; Load the previous button state 21 | bne @skip ; If any button was pressed last frame, skip 22 | 23 | lda bLibButtons ; Load the current button state 24 | jmp @done ; Return the current state 25 | @skip: 26 | lda #0 ; If no buttons were pressed, return 0 27 | @done: 28 | .endmacro 29 | 30 | ;=============================================================================== 31 | 32 | .macro LIBINPUT_GETBLIP_V bPortMask 33 | lda bLibButtonsLast ; Load the previous button state 34 | and #bPortMask ; Mask the previous state 35 | bne @skip ; Skip if the button was pressed last frame 36 | 37 | lda bLibButtons ; Load the current button state 38 | and #bPortMask ; Mask the current state 39 | jmp @done ; If pressed, skip to done 40 | @skip: 41 | lda #0 ; Otherwise, set A to 0 (no button press) 42 | @done: 43 | .endmacro 44 | 45 | ;=============================================================================== 46 | 47 | .macro LIBINPUT_GET_V bPortMask 48 | lda bLibButtons ; Load current button state into A 49 | and #bPortMask ; Apply mask to isolate the desired button state 50 | .endmacro 51 | 52 | ;=============================================================================== 53 | 54 | .macro LIBINPUT_UPDATE 55 | lda bLibButtons ; Store the current state into the last state 56 | sta bLibButtonsLast 57 | 58 | lda #$01 ; Set the strobe bit to reload button states 59 | sta JOYPAD1 ; Begin reading from JOYPAD1 60 | sta bLibButtons ; Store the state of the A button in bLibButtons 61 | lsr ; Clear A (strobe off) 62 | sta JOYPAD1 ; Stop button reloading 63 | 64 | @loop: 65 | lda JOYPAD1 ; Read the current button state 66 | lsr ; Shift bit 0 into the carry flag 67 | rol bLibButtons ; Rotate carry flag into bLibButtons (bit 0) 68 | bcc @loop ; Repeat for all 8 buttons 69 | .endmacro -------------------------------------------------------------------------------- /chapters/library/libSound.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition libSound 3 | ;=============================================================================== 4 | ; Includes 5 | .include "famistudio_ca65.s" 6 | 7 | ;=============================================================================== 8 | ; Macros 9 | 10 | .macro LIBSOUND_INIT music, sfx 11 | ; Init music 12 | ldx #music 14 | lda #1 ; NTSC mode 15 | jsr famistudio_init 16 | 17 | ; Init SFX 18 | ldx #sfx 20 | jsr famistudio_sfx_init 21 | .endmacro 22 | 23 | ;=============================================================================== 24 | 25 | .macro LIBSOUND_MUSICPAUSE pause 26 | lda #pause 27 | jsr famistudio_music_pause 28 | .endmacro 29 | 30 | ;=============================================================================== 31 | 32 | .macro LIBSOUND_MUSICPLAY track 33 | lda #track 34 | jsr famistudio_music_play 35 | .endmacro 36 | 37 | ;=============================================================================== 38 | 39 | .macro LIBSOUND_MUSICSTOP 40 | jsr famistudio_music_stop 41 | .endmacro 42 | 43 | ;=============================================================================== 44 | 45 | .macro LIBSOUND_SFXPLAY track, channel 46 | lda #track 47 | ldx #channel 48 | jsr famistudio_sfx_play 49 | .endmacro -------------------------------------------------------------------------------- /chapters/library/libSprite.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition libSprite 3 | ;=============================================================================== 4 | ; Macros 5 | 6 | .macro LIBSPRITE_INIT 7 | ; Set all 64 sprites' Y positions to 255 (off-screen) 8 | lda #255 9 | sta bLibTemp2 10 | ldx #0 11 | @loop: 12 | stx bLibTemp1 13 | LIBSPRITE_SETPOSITIONY_AA bLibTemp1, bLibTemp2 14 | inx 15 | cpx #64 16 | bne @loop 17 | .endmacro 18 | 19 | ;=============================================================================== 20 | 21 | .macro LIBSPRITE_SETFRAME8x16_AAV bSprite, bIndex, bPatternTable 22 | ; Every 4th byte from 1 23 | lda bSprite 24 | asl ; Multiply by 2 25 | asl ; Multiply by 4 26 | tay 27 | iny ; +1 28 | lda bIndex ; Load bIndex 29 | ora #bPatternTable 30 | sta SPRITERAM,y ; Set the calculated address to bIndex 31 | .endmacro 32 | 33 | ;=============================================================================== 34 | 35 | .macro LIBSPRITE_SETFRAME8x16_VVV bSprite, bIndex, bPatternTable 36 | ; Every 4th byte from 1 37 | lda #bSprite 38 | asl ; Multiply by 2 39 | asl ; Multiply by 4 40 | tay 41 | iny ; +1 42 | lda #bIndex ; Load bIndex 43 | ora #bPatternTable 44 | sta SPRITERAM,y ; Set the calculated address to bIndex 45 | .endmacro 46 | 47 | ;=============================================================================== 48 | 49 | .macro LIBSPRITE_SETPALETTE_AA bSprite, bPalette 50 | ; Every 4th byte from 2 51 | lda bSprite 52 | asl ; Multiply by 2 53 | asl ; Multiply by 4 54 | tay 55 | iny ; +1 56 | iny ; +2 57 | lda SPRITERAM,y ; Load from calculated address 58 | and #%11111100 ; Clear bits 0 & 1 59 | ora bPalette ; Merge with bPalette 60 | sta SPRITERAM,y ; Set back to calculated address 61 | .endmacro 62 | 63 | ;=============================================================================== 64 | 65 | .macro LIBSPRITE_SETPALETTE_VV bSprite, bPalette 66 | ; Every 4th byte from 2 67 | lda #bSprite 68 | asl ; Multiply by 2 69 | asl ; Multiply by 4 70 | tay 71 | iny ; +1 72 | iny ; +2 73 | lda SPRITERAM,y ; Load from calculated address 74 | and #%11111100 ; Clear bits 0 & 1 75 | ora #bPalette ; Merge with bPalette 76 | sta SPRITERAM,y ; Set back to calculated address 77 | .endmacro 78 | 79 | ;=============================================================================== 80 | 81 | .macro LIBSPRITE_SETPOSITIONX_AA bSprite, bXPos 82 | ; Every 4th byte from 3 83 | lda bSprite 84 | asl ; Multiply by 2 85 | asl ; Multiply by 4 86 | tay 87 | iny ; +1 88 | iny ; +2 89 | iny ; +3 90 | lda bXPos ; Load X position 91 | sta SPRITERAM,y ; Set to calculated address 92 | .endmacro 93 | 94 | ;=============================================================================== 95 | 96 | .macro LIBSPRITE_SETPOSITIONX_VV bSprite, bXPos 97 | ; Every 4th byte from 3 98 | lda #bSprite 99 | asl ; Multiply by 2 100 | asl ; Multiply by 4 101 | tay 102 | iny ; +1 103 | iny ; +2 104 | iny ; +3 105 | lda #bXPos ; Load X position 106 | sta SPRITERAM,y ; Set to calculated address 107 | .endmacro 108 | 109 | ;=============================================================================== 110 | 111 | .macro LIBSPRITE_SETPOSITIONY_AA bSprite, bYPos 112 | ; Every 4th byte from 0 113 | lda bSprite 114 | asl ; Multiply by 2 115 | asl ; Multiply by 4 116 | tay 117 | lda bYPos ; Load Y position 118 | sta SPRITERAM,y ; Set to calculated address 119 | .endmacro 120 | 121 | ;=============================================================================== 122 | 123 | .macro LIBSPRITE_SETPOSITIONY_VV bSprite, bYPos 124 | ; Every 4th byte from 0 125 | lda #bSprite 126 | asl ; Multiply by 2 127 | asl ; Multiply by 4 128 | tay 129 | lda #bYPos ; Load Y position 130 | sta SPRITERAM,y ; Set to calculated address 131 | .endmacro 132 | 133 | ;=============================================================================== 134 | 135 | .macro LIBSPRITE_SETPOSITION_AAA bSprite, bXPos, bYPos 136 | ; Every 4th byte from 0 (Y position) 137 | lda bSprite 138 | asl ; Multiply by 2 139 | asl ; Multiply by 4 140 | tay 141 | lda bYPos ; Load Y position 142 | sta SPRITERAM,y ; Set to calculated address 143 | 144 | ; Every 4th byte from 3 (X position) 145 | iny ; +1 146 | iny ; +2 147 | iny ; +3 148 | lda bXPos ; Load X position 149 | sta SPRITERAM,y ; Set to calculated address 150 | .endmacro 151 | 152 | ;=============================================================================== 153 | 154 | .macro LIBSPRITE_UPDATE 155 | lda #>SPRITERAM ; Load high byte of SPRITERAM address 156 | sta OAMDMA ; Trigger OAM DMA to copy sprite data 157 | .endmacro -------------------------------------------------------------------------------- /chapters/library/libVariables.asm: -------------------------------------------------------------------------------- 1 | ;=============================================================================== 2 | ; RetroGameDev NES Edition libVariables 3 | ;=============================================================================== 4 | ; Variables 5 | 6 | ; Scroll Variables 7 | wBottomScrollX: .word 0 ; 1st byte = subpixels, 2nd byte = pixels 8 | .byte 0 ; 3rd byte = nametable 9 | wBottomScrollY: .word 0 10 | 11 | wTopScrollX: .word 0 ; 1st byte = subpixels, 2nd byte = pixels 12 | .byte 0 ; 3rd byte = nametable 13 | wTopScrollY: .word 0 14 | 15 | ; Render Flags 16 | bFrameReady: .byte 0 17 | bLoadRequested: .byte 0 18 | bLoadMenu: .byte 0 19 | bLoadDesert: .byte 0 20 | 21 | ; PPU Register Shadows (used to mirror and track changes to the PPU registers) 22 | PPUCTRL_SHADOW: .byte 0 23 | PPUMASK_SHADOW: .byte 0 24 | 25 | ; Controller Variables 26 | bLibButtons: .byte 0 ; Current state of controller buttons 27 | bLibButtonsLast: .byte 0 ; Previous state of controller buttons 28 | 29 | ; Temporary Variables (used for various calculations) 30 | bLibTemp1: .byte 0 ; General purpose temp variable (8-bit) 31 | bLibTemp2: .byte 0 ; General purpose temp variable (8-bit) 32 | bLibTemp3: .byte 0 ; General purpose temp variable (8-bit) 33 | bLibTemp4: .byte 0 ; General purpose temp variable (8-bit) 34 | wLibTemp1: .word 0 ; General purpose temp variable (16-bit) 35 | wLibTemp2: .word 0 ; General purpose temp variable (16-bit) -------------------------------------------------------------------------------- /nes.cfg: -------------------------------------------------------------------------------- 1 | MEMORY { 2 | ZP: start = $0000, size = $0100, file = ""; 3 | HEAD: start = $0000, size = $0010; 4 | RM: start = $0300, size = $0400, type = rw, file = ""; 5 | ROM0: start = $8000, size = $7FFA, fill = yes; 6 | ROMV: start = $FFFA, size = $0006, fill = yes; 7 | ROM2: start = $0000, size = $2000, fill = yes; 8 | } 9 | SEGMENTS { 10 | ZEROPAGE: load = ZP, type = zp; 11 | HEADER: load = HEAD, type = ro; 12 | RAM: load = RM, type = bss; 13 | CODE: load = ROM0, type = ro; 14 | VECTORS: load = ROMV, type = ro; 15 | CHARS: load = ROM2, type = ro; 16 | } -------------------------------------------------------------------------------- /tankrace.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retrogamedev/nesedition/d70ef377d0dada706e717c92647c236d53136210/tankrace.nes -------------------------------------------------------------------------------- /tools/cc65/bin/ar65: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retrogamedev/nesedition/d70ef377d0dada706e717c92647c236d53136210/tools/cc65/bin/ar65 -------------------------------------------------------------------------------- /tools/cc65/bin/ca65: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retrogamedev/nesedition/d70ef377d0dada706e717c92647c236d53136210/tools/cc65/bin/ca65 -------------------------------------------------------------------------------- /tools/cc65/bin/cc65: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retrogamedev/nesedition/d70ef377d0dada706e717c92647c236d53136210/tools/cc65/bin/cc65 -------------------------------------------------------------------------------- /tools/cc65/bin/chrcvt65: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retrogamedev/nesedition/d70ef377d0dada706e717c92647c236d53136210/tools/cc65/bin/chrcvt65 -------------------------------------------------------------------------------- /tools/cc65/bin/cl65: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retrogamedev/nesedition/d70ef377d0dada706e717c92647c236d53136210/tools/cc65/bin/cl65 -------------------------------------------------------------------------------- /tools/cc65/bin/co65: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retrogamedev/nesedition/d70ef377d0dada706e717c92647c236d53136210/tools/cc65/bin/co65 -------------------------------------------------------------------------------- /tools/cc65/bin/da65: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retrogamedev/nesedition/d70ef377d0dada706e717c92647c236d53136210/tools/cc65/bin/da65 -------------------------------------------------------------------------------- /tools/cc65/bin/grc65: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retrogamedev/nesedition/d70ef377d0dada706e717c92647c236d53136210/tools/cc65/bin/grc65 -------------------------------------------------------------------------------- /tools/cc65/bin/ld65: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retrogamedev/nesedition/d70ef377d0dada706e717c92647c236d53136210/tools/cc65/bin/ld65 -------------------------------------------------------------------------------- /tools/cc65/bin/od65: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retrogamedev/nesedition/d70ef377d0dada706e717c92647c236d53136210/tools/cc65/bin/od65 -------------------------------------------------------------------------------- /tools/cc65/bin/sim65: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retrogamedev/nesedition/d70ef377d0dada706e717c92647c236d53136210/tools/cc65/bin/sim65 -------------------------------------------------------------------------------- /tools/cc65/bin/sp65: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retrogamedev/nesedition/d70ef377d0dada706e717c92647c236d53136210/tools/cc65/bin/sp65 -------------------------------------------------------------------------------- /tools/cc65/lib/nes.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retrogamedev/nesedition/d70ef377d0dada706e717c92647c236d53136210/tools/cc65/lib/nes.lib -------------------------------------------------------------------------------- /tools/lbl2nl/lbl2nl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retrogamedev/nesedition/d70ef377d0dada706e717c92647c236d53136210/tools/lbl2nl/lbl2nl -------------------------------------------------------------------------------- /tools/lbl2nl/lbl2nl.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define MAX_ADDRESSES 10000 // Adjust as needed for large files 7 | 8 | // Global list of processed addresses 9 | char processedAddresses[MAX_ADDRESSES][16]; 10 | int addressCount = 0; 11 | 12 | // Helper to check if an address is already processed 13 | bool isAddressProcessed(const char* address) 14 | { 15 | for (int i = 0; i < addressCount; i++) 16 | { 17 | if (strcmp(processedAddresses[i], address) == 0) 18 | { 19 | return true; // Address already processed 20 | } 21 | } 22 | return false; 23 | } 24 | 25 | // Add a new address to the processed list 26 | void addAddress(const char* address) 27 | { 28 | if (addressCount < MAX_ADDRESSES) 29 | { 30 | strncpy(processedAddresses[addressCount], address, sizeof(processedAddresses[addressCount]) - 1); 31 | processedAddresses[addressCount][sizeof(processedAddresses[addressCount]) - 1] = '\0'; // Null-terminate 32 | addressCount++; 33 | } 34 | } 35 | 36 | void convertLine(char* line, FILE* outputFile) 37 | { 38 | char* context; 39 | char* prefix = strtok_r(line, " ", &context); 40 | char* address = strtok_r(NULL, " ", &context); 41 | char* label = strtok_r(NULL, " \n\t", &context); 42 | 43 | if (prefix != NULL && address != NULL && label != NULL) 44 | { 45 | // Remove leading dot and sanitize the label 46 | if (label[0] == '.') 47 | { 48 | label++; 49 | } 50 | 51 | // Check if the address is already processed 52 | if (!isAddressProcessed(address)) 53 | { 54 | fprintf(outputFile, "$%s#%s#\n", address, label); 55 | addAddress(address); // Mark the address as processed 56 | } 57 | else 58 | { 59 | //printf("Skipping duplicate address: %s (label: %s)\n", address, label); 60 | } 61 | } 62 | else 63 | { 64 | printf("Skipping line, couldn't parse: prefix=%s, address=%s, label=%s\n", 65 | prefix ? prefix : "NULL", 66 | address ? address : "NULL", 67 | label ? label : "NULL"); 68 | } 69 | } 70 | 71 | int main(int argc, char* argv[]) 72 | { 73 | if (argc < 3) 74 | { 75 | fprintf(stderr, "Usage: %s inputfile outputfile\n", argv[0]); 76 | return 1; 77 | } 78 | 79 | FILE* inputFile = fopen(argv[1], "r"); 80 | if (inputFile == 0) 81 | { 82 | perror("Error opening input file"); 83 | return 1; 84 | } 85 | 86 | FILE* outputFile = fopen(argv[2], "w"); 87 | if (outputFile == 0) 88 | { 89 | perror("Error opening output file"); 90 | fclose(inputFile); 91 | return 1; 92 | } 93 | 94 | char line[256]; // Adjust the buffer size as needed 95 | 96 | while (fgets(line, sizeof(line), inputFile)) 97 | { 98 | // We expect every line to start with "al" 99 | if (strstr(line, "al") == line) 100 | { 101 | convertLine(line, outputFile); 102 | } 103 | else 104 | { 105 | // Debugging output to see which lines are being skipped 106 | printf("Skipping line (not a valid format): %s", line); 107 | } 108 | } 109 | 110 | fclose(inputFile); 111 | fclose(outputFile); 112 | 113 | return 0; 114 | } -------------------------------------------------------------------------------- /tools/lbl2nl/readme: -------------------------------------------------------------------------------- 1 | lbl2nl converts the output .lbl file from the cl65 assembler to a 2 | .nes.0.nl file which is the labels format that the fceux debugger requires. 3 | It's automatically run as part of the tasks.json build process. 4 | 5 | To build from the terminal window use: 6 | gcc -o ./tools/lbl2nl/lbl2nl ./tools/lbl2nl/lbl2nl.cpp 7 | 8 | Then to give access in the codespaces environment use: 9 | chmod +x ./tools/lbl2nl/lbl2nl --------------------------------------------------------------------------------