New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MockingBoard 6522 timer #496

Closed
dschmenk opened this Issue Oct 19, 2017 · 18 comments

Comments

Projects
None yet
2 participants
@dschmenk

dschmenk commented Oct 19, 2017

In writing the MockingBoard sound driver for Lawless Legends, I've found that AppleWin's 6522 emulation fails to start Timer1 unless interrupts are enabled. I'm polling the IFR's Timer1 which fails to get set because of this code:

static void StartTimer(SY6522_AY8910* pMB)
{
//	if((pMB->nAY8910Number & 1) != SY6522_DEVICE_A)
//		return;

>> Why this?
>>
>>	if((pMB->sy6522.IER & IxR_TIMER1) == 0x00)
>>		return;
>>

	USHORT nPeriod = pMB->sy6522.TIMER1_LATCH.w;

//	if(nPeriod <= 0xff)		// Timer1L value has been written (but TIMER1H hasn't)
//		return;

	pMB->nTimerStatus = 1;

	// 6522 CLK runs at same speed as 6502 CLK
	g_n6522TimerPeriod = nPeriod;

	g_bMBTimerIrqActive = true;
	g_nMBTimerDevice = pMB->nAY8910Number;
}

Of course polling the IFR works fine on a real 6522.

@tomcw tomcw self-assigned this Oct 20, 2017

@tomcw

This comment has been minimized.

Show comment
Hide comment
@tomcw

tomcw Oct 20, 2017

Contributor

Hi - can you share the specific snippet of 6522 code for doing this polling?

I guess that this should work..?

LDA #0<<6	; t1 one-shot
STA $C40B	; ACR
LDA #0
STA $C404	; t1 counter.l
LDA #1
STA $C405	; t1 counter.h, clear IFR.t1 & start (countdown from 0x100)

LDX #0
loop:
  LDA $C40D	; IFR
  AND #1<<6	; t1?
  BNE t1_set
  INX
  BNE loop

error:
  BRK

t1_set:
  RTS
Contributor

tomcw commented Oct 20, 2017

Hi - can you share the specific snippet of 6522 code for doing this polling?

I guess that this should work..?

LDA #0<<6	; t1 one-shot
STA $C40B	; ACR
LDA #0
STA $C404	; t1 counter.l
LDA #1
STA $C405	; t1 counter.h, clear IFR.t1 & start (countdown from 0x100)

LDX #0
loop:
  LDA $C40D	; IFR
  AND #1<<6	; t1?
  BNE t1_set
  INX
  BNE loop

error:
  BRK

t1_set:
  RTS

tomcw added a commit that referenced this issue Oct 21, 2017

