Added basic implementation of the NSC810 RAM-I/O-Timer device, and plugged it into the Husky Hunter 2 driver. [Barry Rodewald]

This commit is contained in:
mahlemiut 2014-03-11 06:05:35 +00:00
parent e6dcbb90e4
commit c86b56168c
5 changed files with 478 additions and 6 deletions

2
.gitattributes vendored
View File

@ -2341,6 +2341,8 @@ src/emu/machine/netlist.c svneol=native#text/plain
src/emu/machine/netlist.h svneol=native#text/plain
src/emu/machine/nmc9306.c svneol=native#text/plain
src/emu/machine/nmc9306.h svneol=native#text/plain
src/emu/machine/nsc810.c svneol=native#text/plain
src/emu/machine/nsc810.h svneol=native#text/plain
src/emu/machine/nscsi_bus.c svneol=native#text/plain
src/emu/machine/nscsi_bus.h svneol=native#text/plain
src/emu/machine/nscsi_cb.c svneol=native#text/plain

311
src/emu/machine/nsc810.c Normal file
View File

@ -0,0 +1,311 @@
/*
* nsc810.c
*
* Created on: 10/03/2014
*
* TODO:
* - 128 byte RAM
* - other timer modes (only mode 1 - event counter - is implemented currently)
* - port bit set/clear
* - and lots of other stuff
*/
#include "nsc810.h"
//**************************************************************************
// DEVICE DEFINITIONS
//**************************************************************************
#define LOG (1)
const device_type NSC810 = &device_creator<nsc810_device>;
nsc810_device::nsc810_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
device_t(mconfig, NSC810, "National Semiconductor NSC810", tag, owner, clock, "nsc810", __FILE__),
m_portA_r(*this),
m_portB_r(*this),
m_portC_r(*this),
m_portA_w(*this),
m_portB_w(*this),
m_portC_w(*this),
m_timer0_out(*this),
m_timer1_out(*this)
{
}
void nsc810_device::device_start()
{
m_portA_r.resolve_safe(0);
m_portB_r.resolve_safe(0);
m_portC_r.resolve_safe(0);
m_portA_w.resolve_safe();
m_portB_w.resolve_safe();
m_portC_w.resolve_safe();
m_timer0_out.resolve_safe();
m_timer1_out.resolve_safe();
m_portA_w(0);
m_portB_w(0);
m_portC_w(0);
m_timer0_out(0);
m_timer1_out(0);
m_timer0 = timer_alloc(TIMER0_CLOCK);
m_timer1 = timer_alloc(TIMER1_CLOCK);
}
void nsc810_device::device_reset()
{
m_portA_latch = 0;
m_portB_latch = 0;
m_portC_latch = 0;
m_ddrA = 0;
m_ddrB = 0;
m_ddrC = 0;
m_mode = 0;
m_timer0_mode = 0;
m_timer1_mode = 0;
m_timer0_counter = 0;
m_timer1_counter = 0;
m_timer0_running = false;
m_timer1_running = false;
m_ramselect = false;
}
void nsc810_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
switch(id)
{
case TIMER0_CLOCK:
m_timer0_counter--;
if((m_timer0_mode & 0x07) == 0x01 || (m_timer0_mode & 0x07) == 0x02)
{
if(m_timer0_counter == 0)
{
m_timer0_out(ASSERT_LINE);
m_timer0_counter = m_timer0_base;
if(LOG) logerror("NSC810 '%s': Timer 0 output set\n",tag());
}
}
break;
case TIMER1_CLOCK:
m_timer1_counter--;
if((m_timer1_mode & 0x07) == 0x01 || (m_timer1_mode & 0x07) == 0x02)
{
if(m_timer1_counter == 0)
{
m_timer1_out(ASSERT_LINE);
m_timer1_counter = m_timer1_base;
if(LOG) logerror("NSC810 '%s': Timer 1 output set\n",tag());
}
}
break;
}
}
READ8_MEMBER(nsc810_device::read)
{
UINT8 res = 0xff;
if(m_ramselect)
{
// TODO: 128 byte RAM access
}
else
{
// Register access
switch(offset & 0x1f)
{
case REG_PORTA:
res = m_portA_latch &= m_ddrA;
res |= (m_portA_r() & ~m_ddrA);
//if(LOG) logerror("NSC810 '%s': Port A data read %02x\n",tag(),res);
break;
case REG_PORTB:
res = m_portB_latch &= m_ddrB;
res |= (m_portB_r() & ~m_ddrB);
//if(LOG) logerror("NSC810 '%s': Port B data read %02x\n",tag(),res);
break;
case REG_PORTC:
res = m_portC_latch &= m_ddrC;
res |= (m_portC_r() & ~m_ddrC);
//if(LOG) logerror("NSC810 '%s': Port C data read %02x\n",tag(),res);
break;
case REG_MODE_TIMER0:
res = m_timer0_mode;
break;
case REG_MODE_TIMER1:
res = m_timer1_mode;
break;
case REG_TIMER0_LOW:
res = m_timer0_counter & 0xff;
if((m_timer0_mode & 0x07) == 0x01 || (m_timer0_mode & 0x07) == 0x02)
{
m_timer0_out(CLEAR_LINE);
if(LOG) logerror("NSC810 '%s': Timer 0 output reset\n",tag());
}
break;
case REG_TIMER0_HIGH:
res = m_timer0_counter >> 8;
if((m_timer0_mode & 0x07) == 0x01 || (m_timer0_mode & 0x07) == 0x02)
{
m_timer0_out(CLEAR_LINE);
if(LOG) logerror("NSC810 '%s': Timer 0 output reset\n",tag());
}
break;
case REG_TIMER1_LOW:
res = m_timer1_counter & 0xff;
if((m_timer1_mode & 0x07) == 0x01 || (m_timer1_mode & 0x07) == 0x02)
{
m_timer1_out(0);
if(LOG) logerror("NSC810 '%s': Timer 1 output reset\n",tag());
}
break;
case REG_TIMER1_HIGH:
res = m_timer1_counter >> 8;
if((m_timer1_mode & 0x07) == 0x01 || (m_timer1_mode & 0x07) == 0x02)
{
m_timer1_out(0);
if(LOG) logerror("NSC810 '%s': Timer 1 output reset\n",tag());
}
break;
default:
if(LOG) logerror("NSC810 '%s': unused port %02x read\n",tag(),offset);
}
}
return res;
}
WRITE8_MEMBER(nsc810_device::write)
{
UINT32 rate;
if(m_ramselect)
{
// TODO: 128 byte RAM access
}
else
{
// Register access
switch(offset & 0x1f)
{
case REG_PORTA:
m_portA_latch = data & ~m_ddrA;
m_portA_w(data & m_ddrA);
if(LOG) logerror("NSC810 '%s': Port A data write %02x\n",tag(),data);
break;
case REG_PORTB:
m_portB_latch = data & ~m_ddrB;
m_portB_w(data & m_ddrB);
if(LOG) logerror("NSC810 '%s': Port B data write %02x\n",tag(),data);
break;
case REG_PORTC:
m_portC_latch = data & ~m_ddrC;
m_portC_w(data & m_ddrC);
if(LOG) logerror("NSC810 '%s': Port C data write %02x\n",tag(),data);
break;
case REG_DDRA:
m_ddrA = data;
if(LOG) logerror("NSC810 '%s': Port A direction write %02x\n",tag(),data);
break;
case REG_DDRB:
m_ddrB = data;
if(LOG) logerror("NSC810 '%s': Port B direction write %02x\n",tag(),data);
break;
case REG_DDRC:
m_ddrC = data;
if(LOG) logerror("NSC810 '%s': Port C direction write %02x\n",tag(),data);
break;
case REG_MODE_DEF:
if(LOG) logerror("NSC810 '%s': Mode Definition write %02x\n",tag(),data);
break;
case REG_PORTA_BITCLR:
if(LOG) logerror("NSC810 '%s': Port A bit-clear write %02x\n",tag(),data);
break;
case REG_PORTB_BITCLR:
if(LOG) logerror("NSC810 '%s': Port B bit-clear write %02x\n",tag(),data);
break;
case REG_PORTC_BITCLR:
if(LOG) logerror("NSC810 '%s': Port C bit-clear write %02x\n",tag(),data);
break;
case REG_PORTA_BITSET:
if(LOG) logerror("NSC810 '%s': Port A bit-set write %02x\n",tag(),data);
break;
case REG_PORTB_BITSET:
if(LOG) logerror("NSC810 '%s': Port B bit-set write %02x\n",tag(),data);
break;
case REG_PORTC_BITSET:
if(LOG) logerror("NSC810 '%s': Port C bit-set write %02x\n",tag(),data);
break;
case REG_TIMER0_LOW:
m_timer0_base = (m_timer0_base & 0xff00) | data;
m_timer0_counter = (m_timer0_counter & 0xff00) | data;
if(LOG) logerror("NSC810 '%s': Timer 0 low-byte write %02x (base=%04x)\n",tag(),data,m_timer0_base);
break;
case REG_TIMER0_HIGH:
m_timer0_base = (m_timer0_base & 0x00ff) | (data << 8);
m_timer0_counter = (m_timer0_counter & 0x00ff) | (data << 8);
if(LOG) logerror("NSC810 '%s': Timer 0 high-byte write %02x (base=%04x)\n",tag(),data,m_timer0_base);
break;
case REG_TIMER1_LOW:
m_timer1_base = (m_timer1_base & 0xff00) | data;
m_timer1_counter = (m_timer1_counter & 0xff00) | data;
if(LOG) logerror("NSC810 '%s': Timer 1 low-byte write %02x (base=%04x)\n",tag(),data,m_timer1_base);
break;
case REG_TIMER1_HIGH:
m_timer1_base = (m_timer1_base & 0x00ff) | (data << 8);
m_timer1_counter = (m_timer1_counter & 0x00ff) | (data << 8);
if(LOG) logerror("NSC810 '%s': Timer 1 high-byte write %02x (base=%04x)\n",tag(),data,m_timer1_base);
break;
case REG_TIMER0_STOP:
m_timer0_running = false;
m_timer0->reset();
if(LOG) logerror("NSC810 '%s': Timer 0 Stop write %02x\n",tag(),data);
break;
case REG_TIMER0_START:
if((m_timer0_mode & 0x07) != 0x00 && (m_timer0_mode & 0x07) != 0x07)
{
m_timer0_running = true;
if(m_timer0_mode & 0x10)
rate = m_timer0_clock / 64;
else
if(m_timer0_mode & 0x08)
rate = m_timer0_clock / 2;
else
rate = m_timer0_clock;
m_timer0->adjust(attotime::zero,0,attotime::from_hz(rate));
}
if(LOG) logerror("NSC810 '%s': Timer 0 Start write %02x\n",tag(),data);
break;
case REG_TIMER1_STOP:
m_timer1_running = false;
m_timer1->reset();
if(LOG) logerror("NSC810 '%s': Timer 1 Stop write %02x\n",tag(),data);
break;
case REG_TIMER1_START:
if((m_timer1_mode & 0x07) != 0x00 && (m_timer1_mode & 0x07) != 0x07)
{
m_timer1_running = true;
// no /64 prescaler on timer 1
if(m_timer0_mode & 0x08)
rate = m_timer0_clock / 2;
else
rate = m_timer0_clock;
m_timer1->adjust(attotime::zero,0,attotime::from_hz(rate));
}
if(LOG) logerror("NSC810 '%s': Timer 1 Start write %02x\n",tag(),data);
break;
case REG_MODE_TIMER0:
m_timer0_mode = data;
if(LOG) logerror("NSC810 '%s': Timer 0 Mode write %02x\n",tag(),data);
break;
case REG_MODE_TIMER1:
m_timer1_mode = data;
if(LOG) logerror("NSC810 '%s': Timer 1 Mode write %02x\n",tag(),data);
break;
default:
logerror("NSC810 '%s': Unused register %02x write %02x\n",tag(),offset,data);
}
}
}

