I have a Super Sprint (more on that later) and 720 boardset in my collection. These are untested as they are beasts of pcbs. Two layer, but instead of being sandwiched they are plugged in end to end and bolted to the back door. Yikes.
I came across this video and it prompted me to think about doing something with my pcb sets.
Both these games run on the same hardware, so it’s just a case of burning a new set of roms and playing it right? Unfortunately this is not the case. Atari spent a lot of money developing these games and you (as the operator back in the day) need to pay for this. To stop ingenious operators looking for a quick buck Atari employed a custom chip called a Slapstic.
Fast forward many years and MAME has dissected and simulated every Slapstic version and reproductions have even been produced by UKVAC’er Colin D.
Other slapstic protected games have been hacked out or replaced, Gauntlet I and II and ESB for example. Why not the T-11 based games.
Paperboy is a more desirable game so lets convert Super Sprint into Paperboy without replacing the Slapstic, it can’t be that hard right?
First place to start looking is at MAME’s implementation of a Slapstic, starting with the FAQ written by Aaron Giles https://aarongiles.com/old/slapstic/
The slapstic generally sits between the CPU’s address bus and one of the program ROMs
In the case of the T-11 games it actually handles VRAM banking. The slapstic sits on the address bus and activates a state machine when it’s accessed with certain address combinations which are different for each slapstic, which provides the security mechanism. An address of $0000 and /CS resets the state. In the simplest mode of the slapstic, (there are 3), the next address read sets the bank number output on the BS0 and BS1 pins.
Looking at the the memory map in MAME, reads and writes to the program rom (roms in this case as it’s a 16bit bus) also trigger the slapstics /CS pin. The program rom(s) are mapped into memory location $8000 to $FFFF.
So in theory, there must be code that reads $8000 to clear the Slaptic ready to accept a bank switch which is another read of a memory location between $8000-$FFFF which is mapped to a bank. It is also possible to do bitwise manipulation of the bank pins by accessing certain memory locations in the same range.
Next thing to do is find all accesses to the slapstic region in Paperboy using MAME’s built in debugger.
mame paperboy -debug
wpset 8000,7fff,rw
running the game with the ‘g’ command produces and immediate breakpoint as the cpu is reading the program rom as well. This is not going to help as its not possible to distinguish between slapstic reads and cpu writes.
Looking at the mame code for the slapstic (handily named slapstic.cpp) there is a define for logging slapstic accesses
#define LOG_SLAPSTIC (0)
Changing this to 1 and re-compiling produces a large slapstic.log file when the game is run. This file gets filled with reads in the Slapstic region. Even the initialisation code is logged, hence the mystery multiple reads from the same PC.
':maincpu' (100000): 0000 B=3 ENABLED ':maincpu' (100000): 1234 B=3 ENABLED ':maincpu' (100000): 0000 B=3 ENABLED ':maincpu' (100000): 1234 B=3 ENABLED
The log format is ‘cpu name’, address of instruction (in OCTAL), the offset into the slapstic region and lastly the current bank and state of the slapstic.
':maincpu' (100222): 0049 B=0 DISABLED ':maincpu' (100222): 1234 B=0 DISABLED ':maincpu' (100224): 004A B=0 DISABLED ':maincpu' (100224): 1234 B=0 DISABLED ':maincpu' (100226): 004B B=0 DISABLED ':maincpu' (100226): 1234 B=0 DISABLED ':maincpu' (100230): 004C B=0 DISABLED ':maincpu' (100230): 1234 B=0 DISABLED ':maincpu' (100232): 004D B=0 DISABLED ':maincpu' (100232): 1234 B=0 DISABLED ':maincpu' (100234): 0000 B=0 ENABLED ':maincpu' (100234): 1234 B=0 ENABLED ':maincpu' (100234): 004E B=0 ENABLED ':maincpu' (100234): 1234 B=0 ENABLED ':maincpu' (100236): 004F B=0 ENABLED ':maincpu' (100236): 1234 B=0 ENABLED
Scrolling through the log to where some actual code happens gets is to location 0o100222. Disassembling this in mame shows this routine.
100222: 15DF 0020 0D20 MOV #$0020,$0D20 100230: 0BDF 8000 TST $8000 100234: 0BDF 8020 TST $8020 100240: 0087 RTS
$20 is put into location $D20 which is work ram, then a TST $8000 which reads the Slapstic and resets its state, another read from $8020 which must set the Bank to an unknown value. Since $20 is the same offset as $8020, location $D20 must being used for holding what the current bank is set to.
Continuing in the code are some more routines doing reads from $8000.
; set bank in R0 100242: 00C0 SWAB R0 100244: 0C80 ASR R0 100246: 0C80 ASR R0 100250: 55C0 0020 BIS #$0020,R0 100254: 101F 0D20 MOV R0,$0D20 100260: 0BDF 8000 TST $8000 100264: 0BF0 8000 TST -$8000(R0) 100270: 0087 RTS ; Reset bank to current bank value 100272: 17C0 0D20 MOV $0D20,R0 100276: 0BDF 8000 TST $8000 100302: 0BF0 8000 TST -$8000(R0) 100306: 0087 RTS
The subroutine at 0o100242 sets the bank to the value in R0 after doing some maths on it. This must be the magic code that sets the bank. The reason the address looks weird is that addresses are specified in OCTAL, not HEX although MAME tends to use both at the same time. The rom address is in OCTAL but the disassembly is in HEX, (ie the jump/call address).
Now we have the code that sets the bank we can use breakpoint commands to see what values R0 is set to on the bank calls. Fortunately MAME uses the convention 0oXXXX to specify OCTAL numbers.
mame paperboy -debug bp 0o100242 g
The breakpoint is hit immediately and R0 has a value of $4000. We can single step through the code using F10.
SWAB R0 - R0 changes to $0040 ASR R0 - R0 is $0020 ASR R0 - R0 is $0010 BIS #$0020,R0 - 'BIt Set' #$0020 - R0 becomes $0030 TST $8000 - reset the Slapstic TST -$8000(R0) - Test memory location $8000+R0 ($8030)
So we now know bankswitch addresses are $8020 and $8030.
g
Continuing the code breakpoints again.
This time R0 has a value of $6000, which gets manipulated to $38, meaning a read rom offset $8038, giving us locations of $8020,$8030 and $8038.
The next few Go’s the routine is called with $6000, so to ignore these we can use another feature of MAME’s breakpoint implementation, comparisions
bpclear bp 0o100242,R0 !=0x6000 && R0 != $4000 g
Coining up and playing the game doesn’t seem to trigger any more breakpoints so we have all the info on how Paperboy uses bankswitching in it’s code. Time to see how Super Sprint uses its Slapstic.
Following the same process and examining the slapstic.log file and following the log as the region is reset in mame, then a rom test is executed, the first bank enable is a call to 0o100200
':maincpu' (100200): 0040 B=0 DISABLED ':maincpu' (100200): 1234 B=0 DISABLED ':maincpu' (100202): 0041 B=0 DISABLED ':maincpu' (100202): 1234 B=0 DISABLED ':maincpu' (100204): 0042 B=0 DISABLED ':maincpu' (100204): 1234 B=0 DISABLED ':maincpu' (100206): 0043 B=0 DISABLED ':maincpu' (100206): 1234 B=0 DISABLED ':maincpu' (100210): 0044 B=0 DISABLED ':maincpu' (100210): 1234 B=0 DISABLED ':maincpu' (100212): 0000 B=0 ENABLED ':maincpu' (100212): 1234 B=0 ENABLED ':maincpu' (100212): 0045 B=0 ENABLED ':maincpu' (100212): 1234 B=0 ENABLED ':maincpu' (100214): 0046 B=0 ENABLED ':maincpu' (100214): 1234 B=0 ENABLED
Disassembling this code shows
100200: 15DF 0050 018A MOV #$0050,$018A 100206: 0BDF 8000 TST $8000 100212: 0BDF 8050 TST $8050 100216: 0087 RTS
This code looks very familar. Paper boy reads from $8020 and Super Sprint reads from $8050 in this routine. Looking around this code shows all the functions that set the banks via the slapstic.
; $8072 Reset VRAM bank to current value from $018a 100162: 17C0 018A MOV $018A,R0 100166: 0BDF 8000 TST $8000 100172: 0BF0 8000 TST -$8000(R0) 100176: 0087 RTS ; $8080 Reset to bank 0 ? 100200: 15DF 0050 018A MOV #$0050,$018A 100206: 0BDF 8000 TST $8000 100212: 0BDF 8050 TST $8050 100216: 0087 RTS ; $8090 Set R0 to slapstic address based on bit 13 100220: 35C0 2000 BIT #$2000,R0 100224: 0203 BNE $809C ; Bank 1? 100226: 15C0 0058 MOV #$0058,R0 100232: 0102 BR $80A0 ; $809c Bank 2? 100234: 15C0 005C MOV #$005C,R0 ; save to current bank 100240: 101F 018A MOV R0,$018A ; set bank 100244: 0BDF 8000 TST $8000 100250: 0BF0 8000 TST -$8000(R0) 100254: 0087 RTS
This code is not as fancy as the Paperboy code and just checks a bit in R0 and sets the offset value to one of two values if it’s ON or OFF.
So we now know the slapstic addresses for Super Sprint are $8050, $8058 and $805c.
Making an educated guess, there must be a value missing. $8028 for Paperboy and $8054 for Super Sprint
Offsets Bank0,Bank1??,Bank2,Bank3 Paper boy $8020,$8028??,$8030,$8038 Super Sprint $8050,$8054??,$8058,$805c Champ Sprint $8010,$8014??,$8018,$801c
Now we have all the information to patch Paperboy to use a Super Sprint slapstic.
Patch 1 – Change the bank reset offset
100222: 15DF 0020 0D20 MOV #$0020,$0D20 needs to be changed to 100222: 15DF 0050 0D20 MOV #$0050,$0D20
Patch 2 – Change the address calculation function so the correct values are calculated from R0
100242: 00C0 SWAB R0 100244: 0C80 ASR R0 100246: 0C80 ASR R0 100250: 55C0 0020 BIS #$0020,R0 100254: 101F 0D20 MOV R0,$0D20 100260: 0BDF 8000 TST $8000 100264: 0BF0 8000 TST -$8000(R0) 100270: 0087 RTS
After the ASR (shift right) R0 has the values of $00,$08,$10,$18 whereas Super Sprint needs these to be $00,$04,$08,$10. R0 needs to be shifted right one more time to get these values. Changing the BIS #$0020 to BIS #$0050 gives the page addresses starting at $8050.
We can’t magically insert another instruction so a call to a routine that has an extra shift is necessary. Fortunately there are enough bytes to replace the ASR’s with a call to the routine with the extra shift.
Change ASR's to JSR 100244: 09DF FFF0 JSR $FFF0 177760: 0C80 ASR R0 177762: 0C80 ASR R0 177764: 0C80 ASR R0 177766: 0087 RTS
The rom checksum test in the TEST mode needs fixing but that can be done later in another post.
The files that need patching are shown in the MAME source. As the T-11 is a 16bit cpu the roms are interleaved with the even address’s coming from cpu_l07 and odd address’s coming from cpu_n07
ROM_LOAD16_BYTE( "cpu_l07.rv3", 0x008000, 0x004000, CRC(4024bb9b) SHA1(9030ce5a6a1a3d769c699a92b32a55013f9766aa) ) ROM_LOAD16_BYTE( "cpu_n07.rv3", 0x008001, 0x004000, CRC(0260901a) SHA1(39d786f5c440ca1fd529ee73e2a4d2406cd1db8f) )
Once the roms are patched lets see if it runs.
Using calculator in Window 10 we can convert OCTAL to HEX easily.
The start of the first subroutine to patch is 0o100222 which in hex is $8092. Take away $8000 gives us $0092 offset, divided by 2 (for split roms) is $0049.
100222: 15DF 0020 0D20 MOV #$0020,$0D20 becomes split into two lines in the roms. cpu_l07 Offset $0049 $DF $20 cpu_n07 Offset $0049 $15 $00 Change the $20 to $50 (edit: For Champion Sprint change to $10)
Patch No 2
100244: 0C80 ASR R0 100246: 0C80 ASR R0 100250: 55C0 0020 BIS #$0020,R0
0o100244 is $80A4, take away $8000 is $00A4. Divided by 2 is $0052
cpu_l07 Offset $0052 $0C $0C $55 $00 cpu_n07 Offset $0052 $80 $80 $C0 $20
We need to change this to the JSR instruction, and change the BIS instruction from BIS #$0020 to BIS #$0050
(edit: For Champion Sprint change to BIS #$0010)
100244: 09DF FFF0 JSR $FFF0 100250: 55C0 0050 BIS #$0050,R0
cpu_l07 Offset $0052 $09 $FF $55 $00 cpu_n07 Offset $0052 $DF $F0 $C0 $50
and add the routine at $FFF0
177760: 0C80 ASR R0 177762: 0C80 ASR R0 177764: 0C80 ASR R0 177766: 0087 RTS
cpu_l07 Offset $3ff8 $0C $0C $0C $00 cpu_n07 Offset $3ff8 $80 $80 $80 $87
Now to test in MAME.
Change the slapstic number Paperboy uses (105) in the mame/drivers/atarisy2.cpp file to the Super Sprint slapstic (108).
Recompile MAME to incorporate these changes.
Try running Paperboy now with this change. This is what a real board would do if you just burnt a set of roms without changing the code.
If you don’t already have the two ROM binaries in the mame/roms/paperboy directory, put them there with the correct name.
Start MAME and lets see if it works.
We have a winner. A great looking title shot.
Better check the demo/game works. After showing the highscore page, the demo starts as usual, but then the game messes up with this screen.
This looks like something is screwing with the VRAM banking. We have only changed the slapstic code a little bit and the game initially worked. Lets check the slapstic.log file to see if something strange is happening.
':maincpu' (100300): 0060 B=0 DISABLED ':maincpu' (100300): 1234 B=0 DISABLED ':maincpu' (100302): 0000 B=0 ENABLED ':maincpu' (100302): 1234 B=0 ENABLED ':maincpu' (100302): 0061 B=0 BITWISE1 ':maincpu' (100302): 1234 B=0 BITWISE1 ':maincpu' (100304): 0062 B=0 BITWISE1 ':maincpu' (100304): 1234 B=0 BITWISE1 ':maincpu' (100306): 0028 B=0 BITWISE2 ':maincpu' (100306): 1234 B=0 BITWISE2 ':maincpu' (100306): 0063 B=0 BITWISE2 ':maincpu' (100306): 1234 B=0 BITWISE2
Looking through the log shows the state changing to BITWISE1 and BITWISE2, but this never happened in the normal Paperboy run.
Normally Paperboy uses the Slapstic in simplified mode,but there are other modes which allow the game code to change banks with certain address triggers.
From the Slapstic FAQ
Finally, each slapstic has a mechanism for modifying the value of the
current bank. Earlier chips (101-110) allowed you to twiddle the
specific bits of the bank number, clearing or setting bits 0 and 1
independently.
In mame/machine/slapstic.cpp there is the definition of the Paperboy slapstic
{ 0x0010,0x0014,0x0018,0x001c },/* bank select values */ /* alternate banking */ { 0x007f,0x003d }, /* 1st mask/value in sequence */ { 0x3fff,0x0092 }, /* 2nd mask/value in sequence */ { 0x3ffc,0x00a4 }, /* 3rd mask/value in sequence */ { 0x3ff3,0x0010 }, /* 4th mask/value in sequence */ 0, /* shift to get bank from 3rd */ /* bitwise banking */ { 0x3ff0,0x35b0 }, /* 1st mask/value in sequence */ { 0x3ff3,0x35b0 }, /* clear bit 0 value */ { 0x3ff3,0x35b1 }, /* set bit 0 value */ { 0x3ff3,0x35b2 }, /* clear bit 1 value */ { 0x3ff3,0x35b3 }, /* set bit 1 value */ { 0x3ff8,0x35c0 }, /* final mask/value in sequence */
Interestingly the first line is what we found the bank offset address’s to be but divided by 2 as MAME specifies them in WORD offset rather than byte offset.
The third list is the offsets of address’s that trigger the BITWISE state.
Lets compare this to the Super Sprint definition.
{ 0x0028,0x002a,0x002c,0x002e },/* bank select values */ /* alternate banking */ { 0x007f,0x001f }, /* 1st mask/value in sequence */ { 0x3fff,0x3772 }, /* 2nd mask/value in sequence */ { 0x3ffc,0x3764 }, /* 3rd mask/value in sequence */ { 0x3ff9,0x0028 }, /* 4th mask/value in sequence */ 0, /* shift to get bank from 3rd */ /* bitwise banking */ { 0x3ff0,0x0060 }, /* 1st mask/value in sequence */ { 0x3ff3,0x0060 }, /* clear bit 0 value */ { 0x3ff3,0x0061 }, /* set bit 0 value */ { 0x3ff3,0x0062 }, /* clear bit 1 value */ { 0x3ff3,0x0063 }, /* set bit 1 value */ { 0x3ff8,0x0070 }, /* final mask/value in sequence */
The BITWISE state address’s are at $0060-$0063. Times two and in OCTAL is address 0o100300 to 0o100306. The slapstic.log file shows it changing to BITWISE state at these locations. Something is triggering the BITWISE state.
100272: 17C0 0D20 MOV $0D20,R0 100276: 0BDF 8000 TST $8000 100302: 0BF0 8000 TST -$8000(R0) 100306: 0087 RTS
This is the code for resetting the bank to the current work value.
Either intentionally or by happy accident the Paperboy code is triggering the BITWISE state as well as setting the bank using the simple mode of the slapstic.
We know that to set the bank in simple mode we need the two TST instructions but we need them at another address so the BITWISE state doesn’t get triggered. A JMP to the necessary code elsewhere in the ROM would work, just like adding the extra ASR, but haven’t we seen this code before?
100260: 0BDF 8000 TST $8000 100264: 0BF0 8000 TST -$8000(R0) 100270: 0087 RTS
A few lines above is the end of the routine which sets the bank to 0. Changing the first TST $8000 at 0o100276 to a JMP $80B0 is all that is necesary to avoid the BITWISE trigger.
100276: 005F 80B0 JMP $80B0
cpu_l07 Offset $005F $5F $B0 cpu_n07 Offset $005F $00 $80
Testing in MAME shows we have a winner.
This all has been just done with MAME, but now it needs testing on a real PCB.
So I pull out my System 2 pcb…. and its a Championship Sprint, not a Super Sprint.. Argh.
Now I need to do the same process to Championship Sprint. Looking at this initially, it looks like I won’t hit the BITWISE state issue with the Championship Sprint slapstic.
Edit: Champion Sprint is the same code effectively except it uses offsets of $10,$14,$18,$1c instead of $50,$54,$59,$5c. The extra ASR shift is needed but instead of BIS #$0050, we use BIS #$0010.
Testing on real hardware will have to wait until the next post..