Support polling of Mockingboard's & Phasor's 6522 IFR.Timer1 (#496)
. Extended save-state (Mockingboard & Phasor: version 2) to support 6522 timer being active
@tomcw

This comment has been minimized.

Show comment
Hide comment
@tomcw
Contributor

tomcw commented Oct 21, 2017

@tomcw

This comment has been minimized.

Show comment
Hide comment
@tomcw

tomcw Oct 21, 2017

Contributor

Try this experimental build: v1.26.3.2.

Contributor

tomcw commented Oct 21, 2017

Try this experimental build: v1.26.3.2.

@dschmenk

This comment has been minimized.

Show comment
Hide comment
@dschmenk

dschmenk Oct 21, 2017

dschmenk commented Oct 21, 2017

@tomcw

This comment has been minimized.

Show comment
Hide comment
@tomcw

tomcw Oct 22, 2017

Contributor

Hi Dave,

Yes, the MB emulation is really only sufficient for what existing games, etc are doing. Currently the last 6522 timer1 that's setup is the only one that generates interrupts and sets the IFR.TIMER1 bit.

Other limitations:

  • 6522 timer2 doesn't work (no interrupt, no IFR.TIMER2 gets set on underflow)
  • 6522 timer1: only the last one setup from both MBs (in slot4 & slot5) will generate an interrupt and set the IFR.TIMER1 bit

I'll keep this issue open to fix things so that in non-interrupt mode, all timers can run simultaneously and set IFR.TIMERx on underflow.

Contributor

tomcw commented Oct 22, 2017

Hi Dave,

Yes, the MB emulation is really only sufficient for what existing games, etc are doing. Currently the last 6522 timer1 that's setup is the only one that generates interrupts and sets the IFR.TIMER1 bit.

Other limitations:

  • 6522 timer2 doesn't work (no interrupt, no IFR.TIMER2 gets set on underflow)
  • 6522 timer1: only the last one setup from both MBs (in slot4 & slot5) will generate an interrupt and set the IFR.TIMER1 bit

I'll keep this issue open to fix things so that in non-interrupt mode, all timers can run simultaneously and set IFR.TIMERx on underflow.

@dschmenk

This comment has been minimized.

Show comment
Hide comment
@dschmenk

dschmenk Oct 22, 2017

Hi Tom-

So I'm now getting the interrupt flag for Timer1, however the timing is off a great deal. I'm guessing the culprit might be:

double MB_GetFramePeriod()
{
	return (g_bMBTimerIrqActive||(g_MB[0].sy6522.IFR & IxR_TIMER1)) ? (double)g_n6522TimerPeriod : g_f6522TimerPeriod_NoIRQ;
}

would something like:

double MB_GetFramePeriod()
{
	return g_bMB_Active ? (double)g_n6522TimerPeriod : g_f6522TimerPeriod_NoIRQ;
}

be a better test for MB activity? I'm not positive this is the problem, but it is one of the few places where timing gets affected by whether IRQ is enabled or not.

Update: Altering the order of initialization helps somewhat. Still has different timing than an actual 6522, but getting better. BTW, I'm programming Timer1 to countdown 16 times/sec (T1L=$F9C2). That shouldn't put undo stress on the timing, but it is quite noticeable how different it is from a real 6522.

dschmenk commented Oct 22, 2017

Hi Tom-

So I'm now getting the interrupt flag for Timer1, however the timing is off a great deal. I'm guessing the culprit might be:

double MB_GetFramePeriod()
{
	return (g_bMBTimerIrqActive||(g_MB[0].sy6522.IFR & IxR_TIMER1)) ? (double)g_n6522TimerPeriod : g_f6522TimerPeriod_NoIRQ;
}

would something like:

double MB_GetFramePeriod()
{
	return g_bMB_Active ? (double)g_n6522TimerPeriod : g_f6522TimerPeriod_NoIRQ;
}

be a better test for MB activity? I'm not positive this is the problem, but it is one of the few places where timing gets affected by whether IRQ is enabled or not.

Update: Altering the order of initialization helps somewhat. Still has different timing than an actual 6522, but getting better. BTW, I'm programming Timer1 to countdown 16 times/sec (T1L=$F9C2). That shouldn't put undo stress on the timing, but it is quite noticeable how different it is from a real 6522.

@tomcw

This comment has been minimized.

Show comment
Hide comment
@tomcw

tomcw Oct 22, 2017

Contributor

Hi Dave,

IIRC, Music Construction Set (MCS) is the only Mockingboard playback routine not to use an interrupt.
I think MB_GetFramePeriod() is written to support MCS by using this constant value of g_f6522TimerPeriod_NoIRQ (= 1.023MHz / 60Hz).

I don't understand your comment: "timing is off a great deal", as the this should be happening:

  • At 60Hz, main loop calls MB_EndOfVideoFrame()
  • No timer IRQ being used, so call MB_Update(), which fills part of the sound ring-buffer
  • So roughly this update will occur at ~4x the rate of your Timer1 countdown

Hmmm... because this 1/60s update is not in sync with your 1/16s update of the Mockingboard registers, then this could explain when you say "timing is off a great deal" - there will be long and short sections of sound as the two 60Hz and 16Hz acoustically beat.

Can I just check that you are only using one of the 6522 timers? (This just helps me come up with a solution.)

If you only use one timer, then I can probably just mimic the way the timer IRQ works, ie. update the sound buffer when the timer counter underflows. This gives a fixed-period update to the sound buffer (with the Mockingboard registers from the prior update, 1/16s ago).

Contributor

tomcw commented Oct 22, 2017

Hi Dave,

IIRC, Music Construction Set (MCS) is the only Mockingboard playback routine not to use an interrupt.
I think MB_GetFramePeriod() is written to support MCS by using this constant value of g_f6522TimerPeriod_NoIRQ (= 1.023MHz / 60Hz).

I don't understand your comment: "timing is off a great deal", as the this should be happening:

  • At 60Hz, main loop calls MB_EndOfVideoFrame()
  • No timer IRQ being used, so call MB_Update(), which fills part of the sound ring-buffer
  • So roughly this update will occur at ~4x the rate of your Timer1 countdown

Hmmm... because this 1/60s update is not in sync with your 1/16s update of the Mockingboard registers, then this could explain when you say "timing is off a great deal" - there will be long and short sections of sound as the two 60Hz and 16Hz acoustically beat.

Can I just check that you are only using one of the 6522 timers? (This just helps me come up with a solution.)

If you only use one timer, then I can probably just mimic the way the timer IRQ works, ie. update the sound buffer when the timer counter underflows. This gives a fixed-period update to the sound buffer (with the Mockingboard registers from the prior update, 1/16s ago).

@tomcw

This comment has been minimized.

Show comment
Hide comment
@tomcw

tomcw Oct 22, 2017

Contributor

BTW. I checked MCS and that does use an interrupt.
So I think the non-interrupt support is just for playback of continuous tones, eg. poking the MB registers directly from the monitor!

Contributor

tomcw commented Oct 22, 2017

BTW. I checked MCS and that does use an interrupt.
So I think the non-interrupt support is just for playback of continuous tones, eg. poking the MB registers directly from the monitor!

@dschmenk

This comment has been minimized.

Show comment
Hide comment
@dschmenk

dschmenk Oct 22, 2017

Cursory review of the 6522 emulation looks pretty straightforward and not obvious what the discrepancy could be. I've put together my sample code and test disk image. I can also make a video of what it sounds like on real hardware, if that would be of any benefit. Its in PLASMA, but it really doesn't do anything quirky, except for emulating 9 voices per PSG by time slicing the note table every 16th of a second.

https://github.com/dschmenk/PLASMA/tree/master/src/mockingboard

Even the difference between the 60Hz emulation update and my 16 Hz timer count wouldn't account for the wacky sounds. I might try a test case of just timing a few seconds with the 6522 without playing anything to see if that makes a difference. Thanks again for all your help.

dschmenk commented Oct 22, 2017

Cursory review of the 6522 emulation looks pretty straightforward and not obvious what the discrepancy could be. I've put together my sample code and test disk image. I can also make a video of what it sounds like on real hardware, if that would be of any benefit. Its in PLASMA, but it really doesn't do anything quirky, except for emulating 9 voices per PSG by time slicing the note table every 16th of a second.

https://github.com/dschmenk/PLASMA/tree/master/src/mockingboard

Even the difference between the 60Hz emulation update and my 16 Hz timer count wouldn't account for the wacky sounds. I might try a test case of just timing a few seconds with the 6522 without playing anything to see if that makes a difference. Thanks again for all your help.

@tomcw

This comment has been minimized.

Show comment
Hide comment
@tomcw

tomcw Oct 23, 2017

Contributor

Here's a new build. Can you try this... AppleWin1.26.3.3.zip

Contributor

tomcw commented Oct 23, 2017

Here's a new build. Can you try this... AppleWin1.26.3.3.zip

@tomcw

This comment has been minimized.

Show comment
Hide comment
@tomcw

tomcw Oct 23, 2017

Contributor

FYI, code changes are in a different branch for now (GH496-MB).
Main changes are:

  • Add a new variable (g_nMBTimerDeviceForPolling) for when timer1 is started but:
    • IER.TIMER1==0 && ACR.RUNMODE==CONTINUOUS
  • When Timer1 underflows then call MB_Update() - ie. update the sound buffer
  • MB_GetFramePeriod() will return the correct period (0xF9C2 in your case)

So the MB emulation (when polling Timer1) now only calls MB_Update() on Timer1 underflow... which mirrors the regular interrupt case.

Contributor

tomcw commented Oct 23, 2017

FYI, code changes are in a different branch for now (GH496-MB).
Main changes are:

  • Add a new variable (g_nMBTimerDeviceForPolling) for when timer1 is started but:
    • IER.TIMER1==0 && ACR.RUNMODE==CONTINUOUS
  • When Timer1 underflows then call MB_Update() - ie. update the sound buffer
  • MB_GetFramePeriod() will return the correct period (0xF9C2 in your case)

So the MB emulation (when polling Timer1) now only calls MB_Update() on Timer1 underflow... which mirrors the regular interrupt case.

@dschmenk

This comment has been minimized.

Show comment
Hide comment
@dschmenk

dschmenk Oct 24, 2017

That's it! Working just like hardware.

Of course I just realized last night that I could have disabled interrupts on the 6502 and enabled them on the MockingBoard and perhaps skipped all this work. But, I really know a lot more about MockingBoard emulation than I did before...

Thanks for all your help and support,

Dave...

dschmenk commented Oct 24, 2017

That's it! Working just like hardware.

Of course I just realized last night that I could have disabled interrupts on the 6502 and enabled them on the MockingBoard and perhaps skipped all this work. But, I really know a lot more about MockingBoard emulation than I did before...

Thanks for all your help and support,

Dave...

@dschmenk dschmenk closed this Oct 24, 2017

@tomcw

This comment has been minimized.

Show comment
Hide comment
@tomcw

tomcw Oct 24, 2017

Contributor

Great - good to hear!

Of course I just realized last night that I could have disabled interrupts on the 6502 and enabled them on the MockingBoard and perhaps skipped all this work.

Yes, but I wouldn't want you making design / implementation decisions based on poor emulation. Target the real hardware and get the emulators to keep up!

Is it OK to add your mbtest.po disk image to the AppleWin-Test repo?
I need a regression test for this, otherwise emulation support may break in the future.

Contributor

tomcw commented Oct 24, 2017

Great - good to hear!

Of course I just realized last night that I could have disabled interrupts on the 6502 and enabled them on the MockingBoard and perhaps skipped all this work.

Yes, but I wouldn't want you making design / implementation decisions based on poor emulation. Target the real hardware and get the emulators to keep up!

Is it OK to add your mbtest.po disk image to the AppleWin-Test repo?
I need a regression test for this, otherwise emulation support may break in the future.

@dschmenk

This comment has been minimized.

Show comment
Hide comment
@dschmenk

dschmenk Oct 24, 2017

Tom - you can now find both a polled and an IRQ based disk image here:

https://github.com/dschmenk/PLASMA/tree/master/src/mockingboard

I had overwritten the previous mbtest.po with the IRQ version so I broke them out into separate images.

Lawless Legends thanks you!

dschmenk commented Oct 24, 2017

Tom - you can now find both a polled and an IRQ based disk image here:

https://github.com/dschmenk/PLASMA/tree/master/src/mockingboard

I had overwritten the previous mbtest.po with the IRQ version so I broke them out into separate images.

Lawless Legends thanks you!

tomcw added a commit that referenced this issue Oct 24, 2017

Support Mockingboard/Phasor when polling IFR.Timer1 (#496)
* Support precise AY8910 sound-buffer filling when polling 6522 IFR.Timer1 (ACR=Free Running mode)
  - same as if using 6522 IER.Timer1 interrupt
* Support 6522's Timer2 and some code refactoring
* Bump version: 1.26.3.4
@tomcw

This comment has been minimized.

Show comment
Hide comment
@tomcw

tomcw Oct 28, 2017

Contributor

Tom - you can now find both a polled and an IRQ based disk image here:

Actually both these disk images are binary identical - both using IRQs.
If you have time, could you build & resubmit the polling version.

Thanks.

Contributor

tomcw commented Oct 28, 2017

Tom - you can now find both a polled and an IRQ based disk image here:

Actually both these disk images are binary identical - both using IRQs.
If you have time, could you build & resubmit the polling version.

Thanks.

@dschmenk

This comment has been minimized.

Show comment
Hide comment
@dschmenk

dschmenk Oct 30, 2017

Sorry about that. I've made sure the polled version is checked in: https://github.com/dschmenk/PLASMA/blob/master/src/mockingboard/mbtest-poll.po

Update 11/1/17
Ok, no interrupts enabled on the MockingBoard for real this time, honest.

dschmenk commented Oct 30, 2017

Sorry about that. I've made sure the polled version is checked in: https://github.com/dschmenk/PLASMA/blob/master/src/mockingboard/mbtest-poll.po

Update 11/1/17
Ok, no interrupts enabled on the MockingBoard for real this time, honest.

@tomcw

This comment has been minimized.

Show comment
Hide comment
@tomcw

tomcw Nov 12, 2017

Contributor

Thanks Dave.

Added test for mbtest-poll.po: AppleWin/AppleWin-Test@476236b

Contributor

tomcw commented Nov 12, 2017

Thanks Dave.

Added test for mbtest-poll.po: AppleWin/AppleWin-Test@476236b

@tomcw

This comment has been minimized.

Show comment
Hide comment
@tomcw

tomcw Nov 12, 2017

Contributor

btw. I've built a slightly updated 1.26.3.4 here, just so that there's a proper release build with this fix in it.

Contributor

tomcw commented Nov 12, 2017

btw. I've built a slightly updated 1.26.3.4 here, just so that there's a proper release build with this fix in it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment