summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/ti99x/genboard.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/bus/ti99x/genboard.c')
-rw-r--r--src/devices/bus/ti99x/genboard.c1994
1 files changed, 1994 insertions, 0 deletions
diff --git a/src/devices/bus/ti99x/genboard.c b/src/devices/bus/ti99x/genboard.c
new file mode 100644
index 00000000000..0c021bd0234
--- /dev/null
+++ b/src/devices/bus/ti99x/genboard.c
@@ -0,0 +1,1994 @@
+// license:LGPL-2.1+
+// copyright-holders:Michael Zapf
+/***************************************************************************
+ Geneve 9640 mapper and more components
+
+ This file contains 3 classes:
+ - mapper: main function of the Gate Array on the Geneve board. Maps logical
+ memory accesses to a wider address space using map registers.
+ - keyboard: an implementation of a XT-style keyboard. This should be dropped
+ and replaced by a proper XT keyboard implementation.
+ - mouse: an implementation of an Atari-style mouse connected to the v9938.
+
+ Onboard SRAM configuration:
+ There is an adjustable SRAM configuration on board, representing the
+ various enhancements by users.
+
+ The standard memory configuration as reported by chkdsk (32 KiB):
+ 557056 bytes of total memory
+
+ With 64 KiB SRAM:
+ 589824 bytes of total memory
+
+ With 384 KiB SRAM:
+ 917504 bytes of total memory
+
+ The original 32 KiB SRAM memory needs to be expanded to 64 KiB for
+ MDOS 2.50s and higher, or the system will lock up. Therefore the emulation
+ default is 64 KiB.
+
+ The ultimate expansion is a 512 KiB SRAM circuit wired to the gate array
+ to provide 48 pages of fast static RAM. This also requires to build an
+ adapter for a larger socket. From the 512 KiB, only 384 KiB will be
+ accessed, since the higher pages are hidden behind the EPROM pages.
+
+ === Address map ===
+ p,q = page value bit (q = AMC, AMB, AMA)
+ c = address offset within 8 KiB page
+
+ p pqqq pppc cccc cccc cccc
+
+ 0 0... .... .... .... .... on-board dram 512 KiB
+
+ 0 1... .... .... .... .... on-board future expansion 512 KiB or Memex with Genmod
+
+ 1 00.. .... .... .... .... p-box AMA=0 (256 KiB)
+ 1 010. .... .... .... .... p-box AMA=1 AMB=0 (128 KiB)
+ 1 0110 .... .... .... .... p-box AMA=1 AMB=1 AMC=0 (64 KiB)
+
+ 1 0111 00.. .... .... .... p-box address block 0xxx, 2xxx
+ 1 0111 010. .... .... .... p-box address block 4xxx (DSR)
+ 1 0111 011. .... .... .... p-box address block 6xxx
+ 1 0111 100. .... .... .... p-box address block 8xxx (Speech at 0x9000)
+ 1 0111 101. .... .... .... p-box address block axxx
+ 1 0111 11.. .... .... .... p-box address block cxxx, exxx
+
+ 1 100. .... .... .... .... on-board sram (128K) -\
+ 1 101. .... .... .... .... on-board sram (128K) --+- maximum SRAM expansion
+ 1 1100 .... .... .... .... on-board sram (64K) --/
+ 1 1101 0... .... .... .... on-board sram (32K) - additional 32 KiB required for MDOS 2.50s and higher
+ 1 1101 1... .... .... .... on-board sram (32K) - standard setup
+
+ 1 111. ..0. .... .... .... on-board boot1
+ 1 111. ..1. .... .... .... on-board boot2
+
+ The TI console (or more precise, the Flex Cable Interface) sets the AMA/B/C
+ lines to 1. Most cards actually check for AMA/B/C=1. However, this decoding
+ was forgotten in third party cards which cause the card address space
+ to be mirrored. The usual DSR space at 4000-5fff which would be reachable
+ via page 0xba is then mirrored on a number of other pages:
+
+ 10 xxx 010x = 82, 8a, 92, 9a, a2, aa, b2, ba
+
+ Another block to take care of is 0xbc which covers 8000-9fff since this
+ area contains the speech synthesizer port at 9000/9400.
+
+ For the standard Geneve, only prefix 10 is routed to the P-Box. The Genmod
+ modification wires these address lines to pins 8 and 9 in the P-Box as AMD and
+ AME. This requires all cards to be equipped with an additional selection logic
+ to detect AMD=0, AME=1. Otherwise these cards, although completely decoding the
+ 19-bit address, would reappear at 512 KiB distances.
+
+ Genmod's double switch box is also emulated. There are two switches:
+ - Turbo mode: Activates or deactivates the wait state logic on the Geneve
+ board. This switch may be changed at any time.
+ - TI mode: Selects between the on-board memory, which is obviously required
+ for the GPL interpreter, and the external Memex memory. This switch
+ triggers a reset when changed.
+
+
+ ===================
+ Mapping
+ ===================
+
+ Logical address space: 64 KiB
+
+ Geneve mode:
+
+ Video: F100, F102, F104, F106 (mirror: +8)
+ Mapper: F110 - F117
+ Keyboard: F118
+ Clock: F130 - F13F
+ Sound: F120
+
+ TI mode:
+
+ Video: 8800, 8802, 8804, 8806
+ Mapper: 8000 - 8007
+ Keyboard: 8008 - 800F
+ Clock: 8010 - 801F
+ Speech: 9000 / 9400
+ Grom: 9800 / 9802
+
+ Physical address space: 2 MiB
+
+ Start End Banks
+ 000000 - 07FFFF 00-3F 512 KiB DRAM on-board
+ 080000 - 0FFFFF 40-7F 512 KiB on-board expansion (never used)
+ 100000 - 16FFFF 80-B7 448 KiB P-Box space (special cards, like MEMEX)
+ 170000 - 17FFFF B8-BF 64 KiB P-Box space (current cards)
+ 180000 - 1DFFFF C0-EF 384 KiB SRAM space on-board; stock Geneve comes with 32 KiB
+ 1E0000 - 1FFFFF F0-FF 128 KiB EPROM space; 16 KiB actually used, 8 mirrors
+
+
+ GenMod modification:
+
+ TI mode
+ 000000 - 07FFFF 00-3F 512 KiB DRAM on-board
+ 080000 - 1DFFFF 40-EF 1408 KiB P-Box space
+ 1E0000 - 1FFFFF F0-FF 128 KiB EPROM space; 16 KiB actually used, 8 mirrors
+
+ Non-TI mode
+ 000000 - 1DFFFF 00-EF 1920 KiB P-Box space
+ 1E0000 - 1FFFFF F0-FF 128 KiB EPROM space; 16 KiB actually used, 8 mirrors
+
+ Waitstate handling
+ ------------------
+ Waitstates are caused by a cleared READY line of the TMS9995 processor
+ during an external memory cycle. That means that waitstates have no effect
+ for operations within the on-chip memory, and only when an access to the
+ external memory or other devices occurs, a delay will be noticed.
+
+ The waitstates are generated by the custom Gate Array chip on the board
+ and the PAL 16R4, both lacking proper documentation. All of the following
+ numbers have been determined by experiments with the real machine.
+
+ Waitstates are generated for:
+ - memory-mapped devices (mapper, clock, keyboard): 1 WS
+ - accesses to the peripheral expansion box: 1 WS
+ - accesses to on-board DRAM: 1 WS
+ - accesses to video: 15 WS
+ - accesses to sound: ~25 WS
+ - accesses to SRAM: 0 WS
+
+ Additional waitstates are created when one of the CRU bits is set. In that
+ case, all delays are extended to 2 WS (including SRAM).
+
+ Sound waitstates are somewhat unpredictable. It seems as if they depend
+ on the clock of the sound chip; the theory is that the READY line is
+ pulled down until the next clock pulse, which may take some value between
+ 18 CPU cycles and 30 CPU cycles.
+
+ The gate array is able to create wait states for video accesses. However,
+ these wait states are effective after the video access has been completed.
+ Wait states are not effective when the execution is running in on-chip
+ RAM. Additional wait states are requested by m_video_waitstates = true.
+ Without additional wait states, the video access takes the usual 1 or 2 WS.
+
+ Waitstate behavior (Nov 2013)
+ Almost perfect. Only video read access from code in DRAM is too fast by one WS
+
+ ==========================
+ PFM expansion
+ ==========================
+
+ The "Programmable Flash Memory expansion" is a replacement for the boot
+ EPROM.
+
+ PFM: Original version, 128 KiB
+ PFM+: Expansion of the original version, piggybacked, adds another 128KiB
+ PFM512: Using an AT29C040 (not A), 512 KiB
+
+ The PFM is visible as four banks in memory pages 0xF0 - 0xFF.
+
+ Bank switching is done by four 9901 pins:
+
+ 0028: LSB of bank number
+ 003A: MSB of bank number
+
+ Bank 0 is the boot code, while banks 1-3 can be used as flash drives
+
+ Michael Zapf, October 2011
+ February 2012: rewritten as class, restructured
+ Aug 2015: PFM added
+
+***************************************************************************/
+
+#include "genboard.h"
+
+#define TRACE_READ 0
+#define TRACE_WRITE 0
+#define TRACE_DETAIL 0
+#define TRACE_KEYBOARD 0
+#define TRACE_CLOCK 0
+#define TRACE_LINES 0
+#define TRACE_SETTING 1
+#define TRACE_PFM 0
+
+geneve_mapper_device::geneve_mapper_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+: device_t(mconfig, GENEVE_MAPPER, "Geneve Gate Array", tag, owner, clock, "geneve_mapper", __FILE__),
+ m_ready(*this)
+{
+ m_eprom = NULL;
+}
+
+INPUT_CHANGED_MEMBER( geneve_mapper_device::settings_changed )
+{
+ int number = (int)((UINT64)param&0x03);
+ int value = newval;
+
+ switch (number)
+ {
+ case 1:
+ // Turbo switch. May be changed at any time.
+ if (TRACE_SETTING) logerror("%s: Setting turbo flag to %d\n", tag(), value);
+ m_turbo = (value!=0);
+ break;
+ case 2:
+ // TIMode switch. Causes reset when changed.
+ if (TRACE_SETTING) logerror("%s: Setting timode flag to %d\n", tag(), value);
+ m_timode = (value!=0);
+ machine().schedule_hard_reset();
+ break;
+ case 3:
+ // Used when switching the boot ROMs during runtime, especially the PFM
+ set_boot_rom(value);
+ break;
+ default:
+ logerror("%s: Unknown setting %d ignored\n", tag(), number);
+ }
+}
+
+/****************************************************************************
+ GROM simulation. The Geneve board simulated GROM circuits within its gate
+ array.
+*****************************************************************************/
+
+/*
+ Simulates GROM. The real Geneve does not use GROMs but simulates them
+ within the gate array. Unlike with real GROMs, no address wrapping occurs,
+ and the complete 64K space is available.
+*/
+READ8_MEMBER( geneve_mapper_device::read_grom )
+{
+ UINT8 reply;
+ if (offset & 0x0002)
+ {
+ // GROM address handling
+ m_gromwaddr_LSB = false;
+
+ if (m_gromraddr_LSB)
+ {
+ reply = m_grom_address & 0xff;
+ m_gromraddr_LSB = false;
+ }
+ else
+ {
+ reply = (m_grom_address >> 8) & 0xff;
+ m_gromraddr_LSB = true;
+ }
+ }
+ else
+ {
+ // GROM data handling
+ // GROMs are stored in pages 38..3f
+ int page = 0x38;
+ reply = m_dram[(page<<13) + m_grom_address];
+ m_grom_address = (m_grom_address + 1) & 0xffff;
+ m_gromraddr_LSB = m_gromwaddr_LSB = false;
+ }
+ return reply;
+}
+
+/*
+ Simulates GROM. The real Geneve does not use GROMs but simulates them
+ within the gate array.
+*/
+WRITE8_MEMBER( geneve_mapper_device::write_grom )
+{
+ if (offset & 0x0002)
+ {
+ // set address
+ m_gromraddr_LSB = false;
+ if (m_gromwaddr_LSB)
+ {
+ m_grom_address = (m_grom_address & 0xff00) | data;
+ m_grom_address = (m_grom_address + 1) & 0xffff;
+ m_gromwaddr_LSB = false;
+ }
+ else
+ {
+ m_grom_address = (m_grom_address & 0x00ff) | ((UINT16)data<<8);
+ m_gromwaddr_LSB = true;
+ }
+ }
+ else
+ { // write GPL data
+ // The Geneve GROM simulator allows for GROM writing (verified with a real system)
+ int page = 0x38;
+ m_dram[(page<<13) + m_grom_address] = data;
+
+ m_grom_address = (m_grom_address + 1) & 0xffff;
+ m_gromraddr_LSB = m_gromwaddr_LSB = false;
+ }
+}
+
+void geneve_mapper_device::set_wait(int min)
+{
+ if (m_debug_no_ws) return;
+ if (m_extra_waitstates && min < 2) min = 2;
+
+ // if we still have video wait states, do not set this counter
+ // (or it will assert READY when expiring)
+ if (m_ext_waitcount > min) return;
+
+ // need one more pass so that READY will be asserted again
+ m_waitcount = min + 1;
+ if (m_waitcount > 1)
+ {
+ if (TRACE_LINES) logerror("%s: Pulling down READY line for %d cycles\n", tag(), min);
+ m_ready(CLEAR_LINE);
+ m_ready_asserted = false;
+ }
+}
+
+void geneve_mapper_device::set_ext_wait(int min)
+{
+ if (m_debug_no_ws) return;
+ m_ext_waitcount = min;
+}
+
+void geneve_mapper_device::set_boot_rom(int selection)
+{
+ switch (selection)
+ {
+ case GENEVE_098:
+ logerror("%s: Using 0.98 boot eprom\n", tag());
+ m_eprom = machine().root_device().memregion("maincpu")->base() + 0x4000;
+ m_pfm_mode = 0;
+ break;
+ case GENEVE_100:
+ logerror("%s: Using 1.00 boot eprom\n", tag());
+ m_eprom = machine().root_device().memregion("maincpu")->base();
+ m_pfm_mode = 0;
+ break;
+ case GENEVE_PFM512:
+ logerror("%s: Using PFM512 (AT29C040)\n", tag());
+ m_pfm_mode = 1;
+ break;
+ case GENEVE_PFM512A:
+ logerror("%s: Using PFM512A (AT29C040A)\n", tag());
+ m_pfm_mode = 2;
+ break;
+ default:
+ logerror("%s: Unknown boot ROM selection\n", tag());
+ }
+}
+
+void geneve_mapper_device::set_geneve_mode(bool geneve)
+{
+ if (TRACE_SETTING) logerror("%s: Setting Geneve mode = %d\n", tag(), geneve);
+ m_geneve_mode = geneve;
+}
+
+void geneve_mapper_device::set_direct_mode(bool direct)
+{
+ if (TRACE_SETTING) logerror("%s: Setting direct mode = %d\n", tag(), direct);
+ m_direct_mode = direct;
+}
+
+void geneve_mapper_device::set_cartridge_size(int size)
+{
+ if (TRACE_SETTING) logerror("%s: Setting cartridge size to %d\n", tag(), size);
+ m_cartridge_size = size;
+}
+
+void geneve_mapper_device::set_cartridge_writable(int base, bool write)
+{
+ if (TRACE_SETTING) logerror("%s: Cartridge %04x space writable = %d\n", tag(), base, write);
+ if (base==0x6000) m_cartridge6_writable = write;
+ else m_cartridge7_writable = write;
+}
+
+void geneve_mapper_device::set_video_waitstates(bool wait)
+{
+ if (TRACE_SETTING) logerror("%s: Setting video waitstates = %d\n", tag(), wait);
+ m_video_waitstates = wait;
+}
+
+void geneve_mapper_device::set_extra_waitstates(bool wait)
+{
+ if (TRACE_SETTING) logerror("%s: Setting extra waitstates = %d\n", tag(), wait);
+ m_extra_waitstates = wait;
+}
+
+
+/************************************************************************
+ Called by the address map
+************************************************************************/
+/*
+ Constants for mapper decoding. Naming scheme:
+ M=mapper
+ L=Logical space; P=Physical space
+ G=Geneve mode; T=TI mode; GM=GenMod
+*/
+enum
+{
+ MLGVIDEO=1,
+ MLGMAPPER,
+ MLGKEY,
+ MLGCLOCK,
+ MLGSOUND,
+ MLTMAPPER,
+ MLTKEY,
+ MLTCLOCK,
+ MLTVIDEO,
+ MLTSPEECH,
+ MLTGROM,
+ MLTSOUND,
+ MPGDRAM,
+ MPGEXP,
+ MPGEPROM,
+ MPGSRAM,
+ MPGBOX,
+ MPGMDRAM,
+ MPGMEPROM,
+ MPGMBOX
+};
+
+/*
+ Read a byte via the data bus. The decoding has already been done in the
+ SETOFFSET method, and we re-use the values stored there to quickly
+ access the appropriate component.
+*/
+READ8_MEMBER( geneve_mapper_device::readm )
+{
+ UINT8 value = 0;
+
+ decdata *dec;
+ decdata debug;
+
+ // For the debugger, do the decoding here with no wait states
+ if (space.debugger_access())
+ {
+ if (m_cpu->is_onchip(offset)) return m_cpu->debug_read_onchip_memory(offset&0xff);
+ dec = &debug;
+ m_debug_no_ws = true;
+ decode(space, offset, true, dec);
+ }
+ else
+ {
+ // Use the values found in the setaddress phase
+ dec = &m_decoded;
+ m_debug_no_ws = false;
+ }
+
+ switch (dec->function)
+ {
+ case MLGVIDEO:
+ m_video->readz(space, dec->offset, &value, 0xff);
+ if (TRACE_READ) logerror("%s: Read video %04x -> %02x\n", tag(), dec->offset, value);
+ // Video wait states are created *after* the access
+ // Accordingly, they have no effect when execution is in onchip RAM
+ if (m_video_waitstates) set_ext_wait(15);
+ break;
+
+ case MLGMAPPER:
+ // mapper
+ value = m_map[dec->offset];
+ if (TRACE_READ) logerror("%s: read mapper %04x -> %02x\n", tag(), dec->offset, value);
+ break;
+
+ case MLGKEY:
+ // key
+ if (!space.debugger_access()) value = m_keyboard->get_recent_key();
+ if (TRACE_READ) logerror("%s: Read keyboard -> %02x\n", tag(), value);
+ break;
+
+ case MLGCLOCK:
+ // clock
+ // tests on the real machine showed that
+ // upper nibble is 0xf (probably because of the location at 0xf130?)
+ value = m_clock->read(space, dec->offset) | 0xf0;
+ if (TRACE_READ) logerror("%s: Read clock %04x -> %02x\n", tag(), dec->offset, value);
+ break;
+
+ case MLTMAPPER:
+ // mapper
+ value = m_map[dec->offset];
+ if (TRACE_READ) logerror("%s: Read mapper %04x -> %02x\n", tag(), dec->offset, value);
+ break;
+
+ case MLTKEY:
+ // key
+ if (!space.debugger_access()) value = m_keyboard->get_recent_key();
+ if (TRACE_READ) logerror("%s: Read keyboard -> %02x\n", tag(), value);
+ break;
+
+ case MLTCLOCK:
+ // clock
+ // upper nibble is 1, only last byte gets a 2
+ // probably because of the location at 8010...8020?
+ // (TI mode used swapped byte order)
+ // unless we use a workspace at >F000, in which case we get 8x values
+ // Obscure, needs more investigation. We might as well ignore this,
+ // as the high nibble is obviously undefined and takes some past
+ // value floating around.
+ value = m_clock->read(space, dec->offset);
+ value |= (dec->offset==0x000f)? 0x20 : 0x10;
+ if (TRACE_READ) logerror("%s: Read clock %04x -> %02x\n", tag(), dec->offset, value);
+ break;
+
+ case MLTVIDEO:
+ // video
+ // ++++ ++-- ---- ---+
+ // 1000 1000 0000 00x0
+ m_video->readz(space, dec->offset, &value, 0xff);
+ if (TRACE_READ) logerror("%s: Read video %04x -> %02x\n", tag(), dec->offset, value);
+ // See above
+ if (m_video_waitstates) set_ext_wait(15);
+ break;
+
+ case MLTSPEECH:
+ // speech
+ // ++++ ++-- ---- ---+
+ // 1001 0000 0000 0000
+ // We need to add the address prefix bits
+ m_peribox->readz(space, dec->offset, &value, 0xff);
+ if (TRACE_READ) logerror("%s: Read speech -> %02x\n", tag(), value);
+ break;
+
+ case MLTGROM:
+ // grom simulation
+ // ++++ ++-- ---- ---+
+ // 1001 1000 0000 00x0
+ if (!space.debugger_access()) value = read_grom(space, dec->offset, 0xff);
+ if (TRACE_READ) logerror("%s: Read GROM %04x -> %02x\n", tag(), dec->offset, value);
+ break;
+
+ case MLGSOUND:
+ case MLTSOUND:
+ value = 0;
+ break;
+
+
+ case MPGDRAM:
+ // DRAM.
+ value = m_dram[dec->physaddr];
+// LOG("dram read physaddr = %06x logaddr = %04x value = %02x\n", dec->physaddr, dec->offset, value);
+ if (TRACE_READ) logerror("%s: Read DRAM %04x (%06x) -> %02x\n", tag(), dec->offset, dec->physaddr, value);
+ break;
+
+ case MPGEXP:
+ // On-board memory expansion for standard Geneve (never used)
+ if (TRACE_READ) logerror("%s: Read unmapped area %06x\n", tag(), dec->physaddr);
+ value = 0;
+ break;
+
+ case MPGEPROM:
+ // 1 111. ..xx xxxx xxxx xxxx on-board eprom (16K)
+ // mirrored for f0, f2, f4, ...; f1, f3, f5, ...
+ if (m_pfm_mode == 0)
+ {
+ value = m_eprom[dec->physaddr & 0x003fff];
+ if (TRACE_READ) logerror("%s: Read EPROM %04x (%06x) -> %02x\n", tag(), dec->offset, dec->physaddr, value);
+ }
+ else value = read_from_pfm(space, dec->physaddr, 0xff);
+
+ break;
+
+ case MPGSRAM:
+ if ((dec->physaddr & m_sram_mask)==m_sram_val)
+ {
+ value = m_sram[dec->physaddr & ~m_sram_mask];
+ }
+ else value = 0;
+ // Return in any case
+// LOG("sram read physaddr = %06x logaddr = %04x value = %02x\n", dec->physaddr, dec->offset, value);
+ if (TRACE_READ) logerror("%s: Read SRAM %04x (%06x) -> %02x\n", tag(), dec->offset, dec->physaddr, value);
+ break;
+
+ case MPGBOX:
+ // Route everything else to the P-Box
+ // 0x000000-0x07ffff for the stock Geneve (AMC,AMB,AMA,A0 ...,A15)
+ // 0x000000-0x1fffff for the GenMod.(AME,AMD,AMC,AMB,AMA,A0 ...,A15)
+
+ m_peribox->readz(space, dec->physaddr, &value, 0xff);
+ if (TRACE_READ) logerror("%s: Read P-Box %04x (%06x) -> %02x\n", tag(), dec->offset, dec->physaddr, value);
+ break;
+
+ case MPGMDRAM:
+ // DRAM. One wait state.
+ value = m_dram[dec->physaddr];
+ break;
+
+ case MPGMEPROM:
+ // 1 111. ..xx xxxx xxxx xxxx on-board eprom (16K)
+ // mirrored for f0, f2, f4, ...; f1, f3, f5, ...
+ if (m_pfm_mode == 0)
+ {
+ value = m_eprom[dec->physaddr & 0x003fff];
+ if (TRACE_READ) logerror("%s: Read EPROM %04x (%06x) -> %02x\n", tag(), dec->offset, dec->physaddr, value);
+ }
+ else value = read_from_pfm(space, dec->physaddr, 0xff);
+ break;
+
+ case MPGMBOX:
+ // Route everything else to the P-Box
+ m_peribox->readz(space, dec->physaddr, &value, 0xff);
+ break;
+ }
+ return value;
+}
+
+WRITE8_MEMBER( geneve_mapper_device::writem )
+{
+ decdata *dec;
+ decdata debug;
+
+ // For the debugger, do the decoding here with no wait states
+ if (space.debugger_access())
+ {
+ dec = &debug;
+ m_debug_no_ws = true;
+ decode(space, offset, false, dec);
+ }
+ else
+ {
+ // Use the values found in the setaddress phase
+ m_debug_no_ws = false;
+ dec = &m_decoded;
+ }
+
+ switch (dec->function)
+ {
+ case MLGVIDEO:
+ // video
+ // ++++ ++++ ++++ ---+
+ // 1111 0001 0000 .cc0
+ m_video->write(space, dec->offset, data, 0xff);
+ if (TRACE_WRITE) logerror("%s: Write video %04x <- %02x\n", tag(), offset, data);
+ // See above
+ if (m_video_waitstates) set_ext_wait(15);
+ break;
+
+ case MLGMAPPER:
+ // mapper
+ m_map[dec->offset] = data;
+ if (TRACE_WRITE) logerror("%s: Write mapper %04x <- %02x\n", tag(), offset, data);
+ break;
+
+ case MLGCLOCK:
+ // clock
+ // ++++ ++++ ++++ ----
+ m_clock->write(space, dec->offset, data);
+ if (TRACE_WRITE) logerror("%s: Write clock %04x <- %02x\n", tag(), offset, data);
+ break;
+
+ case MLGSOUND:
+ // sound
+ // ++++ ++++ ++++ ---+
+ m_sound->write(space, 0, data, 0xff);
+ if (TRACE_WRITE) logerror("%s: Write sound <- %02x\n", tag(), data);
+ break;
+
+ case MLTMAPPER:
+ // mapper
+ m_map[dec->offset] = data;
+ if (TRACE_WRITE) logerror("%s: Write mapper %04x <- %02x\n", tag(), offset, data);
+ break;
+
+ case MLTCLOCK:
+ // clock
+ m_clock->write(space, dec->offset, data);
+ if (TRACE_WRITE) logerror("%s: Write clock %04x <- %02x\n", tag(), offset, data);
+ break;
+
+ case MLTVIDEO:
+ // video
+ // ++++ ++-- ---- ---+
+ // 1000 1100 0000 00c0
+ // Initialize waitstate timer
+ m_video->write(space, dec->offset, data, 0xff);
+ if (TRACE_WRITE) logerror("%s: Write video %04x <- %02x\n", tag(), offset, data);
+ // See above
+ if (m_video_waitstates) set_ext_wait(15);
+ break;
+
+ case MLTSPEECH:
+ // speech
+ // ++++ ++-- ---- ---+
+ // 1001 0100 0000 0000
+ // We need to add the address prefix bits
+ m_peribox->write(space, dec->offset, data, 0xff);
+ if (TRACE_WRITE) logerror("%s: Write speech <- %02x\n", tag(), data);
+ break;
+
+ case MLTGROM:
+ // grom simulation
+ // ++++ ++-- ---- ---+
+ // 1001 1100 0000 00c0
+ write_grom(space, dec->offset, data, 0xff);
+ if (TRACE_WRITE) logerror("%s: Write GROM %04x <- %02x\n", tag(), offset, data);
+ break;
+
+ case MLTSOUND:
+ // sound
+ // ++++ ++-- ---- ---+
+ // 1000 0100 0000 0000
+ m_sound->write(space, 0, data, 0xff);
+ if (TRACE_WRITE) logerror("%s: Write sound <- %02x\n", tag(), data);
+ break;
+
+ case MLTKEY:
+ case MLGKEY:
+ break;
+
+ case MPGDRAM:
+ // DRAM write. One wait state. (only for normal Geneve)
+ m_dram[dec->physaddr] = data;
+ if (TRACE_WRITE) logerror("%s: Write DRAM %04x (%06x) <- %02x\n", tag(), offset, dec->physaddr, data);
+ break;
+
+ case MPGEXP:
+ // On-board memory expansion for standard Geneve (never used)
+ if (TRACE_WRITE) logerror("%s: Write unmapped area %06x\n", tag(), dec->physaddr);
+ break;
+
+ case MPGEPROM:
+ // 1 111. ..xx xxxx xxxx xxxx on-board eprom (16K)
+ // mirrored for f0, f2, f4, ...; f1, f3, f5, ...
+ // Ignore EPROM write (unless PFM)
+ if (m_pfm_mode != 0) write_to_pfm(space, dec->physaddr, data, 0xff);
+ else
+ logerror("%s: Write EPROM %04x (%06x) <- %02x, ignored\n", tag(), offset, dec->physaddr, data);
+ break;
+
+ case MPGSRAM:
+ if ((dec->physaddr & m_sram_mask)==m_sram_val)
+ {
+ m_sram[dec->physaddr & ~m_sram_mask] = data;
+ }
+ if (TRACE_WRITE) logerror("%s: Write SRAM %04x (%06x) <- %02x\n", tag(), offset, dec->physaddr, data);
+ break;
+
+ case MPGBOX:
+ dec->physaddr = (dec->physaddr & 0x0007ffff); // 19 bit address
+ if (TRACE_WRITE) logerror("%s: Write P-Box %04x (%06x) <- %02x\n", tag(), offset, dec->physaddr, data);
+ m_peribox->write(space, dec->physaddr, data, 0xff);
+ break;
+
+ case MPGMDRAM:
+ // DRAM. One wait state.
+ m_dram[dec->physaddr] = data;
+ break;
+
+ case MPGMEPROM:
+ // 1 111. ..xx xxxx xxxx xxxx on-board eprom (16K)
+ // mirrored for f0, f2, f4, ...; f1, f3, f5, ...
+ // Ignore EPROM write
+ if (m_pfm_mode != 0) write_to_pfm(space, dec->physaddr, data, 0xff);
+ else
+ logerror("%s: Write EPROM %04x (%06x) <- %02x, ignored\n", tag(), offset, dec->physaddr, data);
+ break;
+
+ case MPGMBOX:
+ // Route everything else to the P-Box
+ m_peribox->write(space, dec->physaddr, data, 0xff);
+ break;
+ }
+}
+
+void geneve_mapper_device::decode(address_space& space, offs_t offset, bool read_mode, geneve_mapper_device::decdata* dec)
+{
+ dec->function = 0;
+ dec->offset = offset;
+ dec->physaddr = 0;
+
+ int page;
+
+ if (read_mode) // got this from DBIN
+ {
+ // Logical addresses
+ if (m_geneve_mode)
+ {
+ // TODO: shortcut offset & 0xffc0 = 0xf100
+ if ((offset & 0xfff5)==0xf100)
+ {
+ // video
+ // ++++ ++++ ++++ -+-+
+ // 1111 0001 0000 0000
+ // 1111 0001 0000 0010
+ // 1111 0001 0000 1000
+ // 1111 0001 0000 1010
+
+ dec->function = MLGVIDEO;
+ set_wait(1);
+ return;
+ }
+ if ((offset & 0xfff8)==0xf110)
+ {
+ // mapper
+ dec->function = MLGMAPPER;
+ dec->offset = dec->offset & 0x0007;
+ set_wait(1);
+ return;
+ }
+ if ((offset & 0xfff8) == 0xf118)
+ {
+ // key
+ dec->function = MLGKEY;
+ set_wait(1);
+ return;
+ }
+ if ((offset & 0xfff0)==0xf130)
+ {
+ // clock
+ // tests on the real machine showed that
+ // upper nibble is 0xf (probably because of the location at 0xf130?)
+ dec->function = MLGCLOCK;
+ dec->offset = dec->offset & 0x000f;
+ set_wait(1);
+ return;
+ }
+ }
+ else
+ {
+ if ((offset & 0xfff8)==0x8000)
+ {
+ // mapper
+ dec->function = MLTMAPPER;
+ dec->offset = dec->offset & 0x0007;
+ set_wait(1);
+ return;
+ }
+ if ((offset & 0xfff8)== 0x8008)
+ {
+ // key
+ dec->function = MLTKEY;
+ set_wait(1);
+ return;
+ }
+ if ((offset & 0xfff0)==0x8010)
+ {
+ // clock
+ dec->function = MLTCLOCK;
+ dec->offset = dec->offset & 0x000f;
+ set_wait(1);
+ return;
+ }
+ if ((offset & 0xfc01)==0x8800)
+ {
+ // video
+ // ++++ ++-- ---- ---+
+ // 1000 1000 0000 00x0
+ // 1 WS is always added; any pending video waitstates are canceled
+ dec->function = MLTVIDEO;
+ set_wait(1);
+ return;
+ }
+ if ((offset & 0xfc01)==0x9000)
+ {
+ // speech
+ // ++++ ++-- ---- ---+
+ // 1001 0000 0000 0000
+ // We need to add the address prefix bits
+ dec->function = MLTSPEECH;
+ dec->offset = offset | ((m_genmod)? 0x170000 : 0x070000);
+ m_peribox->setaddress_dbin(space, dec->offset, read_mode);
+ set_wait(1);
+ return;
+ }
+ if ((offset & 0xfc01)==0x9800)
+ {
+ // grom simulation
+ // ++++ ++-- ---- ---+
+ // 1001 1000 0000 00x0
+ dec->function = MLTGROM;
+ set_wait(1);
+ return;
+ }
+ }
+ // still here? Then go via mapping.
+ page = (offset & 0xe000) >> 13;
+
+ // Determine physical address
+ if (m_direct_mode)
+ {
+ dec->physaddr = 0x1f0000; // points to boot eprom (page F8)
+ }
+ else
+ {
+ if (!m_geneve_mode && page==3)
+ {
+ if (m_cartridge_size==0x4000 && m_cartridge_secondpage) dec->physaddr = 0x06e000;
+ else dec->physaddr = 0x06c000;
+ }
+ else
+ {
+ dec->physaddr = (m_map[page] << 13);
+ }
+ }
+ dec->physaddr |= (offset & 0x1fff);
+
+ if (!m_genmod) // Standard Geneve
+ {
+ if ((dec->physaddr & 0x180000)==0x000000)
+ {
+ // DRAM.
+ dec->physaddr = dec->physaddr & 0x07ffff;
+ dec->function = MPGDRAM;
+ set_wait(1);
+ return;
+ }
+
+ if ((dec->physaddr & 0x180000)==0x080000)
+ {
+ // On-board memory expansion for standard Geneve (never used)
+ dec->function = MPGEXP;
+ set_wait(1);
+ return;
+ }
+
+ if ((dec->physaddr & 0x1e0000)==0x1e0000)
+ {
+ // 1 111. ..xx xxxx xxxx xxxx on-board eprom (16K)
+ // mirrored for f0, f2, f4, ...; f1, f3, f5, ... unless using PFM
+ dec->function = MPGEPROM;
+ set_wait(0);
+ return;
+ }
+
+ if ((dec->physaddr & 0x180000)==0x180000)
+ {
+ dec->function = MPGSRAM;
+ set_wait(0);
+ return;
+ }
+
+ // Route everything else to the P-Box
+ // 0x000000-0x07ffff for the stock Geneve (AMC,AMB,AMA,A0 ...,A15)
+ // 0x000000-0x1fffff for the GenMod.(AME,AMD,AMC,AMB,AMA,A0 ...,A15)
+ // Add a wait state
+ set_wait(1);
+ dec->function = MPGBOX;
+
+ dec->physaddr = (dec->physaddr & 0x0007ffff); // 19 bit address (with AMA..AMC)
+ m_peribox->setaddress_dbin(space, dec->physaddr, read_mode);
+ return;
+ }
+ else
+ {
+ // GenMod mode
+ if ((m_timode) && ((dec->physaddr & 0x180000)==0x000000))
+ {
+ // DRAM. One wait state.
+ dec->function = MPGMDRAM;
+ dec->physaddr = dec->physaddr & 0x07ffff;
+ if (!m_turbo) set_wait(1);
+ return;
+ }
+
+ if ((dec->physaddr & 0x1e0000)==0x1e0000)
+ {
+ // 1 111. ..xx xxxx xxxx xxxx on-board eprom (16K)
+ // mirrored for f0, f2, f4, ...; f1, f3, f5, ... unless using PFM
+ dec->function = MPGMEPROM;
+ set_wait(0);
+ return;
+ }
+
+ // Route everything else to the P-Box
+ dec->physaddr = (dec->physaddr & 0x001fffff); // 21 bit address for Genmod
+ dec->function = MPGMBOX;
+
+ if (!m_turbo) set_wait(1);
+ // Check: Are waitstates completely turned off for turbo mode, or
+ // merely the waitstates for DRAM memory access and box access?
+
+ m_peribox->setaddress_dbin(space, dec->physaddr, read_mode);
+ return;
+ }
+ }
+ else
+ { // Write access
+ // Logical addresses
+ if (m_geneve_mode)
+ {
+ if ((offset & 0xfff1)==0xf100)
+ {
+ // 1 WS is always added; any pending video waitstates are canceled
+ dec->function = MLGVIDEO;
+ set_wait(1);
+ return;
+ }
+ if ((offset & 0xfff8)==0xf110)
+ {
+ dec->function = MLGMAPPER;
+ dec->offset = dec->offset & 0x0007;
+ set_wait(1);
+ return;
+ }
+ if ((offset & 0xfff1)==0xf120)
+ {
+ // Add 24 waitstates. This is an average value, as the
+ // waitstate generation seems to depend on an external timer of
+ // the sound chip
+ // TODO: do it properly with the use of READY
+ dec->function = MLGSOUND;
+ set_wait(24);
+ return;
+ }
+ if ((offset & 0xfff0)==0xf130)
+ {
+ dec->function = MLGCLOCK;
+ dec->offset = dec->offset & 0x00f;
+ set_wait(1);
+ return;
+ }
+ }
+ else
+ {
+ // TI mode
+ if ((offset & 0xfff8)==0x8000)
+ {
+ dec->function = MLTMAPPER;
+ dec->offset = dec->offset & 0x0007;
+ set_wait(1);
+ return;
+ }
+ if ((offset & 0xfff0)==0x8010)
+ {
+ dec->function = MLTCLOCK;
+ dec->offset = dec->offset & 0x00f;
+ set_wait(1);
+ return;
+ }
+ if ((offset & 0xfc01)==0x9c00)
+ {
+ dec->function = MLTGROM;
+ set_wait(1);
+ return;
+ }
+ if ((offset & 0xfc01)==0x8400)
+ {
+ // Add 24 waitstates. This is an approximation, as the
+ // waitstate generation seems to depend on an external timer of
+ // the sound chip
+ // TODO: do it properly with the use of READY-
+ dec->function = MLTSOUND;
+ set_wait(24);
+ return;
+ }
+ if ((offset & 0xfc01)==0x8c00)
+ {
+ // 1 WS is always added; any pending video waitstates are canceled
+ dec->function = MLTVIDEO;
+ set_wait(1);
+ return;
+ }
+
+ if ((offset & 0xfc01)==0x9400)
+ {
+ dec->function = MLTSPEECH;
+ dec->offset = dec->offset | ((m_genmod)? 0x170000 : 0x070000);
+ m_peribox->setaddress_dbin(space, dec->offset, read_mode);
+ set_wait(1);
+ return;
+ }
+ }
+
+ // Determine physical address
+ page = (dec->offset & 0xe000) >> 13;
+
+ if (m_direct_mode)
+ {
+ dec->physaddr = 0x1e0000; // points to boot eprom
+ }
+ else
+ {
+ if (!m_geneve_mode && page==3)
+ {
+ if (m_cartridge_size==0x4000)
+ {
+ m_cartridge_secondpage = ((dec->offset & 0x0002)!=0);
+ if (TRACE_WRITE) logerror("%s: Set cartridge page %02x\n", tag(), m_cartridge_secondpage);
+ set_wait(1);
+ return;
+ }
+ else
+ {
+ // writing into cartridge rom space (no bankswitching)
+ if ((((dec->offset & 0x1000)==0x0000) && !m_cartridge6_writable)
+ || (((dec->offset & 0x1000)==0x1000) && !m_cartridge7_writable))
+ {
+ logerror("%s: Writing to protected cartridge space %04x ignored\n", tag(), dec->offset);
+ return;
+ }
+ else
+ // TODO: Check whether secondpage is really ignored
+ dec->physaddr = 0x06c000;
+ }
+ }
+ else
+ dec->physaddr = (m_map[page] << 13);
+ }
+
+ dec->physaddr |= dec->offset & 0x1fff;
+
+ if (!m_genmod)
+ {
+ if ((dec->physaddr & 0x180000)==0x000000)
+ {
+ dec->function = MPGDRAM;
+ dec->physaddr = dec->physaddr & 0x07ffff;
+ set_wait(1);
+ return;
+ }
+ if ((dec->physaddr & 0x180000)==0x080000)
+ {
+ dec->function = MPGEXP;
+ set_wait(1);
+ return;
+ }
+
+ if ((dec->physaddr & 0x1e0000)==0x1e0000)
+ {
+ dec->function = MPGEPROM;
+ set_wait(0); // EPROM
+ return;
+ }
+ if ((dec->physaddr & 0x180000)==0x180000)
+ {
+ dec->function = MPGSRAM;
+ set_wait(0); // SRAM
+ return;
+ }
+
+ // Route everything else to the P-Box
+ // Add a wait state
+
+ // only AMA, AMB, AMC are used; AMD and AME are not used
+ dec->function = MPGBOX;
+ dec->physaddr = (dec->physaddr & 0x0007ffff); // 19 bit address
+ m_peribox->setaddress_dbin(space, dec->physaddr, read_mode);
+ set_wait(1);
+ }
+ else
+ {
+ // GenMod mode
+ if ((dec->physaddr & 0x1e0000)==0x1e0000)
+ { // EPROM, ignore (unless PFM)
+ dec->function = MPGMEPROM;
+ set_wait(0);
+ return;
+ }
+
+ if (m_timode && ((dec->physaddr & 0x180000)==0x000000))
+ {
+ dec->function = MPGMDRAM;
+ dec->physaddr = dec->physaddr & 0x07ffff;
+ if (!m_turbo) set_wait(1);
+ return;
+ }
+
+ // Route everything else to the P-Box
+ dec->function = MPGMBOX;
+ dec->physaddr = (dec->physaddr & 0x001fffff); // 21 bit address for Genmod
+ m_peribox->setaddress_dbin(space, dec->physaddr, read_mode);
+ if (!m_turbo) set_wait(1);
+ }
+ }
+}
+
+/*
+ Read from PFM.
+*/
+READ8_MEMBER( geneve_mapper_device::read_from_pfm )
+{
+ UINT8 value = 0;
+ if (!m_pfm_output_enable) return 0;
+
+ int address = (offset & 0x01ffff) | (m_pfm_bank<<17);
+
+ switch (m_pfm_mode)
+ {
+ case 1:
+ value = m_pfm512->read(space, address, mem_mask);
+ break;
+ case 2:
+ value = m_pfm512a->read(space, address, mem_mask);
+ break;
+ default:
+ logerror("%s: Illegal mode for reading PFM: %d\n", tag(), m_pfm_mode);
+ return 0;
+ }
+
+ if (TRACE_PFM) logerror("%s: Reading from PFM at address %05x -> %02x\n", tag(), address, value);
+ return value;
+}
+
+WRITE8_MEMBER( geneve_mapper_device::write_to_pfm )
+{
+ // Nota bene: The PFM must be write protected on startup, or the RESET
+ // of the 9995 will attempt to write the return vector into the flash EEPROM
+ int address = (offset & 0x01ffff) | (m_pfm_bank<<17);
+ if (TRACE_PFM) logerror("%s: Writing to PFM at address %05x <- %02x\n", tag(), address, data);
+
+ switch (m_pfm_mode)
+ {
+ case 1:
+ m_pfm512->write(space, address, data, mem_mask);
+ break;
+ case 2:
+ m_pfm512a->write(space, address, data, mem_mask);
+ break;
+ default:
+ logerror("%s: Illegal mode for writing to PFM: %d\n", tag(), m_pfm_mode);
+ }
+}
+
+
+/*
+ Accept the address passed over the address bus and decode it appropriately.
+ This decoding will later be used in the READ/WRITE member functions. Also,
+ we initiate wait state creation here.
+*/
+SETOFFSET_MEMBER( geneve_mapper_device::setoffset )
+{
+ if (TRACE_DETAIL) logerror("%s: setoffset = %04x\n", tag(), offset);
+ m_debug_no_ws = false;
+ decode(space, offset, m_read_mode, &m_decoded);
+}
+
+/*
+ The mapper is connected to the clock line in order to operate
+ the wait state counter.
+ The wait counter is decremented on each rising clock edge; when 0, the
+ READY line is asserted. However, there is a second counter which is used for
+ video wait states.
+ The READY line must be asserted when the wait counter reaches 0, but must be
+ cleared immediately again if the video counter has not reached 0.
+ (See comments at the file header: The additional video wait states do not
+ affect the video access itself but become effective after the access; if
+ the code runs on the chip, these wait states are ignored.)
+*/
+WRITE_LINE_MEMBER( geneve_mapper_device::clock_in )
+{
+ if (state==ASSERT_LINE)
+ {
+ // Rising edge
+ if (!m_ready_asserted)
+ {
+ if (m_waitcount > 0)
+ {
+ m_waitcount--;
+ if (m_waitcount == 0)
+ {
+ if (TRACE_CLOCK) logerror("%s: clock, READY asserted\n", tag());
+ m_ready(ASSERT_LINE);
+ m_ready_asserted = true;
+ }
+ else
+ {
+ if (TRACE_CLOCK) logerror("%s: clock\n", tag());
+ }
+ }
+ else
+ {
+ if (m_ext_waitcount > 0)
+ {
+ m_ext_waitcount--;
+ if (m_ext_waitcount == 0)
+ {
+ if (TRACE_CLOCK) logerror("%s: clock, READY asserted after video\n", tag());
+ m_ready(ASSERT_LINE);
+ m_ready_asserted = true;
+ }
+ else
+ {
+ if (TRACE_CLOCK) logerror("%s: vclock, ew=%d\n", tag(), m_ext_waitcount);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ // Falling edge
+ // Do we have video wait states? In that case, clear the line again
+ if ((m_waitcount == 0) && (m_ext_waitcount > 0) && m_ready_asserted)
+ {
+ if (TRACE_CLOCK) logerror("%s: clock, READY cleared for video\n", tag());
+ m_ready(CLEAR_LINE);
+ m_ready_asserted = false;
+ }
+ }
+}
+
+/*
+ We need the DBIN line for the setoffset operation.
+*/
+WRITE_LINE_MEMBER( geneve_mapper_device::dbin_in )
+{
+ m_read_mode = (state==ASSERT_LINE);
+ if (TRACE_DETAIL) logerror("%s: dbin = %02x\n", tag(), m_read_mode? 1:0);
+}
+
+/*
+ PFM expansion: Setting the bank.
+*/
+WRITE_LINE_MEMBER( geneve_mapper_device::pfm_select_lsb )
+{
+ if (state==ASSERT_LINE) m_pfm_bank |= 1;
+ else m_pfm_bank &= 0xfe;
+ if (TRACE_PFM) logerror("%s: Setting bank (l) = %d\n", tag(), m_pfm_bank);
+}
+
+WRITE_LINE_MEMBER( geneve_mapper_device::pfm_select_msb )
+{
+ if (state==ASSERT_LINE) m_pfm_bank |= 2;
+ else m_pfm_bank &= 0xfd;
+ if (TRACE_PFM) logerror("%s: Setting bank (u) = %d\n", tag(), m_pfm_bank);
+}
+
+WRITE_LINE_MEMBER( geneve_mapper_device::pfm_output_enable )
+{
+ // Negative logic
+ m_pfm_output_enable = (state==CLEAR_LINE);
+ if (TRACE_PFM) logerror("%s: PFM output %s\n", tag(), m_pfm_output_enable? "enable" : "disable");
+}
+
+//====================================================================
+// Common device lifecycle
+//====================================================================
+
+void geneve_mapper_device::device_start()
+{
+ // Get pointers
+ m_peribox = machine().device<bus8z_device>(PERIBOX_TAG);
+ m_keyboard = machine().device<geneve_keyboard_device>(GKEYBOARD_TAG);
+ m_video = machine().device<bus8z_device>(VIDEO_SYSTEM_TAG);
+ m_sound = machine().device<bus8z_device>(TISOUND_TAG);
+ m_clock = machine().device<mm58274c_device>(GCLOCK_TAG);
+
+ // PFM expansion
+ m_pfm512 = machine().device<at29c040_device>(PFM512_TAG);
+ m_pfm512a = machine().device<at29c040a_device>(PFM512A_TAG);
+
+ m_ready.resolve();
+
+ m_sram = machine().root_device().memregion(SRAM_TAG)->base();
+ m_dram = machine().root_device().memregion(DRAM_TAG)->base();
+ m_cpu = static_cast<tms9995_device*>(machine().device("maincpu"));
+
+ m_geneve_mode = false;
+ m_direct_mode = true;
+}
+
+void geneve_mapper_device::device_reset()
+{
+ m_extra_waitstates = false;
+ m_video_waitstates = true;
+ m_read_mode = false;
+ m_waitcount = 0;
+ m_ext_waitcount = 0;
+ m_ready_asserted = true;
+
+ m_geneve_mode =false;
+ m_direct_mode = true;
+ m_cartridge_size = 0x4000;
+ m_cartridge_secondpage = false;
+ m_cartridge6_writable = false;
+ m_cartridge7_writable = false;
+ m_grom_address = 0;
+ m_pfm_bank = 0;
+ m_pfm_output_enable = true;
+
+ // Clear map
+ for (int i=0; i < 8; i++) m_map[i] = 0;
+
+ m_genmod = false;
+
+ // Check which boot EPROM we are using (or PFM)
+ set_boot_rom(machine().root_device().ioport("BOOTROM")->read());
+
+ // Check for GenMod. We assume that GenMod can be combined with PFM.
+ if (machine().root_device().ioport("MODE")->read()!=0)
+ {
+ logerror("%s: Using GenMod modification\n", tag());
+ m_eprom = machine().root_device().memregion("maincpu")->base() + 0x8000;
+ if (m_eprom[0] != 0xf0)
+ {
+ fatalerror("genboard: GenMod boot ROM missing\n");
+ }
+ m_genmod = true;
+ m_turbo = ((machine().root_device().ioport("GENMODDIPS")->read() & GM_TURBO)!=0);
+ m_timode = ((machine().root_device().ioport("GENMODDIPS")->read() & GM_TIM)!=0);
+ }
+
+ switch (machine().root_device().ioport("SRAM")->read())
+ {
+/* 1 100. .... .... .... .... on-board sram (128K) -+
+ 1 101. .... .... .... .... on-board sram (128K) -+-- maximum SRAM expansion
+ 1 1100 .... .... .... .... on-board sram (64K) --+
+ 1 1101 0... .... .... .... on-board sram (32K) - additional 32 KiB required for MDOS 2.50s and higher
+ 1 1101 1... .... .... .... on-board sram (32K) - standard setup
+*/
+ case 0: // 32 KiB
+ m_sram_mask = 0x1f8000;
+ m_sram_val = 0x1d8000;
+ break;
+ case 1: // 64 KiB
+ m_sram_mask = 0x1f0000;
+ m_sram_val = 0x1d0000;
+ break;
+ case 2: // 384 KiB (actually 512 KiB, but the EPROM masks the upper 128 KiB)
+ m_sram_mask = 0x180000;
+ m_sram_val = 0x180000;
+ break;
+ }
+}
+
+const device_type GENEVE_MAPPER = &device_creator<geneve_mapper_device>;
+
+/****************************************************************************
+ Keyboard support
+****************************************************************************/
+
+static const char *const KEYNAMES[] = { "KEY0", "KEY1", "KEY2", "KEY3", "KEY4", "KEY5", "KEY6", "KEY7" };
+
+static const UINT8 MF1_CODE[0xe] =
+{
+ /* extended keys that are equivalent to non-extended keys */
+ 0x1c, /* keypad enter */
+ 0x1d, /* right control */
+ 0x38, /* alt gr */
+ // extra codes are 0x5b for Left Windows, 0x5c for Right Windows, 0x5d
+ // for Menu, 0x5e for power, 0x5f for sleep, 0x63 for wake, but I doubt
+ // any Geneve program would take advantage of these. */
+
+ // extended key that is equivalent to a non-extended key
+ // with shift off
+ 0x35, /* pad slash */
+
+ // extended keys that are equivalent to non-extended keys
+ // with numlock off
+ 0x47, /* home */
+ 0x48, /* up */
+ 0x49, /* page up */
+ 0x4b, /* left */
+ 0x4d, /* right */
+ 0x4f, /* end */
+ 0x50, /* down */
+ 0x51, /* page down */
+ 0x52, /* insert */
+ 0x53 /* delete */
+};
+
+geneve_keyboard_device::geneve_keyboard_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+: device_t(mconfig, GENEVE_KEYBOARD, "Geneve XT-style keyboard", tag, owner, clock, "geneve_keyboard", __FILE__),
+ m_interrupt(*this)
+{
+}
+
+void geneve_keyboard_device::post_in_key_queue(int keycode)
+{
+ m_key_queue[(m_key_queue_head + m_key_queue_length) % KEYQUEUESIZE] = keycode;
+ m_key_queue_length++;
+
+ if (TRACE_KEYBOARD) logerror("%s: Posting keycode %02x\n", tag(), keycode);
+}
+
+void geneve_keyboard_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ poll();
+}
+
+void geneve_keyboard_device::poll()
+{
+ UINT32 keystate;
+ UINT32 key_transitions;
+ int i, j;
+ int keycode;
+ int pressed;
+ if (TRACE_KEYBOARD) logerror("%s: Poll keyboard\n", tag());
+ if (m_key_reset) return;
+
+ /* Poll keyboard */
+ for (i = 0; (i < 4) && (m_key_queue_length <= (KEYQUEUESIZE-MAXKEYMSGLENGTH)); i++)
+ {
+ keystate = ioport(KEYNAMES[2*i])->read() | (ioport(KEYNAMES[2*i + 1])->read() << 16);
+ key_transitions = keystate ^ m_key_state_save[i];
+ if (key_transitions)
+ {
+ for (j = 0; (j < 32) && (m_key_queue_length <= (KEYQUEUESIZE-MAXKEYMSGLENGTH)); j++)
+ {
+ if ((key_transitions >> j) & 1)
+ {
+ keycode = (i << 5) | j;
+ pressed = ((keystate >> j) & 1);
+ if (pressed)
+ m_key_state_save[i] |= (1 << j);
+ else
+ m_key_state_save[i] &= ~ (1 << j);
+
+ /* Update auto-repeat */
+ if (pressed)
+ {
+ m_key_autorepeat_key = keycode;
+ m_key_autorepeat_timer = KEYAUTOREPEATDELAY+1;
+ }
+ else /*if (keycode == m_key_autorepeat_key)*/
+ m_key_autorepeat_key = 0;
+
+ // Release Fake Shift/Unshift if another key is pressed
+ // We do so if a key is released, though it is actually
+ // required only if it is a modifier key
+ /*if (pressed)*/
+ //{
+ if (m_key_fake_shift_state)
+ {
+ /* Fake shift release */
+ post_in_key_queue(0xe0);
+ post_in_key_queue(0xaa);
+ m_key_fake_shift_state = false;
+ }
+ if (m_key_fake_unshift_state)
+ {
+ /* Fake shift press */
+ post_in_key_queue(0xe0);
+ post_in_key_queue(0x2a);
+ m_key_fake_unshift_state = false;
+ }
+ //}
+
+ /* update shift and numlock state */
+ if ((keycode == 0x2a) || (keycode == 0x36))
+ m_key_real_shift_state = m_key_real_shift_state + (pressed ? +1 : -1);
+ if ((keycode == 0x1d) || (keycode == 0x61))
+ m_key_ctrl_state = m_key_ctrl_state + (pressed ? +1 : -1);
+ if ((keycode == 0x38) || (keycode == 0x62))
+ m_key_alt_state = m_key_alt_state + (pressed ? +1 : -1);
+ if ((keycode == 0x45) && pressed)
+ m_key_numlock_state = !m_key_numlock_state;
+
+ if ((keycode >= 0x60) && (keycode < 0x6e))
+ { /* simpler extended keys */
+ /* these keys are emulated */
+
+ if ((keycode >= 0x63) && pressed)
+ {
+ /* Handle shift state */
+ if (keycode == 0x63)
+ { /* non-shifted key */
+ if (m_key_real_shift_state!=0)
+ /* Fake shift unpress */
+ m_key_fake_unshift_state = true;
+ }
+ else /*if (keycode >= 0x64)*/
+ { /* non-numlock mode key */
+ if (m_key_numlock_state & (m_key_real_shift_state==0))
+ /* Fake shift press if numlock is active */
+ m_key_fake_shift_state = true;
+ else if ((!m_key_numlock_state) & (m_key_real_shift_state!=0))
+ /* Fake shift unpress if shift is down */
+ m_key_fake_unshift_state = true;
+ }
+
+ if (m_key_fake_shift_state)
+ {
+ post_in_key_queue(0xe0);
+ post_in_key_queue(0x2a);
+ }
+
+ if (m_key_fake_unshift_state)
+ {
+ post_in_key_queue(0xe0);
+ post_in_key_queue(0xaa);
+ }
+ }
+
+ keycode = MF1_CODE[keycode-0x60];
+ if (!pressed) keycode |= 0x80;
+ post_in_key_queue(0xe0);
+ post_in_key_queue(keycode);
+ }
+ else if (keycode == 0x6e)
+ { /* emulate Print Screen / System Request (F13) key */
+ /* this is a bit complex, as Alt+PrtScr -> SysRq */
+ /* Additionally, Ctrl+PrtScr involves no fake shift press */
+ if (m_key_alt_state!=0)
+ {
+ /* SysRq */
+ keycode = 0x54;
+ if (!pressed) keycode |= 0x80;
+ post_in_key_queue(keycode);
+ }
+ else
+ {
+ /* Handle shift state */
+ if (pressed && (m_key_real_shift_state==0) && (m_key_ctrl_state==0))
+ { /* Fake shift press */
+ post_in_key_queue(0xe0);
+ post_in_key_queue(0x2a);
+ m_key_fake_shift_state = true;
+ }
+
+ keycode = 0x37;
+ if (!pressed) keycode |= 0x80;
+ post_in_key_queue(0xe0);
+ post_in_key_queue(keycode);
+ }
+ }
+ else if (keycode == 0x6f)
+ { // emulate pause (F15) key
+ // this is a bit complex, as Pause -> Ctrl+NumLock and
+ // Ctrl+Pause -> Ctrl+ScrLock. Furthermore, there is no
+ // repeat or release.
+ if (pressed)
+ {
+ if (m_key_ctrl_state!=0)
+ {
+ post_in_key_queue(0xe0);
+ post_in_key_queue(0x46);
+ post_in_key_queue(0xe0);
+ post_in_key_queue(0xc6);
+ }
+ else
+ {
+ post_in_key_queue(0xe1);
+ post_in_key_queue(0x1d);
+ post_in_key_queue(0x45);
+ post_in_key_queue(0xe1);
+ post_in_key_queue(0x9d);
+ post_in_key_queue(0xc5);
+ }
+ }
+ }
+ else
+ {
+ if (!pressed) keycode |= 0x80;
+ post_in_key_queue(keycode);
+ }
+ signal_when_key_available();
+ }
+ }
+ }
+ }
+
+ /* Handle auto-repeat */
+ if ((m_key_queue_length <= (KEYQUEUESIZE-MAXKEYMSGLENGTH)) && (m_key_autorepeat_key!=0) && (--m_key_autorepeat_timer == 0))
+ {
+ if ((m_key_autorepeat_key >= 0x60) && (m_key_autorepeat_key < 0x6e))
+ {
+ post_in_key_queue(0xe0);
+ post_in_key_queue(MF1_CODE[m_key_autorepeat_key-0x60]);
+ }
+ else if (m_key_autorepeat_key == 0x6e)
+ {
+ if (m_key_alt_state!=0)
+ post_in_key_queue(0x54);
+ else
+ {
+ post_in_key_queue(0xe0);
+ post_in_key_queue(0x37);
+ }
+ }
+ else if (m_key_autorepeat_key == 0x6f)
+ ;
+ else
+ {
+ post_in_key_queue(m_key_autorepeat_key);
+ }
+ signal_when_key_available();
+ m_key_autorepeat_timer = KEYAUTOREPEATRATE;
+ }
+}
+
+UINT8 geneve_keyboard_device::get_recent_key()
+{
+ if (m_key_in_buffer) return m_key_queue[m_key_queue_head];
+ else return 0;
+}
+
+void geneve_keyboard_device::signal_when_key_available()
+{
+ // if keyboard reset is not asserted, and key clock is enabled, and key
+ // buffer clear is disabled, and key queue is not empty. */
+ if ((!m_key_reset) && (m_keyboard_clock) && (m_keep_keybuf) && (m_key_queue_length != 0))
+ {
+ if (TRACE_KEYBOARD) logerror("%s: Signalling key available\n", tag());
+ m_interrupt(ASSERT_LINE);
+ m_key_in_buffer = true;
+ }
+}
+
+WRITE_LINE_MEMBER( geneve_keyboard_device::clock_control )
+{
+ bool rising_edge = (!m_keyboard_clock && (state==ASSERT_LINE));
+ m_keyboard_clock = (state==ASSERT_LINE);
+ if (TRACE_KEYBOARD) logerror("%s: Keyboard clock_control state=%d\n", tag(), m_keyboard_clock);
+ if (rising_edge)
+ signal_when_key_available();
+}
+
+WRITE_LINE_MEMBER( geneve_keyboard_device::send_scancodes )
+{
+ bool rising_edge = (!m_keep_keybuf && (state==ASSERT_LINE));
+ bool falling_edge = (m_keep_keybuf && (state==CLEAR_LINE));
+ m_keep_keybuf = (state==ASSERT_LINE);
+
+ if (rising_edge) signal_when_key_available();
+ else
+ {
+ if (falling_edge)
+ {
+ if (m_key_queue_length != 0)
+ {
+ m_key_queue_head = (m_key_queue_head + 1) % KEYQUEUESIZE;
+ m_key_queue_length--;
+ }
+ /* clear keyboard interrupt */
+ m_interrupt(CLEAR_LINE);
+ m_key_in_buffer = false;
+ }
+ }
+}
+
+WRITE_LINE_MEMBER( geneve_keyboard_device::reset_line )
+{
+ m_key_reset = !(state==ASSERT_LINE);
+
+ if (m_key_reset)
+ {
+ /* reset -> clear keyboard key queue, but not geneve key buffer */
+ m_key_queue_length = (m_key_in_buffer)? 1 : 0;
+ m_key_queue_head = 0;
+ memset(m_key_state_save, 0, sizeof(m_key_state_save));
+ m_key_numlock_state = false;
+ m_key_ctrl_state = 0;
+ m_key_alt_state = 0;
+ m_key_real_shift_state = 0;
+ m_key_fake_shift_state = false;
+ m_key_fake_unshift_state = false;
+ m_key_autorepeat_key = 0;
+ }
+}
+
+void geneve_keyboard_device::device_start()
+{
+ m_timer = timer_alloc(0);
+ m_interrupt.resolve();
+}
+
+void geneve_keyboard_device::device_reset()
+{
+ m_key_in_buffer = false;
+ reset_line(CLEAR_LINE);
+ m_key_queue_length = 0;
+ m_key_reset = true;
+ m_keyboard_clock = false;
+ m_keep_keybuf = false;
+ m_timer->adjust(attotime::from_usec(1), 0, attotime::from_hz(120));
+}
+
+INPUT_PORTS_START( genkeys )
+ PORT_START("KEY0") /* IN3 */
+ PORT_BIT ( 0x0001, 0x0000, IPT_UNUSED ) /* unused scancode 0 */
+ PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC) /* Esc 01 81 */
+ PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1 !") PORT_CODE(KEYCODE_1) /* 1 02 82 */
+ PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2 @") PORT_CODE(KEYCODE_2) /* 2 03 83 */
+ PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3 #") PORT_CODE(KEYCODE_3) /* 3 04 84 */
+ PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4 $") PORT_CODE(KEYCODE_4) /* 4 05 85 */
+ PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5 %") PORT_CODE(KEYCODE_5) /* 5 06 86 */
+ PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6 ^") PORT_CODE(KEYCODE_6) /* 6 07 87 */
+ PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7 &") PORT_CODE(KEYCODE_7) /* 7 08 88 */
+ PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8 *") PORT_CODE(KEYCODE_8) /* 8 09 89 */
+ PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9 (") PORT_CODE(KEYCODE_9) /* 9 0A 8A */
+ PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0 )") PORT_CODE(KEYCODE_0) /* 0 0B 8B */
+ PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("- _") PORT_CODE(KEYCODE_MINUS) /* - 0C 8C */
+ PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("= +") PORT_CODE(KEYCODE_EQUALS) /* = 0D 8D */
+ PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) /* Backspace 0E 8E */
+ PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Tab") PORT_CODE(KEYCODE_TAB) /* Tab 0F 8F */
+
+ PORT_START("KEY1") /* IN4 */
+ PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q) /* Q 10 90 */
+ PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W) /* W 11 91 */
+ PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) /* E 12 92 */
+ PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) /* R 13 93 */
+ PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T) /* T 14 94 */
+ PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y) /* Y 15 95 */
+ PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U) /* U 16 96 */
+ PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I) /* I 17 97 */
+ PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O) /* O 18 98 */
+ PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) /* P 19 99 */
+ PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("[ {") PORT_CODE(KEYCODE_OPENBRACE) /* [ 1A 9A */
+ PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("] }") PORT_CODE(KEYCODE_CLOSEBRACE) /* ] 1B 9B */
+ PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) /* Enter 1C 9C */
+ PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L-Ctrl") PORT_CODE(KEYCODE_LCONTROL) /* Left Ctrl 1D 9D */
+ PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) /* A 1E 9E */
+ PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) /* S 1F 9F */
+
+ PORT_START("KEY2") /* IN5 */
+ PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) /* D 20 A0 */
+ PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) /* F 21 A1 */
+ PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) /* G 22 A2 */
+ PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H) /* H 23 A3 */
+ PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J) /* J 24 A4 */
+ PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K) /* K 25 A5 */
+ PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) /* L 26 A6 */
+ PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("; :") PORT_CODE(KEYCODE_COLON) /* ; 27 A7 */
+ PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("' \"") PORT_CODE(KEYCODE_QUOTE) /* ' 28 A8 */
+ PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("` ~") PORT_CODE(KEYCODE_TILDE) /* ` 29 A9 */
+ PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L-Shift") PORT_CODE(KEYCODE_LSHIFT) /* Left Shift 2A AA */
+ PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("\\ |") PORT_CODE(KEYCODE_BACKSLASH) /* \ 2B AB */
+ PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z) /* Z 2C AC */
+ PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X) /* X 2D AD */
+ PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) /* C 2E AE */
+ PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) /* V 2F AF */
+
+ PORT_START("KEY3") /* IN6 */
+ PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) /* B 30 B0 */
+ PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) /* N 31 B1 */
+ PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) /* M 32 B2 */
+ PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(", <") PORT_CODE(KEYCODE_COMMA) /* , 33 B3 */
+ PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(". >") PORT_CODE(KEYCODE_STOP) /* . 34 B4 */
+ PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH) /* / 35 B5 */
+ PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R-Shift") PORT_CODE(KEYCODE_RSHIFT) /* Right Shift 36 B6 */
+ PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP * (PrtScr)") PORT_CODE(KEYCODE_ASTERISK ) /* Keypad * (PrtSc) 37 B7 */
+ PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Alt") PORT_CODE(KEYCODE_LALT) /* Left Alt 38 B8 */
+ PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Space") PORT_CODE(KEYCODE_SPACE) /* Space 39 B9 */
+ PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Caps") PORT_CODE(KEYCODE_CAPSLOCK) /* Caps Lock 3A BA */
+ PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F1") PORT_CODE(KEYCODE_F1) /* F1 3B BB */
+ PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2) /* F2 3C BC */
+ PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F3") PORT_CODE(KEYCODE_F3) /* F3 3D BD */
+ PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F4") PORT_CODE(KEYCODE_F4) /* F4 3E BE */
+ PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F5") PORT_CODE(KEYCODE_F5) /* F5 3F BF */
+
+ PORT_START("KEY4") /* IN7 */
+ PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F6") PORT_CODE(KEYCODE_F6) /* F6 40 C0 */
+ PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F7") PORT_CODE(KEYCODE_F7) /* F7 41 C1 */
+ PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F8") PORT_CODE(KEYCODE_F8) /* F8 42 C2 */
+ PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F9") PORT_CODE(KEYCODE_F9) /* F9 43 C3 */
+ PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F10") PORT_CODE(KEYCODE_F10) /* F10 44 C4 */
+ PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("NumLock") PORT_CODE(KEYCODE_NUMLOCK) /* Num Lock 45 C5 */
+ PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ScrLock (F14)") PORT_CODE(KEYCODE_SCRLOCK) /* Scroll Lock 46 C6 */
+ PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 7 (Home)") PORT_CODE(KEYCODE_7_PAD ) /* Keypad 7 (Home) 47 C7 */
+ PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 8 (Up)") PORT_CODE(KEYCODE_8_PAD ) /* Keypad 8 (Up arrow) 48 C8 */
+ PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 9 (PgUp)") PORT_CODE(KEYCODE_9_PAD ) /* Keypad 9 (PgUp) 49 C9 */
+ PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP -") PORT_CODE(KEYCODE_MINUS_PAD) /* Keypad - 4A CA */
+ PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 4 (Left)") PORT_CODE(KEYCODE_4_PAD ) /* Keypad 4 (Left arrow) 4B CB */
+ PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 5") PORT_CODE(KEYCODE_5_PAD) /* Keypad 5 4C CC */
+ PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 6 (Right)") PORT_CODE(KEYCODE_6_PAD ) /* Keypad 6 (Right arrow) 4D CD */
+ PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP +") PORT_CODE(KEYCODE_PLUS_PAD) /* Keypad + 4E CE */
+ PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 1 (End)") PORT_CODE(KEYCODE_1_PAD ) /* Keypad 1 (End) 4F CF */
+
+ PORT_START("KEY5") /* IN8 */
+ PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 2 (Down)") PORT_CODE(KEYCODE_2_PAD ) /* Keypad 2 (Down arrow) 50 D0 */
+ PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 3 (PgDn)") PORT_CODE(KEYCODE_3_PAD ) /* Keypad 3 (PgDn) 51 D1 */
+ PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP 0 (Ins)") PORT_CODE(KEYCODE_0_PAD ) /* Keypad 0 (Ins) 52 D2 */
+ PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KP . (Del)") PORT_CODE(KEYCODE_DEL_PAD ) /* Keypad . (Del) 53 D3 */
+ PORT_BIT ( 0x0030, 0x0000, IPT_UNUSED )
+ PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(84/102)\\") PORT_CODE(KEYCODE_BACKSLASH2) /* Backslash 2 56 D6 */
+ PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)F11") PORT_CODE(KEYCODE_F11) /* F11 57 D7 */
+ PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)F12") PORT_CODE(KEYCODE_F12) /* F12 58 D8 */
+ PORT_BIT ( 0xfe00, 0x0000, IPT_UNUSED )
+
+ PORT_START("KEY6") /* IN9 */
+ PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)KP Enter") PORT_CODE(KEYCODE_ENTER_PAD) /* PAD Enter 60 e0 */
+ PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)R-Control") PORT_CODE(KEYCODE_RCONTROL) /* Right Control 61 e1 */
+ PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)ALTGR") PORT_CODE(KEYCODE_RALT) /* ALTGR 64 e4 */
+
+ PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)KP /") PORT_CODE(KEYCODE_SLASH_PAD) /* PAD Slash 62 e2 */
+
+ PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Home") PORT_CODE(KEYCODE_HOME) /* Home 66 e6 */
+ PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Cursor Up") PORT_CODE(KEYCODE_UP) /* Up 67 e7 */
+ PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Page Up") PORT_CODE(KEYCODE_PGUP) /* Page Up 68 e8 */
+ PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Cursor Left") PORT_CODE(KEYCODE_LEFT) /* Left 69 e9 */
+ PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Cursor Right") PORT_CODE(KEYCODE_RIGHT) /* Right 6a ea */
+ PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)End") PORT_CODE(KEYCODE_END) /* End 6b eb */
+ PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Cursor Down") PORT_CODE(KEYCODE_DOWN) /* Down 6c ec */
+ PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Page Down") PORT_CODE(KEYCODE_PGDN) /* Page Down 6d ed */
+ PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Insert") PORT_CODE(KEYCODE_INSERT) /* Insert 6e ee */
+ PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Delete") PORT_CODE(KEYCODE_DEL) /* Delete 6f ef */
+
+ PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)PrtScr (F13)") PORT_CODE(KEYCODE_PRTSCR) /* Print Screen 63 e3 */
+ PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(101)Pause (F15)") PORT_CODE(KEYCODE_PAUSE) /* Pause 65 e5 */
+
+ PORT_START("KEY7") /* IN10 */
+ PORT_BIT ( 0xffff, 0x0000, IPT_UNUSED )
+#if 0
+ PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Print Screen") PORT_CODE(KEYCODE_PRTSCR) /* Print Screen alternate 77 f7 */
+ PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left Win") /* Left Win 7d fd */
+ PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right Win") /* Right Win 7e fe */
+ PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Menu") /* Menu 7f ff */
+#endif
+INPUT_PORTS_END
+
+ioport_constructor geneve_keyboard_device::device_input_ports() const
+{
+ return INPUT_PORTS_NAME( genkeys );
+}
+
+const device_type GENEVE_KEYBOARD = &device_creator<geneve_keyboard_device>;
+
+/****************************************************************************
+ Mouse support
+****************************************************************************/
+
+geneve_mouse_device::geneve_mouse_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+: device_t(mconfig, GENEVE_MOUSE, "Geneve mouse", tag, owner, clock, "geneve_mouse", __FILE__)
+{
+}
+
+line_state geneve_mouse_device::left_button()
+{
+ return ((ioport("MOUSE0")->read() & 0x04)!=0)? ASSERT_LINE : CLEAR_LINE;
+}
+
+void geneve_mouse_device::poll()
+{
+ int new_mx, new_my;
+ int delta_x, delta_y, buttons;
+
+ buttons = ioport("MOUSE0")->read();
+ new_mx = ioport("MOUSEX")->read();
+ new_my = ioport("MOUSEY")->read();
+
+ /* compute x delta */
+ delta_x = new_mx - m_last_mx;
+
+ /* check for wrap */
+ if (delta_x > 0x80)
+ delta_x = 0x100-delta_x;
+ if (delta_x < -0x80)
+ delta_x = -0x100-delta_x;
+
+ m_last_mx = new_mx;
+
+ /* compute y delta */
+ delta_y = new_my - m_last_my;
+
+ /* check for wrap */
+ if (delta_y > 0x80)
+ delta_y = 0x100-delta_y;
+ if (delta_y < -0x80)
+ delta_y = -0x100-delta_y;
+
+ m_last_my = new_my;
+
+ // only middle and right button go to V9938
+ m_v9938->update_mouse_state(delta_x, delta_y, buttons & 0x03);
+}
+
+INPUT_PORTS_START( genmouse )
+ PORT_START("MOUSEX") /* Mouse - X AXIS */
+ PORT_BIT( 0xff, 0x00, IPT_TRACKBALL_X) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_PLAYER(1)
+
+ PORT_START("MOUSEY") /* Mouse - Y AXIS */
+ PORT_BIT( 0xff, 0x00, IPT_TRACKBALL_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_PLAYER(1)
+
+ PORT_START("MOUSE0") /* mouse buttons */
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Left mouse button")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("Right mouse button")
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON3) PORT_NAME("Middle mouse button")
+INPUT_PORTS_END
+
+void geneve_mouse_device::device_start()
+{
+ m_v9938 = machine().device<v9938_device>(VDP_TAG);
+}
+
+void geneve_mouse_device::device_reset()
+{
+ m_last_mx = 0;
+ m_last_my = 0;
+}
+
+ioport_constructor geneve_mouse_device::device_input_ports() const
+{
+ return INPUT_PORTS_NAME( genmouse );
+}
+
+const device_type GENEVE_MOUSE = &device_creator<geneve_mouse_device>;