act/victor9k.cpp: Get audio working on Victor 9000 (victor9k) (#13549)

* 6522via: Don't generate a signal on PB7 when the timer 1 latch is
programmed with 0.

On the Victor 9000, the clock for the audio codec is generated by a
via6522.  PB7 is connected to the codec's clock input.  Non-speech
sounds (e.g. beeps, musical notes) are produced by outputting a cyclical
waveform from the codec, with the pitch determined by the frequency of
the codec clock.

Software running on the Victor 9000 has been observed to attempt to
silence the audio by writing 0 to the T1 timer latch (not by turning off
continuous mode or PB7 output).  With the emulated via6522, this resulted
in a high-frequency clock signal being output on PB7, causing a
high-pitched squeal whenever notes are not being played.

From this observed behavior we could infer that the original 6522 HW
does not output a signal on PB7 when the latch value is 0, and that the
Victor 9000 software was relying on this behaviour to silence the audio
output (one would have to assume that the real hardware didn't produce
a squeal).

* mc6852: fix various transmit-related issues which were preventing
victor9k Audio output from working.

The expected behavior of mc6852 is to write the data received in the
FIFO register serially via tx_data_callback().  However, this was not
implemented, and the only way data in the transmit FIFO could be
removed and transmitted was by calling get_tx_byte(), and to then
serially transmit the data by some external mechanism.

Only m68sfdc.cpp calls get_tx_byte(), so it's hard to see how any
other device using the mc6852 would have been able to transmit data.

Software running on victor9k attempting to play audio would hang,
since it would block waiting for the TX FIFO to empty.  With these
changes, Victor 9000 audio playback works as expected, with the data
getting sent serially to the Audio codec.

In order to avoid breaking m68sfdc, a new API call is added to allow
data to be sent the "old" way.  m68sfdc now calls
set_tx_pull_mode(true), to get the previous behavior.  As I understand
it, other devices using mc6852 must be broken, and would need to
be revisited.

* victor9k: complete Audio support, adding a low-pass filter after
the HC-55516 codec.  Mark victor9k Audio as working.
This commit is contained in:
donohoe00 2025-04-11 18:48:57 -04:00 committed by GitHub
parent 8c5e73a6fa
commit 9cdce3047e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 100 additions and 16 deletions

View File

@ -535,7 +535,14 @@ TIMER_CALLBACK_MEMBER(via6522_device::t1_tick)
if (T1_CONTINUOUS (m_acr))
{
m_t1_pb7 = !m_t1_pb7;
m_t1->adjust(clocks_to_attotime(TIMER1_VALUE + IFR_DELAY));
if (TIMER1_VALUE > 0)
{
m_t1->adjust(clocks_to_attotime(TIMER1_VALUE + IFR_DELAY));
}
else
{
m_t1_active = 0;
}
}
else
{
@ -934,8 +941,15 @@ void via6522_device::write(offs_t offset, u8 data)
output_pb();
}
m_t1->adjust(clocks_to_attotime(TIMER1_VALUE + IFR_DELAY));
m_t1_active = 1;
if (TIMER1_VALUE > 0)
{
m_t1->adjust(clocks_to_attotime(TIMER1_VALUE + IFR_DELAY));
m_t1_active = 1;
}
else
{
m_t1_active = 0;
}
break;
case VIA_T2CL:

View File

@ -842,6 +842,7 @@ void m68sfdc_device::device_add_mconfig(machine_config &config)
m_pia->irqb_handler().set(FUNC(m68sfdc_device::handle_irq));
MC6852(config, m_ssda, 0);
m_ssda->set_tx_pull_mode(true);
}
DEFINE_DEVICE_TYPE(M68SFDC, m68sfdc_device, "m68sfdc", "M68SFDC")

View File

