mame icon indicating copy to clipboard operation
mame copied to clipboard

Apple IIe MouseCard/screen emulation is wrong

Open colinleroy opened this issue 9 months ago • 47 comments

The Apple MouseCard is supposed to fire interrupts synced on VBL (cf https://mirrors.apple2.org.za/Apple%20II%20Documentation%20Project/Interface%20Cards/Digitizers/Apple%20Mouse%20Interface%20Card/Documentation/Apple%20II%20Mouse%20Technical%20Notes.pdf for example).

However, doing so flickers a lot when using the mouse interrupt as VBL detection, even when it should not: https://www.youtube.com/watch?v=NJsZvI407XI

On the contrary, emulating a IIc or IIgs never flickers, even when the drawing is too slow to stay ahead of the beam.

None of those situations are ideal and make debugging more complicated than it should be. I workaround the issue by using the $C019 softswitch on IIe, but that's not great.

I would love to help fix it, but have so far no idea where to look for what to fix. Thanks!

colinleroy avatar Apr 16 '25 21:04 colinleroy

Hi Colin,

The Technical Notes mention that the mouse card can generate either 50 or 60 Hz interrupts via a setting, but they don't say the interrupt is synced to the actual vertical blank anywhere that I'm seeing. I'm not even sure how that would work, as there are no video-related signals on slot 4 where the mouse typically was installed.

I guess it's possible that the 6502 firmware polls $C019 on a IIe and tries to sync it that way, but first we need to determine if the interrupts are really synced on a IIe (or II Plus, for that matter).

-RB

rb6502 avatar Apr 17 '25 01:04 rb6502

The card polls $C019 on the IIe, or vapor-lock on the II+, as a best-effort starting point. It really is trying to sync.

On Wed, 16 Apr 2025 at 18:05, R. Belmont @.***> wrote:

Hi Colin,

The Technical Notes mention that the mouse card can generate either 50 or 60 Hz interrupts via a setting, but they don't say the interrupt is synced to the actual vertical blank anywhere that I'm seeing. I'm not even sure how that would work, as there are no video-related signals on slot 4 where the mouse typically was installed.

I guess it's possible that the 6502 firmware polls $C019 on a IIe and tries to sync it that way, but first we need to determine if the interrupts are really synced on a IIe (or II Plus, for that matter).

-RB

— Reply to this email directly, view it on GitHub https://github.com/mamedev/mame/issues/13598#issuecomment-2811379259, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABWSFM3ZVQWBHLGRCEGA6C32Z344BAVCNFSM6AAAAAB3JLL3TOVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDQMJRGM3TSMRVHE . You are receiving this because you are subscribed to this thread.Message ID: @.***> rb6502 left a comment (mamedev/mame#13598) https://github.com/mamedev/mame/issues/13598#issuecomment-2811379259

Hi Colin,

The Technical Notes mention that the mouse card can generate either 50 or 60 Hz interrupts via a setting, but they don't say the interrupt is synced to the actual vertical blank anywhere that I'm seeing. I'm not even sure how that would work, as there are no video-related signals on slot 4 where the mouse typically was installed.

I guess it's possible that the 6502 firmware polls $C019 on a IIe and tries to sync it that way, but first we need to determine if the interrupts are really synced on a IIe (or II Plus, for that matter).

-RB

— Reply to this email directly, view it on GitHub https://github.com/mamedev/mame/issues/13598#issuecomment-2811379259, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABWSFM3ZVQWBHLGRCEGA6C32Z344BAVCNFSM6AAAAAB3JLL3TOVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDQMJRGM3TSMRVHE . You are receiving this because you are subscribed to this thread.Message ID: @.***>

peterferrie avatar Apr 17 '25 01:04 peterferrie

If it does that, then it should just work in MAME.

rb6502 avatar Apr 17 '25 02:04 rb6502

If it does that, then it should just work in MAME.

And yet, it doesn't.

I have questioned my sanity over that, and I have questioned Apple engineer's sanity. I made schematics with theories:

Image

I have sent the binary that you saw running under MAME in the video linked in the issue and I got, so far, two different persons telling me "I do not see any flickering and yes it drawn clearly.", "I don't see any flickering at 60Hz, also the puck moves SO SMOOTHLY".

Which is why I think something is wrong with MAME, and would love some help figuring it out. I have a theory but I don't really want to go on a multiple-evenings-long sidequest blindly, one of them is this comment in apple2e.cpp:

m_screen->set_raw(1021800*14, (65*7)*2, 0, (40*7)*2, 262, 0, 192);

I also see nothing related to the screen in src/devices/bus/a2bus/mouse.cpp.

This does not seem to take into account the stretched cycle. Could it be a lead?

Thanks in advance.

colinleroy avatar Apr 17 '25 05:04 colinleroy

Maybe another lead, MAME outputs warnings from the a2bus/mouse PIA:

[:sl4:mouse:a2mse_pia] Warning! No port CB2 write handler. Previous value has been lost!

colinleroy avatar Apr 17 '25 06:04 colinleroy

Finally, but this proves about nothing as these are also not the real hardware, AppleWin and Virtual][ don't show this behaviour and my code does not flicker when ran by thoses emulators.

colinleroy avatar Apr 17 '25 06:04 colinleroy

We don't emulate the stretched cycle, but we don't emulate it for either the CPU or the video generation so vapor lock and CPU/video cycle sync work.

AppleWin and V][ make no attempts to emulate the actual mouse card hardware so that's not useful data, as you surmised.

rb6502 avatar Apr 17 '25 11:04 rb6502

Oh, and CB2 on the PIA is not connected, as per the schematic on A2DP.

rb6502 avatar Apr 17 '25 12:04 rb6502

Try setting the 68705 clock to 1021800 in mouse.cpp. That seems to make the cursor smoother in Apple II Desktop, and the schematic shows both the microcontroller and the PIA are connected to the slot's phase 0 so I'm not sure why it was double that before.

rb6502 avatar Apr 17 '25 12:04 rb6502

Thanks. I tried that, and it doesn't really help. I've patched MAME to verbosely log the RDVBL reads, the PIA, and the MCU, and... I don't really know what to do with it :/

Does this log give you any idea ? Right after the "RD VBLBAR 80" is when, I suppose, the firmware should tell the PIA that should tell the MCU to reset its timer?

Thanks in advance!

log.txt

pia_mcu_verbose.patch.txt

colinleroy avatar Apr 17 '25 15:04 colinleroy

Adding a bit more log in m6805_timer::update, I see no timer reset either at the end of the VBL polling. I suppose that's at least part of the problem ?

...
RD VBLBAR 80
RD VBLBAR 80
[:sl4:mouse:a2mse_mcu] PORTC is unset
[:sl4:mouse:a2mse_mcu] m_tdr 216, m_source 0, prescale 10, m_divisor 0, decrements 10, interrupt 0 <=
RD VBLBAR 80
RD VBLBAR 80
RD VBLBAR 80
RD VBLBAR 80
RD VBLBAR 80
[:sl4:mouse:a2mse_mcu] PORTC is unset
[:sl4:mouse:a2mse_mcu] m_tdr 206, m_source 0, prescale 10, m_divisor 0, decrements 10, interrupt 0 <=
RD VBLBAR 80
RD VBLBAR 80
RD VBLBAR 80
RD VBLBAR 00
[:sl4:mouse:a2mse_mcu] PORTC is unset
[:sl4:mouse:a2mse_mcu] m_tdr 196, m_source 0, prescale 10, m_divisor 0, decrements 10, interrupt 0 <=
[:sl4:mouse:a2mse_pia] PIA port B read = 04
[:sl4:mouse:a2mse_pia] PIA port B read = 04
[:sl4:mouse:a2mse_pia] PIA port B write = 0C DDRB=3e
[:sl4:mouse:a2mse_mcu] PORTC is unset
[:sl4:mouse:a2mse_mcu] m_tdr 186, m_source 0, prescale 10, m_divisor 0, decrements 10, interrupt 0 <=
[:sl4:mouse:a2mse_pia] PIA port B read = 0C
[:sl4:mouse:a2mse_pia] PIA control A write = 00
[:sl4:mouse:a2mse_pia]  - CA1 interrupts disabled
[:sl4:mouse:a2mse_pia]  - CA1 interrupts active on high-to-low transition
[:sl4:mouse:a2mse_pia]  - Port A DDR register selected
[:sl4:mouse:a2mse_pia] PIA DDR A read = 00
[:sl4:mouse:a2mse_pia] PIA DDR A write = FF (output mode)
[:sl4:mouse:a2mse_pia] PIA port A write due to DDR change = 50 DDRA=ff
[:sl4:mouse:a2mse_pia] PIA control A write = 04
[:sl4:mouse:a2mse_pia]  - CA1 interrupts disabled
[:sl4:mouse:a2mse_pia]  - CA1 interrupts active on high-to-low transition
[:sl4:mouse:a2mse_pia]  - Port A Data register selected
[:sl4:mouse:a2mse_mcu] PORTC is unset
[:sl4:mouse:a2mse_mcu] m_tdr 176, m_source 0, prescale 10, m_divisor 0, decrements 10, interrupt 0 <=
[:sl4:mouse:a2mse_pia] PIA port A read = 50

Edit: Sorry, there's one just ten lines after, it goes back to 255.

colinleroy avatar Apr 17 '25 16:04 colinleroy

I'm going to have to read up on how the timer works on the 68705, but it probably auto-repeats if you don't do anything.

rb6502 avatar Apr 18 '25 18:04 rb6502

The 68705 timer is working. We’ve got lots of nasty test cases for it. The Zorba keyboard and Xain’d Sleena MCU were particularly nasty to get right. It isn’t free-running like the 68HC05 timers.

cuavas avatar Apr 18 '25 18:04 cuavas

Edit: Sorry, there's one just ten lines after, it goes back to 255.

That’s correct, it triggers an interrupt when it passes zero and continues to count down, wrapping around to 255. This allows the software to determine how long it was from when the timer triggered to when it the interrupt is serviced. This is important for things that use it to generate asynchronous serial timings (like the Zorba keyboard does), etc.

cuavas avatar Apr 18 '25 18:04 cuavas

OK, after looking at the schematics, I noticed the 6821’s CB1 is tied high, but we haven’t hooked this up in MAME. It’s worth giving that a shot just to eliminate it as a possibility. Try setting it in device_start:

void a2bus_mouse_device::device_start()
{
	// register save state variables
	save_item(NAME(m_port_a_in));
	save_item(NAME(m_port_b_in));
	save_item(NAME(m_last));
	save_item(NAME(m_count));

	m_pia->cb1_w(1); // tied high via 10k resistor
}

cuavas avatar Apr 18 '25 18:04 cuavas

Hi! Thanks for looking into it :) It seems to be better with cb1 tied high, but it's not stable, though: https://youtu.be/TbKvR71oMg0

