Here are some of my notes from adding a "Free Play" option to Farfalla. The other Zaccaria 2nd Generation games should be similar, though since I haven't actually done them yet, we'll see how true that statement turns out to be.
My original concept was to take an otherwise un-used setting option (#30) and convert it to my own use to enable or disable a "free play" option for Farfalla. Setting 30 normally controls whether or not the game expects to see that a coin meter is connected, and will disable the game if the coin meter is not detected. Since nobody I know has ever seen one of these coin meters, this seemed like a setting nobody is likely to use, making it perfect for my purposes. "Free Play", here, means that the game will allow you to start a new game, even when Credits is 0. I still want it to correctly add and subtract credits normally.
The first step is figuring out where everyhing is. Here, I can cheat a bit. Having already written a comprehensive Test ROM for the 2nd Generation games, I know where everything is.
The 2650 processor has a 15 bit address space, arranged in to four banks. The game ROMs for Farfalla are broken up in to 0x0800 byte banks.
This is derived straight from the CPU board schematics. Working out each possible address, and where it leads. Relatively easy to do, but tedious.
|Start Address||End Address||Device||Bank|
The addresses 0x1800 0x1C00 0x3800 and 0x3C00 are all equivalent. In the hardware, they all end up at the same location (RAM). I'm used to using 0x1800 for the base of RAM. Farfalla uses 0x3800 for the base of RAM.
I used the Linux 'dd' command to break up the ROM images, and to create some blank spaces. Then reassembled the image parts in to a memory image.
dd bs=2048 count=1 if=cpurom1.bin of=Rom1Bank1 skip=0 dd bs=2048 count=1 if=cpurom1.bin of=Rom1Bank2 skip=1 dd bs=2048 count=1 if=cpurom1.bin of=Rom1Bank3 skip=2 dd bs=2048 count=1 if=cpurom1.bin of=Rom1Bank4 skip=3 dd bs=2048 count=1 if=cpurom2.bin of=Rom2Bank1 skip=0 dd bs=2048 count=1 if=cpurom2.bin of=Rom2Bank2 skip=1 dd bs=2048 count=1 if=cpurom2.bin of=Rom2Bank3 skip=2 dd bs=2048 count=1 if=cpurom2.bin of=Rom2Bank4 skip=3 dd bs=2048 count=1 if=/dev/zero of=RAM skip=0 dd bs=2048 count=1 if=/dev/zero of=RAM skip=0 dd bs=2048 count=3 if=/dev/zero of=Hole1 skip=0 dd bs=2048 count=1 if=/dev/zero of=Hole2 skip=0 cat ROM1Bank1 ROM2Bank1 ROM2Bank2 RAM ROM1Bank2 ROM2Bank3 ROM2Bank4 RAM ROM1Bank3 Hole1 ROM1Bank4 Hole2 > Farfalla.bin
Once the memory image binary has been produced from the ROM images, create a "symbols" file (Farfalla.sym) like:
cpu 2650 numformat C org 0x0000 code 0x0000 CODESTART vector 0x003f ISRV
This tells the disassembler what processor (2650), the format for numbers ("C" formated, ie: 0x1F), where to find known things like the start of code and the ISR vector.
Further symbols file information may help in producing a fully understandable source listing, but this should be enough for the first pass and investigation.
This will produce Farfalla.lst (source listing) using the Farfalla.sym symbols file.
dasmx -a Farfalla.bin
This will produce Farfalla.asm (source listing) using the Farfalla.sym symbols file.
Finding the address in RAM where Credits are stored
RAM Layout: 0x1800 – 0x1BFF IC23 0x1800 – 0x18FF IC4 / IC5 0x1800 – 0x1BFF Displays 0x18C0 – 0x18E0 Solenoids 0x1840 – 0x1857 Lamps 0x1870 – 0x18BF
These were worked out in my Test ROM development, so I know them to be true.
Settings and audits must be in 0x1900 – 0x1BFF range
Data stored in low-order bits (5514 @ IC4) for battery backup to work
Data in high-order bits (2214 @ IC5) is transient with power
Credits are stored @ 0x1F22 in RAM (0x18)
Credits are also stored bit-flipped @ 0x1F4D (0xE7)
Setting 30 is stored @ 0x1F98 in RAM (0x00)
Setting 30 is also stored bit-flipped @ 0x1FC8 (0xFF)
Credits and Setting 30 were found using PinMAME and watching the RAM values change as credits were added and settings changed.
Storing settings twice, one regular and one bit-flipped, may be a form of error detection for RAM. This may be how the game knows whether or not to use the data in RAM for settings or to fall back to the DIP switch defaults.
These seemed like reasonable assumptions to begin the project with to me. And since I know where in RAM the Credits value is stored, finding where it is changed shouldn't be too hard.
Looking through the disassembled source listing, I found this subroutine
2581 L2581: 2581 : CC 1F 22 stra,r0 X3F22 ; Credits location in RAM 2584 : C1 strz r1 2585 : 25 FF eori,r1 H'FF' ; Bit-flip Credits value 2587 X2587: ; used for error checking? 2587 : CD 1F 4D stra,r1 X3F4D ; Secondary Credits location 258A : 44 F0 andi,r0 H'F0' ; Strip high-order bits 258C : 50 rrr,r0 ; Shift low-order nybble to high 258D : 50 rrr,r0 258E : 50 rrr,r0 258F : 50 rrr,r0 2590 : CC 1F 23 stra,r0 X3F23 ;Second nybble credits location? 2593 : 17 retc,un
That looks promising!
I then found these two blocks of code that call (BSTA - Branch Subroutine Condtion True Absolute) this subroutine.
278D L278D: 278D : 0D 1D C4 loda,r1 X3DC4 2790 : 98 08 bcfr,eq L279A 2792 : 0C 1F 22 loda,r0 X3F22 ; Credits location in RAM 2795 : A4 01 subi,r0 H'01' ; Decrement 2797 : 3F 25 81 bsta,un L2581 ; Store updated Credits
That looks a lot like a block of code to get the current Credits value, subtract one from it, and store it again.
259B L259B: 259B : 0C 1F 22 loda,r0 X3F22 ; Credits location in RAM 259E : 84 01 addi,r0 H'01' ; Increment 25A0 : 3B 5F bstr,un L2581 ; Store updated Credits
And that looks a lot like a block of code to get the current Credits value, add one to it, and store it again.
Looks like simply patching out the decrement at 0x2795 should provide for “free play” because Credits will never decrement.
Patch [A4 01] to [C0 C0] (0x0F95 in ROM1 image)
This changes the Subtract Imediate , Register 0, 0x01 to two NOPs.
And, this worked. The game could still add credits, but it no longer subtracted them.
This source block looks relevant to the problem:
273F L273F: 273F : 0D 5D AE loda,r0 X3DAE,r1,- 2742 : 16 retc,lt 2743 : 59 7A brnr,r1 L273F 2745 : 17 retc,un 2746 : 20 eorz r0 2747 : CC 1D C4 stra,r0 X3DC4 274A : 0C 1D C1 loda,r0 X3DC1 274D : 15 retc,gt 274E : 0C 1D C6 loda,r0 X3DC6 2751 : 6C 1D CB iora,r0 X3DCB 2754 : 16 retc,lt 2755 : 0C 1F 22 loda,r0 X3F22 ; Credits loaded sets CC 2758 : 14 retc,eq ; Return if CC == 0 2759 L2759: 2759 : 0C 1D 00 loda,r0 X3D00 275C : 18 16 bctr,eq L2774 275E L275E: 275E : 3F 22 1A bsta,un L221A 2761 : 05 FF lodi,r1 H'FF'
It looks like the code at 0x2755 is the one I'm looking for. The loda instruction sets the Condition Code to 0 if the loaded data from RAM is 0. Then the retc,eq returns if CC is 0.
Looks like simply patching out the return at 0x2758 should provide for “free play” by allowing the game to start when Credits == 0.
Patch  to [C0] (0x0F58 in ROM1 image)
And, one NOP later, this worked too. The game no longer knows if credits is 0 or not and will always start. This works ok for the moment, as credits can only ever increase with the previous patch installed. But it'll get funny if credits is allowed to decrement.
The first task here is to find out how the game detects a coin meter being installed in the first place. Not having a coin meter, or any schematics for one, or any other information, I had to make a guess. The schematics show an optional connector. In some versions, especially older games, this is labled as being for a "printer" option. In some it seems to indicate that the coin meter may connect here.
Mystery connector in the backbox is probably involved in this. A Coin Meter would have to be connected somewhere, and this seems a likely place to connect it. Could also be used for the Printer connection.
8-pin (2 rows, 4 pins each) connector. AMP Modu2.
|Pink / White||Purple / Yellow||Grey / Yellow||N/C|
|N/C||Black / Pink||Orange / Yellow||N/C|
From the schematics, this is wired so that:
|Pin 1||Pink / White||CN10 (3)||Serial I/O TX-|
|Pin 2||Purple / Yellow||CN10 (18)||Switch Matrix Column 7|
|Pin 3||Grey / Yellow||CN10 (2)||Serial I/O RX-|
|Pin 6||Black / Pink||CN10 (4)||Serial I/O TX+|
|Pin 7||Orange / Yellow||CN10 (1)||Serial I/O RX+|
If setting 30 is not 0x00, then the game is disabled:
Using a 10K resistor, connect Pin 6 to Pin 7.
This pulls the SENSE line low at 2650A.
This seems to be all that is being used to detect if the Coin Meter is connected. With the resistor in place, the game goes back to normal operation with Setting 30 set to 1.
From my Test ROM development, I know that the SENSE and FLAG bits are in the Program Status Word (Upper).
Code must read the Program Status Word (Upper) to get the SENSE bit status.
In my Test ROM code, I'm using tpsu 0x80 to test the SENSE bit status and set the Condition Code. Looking through the source for code accessing the PSU, I found:
237A L237A: 237A : 07 00 lodi,r3 H'00' ; Return code (OK) 237C : 05 18 lodi,r1 H'18' 237E : 3F 25 2C bsta,un L252C 2381 : 18 0D bctr,eq L2390 2383 : 12 spsu ; Store PSU Upper to R0 2384 : 44 C0 andi,r0 H'C0' ; Strips everything but F and S 2386 : E4 80 comi,r0 H'80' ; Is SENSE High, FLAG Low? 2388 : 18 04 bctr,eq L238E ; Yes... 238A : E4 40 comi,r0 H'40' ; Is SENSE Low, FLAG High? 238C : 98 02 bcfr,eq L2390 ; Yes... 238E L238E: 238E : 07 FF lodi,r3 H'FF' ; Return code (error) 2390 L2390: 2390 : CF 1D C6 stra,r3 X3DC6 2393 : 17 retc,un ; Subroutine return
This seems likely to be what I'm looking for. I guessed that 0x00 is a "success" return, and 0xff is an "error" return. No special reason, really, but I've been a C programmer for 30 years, and 0x00 to me means "success". Looking at the logic, that seemed to be correct.
If this subroutine always return 0x00, then SENSE detection (Coin Meter detection) should be defeated. Patch at 0x238E to return 0x00 instead of 0xFF.
Patch [07 FF] to [07 00] (0x0B8E in ROM1 image)
And this worked too. The game no longer knows if a coin meter is installed, and will work normally whether setting 30 is 0 or 1.
Farfalla has three holes that may be useful.
|0x36A0||ROM2 Bank 4||352 bytes|
|0x47E0||ROM1 Bank 3||44 bytes|
|0x6000||ROM1 Bank 4||2048 bytes|
Keeping all changes in the ROM1 image may be desirable. That way, ROM2 remains unchanged.
I wrote two new blocks of code to implement the features I wanted here. The first disables the Credits=0 check if Setting 30 is a value other than 0.
Patch1: .org #0x6000 loda r0,[P1Setting30] ; Get value of setting 30 andi r0,#0x0F ; Strip off high order bits comi r0,#0x00 ; Is Setting 30 =?= 0 bcta .eq.,Patch1Done ; Yes: continue with original code lodi r0,#0x01 ; No: disable credit check bctr .un.,[Patch1Return] ; Return from patch Patch1Done: loda r0,[P1Credits] ; Original code bctr .un.,[Patch1Return] Patch1Return: .dw #0x2758 ; Return from patch P1Credits: .dw #0x3f22 ; Credits in RAM P1Setting30: .dw #0x1f98 ; Setting 30 in RAM
The second disables credit decrement is Setting 30 is a value other than 0.
Patch2: .org #0x6040 loda r0,[P2Setting30] ; Get value of setting 30 andi r0,#0x0F ; Strip off high order bits comi r0,#0x00 ; Is Setting 30 =?= 0 bcta .eq.,Patch2Done ; Yes: continue with original code loda r0,[P2Credits] ; No: disable credit decrement below 0 bcta .gt.,Patch2Done ; Credit=0? – allow credit decrement bcta .un.,[Patch2Return] ; Return from patch Patch2Done: loda r0,[P2Credits] subi r0,#0x01 ; Decrement credits bcta .un.,[Patch2Return] ; Return from patch Patch2Return: .dw #0x2797 ; Return from patch P2Credits: .dw #0x3f22 ; Credits in RAM P2Setting30: .dw #0x1f98 ; Setting 30 in RAM
Then, the only trick is to insert two branches to the new code in to the previously identified code.
273F L273F: 273F : 0D 5D AE loda,r0 X3DAE,r1,- 2742 : 16 retc,lt 2743 : 59 7A brnr,r1 L273F 2745 : 17 retc,un 2746 : 20 eorz r0 2747 : CC 1D C4 stra,r0 X3DC4 274A : 0C 1D C1 loda,r0 X3DC1 274D : 15 retc,gt 274E : 0C 1D C6 loda,r0 X3DC6 2751 : 6C 1D CB iora,r0 X3DCB 2754 : 16 retc,lt 2755 : bcta un,Patch1 ; Patch1 @0x6000 (3 bytes) 2758 : retc,eq 2759 L2759: 2759 : 0C 1D 00 loda,r0 X3D00 275C : 18 16 bctr,eq L2774 275E L275E: 275E : 3F 22 1A bsta,un L221A 2761 : 05 FF lodi,r1 H'FF'
278D L278D: 278D : 0D 1D C4 loda,r1 X3DC4 2790 : 98 08 bcfr,eq L279A 2792 : bcta un,0x6040 ; Patch2 @0x6040 (3 bytes) 2795 : nop 2796 : nop 2797 : 3F 25 81 bsta,un L2581 ; Store updated Credits
Patch [07 FF] to [07 00] (0x0B8E in ROM1 image)
Patch [0C 1F 22] to [1F 60 00] (0x0F55 in ROM1 image)
Patch [0C 1F 22 A4 01] to [1F 60 40 C0 C0] (0x0F92 in ROM1 image)
Build the modified ROM1 image with the new code added where there was unused ROM space:
as2650 -l -o -s -p FreePlay.asm aslink -i FreePlay.rel ihex2bin -o FreePlay.bin FreePlay.ihx dd bs=2048 count=1 if=FreePlay.bin of=Rom1Bank4FP skip=12 cat Rom1Bank1 Rom1Bank2 Rom1Bank3 Rom1Bank4FP > cpurom1.bin
My initial testing was done in PinMAME. This is not a perfect environment, but it is a good way to initially test something without the time and effort of burning ROMs and swapping them in to a game.
Once that seemed to work, I burned a 2764 and popped in to my Farfalla ROM1 and confirmed that it seemed to work fine for me. I could add credits (coin switches, win free games, or match), I could remove credits by starting a game, and I could start a game whether credits was 0 or not. I could enable this, or disable it, with Setting #30.
I released this to a few people to beta test it. And, unexpectedly, I got a bug report from one. He said it worked fine, but that it would revert to "free play" when the game was power cycled.
I could not reproduce this in PinMAME, but I could reproduce it in my Farfalla. More interestingly, I could not reproduce it in my Farfalla when it was running a modified CPU board with a ZeroPower RAM installed, but I could with an original board.
That led me to the theory that the problem was that part of the RAM is battery backed, but part of it is not. In the ZeroPower RAM board, all of RAM is battery backed. In PinMAME there is no RAM, it's just a file on disk.
Data @0x3F98: 0x00 0000:0000 (credits = 0) Data @0x3FC8: 0xFF 1111:1111 (credits = 0 inverted)
6514 is the Low Order bits. 6514 is battery backed and maintains state through power cycles.
2114 is the High Order bits. 2114 is not battery backed, and essentially random on power up.
Twiddle @0x3F98 to: 0x20 0010:0000 Twiddle @0x3FC8 to: 0x4F 0100:1111
"Power on" in PinMAME - the values at 0x3F98 and 0x3FC8 are not corrected.
Use "Return" to get to Setting 30. As soon as Setting 30 comes up, the values in RAM are corrected to 0x00 and 0xFF.
So the bug is that I'm testing the High Order bits, which are unreliable after a power cycle. To fix, need to strip the High Order bits before testing.
loda r0,[P1Setting30] ; Get value of setting 30 andi r0,#0x0F ; Strip off high order bits <-- BUG FIX comi r0,#0x00 ; Is Setting 30 =?= 0
Code changed. Tested. Problem fixed.
David Gersic email@example.com
Copyright © 2011. All rights reserved.
This document may be freely distributed so long as the content is not modified.