Hacking, Coding and Gaming | @[email protected]

Some time ago I learned about a Nintendo NES (Famicom) cartridge called "van der Merwe en Boetie", released only in South Africa and supposedly a Super Mario Bros clone. The title is a common Afrikaans surname ("van der Merwe") followed by "and his brother" (in Afrikaans) - definitely local, and relatively close to "Mario Bros".

I'd seen a few photos of the cartridge - even internationally on Twitter - and a few people in the local vintage computing group claimed to have a copy of the cartridge but either couldn't find it or weren't keen on sharing it. Eventually I tracked down someone willing to lend me their cartridge for dumping and analysis (thanks Brad!), which this post is about as I feel the preservation of retro and rare computing is important.

The Game

The sticker on the cartridge is clearly custom, showing the local "khaki shorts" and other African attire:

Using a Kazzo programmer/dumper, and the PRG and CHR configs for Super Mario Bros, I was able to dump the cartridge to a rom file I could run! The first noticeable difference is the custom title screen:

The Mario sprite has been changed as well as the name (in the top left) and some other text.

I extracted most of the sprites and made them in to animations where relevant:

The only other change is a level hack built in to the title screen, by pressing the "A" and "B" buttons to increment the world/level number before the game is started. This comes at the cost of corrupting the music played after defeating bowser and rescuing Toad/Princess (thanks Deen!).

While I was hoping South Africa had some genius back in the day capable of pulling off such a graphic and text modification, mass production of NES cartridges, AND a inject level hack... it turns out this rom is based on a Super Mario Bros rom which has a similar title screen (most noticeably the thicker border on the right and lack of the "Nintendo" brand) with the level hack code with sound corruption.

I'm not going to provide the dumped rom as it's mostly Super Mario Bros content which I don't own the copyright for. A dump of the rom can be found on The Internet Archive with some searching, for the curious.

The Analysis

Having dumped the rom, and seeing it was based on Super Mario Bros, I did a hex comparison with the vanilla Mario rom and found many differences. It's worth mentioning the game is based on the Super Mario Bros "world" release, as opposed to the "European" release (thanks again Deen for raising this) - as it turns out there are some noticeable differences.

I found some tools to modify the noticeable differences in "van der Merwe en Boetie" rom, in the original Super Mario Bros... saving each modification as a separate file:

I did a hex comparison of each of my modified files against the original, and made note of the offsets that had been changed. Comparing these address to the changes in the "van der Merwe en Boetie" rom I could identify what the differences were:

Offset 0x0763 - 0x0817 = text changes
Offset 0x8010 - 0x8A0C = sprite changes
Offset 0x9D00 - 0x9FC2 = title screen changes

The remaining changes consisted of two little changes, and a large change in the sound data of the game. Using the FCEUX emulator I could see the game code and see the newly added jump to a new block of code in the sound data, identifying the remaining changes:

Offset 0x00B3 - 0x00B5 = minor code changes (jumping to custom level-select code)
Offset 0x024D - 0x024E = minor code changes
Offset 0x7E10 - 0x7EC0 = custom level-select code (replaces music played when rescuing Toad/Peach)

This left with some magical level-select code I began reverse engineering but wanted to know where it came from. A few people mentioned they were sure they'd seen a multi cart with a Mario game with a similar level select modification.

Starting with the "GoodNES" collection of "multicart" roms, I output each rom's hex contents to text a text file and then searched through them for the known (hex) bytes of the level select code which returned some results!

~: ls *.nes | while read FILE; do xxd -p -c 1000000 $FILE > hex/$FILE; done;

~: grep -nri 8DE601AC5C07D0068C60074C4582 hex | awk -F':' '{print $1}'
./100-in-1 Arcade Action II (AT-103) (Unl) [b1].nes
./100-in-1 Contra Function 16 [p1][!].nes
./168-in-1 [p1][!].nes
./180-in-1 (15-in-1, 18-in-1, 30-in-1, 52-in-1, 58-in-1, 160-in-1, 288-in-1) (SJ-0027) [p1][b1].nes
./255-in-1 (As) [!].nes
./4-in-1 Digital Adventure (Unl) [b1].nes
./55-in-1 (WQ2006 B) [p1].nes
./8-in-1 Supergame (KY8002) [p1][b1].nes
./9-in-1 (KY-9005) [p1][b1].nes
./Famicom Yarou 54 (Unl) [!].nes
./Portable FC-LCD 14-in-1 Game (Ch) [!].nes
./PowerJoy 84-in-1 (PJ-008) (Unl) [!].nes

One or two were false positives, or roms which wouldn't run, but sure enough I could find some variant of Super Mario Bros (always with a similar title screen) with the level hack code. This didn't quite reveal the base rom used to make "van der Merwe en Boetie".

I found the huge "No-Intro" NES rom collection and did the same as above, revealing some individual roms:

Frog Prince (SMB1 Hack).nes
Mushroom (Unl).nes
Pandamar (SMB1 Hack).nes
Super Mario Bros. (Unl) [p1][!].nes
Super Mario Bros. (Unl) [p1][!].nes
Super Mario Bros. (W) [p2].nes

With these rom names I was able to find out more about Pandamar and Frog Prince / Mushroom. Alas, that means there was no custom code crafted in South Africa.

Level select code code

Before discovering the origins of the level select code I began trying to analyze it in the hopes of finding more secrets or something that might hint at the author or their skill level. This is only a partial, and possible incorrect, analysis of the level select code:

