amstrad/pda600.cpp: Added simulation of coprocessor, input and storage. (#10862)

* Added HLE character recognition.
* Added pen display input.
* Added PCMCIA memory card support and initial software list.
* Added internal layout.
* cpu/z180: Fixed SLP instruction.

New working software list items
-------------------------------
pda600: Games (Crazy Money, Mosaic and Pagged)
pda600: Games Demo (Game 44, Squares and FliView)
This commit is contained in:
Sandro Ronco 2023-03-14 17:30:13 +01:00 committed by GitHub
parent dba22f8ad1
commit 4866b24120
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 1162 additions and 86 deletions

35
hash/pda600.xml Normal file
View File

@ -0,0 +1,35 @@
<?xml version="1.0"?>
<!DOCTYPE softwarelist SYSTEM "softwarelist.dtd">
<!--
license:CC0-1.0
Undumped cards:
- PenCalc
- PenMail
- Home Finance
- PenFriend
- WordPro
-->
<softwarelist name="pda600" description="Amstrad PenPad PDA 600 PCMCIA cards">
<software name="games">
<description>Games (Crazy Money, Mosaic and Pagged)</description>
<year>199?</year>
<publisher>Maddox Games</publisher>
<part name="card" interface="pcmcia">
<dataarea name="rom" size="0x80000">
<rom name="games.bin" size="0x80000" crc="0ec748d8" sha1="fd1c24cde1decf76080b962ee1fdf5bd8c78c673"/>
</dataarea>
</part>
</software>
<software name="gamesd">
<description>Games Demo (Game 44, Squares and FliView)</description>
<year>199?</year>
<publisher>Maddox Games</publisher>
<part name="card" interface="pcmcia">
<dataarea name="rom" size="0x80000">
<rom name="games_demo.bin" size="0x80000" crc="e0c5d590" sha1="bc550a00de6b6829d2cb8063b2c5507b631ae03f"/>
</dataarea>
</part>
</software>
</softwarelist>

View File

@ -14,8 +14,15 @@
#define LEAVE_HALT() { \
if( m_HALT ) \
{ \
if( m_HALT == 2 ) \
{ \
_PC += 2; \
} \
else \
{ \
_PC++; \
} \
m_HALT = 0; \
_PC++; \
} \
}
@ -952,6 +959,7 @@ uint8_t z180_device::SET(uint8_t bit, uint8_t value)
* OTDMR
***************************************************************/
#define SLP { \
_PC -= 2; \
m_icount = 0; \
m_HALT = 2; \
}

View File