@ -51,6 +51,8 @@ mc6852_device::mc6852_device(const machine_config &mconfig, const char *tag, dev
m_write_irq(*this),
m_write_sm_dtr(*this),
m_write_tuf(*this),
m_tx_pull_mode(false),
m_tx_active(false),
m_rx_clock(0),
m_tx_clock(0),
m_cts(1),
@ -84,6 +86,8 @@ void mc6852_device::device_start()
save_item(NAME(m_sm_dtr));
save_item(NAME(m_tuf));
save_item(NAME(m_in_sync));
save_item(NAME(m_tx_active));
save_item(NAME(m_tx_pull_mode));
}
@ -125,7 +129,39 @@ void mc6852_device::tra_callback()
void mc6852_device::tra_complete()
{
// TODO
int trigger = (m_cr[1] & C2_1_2_BYTE) ? 1 : 2;
int available = 3 - m_tx_fifo.size();
uint8_t byte_to_send;
if (available < 3)
{
// FIFO not empty - send the next byte.
byte_to_send = m_tx_fifo.front();
m_tx_fifo.pop();
available++;
}
else
{
// TX underflow
if (m_cr[1] & C2_TX_SYNC)
{
m_status |= S_TUF;
byte_to_send = m_scr; // Send Sync Code
// TODO assert TUF pin for "approximately one Tx CLK high period"
}
else
{
byte_to_send = 0xff; // Send a "Mark"
}
}
transmit_register_setup(byte_to_send);
if (available >= trigger)
{
m_status |= S_TDRA;
}
}
//-------------------------------------------------
@ -234,7 +270,7 @@ uint8_t mc6852_device::read(offs_t offset)
// TODO this might not be quite right, the datasheet
// states that the RX overrun flag is cleared by
// reading the status, and the RX data fifo?
m_status &= S_RX_OVRN;
m_status &= ~S_RX_OVRN;
}
}
@ -311,9 +347,9 @@ void mc6852_device::write(offs_t offset, uint8_t data)
int data_bit_count = 0;
parity_t parity = PARITY_NONE;
stop_bits_t stop_bits = STOP_BITS_1;
stop_bits_t stop_bits = STOP_BITS_0;
switch (data & C2_WS_MASK)
switch ((data & C2_WS_MASK) >> C2_WS_SHIFT)
{
case 0: data_bit_count = 6; parity = PARITY_EVEN; break;
case 1: data_bit_count = 6; parity = PARITY_ODD; break;
@ -325,7 +361,7 @@ void mc6852_device::write(offs_t offset, uint8_t data)
case 7: data_bit_count = 8; parity = PARITY_ODD; break;
}
set_data_frame(1, data_bit_count, parity, stop_bits);
set_data_frame(0, data_bit_count, parity, stop_bits);
// The fifo trigger levels may have changed, so update
// the status bits.
@ -374,8 +410,17 @@ void mc6852_device::write(offs_t offset, uint8_t data)
if (available > 0)
{
LOG("MC6852 Transmit FIFO %02x\n", data);
m_tx_fifo.push(data);
available--;
if (!m_tx_pull_mode && !m_tx_active) {
// transfer is idle: this emulates moving the first byte
// into the shift register, and kicking off transmission.
// tra_complete() will be called when the byte has been
// sent.
m_tx_active = true;
transmit_register_setup(data);
} else {
m_tx_fifo.push(data);
available--;
}
}
else
{
@ -426,6 +471,7 @@ void mc6852_device::write(offs_t offset, uint8_t data)
m_status &= ~(S_TUF | S_CTS);
m_status |= S_TDRA;
m_tx_fifo = std::queue<uint8_t>();
m_tx_active = false;
transmit_register_reset();
}

View File

@ -47,6 +47,8 @@ public:
void set_rx_clock(int clock) { m_rx_clock = clock; }
void set_tx_clock(int clock) { m_tx_clock = clock; }
void set_tx_pull_mode(bool tx_pull_mode) { m_tx_pull_mode = tx_pull_mode; }
auto tx_data_callback() { return m_write_tx_data.bind(); }
auto irq_callback() { return m_write_irq.bind(); }
auto sm_dtr_callback() { return m_write_sm_dtr.bind(); }
@ -120,7 +122,8 @@ private:
C2_1_2_BYTE = 0x04,
C2_PC_MASK = 0x03,
C2_PC2 = 0x02,
C2_PC1 = 0x01
C2_PC1 = 0x01,
C2_WS_SHIFT = 3
};
enum
@ -147,6 +150,13 @@ private:
std::queue<uint8_t> m_rx_fifo;
std::queue<uint8_t> m_tx_fifo;
// If m_tx_pull_mode is true, get_tx_byte() must be called to retrieve
// the next byte to transmit, and the actual transmission must be
// carried out by some external mechanism.
bool m_tx_pull_mode;
bool m_tx_active;
int m_rx_clock;
int m_tx_clock;
int m_cts; // clear to send

