mirror of
https://github.com/holub/mame
synced 2025-06-03 19:36:26 +03:00
806 lines
27 KiB
C
806 lines
27 KiB
C
/*
|
|
MESS Driver for the Myarc Geneve 9640.
|
|
|
|
The Geneve has two operation modes. One is compatible with the TI-99/4a,
|
|
the other is not.
|
|
|
|
|
|
General architecture:
|
|
|
|
TMS9995@12MHz (including 256 bytes of on-chip 16-bit RAM and a timer),
|
|
V9938, SN76496 (compatible with TMS9919), TMS9901, MM58274 RTC, 512 kbytes
|
|
of 1-wait-state CPU RAM (expandable to almost 2 Mbytes), 32 kbytes of
|
|
0-wait-state CPU RAM (expandable to 64 kbytes), 128 kbytes of VRAM
|
|
(expandable to 192 kbytes).
|
|
|
|
|
|
Memory map:
|
|
|
|
64-kbyte CPU address space is mapped to a 2-Mbyte virtual address space.
|
|
8 8-kbyte pages are available simultaneously, out of a total of 256.
|
|
|
|
Page map (regular console):
|
|
>00->3f: 512kbytes of CPU RAM (pages >36 and >37 are used to emulate
|
|
cartridge CPU ROMs in ti99 mode, and pages >38 through >3f are used to
|
|
emulate console and cartridge GROMs in ti99 mode)
|
|
>40->7f: optional Memex RAM (except >7a and >7c that are mirrors of >ba and
|
|
>bc?)
|
|
>80->b7: PE-bus space using spare address lines (AMA-AMC)? Used by RAM
|
|
extension (Memex or PE-Bus 512k card).
|
|
>b8->bf: PE-bus space (most useful pages are >ba: DSR space and >bc: speech
|
|
synthesizer page; other pages are used by RAM extension)
|
|
>c0->e7: optional Memex RAM
|
|
>e8->ef: 64kbytes of 0-wait-state RAM (with 32kbytes of SRAM installed,
|
|
only even pages (>e8, >ea, >ec and >ee) are available)
|
|
>f0->f1: boot ROM
|
|
>f2->fe: optional Memex RAM? (except >fa and >fc that are mirrors of >ba
|
|
and >bc?)
|
|
>ff: always empty?
|
|
|
|
Page map (genmod console):
|
|
>00->39, >40->b9, >bb, >bd->ef, f2->fe: Memex RAM (except >3a, >3c, >7a,
|
|
>7c, >fa and >fc that are mirrors of >ba and >bc?) (I don't know if
|
|
>e8->ef is on the Memex board or the Geneve motherboard)
|
|
>ba: DSR space
|
|
>bc: speech synthesizer space
|
|
>f0->f1: boot ROM
|
|
>ff: always empty?
|
|
|
|
>00->3f: switch-selectable(?): 512kbytes of onboard RAM (1-wait-state DRAM)
|
|
or first 512kbytes of the Memex Memory board (0-wait-state SRAM). The
|
|
latter setting is incompatible with TI mode.
|
|
|
|
Note that >bc is actually equivalent to >8000->9fff on the /4a bus,
|
|
therefore the speech synthesizer is only available at offsets >1800->1bff
|
|
(read port) and >1c00->1fff (write port). OTOH, if you installed a FORTI
|
|
sound card, it will be available in the same page at offsets >0400->7ff.
|
|
|
|
|
|
Unpaged locations (ti99 mode):
|
|
>8000->8007: memory page registers (>8000 for page 0, >8001 for page 1,
|
|
etc. register >8003 is ignored (this page is hard-wired to >36->37).
|
|
>8008: key buffer
|
|
>8009->800f: ???
|
|
>8010->801f: clock chip
|
|
>8400->9fff: sound, VDP, speech, and GROM registers (according to one
|
|
source, the GROM and sound registers are only available if page >3
|
|
is mapped in at location >c000 (register 6). I am not sure the Speech
|
|
registers are implemented, though I guess they are.)
|
|
Note that >8020->83ff is mapped to regular CPU RAM according to map
|
|
register 4.
|
|
|
|
Unpaged locations (native mode):
|
|
>f100: VDP data port (read/write)
|
|
>f102: VDP address/status port (read/write)
|
|
>f104: VDP port 2
|
|
>f106: VDP port 3
|
|
>f108->f10f: VDP mirror (used by Barry Boone's converted Tomy cartridges)
|
|
>f110->f117: memory page registers (>f110 for page 0, >f111 for page 1,
|
|
etc.)
|
|
>f118: key buffer
|
|
>f119->f11f: ???
|
|
>f120: sound chip
|
|
>f121->f12f: ???
|
|
>f130->f13f: clock chip
|
|
|
|
Unpaged locations (tms9995 locations):
|
|
>f000->f0fb and >fffc->ffff: tms9995 RAM
|
|
>fffa->fffb: tms9995 timer register
|
|
Note: accessing tms9995 locations will also read/write corresponding paged
|
|
locations.
|
|
|
|
|
|
GROM emulator:
|
|
|
|
The GPL interface is accessible only in TI99 mode. GPL data is fetched
|
|
from pages >38->3f. It uses a straight 16-bit address pointer. The
|
|
address pointer is incremented when the read data and write data ports
|
|
are accessed, and when the second byte of the GPL address is written. A
|
|
weird consequence of this is the fact that GPL data is always off by one
|
|
byte, i.e. GPL bytes 0, 1, 2... are stored in bytes 1, 2, 3... of pages
|
|
>38->3f (byte 0 of page >38 corresponds to GPL address >ffff).
|
|
|
|
I think that I have once read that the geneve GROM emulator does not
|
|
emulate wrap-around within a GROM, i.e. address >1fff is followed by >2000
|
|
(instead of >0000 with a real GROM).
|
|
|
|
There are two ways to implement GPL address load and store.
|
|
One is maintaining 2 flags (one for address read and one for address write)
|
|
that tell if you are accessing address LSB: these flags must be cleared
|
|
whenever data is read, and the read flag must be cleared when the write
|
|
address port is written to.
|
|
The other is to shift the register and always read/write the MSByte of the
|
|
address pointer. The LSByte is copied to the MSbyte when the address is
|
|
read, whereas the former MSByte is copied to the LSByte when the address is
|
|
written. The address pointer must be incremented after the address is
|
|
written to. It will not harm if it is incremented after the address is
|
|
read, provided the LSByte has been cleared.
|
|
|
|
|
|
Cartridge emulator:
|
|
|
|
The cartridge interface is in the >6000->7fff area.
|
|
|
|
If CRU bit @>F7C is set, the cartridge area is always mapped to virtual
|
|
page >36. Writes in the >6000->6fff area are ignored if the CRU bit @>F7D
|
|
is 0, whereas writes in the >7000->7fff area are ignored if the CRU bit
|
|
@>F7E is 0.
|
|
|
|
If CRU bit @>F7C is clear, the cartridge area is mapped either to virtual
|
|
page >36 or to page >37 according to which page is currently selected.
|
|
Writing data to address >6000->7fff will select page >36 if A14 is 0,
|
|
>37 if A14 is 1.
|
|
|
|
|
|
CRU map:
|
|
|
|
Base >0000: tms9901
|
|
tms9901 pin assignment:
|
|
int1: external interrupt (INTA on PE-bus) (connected to tms9995 (int4/EC)*
|
|
pin as well)
|
|
int2: VDP interrupt
|
|
int3-int7: joystick port inputs (fire, left, right, down, up)
|
|
int8: keyboard interrupt (asserted when key buffer full)
|
|
int9/p13: unused
|
|
int10/p12: third mouse button
|
|
int11/p11: clock interrupt?
|
|
int12/p10: INTB from PE-bus
|
|
int13/p9: (used as output)
|
|
int14/p8: unused
|
|
int15/p7: (used as output)
|
|
p0: PE-bus reset line
|
|
p1: VDP reset line
|
|
p2: joystick select (0 for joystick 1, 1 for joystick 2)
|
|
p3-p5: unused
|
|
p6: keyboard reset line
|
|
p7/int15: external mem cycles 0=long, 1=short
|
|
p9/int13: vdp wait cycles 1=add 15 cycles, 0=add none
|
|
|
|
Base >1EE0 (>0F70): tms9995 flags and geneve mode register
|
|
bits 0-1: tms9995 flags
|
|
Bits 2-4: tms9995 interrupt state register
|
|
Bits 5-15: tms9995 user flags - overlaps geneve mode, but hopefully the
|
|
geneve registers are write-only, so no real conflict happens
|
|
TMS9995 user flags:
|
|
Bit 5: 0 if NTSC, 1 if PAL video
|
|
Bit 6: unused???
|
|
Bit 7: some keyboard flag, set to 1 if caps is on
|
|
Geneve gate array + TMS9995 user flags:
|
|
Bit 8: 1 = allow keyboard clock
|
|
Bit 9: 0 = clear keyboard input buffer, 1 = allow keyboard buffer to be
|
|
loaded
|
|
Bit 10: 1 = geneve mode, 0 = ti99 mode
|
|
Bit 11: 1 = ROM mode, 0 = map mode
|
|
Bit 12: 0 = Enable cartridge paging
|
|
Bit 13: 0 = protect cartridge range >6000->6fff
|
|
Bit 14: 0 = protect cartridge range >7000->7fff
|
|
bit 15: 1 = add 1 extra wait state when accessing 0-wait-state SRAM???
|
|
|
|
|
|
Keyboard interface:
|
|
|
|
The XT keyboard interface is described in various places on the internet,
|
|
like (http://www-2.cs.cmu.edu/afs/cs/usr/jmcm/www/info/key2.txt). It is a
|
|
synchronous unidirectional serial interface: the data line is driven by the
|
|
keyboard to send data to the CPU; the CTS/clock line has a pull up resistor
|
|
and can be driven low by both keyboard and CPU. To send data to the CPU,
|
|
the keyboard pulses the clock line low 9 times, and the Geneve samples all
|
|
8 bits of data (plus one start bit) on each falling edge of the clock.
|
|
When the key code buffer is full, the Geneve gate array asserts the kbdint*
|
|
line (connected to 9901 INT8*). The Geneve gate array will hold the
|
|
CTS/clock line low as long as the keyboard buffer is full or CRU bit @>F78
|
|
is 0. Writing a 0 to >F79 will clear the Geneve keyboard buffer, and
|
|
writing a 1 will resume normal operation: you need to write a 0 to >F78
|
|
before clearing >F79, or the keyboard will be enabled to send data the gate
|
|
array when >F79 is is set to 0, and any such incoming data from the
|
|
keyboard will be cleared as soon as it is buffered by the gate array.
|
|
|
|
Original version 2003 by Raphael Nabet
|
|
|
|
Rewritten 2012 by Michael Zapf
|
|
*/
|
|
|
|
|
|
#include "emu.h"
|
|
#include "cpu/tms9900/tms9995.h"
|
|
#include "machine/tms9901.h"
|
|
#include "machine/mm58274c.h"
|
|
#include "sound/sn76496.h"
|
|
|
|
#include "machine/ti99/genboard.h"
|
|
#include "machine/ti99/peribox.h"
|
|
#include "machine/ti99/videowrp.h"
|
|
|
|
#include "machine/ti99/joyport.h"
|
|
|
|
#define VERBOSE 1
|
|
#define LOG logerror
|
|
|
|
#define SRAM_SIZE 384*1024 // maximum SRAM expansion on-board
|
|
#define DRAM_SIZE 512*1024
|
|
|
|
class geneve_state : public driver_device
|
|
{
|
|
public:
|
|
geneve_state(const machine_config &mconfig, device_type type, const char *tag)
|
|
: driver_device(mconfig, type, tag) { }
|
|
|
|
// CRU (Communication Register Unit) handling
|
|
DECLARE_READ8_MEMBER(cruread);
|
|
DECLARE_WRITE8_MEMBER(cruwrite);
|
|
|
|
// Connections with the system interface TMS9901
|
|
DECLARE_READ8_MEMBER(read_by_9901);
|
|
DECLARE_WRITE_LINE_MEMBER(peripheral_bus_reset);
|
|
DECLARE_WRITE_LINE_MEMBER(VDP_reset);
|
|
DECLARE_WRITE_LINE_MEMBER(joystick_select);
|
|
DECLARE_WRITE_LINE_MEMBER(extbus_wait_states);
|
|
DECLARE_WRITE_LINE_MEMBER(video_wait_states);
|
|
|
|
DECLARE_WRITE_LINE_MEMBER(clock_out);
|
|
DECLARE_WRITE8_MEMBER(external_operation);
|
|
|
|
DECLARE_WRITE8_MEMBER(tms9901_interrupt);
|
|
|
|
DECLARE_WRITE_LINE_MEMBER( keyboard_interrupt );
|
|
|
|
geneve_keyboard_device* m_keyboard;
|
|
geneve_mouse_device* m_mouse;
|
|
tms9901_device* m_tms9901;
|
|
geneve_mapper_device* m_mapper;
|
|
peribox_device* m_peribox;
|
|
tms9995_device* m_cpu;
|
|
joyport_device* m_joyport;
|
|
|
|
DECLARE_WRITE_LINE_MEMBER( inta );
|
|
DECLARE_WRITE_LINE_MEMBER( intb );
|
|
DECLARE_WRITE_LINE_MEMBER( ext_ready );
|
|
DECLARE_WRITE_LINE_MEMBER( mapper_ready );
|
|
|
|
DECLARE_DRIVER_INIT(geneve);
|
|
virtual void machine_start();
|
|
virtual void machine_reset();
|
|
TIMER_DEVICE_CALLBACK_MEMBER(geneve_hblank_interrupt);
|
|
|
|
void set_tms9901_INT2_from_v9938(v99x8_device &vdp, int state);
|
|
|
|
line_state m_inta;
|
|
line_state m_intb;
|
|
line_state m_int2;
|
|
line_state m_keyint;
|
|
line_state m_video_wait; // reflects the line to the mapper for CRU query
|
|
|
|
int m_ready_line, m_ready_line1;
|
|
|
|
private:
|
|
//int m_joystick_select;
|
|
// Some values to keep. Rest is on the geneve_mapper.
|
|
};
|
|
|
|
/*
|
|
Memory map
|
|
*/
|
|
|
|
static ADDRESS_MAP_START(memmap, AS_PROGRAM, 8, geneve_state)
|
|
AM_RANGE(0x0000, 0xffff) AM_DEVREADWRITE(GMAPPER_TAG, geneve_mapper_device, readm, writem)
|
|
ADDRESS_MAP_END
|
|
|
|
/*
|
|
CRU map
|
|
The TMS9901 is fully decoded, no mirroring, so we have 32 bits for it,
|
|
and the rest goes to the board (and from there to the PEB)
|
|
TMS9995 has a full 15-bit CRU bit address space (attached to A0-A14)
|
|
TODO: Check whether A0-A2 are available for CRU addressing since those
|
|
bits are usually routed through the mapper first.
|
|
*/
|
|
static ADDRESS_MAP_START(crumap, AS_IO, 8, geneve_state)
|
|
AM_RANGE(0x0000, 0x0003) AM_DEVREAD(TMS9901_TAG, tms9901_device, read)
|
|
AM_RANGE(0x0000, 0x0fff) AM_READ( cruread )
|
|
|
|
AM_RANGE(0x0000, 0x001f) AM_DEVWRITE(TMS9901_TAG, tms9901_device, write)
|
|
AM_RANGE(0x0000, 0x7fff) AM_WRITE( cruwrite )
|
|
ADDRESS_MAP_END
|
|
|
|
/* TI joysticks. The keyboard is implemented in genboard.c. */
|
|
static INPUT_PORTS_START(geneve)
|
|
|
|
PORT_START( "MODE" )
|
|
PORT_CONFNAME( 0x01, 0x00, "Operating mode" ) PORT_CHANGED_MEMBER(PERIBOX_TAG, peribox_device, genmod_changed, 0)
|
|
PORT_CONFSETTING( 0x00, "Standard" )
|
|
PORT_CONFSETTING( GENMOD, "GenMod" )
|
|
|
|
PORT_START( "BOOTROM" )
|
|
PORT_CONFNAME( 0x01, GENEVE_098, "Boot ROM" ) PORT_CONDITION( "MODE", 0x01, EQUALS, 0x00 )
|
|
PORT_CONFSETTING( GENEVE_098, "Version 0.98" )
|
|
PORT_CONFSETTING( GENEVE_100, "Version 1.00" )
|
|
|
|
PORT_START( "SRAM" )
|
|
PORT_CONFNAME( 0x03, 0x01, "Onboard SRAM" ) PORT_CONDITION( "MODE", 0x01, EQUALS, 0x00 )
|
|
PORT_CONFSETTING( 0x00, "32 KiB" )
|
|
PORT_CONFSETTING( 0x01, "64 KiB" )
|
|
PORT_CONFSETTING( 0x02, "384 KiB" )
|
|
|
|
PORT_START( "GENMODDIPS" )
|
|
PORT_DIPNAME( GM_TURBO, 0x00, "Genmod Turbo mode") PORT_CONDITION( "MODE", 0x01, EQUALS, GENMOD ) PORT_CHANGED_MEMBER(GMAPPER_TAG, geneve_mapper_device, gm_changed, 1)
|
|
PORT_CONFSETTING( 0x00, DEF_STR( Off ))
|
|
PORT_CONFSETTING( GM_TURBO, DEF_STR( On ))
|
|
PORT_DIPNAME( GM_TIM, GM_TIM, "Genmod TI mode") PORT_CONDITION( "MODE", 0x01, EQUALS, GENMOD ) PORT_CHANGED_MEMBER(GMAPPER_TAG, geneve_mapper_device, gm_changed, 2)
|
|
PORT_CONFSETTING( 0x00, DEF_STR( Off ))
|
|
PORT_CONFSETTING( GM_TIM, DEF_STR( On ))
|
|
|
|
INPUT_PORTS_END
|
|
|
|
/****************************************************************************
|
|
CRU handling
|
|
*****************************************************************************/
|
|
|
|
#define CRU_CONTROL_BASE 0x1ee0
|
|
#define CRU_SSTEP_BASE 0x13c0
|
|
|
|
WRITE8_MEMBER ( geneve_state::cruwrite )
|
|
{
|
|
int addroff = offset << 1;
|
|
|
|
// Single step
|
|
// 13c0 - 13fe: 0001 0011 11xx xxx0
|
|
if ((addroff & 0xffc0) == CRU_SSTEP_BASE)
|
|
{
|
|
int bit = (addroff & 0x003e)>>1;
|
|
if (VERBOSE>0) LOG("geneve: Single step not implemented; bit %d set to %d\n", bit, data);
|
|
return;
|
|
}
|
|
|
|
if ((addroff & 0xffe0) == CRU_CONTROL_BASE)
|
|
{
|
|
int bit = (addroff & 0x001e)>>1;
|
|
switch (bit)
|
|
{
|
|
case 5:
|
|
// No one really cares...
|
|
if (VERBOSE>8) LOG("geneve: Set PAL flag = %02x\n", data);
|
|
// m_palvideo = (data!=0);
|
|
break;
|
|
case 7:
|
|
// m_capslock = (data!=0);
|
|
if (VERBOSE>8) LOG("geneve: Set capslock flag = %02x\n", data);
|
|
break;
|
|
case 8:
|
|
if (VERBOSE>8) LOG("geneve: Set keyboard clock flag = %02x\n", data);
|
|
m_keyboard->clock_control((data!=0)? ASSERT_LINE : CLEAR_LINE);
|
|
break;
|
|
case 9:
|
|
if (VERBOSE>8) LOG("geneve: Set keyboard scan flag = %02x\n", data);
|
|
m_keyboard->send_scancodes((data!=0)? ASSERT_LINE : CLEAR_LINE);
|
|
break;
|
|
case 10:
|
|
if (VERBOSE>8) LOG("geneve: Geneve mode = %02x\n", data);
|
|
m_mapper->set_geneve_mode(data!=0);
|
|
break;
|
|
case 11:
|
|
if (VERBOSE>8) LOG("geneve: Direct mode = %02x\n", data);
|
|
m_mapper->set_direct_mode(data!=0);
|
|
break;
|
|
case 12:
|
|
if (VERBOSE>8) LOG("geneve: Cartridge size 8K = %02x\n", data);
|
|
m_mapper->set_cartridge_size((data!=0)? 0x2000 : 0x4000);
|
|
break;
|
|
case 13:
|
|
if (VERBOSE>8) LOG("geneve: Cartridge writable 6000 = %02x\n", data);
|
|
m_mapper->set_cartridge_writable(0x6000, (data!=0));
|
|
break;
|
|
case 14:
|
|
if (VERBOSE>8) LOG("geneve: Cartridge writable 7000 = %02x\n", data);
|
|
m_mapper->set_cartridge_writable(0x7000, (data!=0));
|
|
break;
|
|
case 15:
|
|
if (VERBOSE>8) LOG("geneve: Extra wait states = %02x\n", data==0);
|
|
m_mapper->set_extra_waitstates(data==0); // let's use the inverse semantics
|
|
break;
|
|
default:
|
|
if (VERBOSE>0) LOG("geneve: set CRU address %04x=%02x ignored\n", addroff, data);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_peribox->cruwrite(addroff, data);
|
|
}
|
|
}
|
|
|
|
READ8_MEMBER( geneve_state::cruread )
|
|
{
|
|
UINT8 value = 0;
|
|
int addroff = offset << 4;
|
|
|
|
// Single step
|
|
// 13c0 - 13fe: 0001 0011 11xx xxx0
|
|
if ((addroff & 0xffc0) == CRU_SSTEP_BASE)
|
|
{
|
|
int bit = (addroff & 0x003e)>>1;
|
|
if (VERBOSE>0) LOG("geneve: Single step not implemented; attempting to read bit %d\n", bit);
|
|
return value;
|
|
}
|
|
|
|
// TMS9995-internal CRU locations (1ee0-1efe) are handled within the processor
|
|
// so we just don't arrive here
|
|
|
|
// Propagate the CRU access to external devices
|
|
m_peribox->crureadz(addroff, &value);
|
|
return value;
|
|
}
|
|
|
|
/***********************************************************************
|
|
CRU callbacks
|
|
***********************************************************************/
|
|
|
|
READ8_MEMBER( geneve_state::read_by_9901 )
|
|
{
|
|
int answer = 0;
|
|
|
|
switch (offset & 0x03)
|
|
{
|
|
case TMS9901_CB_INT7:
|
|
//
|
|
// Read pins INT3*-INT7* of Geneve's 9901.
|
|
// bit 1: INTA status
|
|
// bit 2: INT2 status
|
|
// bit 3-7: joystick status
|
|
//
|
|
// |K|K|K|K|K|I2|I1|C|
|
|
// negative logic
|
|
if (m_inta==CLEAR_LINE) answer |= 0x02;
|
|
if (m_int2==CLEAR_LINE) answer |= 0x04;
|
|
answer |= m_joyport->read_port()<<3;
|
|
break;
|
|
|
|
case TMS9901_INT8_INT15:
|
|
// Read pins INT8*-INT15* of Geneve 9901.
|
|
//
|
|
// bit 0: keyboard interrupt
|
|
// bit 1: unused
|
|
// bit 2: mouse left button
|
|
// (bit 3: clock interrupt)
|
|
// bit 4: INTB from PE-bus
|
|
// bit 5 & 7: used as output
|
|
// bit 6: unused
|
|
if (m_keyint==CLEAR_LINE) answer |= 0x01;
|
|
if (m_mouse->left_button()==CLEAR_LINE) answer |= 0x04;
|
|
// TODO: add clock interrupt
|
|
if (m_intb==CLEAR_LINE) answer |= 0x10;
|
|
if (m_video_wait==ASSERT_LINE) answer |= 0x20;
|
|
// TODO: PAL pin 5
|
|
if (VERBOSE>8) LOG("geneve: INT15-8 = %02x\n", answer);
|
|
break;
|
|
|
|
case TMS9901_P0_P7:
|
|
// Read pins P0-P7 of TMS9901. All pins are configured as outputs, so nothing here.
|
|
break;
|
|
|
|
case TMS9901_P8_P15:
|
|
// Read pins P8-P15 of TMS 9901.
|
|
// bit 4: mouse left button
|
|
// video wait is an output; no input possible here
|
|
if (m_intb==CLEAR_LINE) answer |= 0x04; // mirror from above
|
|
// TODO: 0x08 = real-time clock int
|
|
if (m_mouse->left_button()==CLEAR_LINE) answer |= 0x10; // mirror from above
|
|
if (m_keyint==CLEAR_LINE) answer |= 0x40;
|
|
|
|
// Joystick up (mirror of bit 7)
|
|
if ((m_joyport->read_port() & 0x10)==0) answer |= 0x80;
|
|
break;
|
|
}
|
|
return answer;
|
|
}
|
|
|
|
/*
|
|
Write PE bus reset line
|
|
*/
|
|
WRITE_LINE_MEMBER( geneve_state::peripheral_bus_reset )
|
|
{
|
|
if (VERBOSE>0) LOG("geneve: peripheral bus reset request; not implemented yet.\n");
|
|
}
|
|
|
|
/*
|
|
Write VDP reset line
|
|
*/
|
|
WRITE_LINE_MEMBER( geneve_state::VDP_reset )
|
|
{
|
|
if (VERBOSE>0) LOG("geneve: Video reset request; not implemented yet.\n");
|
|
}
|
|
|
|
/*
|
|
Write joystick select line. 1 selects joystick 1 (pin 7), 0 selects joystick 2 (pin 2)
|
|
*/
|
|
WRITE_LINE_MEMBER( geneve_state::joystick_select )
|
|
{
|
|
m_joyport->write_port((state==ASSERT_LINE)? 1:2);
|
|
}
|
|
|
|
/*
|
|
Write external mem cycles (0=long, 1=short)
|
|
*/
|
|
WRITE_LINE_MEMBER( geneve_state::extbus_wait_states )
|
|
{
|
|
if (VERBOSE>0) LOG("geneve: external bus wait states set to %d, not implemented yet.\n", state);
|
|
}
|
|
|
|
/*
|
|
Write vdp wait cycles (1=add 14 cycles, 0=add none)
|
|
see above for waitstate handling
|
|
*/
|
|
WRITE_LINE_MEMBER( geneve_state::video_wait_states )
|
|
{
|
|
if (VERBOSE>1) LOG("geneve: video wait states set to %d\n", state);
|
|
m_mapper->set_video_waitstates(state==ASSERT_LINE);
|
|
m_video_wait = (state!=0)? ASSERT_LINE : CLEAR_LINE;
|
|
}
|
|
|
|
/*
|
|
Called by the 9901 core whenever the state of INTREQ and IC0-3 changes.
|
|
As with the TI-99/4A, the interrupt level is delivered as the offset,
|
|
but again it is ignored. Anyway, the TMS9995 has only two external inputs
|
|
(INT1 and INT4).
|
|
*/
|
|
WRITE8_MEMBER( geneve_state::tms9901_interrupt )
|
|
{
|
|
/* INTREQ is connected to INT1. */
|
|
m_cpu->set_input_line(INPUT_LINE_99XX_INT1, data);
|
|
}
|
|
|
|
/* tms9901 setup */
|
|
const tms9901_interface tms9901_wiring_geneve =
|
|
{
|
|
TMS9901_INT1 | TMS9901_INT2 | TMS9901_INT8 | TMS9901_INTB | TMS9901_INTC, /* only input pins whose state is always known */
|
|
|
|
// read handler
|
|
DEVCB_DRIVER_MEMBER(geneve_state, read_by_9901),
|
|
|
|
{ /* write handlers */
|
|
DEVCB_DRIVER_LINE_MEMBER(geneve_state, peripheral_bus_reset),
|
|
DEVCB_DRIVER_LINE_MEMBER(geneve_state, VDP_reset),
|
|
DEVCB_DRIVER_LINE_MEMBER(geneve_state, joystick_select),
|
|
DEVCB_NULL,
|
|
DEVCB_NULL,
|
|
DEVCB_NULL,
|
|
DEVCB_DEVICE_LINE_MEMBER(GKEYBOARD_TAG, geneve_keyboard_device, reset_line),
|
|
DEVCB_DRIVER_LINE_MEMBER(geneve_state, extbus_wait_states),
|
|
DEVCB_NULL,
|
|
DEVCB_DRIVER_LINE_MEMBER(geneve_state, video_wait_states),
|
|
DEVCB_NULL,
|
|
DEVCB_NULL,
|
|
DEVCB_NULL,
|
|
DEVCB_NULL,
|
|
DEVCB_NULL,
|
|
DEVCB_NULL
|
|
},
|
|
|
|
/* interrupt handler */
|
|
DEVCB_DRIVER_MEMBER(geneve_state, tms9901_interrupt)
|
|
};
|
|
|
|
/*******************************************************************
|
|
Signal lines
|
|
*******************************************************************/
|
|
/*
|
|
inta is connected to both tms9901 IRQ1 line and to tms9995 INT4/EC line.
|
|
*/
|
|
WRITE_LINE_MEMBER( geneve_state::inta )
|
|
{
|
|
m_inta = (state!=0)? ASSERT_LINE : CLEAR_LINE;
|
|
m_tms9901->set_single_int(1, state);
|
|
m_cpu->set_input_line(INPUT_LINE_99XX_INT4, state);
|
|
}
|
|
|
|
/*
|
|
intb is connected to tms9901 IRQ12 line.
|
|
*/
|
|
WRITE_LINE_MEMBER( geneve_state::intb )
|
|
{
|
|
m_intb = (state!=0)? ASSERT_LINE : CLEAR_LINE;
|
|
m_tms9901->set_single_int(12, state);
|
|
}
|
|
|
|
WRITE_LINE_MEMBER( geneve_state::ext_ready )
|
|
{
|
|
if (VERBOSE>6) LOG("ti99_8: READY level (ext) =%02x\n", state);
|
|
m_ready_line = state;
|
|
m_cpu->set_ready((m_ready_line == ASSERT_LINE && m_ready_line1 == ASSERT_LINE)? ASSERT_LINE : CLEAR_LINE);
|
|
}
|
|
|
|
WRITE_LINE_MEMBER( geneve_state::mapper_ready )
|
|
{
|
|
if (VERBOSE>6) LOG("geneve: READY level (mapper) = %02x\n", state);
|
|
m_ready_line1 = state;
|
|
m_cpu->set_ready((m_ready_line == ASSERT_LINE && m_ready_line1 == ASSERT_LINE)? ASSERT_LINE : CLEAR_LINE);
|
|
}
|
|
|
|
/*
|
|
set the state of int2 (called by the v9938 core)
|
|
*/
|
|
void geneve_state::set_tms9901_INT2_from_v9938(v99x8_device &vdp, int state)
|
|
{
|
|
m_int2 = (state!=0)? ASSERT_LINE : CLEAR_LINE;
|
|
m_tms9901->set_single_int(2, state);
|
|
}
|
|
|
|
/*
|
|
Interrupt from the keyboard.
|
|
*/
|
|
WRITE_LINE_MEMBER( geneve_state::keyboard_interrupt )
|
|
{
|
|
m_keyint = (state!=0)? ASSERT_LINE : CLEAR_LINE;
|
|
m_tms9901->set_single_int(8, state);
|
|
}
|
|
|
|
/*
|
|
scanline interrupt
|
|
*/
|
|
TIMER_DEVICE_CALLBACK_MEMBER(geneve_state::geneve_hblank_interrupt)
|
|
{
|
|
int scanline = param;
|
|
|
|
machine().device<v9938_device>(VDP_TAG)->interrupt();
|
|
|
|
if (scanline == 0) // was 262
|
|
{
|
|
// TODO
|
|
// The technical docs do not say anything about the way the mouse
|
|
// is queried. It sounds plausible that the mouse is sampled once
|
|
// per vertical interrupt; however, the mouse sometimes shows jerky
|
|
// behaviour. Maybe we should use an autonomous timer with a higher
|
|
// rate? -> to be checked
|
|
m_mouse->poll();
|
|
}
|
|
}
|
|
|
|
WRITE8_MEMBER( geneve_state::external_operation )
|
|
{
|
|
static const char* extop[8] = { "inv1", "inv2", "IDLE", "RSET", "inv3", "CKON", "CKOF", "LREX" };
|
|
if (VERBOSE>1)
|
|
if (offset != IDLE_OP) LOG("geneve: External operation %s not implemented on Geneve board\n", extop[offset]);
|
|
}
|
|
|
|
/*
|
|
Clock line from the CPU. Used to control wait state generation.
|
|
*/
|
|
WRITE_LINE_MEMBER( geneve_state::clock_out )
|
|
{
|
|
m_mapper->clock_in(state);
|
|
}
|
|
|
|
static TMS9995_CONFIG( geneve_processor_config )
|
|
{
|
|
DEVCB_DRIVER_MEMBER(geneve_state, external_operation),
|
|
DEVCB_NULL, // Instruction acquisition
|
|
DEVCB_DRIVER_LINE_MEMBER(geneve_state, clock_out),
|
|
DEVCB_NULL, // wait
|
|
DEVCB_NULL, // HOLDA
|
|
INTERNAL_RAM, // use internal RAM
|
|
NO_OVERFLOW_INT // The generally available versions of TMS9995 have a deactivated overflow interrupt
|
|
};
|
|
|
|
static TI_SOUND_CONFIG( sound_conf )
|
|
{
|
|
DEVCB_DRIVER_LINE_MEMBER(geneve_state, ext_ready) // READY
|
|
};
|
|
|
|
static const mm58274c_interface geneve_mm58274c_interface =
|
|
{
|
|
1, /* mode 24*/
|
|
0 /* first day of week */
|
|
};
|
|
|
|
static GENEVE_KEYBOARD_CONFIG( geneve_keyb_conf )
|
|
{
|
|
DEVCB_DRIVER_LINE_MEMBER(geneve_state, keyboard_interrupt)
|
|
};
|
|
|
|
static PERIBOX_CONFIG( peribox_conf )
|
|
{
|
|
DEVCB_DRIVER_LINE_MEMBER(geneve_state, inta), // INTA
|
|
DEVCB_DRIVER_LINE_MEMBER(geneve_state, intb), // INTB
|
|
DEVCB_DRIVER_LINE_MEMBER(geneve_state, ext_ready), // READY
|
|
0x00000 // Address bus prefix (Mapper will produce prefixes)
|
|
};
|
|
|
|
static GENEVE_MAPPER_CONFIG( mapper_conf )
|
|
{
|
|
DEVCB_DRIVER_LINE_MEMBER(geneve_state, mapper_ready) // READY
|
|
};
|
|
|
|
static JOYPORT_CONFIG( joyport_60 )
|
|
{
|
|
DEVCB_NULL,
|
|
60
|
|
};
|
|
|
|
DRIVER_INIT_MEMBER(geneve_state,geneve)
|
|
{
|
|
}
|
|
|
|
void geneve_state::machine_start()
|
|
{
|
|
m_tms9901 = static_cast<tms9901_device*>(machine().device(TMS9901_TAG));
|
|
m_mapper = static_cast<geneve_mapper_device*>(machine().device(GMAPPER_TAG));
|
|
m_keyboard = static_cast<geneve_keyboard_device*>(machine().device(GKEYBOARD_TAG));
|
|
m_peribox = static_cast<peribox_device*>(machine().device(PERIBOX_TAG));
|
|
m_mouse = static_cast<geneve_mouse_device*>(machine().device(GMOUSE_TAG));
|
|
m_cpu = static_cast<tms9995_device*>(machine().device("maincpu"));
|
|
m_joyport = static_cast<joyport_device*>(machine().device(JOYPORT_TAG));
|
|
}
|
|
|
|
/*
|
|
Reset the machine.
|
|
*/
|
|
void geneve_state::machine_reset()
|
|
{
|
|
m_inta = CLEAR_LINE; // flag reflecting the INTA line
|
|
m_intb = CLEAR_LINE; // flag reflecting the INTB line
|
|
m_int2 = CLEAR_LINE; // flag reflecting the INT2 line
|
|
m_keyint = CLEAR_LINE;
|
|
|
|
// No automatic wait state (auto wait state is enabled with READY=CLEAR at RESET)
|
|
m_cpu->set_ready(ASSERT_LINE);
|
|
m_cpu->set_hold(CLEAR_LINE);
|
|
|
|
m_ready_line = m_ready_line1 = ASSERT_LINE;
|
|
|
|
m_peribox->set_genmod(machine().root_device().ioport("MODE")->read()==GENMOD);
|
|
|
|
m_joyport->write_port(0x01); // select Joystick 1
|
|
}
|
|
|
|
static MACHINE_CONFIG_START( geneve_60hz, geneve_state )
|
|
// basic machine hardware
|
|
// TMS9995 CPU @ 12.0 MHz
|
|
MCFG_TMS9995_ADD("maincpu", TMS9995, 12000000, memmap, crumap, geneve_processor_config)
|
|
|
|
|
|
// video hardware
|
|
// Although we should have a 60 Hz screen rate, we have to set it to 30 here.
|
|
// The reason is that that the number of screen lines is counted twice for the
|
|
// interlace mode, but in non-interlace modes only half of the lines are
|
|
// painted. Accordingly, the full set of lines is refreshed at 30 Hz,
|
|
// not 60 Hz. This should be fixed in the v9938 emulation.
|
|
MCFG_TI_V9938_ADD(VIDEO_SYSTEM_TAG, 30, SCREEN_TAG, 2500, 512+32, (212+28)*2, DEVICE_SELF, geneve_state, set_tms9901_INT2_from_v9938)
|
|
MCFG_TIMER_DRIVER_ADD_SCANLINE("scantimer", geneve_state, geneve_hblank_interrupt, SCREEN_TAG, 0, 1) /* 262.5 in 60Hz, 312.5 in 50Hz */
|
|
|
|
// Main board components
|
|
MCFG_TMS9901_ADD(TMS9901_TAG, tms9901_wiring_geneve, 3000000)
|
|
MCFG_GENEVE_MAPPER_ADD(GMAPPER_TAG, mapper_conf)
|
|
MCFG_MM58274C_ADD(GCLOCK_TAG, geneve_mm58274c_interface)
|
|
|
|
// Peripheral expansion box (Geneve composition)
|
|
MCFG_PERIBOX_GEN_ADD( PERIBOX_TAG, peribox_conf )
|
|
|
|
// sound hardware
|
|
MCFG_TI_SOUND_76496_ADD( TISOUND_TAG, sound_conf )
|
|
|
|
// User interface devices
|
|
MCFG_GENEVE_KEYBOARD_ADD( GKEYBOARD_TAG, geneve_keyb_conf )
|
|
MCFG_GENEVE_MOUSE_ADD( GMOUSE_TAG )
|
|
MCFG_GENEVE_JOYPORT_ADD( JOYPORT_TAG, joyport_60 )
|
|
|
|
MACHINE_CONFIG_END
|
|
|
|
/*
|
|
ROM loading
|
|
*/
|
|
|
|
ROM_START(geneve)
|
|
/*CPU memory space*/
|
|
ROM_REGION(0xc000, "maincpu", 0)
|
|
ROM_LOAD("genbt100.bin", 0x0000, 0x4000, CRC(8001e386) SHA1(b44618b54dabac3882543e18555d482b299e0109)) /* CPU ROMs v1.0 */
|
|
ROM_LOAD_OPTIONAL("genbt098.bin", 0x4000, 0x4000, CRC(b2e20df9) SHA1(2d5d09177afe97d63ceb3ad59b498b1c9e2153f7)) /* CPU ROMs v0.98 */
|
|
ROM_LOAD_OPTIONAL("gnmbt100.bin", 0x8000, 0x4000, CRC(19b89479) SHA1(6ef297eda78dc705946f6494e9d7e95e5216ec47)) /* CPU ROMs GenMod */
|
|
|
|
ROM_REGION(SRAM_SIZE, SRAM_TAG, 0)
|
|
ROM_FILL(0x0000, SRAM_SIZE, 0)
|
|
|
|
ROM_REGION(DRAM_SIZE, DRAM_TAG, 0)
|
|
ROM_FILL(0x0000, DRAM_SIZE, 0)
|
|
ROM_END
|
|
|
|
/* YEAR NAME PARENT COMPAT MACHINE INPUT INIT COMPANY FULLNAME */
|
|
COMP( 1987,geneve, 0, 0, geneve_60hz, geneve, geneve_state, geneve, "Myarc", "Geneve 9640" , 0)
|