133
src/emu/machine/nsc810.h Normal file
View File

@ -0,0 +1,133 @@
/*
* nsc810.h
*
* Created on: 10/03/2014
*/
#ifndef NSC810_H_
#define NSC810_H_
#include "emu.h"
class nsc810_device : public device_t
{
public:
// construction/destruction
nsc810_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
template<class _Object> static devcb2_base &set_portA_read_callback(device_t &device, _Object object) { return downcast<nsc810_device &>(device).m_portA_r.set_callback(object); }
template<class _Object> static devcb2_base &set_portB_read_callback(device_t &device, _Object object) { return downcast<nsc810_device &>(device).m_portB_r.set_callback(object); }
template<class _Object> static devcb2_base &set_portC_read_callback(device_t &device, _Object object) { return downcast<nsc810_device &>(device).m_portC_r.set_callback(object); }
template<class _Object> static devcb2_base &set_portA_write_callback(device_t &device, _Object object) { return downcast<nsc810_device &>(device).m_portA_w.set_callback(object); }
template<class _Object> static devcb2_base &set_portB_write_callback(device_t &device, _Object object) { return downcast<nsc810_device &>(device).m_portB_w.set_callback(object); }
template<class _Object> static devcb2_base &set_portC_write_callback(device_t &device, _Object object) { return downcast<nsc810_device &>(device).m_portC_w.set_callback(object); }
template<class _Object> static devcb2_base &set_timer0_callback(device_t &device, _Object object) { return downcast<nsc810_device &>(device).m_timer0_out.set_callback(object); }
template<class _Object> static devcb2_base &set_timer1_callback(device_t &device, _Object object) { return downcast<nsc810_device &>(device).m_timer1_out.set_callback(object); }
void set_timer0_clock(UINT32 clk) { m_timer0_clock = clk; }
void set_timer1_clock(UINT32 clk) { m_timer1_clock = clk; }
DECLARE_READ8_MEMBER(read);
DECLARE_WRITE8_MEMBER(write);
protected:
virtual void device_start();
virtual void device_reset();
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
private:
UINT8 m_portA_latch;
UINT8 m_portB_latch;
UINT8 m_portC_latch;
UINT8 m_ddrA;
UINT8 m_ddrB;
UINT8 m_ddrC;
UINT8 m_mode;
emu_timer* m_timer0;
emu_timer* m_timer1;
UINT8 m_timer0_mode;
UINT8 m_timer1_mode;
UINT16 m_timer0_counter;
UINT16 m_timer1_counter;
UINT16 m_timer0_base;
UINT16 m_timer1_base;
bool m_timer0_running;
bool m_timer1_running;
UINT32 m_timer0_clock;
UINT32 m_timer1_clock;
bool m_ramselect;
devcb2_read8 m_portA_r;
devcb2_read8 m_portB_r;
devcb2_read8 m_portC_r;
devcb2_write8 m_portA_w;
devcb2_write8 m_portB_w;
devcb2_write8 m_portC_w;
devcb2_write_line m_timer0_out;
devcb2_write_line m_timer1_out;
static const device_timer_id TIMER0_CLOCK = 0;
static const device_timer_id TIMER1_CLOCK = 1;
enum
{
REG_PORTA = 0x00,
REG_PORTB,
REG_PORTC,
REG_DDRA = 0x04,
REG_DDRB,
REG_DDRC,
REG_MODE_DEF,
REG_PORTA_BITCLR,
REG_PORTB_BITCLR,
REG_PORTC_BITCLR,
REG_PORTA_BITSET = 0x0c,
REG_PORTB_BITSET,
REG_PORTC_BITSET,
REG_TIMER0_LOW = 0x10,
REG_TIMER0_HIGH,
REG_TIMER1_LOW,
REG_TIMER1_HIGH,
REG_TIMER0_STOP,
REG_TIMER0_START,
REG_TIMER1_STOP,
REG_TIMER1_START,
REG_MODE_TIMER0,
REG_MODE_TIMER1
};
};
#define MCFG_NSC810_ADD(_tag, _t0clk, _t1clk) \
MCFG_DEVICE_ADD(_tag, NSC810, 0) \
downcast<nsc810_device *>(device)->set_timer0_clock(_t0clk); \
downcast<nsc810_device *>(device)->set_timer1_clock(_t1clk);
#define MCFG_NSC810_PORTA_READ(_read) \
devcb = &nsc810_device::set_portA_read_callback(*device, DEVCB2_##_read);
#define MCFG_NSC810_PORTB_READ(_read) \
devcb = &nsc810_device::set_portB_read_callback(*device, DEVCB2_##_read);
#define MCFG_NSC810_PORTC_READ(_read) \
devcb = &nsc810_device::set_portC_read_callback(*device, DEVCB2_##_read);
#define MCFG_NSC810_PORTA_WRITE(_write) \
devcb = &nsc810_device::set_portA_write_callback(*device, DEVCB2_##_write);
#define MCFG_NSC810_PORTB_WRITE(_write) \
devcb = &nsc810_device::set_portB_write_callback(*device, DEVCB2_##_write);
#define MCFG_NSC810_PORTC_WRITE(_write) \
devcb = &nsc810_device::set_portC_write_callback(*device, DEVCB2_##_write);
#define MCFG_NSC810_TIMER0_OUT(_write) \
devcb = &nsc810_device::set_timer0_callback(*device, DEVCB2_##_write);
#define MCFG_NSC810_TIMER1_OUT(_write) \
devcb = &nsc810_device::set_timer1_callback(*device, DEVCB2_##_write);
// device type definition
extern const device_type NSC810;
#endif /* NSC810_H_ */

View File

@ -29,6 +29,7 @@
#include "machine/mm58274c.h"
#include "rendlay.h"
#include "sound/speaker.h"
#include "machine/nsc810.h"
class hunter2_state : public driver_device
{
@ -40,6 +41,7 @@ public:
{ }
DECLARE_READ8_MEMBER(port00_r);
DECLARE_READ8_MEMBER(port01_r);
DECLARE_WRITE8_MEMBER(port01_w);
DECLARE_READ8_MEMBER(port02_r);
DECLARE_WRITE8_MEMBER(port60_w);
@ -50,6 +52,7 @@ public:
TIMER_DEVICE_CALLBACK_MEMBER(a_timer);
DECLARE_PALETTE_INIT(hunter2);
DECLARE_DRIVER_INIT(hunter2);
DECLARE_WRITE_LINE_MEMBER(timer0_out);
private:
UINT8 m_keydata;
@ -70,11 +73,11 @@ ADDRESS_MAP_END
static ADDRESS_MAP_START(hunter2_io, AS_IO, 8, hunter2_state)
ADDRESS_MAP_UNMAP_HIGH
ADDRESS_MAP_GLOBAL_MASK(0xff)
//AM_RANGE(0x00, 0x1f) AM_DEVREADWRITE("nsc810", nsc810_device, read, write) // device not yet emulated
AM_RANGE(0x00, 0x00) AM_READ(port00_r)
AM_RANGE(0x01, 0x01) AM_WRITE(port01_w)
AM_RANGE(0x02, 0x02) AM_READ(port02_r)
AM_RANGE(0x03, 0x1F) AM_WRITENOP
AM_RANGE(0x00, 0x1f) AM_DEVREADWRITE("iotimer", nsc810_device, read, write) // device not yet emulated
// AM_RANGE(0x00, 0x00) AM_READ(port00_r)
// AM_RANGE(0x01, 0x01) AM_WRITE(port01_w)
// AM_RANGE(0x02, 0x02) AM_READ(port02_r)
// AM_RANGE(0x03, 0x1F) AM_WRITENOP
AM_RANGE(0x20, 0x20) AM_DEVWRITE("lcdc", hd61830_device, data_w)
AM_RANGE(0x21, 0x21) AM_DEVREADWRITE("lcdc", hd61830_device, status_r, control_w)
AM_RANGE(0x3e, 0x3e) AM_DEVREAD("lcdc", hd61830_device, data_r)
@ -176,14 +179,21 @@ READ8_MEMBER( hunter2_state::port00_r )
return data;
}
READ8_MEMBER( hunter2_state::port01_r )
{
// TODO: bit 7 = RS232 DSR line
return 0x00;
}
WRITE8_MEMBER( hunter2_state::port01_w )
{
m_keydata = data;
logerror("Key row select %02x\n",data);
}
READ8_MEMBER( hunter2_state::port02_r )
{
return 0x2c;
return 0x28;
}
WRITE8_MEMBER( hunter2_state::port60_w )
@ -305,6 +315,12 @@ TIMER_DEVICE_CALLBACK_MEMBER(hunter2_state::a_timer)
m_maincpu->set_input_line(NSC800_RSTA, HOLD_LINE);
}
WRITE_LINE_MEMBER(hunter2_state::timer0_out)
{
if(state == ASSERT_LINE)
m_maincpu->set_input_line(INPUT_LINE_NMI, PULSE_LINE);
}
static MACHINE_CONFIG_START( hunter2, hunter2_state )
/* basic machine hardware */
MCFG_CPU_ADD("maincpu", NSC800, 4000000)
@ -330,6 +346,14 @@ static MACHINE_CONFIG_START( hunter2, hunter2_state )
/* Devices */
MCFG_MM58274C_ADD("rtc", rtc_intf)
//MCFG_TIMER_DRIVER_ADD_PERIODIC("hunter_a", hunter2_state, a_timer, attotime::from_hz(61))
MCFG_NSC810_ADD("iotimer",XTAL_4MHz,XTAL_4MHz)
MCFG_NSC810_PORTA_READ(READ8(hunter2_state,port00_r))
MCFG_NSC810_PORTB_READ(READ8(hunter2_state,port01_r))
MCFG_NSC810_PORTB_WRITE(WRITE8(hunter2_state,port01_w))
MCFG_NSC810_PORTC_READ(READ8(hunter2_state,port02_r))
MCFG_NSC810_TIMER0_OUT(WRITELINE(hunter2_state,timer0_out))
MCFG_NSC810_TIMER1_OUT(INPUTLINE("maincpu",NSC800_RSTA))
MACHINE_CONFIG_END
/* ROM definition */

View File

@ -486,6 +486,7 @@ MACHINES += PC_FDC
MACHINES += DP8390
MACHINES += MPU401
MACHINES += AT_KEYBC
MACHINES += NSC810
#MACHINES += PROFILE
#-------------------------------------------------
@ -2096,6 +2097,7 @@ $(MESSOBJ)/skeleton.a: \
$(MESS_DRIVERS)/hpz80unk.o \
$(MESS_DRIVERS)/ht68k.o \
$(MESS_DRIVERS)/hunter2.o \
$(EMU_MACHINE)/nsc810.o \
$(MESS_DRIVERS)/ibm6580.o \
$(MESS_DRIVERS)/ie15.o \
$(MESS_DRIVERS)/if800.o \