// license:BSD-3-Clause // copyright-holders:Sandro Ronco /*************************************************************************** Xircom / Intel REX 6000 Driver by Sandro Ronco Known REX 6000 banks: 0x0000 - 0x00ff first flash chip 0x0600 - 0x06ff second flash chip 0x1000 - 0x1003 32KB RAM Known DataSlim 2 banks: 0x0000 - 0x00ff first flash chip 0x0b00 - 0x0bff second flash chip 0x1000 - 0x1003 32KB RAM TODO: - deleting all Memos causes an error - alarm doesn't work - alarm sound and keyclick - NVRAM and warm start (without the pen calibration) - serial I/O ****************************************************************************/ #include "emu.h" #include "cpu/z80/z80.h" #include "machine/rp5c01.h" #include "machine/ram.h" #include "machine/intelfsh.h" #include "imagedev/snapquik.h" #include "sound/beep.h" #include "rendlay.h" #define MAKE_BANK(lo, hi) ((lo) | ((hi)<<8)) //irq flag/mask bits #define IRQ_FLAG_KEYCHANGE 0x01 #define IRQ_FLAG_ALARM 0x02 #define IRQ_FLAG_SERIAL 0x04 #define IRQ_FLAG_1HZ 0x10 #define IRQ_FLAG_IRQ2 0x20 #define IRQ_FLAG_IRQ1 0x40 #define IRQ_FLAG_EVENT 0x80 //memory bank types #define BANK_FLASH0_B0 0x00 #define BANK_FLASH0_B1 0x01 #define BANK_FLASH1_B0 0x02 #define BANK_FLASH1_B1 0x03 #define BANK_RAM 0x10 #define BANK_UNKNOWN 0xff #define TC8521_TAG "rtc" class rex6000_state : public driver_device { public: rex6000_state(const machine_config &mconfig, device_type type, const char *tag) : driver_device(mconfig, type, tag), m_maincpu(*this, "maincpu"), m_ram(*this, RAM_TAG), m_beep(*this, "beeper") { } required_device m_maincpu; required_device m_ram; required_device m_beep; fujitsu_29dl16x_device *m_flash[4]; UINT8 m_bank[4]; UINT8 m_beep_io[5]; UINT8 m_lcd_base[2]; UINT8 m_touchscreen[0x10]; UINT8 m_lcd_enabled; UINT8 m_lcd_cmd; UINT8 *m_ram_base; UINT8 m_irq_mask; UINT8 m_irq_flag; UINT8 m_port6; UINT8 m_beep_mode; struct { UINT8 type; UINT16 page; } m_banks[2]; virtual void machine_start(); virtual void machine_reset(); UINT32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); DECLARE_READ8_MEMBER( bankswitch_r ); DECLARE_WRITE8_MEMBER( bankswitch_w ); DECLARE_READ8_MEMBER( lcd_base_r ); DECLARE_WRITE8_MEMBER( lcd_base_w ); DECLARE_READ8_MEMBER( beep_r ); DECLARE_WRITE8_MEMBER( beep_w ); DECLARE_READ8_MEMBER( lcd_io_r ); DECLARE_WRITE8_MEMBER( lcd_io_w ); DECLARE_READ8_MEMBER( irq_r ); DECLARE_WRITE8_MEMBER( irq_w ); DECLARE_READ8_MEMBER( touchscreen_r ); DECLARE_WRITE8_MEMBER( touchscreen_w ); DECLARE_WRITE_LINE_MEMBER( alarm_irq ); DECLARE_READ8_MEMBER( flash_0x0000_r ); DECLARE_WRITE8_MEMBER( flash_0x0000_w ); DECLARE_READ8_MEMBER( flash_0x8000_r ); DECLARE_WRITE8_MEMBER( flash_0x8000_w ); DECLARE_READ8_MEMBER( flash_0xa000_r ); DECLARE_WRITE8_MEMBER( flash_0xa000_w ); UINT8 identify_bank_type(UINT32 bank); DECLARE_PALETTE_INIT(rex6000); DECLARE_INPUT_CHANGED_MEMBER(trigger_irq); TIMER_DEVICE_CALLBACK_MEMBER(irq_timer1); TIMER_DEVICE_CALLBACK_MEMBER(irq_timer2); TIMER_DEVICE_CALLBACK_MEMBER(sec_timer); DECLARE_QUICKLOAD_LOAD_MEMBER(rex6000); }; UINT8 rex6000_state::identify_bank_type(UINT32 bank) { if (bank < 0x080) { return BANK_FLASH0_B0; } else if (bank >= 0x080 && bank < 0x100) { return BANK_FLASH0_B1; } else if ((bank >= 0xb00 && bank < 0xb80) || (bank >= 0x600 && bank < 0x680)) { return BANK_FLASH1_B0; } else if ((bank >= 0xb80 && bank < 0xc00) || (bank >= 0x680 && bank < 0x700)) { return BANK_FLASH1_B1; } else if (bank >= 0x1000 && bank < 0x1004) { return BANK_RAM; } else { //logerror("%04x: unkonwn memory bank %x\n", m_maincpu->pc(), bank); return BANK_UNKNOWN; } } READ8_MEMBER( rex6000_state::bankswitch_r ) { return m_bank[offset]; } WRITE8_MEMBER( rex6000_state::bankswitch_w ) { address_space& program = m_maincpu->space(AS_PROGRAM); m_bank[offset&3] = data; switch (offset) { case 0: //bank 1 low case 1: //bank 1 high { //bank 1 start at 0x8000 m_banks[0].page = (MAKE_BANK(m_bank[0], m_bank[1]&0x0f) + 4); m_banks[0].type = identify_bank_type(m_banks[0].page); if (m_banks[0].type != BANK_UNKNOWN) { program.install_readwrite_handler(0x8000, 0x9fff, 0, 0, read8_delegate(FUNC(rex6000_state::flash_0x8000_r), this), write8_delegate(FUNC(rex6000_state::flash_0x8000_w), this)); } else { program.unmap_readwrite(0x8000, 0x9fff); } break; } case 2: //bank 2 low case 3: //bank 2 high { m_banks[1].page = MAKE_BANK(m_bank[2], m_bank[3]&0x1f); m_banks[1].type = identify_bank_type(m_banks[1].page); if (m_banks[1].type == BANK_RAM) { program.install_ram(0xa000, 0xbfff, m_ram_base + ((m_banks[1].page & 0x03)<<13)); } else if (m_banks[1].type != BANK_UNKNOWN) { program.install_readwrite_handler(0xa000, 0xbfff, 0, 0, read8_delegate(FUNC(rex6000_state::flash_0xa000_r), this), write8_delegate(FUNC(rex6000_state::flash_0xa000_w), this)); } else { program.unmap_readwrite(0xa000, 0xbfff); } break; } } } READ8_MEMBER( rex6000_state::beep_r ) { return m_beep_io[offset]; } WRITE8_MEMBER( rex6000_state::beep_w ) { m_beep_io[offset] = data; switch (offset) { case 0: //alarm mode control /* ---- ---x beep off ---- --x- continuous beep ---- -x-- continuous alarm 1 ---- x--- continuous alarm 2 ---x ---- continuous alarm 3 --x- ---- single very short beep (keyclick) -x-- ---- single alarm x--- ---- single short beep */ //TODO: the beeper frequency and length in alarm mode need to be measured break; case 1: //tone mode control if (m_beep_mode) { m_beep->set_state(BIT(data, 0)); //the beeper frequency is update only if the bit 1 is set if (BIT(data, 1)) m_beep->set_frequency(16384 / (((m_beep_io[2] | (m_beep_io[3]<<8)) & 0x0fff) + 2)); } break; case 4: //select alarm/tone mode if (m_beep_mode != BIT(data, 0)) m_beep->set_state(0); //turned off when mode changes m_beep_mode = BIT(data, 0); break; } } READ8_MEMBER( rex6000_state::lcd_base_r ) { return m_lcd_base[offset]; } WRITE8_MEMBER( rex6000_state::lcd_base_w ) { m_lcd_base[offset&1] = data; } READ8_MEMBER( rex6000_state::lcd_io_r ) { return (offset == 0) ? m_lcd_enabled : m_lcd_cmd; } WRITE8_MEMBER( rex6000_state::lcd_io_w ) { switch (offset) { case 0: m_lcd_enabled = data; break; case 1: m_lcd_cmd = data; break; } } READ8_MEMBER( rex6000_state::irq_r ) { switch (offset) { case 0: //irq flag /* ---- ---x input port ---- --x- alarm ---- -x-- serial ---- x--- ?? ---x ---- 1Hz timer --x- ---- 32Hz timer -x-- ---- 4096Hz timer x--- ---- special events */ return m_irq_flag; case 1: return m_port6; case 2: //irq mask return m_irq_mask; default: return 0; } } WRITE8_MEMBER( rex6000_state::irq_w ) { switch (offset) { case 0: //irq flag m_irq_flag = data; break; case 1: //clear irq flag m_port6 = data; m_irq_flag &= (~data); break; case 2: //irq mask m_irq_mask = data; break; } } READ8_MEMBER( rex6000_state::touchscreen_r ) { UINT16 x = ioport("PENX")->read(); UINT16 y = ioport("PENY")->read(); UINT16 battery = ioport("BATTERY")->read(); switch (offset) { case 0x08: return ((ioport("INPUT")->read() & 0x40) ? 0x20 : 0x00) | 0X10; case 0x09: if (m_touchscreen[4] & 0x80) return (battery>>0) & 0xff; else return (y>>0) & 0xff; case 0x0a: if (m_touchscreen[4] & 0x80) return (battery>>8) & 0xff; else return (y>>8) & 0xff; case 0x0b: return (x>>0) & 0xff; case 0x0c: return (x>>8) & 0xff; } return m_touchscreen[offset&0x0f]; } WRITE8_MEMBER( rex6000_state::touchscreen_w ) { m_touchscreen[offset&0x0f] = data; } READ8_MEMBER( rex6000_state::flash_0x0000_r ) { return m_flash[0]->read(offset); } WRITE8_MEMBER( rex6000_state::flash_0x0000_w ) { m_flash[0]->write(offset, data); } READ8_MEMBER( rex6000_state::flash_0x8000_r ) { return m_flash[m_banks[0].type]->read(((m_banks[0].page & 0x7f)<<13) | offset); } WRITE8_MEMBER( rex6000_state::flash_0x8000_w ) { m_flash[m_banks[0].type]->write(((m_banks[0].page & 0x7f)<<13) | offset, data); } READ8_MEMBER( rex6000_state::flash_0xa000_r ) { return m_flash[m_banks[1].type]->read(((m_banks[1].page & 0x7f)<<13) | offset); } WRITE8_MEMBER( rex6000_state::flash_0xa000_w ) { m_flash[m_banks[1].type]->write(((m_banks[1].page & 0x7f)<<13) | offset, data); } static ADDRESS_MAP_START(rex6000_mem, AS_PROGRAM, 8, rex6000_state) ADDRESS_MAP_UNMAP_HIGH AM_RANGE( 0x0000, 0x7fff ) AM_READWRITE(flash_0x0000_r, flash_0x0000_w) AM_RANGE( 0x8000, 0x9fff ) AM_READWRITE(flash_0x8000_r, flash_0x8000_w) AM_RANGE( 0xa000, 0xbfff ) AM_READWRITE(flash_0xa000_r, flash_0xa000_w) AM_RANGE( 0xc000, 0xffff ) AM_RAMBANK("ram") //system RAM ADDRESS_MAP_END static ADDRESS_MAP_START( rex6000_io, AS_IO, 8, rex6000_state) ADDRESS_MAP_UNMAP_HIGH ADDRESS_MAP_GLOBAL_MASK(0xff) AM_RANGE( 0x01, 0x04 ) AM_READWRITE(bankswitch_r, bankswitch_w) AM_RANGE( 0x05, 0x07 ) AM_READWRITE(irq_r, irq_w) AM_RANGE( 0x10, 0x10 ) AM_READ_PORT("INPUT") AM_RANGE( 0x15, 0x19 ) AM_READWRITE(beep_r, beep_w) AM_RANGE( 0x22, 0x23 ) AM_READWRITE(lcd_base_r, lcd_base_w) AM_RANGE( 0x30, 0x3f ) AM_DEVREADWRITE(TC8521_TAG, rp5c01_device, read, write) AM_RANGE( 0x40, 0x47 ) AM_MIRROR(0x08) AM_NOP //SIO AM_RANGE( 0x50, 0x51 ) AM_READWRITE(lcd_io_r, lcd_io_w) AM_RANGE( 0x60, 0x6f ) AM_READWRITE(touchscreen_r, touchscreen_w) //AM_RANGE( 0x00, 0xff ) AM_RAM ADDRESS_MAP_END INPUT_CHANGED_MEMBER(rex6000_state::trigger_irq) { if (!(m_irq_mask & IRQ_FLAG_KEYCHANGE)) { m_irq_flag |= IRQ_FLAG_KEYCHANGE; m_maincpu->set_input_line(0, HOLD_LINE); } } /* Input ports */ INPUT_PORTS_START( rex6000 ) PORT_START("BATTERY") PORT_CONFNAME( 0x03ff, 0x03ff, "Battery Status" ) PORT_CONFSETTING( 0x03ff, "Good" ) PORT_CONFSETTING( 0x0000, "Poor" ) PORT_START("INPUT") PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Home") PORT_CODE(KEYCODE_HOME) PORT_CHANGED_MEMBER(DEVICE_SELF, rex6000_state, trigger_irq, 0) PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Back") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHANGED_MEMBER(DEVICE_SELF, rex6000_state, trigger_irq, 0) PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Select") PORT_CODE(KEYCODE_SPACE) PORT_CHANGED_MEMBER(DEVICE_SELF, rex6000_state, trigger_irq, 0) PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Up") PORT_CODE(KEYCODE_PGUP) PORT_CHANGED_MEMBER(DEVICE_SELF, rex6000_state, trigger_irq, 0) PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_NAME("Down") PORT_CODE(KEYCODE_PGDN) PORT_CHANGED_MEMBER(DEVICE_SELF, rex6000_state, trigger_irq, 0) PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Pen") PORT_CODE(KEYCODE_ENTER) PORT_CODE(MOUSECODE_BUTTON1) PORT_CHANGED_MEMBER(DEVICE_SELF, rex6000_state, trigger_irq, 0) PORT_START("PENX") PORT_BIT(0x3ff, 0x00, IPT_LIGHTGUN_X) PORT_NAME("Pen X") PORT_CROSSHAIR(X, 1, 0, 0) PORT_SENSITIVITY(50) PORT_KEYDELTA(10) PORT_INVERT PORT_START("PENY") PORT_BIT(0x3ff, 0x00, IPT_LIGHTGUN_Y) PORT_NAME("Pen Y") PORT_CROSSHAIR(Y, 1, 0, 0) PORT_SENSITIVITY(50) PORT_KEYDELTA(10) PORT_INVERT INPUT_PORTS_END void rex6000_state::machine_start() { m_flash[0] = machine().device("flash0a"); m_flash[1] = machine().device("flash0b"); m_flash[2] = machine().device("flash1a"); m_flash[3] = machine().device("flash1b"); m_ram_base = m_ram->pointer(); membank("ram")->set_base(m_ram_base + 0x4000); } void rex6000_state::machine_reset() { address_space& program = m_maincpu->space(AS_PROGRAM); program.install_readwrite_handler(0x8000, 0x9fff, 0, 0, read8_delegate(FUNC(rex6000_state::flash_0x8000_r), this), write8_delegate(FUNC(rex6000_state::flash_0x8000_w), this)); program.install_readwrite_handler(0xa000, 0xbfff, 0, 0, read8_delegate(FUNC(rex6000_state::flash_0xa000_r), this), write8_delegate(FUNC(rex6000_state::flash_0xa000_w), this)); m_banks[0].type = 0x04; m_banks[0].type = 0; m_banks[1].type = 0x00; m_banks[1].type = 0; memset(m_ram_base, 0, m_ram->size()); memset(m_bank, 0, sizeof(m_bank)); memset(m_beep_io, 0, sizeof(m_beep_io)); memset(m_lcd_base, 0, sizeof(m_lcd_base)); memset(m_touchscreen, 0, sizeof(m_touchscreen)); m_lcd_enabled = 0; m_lcd_cmd = 0; m_irq_mask = 0; m_irq_flag = 0; m_port6 = 0; m_beep_mode = 0; } UINT32 rex6000_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { UINT16 lcd_bank = MAKE_BANK(m_lcd_base[0], m_lcd_base[1]&0x1f); UINT8 mem_type = identify_bank_type(lcd_bank); if (m_lcd_enabled && mem_type != BANK_UNKNOWN) { for (int y=0; y<120; y++) for (int x=0; x<30; x++) { UINT8 data = 0; if (mem_type == BANK_RAM) { data = (m_ram_base + ((lcd_bank & 0x03)<<13))[y*30 + x]; } else { data = m_flash[mem_type]->space(0).read_byte(((lcd_bank & 0x7f)<<13) | (y*30 + x)); } for (int b=0; b<8; b++) { bitmap.pix16(y, (x * 8) + b) = BIT(data, 7); data <<= 1; } } } else { bitmap.fill(0, cliprect); } return 0; } TIMER_DEVICE_CALLBACK_MEMBER(rex6000_state::irq_timer1) { if (!(m_irq_mask & IRQ_FLAG_IRQ2)) { m_irq_flag |= IRQ_FLAG_IRQ2; m_maincpu->set_input_line(0, HOLD_LINE); } } TIMER_DEVICE_CALLBACK_MEMBER(rex6000_state::irq_timer2) { if (!(m_irq_mask & IRQ_FLAG_IRQ1)) { m_irq_flag |= IRQ_FLAG_IRQ1; m_maincpu->set_input_line(0, HOLD_LINE); } } TIMER_DEVICE_CALLBACK_MEMBER(rex6000_state::sec_timer) { if (!(m_irq_mask & IRQ_FLAG_1HZ)) { m_irq_flag |= IRQ_FLAG_1HZ; m_maincpu->set_input_line(0, HOLD_LINE); } } WRITE_LINE_MEMBER( rex6000_state::alarm_irq ) { if (!(m_irq_mask & IRQ_FLAG_ALARM) && state) { m_irq_flag |= IRQ_FLAG_ALARM; m_maincpu->set_input_line(0, HOLD_LINE); } } PALETTE_INIT_MEMBER(rex6000_state, rex6000) { palette.set_pen_color(0, rgb_t(138, 146, 148)); palette.set_pen_color(1, rgb_t(92, 83, 88)); } QUICKLOAD_LOAD_MEMBER( rex6000_state,rex6000) { static const char magic[] = "ApplicationName:Addin"; address_space& flash = machine().device("flash0b")->memory().space(0); UINT32 img_start = 0; dynamic_buffer data(image.length()); image.fread(data, image.length()); if(strncmp((const char*)&data[0], magic, 21)) return IMAGE_INIT_FAIL; img_start = strlen((const char*)&data[0]) + 5; img_start += 0xa0; //skip the icon (40x32 pixel) for (UINT32 i=0; i