Implement SCC baud rate calculation (#10181)

- Also fix baud counter registers
Reference: http://www.zilog.com/docs/serial/ps0117.pdf

The X68000 uses the Clock Mode feature of the SCC, which multiplies the baud period by 16. Combined with a bug that read the baud counter from the wrong registers, this meant the emulator had two baud rate expiry callbacks running at some MHz.
This commit is contained in:
grantek 2022-08-18 10:54:08 +10:00 committed by GitHub
parent fbf2f3e394
commit aa2c20398e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 34 additions and 28 deletions

View File

@ -110,6 +110,36 @@ void scc8530_legacy_device::resetchannel(int ch)
channel[ch].baudtimer->adjust(attotime::never, ch); channel[ch].baudtimer->adjust(attotime::never, ch);
} }
/*-------------------------------------------------
updatebaudtimer - baud rate timer calculation
-------------------------------------------------*/
void scc8530_legacy_device::updatebaudtimer(int ch)
{
Chan *pChan = &channel[ch];
// BR Generator Enable
if(!BIT(pChan->reg_val[14], 0))
{
pChan->baudtimer->adjust(attotime::never, ch, attotime::never);
return;
}
// BR Time Constant
int brconst = pChan->reg_val[13] << 8 | pChan->reg_val[12];
// Clock Mode is 1x, 16x, 32x, or 64x
int clockmode = pChan->reg_val[4] >> 6;
int clockrate = 1;
if (clockmode)
{
clockrate = 8 << clockmode;
}
int baudrate = clock() / ((brconst + 2) * 2 * clockrate);
attotime attorate = attotime::from_hz(baudrate);
pChan->baudtimer->adjust(attorate, ch, attorate);
}
/*------------------------------------------------- /*-------------------------------------------------
baud_expire - baud rate timer expiry baud_expire - baud rate timer expiry
-------------------------------------------------*/ -------------------------------------------------*/
@ -117,16 +147,8 @@ void scc8530_legacy_device::resetchannel(int ch)
TIMER_CALLBACK_MEMBER(scc8530_legacy_device::baud_expire) TIMER_CALLBACK_MEMBER(scc8530_legacy_device::baud_expire)
{ {
Chan *pChan = &channel[param]; Chan *pChan = &channel[param];
int brconst = pChan->reg_val[13] << 8 | pChan->reg_val[14];
int rate = 0;
if (brconst) // always flag IRQ pending in case baud IRQ is enabled after this
{
rate = clock() / brconst;
}
// is baud counter IRQ enabled on this channel?
// always flag pending in case it's enabled after this
pChan->baudIRQPending = 1; pChan->baudIRQPending = 1;
if (pChan->baudIRQEnable) if (pChan->baudIRQEnable)
{ {
@ -137,17 +159,7 @@ TIMER_CALLBACK_MEMBER(scc8530_legacy_device::baud_expire)
updateirqs(); updateirqs();
} }
} }
updatebaudtimer(param);
// reset timer according to current register values
if (rate)
{
attotime attorate = attotime::from_hz(rate);
channel[param].baudtimer->adjust(attorate, param, attorate);
}
else
{
channel[param].baudtimer->adjust(attotime::never, param, attotime::never);
}
} }
/*------------------------------------------------- /*-------------------------------------------------
@ -213,7 +225,6 @@ void scc8530_legacy_device::acknowledge()
uint8_t scc8530_legacy_device::getareg() uint8_t scc8530_legacy_device::getareg()
{ {
/* Not yet implemented */
#if LOG_SCC #if LOG_SCC
printf("SCC: port A reg %d read 0x%02x\n", reg, channel[0].reg_val[reg]); printf("SCC: port A reg %d read 0x%02x\n", reg, channel[0].reg_val[reg]);
#endif #endif
@ -392,13 +403,7 @@ void scc8530_legacy_device::putreg(int ch, uint8_t data)
break; break;
case 14: // misc control bits case 14: // misc control bits
if (data & 0x01) // baud rate generator enable? updatebaudtimer(ch);
{
int brconst = pChan->reg_val[13]<<8 | pChan->reg_val[14];
int rate = clock() / brconst;
pChan->baudtimer->adjust(attotime::from_hz(rate), 0, attotime::from_hz(rate));
}
break; break;
case 15: // external/status interrupt control case 15: // external/status interrupt control

View File

@ -86,6 +86,7 @@ private:
devcb_write_line intrq_cb; devcb_write_line intrq_cb;
void updatebaudtimer(int ch);
void updateirqs(); void updateirqs();
void initchannel(int ch); void initchannel(int ch);
void resetchannel(int ch); void resetchannel(int ch);