@ -4,63 +4,106 @@
Amstrad PenPad PDA 600
05/11/2009 Skeleton driver.
PCB Layout (front):
/-----------------------------------------------------------------------\
/ U4 +---+ \
/ X1 HD64610FP U9 U6 | C | \
/ U3 HC157A HC157A U12 | N | \
| KM681000ALT-8 HD64646FS | 3 | |
| +---+ |
| U36 U2 |
| TL061AC 42069 U35 U27 /
| U5 TCM5089 LP324M /
| +---+ 41857 |
| | C | U1 U31 U33 |
| | N | Z8S18016FSC U43 41863 KM62256BLG-10 |
| | 2 | HC541 |
| +---+ U32 \
| U30 U37 HC574A \
| MAX222CWN U24 HC20 +---+ |
| 41864 | C | |
| U21 U25 | N | |
| MAX731CWE MAX641 | 4 | |
| +---+ |
+-------------------------------------------------------------------------------+
Hardware info:
U1: CPU
Z8S180
Enhanced Z80 CPU
1 MB MMU
2 DMAs
2 UARTs (up to 512 Kbps)
Two 16-Bit Timers
Clock Serial I/O
PCB Layout (back):
+-------------------------------------------------------------------------------+
| BAT- +-----+ BAT+ |
| / \ |
| | 3V | U23 |
| | Bat. | HC00A U20 |
| \ / +------+ U26 14052B |
| +-----+ | | 3226NUT /
| U10 U41 | | /
| HC245A HC541 | | U28 |
| X2 | | MC145053D |
| U42 | CN1 | |
| U22 HC541 | | |
| 7673CBA | | \
| +----+ | | \
| / Beep \ | | |
| \ / +------+ |
| +----+ U11 |
\ U18 U8 U7 KM62256BLG /
\ HC00A HC157A HC157A /
\ /
\-----------------------------------------------------------------------/
U2: ROM
contains OS (binary and disassembled source)
apparently compatible to 27C1001 (128K*8)
U3: RAM
128K RAM, static
U4: RTC
U5: Amstrad ASIC (unknown functionality )
U6, U7, U8, U9: Multiplexer
quad channel dual multiplexer
U11: RAM
32K RAM, static
U12: LCD Controller
U21: 5V StepUp converter voltage converter
U27: OA quad operational amplifier
U30: RS232 Driver
U31: Amstrad ASIC
U32: FlipFlop Octal D-Flip-Flop
U33: RAM
32K RAM, static
U35: Tone Dialer
generates DTMF frequencies
U36: unknown
U37: Nand
Dual Quad-Input Nand
U43: Bus Buffer
Octal Bus Buffer
Parts:
U2 - Amstrad 42069 ROM, probably 27C1001
U5 - Amstrad 41857 100 Pin, unknown
U31 - Amstrad 41863 44 Pin, unknown
U24 - Amstrad 41864 18 Pin, unknown
X1 - 32768 XTAL
X2 - 28.6MHz XTAL
Connectors:
LCD
Digitizer
Serial
PCMCIA
CN1 - PCMCIA
CN2 - 8 Pin Serial
CN3 - LCD
CN4 - Digitizer
Additional info:
Two Z8 for power management and character recognition
TODO:
- Sound (DTMF tone generator).
- Refactor the HD64646FS LCD controller into a device.
- Dump the character recognition MCU (possible?).
- Serial port doesn't work.
****************************************************************************/
#include "emu.h"
#include "pda600_copro.h"
#include "bus/generic/slot.h"
#include "bus/generic/carts.h"
#include "bus/rs232/rs232.h"
#include "cpu/z180/z180.h"
#include "machine/nvram.h"
#include "machine/hd64610.h"
#include "machine/timer.h"
#include "sound/beep.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"
#include "pda600.lh"
//**************************************************************************
// CONSTANTS
//**************************************************************************
static constexpr u32 PDA600_SCREEN_X = 38;
static constexpr u32 PDA600_SCREEN_Y = 54;
static constexpr u32 PDA600_SCREEN_W = (PDA600_SCREEN_X * 2 + 240);
static constexpr u32 PDA600_SCREEN_H = (PDA600_SCREEN_Y * 2 + 320);
static constexpr u32 PDA600_CSIO_RATE = 48000;
static constexpr u32 PDA600_SERIAL_PORT_RATE = 9600;
namespace {
@ -71,30 +114,138 @@ public:
pda600_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag)
, m_maincpu(*this, "maincpu")
, m_copro(*this, "copro")
, m_card(*this, "pcmcia")
, m_beep(*this, "beeper")
, m_pen(*this, {"PEN", "PENX", "PENY"})
, m_battery(*this, "BATTERY")
, m_video_ram(*this, "videoram")
{
}
void pda600(machine_config &config);
DECLARE_WRITE_LINE_MEMBER(power_off_w) { m_maincpu->set_input_line(Z180_INPUT_LINE_IRQ1, state); }
private:
required_device<cpu_device> m_maincpu;
required_shared_ptr<uint8_t> m_video_ram;
required_device<z180_device> m_maincpu;
required_device<pda600_copro_device> m_copro;
required_device<generic_slot_device> m_card;
required_device<beep_device> m_beep;
required_ioport_array<3> m_pen;
required_ioport m_battery;
required_shared_ptr<u8> m_video_ram;
virtual void video_start() override;
virtual void machine_start() override;
virtual void machine_reset() override;
uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
void io_w(offs_t offset, u8 data);
u8 io_r(offs_t offset);
void pcmcia_w(offs_t offset, u8 data);
u8 pcmcia_r(offs_t offset);
void tone_w(u8 data);
TIMER_CALLBACK_MEMBER(csio_clk_timer);
TIMER_CALLBACK_MEMBER(serl_clk_timer);
TIMER_DEVICE_CALLBACK_MEMBER(pen_update_timer);
DECLARE_DEVICE_IMAGE_LOAD_MEMBER(card_load);
DECLARE_DEVICE_IMAGE_UNLOAD_MEMBER(card_unload);
void pda600_io(address_map &map);
void pda600_mem(address_map &map);
emu_timer * m_csio_clk_timer;
emu_timer * m_serl_clk_timer;
u8 m_pen_data[6] = {};
u8 m_pen_shift = 0;
u8 m_pen_cnt = 0;
u32 m_card_size = 0;
u8 m_rtc_irq = 0;
u8 m_serl_clk = 0;
u8 m_io_regs[4] = {};
u8 m_lcd_ar = 0;
u8 m_lcd_regs[32] = {};
};
void pda600_state::tone_w(u8 data)
{
// xxxx ---- TCM5089 Column 1-4
// ---- xxxx TCM5089 Row 1-4
// TODO: DTMF tone encoder
if (data & 0x7f)
popmessage("DTMF tone %02X", data);
m_beep->set_state(BIT(data, 7));
}
u8 pda600_state::io_r(offs_t offset)
{
return m_io_regs[offset];
}
void pda600_state::io_w(offs_t offset, u8 data)
{
switch (offset)
{
case 0:
// xx-- ---- PCMCIA bank select
// ---x -x-- Serial
if (BIT(m_io_regs[offset] ^ data, 4))
{
auto period = attotime::zero;
if (BIT(data, 4))
period = attotime::from_hz(PDA600_SERIAL_PORT_RATE * 16 * 2);
m_serl_clk_timer->adjust(period, 0, period);
}
break;
case 1:
break;
case 2:
// ---- xxxx LCD contrast level
// ---x ---- Reset the coprocessor
// --x- ---- Wake up the coprocessor
if ((m_io_regs[offset] ^ data) & 0x0f)
logerror("Set LCD contrast level: %d\n", data & 0x0f);
m_copro->wakeup_w(BIT(data, 4));
m_copro->reset_w(BIT(data, 5));
break;
case 3:
// --X- ---- ??
break;
}
m_io_regs[offset] = data;
}
u8 pda600_state::pcmcia_r(offs_t offset)
{
offset |= (u32)(m_io_regs[0] & 0xc0) << 13;
if (offset < m_card_size)
return m_card->read_ram(offset);
return 0xff;
}
void pda600_state::pcmcia_w(offs_t offset, u8 data)
{
offset |= (u32)(m_io_regs[0] & 0xc0) << 13;
if (offset < m_card_size)
m_card->write_ram(offset, data);
}
void pda600_state::pda600_mem(address_map &map)
{
map.unmap_value_high();
map(0x00000, 0x1ffff).rom();
//map(0x20000, 0x9ffff).ram(); // PCMCIA Card
map(0x20000, 0x9ffff).rw(FUNC(pda600_state::pcmcia_r), FUNC(pda600_state::pcmcia_w));
map(0xa0000, 0xa7fff).ram().share("videoram");
map(0xe0000, 0xfffff).ram().share("nvram");
}
@ -104,37 +255,91 @@ void pda600_state::pda600_io(address_map &map)
map.unmap_value_high();
map.global_mask(0xff);
map(0x00, 0x3f).noprw(); /* Z180 internal registers */
//map(0x40, 0x7f).noprw(); /* Z180 internal registers */
map(0x40, 0x43).rw(FUNC(pda600_state::io_r), FUNC(pda600_state::io_w));
map(0x80, 0x8f).rw("rtc", FUNC(hd64610_device::read), FUNC(hd64610_device::write));
//map(0xc0, 0xc1).noprw(); /* LCD */
map(0xc0, 0xc0).lw8(NAME([this](u8 data) { m_lcd_ar = data & 0x1f; }));
map(0xc1, 0xc1).lw8(NAME([this](u8 data) { m_lcd_regs[m_lcd_ar] = data; }));
}
/* Input ports */
static INPUT_PORTS_START( pda600 )
PORT_START("POWER")
PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_POWER_OFF) PORT_WRITE_LINE_MEMBER(pda600_state, power_off_w)
PORT_START("BATTERY")
PORT_CONFNAME(0x0f, 0x0f, "Main battery status")
PORT_CONFSETTING(0x0f, "Good")
PORT_CONFSETTING(0x0c, "Fair")
PORT_CONFSETTING(0x06, "Low")
PORT_CONFSETTING(0x00, "Replace")
PORT_CONFNAME(0x10, 0x00, "Backup battery status")
PORT_CONFSETTING(0x00, "Good")
PORT_CONFSETTING(0x10, "Replace")
PORT_START("PENX")
PORT_BIT(0x3ff, 0x000, IPT_LIGHTGUN_X) PORT_SENSITIVITY(40) PORT_CROSSHAIR(X, 1, 0, 0) PORT_MINMAX(0, 0x3ff) PORT_KEYDELTA(1)
PORT_START("PENY")
PORT_BIT(0x3ff, 0x000, IPT_LIGHTGUN_Y) PORT_SENSITIVITY(40) PORT_CROSSHAIR(Y, 1, 0, 0) PORT_MINMAX(0, 0x3ff) PORT_KEYDELTA(1)
PORT_START("PEN")
PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Pen")
INPUT_PORTS_END
void pda600_state::machine_start()
{
// state saving
save_item(NAME(m_pen_data));
save_item(NAME(m_pen_shift));
save_item(NAME(m_pen_cnt));
save_item(NAME(m_card_size));
save_item(NAME(m_rtc_irq));
save_item(NAME(m_serl_clk));
save_item(NAME(m_io_regs));
save_item(NAME(m_lcd_ar));
save_item(NAME(m_lcd_regs));
m_csio_clk_timer = timer_alloc(FUNC(pda600_state::csio_clk_timer), this);
m_serl_clk_timer = timer_alloc(FUNC(pda600_state::serl_clk_timer), this);
}
void pda600_state::machine_reset()
{
// the PDA600 soon after start waits for something from the Z180 CSIO, I do not know exactly for what
// the CSIO is used and for now I forced the CNTR End-Flag bit to 1 for allow the emulation to continue.
m_maincpu->set_state_int(Z180_CNTR, m_maincpu->state_int(Z180_CNTR) | 0x80);
m_pen_shift = -1;
m_pen_cnt = 0;
m_lcd_ar = 0;
m_rtc_irq = 0;
m_serl_clk = 0;
std::fill(std::begin(m_pen_data), std::end(m_pen_data), 0U);
std::fill(std::begin(m_io_regs), std::end(m_io_regs), 0U);
std::fill(std::begin(m_lcd_regs), std::end(m_lcd_regs), 0U);
}
void pda600_state::video_start()
{
}
uint32_t pda600_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
u32 pda600_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
for (int y=0; y<320; y++)
for (int x=0; x<30; x++)
bitmap.fill(1);
if (!BIT(m_lcd_regs[0x16], 4))
return 0;
const u16 width = (m_lcd_regs[0x11] << 8) | m_lcd_regs[0x12];
const u16 height = (m_lcd_regs[0x13] << 8) | m_lcd_regs[0x14];
for (int y = 0; y <= height; y++)
for (int x = 0; x < width; x++)
{
uint8_t data = m_video_ram[y*30 + x];
const s32 dst_y = PDA600_SCREEN_Y + y;
const s32 dst_x = PDA600_SCREEN_X + x * 8;
u8 data = m_video_ram[y * m_lcd_regs[1] + x] ^ 0xff;
for (int px=0; px<8; px++)
for (int px = 0; px < 8; px++)
{
bitmap.pix(y, (x * 8) + px) = BIT(data, 7);
if (cliprect.contains(dst_x + px, dst_y))
bitmap.pix(dst_y, dst_x + px) = BIT(data, 7);
data <<= 1;
}
}
@ -206,19 +411,125 @@ static GFXDECODE_START( gfx_pda600 )
GFXDECODE_END
TIMER_DEVICE_CALLBACK_MEMBER(pda600_state::pen_update_timer)
{
u8 pen = m_pen[0]->read();
// Reduce the update rate when the pen is not held down
if (!pen && ++m_pen_cnt < 20)
return;
else
m_pen_cnt = 0;
m_pen_data[0] = 0x80; // Start of new data
m_pen_data[0] |= pen << 6; // Pen up/down
m_pen_data[0] |= m_rtc_irq << 5; // RTC IRQ status
m_pen_data[0] |= m_battery->read(); // Battery status
// Pen position (updated only when the pen is down)
if (pen)
{
u16 penx = m_pen[1]->read();
u16 peny = m_pen[2]->read();
m_pen_data[1] = penx & 0x7f;
m_pen_data[2] = (penx >> 7) & 0x7f;
m_pen_data[3] = peny & 0x7f;
m_pen_data[4] = (peny >> 7) & 0x7f;
}
// Data checksum
m_pen_data[5] = m_pen_data[0] + m_pen_data[1] + m_pen_data[2] + m_pen_data[3] + m_pen_data[4];
// Start CSIO clock
m_pen_shift = 0;
m_csio_clk_timer->adjust(attotime::zero);
}
TIMER_CALLBACK_MEMBER(pda600_state::csio_clk_timer)
{
if (m_pen_shift < 48)
{
m_maincpu->cks_w(0);
m_maincpu->rxs_cts1_w(BIT(m_pen_data[m_pen_shift / 8], m_pen_shift & 7));
m_maincpu->cks_w(1);
m_pen_shift++;
// If there is still data to send, reschedule the timer
if (m_pen_shift < 48)
{
auto delay = attotime::from_hz(PDA600_CSIO_RATE);
// A delay is added after each byte to allow the maincpu to read the transmitted data
if (!(m_pen_shift & 7))
delay += attotime::from_usec(150);
m_csio_clk_timer->adjust(delay);
}
}
}
TIMER_CALLBACK_MEMBER(pda600_state::serl_clk_timer)
{
// External clock for the Z180 ASCI0
m_serl_clk ^= 1;
m_maincpu->cka0_w(m_serl_clk);
}
DEVICE_IMAGE_LOAD_MEMBER(pda600_state::card_load)
{
if (!image.loaded_through_softlist())
{
const u64 size = image.length();
m_card->ram_alloc(size);
if (size != image.fread(m_card->get_ram_base(), size))
return image_init_result::FAIL;
m_card_size = size;
}
else
{
m_card_size = image.get_software_region_length("rom");
if (m_card_size == 0)
return image_init_result::FAIL;
m_card->ram_alloc(m_card_size);
memcpy(m_card->get_ram_base(), image.get_software_region("rom"), m_card_size);
}
m_card->battery_load(m_card->get_ram_base(), m_card_size, nullptr);
return image_init_result::PASS;
}
DEVICE_IMAGE_UNLOAD_MEMBER(pda600_state::card_unload)
{
m_card->battery_save(m_card->get_ram_base(), m_card_size);
memset(m_card->get_ram_base(), 0xff, m_card_size);
m_card_size = 0;
}
void pda600_state::pda600(machine_config &config)
{
/* basic machine hardware */
Z8S180(config, m_maincpu, XTAL(14'318'181));
Z8S180(config, m_maincpu, 28'636'363_Hz_XTAL / 2);
m_maincpu->set_addrmap(AS_PROGRAM, &pda600_state::pda600_mem);
m_maincpu->set_addrmap(AS_IO, &pda600_state::pda600_io);
m_maincpu->txa0_wr_callback().set("serial", FUNC(rs232_port_device::write_txd));
m_maincpu->rts0_wr_callback().set("serial", FUNC(rs232_port_device::write_rts));
m_maincpu->txa1_wr_callback().set(m_copro, FUNC(pda600_copro_device::write_txd));
/* video hardware */
screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD));
screen.set_refresh_hz(50);
screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
screen.set_size(240, 320);
screen.set_visarea(0, 240-1, 0, 320-1);
screen.set_size(PDA600_SCREEN_W, PDA600_SCREEN_H);
screen.set_visarea_full();
screen.set_screen_update(FUNC(pda600_state::screen_update));
screen.set_palette("palette");
@ -226,9 +537,35 @@ void pda600_state::pda600(machine_config &config)
PALETTE(config, "palette", palette_device::MONOCHROME);
// NVRAM needs to be filled with random data to fail the checksum and be initialized correctly
NVRAM(config, "nvram", nvram_device::DEFAULT_RANDOM);
NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);
HD64610(config, "rtc", XTAL(32'768));
hd64610_device &rtc(HD64610(config, "rtc", 32.768_kHz_XTAL));
rtc.irq().set([this](int state) { m_rtc_irq = state; }).invert();
TIMER(config, "pen_update_timer").configure_periodic(FUNC(pda600_state::pen_update_timer), attotime::from_hz(100));
GENERIC_CARTSLOT(config, m_card, generic_romram_plain_slot, "pda600", "bin");
m_card->set_device_load(FUNC(pda600_state::card_load));
m_card->set_device_unload(FUNC(pda600_state::card_unload));
m_card->set_interface("pcmcia");
PDA600_COPRO_HLE(config, m_copro, 28'636'363_Hz_XTAL / 2);
m_copro->tx_callback().set(m_maincpu, FUNC(z180_device::rxa1_w));
m_copro->tone_callback().set(FUNC(pda600_state::tone_w));
rs232_port_device &rs232(RS232_PORT(config, "serial", default_rs232_devices, "printer"));
rs232.rxd_handler().set(m_maincpu, FUNC(z180_device::rxa0_w));
rs232.cts_handler().set(m_maincpu, FUNC(z180_device::cts0_w));
rs232.cts_handler().append_inputline(m_maincpu, Z180_INPUT_LINE_DREQ0).invert();
// sound hardware
SPEAKER(config, "mono").front_center();
BEEP(config, m_beep, 1633).add_route(ALL_OUTPUTS, "mono", 0.80); // TODO: replace with TCM5089
// software lists
SOFTWARE_LIST(config, "card_list").set_original("pda600");
config.set_default_layout(layout_pda600);
}
/* ROM definition */

View File

@ -0,0 +1,518 @@
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/**********************************************************************
Amstrad PenPad PDA 600 character recognition Coprocessor HLE
The protocol flow control:
- 0x01 SOH Start a new frame
- 0x06 ACK Ack a request
- 0x15 NAK Negative ack
Frame format:
+--------+--------+--------+---//---+----------+
| 0x01 | Length | Type | Data | Checksum |
+--------+--------+--------+---//---+----------+
Length:
Length of data + 1.
Type:
Frame type:
- 0x54 (T) Train a character
- 0x52 (R) Recognize a character
- 0x42 (B) Beep
- 0x49 (I) Init
- 0x53 (S) Sleep
- 0x46 (F) Used before training a character
- 0x59 (Y) Affirmative response
- 0x4e (N) Negative response
Checksum:
The two's complement of the 8-bit sum of Length, Type and Data.
**********************************************************************/
#include "emu.h"
#include "pda600_copro.h"
//**************************************************************************
// MACROS / CONSTANTS
//**************************************************************************
static constexpr u8 PDA600_SOH = 0x01;
static constexpr u8 PDA600_ACK = 0x06;
static constexpr u8 PDA600_NAK = 0x15;
enum : u8
{
STATE_READY,
STATE_WAIT_ACK,
STATE_SLEEP,
STATE_BUSY,
STATE_PLAY_TONE,
};
//**************************************************************************
// DEVICE DEFINITIONS
//**************************************************************************
DEFINE_DEVICE_TYPE(PDA600_COPRO_HLE, pda600_copro_device, "pda600_copro", "PDA600 Coprocessor (HLE)")
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
//-------------------------------------------------
// pda600_copro_device - constructor
//-------------------------------------------------
pda600_copro_device::pda600_copro_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: device_t(mconfig, PDA600_COPRO_HLE, tag, owner, clock)
, device_buffered_serial_interface(mconfig, *this)
, m_tx_cb(*this)
, m_tone_cb(*this)
, m_fake_ioport(*this, "FAKE%u", 0U)
, m_state(0)
, m_resp_type(0)
, m_resp_data(0)
, m_buf_size(0)
{
}
static INPUT_PORTS_START(pda600_copro)
PORT_START("FAKE0") // 0x20 - 0x3f
PORT_BIT(0x80000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(' ') PORT_CODE(KEYCODE_SPACE)
PORT_BIT(0x40000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('!')
PORT_BIT(0x20000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'£')
PORT_BIT(0x10000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('#')
PORT_BIT(0x08000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('$')
PORT_BIT(0x04000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('%')
PORT_BIT(0x02000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('&')
PORT_BIT(0x01000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('\'') PORT_CODE(KEYCODE_QUOTE)
PORT_BIT(0x00800000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('(')
PORT_BIT(0x00400000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(')')
PORT_BIT(0x00200000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('*') PORT_CODE(KEYCODE_ASTERISK)
PORT_BIT(0x00100000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('+') PORT_CODE(KEYCODE_PLUS_PAD)
PORT_BIT(0x00080000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(',') PORT_CODE(KEYCODE_COMMA)
PORT_BIT(0x00040000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('-') PORT_CODE(KEYCODE_MINUS)
PORT_BIT(0x00020000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('.') PORT_CODE(KEYCODE_STOP)
PORT_BIT(0x00010000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('/') PORT_CODE(KEYCODE_SLASH)
PORT_BIT(0x00008000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('0') PORT_CODE(KEYCODE_0)
PORT_BIT(0x00004000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('1') PORT_CODE(KEYCODE_1)
PORT_BIT(0x00002000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('2') PORT_CODE(KEYCODE_2)
PORT_BIT(0x00001000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('3') PORT_CODE(KEYCODE_3)
PORT_BIT(0x00000800, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('4') PORT_CODE(KEYCODE_4)
PORT_BIT(0x00000400, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('5') PORT_CODE(KEYCODE_5)
PORT_BIT(0x00000200, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('6') PORT_CODE(KEYCODE_6)
PORT_BIT(0x00000100, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('7') PORT_CODE(KEYCODE_7)
PORT_BIT(0x00000080, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('8') PORT_CODE(KEYCODE_8)
PORT_BIT(0x00000040, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('9') PORT_CODE(KEYCODE_9)
PORT_BIT(0x00000020, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(':')
PORT_BIT(0x00000010, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(';') PORT_CODE(KEYCODE_COLON)
PORT_BIT(0x00000008, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'²')
PORT_BIT(0x00000004, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('=') PORT_CODE(KEYCODE_EQUALS)
PORT_BIT(0x00000002, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'³')
PORT_BIT(0x00000001, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('?')
PORT_START("FAKE1") // 0x40 - 0x5f
PORT_BIT(0x80000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'°')
PORT_BIT(0x40000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('A') PORT_CODE(KEYCODE_A)
PORT_BIT(0x20000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('B') PORT_CODE(KEYCODE_B)
PORT_BIT(0x10000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('C') PORT_CODE(KEYCODE_C)
PORT_BIT(0x08000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('D') PORT_CODE(KEYCODE_D)
PORT_BIT(0x04000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('E') PORT_CODE(KEYCODE_E)
PORT_BIT(0x02000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('F') PORT_CODE(KEYCODE_F)
PORT_BIT(0x01000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('G') PORT_CODE(KEYCODE_G)
PORT_BIT(0x00800000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('H') PORT_CODE(KEYCODE_H)
PORT_BIT(0x00400000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('I') PORT_CODE(KEYCODE_I)
PORT_BIT(0x00200000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('J') PORT_CODE(KEYCODE_J)
PORT_BIT(0x00100000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('K') PORT_CODE(KEYCODE_K)
PORT_BIT(0x00080000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('L') PORT_CODE(KEYCODE_L)
PORT_BIT(0x00040000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('M') PORT_CODE(KEYCODE_M)
PORT_BIT(0x00020000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('N') PORT_CODE(KEYCODE_N)
PORT_BIT(0x00010000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('O') PORT_CODE(KEYCODE_O)
PORT_BIT(0x00008000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('P') PORT_CODE(KEYCODE_P)
PORT_BIT(0x00004000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('Q') PORT_CODE(KEYCODE_Q)
PORT_BIT(0x00002000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('R') PORT_CODE(KEYCODE_R)
PORT_BIT(0x00001000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('S') PORT_CODE(KEYCODE_S)
PORT_BIT(0x00000800, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('T') PORT_CODE(KEYCODE_T)
PORT_BIT(0x00000400, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('U') PORT_CODE(KEYCODE_U)
PORT_BIT(0x00000200, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('V') PORT_CODE(KEYCODE_V)
PORT_BIT(0x00000100, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('W') PORT_CODE(KEYCODE_W)
PORT_BIT(0x00000080, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('X') PORT_CODE(KEYCODE_X)
PORT_BIT(0x00000040, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('Y') PORT_CODE(KEYCODE_Y)
PORT_BIT(0x00000020, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('Z') PORT_CODE(KEYCODE_Z)
PORT_BIT(0x00000010, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'¿')
PORT_BIT(0x00000008, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'Ä')
PORT_BIT(0x00000004, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'Å')
PORT_BIT(0x00000002, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'Æ')
PORT_BIT(0x00000001, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'Ç')
PORT_START("FAKE2") // 0x60 - 0x7f
PORT_BIT(0x80000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'Ë')
PORT_BIT(0x40000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('a')
PORT_BIT(0x20000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('b')
PORT_BIT(0x10000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('c')
PORT_BIT(0x08000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('d')
PORT_BIT(0x04000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('e')
PORT_BIT(0x02000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('f')
PORT_BIT(0x01000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('g')
PORT_BIT(0x00800000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('h')
PORT_BIT(0x00400000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('i')
PORT_BIT(0x00200000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('j')
PORT_BIT(0x00100000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('k')
PORT_BIT(0x00080000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('l')
PORT_BIT(0x00040000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('m')
PORT_BIT(0x00020000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('n')
PORT_BIT(0x00010000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('o')
PORT_BIT(0x00008000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('p')
PORT_BIT(0x00004000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('q')
PORT_BIT(0x00002000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('r')
PORT_BIT(0x00001000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('s')
PORT_BIT(0x00000800, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('t')
PORT_BIT(0x00000400, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('u')
PORT_BIT(0x00000200, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('v')
PORT_BIT(0x00000100, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('w')
PORT_BIT(0x00000080, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('x')
PORT_BIT(0x00000040, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('y')
PORT_BIT(0x00000020, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR('z')
PORT_BIT(0x00000010, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'Ñ')
PORT_BIT(0x00000008, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'Ö')
PORT_BIT(0x00000004, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'Ø')
PORT_BIT(0x00000002, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'Ù')
PORT_BIT(0x00000001, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'Ü')
PORT_START("FAKE3") // 0x80 - 0x9a
PORT_BIT(0x80000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'à')
PORT_BIT(0x40000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'á')
PORT_BIT(0x20000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'â')
PORT_BIT(0x10000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'ä')
PORT_BIT(0x08000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'å')
PORT_BIT(0x04000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'æ')
PORT_BIT(0x02000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'ç')
PORT_BIT(0x01000000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'è')
PORT_BIT(0x00800000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'é')
PORT_BIT(0x00400000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'ê')
PORT_BIT(0x00200000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'ë')
PORT_BIT(0x00100000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'ì')
PORT_BIT(0x00080000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'í')
PORT_BIT(0x00040000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'î')
PORT_BIT(0x00020000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'ï')
PORT_BIT(0x00010000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'ñ')
PORT_BIT(0x00008000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'ò')
PORT_BIT(0x00004000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'ó')
PORT_BIT(0x00002000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'ô')
PORT_BIT(0x00001000, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'ö')
PORT_BIT(0x00000800, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'ø')
PORT_BIT(0x00000400, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'ù')
PORT_BIT(0x00000200, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'ú')
PORT_BIT(0x00000100, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'û')
PORT_BIT(0x00000080, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'ü')
PORT_BIT(0x00000040, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'β')
PORT_BIT(0x00000020, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CHAR(U'§')
INPUT_PORTS_END
ioport_constructor pda600_copro_device::device_input_ports() const
{
return INPUT_PORTS_NAME(pda600_copro);
}
void pda600_copro_device::device_resolve_objects()
{
// resolve callbacks
m_tx_cb.resolve_safe();
m_tone_cb.resolve_safe();
}
void pda600_copro_device::device_start()
{
// parameters used by the MAINCPU to configure the Z180 ASCI1
set_data_frame(1, 8, PARITY_NONE, STOP_BITS_1);
set_rate(clock() / 1280);
// state saving
save_item(NAME(m_state));
save_item(NAME(m_resp_type));
save_item(NAME(m_resp_data));
save_item(NAME(m_buf));
save_item(NAME(m_buf_size));
m_update_timer = timer_alloc(FUNC(pda600_copro_device::update_timer), this);
}
void pda600_copro_device::device_reset()
{
m_tx_cb(1);
m_tone_cb(0);
m_state = STATE_READY;
m_buf_size = 0;
m_resp_type = m_resp_data = 0;
m_update_timer->adjust(attotime::never);
}
TIMER_CALLBACK_MEMBER(pda600_copro_device::update_timer)
{
if (m_state == STATE_PLAY_TONE && m_buf_size >= 2)
{
m_tone_cb(m_buf[0]);
m_update_timer->adjust(attotime::from_msec(m_buf[1] * 10));
m_buf_size -= 2;
std::memmove(m_buf.begin(), m_buf.begin() + 2, m_buf_size);
}
else
{
if (m_state == STATE_PLAY_TONE)
m_tone_cb(0); // Mute the DTMF tone generator
// We are ready to send the response
send_byte(PDA600_SOH);
m_state = STATE_WAIT_ACK;
}
}
WRITE_LINE_MEMBER( pda600_copro_device::wakeup_w )
{
if (m_state != STATE_SLEEP && m_state != STATE_READY)
logerror("PDA600: wakeup_w in %d state\n", m_state);
if (state && m_state == STATE_SLEEP)
send_byte(PDA600_ACK);
m_state = state ? STATE_READY : STATE_SLEEP;
}
void pda600_copro_device::send_byte(u8 byte)
{
transmit_byte(byte);
}
void pda600_copro_device::received_byte(u8 byte)
{
if (m_state != STATE_READY && m_state != STATE_WAIT_ACK)
{
logerror("PDA600: write %02x in %d state\n", byte, m_state);
return;
}
if (m_state == STATE_WAIT_ACK)
{
if (byte == PDA600_ACK)
{
send_resp();
}
else
{
logerror("PDA600: Invalid ack %02x\n", byte);
send_byte(PDA600_NAK);
}
if (m_buf_size > 2 && m_buf[2] == 'S')
m_state = STATE_SLEEP;
else
m_state = STATE_READY;
m_buf_size = 0;
return;
}
m_buf[m_buf_size++] = byte;
if (m_buf[0] == PDA600_ACK)
{
m_buf_size = 0;
}
else if (m_buf[0] != PDA600_SOH)
{
logerror("PDA600: unknown start %02x\n", byte);
send_byte(PDA600_NAK);
m_buf_size = 0;
}
else if (m_buf_size == 1)
{
send_byte(PDA600_ACK);
}
else if (m_buf_size == m_buf[1] + 3)
{
if (0)
{
std::string tmp = "";
for (auto b : m_buf)
tmp += util::string_format("%02X ", b);
logerror("%s\n", tmp);
}
u8 checksum = 0;
for (int i = 1; i < m_buf_size; i++)
checksum += m_buf[i];
m_resp_type = m_resp_data = 0;
if (checksum != 0)
{
logerror("PDA600: invalid frame checksum\n");
send_byte(PDA600_NAK);
return;
}
switch (m_buf[2])
{
case 'T': // Train a character
exec_train();
break;
case 'R': // Recognize a character
exec_recognize();
break;
case 'B': // Beep
exec_beep();
break;
case 'F': // Used before training a character
m_resp_type = 'Y';
[[fallthrough]];
case 'S': // Sleep
case 'I': // Init
m_update_timer->adjust(attotime::from_msec(1)); // Time required to complete the command
break;
default:
logerror("PDA600: unknown frame type %02x\n", m_buf[2]);
send_byte(PDA600_NAK);
}
}
}
void pda600_copro_device::send_resp()
{
// Frame format:
// +--------+--------+--------+--------+--------+--------+----------+
// | 0x01 | Length | Type | Param0 | Param1 | Param2 | Checksum |
// +--------+--------+--------+--------+--------+--------+----------+
//
// Type:
// 'Y' for affirmative response
// 'N' for negative response
//
// Param0:
// Recognized character.
//
// Param1 / Param2:
// Appears to be unused.
u8 checksum = 0;
if (m_resp_type != 0)
{
send_byte(0x04); // Data length
send_byte(m_resp_type); // Frame type
send_byte(m_resp_data); // Param0
send_byte(0x00); // Param1
send_byte(0x00); // Param2
checksum += 0x04 + m_resp_type + m_resp_data;
}
else
send_byte(0x00); // Data length for an empty response
send_byte(0x100 - checksum); // Checksum
m_resp_type = m_resp_data = 0;
}
void pda600_copro_device::exec_beep()
{
// Frame format:
// +--------+--------+--------+--------+----------+---//--+----------+
// | 0x01 | Length | Type | Tone | Duration | ... | Checksum |
// +--------+--------+--------+--------+----------+---//--+----------+
//
// Tone:
// xxxx ---- TMC5089 Column 1-4
// ---- xxxx TMC5089 Row 1-4
//
// Duration:
// Duration in centiseconds.
//
// More than one Tone/Duration pair can be used.
m_resp_type = m_resp_data = 0; // Empty response
m_state = STATE_PLAY_TONE;
m_buf_size -= 4;
std::memmove(m_buf.begin(), m_buf.begin() + 3, m_buf_size);
m_update_timer->adjust(attotime::zero);
}
void pda600_copro_device::exec_recognize()
{
// Frame format:
// +--------+--------+--------+-----//----+---------+-------+----------+
// | 0x01 | Length | Type | Image[32] | Strokes | Flags | Checksum |
// +--------+--------+--------+-----//----+---------+-------+----------+
//
// Image:
// 16x16 monochrome image of the character to be recognized.
//
// Strokes:
// Number of pen strokes used to draw the character.
//
// Flags:
// ---- ---x Set if the upper third of the vertical space is used.
// ---- --x- Set if the middle third of the vertical space is used.
// ---- -x-- Set if the lower third of the vertical space is used.
m_resp_data = recognize_char();
m_resp_type = m_resp_data != 0 ? 'Y' : 'N';
m_state = STATE_BUSY;
m_update_timer->adjust(attotime::from_msec(50)); // Time required to complete the recognition
}
void pda600_copro_device::exec_train()
{
// Frame format:
// +--------+--------+--------+-----//----+---------+-------+--------+----------+
// | 0x01 | Length | Type | Image[32] | Strokes | Flags | Char | Checksum |
// +--------+--------+--------+-----//----+---------+-------+--------+----------+
//
// Image, Strokes, Flags:
// Same as recognition frame.
//
// Char:
// Character to be trained with the given image.
m_resp_type = 'Y';
m_state = STATE_BUSY;
m_update_timer->adjust(attotime::from_msec(50)); // Time required to complete the training
}
u8 pda600_copro_device::recognize_char()
{
// We don't implement any character recognition functionality, but simply return
// the character corresponding to the key pressed.
for (int i = 0; i < 4; i++)
{
u32 value = m_fake_ioport[i]->read();
if (value != 0)
return 0x20 + i * 32 + count_leading_zeros_32(value);
}
return 0;
}

View File

@ -0,0 +1,68 @@
// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/**********************************************************************
Amstrad PDA600 Coprocessor HLE
**********************************************************************/
#ifndef MAME_MACHINE_PDA600_COPRO_H
#define MAME_MACHINE_PDA600_COPRO_H
#pragma once
#include "diserial.h"
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
// ======================> pda600_copro_device
class pda600_copro_device : public device_t,
public device_buffered_serial_interface<1 + 1 + 255 + 1>
{
public:
pda600_copro_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
auto tx_callback() { return m_tx_cb.bind(); }
auto tone_callback() { return m_tone_cb.bind(); }
DECLARE_WRITE_LINE_MEMBER( wakeup_w );
DECLARE_WRITE_LINE_MEMBER( reset_w ) { if (state) device_reset(); }
DECLARE_WRITE_LINE_MEMBER( write_txd ) { rx_w(state); }
private:
// device-level overrides
virtual ioport_constructor device_input_ports() const override;
virtual void device_resolve_objects() override;
virtual void device_start() override;
virtual void device_reset() override;
// device_serial_interface overrides
void tra_callback() override { m_tx_cb(transmit_register_get_data_bit()); }
void received_byte(u8 byte) override;
TIMER_CALLBACK_MEMBER(update_timer);
void send_byte(u8 byte);
void send_resp();
void exec_beep();
void exec_train();
void exec_recognize();
u8 recognize_char();
devcb_write_line m_tx_cb;
devcb_write8 m_tone_cb;
required_ioport_array<4> m_fake_ioport;
emu_timer * m_update_timer;
u8 m_state;
u8 m_resp_type;
u8 m_resp_data;
u8 m_buf_size;
std::array<u8,256> m_buf;
};
DECLARE_DEVICE_TYPE(PDA600_COPRO_HLE, pda600_copro_device)
#endif // MAME_MACHINE_PDA600_COPRO_H

View File

@ -0,0 +1,81 @@
<?xml version="1.0"?>
<!--
license:CC0-1.0
-->
<mamelayout version="2">
<element name="contrast"> <text string="-/+"> <color red="0" green="0" blue="0" /> </text> </element>
<element name="add_page"> <text string="ADD"> <color red="0" green="0" blue="0" /> </text> </element>
<element name="calculator"> <text string="CALC"> <color red="0" green="0" blue="0" /> </text> </element>
<element name="desk"> <text string="DESK"> <color red="0" green="0" blue="0" /> </text> </element>
<element name="delete"> <text string="DEL"> <color red="0" green="0" blue="0" /> </text> </element>
<element name="previous"> <text string="&lt;&lt;"> <color red="0" green="0" blue="0" /> </text> </element>
<element name="next"> <text string="&gt;&gt;"> <color red="0" green="0" blue="0" /> </text> </element>
<element name="addresses"> <text string="ADDR"> <color red="0" green="0" blue="0" /> </text> </element>
<element name="diary"> <text string="DIARY"> <color red="0" green="0" blue="0" /> </text> </element>
<element name="todo"> <text string="TODO"> <color red="0" green="0" blue="0" /> </text> </element>
<element name="notes"> <text string="NOTES"> <color red="0" green="0" blue="0" /> </text> </element>
<element name="conversion"> <text string="CONV"> <color red="0" green="0" blue="0" /> </text> </element>
<element name="amstrad"> <text string="Amstrad"> <color red="0" green="0" blue="0" /> </text> </element>
<element name="ring">
<rect> <color red="1" green="1" blue="1" /> <bounds x="0.00" y="0.00" width="1.00" height="1.00" /> </rect>
<disk> <color red="0" green="0" blue="0" /> <bounds x="0.00" y="0.05" width="0.25" height="0.90" /> </disk>
<rect> <color red="0" green="0" blue="0" /> <bounds x="0.00" y="0.20" width="1.00" height="0.60" /> </rect>
<rect> <color red="1" green="1" blue="1" /> <bounds x="0.08" y="0.29" width="0.92" height="0.42" /> </rect>
<rect> <color red="0" green="0" blue="0" /> <bounds x="0.93" y="0.29" width="0.07" height="0.21" /> </rect>
</element>
<element name="rect">
<rect> <color red="0" green="0" blue="0" /> <bounds x="0.00" y="0.00" width="1.00" height="1.00" /> </rect>
<rect> <color red="1" green="1" blue="1" /> <bounds x="0.04" y="0.04" width="0.92" height="0.92" /> </rect>
</element>
<element name="background"> <rect> <color red="0.59" green="0.63" blue="0.47" /> </rect> </element>
<element name="overlay"> <rect> <color red="0.91" green="0.92" blue="0.82" /> </rect> </element>
<view name="Internal Layout">
<bounds left="0" top="0" right="316" bottom="428" />
<screen index="0"> <bounds x="0" y="0" width="316" height="428" /> </screen>
<collection name="Screen overlay" visible="yes">
<element ref="overlay" blend="multiply"> <bounds x="38" y="54" width="240" height="320" /> </element>
</collection>
<element ref="background" blend="multiply"> <bounds x="0" y="0" width="38" height="428" /> </element>
<element ref="background" blend="multiply"> <bounds x="278" y="0" width="38" height="428" /> </element>
<element ref="background" blend="multiply"> <bounds x="38" y="0" width="240" height="54" /> </element>
<element ref="background" blend="multiply"> <bounds x="38" y="374" width="240" height="54" /> </element>
<element ref="background" blend="multiply"> <bounds x="0" y="54" width="30" height="320" /> </element>
<element ref="ring" blend="multiply"> <bounds x="13" y="106" width="25" height="10" /> </element>
<element ref="ring" blend="multiply"> <bounds x="13" y="134" width="25" height="10" /> </element>
<element ref="ring" blend="multiply"> <bounds x="13" y="159" width="25" height="10" /> </element>
<element ref="ring" blend="multiply"> <bounds x="13" y="264" width="25" height="10" /> </element>
<element ref="ring" blend="multiply"> <bounds x="13" y="292" width="25" height="10" /> </element>
<element ref="ring" blend="multiply"> <bounds x="13" y="317" width="25" height="10" /> </element>
<element ref="rect" blend="multiply"> <bounds x="39" y="20" width="28" height="26" /> </element>
<element ref="contrast"> <bounds x="42" y="22" width="22" height="22" /> </element>
<element ref="rect" blend="multiply"> <bounds x="74" y="20" width="28" height="26" /> </element>
<element ref="add_page"> <bounds x="77" y="22" width="22" height="22" /> </element>
<element ref="rect" blend="multiply"> <bounds x="109" y="20" width="28" height="26" /> </element>
<element ref="calculator"> <bounds x="112" y="22" width="22" height="22" /> </element>
<element ref="rect" blend="multiply"> <bounds x="144" y="20" width="28" height="26" /> </element>
<element ref="desk"> <bounds x="147" y="22" width="22" height="22" /> </element>
<element ref="rect" blend="multiply"> <bounds x="179" y="20" width="28" height="26" /> </element>
<element ref="delete"> <bounds x="182" y="22" width="22" height="22" /> </element>
<element ref="rect" blend="multiply"> <bounds x="215" y="20" width="28" height="26" /> </element>
<element ref="previous"> <bounds x="218" y="22" width="22" height="22" /> </element>
<element ref="rect" blend="multiply"> <bounds x="249" y="20" width="28" height="26" /> </element>
<element ref="next"> <bounds x="252" y="19" width="22" height="26" /> </element>
<element ref="rect" blend="multiply"> <bounds x="282" y="71" width="23" height="58" /> </element>
<element ref="addresses"> <bounds x="285" y="79" width="17" height="42" /> <orientation rotate="270" /> </element>
<element ref="rect" blend="multiply"> <bounds x="282" y="131" width="23" height="58" /> </element>
<element ref="diary"> <bounds x="285" y="139" width="17" height="42" /> <orientation rotate="270" /> </element>
<element ref="rect" blend="multiply"> <bounds x="282" y="191" width="23" height="58" /> </element>
<element ref="todo"> <bounds x="285" y="199" width="17" height="42" /> <orientation rotate="270" /> </element>
<element ref="rect" blend="multiply"> <bounds x="282" y="251" width="23" height="58" /> </element>
<element ref="notes"> <bounds x="285" y="259" width="17" height="42" /> <orientation rotate="270" /> </element>
<element ref="rect" blend="multiply"> <bounds x="282" y="311" width="23" height="58" /> </element>
<element ref="conversion"> <bounds x="285" y="319" width="17" height="42" /> <orientation rotate="270" /> </element>
<element ref="amstrad"> <bounds x="0" y="385" width="316" height="25"/> </element>
</view>
</mamelayout>

View File

@ -307,9 +307,10 @@ public:
protected:
virtual void machine_start() override;
virtual void machine_reset() override;
private:
void z180_trdr_w(uint8_t data);
void csio_cks_w(int state);
void port90_bitswap_w(uint8_t data);
uint8_t ppi_bitswap_r(offs_t offset);
void ppi_bitswap_w(offs_t offset, uint8_t data);
@ -318,11 +319,14 @@ private:
uint8_t input_port_c_r();
void output_port_c_w(uint8_t data);
uint8_t m_trdr = 0;
uint8_t m_csio_in = 0;
uint16_t m_csio_out = 0;
uint8_t m_csio_txs = 0;
uint8_t m_csio_cnt = 0;
uint8_t m_led_on = 0;
required_device<v9938_device> m_v9938;
required_device<cpu_device> m_maincpu;
required_device<z180_device> m_maincpu;
required_device<i8255_device> m_ppi;
required_device<dac_byte_interface> m_dac;
required_ioport m_aux;
@ -341,10 +345,21 @@ void luckybal_state::machine_start()
{
m_lamps.resolve();
save_item(NAME(m_trdr));
save_item(NAME(m_csio_in));
save_item(NAME(m_csio_out));
save_item(NAME(m_csio_txs));
save_item(NAME(m_csio_cnt));
save_item(NAME(m_led_on));
}
void luckybal_state::machine_reset()
{
m_csio_in = 0;
m_csio_out = 0;
m_csio_txs = 0;
m_csio_cnt = 0;
}
/**************************************
* Memory Map *
@ -361,9 +376,7 @@ void luckybal_state::main_io(address_map &map)
{
map.global_mask(0xff);
map(0x00, 0x0a).nopr().nopw(); // Z180 Internal registers.
map(0x0b, 0x0b).nopr().w(FUNC(luckybal_state::z180_trdr_w));
map(0x0c, 0x3f).nopr().nopw(); // Z180 Internal registers.
map(0x00, 0x3f).nopr().nopw(); // Z180 Internal registers.
map(0x90, 0x90).w(FUNC(luckybal_state::port90_bitswap_w));
map(0xc0, 0xc3).rw(FUNC(luckybal_state::ppi_bitswap_r), FUNC(luckybal_state::ppi_bitswap_w));
@ -394,9 +407,33 @@ M_MAP EQU 90H ; [A]= Bank to select (BIT6=MEM, BIT7=EN_NMI)
* R/W handlers *
**************************************/
void luckybal_state::z180_trdr_w(uint8_t data)
void luckybal_state::csio_cks_w(int state)
{
m_trdr = data;
if (!state)
{
m_maincpu->rxs_cts1_w(BIT(m_csio_out, 0));
m_csio_out >>= 1;
}
else
{
m_csio_in >>= 1;
m_csio_in |= m_csio_txs << 7;
if (++m_csio_cnt == 8)
{
m_csio_cnt = 0;
const uint8_t csio_data = m_csio_in & 0x7f;
if (csio_data <= 36)
{
m_lamps[m_led_on] = 0;
m_lamps[csio_data] = 1;
m_led_on = csio_data;
}
// read back the previously written value
m_csio_out |= m_csio_in << 8;
}
}
}
void luckybal_state::port90_bitswap_w(uint8_t data)
@ -417,16 +454,6 @@ void luckybal_state::ppi_bitswap_w(offs_t offset, uint8_t data)
void luckybal_state::output_port_a_w(uint8_t data)
{
if (m_trdr & 0x80)
m_trdr = m_trdr & 0x7f;
if (m_trdr <= 36)
{
m_lamps[m_led_on] = 0;
m_lamps[m_trdr] = 1;
m_led_on = m_trdr;
}
m_dac->write(data);
}
@ -583,6 +610,8 @@ void luckybal_state::luckybal(machine_config &config)
Z80180(config, m_maincpu, CPU_CLOCK);
m_maincpu->set_addrmap(AS_PROGRAM, &luckybal_state::main_map);
m_maincpu->set_addrmap(AS_IO, &luckybal_state::main_io);
m_maincpu->cks_wr_callback().set(FUNC(luckybal_state::csio_cks_w));
m_maincpu->txs_wr_callback().set([this] (int state) { m_csio_txs = state; });
I8255A(config, m_ppi);
m_ppi->out_pa_callback().set(FUNC(luckybal_state::output_port_a_w));