Standardizes build process and adds OVERVIEW.md for each
This commit is contained in:
1 parent
25423d3828
commit
a29b03b00b
135 files changed
+26562
-420
No files matched your search
@@ -0,0 +1,367 @@
|
||||
# Tennis (JU) — Game Flow from Power-On
|
||||
|
||||
This document describes the execution flow of the disassembled Tennis (JU) PRG bank (16 KB, mapped at CPU **$C000–$FFFF**), starting from power-on. It is based entirely on `copilot/prg.s` in the repository. The rebuild is verified **byte-identical** to the original `prg.bin`.
|
||||
|
||||
---
|
||||
|
||||
## 1. ROM and Memory Layout
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Format | iNES / NES 2.0 |
|
||||
| PRG | 1 × 16 KB (mapped at CPU $C000–$FFFF) |
|
||||
| CHR | 1 × 8 KB |
|
||||
| Mirroring | Vertical |
|
||||
| PRG bank start | CPU $C000 |
|
||||
| Source file | `copilot/prg.s` (9,268 lines) |
|
||||
| Output | `reconstructed_prg.bin` (16,384 bytes) |
|
||||
|
||||
### Interrupt vectors (CPU $FFFA–$FFFF)
|
||||
|
||||
| Vector | Address | Handler |
|
||||
|--------|---------|---------|
|
||||
| NMI | $C07D | `NmiHandler` |
|
||||
| RESET | $C000 | `Reset` |
|
||||
| IRQ / BRK | $C000 | `Reset` (reused — unused) |
|
||||
|
||||
---
|
||||
|
||||
## 2. Build Status
|
||||
|
||||
`make` in `copilot/` assembles `prg.s` with `ca65`, links with `ld65`, and verifies against `prg.bin`.
|
||||
|
||||
**Verification result: BYTE-IDENTICAL** — `reconstructed_prg.bin` matches `prg.bin` exactly (all 16,384 bytes), and `prg.bin` matches the PRG bank of `Tennis (JU).nes`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Reset — CPU $FFFC → $C000
|
||||
|
||||
**`Reset`** ($C000)
|
||||
|
||||
On power-on or reset:
|
||||
|
||||
1. `cld` — clear decimal mode.
|
||||
2. `sei` — disable maskable interrupts.
|
||||
3. **Wait for VBlank** — poll `PPUSTATUS` bit 7 (`@Flow_C002`).
|
||||
4. `PPUCTRL=$00`, `PPUMASK=$00` — disable rendering and NMI.
|
||||
5. Stack pointer = $FF.
|
||||
6. `X = FrameCounter` ($27), then `Y=$06`, `WorkPtrHi=$02`, `WorkPtrLo=$00`, `A=$00`.
|
||||
7. **Clear RAM** — `sta (WorkPtrLo),y` loop decrementing `WorkPtrHi` from $02 down, effectively clearing $0000–$02FF.
|
||||
8. `X` is 0 at this point; if non-zero (it never is on entry), skip; else `X=$5F`.
|
||||
9. `stx $0500` — set $0500 to $5F (or 0).
|
||||
10. `jsr ClearNametables` ($C195) — clear nametable memory.
|
||||
11. `jsr HideAllSprites` ($C1A5) — hide all sprites by writing $F4 to OAM.
|
||||
12. `PPUSCROLL=$00` twice; `GameActiveFlag=$01`.
|
||||
13. `APUSTATUS=$0F` — enable all four APU channels.
|
||||
14. `PPUCTRL=$90`, `PpuCtrlShadow=$90` — enable NMI, $2000 base.
|
||||
15. `PpuMaskShadow=$06`.
|
||||
16. Falls into **`MainLoop`** ($C04F) — the permanent foreground loop.
|
||||
|
||||
---
|
||||
|
||||
## 4. Main Loop — `MainLoop` ($C04F)
|
||||
|
||||
After reset, control enters the infinite foreground loop:
|
||||
|
||||
```
|
||||
MainLoop ($C04F):
|
||||
NmiWorkComplete ($30) = $00
|
||||
if GameActiveFlag ($38) != 0:
|
||||
if GameStateStep ($3E) == 1:
|
||||
jsr RunAudioEngine ($FFF7)
|
||||
Flow_C060:
|
||||
jsr UpdateTimers ($C375)
|
||||
jsr UpdateGameState ($C0AA)
|
||||
NmiUpdatesEnabled ($32) = $01
|
||||
Flow_C06A:
|
||||
if NmiWorkComplete ($30) != 0:
|
||||
FrameCounter ($27)++
|
||||
NmiUpdatesEnabled ($32) = $00
|
||||
jmp MainLoop
|
||||
else:
|
||||
jsr RollRng ($C456)
|
||||
jmp Flow_C06A
|
||||
```
|
||||
|
||||
The main loop is **NMI-synchronized**: it waits for `NmiWorkComplete` ($30) to become non-zero (set by NMI each frame), then advances the frame counter, clears the update-enabled flag, and loops. If NMI work is not complete, it rolls the RNG and retries.
|
||||
|
||||
---
|
||||
|
||||
## 5. NMI / Frame Heartbeat — `NmiHandler` ($C07D)
|
||||
|
||||
The NMI fires once per frame:
|
||||
|
||||
1. **Save registers** (A, X, Y on stack).
|
||||
2. **OAM DMA**: write $00 to OAMADDR ($2003), then $02 to OAMDMA ($4014) — copy $0200–$02FF to sprite RAM.
|
||||
3. If `NmiUpdatesEnabled` ($32) == 0, skip to `@Flow_C0A0`.
|
||||
4. Otherwise:
|
||||
- `jsr WritePalette` ($C1B5) — conditional palette upload based on `PaletteUpdateMode` ($3C).
|
||||
- `jsr WritePpuBuffer` ($C2EC) — flush pending PPU command buffer at $0590.
|
||||
- `jsr SetScroll` ($C3FD) — write scroll from $1E/$1F to PPUSCROLL.
|
||||
- `jsr ReadControllers` ($C2A4) — read both controller ports.
|
||||
5. `NmiWorkComplete ($30) = $01` — signal main loop that the frame update is done.
|
||||
6. `NmiTriggerFlag ($33) = $01`.
|
||||
7. Restore registers, `rti`.
|
||||
|
||||
The label `IrqHandler` appears at the same `rti` address — IRQ/BRK vector points here, but it's unused (same as Reset).
|
||||
|
||||
---
|
||||
|
||||
## 5. Game State Machine — `UpdateGameState` ($C0AA)
|
||||
|
||||
Called each main-loop iteration:
|
||||
|
||||
```
|
||||
UpdateGameState ($C0AA):
|
||||
if GameActiveFlag ($38) == 0:
|
||||
Flow_C0B1:
|
||||
if ControllerHeld ($1A) & START ($10):
|
||||
if StartButtonLatch ($34) != 0 → skip
|
||||
else: StartButtonLatch=$01
|
||||
Y = GameState ($3D)
|
||||
if GameState == 7 → Flow_C0EA
|
||||
if GameState == 4 → Flow_C0CC
|
||||
if GameState == 6 → skip
|
||||
else → Flow_C0FC
|
||||
Flow_C0FC:
|
||||
DispatchInlineAddressTable ($C38B) with GameState index
|
||||
→ GameStateHandlerTable ($C101)
|
||||
else (GameActiveFlag == 0):
|
||||
Flow_C119:
|
||||
if ControllerHeld & $30 (START|SELECT):
|
||||
if bit $10 set → Flow_C134
|
||||
else: GameActiveFlag=$00, GameState=$00, PendingStateFlag=$00
|
||||
jsr MatchRoutine_CBCB, jsr DisableRendering
|
||||
GameMode=$SelectedGameMode ($E0), rts
|
||||
Flow_C134: (complex state-transition logic with StateTransitionFlag, GameMode, etc.)
|
||||
Flow_C180:
|
||||
GameStateStep ($3E) → DispatchInlineAddressTable → GameStateStepHandlerTable ($C185)
|
||||
```
|
||||
|
||||
### `MainStateHandlerTable` ($C101) — indexed by `GameState` ($3D) as a byte offset (even offsets $00–$16):
|
||||
|
||||
| Offset | Handler | Description |
|
||||
|--------|---------|-------------|
|
||||
| $00 | `GameState00_InitializeMatch` ($C70A) | Match initialization |
|
||||
| $02 | `GameState01_WaitForServe` ($C797) | Wait for serve input |
|
||||
| $04 | `GameState02_PrepareRally` ($C7B7) | Prepare rally |
|
||||
| $06 | `GameState03_RunRally` ($C7C6) | Run rally |
|
||||
| $08 | `GameState04_PointEnded` ($C907) | Point ended |
|
||||
| $0A | `GameState05_UpdateScore` ($CA07) | Update score |
|
||||
| $0C | `GameState06_AdvanceGame` ($C9DE) | Advance game |
|
||||
| $0E | `GameState07_PauseOrTransition` ($C42B) | Pause/transition (RTS) |
|
||||
| $10 | `GameState08_WaitForTimer` ($CA28) | Wait for timer |
|
||||
| $12 | `GameState09_RestorePreviousState` ($CA31) | Restore state |
|
||||
| $14 | `GameState0A_ResumePreviousState` ($CA3A) | Resume state |
|
||||
| $16 | `GameState0B_FinishTransition` ($CA5B) | Finish transition |
|
||||
|
||||
### `GameStateStepHandlerTable` ($C185) — indexed by `GameStateStep` ($3E):
|
||||
|
||||
| Offset | Handler |
|
||||
|--------|---------|
|
||||
| $00 | `ModeStep00_DrawSelectionScreen` ($CA87) |
|
||||
| $02 | `ModeStep01_Countdown` ($CAC9) |
|
||||
| $04 | `ModeStep02_InitializeSelection` ($CADD) |
|
||||
| $06 | `ModeStep03_UpdateSelection` ($CAE4) |
|
||||
| $08 | `ModeStep04_ConfirmSelection` ($CAEE) |
|
||||
| $0A | `ModeStep05_StartDemoOrMatch` ($CAF4) |
|
||||
| $0C | `ModeStep06_CompleteSetup` ($CC04) |
|
||||
|
||||
### Key state handlers:
|
||||
|
||||
- **`GameState00_InitializeMatch`** ($C70A): First-time init — clear nametable, render routine, palette mode, set `PendingStateFlag`. Subsequent calls advance through sub-states ($87/$88 counter), render sprites, enable rendering, set `GameState=$07`, call `MatchRoutine_CBCB`.
|
||||
- **`GameState01_WaitForServe`** ($C797): Disable rendering, clear nametable, render court + score, zero point winners, clear animation flags, set ball trajectory flags, advance `GameState`.
|
||||
- **`GameState02_PrepareRally`** ($C7B7): Enable rendering, clear PPU mask shadow bit 4, hide sprites, render routine, advance `GameState`.
|
||||
- **`GameState03_RunRally`** ($C7C6): Render routine, set `PpuBufferReady`, `MatchRoutine_C7E7` (copy initial object state), `PhysicsRoutine_D7E1`, handle serving player, `EngineRoutine_C436`, advance `GameState`.
|
||||
|
||||
---
|
||||
|
||||
## 6. NMI-Driven Per-Frame Work
|
||||
|
||||
The NMI handler (called every VBlank) performs:
|
||||
|
||||
| Step | Routine | Address | Purpose |
|
||||
|------|---------|---------|---------|
|
||||
| OAM DMA | — | $C084–$C089 | Copy $0200–$02FF to sprite RAM |
|
||||
| Palette update | `WritePalette` | $C1B5 | Conditional palette upload ($3F00/$3F10) |
|
||||
| PPU buffer flush | `WritePpuBuffer` | $C2EC | Write pending PPU commands from $0590 buffer |
|
||||
| Scroll | `SetScroll` | $C3FD | Write scroll X/Y from $1E/$1F |
|
||||
| Controller read | `ReadControllers` | $C2A4 | Read both ports, build held/newly-pressed |
|
||||
|
||||
The main loop calls `UpdateGameState` ($C0AA) which dispatches to the active game state handler, then advances timers and audio.
|
||||
|
||||
---
|
||||
|
||||
## 7. Object / Sprite System
|
||||
|
||||
- **OAM buffer** at $0200–$02FF, written via `WriteMetaspriteToOam` ($C440).
|
||||
- **Object data pointers**: `ObjectDataPointerLo/Hi` ($A8/$A9), `ObjectDataOffset` ($AA), `ActiveObjectIndex` ($AB).
|
||||
- **Animation**: `AnimationFrame` ($B0), `AnimationTimer` ($B1), `PoseFrameTimer` ($B3), `PlayerAnimationId` ($B4), `PoseSequenceOffset` ($B5), `PlayerPoseId` ($B6), `PlayerPoseFlags` ($B7).
|
||||
- **Racket**: `RacketTargetX` ($B8), `RacketTargetY` ($B9), `RacketContactFlags` ($BA), `RacketAimOffset` ($BB).
|
||||
- **Player physics**: `PlayerMotionControl` ($60/$65 for P1/P2), X/Y position + subpixel ($61–$69), target coordinates ($80–$83), action type/state ($84–$86), facing direction ($87), position ($88–$89).
|
||||
- **Ball**: `BallMotionControl` ($6A), X/Y position + subpixel ($6B–$6E), height ($6F/$70), target offset ($71), vertical velocity ($72/$73), state flags ($7A), bounce state ($7B), contact state ($8F), trajectory flags ($8D), collision flags ($8E).
|
||||
|
||||
---
|
||||
|
||||
## 8. Audio System
|
||||
|
||||
**`RunAudioEngine`** ($FFF7) — jumps to `AudioEngineUpdate` ($F8E0). Called from MainLoop when `GameStateStep == 1` and `GameActiveFlag != 0`.
|
||||
|
||||
**`InitializeAudioEngine`** ($E1F8):
|
||||
- Clear audio request pointer ($F0/$F1 = $0006), channel table pointer ($F2/$F3 = $0620), sequence table pointer from ROM $F100/$F101.
|
||||
- `audioWorkValue=$40`, `APUSTATUS=$1F`, `APUFRAMECNT=$C0`.
|
||||
- Clear 16 bytes through ($F0), clear 16 entries at 8-byte stride through ($F2).
|
||||
|
||||
**`InitializeAudioChannels`** ($EED0):
|
||||
- Clear audio channel flags, scan 16 eight-byte slots through ($F2).
|
||||
- Slots with values $01–$04 activate a channel: copy 4-byte parameters through indirect pointer $F6.
|
||||
|
||||
**Audio data** at $FF00–$FFF5 (period/mask tables, sequences, instruments), $F450–$F49A (runtime command data), AudioEventSequenceData at $FDFA–$FFEA.
|
||||
|
||||
Audio RAM symbols ($F0–$FF): `audioRequestPtr`, `audioChannelTablePtr`, `audioSequenceTablePtr`, `audioWorkPtr`, `audioWorkValue`, `audioChannelFlags`, `audioRequestIndex`, `audioChannelOffset`, `audioIndirectPtr`, plus channel-specific effect timers ($F5–$F6), sequence pointers/indices ($F7–$FA), stream position ($FB), event priority ($FC), request flags ($FD–$FF).
|
||||
|
||||
---
|
||||
|
||||
## 9. Zero-Page / RAM Symbols (key subset)
|
||||
|
||||
| Address | Symbol | Use |
|
||||
|---------|--------|-----|
|
||||
| $01–$02 | WorkPtrLo/Hi | General indirect pointer |
|
||||
| $09 | PpuCtrlShadow | $2000 mirror |
|
||||
| $0A | PpuMaskShadow | $2001 mirror |
|
||||
| $1A | ControllerHeld | Controller buttons held |
|
||||
| $1B | Zp1B | Previous controller state |
|
||||
| $20 | Zp20 | Timer counter |
|
||||
| $25 | StateTimer | State countdown timer |
|
||||
| $27 | FrameCounter | Frame tick counter |
|
||||
| $30 | NmiWorkComplete | NMI frame-sync flag |
|
||||
| $31 | PpuBufferReady | PPU buffer ready flag |
|
||||
| $32 | NmiUpdatesEnabled | NMI updates enabled flag |
|
||||
| $33 | NmiTriggerFlag | NMI trigger flag |
|
||||
| $34 | StartButtonLatch | Start button debounce latch |
|
||||
| $35 | StateTransitionFlag | State transition flag |
|
||||
| $38 | GameActiveFlag | Game active flag |
|
||||
| $39 | GameMode | Current game mode |
|
||||
| $3C | PaletteUpdateMode | Palette update mode |
|
||||
| $3D | GameState | Main game state |
|
||||
| $3E | GameStateStep | Game state step/substep |
|
||||
| $3F | DemoCounter | Demo counter |
|
||||
| $43 | PendingStateFlag | Pending state flag |
|
||||
| $44 | StateSubstep | State substep |
|
||||
| $46 | PlayerCourtOrder | Player court order |
|
||||
| $48–$4B | ModeAnimTimer/Frame A&B | Mode animation timers/frames |
|
||||
| $4C | ServingPlayer | Serving player |
|
||||
| $4D | ModeAnimSubframe | Animation subframe |
|
||||
| $4E | MessageTimer | Message timer |
|
||||
| $4F | MessagePlayerIndex | Message player index |
|
||||
| $50 | PointWinner | Point winner |
|
||||
| $51–$53 | Player/2 Point/Game Score | Score values |
|
||||
| $57–$5D | Score-related state | Score display/update state |
|
||||
| $5E | PendingGameState | Pending game state |
|
||||
| $60–$6E | Player/Ball motion state | Player and ball physics |
|
||||
| $78–$7F | Animation/ball state | Animation flags, ball state/collision |
|
||||
| $80–$8F | Target/action/collision state | Player targets, action state, collision flags |
|
||||
| $A8–$AA | ObjectDataPointerLo/Hi, Offset | Object data pointers |
|
||||
| $AB | ActiveObjectIndex | Active object index |
|
||||
| $B0–$C9 | Animation/pose/racket state | Animation frames, pose, racket targets |
|
||||
| $CA–$CC | Contact/bounce state | Racket/ball contact state |
|
||||
| $CD–$CF | Movement/action state | Movement speed, computer/action state |
|
||||
| $E0 | SelectedGameMode | Selected game mode |
|
||||
| $E6–$FF | Audio engine state | Audio request pointers, channel flags, sequence state |
|
||||
|
||||
---
|
||||
|
||||
## 10. PPU / Rendering
|
||||
|
||||
- **OAM DMA**: Every NMI, DMA from $0200–$02FF via `OAMDMA` ($4014).
|
||||
- **PPU buffer**: `WritePpuBuffer` ($C2EC) writes a command buffer at $0590 to PPU when `PpuBufferReady` ($31) is set.
|
||||
- **Scroll**: `SetScroll` ($C3FD) writes $1E/$1F to PPUSCROLL.
|
||||
- **Palette**: `WritePalette` ($C1B5) conditionally uploads from RAM buffers based on `PaletteUpdateMode` ($3C).
|
||||
- **Palette shadow**: `PpuCtrlShadow` ($09), `PpuMaskShadow` ($0A).
|
||||
- **Sprite hiding**: `HideAllSprites` ($C1A5) writes $F4 to OAM.
|
||||
- **Metasprite rendering**: `WriteMetaspriteToOam` ($C440) writes sprite data from a pointer.
|
||||
|
||||
---
|
||||
|
||||
## 11. Data Tables
|
||||
|
||||
| Label | Address | Description |
|
||||
|-------|---------|-------------|
|
||||
| `MainStateHandlerTable` | $C101 | 12 main game state handler pointers |
|
||||
| `GameStateStepHandlerTable` | $C185 | 7 mode step handler pointers |
|
||||
| `GameStateHandlerTable` (game mode) | $CAD7 | 8 game-mode state handler pointers |
|
||||
| `InitialMatchObjectStateData` | $C847 | Initial object state data for match start |
|
||||
| `EngineTable_*` | Various | Inline data tables embedded between code |
|
||||
| `AudioEventSequenceData` | $FDFA | Audio event sequence data |
|
||||
| Audio tables | $FF00–$FFF5 | Period/mask tables, sequences, instruments |
|
||||
|
||||
---
|
||||
|
||||
## 12. Summary of Execution Flow
|
||||
|
||||
```
|
||||
Power On / Reset
|
||||
│
|
||||
▼
|
||||
Reset ($C000) ───────────────────────────────────────────────────────┐
|
||||
│ cld, sei, wait VBlank, PPUCTRL/PPUMASK=$00 │
|
||||
│ stack=$FF, clear RAM $0000–$02FF │
|
||||
│ $0500=$5F, clear nametables, hide sprites │
|
||||
│ PPUSCROLL=$00, GameActiveFlag=$01 │
|
||||
│ APUSTATUS=$0F (all channels on) │
|
||||
│ PPUCTRL=$90 (NMI on), PpuCtrlShadow=$90 │
|
||||
│ PpuMaskShadow=$06 │
|
||||
│ │
|
||||
▼ │
|
||||
MainLoop ($C04F) ─────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
├─ NmiWorkComplete ($30) = $00 │
|
||||
├─ if GameActiveFlag && GameStateStep==1: RunAudioEngine ($FFF7) │
|
||||
├─ jsr UpdateTimers ($C375) │
|
||||
├─ jsr UpdateGameState ($C0AA) ──► state machine dispatch │
|
||||
│ ├─ State $00: InitializeMatch │
|
||||
│ ├─ State $01: WaitForServe │
|
||||
│ ├─ State $02: PrepareRally │
|
||||
│ ├─ State $03: RunRally │
|
||||
│ ├─ State $04: PointEnded │
|
||||
│ ├─ State $05: UpdateScore │
|
||||
│ ├─ State $06: AdvanceGame │
|
||||
│ ├─ State $07: PauseOrTransition (RTS) │
|
||||
│ ├─ State $08–$0B: WaitForTimer / Restore / Resume / Finish │
|
||||
│ └─ return to MainLoop │
|
||||
│ │
|
||||
├─ NmiUpdatesEnabled ($32) = $01 │
|
||||
├─ wait for NmiWorkComplete ($30) != 0 ◄── NMI sets this │
|
||||
├─ FrameCounter++, NmiUpdatesEnabled=$00 │
|
||||
└─ jmp MainLoop │
|
||||
│ │
|
||||
│ (if NmiWorkComplete==0: jsr RollRng ($C456), retry) │
|
||||
│ │
|
||||
NMI ($C07D) [once per frame] │
|
||||
│ │
|
||||
├─ save A, X, Y │
|
||||
├─ OAM DMA ($0200–$02FF → $4014) │
|
||||
├─ if NmiUpdatesEnabled: │
|
||||
│ jsr WritePalette ($C1B5) │
|
||||
│ jsr WritePpuBuffer ($C2EC) │
|
||||
│ jsr SetScroll ($C3FD) │
|
||||
├─ jsr ReadControllers ($C2A4) → $4D/$4E/$4F │
|
||||
├─ NmiWorkComplete ($30) = $01 (unblocks main loop) │
|
||||
├─ NmiTriggerFlag ($33) = $01 │
|
||||
├─ restore registers, rti │
|
||||
│ │
|
||||
IrqHandler ($C17E): rti (unused) │
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 13. Notes
|
||||
|
||||
- **Build verification:** `make` in `copilot/` produces `reconstructed_prg.bin` that is **byte-identical** to `prg.bin` and to the PRG bank of `Tennis (JU).nes`.
|
||||
- Vectors confirm NMI at $C07D, and both RESET and IRQ at $C000 (the reset handler doubles as the IRQ handler, just a `rti`).
|
||||
- The foreground main loop (`MainLoop` at $C04F) is strictly NMI-synchronized via `NmiWorkComplete` ($30): it loops until NMI sets it to $01, processes game state and timers, then waits again.
|
||||
- `UpdateGameState` ($C0AA) dispatches to one of 12 main game states via `MainStateHandlerTable` at $C101, indexed by `GameState` ($3D).
|
||||
- State $04 contains a nested secondary state machine (`GameStateStepHandlerTable` at $C185, 7 steps) for mode selection/setup.
|
||||
- Many routine labels are address-based (`EngineRoutine_C1DD`, `MatchRoutine_C7E7`, etc.) reflecting the automated first-pass disassembly; semantic naming is provisional.
|
||||
- `symbols.inc` provides NES hardware register names and controller masks; `locals.inc` provides extensive zero-page symbol definitions covering player/ball physics, audio, and game state.
|
||||
- The disassembly includes inline data tables (`EngineTable_*`) embedded between code regions, preserved verbatim for byte-identity.
|
||||
@@ -1,10 +1,25 @@
|
||||
all: reconstructed_prg.bin verify
|
||||
prg.o: prg.s symbols.inc prg.bin
|
||||
ca65 -g -o $@ prg.s
|
||||
reconstructed_prg.bin: prg.o linker.cfg
|
||||
ld65 -C linker.cfg -m reconstructed.map -Ln reconstructed.lbl -o $@ prg.o
|
||||
verify: reconstructed_prg.bin prg.bin
|
||||
cmp reconstructed_prg.bin prg.bin
|
||||
@echo BYTE-IDENTICAL
|
||||
CA65 ?= ca65
|
||||
LD65 ?= ld65
|
||||
|
||||
all: reconstructed.nes verify
|
||||
|
||||
prg.o: prg.s symbols.inc locals.inc
|
||||
$(CA65) -g -o $@ $<
|
||||
|
||||
header.o: header.s
|
||||
$(CA65) -g -o $@ $<
|
||||
|
||||
chr.o: chr.s
|
||||
$(CA65) -g -o $@ $<
|
||||
|
||||
reconstructed.nes: header.o prg.o chr.o linker.cfg
|
||||
$(LD65) -C linker.cfg -m reconstructed.map -Ln reconstructed.lbl -o $@ header.o prg.o chr.o
|
||||
|
||||
verify: reconstructed.nes
|
||||
cmp reconstructed.nes rom.nes
|
||||
@echo "Byte-identical ROM verified."
|
||||
|
||||
clean:
|
||||
rm -f prg.o reconstructed_prg.bin reconstructed.map reconstructed.lbl
|
||||
rm -f *.o reconstructed.nes reconstructed.map reconstructed.lbl
|
||||
|
||||
.PHONY: all verify clean
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,2 @@
|
||||
.segment "CHR"
|
||||
.incbin "chr.bin"
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,2 @@
|
||||
.segment "HEADER"
|
||||
.incbin "header.bin"
|
||||
@@ -1,6 +1,10 @@
|
||||
MEMORY {
|
||||
PRG: start = $C000, size = $4000, file = %O, fill = yes;
|
||||
HEADER: start = $0000, size = $0010, fill = yes;
|
||||
PRG: start = $C000, size = $4000, fill = yes;
|
||||
CHR: start = $0000, size = $2000, fill = yes;
|
||||
}
|
||||
SEGMENTS {
|
||||
PRG: load = PRG, type = ro;
|
||||
}
|
||||
HEADER: load = HEADER, type = ro;
|
||||
PRG: load = PRG, type = ro;
|
||||
CHR: load = CHR, type = ro;
|
||||
}
|
||||
@@ -178,4 +178,4 @@ AudioRequestFlags3 = $FF
|
||||
|
||||
; Additional traced fields
|
||||
Player2MotionControl = $65
|
||||
AudioSequencePtrHi = $F8
|
||||
AudioSequencePtrHi = $F8
|
||||
Binary file not shown.
@@ -9,6 +9,7 @@
|
||||
.include "locals.inc"
|
||||
|
||||
.segment "PRG"
|
||||
.org $C000
|
||||
|
||||
; =============================================================================
|
||||
; Reset
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
Modules list:
|
||||
-------------
|
||||
header.o:
|
||||
HEADER Offs=000000 Size=000010 Align=00001 Fill=0000
|
||||
prg.o:
|
||||
PRG Offs=000000 Size=004000 Align=00001 Fill=0000
|
||||
chr.o:
|
||||
CHR Offs=000000 Size=002000 Align=00001 Fill=0000
|
||||
|
||||
|
||||
Segment list:
|
||||
-------------
|
||||
Name Start End Size Align
|
||||
----------------------------------------------------
|
||||
CHR 000000 001FFF 002000 00001
|
||||
HEADER 000000 00000F 000010 00001
|
||||
PRG 00C000 00FFFF 004000 00001
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Symlink
+1
@@ -0,0 +1 @@
|
||||
../Tennis (JU).nes
|
||||
Reference in new issue
Block a user