// license:GPL-2.0+
// copyright-holders:Peter Trauner
/******************************************************************************
Pasogo Handheld Console
Koei 1996
This is a handheld console made by Koei in 1996. All of the games available on
this console are variations of the game 'Go'
Main PCB Layout
---------------
PT-GMAIN01D
|--------------|------------||
|POWER VOL | ||
| LM2937*| CART ||
|CE-0702 | SLOT ||
| | CN1 ||
| |MC14071B* ||
| | ||
| PIEZO_SPKR |------------||
|CN5 74HC04* |
| X2 |
| |-------| CN4|
| |VADEM | |
| |VG230 | |
| | | HM514800* |
| | | CN3|
| |-------| HM514800* |
|CN2* X1 |
|----------------------------|
Notes: (all ICs shown)
VG230 - Vadem VG230 single-chip PC platform. Contains 16 MHz NEC uPD70116H V30HL CPU
(which is a high-speed low-power 8086 variation), IBM PC/XT-compatible core
logic, LCD controller (CGA/AT&T640x400), keyboard matrix scanner, dual PCMCIA
2.1 card controller, EMS 4.0 hardware support for up to 64MB, built-in timer
PIC/DMA/UART/RTC controllers. The clock input is 32.2200MHz. An internal divider
creates a 16.11MHz clock for the V30HL CPU.
HM514800 - Hitachi HM514800 512k x8-bit DRAM
MC14071 - Motorola MC14071 Quad 2-input OR gate
74HC04 - 74HC04 Hex inverter
LM2937 - Texas Instruments LM2937ES-5 voltage regulator (Max 26V input, 5V output at 500mA)
CE-0702 - TDK CE-0702 DC-DC converter for LCD in SIP9 package (5V input, -24V output at 25mA)
POWER - 9V DC power input from AC/DC adapter
VOL - Volume pot
CN5 - 5 pin connector for 4-way control pad (up/down/left/right/ground)
CN4 - 5 pin connector for on/off switch and 2 buttons
CN3 - 2 pin power input from 6x AA-battery compartment (input voltage is 9V DC)
CN2 - Flat cable connector for video out to LCD panel. When the LCD is powered on the pixels
are blue. The LCD panel PCB has part number 97-44264-8 LMG6912RPFC LMG6910RPGR
and contains the following parts.....
Matsushita 53008HEB-8
Sanyo LA6324N quad operational amplifier
Hitachi BD66285BFC LCD controller IC (x3)
Hitachi BD66284BFC LCD controller IC (x4)
The LCD flat cable has several wires but 2 of them have frequencies which measure
69.9161Hz and 16.7798kHz. These are assumed to be VSync and HSync
CN1 - Cart slot
X1 - Marked 322. Measures 32.21732MHz so this is a common 32.22MHz OSC.
X2 - No markings. Measures 32.768kHz and used for the RTC
* - These parts are on the other side of the PCB
Carts
-----
All of the carts are identical. Most have only one surface mounted mask ROM. Either a
MX23C8100 (8M) or YRM0442 (4M). Some are populated with additional parts including a 62256
32kx8 SRAM, a 3v coin battery and a MM1081N reset chip plus a few resistors/capacitors and
a transistor. All parts are surface mounted.
PT-GMEM01B
PT-GMEM01C
|---------------|
|-- CR2016 |
|-- MM1081 |
|-- |
|-- |
|-- 62256 |
|-- |
|-- |
|-- MX23C8100|
|-- or YRM0442 |
|---------------|
Notes:
Carts containing just one mask ROM are KS-1002, KS-1003, KS1004 and KS-1009
Carts containing RAM, battery and reset chip are KS-1001 and KS1010
Carts KS-1005, KS-1006, KS-1007 and KS-1008 probably exist but are unknown.
===========================================================================================
PeT mess@utanet.at march 2008
although it is very related to standard pc hardware, it is different enough
to make the standard pc driver one level more complex, so own driver
TODO:
- Make a separate device of the Vadem VG230 core (it is also used in the HP
OmniGo 100 and 120).
******************************************************************************/
#include "emu.h"
#include "cpu/nec/nec.h"
#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "machine/bankdev.h"
#include "machine/genpc.h"
#include "machine/timer.h"
#include "emupal.h"
#include "screen.h"
#include "softlist.h"
/*
rtc interrupt irq 2
*/
class pasogo_state : public driver_device
{
public:
pasogo_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag)
, m_maincpu(*this, "maincpu")
, m_cart(*this, "cartslot")
, m_ems(*this, "ems")
, m_vram(*this, "vram")
, m_palette(*this, "palette")
{ }
void pasogo(machine_config &config);
DECLARE_INPUT_CHANGED_MEMBER(contrast);
private:
required_device<cpu_device> m_maincpu;
required_device<generic_slot_device> m_cart;
required_device<address_map_bank_device> m_ems;
required_shared_ptr<uint16_t> m_vram;
required_device<palette_device> m_palette;
DECLARE_READ16_MEMBER(ems_r);
DECLARE_WRITE16_MEMBER(ems_w);
DECLARE_READ16_MEMBER(emsram_r);
DECLARE_WRITE16_MEMBER(emsram_w);
DECLARE_READ8_MEMBER(vg230_io_r);
DECLARE_WRITE8_MEMBER(vg230_io_w);
struct
{
uint8_t index;
uint8_t data[0x100];
struct {
uint16_t data;
} bios_timer; // 1.19 MHz tclk signal
struct {
int seconds, minutes, hours, days;
int alarm_seconds, alarm_minutes, alarm_hours, alarm_days;
int onehertz_interrupt_on;
int onehertz_interrupt_request;
int alarm_interrupt_on;
int alarm_interrupt_request;
} rtc;
struct {
int write_protected;
} pmu;
} m_vg230;
void machine_reset() override;
void machine_start() override;
uint32_t screen_update_pasogo(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
INTERRUPT_GEN_MEMBER(pasogo_interrupt);
TIMER_DEVICE_CALLBACK_MEMBER(vg230_timer);
memory_region *m_cart_rom;
uint8_t m_ems_index;
uint16_t m_ems_bank[28];
void emsbank_map(address_map &map);
void pasogo_io(address_map &map);
void pasogo_mem(address_map &map);
};
TIMER_DEVICE_CALLBACK_MEMBER(pasogo_state::vg230_timer)
{
m_vg230.rtc.seconds += 1;
if (m_vg230.rtc.seconds >= 60)
{
m_vg230.rtc.seconds = 0;
m_vg230.rtc.minutes += 1;
if (m_vg230.rtc.minutes >= 60)
{
m_vg230.rtc.minutes = 0;
m_vg230.rtc.hours += 1;
if (m_vg230.rtc.hours >= 24)
{
m_vg230.rtc.hours = 0;
m_vg230.rtc.days = (m_vg230.rtc.days + 1) & 0xfff;
}
}
}
if (m_vg230.rtc.seconds == m_vg230.rtc.alarm_seconds
&& m_vg230.rtc.minutes == m_vg230.rtc.alarm_minutes
&& m_vg230.rtc.hours == m_vg230.rtc.alarm_hours
&& (m_vg230.rtc.days & 0x1f) == m_vg230.rtc.alarm_hours)
{
// generate alarm
}
}
void pasogo_state::machine_start()
{
system_time systime;
memset(&m_vg230, 0, sizeof(m_vg230));
m_vg230.pmu.write_protected = true;
machine().base_datetime(systime);
m_vg230.rtc.seconds = systime.local_time.second;
m_vg230.rtc.minutes = systime.local_time.minute;
m_vg230.rtc.hours = systime.local_time.hour;
m_vg230.rtc.days = 0;
m_vg230.bios_timer.data=0x7200; // HACK
}
READ8_MEMBER( pasogo_state::vg230_io_r )
{
int log = true;
uint8_t data = 0;
m_vg230.bios_timer.data += 0x100; //HACK
if (offset&1)
{
data = m_vg230.data[m_vg230.index];
switch (m_vg230.index)
{
case 0x09:
break;
case 0x0a:
if (m_vg230.data[9] & 1)
data=ioport("JOY")->read();
else
data = 0xff;
break;
case 0x30:
data = m_vg230.bios_timer.data & 0xff;
break;
case 0x31:
data = m_vg230.bios_timer.data >> 8;
log = false;
break;
case 0x70:
data = m_vg230.rtc.seconds;
log = false;
break;
case 0x71:
data = m_vg230.rtc.minutes;
log = false;
break;
case 0x72:
data = m_vg230.rtc.hours;
log = false;
break;
case 0x73:
data = m_vg230.rtc.days;
break;
case 0x74:
data = m_vg230.rtc.days >> 8;
break;
case 0x79:
/*rtc status*/
log = false;
break;
case 0x7a:
data &= ~3;
if (m_vg230.rtc.alarm_interrupt_request)
data |= 1<<1;
if (m_vg230.rtc.onehertz_interrupt_request)
data |= 1<<0;
break;
case 0xc1:
data &= ~1;
if (m_vg230.pmu.write_protected)
data |= 1;
m_vg230.pmu.write_protected = false;
log = false;
break;
}
if (log)
logerror("%.5x vg230 %02x read %.2x\n",(int) m_maincpu->pc(), m_vg230.index, data);
// data=machine.root_device().memregion("maincpu")->base()[0x4000+offset];
}
else
data = m_vg230.index;
return data;
}
WRITE8_MEMBER( pasogo_state::vg230_io_w )
{
int log = true;
if (offset & 1)
{
// machine.root_device().memregion("maincpu")->base()[0x4000+offset]=data;
m_vg230.data[m_vg230.index] = data;
switch (m_vg230.index)
{
case 0x09:
break;
case 0x70:
m_vg230.rtc.seconds = data & 0x3f;
break;
case 0x71:
m_vg230.rtc.minutes = data & 0x3f;
break;
case 0x72:
m_vg230.rtc.hours = data & 0x1f;
break;
case 0x73:
m_vg230.rtc.days = (m_vg230.rtc.days & ~0xff) | data;
break;
case 0x74:
m_vg230.rtc.days = (m_vg230.rtc.days & 0xff) | ((data & 0xf) << 8);
break;
case 0x75:
m_vg230.rtc.alarm_seconds = data & 0x3f;
break;
case 0x76:
m_vg230.rtc.alarm_minutes = data & 0x3f;
break;
case 0x77:
m_vg230.rtc.alarm_hours = data & 0x1f;
break;
case 0x78:
m_vg230.rtc.days = data & 0x1f;
break;
case 0x79:
m_vg230.rtc.onehertz_interrupt_on = data & 1;
m_vg230.rtc.alarm_interrupt_on = data & 2;
log = false;
break;
case 0x7a:
if (data & 2)
{
m_vg230.rtc.alarm_interrupt_request = false;
m_vg230.rtc.onehertz_interrupt_request = false; /* update interrupt */
}
break;
}
if (log)
logerror("%.5x vg230 %02x write %.2x\n", (int)m_maincpu->pc(), m_vg230.index, data);
}
else
m_vg230.index = data;
}
READ16_MEMBER( pasogo_state::ems_r )
{
uint8_t data = 0;
uint8_t index;
switch (offset)
{
case 0:
data = m_ems_index;
break;
case 1:
index = (m_ems_index >> 2) & 0x1f;
data = m_ems_bank[index];
break;
}
return data;
}
WRITE16_MEMBER( pasogo_state::ems_w )
{
uint8_t index;
switch (offset)
{
case 0:
m_ems_index = data;
break;
case 1:
index = (m_ems_index >> 2) & 0x1f;
if((index & ~1) == 10)
{
logerror("EMS mapping of CGA framebuffer\n");
break;
}
else if(index >= 28)
{
logerror("EMS index out of range\n");
break;
}
COMBINE_DATA(&m_ems_bank[index]);
break;
}
}
READ16_MEMBER( pasogo_state::emsram_r )
{
m_ems->set_bank(m_ems_bank[(offset >> 13) & 0x1f] & 0x7fff);
return m_ems->read16(space, offset & 0x1fff, mem_mask);
}
WRITE16_MEMBER( pasogo_state::emsram_w )
{
m_ems->set_bank(m_ems_bank[(offset >> 13) & 0x1f] & 0x7fff);
m_ems->write16(space, offset & 0x1fff, data, mem_mask);
}
void pasogo_state::emsbank_map(address_map &map)
{
map(0x04080000, 0x040fffff).ram();
map(0x08000000, 0x080fffff).bankr("bank27");
map(0x10000000, 0x1000ffff).ram(); // cart ram?
}
void pasogo_state::pasogo_mem(address_map &map)
{
map(0x80000, 0xeffff).rw(FUNC(pasogo_state::emsram_r), FUNC(pasogo_state::emsram_w));
map(0xb8000, 0xbffff).ram().share("vram");
map(0xf0000, 0xfffff).bankr("bank27");
}
void pasogo_state::pasogo_io(address_map &map)
{
map(0x0000, 0x00ff).m("mb", FUNC(ibm5160_mb_device::map));
map(0x0026, 0x0027).rw(FUNC(pasogo_state::vg230_io_r), FUNC(pasogo_state::vg230_io_w));
map(0x006c, 0x006f).rw(FUNC(pasogo_state::ems_r), FUNC(pasogo_state::ems_w));
}
static INPUT_PORTS_START( pasogo )
PORT_START("JOY")
// PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_SELECT) PORT_NAME("select")
// PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_START) PORT_NAME("start")
PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("O") /*?*/
PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_NAME("X") /*?*/
PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT)
PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("a") PORT_CODE(KEYCODE_A)
PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("b") PORT_CODE(KEYCODE_B)
PORT_START("COLOR")
PORT_CONFNAME(0x01, 0x01, "Contrast") PORT_CHANGED_MEMBER(DEVICE_SELF, pasogo_state, contrast, 0)
PORT_CONFSETTING(0x00, "Actual")
PORT_CONFSETTING(0x01, "Enhanced")
INPUT_PORTS_END
INPUT_CHANGED_MEMBER(pasogo_state::contrast)
{
if(newval)
{
m_palette->set_pen_color(0, rgb_t(80, 130, 130));
m_palette->set_pen_color(1, rgb_t(40, 60, 140));
}
else
{
m_palette->set_pen_color(0, rgb_t(100, 110, 100));
m_palette->set_pen_color(1, rgb_t(90, 80, 110));
}
}
uint32_t pasogo_state::screen_update_pasogo(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
uint8_t *vram = (uint8_t *)m_vram.target();
int x, y;
for (y=0; y<240; y++)
{
for (x=0; x<(320/8); x++)
{
int a = (y & 3) * 0x2000;
uint8_t d1 = vram[a + (y >> 2) * 80 + x];
uint16_t *line = &bitmap.pix16(y, x << 3);
*line++ = ((d1 >> 7) & 1);
*line++ = ((d1 >> 6) & 1);
*line++ = ((d1 >> 5) & 1);
*line++ = ((d1 >> 4) & 1);
*line++ = ((d1 >> 3) & 1);
*line++ = ((d1 >> 2) & 1);
*line++ = ((d1 >> 1) & 1);
*line++ = ((d1 >> 0) & 1);
}
}
return 0;
}
INTERRUPT_GEN_MEMBER(pasogo_state::pasogo_interrupt)
{
// m_maincpu->pulse_input_line(UPD7810_INTFE1, attotime::zero);
}
void pasogo_state::machine_reset()
{
std::string region_tag;
ioport_port *color = ioport("COLOR");
m_cart_rom = memregion(region_tag.assign(m_cart->tag()).append(GENERIC_ROM_REGION_TAG).c_str());
if (!m_cart_rom) // even with mandatory carts, debug will crash without anything at the boot vector
m_cart_rom = memregion("empty");
membank("bank27")->set_base(m_cart_rom->base());
m_ems_index = 0;
memset(m_ems_bank, 0, sizeof(m_ems_bank));
contrast(*color->fields().first(), nullptr, 0, color->read());
}
MACHINE_CONFIG_START(pasogo_state::pasogo)
MCFG_DEVICE_ADD("maincpu", V30, XTAL(32'220'000)/2)
MCFG_DEVICE_PROGRAM_MAP(pasogo_mem)
MCFG_DEVICE_IO_MAP(pasogo_io)
MCFG_DEVICE_VBLANK_INT_DRIVER("screen", pasogo_state, pasogo_interrupt)
MCFG_DEVICE_IRQ_ACKNOWLEDGE_DEVICE("mb:pic8259", pic8259_device, inta_cb)
ADDRESS_MAP_BANK(config, "ems").set_map(&pasogo_state::emsbank_map).set_options(ENDIANNESS_LITTLE, 16, 32, 0x4000);
MCFG_IBM5160_MOTHERBOARD_ADD("mb", "maincpu")
RAM(config, RAM_TAG).set_default_size("512K");
// It's a CGA device right so lets use isa_cga! Well, not so much.
// The carts use vg230 specific registers and mostly ignore the mc6845.
MCFG_SCREEN_ADD("screen", LCD)
MCFG_SCREEN_REFRESH_RATE(60)
MCFG_SCREEN_SIZE(320, 240)
MCFG_SCREEN_VISIBLE_AREA(0, 320-1, 0, 240-1)
MCFG_SCREEN_UPDATE_DRIVER(pasogo_state, screen_update_pasogo)
MCFG_SCREEN_PALETTE("palette")
MCFG_PALETTE_ADD("palette", 2)
MCFG_GENERIC_CARTSLOT_ADD("cartslot", generic_plain_slot, "pasogo_cart")
MCFG_GENERIC_WIDTH(GENERIC_ROM16_WIDTH)
MCFG_GENERIC_MANDATORY
MCFG_SOFTWARE_LIST_ADD("cart_list","pasogo")
MCFG_TIMER_DRIVER_ADD_PERIODIC("vg230_timer", pasogo_state, vg230_timer, attotime::from_hz(1))
MACHINE_CONFIG_END
ROM_START( pasogo )
ROM_REGION( 0x10000, "empty", ROMREGION_ERASEFF )
ROM_END
// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS
CONS( 1996, pasogo, 0, 0, pasogo, pasogo, pasogo_state, empty_init, "KOEI", "PasoGo", MACHINE_NO_SOUND|MACHINE_NOT_WORKING)