00:80A3:20 5D FE  JSR $FE5D                     ; overwritten code jumping to level select code

...

01:FE00:AD FC 06  LDA $06FC                     ; retrieve button code (being pressed)
01:FE03:29 C0     AND #$C0                      ; if A+B buttons are being pressed
01:FE05:F0 28     BEQ $FE2F                     ;   jump to FE2F
01:FE07:AE E6 01  LDX $01E6                     ;
01:FE0A:D0 26     BNE $FE32                     ;
01:FE0C:29 80     AND #$80                      ; check if button A is pressed
01:FE0E:F0 12     BEQ $FE22                     ;   jump to FE22 if not
01:FE10:EE 5C 07  INC $075C                     ;   (else) increment desired level
01:FE13:AD 5C 07  LDA $075C                     ;     load the level number in to the accumulator
01:FE16:29 03     AND #$03                      ;     check if it's value is 3 (level 4)
01:FE18:8D 5C 07  STA $075C                     ;     and if so reset it to 0 (level 1)
01:FE1B:AD FC 06  LDA $06FC                     ; retrieve button code (being pressed)
01:FE1E:29 40     AND #$40                      ; check if button B is pressed
01:FE20:F0 0B     BEQ $FE2D                     ;  jump to FE2D if not
01:FE22:EE 5F 07  INC $075F                     ;  (else) increment desired world
01:FE25:AD 5F 07  LDA $075F                     ;    load the world number in to the accumulator
01:FE28:29 07     AND #$07                      ;    check if it's value is 7 (world 8)
01:FE2A:8D 5F 07  STA $075F                     ;    and if so reset it to 0 (world 1)
01:FE2D:A9 FF     LDA #$FF                      ; clear the accumulator
01:FE2F:8D E6 01  STA $01E6                     ;
01:FE32:AC 5C 07  LDY $075C                     ;
01:FE35:D0 06     BNE $FE3D                     ;
01:FE37:8C 60 07  STY $0760                     ;
01:FE3A:4C 45 82  JMP $8245                     ; jump back to Mario code?
01:FE3D:AD 5F 07  LDA $075F                     ;
01:FE40:C9 01     CMP #$01                      ;
01:FE42:EA        NOP                           ;
01:FE43:EA        NOP                           ;
01:FE44:C9 02     CMP #$02                      ;
01:FE46:F0 EF     BEQ $FE37                     ;
01:FE48:C9 04     CMP #$04                      ;
01:FE4A:F0 EB     BEQ $FE37                     ;
01:FE4C:C9 05     CMP #$05                      ;
01:FE4E:F0 E7     BEQ $FE37                     ;
01:FE50:C9 07     CMP #$07                      ;
01:FE52:F0 E3     BEQ $FE37                     ;
01:FE54:C8        INY                           ;
01:FE55:D0 E0     BNE $FE37                     ;
01:FE57:29 02     AND #$02                      ;
01:FE59:D0 DC     BNE $FE37                     ;
01:FE5B:F0 F7     BEQ $FE54                     ;
01:FE5D:48        PHA                           ;
01:FE5E:AD E6 01  LDA $01E6                     ;
01:FE61:F0 49     BEQ $FEAC                     ;
01:FE63:AD 14 03  LDA $0314                     ;
01:FE66:48        PHA                           ;
01:FE67:AD 16 03  LDA $0316                     ;
01:FE6A:48        PHA                           ;
01:FE6B:AC 5F 07  LDY $075F                     ; fetch the desired world value
01:FE6E:C8        INY                           ;   increase it by one (value of 7 is world 8 on screen)
01:FE6F:8C 14 03  STY $0314                     ;   update the world text for the title screen bar
01:FE72:AC 5C 07  LDY $075C                     ; fetch the desired level value
01:FE75:C8        INY                           ;   increase it by one (value of 3 is level 4 on screen)
01:FE76:8C 16 03  STY $0316                     ;   update the level text for the title screen bar
01:FE79:A9 10     LDA #$10                      ;
01:FE7B:8D 00 20  STA PPU_CTRL                  ;
01:FE7E:A9 00     LDA #$00                      ;
01:FE80:8D 01 20  STA PPU_MASK                  ;
01:FE83:AE 02 20  LDX PPU_STATUS                ;
01:FE86:A9 20     LDA #$20                      ;
01:FE88:8D 06 20  STA PPU_ADDRESS               ;
01:FE8B:A9 73     LDA #$73                      ;
01:FE8D:8D 06 20  STA PPU_ADDRESS               ;
01:FE8D:8D 06 20  STA PPU_ADDRESS               ;
01:FE90:AD 14 03  LDA $0314                     ;
01:FE93:8D 07 20  STA PPU_DATA                  ;
01:FE96:A9 28     LDA #$28                      ;
01:FE98:8D 07 20  STA PPU_DATA                  ;
01:FE9B:EA        NOP                           ;
01:FE9C:EA        NOP                           ;
01:FE9D:EA        NOP                           ;
01:FE9E:AD 16 03  LDA $0316                     ;
01:FEA1:8D 07 20  STA PPU_DATA                  ;
01:FEA4:68        PLA                           ;
01:FEA5:8D 16 03  STA $0316                     ;
01:FEA8:68        PLA                           ;
01:FEA9:8D 14 03  STA $0314                     ;
01:FEAC:68        PLA                           ;
01:FEAD:8D 01 20  STA PPU_MASK                  ;
01:FEB0:60        RTS                           ; return from subroutine (back to Mario code)