/* zxatasp.c: ZXATASP interface routines Copyright (c) 2003-2005 Garry Lancaster, 2004-2005 Philip Kendall $Id: zxatasp.c,v 1.15 2007/02/02 16:21:52 pak21 Exp $ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Author contact information: E-mail: Philip Kendall */ #include #include #include #include "ide.h" #include "machine.h" #include "memory.h" #include "periph.h" #include "settings.h" #include "ui/ui.h" #include "zxatasp.h" /* TBD: Allow memory size selection (128K/512K) TBD: Should support for secondary channel be removed? No software ever supported it, and v2.0+ boards don't have it. */ /* Private function prototypes */ static libspectrum_byte zxatasp_portA_read( libspectrum_word port, int *attached ); static void zxatasp_portA_write( libspectrum_word port, libspectrum_byte data ); static libspectrum_byte zxatasp_portB_read( libspectrum_word port, int *attached ); static void zxatasp_portB_write( libspectrum_word port, libspectrum_byte data ); static libspectrum_byte zxatasp_portC_read( libspectrum_word port, int *attached ); static void zxatasp_portC_write( libspectrum_word port, libspectrum_byte data ); static libspectrum_byte zxatasp_control_read( libspectrum_word port, int *attached ); static void zxatasp_control_write( libspectrum_word port, libspectrum_byte data ); static void zxatasp_resetports( void ); static void set_zxatasp_bank( int bank ); static void zxatasp_readide( libspectrum_ide_channel *chn, libspectrum_ide_register idereg ); static void zxatasp_writeide( libspectrum_ide_channel *chn, libspectrum_ide_register idereg ); /* Data */ const periph_t zxatasp_peripherals[] = { { 0x039f, 0x009f, zxatasp_portA_read, zxatasp_portA_write }, { 0x039f, 0x019f, zxatasp_portB_read, zxatasp_portB_write }, { 0x039f, 0x029f, zxatasp_portC_read, zxatasp_portC_write }, { 0x039f, 0x039f, zxatasp_control_read, zxatasp_control_write }, }; const size_t zxatasp_peripherals_count = sizeof( zxatasp_peripherals ) / sizeof( periph_t ); static libspectrum_byte zxatasp_control; static libspectrum_byte zxatasp_portA; static libspectrum_byte zxatasp_portB; static libspectrum_byte zxatasp_portC; static size_t current_page; static libspectrum_ide_channel *zxatasp_idechn0; static libspectrum_ide_channel *zxatasp_idechn1; #define ZXATASP_PAGES 32 #define ZXATASP_PAGE_LENGTH 0x4000 static libspectrum_byte ZXATASPMEM[ ZXATASP_PAGES ][ ZXATASP_PAGE_LENGTH ]; static const size_t ZXATASP_NOT_PAGED = 0xff; /* We're ignoring all mode bits and only emulating mode 0, basic I/O */ static const libspectrum_byte MC8255_PORT_C_LOW_IO = 0x01; static const libspectrum_byte MC8255_PORT_B_IO = 0x02; static const libspectrum_byte MC8255_PORT_C_HI_IO = 0x08; static const libspectrum_byte MC8255_PORT_A_IO = 0x10; static const libspectrum_byte MC8255_SETMODE = 0x80; static const libspectrum_byte ZXATASP_IDE_REG = 0x07; static const libspectrum_byte ZXATASP_RAM_BANK = 0x1f; static const libspectrum_byte ZXATASP_IDE_WR = 0x08; static const libspectrum_byte ZXATASP_IDE_RD = 0x10; static const libspectrum_byte ZXATASP_IDE_PRIMARY = 0x20; static const libspectrum_byte ZXATASP_RAM_LATCH = 0x40; static const libspectrum_byte ZXATASP_RAM_DISABLE = 0x80; static const libspectrum_byte ZXATASP_IDE_SECONDARY = 0x80; #define ZXATASP_READ_PRIMARY( x ) \ ( ( x & ( ZXATASP_IDE_PRIMARY | ZXATASP_RAM_LATCH | \ ZXATASP_IDE_RD | ZXATASP_IDE_WR ) ) == \ ( ZXATASP_IDE_PRIMARY | ZXATASP_IDE_RD ) ) #define ZXATASP_WRITE_PRIMARY( x ) \ ( ( x & ( ZXATASP_IDE_PRIMARY | ZXATASP_RAM_LATCH | \ ZXATASP_IDE_RD | ZXATASP_IDE_WR ) ) == \ ( ZXATASP_IDE_PRIMARY | ZXATASP_IDE_WR ) ) #define ZXATASP_READ_SECONDARY( x ) \ ( ( x & ( ZXATASP_IDE_SECONDARY | ZXATASP_RAM_LATCH | \ ZXATASP_IDE_RD | ZXATASP_IDE_WR ) ) == \ ( ZXATASP_IDE_SECONDARY | ZXATASP_IDE_RD ) ) #define ZXATASP_WRITE_SECONDARY( x ) \ ( ( x & ( ZXATASP_IDE_SECONDARY | ZXATASP_RAM_LATCH | \ ZXATASP_IDE_RD | ZXATASP_IDE_WR ) ) == \ ( ZXATASP_IDE_SECONDARY | ZXATASP_IDE_WR ) ) /* Housekeeping functions */ int zxatasp_init( void ) { int error; error = libspectrum_ide_alloc( &zxatasp_idechn0, LIBSPECTRUM_IDE_DATA16 ); if( error ) return error; error = libspectrum_ide_alloc( &zxatasp_idechn1, LIBSPECTRUM_IDE_DATA16 ); if( error ) return error; ui_menu_activate( UI_MENU_ITEM_MEDIA_IDE_ZXATASP_MASTER_EJECT, 0 ); ui_menu_activate( UI_MENU_ITEM_MEDIA_IDE_ZXATASP_SLAVE_EJECT, 0 ); if( settings_current.zxatasp_master_file ) { error = libspectrum_ide_insert( zxatasp_idechn0, LIBSPECTRUM_IDE_MASTER, settings_current.zxatasp_master_file ); if( error ) return error; ui_menu_activate( UI_MENU_ITEM_MEDIA_IDE_ZXATASP_MASTER_EJECT, 1 ); } if( settings_current.zxatasp_slave_file ) { error = libspectrum_ide_insert( zxatasp_idechn0, LIBSPECTRUM_IDE_SLAVE, settings_current.zxatasp_slave_file ); if( error ) return error; ui_menu_activate( UI_MENU_ITEM_MEDIA_IDE_ZXATASP_SLAVE_EJECT, 1 ); } return error; } int zxatasp_end( void ) { int error; error = libspectrum_ide_free( zxatasp_idechn0 ); error = libspectrum_ide_free( zxatasp_idechn1 ) || error; return error; } void zxatasp_reset( void ) { if( !settings_current.zxatasp_active ) return; machine_current->ram.romcs = 1; set_zxatasp_bank( 0 ); current_page = 0; machine_current->memory_map(); zxatasp_control = MC8255_SETMODE | MC8255_PORT_A_IO | MC8255_PORT_B_IO | MC8255_PORT_C_HI_IO | MC8255_PORT_C_LOW_IO; zxatasp_resetports(); libspectrum_ide_reset( zxatasp_idechn0 ); libspectrum_ide_reset( zxatasp_idechn1 ); } int zxatasp_insert( const char *filename, libspectrum_ide_unit unit ) { char **setting; ui_menu_item item; switch( unit ) { case LIBSPECTRUM_IDE_MASTER: setting = &settings_current.zxatasp_master_file; item = UI_MENU_ITEM_MEDIA_IDE_ZXATASP_MASTER_EJECT; break; case LIBSPECTRUM_IDE_SLAVE: setting = &settings_current.zxatasp_slave_file; item = UI_MENU_ITEM_MEDIA_IDE_ZXATASP_SLAVE_EJECT; break; default: return 1; } return ide_insert( filename, zxatasp_idechn0, unit, zxatasp_commit, setting, item ); } int zxatasp_commit( libspectrum_ide_unit unit ) { int error; error = libspectrum_ide_commit( zxatasp_idechn0, unit ); return error; } int zxatasp_eject( libspectrum_ide_unit unit ) { char **setting; ui_menu_item item; switch( unit ) { case LIBSPECTRUM_IDE_MASTER: setting = &settings_current.zxatasp_master_file; item = UI_MENU_ITEM_MEDIA_IDE_ZXATASP_MASTER_EJECT; break; case LIBSPECTRUM_IDE_SLAVE: setting = &settings_current.zxatasp_slave_file; item = UI_MENU_ITEM_MEDIA_IDE_ZXATASP_SLAVE_EJECT; break; default: return 1; } return ide_eject( zxatasp_idechn0, unit, zxatasp_commit, setting, item ); } /* Port read/writes */ libspectrum_byte zxatasp_portA_read( libspectrum_word port GCC_UNUSED, int *attached ) { if( !settings_current.zxatasp_active ) return 0xff; *attached = 1; return zxatasp_portA; } static void zxatasp_portA_write( libspectrum_word port GCC_UNUSED, libspectrum_byte data ) { if( !settings_current.zxatasp_active ) return; if( !( zxatasp_control & MC8255_PORT_A_IO ) ) zxatasp_portA = data; } libspectrum_byte zxatasp_portB_read( libspectrum_word port GCC_UNUSED, int *attached ) { if( !settings_current.zxatasp_active ) return 0xff; *attached = 1; return zxatasp_portB; } static void zxatasp_portB_write( libspectrum_word port GCC_UNUSED, libspectrum_byte data ) { if( !settings_current.zxatasp_active ) return; if( !( zxatasp_control & MC8255_PORT_B_IO ) ) zxatasp_portB = data; } libspectrum_byte zxatasp_portC_read( libspectrum_word port GCC_UNUSED, int *attached ) { if( !settings_current.zxatasp_active ) return 0xff; *attached = 1; return zxatasp_portC; } static void zxatasp_portC_write( libspectrum_word port GCC_UNUSED, libspectrum_byte data ) { libspectrum_byte oldC = zxatasp_portC; libspectrum_byte newC; if( !settings_current.zxatasp_active ) return; /* Determine new port C value, dependent on I/O modes */ newC = ( zxatasp_control & MC8255_PORT_C_LOW_IO ) ? ( oldC & 0x0f ) : ( data & 0x0f ); newC |= ( zxatasp_control & MC8255_PORT_C_HI_IO ) ? ( oldC & 0xf0 ) : ( data & 0xf0 ); /* Set the new port value */ zxatasp_portC = newC; /* No action can occur if high part of port C is in input mode */ if( zxatasp_control & MC8255_PORT_C_HI_IO ) return; /* Check for any I/O action */ if( ( ZXATASP_READ_PRIMARY( newC ) ) & !( ZXATASP_READ_PRIMARY( oldC ) ) ) { zxatasp_readide( zxatasp_idechn0, ( newC & ZXATASP_IDE_REG ) ); return; } if( ( ZXATASP_READ_SECONDARY( newC ) ) & !( ZXATASP_READ_SECONDARY( oldC ) ) ) { zxatasp_readide( zxatasp_idechn1, ( newC & ZXATASP_IDE_REG ) ); return; } if( ( ZXATASP_WRITE_PRIMARY( newC ) ) & !( ZXATASP_WRITE_PRIMARY( oldC ) ) ) { zxatasp_writeide( zxatasp_idechn0, ( newC & ZXATASP_IDE_REG ) ); return; } if( ( ZXATASP_WRITE_SECONDARY( newC ) ) & !( ZXATASP_WRITE_SECONDARY( oldC ) ) ) { zxatasp_writeide( zxatasp_idechn1, ( newC & ZXATASP_IDE_REG ) ); return; } if( newC & ZXATASP_RAM_LATCH ) { set_zxatasp_bank( newC & ZXATASP_RAM_BANK ); if( newC & ZXATASP_RAM_DISABLE ) { machine_current->ram.romcs = 0; current_page = ZXATASP_NOT_PAGED; } else { machine_current->ram.romcs = 1; current_page = newC & ZXATASP_RAM_BANK; } machine_current->memory_map(); return; } } libspectrum_byte zxatasp_control_read( libspectrum_word port, int *attached ) { if( !settings_current.zxatasp_active ) return 0xff; *attached = 1; return zxatasp_control; } static void zxatasp_control_write( libspectrum_word port GCC_UNUSED, libspectrum_byte data ) { if( !settings_current.zxatasp_active ) return; if( data & MC8255_SETMODE ) { /* Set the control word and reset the ports */ zxatasp_control = data; zxatasp_resetports(); } else { /* Set or reset a bit of port C */ libspectrum_byte bit = (data >> 1) & 7; libspectrum_byte newC = zxatasp_portC; if( data & 1 ) { newC |= ( 1 << bit ); } else { newC &= ~( 1 << bit ); } zxatasp_portC_write( 0, newC ); } } static void zxatasp_resetports( void ) { /* In input mode, ports will return 0xff unless IDE is active, which it won't be just after a reset. Output ports are always reset to zero. */ zxatasp_portA = (zxatasp_control & MC8255_PORT_A_IO) ? 0xff : 0x00; zxatasp_portB = (zxatasp_control & MC8255_PORT_B_IO) ? 0xff : 0x00; zxatasp_portC = (zxatasp_control & MC8255_PORT_C_LOW_IO) ? 0x0f : 0x00; zxatasp_portC |= (zxatasp_control & MC8255_PORT_C_HI_IO) ? 0xf0 : 0x00; } static void set_zxatasp_bank( int bank ) { memory_page *page; size_t i, offset; for( i = 0; i < 2; i++ ) { page = &memory_map_romcs[i]; offset = i & 1 ? MEMORY_PAGE_SIZE : 0x0000; page->page = &ZXATASPMEM[ bank ][ offset ]; page->writable = !settings_current.zxatasp_wp; page->contended = 0; page->page_num = bank; page->offset = offset; } } /* IDE access */ static void zxatasp_readide(libspectrum_ide_channel *chn, libspectrum_ide_register idereg) { libspectrum_byte dataHi, dataLo; dataLo = libspectrum_ide_read( chn, idereg ); if( idereg == LIBSPECTRUM_IDE_REGISTER_DATA ) { dataHi = libspectrum_ide_read( chn, idereg ); } else { dataHi = 0xff; } if( zxatasp_control & MC8255_PORT_A_IO ) zxatasp_portA = dataLo; if( zxatasp_control & MC8255_PORT_B_IO ) zxatasp_portB = dataHi; } static void zxatasp_writeide(libspectrum_ide_channel *chn, libspectrum_ide_register idereg) { libspectrum_byte dataHi, dataLo; dataLo = ( zxatasp_control & MC8255_PORT_A_IO ) ? 0xff : zxatasp_portA; dataHi = ( zxatasp_control & MC8255_PORT_B_IO ) ? 0xff : zxatasp_portB; libspectrum_ide_write( chn, idereg, dataLo ); if( idereg == LIBSPECTRUM_IDE_REGISTER_DATA ) libspectrum_ide_write( chn, idereg, dataHi ); } size_t zxatasp_current_page( void ) { return current_page; } libspectrum_byte* zxatasp_ram( size_t page ) { return ZXATASPMEM[ page ]; } void zxatasp_memory_map( void ) { int writable; if( settings_current.zxatasp_wp && ( memory_map_romcs[0].page_num & 1 ) ) { writable = 0; } else { writable = 1; } memory_map_romcs[0].writable = memory_map_romcs[1].writable = writable; if( !settings_current.zxatasp_upload ) { memory_map_read[0] = memory_map_romcs[0]; memory_map_read[1] = memory_map_romcs[1]; } memory_map_write[0] = memory_map_romcs[0]; memory_map_write[1] = memory_map_romcs[1]; } int zxatasp_from_snapshot( libspectrum_snap *snap ) { size_t i, page; if( !libspectrum_snap_zxatasp_active( snap ) ) return 0; settings_current.zxatasp_active = 1; settings_current.zxatasp_upload = libspectrum_snap_zxatasp_upload( snap ); settings_current.zxatasp_wp = libspectrum_snap_zxatasp_writeprotect( snap ); zxatasp_portA = libspectrum_snap_zxatasp_port_a ( snap ); zxatasp_portB = libspectrum_snap_zxatasp_port_b ( snap ); zxatasp_portC = libspectrum_snap_zxatasp_port_c ( snap ); zxatasp_control = libspectrum_snap_zxatasp_control( snap ); page = libspectrum_snap_zxatasp_current_page( snap ); if( page != ZXATASP_NOT_PAGED ) { machine_current->ram.romcs = 1; set_zxatasp_bank( page ); } for( i = 0; i < libspectrum_snap_zxatasp_pages( snap ); i++ ) if( libspectrum_snap_zxatasp_ram( snap, i ) ) memcpy( ZXATASPMEM[ i ], libspectrum_snap_zxatasp_ram( snap, i ), ZXATASP_PAGE_LENGTH ); machine_current->memory_map(); return 0; } int zxatasp_to_snapshot( libspectrum_snap *snap ) { size_t i; libspectrum_byte *buffer; if( !settings_current.zxatasp_active ) return 0; libspectrum_snap_set_zxatasp_active( snap, 1 ); libspectrum_snap_set_zxatasp_upload( snap, settings_current.zxatasp_upload ); libspectrum_snap_set_zxatasp_writeprotect( snap, settings_current.zxatasp_wp ); libspectrum_snap_set_zxatasp_port_a( snap, zxatasp_portA ); libspectrum_snap_set_zxatasp_port_b( snap, zxatasp_portB ); libspectrum_snap_set_zxatasp_port_c( snap, zxatasp_portC ); libspectrum_snap_set_zxatasp_control( snap, zxatasp_control ); libspectrum_snap_set_zxatasp_current_page( snap, current_page ); libspectrum_snap_set_zxatasp_pages( snap, ZXATASP_PAGES ); for( i = 0; i < ZXATASP_PAGES; i++ ) { buffer = malloc( ZXATASP_PAGE_LENGTH * sizeof( libspectrum_byte ) ); if( !buffer ) { ui_error( UI_ERROR_ERROR, "Out of memory at %s:%d", __FILE__, __LINE__ ); return 1; } memcpy( buffer, ZXATASPMEM[ i ], ZXATASP_PAGE_LENGTH ); libspectrum_snap_set_zxatasp_ram( snap, i, buffer ); } return 0; }