diff options
Diffstat (limited to 'src/mame/machine/hp48.c')
-rw-r--r-- | src/mame/machine/hp48.c | 1176 |
1 files changed, 1176 insertions, 0 deletions
diff --git a/src/mame/machine/hp48.c b/src/mame/machine/hp48.c new file mode 100644 index 00000000000..6c6ad253001 --- /dev/null +++ b/src/mame/machine/hp48.c @@ -0,0 +1,1176 @@ +// license:BSD-3-Clause +// copyright-holders:Antoine Mine +/********************************************************************** + + Copyright (C) Antoine Mine' 2008 + + Hewlett Packard HP48 S/SX & G/GX/G+ and HP49 G + +**********************************************************************/ + +#include "emu.h" +#include "sound/dac.h" +#include "cpu/saturn/saturn.h" + +#include "machine/nvram.h" + +#include "includes/hp48.h" + + +/*************************************************************************** + DEBUGGING +***************************************************************************/ + + +#define VERBOSE 0 +#define VERBOSE_SERIAL 0 + +#define LOG(x) do { if (VERBOSE) logerror x; } while (0) +#define LOG_SERIAL(x) do { if (VERBOSE_SERIAL) logerror x; } while (0) + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* state field in hp48_module */ +#define HP48_MODULE_UNCONFIGURED 0 +#define HP48_MODULE_MASK_KNOWN 1 +#define HP48_MODULE_CONFIGURED 2 + +/*************************************************************************** + GLOBAL VARIABLES & CONSTANTS +***************************************************************************/ + +/* current HP48 model */ + +#define HP48_G_SERIES ((m_model==HP48_G) || (m_model==HP48_GX) || (m_model==HP48_GP)) +#define HP48_S_SERIES ((m_model==HP48_S) || (m_model==HP48_SX)) +#define HP48_X_SERIES ((m_model==HP48_SX) || (m_model==HP48_GX)) +#define HP48_GX_MODEL ((m_model==HP48_GX) || (m_model==HP48_GP)) +#define HP49_G_MODEL ((m_model==HP49_G)) + +static const char *const hp48_module_names[6] = +{ "HDW (I/O)", "NCE2 (RAM)", "CE1", "CE2", "NCE3", "NCE1 (ROM)" }; + +/* values returned by C=ID */ +static const UINT8 hp48_module_mask_id[6] = { 0x00, 0x03, 0x05, 0x07, 0x01, 0x00 }; +static const UINT8 hp48_module_addr_id[6] = { 0x19, 0xf4, 0xf6, 0xf8, 0xf2, 0x00 }; + + + + + +/*************************************************************************** + FUNCTIONS +***************************************************************************/ + +void hp48_state::hp48_pulse_irq( int irq_line) +{ + m_maincpu->set_input_line(irq_line, ASSERT_LINE ); + m_maincpu->set_input_line(irq_line, CLEAR_LINE ); +} + + +/* ---------------- serial --------------------- */ + +#define RS232_DELAY attotime::from_usec( 300 ) + +/* end of receive event */ +TIMER_CALLBACK_MEMBER(hp48_state::hp48_rs232_byte_recv_cb) +{ + LOG_SERIAL(( "%f hp48_rs232_byte_recv_cb: end of receive, data=%02x\n", + machine().time().as_double(), param )); + + m_io[0x14] = param & 0xf; /* receive zone */ + m_io[0x15] = param >> 4; + m_io[0x11] &= ~2; /* clear byte receiving */ + m_io[0x11] |= 1; /* set byte received */ + + /* interrupt */ + if ( m_io[0x10] & 2 ) + { + hp48_pulse_irq( SATURN_IRQ_LINE ); + } +} + +/* outside world initiates a receive event */ +void hp48_state::hp48_rs232_start_recv_byte( UINT8 data ) +{ + LOG_SERIAL(( "%f hp48_rs232_start_recv_byte: start receiving, data=%02x\n", + machine().time().as_double(), data )); + + m_io[0x11] |= 2; /* set byte receiving */ + + /* interrupt */ + if ( m_io[0x10] & 1 ) + { + hp48_pulse_irq( SATURN_IRQ_LINE ); + } + + /* schedule end of reception */ + machine().scheduler().timer_set( RS232_DELAY, timer_expired_delegate(FUNC(hp48_state::hp48_rs232_byte_recv_cb),this), data); +} + + +/* end of send event */ +TIMER_CALLBACK_MEMBER(hp48_state::hp48_rs232_byte_sent_cb) +{ + //device_image_interface *xmodem = dynamic_cast<device_image_interface *>(machine().device("rs232_x")); + //device_image_interface *kermit = dynamic_cast<device_image_interface *>(machine().device("rs232_k")); + + LOG_SERIAL(( "%f hp48_rs232_byte_sent_cb: end of send, data=%02x\n", + machine().time().as_double(), param )); + + m_io[0x12] &= ~3; /* clear byte sending and buffer full */ + + /* interrupt */ + if ( m_io[0x10] & 4 ) + { + hp48_pulse_irq( SATURN_IRQ_LINE ); + } + + /* protocol action */ + //if ( xmodem && xmodem->exists() ) xmodem_receive_byte( &xmodem->device(), param ); + //else if ( kermit && kermit->exists() ) kermit_receive_byte( &kermit->device(), param ); +} + +/* CPU initiates a send event */ +void hp48_state::hp48_rs232_send_byte( ) +{ + UINT8 data = HP48_IO_8(0x16); /* byte to send */ + + LOG_SERIAL(( "%s %f hp48_rs232_send_byte: start sending, data=%02x\n", + machine().describe_context(), machine().time().as_double(), data )); + + /* set byte sending and send buffer full */ + m_io[0x12] |= 3; + + /* schedule transmission */ + machine().scheduler().timer_set( RS232_DELAY, timer_expired_delegate(FUNC(hp48_state::hp48_rs232_byte_sent_cb),this), data); +} + + + + +/* ------ Saturn's IN / OUT registers ---------- */ + + +/* CPU sets OUT register (keyboard + beeper) */ +WRITE32_MEMBER( hp48_state::hp48_reg_out ) +{ + LOG(( "%s %f hp48_reg_out: %03x\n", + machine().describe_context(), machine().time().as_double(), data )); + + /* bits 0-8: keyboard lines */ + m_out = data & 0x1ff; + + /* bits 9-10: unused */ + + /* bit 11: beeper */ + m_dac->write_unsigned8((data & 0x800) ? 0x80 : 00 ); +} + +int hp48_state::hp48_get_in( ) +{ + int in = 0; + + /* regular keys */ + if ( (m_out >> 0) & 1 ) in |= ioport( "LINE0" )->read(); + if ( (m_out >> 1) & 1 ) in |= ioport( "LINE1" )->read(); + if ( (m_out >> 2) & 1 ) in |= ioport( "LINE2" )->read(); + if ( (m_out >> 3) & 1 ) in |= ioport( "LINE3" )->read(); + if ( (m_out >> 4) & 1 ) in |= ioport( "LINE4" )->read(); + if ( (m_out >> 5) & 1 ) in |= ioport( "LINE5" )->read(); + if ( (m_out >> 6) & 1 ) in |= ioport( "LINE6" )->read(); + if ( (m_out >> 7) & 1 ) in |= ioport( "LINE7" )->read(); + if ( (m_out >> 8) & 1 ) in |= ioport( "LINE8" )->read(); + + /* on key */ + in |= ioport( "ON" )->read(); + + return in; +} + +/* CPU reads IN register (keyboard) */ +READ32_MEMBER( hp48_state::hp48_reg_in ) +{ + int in = hp48_get_in(); + LOG(( "%s %f hp48_reg_in: %04x\n", + machine().describe_context(), machine().time().as_double(), in )); + return in; +} + +/* key detect */ +void hp48_state::hp48_update_kdn( ) +{ + int in = hp48_get_in(); + + /* interrupt on raising edge */ + if ( in && !m_kdn ) + { + LOG(( "%f hp48_update_kdn: interrupt\n", machine().time().as_double() )); + m_io[0x19] |= 8; /* service request */ + hp48_pulse_irq( SATURN_WAKEUP_LINE ); + hp48_pulse_irq( SATURN_IRQ_LINE ); + } + + m_kdn = (in!=0); +} + +/* periodic keyboard polling, generates an interrupt */ +TIMER_CALLBACK_MEMBER(hp48_state::hp48_kbd_cb) +{ + /* NMI for ON key */ + if ( ioport( "ON" )->read() ) + { + LOG(( "%f hp48_kbd_cb: keyboard interrupt, on key\n", + machine().time().as_double() )); + m_io[0x19] |= 8; /* set service request */ + hp48_pulse_irq( SATURN_WAKEUP_LINE ); + hp48_pulse_irq( SATURN_NMI_LINE ); + return; + } + + /* regular keys */ + hp48_update_kdn(); +} + +/* RSI opcode */ +WRITE_LINE_MEMBER( hp48_state::hp48_rsi ) +{ + LOG(( "%s %f hp48_rsi\n", machine().describe_context(), machine().time().as_double() )); + + /* enables interrupts on key repeat + (normally, there is only one interrupt, when the key is pressed) + */ + m_kdn = 0; +} + + +/* ------------- annonciators ------------ */ + +void hp48_state::hp48_update_annunciators() +{ + /* bit 0: left shift + bit 1: right shift + bit 2: alpha + bit 3: alert + bit 4: busy + bit 5: transmit + bit 7: master enable + */ + int markers = HP48_IO_8(0xb); + output_set_value( "lshift0", (markers & 0x81) == 0x81 ); + output_set_value( "rshift0", (markers & 0x82) == 0x82 ); + output_set_value( "alpha0", (markers & 0x84) == 0x84 ); + output_set_value( "alert0", (markers & 0x88) == 0x88 ); + output_set_value( "busy0", (markers & 0x90) == 0x90 ); + output_set_value( "transmit0", (markers & 0xb0) == 0xb0 ); +} + + +/* ------------- I/O registers ----------- */ + +/* Some part of the I/O registers are simple r/w registers. We store them in hp48_io. + Special cases are registers that: + - have a different meaning on read and write + - perform some action on read / write + */ + +WRITE8_MEMBER(hp48_state::hp48_io_w) +{ + LOG(( "%s %f hp48_io_w: off=%02x data=%x\n", + space.machine().describe_context(), space.machine().time().as_double(), offset, data )); + + switch( offset ) + { + /* CRC register */ + case 0x04: m_crc = (m_crc & 0xfff0) | data; break; + case 0x05: m_crc = (m_crc & 0xff0f) | (data << 4); break; + case 0x06: m_crc = (m_crc & 0xf0ff) | (data << 8); break; + case 0x07: m_crc = (m_crc & 0x0fff) | (data << 12); break; + + /* annunciators */ + case 0x0b: + case 0x0c: + m_io[offset] = data; + hp48_update_annunciators(); + break; + + /* cntrl ROM */ + case 0x29: + { + int old_cntrl = m_io[offset] & 8; + m_io[offset] = data; + if ( old_cntrl != (data & 8) ) + { + hp48_apply_modules(); + } + break; + } + + /* timers */ + case 0x37: m_timer1 = data; break; + case 0x38: m_timer2 = (m_timer2 & 0xfffffff0) | data; break; + case 0x39: m_timer2 = (m_timer2 & 0xffffff0f) | (data << 4); break; + case 0x3a: m_timer2 = (m_timer2 & 0xfffff0ff) | (data << 8); break; + case 0x3b: m_timer2 = (m_timer2 & 0xffff0fff) | (data << 12); break; + case 0x3c: m_timer2 = (m_timer2 & 0xfff0ffff) | (data << 16); break; + case 0x3d: m_timer2 = (m_timer2 & 0xff0fffff) | (data << 20); break; + case 0x3e: m_timer2 = (m_timer2 & 0xf0ffffff) | (data << 24); break; + case 0x3f: m_timer2 = (m_timer2 & 0x0fffffff) | (data << 28); break; + + /* cards */ + case 0x0e: + LOG(( "%s: card control write %02x\n", space.machine().describe_context(), data )); + + /* bit 0: software interrupt */ + if ( data & 1 ) + { + LOG(( "%f hp48_io_w: software interrupt requested\n", + space.machine().time().as_double() )); + hp48_pulse_irq( SATURN_IRQ_LINE ); + data &= ~1; + } + + /* XXX not implemented + bit 1: card test? + */ + + m_io[0x0e] = data; + break; + + case 0x0f: + LOG(( "%s: card info write %02x\n", space.machine().describe_context(), data )); + m_io[0x0f] = data; + break; + + /* serial */ + case 0x13: + m_io[0x11] &= ~4; /* clear error status */ + break; + case 0x16: + /* first nibble of sent data */ + m_io[offset] = data; + break; + case 0x17: + /* second nibble of sent data */ + m_io[offset] = data; + hp48_rs232_send_byte(); + break; + + /* XXX not implemented: + + - 0x0d: RS232c speed: + bits 0-2: speed + 000 = 1200 bauds + 010 = 2400 bauds + 100 = 4800 bauds + 110 = 9600 bauds + bit 3: ? + + - 0x1a: I/R input + bit 0: irq + bit 1: irq enable + bit 2: 1=RS232c mode 0=direct mode + bit 3: receiving + + - 0x1c: I/R output control + bit 0: buffer full + bit 1: transmitting + bit 2: irq enable on buffer empty + bit 3: led on (direct mode) + on HP49 G, flash ROM write enable + + - 0x1d: I/R output buffer + */ + + default: m_io[offset] = data; + } + +} + + +READ8_MEMBER(hp48_state::hp48_io_r) +{ + UINT8 data = 0; + + switch( offset ) + { + /* CRC register */ + case 0x04: data = m_crc & 0xf; break; + case 0x05: data = (m_crc >> 4) & 0xf; break; + case 0x06: data = (m_crc >> 8) & 0xf; break; + case 0x07: data = (m_crc >> 12) & 0xf; break; + + /* battery test */ + case 0x08: + data = 0; + if ( m_io[0x9] & 8 ) /* test enable */ + { + /* XXX not implemented: + bit 3: battery in port 2 + bit 2: battery in port 1 + */ + switch ( ioport( "BATTERY" )->read() ) + { + case 1: data = 2; break; /* low */ + case 2: data = 3; break; /* low | critical */ + } + } + break; + + /* remaining lines in main bitmap */ + case 0x28: + case 0x29: + { + int last_line = HP48_IO_8(0x28) & 0x3f; /* last line of main bitmap before menu */ + int cur_line = space.machine().first_screen()->vpos(); + if ( last_line <= 1 ) last_line = 0x3f; + data = ( cur_line >= 0 && cur_line <= last_line ) ? last_line - cur_line : 0; + if ( offset == 0x29 ) + { + data >>= 4; + data |= HP48_IO_4(0x29) & 0xc; + } + else + { + data &= 0xf; + } + break; + } + + /* timers */ + case 0x37: data = m_timer1; break; + case 0x38: data = m_timer2 & 0xf; break; + case 0x39: data = (m_timer2 >> 4) & 0xf; break; + case 0x3a: data = (m_timer2 >> 8) & 0xf; break; + case 0x3b: data = (m_timer2 >> 12) & 0xf; break; + case 0x3c: data = (m_timer2 >> 16) & 0xf; break; + case 0x3d: data = (m_timer2 >> 20) & 0xf; break; + case 0x3e: data = (m_timer2 >> 24) & 0xf; break; + case 0x3f: data = (m_timer2 >> 28) & 0xf; break; + + /* serial */ + case 0x15: + { + /* second nibble of received data */ + + //device_image_interface *xmodem = dynamic_cast<device_image_interface *>(space.machine().device("rs232_x")); + //device_image_interface *kermit = dynamic_cast<device_image_interface *>(space.machine().device("rs232_k")); + + m_io[0x11] &= ~1; /* clear byte received */ + data = m_io[offset]; + + /* protocol action */ + //if ( xmodem && xmodem->exists() ) xmodem_byte_transmitted( &xmodem->device() ); + //else if ( kermit && kermit->exists() ) kermit_byte_transmitted( &kermit->device() ); + break; + } + + /* cards */ + case 0x0e: /* detection */ + data = m_io[0x0e]; + LOG(( "%s: card control read %02x\n", space.machine().describe_context(), data )); + break; + case 0x0f: /* card info */ + data = 0; + if ( HP48_G_SERIES ) + { + if ( m_port_size[1] ) data |= 1; + if ( m_port_size[0] ) data |= 2; + if ( m_port_size[1] && m_port_write[1] ) data |= 4; + if ( m_port_size[0] && m_port_write[0] ) data |= 8; + } + else + { + if ( m_port_size[0] ) data |= 1; + if ( m_port_size[1] ) data |= 2; + if ( m_port_size[0] && m_port_write[0] ) data |= 4; + if ( m_port_size[1] && m_port_write[1] ) data |= 8; + } + LOG(( "%s: card info read %02x\n", space.machine().describe_context(), data )); + break; + + + default: data = m_io[offset]; + } + + LOG(( "%s %f hp48_io_r: off=%02x data=%x\n", + space.machine().describe_context(), space.machine().time().as_double(), offset, data )); + return data; +} + + +/* ---------- bank switcher --------- */ + +READ8_MEMBER(hp48_state::hp48_bank_r) +{ + /* HP48 GX + bit 0: ignored + bits 2-5: bank number + bit 6: enable + */ + + /* HP49 G + bit 0: ignored + bits 1-2: select bank 0x00000-0x3ffff + bits 3-6: select bank 0x40000-0x7ffff + */ + offset &= 0x7e; + if ( m_bank_switch != offset ) + { + LOG(( "%s %f hp48_bank_r: off=%03x\n", space.machine().describe_context(), space.machine().time().as_double(), offset )); + m_bank_switch = offset; + hp48_apply_modules(); + } + return 0; +} + + +WRITE8_MEMBER(hp48_state::hp49_bank_w) +{ + offset &= 0x7e; + if ( m_bank_switch != offset ) + { + LOG(( "%05x %f hp49_bank_w: off=%03x\n", space.device().safe_pcbase(), space.machine().time().as_double(), offset )); + m_bank_switch = offset; + hp48_apply_modules(); + } +} + + + +/* ---------------- timers --------------- */ + +TIMER_CALLBACK_MEMBER(hp48_state::hp48_timer1_cb) +{ + if ( !(m_io[0x2f] & 1) ) return; /* timer enable */ + + m_timer1 = (m_timer1 - 1) & 0xf; + + /* wake-up on carry */ + if ( (m_io[0x2e] & 4) && (m_timer1 == 0xf) ) + { + LOG(( "wake-up on timer1\n" )); + m_io[0x2e] |= 8; /* set service request */ + m_io[0x18] |= 4; /* set service request */ + hp48_pulse_irq( SATURN_WAKEUP_LINE ); + } + /* interrupt on carry */ + if ( (m_io[0x2e] & 2) && (m_timer1 == 0xf) ) + { + LOG(( "generate timer1 interrupt\n" )); + m_io[0x2e] |= 8; /* set service request */ + m_io[0x18] |= 4; /* set service request */ + hp48_pulse_irq( SATURN_NMI_LINE ); + } +} + +TIMER_CALLBACK_MEMBER(hp48_state::hp48_timer2_cb) +{ + if ( !(m_io[0x2f] & 1) ) return; /* timer enable */ + + m_timer2 = (m_timer2 - 1) & 0xffffffff; + + /* wake-up on carry */ + if ( (m_io[0x2f] & 4) && (m_timer2 == 0xffffffff) ) + { + LOG(( "wake-up on timer2\n" )); + m_io[0x2f] |= 8; /* set service request */ + m_io[0x18] |= 4; /* set service request */ + hp48_pulse_irq( SATURN_WAKEUP_LINE ); + } + /* interrupt on carry */ + if ( (m_io[0x2f] & 2) && (m_timer2 == 0xffffffff) ) + { + LOG(( "generate timer2 interrupt\n" )); + m_io[0x2f] |= 8; /* set service request */ + m_io[0x18] |= 4; /* set service request */ + hp48_pulse_irq( SATURN_NMI_LINE ); + } +} + + + + +/* --------- memory controller ----------- */ + +/* + Clark (S series) and York (G series) CPUs have 6 daisy-chained modules + + + <-- highest --------- priority ------------- lowest --> + + CPU --------------------------------------------------- + | | | | | | + HDW NCE2 CE1 CE2 NCE3 NCE1 + + + However, controller usage is different in both series: + + + controller 48 S series 48 G series 49 G series + (Clark CPU) (York CPU) (York CPU) + + HDW 32B I/O RAM 32B I/O RAM 32B I/O RAM + NCE2 32KB RAM 32/128KB RAM 256KB RAM + CE1 port1 bank switcher bank switcher + CE2 port2 port1 128KB RAM + NCE3 unused port2 128KB RAM + NCE1 256KB ROM 512KB ROM 2MB flash ROM + + + - NCE1 (ROM) cannot be configured, it is always visible at addresses + 00000-7ffff not covered by higher priority modules. + + - only the address of HDW (I/O) can be configured, its size is constant + (64 nibbles) + + - other modules can have their address & size set + + */ + + +/* remap all modules according to hp48_modules */ +void hp48_state::hp48_apply_modules() +{ + int i; + int nce3_enable = 1; + address_space& space = m_maincpu->space(AS_PROGRAM); + + m_io_addr = 0x100000; + + /* NCE1 (ROM) is a bit special, so treat it separately */ + space.unmap_readwrite( 0, 0xfffff, 0, 0 ); + if ( HP49_G_MODEL ) + { + int bank_lo = (m_bank_switch >> 5) & 3; + int bank_hi = (m_bank_switch >> 1) & 15; + LOG(( "hp48_apply_modules: low ROM bank is %i\n", bank_lo )); + LOG(( "hp48_apply_modules: high ROM bank is %i\n", bank_hi )); + space.install_read_bank( 0x00000, 0x3ffff, 0, 0x80000, "bank5" ); + space.install_read_bank( 0x40000, 0x7ffff, 0, 0x80000, "bank6" ); + if ( m_rom ) + { + membank("bank5")->set_base( m_rom + bank_lo * 0x40000 ); + membank("bank6")->set_base( m_rom + bank_hi * 0x40000 ); + } + } + else if ( HP48_G_SERIES ) + { + /* port 2 bank switch */ + if ( m_port_size[1] > 0 ) + { + int off = (m_bank_switch << 16) % m_port_size[1]; + LOG(( "hp48_apply_modules: port 2 offset is %i\n", off )); + m_modules[HP48_NCE3].data = m_port_data[1] + off; + } + + /* ROM A19 (hi 256 KB) / NCE3 (port 2) control switch */ + if ( m_io[0x29] & 8 ) + { + /* A19 */ + LOG(( "hp48_apply_modules: A19 enabled, NCE3 disabled\n" )); + nce3_enable = 0; + space.install_read_bank( 0, 0xfffff, 0, 0, "bank5" ); + } + else + { + /* NCE3 */ + nce3_enable = m_bank_switch >> 6; + LOG(( "hp48_apply_modules: A19 disabled, NCE3 %s\n", nce3_enable ? "enabled" : "disabled" )); + space.install_read_bank( 0, 0x7ffff, 0, 0x80000, "bank5" ); + } + if ( m_rom ) + membank("bank5")->set_base( m_rom ); + } + else + { + space.install_read_bank( 0, 0x7ffff, 0, 0x80000, "bank5" ); + if ( m_rom ) + membank("bank5")->set_base( m_rom ); + } + + + /* from lowest to highest priority */ + for ( i = 4; i >= 0; i-- ) + { + UINT32 select_mask = m_modules[i].mask; + UINT32 nselect_mask = ~select_mask & 0xfffff; + UINT32 base = m_modules[i].base; + UINT32 off_mask = m_modules[i].off_mask; + UINT32 mirror = nselect_mask & ~off_mask; + UINT32 end = base + (off_mask & nselect_mask); + char bank[10]; + sprintf(bank,"bank%d",i); + + if ( m_modules[i].state != HP48_MODULE_CONFIGURED ) continue; + + if ( (i == 4) && !nce3_enable ) continue; + + /* our code assumes that the 20-bit select_mask is all 1s followed by all 0s */ + if ( nselect_mask & (nselect_mask+1) ) + { + logerror( "hp48_apply_modules: invalid mask %05x for module %s\n", + select_mask, hp48_module_names[i] ); + continue; + } + + if (m_modules[i].data) + space.install_read_bank( base, end, 0, mirror, bank ); + else + { + if (!m_modules[i].read.isnull()) + space.install_read_handler( base, end, 0, mirror, m_modules[i].read ); + } + + if (m_modules[i].isnop) + space.nop_write(base, end, 0, mirror); + else + { + if (m_modules[i].data) + { + space.install_write_bank( base, end, 0, mirror, bank ); + } + else + { + if (!m_modules[i].write.isnull()) + space.install_write_handler( base, end, 0, mirror, m_modules[i].write ); + } + } + + LOG(( "hp48_apply_modules: module %s configured at %05x-%05x, mirror %05x\n", + hp48_module_names[i], base, end, mirror )); + + if ( m_modules[i].data ) + { + membank( bank )->set_base( m_modules[i].data ); + } + + if ( i == 0 ) + { + m_io_addr = base; + } + } +} + + +/* reset the configuration */ +void hp48_state::hp48_reset_modules( ) +{ + int i; + /* fixed size for HDW */ + m_modules[HP48_HDW].state = HP48_MODULE_MASK_KNOWN; + m_modules[HP48_HDW].mask = 0xfffc0; + /* unconfigure NCE2, CE1, CE2, NCE3 */ + for ( i = 1; i < 5; i++ ) + { + m_modules[i].state = HP48_MODULE_UNCONFIGURED; + } + + /* fixed configuration for NCE1 */ + m_modules[HP48_NCE1].state = HP48_MODULE_CONFIGURED; + m_modules[HP48_NCE1].base = 0; + m_modules[HP48_NCE1].mask = 0; + + hp48_apply_modules(); +} + + +/* RESET opcode */ +WRITE_LINE_MEMBER( hp48_state::hp48_mem_reset ) +{ + LOG(( "%s %f hp48_mem_reset\n", machine().describe_context(), machine().time().as_double() )); + hp48_reset_modules(); +} + + +/* CONFIG opcode */ +WRITE32_MEMBER( hp48_state::hp48_mem_config ) +{ + int i; + + LOG(( "%s %f hp48_mem_config: %05x\n", machine().describe_context(), machine().time().as_double(), data )); + + /* find the highest priority unconfigured module (except non-configurable NCE1)... */ + for ( i = 0; i < 5; i++ ) + { + /* ... first call sets the address mask */ + if ( m_modules[i].state == HP48_MODULE_UNCONFIGURED ) + { + m_modules[i].mask = data & 0xff000; + m_modules[i].state = HP48_MODULE_MASK_KNOWN; + break; + } + + /* ... second call sets the base address */ + if ( m_modules[i].state == HP48_MODULE_MASK_KNOWN ) + { + m_modules[i].base = data & m_modules[i].mask; + m_modules[i].state = HP48_MODULE_CONFIGURED; + LOG(( "hp48_mem_config: module %s configured base=%05x, mask=%05x\n", + hp48_module_names[i], m_modules[i].base, m_modules[i].mask )); + hp48_apply_modules(); + break; + } + } +} + + +/* UNCFG opcode */ +WRITE32_MEMBER( hp48_state::hp48_mem_unconfig ) +{ + int i; + LOG(( "%s %f hp48_mem_unconfig: %05x\n", machine().describe_context(), machine().time().as_double(), data )); + + /* find the highest priority fully configured module at address v (except NCE1)... */ + for ( i = 0; i < 5; i++ ) + { + /* ... and unconfigure it */ + if ( m_modules[i].state == HP48_MODULE_CONFIGURED && + (m_modules[i].base == (data & m_modules[i].mask)) ) + { + m_modules[i].state = i> 0 ? HP48_MODULE_UNCONFIGURED : HP48_MODULE_MASK_KNOWN; + LOG(( "hp48_mem_unconfig: module %s\n", hp48_module_names[i] )); + hp48_apply_modules(); + break; + } + } +} + + +/* C=ID opcode */ +READ32_MEMBER( hp48_state::hp48_mem_id ) +{ + int i; + int data = 0; /* 0 = everything is configured */ + + /* find the highest priority unconfigured module (except NCE1)... */ + for ( i = 0; i < 5; i++ ) + { + /* ... mask need to be configured */ + if ( m_modules[i].state == HP48_MODULE_UNCONFIGURED ) + { + data = hp48_module_mask_id[i] | (m_modules[i].mask & ~0xff); + break; + } + + /* ... address need to be configured */ + if ( m_modules[i].state == HP48_MODULE_MASK_KNOWN ) + { + data = hp48_module_addr_id[i] | (m_modules[i].base & ~0x3f); + break; + } + } + + LOG(( "%s %f hp48_mem_id = %02x\n", + machine().describe_context(), machine().time().as_double(), data )); + + return data; /* everything is configured */ +} + + + +/* --------- CRC ---------- */ + +/* each memory read by the CPU updates the internal CRC state */ +WRITE32_MEMBER( hp48_state::hp48_mem_crc ) +{ + /* no CRC for I/O RAM */ + if ( offset >= m_io_addr && offset < m_io_addr + 0x40 ) return; + + m_crc = (m_crc >> 4) ^ (((m_crc ^ data) & 0xf) * 0x1081); +} + + + +/* ------ utilities ------- */ + + +/* decodes size bytes into 2*size nibbles (least significant first) */ +void hp48_state::hp48_decode_nibble( UINT8* dst, UINT8* src, int size ) +{ + int i; + for ( i=size-1; i >= 0; i-- ) + { + dst[2*i+1] = src[i] >> 4; + dst[2*i] = src[i] & 0xf; + } +} + +/* inverse of hp48_decode_nibble */ +void hp48_state::hp48_encode_nibble( UINT8* dst, UINT8* src, int size ) +{ + int i; + for ( i=0; i < size; i++ ) + { + dst[i] = (src[2*i] & 0xf) | (src[2*i+1] << 4); + } +} + + + +/* ----- card images ------ */ +const device_type HP48_PORT = &device_creator<hp48_port_image_device>; + +/* helper for load and create */ +void hp48_port_image_device::hp48_fill_port() +{ + hp48_state *state = machine().driver_data<hp48_state>(); + int size = state->m_port_size[m_port]; + LOG(( "hp48_fill_port: %s module=%i size=%i rw=%i\n", tag(), m_module, size, state->m_port_write[m_port] )); + state->m_port_data[m_port] = global_alloc_array(UINT8, 2 * size); + memset( state->m_port_data[m_port], 0, 2 * size ); + state->m_modules[m_module].off_mask = 2 * (( size > 128 * 1024 ) ? 128 * 1024 : size) - 1; + state->m_modules[m_module].read = read8_delegate(); + state->m_modules[m_module].write = write8_delegate(); + state->m_modules[m_module].isnop = state->m_port_write[m_port] ? 0 : 1; + state->m_modules[m_module].data = state->m_port_data[m_port]; + state->hp48_apply_modules(); +} + +/* helper for start and unload */ +void hp48_port_image_device::hp48_unfill_port() +{ + hp48_state *state = machine().driver_data<hp48_state>(); + LOG(( "hp48_unfill_port\n" )); + state->m_modules[m_module].off_mask = 0x00fff; /* 2 KB */ + state->m_modules[m_module].read = read8_delegate(); + state->m_modules[m_module].write = write8_delegate(); + state->m_modules[m_module].data = NULL; + state->m_modules[m_module].isnop = 1; + state->m_port_size[m_port] = 0; +} + + +bool hp48_port_image_device::call_load() +{ + hp48_state *state = machine().driver_data<hp48_state>(); + int size = length(); + if ( size == 0 ) size = m_max_size; /* default size */ + LOG(( "hp48_port_image load: size=%i\n", size )); + + /* check size */ + if ( (size < 32*1024) || (size > m_max_size) || (size & (size-1)) ) + { + logerror( "hp48: image size for %s should be a power of two between %i and %i\n", tag(), 32*1024, m_max_size ); + return IMAGE_INIT_FAIL; + } + + state->m_port_size[m_port] = size; + state->m_port_write[m_port] = !is_readonly(); + hp48_fill_port( ); + fread(state->m_port_data[m_port], state->m_port_size[m_port] ); + state->hp48_decode_nibble( state->m_port_data[m_port], state->m_port_data[m_port], state->m_port_size[m_port] ); + return IMAGE_INIT_PASS; +} + +bool hp48_port_image_device::call_create(int format_type, option_resolution *format_options) +{ + hp48_state *state = machine().driver_data<hp48_state>(); + int size = m_max_size; + LOG(( "hp48_port_image create: size=%i\n", size )); + /* XXX defaults to max_size; get user-specified size instead */ + + /* check size */ + /* size must be a power of 2 between 32K and max_size */ + if ( (size < 32*1024) || (size > m_max_size) || (size & (size-1)) ) + { + logerror( "hp48: image size for %s should be a power of two between %i and %i\n", tag(), 32*1024, m_max_size ); + return IMAGE_INIT_FAIL; + } + + state->m_port_size[m_port] = size; + state->m_port_write[m_port] = 1; + hp48_fill_port(); + return IMAGE_INIT_PASS; +} + +void hp48_port_image_device::call_unload() +{ + hp48_state *state = machine().driver_data<hp48_state>(); + LOG(( "hp48_port image unload: %s size=%i rw=%i\n", + tag(), state->m_port_size[m_port], state->m_port_write[m_port] )); + if ( state->m_port_write[m_port] ) + { + state->hp48_encode_nibble( state->m_port_data[m_port], state->m_port_data[m_port], state->m_port_size[m_port] ); + fseek( 0, SEEK_SET ); + fwrite( state->m_port_data[m_port], state->m_port_size[m_port] ); + } + global_free_array( state->m_port_data[m_port] ); + hp48_unfill_port(); + state->hp48_apply_modules(); +} + +void hp48_port_image_device::device_start() +{ + LOG(("hp48_port_image start\n")); + hp48_unfill_port(); +} + +hp48_port_image_device::hp48_port_image_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, HP48_PORT, "HP48 memory card", tag, owner, clock, "hp48_port_image", __FILE__), + device_image_interface(mconfig, *this) +{ +} + +void hp48_port_image_device::device_config_complete() +{ + update_names(HP48_PORT, "port", "p"); +} + +/*************************************************************************** + MACHINES +***************************************************************************/ + +DRIVER_INIT_MEMBER(hp48_state,hp48) +{ + int i; + LOG(( "hp48: driver init called\n" )); + for ( i = 0; i < 6; i++ ) + { + m_modules[i].off_mask = 0x00fff; /* 2 KB */ + m_modules[i].read = read8_delegate(); + m_modules[i].write = write8_delegate(); + m_modules[i].data = NULL; + m_modules[i].isnop = 0; + } + m_port_size[0] = 0; + m_port_size[1] = 0; + m_rom = NULL; +} + +void hp48_state::machine_reset() +{ + LOG(( "hp48: machine reset called\n" )); + m_bank_switch = 0; + hp48_reset_modules(); + hp48_update_annunciators(); +} + +void hp48_state::hp48_machine_start( hp48_models model ) +{ + UINT8 *ram; + int ram_size, rom_size, i; + + LOG(( "hp48_machine_start: model %i\n", model )); + + m_model = model; + + /* internal RAM */ + ram_size = + HP49_G_MODEL ? (512 * 1024) : + HP48_GX_MODEL ? (128 * 1024) : (32 * 1024); + + ram = auto_alloc_array(machine(), UINT8, 2 * ram_size); + machine().device<nvram_device>("nvram")->set_base(ram, 2 * ram_size); + + + /* ROM load */ + rom_size = + HP49_G_MODEL ? (2048 * 1024) : + HP48_S_SERIES ? (256 * 1024) : (512 * 1024); + m_rom = auto_alloc_array(machine(), UINT8, 2 * rom_size); + hp48_decode_nibble( m_rom, memregion( "maincpu" )->base(), rom_size ); + + /* init state */ + memset( ram, 0, 2 * ram_size ); + memset( m_io, 0, sizeof( m_io ) ); + m_out = 0; + m_kdn = 0; + m_crc = 0; + m_timer1 = 0; + m_timer2 = 0; + m_bank_switch = 0; + + /* I/O RAM */ + m_modules[HP48_HDW].off_mask = 0x0003f; /* 32 B */ + m_modules[HP48_HDW].read = read8_delegate(FUNC(hp48_state::hp48_io_r),this); + m_modules[HP48_HDW].write = write8_delegate(FUNC(hp48_state::hp48_io_w),this); + + /* internal RAM */ + if ( HP49_G_MODEL ) + { + m_modules[HP48_NCE2].off_mask = 2 * 256 * 1024 - 1; + m_modules[HP48_NCE2].data = ram; + m_modules[HP48_CE2].off_mask = 2 * 128 * 1024 - 1; + m_modules[HP48_CE2].data = ram + 2 * 256 * 1024; + m_modules[HP48_NCE3].off_mask = 2 * 128 * 1024 - 1; + m_modules[HP48_NCE3].data = ram + 2 * (128+256) * 1024; + } + else + { + m_modules[HP48_NCE2].off_mask = 2 * ram_size - 1; + m_modules[HP48_NCE2].data = ram; + } + + /* bank switcher */ + if ( HP48_G_SERIES ) + { + m_modules[HP48_CE1].off_mask = 0x00fff; /* 2 KB */ + m_modules[HP48_CE1].read = read8_delegate(FUNC(hp48_state::hp48_bank_r),this); + m_modules[HP48_CE1].write = HP49_G_MODEL ? write8_delegate(FUNC(hp48_state::hp49_bank_w),this) : write8_delegate(); + } + + /* timers */ + machine().scheduler().timer_pulse(attotime::from_hz( 16 ), timer_expired_delegate(FUNC(hp48_state::hp48_timer1_cb),this)); + machine().scheduler().timer_pulse(attotime::from_hz( 8192 ), timer_expired_delegate(FUNC(hp48_state::hp48_timer2_cb),this)); + + /* 1ms keyboard polling */ + machine().scheduler().timer_pulse(attotime::from_msec( 1 ), timer_expired_delegate(FUNC(hp48_state::hp48_kbd_cb),this)); + + /* save state */ + save_item(NAME(m_out) ); + save_item(NAME(m_kdn) ); + save_item(NAME(m_io_addr) ); + save_item(NAME(m_crc) ); + save_item(NAME(m_timer1) ); + save_item(NAME(m_timer2) ); + save_item(NAME(m_bank_switch) ); + for ( i = 0; i < 6; i++ ) + { + save_item(m_modules[i].state, "globals/m_modules[i].state", i); + save_item(m_modules[i].base, "globals/m_modules[i].base", i); + save_item(m_modules[i].mask, "globals/m_modules[i].mask", i); + } + save_item(NAME(m_io) ); + machine().save().register_postload( save_prepost_delegate(FUNC(hp48_state::hp48_update_annunciators), this )); + machine().save().register_postload( save_prepost_delegate(FUNC(hp48_state::hp48_apply_modules), this )); + +} + + +MACHINE_START_MEMBER(hp48_state,hp48s) +{ + hp48_machine_start( HP48_S ); +} + + +MACHINE_START_MEMBER(hp48_state,hp48sx) +{ + hp48_machine_start( HP48_SX ); +} + + +MACHINE_START_MEMBER(hp48_state,hp48g) +{ + hp48_machine_start( HP48_G ); +} + +MACHINE_START_MEMBER(hp48_state,hp48gx) +{ + hp48_machine_start( HP48_GX ); +} + +MACHINE_START_MEMBER(hp48_state,hp48gp) +{ + hp48_machine_start( HP48_GP ); +} + +MACHINE_START_MEMBER(hp48_state,hp49g) +{ + hp48_machine_start( HP49_G ); +} |