From 6908e701326c84befa6ae4683ce8c38f1a75a7e0 Mon Sep 17 00:00:00 2001
From: qufb <93520295+qufb@users.noreply.github.com>
Date: Sat, 16 Nov 2024 18:52:41 +0000
Subject: sega/sega_ferie.cpp: Add new not working systems (#11862)
New systems added as NOT WORKING
---------------------------------------
Ferie Kitten (1994, Sega)
Ferie Puppy (1995, Sega)
Ferie World Travel (1995, Sega)
---
scripts/src/cpu.lua | 2 +
src/devices/cpu/z80/t6a84.cpp | 221 +++++++++++++
src/devices/cpu/z80/t6a84.h | 91 ++++++
src/devices/cpu/z80/z80.cpp | 3 +
src/devices/cpu/z80/z80.h | 14 +
src/devices/cpu/z80/z80.lst | 16 +-
src/mame/layout/ferie.lay | 108 +++++++
src/mame/mame.lst | 5 +
src/mame/sega/sega_ferie.cpp | 699 ++++++++++++++++++++++++++++++++++++++++++
9 files changed, 1154 insertions(+), 5 deletions(-)
create mode 100644 src/devices/cpu/z80/t6a84.cpp
create mode 100644 src/devices/cpu/z80/t6a84.h
create mode 100644 src/mame/layout/ferie.lay
create mode 100644 src/mame/sega/sega_ferie.cpp
diff --git a/scripts/src/cpu.lua b/scripts/src/cpu.lua
index de0f2fe40d8..1b8f0f97b0d 100644
--- a/scripts/src/cpu.lua
+++ b/scripts/src/cpu.lua
@@ -3028,6 +3028,8 @@ if CPUS["Z80"] or CPUS["KC80"] or CPUS["Z80N"] then
files {
MAME_DIR .. "src/devices/cpu/z80/z80.cpp",
MAME_DIR .. "src/devices/cpu/z80/z80.h",
+ MAME_DIR .. "src/devices/cpu/z80/t6a84.cpp",
+ MAME_DIR .. "src/devices/cpu/z80/t6a84.h",
MAME_DIR .. "src/devices/cpu/z80/tmpz84c011.cpp",
MAME_DIR .. "src/devices/cpu/z80/tmpz84c011.h",
MAME_DIR .. "src/devices/cpu/z80/tmpz84c015.cpp",
diff --git a/src/devices/cpu/z80/t6a84.cpp b/src/devices/cpu/z80/t6a84.cpp
new file mode 100644
index 00000000000..07dedcf4193
--- /dev/null
+++ b/src/devices/cpu/z80/t6a84.cpp
@@ -0,0 +1,221 @@
+// license:BSD-3-Clause
+// copyright-holders:QUFB
+/***************************************************************************
+
+ Toshiba T6A84, TLCS-Z80 ASSP Family
+
+***************************************************************************/
+
+#include "emu.h"
+#include "t6a84.h"
+
+#define LOG_PAGE_R (1U << 1)
+#define LOG_PAGE_W (1U << 2)
+#define LOG_MEM (1U << 3)
+
+//#define VERBOSE (LOG_PAGE_R | LOG_PAGE_W | LOG_MEM)
+#include "logmacro.h"
+
+
+DEFINE_DEVICE_TYPE(T6A84, t6a84_device, "t6a84", "Toshiba T6A84")
+
+void t6a84_device::internal_io_map(address_map &map) const
+{
+ map.global_mask(0xff);
+ map(0xfc, 0xfc).rw(FUNC(t6a84_device::stack_page_r), FUNC(t6a84_device::stack_page_w));
+ map(0xfd, 0xfd).rw(FUNC(t6a84_device::data_page_r), FUNC(t6a84_device::data_page_w));
+ map(0xfe, 0xfe).rw(FUNC(t6a84_device::code_page_r), FUNC(t6a84_device::code_page_w));
+ map(0xff, 0xff).rw(FUNC(t6a84_device::vector_page_r), FUNC(t6a84_device::vector_page_w));
+}
+
+t6a84_device::t6a84_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ t6a84_device(mconfig, T6A84, tag, owner, clock, address_map_constructor(FUNC(t6a84_device::internal_io_map), this))
+{ }
+
+t6a84_device::t6a84_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, address_map_constructor io_map)
+ : z80_device(mconfig, type, tag, owner, clock)
+ , m_program_space_config("program", ENDIANNESS_LITTLE, 8, 20, 0, 16, 0)
+ , m_data_space_config("data", ENDIANNESS_LITTLE, 8, 20, 0, 16, 0)
+ , m_stack_space_config("stack", ENDIANNESS_LITTLE, 8, 20, 0, 16, 0)
+ , m_io_space_config("io", ENDIANNESS_LITTLE, 8, 16, 0, io_map)
+ , m_code_page(0)
+ , m_delay_code_page(0)
+ , m_is_delay_code_page_set(false)
+ , m_prev_code_page(0)
+ , m_data_page(8)
+ , m_stack_page(8)
+ , m_vector_page(0)
+{
+ // Interrupt vectors need to be fetched and executed from their corresponding page.
+ // For simplicity, we switch pages via callbacks, instead of using a dedicated address space.
+ irqfetch_cb().set([this](int state) {
+ LOGMASKED(LOG_PAGE_W, "IRQ FETCH %02x => %02x\n", m_code_page, m_vector_page);
+ m_prev_code_page = m_code_page;
+ m_code_page = m_vector_page;
+ });
+ reti_cb().set([this](int state) {
+ LOGMASKED(LOG_PAGE_W, "IRQ RET %02x => %02x\n", m_code_page, m_prev_code_page);
+ m_code_page = m_prev_code_page;
+ });
+ branch_cb().set([this](int state) {
+ LOGMASKED(LOG_PAGE_W, "BRANCH %02x => %02x\n", m_code_page, m_prev_code_page);
+ /*
+ When setting a code page, it only becomes effective after jumping to a far address in that page.
+ Any instructions fetched and executed before that jump still use the previous code page.
+ This can be seen in Sega Ferie Kitten, when test program at page 7 gets mapped, as we are still
+ executing on page 0, but we expect to start executing that program when jumping to RST0:
+
+ ROM_00::1ea9 3e 07 LD A,0x7
+ ROM_00::1eab d3 fe OUT (DAT_io_00fe),A
+ ROM_00::1ead c3 00 00 JP RST0
+ */
+ if (!machine().side_effects_disabled() && m_is_delay_code_page_set) {
+ m_code_page = m_delay_code_page;
+ m_is_delay_code_page_set = false;
+ }
+ });
+}
+
+void t6a84_device::device_start()
+{
+ z80_device::device_start();
+
+ space(AS_PROGRAM).cache(m_args);
+ space(AS_DATA).specific(m_data);
+ space(AS_STACK).specific(m_stack);
+
+ save_item(NAME(m_code_page));
+ save_item(NAME(m_delay_code_page));
+ save_item(NAME(m_is_delay_code_page_set));
+ save_item(NAME(m_prev_code_page));
+ save_item(NAME(m_data_page));
+ save_item(NAME(m_stack_page));
+ save_item(NAME(m_vector_page));
+}
+
+void t6a84_device::device_reset()
+{
+ m_code_page = 0;
+ m_delay_code_page = 0;
+ m_is_delay_code_page_set = false;
+ m_prev_code_page = 0;
+ m_data_page = 8;
+ m_stack_page = 8;
+ m_vector_page = 0;
+
+ z80_device::device_reset();
+}
+
+device_memory_interface::space_config_vector t6a84_device::memory_space_config() const
+{
+ auto r = z80_device::memory_space_config();
+ r.emplace_back(AS_DATA, &m_data_space_config);
+ r.emplace_back(AS_STACK, &m_stack_space_config);
+ for (auto it = r.begin(); it != r.end(); ++it) {
+ if ((*it).first == AS_IO) {
+ (*it).second = &m_io_space_config;
+ } else if ((*it).first == AS_PROGRAM) {
+ (*it).second = &m_program_space_config;
+ }
+ }
+
+ return r;
+}
+
+bool t6a84_device::memory_translate(int spacenum, int intention, offs_t &address, address_space *&target_space)
+{
+ if (spacenum == AS_PROGRAM) {
+ address = code_address(address);
+ } else if (spacenum == AS_DATA) {
+ address = data_address(address);
+ } else if (spacenum == AS_STACK) {
+ address = stack_address(address);
+ }
+
+ target_space = &space(spacenum);
+
+ return true;
+}
+
+uint32_t t6a84_device::code_address(uint16_t address)
+{
+ const uint32_t page_address = PAGE_SIZE * m_code_page + address;
+ LOGMASKED(LOG_MEM, "CODE @ %06x => %06x\n", address, page_address);
+
+ return page_address;
+}
+
+uint32_t t6a84_device::data_address(uint16_t address)
+{
+ const uint32_t page_address = PAGE_SIZE * m_data_page + address;
+ LOGMASKED(LOG_MEM, "DATA @ %06x => %06x\n", address, page_address);
+
+ return page_address;
+}
+
+uint32_t t6a84_device::stack_address(uint16_t address)
+{
+ const uint32_t page_address = PAGE_SIZE * m_stack_page + address;
+ LOGMASKED(LOG_MEM, "STACK @ %06x => %06x\n", address, page_address);
+
+ return page_address;
+}
+
+uint8_t t6a84_device::stack_read(uint16_t addr)
+{
+ return m_stack.read_byte(translate_memory_address(addr));
+}
+
+void t6a84_device::stack_write(uint16_t addr, uint8_t value)
+{
+ m_stack.write_byte(translate_memory_address((uint32_t) addr), value);
+}
+
+uint8_t t6a84_device::data_page_r()
+{
+ LOGMASKED(LOG_PAGE_R, "data_page_r: %02x @ %06x\n", m_data_page, pc());
+ return m_data_page;
+}
+
+uint8_t t6a84_device::stack_page_r()
+{
+ LOGMASKED(LOG_PAGE_R, "stack_page_r: %02x @ %06x\n", m_stack_page, pc());
+ return m_stack_page;
+}
+
+uint8_t t6a84_device::code_page_r()
+{
+ LOGMASKED(LOG_PAGE_R, "code_page_r: %02x @ %06x\n", m_code_page, pc());
+ return m_code_page;
+}
+
+uint8_t t6a84_device::vector_page_r()
+{
+ LOGMASKED(LOG_PAGE_R, "vector_page_r: %02x @ %06x\n", m_vector_page, pc());
+ return m_vector_page;
+}
+
+void t6a84_device::data_page_w(uint8_t page)
+{
+ LOGMASKED(LOG_PAGE_W, "data_page_w: %02x @ %06x\n", page, pc());
+ m_data_page = page;
+}
+
+void t6a84_device::stack_page_w(uint8_t page)
+{
+ LOGMASKED(LOG_PAGE_W, "stack_page_w: %02x @ %06x\n", page, pc());
+ m_stack_page = page;
+}
+
+void t6a84_device::code_page_w(uint8_t page)
+{
+ LOGMASKED(LOG_PAGE_W, "code_page_w: %02x @ %06x\n", page, pc());
+ m_delay_code_page = page;
+ m_is_delay_code_page_set = true;
+}
+
+void t6a84_device::vector_page_w(uint8_t page)
+{
+ LOGMASKED(LOG_PAGE_W, "vector_page_w: %02x @ %06x\n", page, pc());
+ m_vector_page = page;
+}
diff --git a/src/devices/cpu/z80/t6a84.h b/src/devices/cpu/z80/t6a84.h
new file mode 100644
index 00000000000..433cbb40e68
--- /dev/null
+++ b/src/devices/cpu/z80/t6a84.h
@@ -0,0 +1,91 @@
+// license:BSD-3-Clause
+// copyright-holders:QUFB
+/***************************************************************************
+
+ Toshiba T6A84, TLCS-Z80 ASSP Family
+
+ Unknown specs. Disassembled code suggests that this processor uses
+ separate code and data address spaces. Mapped pages on each space are
+ configured by writing to I/O ports. Values 0 to 7 map the corresponding
+ 0x10000 sized page from ROM offset 0 to 0x7ffff, while value 8 seems to
+ map full contents of RAM.
+
+ Pinout: https://patents.google.com/patent/CN2280961Y/en
+
+***************************************************************************/
+
+#ifndef MAME_CPU_Z80_T6A84_H
+#define MAME_CPU_Z80_T6A84_H
+
+#pragma once
+
+#include "z80.h"
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+class t6a84_device : public z80_device
+{
+public:
+ enum address_spaces : uint8_t
+ {
+ AS_STACK = AS_OPCODES + 1
+ };
+
+ t6a84_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ uint32_t code_address(uint16_t address);
+ uint32_t data_address(uint16_t address);
+ uint32_t stack_address(uint16_t address);
+
+protected:
+ static inline constexpr uint32_t PAGE_SIZE = 0x10000;
+
+ t6a84_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, address_map_constructor io_map);
+
+ // device-level overrides
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ // z80 overrides
+ virtual uint8_t stack_read(uint16_t addr) override;
+ virtual void stack_write(uint16_t addr, uint8_t value) override;
+
+ uint8_t code_page_r();
+ uint8_t data_page_r();
+ uint8_t stack_page_r();
+ uint8_t vector_page_r();
+ void code_page_w(uint8_t page);
+ void data_page_w(uint8_t page);
+ void stack_page_w(uint8_t page);
+ void vector_page_w(uint8_t page);
+
+ void internal_io_map(address_map &map) const;
+ virtual space_config_vector memory_space_config() const override;
+ virtual bool memory_translate(int spacenum, int intention, offs_t &address, address_space *&target_space) override;
+
+ const address_space_config m_program_space_config;
+ const address_space_config m_data_space_config;
+ const address_space_config m_stack_space_config;
+ const address_space_config m_io_space_config;
+
+ memory_access<16, 0, 0, ENDIANNESS_LITTLE>::specific m_stack;
+
+private:
+ uint8_t m_code_page;
+ uint8_t m_delay_code_page;
+ bool m_is_delay_code_page_set;
+ uint8_t m_prev_code_page;
+ uint8_t m_data_page;
+ uint8_t m_stack_page;
+ uint8_t m_vector_page;
+};
+
+
+// device type definition
+DECLARE_DEVICE_TYPE(T6A84, t6a84_device)
+
+
+#endif // MAME_CPU_Z80_T6A84_H
diff --git a/src/devices/cpu/z80/z80.cpp b/src/devices/cpu/z80/z80.cpp
index e44b38ba677..dec4cfbbd9a 100644
--- a/src/devices/cpu/z80/z80.cpp
+++ b/src/devices/cpu/z80/z80.cpp
@@ -836,6 +836,9 @@ z80_device::z80_device(const machine_config &mconfig, device_type type, const ch
m_nomreq_cb(*this),
m_halt_cb(*this),
m_busack_cb(*this),
+ m_branch_cb(*this),
+ m_irqfetch_cb(*this),
+ m_reti_cb(*this),
m_m1_cycles(4),
m_memrq_cycles(3),
m_iorq_cycles(4)
diff --git a/src/devices/cpu/z80/z80.h b/src/devices/cpu/z80/z80.h
index b97e9ff2d5f..0db8b93c759 100644
--- a/src/devices/cpu/z80/z80.h
+++ b/src/devices/cpu/z80/z80.h
@@ -43,6 +43,12 @@ public:
auto halt_cb() { return m_halt_cb.bind(); }
auto busack_cb() { return m_busack_cb.bind(); }
+ // Extra callbacks that do not map to any documented signals.
+ // Used by derived classes to customise instruction behaviour.
+ auto branch_cb() { return m_branch_cb.bind(); }
+ auto irqfetch_cb() { return m_irqfetch_cb.bind(); }
+ auto reti_cb() { return m_reti_cb.bind(); }
+
protected:
z80_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock);
@@ -115,6 +121,8 @@ protected:
virtual u8 data_read(u16 addr);
virtual void data_write(u16 addr, u8 value);
+ virtual u8 stack_read(u16 addr) { return data_read(addr); }
+ virtual void stack_write(u16 addr, u8 value) { return data_write(addr, value); }
virtual u8 opcode_read();
virtual u8 arg_read();
@@ -133,6 +141,12 @@ protected:
devcb_write_line m_halt_cb;
devcb_write_line m_busack_cb;
+ // Extra callbacks that do not map to any documented signals.
+ // Used by derived classes to customise instruction behaviour.
+ devcb_write_line m_branch_cb;
+ devcb_write_line m_irqfetch_cb;
+ devcb_write_line m_reti_cb;
+
PAIR16 m_prvpc;
PAIR16 m_pc;
PAIR16 m_sp;
diff --git a/src/devices/cpu/z80/z80.lst b/src/devices/cpu/z80/z80.lst
index fe40d25cded..bdb7544b478 100644
--- a/src/devices/cpu/z80/z80.lst
+++ b/src/devices/cpu/z80/z80.lst
@@ -40,9 +40,9 @@ macro wm16
macro wm16_sp
SP--;
- m_memrq_cycles !! data_write(SP, TDAT_H);
+ m_memrq_cycles !! stack_write(SP, TDAT_H);
SP--;
- m_memrq_cycles !! data_write(SP, TDAT_L);
+ m_memrq_cycles !! stack_write(SP, TDAT_L);
macro rop
m_m1_cycles-2 !! TDAT8 = opcode_read();
@@ -73,9 +73,9 @@ macro eay
m_ea = (u16)(IY + (s8)TDAT8); WZ = m_ea;
macro pop
- m_memrq_cycles !! TDAT_L = data_read(SP);
+ m_memrq_cycles !! TDAT_L = stack_read(SP);
SP++;
- m_memrq_cycles !! TDAT_H = data_read(SP);
+ m_memrq_cycles !! TDAT_H = stack_read(SP);
SP++;
macro push
@@ -85,11 +85,13 @@ macro push
macro jp
call arg16
PC = TDAT; WZ = PC;
+ m_branch_cb(true);
macro jp_cond
if (TDAT8) {
call arg16
PC = TDAT; WZ = PC;
+ m_branch_cb(true);
} else {
// implicit do PC += 2
call arg16
@@ -101,6 +103,7 @@ macro jr
TADR = PC-1;
5 * call nomreq_addr
PC += (s8)TDAT8; WZ = PC;
+ m_branch_cb(true);
macro r800:jr
call arg
@@ -163,7 +166,9 @@ macro z80n:retn
macro reti
call pop
- PC = TDAT; WZ = PC; m_iff1 = m_iff2; daisy_call_reti_device();
+ PC = TDAT; WZ = PC; m_iff1 = m_iff2;
+ m_reti_cb(true);
+ daisy_call_reti_device();
macro ld_r_a
call nomreq_ir 1
@@ -534,6 +539,7 @@ macro take_interrupt
m_r++;
{
// fetch the IRQ vector
+ m_irqfetch_cb(true);
device_z80daisy_interface *intf = daisy_get_irq_device();
m_tmp_irq_vector = (intf != nullptr) ? intf->z80daisy_irq_ack() : standard_irq_callback(0, m_pc.w);
LOGMASKED(LOG_INT, "single INT m_tmp_irq_vector $%02x\n", m_tmp_irq_vector);
diff --git a/src/mame/layout/ferie.lay b/src/mame/layout/ferie.lay
new file mode 100644
index 00000000000..5ad3779234a
--- /dev/null
+++ b/src/mame/layout/ferie.lay
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/mame/mame.lst b/src/mame/mame.lst
index 6a9cf5bb1e0..d2453599578 100644
--- a/src/mame/mame.lst
+++ b/src/mame/mame.lst
@@ -40104,6 +40104,11 @@ beena //
tvochken //
carbeena //
+@source:sega/sega_ferie.cpp
+ferieki //
+feriepu //
+feriewt //
+
@source:sega/sega_sawatte.cpp
sawatte //
diff --git a/src/mame/sega/sega_ferie.cpp b/src/mame/sega/sega_ferie.cpp
new file mode 100644
index 00000000000..10dc5a4784d
--- /dev/null
+++ b/src/mame/sega/sega_ferie.cpp
@@ -0,0 +1,699 @@
+// license:BSD-3-Clause
+// copyright-holders:QUFB
+/******************************************************************************
+
+ Sega Ferie (c) 1994 Sega
+
+ TODO:
+
+ - Fix input press state, e.g. World Travel test program tablet check not passed, unresponsive menu lists
+ - LCD effects, in particular when to clear/show dots, e.g. Kitten test program credits are jumbled
+ - Most tablet commands aren't parsed, e.g. pen callibration positions
+ - Disk beeper (used in Test Program)
+
+ Hardware
+ --------
+
+ Ferie Kitten (first model, just named "Ferie"):
+
+ - PCB Revision: SB1P01B, 171-6896, 837-11123
+ - IC1 (CPU): Toshiba T6A84
+ - IC2 (Mask ROM): Sega MPR-17062-T
+ - IC3 (Static RAM): Sony CXK58257AM-10L
+ - IC4 (Touchpad Driver): Sega 315-5832
+
+ Ferie Puppy:
+
+ - PCB Revision: MB-TPD338-1.0
+ - U1 (CPU): Chip-on-board
+ - U2 (ROM): Chip-on-board
+ - U3 (RAM): Chip-on-board
+ - U4 (Touchpad Driver): Sega 315-5832
+ - LCD Driver: Toshiba T6A04
+
+ Ferie World Travel:
+
+ - PCB Revision: SB1P02A
+ - U1 (CPU): Toshiba T6A84
+ - U2 (Mask ROM): Sega MPR-18080A
+ - U3 (Static RAM): Sony CXK58257AM-10L
+ - U4 (Touchpad Driver): Sega 315-5889
+
+ Mask ROM pinouts are identical to MPR-18201-S: https://www.smspower.org/Development/MaskROMs
+
+ Test Program
+ ------------
+
+ Ferie Kitten stores it in page 7 (ROM offset 0x70000). Currently unknown how to
+ normally load this program. It requires writing "TEST" at RAM address 0x70.
+ To force loading after reset, run "do pc = 0x1ea9" in the debugger.
+
+*******************************************************************************/
+
+#include "emu.h"
+
+#include "cpu/z80/t6a84.h"
+#include "machine/ram.h"
+#include "machine/timer.h"
+
+#include "crsshair.h"
+#include "emupal.h"
+#include "screen.h"
+
+#include "ferie.lh"
+
+#define LOG_MEM (1U << 1)
+#define LOG_LCD (1U << 2)
+#define LOG_TABLET (1U << 3)
+
+//#define VERBOSE (LOG_MEM | LOG_LCD | LOG_TABLET)
+#include "logmacro.h"
+
+namespace {
+
+class sega_ferie_state : public driver_device
+{
+public:
+ sega_ferie_state(const machine_config &mconfig, device_type type, const char *tag)
+ : driver_device(mconfig, type, tag)
+ , m_maincpu(*this, "maincpu")
+ , m_palette(*this, "palette")
+ , m_screen(*this, "screen")
+ , m_ram(*this, RAM_TAG)
+ , m_io_buttons(*this, "BUTTONS")
+ , m_io_pen_x(*this, "PEN_X")
+ , m_io_pen_y(*this, "PEN_Y")
+ , m_io_pen_y_rescale(*this, "PEN_Y_RESCALE")
+ { }
+
+ void sega_ferie(machine_config &config);
+
+ DECLARE_CROSSHAIR_MAPPER_MEMBER(pen_y_mapper);
+ ioport_value pen_y_rescale_r();
+ ioport_value pen_target_r();
+
+private:
+ static inline constexpr uint16_t LCD_W = 120;
+ static inline constexpr uint16_t LCD_H = 64;
+
+ static inline constexpr uint8_t RST1 = 0x08;
+ static inline constexpr uint8_t RST3 = 0x18;
+
+ static inline constexpr uint8_t HIGH_BATTERY = 0x40;
+
+ // Data is sent in sequences of writes to I/O port 0xe5,
+ // filling lines of dots in a configurable direction. An internal index
+ // is increment after each line, and gets reset when it reaches a given threshold.
+ enum lcd_fill_pattern : uint8_t
+ {
+ FILL_8_DOTS_Y, // Column-wise
+ FILL_8_DOTS_X // Row-wise
+ };
+
+ /*
+ Commands for parsing and configuring tablet variables. Both
+ data for requests and replies is shifted one bit at a time:
+
+ ROM_00::086a 4f LD C,data
+ ROM_00::086b 06 08 LD i,0x8
+ ROM_00::086d cd 91 11 CALL set_data_page_8
+ ROM_00::0870 36 00 LD (reply),0x0
+ LAB_ROM_00__0872
+ ROM_00::0872 00 NOP
+ ROM_00::0873 00 NOP
+ ROM_00::0874 00 NOP
+ ROM_00::0875 00 NOP
+ ROM_00::0876 79 LD data,C
+ ROM_00::0877 e6 01 AND 0x1
+ ROM_00::0879 d3 f6 OUT (TABLET_CTRL),data
+ ROM_00::087b 00 NOP
+ ; ...
+ ROM_00::0887 00 NOP
+ ROM_00::0888 79 LD data,C
+ ROM_00::0889 e6 01 AND 0x1
+ ROM_00::088b f6 02 OR 0x2
+ ROM_00::088d d3 f6 OUT (TABLET_CTRL),data
+ ROM_00::088f cb 39 SRL C
+ ROM_00::0891 db f7 IN data,(TABLET_DATA)
+ ROM_00::0893 07 RLCA
+ ROM_00::0894 cb 1e RR (reply)
+ ROM_00::0896 10 da DJNZ LAB_ROM_00__0872
+ */
+ enum tablet_command_request : uint8_t
+ {
+ READ = 0,
+ EFFECTIVE_PEN_X_Y = 0x0a,
+ RAW_PEN_X_Y = 0x15, // Only used for callibration?
+ };
+
+ // Replies are read via one or more sequences of READ commands.
+ enum tablet_command_reply_state : uint16_t
+ {
+ REPLY_0x06 = 0,
+ EFFECTIVE_PEN_STATUS = 0x0a00,
+ EFFECTIVE_PEN_X = 0x0a01,
+ EFFECTIVE_PEN_Y = 0x0a02,
+ RAW_PEN_STATUS = 0x1500,
+ RAW_PEN_X = 0x1501,
+ RAW_PEN_Y = 0x1502,
+ };
+
+ enum input_state : uint8_t
+ {
+ INPUT_RELEASE = 0,
+ INPUT_PRESS = 0x10,
+ INPUT_HOLD = 0x11,
+ };
+
+ enum pen_target : uint8_t
+ {
+ PEN_TARGET_LCD = 0,
+ PEN_TARGET_PANEL = 1,
+ };
+
+ virtual void machine_start() override;
+ virtual void machine_reset() override;
+
+ void ferie_io_map(address_map &map);
+
+ TIMER_DEVICE_CALLBACK_MEMBER(irq);
+
+ void update_rtc();
+
+ void update_crosshair(screen_device &screen);
+ void palette(palette_device &palette) const;
+ uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+ void lcd_ctrl_w(uint8_t data);
+ void lcd_data_w(uint8_t data);
+ uint8_t get_lcd_dots(uint16_t x, uint16_t y);
+
+ uint8_t code_r(offs_t offset);
+ uint8_t data_r(offs_t offset);
+ void data_w(offs_t offset, uint8_t data);
+ uint8_t stack_r(offs_t offset);
+ void stack_w(offs_t offset, uint8_t data);
+ uint8_t get_page_data(uint32_t address);
+
+ void tablet_ctrl_w(uint8_t data);
+ uint8_t tablet_data_r();
+
+ static constexpr float rescale(float x, float min_x, float max_x, float a, float b)
+ {
+ // Rescaling (min-max normalization) from [min_x..max_x] to [a..b].
+ return a + (((x - min_x) * (b - a)) / (max_x - min_x));
+ }
+
+ required_device m_maincpu;
+ optional_device m_palette;
+ required_device m_screen;
+ required_device m_ram;
+
+ required_ioport m_io_buttons;
+ required_ioport m_io_pen_x;
+ required_ioport m_io_pen_y;
+ required_ioport m_io_pen_y_rescale;
+
+ uint8_t *m_rom;
+
+ uint8_t m_irq_ctrl;
+ uint8_t m_irq_status;
+ uint8_t m_irq_vector;
+
+ uint8_t m_rtc;
+
+ std::unique_ptr m_lcd_dots;
+ uint8_t m_lcd_x;
+ uint8_t m_lcd_y;
+ uint8_t m_lcd_i;
+ uint8_t m_lcd_mode;
+
+ uint8_t m_tablet_cmd;
+ uint8_t m_tablet_cmd_prev;
+ uint8_t m_tablet_data_i;
+ uint16_t m_tablet_reply_state;
+ bool m_is_tablet_cmd_bit_set;
+
+ uint8_t m_button_state;
+ uint8_t m_pen_state;
+ uint8_t m_pen_target;
+};
+
+void sega_ferie_state::ferie_io_map(address_map &map)
+{
+ map.global_mask(0xff);
+ map(0xe4, 0xe4).lr8(NAME([]() { return 0; })).w(FUNC(sega_ferie_state::lcd_ctrl_w));
+ map(0xe5, 0xe5).w(FUNC(sega_ferie_state::lcd_data_w));
+ map(0xeb, 0xeb).lrw8(NAME([] () { return 0; /* TODO: On/Off button state */ }), NAME([this] (uint8_t data) { m_irq_ctrl = data; }));
+ map(0xf4, 0xf4).lr8(NAME([this] () { return m_rtc; }));
+ map(0xf5, 0xf5).lr8(NAME([this] () { return m_io_buttons->read() & 0x3f; }));
+ map(0xf6, 0xf6).w(FUNC(sega_ferie_state::tablet_ctrl_w));
+ map(0xf7, 0xf7).r(FUNC(sega_ferie_state::tablet_data_r));
+}
+
+void sega_ferie_state::machine_start()
+{
+ uint8_t *rom = memregion("mask_rom")->base();
+ m_rom = reinterpret_cast(rom);
+ uint8_t *ram = m_ram->pointer();
+
+ m_maincpu->space(AS_PROGRAM).install_rom(0x00000, 0x7ffff, rom);
+ m_maincpu->space(AS_PROGRAM).install_ram(0x80000, 0x87fff, ram);
+ m_maincpu->space(AS_DATA).install_ram(0x00000, 0x7ffff, rom);
+ m_maincpu->space(AS_DATA).install_ram(0x80000, 0x87fff, ram);
+ m_maincpu->space(t6a84_device::AS_STACK).install_ram(0x00000, 0x7ffff, rom);
+ m_maincpu->space(t6a84_device::AS_STACK).install_ram(0x80000, 0x87fff, ram);
+
+ m_maincpu->space(AS_PROGRAM).install_read_handler(0x0000, 0xffff,
+ read8sm_delegate(*this, FUNC(sega_ferie_state::code_r)));
+ m_maincpu->space(AS_DATA).install_readwrite_handler(0x0000, 0xffff,
+ read8sm_delegate(*this, FUNC(sega_ferie_state::data_r)),
+ write8sm_delegate(*this, FUNC(sega_ferie_state::data_w)));
+ m_maincpu->space(t6a84_device::AS_STACK).install_readwrite_handler(0x0000, 0xffff,
+ read8sm_delegate(*this, FUNC(sega_ferie_state::stack_r)),
+ write8sm_delegate(*this, FUNC(sega_ferie_state::stack_w)));
+
+ save_item(NAME(m_irq_ctrl));
+ save_item(NAME(m_irq_status));
+ save_item(NAME(m_irq_vector));
+
+ save_item(NAME(m_rtc));
+
+ m_lcd_dots = make_unique_clear(LCD_W * LCD_H);
+ save_pointer(NAME(m_lcd_dots), LCD_W * LCD_H);
+ save_item(NAME(m_lcd_mode));
+ save_item(NAME(m_lcd_x));
+ save_item(NAME(m_lcd_y));
+ save_item(NAME(m_lcd_i));
+
+ save_item(NAME(m_tablet_cmd));
+ save_item(NAME(m_tablet_cmd_prev));
+ save_item(NAME(m_tablet_data_i));
+ save_item(NAME(m_tablet_reply_state));
+ save_item(NAME(m_is_tablet_cmd_bit_set));
+
+ save_item(NAME(m_button_state));
+ save_item(NAME(m_pen_state));
+ save_item(NAME(m_pen_target));
+}
+
+void sega_ferie_state::machine_reset()
+{
+ m_irq_ctrl = 0;
+ m_irq_status = 0;
+ m_irq_vector = RST3;
+
+ m_rtc = 0;
+
+ memset(m_lcd_dots.get(), 0, LCD_W * LCD_H);
+ m_lcd_mode = FILL_8_DOTS_Y;
+ m_lcd_x = 0;
+ m_lcd_y = 0;
+ m_lcd_i = 0;
+
+ m_tablet_cmd = READ;
+ m_tablet_cmd_prev = READ;
+ m_tablet_data_i = 0;
+ m_tablet_reply_state = REPLY_0x06;
+ m_is_tablet_cmd_bit_set = false;
+
+ m_button_state = INPUT_RELEASE;
+ m_pen_state = INPUT_RELEASE;
+ m_pen_target = PEN_TARGET_LCD;
+}
+
+CROSSHAIR_MAPPER_MEMBER(sega_ferie_state::pen_y_mapper)
+{
+ // Parameter `linear_value` is ignored, since we will read the input port directly
+ // for adjustments, just need to return that value in the expected range [0.0f..1.0f].
+ return (float) pen_y_rescale_r() / 0xff;
+}
+
+ioport_value sega_ferie_state::pen_y_rescale_r()
+{
+ /*
+ There are two distinct areas that can be interacted with the pen:
+
+ - LCD screen visible area: 8 tiles = 64 pixels;
+ - Bottom panel: 3 tiles = 24 pixels;
+
+ In order to transparently map coordinates between each area, we split
+ the value across these areas, but rescaled to the input port's full range.
+ */
+ const int16_t io_pen_y_min = m_io_pen_y->field(0xff)->minval();
+ const int16_t io_pen_y_max = m_io_pen_y->field(0xff)->maxval();
+ const int16_t screen_y_max = io_pen_y_max * 0.75f;
+ int16_t adjusted_value = m_io_pen_y->read();
+ if (adjusted_value > screen_y_max) {
+ adjusted_value = rescale(adjusted_value, screen_y_max, io_pen_y_max, io_pen_y_min, io_pen_y_max);
+ m_pen_target = PEN_TARGET_PANEL;
+ } else {
+ adjusted_value = rescale(adjusted_value, io_pen_y_min, screen_y_max, io_pen_y_min, io_pen_y_max);
+ m_pen_target = PEN_TARGET_LCD;
+ }
+
+ return adjusted_value;
+}
+
+ioport_value sega_ferie_state::pen_target_r()
+{
+ return m_pen_target;
+}
+
+TIMER_DEVICE_CALLBACK_MEMBER(sega_ferie_state::irq)
+{
+ bool is_button_pressed = (m_io_buttons->read() & 0x7f) != 0;
+ if (is_button_pressed && m_button_state == INPUT_RELEASE) {
+ m_button_state = INPUT_PRESS;
+ }
+ if (m_button_state == INPUT_PRESS) {
+ m_irq_vector = (m_irq_ctrl == 0xf7) ? RST1 : RST3;
+ m_button_state = INPUT_HOLD;
+ }
+ if (!is_button_pressed && m_button_state == INPUT_HOLD) {
+ m_irq_vector = RST3;
+ m_button_state = INPUT_RELEASE;
+ }
+
+ m_maincpu->set_input_line_vector(0, 0xc7 | m_irq_vector);
+ m_maincpu->set_input_line(INPUT_LINE_IRQ0, m_irq_status ? ASSERT_LINE : CLEAR_LINE);
+ m_irq_status ^= 1;
+}
+
+uint8_t sega_ferie_state::code_r(offs_t offset)
+{
+ return get_page_data(m_maincpu->code_address(offset));
+}
+
+uint8_t sega_ferie_state::data_r(offs_t offset)
+{
+ return get_page_data(m_maincpu->data_address(offset));
+}
+
+void sega_ferie_state::data_w(offs_t offset, uint8_t data)
+{
+ LOGMASKED(LOG_MEM, "DATA w [%06x] = %02x\n", offset, data);
+ if (offset > 0x7fff) {
+ osd_printf_warning("DATA w OOB @ %06x\n", m_maincpu->pc());
+ } else {
+ m_ram->pointer()[offset] = data;
+ }
+}
+
+uint8_t sega_ferie_state::stack_r(offs_t offset)
+{
+ return get_page_data(m_maincpu->stack_address(offset));
+}
+
+void sega_ferie_state::stack_w(offs_t offset, uint8_t data)
+{
+ LOGMASKED(LOG_MEM, "STACK w [%06x] = %02x\n", offset, data);
+ if (offset > 0x7fff) {
+ osd_printf_warning("STACK w OOB @ %06x\n", m_maincpu->pc());
+ } else {
+ m_ram->pointer()[offset] = data;
+ }
+}
+
+uint8_t sega_ferie_state::get_page_data(uint32_t address)
+{
+ uint8_t data = 0;
+ if (((int32_t) address) - 0x80000 > 0x7fff) {
+ osd_printf_warning("get_page_data OOB @ %06x\n", m_maincpu->pc());
+ } else {
+ data = (address < 0x80000) ? m_rom[address] : m_ram->pointer()[address - 0x80000];
+ }
+ LOGMASKED(LOG_MEM, "get_page_data [%06x] = %02x\n", address, data);
+
+ return data;
+}
+
+uint8_t sega_ferie_state::tablet_data_r()
+{
+ if (machine().side_effects_disabled() || !m_is_tablet_cmd_bit_set) {
+ return HIGH_BATTERY;
+ }
+
+ if (m_tablet_data_i == 0) {
+ if (m_tablet_reply_state == REPLY_0x06) {
+ switch (m_tablet_cmd_prev) {
+ case EFFECTIVE_PEN_X_Y:
+ m_tablet_reply_state = EFFECTIVE_PEN_STATUS;
+ LOGMASKED(LOG_TABLET, "tablet_data_r EFFECTIVE_PEN_X_Y -> EFFECTIVE_PEN_STATUS\n");
+ break;
+ case RAW_PEN_X_Y:
+ m_tablet_reply_state = RAW_PEN_STATUS;
+ LOGMASKED(LOG_TABLET, "tablet_data_r RAW_PEN_X_Y -> RAW_PEN_STATUS\n");
+ break;
+ }
+ }
+
+ if (BIT(m_io_buttons->read(), 6)) {
+ if (m_pen_state == INPUT_RELEASE) {
+ m_pen_state = INPUT_PRESS;
+ LOGMASKED(LOG_TABLET, "tablet_data_r INPUT_RELEASE -> INPUT_PRESS\n");
+ } else if (m_pen_state == INPUT_PRESS) {
+ m_pen_state = INPUT_HOLD;
+ LOGMASKED(LOG_TABLET, "tablet_data_r INPUT_PRESS -> INPUT_HOLD\n");
+ }
+ } else if (m_pen_state != INPUT_RELEASE) {
+ m_pen_state = INPUT_RELEASE;
+ LOGMASKED(LOG_TABLET, "tablet_data_r INPUT_RELEASE\n");
+ }
+ }
+
+ int16_t io_pen_x_min = m_io_pen_x->field(0xff)->minval();
+ int16_t io_pen_x_max = m_io_pen_x->field(0xff)->maxval();
+ int16_t io_pen_y_min = m_io_pen_y->field(0xff)->minval();
+ int16_t io_pen_y_max = m_io_pen_y->field(0xff)->maxval();
+ int16_t io_pen_x_pos = m_io_pen_x->read();
+ int16_t io_pen_y_pos = pen_y_rescale_r();
+ uint8_t reply;
+ switch (m_tablet_reply_state) {
+ case EFFECTIVE_PEN_STATUS:
+ // Fallthrough
+ case RAW_PEN_STATUS:
+ reply = m_pen_state;
+ break;
+ case EFFECTIVE_PEN_X:
+ reply = rescale(io_pen_x_pos, io_pen_x_min, io_pen_x_max, 0x00, 0x80);
+ break;
+ case EFFECTIVE_PEN_Y:
+ reply = (m_pen_target == PEN_TARGET_LCD)
+ ? rescale(io_pen_y_pos, io_pen_y_min, io_pen_y_max, 0x00, 0x40)
+ : rescale(io_pen_y_pos, io_pen_y_min, io_pen_y_max, 0x40, 0x40 + 3 * 8);
+ break;
+ case RAW_PEN_X:
+ reply = io_pen_x_pos;
+ break;
+ case RAW_PEN_Y:
+ reply = io_pen_y_pos;
+ break;
+ default:
+ reply = 0x06;
+ }
+
+ uint8_t data = BIT(reply, m_tablet_data_i) << 7;
+
+ LOGMASKED(LOG_TABLET, "tablet_data_r i = %02x\n", m_tablet_data_i);
+ m_tablet_data_i++;
+ if (m_tablet_data_i == 8) {
+ LOGMASKED(LOG_TABLET, "tablet_data_r cmd = %02x, rpy = %02x @ %06x\n", m_tablet_cmd, reply, m_maincpu->pc());
+ m_tablet_data_i = 0;
+ if (m_tablet_cmd != READ) {
+ m_tablet_cmd_prev = m_tablet_cmd;
+ m_tablet_reply_state = REPLY_0x06;
+ }
+ m_tablet_cmd = READ;
+ switch (m_tablet_reply_state) {
+ case EFFECTIVE_PEN_STATUS:
+ if (m_pen_state == INPUT_HOLD) {
+ m_tablet_reply_state = EFFECTIVE_PEN_X;
+ LOGMASKED(LOG_TABLET, "tablet_data_r EFFECTIVE_PEN_STATUS -> EFFECTIVE_PEN_X\n");
+ }
+ break;
+ case EFFECTIVE_PEN_X:
+ m_tablet_reply_state = EFFECTIVE_PEN_Y;
+ LOGMASKED(LOG_TABLET, "tablet_data_r EFFECTIVE_PEN_X -> EFFECTIVE_PEN_Y\n");
+ break;
+ case RAW_PEN_STATUS:
+ if (m_pen_state == INPUT_HOLD) {
+ m_tablet_reply_state = RAW_PEN_X;
+ LOGMASKED(LOG_TABLET, "tablet_data_r RAW_PEN_STATUS -> RAW_PEN_X\n");
+ }
+ break;
+ case RAW_PEN_X:
+ m_tablet_reply_state = RAW_PEN_Y;
+ LOGMASKED(LOG_TABLET, "tablet_data_r RAW_PEN_X -> RAW_PEN_Y\n");
+ break;
+ default:
+ m_tablet_reply_state = REPLY_0x06;
+ }
+ }
+
+ m_is_tablet_cmd_bit_set = false;
+
+ return data | HIGH_BATTERY;
+}
+
+void sega_ferie_state::tablet_ctrl_w(uint8_t data)
+{
+ if ((data & 2) != 0) {
+ LOGMASKED(LOG_TABLET, "tablet_ctrl_w i = %02x\n", m_tablet_data_i);
+ m_tablet_cmd |= ((data & 1) << m_tablet_data_i);
+ m_is_tablet_cmd_bit_set = true;
+ }
+}
+
+void sega_ferie_state::update_rtc()
+{
+ system_time systime;
+ machine().current_datetime(systime);
+ m_rtc = systime.time;
+}
+
+void sega_ferie_state::update_crosshair(screen_device &screen)
+{
+ // Either screen crosshair or layout view's cursor should be visible at a time.
+ machine().crosshair().get_crosshair(0).set_screen(m_pen_target ? CROSSHAIR_SCREEN_NONE : &screen);
+}
+
+void sega_ferie_state::palette(palette_device &palette) const
+{
+ palette.set_pen_color(0, rgb_t(188, 190, 144)); // Background
+ palette.set_pen_color(1, rgb_t(176, 180, 134)); // LCD pixel off
+ palette.set_pen_color(2, rgb_t( 38, 60, 80)); // LCD pixel on
+}
+
+uint32_t sega_ferie_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ update_rtc();
+ update_crosshair(screen);
+
+ // Render LCD
+ for (size_t y = cliprect.min_y; y <= cliprect.max_y; y++) {
+ for (size_t x = cliprect.min_x; x <= cliprect.max_x / 8; x++) {
+ uint8_t data = get_lcd_dots(x, y);
+ for (size_t bit_i = 0; bit_i < 8; bit_i++) {
+ // TODO: Adjust based on configured contrast
+ bitmap.pix(y, (8 * x) + bit_i) = BIT(data, 7 - bit_i) + 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+uint8_t sega_ferie_state::get_lcd_dots(uint16_t x, uint16_t y)
+{
+ return m_lcd_dots[y * LCD_W + x];
+}
+
+void sega_ferie_state::lcd_ctrl_w(uint8_t data)
+{
+ LOGMASKED(LOG_LCD, "lcd_ctrl_w @ %06x = %02x\n", m_maincpu->pc(), data);
+ if ((data & 0x20) != 0) {
+ m_lcd_x = data & 0x1f;
+ } else if ((data & 0x80) != 0) {
+ m_lcd_y = data & 0x7f;
+ m_lcd_i = 0;
+ } else if (data == 0x7) {
+ LOGMASKED(LOG_LCD, "lcd_ctrl_w FILL_8_DOTS_X\n");
+ m_lcd_mode = FILL_8_DOTS_X;
+ } else if (data == 0x3) {
+ LOGMASKED(LOG_LCD, "lcd_ctrl_w FILL_8_DOTS_Y\n");
+ m_lcd_mode = FILL_8_DOTS_Y;
+ }
+}
+
+void sega_ferie_state::lcd_data_w(uint8_t data)
+{
+ const uint16_t offset = (m_lcd_mode == FILL_8_DOTS_Y)
+ ? (m_lcd_y + m_lcd_i) * LCD_W + m_lcd_x
+ : m_lcd_y * LCD_W + (m_lcd_x + m_lcd_i);
+
+ LOGMASKED(LOG_LCD, "lcd_data_w[y=%02x,x=%02x,i=%02x => %08x] = %02x\n", m_lcd_y, m_lcd_x, m_lcd_i, offset, data);
+ if (offset > LCD_W * LCD_H - 1) {
+ osd_printf_warning("lcd_data_w OOB @ %06x\n", m_maincpu->pc());
+ } else {
+ m_lcd_dots[offset] = data;
+ }
+
+ m_lcd_i += 1;
+ if (m_lcd_mode == FILL_8_DOTS_Y) {
+ if (m_lcd_i == 0x8) {
+ m_lcd_i = 0;
+ }
+ } else if (m_lcd_mode == FILL_8_DOTS_X) {
+ if (m_lcd_i == 0xf) {
+ m_lcd_i = 0;
+ m_lcd_y += 1;
+ }
+ }
+}
+
+static INPUT_PORTS_START( sega_ferie )
+ PORT_START("BUTTONS")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_RIGHT ) PORT_8WAY PORT_PLAYER(1)
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_LEFT ) PORT_8WAY PORT_PLAYER(1)
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_DOWN ) PORT_8WAY PORT_PLAYER(1)
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICKLEFT_UP ) PORT_8WAY PORT_PLAYER(1)
+ PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("Return")
+ PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("On/Off")
+ PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("Pen Down")
+ PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(FUNC(sega_ferie_state::pen_target_r))
+
+ PORT_START("PEN_X")
+ PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_X ) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_SENSITIVITY(30) PORT_KEYDELTA(10) PORT_MINMAX(0, 255) PORT_PLAYER(1) PORT_NAME("Pen X")
+
+ PORT_START("PEN_Y")
+ PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_Y ) PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_SENSITIVITY(30) PORT_KEYDELTA(10) PORT_MINMAX(0, 255) PORT_PLAYER(1) PORT_NAME("Pen Y") PORT_CROSSHAIR_MAPPER_MEMBER(FUNC(sega_ferie_state::pen_y_mapper))
+
+ PORT_START("PEN_Y_RESCALE")
+ PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(sega_ferie_state::pen_y_rescale_r))
+INPUT_PORTS_END
+
+void sega_ferie_state::sega_ferie(machine_config &config)
+{
+ T6A84(config, m_maincpu, XTAL(4'000'000)); // High frequency external crystal, connected to HXIN/HXOUT pins
+ m_maincpu->set_addrmap(AS_IO, &sega_ferie_state::ferie_io_map);
+
+ RAM(config, RAM_TAG).set_default_size("32K").set_default_value(0x00);
+
+ // FIXME: Guessed timings
+ TIMER(config, "irq").configure_periodic(FUNC(sega_ferie_state::irq), attotime::from_hz(200));
+
+ SCREEN(config, m_screen, SCREEN_TYPE_LCD);
+ m_screen->set_refresh_hz(60);
+ m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(0));
+ m_screen->set_size(LCD_W, LCD_H);
+ m_screen->set_visarea(0, LCD_W - 1, 0, LCD_H - 1);
+ m_screen->set_screen_update(FUNC(sega_ferie_state::screen_update));
+ m_screen->set_palette(m_palette);
+
+ PALETTE(config, m_palette, FUNC(sega_ferie_state::palette), 3);
+
+ config.set_default_layout(layout_ferie);
+}
+
+ROM_START( ferieki )
+ ROM_REGION16_LE( 0x80000, "mask_rom", 0 )
+ ROM_LOAD( "mpr-17062-t.ic2", 0x00000, 0x80000, CRC(c693c9f6) SHA1(491e23902f5ef0dea9156f244f5aa2a21ab68505) )
+ROM_END
+
+ROM_START( feriepu )
+ ROM_REGION16_LE( 0x80000, "mask_rom", 0 )
+ ROM_LOAD( "rom.u2", 0x00000, 0x80000, CRC(895bfe47) SHA1(28ca98471a4a4b5084884b39eccbc74bff1cf4c6) )
+ROM_END
+
+ROM_START( feriewt )
+ ROM_REGION16_LE( 0x80000, "mask_rom", 0 )
+ ROM_LOAD( "mpr-18080a.u2", 0x00000, 0x80000, CRC(117aea09) SHA1(73ff933ba2bacd485cbc0580b023341fffac692f) )
+ROM_END
+
+} // anonymous namespace
+
+
+// year, name, parent, compat, machine, input, class, init, company, fullname, flags
+CONS( 1994, ferieki, 0, 0, sega_ferie, sega_ferie, sega_ferie_state, empty_init, "Sega", "Ferie Kitten", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
+CONS( 1995, feriepu, 0, 0, sega_ferie, sega_ferie, sega_ferie_state, empty_init, "Sega", "Ferie Puppy", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
+CONS( 1995, feriewt, 0, 0, sega_ferie, sega_ferie, sega_ferie_state, empty_init, "Sega", "Ferie World Travel", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )
--
cgit v1.2.3