Commit: 0c74fb5cc96cc9d08610bc28d11d8c83b06caa46 Author: Vi Grey Date: 2019-10-24 05:53 UTC Summary: Initial Commit .gitignore | 3 + CHANGELOG.md | 6 + Makefile | 35 +++++ README.md | 104 +++++++++++++ bin/nes-ram-debug-tool-v0_0_0.nes | Bin 0 -> 40976 bytes sav-ram-extract.py | 165 ++++++++++++++++++++ src/defs.asm | 34 ++++ src/graphics/tileset.chr | Bin 0 -> 8192 bytes src/nes-ram-debug-tool.asm | 46 ++++++ src/prg.asm | 676 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/ram.asm | 17 ++ 11 files changed, 1086 insertions(+) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bb29892 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +bin/* +!bin/nes-ram-debug-tool-v* +!bin/archive diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..7551a68 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,6 @@ +# Change Log +All notable changes to this project will be documented in this file. + +## 0.0.0 - 2019-10-24 +### Added +- Initial release diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..9513a04 --- /dev/null +++ b/Makefile @@ -0,0 +1,35 @@ +# Copyright (C) 2018, Vi Grey +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +PKG_NAME := nes-ram-debug-tool +CURRENTDIR := $(dir $(realpath $(firstword $(MAKEFILE_LIST)))) + +all: + mkdir -p $(CURRENTDIR)bin; \ + cd $(CURRENTDIR)src; \ + asm $(PKG_NAME).asm ../bin/$(PKG_NAME).nes; \ + +clean: + rm -f -- $(CURRENTDIR)bin/$(PKG_NAME).nes*; \ diff --git a/README.md b/README.md new file mode 100644 index 0000000..9996e04 --- /dev/null +++ b/README.md @@ -0,0 +1,104 @@ +# NES RAM Debug Tool + +##### Version 0.0.0 + +NES ROM for TKROM boards that extracts initial NES RAM data and stores it in battery backed PRG RAM + +**_NES RAM Debug Tool was created by Vi Grey (https://vigrey.com) and is licensed under the BSD 2-Clause License._** + +### Description: +An NES ROM for TKROM boards and an accompanying Python script to extract the initial NES CPU and PPU RAM data of an NES console on Power On. These tools are used to debug initial NES RAM and was created with the intention of studying how memory changes on an NES console. The initial RAM data will be saved into WRAM and is meant to be dumped with a NES Cartridge Dumper like InfiniteNESLive's INLRetro Dumper-Programmer (https://www.infiniteneslives.com/inlretro.php). + +### Platforms: +- Linux +- macOS +- BSD + +## NES ROM + +### NES ROM Build Dependencies: +- asm6 _(You'll probably have to build asm6 from source. Make sure the asm6 binary is named **asm** and that the binary is executable and accessible in your PATH. The source code can be found at **http://3dscapture.com/NES/asm6.zip**)_ + +### Build NES ROM: +From a terminal, go to the the main directory of this project (the directory this README.md file exists in), you can then build the NES ROM with the following command. + + $ make + +The resulting NES ROM will be located at **bin/nes-ram-debug-tool.nes** + +### Cleaning NES ROM Build Environment: +If you used `make` to build the NES ROM file, you can run the following command to clean up the build environment. + + $ make clean + +### NES ROM .sav File Syntax +**Offset values are based on the beginning of the .sav file (_$0000_), rather than the WRAM location data (_$6000_)** + +The first 2KB of the .sav file (_$0000_-_$07FF_) is the first 2KB of CPU RAM (CPU _$0000_-_$07FF_) + +The next 4KB of the .sav file (_$0800_-_$17FF_) is the 4KB of PPU nametable data (PPU _$2000_-_$2FFF_) + +The next 32B of the .sav file (_$1800_-_$181F_) is the 32KB of PPU palette data (PPU _$3F00_-_$3F1F_) + +## .sav RAM Extract Script + +### .sav RAM Extract Script Dependencies: +- Python >= 3 + +### .sav RAM Extract Script Usage: + $ python3 sav-ram-extract.py -h + Usage: python3 sav-ram-extract.py [ OPTIONS ] [out_dir_path] + + sav_file is the 8KB .sav file for you wish to exctract from. + out_dir_path is an optional directory path to store the created CPU and + PPU RAM files in. + + Options: + -h, --help Print Help (this message) and exit + --version Print version information and exit + + Examples: + python3 sav-ram-extract.py nes-ram-debug-tool.sav + python3 sav-ram-extract.py ~/nes-ram-debug-tool.sav + python3 sav-ram-extract.py ~/nes-ram-debug-tool.sav ram/ + python3 sav-ram-extract.py ~/nes-ram-debug-tool.sav ~/ram/ + python3 sav-ram-extract.py ../nes-ram-debug-tool.sav ~/ram/ + +### .sav RAM Extract Script CPU/PPU RAM Files +When you use the .sav RAM Extract Script, two .ram files should be created, one CPU .ram file and one PPU .ram file. These file names will start with the date/time of file creation, followed by a dash, then cpu.ram or ppu.ram. These .ram files contain the initial editable CPU and PPU ram of the NES console on power on that was stored in the .sav file. + +The CPU .ram file will be a 64KB file that stores the initial console CPU RAM state. Only console RAM data that is editable by the cartridge is properly stored, while everything else is replaced with `00` bytes. PRG ROM data is not stored in this file. Everything at and after _$2000_ in the CPU .ram file is replaced with `00` bytes. + +The PPU .ram file will be a 16KB file that stores the initial console PPU RAM state. Only console RAM data that is editable by the cartridge is properly stored, while everything else is replaced with `00` bytes. CHR ROM data is not stored in this file. Everything before _$2000_ in the PPU .ram file is replaced with `00` bytes. + +### Special Thanks + +- **DwangoAC** (https://tas.bot): For finally convincing me to make a tool like this after I have procrastinated on making a tool like this for many months + +- **Brad Smith** (http://rainwarrior.ca): For providing me with tips and knowledge while streaming production of this + +### License: + Copyright (C) 2019, Vi Grey + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS \`\`AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. diff --git a/bin/nes-ram-debug-tool-v0_0_0.nes b/bin/nes-ram-debug-tool-v0_0_0.nes new file mode 100644 index 0000000..f97d5ac Binary files /dev/null and b/bin/nes-ram-debug-tool-v0_0_0.nes differ diff --git a/sav-ram-extract.py b/sav-ram-extract.py new file mode 100755 index 0000000..4a10d0f --- /dev/null +++ b/sav-ram-extract.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2019, Vi Grey +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +import binascii +import datetime +import os +import sys + +VERSION = "0.0.0" + +def check_sav_file_crc32(): + crc32_sav = sav_file_data[6176: 6180] + generated_crc32_int = binascii.crc32(sav_file_data[:6176]) + generated_crc32_bytes = generated_crc32_int.to_bytes(4, byteorder='big') + if crc32_sav != generated_crc32_bytes: + print('\x1b[91mInvalid NES ROM .sav File (Incorrect CRC32)\x1b[0m') + exit(1) + return + + +def parse_sav_file(): + global cpu_ram_data + global nametable_data + global palette_data + cpu_ram_data = sav_file_data[:2048] + nametable_data = sav_file_data[2048: 6144] + palette_data = sav_file_data[6144: 6176] + return + + +def generate_cpu_ram_file(): + cpu_ram_file_data = cpu_ram_data + cpu_ram_file_data += cpu_ram_data + cpu_ram_file_data += cpu_ram_data + cpu_ram_file_data += cpu_ram_data + cpu_ram_file_data += b'\x00' * 57344 + cpu_ram_path = (cpu_ppu_output_directory + '/' + iso8601datetime + + '-cpu.ram') + try: + cpu_ram_file = open(cpu_ram_path, 'wb') + cpu_ram_file.write(cpu_ram_file_data) + print('\x1b[92mCPU RAM File Successfully Written to ' + cpu_ram_path + + '\x1b[0m') + except: + print('\x1b[91mUnable to Write CPU RAM File to ' + + cpu_ram_path + '\x1b[0m') + exit(1) + return + + +def generate_ppu_ram_file(): + ppu_ram_file_data = b'\x00' * 8192 + ppu_ram_file_data += nametable_data + ppu_ram_file_data += nametable_data[: -256] + ppu_ram_file_data += palette_data + ppu_ram_file_data += palette_data + ppu_ram_file_data += palette_data + ppu_ram_file_data += palette_data + ppu_ram_file_data += palette_data + ppu_ram_file_data += palette_data + ppu_ram_file_data += palette_data + ppu_ram_file_data += palette_data + ppu_ram_path = (cpu_ppu_output_directory + '/' + iso8601datetime + + '-ppu.ram') + try: + ppu_ram_file = open(ppu_ram_path, 'wb') + ppu_ram_file.write(ppu_ram_file_data) + print('\x1b[92mPPU RAM File Successfully Written to ' + ppu_ram_path + + '\x1b[0m') + except: + print('\x1b[91mUnable to Write PPU RAM File to ' + + ppu_ram_path + '\x1b[0m') + exit(1) + return + +def print_version(): + print('sav-ram-extract ' + VERSION) + +def print_help(): + help_string = ('Usage: python3 sav-ram-extract.py [ OPTIONS ] ' + + '[out_dir_path]\n\n' + + 'sav_file is the 8KB .sav file for you wish to exctract ' + + 'from.\n' + + 'out_dir_path is an optional directory path to store the ' + + 'created CPU and\n' + + 'PPU RAM files in.\n\n' + + 'Options:\n' + + ' -h, --help Print Help (this message) and exit\n' + + ' --version Print version information and ' + + 'exit\n\n' + + 'Examples:\n' + + ' python3 sav-ram-extract.py nes-ram-debug-tool.sav\n' + + ' python3 sav-ram-extract.py ~/nes-ram-debug-tool.sav\n' + + ' python3 sav-ram-extract.py ~/nes-ram-debug-tool.sav ' + + 'ram/\n' + + ' python3 sav-ram-extract.py ~/nes-ram-debug-tool.sav ' + + '~/ram/\n' + + ' python3 sav-ram-extract.py ../nes-ram-debug-tool.sav ' + + '~/ram/') + print(help_string) + +sav_file_path = '' +sav_file_data = b'' +cpu_ram_data = b'' +nametable_data = b'' +palette_data = b'' +cpu_ppu_output_directory = './' + +if len(sys.argv) >= 2: + if len(sys.argv) == 3: + cpu_ppu_output_directory = sys.argv[2] + elif sys.argv[1] == '-h' or sys.argv[1] == '--help': + print_help() + exit(0) + elif sys.argv[1] == '--version': + print_version() + exit(0) + sav_file_path = sys.argv[1] + sav_file_path = os.path.expanduser(sav_file_path) + sav_file_path = os.path.abspath(sav_file_path) + try: + sav_file = open(sav_file_path, 'rb') + sav_file_data = sav_file.read() + if len(sav_file_data) != 8192: + print('\x1b[91m1Invalid NES ROM .sav File\x1b[0m') + exit(1) + except: + print('\x1b[91m2NES ROM .sav File Required\x1b[0m') + exit(1) +else: + print('\x1b[91mNES ROM .sav File Required\x1b[0m') + exit(1) + +iso8601datetime = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S:%f") +cpu_ppu_output_directory = os.path.expanduser(cpu_ppu_output_directory) +cpu_ppu_output_directory = os.path.abspath(cpu_ppu_output_directory) + +parse_sav_file() +check_sav_file_crc32() +generate_cpu_ram_file() +generate_ppu_ram_file() diff --git a/src/defs.asm b/src/defs.asm new file mode 100644 index 0000000..4c7db8d --- /dev/null +++ b/src/defs.asm @@ -0,0 +1,34 @@ +BUTTON_A = 1 << 7 +BUTTON_B = 1 << 6 +BUTTON_SELECT = 1 << 5 +BUTTON_START = 1 << 4 +BUTTON_UP = 1 << 3 +BUTTON_DOWN = 1 << 2 +BUTTON_LEFT = 1 << 1 +BUTTON_RIGHT = 1 << 0 + +PPU_CTRL = $2000 +PPU_MASK = $2001 +PPU_STATUS = $2002 +PPU_OAM_ADDR = $2003 +PPU_OAM_DATA = $2004 +PPU_SCROLL = $2005 +PPU_ADDR = $2006 +PPU_DATA = $2007 + +OAM_DMA = $4014 +CONTROLLER1 = $4016 +CONTROLLER2 = $4017 + +MMC3_COMMAND = $8000 +MMC3_BANK = $8001 +MMC3_MIRROR = $A000 +MMC3_WRAM_ENABLE = $A001 +MMC3_WRAM_RW = $80 + +CALLBACK = $FFFA + +CRC32Poly0 = $ED +CRC32Poly1 = $B8 +CRC32Poly2 = $83 +CRC32Poly3 = $20 diff --git a/src/graphics/tileset.chr b/src/graphics/tileset.chr new file mode 100644 index 0000000..e0c9b6f Binary files /dev/null and b/src/graphics/tileset.chr differ diff --git a/src/nes-ram-debug-tool.asm b/src/nes-ram-debug-tool.asm new file mode 100644 index 0000000..b136a0b --- /dev/null +++ b/src/nes-ram-debug-tool.asm @@ -0,0 +1,46 @@ +; Copyright (C) 2019, Vi Grey +; All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions +; are met: +; +; 1. Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; 2. Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; +; THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +; ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +; OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +; SUCH DAMAGE. + +; Version 0.0.0 + + .db "NES", $1A + .db $02 + .db $01 + .db %01000011 + .db $00 + .db 0, 0, 0, 0, 0, 0, 0, 0 + +.include "ram.asm" +.include "defs.asm" + +.base $8000 +.pad $C000, $FF + +.base $C000 + +.include "prg.asm" + +.base $0000 + .incbin "graphics/tileset.chr" diff --git a/src/prg.asm b/src/prg.asm new file mode 100644 index 0000000..abc0f14 --- /dev/null +++ b/src/prg.asm @@ -0,0 +1,676 @@ +RESET: + sei + cld + ldx #$40 + stx $4017 + ldx #$FF + txs + inx + lda #%00000110 + sta PPU_MASK + lda #$00 + sta PPU_CTRL + stx $4010 + ldy #$00 + + +;;;;;;;;;; +;; +;; MMC3 initialization +;; +;;;;;;;;;; + +EnableWRAM: + lda #MMC3_WRAM_RW + sta #MMC3_WRAM_ENABLE + +SetMMC3Mirroring: + lda #$01 + sta MMC3_MIRROR + +SetPatternTableBanks0000To0FFF: + lda #$00 + sta #MMC3_COMMAND + lda #$00 + sta #MMC3_BANK + lda #$01 + sta #MMC3_COMMAND + lda #$02 + sta #MMC3_BANK +SetPatternTableBanks1000To1FFF: + lda #$02 + sta #MMC3_COMMAND + lda #$04 + sta #MMC3_BANK + lda #$03 + sta #MMC3_COMMAND + lda #$05 + sta #MMC3_BANK + lda #$04 + sta #MMC3_COMMAND + lda #$06 + sta #MMC3_BANK + lda #$05 + sta #MMC3_COMMAND + lda #$07 + sta #MMC3_BANK + + +;;;;;;;;;; +;; +;; Initial VBlank Startup Wait +;; +;;;;;;;;;; + +InitialVWait: + ldx #$02 +InitialVWaitLoop: + lda PPU_STATUS + bpl InitialVWaitLoop + dex + bne InitialVWaitLoop + + +;;;;;;;;;; +;; +;; Console Reset Check +;; +;;;;;;;;;; + +CheckReset: + lda reset + cmp #$56 + bne ConsoleNotReset + lda (reset + 1) + cmp #$69 + bne ConsoleNotReset + lda (reset + 2) + cmp #$47 + bne ConsoleNotReset + lda (reset + 3) + cmp #$72 + bne ConsoleNotReset + lda (reset + 4) + cmp #$65 + bne ConsoleNotReset + lda (reset + 5) + cmp #$79 + bne ConsoleNotReset + lda (reset + 6) + cmp #$54 + bne ConsoleNotReset + lda (reset + 7) + cmp #$65 + bne ConsoleNotReset + lda (reset + 8) + cmp #$63 + bne ConsoleNotReset + lda (reset + 9) + cmp #$68 + beq WRAMtoCPU + +ConsoleNotReset: + jmp RAMToWRAMStart + + +;;;;;;;;;; +;; +;; WRAM ($6000 - $67FF) to CPU RAM ($0000 - $07FF) +;; +;;;;;;;;;; + +WRAMtoCPU: + lda #$02 + sta addr + lda #$60 + sta (addr + 1) + ldy #$00 +WRAMtoCPULoop: + lda (addr), y + tax + lda (addr + 1) + sec + sbc #$60 + sta (addr + 1) + txa + sta (addr), y + lda addr + clc + adc #$01 + sta addr + lda (addr + 1) + adc #$60 + sta (addr + 1) + cmp #$68 + bne WRAMtoCPULoop + +;;;;;;;;;; +;; +;; WRAM ($6800 - $77FF) to PPU Nametable RAM ($2000 - $2FFF) +;; +;;;;;;;;;; + +WRAMtoPPU2000To2FFF: + lda PPU_STATUS + lda #$20 + sta PPU_ADDR + lda #$00 + sta PPU_ADDR + sta addr + tay + lda #$68 + sta (addr + 1) +WRAMtoPPU2000To2FFFLoop: + lda (addr), y + sta PPU_DATA + lda addr + clc + adc #$01 + sta addr + lda (addr + 1) + adc #$00 + sta (addr + 1) + cmp #$78 + bne WRAMtoPPU2000To2FFFLoop + + +;;;;;;;;;; +;; +;; WRAM ($7800 - $781F) to PPU Palette RAM ($3F00 - $3F1F) +;; +;;;;;;;;;; + +WRAMtoPPU3F00To3F1F: + lda PPU_STATUS + lda #$3F + sta PPU_ADDR + lda #$00 + sta PPU_ADDR + sta addr + tay + lda #$78 + sta (addr + 1) +WRAMtoPPU3F00To3F1FLoop: + lda (addr), y + sta PPU_DATA + lda addr + clc + adc #$01 + sta addr + cmp #$20 + bne WRAMtoPPU3F00To3F1FLoop + + +;;;;;;;;;; +;; +;; WRAM ($6000 - $6001) to CPU RAM ($0000 - $0001) +;; +;;;;;;;;;; + +WRAMtoCPUEnd: + lda $6000 + sta addr + lda $6001 + sta (addr + 1) + + +;;;;;;;;;; +;; +;; CPU RAM ($0000 - $07FF) to WRAM ($6000 - $67FF) +;; +;;;;;;;;;; + +RAMToWRAMStart: + lda addr + sta $6000 + lda (addr + 1) + sta $6001 +ResetWRAM: + lda #$60 + sta (addr + 1) + lda #$02 + sta addr + tay +ResetWRAMLoop: + lda #$00 + sta (addr), y + iny + bne ResetWRAMLoop + inc (addr + 1) + lda (addr + 1) + cmp #$80 + bne ResetWRAMLoop +RAMToWRAMContinue: + lda #$00 + sta addr + sta (addr + 1) + ldy #$02 +RAMToWRAMLoop: + lda (addr), y + tax + lda (addr + 1) + clc + adc #$60 + sta (addr + 1) + txa + sta (addr), y + lda (addr + 1) + sec + sbc #$60 + sta (addr + 1) + iny + bne RAMToWRAMLoop + inc (addr + 1) + lda (addr + 1) + cmp #$08 + bne RAMToWRAMLoop + + +;;;;;;;;;; +;; +;; PPU Nametable RAM ($2000 - $2FFF) to WRAM ($6800 - $77FF) +;; +;;;;;;;;;; + +PPURAM20002FFFToWRAM: + lda PPU_STATUS + lda #$20 + sta PPU_ADDR + lda #$00 + sta #PPU_ADDR + sta addr + tay + lda #$68 + sta (addr + 1) + lda PPU_DATA +PPURAM20002FFFToWRAMLoop: + lda PPU_DATA + sta (addr), y + iny + bne PPURAM20002FFFToWRAMLoop + inc (addr + 1) + lda (addr + 1) + cmp #$78 + bne PPURAM20002FFFToWRAMLoop + + +;;;;;;;;;; +;; +;; PPU Palette RAM ($3F00 - $3F1F) to WRAM ($7800 - $781F) +;; +;;;;;;;;;; + +PPURAM3F003F1FToWRAM: + lda PPU_STATUS + lda #$3F + sta PPU_ADDR + lda #$00 + sta #PPU_ADDR +PPURAM3F003F1FToWRAMLoop: + lda PPU_DATA + sta (addr), y + iny + cpy #$20 + bne PPURAM3F003F1FToWRAMLoop + + +;;;;;;;;;; +;; +;; RAM CRC32 Calculation ($6000 - $771F) +;; +;;;;;;;;;; + +InitializeCRC32: + lda #$FF + sta crc32 + sta (crc32 + 1) + sta (crc32 + 2) + sta (crc32 + 3) + lda #$60 + sta (addr + 1) + lda #$00 + sta addr + tay +CRC32CalculationLoop: + ldx #$08 + lda (addr), y + eor (crc32 + 3) + sta (crc32 + 3) +CRC32CalculationShiftLoop: + lsr crc32 + ror (crc32 + 1) + ror (crc32 + 2) + ror (crc32 + 3) + bcc CRC32CalculationShiftLoopEnd + lda crc32 + eor #CRC32Poly0 + sta crc32 + lda (crc32 + 1) + eor #CRC32Poly1 + sta (crc32 + 1) + lda (crc32 + 2) + eor #CRC32Poly2 + sta (crc32 + 2) + lda (crc32 + 3) + eor #CRC32Poly3 + sta (crc32 + 3) +CRC32CalculationShiftLoopEnd: + dex + bne CRC32CalculationShiftLoop + lda addr + clc + adc #$01 + sta addr + lda (addr + 1) + adc #$00 + sta (addr + 1) + cmp #$78 + bne CRC32CalculationLoop + lda addr + cmp #$20 + bne CRC32CalculationLoop +CRC32CalculationEnd: + lda #$FF + eor crc32 + sta crc32 + lda #$FF + eor (crc32 + 1) + sta (crc32 + 1) + lda #$FF + eor (crc32 + 2) + sta (crc32 + 2) + lda #$FF + eor (crc32 + 3) + sta (crc32 + 3) + + +;;;;;;;;;; +;; +;; Set Reset Value +;; +;;;;;;;;;; + +SetReset: + lda #$56 + sta reset + lda #$69 + sta (reset + 1) + lda #$47 + sta (reset + 2) + lda #$72 + sta (reset + 3) + lda #$65 + sta (reset + 4) + lda #$79 + sta (reset + 5) + lda #$54 + sta (reset + 6) + lda #$65 + sta (reset + 7) + lda #$63 + sta (reset + 8) + lda #$68 + sta (reset + 9) + + +;;;;;;;;;; +;; +;; Initialize Stack ($0100-$01FF) and Sprite Data ($0200-$02FF) +;; +;;;;;;;;;; + +InitializeRAM: + ldx #$00 +InitializeRAMLoop: + lda #$00 + sta $0100, x + lda #$FE + sta $0200, x + inx + bne InitializeRAMLoop + + +;;;;;;;;;; +;; +;; Pre-NMI House Cleaning +;; +;;;;;;;;;; + +HouseCleaning: + jsr ClearBackground + jsr SetPalettes + ;jsr DrawInitialBytes + jsr DrawTopBar + jsr ResetScroll + +Forever: + jmp Forever + + +;;;;;;;;;; +;; +;; Per Frame Update Management +;; +;;;;;;;;;; + +NMI: + lda #$00 + sta PPU_OAM_ADDR + lda #$02 + sta OAM_DMA + lda PPU_STATUS + jsr Draw + jsr ResetScroll + rti + + +;;;;;;;;;; +;; +;; Screen PPU Management +;; +;;;;;;;;;; + +ResetScroll: + lda #$00 + sta PPU_SCROLL + sta PPU_SCROLL + jsr EnableNMI + rts + +Draw: + lda #%00011110 + sta PPU_MASK + rts + +DisableNMI: + lda #$00 + sta PPU_CTRL + rts + +EnableNMI: + lda #%10001000 + sta PPU_CTRL + rts + +Blank: + lda #%00000110 + sta PPU_MASK + jsr DisableNMI + rts + + +;;;;;;;;;; +;; +;; PPU Background/Palette Management +;; +;;;;;;;;;; + +ClearBackground: + lda PPU_STATUS + lda #$2C + sta PPU_ADDR + lda #$00 + sta PPU_ADDR + ldx #$08 + tay +ClearBackgroundLoop: + sta PPU_DATA + iny + bne ClearBackgroundLoop + dex + bne ClearBackgroundLoop + rts + +SetPalettes: + lda PPU_STATUS + lda #$3F + sta PPU_ADDR + lda #$00 + sta PPU_ADDR + ldx #$08 +SetPalettesLoop: + lda #$0F + sta PPU_DATA + sta PPU_DATA + sta PPU_DATA + lda #$30 + sta PPU_DATA + dex + bne SetPalettesLoop + rts + + +;;;;;;;;;; +;; +;; A Register Byte to Background Tile Conversion +;; +;;;;;;;;;; + +ByteToTiles: + and #$F0 + lsr + lsr + lsr + lsr + adc #$80 + sta bytetiles + lda (addr), y + and #$0F + adc #$80 + sta (bytetiles + 1) + rts + + +;;;;;;;;;; +;; +;; Graphic Placement Management +;; +;;;;;;;;;; + +DrawInitialBytes: + lda #$00 + sta addr + tay + lda #$60 + sta (addr + 1) +; + lda PPU_STATUS + lda #$20 + sta PPU_ADDR + lda #$00 + sta PPU_ADDR +; +DrawInitialBytesLoop: + lda (addr), y + jsr ByteToTiles + lda bytetiles + sta PPU_DATA + lda (bytetiles + 1) + sta PPU_DATA + + lda addr + clc + adc #$01 + sta addr + lda (addr + 1) + adc #$00 + sta (addr + 1) +; + cmp #$62 + bne DrawInitialBytesLoop +; + rts + +DrawTopBar: + lda PPU_STATUS + lda #$20 + sta PPU_ADDR + lda #$60 + sta PPU_ADDR + lda #<(TopBarTable) + sta addr + lda #>(TopBarTable) + sta (addr + 1) + ldy #$00 +DrawTopBarTableLoop: + lda (addr), y + sta PPU_DATA + iny + cpy #$A0 + bne DrawTopBarTableLoop + jsr DrawCRC32OnTopBar + rts + +DrawCRC32OnTopBar: + lda #$20 + sta addr + lda #$78 + sta (addr + 1) + ldy #$00 + lda PPU_STATUS + lda #$20 + sta PPU_ADDR + lda #$AD + sta PPU_ADDR +DrawCRC32OnTopBarLoop: + lda #$20 + sta PPU_DATA + lda (addr), y + jsr ByteToTiles + lda bytetiles + sta PPU_DATA + lda (bytetiles + 1) + sta PPU_DATA + iny + cpy #$04 + bne DrawCRC32OnTopBarLoop + rts + + +;;;;;;;;;; +;; +;; Background Graphics Tables +;; +;;;;;;;;;; + +TopBarTable: + .byte " ", $E0, $E1, $E2, $E3, $E4, $E5, $E6, $E7, $E8, $F0, $F1, $F2, " " + .byte $F8, $F9, $FA, $FB, $FC, $FD, $FE, $FF + .byte $EF, $EB, $EC, $FE, $FF, $ED, $EE, " " + .byte " " + .byte " CRC32: " + .byte " " + .byte $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F + .byte $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F, $0F + +;;;;;;;;;; +;; +;; CALLBACK Vectors +;; +;;;;;;;;;; + + .pad CALLBACK, #$FF + + .dw NMI + .dw RESET + .dw 0 diff --git a/src/ram.asm b/src/ram.asm new file mode 100644 index 0000000..1bd03b8 --- /dev/null +++ b/src/ram.asm @@ -0,0 +1,17 @@ +.enum $0000 + addr dsb 2 + drawline dsb 1 + currentaddress dsb 2 + bytetiles dsb 2 + reset dsb 10 +.ende + +.enum $6000 + cpuram dsb 2048 + ppunametables dsb 4096 + ppupalettes dsb 32 +.ende + +.enum $7820 + crc32 dsb 4 +.ende