m68705: re-implement timer/counter

* Support MOR-controlled mode
* Correct count direction
* Correct startup and reset states

a2bus/mouse.cpp: use new MC68705P3 core and remove obsolete glue
* the card works before and after the change, but by default axes are only mapped to inc/dec keys, not host mouse

quizpun: use new MC68705P5 core, MCU now gets timer interrupts correctly
This commit is contained in:
Vas Crabb 2017-01-16 12:11:18 +11:00
parent 7264353ee5
commit 555bfc95cc
8 changed files with 241 additions and 369 deletions

View File

@ -78,35 +78,22 @@ const device_type A2BUS_MOUSE = &device_creator<a2bus_mouse_device>;
#define MOUSE_ROM_REGION "a2mse_rom"
#define MOUSE_PIA_TAG "a2mse_pia"
#define MOUSE_MCU_TAG "a2mse_mcu"
#define MOUSE_MCU_ROM "a2mse_mcurom"
#define MOUSE_BUTTON_TAG "a2mse_button"
#define MOUSE_XAXIS_TAG "a2mse_x"
#define MOUSE_YAXIS_TAG "a2mse_y"
#define TIMER_68705 0
#define TIMER_QUADRATURE 1
static ADDRESS_MAP_START( mcu_mem, AS_PROGRAM, 8, a2bus_mouse_device )
ADDRESS_MAP_GLOBAL_MASK(0x7ff)
AM_RANGE(0x0000, 0x0000) AM_READWRITE(mcu_port_a_r, mcu_port_a_w)
AM_RANGE(0x0001, 0x0001) AM_READWRITE(mcu_port_b_r, mcu_port_b_w)
AM_RANGE(0x0002, 0x0002) AM_READWRITE(mcu_port_c_r, mcu_port_c_w)
AM_RANGE(0x0004, 0x0004) AM_WRITE(mcu_ddr_a_w)
AM_RANGE(0x0005, 0x0005) AM_WRITE(mcu_ddr_b_w)
AM_RANGE(0x0006, 0x0006) AM_WRITE(mcu_ddr_c_w)
AM_RANGE(0x0008, 0x0009) AM_READWRITE(mcu_timer_r, mcu_timer_w)
AM_RANGE(0x0010, 0x007f) AM_RAM
AM_RANGE(0x0080, 0x07ff) AM_ROM AM_REGION(MOUSE_MCU_ROM, 0x80)
ADDRESS_MAP_END
#define TIMER_QUADRATURE 0
MACHINE_CONFIG_FRAGMENT( mouse )
MCFG_CPU_ADD(MOUSE_MCU_TAG, M68705, 2043600)
MCFG_CPU_PROGRAM_MAP(mcu_mem)
MCFG_CPU_ADD(MOUSE_MCU_TAG, M68705P3, 2043600)
MCFG_M68705_PORTA_R_CB(READ8(a2bus_mouse_device, mcu_port_a_r))
MCFG_M68705_PORTB_R_CB(READ8(a2bus_mouse_device, mcu_port_b_r))
MCFG_M68705_PORTA_W_CB(WRITE8(a2bus_mouse_device, mcu_port_a_w))
MCFG_M68705_PORTB_W_CB(WRITE8(a2bus_mouse_device, mcu_port_b_w))
MCFG_M68705_PORTC_W_CB(WRITE8(a2bus_mouse_device, mcu_port_c_w))
MCFG_DEVICE_ADD(MOUSE_PIA_TAG, PIA6821, 1021800)
MCFG_PIA_READPA_HANDLER(READ8(a2bus_mouse_device, pia_in_a))
MCFG_PIA_READPB_HANDLER(READ8(a2bus_mouse_device, pia_in_b))
MCFG_PIA_WRITEPA_HANDLER(WRITE8(a2bus_mouse_device, pia_out_a))
MCFG_PIA_WRITEPB_HANDLER(WRITE8(a2bus_mouse_device, pia_out_b))
MCFG_PIA_IRQA_HANDLER(WRITELINE(a2bus_mouse_device, pia_irqa_w))
@ -117,7 +104,7 @@ ROM_START( mouse )
ROM_REGION(0x800, MOUSE_ROM_REGION, 0)
ROM_LOAD( "341-0270-c.4b", 0x000000, 0x000800, CRC(0bcd1e8e) SHA1(3a9d881a8a8d30f55b9719aceebbcf717f829d6f) )
ROM_REGION(0x800, MOUSE_MCU_ROM, 0)
ROM_REGION(0x800, MOUSE_MCU_TAG, 0)
ROM_LOAD( "341-0269.2b", 0x000000, 0x000800, CRC(94067f16) SHA1(3a2baa6648efe4456d3ec3721216e57c64f7acfc) )
ROM_REGION(0xc00, "pal", 0)
@ -177,25 +164,18 @@ a2bus_mouse_device::a2bus_mouse_device(const machine_config &mconfig, device_typ
device_a2bus_card_interface(mconfig, *this),
m_pia(*this, MOUSE_PIA_TAG),
m_mcu(*this, MOUSE_MCU_TAG),
m_mouseb(*this, MOUSE_BUTTON_TAG),
m_mousex(*this, MOUSE_XAXIS_TAG),
m_mousey(*this, MOUSE_YAXIS_TAG), m_rom(nullptr), m_ddr_a(0), m_ddr_b(0), m_ddr_c(0), m_port_a_out(0), m_port_b_out(0), m_port_c_out(0), m_port_a_in(0), m_port_b_in(0),
m_port_c_in(0), m_timer_cnt(0), m_timer_ctl(0), m_mask_option(0), last_mx(0), last_my(0), count_x(0), count_y(0), m_timer(nullptr), m_read_timer(nullptr)
m_mouseb(*this, MOUSE_BUTTON_TAG), m_mousex(*this, MOUSE_XAXIS_TAG), m_mousey(*this, MOUSE_YAXIS_TAG),
m_rom(*this, MOUSE_ROM_REGION),
m_port_a_in(0), m_port_b_in(0),
last_mx(0), last_my(0), count_x(0), count_y(0),
m_read_timer(nullptr)
{
m_started = false;
m_rom_bank = 0;
}
a2bus_mouse_device::a2bus_mouse_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
device_t(mconfig, A2BUS_MOUSE, "Apple II Mouse Card", tag, owner, clock, "a2mouse", __FILE__),
device_a2bus_card_interface(mconfig, *this),
m_pia(*this, MOUSE_PIA_TAG),
m_mcu(*this, MOUSE_MCU_TAG),
m_mouseb(*this, MOUSE_BUTTON_TAG),
m_mousex(*this, MOUSE_XAXIS_TAG),
m_mousey(*this, MOUSE_YAXIS_TAG), m_rom(nullptr), m_ddr_a(0), m_ddr_b(0), m_ddr_c(0), m_port_a_out(0), m_port_b_out(0), m_port_c_out(0), m_port_a_in(0), m_port_b_in(0), m_port_c_in(0), m_timer_cnt(0), m_timer_ctl(0), m_mask_option(0), last_mx(0), last_my(0), count_x(0), count_y(0), m_timer(nullptr), m_read_timer(nullptr)
a2bus_mouse_device(mconfig, A2BUS_MOUSE, "Apple II Mouse Card", tag, owner, clock, "a2mouse", __FILE__)
{
m_started = false;
m_rom_bank = 0;
}
@ -208,29 +188,13 @@ void a2bus_mouse_device::device_start()
// set_a2bus_device makes m_slot valid
set_a2bus_device();
m_rom = device().machine().root_device().memregion(this->subtag(MOUSE_ROM_REGION).c_str())->base();
// allocate two timers: one for the 68705, one for the quadrature magic
m_timer = timer_alloc(TIMER_68705, nullptr);
// allocate a timer for the quadrature magic
m_read_timer = timer_alloc(TIMER_QUADRATURE, nullptr);
m_timer->adjust(attotime::never, TIMER_68705);
m_read_timer->adjust(attotime::never, TIMER_QUADRATURE);
// get 68705P3 mask option byte
m_mask_option = m_rom[0x784];
// register save state variables
save_item(NAME(m_ddr_a));
save_item(NAME(m_ddr_b));
save_item(NAME(m_ddr_c));
save_item(NAME(m_port_a_out));
save_item(NAME(m_port_b_out));
save_item(NAME(m_port_c_out));
save_item(NAME(m_port_a_in));
save_item(NAME(m_port_b_in));
save_item(NAME(m_port_c_in));
save_item(NAME(m_timer_cnt));
save_item(NAME(m_timer_ctl));
save_item(NAME(last_mx));
save_item(NAME(last_my));
save_item(NAME(count_x));
@ -239,20 +203,10 @@ void a2bus_mouse_device::device_start()
void a2bus_mouse_device::device_reset()
{
m_started = true;
m_rom_bank = 0;
last_mx = last_my = count_x = count_y = 0;
m_timer_cnt = 0xff;
m_timer_ctl = 0x40; // disable interrupt, everything else clear
m_port_a_in = 0;
m_port_b_in = 0x80;
m_port_c_in = 0;
// are we emulating the mask part with a semi-programmable timer?
if (m_mask_option & 0x40)
{
m_timer_ctl |= m_mask_option & 0x17;
}
m_read_timer->adjust(attotime::from_hz(600.0), TIMER_QUADRATURE, attotime::from_hz(600.0));
}
@ -285,16 +239,6 @@ uint8_t a2bus_mouse_device::read_cnxx(address_space &space, uint8_t offset)
return m_rom[offset+m_rom_bank];
}
READ8_MEMBER(a2bus_mouse_device::pia_in_a)
{
return m_port_a_out;
}
READ8_MEMBER(a2bus_mouse_device::pia_in_b)
{
return (m_port_c_out << 4);
}
WRITE8_MEMBER(a2bus_mouse_device::pia_out_a)
{
m_port_a_in = data;
@ -302,8 +246,7 @@ WRITE8_MEMBER(a2bus_mouse_device::pia_out_a)
WRITE8_MEMBER(a2bus_mouse_device::pia_out_b)
{
m_port_c_in &= 0xf0;
m_port_c_in |= ((data >> 4) & 0xf);
m_mcu->pc_w(space, 0, 0xf0 | ((data >> 4) & 0x0f));
m_rom_bank = (data & 0xe) << 7;
}
@ -318,17 +261,12 @@ WRITE_LINE_MEMBER(a2bus_mouse_device::pia_irqb_w)
READ8_MEMBER(a2bus_mouse_device::mcu_port_a_r)
{
return (m_port_a_out & m_ddr_a) | (m_port_a_in & ~m_ddr_a);
return m_port_a_in;
}
WRITE8_MEMBER(a2bus_mouse_device::mcu_port_a_w)
{
m_port_a_out = data;
}
WRITE8_MEMBER(a2bus_mouse_device::mcu_ddr_a_w)
{
m_ddr_a = data;
m_pia->set_a_input(data, ~mem_mask);
}
READ8_MEMBER(a2bus_mouse_device::mcu_port_b_r)
@ -338,14 +276,12 @@ READ8_MEMBER(a2bus_mouse_device::mcu_port_b_r)
// clear the gates, leave everything else alone between pulses
m_port_b_in &= 0x85;
return (m_port_b_out & m_ddr_b) | (b_in & ~m_ddr_b);
return b_in;
}
WRITE8_MEMBER(a2bus_mouse_device::mcu_port_b_w)
{
m_port_b_out = data;
if (!(data & 0x40))
if (!BIT(data, 6))
{
raise_slot_irq();
}
@ -355,91 +291,9 @@ WRITE8_MEMBER(a2bus_mouse_device::mcu_port_b_w)
}
}
WRITE8_MEMBER(a2bus_mouse_device::mcu_ddr_b_w)
{
m_ddr_b = data;
}
READ8_MEMBER(a2bus_mouse_device::mcu_port_c_r)
{
return (m_port_c_out & m_ddr_c) | (m_port_c_in & ~m_ddr_c);
}
WRITE8_MEMBER(a2bus_mouse_device::mcu_port_c_w)
{
m_port_c_out = data;
}
WRITE8_MEMBER(a2bus_mouse_device::mcu_ddr_c_w)
{
m_ddr_c = data;
}
READ8_MEMBER(a2bus_mouse_device::mcu_timer_r)
{
if (offset == 1)
{
return m_timer_ctl;
}
return m_timer_cnt;
}
WRITE8_MEMBER(a2bus_mouse_device::mcu_timer_w)
{
static const int prescale[8] = { 1, 2, 4, 8, 16, 32, 64, 128 };
bool recalc = false;
// offset 0 = timer data (counts down)
if (offset == 0)
{
m_timer_cnt = data;
recalc = true;
}
// offset 1 = timer control: b7 = IRQ, b6 = IRQ mask (1=suppress),
// b5 = input select (0=CPU clk, 1=ext),
// b4 = enable external timer input,
// b3 = clear, b2-b0 = scaler (1/2/4/8/16/32/64/128)
else
{
// clearing the interrupt?
if ((m_timer_ctl & 0x80) && !(data & 0x80))
{
m_mcu->set_input_line(M68705_INT_TIMER, CLEAR_LINE);
}
if (m_mask_option & 0x40)
{
m_timer_ctl &= 0x3f;
m_timer_ctl |= (data & 0xc0);
}
else
{
// if any parameters that affect the timer changed, recalc now
if ((data & 0x3f) != (m_timer_ctl & 0x3f))
{
recalc = true;
}
// if prescaler reset, recalc
if (data & 0x8)
{
recalc = true;
}
m_timer_ctl = data;
}
}
if (recalc)
{
// recalculate the timer now
uint32_t m_ticks = 2043600 / 4;
m_ticks /= prescale[m_timer_ctl & 7];
m_ticks /= (int)(m_timer_cnt + 1);
m_timer->adjust(attotime::from_hz((double)m_ticks), TIMER_68705, attotime::from_hz((double)m_ticks));
}
m_pia->portb_w(data << 4);
}
/*
@ -452,15 +306,7 @@ WRITE8_MEMBER(a2bus_mouse_device::mcu_timer_w)
*/
void a2bus_mouse_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
if (id == TIMER_68705) // 68705's built-in timer
{
m_timer_ctl |= 0x80; // indicate timer expired
if (!(m_timer_ctl & 0x40)) // if interrupt not suppressed, fire!
{
m_mcu->set_input_line(M68705_INT_TIMER, ASSERT_LINE);
}
}
else if (id == TIMER_QUADRATURE)
if (id == TIMER_QUADRATURE)
{
int new_mx, new_my;
m_port_b_in = 0x80;

View File

@ -35,8 +35,6 @@ public:
virtual ioport_constructor device_input_ports() const override;
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
DECLARE_READ8_MEMBER(pia_in_a);
DECLARE_READ8_MEMBER(pia_in_b);
DECLARE_WRITE8_MEMBER(pia_out_a);
DECLARE_WRITE8_MEMBER(pia_out_b);
DECLARE_WRITE_LINE_MEMBER(pia_irqa_w);
@ -44,15 +42,9 @@ public:
DECLARE_READ8_MEMBER(mcu_port_a_r);
DECLARE_READ8_MEMBER(mcu_port_b_r);
DECLARE_READ8_MEMBER(mcu_port_c_r);
DECLARE_WRITE8_MEMBER(mcu_port_a_w);
DECLARE_WRITE8_MEMBER(mcu_port_b_w);
DECLARE_WRITE8_MEMBER(mcu_port_c_w);
DECLARE_WRITE8_MEMBER(mcu_ddr_a_w);
DECLARE_WRITE8_MEMBER(mcu_ddr_b_w);
DECLARE_WRITE8_MEMBER(mcu_ddr_c_w);
DECLARE_READ8_MEMBER(mcu_timer_r);
DECLARE_WRITE8_MEMBER(mcu_timer_w);
protected:
virtual void device_start() override;
@ -64,27 +56,16 @@ protected:
virtual uint8_t read_cnxx(address_space &space, uint8_t offset) override;
required_device<pia6821_device> m_pia;
required_device<m68705_device> m_mcu;
required_device<m68705p_device> m_mcu;
required_ioport m_mouseb, m_mousex, m_mousey;
private:
uint8_t *m_rom;
bool m_started;
required_region_ptr<uint8_t> m_rom;
int m_rom_bank;
uint8_t m_ddr_a;
uint8_t m_ddr_b;
uint8_t m_ddr_c;
uint8_t m_port_a_out;
uint8_t m_port_b_out;
uint8_t m_port_c_out;
uint8_t m_port_a_in;
uint8_t m_port_b_in;
uint8_t m_port_c_in;
uint8_t m_timer_cnt;
uint8_t m_timer_ctl;
uint8_t m_mask_option;
int last_mx, last_my, count_x, count_y;
emu_timer *m_timer, *m_read_timer;
emu_timer *m_read_timer;
};
// device type definition