colinleroy avatar Apr 18 '25 20:04 colinleroy

Try setting the 68705 clock to 1021800 in mouse.cpp. That seems to make the cursor smoother in Apple II Desktop, and the schematic shows both the microcontroller and the PIA are connected to the slot's phase 0 so I'm not sure why it was double that before.

Schematic here shows the MCU being driven from the Q3 2MHz asymmetrical clock (card edge pin 37) via the 16R4 PAL. Is the firmware we have from a different card?

cuavas avatar Apr 19 '25 18:04 cuavas

Would help to actually link to the schematic: https://archive.org/details/APPLE_050-0101-A-1of1

cuavas avatar Apr 19 '25 18:04 cuavas

8ab7404eac41f92bbf5ca3700ab4043cf3021a24 may help with timing issues.

cuavas avatar Apr 19 '25 19:04 cuavas

Hi Vas,

Thanks for looking into this. I've cherry-picked the commit, but it doesn't help: https://www.youtube.com/watch?v=GTbesntgicQ

If it may help to test, my code is now released at https://www.colino.net/wordpress/wp-content/uploads/shufflepuck-beta-1-960a89c4.po

Is the firmware we have from a different card?

That would explain things... :-)

colinleroy avatar Apr 19 '25 20:04 colinleroy

Very naive question, is the 6821 PIA and the 6520 the same thing? (You can tell I'm out of my depth here) https://www.applefritter.com/content/apple-mouse-card-pia-chip

colinleroy avatar Apr 19 '25 20:04 colinleroy

The 6821 is supposed to be 100% compatible from a software point of view. The main difference is that it’s manufactured using a newer semiconductor process. The electrical characteristics are a bit different, but it shouldn’t matter for emulation purposes.

cuavas avatar Apr 19 '25 21:04 cuavas

I did a decode of the PAL and see the SYNCLATCH is an output that goes to the PIA PB0, maybe its looking for this somehow to sync up?

Equations:

/o12(LS245) = /i8(DEVSEL) + /i9(IOSEL) o12.oe = vcc

these appear not connected to anything

/o13 = /i6 & i7 +
     i6 & /i7
o13.oe = vcc

/rf14 := i6
rf14.oe = OE

/rf15 :=
rf15.oe = OE

/rf16 :=
rf16.oe = OE

/rf17(SYNCLATCH) := i4(D0) rf17.oe = OE (OE tied to GND)

/o18(2MHZCLOCK) = /i3(Q3) o18.oe = vcc

/o19(feedback to pin 1 clock input for rf17) = i2(IOSTR) + i3(Q3) o19.oe = vcc

robjustice avatar Apr 25 '25 22:04 robjustice

So SYNCLATCH is just a passthrough of the A2 bus D0 line and 2MHZCLOCK is Q3 inverted?

rb6502 avatar Apr 26 '25 15:04 rb6502

AppleWin doesn't do anything special with SYNCLATCH that I can see, it should always return 0 there. This is very confusing.

rb6502 avatar Apr 26 '25 18:04 rb6502

My understanding of the logic is that it will clock the value of D0 into SYNCLATCH only when IOSTR is low. When IOSTR is high, the clock input will be stuck at high regardless of the Q3 signal, so effectively disabling the clocking for SYNCLATCH. What its doing this for seems a mystery still.

robjustice avatar Apr 28 '25 09:04 robjustice

I'm absolutely out of my depth here. A thing that I have noticed is that if I let time pass, the flickering changes place while my sprites don't: https://www.youtube.com/watch?v=cBB4_vKQtCA (that code's image at https://colino.net/tmp/shflpuck-mame-test.po if needed).

That makes me think that there might be a very small difference in the VBL frequency versus the mouseCard's IRQ frequency?

Another video showing the flickering on the Cafe screen, at 0:54, 2:05 and 3:14. https://www.youtube.com/watch?v=ADV8-4yEa50

The pointer is a 14x11, 33 bytes sprite that can't ever be out of budget to draw. It takes about 888 cycles for the IRQ to be processed, 1034 cycles to clear the pointer, 1667 to draw it, so it's supposed to be done 1ms before the beam is even back at the top.

colinleroy avatar Apr 28 '25 13:04 colinleroy

I think your right Colin, it seems to creep time between the VBL and the 6805 generated interrupt.

I tried to work this out a bit, please correct me if i'm wrong here:

65 cycles per line * 262 lines per frame = 17030 A2 cycles per frame 6805 is running at 2M/4, so half of this number of cycles needed to be tracked per frame 17030/2 = 8515 if we divide by 256 to get the number of full timer runs in the 6805 (looks like no prescaler set in 6805 timer) = 33x256 + 67 cycles remainder

Looking into the 6805: These are set in the 6805 init code, i suspect the 50hz mode updates some different values $50 is initialised to $C7 from ROM location $6C9 $51 is initialised to $22 from ROM location $6C7

The $22 value lines up with the number of full counter runs. For the remainder, this subtracts $c7 from the counter, 256 - 199 = 57

I think the other cycles (and maybe i'm one out) are the cycles from the suba (4) & sta (5) below. I'm wondering if there is something different happening with the handling of this part?

From 6805 code:

67D  bclr  7, TCR                                        1F 09
67F  dec   $4F   ;dec number of full counter runs        3A 4F
681  bne   $6BF  ;just rti if not done                   26 3C
683  lda   TDR   ;must be the last one                   B6 08
685  suba  $50   ;adjust the remainder count             B0 50
687  sta   TDR   ;and update counter value for last run  B7 08
689  lda   $4F   ;re init 4F                             B6 4F
68B  sbca  $51                                           B2 51
68D  inca                                                4C
68E  sta   $4F                                           B7 4F
690  dec   $56                                           3A 56
692  bne   $6BF                                          26 2B
694  lda   $57                                           B6 57
696  sta   $56                                           B7 56
698  lda   PORTB                                         B6 01
69A  bmi   $6A0                                          2B 04
69C  lda   #$0C                                          A6 0C
69E  bra   $6A4                                          20 04
6A0  lda   #$08                                          A6 08
6A2  bra   $6A4                                          20 00
6A4  ora   $5B                                           BA 5B
6A6  brset 0, $58, $6AD                                  00 58 04
6A9  anda  #$08                                          A4 08
6AB  bra   $6B1                                          20 04
6AD  anda  #$0E                                          A4 0E
6AF  bra   $6B1                                          20 00
6B1  anda  $58                                           B4 58
6B3  beq   $6B7                                          27 02
6B5  bclr  6, PORTB                                      1D 01
6B7  ora   $5C                                           BA 5C
6B9  sta   $5C                                           B7 5C
6BB  lda   #$00                                          A6 00
6BD  sta   $5B                                           B7 5B
6BF  rti                                                 80

robjustice avatar May 03 '25 23:05 robjustice

That's interesting. the 6805 triggers the interrupt at $6B5, bclr 6, PORTB ? I'm having a hard time understanding what this does, but maybe those 10 cycles are the ones between $6A6 and $6B5, which seem to be built to run for a constant 10 cycles.

colinleroy avatar May 04 '25 13:05 colinleroy

I'm not to sure what all that is doing as well.

I tracked the timer values through that part that adjusts the last counter run and think its 2 cycles out. If you start in debug mode, you can update that location $50 in the 6805 just after its init code and set it to $C5, then let the game boot. It then seems to re init this value back to $C7 again, so further update to $C5 and it all looks flicker free for me then.

As to why its two cycles out, that needs someone with more understanding of this to work it out.

robjustice avatar May 05 '25 21:05 robjustice