summaryrefslogtreecommitdiffstatshomepage
path: root/src/mess/machine/cuda.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mess/machine/cuda.c')
-rw-r--r--src/mess/machine/cuda.c563
1 files changed, 563 insertions, 0 deletions
diff --git a/src/mess/machine/cuda.c b/src/mess/machine/cuda.c
new file mode 100644
index 00000000000..547e90e1eb1
--- /dev/null
+++ b/src/mess/machine/cuda.c
@@ -0,0 +1,563 @@
+/*
+ Apple "Cuda" ADB/system controller MCU
+ Emulation by R. Belmont
+
+ Port definitions, primarily from the schematics.
+
+ Port A:
+
+ x------- O ADB data line out
+ -x------ I ADB data line in
+ --x----- I "1" for passive or soft power, "off sense" for secure
+ ---x---- O DFAC latch
+ ----x--- O "Fast Reset"
+ -----x-- I Keyboard power switch
+ ------x- I 1 for secure or passive power, chassis switch for soft
+ -------x ? 1 for passive, power supply control on secure/soft power, input on secure/soft also?
+
+ Port B:
+
+ x------- O DFAC bit clock
+ -x------ B DFAC data I/O (used in both directions)
+ --x----- B VIA shift register data (used in both directions)
+ ---x---- O VIA clock
+ ----x--- I VIA TIP
+ -----x-- I VIA BYTEACK
+ ------x- O VIA TREQ
+ -------x I +5v sense
+
+ Port C:
+ x--- O 680x0 reset
+ -x-- ? 680x0 IPL 2 (used in both directions)
+ --x- ? IPL 1/pull up for passive power, trickle sense for soft and secure
+ ---x ? IPL 0 for passive power, pull-up for soft power, file server switch for secure power
+*/
+
+
+//#define CUDA_SUPER_VERBOSE
+
+#include "emu.h"
+#include "cuda.h"
+#include "cpu/m6805/m6805.h"
+#include "machine/6522via.h"
+#include "sound/asc.h"
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define CUDA_CPU_TAG "cuda"
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+const device_type CUDA = &device_creator<cuda_device>;
+
+ROM_START( cuda )
+ ROM_REGION(0x4400, CUDA_CPU_TAG, 0)
+ ROM_LOAD( "341s0060.bin", 0x1100, 0x1100, CRC(0f5e7b4a) SHA1(972b3778146d9787b18c3a9874d505cf606b3e15) )
+ ROM_LOAD( "341s0788.bin", 0x2200, 0x1100, CRC(df6e1b43) SHA1(ec23cc6214c472d61b98964928c40589517a3172) )
+ ROM_LOAD( "341s0417.bin", 0x3300, 0x1100, CRC(571f24c9) SHA1(a2ae12492389a00e5f4b1ef19b267d6f3a8eadc3) )
+ROM_END
+
+//-------------------------------------------------
+// ADDRESS_MAP
+//-------------------------------------------------
+
+static ADDRESS_MAP_START( cuda_map, AS_PROGRAM, 8, cuda_device )
+ AM_RANGE(0x0000, 0x0002) AM_READWRITE(ports_r, ports_w)
+ AM_RANGE(0x0004, 0x0006) AM_READWRITE(ddr_r, ddr_w)
+ AM_RANGE(0x0007, 0x0007) AM_READWRITE(pll_r, pll_w)
+ AM_RANGE(0x0008, 0x0008) AM_READWRITE(timer_ctrl_r, timer_ctrl_w)
+ AM_RANGE(0x0009, 0x0009) AM_READWRITE(timer_counter_r, timer_counter_w)
+ AM_RANGE(0x0012, 0x0012) AM_READWRITE(onesec_r, onesec_w)
+ AM_RANGE(0x0090, 0x00ff) AM_RAM // work RAM and stack
+ AM_RANGE(0x0100, 0x01ff) AM_READWRITE(pram_r, pram_w)
+ AM_RANGE(0x0f00, 0x1fff) AM_ROM AM_REGION(CUDA_CPU_TAG, 0)
+ADDRESS_MAP_END
+
+//-------------------------------------------------
+// MACHINE_CONFIG
+//-------------------------------------------------
+
+static MACHINE_CONFIG_FRAGMENT( cuda )
+ MCFG_CPU_ADD(CUDA_CPU_TAG, M68HC05EG, XTAL_32_768kHz*192) // 32.768 kHz input clock, can be PLL'ed to x128 = 4.1 MHz under s/w control
+ MCFG_CPU_PROGRAM_MAP(cuda_map)
+MACHINE_CONFIG_END
+
+
+//-------------------------------------------------
+// machine_config_additions - device-specific
+// machine configurations
+//-------------------------------------------------
+
+machine_config_constructor cuda_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( cuda );
+}
+
+const rom_entry *cuda_device::device_rom_region() const
+{
+ return ROM_NAME( cuda );
+}
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+void cuda_device::send_port(address_space &space, UINT8 offset, UINT8 data)
+{
+// printf("PORT %c write %02x (DDR = %02x) (PC=%x)\n", 'A' + offset, data, ddrs[offset], cpu_get_pc(m_maincpu));
+
+ switch (offset)
+ {
+ case 0: // port A
+/* printf("PORT A ADB:%d DFAC:%d PFW:%d\n",
+ (data & 0x80) ? 1 : 0,
+ (data & 0x10) ? 1 : 0,
+ (data & 0x01) ? 1 : 0);*/
+
+ if ((data & 0x80) != last_adb)
+ {
+/* if (data & 0x80)
+ {
+ printf("CU ADB: 1->0 time %lld\n", m_maincpu->total_cycles()-last_adb_time);
+ }
+ else
+ {
+ printf("CU ADB: 0->1 time %lld\n", m_maincpu->total_cycles()-last_adb_time);
+ } */
+ last_adb = data & 0x80;
+ last_adb_time = m_maincpu->total_cycles();
+ }
+ break;
+
+ case 1: // port B
+ {
+ if (treq != ((data>>1)&1))
+ {
+ #ifdef CUDA_SUPER_VERBOSE
+ printf("CU-> TREQ: %d (PC=%x)\n", (data>>1)&1, cpu_get_pc(m_maincpu));
+ #endif
+ treq = (data>>1) & 1;
+ }
+ if (via_data != ((data>>5)&1))
+ {
+ #ifdef CUDA_SUPER_VERBOSE
+ printf("CU-> VIA_DATA: %d (PC=%x)\n", (data>>5)&1, cpu_get_pc(m_maincpu));
+ #endif
+ via_data = (data>>5) & 1;
+ }
+ if (via_clock != ((data>>4)&1))
+ {
+ #ifdef CUDA_SUPER_VERBOSE
+ printf("CU-> VIA_CLOCK: %d (PC=%x)\n", ((data>>4)&1)^1, cpu_get_pc(m_maincpu));
+ #endif
+ via_clock = (data>>4) & 1;
+ via6522_device *via1 = machine().device<via6522_device>("via6522_0");
+ via1->write_cb1(via_clock);
+ }
+ }
+ break;
+
+ case 2: // port C
+ if ((data & 8) != reset_line)
+ {
+ #ifdef CUDA_SUPER_VERBOSE
+ printf("680x0 reset: %d -> %d (PC=%x)\n", (ports[2] & 8)>>3, (data & 8)>>3, cpu_get_pc(m_maincpu));
+ #endif
+ reset_line = (data & 8);
+ // falling edge, should reset the machine too
+ if ((ports[2] & 8) && !(data&8))
+ {
+ m_out_reset_func(ASSERT_LINE);
+ m_out_reset_func(CLEAR_LINE);
+
+ // if PRAM's waiting to be loaded, transfer it now
+ if (!pram_loaded)
+ {
+// memcpy(pram, disk_pram, 0x100);
+ pram_loaded = true;
+ }
+ }
+ }
+ break;
+ }
+}
+
+READ8_MEMBER( cuda_device::ddr_r )
+{
+ return ddrs[offset];
+}
+
+WRITE8_MEMBER( cuda_device::ddr_w )
+{
+// printf("%02x to PORT %c DDR (PC=%x)\n", data, 'A' + offset, cpu_get_pc(m_maincpu));
+
+ send_port(space, offset, ports[offset] & data);
+
+ ddrs[offset] = data;
+}
+
+READ8_MEMBER( cuda_device::ports_r )
+{
+ UINT8 incoming = 0;
+
+ switch (offset)
+ {
+ case 0: // port A
+// incoming |= adb_in ? 0x40 : 0;
+
+ if (cuda_controls_power)
+ {
+ incoming = 0x20; // pull up + chassis switch (which is 0 = on)
+ }
+ else
+ {
+ incoming = 0x02 | 0x01; // pull-up + PFW
+ }
+ break;
+
+ case 1: // port B
+// printf("Read: byteack %d tip %d via_data %d\n", byteack, tip, via_data);
+ incoming |= 0x01; // +5v sense
+ incoming |= byteack<<2;
+ incoming |= tip<<3;
+ incoming |= via_data<<5;
+ incoming |= 0xc0; // show DFAC lines high
+ break;
+
+ case 2: // port C
+ if (cuda_controls_power)
+ {
+ incoming = 0x02 | 0x01; // soft power: trickle sense + pull-up
+ }
+ else
+ {
+ incoming = 0x02 | 0x01; // secure power: pull-up + file server
+ }
+ break;
+ }
+
+ // apply data direction registers
+ incoming &= (ddrs[offset] ^ 0xff);
+ // add in ddr-masked version of port writes
+ incoming |= (ports[offset] & ddrs[offset]);
+
+ // HACK: don't know how this works on h/w...
+ if (!offset)
+ {
+ incoming |= 0x01;
+ }
+
+// printf("PORT %c read = %02x (DDR = %02x latch = %02x) (PC=%x)\n", 'A' + offset, ports[offset], ddrs[offset], ports[offset], cpu_get_pc(m_maincpu));
+
+ return incoming;
+}
+
+WRITE8_MEMBER( cuda_device::ports_w )
+{
+ send_port(space, offset, data);
+
+ ports[offset] = data;
+}
+
+READ8_MEMBER( cuda_device::pll_r )
+{
+ return pll_ctrl;
+}
+
+WRITE8_MEMBER( cuda_device::pll_w )
+{
+ #ifdef CUDA_SUPER_VERBOSE
+ if (pll_ctrl != data)
+ {
+ static const int clocks[4] = { 524288, 1048576, 2097152, 4194304 };
+ printf("PLL ctrl: clock %d TCS:%d BCS:%d AUTO:%d BWC:%d PLLON:%d (PC=%x)\n", clocks[data&3],
+ (data & 0x80) ? 1 : 0,
+ (data & 0x40) ? 1 : 0,
+ (data & 0x20) ? 1 : 0,
+ (data & 0x10) ? 1 : 0,
+ (data & 0x08) ? 1 : 0, cpu_get_pc(m_maincpu));
+ }
+ #endif
+ pll_ctrl = data;
+}
+
+READ8_MEMBER( cuda_device::timer_ctrl_r )
+{
+ return timer_ctrl;
+}
+
+WRITE8_MEMBER( cuda_device::timer_ctrl_w )
+{
+ static const attotime rates[4][5] =
+ {
+ { attotime::from_seconds(1), attotime::from_msec(31.3f), attotime::from_msec(15.6f), attotime::from_msec(7.8f), attotime::from_msec(3.9f) },
+ { attotime::from_seconds(2), attotime::from_msec(62.5f), attotime::from_msec(31.3f), attotime::from_msec(15.6f), attotime::from_msec(7.8f) },
+ { attotime::from_seconds(4), attotime::from_msec(125.0f), attotime::from_msec(62.5f), attotime::from_msec(31.3f), attotime::from_msec(15.6f) },
+ { attotime::from_seconds(8), attotime::from_msec(250.0f), attotime::from_msec(125.1f), attotime::from_msec(62.5f), attotime::from_msec(31.3f) },
+ };
+
+// printf("%02x to timer control (PC=%x)\n", data, cpu_get_pc(m_maincpu));
+
+ if (data & 0x50)
+ {
+ m_prog_timer->adjust(rates[data & 3][(pll_ctrl&3)+1], 1, rates[data&3][(pll_ctrl&3)+1]);
+ ripple_counter = timer_counter;
+ }
+ else
+ {
+ m_prog_timer->adjust(attotime::never);
+ }
+
+ if ((timer_ctrl & 0x80) && !(data & 0x80))
+ {
+ device_set_input_line(m_maincpu, M68HC05EG_INT_TIMER, CLEAR_LINE);
+ timer_ctrl &= ~0x80;
+ }
+ else if ((timer_ctrl & 0x40) && !(data & 0x40))
+ {
+ device_set_input_line(m_maincpu, M68HC05EG_INT_TIMER, CLEAR_LINE);
+ timer_ctrl &= ~0x40;
+ }
+
+ timer_ctrl &= 0xc0;
+ timer_ctrl |= (data & ~0xc0);
+}
+
+READ8_MEMBER( cuda_device::timer_counter_r )
+{
+ return timer_counter;
+}
+
+WRITE8_MEMBER( cuda_device::timer_counter_w )
+{
+// printf("%02x to timer counter (PC=%x)\n", data, cpu_get_pc(m_maincpu));
+ timer_counter = data;
+ ripple_counter = timer_counter;
+}
+
+READ8_MEMBER( cuda_device::onesec_r )
+{
+ return onesec;
+}
+
+WRITE8_MEMBER( cuda_device::onesec_w )
+{
+ m_timer->adjust(attotime::from_seconds(1), 0, attotime::from_seconds(1));
+
+ if ((onesec & 0x40) && !(data & 0x40))
+ {
+ device_set_input_line(m_maincpu, M68HC05EG_INT_CPI, CLEAR_LINE);
+ }
+
+ onesec = data;
+}
+
+READ8_MEMBER( cuda_device::pram_r )
+{
+ return pram[offset];
+}
+
+WRITE8_MEMBER( cuda_device::pram_w )
+{
+ pram[offset] = data;
+}
+
+//-------------------------------------------------
+// cuda_device - constructor
+//-------------------------------------------------
+
+cuda_device::cuda_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, CUDA, "Apple Cuda", tag, owner, clock),
+ device_nvram_interface(mconfig, *this),
+ m_maincpu(*this, CUDA_CPU_TAG)
+{
+}
+
+//-------------------------------------------------
+// static_set_type - configuration helper to set
+// the chip type
+//-------------------------------------------------
+
+void cuda_device::static_set_type(device_t &device, int type)
+{
+ cuda_device &cuda = downcast<cuda_device &>(device);
+ cuda.rom_offset = type;
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void cuda_device::device_start()
+{
+ m_out_reset_func.resolve(m_out_reset_cb, *this);
+
+ m_timer = timer_alloc(0, NULL);
+ m_prog_timer = timer_alloc(1, NULL);
+ save_item(NAME(ddrs[0]));
+ save_item(NAME(ddrs[1]));
+ save_item(NAME(ddrs[2]));
+ save_item(NAME(ports[0]));
+ save_item(NAME(ports[1]));
+ save_item(NAME(ports[2]));
+ save_item(NAME(pll_ctrl));
+ save_item(NAME(timer_ctrl));
+ save_item(NAME(timer_counter));
+ save_item(NAME(ripple_counter));
+ save_item(NAME(onesec));
+ save_item(NAME(treq));
+ save_item(NAME(byteack));
+ save_item(NAME(tip));
+ save_item(NAME(via_data));
+ save_item(NAME(via_clock));
+ save_item(NAME(adb_in));
+ save_item(NAME(reset_line));
+ save_item(NAME(pram_loaded));
+ save_item(NAME(pram));
+ save_item(NAME(disk_pram));
+
+ astring tempstring;
+ UINT8 *rom = device().machine().root_device().memregion(device().subtag(tempstring, CUDA_CPU_TAG))->base();
+
+ if (rom)
+ {
+ memcpy(rom, rom+rom_offset, 0x1100);
+ }
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void cuda_device::device_reset()
+{
+ ddrs[0] = ddrs[1] = ddrs[2] = 0;
+ ports[0] = ports[1] = ports[2] = 0;
+
+ m_timer->adjust(attotime::never);
+ m_prog_timer->adjust(attotime::never);
+
+ cuda_controls_power = false; // set to hard power control
+ adb_in = true; // line is pulled up to +5v, so nothing plugged in would read as "1"
+ reset_line = 0;
+ tip = 0;
+ treq = 0;
+ byteack = 0;
+ via_data = 0;
+ via_clock = 0;
+ pll_ctrl = 0;
+ timer_ctrl = 0;
+ timer_counter = 32;
+ last_adb_time = m_maincpu->total_cycles();
+}
+
+void cuda_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ if (id == 0)
+ {
+ onesec |= 0x40;
+
+ if (onesec & 0x10)
+ {
+ device_set_input_line(m_maincpu, M68HC05EG_INT_CPI, ASSERT_LINE);
+ }
+ }
+ else
+ {
+ timer_ctrl |= 0x80;
+
+ if (timer_ctrl & 0x20)
+ {
+ device_set_input_line(m_maincpu, M68HC05EG_INT_TIMER, ASSERT_LINE);
+ }
+
+ ripple_counter--;
+ if (ripple_counter <= 0)
+ {
+ timer_ctrl |= 0x40;
+
+ ripple_counter = timer_counter;
+
+ if (timer_ctrl & 0x10)
+ {
+ device_set_input_line(m_maincpu, M68HC05EG_INT_TIMER, ASSERT_LINE);
+ }
+ }
+ }
+}
+
+void cuda_device::device_config_complete()
+{
+ m_shortname = "cuda";
+
+ // inherit a copy of the static data
+ const cuda_interface *intf = reinterpret_cast<const cuda_interface *>(static_config());
+ if (intf != NULL)
+ {
+ *static_cast<cuda_interface *>(this) = *intf;
+ }
+ // or initialize to defaults if none provided
+ else
+ {
+ memset(&m_out_reset_cb, 0, sizeof(m_out_reset_cb));
+ }
+}
+
+// the 6805 program clears PRAM on startup (on h/w it's always running once a battery is inserted)
+// we deal with that by loading pram from disk to a secondary buffer and then slapping it into "live"
+// once the cuda reboots the 68k
+void cuda_device::nvram_default()
+{
+ memset(pram, 0, 0x100);
+ memset(disk_pram, 0, 0x100);
+
+ pram[0x1] = 0x10;
+ pram[0x2] = 0x4f;
+ pram[0x3] = 0x48;
+ pram[0x8] = 0x13;
+ pram[0x9] = 0x88;
+ pram[0xb] = 0xcc;
+ pram[0xc] = 0x4e;
+ pram[0xd] = 0x75;
+ pram[0xe] = 0x4d;
+ pram[0xf] = 0x63;
+ pram[0x10] = 0xa8;
+ pram[0x14] = 0xcc;
+ pram[0x15] = 0x0a;
+ pram[0x16] = 0xcc;
+ pram[0x17] = 0x0a;
+ pram[0x1d] = 0x02;
+ pram[0x1e] = 0x63;
+ pram[0x4a] = 0x90;
+ pram[0x4b] = 0xc7;
+ pram[0x57] = 0x29;
+ pram[0x58] = 0x80;
+ pram[0x59] = 0x68;
+ pram[0x5a] = 0x80;
+ pram[0x5b] = 0x80;
+ pram[0x6f] = 0x28;
+ pram[0x77] = 0x01;
+ pram[0x78] = 0xff;
+ pram[0x79] = 0xff;
+ pram[0x7a] = 0xff;
+ pram[0x7b] = 0xdf;
+ pram[0xb8] = 0x35;
+ pram[0xb9] = 0x80;
+ pram_loaded = false;
+}
+
+void cuda_device::nvram_read(emu_file &file)
+{
+ file.read(disk_pram, 0x100);
+ pram_loaded = false;
+}
+
+void cuda_device::nvram_write(emu_file &file)
+{
+ file.write(pram, 0x100);
+}