Apple IIe MouseCard/screen emulation is wrong
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!
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
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: @.***>
If it does that, then it should just work in MAME.
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:
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.
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!
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.
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.
Oh, and CB2 on the PIA is not connected, as per the schematic on A2DP.
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.
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!
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.
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.
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.
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.
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
}
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
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?
Would help to actually link to the schematic: https://archive.org/details/APPLE_050-0101-A-1of1
8ab7404eac41f92bbf5ca3700ab4043cf3021a24 may help with timing issues.
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... :-)
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
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.
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
So SYNCLATCH is just a passthrough of the A2 bus D0 line and 2MHZCLOCK is Q3 inverted?
AppleWin doesn't do anything special with SYNCLATCH that I can see, it should always return 0 there. This is very confusing.
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.
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.
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
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.
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.