// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
#include "emu.h"
#include "cpu/i86/i186.h"
#include "cpu/mcs48/mcs48.h"
#include "imagedev/floppy.h"
#include "machine/upd765.h"
#include "formats/pc_dsk.h"
#include "sound/dac.h"
#include "sound/volt_reg.h"
#include "machine/ins8250.h"
#include "bus/rs232/rs232.h"
#include "screen.h"
#include "speaker.h"
#include "mindset.lh"
// Missing:
// - Correct video timings
// * screen_device does not handle interlaced screens yet
// - Correct timing of the specific-video-position interrupt
// * see previous (so that partial screen update is correct) plus there's no "interrupt at a (x, y) position" interface
// - Interrupt from modules
// * no use example, serial maybe? And may be going through the system mcu
// - HD support module
// * can't find the software that uses it. Hand-drawn schematic at http://bitsavers.org/pdf/mindset/video_input/video_input_module_schematic.jpg (it's the same)
// - Modem modules
// * no information on them, probably no software to use them either
// - Cartridges
// * no dump of the rom ones, no users for the nvram ones. gwbasic could use the nvram ones maybe?
// - Graphics CoProcessor timings
// * need to count the accesses and estimate the mean access duration
// - GCP right-to-left blitting
// * need to find a user, otherwise no way to know if the implementation is correct
// - GCP collision detection
// * need to find a user, or at least a way to distinguish between the mask and the comparison value
// - GCP interrupt
// * need to find a user, especially since it interacts with the system mcu
// - Genlock
// * no osd support for video input
// - Capture card
// * no osd support for video input. Could try with the picture image device maybe?
// - Digitizing tablet
// * no osd support, and a relatively rare device in the real world which is also hard to simulate on something else
// - Power control
// * the system mcu controls the power to the system (some bit of p2 is seems), rocker switch on the keyboard resets the keyboard mcu for "on" and pretends sending a byte for "off". Can be annoying UI-wise, and pretty much requires autosave.
class mindset_module_interface: public device_t {
public:
virtual void map(address_map &map) = 0;
virtual void idmap(address_map &map) = 0;
auto irq_cb() { return m_irq_cb.bind(); }
bool irq_r() const;
protected:
bool m_irq_state;
void irq_w(int state);
mindset_module_interface(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
virtual void device_start() override;
virtual void device_reset() override;
private:
devcb_write_line m_irq_cb;
};
void mindset_module_interface::device_start()
{
m_irq_cb.resolve_safe();
save_item(NAME(m_irq_state));
}
void mindset_module_interface::device_reset()
{
m_irq_state = false;
m_irq_cb(m_irq_state);
}
mindset_module_interface::mindset_module_interface(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
device_t(mconfig, type, tag, owner, clock),
m_irq_cb(*this)
{
}
void mindset_module_interface::irq_w(int state)
{
m_irq_state = state;
m_irq_cb(m_irq_state);
}
class mindset_module: public device_t,
public device_slot_interface
{
public:
template <typename T>
mindset_module(const machine_config &mconfig, const char *tag, device_t *owner, T &&opts, const char *dflt, bool fixed = false)
: mindset_module(mconfig, tag, owner, 0)
{
option_reset();
opts(*this);
set_default_option(dflt);
set_fixed(fixed);
}
mindset_module(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
virtual ~mindset_module() = default;
void map(address_space &space, offs_t base, bool id);
protected:
virtual void device_validity_check(validity_checker &valid) const override;
virtual void device_start() override;
};
DEFINE_DEVICE_TYPE(MINDSET_MODULE, mindset_module, "mindset_module", "MINDSET module")
mindset_module::mindset_module(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
device_t(mconfig, MINDSET_MODULE, tag, owner, clock),
device_slot_interface(mconfig, *this)
{
}
void mindset_module::device_validity_check(validity_checker &valid) const
{
device_t *const carddev = get_card_device();
if (carddev && !dynamic_cast<mindset_module_interface *>(carddev))
osd_printf_error("Card device %s (%s) does not implement mindset_module_interface\n", carddev->tag(), carddev->name());
}
void mindset_module::device_start()
{
}
void mindset_module::map(address_space &space, offs_t base, bool id)
{
mindset_module_interface *module = dynamic_cast<mindset_module_interface *>(get_card_device());
if(module)
space.install_device(base, base+0x3f, *module, id ? &mindset_module_interface::idmap : &mindset_module_interface::map);
}
class mindset_sound_module: public mindset_module_interface {
public:
mindset_sound_module(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
virtual ~mindset_sound_module() = default;
virtual void map(address_map &map) override;
virtual void idmap(address_map &map) override;
protected:
virtual const tiny_rom_entry *device_rom_region() const override;
virtual void device_add_mconfig(machine_config &config) override;
virtual void device_start() override;
virtual void device_reset() override;
private:
u8 m_p1, m_p2;
required_device<i8042_device> m_soundcpu;
required_device<dac_byte_interface> m_dac;
void p1_w(u8 data);
void p2_w(u8 data);
void update_dac();
};
DEFINE_DEVICE_TYPE(MINDSET_SOUND_MODULE, mindset_sound_module, "mindset_sound_module", "MINDSET stereo sound module")
mindset_sound_module::mindset_sound_module(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
mindset_module_interface(mconfig, MINDSET_SOUND_MODULE, tag, owner, clock),
m_soundcpu(*this, "soundcpu"),
m_dac(*this, "dac")
{
}
void mindset_sound_module::device_start()
{
mindset_module_interface::device_start();
save_item(NAME(m_p1));
save_item(NAME(m_p2));
}
void mindset_sound_module::device_reset()
{
mindset_module_interface::device_reset();
m_p1 = 0x80;
m_p2 = 0;
m_dac->write(0x80);
}
void mindset_sound_module::update_dac()
{
// The p1 dac has the waveform (idle at 0x80), while the p2 one is used for the global volume (mute at 0x00, max at 0xff)
m_dac->write((s8(m_p1-0x80)*m_p2/255 + 0x80) & 0xff);
}
void mindset_sound_module::p1_w(u8 data)
{
m_p1 = data;
update_dac();
}
void mindset_sound_module::p2_w(u8 data)
{
m_p2 = data;
update_dac();
}
void mindset_sound_module::map(address_map &map)
{
map(0x00, 0x03).rw(m_soundcpu, FUNC(i8042_device::upi41_master_r), FUNC(i8042_device::upi41_master_w)).umask16(0x00ff).mirror(0x3c);
}
void mindset_sound_module::idmap(address_map &map)
{
map(0x00, 0x3f).lr8(NAME([]() -> u8 { return 0x13; })).umask16(0x00ff);
}
ROM_START(mindset_sound_module)
ROM_REGION(0x0800, "soundcpu", 0)
ROM_LOAD("253006-001.u16", 0, 0x800, CRC(7bea5edd) SHA1(30cdc0dedaa5246f4952df452a99ca22e3cd0636))
ROM_END
const tiny_rom_entry *mindset_sound_module::device_rom_region() const
{
return ROM_NAME(mindset_sound_module);
}
void mindset_sound_module::device_add_mconfig(machine_config &config)
{
I8042(config, m_soundcpu, 12_MHz_XTAL/2);
m_soundcpu->p1_out_cb().set(FUNC(mindset_sound_module::p1_w));
m_soundcpu->p2_out_cb().set(FUNC(mindset_sound_module::p2_w));
SPEAKER(config, "rspeaker").front_right();
DAC_8BIT_R2R(config, m_dac, 0).add_route(ALL_OUTPUTS, "rspeaker", 0.5);
voltage_regulator_device &vref(VOLTAGE_REGULATOR(config, "vref"));
vref.add_route(0, m_dac, 1.0, DAC_VREF_POS_INPUT);
vref.add_route(0, m_dac, -1.0, DAC_VREF_NEG_INPUT);
}
class mindset_rs232_module: public mindset_module_interface {
public:
mindset_rs232_module(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
virtual ~mindset_rs232_module() = default;
virtual void map(address_map &map) override;
virtual void idmap(address_map &map) override;
protected:
virtual void device_add_mconfig(machine_config &config) override;
private:
required_device<ins8250_device> m_ins8250;
required_device<rs232_port_device> m_rs232;
};
DEFINE_DEVICE_TYPE(MINDSET_RS232_MODULE, mindset_rs232_module, "mindset_rs232_module", "MINDSET RS232 module")
mindset_rs232_module::mindset_rs232_module(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
mindset_module_interface(mconfig, MINDSET_RS232_MODULE, tag, owner, clock),
m_ins8250(*this, "ins8250"),
m_rs232(*this, "rs232")
{
}
void mindset_rs232_module::map(address_map &map)
{
map(0x00, 0x0f).rw(m_ins8250, FUNC(ins8250_device::ins8250_r), FUNC(ins8250_device::ins8250_w)).umask16(0x00ff).mirror(0x30);
}
void mindset_rs232_module::idmap(address_map &map)
{
map(0x00, 0x3f).lr8(NAME([this]() -> u8 { return 0x73 | (m_irq_state ? 0x80 : 0x00); })).umask16(0x00ff);
}
void mindset_rs232_module::device_add_mconfig(machine_config &config)
{
INS8250(config, m_ins8250, 12_MHz_XTAL/2/4); // Weird since there's no divider in the module
m_ins8250->out_tx_callback().set(m_rs232, FUNC(rs232_port_device::write_txd));
m_ins8250->out_int_callback().set(FUNC(mindset_rs232_module::irq_w));
RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
m_rs232->rxd_handler().set(m_ins8250, FUNC(ins8250_device::rx_w));
m_rs232->dsr_handler().set(m_ins8250, FUNC(ins8250_device::dsr_w));
}
class mindset_state: public driver_device
{
public:
mindset_state(const machine_config &mconfig, device_type type, const char *tag);
virtual ~mindset_state() = default;
void mindset(machine_config &config);
protected:
required_device<i80186_cpu_device> m_maincpu;
required_device<i8042_device> m_syscpu, m_soundcpu;
required_device<i8749_device> m_kbdcpu;
required_device<screen_device> m_screen;
required_device<i8272a_device> m_fdc;
required_device<floppy_connector> m_fdco[2];
required_shared_ptr<u16> m_vram;
required_ioport_array<11> m_kbd_row;
required_ioport_array<2> m_mouse_axis;
required_ioport m_mouse_btn, m_joystick;
output_finder<2> m_floppy_leds;
output_finder<> m_red_led;
output_finder<> m_yellow_led;
output_finder<> m_green_led;
required_device<dac_byte_interface> m_dac;
required_device_array<mindset_module, 6> m_modules;
memory_access_cache<1, 0, ENDIANNESS_LITTLE> *m_gcps;
floppy_image_device *m_floppy[2];
u32 m_palette[16];
bool m_genlock[16];
u16 m_dispctrl, m_screenpos, m_intpos, m_intaddr, m_fdc_dma_count;
u8 m_kbd_p1, m_kbd_p2, m_borderidx, m_snd_p1, m_snd_p2, m_sys_p2;
u8 m_mouse_last_read[2], m_mouse_counter[2];
bool m_fdc_intext, m_fdc_int, m_fdc_drq, m_trap_int, m_trap_drq;
u16 m_trap_data[8];
u32 m_trap_pos, m_trap_len;
static u16 gcp_blend_0(u16, u16);
static u16 gcp_blend_1(u16, u16);
static u16 gcp_blend_2(u16, u16);
static u16 gcp_blend_3(u16, u16);
static u16 gcp_blend_4(u16, u16);
static u16 gcp_blend_5(u16, u16);
static u16 gcp_blend_6(u16, u16);
static u16 gcp_blend_7(u16, u16);
static u16 (*const gcp_blend[8])(u16, u16);
static inline u16 msk(int bit) { return (1U << bit) - 1; }
static inline u16 sw(u16 data) { return (data >> 8) | (data << 8); }
void maincpu_mem(address_map &map);
void maincpu_io(address_map &map);
void display_mode();
void blit(u16 packet_seg, u16 packet_adr);
void gcp_w(u16);
u16 dispctrl_r();
void dispctrl_w(u16 data);
u16 dispreg_r();
void dispreg_w(u16 data);
int sys_t0_r();
int sys_t1_r();
u8 sys_p1_r();
u8 sys_p2_r();
void sys_p1_w(u8 data);
void sys_p2_w(u8 data);
void kbd_p1_w(u8 data);
void kbd_p2_w(u8 data);
int kbd_t1_r();
u8 kbd_d_r();
void snd_p1_w(u8 data);
void snd_p2_w(u8 data);
void fdc_ctrl_w(u8 data);
void fdc_int_w(int state);
u16 fdc_clear_interrupt();
void fdc_dma_count_w(u16 data);
u8 fdc_dma_r();
void fdc_dma_w(u8 data);
u16 trap_dma_r(offs_t, u16);
u16 trap_r(offs_t offset);
void trap_w(offs_t offset, u16 data);
u16 trap_clear_interrupt();
template<int floppy> void floppy_led_cb(floppy_image_device *, int state);
u16 keyscan();
void update_dac();
void map_modules();
u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
virtual void machine_start() override;
virtual void machine_reset() override;
DECLARE_FLOPPY_FORMATS(floppy_formats);
};
FLOPPY_FORMATS_MEMBER(mindset_state::floppy_formats)
FLOPPY_PC_FORMAT
FLOPPY_FORMATS_END
mindset_state::mindset_state(const machine_config &mconfig, device_type type, const char *tag) :
driver_device(mconfig, type, tag),
m_maincpu(*this, "maincpu"),
m_syscpu(*this, "syscpu"),
m_soundcpu(*this, "soundcpu"),
m_kbdcpu(*this, "kbdcpu"),
m_screen(*this, "screen"),
m_fdc(*this, "fdc"),
m_fdco{{*this, "fdc:0"}, {*this, "fdc:1"}},
m_vram(*this, "vram"),
m_kbd_row(*this, "K%02u", 0U),
m_mouse_axis(*this, "MOUSEAXIS%u", 0U),
m_mouse_btn(*this, "MOUSEBTN"),
m_joystick(*this, "JOYSTICK"),
m_floppy_leds(*this, "drive%u_led", 0U),
m_red_led(*this, "red_led"),
m_yellow_led(*this, "yellow_led"),
m_green_led(*this, "green_led"),
m_dac(*this, "dac"),
m_modules(*this, "m%d", 0U)
{
}
template<int floppy> void mindset_state::floppy_led_cb(floppy_image_device *, int state)
{
m_floppy_leds[floppy] = state;
}
void mindset_state::machine_start()
{
m_floppy_leds.resolve();
m_red_led.resolve();
m_yellow_led.resolve();
m_green_led.resolve();
m_gcps = m_maincpu->space(AS_PROGRAM).cache<1, 0, ENDIANNESS_LITTLE>();
for(int i=0; i<2; i++)
m_floppy[i] = m_fdco[i]->get_device();
if(m_floppy[0])
m_floppy[0]->setup_led_cb(floppy_image_device::led_cb(&mindset_state::floppy_led_cb<0>, this));
if(m_floppy[1])
m_floppy[1]->setup_led_cb(floppy_image_device::led_cb(&mindset_state::floppy_led_cb<1>, this));
save_item(NAME(m_fdc_intext));
save_item(NAME(m_fdc_int));
save_item(NAME(m_fdc_drq));
save_item(NAME(m_trap_int));
save_item(NAME(m_trap_drq));
save_item(NAME(m_trap_pos));
save_item(NAME(m_trap_len));
save_item(NAME(m_trap_data));
save_item(NAME(m_palette));
save_item(NAME(m_genlock));
save_item(NAME(m_dispctrl));
save_item(NAME(m_screenpos));
save_item(NAME(m_intpos));
save_item(NAME(m_intaddr));
save_item(NAME(m_fdc_dma_count));
save_item(NAME(m_kbd_p1));
save_item(NAME(m_kbd_p2));
save_item(NAME(m_borderidx));
save_item(NAME(m_mouse_last_read));
save_item(NAME(m_mouse_counter));
save_item(NAME(m_snd_p1));
save_item(NAME(m_snd_p2));
save_item(NAME(m_sys_p2));
}
void mindset_state::map_modules()
{
auto &space = m_maincpu->space(AS_IO);
bool id = !(m_sys_p2 & 0x40);
space.unmap_readwrite(0x8080, 0x81ff);
for(int i=0; i<6; i++)
m_modules[i]->map(space, 0x8080 + 0x40*i, id);
}
void mindset_state::machine_reset()
{
m_sys_p2 = 0;
map_modules();
m_fdc_intext = m_fdc_int = m_trap_int = m_fdc_drq = m_trap_drq = false;
m_trap_pos = m_trap_len = 0;
memset(m_trap_data, 0, sizeof(m_trap_data));
memset(m_palette, 0, sizeof(m_palette));
memset(m_genlock, 0, sizeof(m_genlock));
m_dispctrl = m_screenpos = m_intpos = m_intaddr = m_fdc_dma_count = 0;
m_kbd_p1 = m_kbd_p2 = m_borderidx = 0;
memset(m_mouse_last_read, 0, sizeof(m_mouse_last_read));
memset(m_mouse_counter, 0, sizeof(m_mouse_counter));
m_snd_p1 = 0x80;
m_snd_p2 = 0;
m_dac->write(0x80);
}
int mindset_state::sys_t0_r()
{
// logerror("SYS: %d read t0 %d (%03x)\n", m_kbdcpu->total_cycles(), (m_kbd_p2 & 0x40) != 0, m_syscpu->pc());
return (m_kbd_p2 & 0x40) != 0;
}
int mindset_state::sys_t1_r()
{
logerror("SYS: read t1\n");
return !m_fdc_int;
}
u8 mindset_state::sys_p1_r()
{
// logerror("SYS: read p1\n");
return 0xff;
}
u8 mindset_state::sys_p2_r()
{
// logerror("SYS: read p2 (%03x)\n", m_syscpu->pc());
return 0xff;
}
void mindset_state::sys_p1_w(u8 data)
{
// m_maincpu->int0_w(!((data & 0x40) || m_fdc->get_irq()));
logerror("SYS: fdc write p1 %02x irq %d\n", data, !!(data & 0x40));
}
void mindset_state::sys_p2_w(u8 data)
{
u8 old = m_sys_p2;
m_sys_p2 = data;
m_yellow_led = !BIT(data, 0);
m_green_led = !BIT(data, 1);
m_red_led = !BIT(data, 2);
if((m_sys_p2 ^ old) & 0x40)
map_modules();
m_maincpu->int3_w(!(data & 0x80));
// logerror("SYS: write p2 %02x\n", data);
}
void mindset_state::kbd_p1_w(u8 data)
{
m_kbd_p1 = data;
}
void mindset_state::kbd_p2_w(u8 data)
{
// if((m_kbd_p2 ^ data) & 0x40)
// logerror("KBD: %d output bit %d\n", m_kbdcpu->total_cycles(), (m_kbd_p2 & 0x40) != 0);
m_kbd_p2 = data;
}
u8 mindset_state::kbd_d_r()
{
return keyscan();
}
int mindset_state::kbd_t1_r()
{
return keyscan() & 0x100;
}
void mindset_state::update_dac()
{
// The p1 dac has the waveform (idle at 0x80), while the p2 one is used for the global volume (mute at 0x00, max at 0xff)
m_dac->write((s8(m_snd_p1-0x80)*m_snd_p2/255 + 0x80) & 0xff);
}
void mindset_state::snd_p1_w(u8 data)
{
m_snd_p1 = data;
update_dac();
}
void mindset_state::snd_p2_w(u8 data)
{
m_snd_p2 = data;
update_dac();
}
u16 mindset_state::keyscan()
{
u16 src = (m_kbd_p2 << 8) | m_kbd_p1;
u16 res = 0x1ff;
for(unsigned int i=0; i<11; i++)
if(!(src & (1 << i)))
res &= m_kbd_row[i]->read();
if(!(src & 0x8000)) {
int axis = (src >> 12) & 1;
u8 aval = m_mouse_axis[axis]->read();
m_mouse_counter[axis] += aval - m_mouse_last_read[axis];
m_mouse_last_read[axis] = aval;
u8 nib;
if((src >> 11) & 1) {
nib = m_mouse_counter[axis] & 0xf;
m_mouse_counter[axis] &= 0xf0;
} else {
nib = m_mouse_counter[axis] >> 4;
m_mouse_counter[axis] &= 0x0f;
}
res &= m_mouse_btn->read() & (nib | 0x1f0);
}
if(!(src & 0x2000))
res &= m_joystick->read();
return res;
}
u16 mindset_state::dispctrl_r()
{
return m_dispctrl;
}
void mindset_state::dispctrl_w(u16 data)
{
u16 chg = m_dispctrl ^ data;
m_dispctrl = data;
if(chg & 0xff88)
logerror("display control %s bank=%c %s %s h=%d ppx=%d w=%s interlace=%d rreg=%d indicator=%s wreg=%d\n",
m_dispctrl & 0x8000 ? "?15" : "?!15",
m_dispctrl & 0x4000 ? '1' : '0',
m_dispctrl & 0x2000 ? "ibm" : "native",
m_dispctrl & 0x1000 ? "?12" : "?!12",
m_dispctrl & 0x0800 ? "400" : "200",
(m_dispctrl & 0x0600) >> 9,
m_dispctrl & 0x0100 ? "320" : "640",
m_dispctrl & 0x0080 ? "on" : "off",
(m_dispctrl & 0x0070) >> 4,
m_dispctrl & 0x0008 ? "on" : "off",
m_dispctrl & 7);
}
u16 mindset_state::dispreg_r()
{
switch((m_dispctrl >> 4) & 7) {
case 1: { // Read vram at interrupt position
u16 v = m_vram[m_intaddr >> 1];
if(m_intaddr & 1)
v >>= 8;
return sw(v << 4);
break;
}
case 5: {
// wants 0080 set to be able to upload the palette
// may be a field indicator
return 0x0080;
}
}
logerror("dispreg read %x\n", (m_dispctrl >> 4) & 7);
return 0;
}
void mindset_state::dispreg_w(u16 data)
{
switch(m_dispctrl & 0x7) {
case 0:
m_screenpos = data;
logerror("screen position (%d, %d)\n", (data >> 8) & 15, (data >> 12) & 15);
break;
case 1:
m_borderidx = (data >> 8) & 0xf;
logerror("border color %x\n", m_borderidx);
break;
case 2: {
m_intpos = data;
int intx = (159 - ((m_intpos >> 8) & 255)) * ((m_dispctrl & 0x100) ? 2 : 4);
int inty = 199 - (m_intpos & 255);
m_intaddr = 0;
bool bank = m_dispctrl & 0x4000;
bool ibm_mode = m_dispctrl & 0x2000;
// bool interleave = m_dispctrl & 0x0800;
int pixels_per_byte_order = (m_dispctrl & 0x0600) >> 9;
bool large_pixels = m_dispctrl & 0x0100;
if(ibm_mode)
m_intaddr = (inty & 1) * 0x2000 + (inty >> 1) * 80 + (intx >> (3 - large_pixels)) ;
else {
int stepy = 0;
if(pixels_per_byte_order == 2) stepy = 40;
if(pixels_per_byte_order == 1) stepy = 80;
if(pixels_per_byte_order == 0) stepy = 160;
m_intaddr = inty * stepy + (intx >> (pixels_per_byte_order - large_pixels + 2));
}
if(bank)
m_intaddr += 16000;
logerror("interrupt position (%3d, %3d) ramdac address %04x\n", intx, inty, m_intaddr);
break;
}
case 4: {
data = sw(data);
u8 r = (0x49*(data & 7)) >> 1;
u8 g = (0x49*((data & 0x38) >> 3)) >> 1;
u8 b = (0x49*((data & 0x1c0) >> 6)) >> 1;
m_palette[m_borderidx] = (r << 16) | (g << 8) | b;
m_genlock[m_borderidx] = data & 0x0200;
logerror("palette[%x] = %04x -> %06x.%d\n", m_borderidx, data, m_palette[m_borderidx], m_genlock[m_borderidx]);
m_borderidx = (m_borderidx + 1) & 0xf;
break;
}
case 5: {
logerror("genlock %s%s, extra=%c\n", data & 0x0200 ? "on" : "off", data & 0x0100 ? " fixed" : "", data & 0x0400 ? '1' : '0');
break;
}
default:
logerror("display reg[%x] = %04x\n", m_dispctrl & 0xf, data);
}
}
u32 mindset_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
// Temporary gross hack
if(cliprect.max_y != 479)
return 0;
const u16 *bank = m_dispctrl & 0x4000 ? m_vram + 8000 : m_vram;
bool ibm_mode = m_dispctrl & 0x2000;
bool interleave = m_dispctrl & 0x0800;
int pixels_per_byte_order = (m_dispctrl & 0x0600) >> 9;
bool large_pixels = m_dispctrl & 0x0100;
bitmap.fill(m_palette[m_borderidx]);
int dx = ((m_screenpos >> 8) & 15) * (751 - 640) / 15;
int dy = ((m_screenpos >> 12) & 15) * (480 - 400) / 15;
if(ibm_mode) {
if(large_pixels) {
static int palind[4] = { 0, 1, 4, 5 };
for(int field=0; field<2; field++) {
for(u32 yy=0; yy<2; yy++) {
const u16 *src = bank + 4096*yy;
for(u32 y=yy; y<200; y+=2) {
u32 *dest = &bitmap.pix32(2*y+field+dy, dx);
for(u32 x=0; x<320; x+=8) {
u16 sv = sw(*src++);
for(u32 xx=0; xx<8; xx++) {
u32 color = m_palette[palind[(sv >> (14-2*xx)) & 3]];
*dest++ = color;
*dest++ = color;
}
}
}
}
}
return 0;
} else {
static int palind[4] = { 0, 4 };
for(int field=0; field<2; field++) {
for(u32 yy=0; yy<2; yy++) {
const u16 *src = bank + 4096*yy;
for(u32 y=yy; y<200; y+=2) {
u32 *dest = &bitmap.pix32(2*y+field+dy, dx);
for(u32 x=0; x<640; x+=16) {
u16 sv = sw(*src++);
for(u32 xx=0; xx<16; xx++) {
u32 color = m_palette[palind[(sv >> (15-xx)) & 1]];
*dest++ = color;
}
}
}
}
}
return 0;
}
} else {
if(large_pixels) {
if(!interleave) {
switch(pixels_per_byte_order) {
case 0: {
const u16 *src = bank;
for(u32 y=0; y<200; y++) {
u32 *dest0 = &bitmap.pix32(2*y+dy, dx);
u32 *dest1 = &bitmap.pix32(2*y+1+dy, dx);
for(u32 x=0; x<320; x+=4) {
u16 sv = sw(*src++);
for(u32 xx=0; xx<4; xx++) {
u32 color = m_palette[(sv >> (12-4*xx)) & 15];
*dest0++ = color;
*dest0++ = color;
*dest1++ = color;
*dest1++ = color;
}
}
}
return 0;
}
case 1: {
static int palind[4] = { 0, 1, 4, 5 };
const u16 *src = bank;
for(u32 y=0; y<200; y++) {
u32 *dest0 = &bitmap.pix32(2*y+dy, dx);
u32 *dest1 = &bitmap.pix32(2*y+1+dy, dx);
for(u32 x=0; x<320; x+=8) {
u16 sv = sw(*src++);
for(u32 xx=0; xx<8; xx++) {
u32 color = m_palette[palind[(sv >> (14-2*xx)) & 3]];
*dest0++ = color;
*dest0++ = color;
*dest1++ = color;
*dest1++ = color;
}
}
}
return 0;
}
case 2: {
const u16 *src = bank;
for(u32 y=0; y<200; y++) {
u32 *dest0 = &bitmap.pix32(2*y+dy, dx);
u32 *dest1 = &bitmap.pix32(2*y+1+dy, dx);
for(u32 x=0; x<320; x+=16) {
u16 sv = sw(*src++);
for(u32 xx=0; xx<16; xx++) {
u32 color = m_palette[(sv >> (15-xx)) & 1];
*dest0++ = color;
*dest0++ = color;
*dest1++ = color;
*dest1++ = color;
}
}
}
return 0;
}
}
} else {
switch(pixels_per_byte_order) {
case 0: {
const u16 *src = bank;
for(u32 y=0; y<400; y++) {
u32 *dest = &bitmap.pix32(y+dy, dx);
for(u32 x=0; x<320; x+=4) {
u16 sv = sw(*src++);
for(u32 xx=0; xx<4; xx++) {
u32 color = m_palette[(sv >> (12-4*xx)) & 15];
*dest++ = color;
*dest++ = color;
}
}
}
return 0;
}
case 1: {
static int palind[4] = { 0, 1, 4, 5 };
const u16 *src = bank;
for(u32 y=0; y<400; y++) {
u32 *dest = &bitmap.pix32(y+dy, dx);
for(u32 x=0; x<320; x+=8) {
u16 sv = sw(*src++);
for(u32 xx=0; xx<8; xx++) {
u32 color = m_palette[palind[(sv >> (14-2*xx)) & 3]];
*dest++ = color;
*dest++ = color;
}
}
}
return 0;
}
}
}
} else {
if(!interleave) {
switch(pixels_per_byte_order) {
case 0: {
static int palind[4] = { 0, 4, 8, 12 };
const u16 *src = bank;
for(u32 y=0; y<200; y++) {
u32 *dest0 = &bitmap.pix32(2*y+dy, dx);
u32 *dest1 = &bitmap.pix32(2*y+1+dy, dx);
for(u32 x=0; x<640; x+=8) {
u16 sv = sw(*src++);
for(u32 xx=0; xx<8; xx++) {
u32 color = m_palette[palind[(sv >> (14-2*xx)) & 3]];
*dest0++ = color;
*dest1++ = color;
}
}
}
return 0;
}
case 1: {
static int palind[4] = { 0, 4 };
const u16 *src = bank;
for(u32 y=0; y<200; y++) {
u32 *dest0 = &bitmap.pix32(2*y+dy, dx);
u32 *dest1 = &bitmap.pix32(2*y+1+dy, dx);
for(u32 x=0; x<640; x+=16) {
u16 sv = sw(*src++);
for(u32 xx=0; xx<16; xx++) {
u32 color = m_palette[palind[(sv >> (15-xx)) & 1]];
*dest0++ = color;
*dest1++ = color;
}
}
}
return 0;
}
}
} else {
static int palind[4] = { 0, 4 };
const u16 *src = bank;
for(u32 y=0; y<400; y++) {
u32 *dest = &bitmap.pix32(y+dy, dx);
for(u32 x=0; x<640; x+=16) {
u16 sv = sw(*src++);
for(u32 xx=0; xx<16; xx++) {
u32 color = m_palette[palind[(sv >> (15-xx)) & 1]];
*dest++ = color;
}
}
}
return 0;
}
}
logerror("Unimplemented native mode (%dx%d, ppb=%d)\n", large_pixels ? 320 : 640, interleave ? 400 : 200, 2 << pixels_per_byte_order);
}
bitmap.fill(0);
return 0;
}
u16 mindset_state::gcp_blend_0(u16 src, u16)
{
return src;
}
u16 mindset_state::gcp_blend_1(u16 src, u16 dst)
{
return src & dst;
}
u16 mindset_state::gcp_blend_2(u16 src, u16 dst)
{
return src | dst;
}
u16 mindset_state::gcp_blend_3(u16 src, u16 dst)
{
return src ^ dst;
}
u16 mindset_state::gcp_blend_4(u16 src, u16)
{
return ~src;
}
u16 mindset_state::gcp_blend_5(u16 src, u16 dst)
{
return (~src) & dst;
}
u16 mindset_state::gcp_blend_6(u16 src, u16 dst)
{
return (~src) | dst;
}
u16 mindset_state::gcp_blend_7(u16 src, u16 dst)
{
return (~src) ^ dst;
}
u16 (*const mindset_state::gcp_blend[8])(u16, u16) = {
gcp_blend_0,
gcp_blend_1,
gcp_blend_2,
gcp_blend_3,
gcp_blend_4,
gcp_blend_5,
gcp_blend_6,
gcp_blend_7
};
void mindset_state::blit(u16 packet_seg, u16 packet_adr)
{
u16 mode = sw(m_gcps->read_word((packet_seg << 4) + ((packet_adr + 0) & 0xffff)));
u16 src_adr = sw(m_gcps->read_word((packet_seg << 4) + ((packet_adr + 2) & 0xffff)));
u16 src_sft = sw(m_gcps->read_word((packet_seg << 4) + ((packet_adr + 4) & 0xffff)));
u16 dst_adr = sw(m_gcps->read_word((packet_seg << 4) + ((packet_adr + 6) & 0xffff)));
u16 dst_sft = sw(m_gcps->read_word((packet_seg << 4) + ((packet_adr + 8) & 0xffff)));
u16 width = sw(m_gcps->read_word((packet_seg << 4) + ((packet_adr + 10) & 0xffff)));
u16 height = sw(m_gcps->read_word((packet_seg << 4) + ((packet_adr + 12) & 0xffff)));
u16 sy = sw(m_gcps->read_word((packet_seg << 4) + ((packet_adr + 14) & 0xffff)));
u16 dy = sw(m_gcps->read_word((packet_seg << 4) + ((packet_adr + 16) & 0xffff)));
u16 rmask = sw(m_gcps->read_word((packet_seg << 4) + ((packet_adr + 18) & 0xffff)));
u16 src_seg = sw(m_gcps->read_word((packet_seg << 4) + ((packet_adr + 20) & 0xffff)));
u16 dst_seg = sw(m_gcps->read_word((packet_seg << 4) + ((packet_adr + 22) & 0xffff)));
u16 wmask = sw(m_gcps->read_word((packet_seg << 4) + ((packet_adr + 24) & 0xffff)));
u16 kmask = sw(m_gcps->read_word((packet_seg << 4) + ((packet_adr + 26) & 0xffff)));
// -f-w wnpi ktxm mm--
// f = fast (pure word copy)
// w = pixel width (1/2/4/8 bits)
// n = invert collision flag (unimplemented)
// p = pattern fill (used by fill_dest_buffer)
// i/f = increment source / don't (used by blt_copy_word)
// t/o = transparent/opaque
// k = detect collision (unimplemented)
// x = go right to left (unimplemented)
// m = blending mode
if(0)
logerror("GCP: p src %04x:%04x.%x dst %04x:%04x.%x sz %xx%x step %x:%x mask %04x:%04x k %x:%x mode %c%d%c%c%c%c%c%c%d\n", src_seg, src_adr, src_sft, dst_seg, dst_adr, dst_sft, width, height, sy, dy, rmask, wmask, (kmask >> 8) & 15, kmask & 15,
mode & 0x4000 ? 'f' : '-',
(mode >> 11) & 3,
mode & 0x400 ? 'n' : '-',
mode & 0x200 ? 'p' : '-',
mode & 0x100 ? 'i' : 'f',
mode & 0x80 ? 'k' : '-',
mode & 0x40 ? 't' : 'o',
mode & 0x20 ? 'x' : '-',
(mode >> 2) & 7);
if(mode & 0x200) {
// pattern fill
u16 src = m_gcps->read_word((src_seg << 4) + src_adr);
for(u16 w=0; w != width/2; w++) {
m_gcps->write_word((dst_seg << 4) + dst_adr, src);
dst_adr += 2;
}
} else if(mode & 0x4000) {
// fast mode
u32 nw = (width+15) >> 4;
for(u32 y=0; y<height; y++) {
u16 src_cadr = src_adr;
u16 dst_cadr = dst_adr;
for(u32 w=0; w!=nw; w++) {
m_gcps->write_word((dst_seg << 4) + dst_cadr, m_gcps->read_word((src_seg << 4) + src_cadr));
src_cadr += 2;
dst_cadr += 2;
}
src_adr += sy;
dst_adr += dy;
}
} else {
auto blend = gcp_blend[(mode >> 2) & 7];
// Need to rotate rmask depending on the shifts too
u16 awmask = ((wmask << 16) | wmask) >> (15 - dst_sft);
u16 swmask, mwmask, ewmask;
if(dst_sft >= width) {
swmask = msk(dst_sft+1) & ~msk(dst_sft - width + 1);
mwmask = 0xffff;
ewmask = swmask;
} else {
swmask = msk(dst_sft+1);
mwmask = 0xffff;
ewmask = ~msk((dst_sft - width + 1) & 15);
}
swmask &= awmask;
mwmask &= awmask;
ewmask &= awmask;
bool preload = dst_sft > src_sft;
int src_do_sft = 15 - src_sft;
int dst_do_sft = (preload ? 31 : 15) - dst_sft;
u16 nw = ((width + (15 - dst_sft)) + 15) >> 4;
for(u32 y=0; y<height; y++) {
u16 src_cadr = src_adr;
u16 dst_cadr = dst_adr;
u16 cmask = swmask;
u16 nw1 = nw;
u32 srcs = 0;
if(preload) {
srcs = sw(m_gcps->read_word((src_seg << 4) + src_cadr)) << src_do_sft;
if(mode & 0x100)
src_cadr += 2;
}
do {
srcs = (srcs << 16) | (sw(m_gcps->read_word((src_seg << 4) + src_cadr)) << src_do_sft);
u16 src = (srcs >> dst_do_sft) & rmask;
u16 dst = sw(m_gcps->read_word((dst_seg << 4) + dst_cadr));
u16 res = blend(src, dst);
if(mode & 0x40) {
u16 tmask;
switch((mode >> 11) & 3) {
case 0:
default:
tmask = src;
break;
case 1:
tmask = (src >> 1) | src;
tmask = (tmask & 0x5555) * 0x3;
break;
case 2:
tmask = (src >> 2) | src;
tmask = (tmask >> 1) | tmask;
tmask = (tmask & 0x1111) * 0xf;
break;
case 3:
tmask = (src >> 4) | src;
tmask = (tmask >> 2) | tmask;
tmask = (tmask >> 1) | tmask;
tmask = (tmask & 0x0101) * 0xff;
break;
}
cmask &= tmask;
}
res = (dst & ~cmask) | (res & cmask);
m_gcps->write_word((dst_seg << 4) + dst_cadr, sw(res));
if(mode & 0x100)
src_cadr += 2;
dst_cadr += 2;
nw1 --;
cmask = nw1 == 1 ? ewmask : mwmask;
} while(nw1);
if(mode & 0x100)
src_adr += sy;
dst_adr += dy;
}
}
}
void mindset_state::gcp_w(u16)
{
u16 packet_seg = sw(m_gcps->read_word(0xbfd7a));
u16 packet_adr = sw(m_gcps->read_word(0xbfd78));
u16 global_mode = sw(m_gcps->read_word(0xbfd76));
if(0)
logerror("GCP: start %04x:%04x mode %04x (%05x)\n", packet_seg, packet_adr, global_mode, m_maincpu->pc());
switch(global_mode) {
case 0x0005:
case 0x0101:
blit(packet_seg, packet_adr);
break;
}
// 100 = done, 200 = done too???, 400 = collision?
m_gcps->write_word(0xbfd74, m_gcps->read_word(0xbfd74) | 0x0700);
// Can trigger an irq, on mode & 2 (or is it 200?) (0x40 on 8282, ack on 0x41, which means the system 8042...)
}
void mindset_state::fdc_ctrl_w(u8 data)
{
logerror("fdc control %02x\n", data);
if(data & 0x04)
m_fdc->reset();
m_floppy[data & 1]->mon_w(!(data & 2));
m_floppy[(data & 1)^1]->mon_w(true);
}
void mindset_state::fdc_int_w(int state)
{
if(!m_fdc_intext && state)
m_fdc_int = true;
m_fdc_intext = state;
m_maincpu->int0_w(m_fdc_int || m_trap_int);
}
u16 mindset_state::fdc_clear_interrupt()
{
m_fdc_int = false;
m_maincpu->int0_w(m_fdc_int || m_trap_int);
return 0x0000;
}
void mindset_state::fdc_dma_count_w(u16 data)
{
m_fdc_dma_count = data;
logerror("fdc dma count %x\n", m_fdc_dma_count);
}
u8 mindset_state::fdc_dma_r()
{
u8 res = m_fdc->dma_r();
if(!m_fdc_dma_count) {
m_fdc->tc_w(1);
m_fdc->tc_w(0);
} else
m_fdc_dma_count--;
return res;
}
void mindset_state::fdc_dma_w(u8 data)
{
m_fdc->dma_w(data);
if(!m_fdc_dma_count) {
m_fdc->tc_w(1);
m_fdc->tc_w(0);
} else
m_fdc_dma_count--;
}
u16 mindset_state::trap_clear_interrupt()
{
m_trap_int = false;
m_maincpu->int0_w(m_fdc_int || m_trap_int);
return 0x0000;
}
u16 mindset_state::trap_r(offs_t offset)
{
// machine().debug_break();
logerror("trap_r %04x\n", offset << 1);
m_trap_data[m_trap_len++] = (offset << 1) | 0x8000;
m_trap_data[m_trap_len++] = 0;
m_trap_drq = true;
m_maincpu->drq1_w(m_fdc_drq || m_trap_drq);
return 0;
}
void mindset_state::trap_w(offs_t offset, u16 data)
{
// machine().debug_break();
logerror("trap_w %04x, %04x\n", offset << 1, data);
m_trap_data[m_trap_len++] = offset << 1;
m_trap_data[m_trap_len++] = data;
m_trap_drq = true;
m_maincpu->drq1_w(m_fdc_drq || m_trap_drq);
}
u16 mindset_state::trap_dma_r(offs_t, u16 mem_mask)
{
u16 res = m_trap_pos < m_trap_len ? m_trap_data[m_trap_pos++] : 0;
logerror("trap dma %04x @ %04x\n", res, mem_mask);
if(m_trap_pos >= m_trap_len) {
m_trap_drq = false;
m_trap_int = true;
m_maincpu->drq1_w(m_fdc_drq || m_trap_drq);
m_maincpu->int0_w(m_fdc_int || m_trap_int);
m_trap_pos = m_trap_len = 0;
}
return res;
}
void mindset_state::maincpu_mem(address_map &map)
{
map(0x00000, 0x3ffff).ram();
map(0xb8000, 0xbffff).ram().share("vram");
map(0xf8000, 0xfffff).rom().region("maincpu", 0);
}
void mindset_state::maincpu_io(address_map &map)
{
map(0x0000, 0x7fff).rw(FUNC(mindset_state::trap_r), FUNC(mindset_state::trap_w));
map(0x8040, 0x8041).r(FUNC(mindset_state::trap_dma_r));
map(0x8048, 0x8049).r(FUNC(mindset_state::trap_clear_interrupt));
map(0x8050, 0x8050).w(FUNC(mindset_state::fdc_ctrl_w));
map(0x8054, 0x8054).rw(FUNC(mindset_state::fdc_dma_r), FUNC(mindset_state::fdc_dma_w));
map(0x8058, 0x8059).w(FUNC(mindset_state::fdc_dma_count_w));
map(0x805c, 0x805d).r(FUNC(mindset_state::fdc_clear_interrupt));
map(0x8060, 0x8060).r(m_fdc, FUNC(i8272a_device::msr_r));
map(0x8062, 0x8062).rw(m_fdc, FUNC(i8272a_device::fifo_r), FUNC(i8272a_device::fifo_w));
#if 0
map(0x8080, 0x8080).lr8("id13", []() -> u8 { return 0x13; }); // sound
map(0x80c0, 0x80c0).lr8("id3f", []() -> u8 { return 0x3f; }); // serial type 1, maybe?
map(0x8100, 0x8100).lr8("id5f", []() -> u8 { return 0x5f; }); // serial type 2
map(0x8140, 0x8140).lr8("id70", []() -> u8 { return 0x70; }); // parallel printer, init writes 0x82 at +6
map(0x8180, 0x8180).lr8("rs232-id", []() -> u8 { return 0x73; }); // rs232
#endif
map(0x8280, 0x8283).rw(m_syscpu, FUNC(i8042_device::upi41_master_r), FUNC(i8042_device::upi41_master_w)).umask16(0x00ff);
map(0x82a0, 0x82a3).rw(m_soundcpu, FUNC(i8042_device::upi41_master_r), FUNC(i8042_device::upi41_master_w)).umask16(0x00ff);
map(0x8300, 0x8301).w(FUNC(mindset_state::gcp_w));
map(0x8320, 0x8321).rw(FUNC(mindset_state::dispreg_r), FUNC(mindset_state::dispreg_w));
map(0x8322, 0x8323).rw(FUNC(mindset_state::dispctrl_r), FUNC(mindset_state::dispctrl_w));
}
static void pc_dd_floppies(device_slot_interface &device)
{
device.option_add("525dd", FLOPPY_525_DD);
}
static void mindset_modules(device_slot_interface &device)
{
device.option_add("stereo", MINDSET_SOUND_MODULE);
device.option_add("rs232", MINDSET_RS232_MODULE);
}
void mindset_state::mindset(machine_config &config)
{
config.set_perfect_quantum(m_syscpu);
config.set_default_layout(layout_mindset);
I80186(config, m_maincpu, 12_MHz_XTAL); // Divides internally by 2 to produce a clkout of 6MHz
m_maincpu->set_addrmap(AS_PROGRAM, &mindset_state::maincpu_mem);
m_maincpu->set_addrmap(AS_IO, &mindset_state::maincpu_io);
I8042(config, m_syscpu, 14.318181_MHz_XTAL/2);
m_syscpu->p1_in_cb().set(FUNC(mindset_state::sys_p1_r));
m_syscpu->p2_in_cb().set(FUNC(mindset_state::sys_p2_r));
m_syscpu->p1_out_cb().set(FUNC(mindset_state::sys_p1_w));
m_syscpu->p2_out_cb().set(FUNC(mindset_state::sys_p2_w));
m_syscpu->t0_in_cb().set(FUNC(mindset_state::sys_t0_r));
m_syscpu->t1_in_cb().set(FUNC(mindset_state::sys_t1_r));
I8042(config, m_soundcpu, 12_MHz_XTAL/2);
m_soundcpu->p1_out_cb().set(FUNC(mindset_state::snd_p1_w));
m_soundcpu->p2_out_cb().set(FUNC(mindset_state::snd_p2_w));
I8749(config, m_kbdcpu, 6_MHz_XTAL);
m_kbdcpu->p1_out_cb().set(FUNC(mindset_state::kbd_p1_w));
m_kbdcpu->p2_out_cb().set(FUNC(mindset_state::kbd_p2_w));
m_kbdcpu->bus_in_cb().set(FUNC(mindset_state::kbd_d_r));
m_kbdcpu->t1_in_cb().set(FUNC(mindset_state::kbd_t1_r));
// Should be NTSC actually... we'll see
// Pretty sure the pixel clock is the 14.x one, the 12MHz one would only allow 630 pixels
SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
m_screen->set_refresh_hz(60);
m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(100));
m_screen->set_size(751, 480);
m_screen->set_visarea(0, 750, 0, 479);
m_screen->set_screen_update(FUNC(mindset_state::screen_update));
// Should be at the position indicated by display reg 2
m_screen->scanline().set([this](int scanline) { m_maincpu->int2_w(scanline == 398); });
m_screen->screen_vblank().set(m_maincpu, FUNC(i80186_cpu_device::int1_w));
I8272A(config, m_fdc, 16_MHz_XTAL/2, true);
m_fdc->intrq_wr_callback().set(FUNC(mindset_state::fdc_int_w));
m_fdc->drq_wr_callback().set([this](int state) { m_fdc_drq = state; m_maincpu->drq1_w(m_fdc_drq || m_trap_drq); });
m_fdc->set_ready_line_connected(false);
FLOPPY_CONNECTOR(config, m_fdco[0], pc_dd_floppies, "525dd", mindset_state::floppy_formats);
FLOPPY_CONNECTOR(config, m_fdco[1], pc_dd_floppies, "525dd", mindset_state::floppy_formats);
SPEAKER(config, "lspeaker").front_left();
DAC_8BIT_R2R(config, m_dac, 0).add_route(ALL_OUTPUTS, "lspeaker", 0.5);
voltage_regulator_device &vref(VOLTAGE_REGULATOR(config, "vref"));
vref.add_route(0, m_dac, 1.0, DAC_VREF_POS_INPUT);
vref.add_route(0, m_dac, -1.0, DAC_VREF_NEG_INPUT);
MINDSET_MODULE(config, "m0", mindset_modules, "stereo", false);
MINDSET_MODULE(config, "m1", mindset_modules, "rs232", false);
MINDSET_MODULE(config, "m2", mindset_modules, nullptr, false);
MINDSET_MODULE(config, "m3", mindset_modules, nullptr, false);
MINDSET_MODULE(config, "m4", mindset_modules, nullptr, false);
MINDSET_MODULE(config, "m5", mindset_modules, nullptr, false);
SOFTWARE_LIST(config, "flop_list").set_original("mindset_flop");
}
static INPUT_PORTS_START(mindset)
PORT_START("K00")
PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')
PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('^')
PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('&')
PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('*')
PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR('(')
PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("K01")
PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F9) PORT_CHAR(UCHAR_MAMEKEY(F9))
PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F10) PORT_CHAR(UCHAR_MAMEKEY(F10))
PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Start")
PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PAUSE) PORT_NAME("Pause")
PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F11) PORT_NAME("Sys config")
PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F12) PORT_NAME("Reset")
PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("K02")
PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F4) PORT_CHAR(UCHAR_MAMEKEY(F4))
PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F5) PORT_CHAR(UCHAR_MAMEKEY(F5))
PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F6) PORT_CHAR(UCHAR_MAMEKEY(F6))
PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F7) PORT_CHAR(UCHAR_MAMEKEY(F7))
PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F8) PORT_CHAR(UCHAR_MAMEKEY(F8))
PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("K03")
PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('_')
PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_CHAR('+')
PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TILDE) PORT_CHAR('`') PORT_CHAR('~')
PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\') PORT_CHAR('|')
PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))
PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGUP) PORT_CHAR(UCHAR_MAMEKEY(PGUP))
PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Break")
PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("K04")
PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR('\t')
PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("K05")
PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[') PORT_CHAR('{')
PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']') PORT_CHAR('}')
PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DEL) PORT_CHAR(UCHAR_MAMEKEY(DEL))
PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PGDN) PORT_CHAR(UCHAR_MAMEKEY(PGDN))
PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_NAME("Caps lock")
PORT_START("K06")
PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL)) PORT_NAME("Control")
PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("K07")
PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L')
PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('"')
PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) PORT_NAME("Return")
PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("K08")
PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(END))
PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_MAMEKEY(LALT)) PORT_NAME("Alt")
PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1) PORT_NAME("Shift (Left)")
PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("K09")
PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1) PORT_NAME("Shift (Right)")
PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_PRTSCR) PORT_CHAR(UCHAR_MAMEKEY(PRTSCR)) PORT_NAME("Prt Scn")
PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SCRLOCK) PORT_CHAR(UCHAR_MAMEKEY(SCRLOCK))
PORT_START("K10")
PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_BIT(0x040, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_BIT(0x080, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_BIT(0x100, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("MOUSEAXIS1")
PORT_BIT(0xff, 0x00, IPT_MOUSE_X) PORT_SENSITIVITY(50) PORT_KEYDELTA(1) PORT_MINMAX(0, 255) PORT_PLAYER(1)
PORT_START("MOUSEAXIS0")
PORT_BIT(0xff, 0x00, IPT_MOUSE_Y) PORT_SENSITIVITY(50) PORT_KEYDELTA(1) PORT_MINMAX(0, 255) PORT_PLAYER(1)
PORT_START("MOUSEBTN")
PORT_BIT(0x1cf, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_PLAYER(1)
PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_PLAYER(1)
PORT_START("JOYSTICK")
PORT_BIT(0x001, IP_ACTIVE_LOW, IPT_JOYSTICK_UP) PORT_PLAYER(2) PORT_8WAY
PORT_BIT(0x002, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN) PORT_PLAYER(2) PORT_8WAY
PORT_BIT(0x004, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT) PORT_PLAYER(2) PORT_8WAY
PORT_BIT(0x008, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_PLAYER(2) PORT_8WAY
PORT_BIT(0x010, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_PLAYER(2)
PORT_BIT(0x020, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_PLAYER(2)
PORT_BIT(0x1c0, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END
ROM_START(mindset)
ROM_REGION(0x8000, "maincpu", 0)
ROM_LOAD16_BYTE("1.7_lo.u60", 0, 0x4000, CRC(00474dc1) SHA1(676f30f170c14174dbff3b5cbf98d0f23472b7c4))
ROM_LOAD16_BYTE("1.7_hi.u59", 1, 0x4000, CRC(1434af10) SHA1(39105eacdd7ddc13e449e2c32743e828bef33595))
ROM_REGION(0x0800, "syscpu", 0)
ROM_LOAD("253002-001.u17", 0, 0x800, CRC(69da82c9) SHA1(2f0bf5b134dc703cbc72e0c6df5b7beda1b39e70))
ROM_REGION(0x0800, "soundcpu", 0)
ROM_LOAD("253006-001.u16", 0, 0x800, CRC(7bea5edd) SHA1(30cdc0dedaa5246f4952df452a99ca22e3cd0636))
ROM_REGION(0x0800, "kbdcpu", 0)
ROM_LOAD("kbd_v3.0.bin", 0, 0x800, CRC(1c6aa433) SHA1(1d01dbda4730f26125ba2564a608c2f8ddfc05b3))
ROM_END
COMP( 1984, mindset, 0, 0, mindset, mindset, mindset_state, empty_init, "Mindset Corporation", "Mindset Personal Computer", MACHINE_SUPPORTS_SAVE)