diff options
Diffstat (limited to 'src/emu/machine')
30 files changed, 11868 insertions, 0 deletions
diff --git a/src/emu/machine/74145.c b/src/emu/machine/74145.c new file mode 100644 index 00000000000..eb3457a7cd8 --- /dev/null +++ b/src/emu/machine/74145.c @@ -0,0 +1,176 @@ +/***************************************************************************** + * + * machine/74145.c + * + * BCD-to-Decimal decoder + * + * __ __ + * 0-| v |-VCC + * 1-| |-A + * 2-| |-B + * 3-| |-C + * 4-| |-D + * 5-| |-9 + * 6-| |-8 + * GND-|_____|-7 + * + * + * Truth table + * _______________________________ + * | Inputs | Outputs | + * | D C B A | 0 1 2 3 4 5 6 7 8 9 | + * |-------------------------------| + * | L L L L | L H H H H H H H H H | + * | L L L H | H L H H H H H H H H | + * | L L H L | H H L H H H H H H H | + * | L L H H | H H H L H H H H H H | + * | L H L L | H H H H L H H H H H | + * |-------------------------------| + * | L H L H | H H H H H L H H H H | + * | L H H L | H H H H H H L H H H | + * | L H H H | H H H H H H H L H H | + * | H L L L | H H H H H H H H L H | + * | H L L H | H H H H H H H H H L | + * |-------------------------------| + * | H L H L | H H H H H H H H H H | + * | H L H H | H H H H H H H H H H | + * | H H L L | H H H H H H H H H H | + * | H H L H | H H H H H H H H H H | + * | H H H L | H H H H H H H H H H | + * | H H H H | H H H H H H H H H H | + * ------------------------------- + * + ****************************************************************************/ + +#include "emu.h" +#include "74145.h" +#include "coreutil.h" + +/***************************************************************************** + GLOBAL VARIABLES +*****************************************************************************/ + +const ttl74145_interface default_ttl74145 = +{ + DEVCB_NULL, + DEVCB_NULL, + DEVCB_NULL, + DEVCB_NULL, + DEVCB_NULL, + DEVCB_NULL, + DEVCB_NULL, + DEVCB_NULL, + DEVCB_NULL, + DEVCB_NULL +}; + + +const device_type TTL74145 = &device_creator<ttl74145_device>; + +/*************************************************************************** + DEVICE INTERFACE +***************************************************************************/ +//------------------------------------------------- +// ttl74145_device - constructor +//------------------------------------------------- + +ttl74145_device::ttl74145_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, TTL74145, "TTL74145", tag, owner, clock) + , m_number(0) +{ +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void ttl74145_device::device_start() +{ + /* resolve callbacks */ + m_output_line_0_func.resolve(m_output_line_0_cb, *this); + m_output_line_1_func.resolve(m_output_line_1_cb, *this); + m_output_line_2_func.resolve(m_output_line_2_cb, *this); + m_output_line_3_func.resolve(m_output_line_3_cb, *this); + m_output_line_4_func.resolve(m_output_line_4_cb, *this); + m_output_line_5_func.resolve(m_output_line_5_cb, *this); + m_output_line_6_func.resolve(m_output_line_6_cb, *this); + m_output_line_7_func.resolve(m_output_line_7_cb, *this); + m_output_line_8_func.resolve(m_output_line_8_cb, *this); + m_output_line_9_func.resolve(m_output_line_9_cb, *this); + + // register for state saving + save_item(NAME(m_number)); +} + +//------------------------------------------------- +// device_config_complete - perform any +// operations now that the configuration is +// complete +//------------------------------------------------- + +void ttl74145_device::device_config_complete() +{ + // inherit a copy of the static data + const ttl74145_interface *intf = reinterpret_cast<const ttl74145_interface *>(static_config()); + if (intf != NULL) + *static_cast<ttl74145_interface *>(this) = *intf; + + // or initialize to defaults if none provided + else + { + memset(&m_output_line_0_cb, 0, sizeof(m_output_line_0_cb)); + memset(&m_output_line_1_cb, 0, sizeof(m_output_line_1_cb)); + memset(&m_output_line_2_cb, 0, sizeof(m_output_line_2_cb)); + memset(&m_output_line_3_cb, 0, sizeof(m_output_line_3_cb)); + memset(&m_output_line_4_cb, 0, sizeof(m_output_line_4_cb)); + memset(&m_output_line_5_cb, 0, sizeof(m_output_line_5_cb)); + memset(&m_output_line_6_cb, 0, sizeof(m_output_line_6_cb)); + memset(&m_output_line_7_cb, 0, sizeof(m_output_line_7_cb)); + memset(&m_output_line_8_cb, 0, sizeof(m_output_line_8_cb)); + memset(&m_output_line_9_cb, 0, sizeof(m_output_line_9_cb)); + } +} + +//------------------------------------------------- +// device_start - device-specific reset +//------------------------------------------------- + +void ttl74145_device::device_reset() +{ + m_number = 0; +} + +/*************************************************************************** + IMPLEMENTATION +***************************************************************************/ + +void ttl74145_device::write(UINT8 data) +{ + /* decode number */ + UINT16 new_number = bcd_2_dec(data & 0x0f); + + /* call output callbacks if the number changed */ + if (new_number != m_number) + { + m_output_line_0_func(new_number == 0); + m_output_line_1_func(new_number == 1); + m_output_line_2_func(new_number == 2); + m_output_line_3_func(new_number == 3); + m_output_line_4_func(new_number == 4); + m_output_line_5_func(new_number == 5); + m_output_line_6_func(new_number == 6); + m_output_line_7_func(new_number == 7); + m_output_line_8_func(new_number == 8); + m_output_line_9_func(new_number == 9); + } + + /* update state */ + m_number = new_number; +} + + +UINT16 ttl74145_device::read() +{ + return (1 << m_number) & 0x3ff; +} diff --git a/src/emu/machine/74145.h b/src/emu/machine/74145.h new file mode 100644 index 00000000000..71cfa9ba2ac --- /dev/null +++ b/src/emu/machine/74145.h @@ -0,0 +1,83 @@ +/*************************************************************************** + + TTL74145 + + BCD-to-Decimal decoder + +***************************************************************************/ + +#ifndef __TTL74145_H__ +#define __TTL74145_H__ + +//************************************************************************** +// INTERFACE CONFIGURATION MACROS +//************************************************************************** +#define MCFG_TTL74145_ADD(_tag, _intf) \ + MCFG_DEVICE_ADD(_tag, TTL74145, 0) \ + MCFG_DEVICE_CONFIG(_intf) + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> ttl74145_interface + +struct ttl74145_interface +{ + devcb_write_line m_output_line_0_cb; + devcb_write_line m_output_line_1_cb; + devcb_write_line m_output_line_2_cb; + devcb_write_line m_output_line_3_cb; + devcb_write_line m_output_line_4_cb; + devcb_write_line m_output_line_5_cb; + devcb_write_line m_output_line_6_cb; + devcb_write_line m_output_line_7_cb; + devcb_write_line m_output_line_8_cb; + devcb_write_line m_output_line_9_cb; +}; + +// ======================> ttl74145_device + +class ttl74145_device : public device_t, + public ttl74145_interface +{ +public: + // construction/destruction + ttl74145_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + UINT16 read(); + void write(UINT8 data); +protected: + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + virtual void device_config_complete(); + +private: + devcb_resolved_write_line m_output_line_0_func; + devcb_resolved_write_line m_output_line_1_func; + devcb_resolved_write_line m_output_line_2_func; + devcb_resolved_write_line m_output_line_3_func; + devcb_resolved_write_line m_output_line_4_func; + devcb_resolved_write_line m_output_line_5_func; + devcb_resolved_write_line m_output_line_6_func; + devcb_resolved_write_line m_output_line_7_func; + devcb_resolved_write_line m_output_line_8_func; + devcb_resolved_write_line m_output_line_9_func; + + /* decoded number */ + UINT16 m_number; +}; + +// device type definition +extern const device_type TTL74145; + +//************************************************************************** +// DEFAULT INTERFACES +//************************************************************************** + +extern const ttl74145_interface default_ttl74145; + + +#endif /* TTL74145 */ diff --git a/src/emu/machine/at29040a.c b/src/emu/machine/at29040a.c new file mode 100644 index 00000000000..95982e67bf0 --- /dev/null +++ b/src/emu/machine/at29040a.c @@ -0,0 +1,444 @@ +/* + Atmel at29c040a flash EEPROM + + 512k*8 FEEPROM, organized in pages of 256 bytes. + + References: + Datasheets were found on Atmel's site (www.atmel.com) + + Raphael Nabet 2003 + + September 2010: Rewritten as device + February 2012: Rewritten as class +*/ + +#include "at29040a.h" + +#define VERBOSE 2 +#define LOG logerror + +#define FEEPROM_SIZE 0x80000 +#define SECTOR_SIZE 0x00100 +#define BOOT_BLOCK_SIZE 0x04000 + +#define ADDRESS_MASK 0x7ffff +#define SECTOR_ADDRESS_MASK 0x7ff00 +#define BYTE_ADDRESS_MASK 0x000ff + +#define PRG_TIMER 1 + +#define VERSION 0 + +/* + Constructor. +*/ +at29040a_device::at29040a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) +: device_t(mconfig, AT29040A, "ATMEL 29040A 512K*8 FEEPROM", tag, owner, clock), + device_nvram_interface(mconfig, *this) +{ +} + +//------------------------------------------------- +// nvram_default - called to initialize NVRAM to +// its default state +//------------------------------------------------- + +void at29040a_device::nvram_default() +{ + memset(m_eememory, 0, FEEPROM_SIZE+2); +} + +//------------------------------------------------- +// nvram_read - called to read NVRAM from the +// .nv file +//------------------------------------------------- + +void at29040a_device::nvram_read(emu_file &file) +{ + file.read(m_eememory, FEEPROM_SIZE+2); +} + +//------------------------------------------------- +// nvram_write - called to write NVRAM to the +// .nv file +//------------------------------------------------- + +void at29040a_device::nvram_write(emu_file &file) +{ + m_eememory[0] = VERSION; + file.write(m_eememory, FEEPROM_SIZE+2); +} + +/* + programming timer callback +*/ +void at29040a_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + switch (m_pgm) + { + case PGM_1: + /* programming cycle timeout */ + if (VERBOSE>7) LOG("at29040a: Programming cycle timeout\n"); + m_pgm = PGM_0; + break; + + case PGM_2: + /* programming cycle start */ + if (VERBOSE>7) LOG("at29040a: Sector write start\n"); + m_pgm = PGM_3; + /* max delay 10ms, typical delay 5 to 7 ms */ + m_programming_timer->adjust(attotime::from_msec(5)); + break; + + case PGM_3: + /* programming cycle end */ + memcpy(m_eememory + 2 + (m_programming_last_offset & ~0xff), m_programming_buffer, SECTOR_SIZE); + if (VERBOSE>7) LOG("at29040a: Sector write completed at location %04x + 2\n", (m_programming_last_offset & ~0xff)); + if (m_enabling_sdb) + { + m_sdp = true; + } + if (m_disabling_sdb) + { + m_sdp = false; + } + if (VERBOSE>7) LOG("at29040a: Software data protection = %d\n", m_sdp); + + m_pgm = PGM_0; + m_enabling_sdb = false; + m_disabling_sdb = false; + + break; + + default: + if (VERBOSE>1) LOG("internal error in %s %d\n", __FILE__, __LINE__); + break; + } +} + +void at29040a_device::sync_flags() +{ + if (m_lower_bbl) m_eememory[1] |= 0x04; + else m_eememory[1] &= ~0x04; + + if (m_higher_bbl) m_eememory[1] |= 0x02; + else m_eememory[1] &= ~0x02; + + if (m_sdp) m_eememory[1] |= 0x01; + else m_eememory[1] &= ~0x01; +} + +/* + read a byte from FEEPROM +*/ +READ8_MEMBER( at29040a_device::read ) +{ + int reply; + + offset &= ADDRESS_MASK; + + /* reading in the midst of any command sequence cancels it (right???) */ + m_cmd = CMD_0; + m_long_sequence = false; + // m_higher_bbl = true; // who says that? + + sync_flags(); + + /* reading before the start of a programming cycle cancels it (right???) */ + if (m_pgm == PGM_1) + { + // attempt to access a locked out boot block: cancel programming + // command if necessary + m_pgm = PGM_0; + m_enabling_sdb = false; + m_disabling_sdb = false; + m_programming_timer->adjust(attotime::never); + } + + if (m_id_mode) + { + switch (offset) + { + case 0x00000: + reply = 0x1f; // Manufacturer code + break; + + case 0x00001: + reply = 0xa4; // Device code + break; + + case 0x00002: + reply = m_lower_bbl? 0xff : 0xfe; + break; + + case 0x7fff2: + reply = m_higher_bbl? 0xff : 0xfe; + break; + + default: + reply = 0; + break; + } + } + else if ((m_pgm == PGM_2) || (m_pgm == PGM_3)) + { + if (m_pgm == PGM_2) + { // DATA* polling starts the programming cycle (right???) + m_pgm = PGM_3; + /* max delay 10ms, typical delay 5 to 7 ms */ + m_programming_timer->adjust(attotime::from_msec(5)); + } + + reply = m_toggle_bit? 0x02 : 0x00; + m_toggle_bit = !m_toggle_bit; + + if ((offset == m_programming_last_offset) && (! (m_programming_buffer[m_programming_last_offset & 0xff] & 0x01))) + reply |= 0x01; + } + else + reply = m_eememory[offset+2]; + + if (VERBOSE>7) LOG("at29040a: %05x -> %02x\n", offset, reply); + + return reply; +} + +/* + Write a byte to FEEPROM +*/ +WRITE8_MEMBER( at29040a_device::write ) +{ + offset &= ADDRESS_MASK; + if (VERBOSE>7) LOG("at29040a: %05x <- %02x\n", offset, data); + + /* The special CFI commands assume a smaller address space according */ + /* to the specification ("address format A14-A0") */ + offs_t cfi_offset = offset & 0x7fff; + + if (m_enabling_bbl) + { + if (VERBOSE>7) LOG("at29040a: Enabling boot block lockout\n"); + m_enabling_bbl = false; + + if ((offset == 0x00000) && (data == 0x00)) + { + if (VERBOSE>7) LOG("at29040a: Enabling lower boot block lockout\n"); + m_lower_bbl = true; + sync_flags(); + return; + } + else + { + if ((offset == 0x7ffff) && (data == 0xff)) + { + if (VERBOSE>7) LOG("at29040a: Enabling higher boot block lockout\n"); + m_higher_bbl = true; + sync_flags(); + return; + } + else + { + if (VERBOSE>1) LOG("at29040a: Invalid boot block specification: %05x/%02x\n", offset, data); + } + } + } + + switch (m_cmd) + { + case CMD_0: + if ((cfi_offset == 0x5555) && (data == 0xaa)) + { + if (VERBOSE>7) LOG("at29040a: Command sequence started\n"); + m_cmd = CMD_1; + } + else + { + m_cmd = CMD_0; + m_long_sequence = false; + } + break; + + case CMD_1: + if ((cfi_offset == 0x2aaa) && (data == 0x55)) + { + m_cmd = CMD_2; + } + else + { + m_cmd = CMD_0; + m_long_sequence = false; + if (VERBOSE>7) LOG("at29040a: Command sequence aborted\n"); + } + break; + + case CMD_2: + if (cfi_offset == 0x5555) + { + if (!m_long_sequence) + if (VERBOSE>7) LOG("at29040a: Command sequence completed\n"); + + m_pgm = PGM_0; + m_enabling_sdb = false; + m_disabling_sdb = false; + m_programming_timer->adjust(attotime::never); + + /* process command */ + switch (data) + { + case 0x10: + /* Software chip erase */ + if (m_long_sequence) + { + if (m_lower_bbl || m_higher_bbl) + { + if (VERBOSE>1) LOG("at29040a: Chip erase sequence deactivated due to previous boot block lockout.\n"); + } + else + { + if (VERBOSE>7) LOG("at29040a: Erase chip\n"); + memset(m_eememory+2, 0xff, FEEPROM_SIZE); + } + } + break; + + case 0x20: + /* Software data protection disable */ + if (VERBOSE>7) LOG("at29040a: Software data protection disable\n"); + // The complete sequence is aa-55-80-aa-55-20 + // so we need a 80 before, else the sequence is invalid + if (m_long_sequence) + { + m_pgm = PGM_1; + m_disabling_sdb = true; + /* set command timeout (right???) */ + //m_programming_timer->adjust(attotime::from_usec(150), id, 0.); + } + break; + + case 0x40: + /* Boot block lockout enable */ + // Complete sequence is aa-55-80-aa-55-40 + if (VERBOSE>7) LOG("at29040a: Boot block lockout enable\n"); + if (m_long_sequence) m_enabling_bbl = true; + break; + + case 0x80: + m_long_sequence = true; + break; + + case 0x90: + /* Software product identification entry */ + if (VERBOSE>7) LOG("at29040a: Identification mode (start)\n"); + m_id_mode = true; + break; + + case 0xa0: + /* Software data protection enable */ + if (VERBOSE>7) LOG("at29040a: Software data protection enable\n"); + m_pgm = PGM_1; + m_enabling_sdb = true; + /* set command timeout (right???) */ + //m_programming_timer->adjust(attotime::from_usec(150), id, 0.); + break; + + case 0xf0: + /* Software product identification exit */ + if (VERBOSE>7) LOG("at29040a: Identification mode (end)\n"); + m_id_mode = false; + break; + } + m_cmd = CMD_0; + if (data != 0x80) m_long_sequence = false; + + /* return, because we don't want to write the EEPROM with the command byte */ + return; + } + else + { + m_cmd = CMD_0; + m_long_sequence = false; + } + } + if ((m_pgm == PGM_2) + && ((offset & ~0xff) != (m_programming_last_offset & ~0xff))) + { + /* cancel current programming cycle */ + if (VERBOSE>7) LOG("at29040a: invalid sector change (from %05x to %05x); cancel programming cycle\n",(offset & ~0xff), (m_programming_last_offset & ~0xff)); + m_pgm = PGM_0; + m_enabling_sdb = false; + m_disabling_sdb = false; + m_programming_timer->adjust(attotime::never); + } + + if (((m_pgm == PGM_0) && !m_sdp) // write directly + || (m_pgm == PGM_1)) // write after unlocking + { + if (((offset < BOOT_BLOCK_SIZE) && m_lower_bbl) + || ((offset >= FEEPROM_SIZE-BOOT_BLOCK_SIZE) && m_higher_bbl)) + { + // attempt to access a locked out boot block: cancel programming + // command if necessary + if (VERBOSE>7) LOG("at29040a: attempt to access a locked out boot block: offset = %05x, lowblock=%d, highblock=%d\n", offset, m_lower_bbl, m_higher_bbl); + + m_pgm = PGM_0; + m_enabling_sdb = false; + m_disabling_sdb = false; + } + else + { /* enter programming mode */ + if (VERBOSE>7) LOG("at29040a: enter programming mode (m_pgm=%d)\n", m_pgm); + memset(m_programming_buffer, 0xff, SECTOR_SIZE); + m_pgm = PGM_2; + } + } + if (m_pgm == PGM_2) + { + /* write data to programming buffer */ + if (VERBOSE>7) LOG("at29040a: Write data to programming buffer\n"); + m_programming_buffer[offset & 0xff] = data; + m_programming_last_offset = offset; + m_programming_timer->adjust(attotime::from_usec(150)); // next byte must be written before the timer expires + } +} + +void at29040a_device::device_start(void) +{ + m_programming_buffer = (UINT8*)malloc(SECTOR_SIZE); + m_programming_timer = timer_alloc(PRG_TIMER); + + m_eememory = (UINT8*)malloc(FEEPROM_SIZE+2); +} + +void at29040a_device::device_stop(void) +{ + free(m_programming_buffer); + free(m_eememory); +} + +void at29040a_device::device_reset(void) +{ + if (m_eememory[0] != VERSION) + { + if (VERBOSE>1) LOG("AT29040A: Warning: Version mismatch; expected %d but found %d for %s. Resetting.\n", VERSION, m_eememory[0], tag()); + m_eememory[0] = 0; + m_eememory[1] = 0; + } + + m_lower_bbl = ((m_eememory[1] & 0x04)!=0); + m_higher_bbl = ((m_eememory[1] & 0x02)!=0); + m_sdp = ((m_eememory[1] & 0x01)!=0); + + if (VERBOSE>7) LOG("at29040a (%s): LowerBBL = %d, HigherBBL = %d, SoftDataProt = %d\n", tag(), m_lower_bbl, m_higher_bbl, m_sdp); + + m_id_mode = false; + m_cmd = CMD_0; + m_enabling_bbl = false; + m_long_sequence = false; + m_pgm = PGM_0; + m_enabling_sdb = false; + m_disabling_sdb = false; + m_toggle_bit = false; + m_programming_last_offset = 0; +} + +const device_type AT29040A = &device_creator<at29040a_device>; diff --git a/src/emu/machine/at29040a.h b/src/emu/machine/at29040a.h new file mode 100644 index 00000000000..4a50e6e6b0c --- /dev/null +++ b/src/emu/machine/at29040a.h @@ -0,0 +1,88 @@ +/* + ATMEL 29040a + + Michael Zapf + September 2010: Rewritten as device + February 2012: Rewritten as class +*/ + +#ifndef __AT29040__ +#define __AT29040__ + +#include "emu.h" + +extern const device_type AT29040A; + +/* + at29c40a state + + Command states (CMD_0 is the initial state): + CMD_0: default state + CMD_1: state after writing aa to 5555 + CMD_2: state after writing 55 to 2aaa + + Programming states (s_programming_0 is the initial state): + PGM_0: default state + PGM_1: a program and enable/disable lock command has been executed, but programming has not actually started. + PGM_2: the programming buffer is being written to + PGM_3: the programming buffer is being burnt to flash ROM +*/ +enum s_cmd_t +{ + CMD_0 = 0x0, + CMD_1 = 0x1, + CMD_2 = 0x2 +}; + +enum s_pgm_t +{ + PGM_0 = 0x0, + PGM_1 = 0x1, + PGM_2 = 0x2, + PGM_3 = 0x3 +}; + +class at29040a_device : public device_t, public device_nvram_interface +{ +public: + at29040a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + DECLARE_READ8_MEMBER( read ); + DECLARE_WRITE8_MEMBER( write ); + +protected: + virtual void device_start(void); + virtual void device_reset(void); + virtual void device_stop(void); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + + void nvram_default(); + void nvram_read(emu_file &file); + void nvram_write(emu_file &file); + +private: + void sync_flags(void); + + UINT8* m_eememory; + + bool m_lower_bbl; /* set when lower boot block lockout is enabled */ + bool m_higher_bbl; /* set when upper boot block lockout is enabled */ + bool m_sdp; /* set when in software data protect mode */ + + bool m_id_mode; /* set when in chip id mode */ + s_cmd_t m_cmd; /* command state */ + bool m_enabling_bbl; /* set when a boot block lockout command is expecting its parameter */ + bool m_long_sequence; /* set if 0x80 command has just been executed (some command require this prefix) */ + s_pgm_t m_pgm; /* programming state */ + bool m_enabling_sdb; /* set when a sdp enable command is in progress */ + bool m_disabling_sdb; /* set when a sdp disable command is in progress */ + bool m_dirty; /* set when the memory contents should be set */ + bool m_toggle_bit; // indicates flashing in progress (toggles for each query) + UINT8* m_programming_buffer; + int m_programming_last_offset; + emu_timer* m_programming_timer; +}; + +#define MCFG_AT29040A_ADD(_tag ) \ + MCFG_DEVICE_ADD(_tag, AT29040A, 0) + +#endif diff --git a/src/emu/machine/at45dbxx.c b/src/emu/machine/at45dbxx.c new file mode 100644 index 00000000000..0128ccf7469 --- /dev/null +++ b/src/emu/machine/at45dbxx.c @@ -0,0 +1,375 @@ +/* + + Atmel Serial DataFlash + + (c) 2001-2007 Tim Schuerewegen + + AT45DB041 - 528 KByte + AT45DB081 - 1056 KByte + AT45DB161 - 2112 KByte + +*/ + +#include "at45dbxx.h" + +#define LOG_LEVEL 1 +#define _logerror(level,x) do { if (LOG_LEVEL > level) logerror x; } while (0) + +#define FLASH_CMD_52 0x52 +#define FLASH_CMD_57 0x57 +#define FLASH_CMD_60 0x60 +#define FLASH_CMD_82 0x82 + +#define FLASH_MODE_XX 0 // unknown +#define FLASH_MODE_SI 1 // input +#define FLASH_MODE_SO 2 // output + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +// device type definition +const device_type AT45DB041 = &device_creator<at45db041_device>; +const device_type AT45DB081 = &device_creator<at45db081_device>; +const device_type AT45DB161 = &device_creator<at45db161_device>; + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// at45db041_device - constructor +//------------------------------------------------- + +at45db041_device::at45db041_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, AT45DB041, "AT45DB041", tag, owner, clock), + device_nvram_interface(mconfig, *this) +{ +} + + +at45db041_device::at45db041_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, type, name, tag, owner, clock), + device_nvram_interface(mconfig, *this) +{ +} + + +at45db081_device::at45db081_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : at45db041_device(mconfig, AT45DB081, "AT45DB081", tag, owner, clock) +{ +} + + +at45db161_device::at45db161_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : at45db041_device(mconfig, AT45DB161, "AT45DB161", tag, owner, clock) +{ +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void at45db041_device::device_start() +{ + m_size = num_pages() * page_size(); + m_data = auto_alloc_array(machine(), UINT8, m_size); + m_buffer1 = auto_alloc_array(machine(), UINT8, page_size()); + m_buffer2 = auto_alloc_array(machine(), UINT8, page_size()); + + // data + save_pointer(NAME(m_data), m_size); + // pins + save_item(NAME(m_pin.cs)); + save_item(NAME(m_pin.sck)); + save_item(NAME(m_pin.si)); + save_item(NAME(m_pin.so)); + save_item(NAME(m_pin.wp)); + save_item(NAME(m_pin.reset)); + save_item(NAME(m_pin.busy)); +} + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void at45db041_device::device_reset() +{ + _logerror( 1, ("at45dbxx_reset\n")); + // mode + m_mode = FLASH_MODE_SI; + // command + memset(&m_cmd.data[0], 0, sizeof(m_cmd.data)); + m_cmd.size = 0; + // input/output + m_io.data = NULL; + m_io.size = 0; + m_io.pos = 0; + // pins + m_pin.cs = 0; + m_pin.sck = 0; + m_pin.si = 0; + m_pin.so = 0; + m_pin.wp = 0; + m_pin.reset = 0; + m_pin.busy = 0; + // output + m_so_byte = 0; + m_so_bits = 0; + // input + m_si_byte = 0; + m_si_bits = 0; +} + + +//------------------------------------------------- +// nvram_default - called to initialize NVRAM to +// its default state +//------------------------------------------------- + +void at45db041_device::nvram_default() +{ + memset(m_data, 0xff, m_size); + + if (region() != NULL) + { + UINT32 bytes = region()->bytes(); + if (bytes > m_size) + bytes = m_size; + + memcpy(m_data, region()->base(), bytes); + } +} + +//------------------------------------------------- +// nvram_read - called to read NVRAM from the +// .nv file +//------------------------------------------------- + +void at45db041_device::nvram_read(emu_file &file) +{ + file.read(m_data, m_size); +} + +//------------------------------------------------- +// nvram_write - called to write NVRAM to the +// .nv file +//------------------------------------------------- + +void at45db041_device::nvram_write(emu_file &file) +{ + file.write(m_data, m_size); +} + +UINT8 at45db041_device::read_byte() +{ + UINT8 data; + // check mode + if ((m_mode != FLASH_MODE_SO) || (!m_io.data)) return 0; + // read byte + data = m_io.data[m_io.pos++]; + _logerror( 2, ("at45dbxx_read_byte (%02X) (%03d/%03d)\n", data, m_io.pos, m_io.size)); + if (m_io.pos == m_io.size) m_io.pos = 0; + return data; +} + +void at45db041_device::flash_set_io(UINT8* data, UINT32 size, UINT32 pos) +{ + m_io.data = data; + m_io.size = size; + m_io.pos = pos; +} + +UINT32 at45db041_device::flash_get_page_addr() +{ + return ((m_cmd.data[1] & 0x0F) << 7) | ((m_cmd.data[2] & 0xFE) >> 1); +} + +UINT32 at45db041_device::flash_get_byte_addr() +{ + return ((m_cmd.data[2] & 0x01) << 8) | ((m_cmd.data[3] & 0xFF) >> 0); +} + +UINT32 at45db081_device::flash_get_page_addr() +{ + return ((m_cmd.data[1] & 0x1F) << 7) | ((m_cmd.data[2] & 0xFE) >> 1); +} + +UINT32 at45db161_device::flash_get_page_addr() +{ + return ((m_cmd.data[1] & 0x3F) << 6) | ((m_cmd.data[2] & 0xFC) >> 2); +} + +UINT32 at45db161_device::flash_get_byte_addr() +{ + return ((m_cmd.data[2] & 0x03) << 8) | ((m_cmd.data[3] & 0xFF) >> 0); +} + +void at45db041_device::write_byte(UINT8 data) +{ + // check mode + if (m_mode != FLASH_MODE_SI) return; + // process byte + if (m_cmd.size < 8) + { + UINT8 opcode; + _logerror( 2, ("at45dbxx_write_byte (%02X)\n", data)); + // add to command buffer + m_cmd.data[m_cmd.size++] = data; + // check opcode + opcode = m_cmd.data[0]; + switch (opcode) + { + // status register read + case FLASH_CMD_57 : + { + // 8 bits command + if (m_cmd.size == 1) + { + _logerror( 1, ("at45dbxx opcode %02X - status register read\n", opcode)); + m_status = (m_status & 0xC7) | device_id(); // 80 = busy / 40 = compare fail + flash_set_io(&m_status, 1, 0); + m_mode = FLASH_MODE_SO; + m_cmd.size = 8; + } + } + break; + // main memory page to buffer 1 compare + case FLASH_CMD_60 : + { + // 8 bits command + 4 bits reserved + 11 bits page address + 9 bits don't care + if (m_cmd.size == 4) + { + UINT32 page; + UINT8 comp; + page = flash_get_page_addr(); + _logerror( 1, ("at45dbxx opcode %02X - main memory page to buffer 1 compare [%04X]\n", opcode, page)); + comp = memcmp( m_data + page * page_size(), m_buffer1, page_size()) == 0 ? 0 : 1; + if (comp) m_status |= 0x40; else m_status &= ~0x40; + _logerror( 1, ("at45dbxx page compare %s\n", comp ? "failure" : "success")); + m_mode = FLASH_MODE_SI; + m_cmd.size = 8; + } + } + break; + // main memory page read + case FLASH_CMD_52 : + { + // 8 bits command + 4 bits reserved + 11 bits page address + 9 bits buffer address + 32 bits don't care + if (m_cmd.size == 8) + { + UINT32 page, byte; + page = flash_get_page_addr(); + byte = flash_get_byte_addr(); + _logerror( 1, ("at45dbxx opcode %02X - main memory page read [%04X/%04X]\n", opcode, page, byte)); + flash_set_io(m_data + page * page_size(), page_size(), byte); + m_mode = FLASH_MODE_SO; + m_cmd.size = 8; + } + } + break; + // main memory page program through buffer 1 + case FLASH_CMD_82 : + { + // 8 bits command + 4 bits reserved + 11 bits page address + 9 bits buffer address + if (m_cmd.size == 4) + { + UINT32 page, byte; + page = flash_get_page_addr(); + byte = flash_get_byte_addr(); + _logerror( 1, ("at45dbxx opcode %02X - main memory page program through buffer 1 [%04X/%04X]\n",opcode, page, byte)); + flash_set_io(m_buffer1, page_size(), byte); + memset( m_buffer1, 0xFF, page_size()); + m_mode = FLASH_MODE_SI; + m_cmd.size = 8; + } + } + break; + // other + default : + { + _logerror( 1, ("at45dbxx opcode %02X - unknown\n", opcode)); + m_cmd.data[0] = 0; + m_cmd.size = 0; + } + break; + } + } + else + { + _logerror( 2, ("at45dbxx_write_byte (%02X) (%03d/%03d)\n", data, m_io.pos + 1, m_io.size)); + // store byte + m_io.data[m_io.pos] = data; + m_io.pos++; + if (m_io.pos == m_io.size) m_io.pos = 0; + } +} + +READ_LINE_MEMBER(at45db041_device::so_r) +{ + if (m_pin.cs == 0) return 0; + return m_pin.so; +} + +WRITE_LINE_MEMBER(at45db041_device::si_w) +{ + if (m_pin.cs == 0) return; + m_pin.si = state; +} + +WRITE_LINE_MEMBER(at45db041_device::cs_w) +{ + // check if changed + if (m_pin.cs == state) return; + // cs low-to-high + if (state != 0) + { + // complete program command + if ((m_cmd.size >= 4) && (m_cmd.data[0] == FLASH_CMD_82)) + { + UINT32 page, byte; + page = flash_get_page_addr(); + byte = flash_get_byte_addr(); + _logerror( 1, ("at45dbxx - program data stored in buffer 1 into selected page in main memory [%04X/%04X]\n", page, byte)); + memcpy( m_data + page * page_size(), m_buffer1, page_size()); + } + // reset + at45db041_device::device_reset(); + } + // save cs + m_pin.cs = state; +} + +WRITE_LINE_MEMBER(at45db041_device::sck_w) +{ + // check if changed + if (m_pin.sck == state) return; + // sck high-to-low + if (state == 0) + { + // output (part 1) + if (m_so_bits == 8) + { + m_so_bits = 0; + m_so_byte = read_byte(); + } + // input + if (m_pin.si) m_si_byte = m_si_byte | (1 << m_si_bits); + m_si_bits++; + if (m_si_bits == 8) + { + m_si_bits = 0; + write_byte(m_si_byte); + m_si_byte = 0; + } + // output (part 2) + m_pin.so = (m_so_byte >> m_so_bits) & 1; + m_so_bits++; + } + // save sck + m_pin.sck = state; +} diff --git a/src/emu/machine/at45dbxx.h b/src/emu/machine/at45dbxx.h new file mode 100644 index 00000000000..1cc85cccadd --- /dev/null +++ b/src/emu/machine/at45dbxx.h @@ -0,0 +1,144 @@ +/* + + Atmel Serial DataFlash + + (c) 2001-2007 Tim Schuerewegen + + AT45DB041 - 528 KByte + AT45DB081 - 1056 KByte + AT45DB161 - 2112 KByte + +*/ + +#ifndef _AT45DBXX_H_ +#define _AT45DBXX_H_ + +#include "emu.h" + + +//************************************************************************** +// INTERFACE CONFIGURATION MACROS +//************************************************************************** + +#define MCFG_AT45DB041_ADD(_tag) \ + MCFG_DEVICE_ADD(_tag, AT45DB041, 0) + +#define MCFG_AT45DB081_ADD(_tag) \ + MCFG_DEVICE_ADD(_tag, AT45DB081, 0) + +#define MCFG_AT45DB161_ADD(_tag) \ + MCFG_DEVICE_ADD(_tag, AT45DB161, 0) + + +// ======================> at45db041_device + +class at45db041_device : public device_t, + public device_nvram_interface +{ +public: + at45db041_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + at45db041_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock); + + DECLARE_WRITE_LINE_MEMBER(cs_w); + DECLARE_WRITE_LINE_MEMBER(sck_w); + DECLARE_WRITE_LINE_MEMBER(si_w); + DECLARE_READ_LINE_MEMBER(so_r); + + UINT8 *get_ptr() { return m_data; } + +protected: + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + // device_nvram_interface overrides + virtual void nvram_default(); + virtual void nvram_read(emu_file &file); + virtual void nvram_write(emu_file &file); + +protected: + virtual int num_pages() const { return 2048; } + virtual int page_size() const { return 264; } + virtual UINT8 device_id() const { return 0x18; } + + UINT8 read_byte(); + void flash_set_io(UINT8* data, UINT32 size, UINT32 pos); + virtual UINT32 flash_get_page_addr(); + virtual UINT32 flash_get_byte_addr(); + void write_byte(UINT8 data); + + // internal state + UINT8 * m_data; + UINT32 m_size; + UINT8 m_mode; + UINT8 m_status; + UINT8 * m_buffer1; + UINT8 * m_buffer2; + UINT8 m_si_byte; + UINT8 m_si_bits; + UINT8 m_so_byte; + UINT8 m_so_bits; + + struct AT45DBXX_PINS + { + int cs; // chip select + int sck; // serial clock + int si; // serial input + int so; // serial output + int wp; // write protect + int reset; // reset + int busy; // busy + } m_pin; + + struct AT45DBXX_IO + { + UINT8 *data; + UINT32 size; + UINT32 pos; + } m_io; + + struct AT45DBXX_CMD + { + UINT8 data[8]; + UINT8 size; + } m_cmd; +}; + +// ======================> at45db081_device + +class at45db081_device : public at45db041_device +{ +public: + at45db081_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + +protected: + virtual int num_pages() const { return 4096; } + virtual int page_size() const { return 264; } + virtual UINT8 device_id() const { return 0x20; } + + virtual UINT32 flash_get_page_addr(); +}; + +// ======================> at45db161_device + +class at45db161_device : public at45db041_device +{ +public: + at45db161_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + +protected: + virtual int num_pages() const { return 4096; } + virtual int page_size() const { return 528; } + virtual UINT8 device_id() const { return 0x28; } + + virtual UINT32 flash_get_page_addr(); + virtual UINT32 flash_get_byte_addr(); +}; + + +// device type definition +extern const device_type AT45DB041; +extern const device_type AT45DB081; +extern const device_type AT45DB161; + +#endif diff --git a/src/emu/machine/ay31015.c b/src/emu/machine/ay31015.c new file mode 100644 index 00000000000..3b46014de19 --- /dev/null +++ b/src/emu/machine/ay31015.c @@ -0,0 +1,760 @@ +/**************************************************************************** + + ay31015.c by Robbbert, May 2008. Bugs fixed by Judge. + + Code for the AY-3-1014A, AY-3-1015(D), AY-5-1013(A), and AY-6-1013 UARTs + The HD6402 UART is compatible with the AY-3-1015 UART. + + This is cycle-accurate according to the specifications. + + It supports independent receive and transmit clocks, + and transmission and reception can occur simultaneously if desired. + +***************************************************************************** + +Differences between the chip types: +- All units have pull-up resistors on the inputs, except for the AY-3-1014A which is CMOS-compatible. +- AY-3-1014A and AY-3-1015 - 1.5 stop bits mode available. +- Max baud rate of 30k, except AY-5-1013 which has 20k. +- AY-5-1013 has extended temperature ratings. +- AY-5-1013 and AY-6-1013 require a -12 volt supply on pin 2. Pin is not used otherwise. +- AY-5-1013 and AY-6-1013 do not reset the received data register when XR pin is used. + +****************************************************************************** + +It is not clear in the documentation as to which settings will reset the device. + To be safe, we will always reset whenever the control register changes. + + Also, it not clear exactly what happens under various error conditions. + +******************************************************************************** + +Device Data: + +* Common Controls: +-- Pin 1 - Vcc - 5 volts +-- Pin 2 - not used (on AY-5-1013 and AY-6-1013 this is Voo = -12 volts) +-- Pin 3 - Gnd - 0 volts +-- Pin 21 - XR - External Reset - resets all registers to initial state except for the control register +-- Pin 35 - NP - No Parity - "1" will kill any parity processing +-- Pin 36 - TSB - Number of Stop Bits - "0" = 1 stop bit; "1" = 2 stop bits. If "1", and 5 bits per character, then we have 1.5 stop bits +-- pin 37 - NB1 +-- pin 38 - NB2 - Number of bits per character = NB1 + (NB2 * 2) + 5 +-- pin 39 - EPS - Odd or Even Parity Select - "0" = Odd parity; "1" = Even parity. Has no effect if NP is high. +-- Pin 34 - CS - Control Strobe - Read NP, TSB, EPS, NB1, NB2 into the control register. + +Format of data stream: +Start bit (low), Bit 0, Bit 1... highest bit, Parity bit (if enabled), 1-2 stop bits (high) + + +* Receiver Controls: +-- Pin 17 - RCP - Clock which is 16x the desired baud rate +-- Pin 20 - SI - Serial input stream - "1" = Mark (waiting for input), "0" = Space (Start bit) initiates the transfer of a byte +-- Pin 4 - RDE - "0" causes the received data to appear on RD1 to RD8. +-- Pins 5 to 12 - RD8 to RD1 - These are the data lines (bits 7 to 0). Data is right-justified. +-- Pin 16 - SWE - Status word enable - causes the status bits (PE, FE, OR, DAV, TBMT) to appear at the pins. +-- Pin 19 - DAV - "1" indicates that a byte has been received by the UART, and should now be accepted by the computer +-- Pin 18 - RDAV - "0" will force DAV low. +-- Pin 13 - PE - Parity error - "1" indicates that a parity error occurred +-- Pin 14 - FE - Framing error - "1" Indicates that the stop bit was missing +-- Pin 15 - OR - overrun - "1" indicates that a new character has become available before the computer had accepted the previous character + +* Transmitter controls: +-- Pin 40 - TCP - Clock which is 16x the desired baud rate +-- Pin 25 - SO - Serial output stream - it will stay at "1" while no data is being transmitted +-- Pins 26 to 33 - DB1 to DB8 - These are the data lines containing the byte to be sent +-- Pin 23 - DS - Data Strobe - "0" will copy DB1 to DB8 into the transmit buffer +-- Pin 22 - TBMT - Transmit buffer Empty - "1" indicates to the computer that another byte may be sent to the UART +-- Pin 24 - EOC - End of Character - "0" means that a character is being sent. + +******************************************* COMMON CONTROLS ********************************************************/ + +#include "emu.h" +#include "ay31015.h" + +enum state_t +{ + IDLE, + START_BIT, + PROCESSING, + PARITY_BIT, + FIRST_STOP_BIT, + SECOND_STOP_BIT, + PREP_TIME +}; + + +struct ay31015_t +{ + const ay31015_config *config; + + int pins[41]; + + UINT8 control_reg; + UINT8 status_reg; + UINT16 second_stop_bit; // 0, 8, 16 + UINT16 total_pulses; // bits * 16 + UINT8 internal_sample; + + state_t rx_state; + UINT8 rx_data; // byte being received + UINT8 rx_buffer; // received byte waiting to be accepted by computer + UINT8 rx_bit_count; + UINT8 rx_parity; + UINT16 rx_pulses; // total pulses left + double rx_clock; + emu_timer *rx_timer; + + state_t tx_state; + UINT8 tx_data; // byte being sent + UINT8 tx_buffer; // next byte to send + UINT8 tx_parity; + UINT16 tx_pulses; // total pulses left + double tx_clock; + emu_timer *tx_timer; + + devcb_resolved_read8 read_si; /* SI - pin 20 - This will be called whenever the SI pin is sampled. Optional */ + devcb_resolved_write8 write_so; /* SO - pin 25 - This will be called whenever data is put on the SO pin. Optional */ + devcb_resolved_write8 status_changed; /* This will be called whenever one of the status pins may have changed. Optional */ +}; + + +/* control reg */ +#define CONTROL_NB1 0x01 +#define CONTROL_NB2 0x02 +#define CONTROL_TSB 0x04 +#define CONTROL_EPS 0x08 +#define CONTROL_NP 0x10 + + +/* status reg */ +#define STATUS_TBMT 0x01 +#define STATUS_DAV 0x02 +#define STATUS_OR 0x04 +#define STATUS_FE 0x08 +#define STATUS_PE 0x10 +#define STATUS_EOC 0x20 + + +/*------------------------------------------------- + get_safe_token - safely gets the data +-------------------------------------------------*/ + +INLINE ay31015_t *get_safe_token(device_t *device) +{ + assert(device != NULL); + assert(device->type() == AY31015); + return (ay31015_t *) downcast<ay31015_device *>(device)->token(); +} + + +INLINE UINT8 ay31015_get_si( device_t *device ) +{ + ay31015_t *ay31015 = get_safe_token( device ); + + if ( !ay31015->read_si.isnull() ) + ay31015->pins[AY31015_SI] = ay31015->read_si( 0 ) ? 1 : 0; + + return ay31015->pins[AY31015_SI]; +} + + +INLINE void ay31015_set_so( device_t *device, int data ) +{ + ay31015_t *ay31015 = get_safe_token( device ); + + ay31015->pins[AY31015_SO] = data ? 1 : 0; + + if ( !ay31015->write_so.isnull() ) + ay31015->write_so( 0, ay31015->pins[AY31015_SO] ); +} + + +INLINE int ay31015_update_status_pin( ay31015_t *ay31015, UINT8 reg_bit, ay31015_output_pin_t pin ) +{ + int new_value = ( ay31015->status_reg & reg_bit ) ? 1 : 0; + + if ( new_value == ay31015->pins[pin] ) + return 0; + + ay31015->pins[pin] = new_value; + return 1; +} + + +/*------------------------------------------------- + ay31015_update_status_pins - Update the status pins +-------------------------------------------------*/ +static void ay31015_update_status_pins( device_t *device ) +{ + ay31015_t *ay31015 = get_safe_token( device ); + int status_pins_changed = 0; + + /* Should status pins be updated? */ + if ( ! ay31015->pins[AY31015_SWE] ) + { + status_pins_changed += ay31015_update_status_pin( ay31015, STATUS_PE, AY31015_PE ); + status_pins_changed += ay31015_update_status_pin( ay31015, STATUS_FE, AY31015_FE ); + status_pins_changed += ay31015_update_status_pin( ay31015, STATUS_OR, AY31015_OR ); + status_pins_changed += ay31015_update_status_pin( ay31015, STATUS_DAV, AY31015_DAV ); + status_pins_changed += ay31015_update_status_pin( ay31015, STATUS_TBMT, AY31015_TBMT ); + } + status_pins_changed += ay31015_update_status_pin( ay31015, STATUS_EOC, AY31015_EOC ); + + if ( status_pins_changed && !ay31015->status_changed.isnull() ) + { + ay31015->status_changed( 0, status_pins_changed ); + } +} + + +/*************************************************** RECEIVE CONTROLS *************************************************/ + + +/*------------------------------------------------- + ay31015_rx_process - convert serial to parallel +-------------------------------------------------*/ +static TIMER_CALLBACK( ay31015_rx_process ) +{ + device_t *device = (device_t *)ptr; + ay31015_t *ay31015 = get_safe_token( device ); + + switch (ay31015->rx_state) + { + case PREP_TIME: // assist sync by ensuring high bit occurs + ay31015->rx_pulses--; + if (ay31015_get_si( device )) + ay31015->rx_state = IDLE; + return; + + case IDLE: + ay31015->rx_pulses--; + if (!ay31015_get_si( device )) + { + ay31015->rx_state = START_BIT; + ay31015->rx_pulses = 16; + } + return; + + case START_BIT: + ay31015->rx_pulses--; + if (ay31015->rx_pulses == 8) // start bit must be low at sample time + { + if ( ay31015_get_si( device ) ) + ay31015->rx_state = IDLE; + } + else + if (!ay31015->rx_pulses) // end of start bit + { + ay31015->rx_state = PROCESSING; + ay31015->rx_pulses = ay31015->total_pulses; + ay31015->rx_bit_count = 0; + ay31015->rx_parity = 0; + ay31015->rx_data = 0; + } + return; + + case PROCESSING: + ay31015->rx_pulses--; + if (!ay31015->rx_pulses) // end of a byte + { + ay31015->rx_pulses = 16; + if (ay31015->control_reg & CONTROL_NP) // see if we need to get a parity bit + ay31015->rx_state = FIRST_STOP_BIT; + else + ay31015->rx_state = PARITY_BIT; + } + else + if (!(ay31015->rx_pulses & 15)) // end of a bit + ay31015->rx_bit_count++; + else + if ((ay31015->rx_pulses & 15) == 8) // sample input stream + { + ay31015->internal_sample = ay31015_get_si( device ); + ay31015->rx_parity ^= ay31015->internal_sample; // calculate cumulative parity + ay31015->rx_data |= ay31015->internal_sample << ay31015->rx_bit_count; + } + return; + + case PARITY_BIT: + ay31015->rx_pulses--; + + if (ay31015->rx_pulses == 8) // sample input stream + { + ay31015->rx_parity ^= ay31015_get_si( device ); // calculate cumulative parity + } + else + if (!ay31015->rx_pulses) // end of a byte + { + ay31015->rx_pulses = 16; + ay31015->rx_state = FIRST_STOP_BIT; + + if ((!(ay31015->control_reg & CONTROL_EPS)) && (ay31015->rx_parity)) + ay31015->rx_parity = 0; // odd parity, ok + else + if ((ay31015->control_reg & CONTROL_EPS) && (!ay31015->rx_parity)) + ay31015->rx_parity = 0; // even parity, ok + else + ay31015->rx_parity = 1; // parity error + } + return; + + case FIRST_STOP_BIT: + ay31015->rx_pulses--; + if (ay31015->rx_pulses == 8) // sample input stream + ay31015->internal_sample = ay31015_get_si( device ); + else + if (ay31015->rx_pulses == 7) // set error flags + { + if (!ay31015->internal_sample) + { + ay31015->status_reg |= STATUS_FE; // framing error - the stop bit not high + ay31015->rx_state = PREP_TIME; // lost sync - start over + // return; + } + else + ay31015->status_reg &= ~STATUS_FE; + + if ((ay31015->rx_parity) && (!(ay31015->control_reg & CONTROL_NP))) + ay31015->status_reg |= STATUS_PE; // parity error + else + ay31015->status_reg &= ~STATUS_PE; + + if (ay31015->status_reg & STATUS_DAV) + ay31015->status_reg |= STATUS_OR; // overrun error - previous byte still in buffer + else + ay31015->status_reg &= ~STATUS_OR; + + ay31015->rx_buffer = ay31015->rx_data; // bring received byte out for computer to read + + ay31015_update_status_pins( device ); + } + else + if (ay31015->rx_pulses == 6) + { + ay31015->status_reg |= STATUS_DAV; // tell computer that new byte is ready + ay31015_update_status_pins( device ); + } + else + if (ay31015->rx_pulses == 4) + { + if (ay31015->second_stop_bit) + { + /* We should wait for the full first stop bit and + the beginning of the second stop bit */ + ay31015->rx_state = SECOND_STOP_BIT; + ay31015->rx_pulses += ay31015->second_stop_bit - 7; + } + else + { + /* We have seen a STOP bit, go back to PREP_TIME */ + ay31015->rx_state = PREP_TIME; + } + } + return; + + case SECOND_STOP_BIT: + ay31015->rx_pulses--; + if (!ay31015->rx_pulses) + ay31015->rx_state = PREP_TIME; + return; + + } +} + + +/*************************************************** TRANSMIT CONTROLS *************************************************/ + + +/*------------------------------------------------- + ay31015_tx_process - convert parallel to serial +-------------------------------------------------*/ +static TIMER_CALLBACK( ay31015_tx_process ) +{ + device_t *device = (device_t *)ptr; + ay31015_t *ay31015 = get_safe_token( device ); + + UINT8 t1; + switch (ay31015->tx_state) + { + case IDLE: + if (!(ay31015->status_reg & STATUS_TBMT)) + { + ay31015->tx_state = PREP_TIME; // When idle, see if a byte has been sent to us + ay31015->tx_pulses = 1; + } + return; + + case PREP_TIME: // This phase lets the transmitter regain sync after an idle period + ay31015->tx_pulses--; + if (!ay31015->tx_pulses) + { + ay31015->tx_state = START_BIT; + ay31015->tx_pulses = 16; + } + return; + + case START_BIT: + if (ay31015->tx_pulses == 16) // beginning of start bit + { + ay31015->tx_data = ay31015->tx_buffer; // load the shift register + ay31015->status_reg |= STATUS_TBMT; // tell computer that another byte can be sent to uart + ay31015_set_so( device, 0 ); /* start bit begins now (we are "spacing") */ + ay31015->status_reg &= ~STATUS_EOC; // we are no longer idle + ay31015->tx_parity = 0; + ay31015_update_status_pins( device ); + } + + ay31015->tx_pulses--; + if (!ay31015->tx_pulses) // end of start bit + { + ay31015->tx_state = PROCESSING; + ay31015->tx_pulses = ay31015->total_pulses; + } + return; + + case PROCESSING: + if (!(ay31015->tx_pulses & 15)) // beginning of a data bit + { + if (ay31015->tx_data & 1) + { + ay31015_set_so( device, 1 ); + ay31015->tx_parity++; // calculate cumulative parity + } + else + ay31015_set_so( device, 0 ); + + ay31015->tx_data >>= 1; // adjust the shift register + } + + ay31015->tx_pulses--; + if (!ay31015->tx_pulses) // all data bits sent + { + ay31015->tx_pulses = 16; + if (ay31015->control_reg & CONTROL_NP) // see if we need to make a parity bit + ay31015->tx_state = FIRST_STOP_BIT; + else + ay31015->tx_state = PARITY_BIT; + } + + return; + + case PARITY_BIT: + if (ay31015->tx_pulses == 16) + { + t1 = (ay31015->control_reg & CONTROL_EPS) ? 0 : 1; + t1 ^= (ay31015->tx_parity & 1); + if (t1) + ay31015_set_so( device, 1 ); /* extra bit to set the correct parity */ + else + ay31015_set_so( device, 0 ); /* it was already correct */ + } + + ay31015->tx_pulses--; + if (!ay31015->tx_pulses) + { + ay31015->tx_state = FIRST_STOP_BIT; + ay31015->tx_pulses = 16; + } + return; + + case FIRST_STOP_BIT: + if (ay31015->tx_pulses == 16) + ay31015_set_so( device, 1 ); /* create a stop bit (marking and soon idle) */ + ay31015->tx_pulses--; + if (!ay31015->tx_pulses) + { + ay31015->status_reg |= STATUS_EOC; // character is completely sent + if (ay31015->second_stop_bit) + { + ay31015->tx_state = SECOND_STOP_BIT; + ay31015->tx_pulses = ay31015->second_stop_bit; + } + else + if (ay31015->status_reg & STATUS_TBMT) + ay31015->tx_state = IDLE; // if nothing to send, go idle + else + { + ay31015->tx_pulses = 16; + ay31015->tx_state = START_BIT; // otherwise immediately start next byte + } + ay31015_update_status_pins( device ); + } + return; + + case SECOND_STOP_BIT: + ay31015->tx_pulses--; + if (!ay31015->tx_pulses) + { + if (ay31015->status_reg & STATUS_TBMT) + ay31015->tx_state = IDLE; // if nothing to send, go idle + else + { + ay31015->tx_pulses = 16; + ay31015->tx_state = START_BIT; // otherwise immediately start next byte + } + } + return; + + } +} + + +/*------------------------------------------------- + ay31015_reset - reset internal state +-------------------------------------------------*/ +static void ay31015_reset( device_t *device ) +{ + ay31015_t *ay31015 = get_safe_token( device ); + + /* total pulses = 16 * data-bits */ + UINT8 t1; + + if ( ay31015->control_reg & CONTROL_NB2 ) + t1 = ( ay31015->control_reg & CONTROL_NB1 ) ? 8 : 7; + else + t1 = ( ay31015->control_reg & CONTROL_NB1 ) ? 6 : 5; + + ay31015->total_pulses = t1 << 4; /* total clock pulses to load a byte */ + ay31015->second_stop_bit = ((ay31015->control_reg & CONTROL_TSB) ? 16 : 0); /* 2nd stop bit */ + if ((t1 == 5) && (ay31015->second_stop_bit == 16)) + ay31015->second_stop_bit = 8; /* 5 data bits and 2 stop bits = 1.5 stop bits */ + ay31015->status_reg = STATUS_EOC | STATUS_TBMT; + ay31015->tx_data = 0; + ay31015->rx_state = PREP_TIME; + ay31015->tx_state = IDLE; + ay31015->pins[AY31015_SI] = 1; + ay31015_set_so( device, 1 ); + + if ( ay31015->config->type == AY_3_1015 ) + ay31015->rx_data = 0; + +} + + +/*------------------------------------------------- + ay31015_transfer_control_pins - transfers contents of controls pins to the control register +-------------------------------------------------*/ +static void ay31015_transfer_control_pins( device_t *device ) +{ + ay31015_t *ay31015 = get_safe_token( device ); + UINT8 control = 0; + + control |= ay31015->pins[AY31015_NP ] ? CONTROL_NP : 0; + control |= ay31015->pins[AY31015_TSB] ? CONTROL_TSB : 0; + control |= ay31015->pins[AY31015_NB1] ? CONTROL_NB1 : 0; + control |= ay31015->pins[AY31015_NB2] ? CONTROL_NB2 : 0; + control |= ay31015->pins[AY31015_EPS] ? CONTROL_EPS : 0; + + if ( ay31015->control_reg != control ) + { + ay31015->control_reg = control; + ay31015_reset( device ); + } +} + + +/*------------------------------------------------- + ay31015_set_input_pin - set an input pin +-------------------------------------------------*/ +void ay31015_set_input_pin( device_t *device, ay31015_input_pin_t pin, int data ) +{ + ay31015_t *ay31015 = get_safe_token(device); + + data = data ? 1 : 0; + + switch ( pin ) + { + case AY31015_SWE: + ay31015->pins[pin] = data; + ay31015_update_status_pins( device ); + break; + case AY31015_RDAV: + ay31015->pins[pin] = data; + if ( ! data ) + { + ay31015->status_reg &= ~STATUS_DAV; + ay31015->pins[AY31015_DAV] = 0; + } + break; + case AY31015_SI: + ay31015->pins[pin] = data; + break; + case AY31015_XR: + ay31015->pins[pin] = data; + if ( data ) + ay31015_reset( device ); + break; + case AY31015_CS: + case AY31015_NP: + case AY31015_TSB: + case AY31015_NB1: + case AY31015_NB2: + case AY31015_EPS: + ay31015->pins[pin] = data; + if ( ay31015->pins[AY31015_CS] ) + ay31015_transfer_control_pins( device ); + break; + } +} + + +/*------------------------------------------------- + ay31015_get_output_pin - get the status of an output pin +-------------------------------------------------*/ +int ay31015_get_output_pin( device_t *device, ay31015_output_pin_t pin ) +{ + ay31015_t *ay31015 = get_safe_token(device); + + return ay31015->pins[pin]; +} + + +INLINE void ay31015_update_rx_timer( device_t *device ) +{ + ay31015_t *ay31015 = get_safe_token( device ); + + if ( ay31015->rx_clock > 0.0 ) + { + ay31015->rx_timer->adjust( attotime::from_hz( ay31015->rx_clock ), 0, attotime::from_hz( ay31015->rx_clock ) ); + } + else + { + ay31015->rx_timer->enable( 0 ); + } +} + + +INLINE void ay31015_update_tx_timer( device_t *device ) +{ + ay31015_t *ay31015 = get_safe_token( device ); + + if ( ay31015->tx_clock > 0.0 ) + { + ay31015->tx_timer->adjust( attotime::from_hz( ay31015->tx_clock ), 0, attotime::from_hz( ay31015->tx_clock ) ); + } + else + { + ay31015->tx_timer->enable( 0 ); + } +} + + +/*------------------------------------------------- + ay31015_set_receiver_clock - set receive clock +-------------------------------------------------*/ +void ay31015_set_receiver_clock( device_t *device, double new_clock ) +{ + ay31015_t *ay31015 = get_safe_token(device); + + ay31015->rx_clock = new_clock; + ay31015_update_rx_timer( device ); +} + + +/*------------------------------------------------- + ay31015_set_transmitter_clock - set transmit clock +-------------------------------------------------*/ +void ay31015_set_transmitter_clock( device_t *device, double new_clock ) +{ + ay31015_t *ay31015 = get_safe_token(device); + + ay31015->tx_clock = new_clock; + ay31015_update_tx_timer( device ); +} + + +/*------------------------------------------------- + ay31015_get_received_data - return a byte to the computer +-------------------------------------------------*/ +UINT8 ay31015_get_received_data( device_t *device ) +{ + ay31015_t *ay31015 = get_safe_token(device); + + return ay31015->rx_buffer; +} + + +/*------------------------------------------------- + ay31015_set_transmit_data - accept a byte to transmit, if able +-------------------------------------------------*/ +void ay31015_set_transmit_data( device_t *device, UINT8 data ) +{ + ay31015_t *ay31015 = get_safe_token(device); + + if (ay31015->status_reg & STATUS_TBMT) + { + ay31015->tx_buffer = data; + ay31015->status_reg &= ~STATUS_TBMT; + ay31015_update_status_pins( device ); + } +} + + +static DEVICE_START(ay31015) +{ + ay31015_t *ay31015 = get_safe_token(device); + + ay31015->config = (const ay31015_config*)device->static_config(); + + ay31015->read_si.resolve(ay31015->config->read_si_cb, *device); + ay31015->write_so.resolve(ay31015->config->write_so_cb, *device); + ay31015->status_changed.resolve(ay31015->config->status_changed_cb, *device); + + ay31015->tx_clock = ay31015->config->transmitter_clock; + ay31015->rx_clock = ay31015->config->receiver_clock; + + ay31015->rx_timer = device->machine().scheduler().timer_alloc(FUNC(ay31015_rx_process), (void *)device ); + ay31015->tx_timer = device->machine().scheduler().timer_alloc(FUNC(ay31015_tx_process), (void *)device ); + + ay31015_update_rx_timer( device ); + ay31015_update_tx_timer( device ); +} + + +static DEVICE_RESET( ay31015 ) +{ + ay31015_t *ay31015 = get_safe_token(device); + + ay31015->control_reg = 0; + ay31015->rx_data = 0; + + ay31015_reset( device ); +} + + +const device_type AY31015 = &device_creator<ay31015_device>; + +ay31015_device::ay31015_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, AY31015, "AY-3-1015", tag, owner, clock) +{ + m_token = global_alloc_clear(ay31015_t); +} + +//------------------------------------------------- +// device_config_complete - perform any +// operations now that the configuration is +// complete +//------------------------------------------------- + +void ay31015_device::device_config_complete() +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void ay31015_device::device_start() +{ + DEVICE_START_NAME( ay31015 )(this); +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void ay31015_device::device_reset() +{ + DEVICE_RESET_NAME( ay31015 )(this); +} diff --git a/src/emu/machine/ay31015.h b/src/emu/machine/ay31015.h new file mode 100644 index 00000000000..44d5e2b57a0 --- /dev/null +++ b/src/emu/machine/ay31015.h @@ -0,0 +1,126 @@ +/* ay31015.h + + Written for MESS by Robbbert on May 29th, 2008. + +*/ + +#ifndef __AY31015_H_ +#define __AY31015_H_ + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + + +enum ay31015_type_t +{ + /* For AY-3-1014A, AY-3-1015(D) and HD6402 variants */ + AY_3_1015, + + /* For AY-3-1014, AY-5-1013 and AY-6-1013 variants */ + AY_5_1013 +}; + + +enum ay31015_input_pin_t +{ + AY31015_SWE=16, /* -SWE - Pin 16 - Status word enable */ + AY31015_RDAV=18, /* -RDAV - Pin 18 - Reset data available */ + AY31015_SI=20, /* SI - Pin 20 - Serial input */ + AY31015_XR=21, /* XR - Pin 21 - External reset */ + AY31015_CS=34, /* CS - Pin 34 - Control strobe */ + AY31015_NP=35, /* NP - Pin 35 - No parity */ + AY31015_TSB=36, /* TSB - Pin 36 - Number of stop bits */ + AY31015_NB1=37, /* NB1 - Pin 37 - Number of bits #1 */ + AY31015_NB2=38, /* NB2 - Pin 38 - Number of bits #2 */ + AY31015_EPS=39 /* EPS - Pin 39 - Odd/Even parity select */ +}; + + +enum ay31015_output_pin_t +{ + AY31015_PE=13, /* PE - Pin 13 - Parity error */ + AY31015_FE=14, /* FE - Pin 14 - Framing error */ + AY31015_OR=15, /* OR - Pin 15 - Over-run */ + AY31015_DAV=19, /* DAV - Pin 19 - Data available */ + AY31015_TBMT=22, /* TBMT - Pin 22 - Transmit buffer empty */ + AY31015_EOC=24, /* EOC - Pin 24 - End of character */ + AY31015_SO=25 /* SO - Pin 25 - Serial output */ +}; + + +struct ay31015_config +{ + ay31015_type_t type; /* Type of chip */ + double transmitter_clock; /* TCP - pin 40 */ + double receiver_clock; /* RCP - pin 17 */ + devcb_read8 read_si_cb; /* SI - pin 20 - This will be called whenever the SI pin is sampled. Optional */ + devcb_write8 write_so_cb; /* SO - pin 25 - This will be called whenever data is put on the SO pin. Optional */ + devcb_write8 status_changed_cb; /* This will be called whenever one of the status pins may have changed. Optional */ +}; + + +/*************************************************************************** + DEVICE CONFIGURATION MACROS +***************************************************************************/ + +#define MCFG_AY31015_ADD(_tag, _config) \ + MCFG_DEVICE_ADD(_tag, AY31015, 0) \ + MCFG_DEVICE_CONFIG(_config) + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +/* Set an input pin */ +void ay31015_set_input_pin( device_t *device, ay31015_input_pin_t pin, int data ); + + +/* Get an output pin */ +int ay31015_get_output_pin( device_t *device, ay31015_output_pin_t pin ); + + +/* Set a new transmitter clock (new_clock is in Hz) */ +void ay31015_set_transmitter_clock( device_t *device, double new_clock ); + + +/* Set a new receiver clock (new_clock is in Hz) */ +void ay31015_set_receiver_clock( device_t *device, double new_clock ); + + +/* Reead the received data */ +/* The received data is available on RD8-RD1 (pins 5-12) */ +UINT8 ay31015_get_received_data( device_t *device ); + + +/* Set the transmitter buffer */ +/* The data to transmit is set on DB1-DB8 (pins 26-33) */ +void ay31015_set_transmit_data( device_t *device, UINT8 data ); + + +/*************************************************************************** + DEVICE INTERFACE +***************************************************************************/ + +class ay31015_device : public device_t +{ +public: + ay31015_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + ~ay31015_device() { global_free(m_token); } + + // access to legacy token + void *token() const { assert(m_token != NULL); return m_token; } +protected: + // device-level overrides + virtual void device_config_complete(); + virtual void device_start(); + virtual void device_reset(); +private: + // internal state + void *m_token; +}; + +extern const device_type AY31015; + +#endif diff --git a/src/emu/machine/er59256.c b/src/emu/machine/er59256.c new file mode 100644 index 00000000000..6eedb17a68a --- /dev/null +++ b/src/emu/machine/er59256.c @@ -0,0 +1,259 @@ +/********************************************************************* + + er59256.c + + Microchip ER59256 serial eeprom. + + +*********************************************************************/ + +#include "emu.h" +#include "er59256.h" + +/* LOGLEVEL 0=no logging, 1=just commands and data, 2=everything ! */ + +#define LOGLEVEL 0 + +#define LOG(level,...) if(LOGLEVEL>=level) logerror(__VA_ARGS__) +#define LOG_BITS(bits) logerror("CS=%d CK=%d DI=%d DO=%d", (bits&CS_MASK) ? 1 : 0, (bits&CK_MASK) ? 1 : 0, (bits&DI_MASK) ? 1 : 0, (bits&DO_MASK) ? 1 : 0) + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +struct er59256_t +{ + /* The actual memory */ + UINT16 eerom[EEROM_WORDS]; + + /* Bits as they appear on the io pins, current state */ + UINT8 io_bits; + + /* Bits as they appear on the io pins, previous state */ + UINT8 old_io_bits; + + + /* the 16 bit shift in/out reg */ + UINT16 in_shifter; + UINT32 out_shifter; + + /* Count of bits received since last CS low->high */ + UINT8 bitcount; + + /* Command & addresss */ + UINT8 command; + + /* Write enable and write in progress flags */ + UINT8 flags; +}; + +/*************************************************************************** + FUNCTION PROTOTYPES +************************************************************************/ + +static void decode_command(er59256_t *er59256); + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +INLINE er59256_t *get_token(device_t *device) +{ + assert(device->type() == ER59256); + return (er59256_t *) downcast<er59256_device *>(device)->token(); +} + + +/*************************************************************************** + IMPLEMENTATION +***************************************************************************/ + +void er59256_preload_rom(device_t *device, const UINT16 *rom_data, int count) +{ + er59256_t *er59256 = get_token(device); + int WordNo; + + logerror("Preloading %d words of data\n",count); + + if(count>EEROM_WORDS) + memcpy(&er59256->eerom,rom_data,count*2); + else + memcpy(&er59256->eerom,rom_data,EEROM_WORDS*2); + + for(WordNo=0;WordNo<EEROM_WORDS;WordNo++) + logerror("%04X ",er59256->eerom[WordNo]); + + logerror("\n"); +} + +UINT8 er59256_data_loaded(device_t *device) +{ + er59256_t *er59256 = get_token(device); + + return (er59256->flags & FLAG_DATA_LOADED) ? 1 : 0; +} + +/*------------------------------------------------- + DEVICE_START( er59256 ) +-------------------------------------------------*/ + +static DEVICE_START( er59256 ) +{ + er59256_t *er59256 = get_token(device); + + memset(er59256, 0x00, sizeof(er59256_t)); + + // Start with rom defaulted to erased + memset(&er59256->eerom, 0xFF, EEROM_WORDS*2); + + er59256->command=CMD_INVALID; + + er59256->flags&= ~FLAG_DATA_LOADED; +} + +static DEVICE_STOP( er59256 ) +{ + /* Save contents of eerom */ +} + +void er59256_set_iobits(device_t *device, UINT8 newbits) +{ + er59256_t *er59256 = get_token(device); + //UINT32 bit; + + // Make sure we only apply valid bits + newbits&=ALL_MASK; + + if(LOGLEVEL>1) + { + logerror("er59256:newbits=%02X : ",newbits); + LOG_BITS(newbits); + logerror(" io_bits=%02X : ",er59256->io_bits); + LOG_BITS(er59256->io_bits); + logerror(" old_io_bits=%02X : ",er59256->old_io_bits); + LOG_BITS(er59256->old_io_bits); + logerror(" bitcount=%d, in_shifter=%04X, out_shifter=%05X, flags=%02X\n",er59256->bitcount,er59256->in_shifter,er59256->out_shifter,er59256->flags); + } + // Only do anything if the inputs have changed + if((newbits&IN_MASK)!=(er59256->io_bits&IN_MASK)) + { + // save the current state, then set the new one, remembering to preserve data out + er59256->old_io_bits=er59256->io_bits; + er59256->io_bits=(newbits & ~DO_MASK) | (er59256->old_io_bits&DO_MASK); + + if(CS_RISE(er59256)) + { + er59256->flags&=~FLAG_START_BIT; + er59256->command=CMD_INVALID; + } + + if(LOGLEVEL>1) + { + if(CK_RISE(er59256)) logerror("er59256:CK rise\n"); + if(CS_RISE(er59256)) logerror("er59256:CS rise\n"); + if(CK_FALL(er59256)) logerror("er59256:CK fall\n"); + if(CS_FALL(er59256)) logerror("er59256:CS fall\n"); + } + + if(CK_RISE(er59256) && CS_VALID(er59256)) + { + if((STARTED(er59256)==0) && (GET_DI(er59256)==1)) + { + er59256->bitcount=0; + er59256->flags|=FLAG_START_BIT; + } + else + { + SHIFT_IN(er59256); + er59256->bitcount++; + + if(er59256->bitcount==CMD_BITLEN) + decode_command(er59256); + + if((er59256->bitcount==WRITE_BITLEN) && ((er59256->command & CMD_MASK)==CMD_WRITE)) + { + er59256->eerom[er59256->command & ADDR_MASK]=er59256->in_shifter; + LOG(1,"er59256:write[%02X]=%04X\n",(er59256->command & ADDR_MASK),er59256->in_shifter); + er59256->command=CMD_INVALID; + } + LOG(1,"out_shifter=%05X, io_bits=%02X\n",er59256->out_shifter,er59256->io_bits); + SHIFT_OUT(er59256); + } + + LOG(2,"io_bits:out=%02X\n",er59256->io_bits); + } + } +} + +UINT8 er59256_get_iobits(device_t *device) +{ + er59256_t *er59256 = get_token(device); + + return er59256->io_bits; +} + + +static void decode_command(er59256_t *er59256) +{ + er59256->out_shifter=0x0000; + er59256->command=(er59256->in_shifter & (CMD_MASK | ADDR_MASK)); + + switch(er59256->command & CMD_MASK) + { + case CMD_READ : er59256->out_shifter=er59256->eerom[er59256->command & ADDR_MASK]; + LOG(1,"er59256:read[%02X]=%04X\n",(er59256->command&ADDR_MASK),er59256->eerom[er59256->command & ADDR_MASK]); + break; + case CMD_WRITE : break; + case CMD_ERASE : if (WRITE_ENABLED(er59256)) er59256->eerom[er59256->command & ADDR_MASK]=0xFF; + LOG(1,"er59256:erase[%02X]\n",(er59256->command&ADDR_MASK)); + break; + case CMD_EWEN : er59256->flags|=FLAG_WRITE_EN; + LOG(1,"er59256:erase/write enabled\n"); + break; + case CMD_EWDS : er59256->flags&=~FLAG_WRITE_EN; + LOG(1,"er59256:erase/write disabled\n"); + break; + case CMD_ERAL : if (WRITE_ENABLED(er59256)) memset(&er59256->eerom, 0xFF, EEROM_WORDS*2); + LOG(1,"er59256:erase all\n"); + break; + } + + if ((er59256->command & CMD_MASK)!=CMD_WRITE) + er59256->command=CMD_INVALID; +} + +const device_type ER59256 = &device_creator<er59256_device>; + +er59256_device::er59256_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, ER59256, "Microchip ER59256 serial eeprom.", tag, owner, clock) +{ + m_token = global_alloc_clear(er59256_t); +} + +//------------------------------------------------- +// device_config_complete - perform any +// operations now that the configuration is +// complete +//------------------------------------------------- + +void er59256_device::device_config_complete() +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void er59256_device::device_start() +{ + DEVICE_START_NAME( er59256 )(this); +} + +//------------------------------------------------- +// device_stop - device-specific stop +//------------------------------------------------- + +void er59256_device::device_stop() +{ + DEVICE_STOP_NAME( er59256 )(this); +} diff --git a/src/emu/machine/er59256.h b/src/emu/machine/er59256.h new file mode 100644 index 00000000000..938a8872ee3 --- /dev/null +++ b/src/emu/machine/er59256.h @@ -0,0 +1,112 @@ +/********************************************************************* + + er59256.h + + Microchip ER59256 serial eeprom. + + +*********************************************************************/ + +#ifndef _ER59256_H_ +#define _ER59256_H_ + +/*************************************************************************** + MACROS +***************************************************************************/ + +class er59256_device : public device_t +{ +public: + er59256_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + ~er59256_device() { global_free(m_token); } + + // access to legacy token + void *token() const { assert(m_token != NULL); return m_token; } +protected: + // device-level overrides + virtual void device_config_complete(); + virtual void device_start(); + virtual void device_stop(); +private: + // internal state + void *m_token; +}; + +extern const device_type ER59256; + + +#define MCFG_ER59256_ADD(_tag) \ + MCFG_DEVICE_ADD((_tag), ER59256, 0) + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define EEROM_WORDS 0x10 + +#define CK_SHIFT 0x00 +#define DI_SHIFT 0x01 +#define DO_SHIFT 0x02 +#define CS_SHIFT 0x03 + +#define CK_MASK (1<<CK_SHIFT) +#define DI_MASK (1<<DI_SHIFT) +#define DO_MASK (1<<DO_SHIFT) +#define CS_MASK (1<<CS_SHIFT) + +#define ALL_MASK (CK_MASK | DI_MASK | DO_MASK | CS_MASK) +#define IN_MASK (CK_MASK | DI_MASK | CS_MASK) + +#define GET_CK(eep) ((eep->io_bits & CK_MASK) >> CK_SHIFT) +#define GET_DI(eep) ((eep->io_bits & DI_MASK) >> DI_SHIFT) +#define GET_DO(eep) ((eep->io_bits & DO_MASK) >> DO_SHIFT) +#define GET_CS(eep) ((eep->io_bits & CS_MASK) >> CS_SHIFT) + +#define SET_CK(eep,data) eep->io_bits=((eep->io_bits & ~CK_MASK) | ((data & 0x01) << CK_SHIFT)) +#define SET_DI(eep,data) eep->io_bits=((eep->io_bits & ~DI_MASK) | ((data & 0x01) << DI_SHIFT)) +#define SET_DO(eep,data) eep->io_bits=((eep->io_bits & ~DO_MASK) | ((data & 0x01) << DO_SHIFT)) +#define SET_CS(eep,data) eep->io_bits=((eep->io_bits & ~CS_MASK) | ((data & 0x01) << CS_SHIFT)) + +#define CK_RISE(eep) ((eep->io_bits & CK_MASK) & ~(eep->old_io_bits & CK_MASK)) +#define CS_RISE(eep) ((eep->io_bits & CS_MASK) & ~(eep->old_io_bits & CS_MASK)) +#define CS_VALID(eep) ((eep->io_bits & CS_MASK) & (eep->old_io_bits & CS_MASK)) + +#define CK_FALL(eep) (~(eep->io_bits & CK_MASK) & (eep->old_io_bits & CK_MASK)) +#define CS_FALL(eep) (~(eep->io_bits & CS_MASK) & (eep->old_io_bits & CS_MASK)) + + +#define SHIFT_IN(eep) eep->in_shifter=(eep->in_shifter<<1) | GET_DI(eep) +#define SHIFT_OUT(eep) SET_DO(eep,(eep->out_shifter & 0x10000)>>16); eep->out_shifter=(eep->out_shifter<<1) + +#define CMD_READ 0x80 +#define CMD_WRITE 0x40 +#define CMD_ERASE 0xC0 +#define CMD_EWEN 0x30 +#define CMD_EWDS 0x00 +#define CMD_ERAL 0x20 +#define CMD_INVALID 0xF0 + +#define CMD_MASK 0xF0 +#define ADDR_MASK 0x0F + +// CMD_BITLEN is 1 start bit plus 4 command bits plus 4 address bits +#define CMD_BITLEN 8 +#define DATA_BITLEN 16 +#define WRITE_BITLEN CMD_BITLEN+DATA_BITLEN + +#define FLAG_WRITE_EN 0x01 +#define FLAG_START_BIT 0x02 +#define FLAG_DATA_LOADED 0x04 + +#define WRITE_ENABLED(eep) ((eep->flags & FLAG_WRITE_EN) ? 1 : 0) +#define STARTED(eep) ((eep->flags & FLAG_START_BIT) ? 1 : 0) + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +void er59256_set_iobits(device_t *device, UINT8 newbits); +UINT8 er59256_get_iobits(device_t *device); +void er59256_preload_rom(device_t *device, const UINT16 *rom_data, int count); +UINT8 er59256_data_loaded(device_t *device); +#endif diff --git a/src/emu/machine/mc68328.c b/src/emu/machine/mc68328.c new file mode 100644 index 00000000000..e7039df5cad --- /dev/null +++ b/src/emu/machine/mc68328.c @@ -0,0 +1,2872 @@ +/********************************************************************** + + Motorola 68328 ("DragonBall") System-on-a-Chip implementation + + By MooglyGuy + contact mooglyguy@gmail.com with licensing and usage questions. + +**********************************************************************/ + +#include "emu.h" +#include "cpu/m68000/m68000.h" +#include "includes/mc68328.h" +#include "mc68328.h" + +#define VERBOSE_LEVEL (0) + +INLINE void verboselog(running_machine &machine, int n_level, const char *s_fmt, ...) +{ + if( VERBOSE_LEVEL >= n_level ) + { + va_list v; + char buf[ 32768 ]; + va_start( v, s_fmt ); + vsprintf( buf, s_fmt, v ); + va_end( v ); + logerror( "%s: %s", machine.describe_context(), buf ); + } +} + +static void mc68328_set_interrupt_line(device_t *device, UINT32 line, UINT32 active) +{ + mc68328_t* mc68328 = mc68328_get_safe_token( device ); + device_t *cpu = device->machine().device(mc68328->iface->m68k_cpu_tag); + + if(active) + { + mc68328->regs.ipr |= line; + + if(!(mc68328->regs.imr & line) && !(mc68328->regs.isr & line)) + { + mc68328->regs.isr |= line; + + if(mc68328->regs.isr & INT_M68K_LINE7) + { + cpu->execute().set_input_line_and_vector(M68K_IRQ_7, ASSERT_LINE, mc68328->regs.ivr | 0x07); + } + else if(mc68328->regs.isr & INT_M68K_LINE6) + { + cpu->execute().set_input_line_and_vector(M68K_IRQ_6, ASSERT_LINE, mc68328->regs.ivr | 0x06); + } + else if(mc68328->regs.isr & INT_M68K_LINE5) + { + cpu->execute().set_input_line_and_vector(M68K_IRQ_5, ASSERT_LINE, mc68328->regs.ivr | 0x05); + } + else if(mc68328->regs.isr & INT_M68K_LINE4) + { + cpu->execute().set_input_line_and_vector(M68K_IRQ_4, ASSERT_LINE, mc68328->regs.ivr | 0x04); + } + else if(mc68328->regs.isr & INT_M68K_LINE3) + { + cpu->execute().set_input_line_and_vector(M68K_IRQ_3, ASSERT_LINE, mc68328->regs.ivr | 0x03); + } + else if(mc68328->regs.isr & INT_M68K_LINE2) + { + cpu->execute().set_input_line_and_vector(M68K_IRQ_2, ASSERT_LINE, mc68328->regs.ivr | 0x02); + } + else if(mc68328->regs.isr & INT_M68K_LINE1) + { + cpu->execute().set_input_line_and_vector(M68K_IRQ_1, ASSERT_LINE, mc68328->regs.ivr | 0x01); + } + } + } + else + { + mc68328->regs.isr &= ~line; + + if((line & INT_M68K_LINE7) && !(mc68328->regs.isr & INT_M68K_LINE7)) + { + cpu->execute().set_input_line(M68K_IRQ_7, CLEAR_LINE); + } + if((line & INT_M68K_LINE6) && !(mc68328->regs.isr & INT_M68K_LINE6)) + { + cpu->execute().set_input_line(M68K_IRQ_6, CLEAR_LINE); + } + if((line & INT_M68K_LINE5) && !(mc68328->regs.isr & INT_M68K_LINE5)) + { + cpu->execute().set_input_line(M68K_IRQ_5, CLEAR_LINE); + } + if((line & INT_M68K_LINE4) && !(mc68328->regs.isr & INT_M68K_LINE4)) + { + cpu->execute().set_input_line(M68K_IRQ_4, CLEAR_LINE); + } + if((line & INT_M68K_LINE3) && !(mc68328->regs.isr & INT_M68K_LINE3)) + { + cpu->execute().set_input_line(M68K_IRQ_3, CLEAR_LINE); + } + if((line & INT_M68K_LINE2) && !(mc68328->regs.isr & INT_M68K_LINE2)) + { + cpu->execute().set_input_line(M68K_IRQ_2, CLEAR_LINE); + } + if((line & INT_M68K_LINE1) && !(mc68328->regs.isr & INT_M68K_LINE1)) + { + cpu->execute().set_input_line(M68K_IRQ_1, CLEAR_LINE); + } + } +} + +static void mc68328_poll_port_d_interrupts(device_t *device) +{ + mc68328_t* mc68328 = mc68328_get_safe_token( device ); + UINT8 line_transitions = mc68328->regs.pddataedge & mc68328->regs.pdirqedge; + UINT8 line_holds = mc68328->regs.pddata &~ mc68328->regs.pdirqedge; + UINT8 line_interrupts = (line_transitions | line_holds) & mc68328->regs.pdirqen; + + if(line_interrupts) + { + mc68328_set_interrupt_line(device, line_interrupts << 8, 1); + } + else + { + mc68328_set_interrupt_line(device, INT_KBDINTS, 0); + } +} + +void mc68328_set_penirq_line(device_t *device, int state) +{ + mc68328_t* mc68328 = mc68328_get_safe_token( device ); + + if(state) + { + mc68328_set_interrupt_line(device, INT_PEN, 1); + } + else + { + mc68328->regs.ipr &= ~INT_PEN; + mc68328_set_interrupt_line(device, INT_PEN, 0); + } +} + +void mc68328_set_port_d_lines(device_t *device, UINT8 state, int bit) +{ + mc68328_t* mc68328 = mc68328_get_safe_token( device ); + UINT8 old_button_state = mc68328->regs.pddata; + + if(state & (1 << bit)) + { + mc68328->regs.pddata |= (1 << bit); + } + else + { + mc68328->regs.pddata &= ~(1 << bit); + } + + mc68328->regs.pddataedge |= ~old_button_state & mc68328->regs.pddata; + + mc68328_poll_port_d_interrupts(device); +} + +static UINT32 mc68328_get_timer_frequency(device_t *device, UINT32 index) +{ + mc68328_t* mc68328 = mc68328_get_safe_token( device ); + UINT32 frequency = 0; + + switch(mc68328->regs.tctl[index] & TCTL_CLKSOURCE) + { + case TCTL_CLKSOURCE_SYSCLK: + frequency = 32768 * 506; + break; + + case TCTL_CLKSOURCE_SYSCLK16: + frequency = (32768 * 506) / 16; + break; + + case TCTL_CLKSOURCE_32KHZ4: + case TCTL_CLKSOURCE_32KHZ5: + case TCTL_CLKSOURCE_32KHZ6: + case TCTL_CLKSOURCE_32KHZ7: + frequency = 32768; + break; + } + frequency /= (mc68328->regs.tprer[index] + 1); + + return frequency; +} + +static void mc68328_maybe_start_timer(device_t *device, UINT32 index, UINT32 new_enable) +{ + mc68328_t* mc68328 = mc68328_get_safe_token( device ); + + if((mc68328->regs.tctl[index] & TCTL_TEN) == TCTL_TEN_ENABLE && (mc68328->regs.tctl[index] & TCTL_CLKSOURCE) > TCTL_CLKSOURCE_STOP) + { + if((mc68328->regs.tctl[index] & TCTL_CLKSOURCE) == TCTL_CLKSOURCE_TIN) + { + mc68328->gptimer[index]->adjust(attotime::never); + } + else if(mc68328->regs.tcmp[index] == 0) + { + mc68328->gptimer[index]->adjust(attotime::never); + } + else + { + UINT32 frequency = mc68328_get_timer_frequency(device, index); + attotime period = (attotime::from_hz(frequency) * mc68328->regs.tcmp[index]); + + if(new_enable) + { + mc68328->regs.tcn[index] = 0x0000; + } + + mc68328->gptimer[index]->adjust(period); + } + } + else + { + mc68328->gptimer[index]->adjust(attotime::never); + } +} + +static void mc68328_timer_compare_event(device_t *device, UINT32 index) +{ + mc68328_t* mc68328 = mc68328_get_safe_token( device ); + + mc68328->regs.tcn[index] = mc68328->regs.tcmp[index]; + mc68328->regs.tstat[index] |= TSTAT_COMP; + + if((mc68328->regs.tctl[index] & TCTL_FRR) == TCTL_FRR_RESTART) + { + UINT32 frequency = mc68328_get_timer_frequency(device, index); + + if(frequency > 0) + { + attotime period = attotime::from_hz(frequency) * mc68328->regs.tcmp[index]; + + mc68328->regs.tcn[index] = 0x0000; + + mc68328->gptimer[index]->adjust(period); + } + else + { + mc68328->gptimer[index]->adjust(attotime::never); + } + } + else + { + UINT32 frequency = mc68328_get_timer_frequency(device, index); + + if(frequency > 0) + { + attotime period = attotime::from_hz(frequency) * 0x10000; + + mc68328->gptimer[index]->adjust(period); + } + else + { + mc68328->gptimer[index]->adjust(attotime::never); + } + } + if((mc68328->regs.tctl[index] & TCTL_IRQEN) == TCTL_IRQEN_ENABLE) + { + mc68328_set_interrupt_line(device, (index == 0) ? INT_TIMER1 : INT_TIMER2, 1); + } +} + +static TIMER_CALLBACK( mc68328_timer1_hit ) +{ + device_t *device = machine.device(MC68328_TAG); + + mc68328_timer_compare_event(device, 0); +} + +static TIMER_CALLBACK( mc68328_timer2_hit ) +{ + device_t *device = machine.device(MC68328_TAG); + + mc68328_timer_compare_event(device, 1); +} + +static TIMER_CALLBACK( mc68328_pwm_transition ) +{ + device_t *device = machine.device(MC68328_TAG); + mc68328_t* mc68328 = mc68328_get_safe_token( device ); + + if(mc68328->regs.pwmw >= mc68328->regs.pwmp || mc68328->regs.pwmw == 0 || mc68328->regs.pwmp == 0) + { + mc68328->pwm->adjust(attotime::never); + return; + } + + if(((mc68328->regs.pwmc & PWMC_POL) == 0 && (mc68328->regs.pwmc & PWMC_PIN) != 0) || + ((mc68328->regs.pwmc & PWMC_POL) != 0 && (mc68328->regs.pwmc & PWMC_PIN) == 0)) + { + UINT32 frequency = 32768 * 506; + UINT32 divisor = 4 << (mc68328->regs.pwmc & PWMC_CLKSEL); // ?? Datasheet says 2 <<, but then we're an octave higher than CoPilot. + attotime period; + + frequency /= divisor; + period = attotime::from_hz(frequency) * (mc68328->regs.pwmp - mc68328->regs.pwmw); + + mc68328->pwm->adjust(period); + + if(mc68328->regs.pwmc & PWMC_IRQEN) + { + mc68328_set_interrupt_line(device, INT_PWM, 1); + } + } + else + { + UINT32 frequency = 32768 * 506; + UINT32 divisor = 4 << (mc68328->regs.pwmc & PWMC_CLKSEL); // ?? Datasheet says 2 <<, but then we're an octave higher than CoPilot. + attotime period; + + frequency /= divisor; + period = attotime::from_hz(frequency) * mc68328->regs.pwmw; + + mc68328->pwm->adjust(period); + } + + mc68328->regs.pwmc ^= PWMC_PIN; + + if( !mc68328->out_pwm.isnull() ) + { + mc68328->out_pwm( 0, (mc68328->regs.pwmc & PWMC_PIN) ? 1 : 0 ); + } +} + +static TIMER_CALLBACK( mc68328_rtc_tick ) +{ + device_t *device = machine.device(MC68328_TAG); + mc68328_t* mc68328 = mc68328_get_safe_token( device ); + + if(mc68328->regs.rtcctl & RTCCTL_ENABLE) + { + UINT32 set_int = 0; + + mc68328->regs.hmsr++; + + if(mc68328->regs.rtcienr & RTCINT_SECOND) + { + set_int = 1; + mc68328->regs.rtcisr |= RTCINT_SECOND; + } + + if((mc68328->regs.hmsr & 0x0000003f) == 0x0000003c) + { + mc68328->regs.hmsr &= 0xffffffc0; + mc68328->regs.hmsr += 0x00010000; + + if(mc68328->regs.rtcienr & RTCINT_MINUTE) + { + set_int = 1; + mc68328->regs.rtcisr |= RTCINT_MINUTE; + } + + if((mc68328->regs.hmsr & 0x003f0000) == 0x003c0000) + { + mc68328->regs.hmsr &= 0xffc0ffff; + mc68328->regs.hmsr += 0x0100000; + + if((mc68328->regs.hmsr & 0x1f000000) == 0x18000000) + { + mc68328->regs.hmsr &= 0xe0ffffff; + + if(mc68328->regs.rtcienr & RTCINT_DAY) + { + set_int = 1; + mc68328->regs.rtcisr |= RTCINT_DAY; + } + } + } + + if(mc68328->regs.stpwtch != 0x003f) + { + mc68328->regs.stpwtch--; + mc68328->regs.stpwtch &= 0x003f; + + if(mc68328->regs.stpwtch == 0x003f) + { + if(mc68328->regs.rtcienr & RTCINT_STOPWATCH) + { + set_int = 1; + mc68328->regs.rtcisr |= RTCINT_STOPWATCH; + } + } + } + } + + if(mc68328->regs.hmsr == mc68328->regs.alarm) + { + if(mc68328->regs.rtcienr & RTCINT_ALARM) + { + set_int = 1; + mc68328->regs.rtcisr |= RTCINT_STOPWATCH; + } + } + + if(set_int) + { + mc68328_set_interrupt_line(device, INT_RTC, 1); + } + else + { + mc68328_set_interrupt_line(device, INT_RTC, 0); + } + } +} + +WRITE16_DEVICE_HANDLER( mc68328_w ) +{ + mc68328_t* mc68328 = mc68328_get_safe_token( device ); + UINT32 address = offset << 1; + UINT16 temp16[4] = { 0 }; + UINT32 imr_old = mc68328->regs.imr, imr_diff; + + switch(address) + { + case 0x000: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: Unknown address (0xfff001) = %02x\n", data & 0x00ff); + } + else + { + verboselog(space.machine(), 2, "mc68328_w: SCR = %02x\n", (data >> 8) & 0x00ff); + } + break; + + case 0x100: + verboselog(space.machine(), 2, "mc68328_w: GRPBASEA = %04x\n", data); + mc68328->regs.grpbasea = data; + break; + + case 0x102: + verboselog(space.machine(), 2, "mc68328_w: GRPBASEB = %04x\n", data); + mc68328->regs.grpbaseb = data; + break; + + case 0x104: + verboselog(space.machine(), 2, "mc68328_w: GRPBASEC = %04x\n", data); + mc68328->regs.grpbasec = data; + break; + + case 0x106: + verboselog(space.machine(), 2, "mc68328_w: GRPBASED = %04x\n", data); + mc68328->regs.grpbased = data; + break; + + case 0x108: + verboselog(space.machine(), 2, "mc68328_w: GRPMASKA = %04x\n", data); + mc68328->regs.grpmaska = data; + break; + + case 0x10a: + verboselog(space.machine(), 2, "mc68328_w: GRPMASKB = %04x\n", data); + mc68328->regs.grpmaskb = data; + break; + + case 0x10c: + verboselog(space.machine(), 2, "mc68328_w: GRPMASKC = %04x\n", data); + mc68328->regs.grpmaskc = data; + break; + + case 0x10e: + verboselog(space.machine(), 2, "mc68328_w: GRPMASKD = %04x\n", data); + mc68328->regs.grpmaskd = data; + break; + + case 0x110: + verboselog(space.machine(), 5, "mc68328_w: CSA0(0) = %04x\n", data); + mc68328->regs.csa0 &= 0xffff0000 | (~mem_mask); + mc68328->regs.csa0 |= data & mem_mask; + break; + + case 0x112: + verboselog(space.machine(), 5, "mc68328_w: CSA0(16) = %04x\n", data); + mc68328->regs.csa0 &= ~(mem_mask << 16); + mc68328->regs.csa0 |= (data & mem_mask) << 16; + break; + + case 0x114: + verboselog(space.machine(), 5, "mc68328_w: CSA1(0) = %04x\n", data); + mc68328->regs.csa1 &= 0xffff0000 | (~mem_mask); + mc68328->regs.csa1 |= data & mem_mask; + break; + + case 0x116: + verboselog(space.machine(), 5, "mc68328_w: CSA1(16) = %04x\n", data); + mc68328->regs.csa1 &= ~(mem_mask << 16); + mc68328->regs.csa1 |= (data & mem_mask) << 16; + break; + + case 0x118: + verboselog(space.machine(), 5, "mc68328_w: CSA2(0) = %04x\n", data); + mc68328->regs.csa2 &= 0xffff0000 | (~mem_mask); + mc68328->regs.csa2 |= data & mem_mask; + break; + + case 0x11a: + verboselog(space.machine(), 5, "mc68328_w: CSA2(16) = %04x\n", data); + mc68328->regs.csa2 &= ~(mem_mask << 16); + mc68328->regs.csa2 |= (data & mem_mask) << 16; + break; + + case 0x11c: + verboselog(space.machine(), 5, "mc68328_w: CSA3(0) = %04x\n", data); + mc68328->regs.csa3 &= 0xffff0000 | (~mem_mask); + mc68328->regs.csa3 |= data & mem_mask; + break; + + case 0x11e: + verboselog(space.machine(), 5, "mc68328_w: CSA3(16) = %04x\n", data); + mc68328->regs.csa3 &= ~(mem_mask << 16); + mc68328->regs.csa3 |= (data & mem_mask) << 16; + break; + + case 0x120: + verboselog(space.machine(), 5, "mc68328_w: CSB0(0) = %04x\n", data); + mc68328->regs.csb0 &= 0xffff0000 | (~mem_mask); + mc68328->regs.csb0 |= data & mem_mask; + break; + + case 0x122: + verboselog(space.machine(), 5, "mc68328_w: CSB0(16) = %04x\n", data); + mc68328->regs.csb0 &= ~(mem_mask << 16); + mc68328->regs.csb0 |= (data & mem_mask) << 16; + break; + + case 0x124: + verboselog(space.machine(), 5, "mc68328_w: CSB1(0) = %04x\n", data); + mc68328->regs.csb1 &= 0xffff0000 | (~mem_mask); + mc68328->regs.csb1 |= data & mem_mask; + break; + + case 0x126: + verboselog(space.machine(), 5, "mc68328_w: CSB1(16) = %04x\n", data); + mc68328->regs.csb1 &= ~(mem_mask << 16); + mc68328->regs.csb1 |= (data & mem_mask) << 16; + break; + + case 0x128: + verboselog(space.machine(), 5, "mc68328_w: CSB2(0) = %04x\n", data); + mc68328->regs.csb2 &= 0xffff0000 | (~mem_mask); + mc68328->regs.csb2 |= data & mem_mask; + break; + + case 0x12a: + verboselog(space.machine(), 5, "mc68328_w: CSB2(16) = %04x\n", data); + mc68328->regs.csb2 &= ~(mem_mask << 16); + mc68328->regs.csb2 |= (data & mem_mask) << 16; + break; + + case 0x12c: + verboselog(space.machine(), 5, "mc68328_w: CSB3(0) = %04x\n", data); + mc68328->regs.csb3 &= 0xffff0000 | (~mem_mask); + mc68328->regs.csb3 |= data & mem_mask; + break; + + case 0x12e: + verboselog(space.machine(), 5, "mc68328_w: CSB3(16) = %04x\n", data); + mc68328->regs.csb3 &= ~(mem_mask << 16); + mc68328->regs.csb3 |= (data & mem_mask) << 16; + break; + + case 0x130: + verboselog(space.machine(), 5, "mc68328_w: CSC0(0) = %04x\n", data); + mc68328->regs.csc0 &= 0xffff0000 | (~mem_mask); + mc68328->regs.csc0 |= data & mem_mask; + break; + + case 0x132: + verboselog(space.machine(), 5, "mc68328_w: CSC0(16) = %04x\n", data); + mc68328->regs.csc0 &= ~(mem_mask << 16); + mc68328->regs.csc0 |= (data & mem_mask) << 16; + break; + + case 0x134: + verboselog(space.machine(), 5, "mc68328_w: CSC1(0) = %04x\n", data); + mc68328->regs.csc1 &= 0xffff0000 | (~mem_mask); + mc68328->regs.csc1 |= data & mem_mask; + break; + + case 0x136: + verboselog(space.machine(), 5, "mc68328_w: CSC1(16) = %04x\n", data); + mc68328->regs.csc1 &= ~(mem_mask << 16); + mc68328->regs.csc1 |= (data & mem_mask) << 16; + break; + + case 0x138: + verboselog(space.machine(), 5, "mc68328_w: CSC2(0) = %04x\n", data); + mc68328->regs.csc2 &= 0xffff0000 | (~mem_mask); + mc68328->regs.csc2 |= data & mem_mask; + break; + + case 0x13a: + verboselog(space.machine(), 5, "mc68328_w: CSC2(16) = %04x\n", data); + mc68328->regs.csc2 &= ~(mem_mask << 16); + mc68328->regs.csc2 |= (data & mem_mask) << 16; + break; + + case 0x13c: + verboselog(space.machine(), 5, "mc68328_w: CSC3(0) = %04x\n", data); + mc68328->regs.csc3 &= 0xffff0000 | (~mem_mask); + mc68328->regs.csc3 |= data & mem_mask; + break; + + case 0x13e: + verboselog(space.machine(), 5, "mc68328_w: CSC3(16) = %04x\n", data); + mc68328->regs.csc3 &= ~(mem_mask << 16); + mc68328->regs.csc3 |= (data & mem_mask) << 16; + break; + + case 0x140: + verboselog(space.machine(), 5, "mc68328_w: CSD0(0) = %04x\n", data); + mc68328->regs.csd0 &= 0xffff0000 | (~mem_mask); + mc68328->regs.csd0 |= data & mem_mask; + break; + + case 0x142: + verboselog(space.machine(), 5, "mc68328_w: CSD0(16) = %04x\n", data); + mc68328->regs.csd0 &= ~(mem_mask << 16); + mc68328->regs.csd0 |= (data & mem_mask) << 16; + break; + + case 0x144: + verboselog(space.machine(), 5, "mc68328_w: CSD1(0) = %04x\n", data); + mc68328->regs.csd1 &= 0xffff0000 | (~mem_mask); + mc68328->regs.csd1 |= data & mem_mask; + break; + + case 0x146: + verboselog(space.machine(), 5, "mc68328_w: CSD1(16) = %04x\n", data); + mc68328->regs.csd1 &= ~(mem_mask << 16); + mc68328->regs.csd1 |= (data & mem_mask) << 16; + break; + + case 0x148: + verboselog(space.machine(), 5, "mc68328_w: CSD2(0) = %04x\n", data); + mc68328->regs.csd2 &= 0xffff0000 | (~mem_mask); + mc68328->regs.csd2 |= data & mem_mask; + break; + + case 0x14a: + verboselog(space.machine(), 5, "mc68328_w: CSD2(16) = %04x\n", data); + mc68328->regs.csd2 &= ~(mem_mask << 16); + mc68328->regs.csd2 |= (data & mem_mask) << 16; + break; + + case 0x14c: + verboselog(space.machine(), 5, "mc68328_w: CSD3(0) = %04x\n", data); + mc68328->regs.csd3 &= 0xffff0000 | (~mem_mask); + mc68328->regs.csd3 |= data & mem_mask; + break; + + case 0x14e: + verboselog(space.machine(), 5, "mc68328_w: CSD3(16) = %04x\n", data); + mc68328->regs.csd3 &= ~(mem_mask << 16); + mc68328->regs.csd3 |= (data & mem_mask) << 16; + break; + + case 0x200: + verboselog(space.machine(), 2, "mc68328_w: PLLCR = %04x\n", data); + mc68328->regs.pllcr = data; + break; + + case 0x202: + verboselog(space.machine(), 2, "mc68328_w: PLLFSR = %04x\n", data); + mc68328->regs.pllfsr = data; + break; + + case 0x206: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: PCTLR = %02x\n", data & 0x00ff); + mc68328->regs.pctlr = data & 0x00ff; + } + else + { + verboselog(space.machine(), 2, "mc68328_w: Unknown address (0xfff206) = %02x\n", (data >> 8) & 0x00ff); + } + break; + + case 0x300: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: Unknown address (0xfff301) = %02x\n", data & 0x00ff); + } + else + { + verboselog(space.machine(), 2, "mc68328_w: IVR = %02x\n", (data >> 8) & 0x00ff); + mc68328->regs.ivr = (data >> 8) & 0x00ff; + } + break; + + case 0x302: + verboselog(space.machine(), 2, "mc68328_w: ICR = %04x\n", data); + mc68328->regs.icr = data; + break; + + case 0x304: + verboselog(space.machine(), 2, "mc68328_w: IMR(16) = %04x\n", data); + mc68328->regs.imr &= ~(mem_mask << 16); + mc68328->regs.imr |= (data & mem_mask) << 16; + mc68328->regs.isr &= ~((data & mem_mask) << 16); + + imr_diff = imr_old ^ mc68328->regs.imr; + mc68328_set_interrupt_line(device, imr_diff, 0); + break; + + case 0x306: + verboselog(space.machine(), 2, "mc68328_w: IMR(0) = %04x\n", data); + mc68328->regs.imr &= 0xffff0000 | (~mem_mask); + mc68328->regs.imr |= data & mem_mask; + mc68328->regs.isr &= ~(data & mem_mask); + + imr_diff = imr_old ^ mc68328->regs.imr; + mc68328_set_interrupt_line(device, imr_diff, 0); + break; + + case 0x308: + { + verboselog(space.machine(), 2, "mc68328_w: IWR(16) = %04x\n", data); + mc68328->regs.iwr &= ~(mem_mask << 16); + mc68328->regs.iwr |= (data & mem_mask) << 16; + } + break; + + case 0x30a: + verboselog(space.machine(), 2, "mc68328_w: IWR(0) = %04x\n", data); + mc68328->regs.iwr &= 0xffff0000 | (~mem_mask); + mc68328->regs.iwr |= data & mem_mask; + break; + + case 0x30c: + verboselog(space.machine(), 2, "mc68328_w: ISR(16) = %04x\n", data); + // Clear edge-triggered IRQ1 + if((mc68328->regs.icr & ICR_ET1) == ICR_ET1 && (data & INT_IRQ1_SHIFT) == INT_IRQ1_SHIFT) + { + mc68328->regs.isr &= ~INT_IRQ1; + } + + // Clear edge-triggered IRQ2 + if((mc68328->regs.icr & ICR_ET2) == ICR_ET2 && (data & INT_IRQ2_SHIFT) == INT_IRQ2_SHIFT) + { + mc68328->regs.isr &= ~INT_IRQ2; + } + + // Clear edge-triggered IRQ3 + if((mc68328->regs.icr & ICR_ET3) == ICR_ET3 && (data & INT_IRQ3_SHIFT) == INT_IRQ3_SHIFT) + { + mc68328->regs.isr &= ~INT_IRQ3; + } + + // Clear edge-triggered IRQ6 + if((mc68328->regs.icr & ICR_ET6) == ICR_ET6 && (data & INT_IRQ6_SHIFT) == INT_IRQ6_SHIFT) + { + mc68328->regs.isr &= ~INT_IRQ6; + } + + // Clear edge-triggered IRQ7 + if((data & INT_IRQ7_SHIFT) == INT_IRQ7_SHIFT) + { + mc68328->regs.isr &= ~INT_IRQ7; + } + break; + + case 0x30e: + verboselog(space.machine(), 2, "mc68328_w: ISR(0) = %04x (Ignored)\n", data); + break; + + case 0x310: + verboselog(space.machine(), 2, "mc68328_w: IPR(16) = %04x (Ignored)\n"); + break; + + case 0x312: + verboselog(space.machine(), 2, "mc68328_w: IPR(0) = %04x (Ignored)\n"); + break; + + case 0x400: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: PADATA = %02x\n", data & 0x00ff); + mc68328->regs.padata = data & 0x00ff; + if(!mc68328->out_port_a.isnull()) + { + mc68328->out_port_a( 0, data & 0x00ff ); + } + } + else + { + verboselog(space.machine(), 2, "mc68328_w: PADIR = %02x\n", (data >> 8) & 0x00ff); + mc68328->regs.padir = (data >> 8) & 0x00ff; + } + break; + + case 0x402: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: PASEL = %02x\n", data & 0x00ff); + mc68328->regs.pasel = data & 0x00ff; + } + else + { + verboselog(space.machine(), 2, "mc68328_w: Unknown address (0xfff402) = %02x\n", (data >> 8) & 0x00ff); + } + break; + + case 0x408: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: PBDATA = %02x\n", data & 0x00ff); + mc68328->regs.pbdata = data & 0x00ff; + if(!mc68328->out_port_b.isnull()) + { + mc68328->out_port_b( 0, data & 0x00ff ); + } + } + else + { + verboselog(space.machine(), 2, "mc68328_w: PBDIR = %02x\n", (data >> 8) & 0x00ff); + mc68328->regs.pbdir = (data >> 8) & 0x00ff; + } + break; + + case 0x40a: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: PBSEL = %02x\n", data & 0x00ff); + mc68328->regs.pbsel = data & 0x00ff; + } + else + { + verboselog(space.machine(), 2, "mc68328_w: Unknown address (0xfff40a) = %02x\n", (data >> 8) & 0x00ff); + } + break; + + case 0x410: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: PCDATA = %02x\n", data & 0x00ff); + mc68328->regs.pcdata = data & 0x00ff; + if(!mc68328->out_port_c.isnull()) + { + mc68328->out_port_c( 0, data & 0x00ff ); + } + } + else + { + verboselog(space.machine(), 2, "mc68328_w: PCDIR = %02x\n", (data >> 8) & 0x00ff); + mc68328->regs.pcdir = (data >> 8) & 0x00ff; + } + break; + + case 0x412: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: PCSEL = %02x\n", data & 0x00ff); + mc68328->regs.pcsel = data & 0x00ff; + } + else + { + verboselog(space.machine(), 2, "mc68328_w: Unknown address (0xfff412) = %02x\n", (data >> 8) & 0x00ff); + } + break; + + case 0x418: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: PDDATA = %02x\n", data & 0x00ff); + + mc68328->regs.pddataedge &= ~(data & 0x00ff); + mc68328_poll_port_d_interrupts(device); + } + else + { + verboselog(space.machine(), 2, "mc68328_w: PDDIR = %02x\n", (data >> 8) & 0x00ff); + mc68328->regs.pddir = (data >> 8) & 0x00ff; + } + break; + + case 0x41a: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: Unknown address (0xfff41b) = %02x\n", data & 0x00ff); + } + else + { + verboselog(space.machine(), 2, "mc68328_w: PDPUEN = %02x\n", (data >> 8) & 0x00ff); + mc68328->regs.pdpuen = (data >> 8) & 0x00ff; + } + break; + + case 0x41c: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: PDIRQEN = %02x\n", data & 0x00ff); + mc68328->regs.pdirqen = data & 0x00ff; + + mc68328_poll_port_d_interrupts(device); + } + else + { + verboselog(space.machine(), 2, "mc68328_w: PDPOL = %02x\n", (data >> 8) & 0x00ff); + mc68328->regs.pdpol = (data >> 8) & 0x00ff; + } + break; + + case 0x41e: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: PDIRQEDGE = %02x\n", data & 0x00ff); + mc68328->regs.pdirqedge = data & 0x00ff; + } + else + { + verboselog(space.machine(), 2, "mc68328_w: Unknown address (0xfff41e) = %02x\n", (data >> 8) & 0x00ff); + } + break; + + case 0x420: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: PEDATA = %02x\n", data & 0x00ff); + mc68328->regs.pedata = data & 0x00ff; + if(!mc68328->out_port_e.isnull()) + { + mc68328->out_port_e( 0, data & 0x00ff ); + } + } + else + { + verboselog(space.machine(), 2, "mc68328_w: PEDIR = %02x\n", (data >> 8) & 0x00ff); + mc68328->regs.pedir = (data >> 8) & 0x00ff; + } + break; + + case 0x422: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: PESEL = %02x\n", data & 0x00ff); + mc68328->regs.pesel = data & 0x00ff; + } + else + { + verboselog(space.machine(), 2, "mc68328_w: PEPUEN = %02x\n", (data >> 8) & 0x00ff); + mc68328->regs.pepuen = (data >> 8) & 0x00ff; + mc68328->regs.pedata |= mc68328->regs.pepuen; + } + break; + + case 0x428: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: PFDATA = %02x\n", data & 0x00ff); + mc68328->regs.pfdata = data & 0x00ff; + if(!mc68328->out_port_f.isnull()) + { + mc68328->out_port_f( 0, data & 0x00ff ); + } + } + else + { + verboselog(space.machine(), 2, "mc68328_w: PFDIR = %02x\n", (data >> 8) & 0x00ff); + mc68328->regs.pfdir = (data >> 8) & 0x00ff; + } + break; + + case 0x42a: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: PFSEL = %02x\n", data & 0x00ff); + mc68328->regs.pfsel = data & 0x00ff; + } + else + { + verboselog(space.machine(), 2, "mc68328_w: PFPUEN = %02x\n", (data >> 8) & 0x00ff); + mc68328->regs.pfpuen = (data >> 8) & 0x00ff; + } + break; + + case 0x430: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: PGDATA = %02x\n", data & 0x00ff); + mc68328->regs.pgdata = data & 0x00ff; + if(!mc68328->out_port_g.isnull()) + { + mc68328->out_port_g( 0, data & 0x00ff ); + } + } + else + { + verboselog(space.machine(), 2, "mc68328_w: PGDIR = %02x\n", (data >> 8) & 0x00ff); + mc68328->regs.pgdir = (data >> 8) & 0x00ff; + } + break; + + case 0x432: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: PGSEL = %02x\n", data & 0x00ff); + mc68328->regs.pgsel = data & 0x00ff; + } + else + { + verboselog(space.machine(), 2, "mc68328_w: PGPUEN = %02x\n", (data >> 8) & 0x00ff); + mc68328->regs.pgpuen = (data >> 8) & 0x00ff; + } + break; + + case 0x438: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: PJDATA = %02x\n", data & 0x00ff); + mc68328->regs.pjdata = data & 0x00ff; + if(!mc68328->out_port_j.isnull()) + { + mc68328->out_port_j( 0, data & 0x00ff ); + } + } + else + { + verboselog(space.machine(), 2, "mc68328_w: PJDIR = %02x\n", (data >> 8) & 0x00ff); + mc68328->regs.pjdir = (data >> 8) & 0x00ff; + } + break; + + case 0x43a: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: PJSEL = %02x\n", data & 0x00ff); + mc68328->regs.pjsel = data & 0x00ff; + } + else + { + verboselog(space.machine(), 2, "mc68328_w: Unknown address (0xfff43a) = %02x\n", (data >> 8) & 0x00ff); + } + break; + + case 0x440: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: PKDATA = %02x\n", data & 0x00ff); + mc68328->regs.pkdata = data & 0x00ff; + if(!mc68328->out_port_k.isnull()) + { + mc68328->out_port_k( 0, data & 0x00ff ); + } + } + else + { + verboselog(space.machine(), 2, "mc68328_w: PKDIR = %02x\n", (data >> 8) & 0x00ff); + mc68328->regs.pkdir = (data >> 8) & 0x00ff; + } + break; + + case 0x442: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: PKSEL = %02x\n", data & 0x00ff); + mc68328->regs.pksel = data & 0x00ff; + } + else + { + verboselog(space.machine(), 2, "mc68328_w: PKPUEN = %02x\n", (data >> 8) & 0x00ff); + mc68328->regs.pgpuen = (data >> 8) & 0x00ff; + } + break; + + case 0x448: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: PMDATA = %02x\n", data & 0x00ff); + mc68328->regs.pmdata = data & 0x00ff; + if(!mc68328->out_port_m.isnull()) + { + mc68328->out_port_m( 0, data & 0x00ff ); + } + } + else + { + verboselog(space.machine(), 2, "mc68328_w: PMDIR = %02x\n", (data >> 8) & 0x00ff); + mc68328->regs.pmdir = (data >> 8) & 0x00ff; + } + break; + + case 0x44a: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: PMSEL = %02x\n", data & 0x00ff); + mc68328->regs.pmsel = data & 0x00ff; + } + else + { + verboselog(space.machine(), 2, "mc68328_w: PMPUEN = %02x\n", (data >> 8) & 0x00ff); + mc68328->regs.pmpuen = (data >> 8) & 0x00ff; + } + break; + + case 0x500: + verboselog(space.machine(), 2, "mc68328_w: PWMC = %04x\n", data); + + mc68328->regs.pwmc = data; + + if(mc68328->regs.pwmc & PWMC_PWMIRQ) + { + mc68328_set_interrupt_line(device, INT_PWM, 1); + } + + mc68328->regs.pwmc &= ~PWMC_LOAD; + + if((mc68328->regs.pwmc & PWMC_PWMEN) != 0 && mc68328->regs.pwmw != 0 && mc68328->regs.pwmp != 0) + { + UINT32 frequency = 32768 * 506; + UINT32 divisor = 4 << (mc68328->regs.pwmc & PWMC_CLKSEL); // ?? Datasheet says 2 <<, but then we're an octave higher than CoPilot. + attotime period; + frequency /= divisor; + period = attotime::from_hz(frequency) * mc68328->regs.pwmw; + mc68328->pwm->adjust(period); + if(mc68328->regs.pwmc & PWMC_IRQEN) + { + mc68328_set_interrupt_line(device, INT_PWM, 1); + } + mc68328->regs.pwmc ^= PWMC_PIN; + } + else + { + mc68328->pwm->adjust(attotime::never); + } + break; + + case 0x502: + verboselog(space.machine(), 2, "mc68328_w: PWMP = %04x\n", data); + mc68328->regs.pwmp = data; + break; + + case 0x504: + verboselog(space.machine(), 2, "mc68328_w: PWMW = %04x\n", data); + mc68328->regs.pwmw = data; + break; + + case 0x506: + verboselog(space.machine(), 2, "mc68328_w: PWMCNT = %04x\n", data); + mc68328->regs.pwmcnt = 0; + break; + + case 0x600: + verboselog(space.machine(), 2, "mc68328_w: TCTL1 = %04x\n", data); + temp16[0] = mc68328->regs.tctl[0]; + mc68328->regs.tctl[0] = data; + if((temp16[0] & TCTL_TEN) == (mc68328->regs.tctl[0] & TCTL_TEN)) + { + mc68328_maybe_start_timer(device, 0, 0); + } + else if((temp16[0] & TCTL_TEN) != TCTL_TEN_ENABLE && (mc68328->regs.tctl[0] & TCTL_TEN) == TCTL_TEN_ENABLE) + { + mc68328_maybe_start_timer(device, 0, 1); + } + break; + + case 0x602: + verboselog(space.machine(), 2, "mc68328_w: TPRER1 = %04x\n", data); + mc68328->regs.tprer[0] = data; + mc68328_maybe_start_timer(device, 0, 0); + break; + + case 0x604: + verboselog(space.machine(), 2, "mc68328_w: TCMP1 = %04x\n", data); + mc68328->regs.tcmp[0] = data; + mc68328_maybe_start_timer(device, 0, 0); + break; + + case 0x606: + verboselog(space.machine(), 2, "mc68328_w: TCR1 = %04x (Ignored)\n", data); + break; + + case 0x608: + verboselog(space.machine(), 2, "mc68328_w: TCN1 = %04x (Ignored)\n", data); + break; + + case 0x60a: + verboselog(space.machine(), 5, "mc68328_w: TSTAT1 = %04x\n", data); + mc68328->regs.tstat[0] &= ~mc68328->regs.tclear[0]; + if(!(mc68328->regs.tstat[0] & TSTAT_COMP)) + { + mc68328_set_interrupt_line(device, INT_TIMER1, 0); + } + break; + + case 0x60c: + verboselog(space.machine(), 2, "mc68328_w: TCTL2 = %04x\n", data); + temp16[0] = mc68328->regs.tctl[1]; + mc68328->regs.tctl[1] = data; + if((temp16[0] & TCTL_TEN) == (mc68328->regs.tctl[1] & TCTL_TEN)) + { + mc68328_maybe_start_timer(device, 1, 0); + } + else if((temp16[0] & TCTL_TEN) != TCTL_TEN_ENABLE && (mc68328->regs.tctl[1] & TCTL_TEN) == TCTL_TEN_ENABLE) + { + mc68328_maybe_start_timer(device, 1, 1); + } + break; + + case 0x60e: + verboselog(space.machine(), 2, "mc68328_w: TPRER2 = %04x\n", data); + mc68328->regs.tprer[1] = data; + mc68328_maybe_start_timer(device, 1, 0); + break; + + case 0x610: + verboselog(space.machine(), 2, "mc68328_w: TCMP2 = %04x\n", data); + mc68328->regs.tcmp[1] = data; + mc68328_maybe_start_timer(device, 1, 0); + break; + + case 0x612: + verboselog(space.machine(), 2, "mc68328_w: TCR2 = %04x (Ignored)\n", data); + break; + + case 0x614: + verboselog(space.machine(), 2, "mc68328_w: TCN2 = %04x (Ignored)\n", data); + break; + + case 0x616: + verboselog(space.machine(), 2, "mc68328_w: TSTAT2 = %04x\n", data); + mc68328->regs.tstat[1] &= ~mc68328->regs.tclear[1]; + if(!(mc68328->regs.tstat[1] & TSTAT_COMP)) + { + mc68328_set_interrupt_line(device, INT_TIMER2, 0); + } + break; + + case 0x618: + verboselog(space.machine(), 2, "mc68328_w: WCTLR = %04x\n", data); + mc68328->regs.wctlr = data; + break; + + case 0x61a: + verboselog(space.machine(), 2, "mc68328_w: WCMPR = %04x\n", data); + mc68328->regs.wcmpr = data; + break; + + case 0x61c: + verboselog(space.machine(), 2, "mc68328_w: WCN = %04x (Ignored)\n", data); + break; + + case 0x700: + verboselog(space.machine(), 2, "mc68328_w: SPISR = %04x\n", data); + mc68328->regs.spisr = data; + break; + + case 0x800: + verboselog(space.machine(), 2, "mc68328_w: SPIMDATA = %04x\n", data); + if(!mc68328->out_spim.isnull()) + { + mc68328->out_spim( 0, data, 0xffff ); + } + else + { + mc68328->regs.spimdata = data; + } + break; + + case 0x802: + verboselog(space.machine(), 2, "mc68328_w: SPIMCONT = %04x\n", data); + verboselog(space.machine(), 3, " Count = %d\n", data & SPIM_CLOCK_COUNT); + verboselog(space.machine(), 3, " Polarity = %s\n", (data & SPIM_POL) ? "Inverted" : "Active-high"); + verboselog(space.machine(), 3, " Phase = %s\n", (data & SPIM_PHA) ? "Opposite" : "Normal"); + verboselog(space.machine(), 3, " IRQ Enable = %s\n", (data & SPIM_IRQEN) ? "Enable" : "Disable"); + verboselog(space.machine(), 3, " IRQ Pending = %s\n", (data & SPIM_SPIMIRQ) ? "Yes" : "No"); + verboselog(space.machine(), 3, " Exchange = %s\n", (data & SPIM_XCH) ? "Initiate" : "Idle"); + verboselog(space.machine(), 3, " SPIM Enable = %s\n", (data & SPIM_SPMEN) ? "Enable" : "Disable"); + verboselog(space.machine(), 3, " Data Rate = Divide By %d\n", 1 << ((((data & SPIM_RATE) >> 13) & 0x0007) + 2) ); + mc68328->regs.spimcont = data; + // $$HACK$$ We should probably emulate the ADS7843 A/D device properly. + if(data & SPIM_XCH) + { + mc68328->regs.spimcont &= ~SPIM_XCH; + if(mc68328->iface->spim_xch_trigger) + { + (mc68328->iface->spim_xch_trigger)( device ); + } + if(data & SPIM_IRQEN) + { + mc68328->regs.spimcont |= SPIM_SPIMIRQ; + verboselog(space.machine(), 3, "Triggering SPIM Interrupt\n" ); + mc68328_set_interrupt_line(device, INT_SPIM, 1); + } + } + if(!(data & SPIM_IRQEN)) + { + mc68328_set_interrupt_line(device, INT_SPIM, 0); + } + break; + + case 0x900: + verboselog(space.machine(), 2, "mc68328_w: USTCNT = %04x\n", data); + mc68328->regs.ustcnt = data; + break; + + case 0x902: + verboselog(space.machine(), 2, "mc68328_w: UBAUD = %04x\n", data); + mc68328->regs.ubaud = data; + break; + + case 0x904: + verboselog(space.machine(), 2, "mc68328_w: URX = %04x\n", data); + break; + + case 0x906: + verboselog(space.machine(), 2, "mc68328_w: UTX = %04x\n", data); + break; + + case 0x908: + verboselog(space.machine(), 2, "mc68328_w: UMISC = %04x\n", data); + mc68328->regs.umisc = data; + break; + + case 0xa00: + verboselog(space.machine(), 2, "mc68328_w: LSSA(16) = %04x\n", data); + mc68328->regs.lssa &= ~(mem_mask << 16); + mc68328->regs.lssa |= (data & mem_mask) << 16; + verboselog(space.machine(), 3, " Address: %08x\n", mc68328->regs.lssa); + break; + + case 0xa02: + verboselog(space.machine(), 2, "mc68328_w: LSSA(0) = %04x\n", data); + mc68328->regs.lssa &= 0xffff0000 | (~mem_mask); + mc68328->regs.lssa |= data & mem_mask; + verboselog(space.machine(), 3, " Address: %08x\n", mc68328->regs.lssa); + break; + + case 0xa04: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: LVPW = %02x\n", data & 0x00ff); + mc68328->regs.lvpw = data & 0x00ff; + verboselog(space.machine(), 3, " Page Width: %d or %d\n", (mc68328->regs.lvpw + 1) * ((mc68328->regs.lpicf & 0x01) ? 8 : 16)); + } + else + { + verboselog(space.machine(), 2, "mc68328_w: Unknown address (0xfffa04) = %02x\n", (data >> 8) & 0x00ff); + } + break; + + case 0xa08: + verboselog(space.machine(), 2, "mc68328_w: LXMAX = %04x\n", data); + mc68328->regs.lxmax = data; + verboselog(space.machine(), 3, " Width: %d\n", (data & 0x03ff) + 1); + break; + + case 0xa0a: + verboselog(space.machine(), 2, "mc68328_w: LYMAX = %04x\n", data); + mc68328->regs.lymax = data; + verboselog(space.machine(), 3, " Height: %d\n", (data & 0x03ff) + 1); + break; + + case 0xa18: + verboselog(space.machine(), 2, "mc68328_w: LCXP = %04x\n", data); + mc68328->regs.lcxp = data; + verboselog(space.machine(), 3, " X Position: %d\n", data & 0x03ff); + switch(mc68328->regs.lcxp >> 14) + { + case 0: + verboselog(space.machine(), 3, " Cursor Control: Transparent\n"); + break; + + case 1: + verboselog(space.machine(), 3, " Cursor Control: Black\n"); + break; + + case 2: + verboselog(space.machine(), 3, " Cursor Control: Reverse\n"); + break; + + case 3: + verboselog(space.machine(), 3, " Cursor Control: Invalid\n"); + break; + } + break; + + case 0xa1a: + verboselog(space.machine(), 2, "mc68328_w: LCYP = %04x\n", data); + mc68328->regs.lcyp = data; + verboselog(space.machine(), 3, " Y Position: %d\n", data & 0x01ff); + break; + + case 0xa1c: + verboselog(space.machine(), 2, "mc68328_w: LCWCH = %04x\n", data); + mc68328->regs.lcwch = data; + verboselog(space.machine(), 3, " Width: %d\n", (data >> 8) & 0x1f); + verboselog(space.machine(), 3, " Height: %d\n", data & 0x1f); + break; + + case 0xa1e: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: LBLKC = %02x\n", data & 0x00ff); + mc68328->regs.lblkc = data & 0x00ff; + verboselog(space.machine(), 3, " Blink Enable: %d\n", mc68328->regs.lblkc >> 7); + verboselog(space.machine(), 3, " Blink Divisor: %d\n", mc68328->regs.lblkc & 0x7f); + } + else + { + verboselog(space.machine(), 2, "mc68328_w: Unknown address (0xfffa1e) = %02x\n", (data >> 8) & 0x00ff); + } + break; + + case 0xa20: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: LPOLCF = %02x\n", data & 0x00ff); + mc68328->regs.lpolcf = data & 0x00ff; + verboselog(space.machine(), 3, " LCD Shift Clock Polarity: %s\n", (mc68328->regs.lpicf & 0x08) ? "Active positive edge of LCLK" : "Active negative edge of LCLK"); + verboselog(space.machine(), 3, " First-line marker polarity: %s\n", (mc68328->regs.lpicf & 0x04) ? "Active Low" : "Active High"); + verboselog(space.machine(), 3, " Line-pulse polarity: %s\n", (mc68328->regs.lpicf & 0x02) ? "Active Low" : "Active High"); + verboselog(space.machine(), 3, " Pixel polarity: %s\n", (mc68328->regs.lpicf & 0x01) ? "Active Low" : "Active High"); + } + else + { + verboselog(space.machine(), 2, "mc68328_w: LPICF = %02x\n", (data >> 8) & 0x00ff); + mc68328->regs.lpicf = (data >> 8) & 0x00ff; + switch((mc68328->regs.lpicf >> 1) & 0x03) + { + case 0: + verboselog(space.machine(), 3, " Bus Size: 1-bit\n"); + break; + + case 1: + verboselog(space.machine(), 3, " Bus Size: 2-bit\n"); + break; + + case 2: + verboselog(space.machine(), 3, " Bus Size: 4-bit\n"); + break; + + case 3: + verboselog(space.machine(), 3, " Bus Size: unused\n"); + break; + } + verboselog(space.machine(), 3, " Gray scale enable: %d\n", mc68328->regs.lpicf & 0x01); + } + break; + + case 0xa22: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: LACDRC = %02x\n", data & 0x00ff); + mc68328->regs.lacdrc = data & 0x00ff; + } + else + { + verboselog(space.machine(), 2, "mc68328_w: Unknown address (0xfffa22) = %02x\n", (data >> 8) & 0x00ff); + } + break; + + case 0xa24: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: LPXCD = %02x\n", data & 0x00ff); + mc68328->regs.lpxcd = data & 0x00ff; + verboselog(space.machine(), 3, " Clock Divisor: %d\n", mc68328->regs.lpxcd + 1); + } + else + { + verboselog(space.machine(), 2, "mc68328_w: Unknown address (0xfffa24) = %02x\n", (data >> 8) & 0x00ff); + } + break; + + case 0xa26: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: LCKCON = %02x\n", data & 0x00ff); + mc68328->regs.lckcon = data & 0x00ff; + verboselog(space.machine(), 3, " LCDC Enable: %d\n", (mc68328->regs.lckcon >> 7) & 0x01); + verboselog(space.machine(), 3, " DMA Burst Length: %d\n", ((mc68328->regs.lckcon >> 6) & 0x01) ? 16 : 8); + verboselog(space.machine(), 3, " DMA Bursting Clock Control: %d\n", ((mc68328->regs.lckcon >> 4) & 0x03) + 1); + verboselog(space.machine(), 3, " Bus Width: %d\n", ((mc68328->regs.lckcon >> 1) & 0x01) ? 8 : 16); + verboselog(space.machine(), 3, " Pixel Clock Divider Source: %s\n", (mc68328->regs.lckcon & 0x01) ? "PIX" : "SYS"); + } + else + { + verboselog(space.machine(), 2, "mc68328_w: Unknown address (0xfffa26) = %02x\n", (data >> 8) & 0x00ff); + } + break; + + case 0xa28: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: LLBAR = %02x\n", data & 0x00ff); + mc68328->regs.llbar = data & 0x00ff; + verboselog(space.machine(), 3, " Address: %d\n", (mc68328->regs.llbar & 0x7f) * ((mc68328->regs.lpicf & 0x01) ? 8 : 16)); + } + else + { + verboselog(space.machine(), 2, "mc68328_w: Unknown address (0xfffa28) = %02x\n", (data >> 8) & 0x00ff); + } + break; + + case 0xa2a: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: LOTCR = %02x\n", data & 0x00ff); + } + else + { + verboselog(space.machine(), 2, "mc68328_w: Unknown address (0xfffa2a) = %02x\n", (data >> 8) & 0x00ff); + } + break; + + case 0xa2c: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: LPOSR = %02x\n", data & 0x00ff); + mc68328->regs.lposr = data & 0x00ff; + verboselog(space.machine(), 3, " Byte Offset: %d\n", (mc68328->regs.lposr >> 3) & 0x01); + verboselog(space.machine(), 3, " Pixel Offset: %d\n", mc68328->regs.lposr & 0x07); + } + else + { + verboselog(space.machine(), 2, "mc68328_w: Unknown address (0xfffa2c) = %02x\n", (data >> 8) & 0x00ff); + } + break; + + case 0xa30: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_w: LFRCM = %02x\n", data & 0x00ff); + mc68328->regs.lfrcm = data & 0x00ff; + verboselog(space.machine(), 3, " X Modulation: %d\n", (mc68328->regs.lfrcm >> 4) & 0x0f); + verboselog(space.machine(), 3, " Y Modulation: %d\n", mc68328->regs.lfrcm & 0x0f); + } + else + { + verboselog(space.machine(), 2, "mc68328_w: Unknown address (0xfffa30) = %02x\n", (data >> 8) & 0x00ff); + } + break; + + case 0xa32: + verboselog(space.machine(), 2, "mc68328_w: LGPMR = %04x\n", data); + mc68328->regs.lgpmr = data; + verboselog(space.machine(), 3, " Palette 0: %d\n", (mc68328->regs.lgpmr >> 8) & 0x07); + verboselog(space.machine(), 3, " Palette 1: %d\n", (mc68328->regs.lgpmr >> 12) & 0x07); + verboselog(space.machine(), 3, " Palette 2: %d\n", (mc68328->regs.lgpmr >> 0) & 0x07); + verboselog(space.machine(), 3, " Palette 3: %d\n", (mc68328->regs.lgpmr >> 4) & 0x07); + break; + + case 0xb00: + verboselog(space.machine(), 2, "mc68328_w: HMSR(0) = %04x\n", data); + mc68328->regs.hmsr &= ~(mem_mask << 16); + mc68328->regs.hmsr |= (data & mem_mask) << 16; + mc68328->regs.hmsr &= 0x1f3f003f; + break; + + case 0xb02: + verboselog(space.machine(), 2, "mc68328_w: HMSR(16) = %04x\n", data); + mc68328->regs.hmsr &= 0xffff0000 | (~mem_mask); + mc68328->regs.hmsr |= data & mem_mask; + mc68328->regs.hmsr &= 0x1f3f003f; + break; + + case 0xb04: + verboselog(space.machine(), 2, "mc68328_w: ALARM(0) = %04x\n", data); + mc68328->regs.alarm &= ~(mem_mask << 16); + mc68328->regs.alarm |= (data & mem_mask) << 16; + mc68328->regs.alarm &= 0x1f3f003f; + break; + + case 0xb06: + verboselog(space.machine(), 2, "mc68328_w: ALARM(16) = %04x\n", data); + mc68328->regs.alarm &= 0xffff0000 | (~mem_mask); + mc68328->regs.alarm |= data & mem_mask; + mc68328->regs.alarm &= 0x1f3f003f; + break; + + case 0xb0c: + verboselog(space.machine(), 2, "mc68328_w: RTCCTL = %04x\n", data); + mc68328->regs.rtcctl = data & 0x00a0; + break; + + case 0xb0e: + verboselog(space.machine(), 2, "mc68328_w: RTCISR = %04x\n", data); + mc68328->regs.rtcisr &= ~data; + if(mc68328->regs.rtcisr == 0) + { + mc68328_set_interrupt_line(device, INT_RTC, 0); + } + break; + + case 0xb10: + verboselog(space.machine(), 2, "mc68328_w: RTCIENR = %04x\n", data); + mc68328->regs.rtcienr = data & 0x001f; + break; + + case 0xb12: + verboselog(space.machine(), 2, "mc68328_w: STPWTCH = %04x\n", data); + mc68328->regs.stpwtch = data & 0x003f; + break; + + default: + verboselog(space.machine(), 0, "mc68328_w: Unknown address (0x%06x) = %04x (%04x)\n", 0xfff000 + address, data, mem_mask); + break; + } +} + +READ16_DEVICE_HANDLER( mc68328_r ) +{ + mc68328_t* mc68328 = mc68328_get_safe_token( device ); + UINT16 temp16 = 0; + UINT32 address = offset << 1; + + switch(address) + { + case 0x000: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): Unknown address (0xfff001)\n", mem_mask); + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): SCR = %02x\n", mem_mask, mc68328->regs.scr); + return mc68328->regs.scr << 8; + } + break; + + case 0x100: + verboselog(space.machine(), 2, "mc68328_r (%04x): GRPBASEA = %04x\n", mem_mask, mc68328->regs.grpbasea); + return mc68328->regs.grpbasea; + + case 0x102: + verboselog(space.machine(), 2, "mc68328_r (%04x): GRPBASEB = %04x\n", mem_mask, mc68328->regs.grpbaseb); + return mc68328->regs.grpbaseb; + + case 0x104: + verboselog(space.machine(), 2, "mc68328_r (%04x): GRPBASEC = %04x\n", mem_mask, mc68328->regs.grpbasec); + return mc68328->regs.grpbasec; + + case 0x106: + verboselog(space.machine(), 2, "mc68328_r (%04x): GRPBASED = %04x\n", mem_mask, mc68328->regs.grpbased); + return mc68328->regs.grpbased; + + case 0x108: + verboselog(space.machine(), 2, "mc68328_r (%04x): GRPMASKA = %04x\n", mem_mask, mc68328->regs.grpmaska); + return mc68328->regs.grpmaska; + + case 0x10a: + verboselog(space.machine(), 2, "mc68328_r (%04x): GRPMASKB = %04x\n", mem_mask, mc68328->regs.grpmaskb); + return mc68328->regs.grpmaskb; + + case 0x10c: + verboselog(space.machine(), 2, "mc68328_r (%04x): GRPMASKC = %04x\n", mem_mask, mc68328->regs.grpmaskc); + return mc68328->regs.grpmaskc; + + case 0x10e: + verboselog(space.machine(), 2, "mc68328_r (%04x): GRPMASKD = %04x\n", mem_mask, mc68328->regs.grpmaskd); + return mc68328->regs.grpmaskd; + + case 0x110: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSA0(0) = %04x\n", mem_mask, mc68328->regs.csa0 & 0x0000ffff); + return mc68328->regs.csa0 & 0x0000ffff; + + case 0x112: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSA0(16) = %04x\n", mem_mask, mc68328->regs.csa0 >> 16); + return mc68328->regs.csa0 >> 16; + + case 0x114: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSA1(0) = %04x\n", mem_mask, mc68328->regs.csa1 & 0x0000ffff); + return mc68328->regs.csa1 & 0x0000ffff; + + case 0x116: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSA1(16) = %04x\n", mem_mask, mc68328->regs.csa1 >> 16); + return mc68328->regs.csa1 >> 16; + + case 0x118: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSA2(0) = %04x\n", mem_mask, mc68328->regs.csa2 & 0x0000ffff); + return mc68328->regs.csa2 & 0x0000ffff; + + case 0x11a: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSA2(16) = %04x\n", mem_mask, mc68328->regs.csa2 >> 16); + return mc68328->regs.csa2 >> 16; + + case 0x11c: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSA3(0) = %04x\n", mem_mask, mc68328->regs.csa3 & 0x0000ffff); + return mc68328->regs.csa3 & 0x0000ffff; + + case 0x11e: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSA3(16) = %04x\n", mem_mask, mc68328->regs.csa3 >> 16); + return mc68328->regs.csa3 >> 16; + + case 0x120: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSB0(0) = %04x\n", mem_mask, mc68328->regs.csb0 & 0x0000ffff); + return mc68328->regs.csb0 & 0x0000ffff; + + case 0x122: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSB0(16) = %04x\n", mem_mask, mc68328->regs.csb0 >> 16); + return mc68328->regs.csb0 >> 16; + + case 0x124: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSB1(0) = %04x\n", mem_mask, mc68328->regs.csb1 & 0x0000ffff); + return mc68328->regs.csb1 & 0x0000ffff; + + case 0x126: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSB1(16) = %04x\n", mem_mask, mc68328->regs.csb1 >> 16); + return mc68328->regs.csb1 >> 16; + + case 0x128: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSB2(0) = %04x\n", mem_mask, mc68328->regs.csb2 & 0x0000ffff); + return mc68328->regs.csb2 & 0x0000ffff; + + case 0x12a: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSB2(16) = %04x\n", mem_mask, mc68328->regs.csb2 >> 16); + return mc68328->regs.csb2 >> 16; + + case 0x12c: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSB3(0) = %04x\n", mem_mask, mc68328->regs.csb3 & 0x0000ffff); + return mc68328->regs.csb3 & 0x0000ffff; + + case 0x12e: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSB3(16) = %04x\n", mem_mask, mc68328->regs.csb3 >> 16); + return mc68328->regs.csb3 >> 16; + + case 0x130: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSC0(0) = %04x\n", mem_mask, mc68328->regs.csc0 & 0x0000ffff); + return mc68328->regs.csc0 & 0x0000ffff; + + case 0x132: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSC0(16) = %04x\n", mem_mask, mc68328->regs.csc0 >> 16); + return mc68328->regs.csc0 >> 16; + + case 0x134: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSC1(0) = %04x\n", mem_mask, mc68328->regs.csc1 & 0x0000ffff); + return mc68328->regs.csc1 & 0x0000ffff; + + case 0x136: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSC1(16) = %04x\n", mem_mask, mc68328->regs.csc1 >> 16); + return mc68328->regs.csc1 >> 16; + + case 0x138: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSC2(0) = %04x\n", mem_mask, mc68328->regs.csc2 & 0x0000ffff); + return mc68328->regs.csc2 & 0x0000ffff; + + case 0x13a: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSC2(16) = %04x\n", mem_mask, mc68328->regs.csc2 >> 16); + return mc68328->regs.csc2 >> 16; + + case 0x13c: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSC3(0) = %04x\n", mem_mask, mc68328->regs.csc3 & 0x0000ffff); + return mc68328->regs.csc3 & 0x0000ffff; + + case 0x13e: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSC3(16) = %04x\n", mem_mask, mc68328->regs.csc3 >> 16); + return mc68328->regs.csc3 >> 16; + + case 0x140: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSD0(0) = %04x\n", mem_mask, mc68328->regs.csd0 & 0x0000ffff); + return mc68328->regs.csd0 & 0x0000ffff; + + case 0x142: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSD0(16) = %04x\n", mem_mask, mc68328->regs.csd0 >> 16); + return mc68328->regs.csd0 >> 16; + + case 0x144: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSD1(0) = %04x\n", mem_mask, mc68328->regs.csd1 & 0x0000ffff); + return mc68328->regs.csd1 & 0x0000ffff; + + case 0x146: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSD1(16) = %04x\n", mem_mask, mc68328->regs.csd1 >> 16); + return mc68328->regs.csd1 >> 16; + + case 0x148: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSD2(0) = %04x\n", mem_mask, mc68328->regs.csd2 & 0x0000ffff); + return mc68328->regs.csd2 & 0x0000ffff; + + case 0x14a: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSD2(16) = %04x\n", mem_mask, mc68328->regs.csd2 >> 16); + return mc68328->regs.csd2 >> 16; + + case 0x14c: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSD3(0) = %04x\n", mem_mask, mc68328->regs.csd3 & 0x0000ffff); + return mc68328->regs.csd3 & 0x0000ffff; + + case 0x14e: + verboselog(space.machine(), 5, "mc68328_r (%04x): CSD3(16) = %04x\n", mem_mask, mc68328->regs.csd3 >> 16); + return mc68328->regs.csd3 >> 16; + + case 0x200: + verboselog(space.machine(), 2, "mc68328_r (%04x): PLLCR = %04x\n", mem_mask, mc68328->regs.pllcr); + return mc68328->regs.pllcr; + + case 0x202: + verboselog(space.machine(), 2, "mc68328_r (%04x): PLLFSR = %04x\n", mem_mask, mc68328->regs.pllfsr); + mc68328->regs.pllfsr ^= 0x8000; + return mc68328->regs.pllfsr; + + case 0x206: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): Unknown address (0xfff206)\n", mem_mask); + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PCTLR = %02x\n", mem_mask, mc68328->regs.pctlr); + return mc68328->regs.pctlr << 8; + } + break; + + case 0x300: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): Unknown address (0xfff301)\n", mem_mask); + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): IVR = %02x\n", mem_mask, mc68328->regs.ivr); + return mc68328->regs.ivr << 8; + } + break; + + case 0x302: + verboselog(space.machine(), 2, "mc68328_r (%04x): ICR = %04x\n", mem_mask, mc68328->regs.icr); + return mc68328->regs.icr; + + case 0x304: + verboselog(space.machine(), 2, "mc68328_r (%04x): IMR(16) = %04x\n", mem_mask, mc68328->regs.imr >> 16); + return mc68328->regs.imr >> 16; + + case 0x306: + verboselog(space.machine(), 2, "mc68328_r (%04x): IMR(0) = %04x\n", mem_mask, mc68328->regs.imr & 0x0000ffff); + return mc68328->regs.imr & 0x0000ffff; + + case 0x308: + verboselog(space.machine(), 2, "mc68328_r (%04x): IWR(16) = %04x\n", mem_mask, mc68328->regs.iwr >> 16); + return mc68328->regs.iwr >> 16; + + case 0x30a: + verboselog(space.machine(), 2, "mc68328_r (%04x): IWR(0) = %04x\n", mem_mask, mc68328->regs.iwr & 0x0000ffff); + return mc68328->regs.iwr & 0x0000ffff; + + case 0x30c: + verboselog(space.machine(), 2, "mc68328_r (%04x): ISR(16) = %04x\n", mem_mask, mc68328->regs.isr >> 16); + return mc68328->regs.isr >> 16; + + case 0x30e: + verboselog(space.machine(), 2, "mc68328_r (%04x): ISR(0) = %04x\n", mem_mask, mc68328->regs.isr & 0x0000ffff); + return mc68328->regs.isr & 0x0000ffff; + + case 0x310: + verboselog(space.machine(), 2, "mc68328_r (%04x): IPR(16) = %04x\n", mem_mask, mc68328->regs.ipr >> 16); + return mc68328->regs.ipr >> 16; + + case 0x312: + verboselog(space.machine(), 2, "mc68328_r (%04x): IPR(0) = %04x\n", mem_mask, mc68328->regs.ipr & 0x0000ffff); + return mc68328->regs.ipr & 0x0000ffff; + + case 0x400: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PADATA = %02x\n", mem_mask, mc68328->regs.padata); + if(!mc68328->in_port_a.isnull()) + { + return mc68328->in_port_a( 0 ); + } + else + { + return mc68328->regs.padata; + } + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PADIR = %02x\n", mem_mask, mc68328->regs.padir); + return mc68328->regs.padir << 8; + } + break; + + case 0x402: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PASEL = %02x\n", mem_mask, mc68328->regs.pasel); + return mc68328->regs.pasel; + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): Unknown address (0xfff402)\n", mem_mask); + } + break; + + case 0x408: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PBDATA = %02x\n", mem_mask, mc68328->regs.pbdata); + if(!mc68328->in_port_b.isnull()) + { + return mc68328->in_port_b( 0 ); + } + else + { + return mc68328->regs.pbdata; + } + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PBDIR = %02x\n", mem_mask, mc68328->regs.pbdir); + return mc68328->regs.pbdir << 8; + } + break; + + case 0x40a: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PBSEL = %02x\n", mem_mask, mc68328->regs.pbsel); + return mc68328->regs.pbsel; + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): Unknown address (0xfff40a)\n", mem_mask); + } + break; + + case 0x410: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PCDATA = %02x\n", mem_mask, mc68328->regs.pcdata); + if(!mc68328->in_port_c.isnull()) + { + return mc68328->in_port_c( 0 ); + } + else + { + return mc68328->regs.pcdata; + } + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PCDIR = %02x\n", mem_mask, mc68328->regs.pcdir); + return mc68328->regs.pcdir << 8; + } + break; + + case 0x412: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PCSEL = %02x\n", mem_mask, mc68328->regs.pcsel); + return mc68328->regs.pcsel; + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): Unknown address (0xfff412)\n", mem_mask); + } + break; + + case 0x418: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PDDATA = %02x\n", mem_mask, mc68328->regs.pddata); + if(!mc68328->in_port_d.isnull()) + { + return mc68328->in_port_d( 0 ); + } + else + { + return mc68328->regs.pddata; + } + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PDDIR = %02x\n", mem_mask, mc68328->regs.pddir); + return mc68328->regs.pddir << 8; + } + break; + + case 0x41a: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): Unknown address (0xfff41b)\n", mem_mask); + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PDPUEN = %02x\n", mem_mask, mc68328->regs.pdpuen); + return mc68328->regs.pdpuen << 8; + } + break; + + case 0x41c: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PDIRQEN = %02x\n", mem_mask, mc68328->regs.pdirqen); + return mc68328->regs.pdirqen; + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PDPOL = %02x\n", mem_mask, mc68328->regs.pdpol); + return mc68328->regs.pdpol << 8; + } + break; + + case 0x41e: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PDIRQEDGE = %02x\n", mem_mask, mc68328->regs.pdirqedge); + return mc68328->regs.pdirqedge; + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): Unknown address (0xfff41e)\n", mem_mask); + } + break; + + case 0x420: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PEDATA = %02x\n", mem_mask, mc68328->regs.pedata); + if(!mc68328->in_port_e.isnull()) + { + return mc68328->in_port_e( 0 ); + } + else + { + return mc68328->regs.pedata; + } + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PEDIR = %02x\n", mem_mask, mc68328->regs.pedir); + return mc68328->regs.pedir << 8; + } + break; + + case 0x422: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PESEL = %02x\n", mem_mask, mc68328->regs.pesel); + return mc68328->regs.pesel; + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PEPUEN = %02x\n", mem_mask, mc68328->regs.pepuen); + return mc68328->regs.pepuen << 8; + } + break; + + case 0x428: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PFDATA = %02x\n", mem_mask, mc68328->regs.pfdata); + if(!mc68328->in_port_f.isnull()) + { + return mc68328->in_port_f( 0 ); + } + else + { + return mc68328->regs.pfdata; + } + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PFDIR = %02x\n", mem_mask, mc68328->regs.pfdir); + return mc68328->regs.pfdir << 8; + } + break; + + case 0x42a: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PFSEL = %02x\n", mem_mask, mc68328->regs.pfsel); + return mc68328->regs.pfsel; + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PFPUEN = %02x\n", mem_mask, mc68328->regs.pfpuen); + return mc68328->regs.pfpuen << 8; + } + break; + + case 0x430: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PGDATA = %02x\n", mem_mask, mc68328->regs.pgdata); + if(!mc68328->in_port_g.isnull()) + { + return mc68328->in_port_g( 0 ); + } + else + { + return mc68328->regs.pgdata; + } + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PGDIR = %02x\n", mem_mask, mc68328->regs.pgdir); + return mc68328->regs.pgdir << 8; + } + break; + + case 0x432: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PGSEL = %02x\n", mem_mask, mc68328->regs.pgsel); + return mc68328->regs.pgsel; + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PGPUEN = %02x\n", mem_mask, mc68328->regs.pgpuen); + return mc68328->regs.pgpuen << 8; + } + break; + + case 0x438: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PJDATA = %02x\n", mem_mask, mc68328->regs.pjdata); + if(!mc68328->in_port_j.isnull()) + { + return mc68328->in_port_j( 0 ); + } + else + { + return mc68328->regs.pjdata; + } + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PJDIR = %02x\n", mem_mask, mc68328->regs.pjdir); + return mc68328->regs.pjdir << 8; + } + break; + + case 0x43a: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PJSEL = %02x\n", mem_mask, mc68328->regs.pjsel); + return mc68328->regs.pjsel; + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): Unknown address (0xfff43a)\n", mem_mask); + } + break; + + case 0x440: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PKDATA = %02x\n", mem_mask, mc68328->regs.pkdata); + if(!mc68328->in_port_k.isnull()) + { + return mc68328->in_port_k( 0 ); + } + else + { + return mc68328->regs.pkdata; + } + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PKDIR = %02x\n", mem_mask, mc68328->regs.pkdir); + return mc68328->regs.pkdir << 8; + } + break; + + case 0x442: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PKSEL = %02x\n", mem_mask, mc68328->regs.pksel); + return mc68328->regs.pksel; + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PKPUEN = %02x\n", mem_mask, mc68328->regs.pkpuen); + return mc68328->regs.pkpuen << 8; + } + break; + + case 0x448: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PMDATA = %02x\n", mem_mask, mc68328->regs.pmdata); + if(!mc68328->in_port_m.isnull()) + { + return mc68328->in_port_m( 0 ); + } + else + { + return mc68328->regs.pmdata; + } + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PMDIR = %02x\n", mem_mask, mc68328->regs.pmdir); + return mc68328->regs.pmdir << 8; + } + break; + + case 0x44a: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PMSEL = %02x\n", mem_mask, mc68328->regs.pmsel); + return mc68328->regs.pmsel; + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): PMPUEN = %02x\n", mem_mask, mc68328->regs.pmpuen); + return mc68328->regs.pmpuen << 8; + } + break; + + case 0x500: + verboselog(space.machine(), 2, "mc68328_r (%04x): PWMC = %04x\n", mem_mask, mc68328->regs.pwmc); + temp16 = mc68328->regs.pwmc; + if(mc68328->regs.pwmc & PWMC_PWMIRQ) + { + mc68328->regs.pwmc &= ~PWMC_PWMIRQ; + mc68328_set_interrupt_line(device, INT_PWM, 0); + } + return temp16; + + case 0x502: + verboselog(space.machine(), 2, "mc68328_r (%04x): PWMP = %04x\n", mem_mask, mc68328->regs.pwmp); + return mc68328->regs.pwmp; + + case 0x504: + verboselog(space.machine(), 2, "mc68328_r (%04x): PWMW = %04x\n", mem_mask, mc68328->regs.pwmw); + return mc68328->regs.pwmw; + + case 0x506: + verboselog(space.machine(), 2, "mc68328_r (%04x): PWMCNT = %04x\n", mem_mask, mc68328->regs.pwmcnt); + return mc68328->regs.pwmcnt; + + case 0x600: + verboselog(space.machine(), 2, "mc68328_r (%04x): TCTL1 = %04x\n", mem_mask, mc68328->regs.tctl[0]); + return mc68328->regs.tctl[0]; + + case 0x602: + verboselog(space.machine(), 2, "mc68328_r (%04x): TPRER1 = %04x\n", mem_mask, mc68328->regs.tprer[0]); + return mc68328->regs.tprer[0]; + + case 0x604: + verboselog(space.machine(), 2, "mc68328_r (%04x): TCMP1 = %04x\n", mem_mask, mc68328->regs.tcmp[0]); + return mc68328->regs.tcmp[0]; + + case 0x606: + verboselog(space.machine(), 2, "mc68328_r (%04x): TCR1 = %04x\n", mem_mask, mc68328->regs.tcr[0]); + return mc68328->regs.tcr[0]; + + case 0x608: + verboselog(space.machine(), 2, "mc68328_r (%04x): TCN1 = %04x\n", mem_mask, mc68328->regs.tcn[0]); + return mc68328->regs.tcn[0]; + + case 0x60a: + verboselog(space.machine(), 5, "mc68328_r (%04x): TSTAT1 = %04x\n", mem_mask, mc68328->regs.tstat[0]); + mc68328->regs.tclear[0] |= mc68328->regs.tstat[0]; + return mc68328->regs.tstat[0]; + + case 0x60c: + verboselog(space.machine(), 2, "mc68328_r (%04x): TCTL2 = %04x\n", mem_mask, mc68328->regs.tctl[1]); + return mc68328->regs.tctl[1]; + + case 0x60e: + verboselog(space.machine(), 2, "mc68328_r (%04x): TPREP2 = %04x\n", mem_mask, mc68328->regs.tprer[1]); + return mc68328->regs.tprer[1]; + + case 0x610: + verboselog(space.machine(), 2, "mc68328_r (%04x): TCMP2 = %04x\n", mem_mask, mc68328->regs.tcmp[1]); + return mc68328->regs.tcmp[1]; + + case 0x612: + verboselog(space.machine(), 2, "mc68328_r (%04x): TCR2 = %04x\n", mem_mask, mc68328->regs.tcr[1]); + return mc68328->regs.tcr[1]; + + case 0x614: + verboselog(space.machine(), 2, "mc68328_r (%04x): TCN2 = %04x\n", mem_mask, mc68328->regs.tcn[1]); + return mc68328->regs.tcn[1]; + + case 0x616: + verboselog(space.machine(), 2, "mc68328_r (%04x): TSTAT2 = %04x\n", mem_mask, mc68328->regs.tstat[1]); + mc68328->regs.tclear[1] |= mc68328->regs.tstat[1]; + return mc68328->regs.tstat[1]; + + case 0x618: + verboselog(space.machine(), 2, "mc68328_r (%04x): WCTLR = %04x\n", mem_mask, mc68328->regs.wctlr); + return mc68328->regs.wctlr; + + case 0x61a: + verboselog(space.machine(), 2, "mc68328_r (%04x): WCMPR = %04x\n", mem_mask, mc68328->regs.wcmpr); + return mc68328->regs.wcmpr; + + case 0x61c: + verboselog(space.machine(), 2, "mc68328_r (%04x): WCN = %04x\n", mem_mask, mc68328->regs.wcn); + return mc68328->regs.wcn; + + case 0x700: + verboselog(space.machine(), 2, "mc68328_r (%04x): SPISR = %04x\n", mem_mask, mc68328->regs.spisr); + return mc68328->regs.spisr; + + case 0x800: + verboselog(space.machine(), 2, "mc68328_r (%04x): SPIMDATA = %04x\n", mem_mask, mc68328->regs.spimdata); + if(!mc68328->in_spim.isnull()) + { + return mc68328->in_spim( 0, 0xffff ); + } + return mc68328->regs.spimdata; + + case 0x802: + verboselog(space.machine(), 2, "mc68328_r (%04x): SPIMCONT = %04x\n", mem_mask, mc68328->regs.spimcont); + if(mc68328->regs.spimcont & SPIM_XCH) + { + mc68328->regs.spimcont &= ~SPIM_XCH; + mc68328->regs.spimcont |= SPIM_SPIMIRQ; + return ((mc68328->regs.spimcont | SPIM_XCH) &~ SPIM_SPIMIRQ); + } + return mc68328->regs.spimcont; + + case 0x900: + verboselog(space.machine(), 2, "mc68328_r (%04x): USTCNT = %04x\n", mem_mask, mc68328->regs.ustcnt); + return mc68328->regs.ustcnt; + + case 0x902: + verboselog(space.machine(), 2, "mc68328_r (%04x): UBAUD = %04x\n", mem_mask, mc68328->regs.ubaud); + return mc68328->regs.ubaud; + + case 0x904: + verboselog(space.machine(), 5, "mc68328_r (%04x): URX = %04x\n", mem_mask, mc68328->regs.urx); + return mc68328->regs.urx; + + case 0x906: + verboselog(space.machine(), 5, "mc68328_r (%04x): UTX = %04x\n", mem_mask, mc68328->regs.utx); + return mc68328->regs.utx | UTX_FIFO_EMPTY | UTX_FIFO_HALF | UTX_TX_AVAIL; + + case 0x908: + verboselog(space.machine(), 2, "mc68328_r (%04x): UMISC = %04x\n", mem_mask, mc68328->regs.umisc); + return mc68328->regs.umisc; + + case 0xa00: + verboselog(space.machine(), 2, "mc68328_r (%04x): LSSA(16) = %04x\n", mem_mask, mc68328->regs.lssa >> 16); + return mc68328->regs.lssa >> 16; + + case 0xa02: + verboselog(space.machine(), 2, "mc68328_r (%04x): LSSA(0) = %04x\n", mem_mask, mc68328->regs.lssa & 0x0000ffff); + return mc68328->regs.lssa & 0x0000ffff; + + case 0xa04: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): LVPW = %02x\n", mem_mask, mc68328->regs.lvpw); + return mc68328->regs.lvpw; + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): Unknown address (0xfffa04)\n", mem_mask); + } + break; + + case 0xa08: + verboselog(space.machine(), 2, "mc68328_r (%04x): LXMAX = %04x\n", mem_mask, mc68328->regs.lxmax); + return mc68328->regs.lxmax; + + case 0xa0a: + verboselog(space.machine(), 2, "mc68328_r (%04x): LYMAX = %04x\n", mem_mask, mc68328->regs.lymax); + return mc68328->regs.lymax; + + case 0xa18: + verboselog(space.machine(), 2, "mc68328_r (%04x): LCXP = %04x\n", mem_mask, mc68328->regs.lcxp); + return mc68328->regs.lcxp; + + case 0xa1a: + verboselog(space.machine(), 2, "mc68328_r (%04x): LCYP = %04x\n", mem_mask, mc68328->regs.lcyp); + return mc68328->regs.lcyp; + + case 0xa1c: + verboselog(space.machine(), 2, "mc68328_r (%04x): LCWCH = %04x\n", mem_mask, mc68328->regs.lcwch); + return mc68328->regs.lcwch; + + case 0xa1e: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): LBLKC = %02x\n", mem_mask, mc68328->regs.lblkc); + return mc68328->regs.lblkc; + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): Unknown address (0xfffa1e)\n", mem_mask); + } + break; + + case 0xa20: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): LPOLCF = %02x\n", mem_mask, mc68328->regs.lpolcf); + return mc68328->regs.lpolcf; + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): LPICF = %02x\n", mem_mask, mc68328->regs.lpicf); + return mc68328->regs.lpicf << 8; + } + break; + + case 0xa22: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): LACDRC = %02x\n", mem_mask, mc68328->regs.lacdrc); + return mc68328->regs.lacdrc; + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): Unknown address (0xfffa22)\n", mem_mask); + } + break; + + case 0xa24: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): LPXCD = %02x\n", mem_mask, mc68328->regs.lpxcd); + return mc68328->regs.lpxcd; + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): Unknown address (0xfffa24)\n", mem_mask); + } + break; + + case 0xa26: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): LCKCON = %02x\n", mem_mask, mc68328->regs.lckcon); + return mc68328->regs.lckcon; + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): Unknown address (0xfffa26)\n", mem_mask); + } + break; + + case 0xa28: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): LLBAR = %02x\n", mem_mask, mc68328->regs.llbar); + return mc68328->regs.llbar; + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): Unknown address (0xfffa28)\n", mem_mask); + } + break; + + case 0xa2a: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): LOTCR = %02x\n", mem_mask, mc68328->regs.lotcr); + return mc68328->regs.lotcr; + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): Unknown address (0xfffa2a)\n", mem_mask); + } + break; + + case 0xa2c: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): LPOSR = %02x\n", mem_mask, mc68328->regs.lposr); + return mc68328->regs.lposr; + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): Unknown address (0xfffa2c)\n", mem_mask); + } + break; + + case 0xa30: + if( mem_mask & 0x00ff ) + { + verboselog(space.machine(), 2, "mc68328_r (%04x): LFRCM = %02x\n", mem_mask, mc68328->regs.lfrcm); + return mc68328->regs.lfrcm; + } + else + { + verboselog(space.machine(), 2, "mc68328_r (%04x): Unknown address (0xfffa30)\n", mem_mask); + } + break; + + case 0xa32: + verboselog(space.machine(), 2, "mc68328_r (%04x): LGPMR = %04x\n", mem_mask, mc68328->regs.lgpmr); + return mc68328->regs.lgpmr; + + case 0xb00: + verboselog(space.machine(), 2, "mc68328_r (%04x): HMSR(0) = %04x\n", mem_mask, mc68328->regs.hmsr & 0x0000ffff); + return mc68328->regs.hmsr & 0x0000ffff; + + case 0xb02: + verboselog(space.machine(), 2, "mc68328_r (%04x): HMSR(16) = %04x\n", mem_mask, mc68328->regs.hmsr >> 16); + return mc68328->regs.hmsr >> 16; + + case 0xb04: + verboselog(space.machine(), 2, "mc68328_r (%04x): ALARM(0) = %04x\n", mem_mask, mc68328->regs.alarm & 0x0000ffff); + return mc68328->regs.alarm & 0x0000ffff; + + case 0xb06: + verboselog(space.machine(), 2, "mc68328_r (%04x): ALARM(16) = %04x\n", mem_mask, mc68328->regs.alarm >> 16); + return mc68328->regs.alarm >> 16; + + case 0xb0c: + verboselog(space.machine(), 2, "mc68328_r (%04x): RTCCTL = %04x\n", mem_mask, mc68328->regs.rtcctl); + return mc68328->regs.rtcctl; + + case 0xb0e: + verboselog(space.machine(), 2, "mc68328_r (%04x): RTCISR = %04x\n", mem_mask, mc68328->regs.rtcisr); + return mc68328->regs.rtcisr; + + case 0xb10: + verboselog(space.machine(), 2, "mc68328_r (%04x): RTCIENR = %04x\n", mem_mask, mc68328->regs.rtcienr); + return mc68328->regs.rtcienr; + + case 0xb12: + verboselog(space.machine(), 2, "mc68328_r (%04x): STPWTCH = %04x\n", mem_mask, mc68328->regs.stpwtch); + return mc68328->regs.stpwtch; + + default: + verboselog(space.machine(), 0, "mc68328_r (%04x): Unknown address (0x%06x)\n", mem_mask, 0xfff000 + address); + break; + } + return 0; +} + +static DEVICE_RESET( mc68328 ) +{ + mc68328_t* mc68328 = mc68328_get_safe_token( device ); + + mc68328->regs.scr = 0x0c; + mc68328->regs.grpbasea = 0x0000; + mc68328->regs.grpbaseb = 0x0000; + mc68328->regs.grpbasec = 0x0000; + mc68328->regs.grpbased = 0x0000; + mc68328->regs.grpmaska = 0x0000; + mc68328->regs.grpmaskb = 0x0000; + mc68328->regs.grpmaskc = 0x0000; + mc68328->regs.grpmaskd = 0x0000; + mc68328->regs.csa0 = 0x00010006; + mc68328->regs.csa1 = 0x00010006; + mc68328->regs.csa2 = 0x00010006; + mc68328->regs.csa3 = 0x00010006; + mc68328->regs.csb0 = 0x00010006; + mc68328->regs.csb1 = 0x00010006; + mc68328->regs.csb2 = 0x00010006; + mc68328->regs.csb3 = 0x00010006; + mc68328->regs.csc0 = 0x00010006; + mc68328->regs.csc1 = 0x00010006; + mc68328->regs.csc2 = 0x00010006; + mc68328->regs.csc3 = 0x00010006; + mc68328->regs.csd0 = 0x00010006; + mc68328->regs.csd1 = 0x00010006; + mc68328->regs.csd2 = 0x00010006; + mc68328->regs.csd3 = 0x00010006; + + mc68328->regs.pllcr = 0x2400; + mc68328->regs.pllfsr = 0x0123; + mc68328->regs.pctlr = 0x1f; + + mc68328->regs.ivr = 0x00; + mc68328->regs.icr = 0x0000; + mc68328->regs.imr = 0x00ffffff; + mc68328->regs.iwr = 0x00ffffff; + mc68328->regs.isr = 0x00000000; + mc68328->regs.ipr = 0x00000000; + + mc68328->regs.padir = 0x00; + mc68328->regs.padata = 0x00; + mc68328->regs.pasel = 0x00; + mc68328->regs.pbdir = 0x00; + mc68328->regs.pbdata = 0x00; + mc68328->regs.pbsel = 0x00; + mc68328->regs.pcdir = 0x00; + mc68328->regs.pcdata = 0x00; + mc68328->regs.pcsel = 0x00; + mc68328->regs.pddir = 0x00; + mc68328->regs.pddata = 0x00; + mc68328->regs.pdpuen = 0xff; + mc68328->regs.pdpol = 0x00; + mc68328->regs.pdirqen = 0x00; + mc68328->regs.pddataedge = 0x00; + mc68328->regs.pdirqedge = 0x00; + mc68328->regs.pedir = 0x00; + mc68328->regs.pedata = 0x00; + mc68328->regs.pepuen = 0x80; + mc68328->regs.pesel = 0x80; + mc68328->regs.pfdir = 0x00; + mc68328->regs.pfdata = 0x00; + mc68328->regs.pfpuen = 0xff; + mc68328->regs.pfsel = 0xff; + mc68328->regs.pgdir = 0x00; + mc68328->regs.pgdata = 0x00; + mc68328->regs.pgpuen = 0xff; + mc68328->regs.pgsel = 0xff; + mc68328->regs.pjdir = 0x00; + mc68328->regs.pjdata = 0x00; + mc68328->regs.pjsel = 0x00; + mc68328->regs.pkdir = 0x00; + mc68328->regs.pkdata = 0x00; + mc68328->regs.pkpuen = 0xff; + mc68328->regs.pksel = 0xff; + mc68328->regs.pmdir = 0x00; + mc68328->regs.pmdata = 0x00; + mc68328->regs.pmpuen = 0xff; + mc68328->regs.pmsel = 0xff; + + mc68328->regs.pwmc = 0x0000; + mc68328->regs.pwmp = 0x0000; + mc68328->regs.pwmw = 0x0000; + mc68328->regs.pwmcnt = 0x0000; + + mc68328->regs.tctl[0] = mc68328->regs.tctl[1] = 0x0000; + mc68328->regs.tprer[0] = mc68328->regs.tprer[1] = 0x0000; + mc68328->regs.tcmp[0] = mc68328->regs.tcmp[1] = 0xffff; + mc68328->regs.tcr[0] = mc68328->regs.tcr[1] = 0x0000; + mc68328->regs.tcn[0] = mc68328->regs.tcn[1] = 0x0000; + mc68328->regs.tstat[0] = mc68328->regs.tstat[1] = 0x0000; + mc68328->regs.wctlr = 0x0000; + mc68328->regs.wcmpr = 0xffff; + mc68328->regs.wcn = 0x0000; + + mc68328->regs.spisr = 0x0000; + + mc68328->regs.spimdata = 0x0000; + mc68328->regs.spimcont = 0x0000; + + mc68328->regs.ustcnt = 0x0000; + mc68328->regs.ubaud = 0x003f; + mc68328->regs.urx = 0x0000; + mc68328->regs.utx = 0x0000; + mc68328->regs.umisc = 0x0000; + + mc68328->regs.lssa = 0x00000000; + mc68328->regs.lvpw = 0xff; + mc68328->regs.lxmax = 0x03ff; + mc68328->regs.lymax = 0x01ff; + mc68328->regs.lcxp = 0x0000; + mc68328->regs.lcyp = 0x0000; + mc68328->regs.lcwch = 0x0101; + mc68328->regs.lblkc = 0x7f; + mc68328->regs.lpicf = 0x00; + mc68328->regs.lpolcf = 0x00; + mc68328->regs.lacdrc = 0x00; + mc68328->regs.lpxcd = 0x00; + mc68328->regs.lckcon = 0x40; + mc68328->regs.llbar = 0x3e; + mc68328->regs.lotcr = 0x3f; + mc68328->regs.lposr = 0x00; + mc68328->regs.lfrcm = 0xb9; + mc68328->regs.lgpmr = 0x1073; + + mc68328->regs.hmsr = 0x00000000; + mc68328->regs.alarm = 0x00000000; + mc68328->regs.rtcctl = 0x00; + mc68328->regs.rtcisr = 0x00; + mc68328->regs.rtcienr = 0x00; + mc68328->regs.stpwtch = 0x00; + + mc68328->rtc->adjust(attotime::from_hz(1), 0, attotime::from_hz(1)); +} + +static void mc68328_register_state_save(device_t *device) +{ + mc68328_t* mc68328 = mc68328_get_safe_token( device ); + + state_save_register_global(device->machine(), mc68328->regs.scr); + state_save_register_global(device->machine(), mc68328->regs.grpbasea); + state_save_register_global(device->machine(), mc68328->regs.grpbaseb); + state_save_register_global(device->machine(), mc68328->regs.grpbasec); + state_save_register_global(device->machine(), mc68328->regs.grpbased); + state_save_register_global(device->machine(), mc68328->regs.grpmaska); + state_save_register_global(device->machine(), mc68328->regs.grpmaskb); + state_save_register_global(device->machine(), mc68328->regs.grpmaskc); + state_save_register_global(device->machine(), mc68328->regs.grpmaskd); + state_save_register_global(device->machine(), mc68328->regs.csa0); + state_save_register_global(device->machine(), mc68328->regs.csa1); + state_save_register_global(device->machine(), mc68328->regs.csa2); + state_save_register_global(device->machine(), mc68328->regs.csa3); + state_save_register_global(device->machine(), mc68328->regs.csb0); + state_save_register_global(device->machine(), mc68328->regs.csb1); + state_save_register_global(device->machine(), mc68328->regs.csb2); + state_save_register_global(device->machine(), mc68328->regs.csb3); + state_save_register_global(device->machine(), mc68328->regs.csc0); + state_save_register_global(device->machine(), mc68328->regs.csc1); + state_save_register_global(device->machine(), mc68328->regs.csc2); + state_save_register_global(device->machine(), mc68328->regs.csc3); + state_save_register_global(device->machine(), mc68328->regs.csd0); + state_save_register_global(device->machine(), mc68328->regs.csd1); + state_save_register_global(device->machine(), mc68328->regs.csd2); + state_save_register_global(device->machine(), mc68328->regs.csd3); + + state_save_register_global(device->machine(), mc68328->regs.pllcr); + state_save_register_global(device->machine(), mc68328->regs.pllfsr); + state_save_register_global(device->machine(), mc68328->regs.pctlr); + + state_save_register_global(device->machine(), mc68328->regs.ivr); + state_save_register_global(device->machine(), mc68328->regs.icr); + state_save_register_global(device->machine(), mc68328->regs.imr); + state_save_register_global(device->machine(), mc68328->regs.iwr); + state_save_register_global(device->machine(), mc68328->regs.isr); + state_save_register_global(device->machine(), mc68328->regs.ipr); + + state_save_register_global(device->machine(), mc68328->regs.padir); + state_save_register_global(device->machine(), mc68328->regs.padata); + state_save_register_global(device->machine(), mc68328->regs.pasel); + state_save_register_global(device->machine(), mc68328->regs.pbdir); + state_save_register_global(device->machine(), mc68328->regs.pbdata); + state_save_register_global(device->machine(), mc68328->regs.pbsel); + state_save_register_global(device->machine(), mc68328->regs.pcdir); + state_save_register_global(device->machine(), mc68328->regs.pcdata); + state_save_register_global(device->machine(), mc68328->regs.pcsel); + state_save_register_global(device->machine(), mc68328->regs.pddir); + state_save_register_global(device->machine(), mc68328->regs.pddata); + state_save_register_global(device->machine(), mc68328->regs.pdpuen); + state_save_register_global(device->machine(), mc68328->regs.pdpol); + state_save_register_global(device->machine(), mc68328->regs.pdirqen); + state_save_register_global(device->machine(), mc68328->regs.pddataedge); + state_save_register_global(device->machine(), mc68328->regs.pdirqedge); + state_save_register_global(device->machine(), mc68328->regs.pedir); + state_save_register_global(device->machine(), mc68328->regs.pedata); + state_save_register_global(device->machine(), mc68328->regs.pepuen); + state_save_register_global(device->machine(), mc68328->regs.pesel); + state_save_register_global(device->machine(), mc68328->regs.pfdir); + state_save_register_global(device->machine(), mc68328->regs.pfdata); + state_save_register_global(device->machine(), mc68328->regs.pfpuen); + state_save_register_global(device->machine(), mc68328->regs.pfsel); + state_save_register_global(device->machine(), mc68328->regs.pgdir); + state_save_register_global(device->machine(), mc68328->regs.pgdata); + state_save_register_global(device->machine(), mc68328->regs.pgpuen); + state_save_register_global(device->machine(), mc68328->regs.pgsel); + state_save_register_global(device->machine(), mc68328->regs.pjdir); + state_save_register_global(device->machine(), mc68328->regs.pjdata); + state_save_register_global(device->machine(), mc68328->regs.pjsel); + state_save_register_global(device->machine(), mc68328->regs.pkdir); + state_save_register_global(device->machine(), mc68328->regs.pkdata); + state_save_register_global(device->machine(), mc68328->regs.pkpuen); + state_save_register_global(device->machine(), mc68328->regs.pksel); + state_save_register_global(device->machine(), mc68328->regs.pmdir); + state_save_register_global(device->machine(), mc68328->regs.pmdata); + state_save_register_global(device->machine(), mc68328->regs.pmpuen); + state_save_register_global(device->machine(), mc68328->regs.pmsel); + + state_save_register_global(device->machine(), mc68328->regs.pwmc); + state_save_register_global(device->machine(), mc68328->regs.pwmp); + state_save_register_global(device->machine(), mc68328->regs.pwmw); + state_save_register_global(device->machine(), mc68328->regs.pwmcnt); + + state_save_register_global(device->machine(), mc68328->regs.tctl[0]); + state_save_register_global(device->machine(), mc68328->regs.tctl[1]); + state_save_register_global(device->machine(), mc68328->regs.tprer[0]); + state_save_register_global(device->machine(), mc68328->regs.tprer[1]); + state_save_register_global(device->machine(), mc68328->regs.tcmp[0]); + state_save_register_global(device->machine(), mc68328->regs.tcmp[1]); + state_save_register_global(device->machine(), mc68328->regs.tcr[0]); + state_save_register_global(device->machine(), mc68328->regs.tcr[1]); + state_save_register_global(device->machine(), mc68328->regs.tcn[0]); + state_save_register_global(device->machine(), mc68328->regs.tcn[1]); + state_save_register_global(device->machine(), mc68328->regs.tstat[0]); + state_save_register_global(device->machine(), mc68328->regs.tstat[1]); + state_save_register_global(device->machine(), mc68328->regs.wctlr); + state_save_register_global(device->machine(), mc68328->regs.wcmpr); + state_save_register_global(device->machine(), mc68328->regs.wcn); + + state_save_register_global(device->machine(), mc68328->regs.spisr); + + state_save_register_global(device->machine(), mc68328->regs.spimdata); + state_save_register_global(device->machine(), mc68328->regs.spimcont); + + state_save_register_global(device->machine(), mc68328->regs.ustcnt); + state_save_register_global(device->machine(), mc68328->regs.ubaud); + state_save_register_global(device->machine(), mc68328->regs.urx); + state_save_register_global(device->machine(), mc68328->regs.utx); + state_save_register_global(device->machine(), mc68328->regs.umisc); + + state_save_register_global(device->machine(), mc68328->regs.lssa); + state_save_register_global(device->machine(), mc68328->regs.lvpw); + state_save_register_global(device->machine(), mc68328->regs.lxmax); + state_save_register_global(device->machine(), mc68328->regs.lymax); + state_save_register_global(device->machine(), mc68328->regs.lcxp); + state_save_register_global(device->machine(), mc68328->regs.lcyp); + state_save_register_global(device->machine(), mc68328->regs.lcwch); + state_save_register_global(device->machine(), mc68328->regs.lblkc); + state_save_register_global(device->machine(), mc68328->regs.lpicf); + state_save_register_global(device->machine(), mc68328->regs.lpolcf); + state_save_register_global(device->machine(), mc68328->regs.lacdrc); + state_save_register_global(device->machine(), mc68328->regs.lpxcd); + state_save_register_global(device->machine(), mc68328->regs.lckcon); + state_save_register_global(device->machine(), mc68328->regs.llbar); + state_save_register_global(device->machine(), mc68328->regs.lotcr); + state_save_register_global(device->machine(), mc68328->regs.lposr); + state_save_register_global(device->machine(), mc68328->regs.lfrcm); + state_save_register_global(device->machine(), mc68328->regs.lgpmr); + + state_save_register_global(device->machine(), mc68328->regs.hmsr); + state_save_register_global(device->machine(), mc68328->regs.alarm); + state_save_register_global(device->machine(), mc68328->regs.rtcctl); + state_save_register_global(device->machine(), mc68328->regs.rtcisr); + state_save_register_global(device->machine(), mc68328->regs.rtcienr); + state_save_register_global(device->machine(), mc68328->regs.stpwtch); +} + +static DEVICE_START( mc68328 ) +{ + mc68328_t* mc68328 = mc68328_get_safe_token( device ); + + mc68328->iface = (const mc68328_interface*)device->static_config(); + + mc68328->out_port_a.resolve(mc68328->iface->out_port_a_func, *device); + mc68328->out_port_b.resolve(mc68328->iface->out_port_b_func, *device); + mc68328->out_port_c.resolve(mc68328->iface->out_port_c_func, *device); + mc68328->out_port_d.resolve(mc68328->iface->out_port_d_func, *device); + mc68328->out_port_e.resolve(mc68328->iface->out_port_e_func, *device); + mc68328->out_port_f.resolve(mc68328->iface->out_port_f_func, *device); + mc68328->out_port_g.resolve(mc68328->iface->out_port_g_func, *device); + mc68328->out_port_j.resolve(mc68328->iface->out_port_j_func, *device); + mc68328->out_port_k.resolve(mc68328->iface->out_port_k_func, *device); + mc68328->out_port_m.resolve(mc68328->iface->out_port_m_func, *device); + + mc68328->in_port_a.resolve(mc68328->iface->in_port_a_func, *device); + mc68328->in_port_b.resolve(mc68328->iface->in_port_b_func, *device); + mc68328->in_port_c.resolve(mc68328->iface->in_port_c_func, *device); + mc68328->in_port_d.resolve(mc68328->iface->in_port_d_func, *device); + mc68328->in_port_e.resolve(mc68328->iface->in_port_e_func, *device); + mc68328->in_port_f.resolve(mc68328->iface->in_port_f_func, *device); + mc68328->in_port_g.resolve(mc68328->iface->in_port_g_func, *device); + mc68328->in_port_j.resolve(mc68328->iface->in_port_j_func, *device); + mc68328->in_port_k.resolve(mc68328->iface->in_port_k_func, *device); + mc68328->in_port_m.resolve(mc68328->iface->in_port_m_func, *device); + + mc68328->out_pwm.resolve(mc68328->iface->out_pwm_func, *device); + + mc68328->out_spim.resolve(mc68328->iface->out_spim_func, *device); + mc68328->in_spim.resolve(mc68328->iface->in_spim_func, *device); + + mc68328->gptimer[0] = device->machine().scheduler().timer_alloc(FUNC(mc68328_timer1_hit)); + mc68328->gptimer[1] = device->machine().scheduler().timer_alloc(FUNC(mc68328_timer2_hit)); + mc68328->rtc = device->machine().scheduler().timer_alloc(FUNC(mc68328_rtc_tick)); + mc68328->pwm = device->machine().scheduler().timer_alloc(FUNC(mc68328_pwm_transition)); + + mc68328_register_state_save(device); +} + +const device_type MC68328 = &device_creator<mc68328_device>; + +mc68328_device::mc68328_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, MC68328, "Motorola MC68328 (DragonBall) Integrated Processor", tag, owner, clock) +{ + m_token = global_alloc_clear(mc68328_t); +} + +//------------------------------------------------- +// device_config_complete - perform any +// operations now that the configuration is +// complete +//------------------------------------------------- + +void mc68328_device::device_config_complete() +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void mc68328_device::device_start() +{ + DEVICE_START_NAME( mc68328 )(this); +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void mc68328_device::device_reset() +{ + DEVICE_RESET_NAME( mc68328 )(this); +} diff --git a/src/emu/machine/mc68328.h b/src/emu/machine/mc68328.h new file mode 100644 index 00000000000..1d3b9301c24 --- /dev/null +++ b/src/emu/machine/mc68328.h @@ -0,0 +1,568 @@ +/********************************************************************** + + Motorola 68328 ("DragonBall") System-on-a-Chip private data + + By MooglyGuy + contact mooglyguy@gmail.com with licensing and usage questions. + +**********************************************************************/ + +#ifndef __MC68328_PRIVATE_H_ +#define __MC68328_PRIVATE_H_ + +struct mc68328_regs_t +{ + // $(FF)FFF000 + UINT8 scr; // System Control Register + UINT8 unused0[255]; + + // $(FF)FFF100 + UINT16 grpbasea; // Chip Select Group A Base Register + UINT16 grpbaseb; // Chip Select Group B Base Register + UINT16 grpbasec; // Chip Select Group C Base Register + UINT16 grpbased; // Chip Select Group D Base Register + UINT16 grpmaska; // Chip Select Group A Mask Register + UINT16 grpmaskb; // Chip Select Group B Mask Register + UINT16 grpmaskc; // Chip Select Group C Mask Register + UINT16 grpmaskd; // Chip Select Group D Mask Register + UINT32 csa0; // Group A Chip Select 0 Register + UINT32 csa1; // Group A Chip Select 1 Register + UINT32 csa2; // Group A Chip Select 2 Register + UINT32 csa3; // Group A Chip Select 3 Register + UINT32 csb0; // Group B Chip Select 0 Register + UINT32 csb1; // Group B Chip Select 1 Register + UINT32 csb2; // Group B Chip Select 2 Register + UINT32 csb3; // Group B Chip Select 3 Register + UINT32 csc0; // Group C Chip Select 0 Register + UINT32 csc1; // Group C Chip Select 1 Register + UINT32 csc2; // Group C Chip Select 2 Register + UINT32 csc3; // Group C Chip Select 3 Register + UINT32 csd0; // Group D Chip Select 0 Register + UINT32 csd1; // Group D Chip Select 1 Register + UINT32 csd2; // Group D Chip Select 2 Register + UINT32 csd3; // Group D Chip Select 3 Register + UINT8 unused1[176]; + + // $(FF)FFF200 + UINT16 pllcr; // PLL Control Register + UINT16 pllfsr; // PLL Frequency Select Register + UINT8 pad2[3]; + UINT8 pctlr; // Power Control Register + UINT8 unused3[248]; + + // $(FF)FFF300 + UINT8 ivr; // Interrupt Vector Register + UINT8 unused4[1]; + UINT16 icr; // Interrupt Control Register + UINT32 imr; // Interrupt Mask Register + UINT32 iwr; // Interrupt Wakeup Enable Register + UINT32 isr; // Interrupt Status Register + UINT32 ipr; // Interrupt Pending Register + UINT8 unused5[236]; + + // $(FF)FFF400 + UINT8 padir; // Port A Direction Register + UINT8 padata; // Port A Data Register + UINT8 unused6[1]; + UINT8 pasel; // Port A Select Register + UINT8 unused7[4]; + + UINT8 pbdir; // Port B Direction Register + UINT8 pbdata; // Port B Data Register + UINT8 unused8[1]; + UINT8 pbsel; // Port B Select Register + UINT8 unused9[4]; + + UINT8 pcdir; // Port C Direction Register + UINT8 pcdata; // Port C Data Register + UINT8 unused10[1]; + UINT8 pcsel; // Port C Select Register + UINT8 unused11[4]; + + UINT8 pddir; // Port D Direction Register + UINT8 pddata; // Port D Data Register + UINT8 pdpuen; // Port D Pullup Enable Register + UINT8 unused12[1]; + UINT8 pdpol; // Port D Polarity Register + UINT8 pdirqen; // Port D IRQ Enable Register + UINT8 pddataedge; // Port D Data Edge Level + UINT8 pdirqedge; // Port D IRQ Edge Register + + UINT8 pedir; // Port E Direction Register + UINT8 pedata; // Port E Data Register + UINT8 pepuen; // Port E Pullup Enable Register + UINT8 pesel; // Port E Select Register + UINT8 unused14[4]; + + UINT8 pfdir; // Port F Direction Register + UINT8 pfdata; // Port F Data Register + UINT8 pfpuen; // Port F Pullup Enable Register + UINT8 pfsel; // Port F Select Register + UINT8 unused15[4]; + + UINT8 pgdir; // Port G Direction Register + UINT8 pgdata; // Port G Data Register + UINT8 pgpuen; // Port G Pullup Enable Register + UINT8 pgsel; // Port G Select Register + UINT8 unused16[4]; + + UINT8 pjdir; // Port J Direction Register + UINT8 pjdata; // Port J Data Register + UINT8 unused17[1]; + UINT8 pjsel; // Port J Select Register + UINT8 unused18[4]; + UINT8 pkdir; // Port K Direction Register + UINT8 pkdata; // Port K Data Register + UINT8 pkpuen; // Port K Pullup Enable Register + UINT8 pksel; // Port K Select Register + UINT8 unused19[4]; + + UINT8 pmdir; // Port M Direction Register + UINT8 pmdata; // Port M Data Register + UINT8 pmpuen; // Port M Pullup Enable Register + UINT8 pmsel; // Port M Select Register + UINT8 unused20[180]; + + // $(FF)FFF500 + UINT16 pwmc; // PWM Control Register + UINT16 pwmp; // PWM Period Register + UINT16 pwmw; // PWM Width Register + UINT16 pwmcnt; // PWN Counter + UINT8 unused21[248]; + + // $(FF)FFF600 + UINT16 tctl[2]; // Timer Control Register + UINT16 tprer[2]; // Timer Prescaler Register + UINT16 tcmp[2]; // Timer Compare Register + UINT16 tcr[2]; // Timer Capture Register + UINT16 tcn[2]; // Timer Counter + UINT16 tstat[2]; // Timer Status + UINT16 wctlr; // Watchdog Control Register + UINT16 wcmpr; // Watchdog Compare Register + UINT16 wcn; // Watchdog Counter + UINT8 tclear[2]; // Timer Clearable Status + UINT8 unused22[224]; + + // $(FF)FFF700 + UINT16 spisr; // SPIS Register + UINT8 unused23[254]; + + // $(FF)FFF800 + UINT16 spimdata; // SPIM Data Register + UINT16 spimcont; // SPIM Control/Status Register + UINT8 unused24[252]; + + // $(FF)FFF900 + UINT16 ustcnt; // UART Status/Control Register + UINT16 ubaud; // UART Baud Control Register + UINT16 urx; // UART RX Register + UINT16 utx; // UART TX Register + UINT16 umisc; // UART Misc Register + UINT8 unused25[246]; + + // $(FF)FFFA00 + UINT32 lssa; // Screen Starting Address Register + UINT8 unused26[1]; + UINT8 lvpw; // Virtual Page Width Register + UINT8 unused27[2]; + UINT16 lxmax; // Screen Width Register + UINT16 lymax; // Screen Height Register + UINT8 unused28[12]; + UINT16 lcxp; // Cursor X Position + UINT16 lcyp; // Cursor Y Position + UINT16 lcwch; // Cursor Width & Height Register + UINT8 unused29[1]; + UINT8 lblkc; // Blink Control Register + UINT8 lpicf; // Panel Interface Config Register + UINT8 lpolcf; // Polarity Config Register + UINT8 unused30[1]; + UINT8 lacdrc; // ACD (M) Rate Control Register + UINT8 unused31[1]; + UINT8 lpxcd; // Pixel Clock Divider Register + UINT8 unused32[1]; + UINT8 lckcon; // Clocking Control Register + UINT8 unused33[1]; + UINT8 llbar; // Last Buffer Address Register + UINT8 unused34[1]; + UINT8 lotcr; // Octet Terminal Count Register + UINT8 unused35[1]; + UINT8 lposr; // Panning Offset Register + UINT8 unused36[3]; + UINT8 lfrcm; // Frame Rate Control Modulation Register + UINT16 lgpmr; // Gray Palette Mapping Register + UINT8 unused37[204]; + + // $(FF)FFFB00 + UINT32 hmsr; // RTC Hours Minutes Seconds Register + UINT32 alarm; // RTC Alarm Register + UINT8 unused38[4]; + UINT16 rtcctl; // RTC Control Register + UINT16 rtcisr; // RTC Interrupt Status Register + UINT16 rtcienr; // RTC Interrupt Enable Register + UINT16 stpwtch; // Stopwatch Minutes + UINT8 unused42[1260]; +}; + +struct mc68328_t +{ + const mc68328_interface* iface; + + mc68328_regs_t regs; + + emu_timer *gptimer[2]; + emu_timer *rtc; + emu_timer *pwm; + + devcb_resolved_write8 out_port_a; /* 8-bit output */ + devcb_resolved_write8 out_port_b; /* 8-bit output */ + devcb_resolved_write8 out_port_c; /* 8-bit output */ + devcb_resolved_write8 out_port_d; /* 8-bit output */ + devcb_resolved_write8 out_port_e; /* 8-bit output */ + devcb_resolved_write8 out_port_f; /* 8-bit output */ + devcb_resolved_write8 out_port_g; /* 8-bit output */ + devcb_resolved_write8 out_port_j; /* 8-bit output */ + devcb_resolved_write8 out_port_k; /* 8-bit output */ + devcb_resolved_write8 out_port_m; /* 8-bit output */ + + devcb_resolved_read8 in_port_a; /* 8-bit input */ + devcb_resolved_read8 in_port_b; /* 8-bit input */ + devcb_resolved_read8 in_port_c; /* 8-bit input */ + devcb_resolved_read8 in_port_d; /* 8-bit input */ + devcb_resolved_read8 in_port_e; /* 8-bit input */ + devcb_resolved_read8 in_port_f; /* 8-bit input */ + devcb_resolved_read8 in_port_g; /* 8-bit input */ + devcb_resolved_read8 in_port_j; /* 8-bit input */ + devcb_resolved_read8 in_port_k; /* 8-bit input */ + devcb_resolved_read8 in_port_m; /* 8-bit input */ + + devcb_resolved_write8 out_pwm; /* 1-bit output */ + + devcb_resolved_write16 out_spim; /* 16-bit output */ + devcb_resolved_read16 in_spim; /* 16-bit input */ +}; + +#define SCR_BETO 0x80 +#define SCR_WPV 0x40 +#define SCR_PRV 0x20 +#define SCR_BETEN 0x10 +#define SCR_SO 0x08 +#define SCR_DMAP 0x04 +#define SCR_WDTH8 0x01 + +#define ICR_POL6 0x0100 +#define ICR_POL3 0x0200 +#define ICR_POL2 0x0400 +#define ICR_POL1 0x0800 +#define ICR_ET6 0x1000 +#define ICR_ET3 0x2000 +#define ICR_ET2 0x4000 +#define ICR_ET1 0x8000 + +#define INT_SPIM 0x000001 +#define INT_TIMER2 0x000002 +#define INT_UART 0x000004 +#define INT_WDT 0x000008 +#define INT_RTC 0x000010 +#define INT_RESERVED 0x000020 +#define INT_KB 0x000040 +#define INT_PWM 0x000080 +#define INT_INT0 0x000100 +#define INT_INT1 0x000200 +#define INT_INT2 0x000400 +#define INT_INT3 0x000800 +#define INT_INT4 0x001000 +#define INT_INT5 0x002000 +#define INT_INT6 0x004000 +#define INT_INT7 0x008000 +#define INT_KBDINTS 0x00ff00 +#define INT_IRQ1 0x010000 +#define INT_IRQ2 0x020000 +#define INT_IRQ3 0x040000 +#define INT_IRQ6 0x080000 +#define INT_PEN 0x100000 +#define INT_SPIS 0x200000 +#define INT_TIMER1 0x400000 +#define INT_IRQ7 0x800000 + +#define INT_M68K_LINE1 (INT_IRQ1) +#define INT_M68K_LINE2 (INT_IRQ2) +#define INT_M68K_LINE3 (INT_IRQ3) +#define INT_M68K_LINE4 (INT_INT0 | INT_INT1 | INT_INT2 | INT_INT3 | INT_INT4 | INT_INT5 | INT_INT6 | INT_INT7 | \ + INT_PWM | INT_KB | INT_RTC | INT_WDT | INT_UART | INT_TIMER2 | INT_SPIM) +#define INT_M68K_LINE5 (INT_PEN) +#define INT_M68K_LINE6 (INT_IRQ6 | INT_TIMER1 | INT_SPIS) +#define INT_M68K_LINE7 (INT_IRQ7) +#define INT_M68K_LINE67 (INT_M68K_LINE6 | INT_M68K_LINE7) +#define INT_M68K_LINE567 (INT_M68K_LINE5 | INT_M68K_LINE6 | INT_M68K_LINE7) +#define INT_M68K_LINE4567 (INT_M68K_LINE4 | INT_M68K_LINE5 | INT_M68K_LINE6 | INT_M68K_LINE7) +#define INT_M68K_LINE34567 (INT_M68K_LINE3 | INT_M68K_LINE4 | INT_M68K_LINE5 | INT_M68K_LINE6 | INT_M68K_LINE7) +#define INT_M68K_LINE234567 (INT_M68K_LINE2 | INT_M68K_LINE3 | INT_M68K_LINE4 | INT_M68K_LINE5 | INT_M68K_LINE6 | INT_M68K_LINE7) + +#define INT_IRQ1_SHIFT 0x000001 +#define INT_IRQ2_SHIFT 0x000002 +#define INT_IRQ3_SHIFT 0x000004 +#define INT_IRQ6_SHIFT 0x000008 +#define INT_PEN_SHIFT 0x000010 +#define INT_SPIS_SHIFT 0x000020 +#define INT_TIMER1_SHIFT 0x000040 +#define INT_IRQ7_SHIFT 0x000080 + +#define INT_ACTIVE 1 +#define INT_INACTIVE 0 + +#define GRPBASE_BASE_ADDR 0xfff0 +#define GRPBASE_VALID 0x0001 + +#define GRPMASK_BASE_MASK 0xfff0 + +#define CSAB_COMPARE 0xff000000 +#define CSAB_BSW 0x00010000 +#define CSAB_MASK 0x0000ff00 +#define CSAB_RO 0x00000008 +#define CSAB_WAIT 0x00000007 + +#define CSCD_COMPARE 0xfff00000 +#define CSCD_BSW 0x00010000 +#define CSCD_MASK 0x0000fff0 +#define CSCD_RO 0x00000008 +#define CSCD_WAIT 0x00000007 + +#define PLLCR_PIXCLK_SEL 0x3800 +#define PLLCR_PIXCLK_SEL_DIV2 0x0000 +#define PLLCR_PIXCLK_SEL_DIV4 0x0800 +#define PLLCR_PIXCLK_SEL_DIV8 0x1000 +#define PLLCR_PIXCLK_SEL_DIV16 0x1800 +#define PLLCR_PIXCLK_SEL_DIV1_0 0x2000 +#define PLLCR_PIXCLK_SEL_DIV1_1 0x2800 +#define PLLCR_PIXCLK_SEL_DIV1_2 0x3000 +#define PLLCR_PIXCLK_SEL_DIV1_3 0x3800 +#define PLLCR_SYSCLK_SEL 0x0700 +#define PLLCR_SYSCLK_SEL_DIV2 0x0000 +#define PLLCR_SYSCLK_SEL_DIV4 0x0100 +#define PLLCR_SYSCLK_SEL_DIV8 0x0200 +#define PLLCR_SYSCLK_SEL_DIV16 0x0300 +#define PLLCR_SYSCLK_SEL_DIV1_0 0x0400 +#define PLLCR_SYSCLK_SEL_DIV1_1 0x0500 +#define PLLCR_SYSCLK_SEL_DIV1_2 0x0600 +#define PLLCR_SYSCLK_SEL_DIV1_3 0x0700 +#define PLLCR_CLKEN 0x0010 +#define PLLCR_DISPLL 0x0008 + +#define PLLFSR_CLK32 0x8000 +#define PLLFSR_PROT 0x4000 +#define PLLFSR_QCNT 0x0f00 +#define PLLFSR_PCNT 0x00ff + +#define PCTLR_PC_EN 0x80 +#define PCTLR_STOP 0x40 +#define PCTLR_WIDTH 0x1f + +#define CXP_CC 0xc000 +#define CXP_CC_XLU 0x0000 +#define CXP_CC_BLACK 0x4000 +#define CXP_CC_INVERSE 0x8000 +#define CXP_CC_INVALID 0xc000 +#define CXP_MASK 0x03ff + +#define CYP_MASK 0x01ff + +#define CWCH_CW 0x1f00 +#define CWCH_CH 0x001f + +#define BLKC_BKEN 0x80 +#define BLKC_BD 0x7f + +#define LPICF_PBSIZ 0x06 +#define LPICF_PBSIZ_1 0x00 +#define LPICF_PBSIZ_2 0x02 +#define LPICF_PBSIZ_4 0x04 +#define LPICF_PBSIZ_INVALID 0x06 + +#define LPOLCF_LCKPOL 0x08 +#define LPOLCF_FLMPOL 0x04 +#define LPOLCF_LPPOL 0x02 +#define LPOLCF_PIXPOL 0x01 + +#define LACDRC_MASK 0x0f + +#define LPXCD_MASK 0x3f + +#define LCKCON_LCDC_EN 0x80 +#define LCKCON_LCDON 0x80 +#define LCKCON_DMA16 0x40 +#define LCKCON_WS 0x30 +#define LCKCON_WS_1 0x00 +#define LCKCON_WS_2 0x10 +#define LCKCON_WS_3 0x20 +#define LCKCON_WS_4 0x30 +#define LCKCON_DWIDTH 0x02 +#define LCKCON_PCDS 0x01 + +#define LBAR_MASK 0x7f + +#define LPOSR_BOS 0x08 +#define LPOSR_POS 0x07 + +#define LFRCM_XMOD 0xf0 +#define LFRCM_YMOD 0x0f + +#define LGPMR_PAL1 0x7000 +#define LGPMR_PAL0 0x0700 +#define LGPMR_PAL3 0x0070 +#define LGPMR_PAL2 0x0007 + +#define RTCHMSR_HOURS 0x1f000000 +#define RTCHMSR_MINUTES 0x003f0000 +#define RTCHMSR_SECONDS 0x0000003f + +#define RTCCTL_38_4 0x0020 +#define RTCCTL_ENABLE 0x0080 + +#define RTCINT_STOPWATCH 0x0001 +#define RTCINT_MINUTE 0x0002 +#define RTCINT_ALARM 0x0004 +#define RTCINT_DAY 0x0008 +#define RTCINT_SECOND 0x0010 + +#define RTCSTPWTCH_MASK 0x003f + +#define TCTL_TEN 0x0001 +#define TCTL_TEN_ENABLE 0x0001 +#define TCTL_CLKSOURCE 0x000e +#define TCTL_CLKSOURCE_STOP 0x0000 +#define TCTL_CLKSOURCE_SYSCLK 0x0002 +#define TCTL_CLKSOURCE_SYSCLK16 0x0004 +#define TCTL_CLKSOURCE_TIN 0x0006 +#define TCTL_CLKSOURCE_32KHZ4 0x0008 +#define TCTL_CLKSOURCE_32KHZ5 0x000a +#define TCTL_CLKSOURCE_32KHZ6 0x000c +#define TCTL_CLKSOURCE_32KHZ7 0x000e +#define TCTL_IRQEN 0x0010 +#define TCTL_IRQEN_ENABLE 0x0010 +#define TCTL_OM 0x0020 +#define TCTL_OM_ACTIVELOW 0x0000 +#define TCTL_OM_TOGGLE 0x0020 +#define TCTL_CAPTURE 0x00c0 +#define TCTL_CAPTURE_NOINT 0x0000 +#define TCTL_CAPTURE_RISING 0x0040 +#define TCTL_CAPTURE_FALLING 0x0080 +#define TCTL_CAPTURE_BOTH 0x00c0 +#define TCTL_FRR 0x0100 +#define TCTL_FRR_RESTART 0x0000 +#define TCTL_FRR_FREERUN 0x0100 + +#define TSTAT_COMP 0x0001 +#define TSTAT_CAPT 0x0002 + +#define WCTLR_WDRST 0x0008 +#define WCTLR_LOCK 0x0004 +#define WCTLR_FI 0x0002 +#define WCTLR_WDEN 0x0001 + +#define USTCNT_UART_EN 0x8000 +#define USTCNT_RX_EN 0x4000 +#define USTCNT_TX_EN 0x2000 +#define USTCNT_RX_CLK_CONT 0x1000 +#define USTCNT_PARITY_EN 0x0800 +#define USTCNT_ODD_EVEN 0x0400 +#define USTCNT_STOP_BITS 0x0200 +#define USTCNT_8_7 0x0100 +#define USTCNT_GPIO_DELTA_EN 0x0080 +#define USTCNT_CTS_DELTA_EN 0x0040 +#define USTCNT_RX_FULL_EN 0x0020 +#define USTCNT_RX_HALF_EN 0x0010 +#define USTCNT_RX_RDY_EN 0x0008 +#define USTCNT_TX_EMPTY_EN 0x0004 +#define USTCNT_TX_HALF_EN 0x0002 +#define USTCNT_TX_AVAIL_EN 0x0001 + +#define UBAUD_GPIO_DELTA 0x8000 +#define UBAUD_GPIO 0x4000 +#define UBAUD_GPIO_DIR 0x2000 +#define UBAUD_GPIO_SRC 0x1000 +#define UBAUD_BAUD_SRC 0x0800 +#define UBAUD_DIVIDE 0x0700 +#define UBAUD_DIVIDE_1 0x0000 +#define UBAUD_DIVIDE_2 0x0100 +#define UBAUD_DIVIDE_4 0x0200 +#define UBAUD_DIVIDE_8 0x0300 +#define UBAUD_DIVIDE_16 0x0400 +#define UBAUD_DIVIDE_32 0x0500 +#define UBAUD_DIVIDE_64 0x0600 +#define UBAUD_DIVIDE_128 0x0700 +#define UBAUD_PRESCALER 0x00ff + +#define URX_FIFO_FULL 0x8000 +#define URX_FIFO_HALF 0x4000 +#define URX_DATA_READY 0x2000 +#define URX_OVRUN 0x0800 +#define URX_FRAME_ERROR 0x0400 +#define URX_BREAK 0x0200 +#define URX_PARITY_ERROR 0x0100 + +#define UTX_FIFO_EMPTY 0x8000 +#define UTX_FIFO_HALF 0x4000 +#define UTX_TX_AVAIL 0x2000 +#define UTX_SEND_BREAK 0x1000 +#define UTX_IGNORE_CTS 0x0800 +#define UTX_CTS_STATUS 0x0200 +#define UTX_CTS_DELTA 0x0100 + +#define UMISC_CLK_SRC 0x4000 +#define UMISC_FORCE_PERR 0x2000 +#define UMISC_LOOP 0x1000 +#define UMISC_RTS_CONT 0x0080 +#define UMISC_RTS 0x0040 +#define UMISC_IRDA_ENABLE 0x0020 +#define UMISC_IRDA_LOOP 0x0010 + +#define SPIS_SPIS_IRQ 0x8000 +#define SPIS_IRQEN 0x4000 +#define SPIS_ENPOL 0x2000 +#define SPIS_DATA_RDY 0x1000 +#define SPIS_OVRWR 0x0800 +#define SPIS_PHA 0x0400 +#define SPIS_POL 0x0200 +#define SPIS_SPISEN 0x0100 + +#define SPIM_CLOCK_COUNT 0x000f +#define SPIM_POL 0x0010 +#define SPIM_POL_HIGH 0x0000 +#define SPIM_POL_LOW 0x0010 +#define SPIM_PHA 0x0020 +#define SPIM_PHA_NORMAL 0x0000 +#define SPIM_PHA_OPPOSITE 0x0020 +#define SPIM_IRQEN 0x0040 +#define SPIM_SPIMIRQ 0x0080 +#define SPIM_XCH 0x0100 +#define SPIM_XCH_IDLE 0x0000 +#define SPIM_XCH_INIT 0x0100 +#define SPIM_SPMEN 0x0200 +#define SPIM_SPMEN_DISABLE 0x0000 +#define SPIM_SPMEN_ENABLE 0x0200 +#define SPIM_RATE 0xe000 +#define SPIM_RATE_4 0x0000 +#define SPIM_RATE_8 0x2000 +#define SPIM_RATE_16 0x4000 +#define SPIM_RATE_32 0x6000 +#define SPIM_RATE_64 0x8000 +#define SPIM_RATE_128 0xa000 +#define SPIM_RATE_256 0xc000 +#define SPIM_RATE_512 0xe000 + +#define PWMC_PWMIRQ 0x8000 +#define PWMC_IRQEN 0x4000 +#define PWMC_LOAD 0x0100 +#define PWMC_PIN 0x0080 +#define PWMC_POL 0x0040 +#define PWMC_PWMEN 0x0010 +#define PWMC_CLKSEL 0x0007 + +INLINE mc68328_t* mc68328_get_safe_token( device_t *device ) +{ + assert( device != NULL ); + assert( device->type() == MC68328 ); + return (mc68328_t*) downcast<mc68328_device *>(device)->token(); +} + +#endif // __MC68328_PRIVATE_H_ diff --git a/src/emu/machine/mc6843.c b/src/emu/machine/mc6843.c new file mode 100644 index 00000000000..a3a4660be3b --- /dev/null +++ b/src/emu/machine/mc6843.c @@ -0,0 +1,863 @@ +/********************************************************************** + + Copyright (C) Antoine Mine' 2007 + + Motorola 6843 Floppy Disk Controller emulation. + +**********************************************************************/ + +/* + Main MC 6843 features are: + - single density floppies + - IBM 3740 compatible + - DMA-able + - high-level commands (including multi-sector read/write) + + CLONES: HD 46503S seems to be a clone of MC 6843 + + BUGS + The driver was designed with Thomson computer emulation in mind + (CD 90-015 5"1/4 floppy controller) and works in this context. + It might work in other contexts but has currently shortcomings: + - DMA is not emulated + - Free-Format Read is not emulated + - Free-Format Write only supports track formatting, in a specific + format (FWF=1, Thomson-like sector formats) + - very rough timing: basically, there is a fixed delay between + a command request (CMR write) and its response (first byte + available, seek complete, etc.); there is no delay between + read / write + */ + + +#include "emu.h" +#include "mc6843.h" +#include "imagedev/flopdrv.h" + + +/******************* parameters ******************/ + +#define VERBOSE 0 + + +/******************* internal chip data structure ******************/ + +struct mc6843_t +{ + /* interface */ + const mc6843_interface* iface; + + /* registers */ + UINT8 CTAR; /* current track */ + UINT8 CMR; /* command */ + UINT8 ISR; /* interrupt status */ + UINT8 SUR; /* set-up */ + UINT8 STRA; /* status */ + UINT8 STRB; /* status */ + UINT8 SAR; /* sector address */ + UINT8 GCR; /* general count */ + UINT8 CCR; /* CRC control */ + UINT8 LTAR; /* logical address track (=track destination) */ + + /* internal state */ + UINT8 drive; + UINT8 side; + UINT8 data[128]; /* sector buffer */ + UINT32 data_size; /* size of data */ + UINT32 data_idx; /* current read/write position in data */ + UINT32 data_id; /* chrd_id for sector write */ + UINT8 index_pulse; + + /* trigger delayed actions (bottom halves) */ + emu_timer* timer_cont; + +}; + + + +/* macro-command numbers */ +#define CMD_STZ 0x2 /* seek track zero */ +#define CMD_SEK 0x3 /* seek */ +#define CMD_SSR 0x4 /* single sector read */ +#define CMD_SSW 0x5 /* single sector write */ +#define CMD_RCR 0x6 /* read CRC */ +#define CMD_SWD 0x7 /* single sector write with delete data mark */ +#define CMD_MSW 0xd /* multiple sector write */ +#define CMD_MSR 0xc /* multiple sector read */ +#define CMD_FFW 0xb /* free format write */ +#define CMD_FFR 0xa /* free format read */ + +/* coarse delays */ +#define DELAY_SEEK attotime::from_usec( 100 ) /* track seek time */ +#define DELAY_ADDR attotime::from_usec( 100 ) /* search-address time */ + + + +static const char *const mc6843_cmd[16] = +{ + "---", "---", "STZ", "SEK", "SSR", "SSW", "RCR", "SWD", + "---", "---", "FFR", "FFW", "MSR", "MSW", "---", "---", +}; + + +/******************* utility function and macros ********************/ + +#define LOG(x) do { if (VERBOSE) logerror x; } while (0) + + + +INLINE mc6843_t* get_safe_token( device_t *device ) +{ + assert( device != NULL ); + assert( device->type() == MC6843 ); + return (mc6843_t*) downcast<mc6843_device *>(device)->token(); +} + + +/************************** floppy interface ****************************/ + + + +static device_t* mc6843_floppy_image ( device_t *device ) +{ + mc6843_t* mc6843 = get_safe_token( device ); + return floppy_get_device( device->machine(), mc6843->drive ); +} + + + +void mc6843_set_drive( device_t *device, int drive ) +{ + mc6843_t* mc6843 = get_safe_token( device ); + mc6843->drive = drive; +} + + + +void mc6843_set_side( device_t *device, int side ) +{ + mc6843_t* mc6843 = get_safe_token( device ); + mc6843->side = side; +} + + + +/* called after ISR or STRB has changed */ +static void mc6843_status_update( device_t *device ) +{ + mc6843_t* mc6843 = get_safe_token( device ); + int irq = 0; + + /* ISR3 */ + if ( (mc6843->CMR & 0x40) || ! mc6843->STRB ) + mc6843->ISR &= ~8; + else + mc6843->ISR |= 8; + + /* interrupts */ + if ( mc6843->ISR & 4 ) + irq = 1; /* unmaskable */ + if ( ! (mc6843->CMR & 0x80) ) + { + /* maskable */ + if ( mc6843->ISR & ~4 ) + irq = 1; + } + + if ( mc6843->iface->irq_func ) + { + mc6843->iface->irq_func( device, irq ); + LOG(( "mc6843_status_update: irq=%i (CMR=%02X, ISR=%02X)\n", irq, mc6843->CMR, mc6843->ISR )); + } +} + + +void mc6843_set_index_pulse ( device_t *device, int index_pulse ) +{ + mc6843_t* mc6843 = get_safe_token( device ); + mc6843->index_pulse = index_pulse; +} + + +/* called at end of command */ +static void mc6843_cmd_end( device_t *device ) +{ + mc6843_t* mc6843 = get_safe_token( device ); + int cmd = mc6843->CMR & 0x0f; + if ( ( cmd == CMD_STZ ) || ( cmd == CMD_SEK ) ) + { + mc6843->ISR |= 0x02; /* set Settling Time Complete */ + } + else + { + mc6843->ISR |= 0x01; /* set Macro Command Complete */ + } + mc6843->STRA &= ~0x80; /* clear Busy */ + mc6843->CMR &= 0xf0; /* clear command */ + mc6843_status_update( device ); +} + + + +/* Seek Track Zero bottom half */ +static void mc6843_finish_STZ( device_t *device ) +{ + mc6843_t* mc6843 = get_safe_token( device ); + device_t* img = mc6843_floppy_image( device ); + int i; + + /* seek to track zero */ + for ( i=0; i<83; i++ ) + { + if (floppy_tk00_r(img) == CLEAR_LINE) + break; + floppy_drive_seek( img, -1 ); + } + + LOG(( "%f mc6843_finish_STZ: actual=%i\n", device->machine().time().as_double(), floppy_drive_get_current_track( img ) )); + + /* update state */ + mc6843->CTAR = 0; + mc6843->GCR = 0; + mc6843->SAR = 0; + mc6843->STRB |= floppy_tk00_r(img) << 4; + + mc6843_cmd_end( device ); +} + + + +/* Seek bottom half */ +static void mc6843_finish_SEK( device_t *device ) +{ + mc6843_t* mc6843 = get_safe_token( device ); + device_t* img = mc6843_floppy_image( device ); + + /* seek to track */ + floppy_drive_seek( img, mc6843->GCR - mc6843->CTAR ); + + LOG(( "%f mc6843_finish_SEK: from %i to %i (actual=%i)\n", device->machine().time().as_double(), mc6843->CTAR, mc6843->GCR, floppy_drive_get_current_track( img ) )); + + /* update state */ + mc6843->CTAR = mc6843->GCR; + mc6843->SAR = 0; + mc6843_cmd_end( device ); +} + + + +/* preamble to all sector read / write commands, returns 1 if found */ +static int mc6843_address_search( device_t *device, chrn_id* id ) +{ + mc6843_t* mc6843 = get_safe_token( device ); + device_t* img = mc6843_floppy_image( device ); + int r = 0; + + while ( 1 ) + { + if ( ( ! floppy_drive_get_next_id( img, mc6843->side, id ) ) || ( id->flags & ID_FLAG_CRC_ERROR_IN_ID_FIELD ) || ( id->N != 0 ) ) + { + /* read address error */ + LOG(( "%f mc6843_address_search: get_next_id failed\n", device->machine().time().as_double() )); + mc6843->STRB |= 0x0a; /* set CRC error & Sector Address Undetected */ + mc6843_cmd_end( device ); + return 0; + } + + if ( id->C != mc6843->LTAR ) + { + /* track mismatch */ + LOG(( "%f mc6843_address_search: track mismatch: logical=%i real=%i\n", device->machine().time().as_double(), mc6843->LTAR, id->C )); + mc6843->data[0] = id->C; /* make the track number available to the CPU */ + mc6843->STRA |= 0x20; /* set Track Not Equal */ + mc6843_cmd_end( device ); + return 0; + } + + if ( id->R == mc6843->SAR ) + { + /* found! */ + LOG(( "%f mc6843_address_search: sector %i found on track %i\n", device->machine().time().as_double(), id->R, id->C )); + if ( ! (mc6843->CMR & 0x20) ) + { + mc6843->ISR |= 0x04; /* if no DMA, set Status Sense */ + } + return 1; + } + + if ( floppy_drive_get_flag_state( img, FLOPPY_DRIVE_INDEX ) ) + { + r++; + if ( r >= 4 ) + { + /* time-out after 3 full revolutions */ + LOG(( "%f mc6843_address_search: no sector %i found after 3 revolutions\n", device->machine().time().as_double(), mc6843->SAR )); + mc6843->STRB |= 0x08; /* set Sector Address Undetected */ + mc6843_cmd_end( device ); + return 0; + } + } + } + + return 0; /* unreachable */ +} + + + +/* preamble specific to read commands (adds extra checks) */ +static int mc6843_address_search_read( device_t *device, chrn_id* id ) +{ + mc6843_t* mc6843 = get_safe_token( device ); + if ( ! mc6843_address_search( device, id ) ) + return 0; + + if ( id->flags & ID_FLAG_CRC_ERROR_IN_DATA_FIELD ) + { + LOG(( "%f mc6843_address_search_read: data CRC error\n", device->machine().time().as_double() )); + mc6843->STRB |= 0x06; /* set CRC error & Data Mark Undetected */ + mc6843_cmd_end( device ); + return 0; + } + + if ( id->flags & ID_FLAG_DELETED_DATA ) + { + LOG(( "%f mc6843_address_search_read: deleted data\n", device->machine().time().as_double() )); + mc6843->STRA |= 0x02; /* set Delete Data Mark Detected */ + } + + return 1; +} + + + + +/* Read CRC bottom half */ +static void mc6843_finish_RCR( device_t *device ) +{ + chrn_id id; + if ( ! mc6843_address_search_read( device, &id ) ) + return; + mc6843_cmd_end( device ); +} + + + +/* Single / Multiple Sector Read bottom half */ +static void mc6843_cont_SR( device_t *device ) +{ + mc6843_t* mc6843 = get_safe_token( device ); + chrn_id id; + device_t* img = mc6843_floppy_image( device ); + + /* sector seek */ + if ( ! mc6843_address_search_read( device, &id ) ) + return; + + /* sector read */ + floppy_drive_read_sector_data( img, mc6843->side, id.data_id, mc6843->data, 128 ); + mc6843->data_idx = 0; + mc6843->data_size = 128; + mc6843->STRA |= 0x01; /* set Data Transfer Request */ + mc6843_status_update( device ); +} + + + +/* Single / Multiple Sector Write bottom half */ +static void mc6843_cont_SW( device_t *device ) +{ + mc6843_t* mc6843 = get_safe_token( device ); + chrn_id id; + + /* sector seek */ + if ( ! mc6843_address_search( device, &id ) ) + return; + + /* setup sector write buffer */ + mc6843->data_idx = 0; + mc6843->data_size = 128; + mc6843->STRA |= 0x01; /* set Data Transfer Request */ + mc6843->data_id = id.data_id; /* for subsequent write sector command */ + mc6843_status_update( device ); +} + + + +/* bottom halves, called to continue / finish a command after some delay */ +static TIMER_CALLBACK( mc6843_cont ) +{ + device_t* device = (device_t*) ptr; + mc6843_t* mc6843 = get_safe_token( device ); + int cmd = mc6843->CMR & 0x0f; + + LOG(( "%f mc6843_cont: timer called for cmd=%s(%i)\n", device->machine().time().as_double(), mc6843_cmd[cmd], cmd )); + + mc6843->timer_cont->adjust( attotime::never ); + + switch ( cmd ) + { + case CMD_STZ: mc6843_finish_STZ( device ); break; + case CMD_SEK: mc6843_finish_SEK( device ); break; + case CMD_SSR: mc6843_cont_SR( device ); break; + case CMD_SSW: mc6843_cont_SW( device ); break; + case CMD_RCR: mc6843_finish_RCR( device ); break; + case CMD_SWD: mc6843_cont_SW( device ); break; + case CMD_MSW: mc6843_cont_SW( device ); break; + case CMD_MSR: mc6843_cont_SR( device ); break; + } +} + + + +/************************** CPU interface ****************************/ + + + +READ8_DEVICE_HANDLER ( mc6843_r ) +{ + mc6843_t* mc6843 = get_safe_token( device ); + UINT8 data = 0; + + switch ( offset ) { + case 0: /* Data Input Register (DIR) */ + { + int cmd = mc6843->CMR & 0x0f; + + LOG(( "%f $%04x mc6843_r: data input cmd=%s(%i), pos=%i/%i, GCR=%i, ", + space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), + mc6843_cmd[cmd], cmd, mc6843->data_idx, + mc6843->data_size, mc6843->GCR )); + + if ( cmd == CMD_SSR || cmd == CMD_MSR ) + { + /* sector read */ + assert( mc6843->data_size > 0 ); + assert( mc6843->data_idx < mc6843->data_size ); + assert( mc6843->data_idx < sizeof(mc6843->data) ); + data = mc6843->data[ mc6843->data_idx ]; + mc6843->data_idx++; + + if ( mc6843->data_idx >= mc6843->data_size ) + { + /* end of sector read */ + + mc6843->STRA &= ~0x01; /* clear Data Transfer Request */ + + if ( cmd == CMD_MSR ) + { + /* schedule next sector in multiple sector read */ + mc6843->GCR--; + mc6843->SAR++; + if ( mc6843->GCR == 0xff ) + { + mc6843_cmd_end( device ); + } + else if ( mc6843->SAR > 26 ) + + { + mc6843->STRB |= 0x08; /* set Sector Address Undetected */ + mc6843_cmd_end( device ); + } + else + { + mc6843->timer_cont->adjust( DELAY_ADDR ); + } + } + else + { + mc6843_cmd_end( device ); + } + } + } + else if ( cmd == 0 ) + { + data = mc6843->data[0]; + } + else + { + /* XXX TODO: other read modes */ + data = mc6843->data[0]; + logerror( "$%04x mc6843 read in unsupported command mode %i\n", space.machine().firstcpu->pcbase( ), cmd ); + } + + LOG(( "data=%02X\n", data )); + + break; + } + + case 1: /* Current-Track Address Register (CTAR) */ + data = mc6843->CTAR; + LOG(( "%f $%04x mc6843_r: read CTAR %i (actual=%i)\n", + space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), data, + floppy_drive_get_current_track( mc6843_floppy_image( device ) ) )); + break; + + case 2: /* Interrupt Status Register (ISR) */ + data = mc6843->ISR; + LOG(( "%f $%04x mc6843_r: read ISR %02X: cmd=%scomplete settle=%scomplete sense-rq=%i STRB=%i\n", + space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), data, + (data & 1) ? "" : "not-" , (data & 2) ? "" : "not-", + (data >> 2) & 1, (data >> 3) & 1 )); + + /* reset */ + mc6843->ISR &= 8; /* keep STRB */ + mc6843_status_update( device ); + break; + + case 3: /* Status Register A (STRA) */ + { + /* update */ + device_t* img = mc6843_floppy_image( device ); + int flag = floppy_drive_get_flag_state( img, FLOPPY_DRIVE_READY); + mc6843->STRA &= 0xa3; + if ( flag & FLOPPY_DRIVE_READY ) + mc6843->STRA |= 0x04; + + mc6843->STRA |= !floppy_tk00_r(img) << 3; + mc6843->STRA |= !floppy_wpt_r(img) << 4; + + if ( mc6843->index_pulse ) + mc6843->STRA |= 0x40; + + data = mc6843->STRA; + LOG(( "%f $%04x mc6843_r: read STRA %02X: data-rq=%i del-dta=%i ready=%i t0=%i wp=%i trk-dif=%i idx=%i busy=%i\n", + space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), data, + data & 1, (data >> 1) & 1, (data >> 2) & 1, (data >> 3) & 1, + (data >> 4) & 1, (data >> 5) & 1, (data >> 6) & 1, (data >> 7) & 1 )); + break; + } + + case 4: /* Status Register B (STRB) */ + data = mc6843->STRB; + LOG(( "%f $%04x mc6843_r: read STRB %02X: data-err=%i CRC-err=%i dta--mrk-err=%i sect-mrk-err=%i seek-err=%i fi=%i wr-err=%i hard-err=%i\n", + space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), data, + data & 1, (data >> 1) & 1, (data >> 2) & 1, (data >> 3) & 1, + (data >> 4) & 1, (data >> 5) & 1, (data >> 6) & 1, (data >> 7) & 1 )); + + /* (partial) reset */ + mc6843->STRB &= ~0xfb; + mc6843_status_update( device ); + break; + + case 7: /* Logical-Track Address Register (LTAR) */ + data = mc6843->LTAR; + LOG(( "%f $%04x mc6843_r: read LTAR %i (actual=%i)\n", + space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), data, + floppy_drive_get_current_track( mc6843_floppy_image( device ) ) )); + break; + + default: + logerror( "$%04x mc6843 invalid read offset %i\n", space.machine().firstcpu->pcbase( ), offset ); + } + + return data; +} + +WRITE8_DEVICE_HANDLER ( mc6843_w ) +{ + mc6843_t* mc6843 = get_safe_token( device ); + switch ( offset ) { + case 0: /* Data Output Register (DOR) */ + { + int cmd = mc6843->CMR & 0x0f; + int FWF = (mc6843->CMR >> 4) & 1; + + LOG(( "%f $%04x mc6843_w: data output cmd=%s(%i), pos=%i/%i, GCR=%i, data=%02X\n", + space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), + mc6843_cmd[cmd], cmd, mc6843->data_idx, + mc6843->data_size, mc6843->GCR, data )); + + if ( cmd == CMD_SSW || cmd == CMD_MSW || cmd == CMD_SWD ) + { + /* sector write */ + assert( mc6843->data_size > 0 ); + assert( mc6843->data_idx < mc6843->data_size ); + assert( mc6843->data_idx < sizeof(mc6843->data) ); + mc6843->data[ mc6843->data_idx ] = data; + mc6843->data_idx++; + if ( mc6843->data_idx >= mc6843->data_size ) + { + /* end of sector write */ + device_t* img = mc6843_floppy_image( device ); + + LOG(( "%f $%04x mc6843_w: write sector %i\n", space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), mc6843->data_id )); + + floppy_drive_write_sector_data( + img, mc6843->side, mc6843->data_id, + mc6843->data, mc6843->data_size, + (cmd == CMD_SWD) ? ID_FLAG_DELETED_DATA : 0 ); + + mc6843->STRA &= ~0x01; /* clear Data Transfer Request */ + + if ( cmd == CMD_MSW ) + { + mc6843->GCR--; + mc6843->SAR++; + if ( mc6843->GCR == 0xff ) + { + mc6843_cmd_end( device ); + } + else if ( mc6843->SAR > 26 ) + + { + mc6843->STRB |= 0x08; /* set Sector Address Undetected */ + mc6843_cmd_end( device ); + } + else + { + mc6843->timer_cont->adjust( DELAY_ADDR ); + } + } + else + { + mc6843_cmd_end( device ); + } + } + } + else if ( (cmd == CMD_FFW) && FWF ) + { + /* assume we are formatting */ + UINT8 nibble; + nibble = + (data & 0x01) | + ((data & 0x04) >> 1 )| + ((data & 0x10) >> 2 )| + ((data & 0x40) >> 3 ); + + assert( mc6843->data_idx < sizeof(mc6843->data) ); + + mc6843->data[mc6843->data_idx / 2] = + (mc6843->data[mc6843->data_idx / 2] << 4) | nibble; + + if ( (mc6843->data_idx == 0) && (mc6843->data[0] == 0xfe ) ) + { + /* address mark detected */ + mc6843->data_idx = 2; + } + else if ( mc6843->data_idx == 9 ) + { + /* address id field complete */ + if ( (mc6843->data[2] == 0) && (mc6843->data[4] == 0) ) + { + /* valid address id field */ + device_t* img = mc6843_floppy_image( device ); + UINT8 track = mc6843->data[1]; + UINT8 sector = mc6843->data[3]; + UINT8 filler = 0xe5; /* standard Thomson filler */ + LOG(( "%f $%04x mc6843_w: address id detected track=%i sector=%i\n", space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), track, sector)); + floppy_drive_format_sector( img, mc6843->side, sector, track, 0, sector, 0, filler ); + } + else + { + /* abort */ + mc6843->data_idx = 0; + } + } + else if ( mc6843->data_idx > 0 ) + { + /* accumulate address id field */ + mc6843->data_idx++; + } + } + else if ( cmd == 0 ) + { + /* nothing */ + } + else + { + /* XXX TODO: other write modes */ + logerror( "$%04x mc6843 write %02X in unsupported command mode %i (FWF=%i)\n", space.machine().firstcpu->pcbase( ), data, cmd, FWF ); + } + break; + } + + case 1: /* Current-Track Address Register (CTAR) */ + mc6843->CTAR = data & 0x7f; + LOG(( "%f $%04x mc6843_w: set CTAR to %i %02X (actual=%i) \n", + space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), mc6843->CTAR, data, + floppy_drive_get_current_track( mc6843_floppy_image( device ) ) )); + break; + + case 2: /* Command Register (CMR) */ + { + int cmd = data & 15; + + LOG(( "%f $%04x mc6843_w: set CMR to $%02X: cmd=%s(%i) FWF=%i DMA=%i ISR3-intr=%i fun-intr=%i\n", + space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), + data, mc6843_cmd[cmd], cmd, (data >> 4) & 1, (data >> 5) & 1, + (data >> 6) & 1, (data >> 7) & 1 )); + + /* sanitize state */ + mc6843->STRA &= ~0x81; /* clear Busy & Data Transfer Request */ + mc6843->data_idx = 0; + mc6843->data_size = 0; + + /* commands are initiated by updating some flags and scheduling + a bottom-half (mc6843_cont) after some delay */ + + switch (cmd) + { + case CMD_SSW: + case CMD_SSR: + case CMD_SWD: + case CMD_RCR: + case CMD_MSR: + case CMD_MSW: + mc6843->STRA |= 0x80; /* set Busy */ + mc6843->STRA &= ~0x22; /* clear Track Not Equal & Delete Data Mark Detected */ + mc6843->STRB &= ~0x04; /* clear Data Mark Undetected */ + mc6843->timer_cont->adjust( DELAY_ADDR ); + break; + case CMD_STZ: + case CMD_SEK: + mc6843->STRA |= 0x80; /* set Busy */ + mc6843->timer_cont->adjust( DELAY_SEEK ); + break; + case CMD_FFW: + case CMD_FFR: + mc6843->data_idx = 0; + mc6843->STRA |= 0x01; /* set Data Transfer Request */ + break; + } + + mc6843->CMR = data; + mc6843_status_update( device ); + break; + } + + case 3: /* Set-Up Register (SUR) */ + mc6843->SUR = data; + + /* assume CLK freq = 1MHz (IBM 3740 compatibility) */ + LOG(( "%f $%04x mc6843_w: set SUR to $%02X: head settling time=%fms, track-to-track seek time=%f\n", + space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), + data, 4.096 * (data & 15), 1.024 * ((data >> 4) & 15) )); + break; + + case 4: /* Sector Address Register (SAR) */ + mc6843->SAR = data & 0x1f; + LOG(( "%f $%04x mc6843_w: set SAR to %i (%02X)\n", space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), mc6843->SAR, data )); + break; + + case 5: /* General Count Register (GCR) */ + mc6843->GCR = data & 0x7f; + LOG(( "%f $%04x mc6843_w: set GCR to %i (%02X)\n", space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), mc6843->GCR, data )); + break; + + case 6: /* CRC Control Register (CCR) */ + mc6843->CCR = data & 3; + LOG(( "%f $%04x mc6843_w: set CCR to %02X: CRC=%s shift=%i\n", + space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), data, + (data & 1) ? "enabled" : "disabled", (data >> 1) & 1 )); + break; + + case 7: /* Logical-Track Address Register (LTAR) */ + mc6843->LTAR = data & 0x7f; + LOG(( "%f $%04x mc6843_w: set LTAR to %i %02X (actual=%i)\n", + space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), mc6843->LTAR, data, + floppy_drive_get_current_track( mc6843_floppy_image( device ) ) )); + break; + + default: + logerror( "$%04x mc6843 invalid write offset %i (data=$%02X)\n", space.machine().firstcpu->pcbase( ), offset, data ); + } +} + + + +/************************ reset *****************************/ + +static DEVICE_RESET( mc6843 ) +{ + mc6843_t* mc6843 = get_safe_token( device ); + int i; + LOG (( "mc6843 reset\n" )); + + /* setup/reset floppy drive */ + for ( i = 0; i < 4; i++ ) + { + device_t * img = floppy_get_device( device->machine(), i ); + floppy_mon_w(img, CLEAR_LINE); + floppy_drive_set_ready_state( img, FLOPPY_DRIVE_READY, 0 ); + floppy_drive_set_rpm( img, 300. ); + } + + /* reset registers */ + mc6843->CMR &= 0xf0; /* zero only command */ + mc6843->ISR = 0; + mc6843->STRA &= 0x5c; + mc6843->SAR = 0; + mc6843->STRB &= 0x20; + mc6843_status_update( device ); + + mc6843->data_size = 0; + mc6843->data_idx = 0; + mc6843->timer_cont->adjust( attotime::never ); +} + + + +/************************ start *****************************/ + +static DEVICE_START( mc6843 ) +{ + mc6843_t* mc6843 = get_safe_token( device ); + + mc6843->iface = (const mc6843_interface*)device->static_config(); + + mc6843->timer_cont = device->machine().scheduler().timer_alloc(FUNC(mc6843_cont), (void*) device) ; + + state_save_register_item( device->machine(),"mc6843", device->tag(), 0, mc6843->CTAR ); + state_save_register_item( device->machine(),"mc6843", device->tag(), 0, mc6843->CMR ); + state_save_register_item( device->machine(),"mc6843", device->tag(), 0, mc6843->ISR ); + state_save_register_item( device->machine(),"mc6843", device->tag(), 0, mc6843->SUR ); + state_save_register_item( device->machine(),"mc6843", device->tag(), 0, mc6843->STRA ); + state_save_register_item( device->machine(),"mc6843", device->tag(), 0, mc6843->STRB ); + state_save_register_item( device->machine(),"mc6843", device->tag(), 0, mc6843->SAR ); + state_save_register_item( device->machine(),"mc6843", device->tag(), 0, mc6843->GCR ); + state_save_register_item( device->machine(),"mc6843", device->tag(), 0, mc6843->CCR ); + state_save_register_item( device->machine(),"mc6843", device->tag(), 0, mc6843->LTAR ); + state_save_register_item( device->machine(),"mc6843", device->tag(), 0, mc6843->drive ); + state_save_register_item( device->machine(),"mc6843", device->tag(), 0, mc6843->side ); + state_save_register_item_array( device->machine(),"mc6843", device->tag(), 0, mc6843->data ); + state_save_register_item( device->machine(),"mc6843", device->tag(), 0, mc6843->data_size ); + state_save_register_item( device->machine(),"mc6843", device->tag(), 0, mc6843->data_idx ); + state_save_register_item( device->machine(),"mc6843", device->tag(), 0, mc6843->data_id ); +} + + +const device_type MC6843 = &device_creator<mc6843_device>; + +mc6843_device::mc6843_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, MC6843, "Motorola MC6843 floppy controller", tag, owner, clock) +{ + m_token = global_alloc_clear(mc6843_t); +} + +//------------------------------------------------- +// device_config_complete - perform any +// operations now that the configuration is +// complete +//------------------------------------------------- + +void mc6843_device::device_config_complete() +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void mc6843_device::device_start() +{ + DEVICE_START_NAME( mc6843 )(this); +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void mc6843_device::device_reset() +{ + DEVICE_RESET_NAME( mc6843 )(this); +} diff --git a/src/emu/machine/mc6843.h b/src/emu/machine/mc6843.h new file mode 100644 index 00000000000..677899c817f --- /dev/null +++ b/src/emu/machine/mc6843.h @@ -0,0 +1,59 @@ +/********************************************************************** + + Copyright (C) Antoine Mine' 2007 + + Motorola 6843 Floppy Disk Controller emulation. + +**********************************************************************/ + +#ifndef MC6843_H +#define MC6843_H + +class mc6843_device : public device_t +{ +public: + mc6843_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + ~mc6843_device() { global_free(m_token); } + + // access to legacy token + void *token() const { assert(m_token != NULL); return m_token; } +protected: + // device-level overrides + virtual void device_config_complete(); + virtual void device_start(); + virtual void device_reset(); +private: + // internal state + void *m_token; +}; + +extern const device_type MC6843; + + + +/* ---------- configuration ------------ */ + +struct mc6843_interface +{ + void ( * irq_func ) ( device_t *device, int state ); +}; + + +#define MCFG_MC6843_ADD(_tag, _intrf) \ + MCFG_DEVICE_ADD(_tag, MC6843, 0) \ + MCFG_DEVICE_CONFIG(_intrf) + +#define MCFG_MC6843_REMOVE(_tag) \ + MCFG_DEVICE_REMOVE(_tag) + + +/* ---------- functions ------------ */ + +extern DECLARE_READ8_DEVICE_HANDLER ( mc6843_r ); +extern DECLARE_WRITE8_DEVICE_HANDLER ( mc6843_w ); + +extern void mc6843_set_drive ( device_t *device, int drive ); +extern void mc6843_set_side ( device_t *device, int side ); +extern void mc6843_set_index_pulse ( device_t *device, int index_pulse ); + +#endif diff --git a/src/emu/machine/mc6846.c b/src/emu/machine/mc6846.c new file mode 100644 index 00000000000..2db1709d9cd --- /dev/null +++ b/src/emu/machine/mc6846.c @@ -0,0 +1,671 @@ +/********************************************************************** + + Copyright (C) Antoine Mine' 2006 + + Motorola 6846 emulation. + + The MC6846 chip provides ROM (2048 bytes), I/O (8-bit directional data port + + 2 control lines) and a programmable timer. + It may be interfaced with a M6809 cpu. + It is used in some Thomson computers. + + Not yet implemented: + - external clock (CTC) + - latching of port on CP1 + - gate input (CTG) + - timer comparison modes (frequency and pulse width) + - CP2 acknowledge modes + +**********************************************************************/ + +#include "emu.h" +#include "mc6846.h" + +#define VERBOSE 0 + + + +/******************* internal chip data structure ******************/ + + +struct mc6846_t +{ + const mc6846_interface* iface; + + /* registers */ + UINT8 csr; /* 0,4: combination status register */ + UINT8 pcr; /* 1: peripheral control register */ + UINT8 ddr; /* 2: data direction register */ + UINT8 pdr; /* 3: peripheral data register (last cpu write) */ + UINT8 tcr; /* 5: timer control register */ + + /* lines */ + UINT8 cp1; /* 1-bit input */ + UINT8 cp2; /* 1-bit input/output: last external write */ + UINT8 cp2_cpu; /* last cpu write */ + UINT8 cto; /* 1-bit timer output (unmasked) */ + + /* internal state */ + UINT8 time_MSB; /* MSB buffer register */ + UINT8 csr0_to_be_cleared; + UINT8 csr1_to_be_cleared; + UINT8 csr2_to_be_cleared; + UINT16 latch; /* timer latch */ + UINT16 preset; /* preset value */ + UINT8 timer_started; + + /* timers */ + emu_timer *interval; /* interval programmable timer */ + emu_timer *one_shot; /* 1-us x factor one-shot timer */ + + /* CPU write to the outside through chip */ + devcb_resolved_write8 out_port; /* 8-bit output */ + devcb_resolved_write8 out_cp1; /* 1-bit output */ + devcb_resolved_write8 out_cp2; /* 1-bit output */ + + /* CPU read from the outside through chip */ + devcb_resolved_read8 in_port; /* 8-bit input */ + + /* asynchronous timer output to outside world */ + devcb_resolved_write8 out_cto; /* 1-bit output */ + + /* timer interrupt */ + devcb_resolved_write_line irq; + + int old_cif; + int old_cto; +}; + + + +/******************* utility function and macros ********************/ + +#define LOG(x) do { if (VERBOSE) logerror x; } while (0) + +#define PORT \ + ((mc6846->pdr & mc6846->ddr) | \ + ((!mc6846->in_port.isnull() ? mc6846->in_port( 0 ) : 0) & \ + ~mc6846->ddr)) + +#define CTO \ + ((MODE == 0x30 || (mc6846->tcr & 0x80)) ? mc6846->cto : 0) + +#define MODE (mc6846->tcr & 0x38) + +#define FACTOR ((mc6846->tcr & 4) ? 8 : 1) + + + +INLINE mc6846_t* get_safe_token( device_t *device ) +{ + assert( device != NULL ); + assert( device->type() == MC6846 ); + return (mc6846_t*) downcast<mc6846_device *>(device)->token(); +} + + +INLINE UINT16 mc6846_counter( device_t *device ) +{ + mc6846_t* mc6846 = get_safe_token( device ); + if ( mc6846->timer_started ) + { + attotime delay = mc6846->interval ->remaining( ); + return delay.as_ticks(1000000) / FACTOR; + } + else + return mc6846->preset; +} + + + +INLINE void mc6846_update_irq( device_t *device ) +{ + mc6846_t* mc6846 = get_safe_token( device ); + int cif = 0; + /* composite interrupt flag */ + if ( ( (mc6846->csr & 1) && (mc6846->tcr & 0x40) ) || + ( (mc6846->csr & 2) && (mc6846->pcr & 1) ) || + ( (mc6846->csr & 4) && (mc6846->pcr & 8) && ! (mc6846->pcr & 0x20) ) ) + cif = 1; + if ( mc6846->old_cif != cif ) + { + LOG (( "%f: mc6846 interrupt %i (time=%i cp1=%i cp2=%i)\n", + device->machine().time().as_double(), cif, + mc6846->csr & 1, (mc6846->csr >> 1 ) & 1, (mc6846->csr >> 2 ) & 1 )); + mc6846->old_cif = cif; + } + if ( cif ) + { + mc6846->csr |= 0x80; + if ( !mc6846->irq.isnull() ) + mc6846->irq( 1 ); + } + else + { + mc6846->csr &= ~0x80; + if ( !mc6846->irq.isnull() ) + mc6846->irq( 0 ); + } +} + + + +INLINE void mc6846_update_cto ( device_t *device ) +{ + mc6846_t* mc6846 = get_safe_token( device ); + int cto = CTO; + if ( cto != mc6846->old_cto ) + { + LOG (( "%f: mc6846 CTO set to %i\n", device->machine().time().as_double(), cto )); + mc6846->old_cto = cto; + } + if ( !mc6846->out_cto.isnull() ) + mc6846->out_cto( 0, cto ); +} + + + +INLINE void mc6846_timer_launch ( device_t *device ) +{ + mc6846_t* mc6846 = get_safe_token( device ); + int delay = FACTOR * (mc6846->preset+1); + LOG (( "%f: mc6846 timer launch called, mode=%i, preset=%i (x%i)\n", device->machine().time().as_double(), MODE, mc6846->preset, FACTOR )); + + if ( ! (mc6846->tcr & 2) ) + { + logerror( "mc6846 external clock CTC not implemented\n" ); + } + + switch( MODE ) + { + case 0x00: + case 0x10: /* continuous */ + mc6846->cto = 0; + break; + + case 0x20: /* single-shot */ + mc6846->cto = 0; + mc6846->one_shot->reset( attotime::from_usec(FACTOR) ); + break; + + case 0x30: /* cascaded single-shot */ + break; + + default: + logerror( "mc6846 timer mode %i not implemented\n", MODE ); + mc6846->interval->reset( ); + mc6846->timer_started = 0; + return; + } + + mc6846->interval->reset( attotime::from_usec(delay) ); + mc6846->timer_started = 1; + + mc6846->csr &= ~1; + mc6846_update_cto( device ); + mc6846_update_irq( device ); +} + + + +/******************* timer callbacks *********************************/ + +static TIMER_CALLBACK( mc6846_timer_expire ) +{ + device_t* device = (device_t*) ptr; + mc6846_t* mc6846 = get_safe_token( device ); + int delay = FACTOR * (mc6846->latch+1); + + LOG (( "%f: mc6846 timer expire called, mode=%i, latch=%i (x%i)\n", device->machine().time().as_double(), MODE, mc6846->latch, FACTOR )); + + /* latch => counter */ + mc6846->preset = mc6846->latch; + + if ( ! (mc6846->tcr & 2) ) + logerror( "mc6846 external clock CTC not implemented\n" ); + + switch ( MODE ) + { + case 0x00: + case 0x10: /* continuous */ + mc6846->cto = 1 ^ mc6846->cto; + break; + + case 0x20: /* single-shot */ + mc6846->cto = 0; + break; + + case 0x30: /* cascaded single-shot */ + mc6846->cto = ( mc6846->tcr & 0x80 ) ? 1 : 0; + break; + + default: + logerror( "mc6846 timer mode %i not implemented\n", MODE ); + mc6846->interval->reset( ); + mc6846->timer_started = 0; + return; + } + + mc6846->interval->reset( attotime::from_usec(delay) ); + + mc6846->csr |= 1; + mc6846_update_cto( device ); + mc6846_update_irq( device ); +} + + + +static TIMER_CALLBACK( mc6846_timer_one_shot ) +{ + device_t* device = (device_t*) ptr; + mc6846_t* mc6846 = get_safe_token( device ); + LOG (( "%f: mc6846 timer one shot called\n", device->machine().time().as_double() )); + + /* 1 micro second after one-shot launch, we put cto to high */ + mc6846->cto = 1; + mc6846_update_cto( device ); +} + + + +/************************** CPU interface ****************************/ + + +READ8_DEVICE_HANDLER ( mc6846_r ) +{ + mc6846_t* mc6846 = get_safe_token( device ); + switch ( offset ) + { + case 0: + case 4: + LOG (( "$%04x %f: mc6846 CSR read $%02X intr=%i (timer=%i, cp1=%i, cp2=%i)\n", + space.machine().firstcpu->pcbase( ), space.machine().time().as_double(), + mc6846->csr, (mc6846->csr >> 7) & 1, + mc6846->csr & 1, (mc6846->csr >> 1) & 1, (mc6846->csr >> 2) & 1 )); + mc6846->csr0_to_be_cleared = mc6846->csr & 1; + mc6846->csr1_to_be_cleared = mc6846->csr & 2; + mc6846->csr2_to_be_cleared = mc6846->csr & 4; + return mc6846->csr; + + case 1: + LOG (( "$%04x %f: mc6846 PCR read $%02X\n", space.machine().firstcpu->pcbase( ), space.machine().time().as_double(), mc6846->pcr )); + return mc6846->pcr; + + case 2: + LOG (( "$%04x %f: mc6846 DDR read $%02X\n", space.machine().firstcpu->pcbase( ), space.machine().time().as_double(), mc6846->ddr )); + return mc6846->ddr; + + case 3: + LOG (( "$%04x %f: mc6846 PORT read $%02X\n", space.machine().firstcpu->pcbase( ), space.machine().time().as_double(), PORT )); + if ( ! (mc6846->pcr & 0x80) ) + { + if ( mc6846->csr1_to_be_cleared ) + mc6846->csr &= ~2; + if ( mc6846->csr2_to_be_cleared ) + mc6846->csr &= ~4; + mc6846_update_irq( device ); + mc6846->csr1_to_be_cleared = 0; + mc6846->csr2_to_be_cleared = 0; + } + return PORT; + + case 5: + LOG (( "$%04x %f: mc6846 TCR read $%02X\n",space.machine().firstcpu->pcbase( ), space.machine().time().as_double(), mc6846->tcr )); + return mc6846->tcr; + + case 6: + LOG (( "$%04x %f: mc6846 COUNTER hi read $%02X\n", space.machine().firstcpu->pcbase( ), space.machine().time().as_double(), mc6846_counter( device ) >> 8 )); + if ( mc6846->csr0_to_be_cleared ) + { + mc6846->csr &= ~1; + mc6846_update_irq( device ); + } + mc6846->csr0_to_be_cleared = 0; + return mc6846_counter( device ) >> 8; + + case 7: + LOG (( "$%04x %f: mc6846 COUNTER low read $%02X\n", space.machine().firstcpu->pcbase( ), space.machine().time().as_double(), mc6846_counter( device ) & 0xff )); + if ( mc6846->csr0_to_be_cleared ) + { + mc6846->csr &= ~1; + mc6846_update_irq( device ); + } + mc6846->csr0_to_be_cleared = 0; + return mc6846_counter( device ) & 0xff; + + default: + logerror( "$%04x mc6846 invalid read offset %i\n", space.machine().firstcpu->pcbase( ), offset ); + } + return 0; +} + + + +WRITE8_DEVICE_HANDLER ( mc6846_w ) +{ + mc6846_t* mc6846 = get_safe_token( device ); + switch ( offset ) + { + case 0: + case 4: + /* CSR is read-only */ + break; + + case 1: + { + static const char *const cp2[8] = + { + "in,neg-edge", "in,neg-edge,intr", "in,pos-edge", "in,pos-edge,intr", + "out,intr-ack", "out,i/o-ack", "out,0", "out,1" + }; + static const char *const cp1[8] = + { + "neg-edge", "neg-edge,intr", "pos-edge", "pos-edge,intr", + "latched,neg-edge", "latched,neg-edge,intr", + "latcged,pos-edge", "latcged,pos-edge,intr" + }; + LOG (( "$%04x %f: mc6846 PCR write $%02X reset=%i cp2=%s cp1=%s\n", + space.machine().firstcpu->pcbase( ), space.machine().time().as_double(), data, + (data >> 7) & 1, cp2[ (data >> 3) & 7 ], cp1[ data & 7 ] )); + + } + mc6846->pcr = data; + if ( data & 0x80 ) + { /* data reset */ + mc6846->pdr = 0; + mc6846->ddr = 0; + mc6846->csr &= ~6; + mc6846_update_irq( device ); + } + if ( data & 4 ) + logerror( "$%04x mc6846 CP1 latching not implemented\n", space.machine().firstcpu->pcbase( ) ); + if (data & 0x20) + { + if (data & 0x10) + { + mc6846->cp2_cpu = (data >> 3) & 1; + if ( !mc6846->out_cp2.isnull() ) + mc6846->out_cp2( 0, mc6846->cp2_cpu ); + } + else + logerror( "$%04x mc6846 acknowledge not implemented\n", space.machine().firstcpu->pcbase( ) ); + } + break; + + case 2: + LOG (( "$%04x %f: mc6846 DDR write $%02X\n", space.machine().firstcpu->pcbase( ), space.machine().time().as_double(), data )); + if ( ! (mc6846->pcr & 0x80) ) + { + mc6846->ddr = data; + if ( !mc6846->out_port.isnull() ) + mc6846->out_port( 0, mc6846->pdr & mc6846->ddr ); + } + break; + + case 3: + LOG (( "$%04x %f: mc6846 PORT write $%02X (mask=$%02X)\n", space.machine().firstcpu->pcbase( ), space.machine().time().as_double(), data,mc6846->ddr )); + if ( ! (mc6846->pcr & 0x80) ) + { + mc6846->pdr = data; + if ( !mc6846->out_port.isnull() ) + mc6846->out_port( 0, mc6846->pdr & mc6846->ddr ); + if ( mc6846->csr1_to_be_cleared && (mc6846->csr & 2) ) + { + mc6846->csr &= ~2; + LOG (( "$%04x %f: mc6846 CP1 intr reset\n", space.machine().firstcpu->pcbase( ), space.machine().time().as_double() )); + } + if ( mc6846->csr2_to_be_cleared && (mc6846->csr & 4) ) + { + mc6846->csr &= ~4; + LOG (( "$%04x %f: mc6846 CP2 intr reset\n", space.machine().firstcpu->pcbase( ), space.machine().time().as_double() )); + } + mc6846->csr1_to_be_cleared = 0; + mc6846->csr2_to_be_cleared = 0; + mc6846_update_irq( device ); + } + break; + + case 5: + { + static const char *const mode[8] = + { + "continuous", "cascaded", "continuous", "one-shot", + "freq-cmp", "freq-cmp", "pulse-cmp", "pulse-cmp" + }; + LOG (( "$%04x %f: mc6846 TCR write $%02X reset=%i clock=%s scale=%i mode=%s out=%s\n", + space.machine().firstcpu->pcbase( ), space.machine().time().as_double(), data, + (data >> 7) & 1, (data & 0x40) ? "extern" : "sys", + (data & 0x40) ? 1 : 8, mode[ (data >> 1) & 7 ], + (data & 1) ? "enabled" : "0" )); + + mc6846->tcr = data; + if ( mc6846->tcr & 1 ) + { + /* timer preset = initialization without launch */ + mc6846->preset = mc6846->latch; + mc6846->csr &= ~1; + if ( MODE != 0x30 ) + mc6846->cto = 0; + mc6846_update_cto( device ); + mc6846->interval->reset( ); + mc6846->one_shot->reset( ); + mc6846->timer_started = 0; + } + else + { + /* timer launch */ + if ( ! mc6846->timer_started ) + mc6846_timer_launch( device ); + } + mc6846_update_irq( device ); + } + break; + + case 6: + mc6846->time_MSB = data; + break; + + case 7: + mc6846->latch = ( ((UINT16) mc6846->time_MSB) << 8 ) + data; + LOG (( "$%04x %f: mc6846 COUNT write %i\n", space.machine().firstcpu->pcbase( ), space.machine().time().as_double(), mc6846->latch )); + if (!(mc6846->tcr & 0x38)) + { + /* timer initialization */ + mc6846->preset = mc6846->latch; + mc6846->csr &= ~1; + mc6846_update_irq( device ); + mc6846->cto = 0; + mc6846_update_cto( device ); + /* launch only if started */ + if (!(mc6846->tcr & 1)) + mc6846_timer_launch( device ); + } + break; + + default: + logerror( "$%04x mc6846 invalid write offset %i\n", space.machine().firstcpu->pcbase( ), offset ); + } +} + + + +/******************** outside world interface ************************/ + + + +void mc6846_set_input_cp1 ( device_t *device, int data ) +{ + mc6846_t* mc6846 = get_safe_token( device ); + data = (data != 0 ); + if ( data == mc6846->cp1 ) + return; + mc6846->cp1 = data; + LOG (( "%f: mc6846 input CP1 set to %i\n", device->machine().time().as_double(), data )); + if (( data && (mc6846->pcr & 2)) || (!data && !(mc6846->pcr & 2))) + { + mc6846->csr |= 2; + mc6846_update_irq( device ); + } +} + +void mc6846_set_input_cp2 ( device_t *device, int data ) +{ + mc6846_t* mc6846 = get_safe_token( device ); + data = (data != 0 ); + if ( data == mc6846->cp2 ) + return; + mc6846->cp2 = data; + LOG (( "%f: mc6846 input CP2 set to %i\n", device->machine().time().as_double(), data )); + if (mc6846->pcr & 0x20) + { + if (( data && (mc6846->pcr & 0x10)) || (!data && !(mc6846->pcr & 0x10))) + { + mc6846->csr |= 4; + mc6846_update_irq( device ); + } + } +} + + + +/************************ accessors **********************************/ + + + +UINT8 mc6846_get_output_port ( device_t *device ) +{ + mc6846_t* mc6846 = get_safe_token( device ); + return PORT; +} + + + +UINT8 mc6846_get_output_cto ( device_t *device ) +{ + mc6846_t* mc6846 = get_safe_token( device ); + return CTO; +} + + + +UINT8 mc6846_get_output_cp2 ( device_t *device ) +{ + mc6846_t* mc6846 = get_safe_token( device ); + return mc6846->cp2_cpu; +} + + + +UINT16 mc6846_get_preset ( device_t *device ) +{ + mc6846_t* mc6846 = get_safe_token( device ); + return mc6846->preset; +} + + + +/************************ reset *****************************/ + + +static DEVICE_RESET( mc6846 ) +{ + mc6846_t* mc6846 = get_safe_token( device ); + LOG (( "mc6846_reset\n" )); + mc6846->cto = 0; + mc6846->csr = 0; + mc6846->pcr = 0x80; + mc6846->ddr = 0; + mc6846->pdr = 0; + mc6846->tcr = 1; + mc6846->cp1 = 0; + mc6846->cp2 = 0; + mc6846->cp2_cpu = 0; + mc6846->latch = 0xffff; + mc6846->preset = 0xffff; + mc6846->time_MSB = 0; + mc6846->csr0_to_be_cleared = 0; + mc6846->csr1_to_be_cleared = 0; + mc6846->csr2_to_be_cleared = 0; + mc6846->timer_started = 0; + mc6846->interval->reset( ); + mc6846->one_shot->reset( ); +} + + +/************************ start *****************************/ + +static DEVICE_START( mc6846 ) +{ + mc6846_t* mc6846 = get_safe_token( device ); + + mc6846->iface = (const mc6846_interface*)device->static_config(); + mc6846->interval = device->machine().scheduler().timer_alloc(FUNC(mc6846_timer_expire), (void*) device ); + mc6846->one_shot = device->machine().scheduler().timer_alloc(FUNC(mc6846_timer_one_shot), (void*) device ); + + mc6846->out_port.resolve(mc6846->iface->out_port_func, *device); /* 8-bit output */ + mc6846->out_cp1.resolve(mc6846->iface->out_cp1_func, *device); /* 1-bit output */ + mc6846->out_cp2.resolve(mc6846->iface->out_cp2_func, *device); /* 1-bit output */ + + /* CPU read from the outside through chip */ + mc6846->in_port.resolve(mc6846->iface->in_port_func, *device); /* 8-bit input */ + + /* asynchronous timer output to outside world */ + mc6846->out_cto.resolve(mc6846->iface->out_cto_func, *device); /* 1-bit output */ + + /* timer interrupt */ + mc6846->irq.resolve(mc6846->iface->irq_func, *device); + + state_save_register_item( device->machine(), "mc6846", device->tag(), 0, mc6846->csr ); + state_save_register_item( device->machine(), "mc6846", device->tag(), 0, mc6846->pcr ); + state_save_register_item( device->machine(), "mc6846", device->tag(), 0, mc6846->ddr ); + state_save_register_item( device->machine(), "mc6846", device->tag(), 0, mc6846->pdr ); + state_save_register_item( device->machine(), "mc6846", device->tag(), 0, mc6846->tcr ); + state_save_register_item( device->machine(), "mc6846", device->tag(), 0, mc6846->cp1 ); + state_save_register_item( device->machine(), "mc6846", device->tag(), 0, mc6846->cp2 ); + state_save_register_item( device->machine(), "mc6846", device->tag(), 0, mc6846->cp2_cpu ); + state_save_register_item( device->machine(), "mc6846", device->tag(), 0, mc6846->cto ); + state_save_register_item( device->machine(), "mc6846", device->tag(), 0, mc6846->time_MSB ); + state_save_register_item( device->machine(), "mc6846", device->tag(), 0, mc6846->csr0_to_be_cleared ); + state_save_register_item( device->machine(), "mc6846", device->tag(), 0, mc6846->csr1_to_be_cleared ); + state_save_register_item( device->machine(), "mc6846", device->tag(), 0, mc6846->csr2_to_be_cleared ); + state_save_register_item( device->machine(), "mc6846", device->tag(), 0, mc6846->latch ); + state_save_register_item( device->machine(), "mc6846", device->tag(), 0, mc6846->preset ); + state_save_register_item( device->machine(), "mc6846", device->tag(), 0, mc6846->timer_started ); +} + + +const device_type MC6846 = &device_creator<mc6846_device>; + +mc6846_device::mc6846_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, MC6846, "Motorola MC6846 programmable timer", tag, owner, clock) +{ + m_token = global_alloc_clear(mc6846_t); +} + +//------------------------------------------------- +// device_config_complete - perform any +// operations now that the configuration is +// complete +//------------------------------------------------- + +void mc6846_device::device_config_complete() +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void mc6846_device::device_start() +{ + DEVICE_START_NAME( mc6846 )(this); +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void mc6846_device::device_reset() +{ + DEVICE_RESET_NAME( mc6846 )(this); +} diff --git a/src/emu/machine/mc6846.h b/src/emu/machine/mc6846.h new file mode 100644 index 00000000000..9e93c483038 --- /dev/null +++ b/src/emu/machine/mc6846.h @@ -0,0 +1,82 @@ +/********************************************************************** + + Copyright (C) Antoine Mine' 2006 + + Motorola 6846 timer emulation. + +**********************************************************************/ + +#ifndef MC6846_H +#define MC6846_H + +class mc6846_device : public device_t +{ +public: + mc6846_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + ~mc6846_device() { global_free(m_token); } + + // access to legacy token + void *token() const { assert(m_token != NULL); return m_token; } +protected: + // device-level overrides + virtual void device_config_complete(); + virtual void device_start(); + virtual void device_reset(); +private: + // internal state + void *m_token; +}; + +extern const device_type MC6846; + + +/* ---------- configuration ------------ */ + +struct mc6846_interface +{ + /* CPU write to the outside through chip */ + devcb_write8 out_port_func; /* 8-bit output */ + devcb_write8 out_cp1_func; /* 1-bit output */ + devcb_write8 out_cp2_func; /* 1-bit output */ + + /* CPU read from the outside through chip */ + devcb_read8 in_port_func; /* 8-bit input */ + + /* asynchronous timer output to outside world */ + devcb_write8 out_cto_func; /* 1-bit output */ + + /* timer interrupt */ + devcb_write_line irq_func; +}; + + +#define MCFG_MC6846_ADD(_tag, _intrf) \ + MCFG_DEVICE_ADD(_tag, MC6846, 0) \ + MCFG_DEVICE_CONFIG(_intrf) + +#define MCFG_MC6846_MODIFY(_tag, _intrf) \ + MCFG_DEVICE_MODIFY(_tag) \ + MCFG_DEVICE_CONFIG(_intrf) + +#define MCFG_MC6846_REMOVE(_tag) \ + MCFG_DEVICE_REMOVE(_tag) + + +/* ---------- functions ------------ */ +/* interface to CPU via address/data bus*/ +extern DECLARE_READ8_DEVICE_HANDLER ( mc6846_r ); +extern DECLARE_WRITE8_DEVICE_HANDLER ( mc6846_w ); + +/* asynchronous write from outside world into interrupt-generating pins */ +extern void mc6846_set_input_cp1 ( device_t *device, int data ); +extern void mc6846_set_input_cp2 ( device_t *device, int data ); + +/* polling from outside world */ +extern UINT8 mc6846_get_output_port ( device_t *device ); +extern UINT8 mc6846_get_output_cto ( device_t *device ); +extern UINT8 mc6846_get_output_cp2 ( device_t *device ); + +/* partial access to internal state */ +extern UINT16 mc6846_get_preset ( device_t *device ); /* timer interval - 1 in us */ + +#endif diff --git a/src/emu/machine/mc6854.c b/src/emu/machine/mc6854.c new file mode 100644 index 00000000000..f453647b60b --- /dev/null +++ b/src/emu/machine/mc6854.c @@ -0,0 +1,1072 @@ +/********************************************************************** + + Copyright (C) Antoine Mine' 2006 + + Motorola 6854 emulation. + + The MC6854 chip is an Advanced Data-Link Controller (ADLC). + It provides a high-level network interface that can transimit frames with + arbitrary data and address length, and is compatible with the following + standards: + - ADCCP (Advanced Data Communication Control Procedure) + - HDLC (High-Level Data-Link Control) + - SDLC (Synchronous Data-Link Control) + It is designed to be interfaced with a M6800-family CPU. + + It is used in the "Nano-network" extension of the Thomson computers to + link up to 32 computers at 500 Kbps. + Many networks involving one PC server and several MO5 or TO7/70 computers + were build in French schools in the 1980's to teach computer science. + + TODO: + - CRC + - DMA mode + - loop mode + - status prioritization + - NRZI vs. NRZ coding + - FD output + +**********************************************************************/ + + +#include "emu.h" +#include "mc6854.h" + + + +/******************* parameters ******************/ + + + +#define VERBOSE 0 + + +#define MAX_FRAME_LENGTH 65536 +/* arbitrary value, you may need to enlarge it if you get truncated frames */ + +#define FIFO_SIZE 3 +/* hardcoded size of the 6854 FIFO (this is a hardware limit) */ + +#define FLAG 0x7e +/* flag value, as defined by HDLC protocol: 01111110 */ + +#define BIT_LENGTH attotime::from_hz( 500000 ) + + + +/******************* internal chip data structure ******************/ + + +struct mc6854_t +{ + devcb_resolved_write_line out_irq_func; + devcb_resolved_read_line in_rxd_func; + devcb_resolved_write_line out_txd_func; + devcb_resolved_write_line out_rts_func; + devcb_resolved_write_line out_dtr_func; + + /* interface */ + const mc6854_interface* iface; + + /* registers */ + UINT8 cr1, cr2, cr3, cr4; /* control registers */ + UINT8 sr1, sr2; /* status registers */ + + UINT8 cts, dcd; + + /* transmit state */ + UINT8 tstate; + UINT16 tfifo[FIFO_SIZE]; /* X x 8-bit FIFO + full & last marker bits */ + UINT8 tones; /* counter for zero-insertion */ + emu_timer *ttimer; /* when to ask for more data */ + + /* receive state */ + UINT8 rstate; + UINT32 rreg; /* shift register */ + UINT8 rones; /* count '1 bits */ + UINT8 rsize; /* bits in the shift register */ + UINT16 rfifo[FIFO_SIZE]; /* X x 8-bit FIFO + full & addr marker bits */ + + /* frame-based interface*/ + UINT8 frame[MAX_FRAME_LENGTH]; + UINT32 flen, fpos; + +}; + +/* meaning of tstate / rtate: + 0 = idle / waiting for frame flag + 1 = flag sync + 2 = 8-bit address field(s) + 3-4 = 8-bit control field(s) + 5 = 8-bit logical control field(s) + 6 = variable-length data field(s) +*/ + + + +/******************* utility function and macros ********************/ + + + +#define LOG(x) do { if (VERBOSE) logerror x; } while (0) + + + +/* control register 1 */ + +#define AC ( mc6854->cr1 & 1 ) +#define FCTDRA ( mc6854->cr2 & 8 ) +/* extra register select bits */ + +#define RRESET ( mc6854->cr1 & 0x40 ) +#define TRESET ( mc6854->cr1 & 0x80 ) +/* transmit / reset condition */ + +#define RIE ( mc6854->cr1 & 2 ) +#define TIE ( mc6854->cr1 & 4 ) +/* interrupt enable */ + +#define DISCONTINUE ( mc6854->cr1 & 0x20 ) +/* discontinue received frame */ + + + +/* control register 2 */ + +#define PSE ( mc6854->cr2 & 1 ) +/* prioritize status bits (TODO) */ + +#define TWOBYTES ( mc6854->cr2 & 2 ) +/* two-bytes mode */ + +#define FMIDLE ( mc6854->cr2 & 4 ) +/* flag time fill (vs. mark idle) */ + +#define TLAST ( mc6854->cr2 & 0x10 ) +/* transmit last byte of frame */ + +#define RTS ( mc6854->cr2 & 0x80 ) +/* request-to-send */ + + + +/* control register 3 */ + +#define LCF ( mc6854->cr3 & 1 ) +/* logical control field select */ + +#define CEX ( mc6854->cr3 & 2 ) +/* control field is 16 bits instead of 8 */ + +#define AEX ( mc6854->cr3 & 4 ) +/* extended address mode (vs normal 8-bit address mode) */ + +#define IDL0 ( mc6854->cr3 & 8 ) +/* idle condition begins with a '0' instead of a '1" */ + +#define FDSE ( mc6854->cr3 & 0x10 ) +/* enable the flag detect status in SR1 */ + +#define LOOP ( mc6854->cr3 & 0x20 ) +/* loop mode */ + +#define TST ( mc6854->cr3 & 0x40 ) +/* test mode (or go active on poll) */ + +#define DTR ( mc6854->cr3 & 0x80 ) +/* data-transmit-ready (or loop on-line control) */ + + + +/* control register 4 */ + +#define TWOINTER ( mc6854->cr4 & 1 ) +/* both an openning and a closing inter-frame are sent */ + +static const int word_length[4] = { 5, 6, 7, 8 }; +#define TWL word_length[ ( mc6854->cr4 >> 1 ) & 3 ] +#define RWL word_length[ ( mc6854->cr4 >> 3 ) & 3 ] +/* transmit / receive word length */ + +#define ABT ( mc6854->cr4 & 0x20 ) +/* aborts */ + +#define ABTEX ( mc6854->cr4 & 0x40 ) +/* abort generates 16 '1' bits instead of 8 */ + +#define NRZ ( mc6854->cr4 & 0x80 ) +/* zero complement / non-zero complement data format */ + + + +/* status register 1 */ +#define RDA 0x01 /* receiver data available */ +#define S2RQ 0x02 /* status register #2 read request */ +#define FD 0x04 /* flag detect */ +#define CTS 0x10 /* clear-to-send */ +#define TU 0x20 /* transmitter underrun */ +#define TDRA 0x40 /* transmitter data register available */ +#define IRQ 0x80 /* interrupt request */ + + +/* status register 2 */ +#define AP 0x01 /* address present */ +#define FV 0x02 /* frame valid */ +#define RIDLE 0x04 /* receiver idle */ +#define RABT 0x08 /* receiver abort */ +#define ERR 0x10 /* invalid frame error */ +#define DCD 0x20 /* data carrier detect (ignored) */ +#define OVRN 0x40 /* receiver overrun */ +#define RDA2 0x80 /* copy of RDA */ + + + +INLINE mc6854_t* get_safe_token( device_t *device ) +{ + assert( device != NULL ); + assert( device->type() == MC6854 ); + return (mc6854_t*) downcast<mc6854_device *>(device)->token(); +} + + +/*********************** transmit ***********************/ + + + +/* MC6854 fills bit queue */ +static void mc6854_send_bits( device_t *device, UINT32 data, int len, int zi ) +{ + mc6854_t* mc6854 = get_safe_token( device ); + attotime expire; + int i; + if ( zi ) + { + /* zero-insertion mode */ + UINT32 d = 0; + int l = 0; + for ( i = 0; i < len; i++, data >>= 1, l++ ) + { + if ( data & 1 ) + { + d |= 1 << l; + mc6854->tones++; + if ( mc6854->tones == 5 ) + { + /* insert a '0' after 5 consecutive '1" */ + mc6854->tones = 0; + l++; + } + } + else + mc6854->tones = 0; + } + data = d; + len = l; + } + else + mc6854->tones = 0; + + /* send bits */ + if ( !mc6854->out_txd_func.isnull() ) + { + for ( i = 0; i < len; i++, data >>= 1 ) + mc6854->out_txd_func( data & 1 ); + } + + /* schedule when to ask the MC6854 for more bits */ + expire = mc6854->ttimer ->remaining( ); + if ( expire== attotime::never ) + expire = attotime::zero; + mc6854->ttimer->reset( expire + (BIT_LENGTH * len)); +} + + + +/* CPU push -> tfifo[0] -> ... -> tfifo[FIFO_SIZE-1] -> pop */ +static void mc6854_tfifo_push( device_t *device, UINT8 data ) +{ + mc6854_t* mc6854 = get_safe_token( device ); + int i; + + if ( TRESET ) + return; + + /* push towards the rightmost free entry */ + for ( i = FIFO_SIZE - 1; i >= 0; i-- ) + { + if ( ! ( mc6854->tfifo[ i ] & 0x100 ) ) + break; + } + + if ( i >= 0 ) + mc6854->tfifo[ i ] = data | 0x100; + else + logerror( "%f mc6854_tfifo_push: FIFO overrun\n", device->machine().time().as_double() ); + + /* start frame, if needed */ + if ( ! mc6854->tstate ) + { + LOG(( "%f mc6854_tfifo_push: start frame\n", device->machine().time().as_double() )); + mc6854->tstate = 2; + mc6854_send_bits( device, FLAG, 8, 0 ); + } +} + + + +/* CPU asks for normal frame termination */ +static void mc6854_tfifo_terminate( device_t *device ) +{ + mc6854_t* mc6854 = get_safe_token( device ); + + /* mark most recently pushed byte as the last one of the frame */ + int i; + for ( i = 0; i < FIFO_SIZE; i++ ) + { + if ( mc6854->tfifo[ i ] & 0x100 ) + { + mc6854->tfifo[ i ] |= 0x200; + break; + } + } +} + + + +/* call-back to refill the bit-stream from the FIFO */ +static TIMER_CALLBACK(mc6854_tfifo_cb) +{ + device_t* device = (device_t*) ptr; + mc6854_t* mc6854 = get_safe_token( device ); + int i, data = mc6854->tfifo[ FIFO_SIZE - 1 ]; + + if ( ! mc6854->tstate ) + return; + + /* shift FIFO to the right */ + for ( i = FIFO_SIZE - 1; i > 0; i-- ) + mc6854->tfifo[ i ] = mc6854->tfifo[ i - 1 ]; + mc6854->tfifo[ 0 ] = 0; + + if ( data & 0x100 ) + { + /* got data */ + + int blen = 8; + + switch ( mc6854->tstate ) + { + case 2: /* 8-bit address field */ + if ( ( data & 1 ) || ( ! AEX ) ) + mc6854->tstate = 3; + LOG(( "%f mc6854_tfifo_cb: address field $%02X\n", machine.time().as_double(), data & 0xff )); + break; + + case 3: /* 8-bit control field */ + if ( CEX ) + mc6854->tstate = 4; + else if ( LCF ) + mc6854->tstate = 5; + else + mc6854->tstate = 6; + LOG(( "%f mc6854_tfifo_cb: control field $%02X\n", machine.time().as_double(), data & 0xff )); + break; + + case 4: /* 8-bit extended control field (optional) */ + if ( LCF ) + mc6854->tstate = 5; + else + mc6854->tstate = 6; + LOG(( "%f mc6854_tfifo_cb: control field $%02X\n", machine.time().as_double(), data & 0xff )); + break; + + case 5: /* 8-bit logical control (optional) */ + if ( ! ( data & 0x80 ) ) + mc6854->tstate = 6; + LOG(( "%f mc6854_tfifo_cb: logical control field $%02X\n", machine.time().as_double(), data & 0xff )); + break; + + case 6: /* variable-length data */ + blen = TWL; + LOG(( "%f mc6854_tfifo_cb: data field $%02X, %i bits\n", machine.time().as_double(), data & 0xff, blen )); + break; + + default: + LOG(( "%f mc6854_tfifo_cb: state=%i\n", machine.time().as_double(), mc6854->tstate)); + } + + if ( mc6854->flen < MAX_FRAME_LENGTH ) + mc6854->frame[ mc6854->flen++ ] = data; + else + logerror( "mc6854_tfifo_cb: truncated frame, max=%i\n", MAX_FRAME_LENGTH ); + + mc6854_send_bits( device, data, blen, 1 ); + } + else + { + /* data underrun => abort */ + logerror( "%f mc6854_tfifo_cb: FIFO underrun\n", machine.time().as_double() ); + mc6854->sr1 |= TU; + mc6854->tstate = 0; + mc6854_send_bits( device, 0xffff, ABTEX ? 16 : 8, 0 ); + mc6854->flen = 0; + } + + /* close frame, if needed */ + if ( data & 0x200 ) + { + int len = mc6854->flen; + + LOG(( "%f mc6854_tfifo_cb: end frame\n", machine.time().as_double() )); + mc6854_send_bits( device, 0xdeadbeef, 16, 1 ); /* send check-sum: TODO */ + mc6854_send_bits( device, FLAG, 8, 0 ); /* send closing flag */ + + if ( mc6854->tfifo[ FIFO_SIZE - 1 ] & 0x100 ) + { + /* re-open frame asap */ + LOG(( "%f mc6854_tfifo_cb: start frame\n", machine.time().as_double() )); + if ( TWOINTER ) + mc6854_send_bits( device, FLAG, 8, 0 ); + } + else + mc6854->tstate = 0; + + mc6854->flen = 0; + if ( mc6854->iface->out_frame ) + mc6854->iface->out_frame( device, mc6854->frame, len ); + } +} + + + +static void mc6854_tfifo_clear( device_t *device ) +{ + mc6854_t* mc6854 = get_safe_token( device ); + memset( mc6854->tfifo, 0, sizeof( mc6854->tfifo ) ); + mc6854->tstate = 0; + mc6854->flen = 0; + mc6854->ttimer->reset( ); +} + + + +/*********************** receive ***********************/ + + + +/* MC6854 pushes a field in the FIFO */ +static void mc6854_rfifo_push( device_t *device, UINT8 d ) +{ + mc6854_t* mc6854 = get_safe_token( device ); + int i, blen = 8; + unsigned data = d; + + switch ( mc6854->rstate ) + { + case 0: + case 1: + case 2: /* 8-bit address field */ + if ( ( data & 1 ) || ( ! AEX ) ) + mc6854->rstate = 3; + else + mc6854->rstate = 2; + LOG(( "%f mc6854_rfifo_push: address field $%02X\n", device->machine().time().as_double(), data )); + data |= 0x400; /* address marker */ + break; + + case 3: /* 8-bit control field */ + if ( CEX ) + mc6854->rstate = 4; + else if ( LCF ) + mc6854->rstate = 5; + else + mc6854->rstate = 6; + LOG(( "%f mc6854_rfifo_push: control field $%02X\n", device->machine().time().as_double(), data )); + break; + + case 4: /* 8-bit extended control field (optional) */ + if ( LCF ) + mc6854->rstate = 5; + else + mc6854->rstate = 6; + LOG(( "%f mc6854_rfifo_push: control field $%02X\n", device->machine().time().as_double(), data )); + break; + + case 5: /* 8-bit logical control (optional) */ + if ( ! ( data & 0x80 ) ) + mc6854->rstate = 6; + LOG(( "%f mc6854_rfifo_push: logical control field $%02X\n", device->machine().time().as_double(), data )); + break; + + case 6: /* variable-length data */ + blen = RWL; + data >>= 8 - blen; + LOG(( "%f mc6854_rfifo_push: data field $%02X, %i bits\n", device->machine().time().as_double(), data, blen )); + break; + } + + /* no further FIFO fill until FV is cleared! */ + if ( mc6854->sr2 & FV ) + { + LOG(( "%f mc6854_rfifo_push: field not pushed\n", device->machine().time().as_double() )); + return; + } + + data |= 0x100; /* entry full marker */ + + /* push towards the rightmost free entry */ + for ( i = FIFO_SIZE - 1; i >= 0; i-- ) + { + if ( ! ( mc6854->rfifo[ i ] & 0x100 ) ) + break; + } + + if ( i >= 0 ) + mc6854->rfifo[ i ] = data | 0x100; + else + { + /* FIFO full */ + mc6854->sr2 |= OVRN; + mc6854->rfifo[ 0 ] = data; + logerror( "%f mc6854_rfifo_push: FIFO overrun\n", device->machine().time().as_double() ); + } + + mc6854->rsize -= blen; +} + + + +static void mc6854_rfifo_terminate( device_t *device ) +{ + mc6854_t* mc6854 = get_safe_token( device ); + /* mark most recently pushed byte as the last one of the frame */ + int i; + for ( i = 0; i < FIFO_SIZE; i++ ) + { + if ( mc6854->rfifo[ i ] & 0x100 ) + { + mc6854->tfifo[ i ] |= 0x200; + break; + } + + } + + mc6854->flen = 0; + mc6854->rstate = 1; +} + + + +/* CPU pops the FIFO */ +static UINT8 mc6854_rfifo_pop( device_t *device ) +{ + mc6854_t* mc6854 = get_safe_token( device ); + int i, data = mc6854->rfifo[ FIFO_SIZE - 1 ]; + + /* shift FIFO to the right */ + for ( i = FIFO_SIZE - 1; i > 0; i -- ) + mc6854->rfifo[ i ] = mc6854->rfifo[ i - 1 ]; + mc6854->rfifo[ 0 ] = 0; + + if ( mc6854->rfifo[ FIFO_SIZE - 1 ] & 0x200 ) + { + /* last byte in frame */ + mc6854->sr2 |= FV; /* TODO: check CRC & set ERR instead of FV if error*/ + } + + /* auto-refill in frame mode */ + if ( mc6854->flen > 0 ) + { + mc6854_rfifo_push( device, mc6854->frame[ mc6854->fpos++ ] ); + if ( mc6854->fpos == mc6854->flen ) + mc6854_rfifo_terminate( device ); + } + + return data; +} + + +/* MC6854 makes fields from bits */ +WRITE_LINE_DEVICE_HANDLER( mc6854_set_rx ) +{ + mc6854_t* mc6854 = get_safe_token( device ); + int fieldlen = ( mc6854->rstate < 6 ) ? 8 : RWL; + + if ( RRESET || (mc6854->sr2 & DCD) ) + return; + + if ( state ) + { + mc6854->rones++; + mc6854->rreg = (mc6854->rreg >> 1) | 0x80000000; + if ( mc6854->rones >= 8 ) + { + /* abort */ + mc6854->rstate = 0; + mc6854->rsize = 0; + if ( mc6854->rstate > 1 ) + { + /* only in-frame abort */ + mc6854->sr2 |= RABT; + LOG(( "%f mc6854_receive_bit: abort\n", device->machine().time().as_double() )); + } + } + else + { + mc6854->rsize++; + if ( mc6854->rstate && mc6854->rsize >= fieldlen + 24 ) + mc6854_rfifo_push( device, mc6854->rreg ); + } + } + else if ( mc6854->rones == 5 ) + { + /* discards '0' inserted after 5 '1' */ + mc6854->rones = 0; + return; + } + else if ( mc6854->rones == 6 ) + { + /* flag */ + if ( FDSE ) + mc6854->sr1 |= FD; + + if ( mc6854->rstate > 1 ) + { + /* end of frame */ + mc6854->rreg >>= 1; + mc6854->rsize++; + if ( mc6854->rsize >= fieldlen + 24 ) /* last field */ + mc6854_rfifo_push( device, mc6854->rreg ); + mc6854_rfifo_terminate( device ); + LOG(( "%f mc6854_receive_bit: end of frame\n", device->machine().time().as_double() )); + } + mc6854->rones = 0; + mc6854->rstate = 1; + mc6854->rsize = 0; + } else + { + mc6854->rones = 0; + mc6854->rreg >>= 1; + mc6854->rsize++; + if ( mc6854->rstate && mc6854->rsize >= fieldlen + 24 ) + mc6854_rfifo_push( device, mc6854->rreg ); + } +} + + + +static void mc6854_rfifo_clear( device_t *device ) +{ + mc6854_t* mc6854 = get_safe_token( device ); + memset( mc6854->rfifo, 0, sizeof( mc6854->rfifo ) ); + mc6854->rstate = 0; + mc6854->rreg = 0; + mc6854->rsize = 0; + mc6854->rones = 0; + mc6854->flen = 0; +} + + + +int mc6854_send_frame( device_t *device, UINT8* data, int len ) +{ + mc6854_t* mc6854 = get_safe_token( device ); + if ( mc6854->rstate > 1 || mc6854->tstate > 1 || RTS ) + return -1; /* busy */ + + if ( len > MAX_FRAME_LENGTH ) + { + logerror( "mc6854_send_frame: truncated frame, size=%i, max=%i\n", len, MAX_FRAME_LENGTH ); + len = MAX_FRAME_LENGTH; + } + else if ( len < 2 ) + { + logerror( "mc6854_send_frame: frame too short, size=%i, min=2\n", len ); + len = 2; + } + memcpy( mc6854->frame, data, len ); + if ( FDSE ) + mc6854->sr1 |= FD; + mc6854->flen = len; + mc6854->fpos = 0; + mc6854_rfifo_push( device, mc6854->frame[ mc6854->fpos++ ] ); + mc6854_rfifo_push( device, mc6854->frame[ mc6854->fpos++ ] ); + if ( mc6854->fpos == mc6854->flen ) + mc6854_rfifo_terminate( device ); + return 0; +} + + + +/************************** CPU interface ****************************/ + + + +WRITE_LINE_DEVICE_HANDLER( mc6854_set_cts ) +{ + mc6854_t* mc6854 = get_safe_token( device ); + if ( ! mc6854->cts && state ) + mc6854->sr1 |= CTS; + mc6854->cts = state; + + if ( mc6854->cts ) + mc6854->sr1 |= CTS; + else + mc6854->sr1 &= ~CTS; +} + + + +WRITE_LINE_DEVICE_HANDLER( mc6854_set_dcd ) +{ + mc6854_t* mc6854 = get_safe_token( device ); + if ( ! mc6854->dcd && state ) + { + mc6854->sr2 |= DCD; + /* partial reset */ + mc6854->rstate = 0; + mc6854->rreg = 0; + mc6854->rsize = 0; + mc6854->rones = 0; + } + mc6854->dcd = state; +} + + + +static void mc6854_update_sr2( mc6854_t* mc6854 ) +{ + /* update RDA */ + mc6854->sr2 |= RDA2; + if ( ! (mc6854->rfifo[ FIFO_SIZE - 1 ] & 0x100) ) + mc6854->sr2 &= ~RDA2; + else if ( TWOBYTES && ! (mc6854->tfifo[ FIFO_SIZE - 2 ] & 0x100) ) + mc6854->sr2 &= ~RDA2; + + /* update AP */ + if ( mc6854->rfifo[ FIFO_SIZE - 1 ] & 0x400 ) + mc6854->sr2 |= AP; + else + mc6854->sr2 &= ~AP; +} + + + +static void mc6854_update_sr1( mc6854_t* mc6854 ) +{ + mc6854_update_sr2( mc6854 ); + + /* update S2RQ */ + if ( mc6854->sr2 & 0x7f ) + mc6854->sr1 |= S2RQ; + else + mc6854->sr1 &= ~S2RQ; + + /* update TRDA (always prioritized by CTS) */ + if ( TRESET || ( mc6854->sr1 & CTS ) ) + mc6854->sr1 &= ~TDRA; + else + { + mc6854->sr1 |= TDRA; + if ( mc6854->tfifo[ 0 ] & 0x100 ) + mc6854->sr1 &= ~TDRA; + else if ( TWOBYTES && (mc6854->tfifo[ 1 ] & 0x100) ) + mc6854->sr1 &= ~TDRA; + } + + /* update RDA */ + if ( mc6854->sr2 & RDA2 ) + mc6854->sr1 |= RDA; + else + mc6854->sr1 &= ~RDA; + + /* update IRQ */ + mc6854->sr1 &= ~IRQ; + if ( RIE && (mc6854->sr1 & (TU | TDRA) ) ) + mc6854->sr1 |= IRQ; + if ( TIE ) + { + if ( mc6854->sr1 & (S2RQ | RDA | CTS) ) + mc6854->sr1 |= IRQ; + if ( mc6854->sr2 & (ERR | FV | DCD | OVRN | RABT | RIDLE | AP) ) + mc6854->sr1 |= IRQ; + } + + mc6854->out_irq_func((mc6854->sr1 & IRQ) ? ASSERT_LINE : CLEAR_LINE); +} + + + +READ8_DEVICE_HANDLER ( mc6854_r ) +{ + mc6854_t* mc6854 = get_safe_token( device ); + switch ( offset ) + { + case 0: /* status register 1 */ + mc6854_update_sr1( mc6854 ); + LOG(( "%f $%04x mc6854_r: get SR1=$%02X (rda=%i,s2rq=%i,fd=%i,cts=%i,tu=%i,tdra=%i,irq=%i)\n", + space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), mc6854->sr1, + ( mc6854->sr1 & RDA) ? 1 : 0, ( mc6854->sr1 & S2RQ) ? 1 : 0, + ( mc6854->sr1 & FD ) ? 1 : 0, ( mc6854->sr1 & CTS ) ? 1 : 0, + ( mc6854->sr1 & TU ) ? 1 : 0, ( mc6854->sr1 & TDRA) ? 1 : 0, + ( mc6854->sr1 & IRQ) ? 1 : 0 )); + return mc6854->sr1; + + case 1: /* status register 2 */ + mc6854_update_sr2( mc6854 ); + LOG(( "%f $%04x mc6854_r: get SR2=$%02X (ap=%i,fv=%i,ridle=%i,rabt=%i,err=%i,dcd=%i,ovrn=%i,rda2=%i)\n", + space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), mc6854->sr2, + ( mc6854->sr2 & AP ) ? 1 : 0, ( mc6854->sr2 & FV ) ? 1 : 0, + ( mc6854->sr2 & RIDLE) ? 1 : 0, ( mc6854->sr2 & RABT) ? 1 : 0, + ( mc6854->sr2 & ERR ) ? 1 : 0, ( mc6854->sr2 & DCD ) ? 1 : 0, + ( mc6854->sr2 & OVRN ) ? 1 : 0, ( mc6854->sr2 & RDA2) ? 1 : 0 )); + return mc6854->sr2; + + case 2: /* receiver data register */ + case 3: + { + UINT8 data = mc6854_rfifo_pop( device ); + LOG(( "%f $%04x mc6854_r: get data $%02X\n", + space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), data )); + return data; + } + + default: + logerror( "$%04x mc6854 invalid read offset %i\n", space.machine().firstcpu->pcbase( ), offset ); + } + return 0; +} + + + +WRITE8_DEVICE_HANDLER ( mc6854_w ) +{ + mc6854_t* mc6854 = get_safe_token( device ); + switch ( offset ) + { + case 0: /* control register 1 */ + mc6854->cr1 = data; + LOG(( "%f $%04x mc6854_w: set CR1=$%02X (ac=%i,irq=%c%c,%sreset=%c%c)\n", + space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), mc6854->cr1, + AC ? 1 : 0, + RIE ? 'r' : '-', TIE ? 't' : '-', + DISCONTINUE ? "discontinue," : "", + RRESET ? 'r' : '-', TRESET ? 't' : '-' + )); + if ( mc6854->cr1 & 0xc ) + logerror( "$%04x mc6854 DMA not handled (CR1=$%02X)\n", + space.machine().firstcpu->pcbase( ), mc6854->cr1 ); + if ( DISCONTINUE ) + { + /* abort receive FIFO but keeps shift register & synchro */ + mc6854->rstate = 0; + memset( mc6854->rfifo, 0, sizeof( mc6854->rfifo ) ); + } + if ( RRESET ) + { + /* abort FIFO & synchro */ + mc6854_rfifo_clear( device ); + mc6854->sr1 &= ~FD; + mc6854->sr2 &= ~(AP | FV | RIDLE | RABT | ERR | OVRN | DCD); + if ( mc6854->dcd ) mc6854->sr2 |= DCD; + } + if ( TRESET ) + { + mc6854_tfifo_clear( device ); + mc6854->sr1 &= ~(TU | TDRA | CTS); + if ( mc6854->cts ) mc6854->sr1 |= CTS; + } + break; + + case 1: + if ( AC ) + { + /* control register 3 */ + mc6854->cr3 = data; + LOG(( "%f $%04x mc6854_w: set CR3=$%02X (lcf=%i,aex=%i,idl=%i,fdse=%i,loop=%i,tst=%i,dtr=%i)\n", + space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), mc6854->cr3, + LCF ? (CEX ? 16 : 8) : 0, AEX ? 1 : 0, + IDL0 ? 0 : 1, FDSE ? 1 : 0, LOOP ? 1 : 0, + TST ? 1 : 0, DTR ? 1 : 0 + )); + if ( LOOP ) + logerror( "$%04x mc6854 loop mode not handled (CR3=$%02X)\n", space.machine().firstcpu->pcbase( ), mc6854->cr3 ); + if ( TST ) + logerror( "$%04x mc6854 test mode not handled (CR3=$%02X)\n", space.machine().firstcpu->pcbase( ), mc6854->cr3 ); + + mc6854->out_dtr_func( DTR ? 1 : 0 ); + + } + else + { + /* control register 2 */ + mc6854->cr2 = data; + LOG(( "%f $%04x mc6854_w: set CR2=$%02X (pse=%i,bytes=%i,fmidle=%i,%s,tlast=%i,clr=%c%c,rts=%i)\n", + space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), mc6854->cr2, + PSE ? 1 : 0, TWOBYTES ? 2 : 1, FMIDLE ? 1 : 0, + FCTDRA ? "fc" : "tdra", TLAST ? 1 : 0, + data & 0x20 ? 'r' : '-', data & 0x40 ? 't' : '-', + RTS ? 1 : 0 )); + if ( PSE ) + logerror( "$%04x mc6854 status prioritization not handled (CR2=$%02X)\n", space.machine().firstcpu->pcbase( ), mc6854->cr2 ); + if ( TLAST ) + mc6854_tfifo_terminate( device ); + if ( data & 0x20 ) + { + /* clear receiver status */ + mc6854->sr1 &= ~FD; + mc6854->sr2 &= ~(AP | FV | RIDLE | RABT | ERR | OVRN | DCD); + if ( mc6854->dcd ) + mc6854->sr2 |= DCD; + } + if ( data & 0x40 ) + { + /* clear transmitter status */ + mc6854->sr1 &= ~(TU | TDRA | CTS); + if ( mc6854->cts ) + mc6854->sr1 |= CTS; + } + + mc6854->out_rts_func( RTS ? 1 : 0 ); + } + break; + + case 2: /* transmitter data: continue data */ + LOG(( "%f $%04xmc6854_w: push data=$%02X\n", space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), data )); + mc6854_tfifo_push( device, data ); + break; + + case 3: + if ( AC ) + { + /* control register 4 */ + mc6854->cr4 = data; + LOG(( "%f $%04x mc6854_w: set CR4=$%02X (interframe=%i,tlen=%i,rlen=%i,%s%s)\n", space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), mc6854->cr4, + TWOINTER ? 2 : 1, + TWL, RWL, + ABT ? ( ABTEX ? "abort-ext," : "abort,") : "", + NRZ ? "nrz" : "nrzi" )); + if ( ABT ) + { + mc6854->tstate = 0; + mc6854_send_bits( device, 0xffff, ABTEX ? 16 : 8, 0 ); + mc6854->flen = 0; + } + } + else + { + /* transmitter data: last data */ + LOG(( "%f $%04x mc6854_w: push last-data=$%02X\n", space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), data )); + mc6854_tfifo_push( device, data ); + mc6854_tfifo_terminate( device ); + } + break; + + default: + logerror( "$%04x mc6854 invalid write offset %i (data=$%02X)\n", space.machine().firstcpu->pcbase( ), offset, data ); + } +} + +WRITE_LINE_DEVICE_HANDLER( mc6854_rxc_w ) +{ + // TODO +} + +WRITE_LINE_DEVICE_HANDLER( mc6854_txc_w ) +{ + // TODO +} + +/************************ reset *****************************/ + +static DEVICE_RESET( mc6854 ) +{ + mc6854_t* mc6854 = get_safe_token( device ); + LOG (( "mc6854 reset\n" )); + mc6854->cr1 = 0xc0; /* reset condition */ + mc6854->cr2 = 0; + mc6854->cr3 = 0; + mc6854->cr4 = 0; + mc6854->sr1 = 0; + mc6854->sr2 = 0; + mc6854->cts = 0; + mc6854->dcd = 0; + mc6854_tfifo_clear( device ); + mc6854_rfifo_clear( device ); +} + + + +/************************ start *****************************/ + +static DEVICE_START( mc6854 ) +{ + mc6854_t* mc6854 = get_safe_token( device ); + + mc6854->iface = (const mc6854_interface*)device->static_config(); + mc6854->out_irq_func.resolve(mc6854->iface->out_irq_func, *device); + mc6854->in_rxd_func.resolve(mc6854->iface->in_rxd_func, *device); + mc6854->out_txd_func.resolve(mc6854->iface->out_txd_func, *device); + mc6854->out_rts_func.resolve(mc6854->iface->out_rts_func, *device); + mc6854->out_dtr_func.resolve(mc6854->iface->out_dtr_func, *device); + + mc6854->ttimer = device->machine().scheduler().timer_alloc(FUNC(mc6854_tfifo_cb), (void*) device ); + + state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->cr1 ); + state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->cr2 ); + state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->cr3 ); + state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->cr4 ); + state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->sr1 ); + state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->sr2 ); + state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->cts ); + state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->dcd ); + state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->tstate ); + state_save_register_item_array( device->machine(), "mc6854", device->tag(), 0, mc6854->tfifo ); + state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->tones ); + state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->rstate ); + state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->rreg ); + state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->rones ); + state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->rsize ); + state_save_register_item_array( device->machine(), "mc6854", device->tag(), 0, mc6854->rfifo ); + state_save_register_item_array( device->machine(), "mc6854", device->tag(), 0, mc6854->frame ); + state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->flen ); + state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->fpos ); +} + + + +const device_type MC6854 = &device_creator<mc6854_device>; + +mc6854_device::mc6854_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, MC6854, "Motorola MC6854 ADLC", tag, owner, clock) +{ + m_token = global_alloc_clear(mc6854_t); +} + +//------------------------------------------------- +// device_config_complete - perform any +// operations now that the configuration is +// complete +//------------------------------------------------- + +void mc6854_device::device_config_complete() +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void mc6854_device::device_start() +{ + DEVICE_START_NAME( mc6854 )(this); +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void mc6854_device::device_reset() +{ + DEVICE_RESET_NAME( mc6854 )(this); +} diff --git a/src/emu/machine/mc6854.h b/src/emu/machine/mc6854.h new file mode 100644 index 00000000000..e2c5ee3909c --- /dev/null +++ b/src/emu/machine/mc6854.h @@ -0,0 +1,100 @@ +/********************************************************************** + + Copyright (C) Antoine Mine' 2006 + + Motorola 6854 emulation (network interface). + +**********************************************************************/ + +#ifndef MC6854_H +#define MC6854_H + +class mc6854_device : public device_t +{ +public: + mc6854_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + ~mc6854_device() { global_free(m_token); } + + // access to legacy token + void *token() const { assert(m_token != NULL); return m_token; } +protected: + // device-level overrides + virtual void device_config_complete(); + virtual void device_start(); + virtual void device_reset(); +private: + // internal state + void *m_token; +}; + +extern const device_type MC6854; + + +/* we provide two interfaces: + - a bit-based interface: out_tx, set_rx + - a frame-based interface: out_frame, send_frame + + The bit-based interface is low-level and slow. + Use it to simulate the actual bits sent into the wires, e.g., to connect + the emulator to another bit-based emulated network device, or an actual + device. + + The frame-based interface is higher-level and faster. + It passes bytes directly from one end to the other without bothering with + the actual bit-encoding, synchronization, and CRC. + Once completed, a frame is sent through out_frame. Aborted frames are not + transmitted at all. No start flag, stop flag, or crc bits are trasmitted. + send_frame makes a frame available to the CPU through the 6854 (it may + fail and return -1 if the 6854 is not ready to accept the frame; even + if the frame is accepted and 0 is returned, the CPU may abort it). Ony + full frames are accepted. +*/ + + +/* ---------- configuration ------------ */ + +struct mc6854_interface +{ + devcb_write_line out_irq_func; /* interrupt request */ + + /* low-level, bit-based interface */ + devcb_read_line in_rxd_func; /* receive bit */ + devcb_write_line out_txd_func; /* transmit bit */ + + /* high-level, frame-based interface */ + void ( * out_frame ) ( device_t *device, UINT8* data, int length ); + + /* control lines */ + devcb_write_line out_rts_func; /* 1 = transmitting, 0 = idle */ + devcb_write_line out_dtr_func; /* 1 = data transmit ready, 0 = busy */ +}; + + +#define MCFG_MC6854_ADD(_tag, _intrf) \ + MCFG_DEVICE_ADD(_tag, MC6854, 0) \ + MCFG_DEVICE_CONFIG(_intrf) + +#define MCFG_MC6854_REMOVE(_tag) \ + MCFG_DEVICE_REMOVE(_tag) + + +/* ---------- functions ------------ */ +/* interface to CPU via address/data bus*/ +extern DECLARE_READ8_DEVICE_HANDLER ( mc6854_r ); +extern DECLARE_WRITE8_DEVICE_HANDLER ( mc6854_w ); + +/* low-level, bit-based interface */ +WRITE_LINE_DEVICE_HANDLER( mc6854_set_rx ); + +/* high-level, frame-based interface */ +extern int mc6854_send_frame( device_t *device, UINT8* data, int length ); /* ret -1 if busy */ + +/* control lines */ +WRITE_LINE_DEVICE_HANDLER( mc6854_set_cts ); /* 1 = clear-to-send, 0 = busy */ +WRITE_LINE_DEVICE_HANDLER( mc6854_set_dcd ); /* 1 = carrier, 0 = no carrier */ + +/* clock */ +WRITE_LINE_DEVICE_HANDLER( mc6854_rxc_w ); +WRITE_LINE_DEVICE_HANDLER( mc6854_txc_w ); + +#endif diff --git a/src/emu/machine/mm58274c.c b/src/emu/machine/mm58274c.c new file mode 100644 index 00000000000..0b4d270e411 --- /dev/null +++ b/src/emu/machine/mm58274c.c @@ -0,0 +1,552 @@ +/*************************************************************************** + + mm58274c.c + + mm58274c emulation + + Reference: + * National Semiconductor MM58274C Microprocessor Compatible Real Time Clock + <http://www.national.com/ds/MM/MM58274C.pdf> + + Todo: + * Clock initialization will only work with the BwG: we need to provide + a way to customize it. + * Save the config to NVRAM? + * Support interrupt pin output + + Raphael Nabet, 2002 + +***************************************************************************/ + +#include "emu.h" +#include "mm58274c.h" + +struct mm58274c_t +{ + const mm58274c_interface *intf; + + int status; /* status register (*read* from address 0 = control register) */ + int control; /* control register (*write* to address 0) */ + + int clk_set; /* clock setting register */ + int int_ctl; /* interrupt control register */ + + + int wday; /* day of the week (1-7 (1=day1 as set in init)) */ + int years1; /* years (BCD: 0-99) */ + int years2; + int months1; /* months (BCD: 1-12) */ + int months2; + int days1; /* days (BCD: 1-31) */ + int days2; + int hours1; /* hours (BCD : 0-23) */ + int hours2; + int minutes1; /* minutes (BCD : 0-59) */ + int minutes2; + int seconds1; /* seconds (BCD : 0-59) */ + int seconds2; + int tenths; /* tenths of second (BCD : 0-9) */ + + emu_timer *increment_rtc; + emu_timer *interrupt_timer; +}; + +enum +{ + st_dcf = 0x8, /* data-changed flag */ + st_if = 0x1, /* interrupt flag */ + + ctl_test = 0x8, /* test mode (0=normal, 1=test) (not emulated) */ + ctl_clkstop = 0x4, /* clock start/stop (0=run, 1=stop) */ + ctl_intsel = 0x2, /* interrupt select (0=clock setting register, 1=interrupt register) */ + ctl_intstop = 0x1, /* interrupt start stop (0=interrupt run, 1=interrupt stop) */ + + clk_set_leap = 0xc, /* leap year counter (0 indicates a leap year) */ + clk_set_leap_inc = 0x4, /* leap year increment */ + clk_set_pm = 0x2, /* am/pm indicator (0 = am, 1 = pm, 0 in 24-hour mode) */ + clk_set_24 = 0x1, /* 12/24-hour select bit (1= 24-hour mode) */ + + int_ctl_rpt = 0x8, /* 1 for repeated interrupt */ + int_ctl_dly = 0x7 /* 0 no interrupt, 1 = .1 second, 2=.5, 3=1, 4=5, 5=10, 6=30, 7=60 */ +}; + + +INLINE mm58274c_t *get_safe_token(device_t *device) +{ + assert(device != NULL); + assert(device->type() == MM58274C); + + return (mm58274c_t *)downcast<mm58274c_device *>(device)->token(); +} + +static attotime interrupt_period_table(int val) +{ + switch(val) + { + case 0: return attotime::from_msec(0); + case 1: return attotime::from_msec(100); + case 2: return attotime::from_msec(500); + case 3: return attotime::from_seconds(1); + case 4: return attotime::from_seconds(5); + case 5: return attotime::from_seconds(10); + case 6: return attotime::from_seconds(30); + case 7: return attotime::from_seconds(60); + default: fatalerror("out of range\n"); + } +}; + +READ8_DEVICE_HANDLER( mm58274c_r ) +{ + mm58274c_t *mm58274c = get_safe_token(device); + int reply; + + offset &= 0xf; + + switch (offset) + { + case 0x0: /* Control Register */ + reply = mm58274c->status; + mm58274c->status = 0; + break; + + case 0x1: /* Tenths of Seconds */ + reply = mm58274c->tenths; + break; + + case 0x2: /* Units Seconds */ + reply = mm58274c->seconds2; + break; + + case 0x3: /* Tens Seconds */ + reply = mm58274c->seconds1; + break; + + case 0x04: /* Units Minutes */ + reply = mm58274c->minutes2; + break; + + case 0x5: /* Tens Minutes */ + reply = mm58274c->minutes1; + break; + + case 0x6: /* Units Hours */ + reply = mm58274c->hours2; + break; + + case 0x7: /* Tens Hours */ + reply = mm58274c->hours1; + break; + + case 0x8: /* Units Days */ + reply = mm58274c->days2; + break; + + case 0x9: /* Tens Days */ + reply = mm58274c->days1; + break; + + case 0xA: /* Units Months */ + reply = mm58274c->months2; + break; + + case 0xB: /* Tens Months */ + reply = mm58274c->months1; + break; + + case 0xC: /* Units Years */ + reply = mm58274c->years2; + break; + + case 0xD: /* Tens Years */ + reply = mm58274c->years1; + break; + + case 0xE: /* Day of Week */ + reply = mm58274c->wday; + break; + + case 0xF: /* Clock Setting & Interrupt Registers */ + if (mm58274c->control & ctl_intsel) + /* interrupt register */ + reply = mm58274c->int_ctl; + else + { /* clock setting register */ + if (mm58274c->clk_set & clk_set_24) + /* 24-hour mode */ + reply = mm58274c->clk_set & ~clk_set_pm; + else + /* 12-hour mode */ + reply = mm58274c->clk_set; + } + break; + + default: + reply = 0; + break; + } + + return reply; +} + + +WRITE8_DEVICE_HANDLER (mm58274c_w) +{ + mm58274c_t *mm58274c = get_safe_token(device); + + offset &= 0xf; + data &= 0xf; + + switch (offset) + { + case 0x0: /* Control Register (test mode and interrupt not emulated) */ + if ((! (mm58274c->control & ctl_intstop)) && (data & ctl_intstop)) + /* interrupt stop */ + mm58274c->interrupt_timer->enable(0); + else if ((mm58274c->control & ctl_intstop) && (! (data & ctl_intstop))) + { + /* interrupt run */ + attotime period = interrupt_period_table(mm58274c->int_ctl & int_ctl_dly); + + mm58274c->interrupt_timer->adjust(period, 0, mm58274c->int_ctl & int_ctl_rpt ? period : attotime::zero); + } + if (data & ctl_clkstop) + /* stopping the clock clears the tenth counter */ + mm58274c->tenths = 0; + mm58274c->control = data; + break; + + case 0x1: /* Tenths of Seconds: cannot be written */ + break; + + case 0x2: /* Units Seconds */ + mm58274c->seconds2 = data; + break; + + case 0x3: /* Tens Seconds */ + mm58274c->seconds1 = data; + break; + + case 0x4: /* Units Minutes */ + mm58274c->minutes2 = data; + break; + + case 0x5: /* Tens Minutes */ + mm58274c->minutes1 = data; + break; + + case 0x6: /* Units Hours */ + mm58274c->hours2 = data; + break; + + case 0x7: /* Tens Hours */ + mm58274c->hours1 = data; + break; + + case 0x8: /* Units Days */ + mm58274c->days2 = data; + break; + + case 0x9: /* Tens Days */ + mm58274c->days1 = data; + break; + + case 0xA: /* Units Months */ + mm58274c->months2 = data; + break; + + case 0xB: /* Tens Months */ + mm58274c->months1 = data; + break; + + case 0xC: /* Units Years */ + mm58274c->years2 = data; + break; + + case 0xD: /* Tens Years */ + mm58274c->years1 = data; + break; + + case 0xE: /* Day of Week */ + mm58274c->wday = data; + break; + + case 0xF: /* Clock Setting & Interrupt Registers */ + if (mm58274c->control & ctl_intsel) + { + /* interrupt register (not emulated) */ + mm58274c->int_ctl = data; + if (! (mm58274c->control & ctl_intstop)) + { + /* interrupt run */ + attotime period = interrupt_period_table(mm58274c->int_ctl & int_ctl_dly); + + mm58274c->interrupt_timer->adjust(period, 0, mm58274c->int_ctl & int_ctl_rpt ? period : attotime::zero); + } + } + else + { + /* clock setting register */ + mm58274c->clk_set = data; + #if 0 + if (mm58274c->clk_set & clk_set_24) + /* 24-hour mode */ + mm58274c->clk_set &= ~clk_set_pm; + #endif + } + break; + } +} + + +/* + Set RTC interrupt flag +*/ +static TIMER_CALLBACK(rtc_interrupt_callback) +{ + device_t *device = (device_t *)ptr; + mm58274c_t *mm58274c = get_safe_token(device); + mm58274c->status |= st_if; +} + + +/* + Increment RTC clock (timed interrupt every 1/10s) +*/ + +static TIMER_CALLBACK(increment_rtc) +{ + device_t *device = (device_t *)ptr; + mm58274c_t *mm58274c = get_safe_token(device); + if (! (mm58274c->control & ctl_clkstop)) + { + mm58274c->status |= st_dcf; + + if ((++mm58274c->tenths) == 10) + { + mm58274c->tenths = 0; + + if ((++mm58274c->seconds2) == 10) + { + mm58274c->seconds2 = 0; + + if ((++mm58274c->seconds1) == 6) + { + mm58274c->seconds1 = 0; + + if ((++mm58274c->minutes2) == 10) + { + mm58274c->minutes2 = 0; + + if ((++mm58274c->minutes1) == 6) + { + mm58274c->minutes1 = 0; + + if ((++mm58274c->hours2) == 10) + { + mm58274c->hours2 = 0; + + mm58274c->hours1++; + } + + /* handle wrap-around */ + if ((! (mm58274c->clk_set & clk_set_24)) + && ((mm58274c->hours1*10 + mm58274c->hours2) == 12)) + { + mm58274c->clk_set ^= clk_set_pm; + } + if ((! (mm58274c->clk_set & clk_set_24)) + && ((mm58274c->hours1*10 + mm58274c->hours2) == 13)) + { + mm58274c->hours1 = 0; + mm58274c->hours2 = 1; + } + + if ((mm58274c->clk_set & clk_set_24) + && ((mm58274c->hours1*10 + mm58274c->hours2) == 24)) + { + mm58274c->hours1 = mm58274c->hours2 = 0; + } + + /* increment day if needed */ + if ((mm58274c->clk_set & clk_set_24) + ? ((mm58274c->hours1*10 + mm58274c->hours2) == 0) + : (((mm58274c->hours1*10 + mm58274c->hours2) == 12) + && (! (mm58274c->clk_set & clk_set_pm)))) + { + int days_in_month; + + if ((++mm58274c->days2) == 10) + { + mm58274c->days2 = 0; + + mm58274c->days1++; + } + + if ((++mm58274c->wday) == 8) + mm58274c->wday = 1; + + { + static const int days_in_month_array[] = + { + 31,28,31, 30,31,30, + 31,31,30, 31,30,31 + }; + + if (((mm58274c->months1*10 + mm58274c->months2) != 2) || (mm58274c->clk_set & clk_set_leap)) + days_in_month = days_in_month_array[mm58274c->months1*10 + mm58274c->months2 - 1]; + else + days_in_month = 29; + } + + + if ((mm58274c->days1*10 + mm58274c->days2) == days_in_month+1) + { + mm58274c->days1 = 0; + mm58274c->days2 = 1; + + if ((++mm58274c->months2) == 10) + { + mm58274c->months2 = 0; + + mm58274c->months1++; + } + + if ((mm58274c->months1*10 + mm58274c->months2) == 13) + { + mm58274c->months1 = 0; + mm58274c->months2 = 1; + + mm58274c->clk_set = (mm58274c->clk_set & ~clk_set_leap) + | ((mm58274c->clk_set + clk_set_leap_inc) & clk_set_leap); + + if ((++mm58274c->years2) == 10) + { + mm58274c->years2 = 0; + + if ((++mm58274c->years1) == 10) + mm58274c->years1 = 0; + } + } + } + } + } + } + } + } + } + } +} + +/* Device Interface */ + +static DEVICE_START( mm58274c ) +{ + mm58274c_t *mm58274c = get_safe_token(device); + + // validate arguments + assert(device != NULL); + assert(device->tag() != NULL); + assert(device->static_config() != NULL); + + mm58274c->intf = (const mm58274c_interface*)device->static_config(); + // register for state saving + state_save_register_item(device->machine(), "mm58274c", device->tag(), 0, mm58274c->status); + state_save_register_item(device->machine(), "mm58274c", device->tag(), 0, mm58274c->control); + state_save_register_item(device->machine(), "mm58274c", device->tag(), 0, mm58274c->clk_set); + state_save_register_item(device->machine(), "mm58274c", device->tag(), 0, mm58274c->int_ctl); + state_save_register_item(device->machine(), "mm58274c", device->tag(), 0, mm58274c->wday); + state_save_register_item(device->machine(), "mm58274c", device->tag(), 0, mm58274c->years1); + state_save_register_item(device->machine(), "mm58274c", device->tag(), 0, mm58274c->years2); + state_save_register_item(device->machine(), "mm58274c", device->tag(), 0, mm58274c->months1); + state_save_register_item(device->machine(), "mm58274c", device->tag(), 0, mm58274c->months2); + state_save_register_item(device->machine(), "mm58274c", device->tag(), 0, mm58274c->days1); + state_save_register_item(device->machine(), "mm58274c", device->tag(), 0, mm58274c->days2); + state_save_register_item(device->machine(), "mm58274c", device->tag(), 0, mm58274c->hours1); + state_save_register_item(device->machine(), "mm58274c", device->tag(), 0, mm58274c->hours2); + state_save_register_item(device->machine(), "mm58274c", device->tag(), 0, mm58274c->minutes1); + state_save_register_item(device->machine(), "mm58274c", device->tag(), 0, mm58274c->minutes2); + state_save_register_item(device->machine(), "mm58274c", device->tag(), 0, mm58274c->seconds1); + state_save_register_item(device->machine(), "mm58274c", device->tag(), 0, mm58274c->seconds2); + state_save_register_item(device->machine(), "mm58274c", device->tag(), 0, mm58274c->tenths); + + mm58274c->increment_rtc = device->machine().scheduler().timer_alloc(FUNC(increment_rtc), ((void*)device)); + mm58274c->increment_rtc->adjust(attotime::zero, 0, attotime::from_msec(100)); + mm58274c->interrupt_timer = device->machine().scheduler().timer_alloc(FUNC(rtc_interrupt_callback), ((void*)device)); +} + + +static DEVICE_RESET( mm58274c ) +{ + mm58274c_t *mm58274c = get_safe_token(device); + system_time systime; + + /* get the current date/time from the core */ + device->machine().current_datetime(systime); + + mm58274c->clk_set = systime.local_time.year & 3 << 2; + if (mm58274c->intf->mode24) + mm58274c->clk_set |= clk_set_24; + + /* The clock count starts on 1st January 1900 */ + mm58274c->wday = 1 + ((systime.local_time.weekday - mm58274c->intf->day1)%7); + mm58274c->years1 = (systime.local_time.year / 10) % 10; + mm58274c->years2 = systime.local_time.year % 10; + mm58274c->months1 = (systime.local_time.month + 1) / 10; + mm58274c->months2 = (systime.local_time.month + 1) % 10; + mm58274c->days1 = systime.local_time.mday / 10; + mm58274c->days2 = systime.local_time.mday % 10; + if (!mm58274c->intf->mode24) + { + /* 12-hour mode */ + if (systime.local_time.hour > 12) + { + systime.local_time.hour -= 12; + mm58274c->clk_set |= clk_set_pm; + } + if (systime.local_time.hour == 0) + systime.local_time.hour = 12; + } + mm58274c->hours1 = systime.local_time.hour / 10; + mm58274c->hours2 = systime.local_time.hour % 10; + mm58274c->minutes1 = systime.local_time.minute / 10; + mm58274c->minutes2 = systime.local_time.minute % 10; + mm58274c->seconds1 = systime.local_time.second / 10; + mm58274c->seconds2 = systime.local_time.second % 10; + mm58274c->tenths = 0; +} + +const device_type MM58274C = &device_creator<mm58274c_device>; + +mm58274c_device::mm58274c_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, MM58274C, "National Semiconductor MM58274C", tag, owner, clock) +{ + m_token = global_alloc_clear(mm58274c_t); +} + +//------------------------------------------------- +// device_config_complete - perform any +// operations now that the configuration is +// complete +//------------------------------------------------- + +void mm58274c_device::device_config_complete() +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void mm58274c_device::device_start() +{ + DEVICE_START_NAME( mm58274c )(this); +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void mm58274c_device::device_reset() +{ + DEVICE_RESET_NAME( mm58274c )(this); +} diff --git a/src/emu/machine/mm58274c.h b/src/emu/machine/mm58274c.h new file mode 100644 index 00000000000..983d501dc59 --- /dev/null +++ b/src/emu/machine/mm58274c.h @@ -0,0 +1,56 @@ +#ifndef MM58274C_H +#define MM58274C_H + +/*************************************************************************** + MACROS +***************************************************************************/ + +class mm58274c_device : public device_t +{ +public: + mm58274c_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + ~mm58274c_device() { global_free(m_token); } + + // access to legacy token + void *token() const { assert(m_token != NULL); return m_token; } +protected: + // device-level overrides + virtual void device_config_complete(); + virtual void device_start(); + virtual void device_reset(); +private: + // internal state + void *m_token; +}; + +extern const device_type MM58274C; + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ +/* interface */ +/* + Initializes the clock chip. + day1 must be set to a value from 0 (sunday), 1 (monday) ... + to 6 (saturday) and is needed to correctly retrieve the day-of-week + from the host system clock. +*/ +struct mm58274c_interface +{ + int mode24; /* 24/12 mode */ + int day1; /* first day of week */ +}; + +DECLARE_READ8_DEVICE_HANDLER ( mm58274c_r ); +DECLARE_WRITE8_DEVICE_HANDLER( mm58274c_w ); + +/*************************************************************************** + DEVICE CONFIGURATION MACROS +***************************************************************************/ + +#define MCFG_MM58274C_ADD(_tag, _intrf) \ + MCFG_DEVICE_ADD(_tag, MM58274C, 0) \ + MCFG_DEVICE_CONFIG(_intrf) + +#endif /* MM58274C_H */ diff --git a/src/emu/machine/mos6530.c b/src/emu/machine/mos6530.c new file mode 100644 index 00000000000..9075b9a2cb5 --- /dev/null +++ b/src/emu/machine/mos6530.c @@ -0,0 +1,469 @@ +/*************************************************************************** + + MIOT 6530 emulation + +The timer seems to follow these rules: +- When the timer flag changes from 0 to 1 the timer continues to count + down at a 1 cycle rate. +- When the timer is being read or written the timer flag is reset. +- When the timer flag is set and the timer contents are 0, the counting + stops. + +From the operation of the KIM1 it expects the irqflag to be set whenever +the unit is reset. This is something that is not clear from the datasheet +and should be verified against real hardware. + +***************************************************************************/ + +#include "emu.h" +#include "mos6530.h" + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +enum +{ + TIMER_IDLE, + TIMER_COUNTING, + TIMER_FINISHING +}; + +#define TIMER_FLAG 0x80 + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +struct mos6530_port +{ + devcb_resolved_read8 in_port_func; + devcb_resolved_write8 out_port_func; + + UINT8 in; + UINT8 out; + UINT8 ddr; +}; + + +struct mos6530_state +{ + devcb_resolved_write_line out_irq_func; + + mos6530_port port[2]; + + UINT8 irqstate; + UINT8 irqenable; + + UINT8 timershift; + UINT8 timerstate; + emu_timer * timer; + + UINT32 clock; +}; + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + get_safe_token - convert a device's token + into a mos6530_state +-------------------------------------------------*/ + +INLINE mos6530_state *get_safe_token(device_t *device) +{ + assert(device != NULL); + assert(device->type() == MOS6530); + return (mos6530_state *)downcast<mos6530_device *>(device)->token(); +} + + +/*------------------------------------------------- + update_irqstate - update the IRQ state + based on interrupt enables +-------------------------------------------------*/ + +INLINE void update_irqstate(device_t *device) +{ + mos6530_state *miot = get_safe_token(device); + UINT8 out = miot->port[1].out; + + if ( miot->irqenable ) + out = ( ( miot->irqstate & TIMER_FLAG ) ? 0x00 : 0x80 ) | ( out & 0x7F ); + + if (!miot->port[1].out_port_func.isnull()) + miot->port[1].out_port_func(0, out); + else + logerror("6530MIOT chip %s: Port B is being written to but has no handler.\n", device->tag()); +} + + +/*------------------------------------------------- + get_timer - return the current timer value +-------------------------------------------------*/ + +INLINE UINT8 get_timer(mos6530_state *miot) +{ + /* if idle, return 0 */ + if (miot->timerstate == TIMER_IDLE) + return 0; + + /* if counting, return the number of ticks remaining */ + else if (miot->timerstate == TIMER_COUNTING) + return miot->timer->remaining().as_ticks(miot->clock) >> miot->timershift; + + /* if finishing, return the number of ticks without the shift */ + else + return miot->timer->remaining().as_ticks(miot->clock); +} + + +/*************************************************************************** + INTERNAL FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + timer_end_callback - callback to process the + timer +-------------------------------------------------*/ + +static TIMER_CALLBACK( timer_end_callback ) +{ + device_t *device = (device_t *)ptr; + mos6530_state *miot = get_safe_token(device); + + assert(miot->timerstate != TIMER_IDLE); + + /* if we finished counting, switch to the finishing state */ + if (miot->timerstate == TIMER_COUNTING) + { + miot->timerstate = TIMER_FINISHING; + miot->timer->adjust(attotime::from_ticks(256, miot->clock)); + + /* signal timer IRQ as well */ + miot->irqstate |= TIMER_FLAG; + update_irqstate(device); + } + + /* if we finished finishing, switch to the idle state */ + else if (miot->timerstate == TIMER_FINISHING) + { + miot->timerstate = TIMER_IDLE; + miot->timer->adjust(attotime::never); + } +} + + + +/*************************************************************************** + I/O ACCESS +***************************************************************************/ + +/*------------------------------------------------- + mos6530_w - master I/O write access +-------------------------------------------------*/ + +WRITE8_DEVICE_HANDLER( mos6530_w ) +{ + mos6530_state *miot = get_safe_token(device); + + /* if A2 == 1, we are writing to the timer */ + if (offset & 0x04) + { + static const UINT8 timershift[4] = { 0, 3, 6, 10 }; + attotime curtime = space.machine().time(); + INT64 target; + + /* A0-A1 contain the timer divisor */ + miot->timershift = timershift[offset & 3]; + + /* A3 contains the timer IRQ enable */ + if (offset & 8) + miot->irqenable |= TIMER_FLAG; + else + miot->irqenable &= ~TIMER_FLAG; + + /* writes here clear the timer flag */ + if (miot->timerstate != TIMER_FINISHING || get_timer(miot) != 0xff) + miot->irqstate &= ~TIMER_FLAG; + update_irqstate(device); + + /* update the timer */ + miot->timerstate = TIMER_COUNTING; + target = curtime.as_ticks(miot->clock) + 1 + (data << miot->timershift); + miot->timer->adjust(attotime::from_ticks(target, miot->clock) - curtime); + } + + /* if A2 == 0, we are writing to the I/O section */ + else + { + /* A1 selects the port */ + mos6530_port *port = &miot->port[(offset >> 1) & 1]; + + /* if A0 == 1, we are writing to the port's DDR */ + if (offset & 1) + port->ddr = data; + + /* if A0 == 0, we are writing to the port's output */ + else + { + UINT8 olddata = port->out; + port->out = data; + + if ( ( offset & 2 ) && miot->irqenable ) + { + olddata = ( ( miot->irqstate & TIMER_FLAG ) ? 0x00 : 0x80 ) | ( olddata & 0x7F ); + data = ( ( miot->irqstate & TIMER_FLAG ) ? 0x00 : 0x80 ) | ( data & 0x7F ); + } + + if (!port->out_port_func.isnull()) + port->out_port_func(0, data); + else + logerror("6530MIOT chip %s: Port %c is being written to but has no handler. PC: %08X - %02X\n", device->tag(), 'A' + (offset & 1), space.machine().firstcpu->pc(), data); + } + } +} + + +/*------------------------------------------------- + mos6530_r - master I/O read access +-------------------------------------------------*/ + +READ8_DEVICE_HANDLER( mos6530_r ) +{ + mos6530_state *miot = get_safe_token(device); + UINT8 val = 0; + + /* if A2 == 1 and A0 == 1, we are reading interrupt flags */ + if ((offset & 0x05) == 0x05) + { + val = miot->irqstate; + } + + /* if A2 == 1 and A0 == 0, we are reading the timer */ + else if ((offset & 0x05) == 0x04) + { + val = get_timer(miot); + + /* A3 contains the timer IRQ enable */ + if (offset & 8) + miot->irqenable |= TIMER_FLAG; + else + miot->irqenable &= ~TIMER_FLAG; + + /* implicitly clears the timer flag */ + if (miot->timerstate != TIMER_FINISHING || val != 0xff) + miot->irqstate &= ~TIMER_FLAG; + update_irqstate(device); + } + + /* if A2 == 0 and A0 == anything, we are reading from ports */ + else + { + /* A1 selects the port */ + mos6530_port *port = &miot->port[(offset >> 1) & 1]; + + /* if A0 == 1, we are reading the port's DDR */ + if (offset & 1) + val = port->ddr; + + /* if A0 == 0, we are reading the port as an input */ + else + { + UINT8 out = port->out; + + if ( ( offset & 2 ) && miot->irqenable ) + out = ( ( miot->irqstate & TIMER_FLAG ) ? 0x00 : 0x80 ) | ( out & 0x7F ); + + /* call the input callback if it exists */ + if (!port->in_port_func.isnull()) + { + port->in = port->in_port_func(0); + } + else + logerror("6530MIOT chip %s: Port %c is being read but has no handler. PC: %08X\n", device->tag(), 'A' + (offset & 1), space.machine().firstcpu->pc()); + + /* apply the DDR to the result */ + val = (out & port->ddr) | (port->in & ~port->ddr); + } + } + return val; +} + + +/*------------------------------------------------- + mos6530_porta_in_set - set port A input + value +-------------------------------------------------*/ + +void mos6530_porta_in_set(device_t *device, UINT8 data, UINT8 mask) +{ + mos6530_state *miot = get_safe_token(device); + miot->port[0].in = (miot->port[0].in & ~mask) | (data & mask); +} + + +/*------------------------------------------------- + mos6530_portb_in_set - set port B input + value +-------------------------------------------------*/ + +void mos6530_portb_in_set(device_t *device, UINT8 data, UINT8 mask) +{ + mos6530_state *miot = get_safe_token(device); + miot->port[1].in = (miot->port[1].in & ~mask) | (data & mask); +} + + +/*------------------------------------------------- + mos6530_porta_in_get - return port A input + value +-------------------------------------------------*/ + +UINT8 mos6530_porta_in_get(device_t *device) +{ + mos6530_state *miot = get_safe_token(device); + return miot->port[0].in; +} + + +/*------------------------------------------------- + mos6530_portb_in_get - return port B input + value +-------------------------------------------------*/ + +UINT8 mos6530_portb_in_get(device_t *device) +{ + mos6530_state *miot = get_safe_token(device); + return miot->port[1].in; +} + + +/*------------------------------------------------- + mos6530_porta_in_get - return port A output + value +-------------------------------------------------*/ + +UINT8 mos6530_porta_out_get(device_t *device) +{ + mos6530_state *miot = get_safe_token(device); + return miot->port[0].out; +} + + +/*------------------------------------------------- + mos6530_portb_in_get - return port B output + value +-------------------------------------------------*/ + +UINT8 mos6530_portb_out_get(device_t *device) +{ + mos6530_state *miot = get_safe_token(device); + return miot->port[1].out; +} + + +/*************************************************************************** + DEVICE INTERFACE +***************************************************************************/ + +static DEVICE_START( mos6530 ) +{ + mos6530_state *miot = get_safe_token(device); + const mos6530_interface *intf = (const mos6530_interface*)device->static_config(); + + /* validate arguments */ + assert(device != NULL); + assert(device->tag() != NULL); + + /* set static values */ + miot->clock = device->clock(); + + /* resolve callbacks */ + miot->port[0].in_port_func.resolve(intf->in_pa_func, *device); + miot->port[1].in_port_func.resolve(intf->in_pb_func, *device); + miot->port[0].out_port_func.resolve(intf->out_pa_func, *device); + miot->port[1].out_port_func.resolve(intf->out_pb_func, *device); + + /* allocate timers */ + miot->timer = device->machine().scheduler().timer_alloc(FUNC(timer_end_callback), (void *)device); + + /* register for save states */ + device->save_item(NAME(miot->port[0].in)); + device->save_item(NAME(miot->port[0].out)); + device->save_item(NAME(miot->port[0].ddr)); + device->save_item(NAME(miot->port[1].in)); + device->save_item(NAME(miot->port[1].out)); + device->save_item(NAME(miot->port[1].ddr)); + + device->save_item(NAME(miot->irqstate)); + device->save_item(NAME(miot->irqenable)); + + device->save_item(NAME(miot->timershift)); + device->save_item(NAME(miot->timerstate)); +} + + +static DEVICE_RESET( mos6530 ) +{ + mos6530_state *miot = get_safe_token(device); + + /* reset I/O states */ + miot->port[0].out = 0; + miot->port[0].ddr = 0; + miot->port[1].out = 0; + miot->port[1].ddr = 0; + + /* reset IRQ states */ + miot->irqenable = 0; + miot->irqstate = TIMER_FLAG; + update_irqstate(device); + + /* reset timer states */ + miot->timershift = 0; + miot->timerstate = TIMER_IDLE; + miot->timer->adjust(attotime::never); +} + + +const device_type MOS6530 = &device_creator<mos6530_device>; + +mos6530_device::mos6530_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, MOS6530, "MOS6530", tag, owner, clock) +{ + m_token = global_alloc_clear(mos6530_state); +} + +//------------------------------------------------- +// device_config_complete - perform any +// operations now that the configuration is +// complete +//------------------------------------------------- + +void mos6530_device::device_config_complete() +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void mos6530_device::device_start() +{ + DEVICE_START_NAME( mos6530 )(this); +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void mos6530_device::device_reset() +{ + DEVICE_RESET_NAME( mos6530 )(this); +} diff --git a/src/emu/machine/mos6530.h b/src/emu/machine/mos6530.h new file mode 100644 index 00000000000..99af148500c --- /dev/null +++ b/src/emu/machine/mos6530.h @@ -0,0 +1,97 @@ +/********************************************************************** + + MOS Technology 6530 Memory, I/O, Timer Array emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +********************************************************************** + _____ _____ + Vss 1 |* \_/ | 40 PA1 + PA0 2 | | 39 PA2 + phi2 3 | | 38 PA3 + RS0 4 | | 37 PA4 + A9 5 | | 36 PA5 + A8 6 | | 35 PA6 + A7 7 | | 34 PA7 + A6 8 | | 33 DB0 + R/W 9 | | 32 DB1 + A5 10 | MCS6530 | 31 DB2 + A4 11 | | 30 DB3 + A3 12 | | 29 DB4 + A2 13 | | 28 DB5 + A1 14 | | 27 DB6 + A0 15 | | 26 DB7 + _RES 16 | | 25 PB0 + IRQ/PB7 17 | | 24 PB1 + CS1/PB6 18 | | 23 PB2 + CS2/PB5 19 | | 22 PB3 + Vcc 20 |_____________| 21 PB4 + +**********************************************************************/ + +#ifndef __MIOT6530_H__ +#define __MIOT6530_H__ + +/*************************************************************************** + MACROS / CONSTANTS +***************************************************************************/ + +class mos6530_device : public device_t +{ +public: + mos6530_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + ~mos6530_device() { global_free(m_token); } + + // access to legacy token + void *token() const { assert(m_token != NULL); return m_token; } +protected: + // device-level overrides + virtual void device_config_complete(); + virtual void device_start(); + virtual void device_reset(); +private: + // internal state + void *m_token; +}; + +extern const device_type MOS6530; + + +#define MCFG_MOS6530_ADD(_tag, _clock, _config) \ + MCFG_DEVICE_ADD((_tag), MOS6530, _clock) \ + MCFG_DEVICE_CONFIG(_config) + +#define MOS6530_INTERFACE(name) \ + const mos6530_interface (name) = + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +struct mos6530_interface +{ + devcb_read8 in_pa_func; + devcb_write8 out_pa_func; + + devcb_read8 in_pb_func; + devcb_write8 out_pb_func; +}; + +/*************************************************************************** + PROTOTYPES +***************************************************************************/ + +DECLARE_READ8_DEVICE_HANDLER( mos6530_r ); +DECLARE_WRITE8_DEVICE_HANDLER( mos6530_w ); + +void mos6530_porta_in_set(device_t *device, UINT8 data, UINT8 mask); +void mos6530_portb_in_set(device_t *device, UINT8 data, UINT8 mask); + +UINT8 mos6530_porta_in_get(device_t *device); +UINT8 mos6530_portb_in_get(device_t *device); + +UINT8 mos6530_porta_out_get(device_t *device); +UINT8 mos6530_portb_out_get(device_t *device); + +#endif diff --git a/src/emu/machine/pcf8593.c b/src/emu/machine/pcf8593.c new file mode 100644 index 00000000000..6dd6c894354 --- /dev/null +++ b/src/emu/machine/pcf8593.c @@ -0,0 +1,283 @@ +/********************************************************************* + + Philips PCF8593 CMOS clock/calendar circuit + + (c) 2001-2007 Tim Schuerewegen + +*********************************************************************/ + +#include "pcf8593.h" + + +/*************************************************************************** + PARAMETERS/CONSTANTS/MACROS +***************************************************************************/ + +#define LOG_LEVEL 1 +#define _logerror(level,x) do { if (LOG_LEVEL > level) logerror x; } while (0) + +// get/set date +#define RTC_GET_DATE_YEAR ((m_data[5] >> 6) & 3) +#define RTC_SET_DATE_YEAR(x) m_data[5] = (m_data[5] & 0x3F) | (((x) % 4) << 6) +#define RTC_GET_DATE_MONTH bcd_to_integer( m_data[6]) +#define RTC_SET_DATE_MONTH(x) m_data[6] = convert_to_bcd( x) +#define RTC_GET_DATE_DAY (bcd_to_integer( m_data[5] & 0x3F)) +#define RTC_SET_DATE_DAY(x) m_data[5] = (m_data[5] & 0xC0) | convert_to_bcd( x) + +// get/set time +#define RTC_GET_TIME_HOUR bcd_to_integer( m_data[4]) +#define RTC_SET_TIME_HOUR(x) m_data[4] = convert_to_bcd( x) +#define RTC_GET_TIME_MINUTE bcd_to_integer( m_data[3]) +#define RTC_SET_TIME_MINUTE(x) m_data[3] = convert_to_bcd( x) +#define RTC_GET_TIME_SECOND bcd_to_integer( m_data[2]) +#define RTC_SET_TIME_SECOND(x) m_data[2] = convert_to_bcd( x) + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +const device_type PCF8593 = &device_creator<pcf8593_device>; + + +//------------------------------------------------- +// pcf8593_device - constructor +//------------------------------------------------- + +pcf8593_device::pcf8593_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, PCF8593, "PCF8593 RTC", tag, owner, clock), + device_rtc_interface(mconfig, *this), + device_nvram_interface(mconfig, *this) +{ +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void pcf8593_device::device_start() +{ + _logerror( 0, ("pcf8593_init\n")); + memset(m_register, 0, sizeof(m_register)); + m_timer = timer_alloc(TIMER_UPDATE_COUNTER); + m_timer->adjust(attotime::from_seconds(1), 0, attotime::from_seconds(1)); +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void pcf8593_device::device_reset() +{ + _logerror( 0, ("pcf8593_reset\n")); + m_pin_scl = 1; + m_pin_sda = 1; + m_active = FALSE; + m_inp = 0; + m_mode = RTC_MODE_RECV; + m_bits = 0; + m_pos = 0; + clear_buffer_rx(); + set_time(true, RTC_GET_DATE_YEAR, RTC_GET_DATE_MONTH, RTC_GET_DATE_DAY, 0, RTC_GET_TIME_HOUR, RTC_GET_TIME_MINUTE, RTC_GET_TIME_SECOND); +} + + +//------------------------------------------------- +// device_timer - handler timer events +//------------------------------------------------- + +void pcf8593_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + switch(id) + { + case TIMER_UPDATE_COUNTER: + _logerror( 2, ("pcf8593_timer_callback (%d)\n", param)); + // check if counting is enabled + if (!(m_data[0] & 0x80)) + advance_seconds(); + break; + } +} + + +//------------------------------------------------- +// rtc_clock_updated - +//------------------------------------------------- + +void pcf8593_device::rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second) +{ + RTC_SET_TIME_SECOND(second); + RTC_SET_TIME_MINUTE(minute); + RTC_SET_TIME_HOUR(hour); + RTC_SET_DATE_DAY(day); + RTC_SET_DATE_MONTH(month); + RTC_SET_DATE_YEAR(year); +} + + +//------------------------------------------------- +// nvram_default - called to initialize NVRAM to +// its default state +//------------------------------------------------- + +void pcf8593_device::nvram_default() +{ + memset(m_data, 0, sizeof(m_data)); +} + +//------------------------------------------------- +// nvram_read - called to read NVRAM from the +// .nv file +//------------------------------------------------- + +void pcf8593_device::nvram_read(emu_file &file) +{ + file.read(m_data, sizeof(m_data)); +} + + +//------------------------------------------------- +// nvram_write - called to write NVRAM to the +// .nv file +//------------------------------------------------- + +void pcf8593_device::nvram_write(emu_file &file) +{ + file.write(m_data, sizeof(m_data)); +} + + + +/*------------------------------------------------- + pcf8593_pin_scl +-------------------------------------------------*/ + +WRITE_LINE_MEMBER(pcf8593_device::scl_w) +{ + // send bit + if ((m_active) && (!m_pin_scl) && (state)) + { + switch (m_mode) + { + // HOST -> RTC + case RTC_MODE_RECV : + { + // get bit + if (m_pin_sda) m_data_recv[m_data_recv_index] = m_data_recv[m_data_recv_index] | (0x80 >> m_bits); + m_bits++; + // bit 9 = end + if (m_bits > 8) + { + _logerror( 2, ("pcf8593_write_byte(%02X)\n", m_data_recv[m_data_recv_index])); + // enter receive mode when 1st byte = 0xA3 + if ((m_data_recv[0] == 0xA3) && (m_data_recv_index == 0)) + { + m_mode = RTC_MODE_SEND; + } + // A2 + xx = "read from pos xx" command + if ((m_data_recv[0] == 0xA2) && (m_data_recv_index == 1)) + { + m_pos = m_data_recv[1]; + } + // A2 + xx + .. = write byte + if ((m_data_recv[0] == 0xA2) && (m_data_recv_index >= 2)) + { + UINT8 rtc_pos, rtc_val; + rtc_pos = m_data_recv[1] + (m_data_recv_index - 2); + rtc_val = m_data_recv[m_data_recv_index]; + //if (rtc_pos == 0) rtc_val = rtc_val & 3; // what is this doing here? + m_data[rtc_pos] = rtc_val; + set_time(false, RTC_GET_DATE_YEAR, RTC_GET_DATE_MONTH, RTC_GET_DATE_DAY, 0, RTC_GET_TIME_HOUR, RTC_GET_TIME_MINUTE, RTC_GET_TIME_SECOND); + } + // next byte + m_bits = 0; + m_data_recv_index++; + } + } + break; + // RTC -> HOST + case RTC_MODE_SEND : + { + // set bit + m_inp = (m_data[m_pos] >> (7 - m_bits)) & 1; + m_bits++; + // bit 9 = end + if (m_bits > 8) + { + _logerror( 2, ("pcf8593_read_byte(%02X)\n", m_data[m_pos])); + // end ? + if (m_pin_sda) + { + _logerror( 2, ("pcf8593 end\n")); + m_mode = RTC_MODE_RECV; + clear_buffer_rx(); + } + // next byte + m_bits = 0; + m_pos++; + } + } + break; + } + } + // save scl + m_pin_scl = state; +} + + + +/*------------------------------------------------- + pcf8593_pin_sda_w +-------------------------------------------------*/ + +WRITE_LINE_MEMBER(pcf8593_device::sda_w) +{ + // clock is high + if (m_pin_scl) + { + // log init I2C + if (state) _logerror( 1, ("pcf8593 init i2c\n")); + // start condition (high to low when clock is high) + if ((!state) && (m_pin_sda)) + { + _logerror( 1, ("pcf8593 start condition\n")); + m_active = TRUE; + m_bits = 0; + m_data_recv_index = 0; + clear_buffer_rx(); + //m_pos = 0; + } + // stop condition (low to high when clock is high) + if ((state) && (!m_pin_sda)) + { + _logerror( 1, ("pcf8593 stop condition\n")); + m_active = FALSE; + } + } + // save sda + m_pin_sda = state; +} + + + +/*------------------------------------------------- + pcf8593_pin_sda_r +-------------------------------------------------*/ + +READ_LINE_MEMBER(pcf8593_device::sda_r) +{ + return m_inp; +} + + + +/*------------------------------------------------- + pcf8593_clear_buffer_rx +-------------------------------------------------*/ + +void pcf8593_device::clear_buffer_rx() +{ + memset(&m_data_recv[0], 0, sizeof( m_data_recv)); + m_data_recv_index = 0; +} diff --git a/src/emu/machine/pcf8593.h b/src/emu/machine/pcf8593.h new file mode 100644 index 00000000000..887a68f219c --- /dev/null +++ b/src/emu/machine/pcf8593.h @@ -0,0 +1,77 @@ +/********************************************************************* + + Philips PCF8593 CMOS clock/calendar circuit + + (c) 2001-2007 Tim Schuerewegen + +*********************************************************************/ + +#ifndef __PCF8593_H__ +#define __PCF8593_H__ + +#include "emu.h" + + +//************************************************************************** +// INTERFACE CONFIGURATION MACROS +//************************************************************************** + +#define MCFG_PCF8593_ADD(_tag) \ + MCFG_DEVICE_ADD(_tag, PCF8593, 0) + +#define MCFG_PCF8593_REMOVE(_tag) \ + MCFG_DEVICE_REMOVE(_tag) + + +// ======================> pcf8593_device + +class pcf8593_device : public device_t, + public device_rtc_interface, + public device_nvram_interface +{ +public: + pcf8593_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + DECLARE_WRITE_LINE_MEMBER(scl_w); + DECLARE_WRITE_LINE_MEMBER(sda_w); + DECLARE_READ_LINE_MEMBER(sda_r); + +protected: + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + + // device_rtc_interface overrides + virtual bool rtc_feature_y2k() { return true; } + virtual void rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second); + + // device_nvram_interface overrides + virtual void nvram_default(); + virtual void nvram_read(emu_file &file); + virtual void nvram_write(emu_file &file); + +private: + void clear_buffer_rx(); + + static const device_timer_id TIMER_UPDATE_COUNTER = 0; + + // internal state + UINT8 m_data[16]; + int m_pin_scl; + int m_pin_sda; + int m_inp; + int m_active; + int m_bits; + UINT8 m_data_recv_index; + UINT8 m_data_recv[50]; + UINT8 m_mode; + UINT8 m_pos; + emu_timer * m_timer; + enum { RTC_MODE_NONE, RTC_MODE_SEND, RTC_MODE_RECV }; +}; + +// device type definition +extern const device_type PCF8593; + +#endif /* __PCF8593_H__ */ diff --git a/src/emu/machine/upd7002.c b/src/emu/machine/upd7002.c new file mode 100644 index 00000000000..84770daa1cf --- /dev/null +++ b/src/emu/machine/upd7002.c @@ -0,0 +1,245 @@ +/****************************************************************************** + uPD7002 Analogue to Digital Converter + + MESS Driver By: + + Gordon Jefferyes + mess_bbc@gjeffery.dircon.co.uk + +******************************************************************************/ + +#include "emu.h" +#include "upd7002.h" + + +struct uPD7002_t +{ + /* Pointer to our interface */ + const uPD7002_interface *intf; + + /* Status Register + D0 and D1 define the currently selected input channel + D2 flag output + D3 0 = 8 bit mode 1 = 12 bit mode + D4 2nd MSB of conversion + D5 MSB of conversion + D6 0 = busy, 1 = not busy (~busy) + D7 0 = conversion completed, 1 = conversion not completed (~EOC) + */ + int status; + + /* High data byte + This byte contains the 8 most significant bits of the analogue to digital conversion. */ + int data1; + + /* Low data byte + In 12 bit mode: Bits 7 to 4 define the four low order bits of the conversion. + In 8 bit mode. All bits 7 to 4 are inaccurate. + Bits 3 to 0 are always set to low. */ + int data0; + + + /* temporary store of the next A to D conversion */ + int digitalvalue; + + /* this counter is used to check a full end of conversion has been reached + if the uPD7002 is half way through one conversion and a new conversion is requested + the counter at the end of the first conversion will not match and not be processed + only then at the end of the second conversion will the conversion complete function run */ + int conversion_counter; +}; + + +/***************************************************************************** + Implementation +*****************************************************************************/ + +INLINE uPD7002_t *get_safe_token(device_t *device) +{ + assert(device != NULL); + assert(device->type() == UPD7002); + + return (uPD7002_t *)downcast<uPD7002_device *>(device)->token(); +} + +READ8_DEVICE_HANDLER ( uPD7002_EOC_r ) +{ + uPD7002_t *uPD7002 = get_safe_token(device); + return (uPD7002->status>>7)&0x01; +} + + +static TIMER_CALLBACK(uPD7002_conversioncomplete) +{ + device_t *device = (device_t *)ptr; + uPD7002_t *uPD7002 = get_safe_token(device); + + int counter_value = param; + if (counter_value==uPD7002->conversion_counter) + { + // this really always does a 12 bit conversion + uPD7002->data1 = uPD7002->digitalvalue>>8; + uPD7002->data0 = uPD7002->digitalvalue&0xf0; + + // set the status register with top 2 MSB, not busy and conversion complete + uPD7002->status = (uPD7002->status & 0x0f)|((uPD7002->data1 & 0xc0)>>2)|0x40; + + // call the EOC function with EOC from status + // uPD7002_EOC_r(0) this has just been set to 0 + if (uPD7002->intf->EOC_func) (uPD7002->intf->EOC_func)(device,0); + uPD7002->conversion_counter=0; + } +} + + +READ8_DEVICE_HANDLER ( uPD7002_r ) +{ + uPD7002_t *uPD7002 = get_safe_token(device); + + switch(offset&0x03) + { + case 0: + return uPD7002->status; + + case 1: + return uPD7002->data1; + + case 2: case 3: + return uPD7002->data0; + } + return 0; +} + + + +WRITE8_DEVICE_HANDLER ( uPD7002_w ) +{ + uPD7002_t *uPD7002 = get_safe_token(device); + /* logerror("write to uPD7002 $%02X = $%02X\n",offset,data); */ + + switch(offset&0x03) + { + case 0: + /* + Data Latch/AD start + D0 and D1 together define which one of the four input channels is selected + D2 flag input, normally set to 0???? + D3 defines whether an 8 (0) or 12 (1) bit resolution conversion should occur + D4 to D7 not used. + + an 8 bit conversion typically takes 4ms + an 12 bit conversion typically takes 10ms + + writing to this register will initiate a conversion. + */ + + /* set D6=0 busy ,D7=1 conversion not complete */ + uPD7002->status=(data & 0x0f) | 0x80; + + // call the EOC function with EOC from status + // uPD7002_EOC_r(0) this has just been set to 1 + if (uPD7002->intf->EOC_func) uPD7002->intf->EOC_func(device, 1); + + /* the uPD7002 works by sampling the analogue value at the start of the conversion + so it is read hear and stored until the end of the A to D conversion */ + + // this function should return a 16 bit value. + uPD7002->digitalvalue = uPD7002->intf->get_analogue_func(device, uPD7002->status & 0x03); + + uPD7002->conversion_counter++; + + // call a timer to start the conversion + if (uPD7002->status & 0x08) + { + // 12 bit conversion takes 10ms + space.machine().scheduler().timer_set(attotime::from_msec(10), FUNC(uPD7002_conversioncomplete), uPD7002->conversion_counter, (void *)device); + } else { + // 8 bit conversion takes 4ms + space.machine().scheduler().timer_set(attotime::from_msec(4), FUNC(uPD7002_conversioncomplete), uPD7002->conversion_counter, (void *)device); + } + break; + + case 1: case 2: + /* Nothing */ + break; + + case 3: + /* Test Mode: Used for inspecting the device, The data input-output terminals assume an input + state and are connected to the A/D counter. Therefore, the A/D conversion data + read out after this is meaningless. + */ + break; + } +} + +/* Device Interface */ + +static DEVICE_START( uPD7002 ) +{ + uPD7002_t *uPD7002 = get_safe_token(device); + // validate arguments + + assert(device != NULL); + assert(device->tag() != NULL); + assert(device->static_config() != NULL); + + uPD7002->intf = (const uPD7002_interface*)device->static_config(); + uPD7002->status = 0; + uPD7002->data1 = 0; + uPD7002->data0 = 0; + uPD7002->digitalvalue = 0; + uPD7002->conversion_counter = 0; + + // register for state saving + state_save_register_item(device->machine(), "uPD7002", device->tag(), 0, uPD7002->status); + state_save_register_item(device->machine(), "uPD7002", device->tag(), 0, uPD7002->data1); + state_save_register_item(device->machine(), "uPD7002", device->tag(), 0, uPD7002->data0); + state_save_register_item(device->machine(), "uPD7002", device->tag(), 0, uPD7002->digitalvalue); + state_save_register_item(device->machine(), "uPD7002", device->tag(), 0, uPD7002->conversion_counter); +} + +static DEVICE_RESET( uPD7002 ) +{ + uPD7002_t *uPD7002 = get_safe_token(device); + uPD7002->status = 0; + uPD7002->data1 = 0; + uPD7002->data0 = 0; + uPD7002->digitalvalue = 0; + uPD7002->conversion_counter = 0; +} + +const device_type UPD7002 = &device_creator<uPD7002_device>; + +uPD7002_device::uPD7002_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, UPD7002, "uPD7002", tag, owner, clock) +{ + m_token = global_alloc_clear(uPD7002_t); +} + +//------------------------------------------------- +// device_config_complete - perform any +// operations now that the configuration is +// complete +//------------------------------------------------- + +void uPD7002_device::device_config_complete() +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void uPD7002_device::device_start() +{ + DEVICE_START_NAME( uPD7002 )(this); +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void uPD7002_device::device_reset() +{ + DEVICE_RESET_NAME( uPD7002 )(this); +} diff --git a/src/emu/machine/upd7002.h b/src/emu/machine/upd7002.h new file mode 100644 index 00000000000..d7456ed0f44 --- /dev/null +++ b/src/emu/machine/upd7002.h @@ -0,0 +1,76 @@ +/***************************************************************************** + * + * machine/upd7002.h + * + * uPD7002 Analogue to Digital Converter + * + * Driver by Gordon Jefferyes <mess_bbc@gjeffery.dircon.co.uk> + * + ****************************************************************************/ + +#ifndef UPD7002_H_ +#define UPD7002_H_ + +/*************************************************************************** + MACROS +***************************************************************************/ + +class uPD7002_device : public device_t +{ +public: + uPD7002_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + ~uPD7002_device() { global_free(m_token); } + + // access to legacy token + void *token() const { assert(m_token != NULL); return m_token; } +protected: + // device-level overrides + virtual void device_config_complete(); + virtual void device_start(); + virtual void device_reset(); +private: + // internal state + void *m_token; +}; + +extern const device_type UPD7002; + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +typedef int (*uPD7002_get_analogue_func)(device_t *device, int channel_number); +#define UPD7002_GET_ANALOGUE(name) int name(device_t *device, int channel_number ) + +typedef void (*uPD7002_eoc_func)(device_t *device, int data); +#define UPD7002_EOC(name) void name(device_t *device, int data ) + + +struct uPD7002_interface +{ + uPD7002_get_analogue_func get_analogue_func; + uPD7002_eoc_func EOC_func; +}; + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +/* Standard handlers */ + +DECLARE_READ8_DEVICE_HANDLER ( uPD7002_EOC_r ); +DECLARE_READ8_DEVICE_HANDLER ( uPD7002_r ); +DECLARE_WRITE8_DEVICE_HANDLER ( uPD7002_w ); + + +/*************************************************************************** + DEVICE CONFIGURATION MACROS +***************************************************************************/ + +#define MCFG_UPD7002_ADD(_tag, _intrf) \ + MCFG_DEVICE_ADD(_tag, UPD7002, 0) \ + MCFG_DEVICE_CONFIG(_intrf) + + +#endif /* UPD7002_H_ */ diff --git a/src/emu/machine/wd11c00_17.c b/src/emu/machine/wd11c00_17.c new file mode 100644 index 00000000000..58e956b311a --- /dev/null +++ b/src/emu/machine/wd11c00_17.c @@ -0,0 +1,496 @@ +/********************************************************************** + + Western Digital WD11C00-17 PC/XT Host Interface Logic Device + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**********************************************************************/ + +#include "machine/wd11c00_17.h" + + + +//************************************************************************** +// MACROS / CONSTANTS +//************************************************************************** + +#define LOG 1 + + +// status register +#define STATUS_IRQ 0x20 +#define STATUS_DRQ 0x10 +#define STATUS_BUSY 0x08 +#define STATUS_C_D 0x04 +#define STATUS_I_O 0x02 +#define STATUS_REQ 0x01 + + +// mask register +#define MASK_IRQ 0x02 +#define MASK_DMA 0x01 + + + +//************************************************************************** +// DEVICE DEFINITIONS +//************************************************************************** + +const device_type WD11C00_17 = &device_creator<wd11c00_17_device>; + + +//------------------------------------------------- +// device_config_complete - perform any +// operations now that the configuration is +// complete +//------------------------------------------------- + +void wd11c00_17_device::device_config_complete() +{ + // inherit a copy of the static data + const wd11c00_17_interface *intf = reinterpret_cast<const wd11c00_17_interface *>(static_config()); + if (intf != NULL) + *static_cast<wd11c00_17_interface *>(this) = *intf; + + // or initialize to defaults if none provided + else + { + memset(&m_out_irq5_cb, 0, sizeof(m_out_irq5_cb)); + memset(&m_out_drq3_cb, 0, sizeof(m_out_drq3_cb)); + memset(&m_out_mr_cb, 0, sizeof(m_out_mr_cb)); + memset(&m_out_busy_cb, 0, sizeof(m_out_busy_cb)); + memset(&m_out_req_cb, 0, sizeof(m_out_req_cb)); + memset(&m_out_ra3_cb, 0, sizeof(m_out_ra3_cb)); + memset(&m_in_rd322_cb, 0, sizeof(m_in_rd322_cb)); + memset(&m_in_ramcs_cb, 0, sizeof(m_in_ramcs_cb)); + memset(&m_out_ramwr_cb, 0, sizeof(m_out_ramwr_cb)); + memset(&m_in_cs1010_cb, 0, sizeof(m_in_cs1010_cb)); + memset(&m_out_cs1010_cb, 0, sizeof(m_out_cs1010_cb)); + } +} + + + +//************************************************************************** +// INLINE HELPERS +//************************************************************************** + +//------------------------------------------------- +// check_interrupt - +//------------------------------------------------- + +inline void wd11c00_17_device::check_interrupt() +{ + if (BIT(m_ra, 10)) + { + m_status &= ~STATUS_DRQ; + } + + int ra3 = BIT(m_ra, 3); + + if (m_ra3 != ra3) + { + m_out_ra3_func(ra3 ? ASSERT_LINE : CLEAR_LINE); + m_ra3 = ra3; + } + + int irq5 = ((m_status & STATUS_IRQ) && (m_mask & MASK_IRQ)) ? ASSERT_LINE : CLEAR_LINE; + + if (m_irq5 != irq5) + { + m_out_irq5_func(irq5); + m_irq5 = irq5; + } + + int drq3 = ((m_status & STATUS_DRQ) && (m_mask & MASK_DMA)) ? ASSERT_LINE : CLEAR_LINE; + + if (m_drq3 != drq3) + { + m_out_drq3_func(drq3); + m_drq3 = drq3; + } + + int busy = (m_status & STATUS_BUSY) ? 0 : 1; + + if (m_busy != busy) + { + m_out_busy_func(busy); + m_busy = busy; + } + + int req = (m_status & STATUS_REQ) ? 1 : 0; + + if (m_req != req) + { + m_out_req_func(req); + m_req = req; + } +} + + +//------------------------------------------------- +// increment_address - +//------------------------------------------------- + +inline void wd11c00_17_device::increment_address() +{ + m_ra++; + check_interrupt(); +} + + +//------------------------------------------------- +// read_data - +//------------------------------------------------- + +inline UINT8 wd11c00_17_device::read_data() +{ + UINT8 data = 0; + + if (m_status & STATUS_BUSY) + { + data = m_in_ramcs_func(m_ra & 0x7ff); + + increment_address(); + } + + return data; +} + + +//------------------------------------------------- +// write_data - +//------------------------------------------------- + +inline void wd11c00_17_device::write_data(UINT8 data) +{ + if (m_status & STATUS_BUSY) + { + m_out_ramwr_func(m_ra & 0x7ff, data); + + increment_address(); + } +} + + +//------------------------------------------------- +// software_reset - +//------------------------------------------------- + +inline void wd11c00_17_device::software_reset() +{ + m_out_mr_func(ASSERT_LINE); + m_out_mr_func(CLEAR_LINE); + + device_reset(); +} + + +//------------------------------------------------- +// select - +//------------------------------------------------- + +inline void wd11c00_17_device::select() +{ + m_status = STATUS_BUSY | STATUS_C_D | STATUS_REQ; + + check_interrupt(); +} + + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// wd11c00_17_device - constructor +//------------------------------------------------- + +wd11c00_17_device::wd11c00_17_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, WD11C00_17, "Western Digital WD11C00-17", tag, owner, clock), + m_status(0), + m_ra(0), + m_irq5(CLEAR_LINE), + m_drq3(CLEAR_LINE), + m_busy(1), + m_req(0), + m_ra3(0) +{ +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void wd11c00_17_device::device_start() +{ + // resolve callbacks + m_out_irq5_func.resolve(m_out_irq5_cb, *this); + m_out_drq3_func.resolve(m_out_drq3_cb, *this); + m_out_mr_func.resolve(m_out_mr_cb, *this); + m_out_busy_func.resolve(m_out_busy_cb, *this); + m_out_req_func.resolve(m_out_req_cb, *this); + m_out_ra3_func.resolve(m_out_ra3_cb, *this); + m_in_rd322_func.resolve(m_in_rd322_cb, *this); + m_in_ramcs_func.resolve(m_in_ramcs_cb, *this); + m_out_ramwr_func.resolve(m_out_ramwr_cb, *this); + m_in_cs1010_func.resolve(m_in_cs1010_cb, *this); + m_out_cs1010_func.resolve(m_out_cs1010_cb, *this); +} + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void wd11c00_17_device::device_reset() +{ + m_status &= ~(STATUS_IRQ | STATUS_DRQ | STATUS_BUSY); + m_mask = 0; + m_ra = 0; + + check_interrupt(); +} + + +//------------------------------------------------- +// io_r - +//------------------------------------------------- + +READ8_MEMBER( wd11c00_17_device::io_r ) +{ + UINT8 data = 0xff; + + switch (offset) + { + case 0: // Read Data, Board to Host + if (LOG) logerror("%s WD11C00-17 '%s' Read Data %03x:", machine().describe_context(), tag(), m_ra); + data = read_data(); + if (LOG) logerror("%02x\n", data); + break; + + case 1: // Read Board Hardware Status + data = m_status; + check_interrupt(); + break; + + case 2: // Read Drive Configuration Information + data = m_in_rd322_func(0); + break; + + case 3: // Not Used + break; + } + + return data; +} + + +//------------------------------------------------- +// io_w - +//------------------------------------------------- + +WRITE8_MEMBER( wd11c00_17_device::io_w ) +{ + switch (offset) + { + case 0: // Write Data, Host to Board + if (LOG) logerror("%s WD11C00-17 '%s' Write Data %03x:%02x\n", machine().describe_context(), tag(), m_ra, data); + write_data(data); + break; + + case 1: // Board Software Reset + if (LOG) logerror("%s WD11C00-17 '%s' Software Reset\n", machine().describe_context(), tag()); + software_reset(); + break; + + case 2: // Board Select + if (LOG) logerror("%s WD11C00-17 '%s' Select\n", machine().describe_context(), tag()); + increment_address(); // HACK + select(); + break; + + case 3: // Set/Reset DMA, IRQ Masks + if (LOG) logerror("%s WD11C00-17 '%s' Mask IRQ %u DMA %u\n", machine().describe_context(), tag(), BIT(data, 1), BIT(data, 0)); + m_mask = data; + check_interrupt(); + break; + } +} + + +//------------------------------------------------- +// dack_r - +//------------------------------------------------- + +UINT8 wd11c00_17_device::dack_r() +{ + return read_data(); +} + + +//------------------------------------------------- +// dack_w - +//------------------------------------------------- + +void wd11c00_17_device::dack_w(UINT8 data) +{ + write_data(data); +} + + +//------------------------------------------------- +// read - +//------------------------------------------------- + +READ8_MEMBER( wd11c00_17_device::read ) +{ + UINT8 data = 0; + + switch (offset) + { + case 0x00: + if (LOG) logerror("%s WD11C00-17 '%s' Read RAM %03x:", machine().describe_context(), tag(), m_ra); + data = read_data(); + if (LOG) logerror("%02x\n", data); + break; + + case 0x20: + data = m_in_cs1010_func(m_ra >> 8); + break; + } + + return data; +} + + +//------------------------------------------------- +// write - +//------------------------------------------------- + +WRITE8_MEMBER( wd11c00_17_device::write ) +{ + switch (offset) + { + case 0x00: + if (LOG) logerror("%s WD11C00-17 '%s' Write RAM %03x:%02x\n", machine().describe_context(), tag(), m_ra, data); + write_data(data); + if (m_ra > 0x400) m_ecc_not_0 = 0; // HACK + break; + + case 0x20: + m_out_cs1010_func(m_ra >> 8, data); + break; + + case 0x60: + m_ra = (data & 0x07) << 8; + if (LOG) logerror("%s WD11C00-17 '%s' RA %03x\n", machine().describe_context(), tag(), m_ra); + check_interrupt(); + break; + } +} + + +//------------------------------------------------- +// ireq_w - +//------------------------------------------------- + +WRITE_LINE_MEMBER( wd11c00_17_device::ireq_w ) +{ + if (LOG) logerror("%s WD11C00-17 '%s' IREQ %u\n", machine().describe_context(), tag(), state); + + if (state) m_status |= STATUS_REQ; else m_status &= ~STATUS_REQ; + + if (m_status & STATUS_BUSY) + { + if (state) + { + m_status |= STATUS_IRQ | STATUS_I_O; + } + else + { + if (m_status & STATUS_I_O) + { + m_status &= ~(STATUS_BUSY | STATUS_I_O); + } + } + } + + check_interrupt(); +} + + +//------------------------------------------------- +// io_w - +//------------------------------------------------- + +WRITE_LINE_MEMBER( wd11c00_17_device::io_w ) +{ + if (LOG) logerror("%s WD11C00-17 '%s' I/O %u\n", machine().describe_context(), tag(), state); + + if (state) m_status |= STATUS_I_O; else m_status &= ~STATUS_I_O; +} + + +//------------------------------------------------- +// cd_w - +//------------------------------------------------- + +WRITE_LINE_MEMBER( wd11c00_17_device::cd_w ) +{ + if (LOG) logerror("%s WD11C00-17 '%s' C/D %u\n", machine().describe_context(), tag(), state); + + if (state) m_status |= STATUS_C_D; else m_status &= ~STATUS_C_D; +} + + +//------------------------------------------------- +// clct_w - +//------------------------------------------------- + +WRITE_LINE_MEMBER( wd11c00_17_device::clct_w ) +{ + if (LOG) logerror("%s WD11C00-17 '%s' CLCT %u\n", machine().describe_context(), tag(), state); + + if (state) + { + m_ra &= 0xff00; + check_interrupt(); + } +} + + +//------------------------------------------------- +// mode_w - +//------------------------------------------------- + +WRITE_LINE_MEMBER( wd11c00_17_device::mode_w ) +{ + if (LOG) logerror("%s WD11C00-17 '%s' MODE %u\n", machine().describe_context(), tag(), state); + + m_mode = state; + m_ecc_not_0 = state; // HACK +} + + +//------------------------------------------------- +// busy_r - +//------------------------------------------------- + +READ_LINE_MEMBER( wd11c00_17_device::busy_r ) +{ + return (m_status & STATUS_BUSY) ? 0 : 1; +} + + +//------------------------------------------------- +// ecc_not_0_r - +//------------------------------------------------- + +READ_LINE_MEMBER( wd11c00_17_device::ecc_not_0_r ) +{ + return m_ecc_not_0; +} diff --git a/src/emu/machine/wd11c00_17.h b/src/emu/machine/wd11c00_17.h new file mode 100644 index 00000000000..cf043aa26cc --- /dev/null +++ b/src/emu/machine/wd11c00_17.h @@ -0,0 +1,128 @@ +/********************************************************************** + + Western Digital WD11C00-17 PC/XT Host Interface Logic Device + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**********************************************************************/ + +#pragma once + +#ifndef __WD11C00_17__ +#define __WD11C00_17__ + + +#include "emu.h" + + + +//************************************************************************** +// INTERFACE CONFIGURATION MACROS +//************************************************************************** + +#define MCFG_WD11C00_17_ADD(_tag, _clock, _config) \ + MCFG_DEVICE_ADD(_tag, WD11C00_17, _clock) \ + MCFG_DEVICE_CONFIG(_config) + + +#define WD11C00_17_INTERFACE(_name) \ + const wd11c00_17_interface (_name) = + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> wd11c00_17_interface + +struct wd11c00_17_interface +{ + devcb_write_line m_out_irq5_cb; + devcb_write_line m_out_drq3_cb; + devcb_write_line m_out_mr_cb; + devcb_write_line m_out_busy_cb; + devcb_write_line m_out_req_cb; + devcb_write_line m_out_ra3_cb; + devcb_read8 m_in_rd322_cb; + devcb_read8 m_in_ramcs_cb; + devcb_write8 m_out_ramwr_cb; + devcb_read8 m_in_cs1010_cb; + devcb_write8 m_out_cs1010_cb; +}; + + +// ======================> wd11c00_17_device + +class wd11c00_17_device : public device_t, + public wd11c00_17_interface +{ +public: + // construction/destruction + wd11c00_17_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + DECLARE_READ8_MEMBER( io_r ); + DECLARE_WRITE8_MEMBER( io_w ); + + UINT8 dack_r(); + void dack_w(UINT8 data); + + DECLARE_READ8_MEMBER( read ); + DECLARE_WRITE8_MEMBER( write ); + + DECLARE_WRITE_LINE_MEMBER( ireq_w ); + DECLARE_WRITE_LINE_MEMBER( io_w ); + DECLARE_WRITE_LINE_MEMBER( cd_w ); + DECLARE_WRITE_LINE_MEMBER( clct_w ); + DECLARE_WRITE_LINE_MEMBER( mode_w ); + + DECLARE_READ_LINE_MEMBER( busy_r ); + DECLARE_READ_LINE_MEMBER( ecc_not_0_r ); + +protected: + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + virtual void device_config_complete(); + +private: + inline void check_interrupt(); + inline void increment_address(); + inline UINT8 read_data(); + inline void write_data(UINT8 data); + inline void software_reset(); + inline void select(); + + devcb_resolved_write_line m_out_irq5_func; + devcb_resolved_write_line m_out_drq3_func; + devcb_resolved_write_line m_out_mr_func; + devcb_resolved_write_line m_out_busy_func; + devcb_resolved_write_line m_out_req_func; + devcb_resolved_write_line m_out_ra3_func; + devcb_resolved_read8 m_in_rd322_func; + devcb_resolved_read8 m_in_ramcs_func; + devcb_resolved_write8 m_out_ramwr_func; + devcb_resolved_read8 m_in_cs1010_func; + devcb_resolved_write8 m_out_cs1010_func; + + UINT8 m_status; + UINT8 m_mask; + + offs_t m_ra; + + int m_mode; + int m_ecc_not_0; + + int m_irq5; + int m_drq3; + int m_busy; + int m_req; + int m_ra3; +}; + + +// device type definition +extern const device_type WD11C00_17; + +#endif diff --git a/src/emu/machine/wd2010.c b/src/emu/machine/wd2010.c new file mode 100644 index 00000000000..2cd62904222 --- /dev/null +++ b/src/emu/machine/wd2010.c @@ -0,0 +1,426 @@ +/********************************************************************** + + Western Digital WD2010 Winchester Disk Controller + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**********************************************************************/ + +#include "machine/wd2010.h" + + + +//************************************************************************** +// MACROS / CONSTANTS +//************************************************************************** + +#define LOG 1 + + +// task file +enum +{ + TASK_FILE_ERROR = 1, + TASK_FILE_WRITE_PRECOMP_CYLINDER = TASK_FILE_ERROR, + TASK_FILE_SECTOR_COUNT, + TASK_FILE_SECTOR_NUMBER, + TASK_FILE_CYLINDER_LOW, + TASK_FILE_CYLINDER_HIGH, + TASK_FILE_SDH_REGISTER, + TASK_FILE_STATUS, + TASK_FILE_COMMAND = TASK_FILE_STATUS +}; + +#define WRITE_PRECOMP_CYLINDER \ + (m_task_file[TASK_FILE_WRITE_PRECOMP_CYLINDER] * 4) + +#define SECTOR_COUNT \ + ((m_task_file[TASK_FILE_SECTOR_COUNT] + 1) * 256) + +#define SECTOR_NUMBER \ + (m_task_file[TASK_FILE_SECTOR_NUMBER]) + +#define CYLINDER \ + (((m_task_file[TASK_FILE_CYLINDER_HIGH] & 0x07) << 8) | m_task_file[TASK_FILE_CYLINDER_LOW]) + +#define HEAD \ + (m_task_file[TASK_FILE_SDH_REGISTER] & 0x07) + +#define DRIVE \ + ((m_task_file[TASK_FILE_SDH_REGISTER] >> 3) & 0x03) + +static const int SECTOR_SIZES[4] = { 256, 512, 1024, 128 }; + +#define SECTOR_SIZE \ + SECTOR_SIZES[(m_task_file[TASK_FILE_SDH_REGISTER] >> 5) & 0x03] + + +// status register +#define STATUS_BSY 0x80 +#define STATUS_RDY 0x40 +#define STATUS_WF 0x20 +#define STATUS_SC 0x10 +#define STATUS_DRQ 0x08 +#define STATUS_DWC 0x04 +#define STATUS_CIP 0x02 +#define STATUS_ERR 0x01 + + +// error register +#define ERROR_BB 0x80 +#define ERROR_CRC_ECC 0x40 +#define ERROR_ID 0x10 +#define ERROR_AC 0x04 +#define ERROR_TK 0x02 +#define ERROR_DM 0x01 + + +// command register +#define COMMAND_MASK 0xf0 +#define COMMAND_RESTORE 0x10 +#define COMMAND_SEEK 0x70 +#define COMMAND_READ_SECTOR 0x20 +#define COMMAND_WRITE_SECTOR 0x30 +#define COMMAND_SCAN_ID 0x40 +#define COMMAND_WRITE_FORMAT 0x50 +#define COMMAND_COMPUTE_CORRECTION 0x08 +#define COMMAND_SET_PARAMETER_MASK 0xfe +#define COMMAND_SET_PARAMETER 0x00 + + + +//************************************************************************** +// DEVICE DEFINITIONS +//************************************************************************** + +const device_type WD2010 = &device_creator<wd2010_device>; + + +//------------------------------------------------- +// device_config_complete - perform any +// operations now that the configuration is +// complete +//------------------------------------------------- + +void wd2010_device::device_config_complete() +{ + // inherit a copy of the static data + const wd2010_interface *intf = reinterpret_cast<const wd2010_interface *>(static_config()); + if (intf != NULL) + *static_cast<wd2010_interface *>(this) = *intf; + + // or initialize to defaults if none provided + else + { + memset(&m_out_intrq_cb, 0, sizeof(m_out_intrq_cb)); + memset(&m_out_bdrq_cb, 0, sizeof(m_out_bdrq_cb)); + memset(&m_out_bcr_cb, 0, sizeof(m_out_bcr_cb)); + memset(&m_in_bcs_cb, 0, sizeof(m_in_bcs_cb)); + memset(&m_out_bcs_cb, 0, sizeof(m_out_bcs_cb)); + memset(&m_out_dirin_cb, 0, sizeof(m_out_dirin_cb)); + memset(&m_out_step_cb, 0, sizeof(m_out_step_cb)); + memset(&m_out_rwc_cb, 0, sizeof(m_out_rwc_cb)); + memset(&m_in_drdy_cb, 0, sizeof(m_in_drdy_cb)); + memset(&m_in_index_cb, 0, sizeof(m_in_index_cb)); + memset(&m_in_wf_cb, 0, sizeof(m_in_wf_cb)); + memset(&m_in_tk000_cb, 0, sizeof(m_in_tk000_cb)); + memset(&m_in_sc_cb, 0, sizeof(m_in_sc_cb)); + } +} + + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// wd2010_device - constructor +//------------------------------------------------- + +wd2010_device::wd2010_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, WD2010, "Western Digital WD2010", tag, owner, clock), + m_status(0), + m_error(0) +{ +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void wd2010_device::device_start() +{ + // resolve callbacks + m_out_intrq_func.resolve(m_out_intrq_cb, *this); + m_out_bdrq_func.resolve(m_out_bdrq_cb, *this); + m_out_bcr_func.resolve(m_out_bcr_cb, *this); + m_in_bcs_func.resolve(m_in_bcs_cb, *this); + m_out_bcs_func.resolve(m_out_bcs_cb, *this); + m_out_dirin_func.resolve(m_out_dirin_cb, *this); + m_out_step_func.resolve(m_out_step_cb, *this); + m_out_rwc_func.resolve(m_out_rwc_cb, *this); + m_in_drdy_func.resolve(m_in_drdy_cb, *this); + m_in_index_func.resolve(m_in_index_cb, *this); + m_in_wf_func.resolve(m_in_wf_cb, *this); + m_in_tk000_func.resolve(m_in_tk000_cb, *this); + m_in_sc_func.resolve(m_in_sc_cb, *this); +} + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void wd2010_device::device_reset() +{ +} + + +//------------------------------------------------- +// read - +//------------------------------------------------- + +READ8_MEMBER( wd2010_device::read ) +{ + UINT8 data = 0; + + switch (offset) + { + case TASK_FILE_ERROR: + data = m_error; + break; + + case TASK_FILE_STATUS: + m_out_intrq_func(CLEAR_LINE); + data = m_status | STATUS_RDY | STATUS_SC; + break; + + default: + data = m_task_file[offset]; + break; + } + + return data; +} + + +//------------------------------------------------- +// write - +//------------------------------------------------- + +WRITE8_MEMBER( wd2010_device::write ) +{ + m_task_file[offset] = data; + + switch (offset) + { + case TASK_FILE_WRITE_PRECOMP_CYLINDER: + if (LOG) logerror("%s WD2010 '%s' Write Precomp Cylinder: %u\n", machine().describe_context(), tag(), WRITE_PRECOMP_CYLINDER); + break; + + case TASK_FILE_SECTOR_COUNT: + if (LOG) logerror("%s WD2010 '%s' Sector Count: %u\n", machine().describe_context(), tag(), SECTOR_COUNT); + break; + + case TASK_FILE_SECTOR_NUMBER: + if (LOG) logerror("%s WD2010 '%s' Sector Number: %u\n", machine().describe_context(), tag(), SECTOR_NUMBER); + break; + + case TASK_FILE_CYLINDER_LOW: + if (LOG) logerror("%s WD2010 '%s' Cylinder Low: %u\n", machine().describe_context(), tag(), CYLINDER); + break; + + case TASK_FILE_CYLINDER_HIGH: + if (LOG) logerror("%s WD2010 '%s' Cylinder Low: %u\n", machine().describe_context(), tag(), CYLINDER); + break; + + case TASK_FILE_SDH_REGISTER: + if (LOG) + { + logerror("%s WD2010 '%s' Head: %u\n", machine().describe_context(), tag(), HEAD); + logerror("%s WD2010 '%s' Drive: %u\n", machine().describe_context(), tag(), DRIVE); + logerror("%s WD2010 '%s' Sector Size: %u\n", machine().describe_context(), tag(), SECTOR_SIZE); + } + break; + + case TASK_FILE_COMMAND: + if (data == COMMAND_COMPUTE_CORRECTION) + { + if (LOG) logerror("%s WD2010 '%s' COMPUTE CORRECTION\n", machine().describe_context(), tag()); + compute_correction(data); + } + else if ((data & COMMAND_SET_PARAMETER_MASK) == COMMAND_SET_PARAMETER) + { + if (LOG) logerror("%s WD2010 '%s' SET PARAMETER\n", machine().describe_context(), tag()); + set_parameter(data); + } + else + { + switch (data & COMMAND_MASK) + { + case COMMAND_RESTORE: + if (LOG) logerror("%s WD2010 '%s' RESTORE\n", machine().describe_context(), tag()); + restore(data); + break; + + case COMMAND_SEEK: + if (LOG) logerror("%s WD2010 '%s' SEEK\n", machine().describe_context(), tag()); + seek(data); + break; + + case COMMAND_READ_SECTOR: + if (LOG) logerror("%s WD2010 '%s' READ SECTOR\n", machine().describe_context(), tag()); + read_sector(data); + break; + + case COMMAND_WRITE_SECTOR: + if (LOG) logerror("%s WD2010 '%s' WRITE SECTOR\n", machine().describe_context(), tag()); + write_sector(data); + break; + + case COMMAND_SCAN_ID: + if (LOG) logerror("%s WD2010 '%s' SCAN ID\n", machine().describe_context(), tag()); + scan_id(data); + break; + + case COMMAND_WRITE_FORMAT: + if (LOG) logerror("%s WD2010 '%s' WRITE FORMAT\n", machine().describe_context(), tag()); + format(data); + break; + } + } + break; + } +} + + +//------------------------------------------------- +// compute_correction - +//------------------------------------------------- + +void wd2010_device::compute_correction(UINT8 data) +{ +} + + +//------------------------------------------------- +// set_parameter - +//------------------------------------------------- + +void wd2010_device::set_parameter(UINT8 data) +{ +} + + +//------------------------------------------------- +// restore - +//------------------------------------------------- + +void wd2010_device::restore(UINT8 data) +{ + // reset INTRQ, errors, set BUSY, CIP + m_out_intrq_func(CLEAR_LINE); + m_error = 0; + m_status = STATUS_BSY | STATUS_CIP; + + // reset RWC, set direction=OUT, store step rate + m_out_rwc_func(0); + m_out_dirin_func(0); + + int step_pulses = 0; + + while (step_pulses < 2048) + { + while (!m_in_sc_func()) + { + // drive not ready or write fault? + if (!m_in_drdy_func() || m_in_wf_func()) + { + // pulse BCR, set AC, INTRQ, reset BSY, CIP + m_out_bcr_func(0); + m_out_bcr_func(1); + m_error = ERROR_AC; + m_status = (m_in_drdy_func() << 6) | (m_in_wf_func() << 5) | STATUS_ERR; + m_out_intrq_func(ASSERT_LINE); + return; + } + } + + if (m_in_tk000_func()) + { + // pulse BCR, set INTRQ, reset BSY, CIP + m_out_bcr_func(0); + m_out_bcr_func(1); + m_status &= ~(STATUS_BSY | STATUS_CIP); + m_out_intrq_func(ASSERT_LINE); + return; + } + + if (step_pulses == 2047) + { + // set TK000 error + m_error = ERROR_TK; + m_status |= STATUS_ERR; + + // pulse BCR, set INTRQ, reset BSY, CIP + m_out_bcr_func(0); + m_out_bcr_func(1); + m_status &= ~(STATUS_BSY | STATUS_CIP); + m_out_intrq_func(ASSERT_LINE); + return; + } + + // issue a step pulse + m_out_step_func(1); + m_out_step_func(0); + step_pulses++; + } +} + + +//------------------------------------------------- +// seek - +//------------------------------------------------- + +void wd2010_device::seek(UINT8 data) +{ +} + + +//------------------------------------------------- +// read_sector - +//------------------------------------------------- + +void wd2010_device::read_sector(UINT8 data) +{ +} + + +//------------------------------------------------- +// write_sector - +//------------------------------------------------- + +void wd2010_device::write_sector(UINT8 data) +{ +} + + +//------------------------------------------------- +// scan_id - +//------------------------------------------------- + +void wd2010_device::scan_id(UINT8 data) +{ +} + + +//------------------------------------------------- +// format - +//------------------------------------------------- + +void wd2010_device::format(UINT8 data) +{ +} diff --git a/src/emu/machine/wd2010.h b/src/emu/machine/wd2010.h new file mode 100644 index 00000000000..d6456384712 --- /dev/null +++ b/src/emu/machine/wd2010.h @@ -0,0 +1,109 @@ +/********************************************************************** + + Western Digital WD2010 Winchester Disk Controller + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**********************************************************************/ + +#pragma once + +#ifndef __WD2010__ +#define __WD2010__ + + +#include "emu.h" + + + +//************************************************************************** +// INTERFACE CONFIGURATION MACROS +//************************************************************************** + +#define MCFG_WD2010_ADD(_tag, _clock, _config) \ + MCFG_DEVICE_ADD(_tag, WD2010, _clock) \ + MCFG_DEVICE_CONFIG(_config) + + +#define WD2010_INTERFACE(_name) \ + const wd2010_interface (_name) = + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> wd2010_interface + +struct wd2010_interface +{ + devcb_write_line m_out_intrq_cb; + devcb_write_line m_out_bdrq_cb; + devcb_write_line m_out_bcr_cb; + devcb_read8 m_in_bcs_cb; + devcb_write8 m_out_bcs_cb; + devcb_write_line m_out_dirin_cb; + devcb_write_line m_out_step_cb; + devcb_write_line m_out_rwc_cb; + devcb_read_line m_in_drdy_cb; + devcb_read_line m_in_index_cb; + devcb_read_line m_in_wf_cb; + devcb_read_line m_in_tk000_cb; + devcb_read_line m_in_sc_cb; +}; + + +// ======================> wd2010_device + +class wd2010_device : public device_t, + public wd2010_interface +{ +public: + // construction/destruction + wd2010_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + DECLARE_READ8_MEMBER( read ); + DECLARE_WRITE8_MEMBER( write ); + +protected: + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + virtual void device_config_complete(); + +private: + void compute_correction(UINT8 data); + void set_parameter(UINT8 data); + void restore(UINT8 data); + void seek(UINT8 data); + void read_sector(UINT8 data); + void write_sector(UINT8 data); + void scan_id(UINT8 data); + void format(UINT8 data); + + devcb_resolved_write_line m_out_intrq_func; + devcb_resolved_write_line m_out_bdrq_func; + devcb_resolved_write_line m_out_bcr_func; + devcb_resolved_read8 m_in_bcs_func; + devcb_resolved_write8 m_out_bcs_func; + devcb_resolved_write_line m_out_dirin_func; + devcb_resolved_write_line m_out_step_func; + devcb_resolved_write_line m_out_rwc_func; + devcb_resolved_read_line m_in_drdy_func; + devcb_resolved_read_line m_in_index_func; + devcb_resolved_read_line m_in_wf_func; + devcb_resolved_read_line m_in_tk000_func; + devcb_resolved_read_line m_in_sc_func; + + UINT8 m_status; + UINT8 m_error; + UINT8 m_task_file[8]; +}; + + +// device type definition +extern const device_type WD2010; + +#endif |