View File

@ -34,10 +34,12 @@
#include "machine/pit8253.h"
#include "machine/pic8259.h"
#include "machine/ram.h"
#include "machine/rescap.h"
#include "victor9k_kb.h"
#include "victor9k_fdc.h"
#include "machine/z80sio.h"
#include "sound/hc55516.h"
#include "sound/flt_biquad.h"
#include "video/mc6845.h"
#include "emupal.h"
#include "screen.h"
@ -104,6 +106,8 @@ public:
m_cvsd(*this, HC55516_TAG),
m_crtc(*this, HD46505S_TAG),
m_ram(*this, RAM_TAG),
m_cvsd_filter(*this, "cvsd_filter"),
m_cvsd_filter2(*this, "cvsd_filter2"),
m_kb(*this, KB_TAG),
m_fdc(*this, "fdc"),
m_centronics(*this, "centronics"),
@ -138,6 +142,8 @@ private:
required_device<hc55516_device> m_cvsd;
required_device<mc6845_device> m_crtc;
required_device<ram_device> m_ram;
optional_device<filter_biquad_device> m_cvsd_filter;
optional_device<filter_biquad_device> m_cvsd_filter2;
required_device<victor_9000_keyboard_device> m_kb;
required_device<victor_9000_fdc_device> m_fdc;
required_device<centronics_device> m_centronics;
@ -391,7 +397,12 @@ void victor9k_state::ssda_sm_dtr_w(int state)
{
m_ssda->cts_w(state);
m_ssda->dcd_w(!state);
//m_cvsd->enc_dec_w(!state);
/* ___
* We're supposed to set the ENC/DEC input of the HC55516 to !state,
* but only playback/decode is currently supported, and that input
* is not implemenented.
*/
}
@ -733,10 +744,12 @@ void victor9k_state::victor9k(machine_config &config)
m_crtc->set_begin_update_callback(FUNC(victor9k_state::crtc_begin_update));
// sound hardware
FILTER_BIQUAD(config, m_cvsd_filter2).opamp_mfb_lowpass_setup(RES_K(27), RES_K(15), RES_K(27), CAP_P(4700), CAP_P(1200));
FILTER_BIQUAD(config, m_cvsd_filter).opamp_mfb_lowpass_setup(RES_K(43), RES_K(36), RES_K(180), CAP_P(1800), CAP_P(180));
HC55516(config, m_cvsd, 0).add_route(ALL_OUTPUTS, m_cvsd_filter, 1.0);
m_cvsd_filter->add_route(ALL_OUTPUTS, m_cvsd_filter2, 1.0);
m_cvsd_filter2->add_route(ALL_OUTPUTS, "mono", 0.25);
SPEAKER(config, "mono").front_center();
HC55516(config, m_cvsd, 0);
//MCFG_HC55516_DIG_OUT_CB(WRITELINE(MC6852_TAG, mc6852_device, rx_w))
m_cvsd->add_route(ALL_OUTPUTS, "mono", 0.25);
// devices
IEEE488(config, m_ieee488, 0);
@ -853,4 +866,4 @@ ROM_END
//**************************************************************************
// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS
COMP( 1982, victor9k, 0, 0, victor9k, victor9k, victor9k_state, empty_init, "Victor Business Products", "Victor 9000", MACHINE_IMPERFECT_COLORS | MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE )
COMP( 1982, victor9k, 0, 0, victor9k, victor9k, victor9k_state, empty_init, "Victor Business Products", "Victor 9000", MACHINE_IMPERFECT_COLORS | MACHINE_SUPPORTS_SAVE )