summaryrefslogtreecommitdiffstatshomepage
path: root/src/mess/machine/intv.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mess/machine/intv.c')
-rw-r--r--src/mess/machine/intv.c853
1 files changed, 853 insertions, 0 deletions
diff --git a/src/mess/machine/intv.c b/src/mess/machine/intv.c
new file mode 100644
index 00000000000..27dc0372d85
--- /dev/null
+++ b/src/mess/machine/intv.c
@@ -0,0 +1,853 @@
+#include "emu.h"
+#include "video/stic.h"
+#include "includes/intv.h"
+#include "cpu/cp1610/cp1610.h"
+#include "hashfile.h"
+
+#define INTELLIVOICE_MASK 0x02
+#define ECS_MASK 0x01
+
+
+
+WRITE16_MEMBER( intv_state::intvkbd_dualport16_w )
+{
+ unsigned char *RAM;
+
+ COMBINE_DATA(&m_intvkbd_dualport_ram[offset]);
+
+ /* copy the LSB over to the 6502 OP RAM, in case they are opcodes */
+ RAM = memregion("keyboard")->base();
+ RAM[offset] = (UINT8) (data >> 0);
+}
+
+READ8_MEMBER( intv_state::intvkbd_dualport8_lsb_r )
+{
+ return (UINT8) (m_intvkbd_dualport_ram[offset] >> 0);
+}
+
+WRITE8_MEMBER( intv_state::intvkbd_dualport8_lsb_w )
+{
+ unsigned char *RAM;
+
+ m_intvkbd_dualport_ram[offset] &= ~0x00FF;
+ m_intvkbd_dualport_ram[offset] |= ((UINT16) data) << 0;
+
+ /* copy over to the 6502 OP RAM, in case they are opcodes */
+ RAM = memregion("keyboard")->base();
+ RAM[offset] = data;
+}
+
+
+
+READ8_MEMBER( intv_state::intvkbd_dualport8_msb_r )
+{
+ unsigned char rv;
+
+ if (offset < 0x100)
+ {
+ switch (offset)
+ {
+ case 0x000:
+ rv = ioport("TEST")->read() & 0x80;
+ logerror("TAPE: Read %02x from 0x40%02x - XOR Data?\n",rv,offset);
+ break;
+ case 0x001:
+ rv = (ioport("TEST")->read() & 0x40) << 1;
+ logerror("TAPE: Read %02x from 0x40%02x - Sense 1?\n",rv,offset);
+ break;
+ case 0x002:
+ rv = (ioport("TEST")->read() & 0x20) << 2;
+ logerror("TAPE: Read %02x from 0x40%02x - Sense 2?\n",rv,offset);
+ break;
+ case 0x003:
+ rv = (ioport("TEST")->read() & 0x10) << 3;
+ logerror("TAPE: Read %02x from 0x40%02x - Tape Present\n",rv,offset);
+ break;
+ case 0x004:
+ rv = (ioport("TEST")->read() & 0x08) << 4;
+ logerror("TAPE: Read %02x from 0x40%02x - Comp (339/1)\n",rv,offset);
+ break;
+ case 0x005:
+ rv = (ioport("TEST")->read() & 0x04) << 5;
+ logerror("TAPE: Read %02x from 0x40%02x - Clocked Comp (339/13)\n",rv,offset);
+ break;
+ case 0x006:
+ if (m_sr1_int_pending)
+ rv = 0x00;
+ else
+ rv = 0x80;
+ logerror("TAPE: Read %02x from 0x40%02x - SR1 Int Pending\n",rv,offset);
+ break;
+ case 0x007:
+ if (m_tape_int_pending)
+ rv = 0x00;
+ else
+ rv = 0x80;
+ logerror("TAPE: Read %02x from 0x40%02x - Tape? Int Pending\n",rv,offset);
+ break;
+ case 0x060: /* Keyboard Read */
+ rv = 0xff;
+ if (m_intvkbd_keyboard_col == 0)
+ rv = ioport("ROW0")->read();
+ if (m_intvkbd_keyboard_col == 1)
+ rv = ioport("ROW1")->read();
+ if (m_intvkbd_keyboard_col == 2)
+ rv = ioport("ROW2")->read();
+ if (m_intvkbd_keyboard_col == 3)
+ rv = ioport("ROW3")->read();
+ if (m_intvkbd_keyboard_col == 4)
+ rv = ioport("ROW4")->read();
+ if (m_intvkbd_keyboard_col == 5)
+ rv = ioport("ROW5")->read();
+ if (m_intvkbd_keyboard_col == 6)
+ rv = ioport("ROW6")->read();
+ if (m_intvkbd_keyboard_col == 7)
+ rv = ioport("ROW7")->read();
+ if (m_intvkbd_keyboard_col == 8)
+ rv = ioport("ROW8")->read();
+ if (m_intvkbd_keyboard_col == 9)
+ rv = ioport("ROW9")->read();
+ break;
+ case 0x80:
+ rv = 0x00;
+ logerror("TAPE: Read %02x from 0x40%02x, clear tape int pending\n",rv,offset);
+ m_tape_int_pending = 0;
+ break;
+ case 0xa0:
+ rv = 0x00;
+ logerror("TAPE: Read %02x from 0x40%02x, clear SR1 int pending\n",rv,offset);
+ m_sr1_int_pending = 0;
+ break;
+ case 0xc0:
+ case 0xc1:
+ case 0xc2:
+ case 0xc3:
+ case 0xc4:
+ case 0xc5:
+ case 0xc6:
+ case 0xc7:
+ case 0xc8:
+ case 0xc9:
+ case 0xca:
+ case 0xcb:
+ case 0xcc:
+ case 0xcd:
+ case 0xce:
+ case 0xcf:
+ /* TMS9927 regs */
+ rv = intvkbd_tms9927_r(space, offset-0xc0);
+ break;
+ default:
+ rv = (m_intvkbd_dualport_ram[offset]&0x0300)>>8;
+ logerror("Unknown read %02x from 0x40%02x\n",rv,offset);
+ break;
+ }
+ return rv;
+ }
+ else
+ return (m_intvkbd_dualport_ram[offset]&0x0300)>>8;
+}
+
+static const char *const tape_motor_mode_desc[8] =
+{
+ "IDLE", "IDLE", "IDLE", "IDLE",
+ "EJECT", "PLAY/RECORD", "REWIND", "FF"
+};
+
+WRITE8_MEMBER( intv_state::intvkbd_dualport8_msb_w )
+{
+ unsigned int mask;
+
+ if (offset < 0x100)
+ {
+ switch (offset)
+ {
+ case 0x020:
+ m_tape_motor_mode &= 3;
+ if (data & 1)
+ m_tape_motor_mode |= 4;
+ logerror("TAPE: Motor Mode: %s\n",tape_motor_mode_desc[m_tape_motor_mode]);
+ break;
+ case 0x021:
+ m_tape_motor_mode &= 5;
+ if (data & 1)
+ m_tape_motor_mode |= 2;
+ logerror("TAPE: Motor Mode: %s\n",tape_motor_mode_desc[m_tape_motor_mode]);
+ break;
+ case 0x022:
+ m_tape_motor_mode &= 6;
+ if (data & 1)
+ m_tape_motor_mode |= 1;
+ logerror("TAPE: Motor Mode: %s\n",tape_motor_mode_desc[m_tape_motor_mode]);
+ break;
+ case 0x023:
+ case 0x024:
+ case 0x025:
+ case 0x026:
+ case 0x027:
+ m_tape_unknown_write[offset - 0x23] = (data & 1);
+ break;
+ case 0x040:
+ m_tape_unknown_write[5] = (data & 1);
+ break;
+ case 0x041:
+ if (data & 1)
+ logerror("TAPE: Tape Interrupts Enabled\n");
+ else
+ logerror("TAPE: Tape Interrupts Disabled\n");
+ m_tape_interrupts_enabled = (data & 1);
+ break;
+ case 0x042:
+ if (data & 1)
+ logerror("TAPE: Cart Bus Interrupts Disabled\n");
+ else
+ logerror("TAPE: Cart Bus Interrupts Enabled\n");
+ break;
+ case 0x043:
+ if (data & 0x01)
+ m_intvkbd_text_blanked = 0;
+ else
+ m_intvkbd_text_blanked = 1;
+ break;
+ case 0x044:
+ m_intvkbd_keyboard_col &= 0x0e;
+ m_intvkbd_keyboard_col |= (data&0x01);
+ break;
+ case 0x045:
+ m_intvkbd_keyboard_col &= 0x0d;
+ m_intvkbd_keyboard_col |= ((data&0x01)<<1);
+ break;
+ case 0x046:
+ m_intvkbd_keyboard_col &= 0x0b;
+ m_intvkbd_keyboard_col |= ((data&0x01)<<2);
+ break;
+ case 0x047:
+ m_intvkbd_keyboard_col &= 0x07;
+ m_intvkbd_keyboard_col |= ((data&0x01)<<3);
+ break;
+ case 0x80:
+ logerror("TAPE: Write to 0x40%02x, clear tape int pending\n",offset);
+ m_tape_int_pending = 0;
+ break;
+ case 0xa0:
+ logerror("TAPE: Write to 0x40%02x, clear SR1 int pending\n",offset);
+ m_sr1_int_pending = 0;
+ break;
+ case 0xc0:
+ case 0xc1:
+ case 0xc2:
+ case 0xc3:
+ case 0xc4:
+ case 0xc5:
+ case 0xc6:
+ case 0xc7:
+ case 0xc8:
+ case 0xc9:
+ case 0xca:
+ case 0xcb:
+ case 0xcc:
+ case 0xcd:
+ case 0xce:
+ case 0xcf:
+ /* TMS9927 regs */
+ intvkbd_tms9927_w(space, offset-0xc0, data);
+ break;
+ default:
+ logerror("%04X: Unknown write %02x to 0x40%02x\n",cpu_get_pc(&space.device()),data,offset);
+ break;
+ }
+ }
+ else
+ {
+ mask = m_intvkbd_dualport_ram[offset] & 0x00ff;
+ m_intvkbd_dualport_ram[offset] = mask | ((data<<8)&0x0300);
+ }
+}
+
+READ16_MEMBER( intv_state::intv_gram_r )
+{
+ //logerror("read: %d = GRAM(%d)\n",state->m_gram[offset],offset);
+ if (m_bus_copy_mode || !m_stic_handshake)
+ {
+ return (int)m_gram[offset];
+ }
+ else {return offset;}
+
+}
+
+WRITE16_MEMBER( intv_state::intv_gram_w )
+{
+ data &= 0xFF;
+ if(m_bus_copy_mode || !m_stic_handshake)
+ {
+ m_gram[offset] = data;
+ m_gramdirtybytes[offset] = 1;
+ m_gramdirty = 1;
+ }
+}
+
+
+READ16_MEMBER( intv_state::intv_ram8_r )
+{
+ //logerror("%x = ram8_r(%x)\n",state->m_ram8[offset],offset);
+ return (int)m_ram8[offset];
+}
+
+WRITE16_MEMBER( intv_state::intv_ram8_w )
+{
+ //logerror("ram8_w(%x) = %x\n",offset,data);
+ m_ram8[offset] = data&0xff;
+}
+
+READ16_MEMBER( intv_state::intv_ecs_ram8_r )
+{
+ return (int)m_ecs_ram8[offset];
+}
+
+WRITE16_MEMBER( intv_state::intv_ecs_ram8_w )
+{
+ m_ecs_ram8[offset] = data&0xff;
+}
+
+READ16_MEMBER( intv_state::intv_cart_ram8_r )
+{
+ return (int)m_cart_ram8[offset];
+}
+WRITE16_MEMBER( intv_state::intv_cart_ram8_w )
+{
+ m_cart_ram8[offset] = data&0xff;
+}
+
+READ16_MEMBER( intv_state::intv_ram16_r )
+{
+ //logerror("%x = ram16_r(%x)\n",state->m_ram16[offset],offset);
+ return (int)m_ram16[offset];
+}
+
+WRITE16_MEMBER( intv_state::intv_ram16_w )
+{
+ //logerror("%g: WRITING TO GRAM offset = %d\n",machine.time(),offset);
+ //logerror("ram16_w(%x) = %x\n",offset,data);
+ m_ram16[offset] = data&0xffff;
+}
+
+// ECS and Wsmlb bank switching register handlers
+WRITE16_MEMBER( intv_state::ecs_bank1_page_select )
+{
+ if (offset == 0xFFF)
+ {
+ if (data == 0x2A50)
+ {
+ membank("bank1")->set_base(machine().root_device().memregion("maincpu")->base() + (0x2000 << 1));
+ }
+ else if (data == 0x2A51)
+ {
+ membank("bank1")->set_base(machine().root_device().memregion("ecs_rom")->base() + (0x2000 << 1));
+ }
+ }
+}
+
+WRITE16_MEMBER( intv_state::ecs_bank2_page_select )
+{
+ if (offset == 0xFFF)
+ {
+ if (data == 0x7A50)
+ {
+ membank("bank2")->set_base(machine().root_device().memregion("ecs_rom")->base() + (0x7000 << 1)); // ECS ROM at 0x7000 is on page 1
+ }
+ else if (data == 0x7A51 )
+ {
+ membank("bank2")->set_base(machine().root_device().memregion("maincpu")->base() + (0x7000 << 1));
+ }
+ }
+}
+
+WRITE16_MEMBER( intv_state::ecs_bank3_page_select )
+{
+ if (offset == 0xFFF)
+ {
+ if (data == 0xEA50)
+ {
+ membank("bank3")->set_base(machine().root_device().memregion("maincpu")->base() + (0xE000 << 1));
+ }
+ else if (data == 0xEA51)
+ {
+ membank("bank3")->set_base(machine().root_device().memregion("ecs_rom")->base() + (0xE000 << 1));
+ }
+ }
+}
+
+WRITE16_MEMBER( intv_state::wsmlb_bank_page_select )
+{
+ logerror("offset %x data %x\n",offset,data);
+ if (offset == 0xFFF)
+ {
+ if (data == 0xFA50)
+ {
+ membank("bank4")->set_base(machine().root_device().memregion("maincpu")->base() + (0xF000 << 1));
+ }
+ else if (data == 0xFA51)
+ {
+ membank("bank4")->set_base(machine().root_device().memregion("ecs_rom")->base() + (0xF000 << 1));
+ }
+ }
+}
+
+static int intv_load_rom_file(device_image_interface &image)
+{
+ int i,j;
+
+ UINT8 temp;
+ UINT8 num_segments;
+ UINT8 start_seg;
+ UINT8 end_seg;
+
+ UINT32 current_address;
+ UINT32 end_address;
+
+ UINT8 high_byte;
+ UINT8 low_byte;
+
+ UINT8 *memory = image.device().machine().root_device().memregion("maincpu")->base();
+ intv_state *state = image.device().machine().driver_data<intv_state>();
+ address_space *program = image.device().machine().device("maincpu")->memory().space(AS_PROGRAM);
+ const char *filetype = image.filetype();
+
+ /* if it is in .rom format, we enter here */
+ if (!mame_stricmp (filetype, "rom"))
+ {
+ image.fread( &temp, 1); /* header */
+ if (temp != 0xa8)
+ {
+ return IMAGE_INIT_FAIL;
+ }
+
+ image.fread( &num_segments, 1);
+
+ image.fread( &temp, 1);
+ if (temp != (num_segments ^ 0xff))
+ {
+ return IMAGE_INIT_FAIL;
+ }
+
+ for (i = 0; i < num_segments; i++)
+ {
+ image.fread( &start_seg, 1);
+ current_address = start_seg * 0x100;
+
+ image.fread( &end_seg, 1);
+ end_address = end_seg * 0x100 + 0xff;
+
+ while (current_address <= end_address)
+ {
+ image.fread( &low_byte, 1);
+ memory[(current_address << 1) + 1] = low_byte;
+ image.fread( &high_byte, 1);
+ memory[current_address << 1] = high_byte;
+ current_address++;
+ }
+
+ /* Here we should calculate and compare the CRC16... */
+ image.fread( &temp, 1);
+ image.fread( &temp, 1);
+ }
+
+ /* Access tables and fine address restriction tables are not supported ATM */
+ for (i = 0; i < (16 + 32 + 2); i++)
+ {
+ image.fread( &temp, 1);
+ }
+ return IMAGE_INIT_PASS;
+ }
+ /* otherwise, we load it as a .bin file, using extrainfo from intv.hsi in place of .cfg */
+ else
+ {
+ /* This code is a blatant hack, due to impossibility to load a separate .cfg file in MESS. */
+ /* It shall be eventually replaced by the .xml loading */
+
+ /* extrainfo format */
+ // 1. mapper number (to deal with bankswitch). no bankswitch is mapper 0 (most games).
+ // 2.->5. current images have at most 4 chunks of data. we store here block size and location to load
+ // (value & 0xf0) >> 4 is the location / 0x1000
+ // (value & 0x0f) is the size / 0x800
+ // 6. some images have a ram chunk. as above we store location and size in 8 bits
+ // 7. extra = 1 ECS, 2 Intellivoice
+ int start, size;
+ int mapper, rom[5], ram, extra;
+ const char *extrainfo = hashfile_extrainfo(image);
+ if (!extrainfo)
+ {
+ /* If no extrainfo, we assume a single 0x2000 chunk at 0x5000 */
+ for (i = 0; i < 0x2000; i++ )
+ {
+ image.fread( &low_byte, 1);
+ memory[((0x5000 + i) << 1) + 1] = low_byte;
+ image.fread( &high_byte, 1);
+ memory[(0x5000 + i) << 1] = high_byte;
+ }
+ }
+ else
+ {
+ sscanf(extrainfo,"%d %d %d %d %d %d %d", &mapper, &rom[0], &rom[1], &rom[2],
+ &rom[3], &ram, &extra);
+
+// logerror("extrainfo: %d %d %d %d %d %d %d \n", mapper, rom[0], rom[1], rom[2],
+// rom[3], ram, extra);
+
+ if (mapper)
+ {
+ logerror("Bankswitch not yet implemented! \n");
+ }
+
+ if (ram)
+ {
+ start = (( ram & 0xf0 ) >> 4) * 0x1000;
+ size = ( ram & 0x0f ) * 0x800;
+
+ program->install_readwrite_handler(start, start + size,
+ read16_delegate( FUNC( intv_state::intv_cart_ram8_r ), state),
+ write16_delegate( FUNC( intv_state::intv_cart_ram8_w ), state));
+ }
+ /* For now intellivoice always active
+ if (extra & INTELLIVOICE_MASK)
+ {
+ // tbd
+ }
+ */
+
+ if (extra & ECS_MASK)
+ {
+ logerror("Requires ECS Module\n");
+ }
+
+ for (j = 0; j < 4; j++)
+ {
+ start = (( rom[j] & 0xf0 ) >> 4) * 0x1000;
+ size = ( rom[j] & 0x0f ) * 0x800;
+
+ /* some cart has to be loaded to 0x4800, but none goes to 0x4000. Hence, we use */
+ /* 0x04 << 4 in extrainfo (to reduce the stored values) and fix the value here. */
+ if (start == 0x4000) start += 0x800;
+
+// logerror("step %d: %d %d \n", j, start / 0x1000, size / 0x1000);
+
+ for (i = 0; i < size; i++ )
+ {
+ image.fread( &low_byte, 1);
+ memory[((start + i) << 1) + 1] = low_byte;
+ image.fread( &high_byte, 1);
+ memory[(start + i) << 1] = high_byte;
+ }
+ }
+ }
+
+ return IMAGE_INIT_PASS;
+ }
+}
+
+DEVICE_IMAGE_LOAD( intv_cart )
+{
+
+ if (image.software_entry() == NULL)
+ return intv_load_rom_file(image);
+ else
+ {
+ UINT16 offset[] = {0x4800, 0x5000, 0x6000, 0x7000, 0x9000, 0xa000, 0xc000, 0xd000, 0xf000};
+ const char* region_name[] = {"4800", "5000", "6000", "7000", "9000", "A000", "C000", "D000", "F000"};
+ UINT8 *memory = image.device().machine().root_device().memregion("maincpu")->base();
+ intv_state *state = image.device().machine().driver_data<intv_state>();
+ address_space *program = image.device().machine().device("maincpu")->memory().space(AS_PROGRAM);
+
+ UINT32 size=0;
+ UINT16 address = 0;
+ UINT8 *region;
+ for(int i = 0; i < 9; i++)
+ {
+ address = offset[i];
+ size = image.get_software_region_length(region_name[i]);
+ if (size)
+ {
+ region = image.get_software_region(region_name[i]);
+ for (int j = 0; j < (size>>1); j++)
+ {
+ memory[((address + j) << 1) + 1] = region[2*j];
+ memory[(address + j) << 1] = region[2*j+1];
+ }
+ }
+ }
+ // deal with wsmlb paged rom
+
+ UINT8 *ecs_rom_region = image.device().machine().root_device().memregion("ecs_rom")->base();
+ size = image.get_software_region_length("F000_bank1");
+ if (size && ecs_rom_region) // only load if ecs is plugged in (should probably be done a different way)
+ {
+ region = image.get_software_region("F000_bank1");
+ for (int j = 0; j < (size>>1); j++)
+ {
+ ecs_rom_region[((address + j) << 1) + 1] = region[2*j];
+ ecs_rom_region[(address + j) << 1] = region[2*j+1];
+ }
+ }
+
+ // Cartridge 8bit ram support
+ size = image.get_software_region_length("D000_RAM8");
+ if (size)
+ {
+ program->install_readwrite_handler(0xD000, 0xD000 + size,
+ read16_delegate( FUNC( intv_state::intv_cart_ram8_r ), state),
+ write16_delegate( FUNC( intv_state::intv_cart_ram8_w ), state));
+ }
+
+ size = image.get_software_region_length("8800_RAM8");
+ if (size)
+ {
+ program->install_readwrite_handler(0x8800, 0x8800 + size,
+ read16_delegate( FUNC( intv_state::intv_cart_ram8_r ), state),
+ write16_delegate( FUNC( intv_state::intv_cart_ram8_w ), state));
+ }
+ return IMAGE_INIT_PASS;
+ }
+}
+
+#ifdef UNUSED_FUNCTION
+DRIVER_INIT_MEMBER(intv_state,intv)
+{
+}
+#endif
+
+/* Set Reset and INTR/INTRM Vector */
+MACHINE_RESET( intv )
+{
+ device_set_input_line_vector(machine.device("maincpu"), CP1610_RESET, 0x1000);
+
+ /* These are actually the same vector, and INTR is unused */
+ device_set_input_line_vector(machine.device("maincpu"), CP1610_INT_INTRM, 0x1004);
+ device_set_input_line_vector(machine.device("maincpu"), CP1610_INT_INTR, 0x1004);
+
+ /* Set initial PC */
+ cpu_set_reg(machine.device("maincpu"), CP1610_R7, 0x1000);
+
+ return;
+}
+
+MACHINE_RESET( intvecs )
+{
+ intv_state *state = machine.driver_data<intv_state>();
+ state->membank("bank1")->set_base(machine.root_device().memregion("maincpu")->base() + (0x2000 << 1));
+ state->membank("bank2")->set_base(machine.root_device().memregion("ecs_rom")->base() + (0x7000 << 1));
+ state->membank("bank3")->set_base(machine.root_device().memregion("maincpu")->base() + (0xE000 << 1));
+ state->membank("bank4")->set_base(machine.root_device().memregion("maincpu")->base() + (0xF000 << 1));
+
+ device_set_input_line_vector(machine.device("maincpu"), CP1610_RESET, 0x1000);
+
+ /* These are actually the same vector, and INTR is unused */
+ device_set_input_line_vector(machine.device("maincpu"), CP1610_INT_INTRM, 0x1004);
+ device_set_input_line_vector(machine.device("maincpu"), CP1610_INT_INTR, 0x1004);
+
+ /* Set initial PC */
+ cpu_set_reg(machine.device("maincpu"), CP1610_R7, 0x1000);
+
+ return;
+}
+
+
+static TIMER_CALLBACK(intv_interrupt_complete)
+{
+ intv_state *state = machine.driver_data<intv_state>();
+ cputag_set_input_line(machine, "maincpu", CP1610_INT_INTRM, CLEAR_LINE);
+ state->m_bus_copy_mode = 0;
+}
+
+static TIMER_CALLBACK(intv_btb_fill)
+{
+ intv_state *state = machine.driver_data<intv_state>();
+ UINT8 column;
+ UINT8 row = state->m_backtab_row;
+ //device_adjust_icount(machine.device("maincpu"), -STIC_ROW_FETCH);
+ for(column=0; column < STIC_BACKTAB_WIDTH; column++)
+ {
+ state->m_backtab_buffer[row][column] = state->m_ram16[column + row * STIC_BACKTAB_WIDTH];
+ }
+
+ state->m_backtab_row += 1;
+}
+
+INTERRUPT_GEN( intv_interrupt )
+{
+ intv_state *state = device->machine().driver_data<intv_state>();
+ cputag_set_input_line(device->machine(), "maincpu", CP1610_INT_INTRM, ASSERT_LINE);
+ state->m_sr1_int_pending = 1;
+ state->m_bus_copy_mode = 1;
+ state->m_backtab_row = 0;
+ UINT8 row;
+ device_adjust_icount(device->machine().device("maincpu"), -(12*STIC_ROW_BUSRQ+STIC_FRAME_BUSRQ)); // Account for stic cycle stealing
+ device->machine().scheduler().timer_set(device->machine().device<cpu_device>("maincpu")
+ ->cycles_to_attotime(STIC_VBLANK_END), FUNC(intv_interrupt_complete));
+ for (row=0; row < STIC_BACKTAB_HEIGHT; row++)
+ {
+ device->machine().scheduler().timer_set(device->machine().device<cpu_device>("maincpu")
+ ->cycles_to_attotime(STIC_FIRST_FETCH-STIC_FRAME_BUSRQ+STIC_CYCLES_PER_SCANLINE*STIC_Y_SCALE*state->m_row_delay + (STIC_CYCLES_PER_SCANLINE*STIC_Y_SCALE*STIC_CARD_HEIGHT - STIC_ROW_BUSRQ)*row), FUNC(intv_btb_fill));
+ }
+
+ if (state->m_row_delay == 0)
+ {
+ device_adjust_icount(device->machine().device("maincpu"), -STIC_ROW_BUSRQ); // extra row fetch occurs if vertical delay == 0
+ }
+
+ intv_stic_screenrefresh(device->machine());
+}
+
+/* hand 0 == left, 1 == right, 2 == ECS hand controller 1, 3 == ECS hand controller 2 */
+UINT8 intv_control_r(address_space *space, int hand)
+{
+ static const char* const keypad_name[] = { "KEYPAD1", "KEYPAD2", "KEYPAD3", "KEYPAD4" };
+ static const UINT8 keypad_table[] =
+ {
+ 0xFF, 0x3F, 0x9F, 0x5F, 0xD7, 0xB7, 0x77, 0xDB,
+ 0xBB, 0x7B, 0xDD, 0xBD, 0x7D, 0xDE, 0xBE, 0x7E
+ };
+
+ static const char* const disc_name[] = { "DISC1", "DISC2", "DISC3", "DISC4" };
+ static const UINT8 disc_table[] =
+ {
+ 0xF3, 0xE3, 0xE7, 0xF7, 0xF6, 0xE6, 0xEE, 0xFE,
+ 0xFC, 0xEC, 0xED, 0xFD, 0xF9, 0xE9, 0xEB, 0xFB
+ };
+
+ static const char* const discx_name[] = { "DISCX1", "DISCX2", "DISCX3", "DISCX4" };
+ static const char* const discy_name[] = { "DISCY1", "DISCY2", "DISKY3", "DISCY4" };
+ static const UINT8 discyx_table[5][5] =
+ {
+ { 0xE3, 0xF3, 0xFB, 0xEB, 0xE9 },
+ { 0xE7, 0xE3, 0xFB, 0xE9, 0xF9 },
+ { 0xF7, 0xF7, 0xFF, 0xFD, 0xFD },
+ { 0xF6, 0xE6, 0xFE, 0xEC, 0xED },
+ { 0xE6, 0xEE, 0xFE, 0xFC, 0xEC }
+ };
+
+ int x, y;
+ UINT8 rv = 0xFF;
+
+ /* keypad */
+ x = space->machine().root_device().ioport(keypad_name[hand])->read();
+ for (y = 0; y < 16; y++)
+ {
+ if (x & (1 << y))
+ {
+ rv &= keypad_table[y];
+ }
+ }
+
+ switch ((space->machine().root_device().ioport("OPTIONS")->read() >> hand) & 1)
+ {
+ case 0: /* disc == digital */
+ default:
+
+ x = space->machine().root_device().ioport(disc_name[hand])->read();
+ for (y = 0; y < 16; y++)
+ {
+ if (x & (1 << y))
+ {
+ rv &= disc_table[y];
+ }
+ }
+ break;
+
+ case 1: /* disc == _fake_ analog */
+
+ x = space->machine().root_device().ioport(discx_name[hand])->read();
+ y = space->machine().root_device().ioport(discy_name[hand])->read();
+ rv &= discyx_table[y / 32][x / 32];
+ }
+
+ return rv;
+}
+
+READ8_MEMBER( intv_state::intv_left_control_r )
+{
+ return intv_control_r(&space, 0);
+}
+
+READ8_MEMBER( intv_state::intv_right_control_r )
+{
+ return intv_control_r(&space, 1);
+}
+
+READ8_MEMBER( intv_state::intv_ecs_porta_r )
+{
+ if (ioport("ECS_CNTRLSEL")->read() == 0)
+ return intv_control_r(&space, 2);
+ else
+ return 0xff; // not sure what to return here, maybe it should be last output?
+}
+
+READ8_MEMBER( intv_state::intv_ecs_portb_r )
+{
+ switch (ioport("ECS_CNTRLSEL")->read())
+ {
+ case 0x00: // hand controller
+ {
+ return intv_control_r(&space, 3);
+ }
+ case 0x01: // synthesizer keyboard
+ {
+ static const char* const ecs_synth_rows[] = { "ECS_SYNTH_ROW0", "ECS_SYNTH_ROW1", "ECS_SYNTH_ROW2", "ECS_SYNTH_ROW3", "ECS_SYNTH_ROW4", "ECS_SYNTH_ROW5", "ECS_SYNTH_ROW6" };
+ UINT8 rv = 0xFF;
+ // return correct result if more than one bit of 0xFE is set
+ for (int i=0; i<7; i++)
+ {
+ if (m_ecs_psg_porta & (1<<i))
+ rv &= ioport(ecs_synth_rows[i])->read();
+ }
+ return rv;
+ }
+ case 0x02: // ecs keyboard
+ {
+ static const char* const ecs_keyboard_rows[] = { "ECS_ROW0", "ECS_ROW1", "ECS_ROW2", "ECS_ROW3", "ECS_ROW4", "ECS_ROW5", "ECS_ROW6" };
+ UINT8 rv = 0xFF;
+ // return correct result if more than one bit of 0xFE is set
+ for (int i=0; i<7; i++)
+ {
+ if (m_ecs_psg_porta & (1<<i))
+ rv &= ioport(ecs_keyboard_rows[i])->read();
+ }
+ return rv;
+ }
+ default:
+ return 0xff;
+ }
+}
+
+WRITE8_MEMBER( intv_state::intv_ecs_porta_w )
+{
+ m_ecs_psg_porta = (~data) & 0xFF;
+}
+
+/* Intellivision console + keyboard component */
+
+DEVICE_IMAGE_LOAD( intvkbd_cart )
+{
+ if (strcmp(image.device().tag(),":cart1") == 0) /* Legacy cartridge slot */
+ {
+ /* First, initialize these as empty so that the intellivision
+ * will think that the playcable is not attached */
+ UINT8 *memory = image.device().machine().root_device().memregion("maincpu")->base();
+
+ /* assume playcable is absent */
+ memory[0x4800 << 1] = 0xff;
+ memory[(0x4800 << 1) + 1] = 0xff;
+
+ intv_load_rom_file(image);
+ }
+
+ if (strcmp(image.device().tag(),":cart2") == 0) /* Keyboard component cartridge slot */
+ {
+ UINT8 *memory = image.device().machine().root_device().memregion("keyboard")->base();
+
+ /* Assume an 8K cart, like BASIC */
+ image.fread( &memory[0xe000], 0x2000);
+ }
+
+ return IMAGE_INIT_PASS;
+
+}