diff options
Diffstat (limited to 'src/mame/machine/vtech2.c')
-rw-r--r-- | src/mame/machine/vtech2.c | 489 |
1 files changed, 489 insertions, 0 deletions
diff --git a/src/mame/machine/vtech2.c b/src/mame/machine/vtech2.c new file mode 100644 index 00000000000..5929b4aab47 --- /dev/null +++ b/src/mame/machine/vtech2.c @@ -0,0 +1,489 @@ +// license:GPL-2.0+ +// copyright-holders:Juergen Buchmueller +/*************************************************************************** + vtech2.c + + machine driver + Juergen Buchmueller <pullmoll@t-online.de> MESS driver, Jan 2000 + Davide Moretti <dave@rimini.com> ROM dump and hardware description + + TODO: + Add loading images from WAV files. + Printer and RS232 support. + Check if the FDC is really the same as in the + Laser 210/310 (aka VZ200/300) series. + +****************************************************************************/ + +#include "emu.h" +#include "includes/vtech2.h" +#include "imagedev/flopdrv.h" +#include "imagedev/cassette.h" +#include "sound/speaker.h" + +/* public */ + +/* static */ + +#define TRKSIZE_VZ 0x9a0 /* arbitrary (actually from analyzing format) */ + +static const UINT8 laser_fdc_wrprot[2] = {0x80, 0x80}; + +/* wrappers for bank #1 to #4 */ +WRITE8_MEMBER(vtech2_state::mwa_bank1 ) { mwa_bank(0,offset,data); } +WRITE8_MEMBER(vtech2_state::mwa_bank2 ) { mwa_bank(1,offset,data); } +WRITE8_MEMBER(vtech2_state::mwa_bank3 ) { mwa_bank(2,offset,data); } +WRITE8_MEMBER(vtech2_state::mwa_bank4 ) { mwa_bank(3,offset,data); } + +/* wrappers for bank #1 to #4 */ +READ8_MEMBER(vtech2_state::mra_bank1 ) { return mra_bank(0,offset); } +READ8_MEMBER(vtech2_state::mra_bank2 ) { return mra_bank(1,offset); } +READ8_MEMBER(vtech2_state::mra_bank3 ) { return mra_bank(2,offset); } +READ8_MEMBER(vtech2_state::mra_bank4 ) { return mra_bank(3,offset); } + +/* read banked memory (plain ROM/RAM) */ +static const char *const mra_bank_hard[4] = +{ + "bank1", /* mapped in 0000-3fff */ + "bank2", /* mapped in 4000-7fff */ + "bank3", /* mapped in 8000-bfff */ + "bank4" /* mapped in c000-ffff */ +}; + +/* write banked memory (plain ROM/RAM) */ +static const char *const mwa_bank_hard[4] = +{ + "bank1", /* mapped in 0000-3fff */ + "bank2", /* mapped in 4000-7fff */ + "bank3", /* mapped in 8000-bfff */ + "bank4" /* mapped in c000-ffff */ +}; + +DRIVER_INIT_MEMBER(vtech2_state,laser) +{ + UINT8 *gfx = memregion("gfx2")->base(); + int i; + + m_laser_track_x2[0] = m_laser_track_x2[1] = 80; + m_laser_fdc_bits = 8; + m_laser_drive = -1; + + for (i = 0; i < 256; i++) + gfx[i] = i; + + m_laser_latch = -1; + m_mem = memregion("maincpu")->base(); + + // check ROM expansion + std::string region_tag; + m_cart_rom = memregion(region_tag.assign(m_cart->tag()).append(GENERIC_ROM_REGION_TAG).c_str()); + + for (i = 0; i < ARRAY_LENGTH(m_laser_bank); i++) + m_laser_bank[i] = -1; +} + + + +void vtech2_state::laser_machine_init(int bank_mask, int video_mask) +{ + m_laser_bank_mask = bank_mask; + m_laser_video_bank = video_mask; + m_videoram = m_mem + m_laser_video_bank * 0x04000; + logerror("laser_machine_init(): bank mask $%04X, video %d [$%05X]\n", m_laser_bank_mask, m_laser_video_bank, m_laser_video_bank * 0x04000); + + for (int i = 0; i < ARRAY_LENGTH(m_laser_bank); i++) + laser_bank_select_w(m_maincpu->space(AS_PROGRAM), i, 0); +} + +void vtech2_state::machine_reset() +{ + /* banks 0 to 3 only, optional ROM extension */ + laser_machine_init(0x00f, 3); +} + +MACHINE_RESET_MEMBER(vtech2_state,laser500) +{ + /* banks 0 to 2, and 4-7 only, optional ROM extension */ + laser_machine_init(0x0f7, 7); +} + +MACHINE_RESET_MEMBER(vtech2_state,laser700) +{ + /* all banks except #3 */ + laser_machine_init(0xff7, 7); +} + + +WRITE8_MEMBER(vtech2_state::laser_bank_select_w) +{ + static const char *const bank_name[16] = { + "ROM lo","ROM hi","MM I/O","Video RAM lo", + "RAM #0","RAM #1","RAM #2","RAM #3", + "RAM #4","RAM #5","RAM #6","RAM #7/Video RAM hi", + "ext ROM #0","ext ROM #1","ext ROM #2","ext ROM #3" + }; + char bank[10]; + offset %= 4; + data &= 15; + + if( data != m_laser_bank[offset] ) + { + m_laser_bank[offset] = data; + logerror("select bank #%d $%02X [$%05X] %s\n", offset+1, data, 0x4000 * (data & 15), bank_name[data]); + + /* memory mapped I/O bank selected? */ + if (data == 2) + { + static read8_delegate mra_bank_soft[] = + { + read8_delegate(FUNC(vtech2_state::mra_bank1), this), + read8_delegate(FUNC(vtech2_state::mra_bank2), this), + read8_delegate(FUNC(vtech2_state::mra_bank3), this), + read8_delegate(FUNC(vtech2_state::mra_bank4), this), + }; + + static write8_delegate mwa_bank_soft[] = + { + write8_delegate(FUNC(vtech2_state::mwa_bank1), this), + write8_delegate(FUNC(vtech2_state::mwa_bank2), this), + write8_delegate(FUNC(vtech2_state::mwa_bank3), this), + write8_delegate(FUNC(vtech2_state::mwa_bank4), this), + }; + + m_maincpu->space(AS_PROGRAM).install_readwrite_handler(offset * 0x4000, offset * 0x4000 + 0x3fff, mra_bank_soft[offset], mwa_bank_soft[offset]); + } + else + { + sprintf(bank, "bank%d", offset + 1); + if (data >= 12 && m_cart_rom && (m_cart_rom->bytes() > (data % 12) * 0x4000)) // Expansion ROM banks + { + membank(bank)->set_base(m_cart_rom->base()+ (data % 12) * 0x4000); + m_maincpu->space(AS_PROGRAM).install_read_bank(offset * 0x4000, offset * 0x4000 + 0x3fff, mra_bank_hard[offset]); + m_maincpu->space(AS_PROGRAM).install_write_bank(offset * 0x4000, offset * 0x4000 + 0x3fff, mwa_bank_hard[offset]); + } + else if (data < 12 && (m_laser_bank_mask & (1 << data))) // ROM/RAM banks + { + // video RAM bank selected? + if (data == m_laser_video_bank) + logerror("select bank #%d VIDEO!\n", offset + 1); + + membank(bank)->set_base(&m_mem[0x4000 * m_laser_bank[offset]]); + m_maincpu->space(AS_PROGRAM).install_read_bank(offset * 0x4000, offset * 0x4000 + 0x3fff, mra_bank_hard[offset]); + m_maincpu->space(AS_PROGRAM).install_write_bank(offset * 0x4000, offset * 0x4000 + 0x3fff, mwa_bank_hard[offset]); + } + else + { + logerror("select bank #%d MASKED!\n", offset + 1); + m_maincpu->space(AS_PROGRAM).nop_readwrite(offset * 0x4000, offset * 0x4000 + 0x3fff); + } + } + + } +} + +/************************************************* + * memory mapped I/O read + * bit function + * 7 not assigned + * 6 column 6 + * 5 column 5 + * 4 column 4 + * 3 column 3 + * 2 column 2 + * 1 column 1 + * 0 column 0 + ************************************************/ +int vtech2_state::mra_bank(int bank, int offs) +{ + UINT8 data = 0x7f; + + /* Laser 500/700 only: keyboard rows A through D */ + if( (offs & 0x00ff) == 0x00ff ) + { + if( (offs & 0x0300) == 0x0000 ) /* keyboard row A */ + { + if( ioport("ROWA")->read() != m_row_a ) + { + m_row_a = ioport("ROWA")->read(); + data &= m_row_a; + } + } + if( (offs & 0x0300) == 0x0100 ) /* keyboard row B */ + { + if( ioport("ROWB")->read() != m_row_b ) + { + m_row_b = ioport("ROWB")->read(); + data &= m_row_b; + } + } + if( (offs & 0x0300) == 0x0200 ) /* keyboard row C */ + { + if( ioport("ROWC")->read() != m_row_c ) + { + m_row_c = ioport("ROWC")->read(); + data &= m_row_c; + } + } + if( (offs & 0x0300) == 0x0300 ) /* keyboard row D */ + { + if( ioport("ROWD")->read() != m_row_d ) + { + m_row_d = ioport("ROWD")->read(); + data &= m_row_d; + } + } + } + else + { + /* All Lasers keyboard rows 0 through 7 */ + if( !(offs & 0x01) ) + data &= ioport("ROW0")->read(); + if( !(offs & 0x02) ) + data &= ioport("ROW1")->read(); + if( !(offs & 0x04) ) + data &= ioport("ROW2")->read(); + if( !(offs & 0x08) ) + data &= ioport("ROW3")->read(); + if( !(offs & 0x10) ) + data &= ioport("ROW4")->read(); + if( !(offs & 0x20) ) + data &= ioport("ROW5")->read(); + if( !(offs & 0x40) ) + data &= ioport("ROW6")->read(); + if( !(offs & 0x80) ) + data &= ioport("ROW7")->read(); + } + + /* BIT 7 - tape input */ + + data |= (m_cassette->input() > +0.02) ? 0x80 : 0; + + return data; +} + +/************************************************* + * memory mapped I/O write + * bit function + * 7-6 not assigned + * 5 speaker B ??? + * 4 ??? + * 3 mode: 1 graphics, 0 text + * 2 cassette out (MSB) + * 1 cassette out (LSB) + * 0 speaker A + ************************************************/ +void vtech2_state::mwa_bank(int bank, int offs, int data) +{ + offs += 0x4000 * m_laser_bank[bank]; + switch (m_laser_bank[bank]) + { + case 0: /* ROM lower 16K */ + case 1: /* ROM upper 16K */ + logerror("bank #%d write to ROM [$%05X] $%02X\n", bank+1, offs, data); + break; + case 2: /* memory mapped output */ + if (data != m_laser_latch) + { + logerror("bank #%d write to I/O [$%05X] $%02X\n", bank+1, offs, data); + /* Toggle between graphics and text modes? */ + if ((data ^ m_laser_latch) & 0x01) + m_speaker->level_w(data & 1); + m_laser_latch = data; + } + m_cassette->output( BIT(data, 2) ? -1.0 : +1.0); + break; + case 12: /* ext. ROM #1 */ + case 13: /* ext. ROM #2 */ + case 14: /* ext. ROM #3 */ + case 15: /* ext. ROM #4 */ + logerror("bank #%d write to ROM [$%05X] $%02X\n", bank+1, offs, data); + break; + default: /* RAM */ + if( m_laser_bank[bank] == m_laser_video_bank && m_mem[offs] != data ) + { + logerror("bank #%d write to videoram [$%05X] $%02X\n", bank+1, offs, data); + } + m_mem[offs] = data; + break; + } +} + + +device_t *vtech2_state::laser_file() +{ + return machine().device(m_laser_drive ? FLOPPY_1 : FLOPPY_0); +} + +void vtech2_state::laser_get_track() +{ + sprintf(m_laser_frame_message, "#%d get track %02d", m_laser_drive, m_laser_track_x2[m_laser_drive]/2); + m_laser_frame_time = 30; + /* drive selected or and image file ok? */ + if( m_laser_drive >= 0 && laser_file() != NULL ) + { + int size, offs; + device_image_interface *image = dynamic_cast<device_image_interface *>(laser_file()); + size = TRKSIZE_VZ; + offs = TRKSIZE_VZ * m_laser_track_x2[m_laser_drive]/2; + image->fseek(offs, SEEK_SET); + size = image->fread(m_laser_fdc_data, size); + logerror("get track @$%05x $%04x bytes\n", offs, size); + } + m_laser_fdc_offs = 0; + m_laser_fdc_write = 0; +} + +void vtech2_state::laser_put_track() +{ + device_image_interface *image = dynamic_cast<device_image_interface *>(laser_file()); + /* drive selected and image file ok? */ + if( m_laser_drive >= 0 && laser_file() != NULL ) + { + int size, offs; + offs = TRKSIZE_VZ * m_laser_track_x2[m_laser_drive]/2; + image->fseek(offs + m_laser_fdc_start, SEEK_SET); + size = image->fwrite(&m_laser_fdc_data[m_laser_fdc_start], m_laser_fdc_write); + logerror("put track @$%05X+$%X $%04X/$%04X bytes\n", offs, m_laser_fdc_start, size, m_laser_fdc_write); + } +} + +#define PHI0(n) (((n)>>0)&1) +#define PHI1(n) (((n)>>1)&1) +#define PHI2(n) (((n)>>2)&1) +#define PHI3(n) (((n)>>3)&1) + +READ8_MEMBER(vtech2_state::laser_fdc_r) +{ + int data = 0xff; + switch( offset ) + { + case 1: /* data (read-only) */ + if( m_laser_fdc_bits > 0 ) + { + if( m_laser_fdc_status & 0x80 ) + m_laser_fdc_bits--; + data = (m_laser_data >> m_laser_fdc_bits) & 0xff; +#if 0 + logerror("laser_fdc_r bits %d%d%d%d%d%d%d%d\n", + (data>>7)&1,(data>>6)&1,(data>>5)&1,(data>>4)&1, + (data>>3)&1,(data>>2)&1,(data>>1)&1,(data>>0)&1 ); +#endif + } + if( m_laser_fdc_bits == 0 ) + { + m_laser_data = m_laser_fdc_data[m_laser_fdc_offs]; + logerror("laser_fdc_r %d : data ($%04X) $%02X\n", offset, m_laser_fdc_offs, m_laser_data); + if( m_laser_fdc_status & 0x80 ) + { + m_laser_fdc_bits = 8; + m_laser_fdc_offs = (m_laser_fdc_offs + 1) % TRKSIZE_FM; + } + m_laser_fdc_status &= ~0x80; + } + break; + case 2: /* polling (read-only) */ + /* fake */ + if( m_laser_drive >= 0 ) + m_laser_fdc_status |= 0x80; + data = m_laser_fdc_status; + break; + case 3: /* write protect status (read-only) */ + if( m_laser_drive >= 0 ) + data = laser_fdc_wrprot[m_laser_drive]; + logerror("laser_fdc_r %d : write_protect $%02X\n", offset, data); + break; + } + return data; +} + +WRITE8_MEMBER(vtech2_state::laser_fdc_w) +{ + int drive; + + switch( offset ) + { + case 0: /* latch (write-only) */ + drive = (data & 0x10) ? 0 : (data & 0x80) ? 1 : -1; + if( drive != m_laser_drive ) + { + m_laser_drive = drive; + if( m_laser_drive >= 0 ) + laser_get_track(); + } + if( m_laser_drive >= 0 ) + { + if( (PHI0(data) && !(PHI1(data) || PHI2(data) || PHI3(data)) && PHI1(m_laser_fdc_latch)) || + (PHI1(data) && !(PHI0(data) || PHI2(data) || PHI3(data)) && PHI2(m_laser_fdc_latch)) || + (PHI2(data) && !(PHI0(data) || PHI1(data) || PHI3(data)) && PHI3(m_laser_fdc_latch)) || + (PHI3(data) && !(PHI0(data) || PHI1(data) || PHI2(data)) && PHI0(m_laser_fdc_latch)) ) + { + if( m_laser_track_x2[m_laser_drive] > 0 ) + m_laser_track_x2[m_laser_drive]--; + logerror("laser_fdc_w(%d) $%02X drive %d: stepout track #%2d.%d\n", offset, data, m_laser_drive, m_laser_track_x2[m_laser_drive]/2,5*(m_laser_track_x2[m_laser_drive]&1)); + if( (m_laser_track_x2[m_laser_drive] & 1) == 0 ) + laser_get_track(); + } + else + if( (PHI0(data) && !(PHI1(data) || PHI2(data) || PHI3(data)) && PHI3(m_laser_fdc_latch)) || + (PHI1(data) && !(PHI0(data) || PHI2(data) || PHI3(data)) && PHI0(m_laser_fdc_latch)) || + (PHI2(data) && !(PHI0(data) || PHI1(data) || PHI3(data)) && PHI1(m_laser_fdc_latch)) || + (PHI3(data) && !(PHI0(data) || PHI1(data) || PHI2(data)) && PHI2(m_laser_fdc_latch)) ) + { + if( m_laser_track_x2[m_laser_drive] < 2*40 ) + m_laser_track_x2[m_laser_drive]++; + logerror("laser_fdc_w(%d) $%02X drive %d: stepin track #%2d.%d\n", offset, data, m_laser_drive, m_laser_track_x2[m_laser_drive]/2,5*(m_laser_track_x2[m_laser_drive]&1)); + if( (m_laser_track_x2[m_laser_drive] & 1) == 0 ) + laser_get_track(); + } + if( (data & 0x40) == 0 ) + { + m_laser_data <<= 1; + if( (m_laser_fdc_latch ^ data) & 0x20 ) + m_laser_data |= 1; + if( (m_laser_fdc_edge ^= 1) == 0 ) + { + if( --m_laser_fdc_bits == 0 ) + { + UINT8 value = 0; + m_laser_data &= 0xffff; + if( m_laser_data & 0x4000 ) value |= 0x80; + if( m_laser_data & 0x1000 ) value |= 0x40; + if( m_laser_data & 0x0400 ) value |= 0x20; + if( m_laser_data & 0x0100 ) value |= 0x10; + if( m_laser_data & 0x0040 ) value |= 0x08; + if( m_laser_data & 0x0010 ) value |= 0x04; + if( m_laser_data & 0x0004 ) value |= 0x02; + if( m_laser_data & 0x0001 ) value |= 0x01; + logerror("laser_fdc_w(%d) data($%04X) $%02X <- $%02X ($%04X)\n", offset, m_laser_fdc_offs, m_laser_fdc_data[m_laser_fdc_offs], value, m_laser_data); + m_laser_fdc_data[m_laser_fdc_offs] = value; + m_laser_fdc_offs = (m_laser_fdc_offs + 1) % TRKSIZE_FM; + m_laser_fdc_write++; + m_laser_fdc_bits = 8; + } + } + } + /* change of write signal? */ + if( (m_laser_fdc_latch ^ data) & 0x40 ) + { + /* falling edge? */ + if ( m_laser_fdc_latch & 0x40 ) + { + sprintf(m_laser_frame_message, "#%d put track %02d", m_laser_drive, m_laser_track_x2[m_laser_drive]/2); + m_laser_frame_time = 30; + m_laser_fdc_start = m_laser_fdc_offs; + m_laser_fdc_edge = 0; + } + else + { + /* data written to track before? */ + if( m_laser_fdc_write ) + laser_put_track(); + } + m_laser_fdc_bits = 8; + m_laser_fdc_write = 0; + } + } + m_laser_fdc_latch = data; + break; + } +} |