1
0
mirror of https://git.code.sf.net/p/fuse-emulator/fuse synced 2026-01-27 01:41:34 +03:00
Files
fuse/machines/specplus3.c
Philip Kendall 4b69526c31 Fix warning.
2017-11-19 19:39:30 +00:00

459 lines
13 KiB
C

/* specplus3.c: Spectrum +2A/+3 specific routines
Copyright (c) 1999-2015 Philip Kendall, Darren Salt, Gergely Szasz
Copyright (c) 2016 Gergely Szasz
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.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Author contact information:
E-mail: philip-fuse@shadowmagic.org.uk
*/
#include <config.h>
#include <stdio.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <libspectrum.h>
#include "compat.h"
#include "fuse.h"
#include "machine.h"
#include "machines_periph.h"
#include "memory_pages.h"
#include "periph.h"
#include "peripherals/disk/fdd.h"
#include "peripherals/disk/upd_fdc.h"
#include "peripherals/printer.h"
#include "phantom_typist.h"
#include "settings.h"
#include "snapshot.h"
#include "spec128.h"
#include "spec48.h"
#include "specplus3.h"
#include "spectrum.h"
#include "ui/ui.h"
#include "ui/uimedia.h"
#include "utils.h"
#include "options.h" /* needed for get combo options */
static int normal_memory_map( int rom, int page );
static void special_memory_map( int which );
static void select_special_map( int page1, int page2, int page3, int page4 );
static int specplus3_reset( void );
upd_fdc *specplus3_fdc;
static fdd_t specplus3_drives[ SPECPLUS3_NUM_DRIVES ];
static int ui_drive_is_available( void );
static const fdd_params_t *ui_drive_get_params_a( void );
static const fdd_params_t *ui_drive_get_params_b( void );
static int ui_drive_inserted( const ui_media_drive_info_t *drive, int new );
static int ui_drive_autoload( void );
static ui_media_drive_info_t ui_drives[ SPECPLUS3_NUM_DRIVES ] = {
{
/* .name = */ "+3 Disk A:",
/* .controller_index = */ UI_MEDIA_CONTROLLER_PLUS3,
/* .drive_index = */ SPECPLUS3_DRIVE_A,
/* .menu_item_parent = */ UI_MENU_ITEM_MEDIA_DISK_PLUS3,
/* .menu_item_top = */ UI_MENU_ITEM_INVALID,
/* .menu_item_eject = */ UI_MENU_ITEM_MEDIA_DISK_PLUS3_A_EJECT,
/* .menu_item_flip = */ UI_MENU_ITEM_MEDIA_DISK_PLUS3_A_FLIP_SET,
/* .menu_item_wp = */ UI_MENU_ITEM_MEDIA_DISK_PLUS3_A_WP_SET,
/* .is_available = */ &ui_drive_is_available,
/* .get_params = */ &ui_drive_get_params_a,
/* .insert_hook = */ &ui_drive_inserted,
/* .autoload_hook = */ &ui_drive_autoload,
},
{
/* .name = */ "+3 Disk B:",
/* .controller_index = */ UI_MEDIA_CONTROLLER_PLUS3,
/* .drive_index = */ SPECPLUS3_DRIVE_B,
/* .menu_item_parent = */ UI_MENU_ITEM_MEDIA_DISK_PLUS3,
/* .menu_item_top = */ UI_MENU_ITEM_MEDIA_DISK_PLUS3_B,
/* .menu_item_eject = */ UI_MENU_ITEM_MEDIA_DISK_PLUS3_B_EJECT,
/* .menu_item_flip = */ UI_MENU_ITEM_MEDIA_DISK_PLUS3_B_FLIP_SET,
/* .menu_item_wp = */ UI_MENU_ITEM_MEDIA_DISK_PLUS3_B_WP_SET,
/* .is_available = */ &ui_drive_is_available,
/* .get_params = */ &ui_drive_get_params_b,
/* .insert_hook = */ &ui_drive_inserted,
/* .autoload_hook = */ &ui_drive_autoload,
},
};
int
specplus3_port_from_ula( libspectrum_word port GCC_UNUSED )
{
/* No contended ports */
return 0;
}
int specplus3_init( fuse_machine_info *machine )
{
machine->machine = LIBSPECTRUM_MACHINE_PLUS3;
machine->id = "plus3";
machine->reset = specplus3_reset;
machine->timex = 0;
machine->ram.port_from_ula = specplus3_port_from_ula;
machine->ram.contend_delay = spectrum_contend_delay_76543210;
machine->ram.contend_delay_no_mreq = spectrum_contend_delay_none;
machine->ram.valid_pages = 8;
machine->unattached_port = spectrum_unattached_port_none;
specplus3_765_init();
specplus3_menu_items();
machine->shutdown = specplus3_shutdown;
machine->memory_map = specplus3_memory_map;
return 0;
}
void
specplus3_765_update_fdd( void )
{
specplus3_fdc->speedlock = settings_current.plus3_detect_speedlock ? 0 : -1;
}
void
specplus3_765_init( void )
{
int i;
specplus3_fdc = upd_fdc_alloc_fdc( UPD765A, UPD_CLOCK_4MHZ );
/*!!!! the plus3 only use the US0 pin to select drives,
so drive 2 := drive 0 and drive 3 := drive 1 !!!!*/
specplus3_fdc->drive[0] = &specplus3_drives[ 0 ];
specplus3_fdc->drive[1] = &specplus3_drives[ 1 ];
specplus3_fdc->drive[2] = &specplus3_drives[ 0 ];
specplus3_fdc->drive[3] = &specplus3_drives[ 1 ];
for( i = 0; i < SPECPLUS3_NUM_DRIVES; i++ ) {
specplus3_drives[ i ].disk.flag = DISK_FLAG_PLUS3_CPC;
}
/* builtin drive 1 head 42 track */
fdd_init( &specplus3_drives[ 0 ], FDD_SHUGART, &fdd_params[ 1 ], 0 );
fdd_init( &specplus3_drives[ 1 ], FDD_SHUGART, NULL, 0 ); /* drive geometry 'autodetect' */
specplus3_fdc->set_intrq = NULL;
specplus3_fdc->reset_intrq = NULL;
specplus3_fdc->set_datarq = NULL;
specplus3_fdc->reset_datarq = NULL;
specplus3_765_update_fdd();
for( i = 0; i < SPECPLUS3_NUM_DRIVES; i++ ) {
ui_drives[ i ].fdd = &specplus3_drives[ i ];
ui_media_drive_register( &ui_drives[ i ] );
}
}
void
specplus3_765_reset( void )
{
const fdd_params_t *dt;
upd_fdc_master_reset( specplus3_fdc );
dt = &fdd_params[ option_enumerate_diskoptions_drive_plus3a_type() + 1 ]; /* +1 => there is no `Disabled' */
fdd_init( &specplus3_drives[ 0 ], FDD_SHUGART, dt, 1 );
dt = &fdd_params[ option_enumerate_diskoptions_drive_plus3b_type() ];
fdd_init( &specplus3_drives[ 1 ], dt->enabled ? FDD_SHUGART : FDD_TYPE_NONE,
dt, 1 );
}
static int
specplus3_reset( void )
{
int error;
error = machine_load_rom( 0, settings_current.rom_plus3_0,
settings_default.rom_plus3_0, 0x4000 );
if( error ) return error;
error = machine_load_rom( 1, settings_current.rom_plus3_1,
settings_default.rom_plus3_1, 0x4000 );
if( error ) return error;
error = machine_load_rom( 2, settings_current.rom_plus3_2,
settings_default.rom_plus3_2, 0x4000 );
if( error ) return error;
error = machine_load_rom( 3, settings_current.rom_plus3_3,
settings_default.rom_plus3_3, 0x4000 );
if( error ) return error;
error = specplus3_plus2a_common_reset();
if( error ) return error;
periph_clear();
machines_periph_plus3();
periph_set_present( PERIPH_TYPE_UPD765, PERIPH_PRESENT_ALWAYS );
periph_update();
specplus3_765_reset();
specplus3_menu_items();
spec48_common_display_setup();
return 0;
}
int
specplus3_plus2a_common_reset( void )
{
int error;
size_t i;
machine_current->ram.current_page=0; machine_current->ram.current_rom=0;
machine_current->ram.locked=0;
machine_current->ram.special=0;
machine_current->ram.last_byte=0;
machine_current->ram.last_byte2=0;
memory_current_screen = 5;
memory_screen_mask = 0xffff;
/* All memory comes from the home bank */
for( i = 0; i < MEMORY_PAGES_IN_64K; i++ )
memory_map_read[i].source = memory_map_write[i].source = memory_source_ram;
/* RAM pages 4, 5, 6 and 7 contended */
for( i = 0; i < 8; i++ )
memory_ram_set_16k_contention( i, i >= 4 );
error = normal_memory_map( 0, 0 ); if( error ) return error;
return 0;
}
static int
normal_memory_map( int rom, int page )
{
/* ROM as specified */
memory_map_16k( 0x0000, memory_map_rom, rom );
/* RAM 5 */
memory_map_16k( 0x4000, memory_map_ram, 5 );
/* RAM 2 */
memory_map_16k( 0x8000, memory_map_ram, 2 );
/* RAM 0 */
memory_map_16k( 0xc000, memory_map_ram, page );
return 0;
}
static void
special_memory_map( int which )
{
switch( which ) {
case 0: select_special_map( 0, 1, 2, 3 ); break;
case 1: select_special_map( 4, 5, 6, 7 ); break;
case 2: select_special_map( 4, 5, 6, 3 ); break;
case 3: select_special_map( 4, 7, 6, 3 ); break;
default:
ui_error( UI_ERROR_ERROR, "unknown +3 special configuration %d", which );
fuse_abort();
}
}
static void
select_special_map( int page1, int page2, int page3, int page4 )
{
memory_map_16k( 0x0000, memory_map_ram, page1 );
memory_map_16k( 0x4000, memory_map_ram, page2 );
memory_map_16k( 0x8000, memory_map_ram, page3 );
memory_map_16k( 0xc000, memory_map_ram, page4 );
}
void
specplus3_memoryport2_write_internal( libspectrum_word port GCC_UNUSED,
libspectrum_byte b )
{
/* Let the parallel printer code know about the strobe bit */
printer_parallel_strobe_write( b & 0x10 );
/* If this was called by a machine which has a +3-style disk, set
the state of both floppy drive motors */
if( machine_current->capabilities &&
LIBSPECTRUM_MACHINE_CAPABILITY_PLUS3_DISK ) {
fdd_motoron( &specplus3_drives[0], b & 0x08 );
fdd_motoron( &specplus3_drives[1], b & 0x08 );
}
/* Store the last byte written in case we need it */
machine_current->ram.last_byte2 = b;
machine_current->memory_map();
}
void
specplus3_memoryport2_write( libspectrum_word port, libspectrum_byte b )
{
/* Do nothing else if we've locked the RAM configuration */
if( machine_current->ram.locked ) return;
specplus3_memoryport2_write_internal( port, b );
}
int
specplus3_memory_map( void )
{
int page, rom, screen;
page = machine_current->ram.last_byte & 0x07;
screen = ( machine_current->ram.last_byte & 0x08 ) ? 7 : 5;
rom =
( ( machine_current->ram.last_byte & 0x10 ) >> 4 ) |
( ( machine_current->ram.last_byte2 & 0x04 ) >> 1 );
/* If we changed the active screen, mark the entire display file as
dirty so we redraw it on the next pass */
if( memory_current_screen != screen ) {
display_update_critical( 0, 0 );
display_refresh_main_screen();
memory_current_screen = screen;
}
/* Check whether we want a special RAM configuration */
if( machine_current->ram.last_byte2 & 0x01 ) {
/* If so, select it */
machine_current->ram.special = 1;
special_memory_map( ( machine_current->ram.last_byte2 & 0x06 ) >> 1 );
} else {
/* If not, we're selecting the high bit of the current ROM */
machine_current->ram.special = 0;
normal_memory_map( rom, page );
}
machine_current->ram.current_page = page;
machine_current->ram.current_rom = rom;
memory_romcs_map();
return 0;
}
void
specplus3_menu_items( void )
{
ui_media_drive_update_menus( &ui_drives[ SPECPLUS3_DRIVE_A ],
UI_MEDIA_DRIVE_UPDATE_ALL );
ui_media_drive_update_menus( &ui_drives[ SPECPLUS3_DRIVE_B ],
UI_MEDIA_DRIVE_UPDATE_ALL );
}
libspectrum_byte
specplus3_fdc_status( libspectrum_word port GCC_UNUSED,
libspectrum_byte *attached )
{
*attached = 0xff; /* TODO: check this */
return upd_fdc_read_status( specplus3_fdc );
}
libspectrum_byte
specplus3_fdc_read( libspectrum_word port GCC_UNUSED,
libspectrum_byte *attached )
{
*attached = 0xff; /* TODO: check this */
return upd_fdc_read_data( specplus3_fdc );
}
void
specplus3_fdc_write( libspectrum_word port GCC_UNUSED, libspectrum_byte data )
{
upd_fdc_write_data( specplus3_fdc, data );
}
/* FDC UI related functions */
int
specplus3_disk_insert( specplus3_drive_number which, const char *filename,
int autoload )
{
if( which >= SPECPLUS3_NUM_DRIVES ) {
ui_error( UI_ERROR_ERROR, "specplus3_disk_insert: unknown drive %d",
which );
fuse_abort();
}
return ui_media_drive_insert( &ui_drives[ which ], filename, autoload );
}
fdd_t *
specplus3_get_fdd( specplus3_drive_number which )
{
return &( specplus3_drives[ which ] );
}
static int
ui_drive_is_available( void )
{
return machine_current->capabilities &
LIBSPECTRUM_MACHINE_CAPABILITY_PLUS3_DISK;
}
static const fdd_params_t *
ui_drive_get_params_a( void )
{
/* +1 => there is no `Disabled' */
return &fdd_params[ option_enumerate_diskoptions_drive_plus3a_type() + 1 ];
}
static const fdd_params_t *
ui_drive_get_params_b( void )
{
return &fdd_params[ option_enumerate_diskoptions_drive_plus3b_type() ];
}
static int
ui_drive_inserted( const ui_media_drive_info_t *drive, int new )
{
/* Did real +3 hardware also have problems formatting disks unformatted
disks? */
if( new )
disk_preformat( &drive->fdd->disk ); /* pre-format disk for +3 ??? */
return 0;
}
int
specplus3_shutdown( void )
{
return 0;
}
static int
ui_drive_autoload( void )
{
machine_reset( 0 );
phantom_typist_activate_disk();
return 0;
}