diff --git a/categories/consoles/PS2.md b/categories/consoles/PS2.md
index 097c7dd6..aadb1e4d 100644
--- a/categories/consoles/PS2.md
+++ b/categories/consoles/PS2.md
@@ -30,6 +30,21 @@ On this page, we've compiled a list of links to other pages that cover various t
So grab your DualShock controller, and get ready to dive into the exciting world of PS2 reverse engineering!
+---
+# Game Software Development for the Sony Playstation 2
+
+## How long did it take to develop PS2 games back in the day?
+In *Postmortems from Game Developer*, one LucasArts PlayStation 2 production released in February 2001 is described as taking 30 months to complete [^5].
+At the height of production, the team had approximately 40 full-time developers [^5].
+
+The postmortem also provides a useful snapshot of a relatively large early-PS2 production:
+* **Schedule** - 30 months of development
+* **Team size** - Approximately 40 full-time developers at peak production
+* **Project scale** - 301,000 lines of code, including internal tools
+* **Development hardware** - 700MHz Pentium III PCs with 256MB of RAM, GeForce 256 graphics cards, and dedicated PS2 tools
+* **Software stack** - Windows 2000, Microsoft Visual C++, Metrowerks for PS2, 3D Studio Max, Softimage, Photoshop, Bryce, Visual SourceSafe, Perl, AfterEffects, and Premiere
+* **Specialized technology** - The Eve level design tool, Miles Sound System, ObjectSpace STL, Macromedia/Secret Level Flash, and Planet Blue's Tulip for prerendered cut-scene lip-syncing
+
---
# Hardware
Similar to the original PlayStation the PS2 used a MIPS processor but this time it was 64 bit and codenamed the **Emotion Engine**, along with 2 custom vector processors. Although the PS2 has a much more modern GPU design compared to the PS1, the actual transformation of the vertices were still being process by the CPU core rather than the GPU [^1].
@@ -215,3 +230,4 @@ If you are interested in learning more we have a specific post about the Merkury
[^2]: Psi2 issue 18
[^3]: Psi2 issue 20
[^4]: [GDC 2004 Evolve](https://ia803200.us.archive.org/11/items/evolveeventprogr2004unse/evolveeventprogr2004unse.pdf)
+[^5]: Postmortems from Game Developer (Austin Grossman, 2003) Page 299
diff --git a/categories/consoles/Xbox.md b/categories/consoles/Xbox.md
index 07296820..c2fd941f 100644
--- a/categories/consoles/Xbox.md
+++ b/categories/consoles/Xbox.md
@@ -28,6 +28,25 @@ On this page, we've compiled a list of links to other pages that cover various t
So grab your Xbox controller, and get ready to dive into the exciting world of Xbox reverse engineering!
+---
+# Game Software Development for the Microsoft Xbox
+
+## How long did it take to develop games for the Xbox back in the day?
+In the 2003 book *Postmortems from Game Developer*, the developers of *Cel Damage* describe a 2-year Xbox development cycle with 16 full-time developers and 12 contractors [^1].
+Given the November 1, 2001 release date, that puts the bulk of development roughly in the late-1999 to 2001 period [^1].
+
+The postmortem also gives a useful snapshot of what an original Xbox production looked like at the time:
+* **Team size** - 28 developers in total, split between 16 full-time staff and 12 contractors
+* **Budget** - Approximately $2 million
+* **Project scale** - Around 800,000 lines of code
+* **Development hardware** - 600MHz Pentium III PCs with 256MB of RAM, 30GB hard drives, and Nvidia GeForce graphics cards
+* **Software stack** - Microsoft Visual Studio, 3DS Max, Photoshop, Illustrator, Winamp, and SourceSafe
+* **Performance and tooling** - `pitaSim`, `Vtune`, and Microsoft Visual C++
+
+That is only one data point, but it is still a useful baseline.
+For an early Xbox title, a 2-year schedule with a sub-30-person team and commodity PC workstations appears to have been enough to ship a retail game [^1].
+
+
---
# Games
@@ -61,3 +80,7 @@ This section of our guide will provide you with detailed information and resourc
{% include console.html %}
+
+---
+# References
+[^1]: Postmortems from Game Developer (Austin Grossman, 2003) Page 66
diff --git a/categories/games/Games.md b/categories/games/Games.md
index 8d421f2d..c6f98866 100644
--- a/categories/games/Games.md
+++ b/categories/games/Games.md
@@ -4,7 +4,7 @@ layout: post
category:
- games
- introduction
-title: Games specific posts
+title: Game-specific reverse engineering posts
breadcrumbs:
- name: Home
url: /
@@ -14,11 +14,14 @@ redirect_from:
- /games/all
- /games/
editlink: ../categories/games/Games.md
+updatedAt: '2026-04-26'
tags:
- games
---
This page collects all the posts that are related to reverse engineering a specific game rather than an entire console or platform.
+It starts with a curated set of notable game pages grouped by platform, then points to a few external deep dives that are worth studying, and finally ends with an automatically generated index of broader tagged pages.
+This makes it easier to browse the highlights first without losing the wider archive.
## Decompiled Retail Console Games
This page maintains a comprehensive, curated list of retail console games that have been successfully reverse engineered and decompiled back into compilable source code (C/C++). It tracks the progress of major community projects across platforms like the **Nintendo 64**, **GameCube**, and **PlayStation**, including high-profile achievements such as *Super Mario 64*, *The Legend of Zelda: Ocarina of Time*, and *Jak and Daxter*. We have a specific post all about it here:
@@ -60,7 +63,11 @@ This post covers reverse engineering work on the original **Super Mario Bros** f
This post covers the recovered **Home Alone 2** NES source code and explains what survives in the archive for researchers interested in late-era commercial NES development.
{% include_cached link-to-other-post.html post="/home-alone-2-nes-source-code" %}
-## The Final Fantasy Battle Engine: A Dissection of Physical Attacks
+---
+## External Deep Dives
+This section highlights external reverse engineering breakdowns for specific games that are useful companion material alongside the internal posts above:
+
+### The Final Fantasy Battle Engine: A Dissection of Physical Attacks
[Displaced Gamers](https://www.youtube.com/watch?v=O_CLnBCgJks) has an excellent video dissecting the underlying code, hidden math, and bugs governing physical attacks in the original NES Final Fantasy. The video explores how battle stats like accuracy, critical hit rates, and elemental weaknesses are processed in Assembly, revealing several programming oversights that heavily impact gameplay. It provides a fascinating look into early RPG mechanics and console game reverse engineering.
@@ -69,25 +76,6 @@ Core Architecture:
* **System Scope:** Analysis of the physical attack mechanics within the Final Fantasy (NES/Famicom) battle engine.
* **Code Footprint:** The execution logic for a single physical attack consists of 781 total bytes (excluding called subroutines), responsible for animation, damage calculation, and variable reporting.
-Base Stat Formulas:
-* **Attack Power:** Calculated as `(Character Strength / 2) + Weapon Power`. The decimal value is truncated.
-* **Accuracy:** Calculated as `Base Character Accuracy + Equipped Weapon Accuracy`.
-* **Hits per Attack:** Calculated as `(Accuracy / 32) + 1`. This value determines how many discrete damage rolls are executed per attack command.
-* **Evade:** Calculated as `Base 48 + Agility Modifier - Armor Weight Penalty`.
-* **Absorb (Defense):** Total numerical summation of all equipped armor mitigation values.
-
-The Black Belt / Master Class Exceptions:
-* **Armed:** Attack power receives a hardcoded `+1` modifier in addition to standard formulas.
-* **Unarmed:** Attack power and Critical Hit values bypass standard logic and are set to `Current Level * 2`. The hit count is calculated normally but then strictly doubled.
-* **Unarmored:** Absorb value logic is bypassed and set equal to the character's `Current Level`.
-
-RNG, Hit Validation, and Damage Calculation:
-* **Turn Order:** Agility does not govern turn priority. An array queue of players and enemies undergoes 16 RNG-based memory position swaps to randomize round sequence.
-* **Hit Chance Formula:** `168 (Flat Base Constant) + Attacker Accuracy - Defender Evade`. The initial calculation before the evasion deduction is hard-capped at 255.
-* **Battle RNG Validation:** A random value generated between `0` and `200`. A roll of `200` forces an automatic miss. If `Hit Chance >= Battle RNG`, the attack successfully lands.
-* **Base Damage Roll:** Generates a random value bounded by `[Attack Power]` and `[Attack Power * 2]`.
-* **Calculated Damage:** `Damage Roll - Defender Absorb`. Floor value is strictly clamped at a minimum of `1` damage.
-* **Critical Hits:** If the same `Battle RNG <= Critical Hit Rate`, a critical strike occurs. A secondary raw damage roll is executed and added directly to the damage total, completely ignoring the defender's Absorb stat.
Critical Engine Bugs & Logic Errors:
* **Critical Hit Memory Fetch Error:** When querying the ROM table for a weapon's stored critical hit rate, the engine skips the instruction to load the stat. Instead, it writes the weapon's *index array ID* into RAM. Consequently, later-game weapons (higher index table values) yield artificially high crit rates regardless of intended design.
@@ -156,6 +144,34 @@ This post covers the leaked **Yoshi's Island** source code and the implementatio
This post covers the leaked **A Link to the Past** source code and its importance for understanding large-scale first-party Super Nintendo game development.
{% include_cached link-to-other-post.html post="/zelda-a-link-to-the-past-source-code" %}
+---
+# Development Art Archives
+This section collects game-specific archive pages that focus more on art workspaces, asset pipelines, and development materials than on full source code releases:
+
+### Pilotwings 2D Art Workspace
+This post covers a recovered **Pilotwings** 2D art workspace, which is useful for studying Nintendo's internal art production flow rather than the gameplay code itself.
+{% include_cached link-to-other-post.html post="/pilotwings-2d-art-workspace" %}
+
+### SimCity SNES 2D Art Workspace
+This post covers the **SimCity SNES** art workspace files, giving a narrower look at how project assets were organized during development.
+{% include_cached link-to-other-post.html post="/simcity-snes-2d-art-workspace" %}
+
+### Star Fox 2 2D Art Workspace
+This post covers a **Star Fox 2** art workspace archive that complements the source-code page by showing more of the project's asset-side workflow.
+{% include_cached link-to-other-post.html post="/starfox2-2d-art-workspace" %}
+
+### Stunt Race FX 2D Art Workspace
+This post covers a **Stunt Race FX / Wild Trax** art workspace archive, which helps document the content pipeline around a Super FX title.
+{% include_cached link-to-other-post.html post="/stunt-race-fx-2d-art-workspace" %}
+
+### Super Mario Kart 2D Art Workspace
+This post covers the **Super Mario Kart** art workspace files, which are useful for understanding track and sprite production outside the main game code.
+{% include_cached link-to-other-post.html post="/super-mario-kart-2d-art-workspace" %}
+
+### Zelda Link's Awakening 2D Art Workspace
+This post covers the **Link's Awakening** art workspace archive and adds asset-side context to the related Game Boy source code material.
+{% include_cached link-to-other-post.html post="/zelda-links-awakening-art-workspace" %}
+
---
# GameCube Games
This section collects our GameCube game-specific posts:
@@ -220,13 +236,18 @@ This section collects our PC game-specific posts and source code investigations:
This post covers **Planet X3**, a modern MS-DOS strategy game whose tooling and technical design make it relevant to retro PC development research.
{% include_cached link-to-other-post.html post="/planet-x3-dos" %}
-## Deponia Magnet Puzzle Soft-Lock Fix
+---
+## External Deep Dives
+This section highlights external reverse engineering breakdowns for specific PC games that are useful companion material alongside the internal posts above:
+
+### Deponia Magnet Puzzle Soft-Lock Fix
[Nathan Baggs](https://www.youtube.com/watch?v=lT4McPl5kQU) has a detailed video breakdown about reverse engineering a persistent, game-breaking bug in the point-and-click adventure game **Deponia**. The investigation covers diagnosing a magnet puzzle soft-lock using tools like Ghidra, x64dbg, and RenderDoc to analyze memory and engine behavior.
By discovering an embedded Lua debugger (**mobdebug**) and decompiling the game's bytecode (**LuaJit**), the root cause-a failure to initialize condition states upon reloading-is identified and resolved via custom Lua code injection.
---
-# All Posts related to Specific Games
+# Automatically Listed Game Pages
+This final section is generated from site tags, so it acts as a wider catch-all index beyond the curated platform sections above:
{% include console.html %}
diff --git a/categories/misc/Industry.md b/categories/misc/Industry.md
index 280fe14b..5a69d0d5 100644
--- a/categories/misc/Industry.md
+++ b/categories/misc/Industry.md
@@ -3,8 +3,8 @@ layout: post
title: Life in the Games Industry
permalink: /industry
category: industry
-image: https://github.com/user-attachments/assets/79fee58b-dd24-4b1b-9b9e-6de863927463
-twitterimage: https://github.com/user-attachments/assets/79fee58b-dd24-4b1b-9b9e-6de863927463
+placeholderimages:
+- https://github.com/user-attachments/assets/79fee58b-dd24-4b1b-9b9e-6de863927463
redirect_from:
- /documentary
- /documentaries
@@ -15,8 +15,10 @@ redirect_from:
breadcrumbs:
- name: Home
url: /
- - name: Games Industry Research Material
- url: /#
+ - name: Introduction
+ url: /introduction
+ - name: Life in the Games Industry
+ url: #
editlink: ../categories/misc/Industry.md
recommend:
- industry
@@ -35,14 +37,17 @@ tags:
Have you ever wondered what it was like to be a developer in the games industry in the 80s/90s/00s?
-This page will try to collect all the information we know about to help paint a picture of what it must have been like.
+This page collects documentaries, interviews, magazines, conference history, and company pages that help reconstruct what day-to-day work in the games industry looked like.
-If you know of any other footage or information that should be added to this page please get in touch, there must be more out there!
+It is intended as a hub page rather than a complete chronology, so the goal is to point toward the best material we know about and split deeper topics into their own dedicated posts where needed.
+
+If you know of any other footage or information that should be added to this page please get in touch. There must be more out there.
---
-# Documentaries showing life in the industry
+# Documentary Footage
+This section collects documentaries and broadcast segments that show how studios were organized, how developers worked, and how the wider business looked at the time.
## 1984 - Life in Imagine & Ocean Software (The Battle for Santa's Software)
@@ -50,16 +55,21 @@ If you know of any other footage or information that should be added to this pag
On the **Imagine Software** side Mark Butler was 23 when the documentary was filmed and it shows his extravagant lifestyle with his fast cars and sponsored motorbike teams. He had 70 people working for them in May 1984 and John Gibson was the only programmer over 30 (Known as Granddad).
-The documentary starts with Imagine Software looking great and antisipating the next big game, but sadly ends with bankruptcy. This documentary is great for showing the uncertainty in the early games industry, one minute you are on top and gone the next.
+The documentary starts with Imagine Software looking great and anticipating the next big game, but sadly ends with bankruptcy. This documentary is great for showing the uncertainty in the early games industry, one minute you are on top and gone the next.
Fortunately, former programmers at Imagine Software went on to establish **Psygnosis**, known for the Psy-Q development SDKs.
On the other side was **Ocean Software** and David Ward who managed to survive the time it took to film the documentary and showed off some of their upcoming games for the festive period.
You can find more information about this documentary over on **VHiStory**:
-[Commercial Breaks β Film 84 β Tomorrow's World β Micro Live β tape 1 - VHiStory](https://vhistory.wordpress.com/2022/12/10/commercial-breaks-film-84-tomorrows-world-micro-live-tape-1/)
+{% include link-to-other-site.html
+ url="https://vhistory.wordpress.com/2022/12/10/commercial-breaks-film-84-tomorrows-world-micro-live-tape-1/"
+ title="Commercial Breaks - Film 84 - Tomorrow's World - Micro Live - tape 1"
+ description="VHiStory provides additional background on the 1984 documentary episode that covered Imagine Software and Ocean Software."
+%}
## 1993 - A Visit to id Software (November 1993)
+This video captures **id Software** shortly before the release of **DOOM**, making it a useful snapshot of early 1990s PC game development culture.
---
@@ -74,7 +84,7 @@ To find out more we have a separate post all about it here:
From the BBC show **Working Lunch** the reporter **Rory Cellan Jones** visits the Dundee studios of **DMA Design** in 1996 as they are working on the Original Grand Theft Auto:
-They interview people in the programming (David Kivlin), music (Craig Conner), sound effects, QA (Gordon Ross and Fiona Robertson) and there is a brief view of the motion capture (Darren?) departments for a desperate game.
+They interview people in programming (David Kivlin), music (Craig Conner), sound effects, and QA (Gordon Ross and Fiona Robertson), and there is a brief view of the motion capture department during development.
---
@@ -82,11 +92,11 @@ They interview people in the programming (David Kivlin), music (Craig Conner), s
When researching a video game, for reverse engineering or otherwise it can be tricky to find all the relevant sources due to most development being done behind closed doors in a time before the internet was the force it is today. So it is important to keep track of the physical media such as magazines, books and conference proceedings as they contain many information that has never been released to the internet.
## Magazines
-Before wide spread access to the internet was common, many game developers would buy magazines to keep up with the latest in the industry and development in general, we have a post covering the most relevant magazines:
+Before widespread access to the internet was common, many game developers would buy magazines to keep up with the latest in the industry and development in general. We have a post covering the most relevant magazines:
{% include_cached link-to-other-post.html post="/magazines" description="For more information on all the Game development magazines check out this post." %}
### Develop - UK Magazine dedicated to the games industry
-**Develop** was a magazine published in the UK targetted at games industry professionals, similar to the US magazine Game Developer it provides a unique insight into game engines, SDKs and other middleware which was never published online.
+**Develop** was a magazine published in the UK targeted at games industry professionals. Similar to the US magazine Game Developer, it provides a unique insight into game engines, SDKs, and middleware that was never published online.
{% include_cached link-to-other-post.html post="/develop-magazine" description="For more information on the UK magazine Develop check out this post." %}
---
@@ -108,43 +118,63 @@ Books were a must have for any game developers getting started in the industry,
{% include_cached link-to-other-post.html post="/books" description="For more information on all the Game development Books check out this post." %}
---
-## Virtual Experiences
+## Museum and Preservation Projects
+This section collects interactive preservation projects that try to recreate part of the material culture around video games rather than just documenting code or binaries.
### AUTO MUSEUM 64
Built in Unity **Auto Museum 64** is a virtual Museum of 3D Vehicles from a variety of Nintendo 64 games.
It was built by **leoburke** and is available on Itch.io:
-[Auto Museum 64 by leoburke](https://leoburke.itch.io/auto-museum-64)
+{% include link-to-other-site.html
+ url="https://leoburke.itch.io/auto-museum-64"
+ title="Auto Museum 64 by leoburke"
+ description="A downloadable virtual museum built in Unity that lets you inspect 3D vehicles from a range of Nintendo 64 games."
+%}
-This kind of experience is very cool, but it would be much better if it was in the browser (e.g WebGL) as its not something you would go back to often but it is something that would be good to share links with friends.
+This kind of experience is very cool, but it would be even better in the browser, for example via WebGL. It is the kind of thing people may only visit occasionally, but it would be much easier to share and preserve as a simple web link.
Hopefully this will be the start of more Virtual museums that showcase 3d models or even maps from 3D video games.
### Virtual Game Shop
-Sadly due to the pandemic and the move to online downloads many of the excellent Specialist Video Game Stores have closed down. Many of us who grew up with them have nostalgic memories of their rows and rows of brand new boxed video games for whatever the current Sega/Nintendo/Sony/Microsoft console was hot at the time.
+Specialist game shops were an important part of games culture for decades, especially when boxed software, demo kiosks, strategy guides, and magazine racks all existed in the same physical space.
-A VR experience to simulate a Video Game shop with customisable games/posters/shelves/magazines/consoles would be a pretty relaxing experience, probably not exactly a big seller but would have its niche.
+A careful virtual reconstruction of that environment could be a useful preservation project in its own right, especially if it documented how games were displayed, marketed, and sold across different eras and regions.
Found some more images & information on the Blockbuster Games concept; and it's companion store (in the next post), Blockbuster Music, in 'Stores of the Year 9' (1995)
— Evan Collins | BLM π³οΈβπ (@EvanCollins90) July 4, 2020
---
-## Commercial Documentaries on game development
+## Commercial Documentary Catalogues
So far we have listed documentaries that are freely available on youtube, mostly from TV broadcasts, but for a list of video game industry documentaries that you can buy check out **IMDB**'s list:
-[Video game documentaries](https://www.imdb.com/list/ls079153183/)
+{% include link-to-other-site.html
+ url="https://www.imdb.com/list/ls079153183/"
+ title="Video Game Documentaries"
+ description="IMDb hosts a broad list of commercially released documentaries related to video game history and development."
+%}
-## Interviews
+## Interviews and Oral Histories
One of the best sources of information for how retro games were developed are direct interviews with the programmers themselves. This section lists interviews available online, but for even more interviews check out the magazines section above.
### The Untold History of Japanese Game Developers DVDs
DVD produced for the Kickstarter for the book The Untold History of Japanese Game Developers back in 2013:
-[The Untold History of Japanese Game Developers DVD 1 - YouTube](https://www.youtube.com/watch?v=PmelLhMEpo4&t=29s)
+
+{% include link-to-other-site.html
+ url="https://www.youtube.com/watch?v=PmelLhMEpo4&t=29s"
+ title="The Untold History of Japanese Game Developers DVD 1"
+ description="The first Kickstarter bonus DVD collects long-form interviews with Japanese game developers discussing their careers and production history."
+%}
+
DVD 2:
-[The Untold History of Japanese Game Developers DVD 2 - YouTube](https://www.youtube.com/watch?v=wKbHimftRIA)
+
+{% include link-to-other-site.html
+ url="https://www.youtube.com/watch?v=wKbHimftRIA"
+ title="The Untold History of Japanese Game Developers DVD 2"
+ description="The second DVD continues the interview collection and includes useful discussion of tools, reverse engineering, and day-to-day studio work."
+%}
Of particular note is in the second DVD:
Enix programmer Toru Hidaka learned to program by reverse engineering the machine code of Cosmic Soldier, and went on to program games, create graphics, sound and compression utilities for Enix, write books on using machine code, and assist in teaching a new generation of programmers.
-Also shows of a development utility created to help designers create levels for Gandhara on the PC-88 the numbers are where the enemies appear
+It also shows a development utility created to help designers build levels for **Gandhara** on the **PC-88**, where the numbers indicate enemy placement.
---
# Companies
@@ -152,7 +182,7 @@ The games industry is made up of many companies, some as small as a single perso
There are so many that have come and gone over the years that we can't write about them all. Instead we focus on companies that pushed the boundaries in terms of the underlying technology, companies that most gamers may never have heard of but that made considerable technical achievements.
Companies that defined iconic game engines such as **Krome's Merkury**, others such as **SN Systems** or **Cross Products** that created the game development tools that were used by most of the industry.
-Others who used their genius a little more on the unauthorized size by creating products that could allow unauthorized code to run on the hardware such as **Datel's Action Replay** line of cheat devices.
+Others used their technical expertise on the unauthorized side by creating products that could run unofficial code on retail hardware, such as **Datel's Action Replay** line of cheat devices.
## Game Development Tooling Companies
There are a bunch of notable companies related to retro game development, many of which specialized in creating the Software Development Kits for popular consoles such as the **Mega Drive** (Cross Products) or PlayStation 1 (SN Systems).
@@ -169,7 +199,7 @@ SN Systems is a company that specialised in creating development tools for game
---
## Game Hacking Companies
-There are a bunch of notable companies related to game hacking which not only worked on Cheat devices like the **Game genie** or **Action Replay** but also unlicenced products that allowed homebrew games consoles much to the annoyance of the console manufacturer.
+There are a bunch of notable companies related to game hacking which not only worked on cheat devices like the **Game Genie** or **Action Replay** but also built unlicensed products that enabled homebrew on retail consoles, much to the annoyance of the console manufacturer.
### Datel
**Datel** is most famous for their **Action Replay** line of products but also created homebrew enablers for the Gamecube and Nintendo DS along with tons of unlicensed games and applications for a wide range of home consoles and handhelds.
@@ -178,9 +208,46 @@ There are a bunch of notable companies related to game hacking which not only wo
### Codemasters
+**Codemasters** is best known for its unlicensed NES work, the **Game Genie**, and its broader role in shaping the home computer and console market in the UK. It is also a useful case study in how a commercial game studio could overlap with hardware add-ons, cartridge publishing, and unauthorized console development.
{% include_cached link-to-other-post.html post="/companies/codemasters" description="For more information about Codemasters check out this post." %}
+---
+# Game Development Postmortems
+Over the years game developers have posted "Postmortems" of their games, listing the good and bad parts of the development process, these were often given as talks at GDC or published in the physical **Game Developer** magazine.
+
+## 2003 book Postmortems from Game Developer
+It is a collection of 25 postmortem articles originally from **Game Developer** magazine. Based on the published table of contents, these are the included games and their primary original platforms. Most chapters are about Windows PC development, but I have listed the main release platforms relevant to the era if the game was ported [^1].
+
+Game | Primary Platform(s)
+------------------------------------ | ----------------------------------------
+System Shock 2 | PC (Windows)
+Operation Flashpoint | PC (Windows)
+Drakan: Order of the Flame | PC (Windows)
+Cel Damage | Xbox (later ported to GameCube, PS2)
+Vampire: The Masquerade - Redemption | PC (Windows)
+Age of Empires | PC (Windows, Mac later)
+Diablo II | PC (Windows, Mac)
+Unreal Tournament | PC (Windows, Mac, Linux), Dreamcast, PS2
+Command & Conquer: Tiberian Sun | PC (Windows)
+Age of Empires II: The Age of Kings | PC (Windows, Mac later)
+Myst III: Exile | PC, Mac
+Tropico | PC
+Black & White | PC (Windows, Mac later)
+Myth: The Fallen Lords | PC, Mac
+Thief: The Dark Project | PC
+Trespasser | PC
+Deus Ex | PC, Mac, PS2
+Jak and Daxter: The Precursor Legacy | PlayStation 2
+Star Wars Starfighter | PS2, Xbox, PC
+Star Trek: Voyager - Elite Force | PC, PS2, Mac
+Rainbow Six | PC, Mac, N64, Dreamcast, PS1
+Soldier of Fortune | PC, Dreamcast, PS2
+Dark Age of Camelot | PC
+FireTeam | PC (online multiplayer)
+Asheron's Call | PC
+
+
---
# Interviews with Game Developers (by platform)
@@ -233,6 +300,7 @@ Retro Gamer issue 39 | Developers at Rare | **Jetpac Refuelled** | Discuss the m
---
## Recruitment in the games industry
+This section collects smaller notes on how developers found work and how studios staffed projects in different periods.
### How did game programmers find work?
Many Game Boy developers worked freelance and were contracted out by companies to work on a title with a harsh deadline of a couple of months or sometimes even just weeks! Others were hired full-time by companies and would work on a constant stream of new games.
@@ -242,7 +310,7 @@ Many Game Boy developers worked freelance and were contracted out by companies t
He heard about the job through his agent which was a common way for game programmers to find work in the late 80s to early 90s. But by the 2000s games had grown so much that they would require whole teams of developers and hiring freelances who worked from home became rarer.
---
-## Games Industry Legends
+## Industry Legends and Retrospectives
### Remembering Archer Maclean (1962-2022)
[Mamemeister](https://www.youtube.com/watch?v=5yS_tmO_vSs) presents a retrospective on the life and work of Archer Maclean, a renowned programmer known for pushing the technical limits of 8-bit and 16-bit hardware. The video discusses his most famous works, such as *Dropzone* and *International Karate +*, and shares personal anecdotes about his passion for arcade hardware and game development.
@@ -250,26 +318,28 @@ He heard about the job through his agent which was a common way for game program
---
-# Indie Game Development
+# Platform Holders and Independent Developers
+This section covers how console manufacturers viewed outside developers, unlicensed publishing, and later indie support.
-## 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.
+## Why Nintendo restricted third-party developers in the 1980s
+When Nintendo prepared to launch the Famicom in the United States, it was entering a market still shaped by the North American video game crash. The company wanted tighter control over software quality, publishing volume, and licensing than many earlier platform holders had exercised.
-**Hiroshi Yamauchi** president of Nintendo at the time believed the failure was due to too many 3rd party rubbish games:
+**Hiroshi Yamauchi**, Nintendo's president at the time, believed part of the collapse came from a flood of low-quality third-party software:

-Ever since Nintendo has been very wary of allowing 3rd party developers to release software for their consoles.
+That attitude helps explain Nintendo's stricter licensing model, manufacturing control, and technical lockout measures in the NES era.
-They have even put in place a number of security measures to stop unlicensed developers including both hardware and software methods.
+Nintendo also deployed a range of hardware and software restrictions to limit unauthorized publishing and preserve control over the platform ecosystem.
-This along with the threat of piracy has kept nintendo away from allowing 'homebrew development' on any of their consoles.
+For many years this made Nintendo comparatively cautious about homebrew and unofficial development compared with the later PC and mobile ecosystems.
-### 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.
+### Later Shift Toward Indies
+That position softened much later, once downloadable storefronts and successful independent releases showed that smaller external teams could strengthen a platform rather than dilute it.
---
-# All Games Industry Posts
+# Automatically Listed Industry Pages
+This final section is generated from site tags, so it works as a wider catch-all index beyond the curated sections above:
{% include console.html %}
@@ -277,3 +347,4 @@ It wasn't until they saw the success of many indie games such as minecraft and t
---
# References
[^1]: [Terminator 2, R-Type, Altered Beast with Jas Austin - The Retro Hour EP320 - YouTube](https://www.youtube.com/watch?v=5ilnsca4jYc)
+[^2]: ["Postmortems from Game Developer: Insights from the ..."](https://www.routledge.com/Postmortems-from-Game-Developer-Insights-from-the-Developers-of-Unreal-Tournament-Black-amp-White-Age-of-Empire-and-Other-Top-Selling-Games/Grossman/p/book/9781578202140)
diff --git a/codex/skills/retroreversing-category-pages/SKILL.md b/codex/skills/retroreversing-category-pages/SKILL.md
index a7fb1622..1b00f3f4 100644
--- a/codex/skills/retroreversing-category-pages/SKILL.md
+++ b/codex/skills/retroreversing-category-pages/SKILL.md
@@ -116,6 +116,7 @@ Typical `# All Posts` section:
Common optional H1 sections when the platform/topic needs them:
+* `# Game Software Development for the `
* `# File Formats`
* `# Emulation`
* `# Development Kits (Hardware)`
@@ -151,7 +152,8 @@ For hub-style pages, follow the newer pattern seen in `categories/consoles/WiiU.
* `# Homebrew Development`
* `# Reverse Engineering games`
Only add these if you have enough links/content to avoid thin sections.
-* **FAQ-style subheadings** - within an H1 section, use `###` question headings ("How long did it take�", "What did the SDK look like?") to make the page skimmable; each answer should end in one or more links (internal or external).
+* **Game development production snapshots** - for console hubs, `# Game Software Development for the ` is often a strong section to include when you have sourced postmortem or interview material. It works especially well for compact facts such as schedule length, team size, budget, workstation specs, software stack, tools, and codebase size.
+* **FAQ-style subheadings** - within an H1 section, use `###` question headings ("How long did it take�", "What did the SDK look like?") to make the page skimmable; a short "How long did it take to develop games back in the day?" subsection is a good fit when you have reliable production data. Each answer should end in one or more links (internal or external).
* **Primary-source quote blocks** - short `>` quotes are useful for provenance and "why this matters", backed by a footnote when the quote is evidence.
* **Embedded media (sparingly)** - a teardown / explanation video, tweet, or other primary-source embed can be worth keeping when it teaches a hardware concept or preserves development context; keep the surrounding text tight and technical.
* **Preserve existing embeds** - when editing an existing category page, keep embedded videos, tweets, and similar source material unless the user explicitly asks to remove them. Reorganising is fine, deletion is not.
diff --git a/pages/SourceCode/mame/GameAndWatchEmulation.md b/pages/SourceCode/mame/GameAndWatchEmulation.md
new file mode 100644
index 00000000..014b2854
--- /dev/null
+++ b/pages/SourceCode/mame/GameAndWatchEmulation.md
@@ -0,0 +1,1096 @@
+---
+permalink: /game-and-watch-emulation
+layout: post
+title: "How MAME Emulates Game & Watch"
+excerpt: A technical guide to how MAME emulates Nintendo Game and Watch hardware, from the Sharp SM5xx MCU family to LCD segment rendering and input scanning.
+category:
+- sourcecode
+breadcrumbs:
+ - name: Home
+ url: /
+ - name: Source Code
+ url: /sourcecode
+ - name: "How MAME Emulates Game & Watch"
+ url: #
+recommend:
+- sourcecode
+- emulation
+- handhelds
+tags:
+- sourcecode
+- emulation
+- handhelds
+editlink: ../pages/SourceCode/mame/GameAndWatchEmulation.md
+updatedAt: '2026-04-28'
+---
+# How MAME Emulates Game & Watch
+Game & Watch emulation in MAME is hardware emulation, not game reimplementation.
+The actual machine code dumped from original Nintendo microcontroller chips runs unmodified.
+MAME replicates the Sharp SM5xx CPU, the LCD segment controller, multiplexed inputs, and piezo buzzer - the game logic follows automatically.
+
+## Glossary of Key Terms
+If you are new to the hardware involved, this quick glossary explains the key terms used throughout:
+
+* **MCU** - Microcontroller Unit. A self-contained chip combining CPU, RAM, ROM, and I/O peripherals. Each Game & Watch unit contains one Sharp SM5xx MCU.
+* **LCD** - Liquid Crystal Display. The segment-based screen used in G&W games. Individual segments switch on or off; they do not form pixels.
+* **ROM** - Read-Only Memory. The game program, permanently baked into the MCU die.
+* **ACL** - All Clear. The reset button found on every G&W unit, tied directly to the MCU reset line.
+* **SVG** - Scalable Vector Graphics. The format MAME uses to describe LCD panel geometry, with each segment as a named vector shape.
+* **LFSR** - Linear Feedback Shift Register. A shift register whose output feeds back as input, producing a pseudo-random sequence. The SM5xx uses a 6-bit LFSR as a program counter within each ROM page.
+* **LUT** - Look-Up Table. A precomputed array used in the SM511 melody controller to translate tone commands into duty cycle counts.
+* **BL/BM** - The lower and upper halves of the SM5xx RAM address register. Together they select the current 4-bit RAM nibble.
+
+---
+## The Sharp SM5xx CPU Family
+
+Nintendo used a family of 4-bit Sharp microcontrollers across the Game & Watch product line.
+Each variant differs in ROM capacity, RAM layout, and peripheral features.
+All variants share a common instruction set and are emulated through a single inheritance hierarchy in `src/devices/cpu/sm510/`.
+
+The table below covers the main variants you will encounter when exploring MAME drivers:
+
+Name | ROM | RAM | Notes
+--- | --- | --- | ---
+SM5A | 1.8 KB | 5x13x4 nibbles | Oldest series, ~1980
+SM510 | 2.7 KB | 128x4 nibbles | Standard series, non-contiguous ROM map
+SM511 | 4 KB | 128x4 nibbles | Adds dedicated melody controller
+SM512 | 4 KB | 160x4 nibbles | SM511 with extended LCD RAM
+SM530 | 2 KB | Varies | Later variant, used in select titles
+
+The Soviet Union manufactured licensed clones of these chips, renaming them to ΠΠ1013ΠΠ1-2 (SM5A), ΠΠ1013ΠΠ4-2 (SM510), and ΠΠ1013ΠΠ7-2 (SM511).
+MAME supports Soviet Elektronika G&W clones through the same driver.
+MAME's shared SM5xx core source header also cites Sharp semiconductor data books as background documentation for the implementation [^9] [^10].
+
+
+The `sm510_base_device` class defines the complete SM5xx interface.
+It declares 95 virtual methods - the majority being opcode handlers (`op_tl`, `op_add11`, `op_cend`, etc.) that subclasses override for variant-specific behaviour.
+The 55 member variables cover CPU registers, LCD state, the divider, melody controller state, and I/O callbacks.
+Relevant interface definition [^4].
+
+
+
+### CPU Architecture
+
βοΈ
+All SM5xx variants are 4-bit processors with a Harvard architecture.
+The core registers are:
+
+The following registers make up the CPU core state, accessed during every instruction:
+
+* **ACC** (`m_acc`) - 4-bit accumulator. All arithmetic and logic operations pass through here.
+* **BL/BM** (`m_bl`, `m_bm`) - RAM address register. BL holds the lower 4 bits, BM the upper bits. Together they index into the 4-bit RAM array.
+* **PC** (`m_pc`) - 12-bit program counter. Advances via LFSR rather than simple increment.
+* **C** (`m_c`) - 1-bit carry flag. Set or cleared by arithmetic operations, testable by `TC` (skip-if-no-carry).
+* **Skip** (`m_skip`) - When set, the next fetched instruction is discarded as a NOP. Used to implement conditional branches.
+* **Halt** (`m_halt`) - Puts the MCU into low-power standby. The CPU stays halted until an external K input or the gamma timer wakes it.
+* **W** (`m_w`) - 8-bit output shift register. The `WR`/`WS` opcodes shift 0 or 1 into this register. `PTW` latches the value to the S output port.
+* **DIV** (`m_div`) - 15-bit free-running divider. Increments every crystal tick. Provides time references for the LCD refresh, melody controller, and the gamma interrupt.
+
+The execute loop is the heart of the emulation.
+It runs until the instruction count budget (`m_icount`) is exhausted.
+
+```cpp
+void sm510_base_device::execute_run()
+{
+ while (m_icount > 0)
+ {
+ // in halt mode, wake up after gamma signal or K input
+ if (m_halt)
+ {
+ if (m_ext_wakeup || m_gamma)
+ do_interrupt();
+ else
+ {
+ debugger_wait_hook();
+ m_icount = 0;
+ return;
+ }
+ }
+
+ m_icount--;
+
+ m_prev_op = m_op;
+ m_prev_pc = m_pc;
+
+ if (!m_skip)
+ debugger_instruction_hook(m_pc);
+ m_op = m_program->read_byte(m_pc);
+ increment_pc();
+
+ // 2-byte opcodes
+ if (op_argument())
+ {
+ m_icount--;
+ m_param = m_program->read_byte(m_pc);
+ increment_pc();
+ }
+
+ // handle opcode if it's not skipped
+ if (m_skip)
+ {
+ m_skip = false;
+ m_op = 0; // fake nop
+ }
+ else
+ execute_one();
+ }
+}
+```
+
+Relevant source [^3].
+
+Two-byte opcodes (such as `TL xyz` long jump and `TML xyz` long call) consume an extra cycle and read a second byte as `m_param`.
+If the skip flag is set, the fetched opcode is replaced with a fake `0x00` NOP rather than branching away - this means the 2-byte instruction is still consumed.
+
+---
+### Address Space and ROM Layout
+The SM510 and SM511 differ in how they map their program ROM into the address space.
+
+The SM510 uses a **non-contiguous** map across a 12-bit address space.
+The 704-byte pages are placed at specific offsets and the gaps in between are unmapped:
+
+```cpp
+void sm510_device::program_2_7k(address_map &map)
+{
+ map(0x0000, 0x02bf).rom();
+ map(0x0400, 0x06bf).rom();
+ map(0x0800, 0x0abf).rom();
+ map(0x0c00, 0x0ebf).rom();
+}
+```
+
+Relevant source [^6].
+
+The SM511 uses a **contiguous** 4 KB region starting at address 0:
+
+```cpp
+void sm511_device::program_4k(address_map &map)
+{
+ map(0x0000, 0x0fff).rom();
+}
+```
+
+Relevant source [^7].
+
+The data address map is identical between SM510 and SM511.
+The lower 96 bytes (`0x00-0x5F`) are general-purpose RAM.
+The upper 32 bytes (`0x60-0x7F`) are shared with the LCD controller as `lcd_ram_a` and `lcd_ram_b`.
+The SM512 adds a third shared region (`lcd_ram_c`) at `0x50-0x5F`.
+
+```cpp
+void sm511_device::data_96_32x4(address_map &map)
+{
+ map(0x00, 0x5f).ram();
+ map(0x60, 0x6f).ram().share("lcd_ram_a");
+ map(0x70, 0x7f).ram().share("lcd_ram_b");
+}
+```
+
+Relevant source [^7].
+
+The program counter advances within each 64-instruction page using a **6-bit LFSR** rather than a plain counter.
+The LFSR produces a non-sequential order of addresses within the page - the game's ROM must be placed at exactly the addresses the hardware would visit.
+
+```cpp
+void sm510_base_device::increment_pc()
+{
+ // PL(program counter low 6 bits) is a simple LFSR: newbit = (bit0==bit1)
+ int msb = m_pagemask >> 1 ^ m_pagemask;
+ int feed = ((m_pc >> 1 ^ m_pc) & 1) ? 0 : msb;
+ m_pc = feed | (m_pc >> 1 & m_pagemask >> 1) | (m_pc & ~m_pagemask);
+}
+```
+
+Relevant source [^3].
+
+The upper bits of `m_pc` (PM and PU) select the page, while PL (the lower 6 bits) steps through the LFSR sequence.
+
+---
+### The Instruction Set
+
π
+The SM510 instruction set is compact and purpose-built for the G&W use case.
+Most opcodes are one byte. Long jumps and calls use two bytes.
+
+
+All 64 opcode handlers are defined here as methods of `sm510_base_device`.
+They cover five categories: RAM address manipulation, ROM address/control flow, data transfer and I/O, arithmetic and test, and melody control.
+Each opcode handler is a tiny function, typically 2-5 lines.
+
+
+
+The following examples show the key instruction categories, with their exact C++ implementations.
+
+#### Control Flow
+`TL xyz` performs a long jump to an absolute 12-bit address.
+The target is assembled from the 4-bit opcode low nibble and the full second byte:
+
+```cpp
+void sm510_base_device::op_tl()
+{
+ // TL xyz: long jump
+ do_branch(m_param >> 6 & 3, m_op & 0xf, m_param & 0x3f);
+}
+```
+
+`TML xyz` is the call variant - it pushes the return address onto the stack first:
+
+```cpp
+void sm510_base_device::op_tml()
+{
+ // TML xyz: long call
+ push_stack();
+ do_branch(m_param >> 6 & 3, m_op & 3, m_param & 0x3f);
+}
+```
+
+`RTN0` returns from a subroutine by popping the stack:
+
+```cpp
+void sm510_base_device::op_rtn0()
+{
+ // RTN0: return from subroutine
+ pop_stack();
+}
+```
+
+Relevant source [^5].
+
+#### I/O and LCD Instructions
+The game reads buttons via `KTA` (K-to-ACC).
+This is the primary way the game logic checks which buttons are pressed:
+
+```cpp
+void sm510_base_device::op_kta()
+{
+ // KTA: input K to ACC
+ m_acc = m_read_k() & 0xf;
+}
+```
+
+The `WR`/`WS` opcodes build up the S strobe output one bit at a time.
+`WR` shifts a 0 into the shift register W; `WS` shifts a 1.
+After building the desired strobe pattern, `PTW` latches it to the output port:
+
+```cpp
+void sm510_base_device::op_wr()
+{
+ // WR: shift 0 into W
+ m_w = m_w << 1 | 0;
+ update_w_latch();
+}
+
+void sm510_base_device::op_ws()
+{
+ // WS: shift 1 into W
+ m_w = m_w << 1 | 1;
+ update_w_latch();
+}
+```
+
+`ATL` and `ATX` write the accumulator into the LCD control registers L and X.
+These affect which segments blink and which LCD common lines are active:
+
+```cpp
+void sm510_base_device::op_atl()
+{
+ // ATL: output ACC to L
+ m_l = m_acc;
+}
+
+void sm510_base_device::op_atx()
+{
+ // ATX: output ACC to X
+ m_x = m_acc;
+}
+```
+
+Relevant source [^5].
+
+#### Arithmetic
+`ADD11` is the carry-propagating add instruction.
+It adds RAM, ACC, and carry together, sets carry on overflow, and skips the next instruction if carry is set:
+
+```cpp
+void sm510_base_device::op_add11()
+{
+ // ADD11: add RAM and carry to ACC and carry, skip next on carry
+ m_acc += ram_r() + m_c;
+ m_c = m_acc >> 4 & 1;
+ m_skip = (m_c == 1);
+ m_acc &= 0xf;
+}
+```
+
+`ROT` rotates the 4-bit accumulator right through carry.
+This provides a simple way to inspect individual bits:
+
+```cpp
+void sm510_base_device::op_rot()
+{
+ // ROT: rotate ACC right through carry
+ u8 c = m_acc & 1;
+ m_acc = m_acc >> 1 | m_c << 3;
+ m_c = c;
+}
+```
+
+Relevant source [^5].
+
+#### Halt
+`CEND` stops the CPU clock and enters low-power mode.
+Games use this to idle between frames.
+The CPU remains halted until an external K input or the divider gamma signal fires:
+
+```cpp
+void sm510_base_device::op_cend()
+{
+ // CEND: stop clock (halt the cpu and go into low-power mode)
+ m_halt = true;
+}
+```
+
+Relevant source [^5].
+
+---
+## ROM Data
+
πΎ
+Each Game & Watch driver in `src/mame/handheld/hh_sm510.cpp` defines three categories of ROM data.
+
+The simplest example is Ball (AC-01), the very first Game & Watch game.
+It uses the SM5A and has a single LCD screen:
+
+```cpp
+ROM_START( gnw_ball )
+ ROM_REGION( 0x800, "maincpu", 0 )
+ ROM_LOAD( "ac-01", 0x0000, 0x0740, CRC(ac94e6e4) SHA1(8270cb61f9fbff252eafec411b4c67f0171f8687) )
+
+ ROM_REGION( 71748, "screen", 0)
+ ROM_LOAD( "gnw_ball.svg", 0, 71748, CRC(7c116eaf) SHA1(578882af492b8a9f1eb72e06a547c8b574255fb9) )
+ROM_END
+```
+
+Relevant source [^1].
+
+The three ROM types are:
+
+* **Program ROM** - The machine code dump from the MCU, placed at the program address space. `ac-01` here is 0x0740 (1856) bytes of actual SM5A instructions.
+* **SVG file** - A vector graphics description of the LCD panel. The `"screen"` region holds the entire SVG as raw binary data. MAME's SVG renderer reads this at startup.
+* **Melody ROM** - Only present on SM511 and SM512 games. A 256-byte region tagged `"maincpu:melody"` containing tone commands for the melody controller.
+
+Donkey Kong (DK-52) uses the SM510 with a dual vertical screen layout.
+It has 4 KB of program ROM and two separate SVG files:
+
+```cpp
+ROM_START( gnw_dkong )
+ ROM_REGION( 0x1000, "maincpu", 0 )
+ ROM_LOAD( "dk-52", 0x0000, 0x1000, CRC(5180cbf8) SHA1(5174570a8d6a601226f51e972bac6735535fe11d) )
+
+ ROM_REGION( 176843, "screen_top", 0)
+ ROM_LOAD( "gnw_dkong_top.svg", 0, 176843, CRC(16c16b84) SHA1(fa2e54c04366a30b51de024296b9f94c1cb76d68) )
+
+ ROM_REGION( 145516, "screen_bottom", 0)
+ ROM_LOAD( "gnw_dkong_bottom.svg", 0, 145516, CRC(2b711e9d) SHA1(0e263020cbe0e8b88bb68e3176630639b518935e) )
+ROM_END
+```
+
+Relevant source [^1].
+
+Notice that the SVG region sizes are exact byte counts, treated like any other ROM dump.
+Checksums (CRC and SHA1) verify the SVG file integrity the same way they verify a program ROM.
+
+---
+## The LCD Driver
+
π
+The LCD controller lives inside the CPU device itself, not in the driver.
+The CPU device exposes a `write_segs()` callback that fires once per LCD refresh cycle.
+The driver connects this callback to its own display processing pipeline.
+
+
+`sm510base.cpp` implements the shared infrastructure: startup, reset, the LCD controller, the divider timer, interrupt handling, PC advancement, and the main execution loop.
+Device-specific behaviour (opcode dispatch, ROM address maps) lives in the per-variant files.
+
+
+
+The `init_lcd_driver()` function creates a timer that fires at approximately 1 kHz.
+This matches the real hardware behaviour where the LCD is strobed once per 32 divider ticks:
+
+```cpp
+void sm510_base_device::init_lcd_driver()
+{
+ // note: in reality, this timer runs at high frequency off the main divider,
+ // strobing one segment at a time
+ m_lcd_timer = timer_alloc(FUNC(sm510_base_device::lcd_timer_cb), this);
+ attotime period = attotime::from_ticks(0x20, unscaled_clock()); // default 1kHz
+ m_lcd_timer->adjust(period, 0, period);
+}
+```
+
+Relevant source [^3].
+
+Each timer tick calls `lcd_update()`.
+This function reads four columns of LCD data out of `lcd_ram_a`, `lcd_ram_b`, and `lcd_ram_c`, then fires the `write_segs()` callback for each:
+
+```cpp
+void sm510_base_device::lcd_update()
+{
+ // 4 columns
+ for (int h = 0; h < 4; h++)
+ {
+ // 16 segments per row from upper part of RAM
+ m_write_segs(h | SM510_PORT_SEGA, get_lcd_row(h, m_lcd_ram_a));
+ m_write_segs(h | SM510_PORT_SEGB, get_lcd_row(h, m_lcd_ram_b));
+ m_write_segs(h | SM510_PORT_SEGC, get_lcd_row(h, m_lcd_ram_c));
+
+ // bs output from L/X and Y regs
+ u8 blink = (m_div & 0x4000) ? m_y : 0;
+ u8 bs = ((m_l & ~blink) >> h & 1) | ((m_x*2) >> h & 2);
+ m_write_segs(h | SM510_PORT_SEGBS, (m_bc || !(m_bp & 1)) ? 0 : bs);
+ }
+}
+```
+
+Relevant source [^3].
+
+The helper `get_lcd_row()` assembles a 16-bit word from one column of LCD RAM.
+It returns zero immediately if the LCD backplate (`m_bp`) or bleeder (`m_bc`) is in the off state:
+
+```cpp
+inline u16 sm510_base_device::get_lcd_row(int column, u8* ram)
+{
+ if (ram == nullptr || m_bc || !(m_bp & 1))
+ return 0;
+
+ u16 rowdata = 0;
+ for (int i = 0; i < 0x10; i++)
+ rowdata |= (ram[i] >> column & 1) << i;
+
+ return rowdata;
+}
+```
+
+Relevant source [^3].
+
+The BS (blinking segment) column deserves special mention.
+The `m_div` bit 14 (the F1 divider flag) controls blink timing.
+When this bit is set, the Y register masks specific L bits, making those segments blink at a low frequency without any CPU intervention.
+
+---
+## The Display System
+
πΌοΈ
+The LCD driver inside the CPU device fires the `write_segs()` callback, but that callback lands in `hh_sm510_state` inside `hh_sm510.cpp`.
+This is where the CPU output is converted into named screen outputs that the SVG renderer can read.
+
+
+`hh_sm510_state` is the base class for all ~200 Game & Watch drivers.
+It bridges the CPU device output to MAME's screen and output systems.
+The 20 member variables cover display geometry, decay simulation state, input multiplexer state, and audio output.
+Relevant state definition [^2].
+
+
+
+### Segment Dimensions
+The SM510 series and the SM500/SM5A series use different LCD geometries.
+`set_display_size()` tells the display system which dimensions to use:
+
+```cpp
+void hh_sm510_state::set_display_size(u8 x, u8 y, u8 z)
+{
+ // x = groups(in bits)
+ // y = number of segments per group
+ // z = commons(in bits)
+ m_display_x_len = x;
+ m_display_y_len = y;
+ m_display_z_len = z;
+}
+```
+
+The segment write callbacks call this before storing the segment data:
+
+```cpp
+void hh_sm510_state::sm510_lcd_segment_w(offs_t offset, u16 data)
+{
+ set_display_size(2, 16, 2);
+ m_display_state[offset] = data;
+}
+
+void hh_sm510_state::sm500_lcd_segment_w(offs_t offset, u16 data)
+{
+ set_display_size(4, 4, 1);
+ m_display_state[offset] = data;
+}
+```
+
+Relevant source [^1].
+
+For the SM510 family, the segment space is organised as:
+
+* **x** - 2 bits = 4 groups (a, b, bs, c)
+* **y** - 16 segments per group (bits 0-15 in the 16-bit data word)
+* **z** - 2 bits = 4 commons (H1 to H4)
+
+For SM500/SM5A, the space is smaller:
+
+* **x** - 4 bits = up to 16 O groups
+* **y** - 4 segments per group
+* **z** - 1 bit = 2 commons (H1 and H2)
+
+The older SM500-family display and opcode behaviour is implemented in a separate source file from the SM510 family core [^8].
+
+---
+### LCD Decay Simulation
+
π‘
+A naive implementation would turn LCD segments on and off as the CPU writes to LCD RAM.
+Real LCD panels do not behave this way.
+Physical LCD cells take a few milliseconds to fully activate and deactivate.
+MAME simulates this persistence using a per-segment decay counter.
+
+The decay system is driven by a second independent 1024 Hz timer, created in `machine_start()`:
+
+```cpp
+m_display_decay_timer = timer_alloc(FUNC(hh_sm510_state::display_decay_tick), this);
+m_display_decay_timer->adjust(attotime::from_hz(1024), 0, attotime::from_hz(1024));
+```
+
+Relevant source [^1].
+
+Each tick calls `update_display()`.
+This is the full implementation:
+
+```cpp
+void hh_sm510_state::update_display()
+{
+ u8 z_mask = (1 << m_display_z_len) - 1;
+ u8 zx_len = 1 << (m_display_x_len + m_display_z_len);
+
+ for (int zx = 0; zx < zx_len; zx++)
+ {
+ for (int y = 0; y < m_display_y_len; y++)
+ {
+ // delay lcd segment on/off state
+ if (m_display_state[zx] >> y & 1)
+ {
+ if (m_display_decay[y][zx] < (m_decay_pivot + m_decay_len))
+ m_display_decay[y][zx]++;
+ }
+ else if (m_display_decay[y][zx] > 0)
+ m_display_decay[y][zx]--;
+ u8 active_state = (m_display_decay[y][zx] < m_decay_pivot) ? 0 : 1;
+
+ // SM510 series: output to x.y.z, where:
+ // x = group a/b/bs/c (0/1/2/3)
+ // y = segment 1-16 (0-15)
+ // z = common H1-H4 (0-3)
+
+ // SM500/SM530 series: output to x.y.z, where:
+ // x = O group (0-*)
+ // y = O segment 1-4 (0-3)
+ // z = common H1/H2 (0/1)
+ m_out_x[zx >> m_display_z_len][y][zx & z_mask] = active_state;
+ }
+ }
+}
+```
+
+Relevant source [^1].
+
+The decay logic works as follows.
+Each segment has a counter in `m_display_decay[y][zx]`.
+When the segment data is 1 (on), the counter increments each tick up to a ceiling of `m_decay_pivot + m_decay_len`.
+When the data is 0 (off), it decrements toward zero.
+The output is set active only when the counter is at or above `m_decay_pivot`.
+
+This produces two distinct visual effects.
+
+##### On Delay
+A segment that was off and turns on does not appear until the counter reaches `m_decay_pivot`.
+With the default of 8 ticks at 1024 Hz, that is about 8 ms of activation lag.
+This prevents very brief CPU writes from producing a visible flash.
+
+##### Persistence
+A segment that turns off continues to appear until the counter drains below `m_decay_pivot`.
+With the default `m_decay_len` of 17 ticks, that gives about 17 ms of afterglow.
+This is what makes G&W segments look solid rather than flickering as the CPU scans through them.
+
+##### Per-Game Tuning
+The defaults work well for most games.
+Some games need different values.
+Turtle Bridge (TL-28), for example, has segments that incorrectly activate at default settings.
+Its constructor increases both parameters:
+
+```cpp
+gnw_tbridge_state(const machine_config &mconfig, device_type type, const char *tag) :
+ hh_sm510_state(mconfig, type, tag)
+{
+ // increase lcd decay: unwanted segments light up
+ m_decay_pivot = 25;
+ m_decay_len = 25;
+}
+```
+
+Relevant source [^1].
+
+The higher `m_decay_pivot` means a segment must receive a full 25 ticks of data before appearing.
+Short spurious pulses from the real hardware (which on Turtle Bridge accidentally activate neighbouring segments) are filtered out because they never accumulate enough ticks to cross the threshold.
+
+---
+### Segment-to-Screen Mapping
+The final line of `update_display()` writes to `m_out_x`:
+
+```cpp
+m_out_x[zx >> m_display_z_len][y][zx & z_mask] = active_state;
+```
+
+`m_out_x` is declared as `output_finder<16, 16, 4>` with the format string `"%u.%u.%u"`.
+MAME expands this into named output values like `"0.0.0"`, `"0.1.0"`, `"1.3.2"`, and so on.
+The SVG file must use those exact strings as element IDs.
+When `m_out_x[0][3][1]` is 1, MAME looks for an SVG element with ID `"0.3.1"` and makes it visible.
+
+This is the complete rendering pipeline: CPU writes nibbles to LCD RAM, the LCD driver assembles them into 16-bit words per column, the segment callback stores them into `m_display_state`, the decay timer updates the visibility state, and the named output system maps that state into SVG element visibility.
+
+---
+## SVG Rendering Pipeline
+
πΌοΈ
+MAME's SVG screen type renders vector artwork that changes dynamically based on named output values.
+Each SVG element whose `id` matches an output name is shown when that output is 1 and hidden when it is 0.
+The SVG describes the exact shape, position, and colour of every LCD segment on the physical panel.
+
+The `mcfg_svg_screen()` helper configures one screen device per display:
+
+```cpp
+void hh_sm510_state::mcfg_svg_screen(machine_config &config, u16 width, u16 height, const char *tag)
+{
+ if (width == 0 || height == 0)
+ return;
+
+ screen_device &screen(SCREEN(config, tag, SCREEN_TYPE_SVG));
+ screen.set_refresh_hz(60);
+ screen.set_size(width, height);
+ screen.set_visarea_full();
+
+ config.set_default_layout(layout_hh_sm510_single);
+}
+```
+
+Relevant source [^1].
+
+The width and height values come from the individual game driver.
+Ball, for example, passes `1671` and `1080`.
+These are the pixel dimensions used for the SVG viewport.
+
+The layout file (`hh_sm510_single.lh`, or `hh_sm510_dualv.lh` etc.) is a compressed MAME artwork file included at compile time.
+It positions the screen within the MAME window.
+The four layout variants are included at the top of the driver file:
+
+```cpp
+#include "hh_sm510_single.lh"
+#include "hh_sm510_dualv.lh"
+#include "hh_sm510_dualh.lh"
+#include "hh_sm510_tripleh.lh"
+```
+
+Relevant source [^1].
+
+### Multi-Screen Games
+Dual-screen and triple-screen G&W games simply add more screen devices.
+Each screen gets its own SVG ROM region and its own named output namespace.
+
+For a dual vertical layout (top/bottom, like Donkey Kong), `sm510_dualv()` creates two screens:
+
+```cpp
+void hh_sm510_state::sm510_dualv(machine_config &config,
+ u16 topwidth, u16 topheight, u16 botwidth, u16 botheight)
+{
+ mcfg_cpu_sm510(config);
+ mcfg_sound_r1(config);
+ mcfg_svg_screen(config, topwidth, topheight, "screen_top");
+ mcfg_svg_screen(config, botwidth, botheight, "screen_bottom");
+
+ config.set_default_layout(layout_hh_sm510_dualv);
+}
+```
+
+The Donkey Kong driver calls it with the screen dimensions halved from the SVG file natural size:
+
+```cpp
+void gnw_dkong_state::gnw_dkong(machine_config &config)
+{
+ sm510_dualv(config, 1920/2, 1266/2, 1920/2, 1266/2);
+}
+```
+
+Relevant source [^1].
+
+For a dual horizontal layout (left/right, like Mario Bros), `sm510_dualh()` creates `"screen_left"` and `"screen_right"`.
+For triple horizontal (like Zelda), `sm511_tripleh()` creates left, middle, and right screens.
+In each case, the CPU's LCD output is split between the screens based on which output names appear in which SVG file.
+
+---
+## Input Handling
+
π§
+The SM5xx input system uses multiplexing.
+The CPU writes a strobe pattern to the S output port using the W shift register.
+The driver reads the K, BA, and B input pins in response to that strobe.
+This allows more buttons than the CPU has input pins.
+
+### The Input Functions
+`read_inputs()` scans the active input rows based on the current multiplexer mask `m_inp_mux`:
+
+```cpp
+u8 hh_sm510_state::read_inputs(int columns, int fixed)
+{
+ u8 ret = 0;
+
+ // read selected input rows
+ for (int i = 0; i < columns; i++)
+ if (BIT(m_inp_mux, i))
+ ret |= m_inputs[i]->read();
+
+ if (fixed >= 0)
+ ret |= m_inputs[fixed]->read();
+
+ return ret;
+}
+```
+
+`input_r()` is the callback the CPU calls when reading the K port.
+It delegates to `read_inputs()` with the configured column count and fixed line:
+
+```cpp
+u8 hh_sm510_state::input_r()
+{
+ return read_inputs(m_inp_lines, m_inp_fixed);
+}
+```
+
+Relevant source [^1].
+
+`update_k_line()` feeds the current input state directly to the MCU interrupt line.
+This allows the CPU to wake from halt mode when a button is pressed:
+
+```cpp
+void hh_sm510_state::update_k_line()
+{
+ // this is necessary because the MCU can wake up on K input activity
+ m_maincpu->set_input_line(0, input_r() ? ASSERT_LINE : CLEAR_LINE);
+}
+```
+
+Relevant source [^1].
+
+---
+### Input Port Definitions
+Simple games like Ball use only the BA and B input pins directly.
+The CPU reads them as single bits without any strobe:
+
+```cpp
+static INPUT_PORTS_START( gnw_ball )
+ PORT_START("IN.0")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
+ PORT_CONFNAME( 0x08, 0x00, "Invincibility (Cheat)" )
+ PORT_CONFSETTING( 0x00, DEF_STR( Off ) )
+ PORT_CONFSETTING( 0x08, DEF_STR( On ) )
+
+ PORT_START("BA")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
+
+ PORT_START("B")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed) PORT_16WAY
+
+ PORT_START("ACL")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
+INPUT_PORTS_END
+```
+
+Donkey Kong uses three multiplexed strobe rows (S1, S2, S3), accessed by the CPU as `"IN.0"`, `"IN.1"`, `"IN.2"`:
+
+```cpp
+static INPUT_PORTS_START( gnw_dkong )
+ PORT_START("IN.0") // S1
+ PORT_BIT( 0x07, IP_ACTIVE_HIGH, IPT_UNUSED )
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CHANGED_CB(input_changed) // Jump
+
+ PORT_START("IN.1") // S2
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_CHANGED_CB(input_changed)
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_CHANGED_CB(input_changed)
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_CHANGED_CB(input_changed)
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_CHANGED_CB(input_changed)
+
+ PORT_START("IN.2") // S3
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_CHANGED_CB(input_changed) PORT_NAME("Time")
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game B")
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Game A")
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_SERVICE2 ) PORT_CHANGED_CB(input_changed) PORT_NAME("Alarm")
+
+ PORT_START("ACL")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_CHANGED_CB(acl_button) PORT_NAME("ACL")
+INPUT_PORTS_END
+```
+
+Relevant source [^1].
+
+The game code writes different values to the S port to select which row it wants to read, then reads K to get the button state for that row.
+This is the same technique used in keyboard matrix scanning.
+
+---
+## Sound
+
π§
+Game & Watch sound is simple by modern standards.
+A piezoelectric buzzer produces tones from a 1-bit or 2-bit digital output.
+The SM511 and SM512 added a dedicated melody controller with a 256-byte tone ROM.
+
+### 1-bit Piezo (SM510 and SM5A)
+The simplest configuration routes the R port directly to a `speaker_sound_device`.
+`mcfg_sound_r1()` sets this up:
+
+```cpp
+void hh_sm510_state::mcfg_sound_r1(machine_config &config)
+{
+ SPEAKER(config, "mono").front_center();
+ SPEAKER_SOUND(config, m_speaker);
+ m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
+
+ m_maincpu->write_r().set(FUNC(hh_sm510_state::piezo_r1_w));
+}
+```
+
+The callback simply writes the low bit of the R output to the speaker level:
+
+```cpp
+void hh_sm510_state::piezo_r1_w(u8 data)
+{
+ // R1 to piezo (SM511 R pin is melody output)
+ m_speaker->level_w(data & 1);
+}
+```
+
+Relevant source [^1].
+
+The SM510 `clock_melody()` method controls tone frequency via a divider mask.
+When `m_r_mask_option` is `RMASK_DIRECT`, the raw R register bits drive the speaker.
+When set to a divider bit index, the divider free-runs and the CPU only gates the output on or off.
+This is how the SM510 produces alarm tones without needing to toggle a bit at audio frequency.
+
+The SM5A complicates this slightly.
+On the SM5A, the R port serves double duty for both sound and input multiplexing.
+The `sm5a_common()` config overrides the default R callback with `piezo_input_w`, which handles both functions from a single write:
+
+```cpp
+void hh_sm510_state::piezo_input_w(u8 data)
+{
+ // R1 to piezo, other to input mux
+ piezo_r1_w(data & 1);
+ input_w(data >> 1);
+}
+```
+
+Relevant source [^1].
+
+Bit 0 goes to the speaker, higher bits go to the input multiplexer.
+
+---
+### Melody ROM (SM511 and SM512)
+
πΌ
+The SM511 adds a dedicated 256-byte melody ROM and a hardware melody controller.
+The CPU program does not need to toggle the R pin at audio frequency.
+Instead, it writes a melody address via the `PRE` opcode and enables playback via `SME`.
+The hardware automatically advances through the melody data and generates tones.
+
+##### Tone Command Format
+The tone cycle table is a 64-entry [LUT](#glossary-lut) embedded in the emulator.
+It maps each combination of tone command (0-15) and duty cycle index (0-3) to a tick count:
+
+```cpp
+static const u8 lut_tone_cycles[4*16] =
+{
+ 0, 0, 7, 8, 8, 9, 9, 10, 11, 11, 12, 13, 14, 14, 0, 0,
+ 0, 0, 8, 8, 9, 9, 10, 11, 11, 12, 13, 13, 14, 15, 0, 0,
+ 0, 0, 8, 8, 9, 9, 10, 10, 11, 12, 12, 13, 14, 15, 0, 0,
+ 0, 0, 8, 9, 9, 10, 10, 11, 11, 12, 13, 14, 14, 15, 0, 0,
+};
+```
+
+Commands 0 and 1 are rest and stop respectively.
+Commands 2-13 produce tones.
+Commands 14-15 are illegal.
+The OCT bit (bit 4 of the command byte) shifts the cycle count left by 1, halving the frequency - this is the octave selector.
+
+##### Playback and Note Advancement
+The complete `clock_melody()` function runs on every divider tick:
+
+```cpp
+void sm511_device::clock_melody()
+{
+ if (!m_melody_rom)
+ return;
+
+ u8 cmd = m_melody_rom[m_melody_address] & 0x3f;
+ u8 out = 0;
+
+ // clock duty cycle if tone is active
+ if ((cmd & 0xf) >= 2 && (cmd & 0xf) <= 13)
+ {
+ out = m_melody_duty_index & m_melody_rd & 1;
+ m_melody_duty_count++;
+ int index = m_melody_duty_index << 4 | (cmd & 0xf);
+ int shift = ~cmd >> 4 & 1; // OCT
+
+ if (m_melody_duty_count >= (lut_tone_cycles[index] << shift))
+ {
+ m_melody_duty_count = 0;
+ m_melody_duty_index = (m_melody_duty_index + 1) & 3;
+ }
+ }
+ else if ((cmd & 0xf) == 1)
+ {
+ // set melody stop flag
+ m_melody_rd |= 2;
+ }
+
+ // clock time base on divider F7/F8
+ if ((m_div & melody_step_mask()) == 0)
+ {
+ u8 mask = (cmd & 0x20) ? 0x1f : 0x0f;
+ m_melody_step_count = (m_melody_step_count + 1) & mask;
+
+ if (m_melody_step_count == 0)
+ m_melody_address++;
+ }
+
+ // output to R pin
+ if (out != m_r_out)
+ {
+ m_write_r(out);
+ m_r_out = out;
+ }
+}
+```
+
+Relevant source [^7].
+
+The duty cycle index cycles through 0-3.
+The output bit is `m_melody_duty_index & m_melody_rd & 1`.
+This means only when both the duty index is odd and melody is enabled does the output go high.
+The result is a square-wave-like pattern whose frequency is set by how quickly the duty counter fills to the LUT threshold.
+
+The melody address advances whenever the lower divider bits (`melody_step_mask()`) wrap around to zero.
+The duration field at bits 5-4 of the ROM byte doubles or quadruples the note length by widening the step counter mask.
+
+The SM511 also resets its clock divider to 4 rather than 2, halving the effective CPU instruction rate from 16384 Hz to 8192 Hz.
+This is set in `device_reset()`:
+
+```cpp
+void sm511_device::device_reset()
+{
+ sm510_base_device::device_reset();
+ m_melody_rd &= ~1;
+ m_clk_div = 4; // 8kHz
+ notify_clock_changed();
+}
+```
+
+Relevant source [^7].
+
+A melody ROM region in the driver provides the tune data.
+The region tag `"maincpu:melody"` is how the CPU device locates it at runtime.
+The verifier in `init_melody()` checks every byte and logs a warning if any byte has illegal bits set.
+
+---
+# References
+[^1]: `src/mame/handheld/hh_sm510.cpp` - Main Game & Watch driver, ~200 games
+[^2]: `src/mame/handheld/hh_sm510.h` - Driver state class definition
+[^3]: `src/devices/cpu/sm510/sm510base.cpp` - SM5xx shared CPU implementation
+[^4]: `src/devices/cpu/sm510/sm510base.h` - SM5xx base class and opcode declarations
+[^5]: `src/devices/cpu/sm510/sm510op.cpp` - SM510 opcode handler implementations
+[^6]: `src/devices/cpu/sm510/sm510.cpp` - SM510 device, ROM maps, buzzer controller
+[^7]: `src/devices/cpu/sm510/sm511.cpp` - SM511/SM512 device, melody controller
+[^8]: `src/devices/cpu/sm510/sm500op.cpp` - SM500 opcode handlers (shift-register display ops)
+[^9]: 1986 Sharp Semiconductor Data Book (referenced in sm510base.cpp source header)
+[^10]: 1990 Sharp Microcomputers Data Book (referenced in sm510base.cpp source header)
diff --git a/pages/consoles/gameboy/Mrdo.md b/pages/consoles/gameboy/Mrdo.md
index a9e191cf..753f50ce 100644
--- a/pages/consoles/gameboy/Mrdo.md
+++ b/pages/consoles/gameboy/Mrdo.md
@@ -8,7 +8,8 @@ title: Mr Do! Source Code (Game Boy)
category:
- gameboy
- sourcecode
-image: /public/games/MrDoGameboy.jpg
+placeholderimages:
+- /public/games/MrDoGameboy.jpg
permalink: /mrdo
breadcrumbs:
- name: Home
diff --git a/public/generated/placeholders/mrdo.jpg b/public/generated/placeholders/mrdo.jpg
new file mode 100644
index 00000000..e852bf84
Binary files /dev/null and b/public/generated/placeholders/mrdo.jpg differ