diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ae64e693..12be217f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -119,7 +119,7 @@ Here are some of the markdown rules:
Rules for lists:
* Lists can improve readability when used appropriately but should only be used when the context makes sense, don't overuse lists.
* **Never use numbered lists** - Just use `*` for all unordered lists.
-* **Always have a sentence before the list** explaining the list, never just have a list after a heading.
+* **Always have a sentence before the list** explaining the list, never just have a list after a heading. The sentence should normally end with a colon `:` and there should not be a blank line between the colon and the first list element
If using a list, we have a preferred format for lists where each list item has a short bold part followed by a dash (-) and more information:
```markdown
@@ -194,7 +194,7 @@ Field | Purpose
`title` | Full page title shown in the page header and metadata (do not use colons! as it messes with the yaml frontmatter)
`category` | Main site grouping such as the games console name or others such as `leak`, `introduction`, `gameengines`, `maths`, or another section-specific category. This can be a single value (`category: leak`) or a list (`category: [leak, snes]`) when a page belongs to multiple categories.
`image` | Optional main preview image used by the page and site cards, if there is not a unique one leave it blank and it will be generated based on the category and title, for new pages leave blank (don't include)
-`twitterimage` | Absolute URL version of the preview image for social sharing, leave blank and it will be generated, if in doubt leave blank
+`twitterimage` | Absolute URL version of the preview image for social sharing, leave blank and it will be generated, if in doubt leave blank (avoid using but don't remove)
`permalink` | Final public path for the page (do not end with a trailing `/`; that is legacy format we are moving away from)
`breadcrumbs` | Breadcrumb trail shown at the top of the page
`recommend` | Related-topic tags used to build the recommended sidebar and card labels; these should usually be chosen from tags already used elsewhere on the site, only use valid tags from `valid-tags.json`
@@ -202,7 +202,7 @@ Field | Purpose
`updatedAt` | Last meaningful content update date for the page
`excerpt` | Optional short summary for pages that need it
`hidden` | Optional flag for pages that should not appear in normal discovery
-`videocarousel` | Optional list used only on pages that embed the video header carousel
+`videocarousel` | Optional list used only on pages that embed the video header carousel (avoid using)
Some frontmatter keys are now legacy or optional:
* **Only add optional keys when they are actually needed** - Avoid copying large frontmatter blocks from unrelated pages
@@ -228,6 +228,22 @@ If it's a link, ensure it's a valid Markdown link so it's clickable:
[^2]: Page X of Book Y
```
+### External links vs citations
+Footnotes are for sources backing up claims (dates, attribution, technical assertions, quotes, etc). Not every link on a page needs to be a footnote, especially when the link is there as "further reading" that you want the reader to click.
+
+If you include an external link inline as part of a sentence, keep it as a normal Markdown link and only add a footnote if the link is acting as evidence for a specific claim.
+
+If an external link is presented on its own line / paragraph or end of a sentence (not inline in a table, list, or middle of a sentence), ALWAYS use the `link-to-other-site.html` include so it renders consistently and includes a short description:
+
+```ruby
+{% raw %}
+{% include link-to-other-site.html
+ title="Example external article title"
+ url="https://example.com/article"
+ description="1-2 sentences describing why this link is relevant." %}
+{% endraw %}
+```
+
---
### Linking to other RetroReversing pages
diff --git a/categories/Introduction.md b/categories/Introduction.md
index 0978d946..85180d4c 100644
--- a/categories/Introduction.md
+++ b/categories/Introduction.md
@@ -702,7 +702,7 @@ We have pages on each of the following **Nintendo** consoles:
* [Nintendo GameCube (Dolphin)](https://www.retroreversing.com/gamecube/)
* [Nintendo Wii](https://www.retroreversing.com/wii/)
* [Nintendo 3DS](https://www.retroreversing.com/3ds/)
-* [Nintendo Wii U](https://www.retroreversing.com/wiiU/)
+* [Nintendo Wii U](https://www.retroreversing.com/wiiu)
## SEGA Consoles
We have pages on each of the following **SEGA** consoles:
diff --git a/categories/consoles/Atari2600.md b/categories/consoles/Atari2600.md
index 61381300..263b5be8 100644
--- a/categories/consoles/Atari2600.md
+++ b/categories/consoles/Atari2600.md
@@ -1,11 +1,8 @@
---
permalink: /atari2600
layout: post
-category: atari2600
+category: atari
title: Atari 2600 VCS Reverse Engineering
-consoleimage: /public/consoles/Atari 2600.png
-image_: /public/images/nes/Atari 2600.jpg
-twitterimage_: https://www.retroreversing.com/public/images/atari/Atari 2600.jpg
excerpt: Awesome list of Atari Game Development and Reverse Engineering information
breadcrumbs:
- name: Home
@@ -19,6 +16,8 @@ recommend:
recommendTitle: All Atari 2600 Posts
editlink: ../categories/consoles/Atari2600.md
updatedAt_: '2022-12-08'
+redirect_from:
+ - /atari
tags:
- atari
---
diff --git a/categories/consoles/AtariJaguar.md b/categories/consoles/AtariJaguar.md
index 630b1183..76a0a22e 100644
--- a/categories/consoles/AtariJaguar.md
+++ b/categories/consoles/AtariJaguar.md
@@ -1,7 +1,7 @@
---
-permalink: /atarijaguar
+permalink: /jaguar
layout: post
-category: atarijaguar
+category: jaguar
title: Atari Jaguar Reverse Engineering
consoleimage: /public/consoles/Atari Jaguar.png
image_: /public/images/nes/Atari Jaguar.jpg
@@ -20,6 +20,8 @@ recommend:
recommendTitle: All Atari Jaguar Posts
editlink: ../categories/consoles/AtariJaguar.md
updatedAt_: '2024-05-03'
+redirect_from:
+ - /atarijaguar
tags:
- atari
- jaguar
diff --git a/categories/consoles/C64.md b/categories/consoles/C64.md
index a6e013d2..db103bcb 100644
--- a/categories/consoles/C64.md
+++ b/categories/consoles/C64.md
@@ -27,3 +27,8 @@ redirect_from:
[pditincho](https://github.com/pditincho/mm-explained) has produced a reconstructed and fully commented disassembly of the Commodore 64 implementation of Maniac Mansion. This project documents the inner workings of the engine's C64 iteration, covering 6502 assembly routines, specialized disk loaders, and hardware-level interactions with the VIC-II and SID chips. It provides a detailed technical breakdown of game entities such as rooms, costumes, and scripts, serving as a comprehensive resource for understanding the game's original architecture.
{% include link-to-other-site.html url="https://github.com/pditincho/mm-explained" description="pditincho has released mm-explained, a comprehensive disassembly and technical documentation of the C64 version of Maniac Mansion, detailing its assembly logic and engine structure." image="https://opengraph.githubassets.com/1/pditincho/mm-explained" title="GitHub - pditincho/mm-explained: Maniac Mansion C64 explained" %}
+
+### Freeload: The Ocean Loader Technical Retrospective
+[Paulie Hughes](http://www.pauliehughes.com/freeload.htm) provides a detailed technical breakdown of "Freeload," the iconic Commodore 64 turbo loader used by Ocean Software. The page explores the evolution of the loader's design, including its signature raster bars, the integration of loading music by composers like Martin Galway, and the various anti-piracy encryption methods implemented toward the end of the C64's lifecycle. Crucially, it discusses the transition from standard loading routines to more advanced techniques, such as loading data directly into the stack to execute games via a manual stack pointer manipulation.
+
+{% include_cached link-to-other-site.html url="http://www.pauliehughes.com/freeload.htm" description="Original developer Paul Hughes shares the history and 6502 assembly source code for Freeload, the legendary Ocean Loader used for C64 titles." title="Freeload - The Ocean Loader by Paulie Hughes" %}
\ No newline at end of file
diff --git a/categories/consoles/DOS.md b/categories/consoles/DOS.md
index 1a2d37c8..56cdcf57 100644
--- a/categories/consoles/DOS.md
+++ b/categories/consoles/DOS.md
@@ -9,6 +9,7 @@ consoleimage: /public/consoles/Computer Old Design.png
excerpt: Awesome list of DOS Game Development and Reverse Engineering information
redirect_from:
- /msdos
+ - /RetroCityRampage
breadcrumbs:
- name: Home
url: /
@@ -42,6 +43,13 @@ Note that if you are interested in reversing Windows bases PC games we have a se
Note that if you are interested in the MSX PC we have a separate post on that topic:
{% include_cached link-to-other-post.html post="/msx" description="For more information on the MSX check out this post." %}
+## Glossary of Key Terms
+If you are unfamiliar with the technical specifications of late 80s and early 90s PC hardware, this glossary provides context for the techniques mentioned:
+* **Mode 13h** - A standard VGA graphics mode providing 320x200 resolution with 256 colors from a palette of 262,144.
+* **PIT** - The Programmable Interval Timer (Intel 8253/8254), used for system timing and generating precise hardware interrupts.
+* **IRQ0** - The highest priority hardware interrupt on the PC, typically mapped to the system timer.
+* **Open Watcom** - A C/C++ compiler suite famous for its high-performance code generation for DOS extenders.
+
---
# DOS-era PC Hardware
@@ -102,6 +110,28 @@ Thanks to debug symbols being found in the **Carmageddon Splat Pack** expansion
{% include link-to-other-site.html url="https://pizzalegacy.nl/blog/traffic-system.html" description="The Pizza Legacy blog explores the reverse engineering of Pizza Tycoon's 1994 traffic engine, detailing how developers managed city-wide vehicle simulation with minimal CPU overhead." image="" title="How Pizza Tycoon simulated traffic on a 25 MHz CPU" %}
+---
+# DOS Game Development
+
+## Porting Retro City Rampage to MS-DOS
+
+
+[GDC](https://www.youtube.com/watch?v=kSKeWH4TY9Y) hosted this technical presentation by Brian Provinciano, the creator of *Retro City Rampage*. The talk details the process of back-porting a modern game to the constraints of a 1990s MS-DOS environment, specifically targeting 486-class hardware with strict memory and storage limitations.
+
+### Technical Constraints and Goals
+The porting process was defined by the need to shrink a modern codebase into a footprint compatible with legacy hardware:
+* **Target Hardware** - Minimum requirement of a 486 DX IBM PC compatible with 4MB of RAM.
+* **Operating System** - Designed for MS-DOS 3.3 or higher.
+* **Storage Limit** - The entire game, including assets, was required to fit on a single 1.44MB 3.5" high-density floppy disk.
+* **Graphics Architecture** - Utilized a simple 1-byte-per-pixel buffer to map directly to VGA memory for performance.
+
+### Optimization Strategies
+Provinciano discusses several low-level techniques used to achieve real-time performance on the 486:
+* **Reprogramming PIT** - The system timer was reprogrammed to increase precision beyond the default 55ms tick, allowing for synchronized audio and video.
+* **Inline Assembly** - Used for performance-critical sections such as hiding the mouse cursor via `int 33h` and custom tile-drawing routines.
+* **Asset Compression** - Implemented a two-stage compression pipeline: RLE (Run-Length Encoding) for fast runtime rendering and zlib for minimizing executable size on disk.
+* **Data Generation** - To save disk space, pathfinding navigation data and car collision objects were generated procedurally from world collision data rather than being stored as distinct files.
+* **Memory Management** - Aggressive use of `#ifdef` blocks to strip out modern engine features while maintaining a shared codebase between PS4 and DOS versions.
---
## DOS Gaming Aspect Ratio - 320x200
diff --git a/categories/consoles/N64.md b/categories/consoles/N64.md
index e330680d..71872aac 100644
--- a/categories/consoles/N64.md
+++ b/categories/consoles/N64.md
@@ -1,12 +1,8 @@
---
permalink: /n64
-redirect_from:
- - /n64/
layout: post
category: n64
-title: 'Nintendo 64 (Project Reality)'
-consoleimage: /public/consoles/Nintendo 64.png
-recommend: n64
+title: 'Nintendo 64 (Project Reality) Reversing'
recommendTitle: All N64 Posts
editlink: ../categories/consoles/N64.md
excerpt: 'Interested in learning more about the the N64? Excellent! This section will guide you through the basics, starting from basic MIPS assembly language all the way to an introduction to reverse engineering your first game!'
@@ -19,6 +15,10 @@ breadcrumbs:
url: #
tags:
- n64
+ - introduction
+redirect_from:
+ - /n64/
+ - /mystical-ninja-n64-memory-rom-editing/
---
# Introduction to Hacking the Nintendo 64
@@ -135,7 +135,25 @@ Nintendo 64 games can look a bit dated due to their low resolution textures, but
## N64 Cheats, Secrets & Glitches
Whether you just want to get further in your favourite game, unlock hidden content or even completely corrupt/glitch the game, you can use a cheat cartridge such as Action Replay or emulator memory editing to change games in real-time.
{% include_cached link-to-other-post.html post="/emulator-game-memory-corruption" description="For details about corrupting ROMs and memory at runtime to create check out this post." %}
-
+
+### Mystical Ninja N64 Memory ROM Editing
+[60namrruC](https://www.youtube.com/watch?v=HCOhOoajBFY) has a video covering Mystical Ninja N64 Memory ROM Editing utilizing **CheatEngine** alongside the **Mupen64** emulator. The author goes over the progress made in reverse engineering and understanding the memory layout of the *Mystical Ninja Starring Goemon* ROM file, showcasing findings that are documented in text files and a collaborative CheatEngine table.
+
+
+
+Mupen64 allocates a specific block of host PC memory to simulate the N64's physical RDRAM.
+
+Rather than statically decompiling the ROM binary block by block, he attaches CheatEngine directly to the running MuPen64 executable. This hooks into the emulator's memory space, allowing the user to view, freeze, or write over raw hexadecimal values in real-time as the N64 game executes instructions.
+
+#### State Isolation and Sub-Searching
+To identify unknown memory addresses governing specific game mechanics (such as Goemon's health, Ryo currency, or X/Y/Z coordinates), the researcher uses an iterative scanning technique known as sub-searching.
+* First, an initial broad scan is made for a known integer or float value (e.g., current health).
+* The researcher then intentionally alters that state in the emulator (e.g., taking damage).
+* A secondary "sub-search" is executed exclusively within the previous subset of results for the new value. This rapidly filters out dynamic background data, narrowing thousands of addresses down to the exact hex offset governing the targeted mechanic.
+
+#### Documenting the ROM Structure
+Once dynamic RAM addresses are confirmed, the technical hurdle is understanding how these align with the data structures loaded from the game cartridge. The progress showcased involves taking these isolated addresses and compiling them into a collaborative CheatEngine table (`.CT` file) and documented text maps. This shared structural blueprint allows multiple reverse engineers to manipulate game logic and aids in broader N64 decompilation efforts.
+
---
# N64 Anti-piracy
The main Nintendo 64 anti-piracy measure was the enhanced `CIC` chip based on the Super Nintendos CIC chip design but far more secure.
diff --git a/categories/consoles/Nes.md b/categories/consoles/Nes.md
index 3eb76cea..14fc3c5f 100644
--- a/categories/consoles/Nes.md
+++ b/categories/consoles/Nes.md
@@ -16,6 +16,7 @@ breadcrumbs:
url: #
redirect_from:
- /writing-nes-games/
+- /nes-programming/
recommend:
- nes
- snes
@@ -247,6 +248,16 @@ Mesen even comes with a HD Pack Builder Tool to create your own texture packs, f
## NES Assembly Programming
+### Programming the Nintendo Entertainment System
+[Levi D. Smith](https://www.youtube.com/watch?v=XT95C4fT6zA) provides a deep dive into the NES architecture, specifically detailing the 6502 CPU registers, PPU and APU memory mapping, and cartridge components like PRG and CHR ROM.
+
+
+
+The presentation extensively covers writing 6502 assembly, translating high-level programming constructs like loops and subroutines into opcodes, handling hardware interrupts (NMI), reading controller I/O ports, and utilizing the five audio channels.
+
+Finally, it demonstrates the practical application of these concepts by assembling a custom homebrew title, "Space Dude," using the NESASM3 assembler and YCHR sprite editor.
+
+
### Nerdy Nights NES Programming Tutorials
The best 6502 Assembly tutorial for the NES has to be **Brian Parker's** Nerdy Nights tutorial series which goes from the basics all the way up to writing a version of pong!
@@ -261,6 +272,8 @@ The original posts have been taken down but you can find a mirror here:
+
+
## How are NES games so small (40KB)?
The game developer **Morphcat Games** has released a video on how they created an impressive game called **Micro Mages** in just 40KB without using a mapper (NROM board):
diff --git a/categories/consoles/PokemonMini.md b/categories/consoles/PokemonMini.md
index a7217373..bf273d0e 100644
--- a/categories/consoles/PokemonMini.md
+++ b/categories/consoles/PokemonMini.md
@@ -3,7 +3,7 @@ permalink: /PokemonMini
redirect_from:
- /PokemonMini/
layout: post
-category: PokemonMini
+category: pokemonmini
title: 'Pokemon Mini'
consoleimage: /public/consoles/PokemonMini.png
image: https://www.retroreversing.com/public/images/PokemonMini/Pokemon Mini Introduction.jpg
diff --git a/categories/consoles/WiiU.md b/categories/consoles/WiiU.md
index 0fd755f1..eb98ff2e 100644
--- a/categories/consoles/WiiU.md
+++ b/categories/consoles/WiiU.md
@@ -1,6 +1,10 @@
---
permalink: /wiiu
-title: Nintendo WiiU Reverse Engineering
+redirect_from:
+ - /wiiu/
+ - /wiiU
+ - /wiiU/
+title: Nintendo Wii U Reverse Engineering
layout: post
recommendTitle: All WiiU Posts
editlink: ../categories/consoles/WiiU.md
@@ -11,7 +15,7 @@ breadcrumbs:
url: /
- name: Consoles
url: /consoles
- - name: Nintendo WiiU Reverse Engineering
+ - name: Nintendo Wii U Reverse Engineering
url: #
tags:
- wiiu
@@ -22,25 +26,62 @@ recommend:
---
# Introduction
-Welcome to our page dedicated to Wii U reverse engineering! The Wii U was a gaming console released by Nintendo in 2012, and it introduced several innovative features to the gaming world, such as a touch screen on the controller and the ability to play games on both the TV and the controller. If you're interested in learning more about the technical aspects of this console and how it works, you've come to the right place.
+Welcome to our page dedicated to Wii U reverse engineering! The Wii U was a gaming console released by Nintendo in 2012. Its mix of disc titles, eShop downloads, and Virtual Console releases makes it an interesting target for digital archaeology.
-On this page, we've compiled a list of links to other pages that cover various topics related to Wii U reverse engineering. Whether you're interested in understanding the hardware architecture of the console, analyzing game code, or exploring the many mods and hacks that have been created by enthusiasts over the years, you'll find a wealth of resources and information on the pages we've linked to.
+On this page, we've compiled links to the Wii U posts on RetroReversing. The sections are organised so you can jump in at whatever stage you're at, whether that's unpacking a title, identifying an engine, or picking a good first game to reverse.
-So grab your Wii U gamepad, and get ready to dive into the exciting world of Wii U reverse engineering!
+---
+# Hardware
+Before you start digging into binaries, it helps to understand the storage and OS-level environment the software runs under.
+
+## Nintendo Wii U Architecture
+[Copetti.org](https://www.copetti.org/writings/consoles/wiiu/) hosts a comprehensive technical write-up detailing the internal architecture of the Nintendo Wii U. The article explores the "Espresso" CPU and "Latte" GPU, providing insights into the console's memory hierarchy and the "Starbuck" security ARM processor. It further analyzes the hardware-level backward compatibility with the Wii and the low-latency wireless protocol used for GamePad communication.
+
+{% include_cached link-to-other-site.html url="https://www.copetti.org/writings/consoles/wiiu/" description="Rodrigo Copetti provides a comprehensive architectural deep dive into the Wii U, covering its multi-core Espresso CPU, Latte GPU, and security subsystems." title="Wii U Architecture: A Practical Analysis" %}
-{% comment %}
+## NAND and Storage
+The NAND layout is one of the first Wii U-specific topics worth understanding, as it impacts both data extraction and the safety of modifications:
+{% include_cached link-to-other-post.html post="/WiiUNAND" description="Background on the Wii U NAND layout (SLC/MLC), common pitfalls, and why storage knowledge matters when extracting, modding, or recovering data." %}
---
-# Hardware
-If you're interested in reverse engineering software for the Wii U gaming console, it's crucial to have a thorough understanding of the hardware that powers it. By comprehending the inner workings of the Wii U hardware, you can better understand how the software interacts with the hardware and how you can potentially modify or enhance it.
+# Reverse Engineering
+If you are actively reversing Wii U titles, these pages cover the recurring platform-level topics you will keep meeting across games:
+
+## File Formats and Title Layout
+Start here if you want to understand how Wii U titles are packaged on disc and in eShop/NUS formats:
+{% include_cached link-to-other-post.html post="/WiiUFileFormats" description="Practical notes on common Wii U container formats, and the title layout you will repeatedly run into when dumping discs or eShop titles." %}
-In this section of our guide, we will provide you with comprehensive information and resources on the hardware of the Wii U, including retail, prototype, and development hardware.
+## Virtual Console Internals
+If you are interested in reverse engineering Nintendo's emulation-based releases, this page covers the shared structure and where to begin looking:
+{% include_cached link-to-other-post.html post="/WiiUVirtualConsole" description="A breakdown of the common Virtual Console title structure, and what to look at when reversing Nintendo's bundled emulators." %}
-## Retail Hardware
+---
+# Middleware
+Some Wii U titles use platform-specific middleware that changes what "reverse engineering" looks like compared to a normal native title.
-## Development Hardware
+## Nintendo Web Framework
+Some Wii U applications are effectively WebKit-based apps rather than traditional native titles, which changes what artifacts you should prioritise:
+{% include_cached link-to-other-post.html post="/WiiUNWF" description="Notes on Nintendo's WebKit-based application framework and why it matters for reverse engineering HTML5-based Wii U titles." %}
+
+---
+# Game Engines
+For many Wii U eShop titles, identifying the engine early can save time. It gives you strong hints about file formats, scripting/runtime components, and what tooling approaches tend to work.
+
+## eShop Game Engines
+If you can identify the engine up front, you can usually predict what file formats and runtime components you will run into:
+{% include_cached link-to-other-post.html post="/WiiUeShopEngines" description="A survey of third-party engines seen on the Wii U eShop and hints for identifying them inside binaries." %}
+
+## Unity Titles on the eShop
+Unity was common on the Wii U eShop, and treating it as an engine-first reverse engineering problem can be a big time saver:
+{% include_cached link-to-other-post.html post="/WiiUUnity" description="A reference list of Unity-powered Wii U eShop titles, useful for engine-specific reversing workflows." %}
+
+---
+# Games
+If you want a higher-leverage target to start with, look for titles with debug symbols or otherwise unstripped binaries.
-{% endcomment %}
+## Games with Debug Symbols
+If you want a friendlier first target, games with symbols or unstripped binaries can dramatically reduce the amount of guesswork:
+{% include_cached link-to-other-post.html post="/wii-u-unstripped" description="A large reference table of Wii U titles known to ship with debug symbols or otherwise unstripped binaries." %}
---
diff --git a/categories/misc/Conferences.md b/categories/misc/Conferences.md
index cae5eccf..8a4fa96e 100644
--- a/categories/misc/Conferences.md
+++ b/categories/misc/Conferences.md
@@ -246,25 +246,20 @@ The key points from Keynote are as are as follows:
* **Evaluation of Doom 3 Engine Decisions:**
* He reflects on decisions made for the Doom 3 renderer over four years ago.
* Acknowledges some flaws, such as seams on character heads due to mirroring repeat in texturing.
-
* **Specularity and Lighting Improvements:**
* Talks about limitations in skin tone realism due to a single level of specularity.
* Introduces new technology for specular maps, allowing control over the breadth of specular highlights.
* Addresses issues with specularity on broad surfaces and introduces reflection vector calculation for more accurate highlights.
* Discusses the use of cubic environment maps and normalization for better quality highlights.
-
* **Anti-Aliasing Challenges:**
* Highlights the aliasing challenges in surfaces with normal maps and specular highlights.
* Mentions ongoing work to combat aliasing, considering the analysis of surface normals and specularity factors.
-
* **Multi-Channel Texture Considerations:**
* Discusses the complexity introduced when combining multiple maps (normal, diffuse, specular, etc.) and the need for coordinated adjustments.
* Notes potential challenges in scaling and rotating independent maps.
-
* **Quality Improvement through Renormalization:**
* Describes the benefits of renormalization of normal maps before lighting calculations for improved surface quality.
* Addresses the issue of denormalization in cases where normal vectors deviate significantly.
-
* **General Improvements and Considerations:**
* Mentions the need for reevaluation and development of a new rendering engine based on current hardware capabilities.
* Indicates ongoing efforts to enhance the Doom 3 graphics engine, considering issues like aliasing, specularity, and normal map quality.
diff --git a/categories/misc/Hacking.md b/categories/misc/Hacking.md
index 024503e1..c947b151 100644
--- a/categories/misc/Hacking.md
+++ b/categories/misc/Hacking.md
@@ -11,8 +11,6 @@ recommend:
- industry
- introduction
- pc
-image_: /public/images/categories/Hacking.jpg
-twitterimage_: https://www.retroreversing.com/public/images/categories/Hacking.jpg
tags:
- industry
- hacking
@@ -20,6 +18,11 @@ tags:
# Hacking History
+### 2000 - ILOVEYOU Virus: Technical Breakdown and Demonstration
+[NationSquid](https://www.youtube.com/watch?v=NZDiQczOsdc) features a technical overview and demonstration of the ILOVEYOU worm, focusing on its VBScript architecture and rapid propagation through the MAPI interface. The video details how the malware manipulated files and utilized social engineering to achieve widespread system infections and data loss.
+
+
+
## 2005 - Samy Worm: The Myspace XSS Exploit
[Motherboard](https://www.youtube.com/watch?v=DtnuaHl378M) features an interview with **Samy Kamkar** detailing the infamous 2005 "Samy Worm" that took down Myspace. Kamkar explains the technical mechanics of the Cross-Site Scripting (XSS) vulnerability that allowed the worm to exponentially propagate by automatically adding him as a friend and infecting visiting profiles. The video also covers the aftermath, including the site-wide outage and the legal repercussions that led to a three-year ban from computer use.
diff --git a/categories/misc/Industry.md b/categories/misc/Industry.md
index 78e7edd4..6612b996 100644
--- a/categories/misc/Industry.md
+++ b/categories/misc/Industry.md
@@ -10,6 +10,7 @@ redirect_from:
- /documentaries
- /sega
- /companies
+ - /indie
breadcrumbs:
- name: Home
url: /
@@ -42,7 +43,6 @@ If you know of any other footage or information that should be added to this pag
---
# Documentaries showing life in the industry
-
## 1984 - Life in Imagine & Ocean Software (The Battle for Santa's Software)
**Commercial Breaks** was a 30 minute documentary series in the UK about businesses and one episode in particular from 1984 is of interest to this site, it was called "The Battle for Santa's Software". It followed two British software companies, Imagine software and Ocean, only one remained at the end of the show!
@@ -233,6 +233,23 @@ He heard about the job through his agent which was a common way for game program
+---
+# Indie Game Development
+
+## Why Nintendo doesn't like 3rd party developers?
+In 1986 the year in which many believed the video games industry was finished due to the Atari collapse (google for the ET situation..). Nintendo were doing well in Japan but they were worried about the collapsed video game market in the USA. They wanted to release their Famicom system in the US but due to what had just happened to Atari they were of course cautious.
+
+**Hiroshi Yamauchi** president of Nintendo at the time believed the failure was due to too many 3rd party rubbish games:
+
+
+Ever since Nintendo has been very wary of allowing 3rd party developers to release software for their consoles.
+
+They have even put in place a number of security measures to stop unlicensed developers including both hardware and software methods.
+
+This along with the threat of piracy has kept nintendo away from allowing 'homebrew development' on any of their consoles.
+
+### A Change In Direction?
+It wasn't until they saw the success of many indie games such as minecraft and the success of 'app stores for games' that they have started opening up their hardware to indie developers.
---
diff --git a/categories/tools/Disassemblers.md b/categories/tools/Disassemblers.md
index 30938394..df193ef6 100644
--- a/categories/tools/Disassemblers.md
+++ b/categories/tools/Disassemblers.md
@@ -339,9 +339,7 @@ However, it requires running the program, which might be risky if the program is
Dynamic disassemblers function by instrumenting the program as it runs. This can be done in several ways:
* **Binary Instrumentation**: The disassembler inserts additional code (probes) into the binary to monitor the execution of instructions. This method allows the disassembler to collect data such as which instructions are executed, how often they are run, and how they interact with memory and registers.
-
* **Emulation**: In some cases, dynamic disassemblers use emulation to simulate the execution of the program in a controlled environment. The disassembler steps through the instructions as they would execute on the actual hardware, allowing for detailed observation of the program's behavior.
-
* **Debugging Interface**: Some dynamic disassemblers leverage the debugging APIs provided by operating systems. By attaching to a running process or launching a program in a debug mode, the disassembler can intercept and analyze instructions as they are executed.
---
@@ -356,9 +354,7 @@ Dynamic Disassemblers have the following advantages:
## Challenges of Dynamic Disassemblers
Dynamic Disassemblers have the following challenges:
* **Performance Overhead**: Because dynamic disassembly involves running the program and monitoring its behavior, it often incurs significant performance overhead. The process can be much slower than static analysis, especially if instrumentation or emulation is used.
-
* **Partial Coverage**: Dynamic disassembly is dependent on the execution paths taken during analysis. If certain parts of the code are not triggered during the monitored execution, they will not be disassembled. This makes it crucial to ensure comprehensive coverage during analysis, which can be challenging.
-
* **Complex Setup**: Setting up a dynamic disassembler can be more complex than using a static disassembler. It often requires a controlled environment, such as a sandbox, and careful management of the execution context to avoid unwanted side effects.
---
@@ -388,19 +384,15 @@ An interactive disassembler is a software tool that converts machine code (binar
### Interactive Code Exploration
* **Control Flow Graphs (CFGs)**: Interactive disassemblers often generate visual representations of a program's control flow, showing how different functions and loops interact. Users can click on different nodes and edges to explore these paths more thoroughly.
-
* **Jump and Call References**: Users can easily see where functions are called from or where jump instructions lead, making it easier to trace the flow of execution.
### Manual Adjustments
* **Marking Code and Data**: Users can manually specify whether a section of the binary is code or data, which is particularly useful in binaries where the boundaries between code and data are not clear.
-
* **Renaming and Commenting**: Functions, variables, and memory locations can be renamed to more meaningful names, and users can add comments to help document the disassembly.
-
* **Defining Data Structures**: Users can define and apply custom data structures to areas of memory, improving the clarity of complex data segments.
### Scripting and Automation
* **Scripting Support**: Many interactive disassemblers support scripting languages like Python or JavaScript, allowing users to automate repetitive tasks, write custom analyses, or extend the functionality of the disassembler.
-
* **Macros and Plugins**: Users can create or import plugins and macros to add new features, such as custom decoders for specific binary formats or automated analysis routines.
### Cross-Referencing
diff --git a/external-links.json b/external-links.json
index 303f62ea..6d6e7c17 100644
--- a/external-links.json
+++ b/external-links.json
@@ -567,7 +567,7 @@
"url": "https://www.youtube.com/embed/LMhrnuj01oY?si=7orEHKZ8cIsoG6V2",
"type": "direct_link",
"files": [
- "categories/misc/Books.md"
+ "pages/Industry/Books.md"
],
"title": "Writing a 3D game engine without Unity like it's 1995 again",
"author": "ciciplusplus",
@@ -598,7 +598,7 @@
"type": "direct_link",
"files": [
"categories/misc/Conferences.md",
- "categories/misc/GDC.md"
+ "pages/Industry/GDC.md"
],
"title": "The History of Game Developers Conference (documentary, 2019/2020)",
"author": "GamersGlobal",
@@ -608,7 +608,7 @@
"url": "https://www.youtube.com/embed/VwZi58u1FjI?si=hdShaCr7SG10Am3X",
"type": "direct_link",
"files": [
- "categories/misc/GDC.md"
+ "pages/Industry/GDC.md"
],
"title": "GDC Founder Chris Crawford's Dragon Speech",
"author": "Game Developers Conference",
@@ -618,7 +618,7 @@
"url": "https://www.youtube.com/embed/a9DlhDRZ0yA?si=ItR3cSooTvgHatKQ",
"type": "direct_link",
"files": [
- "categories/misc/GDC.md"
+ "pages/Industry/GDC.md"
],
"title": "Shigeru Miyamoto's 1999 GDC Keynote",
"author": "Game Developers Conference",
@@ -628,7 +628,7 @@
"url": "https://www.youtube.com/embed/mf_bfLmoOcw?si=1BWX-c9SnLTirvkF",
"type": "direct_link",
"files": [
- "categories/misc/GDC.md"
+ "pages/Industry/GDC.md"
],
"title": "Sega Dreamcast Keynote GDC 1999 with Bernie Stolar",
"author": "freakdave",
@@ -638,7 +638,7 @@
"url": "https://www.youtube.com/embed/iCj0OqZ8ByQ?si=jzBl4ABpkb0XH3PN",
"type": "direct_link",
"files": [
- "categories/misc/GDC.md"
+ "pages/Industry/GDC.md"
],
"title": "GDC ( Game Development Conference ) 2000 Microsoft keynote ( Prototype Original Xbox Unveiling )",
"author": "OG-XboxMods",
@@ -648,14 +648,14 @@
"url": "https://www.youtube.com/embed/uFawiEumVLM?si=EzOeek5JpprN6Uhd",
"type": "direct_link",
"files": [
- "categories/misc/GDC.md"
+ "pages/Industry/GDC.md"
]
},
"l6he8GFGPzU": {
"url": "https://www.youtube.com/embed/l6he8GFGPzU?si=7Rv0YPS3HLCz9xdl",
"type": "direct_link",
"files": [
- "categories/misc/GDC.md"
+ "pages/Industry/GDC.md"
],
"title": "Extended Play - GDC 2002 (incomplete)",
"author": "ENunn",
@@ -665,7 +665,7 @@
"url": "https://www.youtube.com/embed/MusKnL2GbQE?si=6nXzztPh3XWHkEfk",
"type": "direct_link",
"files": [
- "categories/misc/GDC.md"
+ "pages/Industry/GDC.md"
],
"title": "GDC 2004 - John Carmack Keynote",
"author": "Charlie",
@@ -675,7 +675,7 @@
"url": "https://www.youtube.com/embed/RMrj8gdUfCU?si=ro-uTNmYG1SfHF4q",
"type": "direct_link",
"files": [
- "categories/misc/GDC.md"
+ "pages/Industry/GDC.md"
],
"title": "Satoru Iwata - Heart of a Gamer",
"author": "Game Developers Conference",
@@ -2886,7 +2886,7 @@
"url": "https://twitter.com/dosnostalgic/status/1489032479252041736?ref_src=twsrc%5Etfw",
"type": "direct_link",
"files": [
- "categories/misc/Books.md"
+ "pages/Industry/Books.md"
]
},
"1459294881139023874": {
@@ -4025,105 +4025,105 @@
"url": "https://github.com/user-attachments/assets/0cd0b132-5159-4583-b742-a68012ca5a44",
"alt_text": "CGDC 1993 Proceedings Cover",
"files": [
- "categories/misc/GDC.md"
+ "pages/Industry/GDC.md"
]
},
"36230338-99ba-4a18-b285-38d9d6b33151": {
"url": "https://github.com/user-attachments/assets/36230338-99ba-4a18-b285-38d9d6b33151",
"alt_text": "CGDC 1994 Proceedings",
"files": [
- "categories/misc/GDC.md"
+ "pages/Industry/GDC.md"
]
},
"300ca4a6-6c79-40ff-88cb-cddd12f95665": {
"url": "https://github.com/user-attachments/assets/300ca4a6-6c79-40ff-88cb-cddd12f95665",
"alt_text": "CGDC 1995",
"files": [
- "categories/misc/GDC.md"
+ "pages/Industry/GDC.md"
]
},
"f171d6a3-2725-43c9-9840-8875ede96bda": {
"url": "https://github.com/user-attachments/assets/f171d6a3-2725-43c9-9840-8875ede96bda",
"alt_text": "CGDC 1996",
"files": [
- "categories/misc/GDC.md"
+ "pages/Industry/GDC.md"
]
},
"5842bfd7-c05a-47ac-a425-35037ff9de08": {
"url": "https://github.com/user-attachments/assets/5842bfd7-c05a-47ac-a425-35037ff9de08",
"alt_text": "CGDC 1997",
"files": [
- "categories/misc/GDC.md"
+ "pages/Industry/GDC.md"
]
},
"c471536a-b80f-44de-ad23-e8707a67fef0": {
"url": "https://github.com/user-attachments/assets/c471536a-b80f-44de-ad23-e8707a67fef0",
"alt_text": "The CGDC hosted an all-day class on Friday April 25 on managing game development. The classic conference classes (April 25 to 29) covered everything from programming and production to business concerns and legal issues pertinent to the industry. Also, several roundtables focused on issues of game design and the future direction of gaming. Keynote speakers at the show included John Romero, Chris Roberts, and Nolan Bushnell.\nIntensive two-day tutorials focused on object-oriented game design, creating online games with lavg.\nSoftimage development tools and techniques, and 3D Studio Max. One-day tutorials covered OpenGL, windows programming, Debabelizer, and modeling.",
"files": [
- "categories/misc/GDC.md"
+ "pages/Industry/GDC.md"
]
},
"110c049a-0b02-48b1-a4dd-8370dcf1826a": {
"url": "https://github.com/user-attachments/assets/110c049a-0b02-48b1-a4dd-8370dcf1826a",
"alt_text": "The CGDC show has been criticized since changing hands a year ago. Formerly put on by Computer Game Developers* Association (CGDA), the CDC was known as a small homegrown operation nurtured by the tight-knit game developer community. The first annual CDC was a group of 20 people (basically the CGDA) gathered in a living room. Although\nCGDA has always held and continues to hold a stake in the proceedings, in 1996 Miller Freeman Inc. took over the show. Many bemoaned the inevitable \"growth\" that MFI meant for the CDC, arguing that heavy traffic would ruin the independent feel that was once so integral to attendees' enjoyment of the show. The 1995 show (pre-MFI) saw 2,200 people, and MFI grew the 1996 show to 4,000.",
"files": [
- "categories/misc/GDC.md"
+ "pages/Industry/GDC.md"
]
},
"af8f7754-a880-4972-bb19-ea122bd464c1": {
"url": "https://github.com/user-attachments/assets/af8f7754-a880-4972-bb19-ea122bd464c1",
"alt_text": "GDC 1998",
"files": [
- "categories/misc/GDC.md"
+ "pages/Industry/GDC.md"
]
},
"3d3b6bf1-44c6-4857-b0b0-36ed7d5a622d": {
"url": "https://github.com/user-attachments/assets/3d3b6bf1-44c6-4857-b0b0-36ed7d5a622d",
"alt_text": "GDC 1999",
"files": [
- "categories/misc/GDC.md"
+ "pages/Industry/GDC.md"
]
},
"c95f1f00-da6e-42e0-8b2c-529f2866efcb": {
"url": "https://github.com/user-attachments/assets/c95f1f00-da6e-42e0-8b2c-529f2866efcb",
"alt_text": "GDC 2000",
"files": [
- "categories/misc/GDC.md"
+ "pages/Industry/GDC.md"
]
},
"aa852a36-1639-4d6a-834d-2f9c6b1f63b5": {
"url": "https://github.com/user-attachments/assets/aa852a36-1639-4d6a-834d-2f9c6b1f63b5",
"alt_text": "GDC 2001",
"files": [
- "categories/misc/GDC.md"
+ "pages/Industry/GDC.md"
]
},
"46311f37-90c9-4e9d-9fea-7e47187b18c1": {
"url": "https://github.com/user-attachments/assets/46311f37-90c9-4e9d-9fea-7e47187b18c1",
"alt_text": "Outside of GDC 2002",
"files": [
- "categories/misc/GDC.md"
+ "pages/Industry/GDC.md"
]
},
"542ff24b-93ad-4226-851b-1e71657a45ec": {
"url": "https://github.com/user-attachments/assets/542ff24b-93ad-4226-851b-1e71657a45ec",
"alt_text": "GDC 2002 Proceedings",
"files": [
- "categories/misc/GDC.md"
+ "pages/Industry/GDC.md"
]
},
"b7a37493-bb7b-421c-8148-cd25111f7e7b": {
"url": "https://github.com/user-attachments/assets/b7a37493-bb7b-421c-8148-cd25111f7e7b",
"alt_text": "GDC 2003",
"files": [
- "categories/misc/GDC.md"
+ "pages/Industry/GDC.md"
]
},
"793d25a9-71b6-40e9-b91c-0111d27f98d9": {
"url": "https://github.com/user-attachments/assets/793d25a9-71b6-40e9-b91c-0111d27f98d9",
"alt_text": "GDC 2005",
"files": [
- "categories/misc/GDC.md"
+ "pages/Industry/GDC.md"
]
},
"fef02646-f3d2-41a7-a468-6109fb25e091": {
diff --git a/categories/misc/Books.md b/pages/Industry/Books.md
similarity index 99%
rename from categories/misc/Books.md
rename to pages/Industry/Books.md
index 30fce287..843f025c 100644
--- a/categories/misc/Books.md
+++ b/pages/Industry/Books.md
@@ -10,7 +10,7 @@ breadcrumbs:
url: /industry
redirect_from:
- /book
-editlink: ../categories/misc/Books.md
+editlink: ../pages/Industry/Books.md
recommend: industry
image: /public/images/categories/Games Industry Books.jpg
twitterimage: https://www.retroreversing.com/public/images/categories/Games Industry Books.jpg
diff --git a/categories/misc/GDC.md b/pages/Industry/GDC.md
similarity index 99%
rename from categories/misc/GDC.md
rename to pages/Industry/GDC.md
index 724732a0..12ed0f5d 100644
--- a/categories/misc/GDC.md
+++ b/pages/Industry/GDC.md
@@ -10,7 +10,7 @@ breadcrumbs:
url: /conferences
- name: Game Developers Conference
url: /gdc
-editlink: ../categories/misc/GDC.md
+editlink: ../pages/Industry/GDC.md
recommend:
- industry
- introduction
diff --git a/pages/Industry/IndieDevelopment.md b/pages/Industry/IndieDevelopment.md
deleted file mode 100644
index 4b3336fa..00000000
--- a/pages/Industry/IndieDevelopment.md
+++ /dev/null
@@ -1,34 +0,0 @@
----
-layout: post
-tags:
-- industry
-title: Indie Game Development
-category: industry
-image: /public/kCxNlbEqQBTmGsgDrzeh4w_img_0.png
-permalink: /indie
-breadcrumbs:
- - name: Home
- url: /
- - name: Industry
- url: /industry
- - name: Console Indie Game Development
- url: #
-recommend: industry
-editlink: ./industry/IndieDevelopment.md
----
-
-# Why Nintendo doesn't like 3rd party developers?
-In 1986 the year in which many believed the video games industry was finished due to the Atari collapse (google for the ET situation..). Nintendo were doing well in Japan but they were worried about the collapsed video game market in the USA. They wanted to release their Famicom system in the US but due to what had just happened to Atari they were of course cautious.
-
-Hiroshi Yamauchi president of Nintendo at the time believed the failure was due to too many 3rd party rubbish games:
-
-
-
-Ever since Nintendo has been very wary of allowing 3rd party developers to release software for their consoles.
-
-They have even put in place a number of security measures to stop unlicensed developers including both hardware and software methods.
-
-This along with the threat of piracy has kept nintendo away from allowing 'homebrew development' on any of their consoles.
-
-It wasn't until they saw the success of many indie games such as minecraft and the success of 'app stores for games' that they have started opening up their hardware to indie developers.
-
diff --git a/pages/consoles/gameboy/Mrdo.md b/pages/consoles/gameboy/Mrdo.md
index d984c253..2124ed74 100644
--- a/pages/consoles/gameboy/Mrdo.md
+++ b/pages/consoles/gameboy/Mrdo.md
@@ -16,21 +16,39 @@ breadcrumbs:
url: /gameboy
- name: Mr Do! Source Code (Game Boy)
url: #
-recommend: gameboy
+recommend:
+- gameboy
editlink: /consoles/gameboy/Mrdo.md
+updatedAt: '2026-04-14'
---
-The source code for Ocean Software's Mr Do! port to the Game Boy has been officially released by two of the original developers.
-Description from Paul Hughes [^1]
-```
-Many moons ago I debugged and finished off Ocean's Mr Do! for the original Game Boy.
+# Introduction
+This page documents the official release of the assembly source for Ocean Software's Mr Do! port to the Game Boy.
+It focuses on what the code is doing (maps, chewing, actors, rendering timing, and data formats), plus how to verify it in SameBoy.
-As **Joffa**, the late, great original author, decided to release the source code,
+## Start here
+If you only read a few sections, these are the best "entry points" for understanding how the engine works:
+* **Data formats** - Jump to [Scene format (maps, cherries, apples, food)](#scene-format-maps-cherries-apples-food) for the `SCENE` stream and the `BYTESCREEN` control map.
+* **Core trick** - Jump to [Dirty-tile updates and why BACKSCREEN exists](#dirty-tile-updates-and-why-backscreen-exists) for the `BACKSCREEN` + `CHRDUMP` design.
+* **Timing** - Jump to [Timing, VBlank, and the window split](#timing-vblank-and-the-window-split) for the mid-frame `LCDC` swap and OAM DMA strategy.
+* **Hands-on** - Jump to [SameBoy debugger walkthrough](#sameboy-debugger-walkthrough) for watchpoints you can run immediately.
-I thought I'd also put it up.
-```
+## Mr Do! - Game Boy Review
+This video provides a brief look at the Game Boy port and is useful context before diving into the source [^6]:
+
+
+---
+# Source code release
+The original release is a single monolithic assembly file (`mrdo.asm`) containing code, data tables, and large blocks of embedded graphics data [^2].
+
+Description from Paul Hughes [^1]:
+> Many moons ago I debugged and finished off Ocean's Mr Do! for the original Game Boy.
+>
+> As **Joffa**, the late, great original author, decided to release the source code,
+>
+> I thought I'd also put it up.
-The header for the source file also mentions **Wesley Knackets&& developed it between the 28th of June and the 5th of September 1990, which is just a little over 2 months!
+The header for the source file also mentions Wesley Knackers and gives a start date of June 28, 1990 and a last date of September 5, 1990:
```cpp
****************************************************************************
@@ -45,15 +63,1399 @@ The header for the source file also mentions **Wesley Knackets&& developed it be
****************************************************************************
```
-Known developers:
-* **Paul Hughes**
-* **Joffa**
-* **Wesley Knackers**
+Known developers mentioned across the release and related posts:
+* **Paul Hughes** - Debugged and finished off Ocean's Mr Do! for the original Game Boy (per his note) [^1].
+* **Joffa** - The original author who released the source code (per Hughes) [^1].
+* **Wesley Knackers** - Credited as the author in the `mrdo.asm` header [^2].
+
+---
+## Glossary of Key Terms
+If you are new to Game Boy reverse engineering terminology, this quick glossary should help:
+* **DMA** - The Game Boy OAM DMA mechanism used to copy 160 bytes of sprite attribute data into OAM via the `DMA` register (`$FF46`) [^3].
+* **OAM** - Object Attribute Memory (`$FE00`) containing the hardware sprite list (position, tile, attributes) [^3].
+* **VRAM** - Video RAM (`$8000-$9FFF`) containing tile graphics and background/window tilemaps [^3].
+* **WRAM** - Work RAM (`$C000-$DFFF`) used for variables, buffers, and scratch space [^3].
+* **HRAM** - High RAM (`$FF80-$FFFE`) used here to run short routines (including the DMA trigger) without being blocked during OAM DMA [^3].
+
+---
+# Code overview
+The source is useful because it is not a "disassembly" or a ROM dump.
+It is proper annotated game code with labels and routines that map closely onto the retail behaviour.
+
+Some highlights worth skimming first:
+* **Main loop** - `START` runs `SYSETUP`, `MENU`, and then enters a per-level loop that calls the gameplay subsystems in a predictable order.
+* **State-machine style** - Multiple behaviours are selected via jump tables (`BADTAB`, `APPLETAB`, `LOGOTAB`, etc.) rather than long chains of branches.
+* **2x2 meta-tiles** - The map is built from 4-tile blocks (top-left/top-right/bottom-left/bottom-right) with additional tables for "eaten" wall variants.
+* **Split-screen rendering** - `SPLITSCREEN` does a status-window pass, then triggers OAM DMA for gameplay sprites after a timing delay.
+
+---
+# Deeper file tour
+If you want to understand the game quickly, it helps to treat `mrdo.asm` as a handful of tightly-coupled subsystems that run in a strict per-frame pipeline.
+
+## Frame pipeline
+The `MAINLOOP` order is deliberate.
+The parts that must run during VBlank (tilemap updates, OAM DMA) are clustered in `SPLITSCREEN`, and the rest of gameplay runs with predictable data flow:
+
+Routine | What it does | Why it matters
+---|---|---
+`SPLITSCREEN` | Waits for VBlank, updates status, dumps dirty tiles, prints apples, then does OAMDMA swaps | Coordinates tilemap writes and sprite DMA so VRAM/OAM access stays safe
+`KEYS` | Polls the joypad and writes `KEYPRESS` | Centralizes input sampling for the frame
+`DECODE` | Updates Mr Do movement/animation and handles ball throwing | Implements turn validity checks and scroll updates
+`MRDOCHEW` | Updates the "chewed" tunnel tiles around Mr Do | Writes tunnel state into the map mirror and queues tilemap updates
+`BADDIES` | Updates all active enemies and special actors via `BADTAB` | Shared AI + per-type state machine updates
+`COLLISIONS` | Checks the thrown ball against all 16x16 actors via `HITBALLTAB` | Handles catches, kills, freeze/unfreeze logic, and score popups
+`APPLEPIE` | Updates apples via `APPLETAB` | Apple state machine (waiting, jiggle, falling, splitting)
+`DUMPOBJ` | Builds the gameplay OAM list (`GAMEOBJ`) from sprite records | Includes a simple OAM-order mixing trick to reduce persistent flicker patterns
+`STATSP` | Builds the status-window OAM list (`STATUSOBJ`) | Renders lives/extra letters + bonus monster status sprites
+`CLOCK` / `RAND` / `FLAGS` | Timekeeping, RNG stirring, animation helpers | Keeps animation offsets and randomness consistent frame-to-frame
+
+This diagram shows the per-frame call order as a pipeline:
+```mermaid
+flowchart LR
+ SPLIT["SPLITSCREEN (VBlank work + OAM DMA)"] --> KEYS["KEYS (poll joypad)"]
+ KEYS --> DECODE["DECODE (Mr Do movement + ball throw)"]
+ DECODE --> CHEW["MRDOCHEW (tunnel updates + CHRDUMP)"]
+ CHEW --> BADDIES["BADDIES (actor update via BADTAB)"]
+ BADDIES --> COLL["COLLISIONS (ball vs actors via HITBALLTAB)"]
+ COLL --> APPLE["APPLEPIE (apple states via APPLETAB)"]
+ APPLE --> DUMP["DUMPOBJ (build GAMEOBJ OAM list)"]
+ DUMP --> STATSP["STATSP (build STATUSOBJ OAM list)"]
+ STATSP --> CLOCK["CLOCK"]
+ CLOCK --> RAND["RAND"]
+ RAND --> FLAGS["FLAGS"]
+```
+
+---
+## Joypad polling and turn validation
+Input is polled by `KEYS` using the standard `$FF00` joypad register scan, with repeated reads and bit-masking before the final nibble merge into `KEYPRESS`.
+Mr Do turning is restricted to tile boundaries:
+* **Tile boundary gating** - `DECODE` only considers direction changes when both `(X+8)&15 == 0` and `(Y+8)&15 == 0`, which effectively makes turns occur on a 16x16 grid even though positions are stored in pixels.
+* **Directional validity tables** - The `VALIDLR` and `VALIDUD` tables translate the pressed direction bits into a "direction+1" value, letting the code reject invalid transitions cheaply.
+
+---
+## Coordinate transforms you will see everywhere
+Multiple helper routines convert between pixel positions and addresses in different memory-backed maps:
+* **`LOWAD` / `PIXAD`** - Convert pixel XY into a `DISPSCREEN` tilemap address.
+* **`GETMAPHI` / `GETMAPLO`** - Convert pixel XY into a `BACKSCREEN` tilemap address.
+* **`GETBYTEHI` / `GETBYTELO`** - Convert pixel XY into a `BYTESCREEN` byte-map address (used as a compact "control map" for tunnels/items).
+
+If you are tracing code in an emulator, these routines are great stepping stones for understanding whether a subsystem is reading the "visual map" (`DISPSCREEN` / `BACKSCREEN`) or the compact control map (`BYTESCREEN`).
+
+---
+## Dirty-tile updates and why BACKSCREEN exists
+The tunnel chewing system is optimized around a RAM mirror of the background tilemap:
+* **Canonical map mirror** - `COPYMAP` copies `DISPSCREEN` into `BACKSCREEN` so gameplay logic can read/modify a RAM copy without touching VRAM constantly.
+* **Chew writes go to `BACKSCREEN`** - `MRDOCHEW` edits `BACKSCREEN` tiles using direction-specific lookup tables (`UTL`, `UTR`, `UBL`, `UBR`, and friends).
+* **Changes are queued** - The chewing code writes address+tile triples into `CHRDUMP` and increments `DUMPTOT`.
+* **VBlank flush** - `CHRDUMPER` runs inside `SPLITSCREEN` and copies only the queued tile changes back into `DISPSCREEN`.
+
+`CHRDUMPER` contains a particularly neat trick.
+It stores queued addresses in the `BACKSCREEN` address space and then XORs the high byte so they point at the equivalent `DISPSCREEN` tilemap location.
+That avoids storing two pointers per tile update and keeps the dirty list compact.
+
+This is the `CHRDUMPER` hot loop, showing the XOR high-byte mapping:
+```nasm
+CHRDUMPER LD A,(DUMPTOT) ;ANY CHRS TO DUMP?
+ OR A
+ RET Z
+ LD B,A
+ XOR A
+ LD (DUMPTOT),A
+ LD HL,CHRDUMP
+ LD C,>DISPSCREEN^>BACKSCREEN
+CDUMP LD E,(HL)
+ INC L
+ LD A,(HLI)
+ XOR C
+ LD D,A
+ LD A,(HLI)
+ LD (DE),A
+ DEC B
+ JR NZ,CDUMP
+ RET
+```
+
+---
+## Scene format (maps, cherries, apples, food)
+The map/scene data (`SCENE1`..`SCENE10`) is a compact stream consumed by `DRAWMAP`.
+At a high level, each scene contains:
+
+Part | Encoding | Consumed by
+---|---|---
+Mr Do start + initial tunnel | 4 bytes: `MRDO_Y, MRDO_X, TUNNEL_Y, TUNNEL_X` | `DRAWMAP` then `DOTUNNEL`
+Tunnel strokes | Repeating triples: `START_BLOCK, DIRECTION, LENGTH` terminated by `0xFF` | `DOTUNNEL` (draws a series of 2x2 blocks using `VECTAB2`)
+End cap block | 1 byte: `END_BLOCK` | `ENDTUNNEL` (draws one final 2x2 block)
+Cherry placements | Repeating pairs: `Y, X` terminated by `0xFF` | `PUTCHERRY` (draws a 2x2 cherry block at 4 offsets and increments `CHERRYTOT`)
+Apple placements | Repeating pairs: `Y, X` terminated by `0xFF` | `PUTAPPLES` (initializes apple records and draws apple blocks)
+Food placement | 3 bytes: `Y, X, UNDERLAY_BLOCK_INDEX` | `PUTFOOD` (draws the food block, then patches the underlay in `BACKSCREEN`)
+
+This diagram shows the scene stream at a glance:
+```mermaid
+flowchart TB
+ H["Header: MRDO_Y, MRDO_X, TUNNEL_Y, TUNNEL_X (4 bytes)"] --> T["Tunnel strokes: (START_BLOCK, DIR, LEN) repeated"]
+ T --> TEND["0xFF terminator"]
+ TEND --> E["End cap: END_BLOCK (1 byte)"]
+ E --> C["Cherry placements: (Y, X) repeated"]
+ C --> CEND["0xFF terminator"]
+ CEND --> A["Apple placements: (Y, X) repeated"]
+ A --> AEND["0xFF terminator"]
+ AEND --> F["Food placement: (Y, X, UNDERLAY_BLOCK_INDEX) (3 bytes)"]
+```
+
+At the assembly level, the tunnel-stroke part of the stream is parsed by `DOTUNNEL` as a small self-recursing loop:
+```nasm
+DOTUNNEL LD A,(HLI) ;START BLOCK NUMBER
+ CP -1
+ RET Z
+ ADD A,A
+ ADD A,A
+ LD C,A
+ LD A,1
+ CALL DRAWBLOCK ;DRAW START BLOCK
+
+ LD A,(HLI) ;DIRECTION 0TO4
+ ADD A,A
+ LD B,A
+ ADD A,A
+ LD C,A ;C=BLOCK ADDR LOW
+ LD A,VECTAB2
+DRAWREP PUSH AF
+ LD L,B
+ LD A,(HLI) ;MOVE TO NEXT POS
+ ADD A,E
+ LD E,A
+ LD A,(HL)
+ ADD A,D
+ LD D,A
+ LD A,1
+ CALL DRAWBLOCK ;DRAW REPEAT BLOCK
+ POP AF
+ DEC A
+ JR NZ,DRAWREP
+ POP HL
+ JR DOTUNNEL
+```
+
+The control-layer that makes this practical is `BYTESCREEN` (`$CC00`), which is defined as `$100` bytes.
+That size is a strong hint that the gameplay logic is operating on a 16x16 grid of "macro cells" (each macro cell is a 2x2 set of 8x8 tiles, i.e. 16x16 pixels).
+
+`DRAWBLOCK` writes a macro-cell value into `BYTESCREEN`, and `DECODE` reads it (via `GETBYTEHI`) to choose what happens when Mr Do enters a cell.
+The values map directly onto the `EATJP` jump table:
+
+Value | Meaning | `EATJP` target
+---|---|---
+0 | Solid wall / gravel (not yet tunneled) | `EATWALL` (slows Mr Do down while chewing)
+1 | Tunnel / already-open cell | `EATUNNEL` (no-op)
+2 | Cherry | `EATCHERRY` (score + sequence bonus + decrements `CHERRYTOT`)
+3 | Apple | `EATAPPLE` (no-op here, apples are handled via the apple state machine)
+4 | Food | `EATFOOD` (score + palette/freeze + spawns ghosts/bonus monster behaviour)
+
+This is the core `BYTESCREEN` dispatch from `DECODE`, including the `EATJP` jump table:
+```nasm
+EATJP DEFW EATWALL ;00
+ DEFW EATUNNEL ;01
+ DEFW EATCHERRY ;02
+ DEFW EATAPPLE ;03
+ DEFW EATFOOD ;04
+
+ CALL GETBYTEHI
+ LD A,(HL) ;GET CONTROL BYTE
+ LD (HL),1 ;SET TUNNEL BYTE
+ ADD A,A
+ ADD A,EATJP
+ LD A,(HLI)
+ LD H,(HL)
+ LD L,A
+ CALL JPHL ;WORK EAT ROUTINE
+```
+
+The 2x2 meta-tile blocks themselves come from the `BLOCKS` table.
+These are not "gameplay types", they are tilemap stamps (four bytes each) built from tile-id groups like `ED`, `CN`, `DT`, `CH`, `FD`, and `AP0`:
+
+Block index | Purpose (from comments) | Typical use
+---|---|---
+`$00-$03` | Tunnel segments (U/R/D/L variants) | Repeated stamps for a tunnel stroke (`DOTUNNEL`)
+`$04-$07` | Tunnel ends (U/R/D/L) | Start/end caps for strokes and final end cap (`DOTUNNEL` / `ENDTUNNEL`)
+`$08-$0B` | Corners (TL/TR/BR/BL) | Corner shaping when building complex tunnels
+`$0C-$0F` | Walls (top/right/bottom/left) | Wall shaping and underlays
+`$10` | Cherry block | `PUTCHERRY` (placed as a 2x2 cluster of macro cells)
+`$11` | Food block | `PUTFOOD` (drawn in VRAM)
+`$12` | Apple block | `PUTAPPLES`
+`$13` | Middle / filler | Used as a special-case stamp
+
+The tunnel stroke direction encoding is consistent across the code:
+the direction byte is used to index `VECTAB2` (macro-cell steps of 2 tiles) and to select which tunnel segment block (`$00-$03`) to stamp repeatedly.
+In practice this behaves like a 4-way direction enum (up/right/down/left).
+
+The food placement code is worth reading closely because it shows the kind of "tight" control-flow you get in commercial LR35902 assembly.
+After drawing the food into VRAM using `DRAWBLOCK`, `PUTFOOD` computes the `BACKSCREEN` address for the same position and then jumps into the middle of `DRAWBLOCK` (`DRWBLOCK`) to stamp a 2x2 underlay block into the RAM mirror.
+To make the stack clean up properly, it pushes `AF` three times so the `POP DE`, `POP BC`, and `POP HL` epilogue inside `DRAWBLOCK` has something to consume.
+It is a tiny micro-optimization, but it is also a very "real world" example of trading readability for speed and code size.
+
+---
+## Tile ID taxonomy
+This codebase relies heavily on treating a tile ID as a semantic category, not just a graphic.
+Most comparisons are against the base constants that define the background tile groups:
+
+Constant | Value | Used as
+---|---|---
+`WL` | `$00` | "Wall/gravel" tile group used for initial fill and tunnel shaping
+`CH` | `$10` | Cherry tile group
+`ED` | `$14` | Tunnel edge tile group (used by chewing, apple deformation, and ball bounce tables)
+`CN` | `$1C` | Corner tile group
+`DT` | `$20` | Dots/walkable tile group (also used by passability tests)
+`WT` | `$24` | A single special tile labelled "WHITE CHR!"
+`FD` | `$25` | Food tile group (background)
+`AP0` | `$5C` | Apple tile group (background)
+
+When you see code doing things like `CP DT+3` or `CP ED+7`, it is not doing collision against an object.
+It is testing whether the background tile under an actor belongs to one of these groups.
+You will also see a common trick in passability checks: it ORs the tile with `1` (`tile|1`) before comparing, which makes even/odd variants of an edge tile compare the same without a second branch.
+
+There are similar "semantic tile ID" patterns on the sprite side.
+For example, the 2x2 sprite expansion uses `CHRTABLE` to translate an animation frame index into four tile IDs plus per-quadrant flags (flip, palette, etc.).
+
+---
+## Chew algorithm deep dive
+The chewing system is split between "gameplay semantics" (what happens when you enter a macro cell) and "visual updates" (how tiles are rewritten).
+
+This diagram shows those two layers side-by-side:
+```mermaid
+flowchart LR
+ subgraph "Gameplay semantics"
+ DECODE["DECODE"] --> BYTES["Read BYTESCREEN macro-cell value"]
+ BYTES --> EATJP["EATJP jump table"]
+ EATJP --> EFFECTS["Score/timers/palette/freeze side effects"]
+ end
+
+ subgraph "Visual updates"
+ CHEW["MRDOCHEW"] --> BACK["Rewrite BACKSCREEN (RAM mirror)"]
+ CHEW --> DUMPQ["Append dirty tiles to CHRDUMP (via DUMPTOT)"]
+ SPLIT["SPLITSCREEN (VBlank)"] --> FLUSH["CHRDUMPER flushes to DISPSCREEN (VRAM tilemap)"]
+ end
+
+ BACK --> FLUSH
+ DUMPQ --> FLUSH
+```
+
+At the semantic level, `DECODE` reads a macro-cell value from `BYTESCREEN` and dispatches via `EATJP`:
+* **0** - `EATWALL` slows movement (sets `SPEED` and `SPDCOW`) while you are chewing.
+* **1** - `EATUNNEL` does nothing (already-open cell).
+* **2** - `EATCHERRY` decrements `CHERRYTOT` and adds score, including a small sequence bonus controlled by `CHERRYBON`/`CHERRYDEL`.
+* **3** - `EATAPPLE` is a no-op here (apples are driven by the apple state machine).
+* **4** - `EATFOOD` adds score and triggers the "food mode" effects (palette change + `FREEZE` + extra/ghost behaviour).
+
+At the visual level, `MRDOCHEW` performs a 2x2 macro-cell rewrite into `BACKSCREEN`, and queues the corresponding `DISPSCREEN` updates for VBlank:
+* **Grid gating** - It only chews when the mouth position is aligned to an 8-pixel boundary (`(x|y)&7 == 0` after a small offset).
+* **Allocate dirty slots** - It uses `DUMPTOT` as an index into `CHRDUMP`, increments it by 4, and computes `HL` so there is room for four tile updates.
+* **Resolve direction** - It dispatches through `CHEWJP` based on Mr Do's facing direction (`MRDOSP+FLG`).
+* **Rewrite a 2x2** - Each `CHEW*` routine computes four replacement tiles using direction-specific tables (`UTL/UTR/UBL/UBR`, `LTL/LTR/LBL/LBR`, `DTL/DTR/DBL/DBR`, etc.), writes the new tiles into `BACKSCREEN`, and writes four `(addrLo, addrHi, tile)` triples into `CHRDUMP`.
+* **VBlank flush** - `CHRDUMPER` runs during the next `SPLITSCREEN` and applies each queued tile to `DISPSCREEN`.
+
+The core reason this is robust is that `BACKSCREEN` is treated as the canonical map state.
+VRAM only gets updated in bursts via `CHRDUMPER`, which keeps the chew logic simple and makes it easy to reproduce in a reimplementation.
+
+---
+## Apple state machine
+Apples are driven by a compact state machine very similar to the enemy and ball systems.
+Each apple is a fixed-size record in `APPLESP`, and `APPLEPIE` iterates `APNUM` entries and dispatches via `APPLETAB` based on `TYP`.
+
+The apple states are:
+
+Value | Meaning | Update routine
+---|---|---
+0 | Inactive slot | `NOAPPLE`
+1 | Waiting / on the map | `APPLEWAIT`
+2 | Wobble ("jig") before falling | `APPLEJIG`
+3 | Falling | `APPLEFALL`
+4 | Splitting / impact animation | `APPLESPLIT`
+
+`APPLEWAIT` does a very cheap support test by reading the two tiles under the apple (left and right) from the background mirror.
+If either tile is less than `CH+4`, it treats that as "solid" and the apple does not fall.
+If both tiles look passable, it increments `TYP` and starts a 60-frame wobble.
+
+`APPLEJIG` uses `SPEEDFLAG` bit 2 (`%100`) to toggle the tile index (`AP0` vs `AP0+4`), which gives you a free shake animation without moving the apple.
+
+`APPLEFALL` is the most interesting part because it is integrated with the dirty-tile system:
+* **Rate control** - It uses `APPLEAND` masked with `SPEEDFLAG` to slow the fall rate (difficulty scaling).
+* **Background restore** - It queues tile restores into `CHRDUMP` so the old apple stamp is erased in the next VBlank.
+* **Wall deformation** - After a "critical point" (when the falling counter reaches 3), it starts modifying the edge tiles it passes through using `LAFALL` and `RAFALL`.
+
+`APPLESPLIT` enforces that only one apple can do the expensive 4-tile split write per frame using the `SPLAT` flag ("done once").
+That is a very pragmatic performance guard: without it, multiple apples impacting in the same frame would explode the dirty-tile list and VRAM work.
+
+---
+## Ball states and bounce tables
+The ball is implemented as another actor type dispatched via `BADTAB`, with multiple states:
+carried (`CARRYBALL`), thrown (`THROWBALL`), spinning out (`OUTBALL`), and returning (`INBALL`).
+
+When carried, the ball is positioned relative to Mr Do using the facing direction and a small offset table.
+The key tables are:
+
+Table | Role | Notes
+---|---|---
+`BALLOFF` | Base XY offset from Mr Do | Indexed by direction
+`BALLXY` | Extra 1-pixel offsets | Gives a 4-frame wobble animation
+`BALLVEC` | Movement deltas | Maps direction to `(dx, dy)` at `BALLSPEED`
+
+When thrown, `THROWBALL` is deliberately grid-gated:
+it only does a bounce decision when `(x+4)&7 == 0` or `(y+4)&7 == 0`.
+On those alignments it reads the contacted tile ID from the background mirror:
+* **Wall/cherry class** - If the tile is less than `CH+4`, it always flips direction (`dir ^= 2`).
+* **Bounce lookup** - Otherwise it scans a per-direction list of bounce-trigger tiles (`BALLCPS`) and uses a parallel bounce table (`BALLBOU`) to pick the new direction.
+ One of the bounce entries has bit 7 set, enabling a small `RND3` perturbation that is explicitly commented as preventing the ball from getting trapped in repeatable ricochet loops.
+
+This is the bounce decision core inside `THROWBALL`:
+```nasm
+ LD L,E
+ LD H,D
+ CALL GETMAPHI ;HL=SCRN3 ADDR
+
+ LD A,(HL) ;GET CHR
+ CP CH+4 ;WALL OR CHERRY?
+ JR NC,NOCHWL
+ LD A,B ;THEN ALWAYS FLIP DIRECTION
+ XOR 2
+ LD B,A
+ JR NOCHN
+
+NOCHWL PUSH DE
+ LD E,A
+
+ LD A,B ;GET LAST DIRECTION
+ ADD A,A
+ LD L,A
+ ADD A,A
+ ADD A,L
+ ADD A,BALLCPS
+
+ LD D,6 ;TOTAL NUMBER OF CHRS TO CHECK
+BCHECK LD A,(HLI) ;HAVE WE HIT A VALID CHR?
+ CP E
+ JR NZ,NOBHIT
+
+ LD A,BALLBOU-BALLCPS-1
+ ADD A,L
+ LD L,A ;INDEX BOUNCE VECTORS
+ LD A,(HL) ;GET NEW BOUNCE DIRECTION
+ BIT 7,A ;BIT OF RND?
+ JR Z,NRNDB
+
+ LD B,A
+ LD A,(RND3) ;STOPS BEING TRAPPED!
+ AND 2
+ ADD A,128
+ XOR B
+
+NRNDB LD B,A
+ POP DE
+```
+
+After a kill, the ball enters a circular spin-out phase (`OUTBALL`).
+This uses the `CIRCLE` routine (lookup table + quadrant xor) and a small multiply trick (`MULTIE` / `MULTID`) to scale the circle output, then adds that to a stored centre position.
+When the counter reaches a threshold it transitions into `INBALL` and eventually reattaches to Mr Do.
+
+---
+## Timing, VBlank, and the window split
+Most of the rendering safety in this codebase comes from two tiny wait primitives:
+
+Routine | Mechanism | Used for
+---|---|---
+`WAITBLANK` | Sets `LYC=144` and busy-waits for `STAT` bit 2 (LYC=LY) | Entering VBlank before touching VRAM/tilemaps
+`WAITSYNC` | Sets `LYC=A` and busy-waits for `STAT` bit 2 | Scheduling mid-frame changes (like the status/gameplay split)
+
+This is the full implementation of both waits in `mrdo.asm`:
+```nasm
+WAITBLANK LD A,144
+WAITSYNC LD (LYC),A
+WAITSC LD A,(STAT)
+ BIT 2,A
+ JR Z,WAITSC
+ RET
+```
+
+This diagram shows the split-screen timing model across a frame:
+```mermaid
+flowchart TB
+ VBL["LY=144: WAITBLANK enters VBlank"] --> DMA0["DMA STATUSOBJ (write $FF46)"]
+ DMA0 --> WINON["LCDC=0xE3 (window on for status strip)"]
+ WINON --> VBWORK["VBlank work: PRSCORE + CHRDUMPER + PRAPPLES"]
+ VBWORK --> SYNC16["WAITSYNC to LY=16 (write $FF45=16)"]
+ SYNC16 --> DMA1["DMA GAMEOBJ (write $FF46)"]
+ DMA1 --> WINOFF["LCDC=0xC3 (window off for gameplay)"]
+ WINOFF --> RUN["Gameplay scanlines: LY 16..143"]
+ RUN --> NEXT["Next frame"]
+```
+
+`SPLITSCREEN` combines these waits with `LCDC` writes to effectively toggle the status window on and off within a single frame.
+This is also where OAM DMA happens, so if you are debugging timing issues in an emulator, `WAITBLANK`, `WAITSYNC`, and writes to `LCDC`/`DMA` are the most information-dense breakpoints you can set.
+
+The split itself is implemented as a two-phase OAM DMA swap:
+* **VBlank entry** - `WAITBLANK`, then OAMDMA `STATUSOBJ` so scanlines 0..15 use the status OAM list.
+* **Status mode** - Enable the window with `LCDC=0xE3` and do VBlank work (`PRSCORE`, `CHRDUMPER`, `PRAPPLES`).
+* **Boundary sync** - `WAITSYNC` to `LY==16` (a 16-pixel strip).
+* **Gameplay swap** - OAMDMA `GAMEOBJ`, then disable the window with `LCDC=0xC3` for the rest of the frame.
+
+The DMA trigger itself is a tiny stub (`DMATRANS`) that `SYSETUP` copies into HRAM (`$FF80`) and calls via the `BLITS` label.
+This is the classic safe-DMA pattern: during OAM DMA the CPU can still execute from HRAM even though most other memory access is blocked.
+
+This is the stub and the `SYSETUP` copy loop that installs it into `INTRAM` (`$FF80`):
+```nasm
+DMATRANS DI
+ LD (DMA),A
+ LD A,40
+DMAL DEC A
+ JR NZ,DMAL
+ EI
+ RET
+
+ LD HL,DMATRANS ;SETUP DMA TRANS ROUTINE
+ LD DE,INTRAM
+ LD B,SYSETUP-DMATRANS
+TOINTRAM LD A,(HLI)
+ LD (DE),A
+ INC E
+ DEC B
+ JR NZ,TOINTRAM
+```
+
+One caveat when rebuilding this release is that `SYSETUP` enables interrupts (`IE=1` then `EI`) but the file does not obviously define an interrupt handler ending in `RETI`.
+If you try to assemble/port this code, verify what ends up at the interrupt vectors (especially `$0040`) before leaving IME enabled.
+
+---
+## SameBoy debugger walkthrough
+If you want to verify the claims above, SameBoy's textual debugger is a good fit because it supports write watchpoints and conditional expressions [^5].
+
+To use the textual debugger you generally:
+* **Pause** - Press Control+C (or use the `interrupt` command).
+* **Instrument** - Set breakpoints and watchpoints.
+* **Run** - Use `continue` and let the emulator stop at interesting writes.
+
+### Suggested watchpoints
+These watchpoints catch the most important hardware edges and RAM mirrors:
+
+Target | Why it matters | SameBoy command
+---|---|---
+`$FF46` | OAMDMA trigger (writes happen in `SPLITSCREEN`) | `watch/w $ff46`
+`$FF40` | `LCDC` mode changes (window on/off and LCD state) | `watch/w $ff40`
+`$FF45` | `LYC` scheduling for mid-frame timing (`WAITSYNC`) | `watch/w $ff45`
+`$C800-$CBFF` | `BACKSCREEN` RAM mirror (canonical background state) | `watch/w $c800 to $cbff`
+`$CC00-$CCFF` | `BYTESCREEN` macro-cell control map (0..4 for `EATJP`) | `watch/w $cc00 to $ccff`
+
+If the `BACKSCREEN` and `BYTESCREEN` ranges are too noisy, narrow them temporarily to what you are currently testing (or delete/re-add watchpoints as needed).
+
+### Lab 1 - Prove the window split
+This lab is just enough to prove `SPLITSCREEN` is doing a mid-frame OAM DMA swap and toggling the window:
+* **Instrument** - `watch/w $ff46` and `watch/w $ff40`.
+* **Run** - `continue` and let SameBoy stop on `$ff46`.
+* **Confirm the double-hit** - Continue a few times and you should see `$ff46` hit twice per frame (status OAM, then gameplay OAM).
+* **Confirm the mode change** - When `$ff40` hits, check if the value matches the two modes described above (`0xE3` vs `0xC3`).
+
+### Lab 2 - Prove chewing is BYTESCREEN + BACKSCREEN + CHRDUMP
+This lab connects "macro-cell semantics" to "tilemap updates":
+* **Instrument** - `watch/w $cc00 to $ccff` and `watch/w $c800 to $cbff`.
+* **Chew a wall** - Walk into an unchewed area and wait for a `$cc00-$ccff` write (macro-cell type changes).
+* **Watch the mirror update** - Continue and you should see `$c800-$cbff` writes as edge tiles are rewritten in `BACKSCREEN`.
+* **Watch the flush (optional)** - Temporarily add `watch/w $9800 to $9bff` and you should see the queued updates land in VRAM during the next `SPLITSCREEN`.
+
+### Debugging the window split and OAM DMA
+To see the split-screen renderer in action, do this:
+* **Start running** - Use `continue`.
+* **Stop on OAM DMA** - The `$ff46` watchpoint should hit twice per frame (status DMA, then gameplay DMA).
+* **Inspect state** - Use `lcd`, `dma`, and `registers`, then `disassemble/32 pc` to see the immediate code path.
+
+Useful debugger commands at those stops are:
+```text
+lcd
+dma
+registers
+disassemble/32 pc
+```
+
+If you want to focus on the moment the window toggles, use a conditional watchpoint on `LCDC`:
+```text
+watch/w $ff40 if new != old
+```
+
+### Debugging tunnel chewing and dirty-tile flushes
+To connect Mr Do chewing with background writes:
+* **Stop on `BYTESCREEN` writes** - Move into a wall and watch for writes into `$cc00-$ccff` (macro-cell type changes).
+* **Stop on `BACKSCREEN` writes** - Watch for tunnel edge tiles being rewritten in `$c800-$cbff`.
+* **Observe the VBlank flush** - If you temporarily watch `DISPSCREEN` (`$9800-$9BFF`), you should see the queued updates being applied during the next `SPLITSCREEN` when `CHRDUMPER` runs.
+
+If you do want to watch the VRAM tilemap itself, limit it to short bursts because it is very high traffic:
+```text
+watch/w $9800 to $9bff # DISPSCREEN (VRAM tilemap) - expect lots of hits
+```
+
+### Debugging apples
+Apple falling is a good demonstration of this engine's "update RAM mirror, flush in VBlank" strategy.
+When an apple transitions from `APPLEWAIT` to `APPLEFALL` you should see:
+* **`BACKSCREEN` writes** - The apple punches through tunnel edge tiles (via `LAFALL`/`RAFALL` after the critical point).
+* **Dirty-tile flush** - The queued updates are applied in the next VBlank when `CHRDUMPER` runs.
+
+### Debugging ball bounces
+Ball bounces are easiest to catch by letting the `$c800-$cbff` watchpoint stop you while the ball is in flight, then stepping until you hit a bounce decision and noting the current tile ID being compared against `BALLCPS`.
+
+In practice the bounce decision points are rare because `THROWBALL` only checks collisions on an 8-pixel grid alignment.
+If you are not seeing interesting stops, throw the ball into a dense area of tunnel corners/walls and let it ricochet.
+
+---
+## Enemy AI is shared and table-driven
+The baddie update loop (`BADDIES`) is structurally very similar to the apple loop:
+* **Update dispatch** - `BADTAB` maps `TYP` to the update routine for dinos, ghosts, the bonus monster states, ball states, and score popups.
+* **Junction decisions** - `FINDEXITS` scans the 2x2 neighbourhood around an actor on 8-pixel boundaries and returns an exit bitmask.
+* **Direction selection** - `WORKEXITS` validates the current direction (via `VECTOBIT`) and then chooses a new one using a bounded retry loop against `BITAB`/`BITTOVEC`, stirred by `RND1`/`RND2`.
+
+This is a nice example of how multiple enemies can share navigation logic while still having distinct "animation and special case" behaviour per type.
+
+One detail that helps when tracing enemy movement is the exit bit layout.
+`FINDEXITS` sets bits in the returned mask in a slightly non-obvious order:
+bit 3 is up, bit 0 is right, bit 2 is down, and bit 1 is left.
+That ordering matches the `VECTOBIT` and `BITTOVEC` tables used by `WORKEXITS`.
+
+The other critical detail is that passability is decided purely by tile IDs in `BACKSCREEN`.
+`FINDEXITS` effectively whitelists a handful of `DT+*` and `ED+*` variants:
+* **Up** - tile is `DT+1` or `DT+3` or `(tile|1) == ED+3`
+* **Right** - `(tile|1) == DT+3` or `(tile|1) == ED+5`
+* **Down** - tile is `DT+0` or `DT+2` or `(tile|1) == ED+7`
+* **Left** - tile is `DT+1` or `(tile|1) == ED+1`
+
+This is the full `FINDEXITS` tile-whitelist routine, including the `%UDLR` bit layout:
+```nasm
+;DE=XY RETS A=%UDLR BITS
+FINDEXITS PUSH DE
+ LD L,E
+ DEC L
+ LD H,D
+ DEC H
+ CALL GETMAPHI
+ LD DE,$0100
+
+ LD A,(HLI) ;TOP CHR...
+ CP DT+1
+ JR Z,ISUP
+ CP DT+3
+ JR Z,ISUP
+ OR D
+ CP ED+3
+ JR NZ,NOUP
+ISUP SET 3,E
+NOUP LD A,(HL) ;R CHR...
+ OR D
+ CP DT+3
+ JR Z,ISRT
+ CP ED+5
+ JR NZ,NORT
+ISRT SET 0,E
+NORT LD A,L
+ ADD A,32
+ LD L,A
+ ADC A,H
+ SUB L
+ LD H,A
+ LD A,(HLD) ;BOT CHR...
+ CP DT+0
+ JR Z,ISDW
+ CP DT+2
+ JR Z,ISDW
+ OR D
+ CP ED+7
+ JR NZ,NODW
+ISDW SET 2,E
+NODW LD A,(HL) ;L CHR...
+ OR D
+ CP DT+1
+ JR Z,ISLF
+ CP ED+1
+ JR NZ,NOLF
+ISLF SET 1,E
+NOLF LD A,E
+ POP DE
+ RET
+```
+
+`WORKEXITS` then adds a few pragmatic behaviours:
+* **Keep direction if possible** - If the current direction remains valid, it usually keeps going.
+* **Forced randomness** - Even when a direction is valid, it forces a re-pick roughly 1/8 of the time (`RND2 & 7 == 0`).
+* **Bounded search** - When it must pick a new direction, it tries up to 4 candidates.
+
+This is the core of `WORKEXITS`, showing how it keeps direction when possible and otherwise picks a new one using `BITAB` and `BITTOVEC`:
+```nasm
+WORKEXITS PUSH DE
+ LD D,L
+ LD E,A ;TEMP EXITS
+
+ LD A,B ;GET OLD DIR
+ ADD A,VECTOBIT
+ LD A,(HL)
+ AND E ;IS DIR AN OPTION
+ JR Z,CHANGEDIR
+
+ LD A,(RND2) ;RND MOVE
+ AND 7
+ JR NZ,DINODE
+
+CHANGEDIR LD A,(RND1)
+ ADD A,D
+ SRL A
+ SRL A
+ SRL A
+FINDEX LD D,A
+ AND 3
+ ADD A,VRAM
+`BGSET` | `$9000` | Background tile data base in VRAM
+`DISPSCREEN` | `$9800` | Background tilemap for gameplay
+`STATSCREEN` | `$9C00` | Background tilemap for the status window
+`BACKSCREEN` | `$C800` | RAM buffer used for background work
+`BYTESCREEN` | `$CC00` | Small RAM buffer used as scratch / temp
+`OAMRAM` | `$FE00` | OAM
+`INTRAM` | `$FF80` | HRAM
+
+## Sprite records
+The game uses fixed-size records in WRAM to represent sprites and "actors".
+The comments in the `SPRITES` block give the layout, and you can see the same pattern repeated in multiple systems (Mr Do, dinos/ghosts, apples).
+
+The sprite record fields are:
+
+Field | Offset | Purpose
+---|---|---
+`TYP` | 0 | Actor type (used as an index into jump tables)
+`YNO` | 1 | Y position (pixel units)
+`XNO` | 2 | X position (pixel units)
+`GNO` | 3 | Base tile index / graphics selector
+`FLG` | 4 | Flags (palette, flip, priority, etc.)
+`ADL` | 5 | Pointer / address low byte (varies by actor)
+`YSD` | 6 | Y speed / delta
+`XSD` | 7 | X speed / delta
+
+---
+# Main loop and jump-table pattern
+At the top level the program flow is very direct:
+* **Boot** - `START` sets up the stack, calls `SYSETUP`, runs the menu, and resets the game state.
+* **Per level** - `LEVELSETUP` prepares graphics and variables and then drops into `MAINLOOP`.
+* **Per frame** - `MAINLOOP` calls the major gameplay subsystems (input, Mr Do movement/eating, baddies, collisions, apples, sprite dumping, timers, RNG, and flag updates).
+
+One of the most reusable techniques in the file is the jump-table driven state machine.
+For example, `BADTAB` maps each actor type to its update routine, and `APPLETAB` does the same for apple states.
+This is a good pattern to steal when writing your own LR35902 assembly because it keeps the hot-path branch structure compact.
+
+---
+# Rendering, split-screen, and DMA
+The code uses two different "sprite worlds" and then does a timed swap:
+* **Status sprites** - A separate OAM-shaped buffer (`STATUSOBJ`) is DMA'd first, with the LCDC configured so the status window is enabled.
+* **Gameplay sprites** - After updating the status line graphics, the code waits a fixed amount of time and then DMA's the gameplay sprite list (`GAMEOBJ`) and disables the status window again.
+
+This is orchestrated by `SPLITSCREEN`, and it relies on a tiny DMA-trigger routine being copied into HRAM during `SYSETUP`.
+The routine is then called via the `BLITS` label, passing the source high-byte in `A` before writing to `DMA` (`$FF46`) [^3].
+
+If you are reverse engineering the ROM in an emulator, the easy breakpoint targets are:
+* **OAM DMA** - writes to `$FF46` (DMA).
+* **Mode changes** - writes to `LCDC` (`$FF40`) to enable/disable the window.
+
+---
+# Map format and 2x2 meta-tiles
+The background is assembled from 2x2 blocks of tile IDs.
+The `BLOCKS`, `CORNERS`, `CHERRY`, `FOOD`, and `APPLE` tables each store 4 bytes in the order "top-left, top-right, bottom-left, bottom-right".
+
+There are also multiple "eat tables" (for example `UTL`, `UTR`, `UBL`, `UBR`) that appear to define the replacement tiles to use when Mr Do chews through walls in a particular direction.
+If you are trying to re-implement or rewrite the map system, these tables are a good anchor for reconstructing the exact tile semantics.
+
+---
+# RNG and text routines
+The RNG is a compact 3-byte state (`RND1`, `RND2`, `RND3`) stirred each frame and mixed with the `DIV` hardware register.
+It is small enough that you can single-step it and see how entropy flows into map generation (for example the gravel fill in `DRAWMAP`).
+
+The text routines are also worth a quick look because they show a very practical "engine" approach:
+strings are stored in a compact custom format, and `PRINTEXT` renders them directly into the background tilemap.
+
+---
+# Assembling it today
+The source uses an older assembler dialect with directives like `DEFB`, `DEFW`, `DEFS`, `HEX`, `ORG`, and `ENT`.
+That means you should not expect it to assemble cleanly with modern `rgbds` without some conversion work [^4].
+
+## Original Assembler
+The custom assembler for this file was either Special FX's own Gameboy assembler or Ocean's own Atari ST based assembler according to Paul Hughes in his tweet [^7]:
It was started on Special FX's own Gameboy assembler and hardware and was finished on Ocean's own Atari ST based assembler and hardware.
+It would be great to find out more about either Special FX or Ocean's development kit hardware, so please get in touch if you have any information.
+
+If you want to get it building as an exercise, a reasonable approach is:
+* **Start with `rgbds` scaffolding** - Create a ROM0 header section and make sure the reset entry and cartridge header bytes land at the expected addresses [^3][^4].
+* **Convert directives mechanically** - Map `DEFB`/`DEFW`/`DEFS` to `db`/`dw`/`ds`, and replace `HEX` blocks with `db $..` sequences.
+* **Replace `ORG` with sections** - Translate fixed `ORG` placements into `SECTION` blocks pinned to ROM0/ROMX addresses.
+* **Validate in an emulator** - Use breakpoints on `$FF46` and `LCDC` to confirm you are hitting the same high-level flow as the original.
+
+Also note that the in-file cartridge header comments claim a 256K ROM, but the ROM size byte in the header is written as `0`.
+If you do attempt a rebuild, you will need to reconcile those fields with the actual output ROM size.
+
+## RGBDS conversion script
+We have written a best-effort converter that keeps the original `mrdo.asm` untouched and produces a RGBDS-parseable `.asm` file:
+
+
+What it does:
+* converts `EQU`/`DEFB`/`DEFW`/`DEFS`, turns `HEX` into `db $..`, and maps each `ORG` to an explicit `SECTION`.
+* **Heuristic bank split** - The converter includes a pragmatic split so the output links as a simple ROM-only build: `ORG $800` becomes fixed ROMX bank 1 code, and the `SCENE1`+ data block is placed back into ROM0 at `$0800`.
+* **Retail-style profile** - The converter also supports a `--profile retail-mbc1` mode that attempts to produce a 64 KiB `MBC1`-style image by placing a handful of large asset blocks into banks that match signature hits in the retail ROM.
+* **Main limitation** - The original toolchain could pre-initialize RAM, but RGBDS cannot, so WRAM/HRAM sections in the output are primarily for symbol addresses (you still need real init/copy code for a working rebuild).
+* **Interrupt vector stubs** - The converter injects `reti` stubs at `$0040/$0048/$0050/$0058/$0060` because the release enables interrupts (`IE=1` then `EI`) but does not define handlers in the vector table. Without this, a rebuild will often crash or "flash" as soon as VBlank fires [^3].
+
+One thing to keep in mind is that this converted build is not trying to match the retail ROM layout.
+The retail Mr Do! ROM is a banked `MBC1` cartridge and is effectively a 64 KiB-class image (4 x 16 KiB banks), while the current converter is set up to link a minimal 2-bank layout so you can explore the code paths quickly.
+To produce a ROM image that matches retail size and banking, you will need to:
+* split the file into multiple `ROMX` banks (and use a MBC header / `rgbfix` flags that match the chosen banking model) and
+* reconcile which content belongs in which bank by comparing against a known-good retail ROM (checksums + bank contents).
+
+
+### Installing RGBDS
+If you want to run the `rgbasm`/`rgblink` sanity-check commands locally, install RGBDS first:
+* **macOS** - Install via Homebrew (`brew install rgbds`) [^8].
+* **Linux** - Install via your distro package manager, or use the official release tarball + `install.sh` [^9].
+* **Windows** - Use WSL and follow the Linux instructions, or use the official `.zip` release and add it to your `PATH` [^10].
+
+To generate a converted file:
+```bash
+python3 scripts/convert-mrdo-to-rgbds.py mrdo.asm build/mrdo.rgbds.asm
+```
+
+If you have RGBDS installed, you can sanity-check that the output parses and links:
+```bash
+rgbasm -o build/mrdo.o build/mrdo.rgbds.asm
+rgblink -m build/mrdo.map -n build/mrdo.sym -o build/mrdo.gb build/mrdo.o
+rgbfix -v -p 0xFF -m 0x00 -t "MRDO!" build/mrdo.gb
+```
+
+---
+### Comparing against a retail ROM
+Once you have a retail ROM to compare against, you can use the `scripts/compare-gb-roms.py` helper to quantify how close a rebuilt image is.
+It supports three useful comparisons:
+* **Header sanity** - title, MBC type, ROM size byte, plus recalculated header/global checksums.
+* **Coverage scan** - a fast sliding-window scan that estimates how much of the rebuilt ROM appears verbatim in the retail ROM (useful for data/graphics blocks that should match exactly).
+* **Signature search from a map** - take a `rgblink -m` map from the rebuilt ROM and search for `N`-byte sequences inside the retail ROM to find where specific labels land.
+
+For a retail-like 64 KiB build attempt:
+```bash
+python3 scripts/convert-mrdo-to-rgbds.py mrdo.asm build/mrdo.retail.rgbds.asm --profile retail-mbc1
+rgbasm -o build/mrdo.retail.o build/mrdo.retail.rgbds.asm
+rgblink -m build/mrdo.retail.map -n build/mrdo.retail.sym -o build/mrdo.retail.gb build/mrdo.retail.o
+rgbfix -v -p 0xFF -m MBC1 -t "MR.DO!" build/mrdo.retail.gb
+```
+
+Then compare it against your retail ROM:
+```bash
+python3 scripts/compare-gb-roms.py \
+ --rebuilt build/mrdo.retail.gb \
+ --original build/mrdo_original.gb \
+ --map build/mrdo.retail.map \
+ --scan-map \
+ --sig-len 128 \
+ --window 256 \
+ --strings
+```
+
+---
+#### Current repo comparison results
+This repository already includes a known-good retail ROM at `build/mrdo_original.gb`, plus two rebuild outputs (`build/mrdo.gb` and `build/mrdo.retail.gb`).
+As of `2026-04-12`, the headline results are:
+
+ROM | Size | Title | Cart type | SHA256
+---|---|---|---|---
+`build/mrdo_original.gb` | 64 KiB | `MR.DO!` | `MBC1` | `c19f7ec9ff29fa438d7ef189f81711dcaedaa55c86b192d6d9020f5f7dc22702`
+`build/mrdo.retail.gb` | 64 KiB | `MR.DO!` | `MBC1` | `d6e4a52072e4c6d4d1a34bd126617ce4340150cd351ddeafdcc7d842bbb6e4f3`
+`build/mrdo.gb` | 32 KiB | `MRDO!` | `ROM ONLY` | `bb824ead872abaf5055be48c42c01873bbda749afb400353682bea9bfc565fda`
+
+Both 64 KiB images use the same visible header identity (title `MR.DO!`, cartridge type `MBC1`, ROM size code `0x01`).
+However, neither rebuilt image matches the retail binary byte-for-byte:
+* **No exact bank matches** - none of the 16 KiB banks in `build/mrdo.retail.gb` are identical to any bank in `build/mrdo_original.gb`.
+* **High verbatim data coverage** - a 256-byte sliding window scan finds `49600/65536` bytes (75.7%) from `build/mrdo.retail.gb` somewhere in the retail ROM.
+* **Explore build coverage is lower** - the ROM-only explore build (`build/mrdo.gb`) still finds `17024/32768` bytes (52.0%) somewhere in the retail ROM, which is a useful sanity check that many assets are shared even when the overall layout differs.
+* **Different build lineage strings** - text like `"CLONE"` / `"TWINS"` / `"REMIX"` appears in the source-derived builds but does not appear in the retail ROM, which is a quick way to confirm you are not comparing a simple re-link.
+
+If you use `--scan-map` with a longer signature length (128 bytes), you do start getting "anchor" hits for a handful of large `HEX` blocks and assets.
+These are useful when tightening the bank-placement heuristic for a more retail-like layout:
+
+Symbol | Rebuilt bank:addr | Retail hit bank:addr
+---|---|---
+`CBADS` | `bank3:$4C00` | `bank1:4C00`, `bank3:4C00`
+`CHEADS` | `bank2:$4D60` | `bank2:4D60`
+`CHRTABLE` | `bank0:$0400` | `bank0:0400`
+`CICONS` | `bank2:$4B20` | `bank2:4B20`
+`CLOGO` | `bank2:$4080` | `bank2:4080`
+`CMRDO` | `bank3:$4000` | `bank3:4000`
+`CMRSDO` | `bank3:$4300` | `bank3:4300`
+`CSTAR` | `bank2:$47F0` | `bank2:47F0`
+`LOGO` | `bank0:$0E3B` | `bank0:0E3B`
+
+These numbers can look contradictory at first glance (for example: a symbol can have a 128-byte signature hit, while still sitting inside a "no 256-byte window match" region).
+That is simply an artifact of the window size: 128-byte runs can match without any 256-byte run matching.
+
+---
+#### Header-level differences
+The retail ROM's banking/layout is not described by the header block embedded in the released `mrdo.asm`.
+For example, the source's `ORG $100` header declares `ROM ONLY` and a ROM size code of `0`, while the retail ROM is a 64 KiB `MBC1` image.
+
+In this repository, the retail-style converter can splice the exact `$0100-$014F` header bytes from `build/mrdo_original.gb` into the rebuilt image so the header matches retail byte-for-byte.
+This is useful for isolating layout and bank-placement differences from header/metadata differences.
+If you run `rgbfix -v` afterwards, RGBDS will recalculate and overwrite the checksum fields (`$014D` and `$014E-$014F`), so the rebuilt header will no longer be byte-identical to retail even if the title/logo fields match.
+
+---
+#### Offset-aligned similarity vs relocated similarity
+There are two different "closeness" metrics that are both useful:
+* **Offset-aligned equality** - how many bytes are identical at the same file offsets.
+* **Window coverage** - how much of the rebuild appears verbatim *somewhere* in the retail ROM, even if it moved.
+
+For the current retail-style rebuild, only `16278/65536` bytes (24.8%) are identical at the same offsets, because large blocks have moved between banks and within banks.
+By contrast, the 256-byte window coverage scan finds 75.7% of rebuilt bytes occurring verbatim somewhere in retail (and 77.9% at a 128-byte window).
+
+Per-bank offset-aligned equality looks like this:
+
+Bank | Equal bytes at same offsets | Notes
+---|---|---
+bank0 | `1496/16384` (9.1%) | Header is identical, but ROM0 tables are still in flux
+bank1 | `1080/16384` (6.6%) | Bank 1 still differs heavily (code + tables)
+bank2 | `5560/16384` (33.9%) | Many assets line up at the same offsets
+bank3 | `8142/16384` (49.7%) | Largest "same-offset" overlap in this build
+
+---
+#### Why the banking/layout differs
+Even when large chunks match byte-for-byte, the bank placement and offsets can still diverge from retail.
+In practice, the biggest drivers are:
+
+* **Header vs actual cartridge** - the embedded header block in `mrdo.asm` does not reflect the retail cartridge (it declares `ROM ONLY` / size code `0` while retail is 64 KiB `MBC1`).
+* **Toolchain differences** - the original Special FX/Ocean build tools could place and pack data/code into banks differently from modern RGBDS, even when assembling the same logical source.
+* **`ORG` vs final placement** - a monolithic file with `ORG` anchors does not automatically encode the final bank split; a linker/pack step may have decided which blocks live in which bank.
+* **Late-stage layout tuning** - it is common for a final build to reorder/move blocks to reduce bank switches or reclaim space, without meaningfully changing the underlying logic.
+
+The net effect is that a build can have high "verbatim somewhere in ROM" coverage while still having low same-offset equality.
+
+---
+#### Why retail uses MBC1 even if a ROM-only build exists
+It is tempting to assume a `ROM ONLY` image implies the retail cartridge could have used a no-mapper PCB.
+In practice, a ROM-only rebuild can exist for several reasons that do not contradict a banked retail cart:
+
+* **The converter is a minimal build** - the current RGBDS output is aimed at making the release assemble, not reproducing every late-stage asset, layout, or build-flag that shipped.
+* **Retail likely had more content** - additional assets, levels, audio, bugfix code, region tweaks, or tool-generated tables can push a project past 32 KiB.
+* **Banking is also about engineering** - even when size is close, MBC banking lets you keep hot code/data in fixed areas and move large/rarely-used blocks into switchable banks.
+* **Production standardization** - studios often standardized on a cart/PCB type across multiple titles for sourcing and manufacturing, even if some games could technically squeeze into a smaller ROM.
+
+Treat the `ORG $100` header block in `mrdo.asm` as "what this particular source snapshot declares", not as authoritative evidence for the shipped cartridge configuration.
+
+---
+#### What is in the "no 256-byte match" regions
+The 256-byte window scan flags a few large regions where **no 256-byte chunk** matches anywhere in the retail ROM.
+To make those regions actionable, here are the notable map labels that live inside them:
+
+* `bank1:4000..bank1:56DE` - tables + core code, including `DMATRANS`, `SYSETUP`, and `START`.
+* `bank2:4EC0..bank2:56FF` - large graphics blocks `CBIGMRDO` and `CBIGAPPLES`.
+* `bank3:4C00..bank3:609F` - a large asset + menu cluster starting at `CBADS`, plus `MENU`/`SLOGO`/`OPTIONS` and friends.
+* `bank0:0000..bank0:03FF` - boot/interrupt-area differences (the converter emits minimal `reti` stubs rather than retail handlers).
+
+If you see:
+* **No exact bank matches** - that is normal when the layout does not match byte-for-byte.
+* **High window coverage** - that is a strong sign that many assets are identical, even if the overall banking/layout differs.
+* **Repeated signature hits for `HEX` labels** - that is usually where the retail build stores those graphics blocks, and it is a good next target for tightening the placement map.
+
+---
+### Rebuilt procedure map
+This table lists every procedure-style label detected in the converted RGBDS output, along with its rebuilt `bank:addr` location, so you can set breakpoints quickly:
+The retail columns are intentionally conservative and are only filled when a 32-byte signature from that label matches verbatim at a single location inside `build/mrdo_original.gb`.
+
+Procedure | Rebuilt bank:addr | Retail bank:addr | Retail file offset
+---|---|---|---
+`MULTIE` | `00:06A4` | |
+`MULTID` | `00:06A9` | |
+`MENU` | `00:363C` | |
+`SLOGO` | `00:3661` | |
+`RESETST` | `00:366F` | |
+`LOGOLOOP` | `00:3699` | |
+`WORKMENU` | `00:36B5` | |
+`EXITMENU` | `00:36D6` | |
+`OPTIONS` | `00:36E2` | |
+`NEXTUP` | `00:36FD` | |
+`NEXTMENU` | `00:3702` | |
+`NOGAME` | `00:370A` | |
+`NEWSEL` | `00:3715` | |
+`NOSEL` | `00:3723` | |
+`UPMENU` | `00:3732` | |
+`HANDYMAN` | `00:3743` | |
+`SOUNDOPT` | `00:375D` | |
+`DWMENU` | `00:3765` | |
+`LOGOPULSE` | `00:377B` | |
+`FADE` | `00:37D0` | |
+`LOGOON` | `00:37E0` | |
+`LOGON` | `00:37EB` | |
+`NOMOV` | `00:37FE` | |
+`BARREL` | `00:3803` | |
+`BARON` | `00:3810` | |
+`RASTARS` | `00:3823` | |
+`RASTAR` | `00:3832` | |
+`STARFALL` | `00:3845` | |
+`ANIHEAD` | `00:3872` | |
+`MENUOBJ` | `00:3894` | |
+`WORKSTAR` | `00:38AC` | |
+`STAROFF` | `00:38B7` | |
+`NOBOW` | `00:38E4` | |
+`DUMP3BY3` | `00:38F8` | |
+`SHOWRESULTS` | `00:395F` | |
+`SETBIG` | `00:3989` | |
+`RESLOOP` | `00:39B5` | |
+`APLM` | `00:39CE` | |
+`WORKWELL` | `00:3A1A` | |
+`EMOVE` | `00:3A30` | |
+`UPWELLY` | `00:3A3A` | |
+`UPWELL` | `00:3A3F` | |
+`ALLWAIT` | `00:3A47` | |
+`ALLMOVE` | `00:3A51` | |
+`DODIMOVE` | `00:3A58` | |
+`DUMPBIG` | `00:3A7E` | |
+`DUMPBG` | `00:3A93` | |
+`SHOWAVERAGES` | `00:3AA2` | |
+`PUTAVER` | `00:3ABF` | |
+`AVELOOP` | `00:3B10` | |
+`RESETBOARD` | `00:3B29` | |
+`RESETB` | `00:3B2E` | |
+`BOARDLINED` | `00:3B41` | |
+`BOARDLINE` | `00:3B61` | |
+`PUTSCORE` | `00:3B73` | |
+`TOTOTAL` | `00:3B9C` | |
+`UPTOTAL` | `00:3BA1` | |
+`DIGITADD` | `00:3BA6` | |
+`DUMPENDOBJ` | `00:3BB8` | `00:35F6` | `0x35F6`
+`DUMP2BY2SEQU` | `00:3BBD` | `00:35FB` | `0x35FB`
+`CHECKHIGH` | `00:3BE9` | |
+`CHECKLINE` | `00:3BEE` | |
+`CHECKCHR` | `00:3BF4` | |
+`NEXTLINE` | `00:3BFF` | |
+`ISBIG` | `00:3C08` | |
+`SHUNTLINE` | `00:3C11` | |
+`PUTDIG` | `00:3C20` | |
+`PRHIGHSCORES` | `00:3C39` | |
+`PRHIGHS` | `00:3C41` | |
+`PRHGH` | `00:3C85` | |
+`DMATRANS` | `01:40E9` | |
+`DMAL` | `01:40EF` | |
+`SYSETUP` | `01:40F4` | |
+`RESETV` | `01:4101` | |
+`TOINTRAM` | `01:4114` | |
+`GAMESETUP` | `01:4122` | |
+`LEVELSETUP` | `01:4141` | |
+`ISMISTER` | `01:4165` | |
+`RESETSP` | `01:4198` | |
+`SETAPPLES` | `01:41AB` | |
+`RESETEX` | `01:41BD` | |
+`SETET` | `01:420E` | |
+`START` | `01:4219` | |
+`MAINLOOP` | `01:4228` | |
+`EXITLEVEL` | `01:426A` | |
+`NOMAPR` | `01:4288` | |
+`NOLEVR` | `01:42A4` | |
+`WAITISH` | `01:42AA` | |
+`WINHOW` | `01:42B2` | |
+`EXTRALIFE` | `01:42BE` | |
+`SETEXTRA` | `01:42C8` | |
+`RESETET` | `01:42D3` | |
+`PAUSEGAME` | `01:42DE` | |
+`NOSTRT` | `01:42F3` | |
+`WAITNOK` | `01:4303` | |
+`SPLITSCREEN` | `01:430A` | |
+`PRAPPLES` | `01:434C` | |
+`PRALOOP` | `01:4352` | |
+`APPLEPIE` | `01:437F` | |
+`APLOOP` | `01:4388` | |
+`WORKAPPLE` | `01:439A` | |
+`NOAPPLE` | `01:43AD` | |
+`NOLFALL` | `01:43B0` | |
+`GOSPLIT` | `01:43B2` | |
+`APPLEWAIT` | `01:43BA` | |
+`APPLEJIG` | `01:43D5` | |
+`APPLEFALL` | `01:43E4` | |
+`LEEV0` | `01:4419` | |
+`LEEV1` | `01:442B` | |
+`FLOUT` | `01:442D` | |
+`HLOUT` | `01:443E` | |
+`REPBACK` | `01:4440` | |
+`STOPAFALL` | `01:444B` | |
+`APPLESPLIT` | `01:4455` | |
+`NOASPL` | `01:4461` | |
+`LEEV2` | `01:4493` | |
+`LEEV3` | `01:44A5` | |
+`BRINGON` | `01:44BE` | |
+`GETDINO` | `01:44D2` | |
+`GETSP` | `01:44D8` | |
+`GETAPPLE` | `01:44E3` | |
+`GETAP` | `01:44E9` | `00:1BE4` | `0x1BE4`
+`FLAGS` | `01:44F4` | `00:1BEF` | `0x1BEF`
+`NOT3` | `01:451B` | |
+`COLLISIONS` | `01:451F` | |
+`BALLCP` | `01:4522` | |
+`BALLCPL` | `01:4535` | |
+`NBALLCP` | `01:4551` | |
+`MRDOCATCH` | `01:455A` | |
+`GHOSTDIE` | `01:4563` | |
+`NOTLASTG` | `01:4574` | |
+`EXDIE` | `01:4582` | |
+`GAPPLE` | `01:45D6` | |
+`GOAPPLE` | `01:45DF` | |
+`GOBALLBANG` | `01:45FE` | |
+`BALLSCORE` | `01:4606` | |
+`DUFKILL` | `01:462B` | |
+`EATCHERRY` | `01:462D` | |
+`NOCHSEQU` | `01:4651` | |
+`RESCHSEQU` | `01:4656` | |
+`EATWALL` | `01:4668` | |
+`EATFOOD` | `01:4675` | |
+`GETLET` | `01:46A0` | |
+`GOTLET` | `01:46AB` | |
+`ISOUT` | `01:46D1` | |
+`PUTGHST` | `01:46D8` | |
+`SETGHST` | `01:46E1` | |
+`DECODE` | `01:46F5` | |
+`NOGODEL` | `01:4700` | |
+`NODECBON` | `01:470A` | |
+`DECODER` | `01:4734` | |
+`ISSLOW` | `01:4749` | |
+`NOSLOW` | `01:4751` | |
+`INVALIDX` | `01:4776` | |
+`NEWMOVE` | `01:4790` | |
+`NOWRAP` | `01:479D` | |
+`NOSCROLL` | `01:47C4` | |
+`INVALID` | `01:47DD` | |
+`DOBALL` | `01:47E7` | |
+`BADDIES` | `01:480E` | |
+`BADLOOP` | `01:4813` | |
+`WORKBADDIE` | `01:4835` | |
+`NOBAD` | `01:4847` | |
+`CIRCLE` | `01:484C` | `00:216A` | `0x216A`
+`MOVEBADF` | `01:4876` | |
+`MOVEBAD` | `01:487B` | |
+`FINDEXITS` | `01:4889` | |
+`NOUP` | `01:48A4` | |
+`NORT` | `01:48B0` | |
+`NODW` | `01:48C7` | |
+`NOLF` | `01:48D3` | |
+`WORKEXITS` | `01:48D6` | |
+`CHANGEDIR` | `01:48EA` | |
+`FINDEX` | `01:48F4` | |
+`GOEXIT` | `01:4902` | |
+`DINODE` | `01:4907` | |
+`DINO` | `01:4909` | |
+`DINOMOVE` | `01:491A` | |
+`DINOEAT` | `01:492A` | |
+`DINOPUSH` | `01:492C` | |
+`EXDANCE` | `01:492E` | |
+`NOEXFL` | `01:4941` | |
+`UPTYPE` | `01:4973` | |
+`EXUP` | `01:4979` | |
+`EXEAT` | `01:4985` | |
+`EXWALK` | `01:4987` | |
+`EXMOVE` | `01:4998` | |
+`EXOUT` | `01:49A1` | |
+`GHOST` | `01:49A3` | |
+`GHOSTMOVE` | `01:49B4` | |
+`GHOSTR` | `01:49C0` | |
+`GHOSTEAT` | `01:49C7` | |
+`CARRYBALL` | `01:49C9` | |
+`THROWBALL` | `01:49EE` | |
+`CDEL0` | `01:49F8` | |
+`ISCHN` | `01:4A06` | |
+`NOCHWL` | `01:4A16` | |
+`BCHECK` | `01:4A24` | |
+`NRNDB` | `01:4A3A` | |
+`NOBHIT` | `01:4A3D` | |
+`NOCHN` | `01:4A41` | |
+`OUTBALL` | `01:4A50` | |
+`GOINBALL` | `01:4A7D` | |
+`INBALL` | `01:4A87` | |
+`INBALL0` | `01:4A91` | |
+`INBALL1` | `01:4A9B` | |
+`POINTS` | `01:4AC3` | |
+`DUMPOBJ` | `01:4ACF` | |
+`MPLEX` | `01:4AEE` | |
+`DUMPL` | `01:4B14` | |
+`DUMP2BY1` | `01:4B29` | `00:283A` | `0x283A`
+`DUMP1BY1` | `01:4B4F` | |
+`DUMP2BY2` | `01:4B73` | `00:2884` | `0x2884`
+`DUMP2BY2S` | `01:4B86` | `00:2897` | `0x2897`
+`MRDOCHEW` | `01:4BCA` | |
+`GOCHEW` | `01:4BE1` | |
+`CHEWS` | `01:4BF2` | |
+`CHEWUP` | `01:4C14` | |
+`CHU0` | `01:4C21` | |
+`CHU1` | `01:4C32` | |
+`CHU2` | `01:4C49` | |
+`CHU3` | `01:4C5A` | |
+`CHEWRT` | `01:4C61` | |
+`CHR0` | `01:4C6E` | |
+`CHR1` | `01:4C7F` | |
+`CHR2` | `01:4C96` | |
+`CHR3` | `01:4CA7` | |
+`CHEWDW` | `01:4CAE` | |
+`CHD0` | `01:4CBB` | |
+`CHD1` | `01:4CCC` | |
+`CHD2` | `01:4CE3` | |
+`CHD3` | `01:4CF4` | |
+`CHEWLT` | `01:4CFB` | |
+`CHL0` | `01:4D0A` | |
+`CHL1` | `01:4D1B` | |
+`CHL2` | `01:4D32` | |
+`CHL3` | `01:4D43` | |
+`CHRDUMPER` | `01:4D4A` | |
+`CDUMP` | `01:4D59` | |
+`PRSCORE` | `01:4D64` | |
+`SCOREADD` | `01:4D81` | |
+`UPSCORE` | `01:4D97` | |
+`NOSCRP` | `01:4DA3` | |
+`CLOCK` | `01:4DA5` | |
+`UPCLOCK` | `01:4DC0` | |
+`STATUS` | `01:4DC5` | |
+`STATSP` | `01:4DDE` | |
+`PUTSSP` | `01:4DF4` | |
+`ONSTAT` | `01:4E1A` | |
+`DRAWMAP` | `01:4E22` | |
+`FILLMAP` | `01:4E28` | |
+`FILLBYTE` | `01:4E3F` | |
+`PUTFOOD` | `01:4E76` | |
+`DRAWBLOCK` | `01:4E91` | |
+`DRWBLOCK` | `01:4EA1` | |
+`DOTUNNEL` | `01:4EB9` | |
+`DRAWREP` | `01:4ED2` | |
+`ENDTUNNEL` | `01:4EE6` | |
+`PUTCHERRY` | `01:4EEF` | |
+`PUTAPPLE` | `01:4F1F` | |
+`COPYMAP` | `01:4F41` | |
+`COPYM` | `01:4F4A` | |
+`COPYLETTER` | `01:4F53` | |
+`PUTLINE` | `01:4F80` | `00:2CFB` | `0x2CFB`
+`KEYS` | `01:4FB6` | |
+`PIXAD` | `01:4FED` | `00:2D63` | `0x2D63`
+`GETMAPHI` | `01:5004` | |
+`GETMAPLODE` | `01:501B` | |
+`GETMAPLO` | `01:502D` | |
+`GETBYTEHI` | `01:503F` | `00:2DCC` | `0x2DCC`
+`GETBYTELO` | `01:504D` | |
+`LOWAD` | `01:5059` | |
+`WAITSC` | `01:5070` | |
+`RESETOBJ` | `01:5078` | |
+`RESETOB` | `01:5087` | |
+`RESETO` | `01:508B` | |
+`RAND` | `01:5097` | |
+`PRINTEXT` | `01:50B1` | `00:2E45` | `0x2E45`
+`TEXTL` | `01:50B9` | `00:2E4D` | `0x2E4D`
+`PRINTSHAPE` | `01:50C1` | |
+`YROWS` | `01:50C5` | |
+`XROWS` | `01:50C7` | |
+`DIVIDE` | `01:50DA` | |
+`DIVE` | `01:50DE` | |
+`CREATESET` | `01:50E6` | |
+`CLEARSET` | `01:50FB` | |
+`SHUNT` | `01:5106` | |
+`DISPBIN` | `01:510F` | |
+`DOBIN` | `01:5114` | `00:2EDA` | `0x2EDA`
+`ISNONE` | `01:511B` | `00:2EE1` | `0x2EE1`
+`CLEARSTAT` | `01:5120` | `00:2EE6` | `0x2EE6`
+`CLR` | `01:512B` | |
+`HEXBYTE` | `01:5134` | |
+`HEXWORD` | `01:5139` | |
+`PRHEX` | `01:5143` | |
+`PRDEC` | `01:5158` | |
+`PRDECDIGITS` | `01:5160` | |
+`PRDEC1` | `01:516B` | |
+
+---
+### Retail offsets for known verbatim code blocks
+If you want to map the retail ROM quickly, these labels have a unique 32-byte verbatim signature match inside `build/mrdo_original.gb` (found by scanning `build/mrdo.retail.map` against the retail binary):
+
+Label | Retail bank:addr | Retail file offset
+---|---|---
+`STATLINE` | `00:0D44` | `0x0D44`
+`LOGO` | `00:0E3B` | `0x0E3B`
+`GETAP` | `00:1BE4` | `0x1BE4`
+`FLAGS` | `00:1BEF` | `0x1BEF`
+`CIRCLE` | `00:216A` | `0x216A`
+`DUMP2BY1` | `00:283A` | `0x283A`
+`DUMP2BY2` | `00:2884` | `0x2884`
+`DUMP2BY2S` | `00:2897` | `0x2897`
+`PUTLINE` | `00:2CFB` | `0x2CFB`
+`PIXAD` | `00:2D63` | `0x2D63`
+`GETBYTEHI` | `00:2DCC` | `0x2DCC`
+`PRINTEXT` | `00:2E45` | `0x2E45`
+`TEXTL` | `00:2E4D` | `0x2E4D`
+`DOBIN` | `00:2EDA` | `0x2EDA`
+`ISNONE` | `00:2EE1` | `0x2EE1`
+`CLEARSTAT` | `00:2EE6` | `0x2EE6`
+`DUMPENDOBJ` | `00:35F6` | `0x35F6`
+`DUMP2BY2SEQU` | `00:35FB` | `0x35FB`
+
+These are also good candidates for setting breakpoints in SameBoy, because you can place the breakpoint at a confirmed retail address and then work backwards to the calling code.
+
+---
+### Retail offsets for known verbatim asset blocks
+If you are mapping data/graphics rather than code, these blocks also have unique 32-byte verbatim matches in the retail ROM:
+
+Label | Retail bank:addr | Retail file offset | Hit count
+---|---|---|---
+`CHRTABLE` | `00:0400` | `0x0400` | 1
+`CTAB` | `00:09A8` | `0x09A8` | 1
+`WAVETAB` | `00:0AA4` | `0x0AA4` | 1
+`CMRDO` | `01:4600`, `03:4000`, `03:4600` | `0x4600`, `0xC000`, `0xC600` | 3
+`CBADS` | `01:4C00`, `03:4C00` | `0x4C00`, `0xCC00` | 2
+`CLOGO` | `02:4080` | `0x8080` | 1
+`CSTAR` | `02:47F0` | `0x87F0` | 1
+`CICONS` | `02:4B20` | `0x8B20` | 1
+`CHEADS` | `02:4D60` | `0x8D60` | 1
+`CFINI` | `02:5840` | `0x9840` | 1
+`CMRSDO` | `03:4300` | `0xC300` | 1
+
+When a block has multiple hits, it means the same 32-byte signature occurs in multiple places in the retail ROM (so treat those offsets as "this data exists here", not "this is the only copy").
+
+---
+## Rebuild feasibility checklist
+If you want to go from "interesting source release" to a reproducible modern build, these are the main technical unknowns to resolve:
+* **Interrupt vectors** - `SYSETUP` sets `IE=1` and executes `EI`, but `mrdo.asm` does not obviously define an ISR ending in `RETI`. You will want to verify what the original ROM has at `$0040` and friends before leaving IME enabled.
+* **Header fields** - The header code writes `CARTRIDGE TYPE = 0` (ROM-only) and `ROM SIZE = 0` (which is 32 KiB in the standard header encoding), but the comment claims 256K. A rebuilt ROM must pick consistent header values and banking strategy.
+* **Banking model** - The file uses fixed `ORG` placements and does not contain explicit MBC register writes, which is consistent with ROM-only or a custom build step that split content across banks. If the retail game is larger than 32 KiB, you will need to decide how to map this file into ROM0/ROMX.
+
+
+## Key takeaways
+If you want a quick summary of what makes this source release interesting from a reverse engineering perspective:
+* **RAM mirror + dirty queue** - `BACKSCREEN` acts as the canonical map state and `CHRDUMP` batches the VRAM writes for VBlank.
+* **Macro-cell control map** - `BYTESCREEN` turns map semantics into a simple 0..4 lookup (`EATJP`), separating gameplay rules from tile stamping.
+* **Table-driven everything** - Actors, apples, collisions, and menus all lean on jump tables rather than deep branch trees.
+* **Timing-first renderer** - The split-screen HUD is a deliberate `LYC`-timed `LCDC` toggle with an OAM DMA swap mid-frame.
+* **Pragmatic performance guards** - Small flags like `SPLAT` and amortized updates like `PRSCORE` keep worst-case frames under control.
+
+---
+# Fun breadcrumbs in the comments
+The release is full of tiny details that give it some personality and make it easier to follow.
+Some examples you will see while browsing:
+* **"STOPS STUPID BUG!"** - A defensive load before a compare in the status-sprite dump routine.
+* `HIGHBUFF` - A hardcoded string containing `FROBUSH HERE` in the high-score data block.
+
+
---
# References
-[^1]: http://www.pauliehughes.com/downloads/index.html
+[^1]: [Paul Hughes - Downloads](https://www.pauliehughes.com/downloads.htm)
+[^2]: [Paul Hughes - Mr Do GB source (mrdo.asm)](https://www.pauliehughes.com/index_htm_files/mrdo.asm)
+[^3]: [Pan Docs (Game Boy technical reference)](https://gbdev.io/pandocs/)
+[^4]: [RGBDS documentation](https://rgbds.gbdev.io/)
+[^5]: [SameBoy debugger documentation](https://sameboy.github.io/debugger/)
+[^6]: [Mr. Do! for Game Boy review video](https://www.youtube.com/watch?v=WniQSM86SP4)
+[^7]: [Paul Hughes tweet about dev hardware/toolchain](https://twitter.com/PaulieHughes/status/1231294467879116800)
+[^8]: [Installing RGBDS on macOS](https://rgbds.gbdev.io/install/macos)
+[^9]: [Installing RGBDS on Linux](https://rgbds.gbdev.io/install/linux)
+[^10]: [Installing RGBDS on Windows](https://rgbds.gbdev.io/install/windows)
diff --git a/pages/consoles/n64/Maestro64.md b/pages/consoles/n64/Maestro64.md
index 3f402983..142dbfbd 100644
--- a/pages/consoles/n64/Maestro64.md
+++ b/pages/consoles/n64/Maestro64.md
@@ -47,8 +47,6 @@ The back shows the port where you connect to the PC.
## References
* [http://devkits.handheldmuseum.com/SN64.htm](http://devkits.handheldmuseum.com/SN64.htm)
-
* Official Website archive - [https://web.archive.org/web/19980523231505/http://www.snsys.com:80/snsys/page.asp?c=maestro64](https://web.archive.org/web/19980523231505/http://www.snsys.com:80/snsys/page.asp?c=maestro64)
-
* Images from [http://www.retrogames.co.uk/040010/Nintendo/N64-Maestro-Development-Kit](http://www.retrogames.co.uk/040010/Nintendo/N64-Maestro-Development-Kit)
diff --git a/pages/consoles/n64/Mystical-Ninja-N64-Memory-Rom-Editing.md b/pages/consoles/n64/Mystical-Ninja-N64-Memory-Rom-Editing.md
deleted file mode 100644
index 2aacba33..00000000
--- a/pages/consoles/n64/Mystical-Ninja-N64-Memory-Rom-Editing.md
+++ /dev/null
@@ -1,30 +0,0 @@
----
-layout: post
-tags:
-- n64
-- memory
-title: Mystical Ninja N64 Memory Rom Editing
-category: n64
-youtube: "HCOhOoajBFY"
-image: /public/zPL1VU0wRtcGNpCC99q46g_img_0.png
-permalink: /mystical-ninja-n64-memory-rom-editing
-redirect_from:
- - /mystical-ninja-n64-memory-rom-editing/
-breadcrumbs:
- - name: Home
- url: /
- - name: Nintendo 64
- url: /n64
- - name: Mystical Ninja N64 Memory Rom Editing
- url: #
-recommend: n64
-editlink: /consoles/n64/Mystical-Ninja-N64-Memory-Rom-Editing.md
----
-
-# Mystical Ninja N64 Memory Rom Editing with CheatEngine and MuPen64
-
-
-In this video the author goes over some progress he has made trying to understand the layout of the Goemon rom file. He seems to have made great progress and it is documented in a number of text files and even has a collaboration CheatEngine file!
-
-The video is available here: [https://gaming.youtube.com/watch?v=HCOhOoajBFY](https://gaming.youtube.com/watch?v=HCOhOoajBFY)
-
diff --git a/pages/consoles/n64/N64MipsAssembly.md b/pages/consoles/n64/N64MipsAssembly.md
index 6ec2a7a3..6aea0e0c 100644
--- a/pages/consoles/n64/N64MipsAssembly.md
+++ b/pages/consoles/n64/N64MipsAssembly.md
@@ -4,8 +4,8 @@ tags:
- n64
- assembly
- tutorial
+- incomplete
title: N64 MIPS Assembly Video Tutorials
-youtube: "m_eKDuWhBo8?list=PLjwOF_LvxhqTXVUdWZJEVZxEUG5qt8fsA"
image: /public/N64/N64LearnMIPSAssemblyLanguage.jpg
category: n64
permalink: /N64MipsAssembly
@@ -16,20 +16,682 @@ breadcrumbs:
url: /n64
- name: N64 MIPS Assembly Video Tutorials
url: #
-recommend:
-- n64
-- assembly
-- tutorial
editlink: /consoles/n64/N64MipsAssembly.md
---
-# Subscribe to Fraser N64!
+# Introduction
+[Fraser N64](https://www.youtube.com/channel/UC3tcfSES8CB45DmTbHhUP1w) has created a series of YouTube videos where he teaches N64 [MIPS](#glossary-mips) programming from the ground up. This page is designed to act as notes as you follow along with the tutorials.
+
First of all in order to help support the creation of more excellent N64 MIPS tutorials, consider subscribing to Fraser here:
[Fraser N64 - YouTube](https://www.youtube.com/channel/UC3tcfSES8CB45DmTbHhUP1w)
-# Watch live on twitch
-If you are lucky you can catch the stream live here: [fraserN64 - Twitch](https://www.twitch.tv/frasern64/)
+Also if you are lucky you can catch the stream live here:
+[fraserN64 - Twitch](https://www.twitch.tv/frasern64/)
+
+## Source Code and Resources
+You can access all the source code and resources referenced in the videos here:
+[fraser125/N64_ASM_Videos: The files complementing my video series N64 MIPS Assembly](https://github.com/fraser125/N64_ASM_Videos)
+
+## Glossary of Key Terms
+If you are new to MIPS or N64 development, these terms are fundamental to the workflow:
+* **MIPS** - Microprocessor without Interlocked Pipelined Stages; the RISC architecture used by the N64.
+* **bass** - A versatile, multi-architecture assembler created by near, used extensively for N64 projects.
+* **n64chain** - A pre-built GCC toolchain specifically configured for creating Nintendo 64 programs.
+* **MAME** - Multiple Arcade Machine Emulator; used here for its built-in debugger which is superior for inspecting N64 CPU state.
+
+---
+# Lesson 001 - Dev Environment Setup
+This section is based on the technical walkthrough by [Fraser N64](https://www.youtube.com/channel/UC3tcfSES8CB45DmTbHhUP1w) and covers the initial setup of a Windows-based development environment for N64 [MIPS](#glossary-mips) development.
+
+
+
+## Essential Development Tooling
+Here is the essential software stack:
+* **[bass](#glossary-bass) Assembler** - The primary tool for converting assembly source into N64 ROM data. The ARM9 github fork is the standard version used for these tutorials.
+* **GCC for Windows** - Used for creating Windows-side tools or if integrating C code into the pipeline.
+* **[n64chain](#glossary-n64chain)** - Essential for projects requiring the standard C library or more complex compilation.
+* **Notepad++** - A recommended text editor for its speed, simplicity, and customization options.
+* **Legacy Windows Calculator** - The Windows 7 version is preferred over the Windows 10 app for its much more intuitive binary bit-view, which is crucial for manually verifying register values and bitwise operations.
+* **GitHub Desktop** - Recommended for users who prefer a GUI for version control and cloning test repositories, such as Peter Lemon's N64 tests.
+
+## Emulator and Debugger Configuration
+To run N64 software in [MAME](#glossary-mame), you must have the N64 BIOS files. The file `n64.zip` must be placed inside the `roms/` directory of your MAME installation. It is recommended to leave this file zipped to ensure MAME recognizes it correctly.
+
+### Launching the Debugger
+This view provides direct access to the MIPS architecture internals:
+
+* **GPRs** - General Purpose Registers. MIPS contains 32 of these (ranging from `zero` to `ra`), though roughly 30 are practically usable since a few (like the `zero` register) are hard-wired by the architecture.
+* **HI / LO Registers** - Special-purpose registers used specifically to hold the 64-bit results of integer multiplication and division.
+* **Floating Point Registers** - The Coprocessor 1 (CP1/FPU) register set natively used for floating-point mathematical operations on the N64.
+
+---
+## Repo Layout and Template Baseline
+The `N64_ASM_Videos` repo is structured as a set of self-contained projects per lesson plus a shared `LIB/` folder. Knowing where things live makes the later lessons much easier to follow.
+
+Here is the high-level layout used by the repo:
+* **Template** - A minimal bootable ROM skeleton (`Template/Template.asm`, `Template/N64_Header.asm`, `Template/make.cmd`).
+* **LIB** - Shared includes, macros, and binary assets such as `LIB/N64.INC`, `LIB/N64_GFX.INC`, and `LIB/N64_BOOTCODE.BIN`.
+* **Video002 ... Video009** - Per-video projects that repeat the same pattern: `Video00X.asm`, `N64_Header.asm`, `make.cmd`, and the built `Video00X.N64`.
+
+## Build Loop (bass + chksum64)
+The repo uses the same 2-step build loop in every `make.cmd`: assemble, then patch header checksums.
+
+For example, `Template/make.cmd` looks like this:
+```bat
+@echo off
+bass Template.asm -strict -benchmark
+chksum64 Template.N64
+```
+
+This matters because `chksum64` is not optional: without it, CRC placeholders in the header remain unpatched and the ROM often will not boot reliably in stricter emulators or on hardware.
+
+## One-Click MAME Run and Debug Scripts
+The repo includes wrapper scripts that take a ROM path and launch MAME from a fixed install directory. This avoids retyping long `mame64.exe ...` commands every iteration.
+
+The basic pattern used in `bass/run.cmd` is:
+```bat
+@echo off
+set ROM=%~dpnx1
+set OLDDIR=%CD%
+cd \mame
+mame64.exe n64 -window -cart %ROM% -switchres -nofilter
+chdir /d %OLDDIR%
+```
+
+`bass/debug.cmd` is the same idea but launches with `-debug -log -verbose` enabled so you drop straight into the debugger and get a log file.
+
+## Toolchain Shells (n64chain + gcc)
+If you also use the repo's `n64chain/` and `gcc/` tooling, it provides small helper scripts that just set `PATH` and keep a dedicated prompt open.
+
+For example, `n64chain/WinN64.cmd` is:
+```bat
+@ECHO OFF
+SET PATH=C:\n64chain\bin;%PATH%
+ECHO N64 GCC Command Prompt
+```
+
+## Repo Naming Conventions
+The repo uses simple file naming conventions to separate macros/constants from executable code:
+* **`.asm`** - Main program file (entry point + top-level flow).
+* **`.INC`** - Variables, constants, and macros (included as source).
+* **`.S`** - Function bodies, often called via macros or `jal`.
+
+---
+## Workflow Automation
+This section covers small Windows-side workflow tweaks that keep your build loop fast and your logs visible.
+
+### Persistent Command Prompts
+Standard batch files close their terminal window immediately upon completion, which wipes out your compilation errors and build history.
+
+By creating a Windows shortcut to your batch file and modifying the target, you can force the environment to stay open:
+```bat
+%COMSPEC% /k "C:\path\to\your\script.bat"
+```
+
+The `/k` parameter tells the command processor to carry out the command specified by the string but remain open, leaving you at a command prompt precisely where you need to be to run repeated `bass` assembly commands. You can pin these shortcuts directly to your taskbar for quick access.
+
+### MAME Launch Scripts
+Because developers do not use a frontend, typing the full MAME executable path and N64 launch parameters repeatedly becomes tedious. A common pattern is to create a `run.cmd` and `debug.cmd` wrapper that takes a ROM path, changes into the MAME install directory, then launches MAME.
+
+If you are following along with the `N64_ASM_Videos` repo, prefer its existing wrappers (`bass/run.cmd` and `bass/debug.cmd`) rather than inventing your own. The key detail is that the wrapper should pass an absolute ROM path (the repo uses `%~dpnx1`) so it still works after switching directories.
+
+---
+# Lesson 002 - bass Assembler Keywords
+This section covers the fundamental keywords and directives used by the [bass](#glossary-bass) assembler to structure a Nintendo 64 ROM.
+
+
+
+## Walkthrough: Video002.asm Minimal Bootable ROM
+The `N64_ASM_Videos` repo includes a minimal, bootable skeleton in `Video002/Video002.asm` that is a good reference for what a known-good `bass` project looks like.
+
+This is the directive block used by the repo:
+```nasm
+arch n64.cpu
+endian msb
+output "Video002.N64", create
+
+fill $0010'1000
+origin $00000000
+base $80000000
+
+include "../LIB/N64.INC"
+include "../LIB/N64_GFX.INC"
+include "N64_Header.asm"
+insert "../LIB/N64_BOOTCODE.BIN"
+```
+
+Two directives that are easy to confuse early on are `origin` and `base`:
+* `origin` is the current write cursor inside the output ROM file (a file offset).
+* `base` is the runtime address space that labels resolve to (the address you will see in the debugger).
+
+In the repo's layout, the first `0x1000` bytes are deliberately reserved for the header + bootcode, which is why the label `Start:` typically ends up at `0x80001000` with `base $80000000` (this is explained in more detail in the header section below).
+
+Build uses the same `make.cmd` pattern described in Lesson 001: assemble with `bass -strict -benchmark`, then run `chksum64` on the resulting ROM to patch header CRCs.
+
+## Core bass Directives
+The main directives required for an N64 project include:
+* **arch** - Defines the target architecture. For the primary N64 CPU, use `arch n64.cpu`. The assembler also supports `n64.rsp` for the Reality Signal Processor and `n64.rdp` for the Reality Display Processor.
+* **endian** - Defines the byte order. The N64 uses Big-Endian, so this must be set to `endian msb` (Most Significant Bit).
+* **output** - Specifies the filename of the generated ROM. Using `output "name.n64", create` tells the assembler to generate a completely new ROM from scratch.
+* **origin** - Sets the current positional cursor within the output ROM file, which dictates exactly where the following bytes of code will be physically placed.
+* **fill** - Used to write zero-padding into the ROM.
+
+Here is an example of what the top of your main assembly file will look like:
+```nasm
+arch n64.cpu
+endian msb
+output "lesson2.n64", create
+fill 1052672
+```
+
+### The Purpose of Fill
+When the N64 boots, it automatically copies the first 1 Megabyte of the game cartridge into the console's RAM.
+To prevent the console from copying garbage memory if the game is smaller than 1MB, developers pad the ROM with zeros.
+
+The value `1052672` is derived from adding the size of the 4KB header (`4096` bytes) to exactly 1 Megabyte (`1048576` bytes).
+
+## File Modularization Directives
+It is crucial to understand the difference between these two keywords:
+* **include** - Works like `#include` in C. It parses the external file as source code. If the file contains macros or constants, they are loaded into memory but no bytes are written to the ROM unless those macros are explicitly invoked.
+* **insert** - Treats the target file as raw binary data. It grabs the file byte-by-byte and writes it directly to the output ROM at the exact physical location of the `insert` command.
+
+Example usage:
+```nasm
+include "lib/n64.inc" // Loads constants and macros, writes nothing yet.
+insert "n64_bootcode.bin" // Physically dumps 4KB of raw binary boot code here.
+```
+
+---
+## The N64 Header
+This section covers the small metadata block at the start of an N64 ROM, and the handful of fields you typically touch first when building a ROM from scratch.
+
+In the `N64_ASM_Videos` repo, the header file `N64_Header.asm` is structured to occupy exactly `0x40` (64) bytes, and it is immediately followed by a `0xFC0` (4032) byte bootcode blob. Together, these add up to `0x1000` (4096) bytes, which is why the repo's entrypoint code label typically starts at `0x80001000` when `base $80000000` is used.
+
+### Data Width Keywords
+When manually constructing the header or laying out data, you use specific keywords to tell `bass` how many bytes to allocate for the literal value:
+
+* **db** - Data Byte (1 byte / 8-bit)
+* **dw** - Data Word (4 bytes / 32-bit). Note: A "word" on the MIPS architecture is strictly 32 bits.
+* **dd** - Data Double (8 bytes / 64-bit)
+
+Strings can be placed easily using `db`. The N64 header allows for a 27-character string name:
+```nasm
+db "MY FIRST N64 GAME " // Pads the remaining space with spaces
+```
+
+### CRC Calculation
+The header also contains two placeholder words for the CRC (Cyclic Redundancy Check) checksums. The N64 boot process calculates the checksum of the first megabyte and compares it to these header values. If they do not match, the game will not boot on hardware.
+
+Because the CRC requires the final compiled ROM, the assembler leaves placeholder `dw` values. You must run a secondary tool-such as `sum64` or `chksum64`-as a post-assembly step in your build script to calculate the true CRC and inject it back into the header.
+
+---
+## Debugging in MAME
+In the [MAME](#glossary-mame) debugger command line, use the `bp` (breakpoint) command:
+```
+bp 80000400
+```
+
+Address `0x80000400` is conventionally where control is handed over to the user's game loop. From there, you can press `F10` (Step Over) or `F11` (Step Into) to walk through the instructions line by line while inspecting the MIPS registers.
+
+---
+# Lesson 003 - Registers and First 6 Instructions
+This section covers the [MIPS](#glossary-mips) architecture fundamentals, register layout, and foundational instructions.
+
+
+## Walkthrough: Video003 First Instructions
+The `N64_ASM_Videos` repo includes a minimal entrypoint in `Video003/Video003.asm` that is useful for understanding what these first instructions do in practice.
+
+This is the first block of real code the repo runs after `Start:`:
+```nasm
+Start:
+ lui t0, PIF_BASE
+ addi t1, zero, 8
+ sw t1, PIF_CTRL(t0)
+
+Loop:
+ j Loop
+ nop
+```
+
+This short sequence is doing three important things:
+* It uses `lui` to build a memory-mapped IO base address in a register (`t0`).
+* It uses `addi` with `zero` to create a small immediate (`t1 = 8`) without needing a pseudo-instruction.
+* It uses `sw base+offset` addressing to write to a specific hardware register.
+
+If you expand the address math, this write becomes very concrete:
+* `PIF_BASE` is defined in `LIB/N64.INC` as `0xBFC0`, so `lui t0, PIF_BASE` yields `t0 = 0xBFC00000`.
+* `PIF_CTRL` is defined in `LIB/N64.INC` as `0x07FC`, so `sw t1, PIF_CTRL(t0)` writes to `0xBFC00000 + 0x07FC = 0xBFC007FC`.
+
+In debuggers, you will often see these addresses sign-extended into 64-bit registers as `FFFFFFFFBFC00000` and similar values. That is normal on the VR4300 and is the same sign-extension behaviour described later in this lesson.
+
+---
+## Memory Hierarchy
+The MIPS architecture is referred to as a register architecture because all data must be loaded into a register, operated upon, and then written back out to memory.
+
+The typical flow follows this speed hierarchy (fastest to slowest):
+* **Registers** - Located physically at the center of the CPU, these operate with zero latency.
+* **Cache** - The N64 MIPS CPU has separate Data and Instruction caches.
+* **Console RAM** - Fast, dynamically allocated memory.
+* **Cartridge ROM** - The slowest medium of the immediate hierarchy.
+
+## MIPS Register Architecture
+While you can technically use any register for anything (except register `0`), developers adhere to strict conventions to ensure code compatibility:
+* **zero (r0)** - Hardwired to the value `0`. Any attempt to write to it fails silently.
+* **at (r1)** - Assembler Temporary. Reserved for assembler pseudo-instructions.
+* **v0-v1 (r2-r3)** - Used exclusively for function return values.
+* **a0-a3 (r4-r7)** - Used to pass arguments into functions.
+* **t0-t9 (r8-r15, r24-r25)** - Temporaries. Function-local variables that do not need to be saved to the stack before calling another function.
+* **s0-s8 (r16-r23, r30)** - Saved registers. Global variables; if a function modifies these, it must back up the original value to the stack and restore it before returning.
+* **k0-k1 (r26-r27)** - Kernel variables. Strictly reserved for system exception handling.
+* **sp (r29)** - Stack Pointer. Used to save context during function calls.
+* **ra (r31)** - Return Address. Populated by Jump-and-Link instructions so the CPU knows where to return after a function call completes.
+
+---
+## Assembly Syntax and Formatting
+These formatting rules are conventions that make low-level code easier to scan, especially when you are comparing register state in a debugger to what you wrote in source:
+* **Whitespace is ignored** - Tabs or spaces are purely for human readability.
+* **Instruction format** - The line begins with the instruction mnemonic, followed by 1 to 4 parameters separated by commas.
+* **Right-to-Left evaluation** - For most instructions, the destination register is listed first on the left, but conceptually the operation evaluates the right-hand elements and places the result into the left.
+* **Exception: sw** - The Store Word instruction works left-to-right (take the data on the left, store it to the address on the right).
+
+---
+## Foundational MIPS Instructions
+These are the baseline building blocks used throughout the early lessons before you start leaning on heavier macro abstractions.
+
+### Loading Immediate Data
+An immediate instruction incorporates literal data directly inside the instruction's opcode itself, rather than reading it from RAM.
+* **lui (Load Upper Immediate)** - Takes a 16-bit constant and loads it into the upper 16 bits of the 32-bit space of a register, padding the lower 16 bits with zeros.
+* **addi (Add Immediate)** - Adds a literal constant to the value of a source register and stores the result.
+
+---
+### Memory Storage
+These instructions move data between registers and memory.
+* **sw (Store Word)** - Grabs a 32-bit word from a register and writes it into memory. It takes a destination address and an offset value. MIPS also has variants like `sb` (Store Byte), `sh` (Store Halfword), and `sd` (Store Double/64-bit).
+
+### Control Flow
+These instructions change what executes next.
+* **j (Jump)** - Unconditionally shifts execution to a specific label/address. An infinite loop can simply be `j loop_label`.
+* **nop (No Operation)** - Instructs the CPU to do absolutely nothing for one cycle.
+
+### Delay Slots
+MIPS architecture relies heavily on Delay Slots-a quirk where the instruction placed immediately after a branch or jump is executed before the jump actually takes effect. This happens because the CPU pipeline has already fetched the next instruction while calculating the jump destination. During early development, it is common practice to simply place a `nop` in the delay slot to prevent execution errors.
+
+---
+## Sign Extension
+When loading 32-bit values into the N64's 64-bit registers, the CPU performs Sign Extension.
+
+If the highest bit (the sign bit) of the 32-bit value is `1` (indicating a negative value in a signed integer), the CPU extends that `1` across the entire upper 32 bits of the 64-bit register. This is why memory addresses frequently appear in the debugger padded with `F`s (e.g., `FFFFFFFFBFC00000`). While initially confusing, the N64 memory management unit largely ignores the upper 32 bits when addressing 32-bit software space.
+
+---
+# Lesson 004 - Memory Mapped Hardware and Video Init
+This section details the Nintendo 64 memory mapping, hardware interfaces, and the assembly required to initialize the video subsystem.
+
+
+## Walkthrough: Video004 VI Register Init
+The `N64_ASM_Videos` repo includes a complete, explicit VI init sequence in `Video004/Video004.asm`. It is a useful reference because it shows the exact register writes the later `ScreenNTSC(...)` macro wraps.
+
+The lesson uses a classic pattern: load the VI base, then `sw` to a series of fixed offsets:
+```nasm
+lui t0, VI_BASE
+
+li t1, BPP16
+sw t1, VI_STATUS(t0)
+
+li t1, $A000'0000
+sw t1, VI_ORIGIN(t0)
+
+li t1, 320
+sw t1, VI_WIDTH(t0)
+```
+
+If you are new to memory-mapped IO, it helps to see the address math that `bass` is doing for you. This table shows a few of the key VI registers and how their offsets relate to the VI base address:
+
+Register | Offset | Absolute address (KSEG1)
+---|---|---
+VI_STATUS | `0x00` | `0xA4400000`
+VI_ORIGIN | `0x04` | `0xA4400004`
+VI_WIDTH | `0x08` | `0xA4400008`
+VI_X_SCALE | `0x30` | `0xA4400030`
+VI_Y_SCALE | `0x34` | `0xA4400034`
+
+Once you understand the explicit sequence, you can switch to the repo's macro in `LIB/N64_GFX.INC`:
+```nasm
+ScreenNTSC(320, 240, BPP16, $A010'0000)
+```
+
+That macro writes the same VI registers, but it also documents what the βmagicβ constants mean (for example, the `VI_TIMING` and `VI_H_VIDEO` fields), which makes it a good long-term reference when you start changing resolution, origin, or scaling.
+
+## The Flat Memory Map
+When the console boots, the BIOS places the [MIPS](#glossary-mips) processor into a 32-bit kernel mode. This provides a 4 Gigabyte (`0x00000000` to `0xFFFFFFFF`) address space. Even though 64-bit mathematics are available, memory addressing remains firmly in 32-bit.
+
+This flat structure makes assembly programming relatively straightforward because all internal systems-such as the Video Interface (VI), Audio Interface (AI), Peripheral Interface (PI), and Serial Interface (SI)-are mapped directly to specific addresses. You do not need complex port configurations; you interact with the console's hardware simply by reading and writing to standard memory addresses.
+
+### Cached vs. Uncached Memory
+These address ranges actually point to the exact same physical memory on the motherboard:
+* **0x80000000 (Cached)** - Operations hit the CPU caches first. While reads from the cache take just 1 CPU cycle, writes stay in the cache until manually flushed to physical RAM.
+* **0xA0000000 (Uncached)** - Bypasses the internal CPU cache entirely and interacts directly with physical RAM.
+
+For example, `0x80000400` and `0xA0000400` access the identical location in memory. When updating hardware subsystems like the Video Interface, you must either write to the `0xA` uncached range or write to the `0x8` cached range and remember to trigger a cache flush command so the hardware actually receives the data.
+
+---
+## Initializing the Video Interface (VI)
+When writing values to configure the Video Interface, two technical boundaries exist:
+
+* **Resolution Limits** - The hardware only outputs at `320x240` or `640x480`. Attempting to render non-standard internal resolutions will just draw them inside the standard 320x240 canvas surrounded by physical black borders.
+* **Color Depth** - The standard format is 16 Bits-Per-Pixel (BPP). The n64 structures this as an RGBA 5-5-5-1 layout (5 bits Red, 5 bits Green, 5 bits Blue, and 1 bit Alpha transparency).
+
+A fun trick during early development: Setting the frame buffer origin address to point to the start of your game code memory (e.g., `0xA0001000`) will force the VI to render your compiled assembly instructions to the television screen as blobs of static color data.
+
+## Assembly Quality of Life Tricks
+Here are techniques commonly used in MIPS projects:
+* **Pseudo-instructions** - The `li` (Load Immediate) command doesn't actually exist in the MIPS CPU. It is a pseudo-instruction that the assembler automatically splits into two real instructions at compile time: `lui` (Load Upper Immediate) and `ori` (Or Immediate). This keeps your source code highly readable without sacrificing exact register control.
+* **Hexadecimal Ticks** - Reading long memory addresses is error-prone. The `bass` assembler allows inserting a single quote tick `'` anywhere inside a hexadecimal number as a visual separator (e.g. `0x8000'0000`). It compiles the same but is far easier to read.
+* **Compile-time Math** - Mathematical functions can be placed directly in the source file, such as configuring resolutions via `320 * 240`. The assembler evaluates this calculation before generating the ROM, costing zero CPU cycles during gameplay.
+* **NOP Padding** - Using three consecutive `nop` (No Operation) instructions creates an obvious blank block inside the MAME memory debugger. This is an excellent tactic for placing visual boundaries between distinct functions or initialization loops.
+
+---
+# Lesson 005 - Draw Line & Branching Delay Slot
+This section covers the fifth technical walkthrough by Fraser MIPS, focusing on drawing basic lines to the screen and understanding the nuances of the [MIPS](#glossary-mips) pipeline and delay slots.
+
+
+## Frame Buffer Memory Positioning
+A standard practice is to place the frame buffer exactly at the 1 Megabyte mark (`0x00100000`).
+
+To set this up cleanly using the assembler:
+* Convert 1 Megabyte to bytes: `1024 * 1024 = 1,048,576 bytes`.
+* Convert to hexadecimal: `0x100000`.
+* Assign this as your VI Origin (Video Interface Origin) pointer.
+
+---
+## Pixel Coordinate Math
+The formula for finding the offset of a single pixel is:
+`((Y_Position * Screen_Width) + X_Position) * Bytes_Per_Pixel`
+
+For example, to start a line 15 rows down and 110 columns in on a 16-bit (2 bytes per pixel) display:
+`((15 * 320) + 110) * 2`
+
+This yields the literal memory offset that should be added to your frame buffer's base address to locate the exact destination for your pixel color data.
+
+---
+## Writing 32-bit Colors
+You achieve this by loading the same 16-bit color constant into both the upper and lower halves of a temporary register:
+* **lui** - Loads the 16-bit color into the upper half of the register.
+* **ori** - Loads the identical 16-bit color into the lower half of the register.
+
+Now, a single `sw` (Store Word) command writes two adjacent pixels to the frame buffer simultaneously.
+
+---
+## Walkthrough: Video005 Horizontal Line (Two Pixels Per Store)
+The repo's `Video005/Video005.asm` is a concrete implementation of the pixel math described above. It draws a horizontal line by writing 32-bit words into a 16bpp framebuffer.
+
+The key trick is that the repo packs a single 16-bit color constant into *both* halfwords of a 32-bit register, so each `sw` produces two adjacent pixels of the same color:
+```nasm
+lui t0, LAWN_GREEN16
+ori t0, t0, LAWN_GREEN16
+```
+
+Then it computes the starting pixel pointer using the same formula, and loops in 4-byte steps:
+```nasm
+la t1, $A010'0000
+addi t1, t1, ((320 * 15) + 110) * 2
+addi t2, t1, 200
+
+do_Store2Pixels:
+ sw t0, 0(t1)
+ bne t1, t2, do_Store2Pixels
+ addi t1, t1, 4
+```
+
+This loop is also a good demonstration of why delay slots matter: the `addi t1, t1, 4` runs in the branch delay slot, so it executes even on the final iteration when the branch falls through. When you start writing your own loops, be explicit about whether your end pointer is inclusive or end-exclusive so you do not accidentally do one extra store.
+
+## Loop Construction
+MIPS favors a do-while loop structure. The typical flow is:
+* Calculate the starting pixel address (`t1`).
+* Calculate the ending pixel address (`t2`).
+* Store the pixel color into the address at `t1`.
+* Increment the pointer `t1` by 4 bytes (`addi t1, t1, 4`).
+* Compare `t1` and `t2`. If they are not equal, branch back to Step 3 (`bne t1, t2, loop_label`).
+
+## The MIPS Pipeline and Delay Slots
+The **Branch Delay Slot** is the single instruction located *immediately after* a branch or jump command. Because of how the pipeline fetches instructions, this slot is executed *before* the jump actually takes effect.
+
+### Pipeline Rules and Performance
+Here are a few practical rules of thumb that help avoid accidental slowdowns and hard-to-debug control flow issues.
+* **Register-to-Register (e.g., `addi`)**: These take 5 pipeline cycles. Placing one in a branch delay slot is the most efficient use of CPU time, essentially granting you a "free" instruction calculation while the branch evaluates.
+* **Load and Store (e.g., `sw`)**: These are blocking operations. If placed in a delay slot, they halt the pipeline, taking 11 pipeline cycles (wasting roughly 3 clock cycles). Do not place them in delay slots.
+* **Branch inside a Branch**: Placing a jump or branch inside the delay slot of another jump causes completely unpredictable execution behavior and should be strictly avoided.
+* **Pseudo-instructions**: Never place pseudo-instructions (like `la` or `li`) inside a delay slot. They can secretly expand to multiple literal instructions during assembly, meaning only half of the intended operation will fit into the single delay slot, breaking your code.
+
+
+**Best Practice:** If you cannot find a suitable register-to-register calculation to place in a delay slot, always use a `nop` (No Operation) to keep the pipeline stable.
+
+---
+# Lesson 006 - Draw Vertical Line & Delay Slot Optimization
+This section covers the sixth technical walkthrough by Fraser MIPS, detailing the structural changes needed to draw vertically and how to implement a practical [MIPS](#glossary-mips) delay slot optimization.
+
+
+## Vertical Line Logic and Coordinate Math
+Instead of an end address, a vertical line loop relies on a specific **counter register** (e.g., `t2`) that decrements on each pass, terminating when it hits zero. This is easily checked using the MIPS `zero` register (`r0`).
+
+### The Screen Pitch Trap
+When moving horizontally, memory is perfectly linear. When moving vertically, you must jump down an entire row in memory.
+
+A common bug when calculating this jump is forgetting about the pixel data size. If the screen is `320` pixels wide, you do not simply add `320` to the pointer to move down one row. You must multiply the width by the **Bytes-Per-Pixel (BPP)**.
+
+For a 16-bit display (2 bytes per pixel), the mathematical stride (or "pitch") is:
+`320 * 2 = 640 bytes`.
+
+If you incorrectly add just `320`, the N64 will write halfway across the screen horizontally rather than directly below, resulting in strange graphical glitches like drawing multiple half-height lines diagonally.
+
+---
+## Practical Delay Slot Optimization
+A typical vertical line loop might evaluate like this:
+* `addi t2, t2, -1` (Subtract 1 from the counter)
+* `addi t1, t1, 640` (Move memory pointer down one screen row)
+* `bne t2, zero, loop_label` (Branch back if counter is not zero)
+* `nop` (Delay slot buffer)
+
+To optimize this and save execution time, you can pull the counter subtraction out of the main loop body and place it directly into the branch delay slot:
+* `addi t1, t1, 640`
+* `bne t2, zero, loop_label`
+* `addi t2, t2, -1` (Executes "for free" while the branch resolves)
+
+Because the CPU always evaluates the delay slot before taking the jump, the counter is safely decremented on every pass, speeding up the geometry rendering loop.
+
+## Walkthrough: Video006 Vertical Line (Pitch in the Delay Slot)
+The repo's `Video006/Video006.asm` shows a practical version of this optimisation pattern. It decrements the counter in the loop body, then advances the framebuffer pointer by one full row in the branch delay slot:
+```nasm
+addi t2, r0, 200
+do_Store2Pixels:
+ sw t0, 0(t1)
+ addi t2, t2, -1
+ bne t2, r0, do_Store2Pixels
+ addi t1, t1, 320 * 2
+```
+
+This is a good template for learning because the delay-slot instruction is a single real instruction (`addi`), not a pseudo-instruction like `li` or `la`, so it cannot expand into multiple instructions and accidentally break control flow.
+
+## Pitfall: `sw` Writes Two Pixels in 16bpp Mode
+In `Video006/Video006.asm`, the color is loaded as `lui t0, LIGHT_BLUE16` but the matching `ori` is commented out. That means the lower 16 bits of the store are `0x0000`, so each `sw` writes one LIGHT_BLUE pixel and one black pixel.
+
+If you want each `sw` to write two identical 16bpp pixels, pack the color into both halfwords:
+```nasm
+lui t0, LIGHT_BLUE16
+ori t0, t0, LIGHT_BLUE16
+```
+
+---
+# Lesson 007 - bass Macros and Debugger Scripts
+This section covers the seventh technical walkthrough by Fraser MIPS, focusing on code restructuring using [bass](#glossary-bass) assembler macros and automating the [MAME](#glossary-mame) debugger.
+
+
+## Restructuring with Compile-Time Macros
+It is crucial to understand that [bass](#glossary-bass) macros evaluate strictly at **compile-time**. When the assembler encounters a macro call, it literally copies and pastes the underlying instruction block into the compiled ROM at that exact location. It does not perform a function jump (`jal`). If you invoke a large macro 20 times, the code is duplicated 20 times, heavily inflating the ROM size.
+
+## Repo Macro Examples (LIB/*.INC)
+The `N64_ASM_Videos` repo leans heavily on macros in `LIB/*.INC` to keep the main `.asm` files readable while still being explicit about what gets written to hardware.
+
+Two macros you will see in almost every lesson project are:
+* `init()` from `LIB/N64.INC`, which performs early boot stabilisation and sets up a usable stack pointer.
+* `ScreenNTSC(width, height, status, origin)` from `LIB/N64_GFX.INC`, which is covered in more detail in Lesson 004 (it wraps the same VI register writes shown in `Video004/Video004.asm`).
+
+When you are learning, it is worth reading the macro bodies in `LIB/N64.INC` and `LIB/N64_GFX.INC` because they also serve as documentation: the repo includes comments that describe what each VI timing constant represents.
+
+### Macro Syntax Rules
+When declaring a macro in `bass`, you must follow strict syntax constraints:
+* The macro must be declared *above* where it is used in the source file.
+* The opening curly brace `{` **must** reside on the exact same line as the macro declaration.
+* The closing curly brace `}` can be on its own line.
+
+Example of wrapping the Video Interface setup:
+```nasm
+macro init_video() {
+lui t0, 0xA440
+sw zero, 0x0010(t0)
+// ... extra instructions ...
+}
+```
+
+### Handling Macro Parameters
+Because macros are compile-time text replacements, the passed arguments do not automatically land in CPU registers. You must explicitly load the parameter placeholder into a register inside the macro using pseudo-instructions like `la` (Load Address) or `li` (Load Immediate).
+```nasm
+macro screen_setup(width, height, origin) {
+la t0, {origin}
+li t1, {width}
+// Now t0 and t1 contain the passed parameters
+}
+```
+
+## Automating the MAME Debugger
+To automate breakpoints, create a simple text file (e.g., `n64_debug.txt`) in your working directory and populate it with standard MAME debugger commands:
+```text
+bp 80000400
+wp 80000400, 4, rw
+```
+If you want to launch into MAME's debugger without rewriting command lines, use the repo's `bass/debug.cmd` wrapper described earlier in Lesson 001.
+
+### Advanced Watchpoints
+The `wp` command in the script above sets a **Watchpoint**. Unlike a standard breakpoint that halts execution when the Program Counter reaches a line of code, a watchpoint halts execution whenever a specific memory address is accessed.
+
+The parameters `80000400, 4, rw` instruct MAME to halt if the 4-byte range starting at `0x80000400` is either read (`r`) or written to (`w`). This is an incredibly powerful tool for tracking down exactly which function is modifying a variable.
+
+---
+### Loading the Debug Script
+To use the script, you append the `-debugscript` parameter to your `debug.bat` launch file:
+```bash
+mame64 -debug -log -verbose n64 -window -cart "%ROM_PATH%" -debugscript "n64_debug.txt"
+```
+
+When MAME launches, it will execute the commands in the text file and halt at the system reset vector. Simply press `F5` to resume execution, and MAME will instantly warp to your custom `80000400` breakpoint.
+
+---
+# Lesson 008 - Fonts Part 1: Macros and 1-Bit Expansion
+This section covers the eighth technical walkthrough by Fraser MIPS, introducing custom fonts and how to manipulate 1-bit per pixel assets for the N64's 16-bit display in [MIPS](#glossary-mips)-based projects.
+
+
+## 1-Bit Fonts on the N64
+The standard ASCII table contains 32 non-printable control characters at the beginning. If your font starts at the first printable character (the Space character), you can easily find the correct graphic by subtracting `32` from the character's standard ASCII decimal value.
+
+Because of the 1bpp density, an entire 8x8 ASCII font set requires extremely little memory-roughly 760 bytes. However, the N64 hardware only draws in 16-bit or 32-bit color depths. Therefore, before the font can be drawn to the screen, a routine must parse the 1bpp data and expand it into full 16-bit colors in RAM.
+
+---
+## Repo Walkthrough: PIXEL8 Font Pipeline (Video008/Video009)
+The `N64_ASM_Videos` repo implements an end-to-end font pipeline using a 1bpp 8x8 font file and a pair of routines in `LIB/PIXEL8_UTIL.S`.
+
+At a high level, the pipeline is:
+* Insert the 1bpp font data into the ROM (the repo uses `LIB/PIXEL8.FNT`).
+* Call `pixel8_init16(...)` once to expand the 1bpp font into a 16bpp font atlas in RAM.
+* Call `pixel8_static16(...)` to blit characters from the expanded atlas into the framebuffer.
+
+The repo wraps these routines with small calling macros in `LIB/PIXEL8_UTIL.INC` so the call sites stay readable. For example, `pixel8_init16(destination, forecolor, backcolor)` is implemented as a macro that sets `a0/a1/a2` and then calls the real routine:
+```nasm
+macro pixel8_init16(destination, forecolor, backcolor) {
+ li a0, {destination}
+ ori a1, r0, {forecolor}
+ jal pixel8_init16
+ ori a2, r0, {backcolor}
+}
+```
+
+There are two details worth noticing in that macro:
+* It uses the `jal` delay slot intentionally to load `a2` with a single real instruction (`ori`), avoiding the pseudo-instruction hazard described later in this lesson.
+* It uses registers `a0/a1/a2` because the real implementation in `LIB/PIXEL8_UTIL.S` treats those as fixed parameters (`a0 = font destination`, `a1 = forecolor`, `a2 = backcolor`).
+
+`Video008/Video008.asm` demonstrates the "expand font into RAM" step by calling `pixel8_init16(...)` twice with different color pairs. `Video009/Video009.asm` demonstrates rendering text by calling `pixel8_static16(...)` and defining a ROM string with `db "Hello World!"`.
+
+---
+## Data Inclusion and Alignment
+Always `insert` external binary assets at the very end of your source file, far away from your program loops.
+
+### Byte Alignment
+When inserting binary files, it is highly recommended to enforce byte alignment. You can use the `align` macro in [bass](#glossary-bass) to force the assembler to pad the file with zeros until the data sits on a clean boundary.
+```nasm
+ALIGN(8)
+include "../LIB/PIXEL8_UTIL.S"
+```
+Aligning data to an 8-byte boundary is a defensive programming habit that prevents critical crashes when reading blocks of memory into hardware systems like the DMA (Direct Memory Access) controller later on.
+
+### Repo Detail: How pixel8_static16 Passes Position and Length
+In the repo, the `pixel8_static16(...)` macro packs the on-screen position into a single register (`a2`) to keep the call ABI simple. The upper 16 bits are `top` and the lower 16 bits are `left`.
+
+The macro builds that packed value using two real instructions, avoiding `li` in a delay slot:
+```nasm
+lui a2, {top}
+ori a2, {left}
+```
+
+Inside the implementation in `LIB/PIXEL8_UTIL.S`, the routine unpacks those halves with:
+```nasm
+srl top, position, 16
+andi left, position, 0xFFFF
+```
+
+The macro also passes the string length in `v1` as a simple safety bound. In the repo this is set with `ori v1, zero, {length}`, which again stays as a single instruction.
+
+---
+## File Structure Conventions
+These conventions help keep large projects modular without accidentally emitting bytes into the ROM.
+* **.inc files**: Used exclusively for macros and constants. Including these at the top of your main file will not write any arbitrary bytes to the ROM.
+* **.s files**: Used exclusively for pure assembly routines. These should be included carefully, typically at the bottom of your main file, so the routines do not execute accidentally during standard program flow.
+
+## Jump and Link (JAL)
+Instead, the macro should only prepare the arguments and immediately call a single, shared assembly routine using **Jump and Link (`jal`)**:
+```nasm
+macro init_font(dest, fg_color, bg_color) {
+li a0, {dest}
+li a1, {fg_color}
+li a2, {bg_color}
+jal expand_font_routine
+nop // Delay slot
+}
+```
+
+The `jal` instruction serves two purposes:
+* It jumps to the target label (`expand_font_routine`).
+* It automatically saves the address of the *next* instruction into the **Return Address (`ra`)** register.
+
+Once the `expand_font_routine` finishes its work, it terminates with a **Jump Register (`jr ra`)** instruction, sending the CPU straight back to where the macro was initially called.
+
+## Repo Implementation Notes: pixel8_init16 Expansion Loop
+The repo's `LIB/PIXEL8_UTIL.S` shows what a 1bpp-to-16bpp expansion loop looks like in real VR4300 code. Conceptually, it streams bits out of the 1bpp font and writes either the foreground or background 16-bit color into a destination buffer in RAM.
+
+One interesting instruction choice in the repo is the use of a branch-likely to place the foreground store in the delay slot:
+```nasm
+bltzl t1, _continue
+sh forecolor, 0(font_addr)
+sh backcolor, 0(font_addr)
+```
+
+Because `bltzl` only executes its delay-slot instruction when the branch is taken, this pattern becomes a compact per-bit decision:
+* If the current bit is set, store `forecolor` (delay slot runs) and skip the `backcolor` store.
+* If the bit is not set, the delay slot is nullified and the following `sh backcolor, ...` runs instead.
+
+The routine returns the expanded font memory size (in the repo this is `0x2F80`) so higher-level code can allocate multiple font atlases in RAM without guessing sizes.
+
+---
+## The Delay Slot Pseudo-Instruction Trap
+**Never place an `li` (Load Immediate) into a branch delay slot.**
-# Source Code and Resources
-You can access all the source code and resources referenced in the videos here: [fraser125/N64_ASM_Videos: The files complementing my video series N64 MIPS Assembly](https://github.com/fraser125/N64_ASM_Videos)
+`li` is a pseudo-instruction. The assembler automatically breaks it down into *two* separate hardware instructions (`lui` and `ori`). The branch delay slot is strictly hardware-enforced to hold only *one* single instruction. If you place an `li` there, only the `lui` half will execute before the jump takes effect, resulting in a corrupted parameter and a broken font initialization.
+If you must optimize a parameter load into a delay slot, explicitly write the exact single hardware instruction (like `ori` or `addi`) yourself.
diff --git a/pages/consoles/nes/NESDevkitHardware.md b/pages/consoles/nes/NESDevkitHardware.md
index 98cc3e5f..0536f4d2 100644
--- a/pages/consoles/nes/NESDevkitHardware.md
+++ b/pages/consoles/nes/NESDevkitHardware.md
@@ -24,8 +24,7 @@ recommend:
editlink: /consoles/nes/NESDevkitHardware.md
redirect_from:
- /official-nes-devkit/
- - /famicom-nes-development-kit/
-updatedAt: '2020-07-18'
+updatedAt: '2026-04-12'
---
# Introduction
@@ -175,7 +174,8 @@ Due to the lack of official NES development kits, many companies had to reverse
-The `Mission Control NES development kit` was created by Rocket Science Productions to help smaller developers get into the market of creating games for Nintendos new console [^1].
+
+The **Mission Control NES development kit** was created by **Rocket Science Productions** to help smaller developers get into the market of creating games for Nintendos new console [^1].
It consists of a breadboard filled with chips and screwed to a plank of wood and a modified retail NES console [^2].
@@ -203,7 +203,7 @@ Software Creations Ltd has a problem on their hands, they wanted to develop game
This Catch-22 situation resulted in Mike Webb reverse engineering the NES hardware and creating his own development kit. According to an interview in Retro Gamer Magazine issue 37 it was quite an elaborate creation consisting of a stack of RAM chips that could be written on a Commodore 64 and then read via the cartridge port of a retail NES [^4].
-You can see Mike Webb talking about the making of Solstice for NES, a game he not only programmed but also created the hardware development kit for, in the video below.
+You can see **Mike Webb** talking about the making of Solstice for NES, a game he not only programmed but also created the hardware development kit for, in the video below.
diff --git a/pages/consoles/nes/NesProgrammingIntro.md b/pages/consoles/nes/NesProgrammingIntro.md
deleted file mode 100644
index c55389ae..00000000
--- a/pages/consoles/nes/NesProgrammingIntro.md
+++ /dev/null
@@ -1,38 +0,0 @@
----
-title: Programming the Nintendo Entertainment System
-permalink: /nes-programming
-redirect_from:
- - /nes-programming/
-tags:
-- nes
-- introduction
-layout: post
-image: https://img.youtube.com/vi/XT95C4fT6zA/maxresdefault.jpg
-category: nes
-breadcrumbs:
- - name: Home
- url: /
- - name: NES
- url: /nes
- - name: Programming the Nintendo Entertainment System
- url: #
-recommend: nes
-videocarousel:
- - image: https://img.youtube.com/vi/XT95C4fT6zA/maxresdefault.jpg
- youtube: 'XT95C4fT6zA'
- - image: https://img.youtube.com/vi/IbS7uEsHV_A/maxresdefault.jpg
- youtube: IbS7uEsHV_A
-references:
- - youtube
-editlink: /consoles/nes/NesProgrammingIntro.md
----
-
-## Programming the NES
-Dry run of the Programming the Nintendo Entertainment System talk that Levi D Smith gave at Codestock 2014 in Knoxville, Tennessee.
-
-## !!Con 2017: Writing NES Games! with Assembly!! by Christian Joudrey
-Writing NES Games! with Assembly!! by Christian Joudrey
-
-I'd like to take you on a stroll down memory lane and dig into the internals of the Nintendo Entertainment System (NES) to figure out how it works. While we're there, we'll see how to build a game for the NES using 6502 assembly with the help of a few modern tools. We'll gain a new respect for '80s developers and an appreciation for the high-level languages we have today!
-
-Christian is a self-taught developer based in Montreal. At a young age he discovered Visual Basic 6 on his home computer and instantly fell in love with programming. When he is not reading about technology and programming, you may find him rock climbing, cooking or drinking a stout.
diff --git a/pages/consoles/nes/Super-Mario-Bros.md b/pages/consoles/nes/Super-Mario-Bros.md
index 80f36625..6613ff14 100644
--- a/pages/consoles/nes/Super-Mario-Bros.md
+++ b/pages/consoles/nes/Super-Mario-Bros.md
@@ -388,9 +388,7 @@ By setting bit 4 to 1 (0x10) (with `| 0x10`), the code configures the PPU to use
By clearing the lower nibble (with `& 0xF0`), the code is explicitly resetting bits 0β3 to 0. This ensures that specific settings for nametable selection, VRAM increment, and pattern table addresses are cleared. This can be important if the code needs a clean slate, especially if these bits were previously set to something that could conflict with the desired behavior.
* **Reset Specific Settings** - By clearing the lower nibble (`& 0xF0`), the code is explicitly resetting bits 0β3 to `0`. This ensures that specific settings for nametable selection, VRAM increment, and pattern table addresses are cleared. This can be important if the code needs a clean slate, especially if these bits were previously set to something that could conflict with the desired behavior.
-
* **Avoid Unintended Behavior** - If the lower nibble were not cleared, any leftover settings in bits 0β3 could cause unintended changes in how the PPU handles background rendering or sprite patterns. For example, if bit 3 were set to `1` instead of `0`, the background would use the first 4 KB of the pattern table instead of the second 4 KB, which could result in incorrect graphics being displayed.
-
* **Explicit Control** - Clearing the lower nibble provides explicit control over which bits are set in the `PPUCTRL` register. This is especially important in NES development, where fine control over the PPU is crucial for correct rendering.
#### What are the Lower nibble bytes doing?
diff --git a/pages/consoles/pc/RetroCityRampage.md b/pages/consoles/pc/RetroCityRampage.md
deleted file mode 100644
index 9172d7ea..00000000
--- a/pages/consoles/pc/RetroCityRampage.md
+++ /dev/null
@@ -1,39 +0,0 @@
----
-layout: post
-tags:
- - pc
- - dos
-title: Porting Retro City Rampage to MS-DOS From PS4 to 1.44MB Floppy
-image: https://img.youtube.com/vi/kSKeWH4TY9Y/0.jpg
-videocarousel:
- - title: Porting Retro City Rampage to MS-DOS From PS4 to 1.44MB Floppy
- image: https://img.youtube.com/vi/kSKeWH4TY9Y/0.jpg
- youtube: 'kSKeWH4TY9Y'
-category: pc
-permalink: /RetroCityRampage
-breadcrumbs:
- - name: Home
- url: /
- - name: PC
- url: /pc
- - name: Porting Retro City Rampage to MS-DOS From PS4 to 1.44MB Floppy
- url: #
-recommend:
-- pc
-- dos
-editlink: /consoles/pc/RetroCityRampage.md
-updatedAt: '2020-06-27'
----
-
-From the GDC Youtube Channel:
-
-> Not many people are designing video games for MS-DOS these days, but that didn't stop Vblank Entertainment's Brian Provinciano from taking on the task.
->
-> In this 2016 talk, hear Provinciano explain how he reduced memory usage down to 4MB, increased performance to run on a 486 PC, reduced disk space to fit on a single 1.44MB floppy, and finally ported the game to MS-DOS itself.
->
-> GDC talks cover a range of developmental topics including game design, programming, audio, visual arts, business management, production, online games, and much more.
->
-> We post a fresh GDC video every weekday. Subscribe to the channel to stay on top of regular updates, and check out GDC Vault for thousands of more in-depth talks from our archives [^1].
-
-# References
-[^1]: https://www.youtube.com/watch?v=kSKeWH4TY9Y
diff --git a/pages/consoles/pokemonMini/OfficialPokeMiniSDK.md b/pages/consoles/pokemonMini/OfficialPokeMiniSDK.md
index 25b8ec0c..6c30f548 100644
--- a/pages/consoles/pokemonMini/OfficialPokeMiniSDK.md
+++ b/pages/consoles/pokemonMini/OfficialPokeMiniSDK.md
@@ -1,10 +1,10 @@
---
layout: post
tags:
-- PokemonMini
+- pokemonmini
- sdk
title: Pokemon Mini Official Software Development Kit (Toolchain)
-category: PokemonMini
+category: pokemonmini
permalink: /official-pokemon-mini-sdk
breadcrumbs:
- name: Home
@@ -12,11 +12,11 @@ breadcrumbs:
- name: SDKs
url: /sdks
- name: Pokemon Mini
- url: /PokemonMini/
+ url: /PokemonMini
- name: Pokemon Mini Official Software Development Kit (Toolchain)
url: #
recommend:
-- PokemonMini
+- pokemonmini
- sdk
editlink: /consoles/pokemonMini/OfficialPokeMiniSDK.md
---
diff --git a/pages/consoles/ps1/psx-exe.md b/pages/consoles/ps1/psx-exe.md
index afa9c363..4cf1825c 100644
--- a/pages/consoles/ps1/psx-exe.md
+++ b/pages/consoles/ps1/psx-exe.md
@@ -5,6 +5,7 @@ tags:
- sdk
- fileformats
title: PSX-EXE Format
+category: ps1
image: /public/psx-exe.png
permalink: /ps1-exe
breadcrumbs:
@@ -18,32 +19,188 @@ recommend:
- ps1
- fileformats
editlink: /consoles/ps1/psx-exe.md
+updatedAt: '2026-04-11'
---
-The PSX-EXE executable file format is a format similar to the standard UNIX ELF format.
-It is used on the Sony PlayStation 1 and it is a MIPS 32-bit executable. It contains both code and data.
+The PS-X EXE format is the PlayStation 1's main "load this into RAM and jump to it" executable format.
+It is used for both retail discs and homebrew.
-In order to use tools like the RetDec decompiler we need to convert the exe format into elf, and in order to run on the PlayStation we need to convert compiled elf files into exe files.
+Despite the name, it is not really similar to ELF.
+A PS-X EXE is a fixed-size header followed by a flat binary blob.
+The blob is copied to a specific RAM address and executed.
-This document will contain my findings with regards to the PSX-EXE format.
+In practice you normally build an ELF first (because toolchains and linkers understand ELF well).
+Then you convert ELF to PS-X EXE for running on console or in emulators.
+For reverse engineering you often do the inverse: treat a PS-X EXE as a raw binary and re-wrap it as an ELF (or import it as "raw" with a base address).
-Some Information about the format:
-* Sony's CD player can't read executables file that aren't multiple of 2048 bytes [^1]
+Some quick facts about the format:
+* **Header size** - The header is `0x800` bytes (2048 bytes), i.e. exactly one CD-ROM sector [^2] [^5] [^6]
+* **Payload location** - The actual program data begins at file offset `0x800` [^2] [^5] [^6]
+* **CD alignment** - The total file size is usually padded up to a multiple of 2048 bytes [^1] [^5] [^6]
+---
+## Glossary of Key Terms
+If you are new to PS1 executable terminology, this quick glossary should help:
+
+* **ELF** - A standard executable format used by many Unix-like toolchains.
+* **PC** - Program Counter, the address the CPU will jump to when starting the program.
+* **GP** - The MIPS `$gp` global pointer register, used for "small data" accesses.
+* **SP** - The MIPS `$sp` stack pointer register.
+* **BSS** - Uninitialized globals that should start as zero.
+* **objcopy** - GNU binutils tool used to transform object/executable formats (for example, ELF to raw binary).
+
+---
# Useful Sources
-* ELF2EXE source code - https://github.com/cetygamer/psxsdk/blob/master/tools/elf2exe.c
+These links are worth keeping open while reading the header tables below:
+* **PSXSDK elf2exe** - A minimal ELF to PS-X EXE converter (`tools/elf2exe.c`) that shows which header fields are required in practice [^3]
+* **PS-X EXE header notes** - A readable breakdown of the common header fields and offsets [^2]
+* **Net Yaroze User Guide** - An official document that describes PS-X EXE layout and the key fields expected by the loader (entrypoint, GP, and the "data without initial values" region) [^5]
+* **BIOS Exec behaviour** - Useful when you want to know which fields the BIOS `Exec` function actually reads (PC, GP, stack, memfill) [^4]
+
+---
+# High-Level File Layout
+A PS-X EXE is a 2048-byte header followed by the payload.
+This is the layout you will see in most files:
+
+```text
+0x0000..0x07FF Header (0x800 bytes)
+0x0800.. Payload (flat binary to copy into RAM)
+... Optional padding up to a 2048-byte boundary
+```
+
+---
+# Header Fields
+The table below lists the fields you will most commonly care about when reversing or generating PS-X EXEs.
+All multi-byte integers are little-endian.
+
+Offset | Size | Name | Description
+--- | --- | --- | ---
+0x00 | 8 | Magic | ASCII `PS-X EXE`
+0x08 | 8 | Reserved | Usually zero
+0x10 | 4 | Initial PC | Initial PC value (entrypoint)
+0x14 | 4 | Initial GP | Initial GP value
+0x18 | 4 | Destination address | RAM address where the payload (from `0x800`) will be copied [^6]
+0x1C | 4 | Payload size | Bytes to load from the file body (excluding the `0x800` byte header). The kernel expects this to be a multiple of `0x800`. [^6]
+0x20 | 4 | Data address | Optional "data section" address. Usually zero in most executables. [^6] [^7]
+0x24 | 4 | Data size | Optional "data section" size in bytes. Usually zero in most executables. [^6] [^7]
+0x28 | 4 | BSS start | Uninitialized data (BSS) start address. Also used as the BIOS "memfill" start. [^6] [^4]
+0x2C | 4 | BSS size | Uninitialized data size in bytes. Also used as the BIOS "memfill" size. [^6] [^4]
+0x30 | 4 | Stack base | If non-zero, BIOS `Exec` sets SP and FP to `stack_base + stack_offset`. [^6] [^4]
+0x34 | 4 | Stack offset | Added to stack base by BIOS `Exec`. [^6] [^4]
+0x38 | 0x14 | Exec reserved | Must be zero in the file. Used by BIOS `Exec` to save caller registers when chaining executables. [^6] [^4]
+0x4C | 0x7B4 | Marker and reserved | A region string often set to something like "Sony Computer Entertainment Inc. for North America area" followed by zero padding. The BIOS does not verify it. [^6]
+0x800 | ... | Payload start | Program data begins here (copied to the load address)
+
+When using the BIOS `LoadTest/Load` functions, the BIOS copies header bytes `0x10..0x4B` into a separate `headerbuf` structure.
+In PsyQ documentation this `headerbuf` layout is the `EXEC` structure (PC, GP, text/data/BSS addresses and sizes, and stack fields). [^7]
+The BIOS `Exec` function then reads the PC/GP/stack/memfill values from that buffer and starts the program [^4]
+This means the ASCII marker at `0x4C` is not part of the data structure that `Exec` reads [^4]
+
+---
+# BIOS Load and Exec Semantics
+The key thing to know is that the BIOS/kernel does not execute the file "in place".
+It reads the header, copies the payload to RAM, optionally clears a memory range, and then jumps to the entrypoint [^6] [^4]
+
+The standard sequence is:
+* Call `LoadTest` to parse the PS-X EXE header into an in-memory `headerbuf` (`0x10..0x4B` from the file header) [^4]
+* Call `Load` to additionally copy the payload to the destination address (and flush caches) [^4]
+* Call `Exec(headerbuf, param1, param2)` to clear the BSS/memfill range, set up registers, and jump to the entrypoint [^4]
+
+Some details that affect real programs and reverse engineering:
+* **Memfill is word-based and slow** - The BIOS memfill runs word-by-word, so the BSS address and size must be multiples of 4. It also runs in slow ROM, so large memfills are expensive. [^6]
+* **The Exec reserved region is live state** - `Exec` saves the caller's `RA`, `SP`, `R30`, `R28`, `R16` into the reserved region (`0x38..0x4B`) in the `headerbuf` structure, not onto the stack. [^6] [^4]
+* **Stack fields are context dependent** - If the stack base is zero, `Exec` leaves the caller's stack unchanged. Boot executables often end up using the `SYSTEM.CNF` stack instead of the EXE header values. [^6] [^4]
+* **Two parameters are passed to the entrypoint** - `param1` and `param2` are passed in `R4` and `R5`. Many boot flows end up passing `R4=1` and `R5=0`. [^6] [^4]
+* **Return-to-caller is possible (sometimes)** - A non-boot executable can return to the caller by jumping to the incoming `RA` (and by preserving stack/register conventions). If the boot executable returns to the BIOS, the BIOS typically locks up. [^6]
+
+---
+# Boot Executable and SYSTEM.CNF Notes
+On CD-ROM, the first executable is normally selected via `SYSTEM.CNF` [^6]
-# EXE Header
-The table below lists the relevant entries.
+A typical `SYSTEM.CNF` includes:
+* **BOOT line** - Path to the boot executable, plus an optional argument string
+* **STACK** - The stack top used by the boot process
+* **TCB/EVENT** - Kernel configuration for threads and events
-Name | Length | Description
---- | --- | ---
-PSX Magic Number | 8 | Spells out 'PS-X EXE'
-Unknown | 16 | ???
-Initial Program Counter | 4 | e.g 0x80010000 (stored opposite way around due to little endian)
-Global Pointer | 4 | e.g 0x0009B000
+The kernel can copy the optional `BOOT` argument string to RAM at `0x00000180`, where the executable can read it if it wants to implement a command line. [^6]
+---
+# Deep Dive Into PSXSDK elf2exe
+The PSXSDK `elf2exe.c` tool is intentionally small.
+It does not parse ELF program headers or sections.
+Instead, it shells out to objcopy to turn the ELF into a flat binary.
+Then it writes a PS-X EXE header with a few hard-coded defaults, and appends the binary blob.
+
+This is the rough flow of the tool:
+* **Write header skeleton** - Write the magic at `0x00`, then `fseek` around to fill a handful of fields [^3]
+* **Hard-code entrypoint and destination address** - It writes `0x80010000` into both Initial PC (`0x10`) and Destination address (`0x18`) [^3]
+* **Optionally set GP** - It writes the Initial GP (`0x14`) from `-gp=`, defaulting to `0` [^3]
+* **Hard-code stack** - It writes `0x801FFFF0` at `0x30` (and leaves the stack offset at `0`) [^3]
+* **Write an ASCII marker** - It writes a region string at `0x4C` selected by `-mark_jpn`, `-mark_eur`, or `-mark=` [^3]
+* **Run objcopy** - It calls `objcopy -O binary input.elf input.elf.bin` [^3]
+* **Append payload at 0x800** - It copies `input.elf.bin` into the output file starting at `0x800` [^3]
+* **Pad to 2048 bytes** - It extends the output file to a multiple of 2048 bytes [^3]
+* **Backpatch the payload size** - It writes `(final_file_size - 0x800)` into the Payload size field at `0x1C` [^3]
+
+---
+## Header Values Written by elf2exe
+If you are sanity-checking an output file in a hex editor, these are the key offsets and values written by `elf2exe.c` [^3]
+
+Offset | Field | Value written by PSXSDK elf2exe | Notes
+--- | --- | --- | ---
+0x00 | Magic | `PS-X EXE` | Always written
+0x10 | Initial PC | `0x80010000` | Hard-coded
+0x14 | Initial GP | `-gp=` (default `0x00000000`) | Supported but not listed in the `usage:` help text
+0x18 | Destination address | `0x80010000` | Hard-coded
+0x1C | Payload size | `(aligned_output_size - 0x800)` | Backpatched after padding
+0x30 | Stack base | `0x801FFFF0` | Hard-coded
+0x34 | Stack offset | `0x00000000` | Left untouched (the file is sparse/zero-filled here)
+0x4C | Marker | Region string | `-mark_jpn`, `-mark_eur`, or `-mark=` (default: USA string)
+
+---
+## Toolchain Assumptions
+The converter relies on `objcopy` to flatten the ELF into a raw blob.
+In `elf2exe.c` the command is built using the `OBJCOPY_PATH` macro, which must be defined when compiling the tool (or patched into the source) [^3]
+
+It also writes the intermediate file by simply appending `.bin` to the input filename.
+So an input named `main.elf` will produce an intermediate `main.elf.bin` before it is copied into the PS-X EXE and deleted again [^3]
+
+Two details matter a lot if you try to use this tool as-is:
+* **It assumes a fixed link address** - Because it forces the destination address and PC to `0x80010000`, your ELF must be linked to run from `0x80010000`.
+ If your code is linked for a different address, the BIOS will copy it to the wrong place and jump to the wrong place.
+* **It only fills a subset of the header** - Fields like Memfill start/size are left as zero, so the BIOS won't clear a BSS region for you.
+There is also a small implementation gotcha in the file copy loop.
+It uses `while(!feof(f)) { fgetc(); fputc(); }`, which typically writes one extra byte (`0xFF`) after the real payload.
+If you depend on exact payload size, you should fix the loop (or be aware that the PS-X EXE may contain a single trailing junk byte before padding).
+---
+# Reversing Notes
+If you are importing a PS-X EXE into a disassembler, the easiest approach is usually to treat it as raw binary plus metadata from the header:
+* **Base address** - Use Destination address (`0x18`) as the base address for the payload
+* **Entrypoint** - Use Initial PC (`0x10`) as the entrypoint
+* **Skip header** - Skip the first `0x800` bytes when importing, because that is the header
+
+For example, you can extract just the payload like this:
+
+```bash
+dd if=GAME.EXE of=payload.bin bs=2048 skip=1
+```
+
+Then import `payload.bin` as a raw MIPS little-endian binary at the destination address you read from the header.
+
+---
+# Related Formats
+If you are dealing with PS1 discs or dev builds, you may also run into formats that are not plain PS-X EXE:
+* **PsyQ .CPE** - A chunked "debug executable" format used by PsyQ toolchains. It can contain many small load chunks and explicit register settings for the entrypoint. [^6]
+* **Custom relocatable executables** - Rarely, a disc can contain executables with magic like `PS-X EXR` that are not supported by the standard PSX kernel loader. [^6]
+
+---
# References
-[^1]: https://www.linux-mips.org/wiki/PS1
+[^1]: [linux-mips PS1](https://www.linux-mips.org/wiki/PS1)
+[^2]: [Zanneth PS-X EXE file format](https://zanneth.com/2020/04/06/psx-exe-file-format/)
+[^3]: [PSXSDK elf2exe.c](https://github.com/cetygamer/psxsdk/blob/master/tools/elf2exe.c)
+[^4]: [psx-spx BIOS kernel `Exec`](https://psx-spx.consoledev.net/kernelbios/#exec)
+[^5]: [Net Yaroze Official Startup Guide (PDF)](https://www.psxdev.net/downloads/Net%20Yaroze%20Official%20-%20Startup%20Guide.pdf)
+[^6]: [psx-spx CDROM File Formats (PS-X EXE and SYSTEM.CNF)](https://psx-spx.consoledev.net/cdromfileformats/)
+[^7]: [PsyQ Run-Time Library Reference (EXEC structure)](https://psx.arthus.net/sdk/Psy-Q/DOCS/Devrefs/Libref.pdf)
diff --git a/pages/consoles/ps2/SN-Systems-Network-Development-Kit-for-PS2.md b/pages/consoles/ps2/SN-Systems-Network-Development-Kit-for-PS2.md
index 136fee08..c75c878a 100644
--- a/pages/consoles/ps2/SN-Systems-Network-Development-Kit-for-PS2.md
+++ b/pages/consoles/ps2/SN-Systems-Network-Development-Kit-for-PS2.md
@@ -67,21 +67,13 @@ One big bonus feature of the SN Systems NDK is that it actually supports not jus
According to the official specifications of the NDK it claims to support more than just TCP/IP [4]:
* TCP/IP - Transport Control Protocol / Internet Protocol
-
* UDP - User Datagram Protocol
-
* PPP - Point-to-Point Protocol (PPP is always built into the Stack irx file but will only be used when a modem driver is used)
-
* PPPoE - PPP over Ethernet
-
* ARP - Address Resolution Protocol (ARP will only be used when an Ethernet or DECI2 driver is loaded)
-
* ICMP - Internet Control Message Protocol
-
* DHCP - Dynamic Host Configuration Protocol
-
* CHAP - Challenge-Handshake Authentication Protocol
-
* PAP - Password Authentication Protocol
## NDK Analyzer
diff --git a/pages/consoles/wiiU/WiiUFileFormats.md b/pages/consoles/wiiU/WiiUFileFormats.md
index 279d3ee3..27cc10b5 100644
--- a/pages/consoles/wiiU/WiiUFileFormats.md
+++ b/pages/consoles/wiiU/WiiUFileFormats.md
@@ -1,7 +1,7 @@
---
layout: post
tags:
-- wiiU
+- wiiu
- fileformats
title: Wii U File Formats
image: /public/wiiU/WiiUFileFormats.jpg
@@ -11,10 +11,10 @@ breadcrumbs:
- name: Home
url: /
- name: WiiU
- url: /wiiU
+ url: /wiiu
- name: Wii U File Formats
url: #
-recommend: wiiU
+recommend: wiiu
editlink: /consoles/wiiU/WiiUFileFormats.md
---
diff --git a/pages/consoles/wiiU/WiiUGameEngines.md b/pages/consoles/wiiU/WiiUGameEngines.md
index e86b9f90..7ed76428 100644
--- a/pages/consoles/wiiU/WiiUGameEngines.md
+++ b/pages/consoles/wiiU/WiiUGameEngines.md
@@ -1,7 +1,7 @@
---
layout: post
tags:
-- wiiU
+- wiiu
- gameengines
title: WiiU eShop Game Engines
image: /public/wiiU/WiiUeShopGameEngines.jpg
@@ -11,10 +11,10 @@ breadcrumbs:
- name: Home
url: /
- name: WiiU
- url: /wiiU
+ url: /wiiu
- name: WiiU eShop Game Engines
url: #
-recommend: wiiU
+recommend: wiiu
editlink: /consoles/wiiU/WiiUGameEngines.md
---
diff --git a/pages/consoles/wiiU/WiiUNAND.md b/pages/consoles/wiiU/WiiUNAND.md
index 760bc2e8..59559e35 100644
--- a/pages/consoles/wiiU/WiiUNAND.md
+++ b/pages/consoles/wiiU/WiiUNAND.md
@@ -1,7 +1,7 @@
---
layout: post
tags:
-- wiiU
+- wiiu
title: WiiU NAND Flash
image: /public/wiiU/WiiUnandFlash.jpg
twitterimage: https://www.retroreversing.com/public/wiiU/WiiUnandFlash.jpg
@@ -11,32 +11,249 @@ breadcrumbs:
- name: Home
url: /
- name: WiiU
- url: /wiiU
+ url: /wiiu
- name: WiiU NAND Flash
url: #
-recommend: wiiU
+recommend: wiiu
editlink: /consoles/wiiU/WiiUNAND.md
updatedAt: '2019-03-02'
---
-# What is NAND?
-NAND Flash Memory is the built in memory of the Wii console. It houses save data, downloaded channels, and the Wii U Menu [^3].
+The Wii U uses NAND flash memory as its **primary non-volatile storage**, meaning it retains data even when powered off. This is fundamental to how the system boots, operates, and stores user data. It houses save data, downloaded channels, and the Wii U Menu [^3].
-# Physical NAND chips
-The Wii U has 2 NAND chips. The MLC, which stores Wii U data, and the SLC, which has 2 separate sections ("banks" if you will), one that stores vWii data only and one that stores the rest of the Wii U data.
+## What is NAND?
+NAND stands for **"NOT AND"**. It originates from Boolean algebra, where NAND is a type of **logic gate**.
+
+NAND flash memory (like in the Wii U) is built using arrays of NAND logic gates:
+* These gates store bits by trapping electrical charge
+* Their structure allows **high-density storage**
+* Cheaper and more compact than alternatives like NOR flash
+
+So when you hear "NAND memory", it literally refers to memory built using **NAND (NOT AND) logic gate architecture**.
+
+## Why does the Wii U use NAND?
+Thw Wii U uses NAND because it:
+* Retains data without power
+* Is compact and soldered directly to the motherboard
+* Has faster access than traditional HDDs for system-level operations
+
+### Why NAND specifically (vs other storage)?
+NAND flash is chosen because:
+* No moving parts -> durability
+* Lower power consumption
+* High density for small form factor
+* Sufficient speed for firmware execution
+
+This makes it ideal for embedded systems like consoles.
+
+---
+## What is stored on the Wii U NAND?
+NAND is used on the the Wii U to store:
+* User profiles
+* Save data
+* System settings
+* Installed titles (on Basic models especially)
+
+### System firmware and boot process
+The NAND contains the **entire operating system stack**, including:
+* Bootloaders (early-stage code executed at power-on)
+* System firmware (menu, system services)
+* Security components (encryption keys, signature checks)
+
+Without NAND, the console has **no instructions to execute**, so it cannot boot at all. Unlike systems that rely on removable storage, the Wii Uβs internal NAND is mandatory for initialization.
+
+### System updates and installed content
+System updates are written directly to NAND:
+* Firmware updates modify system titles in NAND
+* Downloaded games (especially on 8GB models) are stored there
+* Virtual Console and system apps reside in NAND
+
+---
+## How much NAND is available to the Wii U?
+The Wii U shipped in two variants:
+* Basic model - 8GB MLC NAND (~3GB free after system files)
+* Deluxe model - 32GB MLC NAND (~25GB free after system files)
+
+In both cases the SLC is the same size at roughly 512MB.
+
+---
+# Wii U's NAND Hardware
+Internally, the Wii U actually uses two types of NAND-based storage:
+* **SLC** NAND - Stores critical system data (bootloaders, OS, security) (~512MB, fast, high durability)
+* **MLC** NAND - Stores user data (games, saves, downloads) (8GB or 32GB, slower, cheaper)
+
+The system **boots entirely from SLC first**, then mounts and uses MLC for user data.
All of these are accessed as separate partitions by the Wii U [^2].
-## Caution changing NAND!
-Be very careful with NAND changes as the wiiU has no way of fixing the NAND if it breaks, so basically it bricks your console.
+## SLC (Single-Level Cell) NAND
+The **SLC (Single-Level Cell) NAND** is the **critical system storage**. It is small (~512MB) but extremely important because it contains everything required to **boot, verify, and control the system**.
+
+The SLC contains both Wii U and vWii system data, these are separated logically via filesystem structure and title IDs. So vWii operates in a sandboxed environment, but not on a physically separate NAND bank.
+
+### Boot chain (lowest-level startup code)
+The SLC contains the early boot stages:
+* **boot0** - Hardcoded entry point (partially in ROM, partially tied to NAND), Initializes hardware and loads next stage
+* **boot1** - Verified by boot0 (cryptographic check) then Loads further system components
+* **boot2 / IOSU kernel loading** - Brings up the main system OS layer
+
+If anything here is corrupted, the console **cannot boot at all** (hard brick).
+
+### IOSU (system operating environment)
+The Wii U runs a subsystem similar to the Wiiβs IOS, called **IOSU** which handles:
+* File system
+* Permissions
+* Hardware access
+* Security enforcement
+
+This lives primarily on SLC because:
+* It must be **trusted**
+* It must be **available immediately at boot**
+
+### System titles and core modules
+SLC stores essential system titles:
+* System menu components
+* Core libraries
+* Background services (account handling, update services)
+
+These are distinct from user-installed titles (which go on MLC).
+
+---
+### Security data and keys
+This is one of the most important parts:
+* **Console-unique encryption keys**
+* Certificates
+* Signing data used to verify software
+
+These enforce Nintendoβs trust chain:
+* Every executable must be signed
+* Boot stages verify each other
+* Prevents unauthorized code execution
+
+---
+### Configuration and critical system data
+Includes:
+* System configuration (region, settings)
+* Internal databases for system operation
+* Low-level logs and flags
+
+### Why SLC is used for this
+SLC NAND is chosen specifically because:
+* **Higher reliability** (fewer bit errors)
+* **Much higher write endurance**
+* **Faster read/write latency**
-## NAND Backup
+This is essential for:
+* Boot integrity
+* System stability
+* Preventing corruption in critical areas
+
+### Failure implications
+If SLC is damaged:
+* No recovery through normal means
+* System cannot initialize hardware or OS
+* Requires NAND backup restoration, or Hardware-level repair
+
+This is why in modding:
+* **SLC dumps are the most critical backup**
+* Corrupting SLC is far more dangerous than MLC
+
+---
+## MLC (Multi-Level Cell) NAND
+The **MLC (Multi-Level Cell) NAND** is the **main bulk storage** of the system. This is the part that differs between the 8GB and 32GB models and is where almost all **user-facing data** lives.
+
+### Installed games and applications
+The MLC stores:
+* eShop downloads
+* Game updates and patches
+* DLC content
+* Virtual Console titles
+
+These are stored as **titles** in a structured directory layout (similar concept to SLC, but much larger scale).
+
+---
+### User data
+The MLC also stores:
+* Save files
+* User profiles
+* Account-linked data (e.g. NNID)
+* Game-specific settings
+
+This is the data that changes frequently during gameplay.
+
+---
+### System data (non-critical)
+MLC also holds less critical system components:
+* Some system applications
+* Update data before installation
+* Cache data
+
+Important distinction:
+* If MLC data is corrupted β system may still boot
+* If SLC is corrupted β system will not boot
+
+---
+## Filesystem and layout
+The MLC uses a filesystem often referred to as the **WFS (Wii U File System)** which is encrypted per-console and structured into:
+* `/usr/` - user data, saves, installs
+* `/sys/` - system-related data (non-boot-critical)
+
+Each installed title has:
+* A title ID
+* Its own directory structure
+* Metadata and content files
+
+### Performance and behavior
+MLC is:
+* Slower than SLC
+* More prone to wear over time
+* Managed by wear-leveling algorithms
+
+This is why:
+* Game installs can be relatively slow
+* Heavy write usage (e.g. frequent saves) is managed carefully
+
+### Failure characteristics
+MLC issues are more common than SLC:
+* Bad blocks can develop over time
+* Filesystem corruption can occur
+
+This Leads to missing games, save data loss and installation failures but typically the system still boots and data can sometimes be rebuilt or reinstalled
+
+---
+# Security and anti-piracy
+The NAND plays a critical role in enforcing Nintendoβs security model:
+* Stores **console-unique encryption keys**
+* Holds signed system titles verified during boot
+* Prevents unauthorized code execution
+
+If NAND contents are corrupted or mismatched, the system can fail to boot (brick), because security checks fail.
+
+So be very careful with NAND changes as the wiiU has no way of fixing the NAND if it breaks, so basically it bricks your console.
+
+## Backing Up WiiU NAND
+If the SLC NAND is:
+* **Corrupted** -> system may brick
+* **Removed** -> system cannot boot
+* **Modified incorrectly** -> fails security checks
+
+This is why SLC NAND backups are critical in modding communities.
+
+MLC dumps on the other hand are large but **less critical than SLC**, in fact rebuilding MLC is sometimes possible if SLC is intact.
+
+Many hacks interact with MLC:
+ * Title installation
+ * Save injection
+
+### NAND Backup
Every NAND backup is unique and only belongs to the system it came from. Every console has it's own unique soul you can say [^1].
-So you can't use someone else's NAND backup for your own wiiU!
+So you can't use someone else's NAND backup for your own wii U!
-## redNAND (Redirected NAND)
+### redNAND (Redirected NAND)
Imagine you could use your SD card instead of the internal system NAND, that means you could modify it as much as you want and when it bricks you just fix the files on SD card. Well that is exactly what redNAND does!
+[StroopwafelCFW/minute_minute: Wii U boot1 replacement](https://github.com/StroopwafelCFW/minute_minute)
+---
# References
[^1]: https://www.reddit.com/r/WiiUHacks/comments/7ihoql/is_there_any_type_of_recovery_boot_mode/
[^2]: https://gbatemp.net/threads/nand-wiiu-which-is-what.465750/
-[^3]: https://dolphin-emu.org/docs/guides/nand-usage-guide/
+[^3]: https://dolphin-emu.org/docs/guides/nand-usage-guide/
\ No newline at end of file
diff --git a/pages/consoles/wiiU/WiiUNWF.md b/pages/consoles/wiiU/WiiUNWF.md
index 17b42b84..58a0e409 100644
--- a/pages/consoles/wiiU/WiiUNWF.md
+++ b/pages/consoles/wiiU/WiiUNWF.md
@@ -1,7 +1,7 @@
---
layout: post
tags:
-- wiiU
+- wiiu
- middleware
- gameengines
title: Wii U Nintendo Web Framework
@@ -12,11 +12,11 @@ breadcrumbs:
- name: Home
url: /
- name: WiiU
- url: /wiiU
+ url: /wiiu
- name: Wii U Nintendo Web Framework (NWF)
url: #
recommend:
- - wiiU
+ - wiiu
- middleware
editlink: /consoles/wiiU/WiiUNWF.md
---
diff --git a/pages/consoles/wiiU/WiiUUnStrippedBinaries.md b/pages/consoles/wiiU/WiiUUnStrippedBinaries.md
index fdc4557a..5592d519 100644
--- a/pages/consoles/wiiU/WiiUUnStrippedBinaries.md
+++ b/pages/consoles/wiiU/WiiUUnStrippedBinaries.md
@@ -1,7 +1,7 @@
---
layout: post
tags:
-- wiiU
+- wiiu
- symbols
- games
title: Wii U Games with Debug symbols (UnStripped Binaries)
@@ -15,10 +15,10 @@ breadcrumbs:
- name: Home
url: /
- name: WiiU
- url: /wiiU
+ url: /wiiu
- name: Wii U Games with Debug symbols (UnStripped Binaries)
url: #
-recommend: wiiU
+recommend: wiiu
editlink: /consoles/wiiU/WiiUUnStrippedBinaries.md
references:
- youtube
diff --git a/pages/consoles/wiiU/WiiUUnity.md b/pages/consoles/wiiU/WiiUUnity.md
index 1865005d..0f73c91f 100644
--- a/pages/consoles/wiiU/WiiUUnity.md
+++ b/pages/consoles/wiiU/WiiUUnity.md
@@ -1,7 +1,7 @@
---
layout: post
tags:
-- wiiU
+- wiiu
- gameengines
- unity3d
title: Unity Game Engine Games on wiiU eShop
@@ -12,11 +12,11 @@ breadcrumbs:
- name: Home
url: /
- name: WiiU
- url: /wiiU
+ url: /wiiu
- name: WiiU Unity Game Engine
url: #
recommend:
- - wiiU
+ - wiiu
- gameengines
- unity3d
editlink: /consoles/wiiU/WiiUUnity.md
diff --git a/pages/consoles/wiiU/WiiUVirtualConsole.md b/pages/consoles/wiiU/WiiUVirtualConsole.md
index cc7e032b..5f7b4f3e 100644
--- a/pages/consoles/wiiU/WiiUVirtualConsole.md
+++ b/pages/consoles/wiiU/WiiUVirtualConsole.md
@@ -1,7 +1,7 @@
---
layout: post
tags:
-- wiiU
+- wiiu
title: Wii U Virtual Console Reversing
image: /public/wiiU/WiiU Virtual Console Reversing.jpg
twitterimage: https://www.retroreversing.com/public/wiiU/WiiU Virtual Console Reversing.jpg
@@ -11,10 +11,10 @@ breadcrumbs:
- name: Home
url: /
- name: WiiU
- url: /wiiU
+ url: /wiiu
- name: Wii U Virtual Console Reversing
url: #
-recommend: wiiU
+recommend: wiiu
editlink: /consoles/wiiU/WiiUVirtualConsole.md
updatedAt: '2019-03-04'
---
diff --git a/pages/leaks/Nintendo/GigaleakNEWS05.md b/pages/leaks/Nintendo/GigaleakNEWS05.md
index 971efcf8..c182535a 100644
--- a/pages/leaks/Nintendo/GigaleakNEWS05.md
+++ b/pages/leaks/Nintendo/GigaleakNEWS05.md
@@ -57,35 +57,20 @@ The archive preserves two distinct working environments:
If you are new to SNES 3D development and Nintendo's internal tool ecosystem, this glossary will help:
* **CAD** - Computer-Aided Design. Here, a proprietary 3D modeling and animation tool built by Nintendo for creating polygon geometry and animation keyframes on X11 workstations.
-
* **NCA** - Nintendo CAD Animation binary format. Compiled output from the CAD tool, optimized for SNES hardware execution. Contains both geometry and animation keyframes in a compact binary layout.
-
* **ANM** - Animation timeline source format. Text-readable keyframe data defining how 3D geometry transforms over time (skeletal animation, vertex morphing).
-
* **CAD Source** - `.cad` and `.txt` files containing 3D model definitions. The `.txt` format is ASCII vertex lists; `.cad` is the compiled binary model.
-
* **Transfer Protocol** - Mechanism for sending compiled models and animations from the Unix workstation to SNES development hardware via serial link or Ethernet.
-
* **Fundoshi** - Likely a Nintendo-internal CPU optimization variant. Represents SNES-specific compiled binaries and math libraries optimized for the 65C816 processor.
-
* **IBM Variant** - PC-compatible version of Kimura's utilities, allowing asset preview and testing on standard DOS/Windows workstations before final SNES compilation.
-
* **VRAM** - Video RAM used on SNES for tile data, background maps, and sprite attributes. Extremely limited (64 KB total), requiring careful asset management.
-
* **WRAM** - Work RAM (128 KB on SNES). Used for game state, sprite data, and runtime animation state.
-
* **EPROM** - Erasable Programmable Read-Only Memory. SNES development boards used 1MB EPROM chips; large games like SF2 required multiple chips. `partition.c` managed splitting ROM images across these boundaries.
-
* **Z-Buffer** - Depth buffer for 3D rendering. SNES has no hardware Z-buffer, so `depth.c` implements painter's algorithm (sorting polygons by depth).
-
* **X11** - Network-capable graphical display system used on Unix workstations. The CAD tool uses X11 for its GUI.
-
* **Skeletal Animation** - Animation system where a 3D model is defined by bones/joints in a hierarchy. Transformations (translation, rotation, scale) are applied to bones, and geometry deforms based on bone positions.
-
* **Keyframe Interpolation** - Smooth transitions between animation poses. Animators define key poses at certain frames; the system automatically tweens intermediate frames.
-
* **Painter's Algorithm** - Rendering technique that sorts polygons by depth and draws them back-to-front. Used on SNES because hardware Z-buffering is unavailable.
-
* **Mode 7** - SNES background rotation/scaling capability. Used in F-Zero for the track perspective effect. `cnvmode7.c` converts graphics to Mode 7 format.
---
@@ -1021,11 +1006,9 @@ The **24 BGM modules** (`xlbgm01.bin` through `xlbgm24.bin`) suggest this was a
* Sound effect playback
* BGM sequencing
* Real-time mixing/volume control
-
* **Sound Effects Library** (`xlsound.bin`, `xlsnd01` through `xlsnd04`)
* Compiled SFX banks (weapon fire, explosions, footsteps)
* Indexed by game state (which SFX to play for which action)
-
* **BGM Database** (24 Γ musical pieces)
* Titles, composers, loop points
* Orchestration (which instruments active in each section)
@@ -1568,19 +1551,16 @@ Take fundoshi/depth.c (optimized for 65C816)
* Modify transfer.c to output SF2-compatible `.nca` format
* Create new stages or modify existing stages
* Build custom ROM with replacement assets
-
* **Character Model Swaps**
* Extract `.cad` files from stages
* Modify geometry (smooth, sharpen, enlarge)
* Recompile and test in emulator
* Example: Create "Giant Andross" mod
-
* **Animation Editing**
* Parse `.anm` keyframe format
* Create custom animation editor
* Blend animations (walk + run hybrid)
* Slow-motion or speed-up effects
-
* **Palette Hacking**
* Extract `.col` color palettes
* Create new color schemes
diff --git a/pages/tools/GameMaker.md b/pages/tools/GameMaker.md
index 69c82d76..0163b5e4 100644
--- a/pages/tools/GameMaker.md
+++ b/pages/tools/GameMaker.md
@@ -474,11 +474,8 @@ In summary, **Game Maker 2.0** modernized the tool's interface and expanded its
On the 23rd November 2001, Overmars released **Game Maker 3.0**, which was a milestone for the software's graphics and performance. The hallmark of version 3.0 was the introduction of **DirectX rendering support** for the first time [^5]. This had several important effects:
* **Hardware-Accelerated Graphics:** By leveraging Microsoft DirectX (likely DirectDraw at this stage), Game Maker could now render graphics more efficiently. Games ran faster and could use full-screen modes and graphical effects that were not feasible under the old software-based renderer. This was a significant step up in capability, as it unlocked the potential for smoother animations and richer visuals.
-
* **Same Feature Set, But Faster:** Other than the new DirectX-powered renderer, version 3.0 did not radically change the game creation features introduced in 2.0. The user interface and workflow remained similar, but everything was generally more **polished and performant**. For instance, operations that previously might have lagged (like drawing many sprites) could now benefit from DirectX's blitting capabilities. In essence, 3.0 "implemented DirectX for the first time" to boost graphics handling [^5], while maintaining the drag-and-drop and GML systems as they were.
-
* **No Stand-alone Executable Yet:** It's worth noting that even with DirectX, Game Maker 3.0 still lacked an independent runtime or compilation to EXE. Games were executed via the Game Maker environment (or a packaged interpreter). The ability to create a true stand-alone game file was still not present at this stage, coming a bit later.
-
* **Minor 3.x Updates:** Following 3.0, a few minor revisions (3.1, 3.2, and 3.3) were released in 2001 to fix bugs and add minor improvements. These updates improved stability and further increased DirectX support, such as verison 3.1 which removed Exclusive mode in favour of DirectX in windowed mode (3.0 was only DirectX in fullscreen mode). Version 3.2 added new room options: more background options, multiple views, and transitions between rooms
Overall, **Game Maker 3.x** dramatically improved the engine's under-the-hood performance. The use of DirectX was a turning point that allowed users to create more complex and graphically intense games than before [^5].
@@ -492,15 +489,10 @@ This helped Game Maker's community grow even more, as the quality and smoothness
**Game Maker 4.0**, released on the 16th July 2001, was a complete overhaul of the software. Mark Overmars rewrote large portions of Game Maker from scratch for this version [^5], making sweeping changes to the interface, architecture, and capabilities. Important highlights of version 4.0 include:
* **Entirely New Interface:** The IDE in Game Maker 4 was significantly redesigned. The layout and organization of resources were improved, giving the tool a more professional and user-friendly feel. In fact, the Game Maker 4.3 interface (the final revision of this line) looks very *familiar* even to users of much later versions β it established the general design paradigm that persisted in subsequent releases [^2]. This means that by 4.x, Game Maker had a resource tree, event selectors, and editors that resemble those used for years to come.
-
* **Introduction of Multiplayer Functions:** Version 4.0 was the first to include built-in support for basic multiplayer/networking features. A set of **MPlay networking functions** was added, allowing simple multi-computer play over a network or the internet. This was a notable expansion of Game Maker's capabilities beyond single-player games. Although the networking system was rudimentary (suitable for simple games or turn-based exchanges), it demonstrated Overmars's intent to broaden the engine's scope.
-
* **Standalone Executables (EXE) Export:** For the **first time**, Game Maker could compile games into independent executable files. With version 4, developers were no longer confined to sharing editable project files; they could **create a stand-alone Windows EXE** for their game and distribute it to others who didn't have Game Maker. Under the hood, this worked by bundling the game's resources with a runner program. The new feature was transformative - games made in GM4 could be run like any other Windows program [^7]. (This capability coincided with the introduction of a separate "game runner" module in the architecture). The addition of EXE output in 4.0 greatly increased Game Maker's appeal, as creators could publish their games more easily.
-
* **Continued DirectX Support:** Building on version 3, Game Maker 4 fully embraced DirectX for rendering. The rewrite likely optimized the use of DirectDraw and related technologies even further, making 2D drawing faster and enabling things like smooth sprite rotations and better transparency handling by default. The combination of DirectX acceleration *and* stand-alone export made GM4-generated games much closer to "real" indie games of the time.
-
* **New Icon/Branding:** Game Maker 4 introduced a new program icon (a **red gear/hammer icon**), replacing the icon used in versions 1β3 [^6]. This visually signaled a new era for the software. The red icon design continued to be used through versions 5, 6, and 7 [^6], indicating that GM4 set a branding precedent as well.
-
* **Game Maker 4.x Updates:** After 4.0's release, Overmars issued a few updates (4.1, 4.2 and 4.3) in 2001β2002 to refine the new system. By **Game Maker 4.3b** (released in 2002), the software was very stable and feature-rich for its generation [^2]. This period saw frequent updates, a growing library of user-made examples, and even the launch of the first community-made Game Maker magazine [^2]. The 4.x series firmly established Game Maker's core design; many long-time users started with version 4.2 or 4.3 and found the experience recognizable even in later versions.
In summary, **Game Maker 4.0 was a landmark release**. It delivered a modernized, rewritten IDE, support for networking play, and the much-demanded ability to compile games into executables [^5] [^7]. The engine had matured considerably, and by the end of the 4.x cycle Game Maker was a robust tool for 2D game development. These changes propelled Game Maker into the "prominence" phase β the user community greatly expanded around this time, thanks in part to the newfound ease of sharing completed games.
@@ -511,13 +503,9 @@ In summary, **Game Maker 4.0 was a landmark release**. It delivered a modernized
Released in **April 2003**, **Game Maker 5.0** built upon the solid foundation of the 4.x series and introduced a couple of notable new features. It also marked a shift in Game Maker's distribution model from freeware to a shareware/registration model. Key points for version 5.0 include:
* **External Files and Custom Data:** Game Maker 5 added support for using external files in games [^5]. This meant games could read and write files (such as saving custom data, configurations, or high scores to an external text file) more easily, and could include external resources. This opened the door for more complex game behavior (for example, loading level data from files, or modifiable content). Essentially, GM5 introduced new functions to handle files and perhaps binary data, giving developers more flexibility beyond the fixed resources in the editor.
-
* **Timelines:** Another major feature in 5.0 was the introduction of **Timelines** as a resource type [^5]. A timeline in Game Maker allows the creator to schedule actions to occur at specific steps (moments) during game execution. This is useful for coordinating sequences of events (for instance, scripting a cutscene or orchestrating waves of enemies in a shooter). The timeline editor let users create a list of actions indexed by time without writing code, which was a powerful addition to the drag-and-drop toolkit.
-
* **General Improvements:** Version 5 continued to improve overall stability and added smaller features. For example, there were likely new actions and functions (taking advantage of the external file capability), and quality-of-life improvements in the IDE. It also kept all the important features from 4.x: DirectX graphics, EXE output, etc., refining them further. By this time, Game Maker was quite feature-rich in 2D game mechanics, so 5.0's main innovations were about data and structure (files and timelines).
-
* **Registration System Introduced:** **Game Maker 5.0 was the first version that was not completely free**. Mark Overmars introduced a registration fee of $15 USD for the software [^2]. The initial approach was that Game Maker 5 could be downloaded for free, but it would run in a limited mode (with certain advanced features disabled and a **nag screen** at startup reminding users to register). Users could pay $15 to unlock the full "registered" version. This change was made to support ongoing development, as Overmars had previously only asked for voluntary donations [^2]. The **nag screen** (displayed when running the GM5 IDE or when launching games made with the unregistered version) became famous β it showed the Game Maker logo and a request to register [^2]. Once registered, Game Maker 5 allowed access to all features, which included things like using DLLs and other advanced functions (some of these pro-only features were added in minor updates or were present but locked for unregistered users).
-
* **Community Growth:** Alongside GM5's release, 2003 also saw the launch of the official Game Maker Community forums in their modern form [^2]. This greatly helped users share knowledge, and a surge of new users joined around this time, drawn by Game Maker's expanding capabilities. Many high-quality example games and tutorials from the community began appearing on the official site during the GM5 era [^2].
In essence, **Game Maker 5.0** was an evolutionary update that extended the engine's functionality into new areas like file I/O and event scheduling (timelines) [^5]. It also marked Game Maker's transition to a partly commercial product with the introduction of a registration fee [^2]. Despite some initial community resistance to paying for previously free software, the modest price and the promise of continued improvements kept Game Maker's user base growing. GM5's enhancements were particularly welcomed by more advanced users, as they allowed for games with persistent data and more complex scripted sequences.
@@ -528,13 +516,9 @@ In essence, **Game Maker 5.0** was an evolutionary update that extended the engi
**Game Maker 6.0** was released in October 2004 and represented another major technological upgrade for the engine. The most significant change was a **completely rewritten graphics engine using Direct3D** (part of DirectX) as the new backend [^5]. This brought substantial new graphical capabilities to Game Maker:
* **3D Graphics Functions:** For the first time, Game Maker had built-in support for **3D graphics**. Using Direct3D allowed Overmars to expose functions for drawing 3D primitives, textured shapes, and basic 3D models. Version 6 introduced a set of GML functions (and possibly drag-and-drop actions) that let users create simple 3D scenes β for example, drawing 3D boxes, floors, walls, and even applying textures to them [^2]. This was a *big* change; while GM6 was still primarily a 2D game engine, adventurous users could now experiment with 3D (for instance, making simple first-person or 3D racing games). Many users remember seeing demo projects of 3D spinning cubes and primitive 3D engines soon after GM6's release.
-
* **Enhanced 2D Drawing:** Even for 2D games, the switch to Direct3D brought benefits. It made advanced effects easier β **alpha transparency** (translucency) was supported more smoothly, and **sprite rotation** and scaling became hardware-accelerated operations [^5]. Under the previous DirectDraw system, rotations and alpha blending were either not possible or had to be done via slow software routines. In GM6, one could rotate sprites or set their transparency and have Direct3D handle it efficiently, which opened the door to better visual effects in 2D games (like smooth object rotations, fading objects, particle effects, etc.).
-
* **Performance Improvements:** The use of Direct3D generally improved rendering performance across the board. Games that might have struggled with many objects on screen in GM5 could run faster in GM6 if they took advantage of the new graphics pipeline. Full-screen mode and resolution handling were also improved through Direct3D.
-
* **Minor Changes and Fixes:** Aside from graphics, GM6 continued to refine other aspects. It likely fixed bugs from GM5 and could have introduced minor features or adjustments in response to the community (for example, improvements to the sound engine or timeline system). The overall workflow of Game Maker remained consistent; the big differences were under the hood.
-
* **File Format and Compatibility:** Game Maker 6 used a new file format (.gm6) for saved projects, reflecting the engine changes. Notably, games made in GM6 were not backward-compatible with GM5 due to the new features. Overmars included a converter for GM5 -> GM6 projects, but once a project was in GM6 format, it couldn't be opened in older versions. This was a common pattern with each major release.
Game Maker 6.0's introduction of Direct3D and 3D capabilities was a headline change widely discussed in the community [^2][^5]. Although the typical user base continued to make 2D games, they benefited from the enhanced visuals and effects made possible in this version. The inclusion of 3D functions was somewhat experimental but showcased Game Maker's flexibility. As a contemporary note, 2006 (during the GM6 era) also saw the publication of *"The Game Maker's Apprentice"* (a book by Mark Overmars and Jacob Habgood) which used Game Maker 6 to teach game development [^2]. This further boosted GM6's profile as an educational and hobbyist tool.
@@ -545,15 +529,10 @@ Game Maker 6.0's introduction of Direct3D and 3D capabilities was a headline cha
**Game Maker 7.0** was released on February 28, 2007, and it marked the beginning of the YoYo Games era [^5]. This version was the first published under a partnership with **YoYo Games Ltd.**, a UK-based startup co-founded by Sandy Duncan, which Overmars joined to help expand Game Maker's development and global presence [^2]. Version 7.0 introduced new features and changes both in functionality and in how the product was managed:
* **Extension Packages:** The most touted new feature of GM7 was the ability to **extend Game Maker's functionality through extensions** [^5]. Overmars added a system where users could create and use *Extension Packages* (.gex files), which bundled custom GML scripts, actions, and resources into a reusable form. This meant that advanced users or third parties could add new libraries of functions to Game Maker without needing built-in support from Overmars. For example, one could create an extension to provide physics engine functions, new particle effects, or integration with external APIs, and then import that into Game Maker. This greatly increased the flexibility of the tool. Essentially, Game Maker became somewhat modular β features could be added via extensions, and Mark didn't have to hard-code every new idea into the main program. (In practice, many popular community-made extensions emerged after GM7's release.)
-
* **Resource Library Changes:** In prior versions, users could create their own drag-and-drop action libraries using a separate program (Library Maker). With GM7 and the extension mechanism, the way custom actions were handled changed. It integrated more smoothly to allow extended D&D actions via packages [^5].
-
* **Changes in Asset Storage:** Game Maker 7 introduced a new format for saving projects (.gmk). One notable change was that sprites, sounds, and other resources could optionally be stored *externally* (to avoid bloating the main file). Also, GM7 used an encrypted format for its resource packages to deter easy decompilation of game files (starting with version 7.0's release candidates, game data was encrypted due to concerns over people creating other tools to import .gm6 files (**G-Java**, **LateralGM**)) [^5].
-
* **YoYo Games Integration:** With YoYo Games involved, GM7 began tying into online features. YoYo Games launched a website for sharing Game Maker games (the "Sandbox"), and Game Maker 7 had menu links and features that integrated with this service. For instance, it offered easy uploading of games to the YoYo Games website. Additionally, the registration system changed: YoYo Games handled selling license keys, and the software required an internet connection to activate a license (a shift from the old offline registration of GM6). This was a significant change in how Game Maker was delivered β it reflected a more commercial, multi-platform ambition under YoYo Games.
-
* **Minor Feature Additions:** Besides extensions, GM7 added some other smaller improvements. For example, there were improvements to the sprite and image editors, new actions for particle systems, and better sound format support (GM7 introduced support for .ogg audio files for background music). It also improved the reliability of the new graphics engine introduced in GM6. However, *no major changes to the core engine (rendering or physics)* were made β GM7's games ran similarly to GM6's, with 2D and basic 3D via Direct3D. The emphasis was on extensibility and preparing for future platform support.
-
* **Platform and Community Notes:** Game Maker 7 was the last version that ran on Windows only (a separate port of GM7 for Mac was eventually created by YoYo Games in 2008, but that was a parallel product). The release of GM7 via the YoYo Games website also coincided with an expanding international user base, since YoYo's involvement brought marketing and more visibility. Mark Overmars was still the lead developer of Game Maker 7, but now backed by a team.
In summary, **Game Maker 7.0** didn't radically change what you could make with Game Maker in terms of game genre or engine power, but it **expanded the software's openness and infrastructure**. With extension support, advanced users could push Game Maker further than before by adding new capabilities on their own [^5]. And with YoYo Games taking over distribution, Game Maker began evolving from a one-man project into a more professional product. This version set the stage for multi-platform targets and a larger community engagement that would fully manifest with subsequent versions. (Notably, there was a longer gap after 7.0 β it would be about two and a half years before the next version, as YoYo Games focused on community features and planning Game Maker's future[^2].)
@@ -562,17 +541,11 @@ In summary, **Game Maker 7.0** didn't radically change what you could make with
**Game Maker 8.0** (often just called *Game Maker 8*) was released on December 22, 2009 [^5]. It was the last major version of the "classic" Game Maker line developed with Mark Overmars's direct involvement. GM8 came after a lengthy gap and delivered numerous improvements to the user experience, though it didn't drastically change the engine's underpinnings. Notable features and changes in Game Maker 8.0 include:
* **Revamped Script Editor:** One of the headline enhancements was a completely **revamped code editor window** for GML scripting [^5]. The new script editor had better syntax highlighting, auto-indentation, and a more user-friendly interface for writing code. This was a welcome improvement for users who wrote a lot of GML, making the coding experience smoother and more akin to standard programming IDEs. It included line numbers, find/replace functionality, and other conveniences that were lacking or rudimentary in previous versions.
-
* **Improved Image/Sprite Editor:** Game Maker 8 introduced a significantly improved built-in image editor for creating and editing sprites. The sprite editor got new tools and a better UI β for example, support for alpha transparency editing (RGBA), more drawing tools, and perhaps onion-skinning for subimages/animation previews. This meant users could do more pixel art and image touch-ups directly in Game Maker without needing an external graphics program. The overhaul made it easier to create higher-quality sprites and tiles within the IDE [^5].
-
* **Import/Export of Resources:** GM8 added the ability to **import and export resources** (sprites, objects, scripts, etc.) between project files [^5]. This was implemented through a mechanism to save individual resources or groups of resources to an external file and then load them into another project. It greatly facilitated sharing and reusing code or assets. For instance, a user could export a monster object or a script from one game and import it into another without recreating it from scratch. This feature made Game Maker more modular and collaborative.
-
* **New Default Font and Minor UI Tweaks:** The appearance of the IDE was refreshed slightly β for example, GM8 used a different default font (Calibri) in the interface, which gave it a more modern look compared to the older versions that used MS Sans Serif. There were also new icons for some actions and minor layout adjustments. Overall, the IDE looked cleaner and more up-to-date in GM8. (This version also infamously introduced a new logo for Game Maker β a green circular "G" icon β chosen via a community contest, replacing the long-standing red ball icon [^6]. The logo change stirred some controversy in the community, but it was purely cosmetic and did not affect functionality.)
-
* **Removed Legacy Features:** In moving to GM8, some very old/obsolete features were pruned. For example, support for really outdated Windows versions was dropped and certain deprecated functions were removed or changed. This was part of cleaning up the codebase for the future.
-
* **Bug Fixes and Stability:** After the significant architectural changes in GM7 (with extensions and new file I/O) and the long development cycle, GM8 focused on stability. Many bugs from GM7 were fixed. The extension mechanism was still present, but more integrated. The game runner and executables produced were more stable on modern Windows OSes. Additionally, game performance saw minor improvements in some areas (though the engine was largely the same as GM7's, aside from the editor upgrades).
-
* **Monetization and Editions:** Game Maker 8.0 continued the paid registration model. In fact, YoYo Games adjusted the pricing around this time (GM8 Standard was priced around $25). There was still a Lite version available for free with certain features (like 3D and extensions) disabled, and a Pro/registered version that unlocked full capabilities [^5]. Activation was handled online via YoYo Games accounts.
As the final version developed under Mark Overmars, **Game Maker 8.0** was a polished and user-friendly culmination of a decade of development. It **did not radically change the types of games one could make** (the engine was still 2D-focused with optional simple 3D, and used Direct3D8 for rendering just like GM7). However, it significantly refined the development experience - coding, painting sprites, and managing game assets became easier and more efficient in GM8 [^5]. The community received GM8 very positively; it became a stable workhorse for many hobbyist and educational projects in the early 2010s. (An update *Game Maker 8.1* would later be released in 2011 by YoYo Games, primarily to improve Windows Vista/7 compatibility and add minor features, but 8.0 was the last version where Overmars was deeply involved in the design.)
diff --git a/public/consoles/GameEngine.png b/public/consoles/GameEngine.png
new file mode 100644
index 00000000..b5c479c0
Binary files /dev/null and b/public/consoles/GameEngine.png differ
diff --git a/public/css/homepage.css b/public/css/homepage.css
index fc2b07dc..a7559647 100644
--- a/public/css/homepage.css
+++ b/public/css/homepage.css
@@ -120,14 +120,22 @@ div.rr-post-playlist-row:not(:has(> :nth-child(5))) {
}
.console-post-numbers {
- border-radius: 15px;
- padding: 5px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 32px;
+ height: 32px;
+ min-width: 32px;
+ margin-top: 2px;
margin-left: 20px;
- font-size: small;
- height: 30px;
- min-width: 30px;
+ border-radius: 50%;
+ background: rgba(255, 255, 255, 0.12);
+ border: 1px solid rgba(255, 255, 255, 0.08);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
+ color: rgba(255, 255, 255, 0.92);
+ font-size: 0.85rem;
+ line-height: 1;
text-align: center;
- margin-top: 5px;
}
/* End of Homepage Console selector section */
diff --git a/public/generated/placeholders/atari2600.jpg b/public/generated/placeholders/atari2600.jpg
index 457c7a7f..ba6f4643 100644
Binary files a/public/generated/placeholders/atari2600.jpg and b/public/generated/placeholders/atari2600.jpg differ
diff --git a/public/generated/placeholders/gamecom.jpg b/public/generated/placeholders/gamecom.jpg
index b8ed1253..d33acebd 100644
Binary files a/public/generated/placeholders/gamecom.jpg and b/public/generated/placeholders/gamecom.jpg differ
diff --git a/public/generated/placeholders/atarijaguar.jpg b/public/generated/placeholders/jaguar.jpg
similarity index 94%
rename from public/generated/placeholders/atarijaguar.jpg
rename to public/generated/placeholders/jaguar.jpg
index f10b020f..d9ab0ad9 100644
Binary files a/public/generated/placeholders/atarijaguar.jpg and b/public/generated/placeholders/jaguar.jpg differ
diff --git a/public/generated/placeholders/n64.jpg b/public/generated/placeholders/n64.jpg
index ec220825..db3aefeb 100644
Binary files a/public/generated/placeholders/n64.jpg and b/public/generated/placeholders/n64.jpg differ
diff --git a/public/generated/placeholders/sdks.jpg b/public/generated/placeholders/sdks.jpg
index fa46401f..5b0b5901 100644
Binary files a/public/generated/placeholders/sdks.jpg and b/public/generated/placeholders/sdks.jpg differ
diff --git a/scripts/format-docs.js b/scripts/format-docs.js
index a7bb440e..55c1bf56 100755
--- a/scripts/format-docs.js
+++ b/scripts/format-docs.js
@@ -15,6 +15,7 @@
* table-pipes | col | β col (no leading/trailing pipe on rows)
* table-blank text\ntbl β text\n\ntbl (blank line before table)
* blank-before-heading text\n## β text\n\n## (blank line before headings)
+ * tight-lists * a\n\n* b β * a\n* b (no blank lines between list items)
* hr-heading ---\n\n## β ---\n## (no blank between HR and heading)
* heading-content ##\n\n txt β ##\n txt (no blank after heading before prose)
*
@@ -85,6 +86,11 @@ function makeFenceTracker() {
};
}
+/** True when a line is a Markdown list item marker (unordered or ordered). */
+function isListItemLine(ln) {
+ return /^\s*(?:[*+-]|\d+\.)\s+/.test(ln);
+}
+
// ---------------------------------------------------------------------------
// Per-line rules (applied while tracking fenced code block state)
// ---------------------------------------------------------------------------
@@ -324,7 +330,48 @@ function applyMultiLineRules(text, fixes) {
return out.join('\n');
})();
- return after4;
+ // Rule: no blank line(s) between adjacent list items.
+ // e.g. * item a\n\n* item b β * item a\n* item b
+ // Uses protected-block tracking so fenced code, frontmatter, and capture
+ // blocks are never modified by this rule.
+ const after5 = (() => {
+ const lines = after4.split('\n');
+ const out = [];
+ const isProtected = makeFenceTracker();
+ let modified = false;
+
+ for (let i = 0; i < lines.length; i++) {
+ const ln = lines[i];
+
+ if (isProtected(ln)) {
+ out.push(ln);
+ continue;
+ }
+
+ if (ln.trim() === '') {
+ let prevIndex = out.length - 1;
+ while (prevIndex >= 0 && out[prevIndex].trim() === '') prevIndex--;
+
+ let nextIndex = i + 1;
+ while (nextIndex < lines.length && lines[nextIndex].trim() === '') nextIndex++;
+
+ const prev = prevIndex >= 0 ? out[prevIndex] : '';
+ const next = nextIndex < lines.length ? lines[nextIndex] : '';
+
+ if (isListItemLine(prev) && isListItemLine(next)) {
+ modified = true;
+ continue;
+ }
+ }
+
+ out.push(ln);
+ }
+
+ if (modified) fixes.push('multiline: blank between list items');
+ return out.join('\n');
+ })();
+
+ return after5;
}
// ---------------------------------------------------------------------------