View File

@ -235,6 +235,7 @@ void m6805_base_device::interrupt()
m_pending_interrupts &= ~(1 << HD63705_INT_NMI);
m_icount -= 11;
burn_cycles(11);
}
else if((m_pending_interrupts & ((1 << M6805_IRQ_LINE) | HD63705_INT_MASK)) != 0)
{
@ -254,6 +255,7 @@ void m6805_base_device::interrupt()
m_pending_interrupts &= ~(1 << M6805_IRQ_LINE);
}
m_icount -= 11;
burn_cycles(11);
}
}
@ -498,9 +500,9 @@ void m6805_base_device::execute_run()
debugger_instruction_hook(this, PC);
ireg=M_RDOP(PC++);
ireg = M_RDOP(PC++);
switch( ireg )
switch (ireg)
{
case 0x00: brset(0x01); break;
case 0x01: brclr(0x01); break;
@ -764,7 +766,9 @@ void m6805_base_device::execute_run()
case 0xff: stx_ix(); break;
}
m_icount -= m_cycles1[ireg];
} while( m_icount > 0 );
burn_cycles(m_cycles1[ireg]);
}
while (m_icount > 0);
}
/****************************************************************************

View File

@ -52,6 +52,9 @@ protected:
// device_state_interface overrides
virtual void state_string_export(const device_state_entry &entry, std::string &str) const override;
// for devices with timing-sensitive peripherals
virtual void burn_cycles(unsigned count) { }
private:
// opcode/condition tables
static const uint8_t m_flags8i[256];

View File

@ -244,6 +244,9 @@ m68705_new_device::m68705_new_device(
, m_port_ddr{ 0x00, 0x00, 0x00, 0x00 }
, m_port_cb_r{ { *this }, { *this }, { *this }, { *this } }
, m_port_cb_w{ { *this }, { *this }, { *this }, { *this } }
, m_prescaler(0x7f)
, m_tdr(0xff)
, m_tcr(0x7f)
, m_vihtp(CLEAR_LINE)
, m_pcr(0xff)
, m_pl_data(0xff)
@ -254,15 +257,15 @@ m68705_new_device::m68705_new_device(
template <offs_t B> READ8_MEMBER(m68705_new_device::eprom_r)
{
// read locked out when /VPON and /PLE are asserted
return (BIT(m_pcr, 2) || BIT(m_pcr, 0)) ? m_user_rom[B + offset] : 0xff;
return (!pcr_vpon() || !pcr_ple()) ? m_user_rom[B + offset] : 0xff;
}
template <offs_t B> WRITE8_MEMBER(m68705_new_device::eprom_w)
{
// programming latch enabled when /VPON and /PLE are asserted
if (!BIT(m_pcr, 2) && !BIT(m_pcr, 0))
if (pcr_vpon() && pcr_ple())
{
if (BIT(m_pcr, 1))
if (!pcr_pge())
{
m_pl_data = data;
m_pl_addr = B + offset;
@ -317,6 +320,65 @@ template <std::size_t N> void m68705_new_device::port_cb_w()
m_port_cb_w[N](space(AS_PROGRAM), 0, data, mask);
}
READ8_MEMBER(m68705_new_device::tdr_r)
{
return m_tdr;
}
WRITE8_MEMBER(m68705_new_device::tdr_w)
{
m_tdr = data;
}
READ8_MEMBER(m68705_new_device::tcr_r)
{
// in MOR controlled mode, only TIR, TIM and TOPT are visible
return m_tcr | (tcr_topt() ? 0x37 : 0x00);
}
WRITE8_MEMBER(m68705_new_device::tcr_w)
{
// 7 TIR RW Timer Interrupt Request Status
// 6 TIM RW Timer Interrupt Mask
// 5 TIN RW Timer Input Select
// 4 TIE RW Timer External Input Enable
// 3 TOPT R Timer Mask/Programmable Option
// 3 PSC W Prescaler Clear
// 2 PS2 RW Prescaler Option
// 1 PS1 RW Prescaler Option
// 0 PS0 RW Prescaler Option
// TIN TIE CLOCK
// 0 0 Internal Clock (phase 2)
// 0 1 Gated (AND) of External and Internal Clocks
// 1 0 No Clock
// 1 1 External Clock
// in MOR controlled mode, TIN/PS2/PS1/PS0 are loaded from MOR on reset and TIE is always 1
// in MOR controlled mode, TIN, TIE, PS2, PS1, and PS0 always read as 1
// TOPT isn't a real bit in this register, it's a pass-through to the MOR register
// it's theoretically possible to get into a weird state by writing to the MOR while running
// for simplicity, we don't emulate this - we just check the MOR on initialisation and reset
if (tcr_topt())
{
m_tcr = (m_tcr & 0x3f) | (data & 0xc0);
}
else
{
if (BIT(data, 3))
m_prescaler = 0;
m_tcr = (m_tcr & 0x08) | (data & 0xf7);
}
// TODO: this is tied up with /INT2 on R/U devices
if (tcr_tir() && !tcr_tim())
set_input_line(M68705_INT_TIMER, ASSERT_LINE);
else
set_input_line(M68705_INT_TIMER, CLEAR_LINE);
}
READ8_MEMBER(m68705_new_device::misc_r)
{
logerror("unsupported read MISC\n");
@ -348,7 +410,7 @@ WRITE8_MEMBER(m68705_new_device::pcr_w)
data |= ((data & 0x01) << 1);
// write EPROM if /PGE is asserted (erase requires UV so don't clear bits)
if (!BIT(m_pcr, 2) && (0x20 & ((m_pcr ^ data) & ~data)))
if (pcr_vpon() && !pcr_pge() && !BIT(data, 1))
m_user_rom[m_pl_addr] |= m_pl_data;
m_pcr = (m_pcr & 0xfc) | (data & 0x03);
@ -399,132 +461,62 @@ WRITE8_MEMBER(m68705_new_device::arr_w)
logerror("unsupported write ARR = %02X\n", data);
}
READ8_MEMBER(m68705_new_device::internal_68705_tdr_r)
{
//logerror("internal_68705 TDR read, returning %02X\n", m_tdr);
return m_tdr;
}
WRITE8_MEMBER(m68705_new_device::internal_68705_tdr_w)
{
//logerror("internal_68705 TDR written with %02X, was %02X\n", data, m_tdr);
m_tdr = data;
}
READ8_MEMBER(m68705_new_device::internal_68705_tcr_r)
{
//logerror("internal_68705 TCR read, returning %02X\n", (m_tcr&0xF7));
return (m_tcr & 0xF7);
}
WRITE8_MEMBER(m68705_new_device::internal_68705_tcr_w)
{
/*
logerror("internal_68705 TCR written with %02X\n", data);
if (data&0x80) logerror(" TIR=1, Timer Interrupt state is set\n"); else logerror(" TIR=0; Timer Interrupt state is cleared\n");
if (data&0x40) logerror(" TIM=1, Timer Interrupt is now masked\n"); else logerror(" TIM=0, Timer Interrupt is now unmasked\n");
if (data&0x20) logerror(" TIN=1, Timer Clock source is set to external\n"); else logerror(" TIN=0, Timer Clock source is set to internal\n");
if (data&0x10) logerror(" TIE=1, Timer External pin is enabled\n"); else logerror(" TIE=0, Timer External pin is disabled\n");
if (data&0x08) logerror(" PSC=1, Prescaler counter cleared\n"); else logerror(" PSC=0, Prescaler counter left alone\n");
logerror(" Prescaler: %d\n", (1<<(data&0x7)));
*/
// if timer was enabled but now isn't, shut it off.
// below is a hack assuming the TIMER pin isn't going anywhere except tied to +5v, so basically TIN is acting as an active-low timer enable, and TIE is ignored even in the case where TIE=1, the timer will end up being 5v ANDED against the internal timer clock which == the internal timer clock.
// Note this hack is incorrect; the timer pin actually does connect somewhere (vblank or maybe one of the V counter bits?), but the game never actually uses the timer pin in external clock mode, so the TIMER connection must be left over from development. We can apparently safely ignore it.
if ((m_tcr^data)&0x20)// check if TIN state changed
{
/* logerror("timer enable state changed!\n"); */
if (data&0x20) m_68705_timer->adjust(attotime::never, TIMER_68705_PRESCALER_EXPIRED);
else m_68705_timer->adjust(attotime::from_hz(((clock())/4)/(1<<(data&0x7))), TIMER_68705_PRESCALER_EXPIRED);
}
// prescaler check: if timer prescaler has changed, or the PSC bit is set, adjust the timer length for the prescaler expired timer, but only if the timer would be running
if ( (((m_tcr&0x07)!=(data&0x07))||(data&0x08)) && ((data&0x20)==0) )
{
/* logerror("timer reset due to PSC or prescaler change!\n"); */
m_68705_timer->adjust(attotime::from_hz(((clock())/4)/(1<<(data&0x7))), TIMER_68705_PRESCALER_EXPIRED);
}
m_tcr = data;
// if int state is set, and TIM is unmasked, assert an interrupt. otherwise clear it.
if ((m_tcr&0xC0) == 0x80)
set_input_line(M68705_INT_TIMER, ASSERT_LINE);
else
set_input_line(M68705_INT_TIMER, CLEAR_LINE);
}
TIMER_CALLBACK_MEMBER(m68705_new_device::timer_68705_increment)
{
m_tdr++;
if (m_tdr == 0x00) m_tcr |= 0x80; // if we overflowed, set the int bit
if ((m_tcr&0xC0) == 0x80)
set_input_line(M68705_INT_TIMER, ASSERT_LINE);
else
set_input_line(M68705_INT_TIMER, CLEAR_LINE);
m_68705_timer->adjust(attotime::from_hz((clock() / 4) / (1 << (m_tcr & 0x07))), TIMER_68705_PRESCALER_EXPIRED);
}
void m68705_new_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
switch (id)
{
case TIMER_68705_PRESCALER_EXPIRED:
timer_68705_increment(ptr, param);
break;
default:
m68705_device::device_timer(timer, id, param, ptr);
}
}
void m68705_new_device::device_start()
{
m68705_device::device_start();
save_item(NAME(m_tdr));
save_item(NAME(m_tcr));
save_item(NAME(m_port_input));
save_item(NAME(m_port_latch));
save_item(NAME(m_port_ddr));
save_item(NAME(m_prescaler));
save_item(NAME(m_tdr));
save_item(NAME(m_tcr));
save_item(NAME(m_vihtp));
save_item(NAME(m_pcr));
save_item(NAME(m_pl_data));
save_item(NAME(m_pl_addr));
// initialise digital I/O
for (u8 &input : m_port_input) input = 0xff;
for (devcb_read8 &cb : m_port_cb_r) cb.resolve();
for (devcb_write8 &cb : m_port_cb_w) cb.resolve_safe();
// initialise timer/counter
u8 const options(get_mask_options());
m_tcr = 0x40 | (options & 0x37);
if (BIT(options, 6))
m_tcr |= 0x18;
// initialise EPROM control
m_vihtp = CLEAR_LINE;
m_pcr = 0xff;
m_pl_data = 0xff;
m_pl_addr = 0xffff;
// allocate the MCU timer, and set it to fire NEVER.
m_68705_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(m68705_new_device::timer_68705_increment),this));
m_68705_timer->adjust(attotime::never);
}
void m68705_new_device::device_reset()
{
m68705_device::device_reset();
if (CLEAR_LINE != m_vihtp)
RM16(0xfff6, &m_pc);
// reset digital I/O
port_ddr_w<0>(space(AS_PROGRAM), 0, 0x00, 0xff);
port_ddr_w<1>(space(AS_PROGRAM), 0, 0x00, 0xff);
port_ddr_w<2>(space(AS_PROGRAM), 0, 0x00, 0xff);
port_ddr_w<3>(space(AS_PROGRAM), 0, 0x00, 0xff);
// reset timer/counter
u8 const options(get_mask_options());
m_prescaler = 0x7f;
m_tdr = 0xff;
m_tcr = BIT(options, 6) ? (0x58 | (options & 0x27)) : (0x40 | (m_tcr & 0x37));
// reset EPROM control
m_pcr |= 0xfb; // b2 (/VPON) is driven by external input and hence unaffected by reset
m_tdr = 0xff;
m_tcr = 0xff;
//set_input_line(M68705_IRQ_LINE, CLEAR_LINE);
m_68705_timer->adjust(attotime::from_hz((clock() / 4) / (1 << 7)));
if (CLEAR_LINE != m_vihtp)
RM16(0xfff6, &m_pc);
}
void m68705_new_device::execute_set_input(int inputnum, int state)
@ -532,10 +524,7 @@ void m68705_new_device::execute_set_input(int inputnum, int state)
switch (inputnum)
{
case M68705_VPP_LINE:
if (ASSERT_LINE == state)
m_pcr &= 0xfb;
else
m_pcr |= 0x04;
m_pcr = (m_pcr & 0xfb) | ((ASSERT_LINE == state) ? 0x00 : 0x04);
break;
case M68705_VIHTP_LINE:
// TODO: this is actually the same physical pin as the timer input, so they should be tied up
@ -560,6 +549,26 @@ void m68705_new_device::nvram_write(emu_file &file)
file.write(&m_user_rom[0], m_user_rom.bytes());
}
void m68705_new_device::burn_cycles(unsigned count)
{
// handle internal timer/counter source
if (!tcr_tin()) // TODO: check tcr_tie() and gate on TIMER if appropriate
{
unsigned const ps_opt(tcr_ps());
unsigned const ps_mask((1 << ps_opt) - 1);
unsigned const decrements((count + (m_prescaler & ps_mask)) >> ps_opt);
if (decrements && (decrements >= m_tdr))
{
m_tcr |= 0x80;
if (!tcr_tim())
set_input_line(M68705_INT_TIMER, ASSERT_LINE);
}
m_prescaler = (count + m_prescaler) & 0x7f;
m_tdr = (m_tdr - decrements) & 0xff;
}
}
/****************************************************************************
* M68705Px family
@ -577,8 +586,8 @@ DEVICE_ADDRESS_MAP_START( p_map, 8, m68705p_device )
AM_RANGE(0x0005, 0x0005) AM_WRITE(port_ddr_w<1>)
AM_RANGE(0x0006, 0x0006) AM_WRITE(port_ddr_w<2>)
// 0x0007 not used (no port D)
AM_RANGE(0x0008, 0x0008) AM_READWRITE(internal_68705_tdr_r, internal_68705_tdr_w)
AM_RANGE(0x0009, 0x0009) AM_READWRITE(internal_68705_tcr_r, internal_68705_tcr_w)
AM_RANGE(0x0008, 0x0008) AM_READWRITE(tdr_r, tdr_w)
AM_RANGE(0x0009, 0x0009) AM_READWRITE(tcr_r, tcr_w)
// 0x000a not used
AM_RANGE(0x000b, 0x000b) AM_READWRITE(pcr_r, pcr_w)
// 0x000c-0x000f not used
@ -631,8 +640,8 @@ DEVICE_ADDRESS_MAP_START( u_map, 8, m68705u_device )
AM_RANGE(0x0005, 0x0005) AM_WRITE(port_ddr_w<1>)
AM_RANGE(0x0006, 0x0006) AM_WRITE(port_ddr_w<2>)
// 0x0007 not used (port D is input only)
AM_RANGE(0x0008, 0x0008) AM_READWRITE(internal_68705_tdr_r, internal_68705_tdr_w)
AM_RANGE(0x0009, 0x0009) AM_READWRITE(internal_68705_tcr_r, internal_68705_tcr_w)
AM_RANGE(0x0008, 0x0008) AM_READWRITE(tdr_r, tdr_w)
AM_RANGE(0x0009, 0x0009) AM_READWRITE(tcr_r, tcr_w)
AM_RANGE(0x000a, 0x000a) AM_READWRITE(misc_r, misc_w)
AM_RANGE(0x000b, 0x000b) AM_READWRITE(pcr_r, pcr_w)
// 0x000c-0x000f not used
@ -698,8 +707,8 @@ DEVICE_ADDRESS_MAP_START( r_map, 8, m68705r_device )
AM_RANGE(0x0005, 0x0005) AM_WRITE(port_ddr_w<1>)
AM_RANGE(0x0006, 0x0006) AM_WRITE(port_ddr_w<2>)
// 0x0007 not used (port D is input only)
AM_RANGE(0x0008, 0x0008) AM_READWRITE(internal_68705_tdr_r, internal_68705_tdr_w)
AM_RANGE(0x0009, 0x0009) AM_READWRITE(internal_68705_tcr_r, internal_68705_tcr_w)
AM_RANGE(0x0008, 0x0008) AM_READWRITE(tdr_r, tdr_w)
AM_RANGE(0x0009, 0x0009) AM_READWRITE(tcr_r, tcr_w)
AM_RANGE(0x000a, 0x000a) AM_READWRITE(misc_r, misc_w)
AM_RANGE(0x000b, 0x000b) AM_READWRITE(pcr_r, pcr_w)
// 0x000c-0x000d not used
@ -750,6 +759,11 @@ tiny_rom_entry const *m68705p3_device::device_rom_region() const
return ROM_NAME(m68705p3);
}
u8 m68705p3_device::get_mask_options() const
{
return get_user_rom()[0x0784] & 0xf7; // no SNM bit
}
/****************************************************************************
* M68705P5 device
@ -765,6 +779,11 @@ tiny_rom_entry const *m68705p5_device::device_rom_region() const
return ROM_NAME(m68705p5);
}
u8 m68705p5_device::get_mask_options() const
{
return get_user_rom()[0x0784];
}
/****************************************************************************
* M68705R3 device
@ -780,6 +799,11 @@ tiny_rom_entry const *m68705r3_device::device_rom_region() const
return ROM_NAME(m68705r3);
}
u8 m68705r3_device::get_mask_options() const
{
return get_user_rom()[0x0784] & 0xf7; // no SNM bit
}
/****************************************************************************
* M68705U3 device
@ -794,3 +818,8 @@ tiny_rom_entry const *m68705u3_device::device_rom_region() const
{
return ROM_NAME(m68705u3);
}
u8 m68705u3_device::get_mask_options() const
{
return get_user_rom()[0x0784] & 0xf7; // no SNM bit
}

View File

@ -97,11 +97,6 @@ protected:
PORT_COUNT = 4
};
enum
{
TIMER_68705_PRESCALER_EXPIRED,
};
m68705_new_device(
machine_config const &mconfig,
char const *tag,
@ -125,10 +120,10 @@ protected:
template <std::size_t N> DECLARE_WRITE8_MEMBER(port_ddr_w);
template <std::size_t N> void port_cb_w();
DECLARE_READ8_MEMBER(internal_68705_tdr_r);
DECLARE_WRITE8_MEMBER(internal_68705_tdr_w);
DECLARE_READ8_MEMBER(internal_68705_tcr_r);
DECLARE_WRITE8_MEMBER(internal_68705_tcr_w);
DECLARE_READ8_MEMBER(tdr_r);
DECLARE_WRITE8_MEMBER(tdr_w);
DECLARE_READ8_MEMBER(tcr_r);
DECLARE_WRITE8_MEMBER(tcr_w);
DECLARE_READ8_MEMBER(misc_r);
DECLARE_WRITE8_MEMBER(misc_w);
@ -141,11 +136,6 @@ protected:
DECLARE_READ8_MEMBER(arr_r);
DECLARE_WRITE8_MEMBER(arr_w);
TIMER_CALLBACK_MEMBER(timer_68705_increment);
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
// device-level overrides
virtual void device_start() override;
virtual void device_reset() override;
virtual void execute_set_input(int inputnum, int state) override;
@ -153,27 +143,44 @@ protected:
virtual void nvram_read(emu_file &file) override;
virtual void nvram_write(emu_file &file) override;
u8 m_tdr;
u8 m_tcr;
virtual void burn_cycles(unsigned count) override;
/* Timers */
emu_timer *m_68705_timer;
u8 *const get_user_rom() const { return &m_user_rom[0]; }
virtual u8 get_mask_options() const = 0;
private:
bool tcr_tir() const { return BIT(m_tcr, 7); }
bool tcr_tim() const { return BIT(m_tcr, 6); }
bool tcr_tin() const { return BIT(m_tcr, 5); }
bool tcr_tie() const { return BIT(m_tcr, 4); }
bool tcr_topt() const { return BIT(m_tcr, 3); }
u8 tcr_ps() const { return m_tcr & 0x07; }
bool pcr_vpon() const { return !BIT(m_pcr, 2); }
bool pcr_pge() const { return !BIT(m_pcr, 1); }
bool pcr_ple() const { return !BIT(m_pcr, 0); }
required_region_ptr<u8> m_user_rom;
bool m_port_open_drain[PORT_COUNT];
u8 m_port_mask[PORT_COUNT];
u8 m_port_input[PORT_COUNT];
u8 m_port_latch[PORT_COUNT];
u8 m_port_ddr[PORT_COUNT];
devcb_read8 m_port_cb_r[PORT_COUNT];
devcb_write8 m_port_cb_w[PORT_COUNT];
// digital I/O
bool m_port_open_drain[PORT_COUNT];
u8 m_port_mask[PORT_COUNT];
u8 m_port_input[PORT_COUNT];
u8 m_port_latch[PORT_COUNT];
u8 m_port_ddr[PORT_COUNT];
devcb_read8 m_port_cb_r[PORT_COUNT];
devcb_write8 m_port_cb_w[PORT_COUNT];
u8 m_vihtp;
u8 m_pcr;
u8 m_pl_data;
u16 m_pl_addr;
// timer/counter
u8 m_prescaler;
u8 m_tdr;
u8 m_tcr;
// EPROM control
u8 m_vihtp;
u8 m_pcr;
u8 m_pl_data;
u16 m_pl_addr;
};
@ -288,6 +295,8 @@ public:
protected:
virtual tiny_rom_entry const *device_rom_region() const override;
virtual u8 get_mask_options() const override;
};
@ -300,6 +309,8 @@ public:
protected:
virtual tiny_rom_entry const *device_rom_region() const override;
virtual u8 get_mask_options() const override;
};
@ -312,6 +323,8 @@ public:
protected:
virtual tiny_rom_entry const *device_rom_region() const override;
virtual u8 get_mask_options() const override;
};
@ -324,6 +337,8 @@ public:
protected:
virtual tiny_rom_entry const *device_rom_region() const override;
virtual u8 get_mask_options() const override;
};

