Hacking Farfalla for Free Play

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.

ROM Layout (2 x 2764)

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
0x0000 0x07FF ROM1 Bank 1
0x0800 0x0FFF ROM2 Bank 1
0x1000 0x17FF ROM2 Bank 2
0x1800 0x1FFF RAM (Mine)
0x2000 0x27FF ROM1 Bank 2
0x2800 0x2FFF ROM2 Bank 3
0x3000 0x37FF ROM2 Bank 4
0x3800 0x3FFF RAM (Theirs)
0x4000 0x47FF ROM1 Bank 3
0x6000 0x67FF ROM1 Bank 4

Memory Map / Layout (2 x 2764)

ROM 1
Bank 1
ROM 2
Bank 1
ROM 2
Bank 2
RAM
(Mine)
ROM 1
Bank 2
ROM 2
Bank 3
ROM 2
Bank 4
RAM
(Theirs)
ROM 1
Bank 3
Hole ROM 1
Bank 4
Hole
0x0000
0x07FF
0x0800
0x0FFF
0x1000
0x17FF
0x1800
0x1FFF
0x2000
0x27FF
0x2800
0x2FFF
0x3000
0x37FF
0x3800
0x3FFF
0x4000
0x47FF
0x4800
0x4FFF
0x6000
0x67FF
0x6800
0x6FFF

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.

Building Memory Image for Disassembly

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

Disassembly

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.


dasmx Farfalla.bin

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.

Interesting RAM locations

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.

To Do List

  1. Find code that decrements Credits remaining – disable it
  2. Find code that decides if Credits >= 0 – disable it
  3. Find code that tests if Coin Meter is connected – disable it
  4. Find space in ROM image for additional (new) code
  5. Write code changes for Free Play ROM

1. Disable Credit Decrement

Assumptions:

  1. Code must read current credits from RAM.
  2. Code must decrement credits, then store the result back in RAM.

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.

Success!

2. Disable Credit Check

Assumptions:

  1. Code must read current credits from RAM.
  2. Code must check to see if current credits == 0, then do something appropriate.

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 [14] 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.

Success!

3. Disable Coin Meter Checking

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 1Pink / WhiteCN10 (3)Serial I/O TX-
Pin 2Purple / YellowCN10 (18)Switch Matrix Column 7
Pin 3Grey / YellowCN10 (2)Serial I/O RX-
Pin 6Black / PinkCN10 (4)Serial I/O TX+
Pin 7Orange / YellowCN10 (1)Serial I/O RX+

If setting 30 is not 0x00, then the game is disabled:

  1. Coin mechs are disabled by the coin door solenoid.
  2. Coin switches are disabled (software) and ignored.
  3. Credit (Start button) switch is disabled (software) and ignored, even if there are credits available.

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.

Success!

4. Finding Free Space in ROM Images for New Code

Farfalla has three holes that may be useful.

0x36A0ROM2 Bank 4352 bytes
0x47E0ROM1 Bank 344 bytes
0x6000ROM1 Bank 42048 bytes

Keeping all changes in the ROM1 image may be desirable. That way, ROM2 remains unchanged.

5. Putting it all together

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'

...and...


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

6. Testing

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.

In PinMAME...


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

Copyright © 2011. All rights reserved.

This document may be freely distributed so long as the content is not modified.

Last updated 13 February 2011