View File

@ -22,7 +22,8 @@
* When programming is complete, the "Programmed" LED will be lit. When
* verification is complete, the "Verified" LED will be lit. If
* verification fails, the program stops and the "Address" digits show
* the address one past the location that failed.
* the address one past the location that failed for P3/P5, or the
* location that failed for R3/U3.
*/
#include "emu.h"

View File

@ -82,12 +82,16 @@ Notes:
***************************************************************************/
#include "emu.h"
#include "cpu/z80/z80.h"
#include "cpu/m6805/m68705.h"
#include "machine/gen_latch.h"
#include "cpu/z80/z80.h"
#include "machine/eepromser.h"
#include "machine/gen_latch.h"
#include "sound/2203intf.h"
// very preliminary quizpun2 protection simulation
#define VERBOSE_PROTECTION_LOG 0
@ -150,7 +154,7 @@ public:
DECLARE_WRITE8_MEMBER(quizpun2_protection_w);
// quizpun
uint8_t m_port_a, m_port_b, m_port_c;
uint8_t m_port_a, m_port_b;
bool m_quizpun_pending;
bool m_quizpun_written;
bool m_quizpun_repeat;
@ -269,7 +273,7 @@ void quizpun2_state::machine_reset()
m_prot.addr = 0;
// quizpun
m_port_a = m_port_b = m_port_c = 0;
m_port_a = m_port_b = 0;
m_quizpun_pending = m_quizpun_written = m_quizpun_repeat = false;
}
@ -529,10 +533,11 @@ READ8_MEMBER(quizpun2_state::quizpun_68705_port_b_r)
// bit 1: 0 = main cpu has written
// bit 0: 0 = main cpu is reading
uint8_t ret = m_port_b & 0xf4;
ret |= ( m_quizpun_pending ? 0 : (1 << 3)) |
( (m_quizpun_pending && m_quizpun_written) ? 0 : (1 << 1)) |
( (m_quizpun_pending && !m_quizpun_written) ? 0 : (1 << 0)) ;
uint8_t const ret =
0xf4 |
( m_quizpun_pending ? 0 : (1 << 3)) |
((m_quizpun_pending && m_quizpun_written) ? 0 : (1 << 1)) |
((m_quizpun_pending && !m_quizpun_written) ? 0 : (1 << 0));
// logerror("%s: port B read %02x\n", machine().describe_context(), ret);
return ret;
@ -544,7 +549,7 @@ WRITE8_MEMBER(quizpun2_state::quizpun_68705_port_b_w)
// bit 2: 0->1 run main cpu
if (!(m_port_b & 0x04) && (data & 0x04))
if (!BIT(m_port_b, 2) && BIT(data, 2))
{
m_quizpun_pending = false;
m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);
@ -556,8 +561,7 @@ WRITE8_MEMBER(quizpun2_state::quizpun_68705_port_b_w)
READ8_MEMBER(quizpun2_state::quizpun_68705_port_c_r)
{
uint8_t ret = m_port_c & 0xf7;
ret |= m_eeprom->do_read() ? 0x08 : 0;
uint8_t const ret = 0xf7 | (m_eeprom->do_read() ? 0x08 : 0x00);
// logerror("%s: port C read %02x\n", machine().describe_context(), ret);
return ret;
}
@ -565,33 +569,17 @@ READ8_MEMBER(quizpun2_state::quizpun_68705_port_c_r)
WRITE8_MEMBER(quizpun2_state::quizpun_68705_port_c_w)
{
// latch the bit
m_eeprom->di_write((data & 0x04) >> 2);
m_eeprom->di_write(BIT(data, 2));
// reset line asserted: reset.
m_eeprom->cs_write((data & 0x02) ? ASSERT_LINE : CLEAR_LINE);
m_eeprom->cs_write(BIT(data, 1) ? ASSERT_LINE : CLEAR_LINE);
// clock line asserted: write latch or select next bit to read
m_eeprom->clk_write((data & 0x01) ? ASSERT_LINE : CLEAR_LINE);
m_eeprom->clk_write(BIT(data, 0) ? ASSERT_LINE : CLEAR_LINE);
// logerror("%s: port C write %02x\n", machine().describe_context(), data);
m_port_c = data;
}
static ADDRESS_MAP_START( mcu_map, AS_PROGRAM, 8, quizpun2_state )
ADDRESS_MAP_GLOBAL_MASK(0x7ff)
AM_RANGE(0x000, 0x000) AM_READWRITE(quizpun_68705_port_a_r, quizpun_68705_port_a_w)
AM_RANGE(0x001, 0x001) AM_READWRITE(quizpun_68705_port_b_r, quizpun_68705_port_b_w)
AM_RANGE(0x002, 0x002) AM_READWRITE(quizpun_68705_port_c_r, quizpun_68705_port_c_w)
AM_RANGE(0x004, 0x004) AM_NOP // DDR A
AM_RANGE(0x005, 0x005) AM_NOP // DDR B
AM_RANGE(0x006, 0x006) AM_NOP // DDR C
AM_RANGE(0x010, 0x07f) AM_RAM
AM_RANGE(0x080, 0x7ff) AM_ROM
ADDRESS_MAP_END
/***************************************************************************
Memory Maps - Sound CPU
***************************************************************************/
@ -731,8 +719,13 @@ static MACHINE_CONFIG_DERIVED( quizpun, quizpun2 )
MCFG_CPU_MODIFY("maincpu")
MCFG_CPU_IO_MAP(quizpun_io_map)
MCFG_CPU_ADD("mcu", M68705, XTAL_4MHz) // xtal is 4MHz, divided by 4 internally
MCFG_CPU_PROGRAM_MAP(mcu_map)
MCFG_CPU_ADD("mcu", M68705P5, XTAL_4MHz) // xtal is 4MHz, divided by 4 internally
MCFG_M68705_PORTA_R_CB(READ8(quizpun2_state, quizpun_68705_port_a_r))
MCFG_M68705_PORTB_R_CB(READ8(quizpun2_state, quizpun_68705_port_b_r))
MCFG_M68705_PORTC_R_CB(READ8(quizpun2_state, quizpun_68705_port_c_r))
MCFG_M68705_PORTA_W_CB(WRITE8(quizpun2_state, quizpun_68705_port_a_w))
MCFG_M68705_PORTB_W_CB(WRITE8(quizpun2_state, quizpun_68705_port_b_w))
MCFG_M68705_PORTC_W_CB(WRITE8(quizpun2_state, quizpun_68705_port_c_w))
MACHINE_CONFIG_END
/***************************************************************************