mirror of
https://git.code.sf.net/p/fuse-emulator/fuse
synced 2026-01-30 04:22:18 +03:00
1136 lines
28 KiB
C
1136 lines
28 KiB
C
/* trdos.c: Routines for handling the Betadisk interface
|
|
Copyright (c) 2002-2004 Dmitry Sanarin, Fredrick Meunier, Philip Kendall
|
|
|
|
$Id$
|
|
|
|
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:
|
|
|
|
Philip: pak21-fuse@srcf.ucam.org
|
|
Post: 15 Crescent Road, Wokingham, Berks, RG40 2DB, England
|
|
|
|
Dmitry: sdb386@hotmail.com
|
|
Fred: fredm@spamcop.net
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#ifdef HAVE_STRINGS_H
|
|
#include <strings.h> /* Needed for strncasecmp() on QNX6 */
|
|
#endif /* #ifdef HAVE_STRINGS_H */
|
|
#include <limits.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <libspectrum.h>
|
|
|
|
#include "compat.h"
|
|
#include "event.h"
|
|
#include "machine.h"
|
|
#include "memory.h"
|
|
#include "spectrum.h"
|
|
#include "trdos.h"
|
|
#include "ui/ui.h"
|
|
#include "utils.h"
|
|
|
|
#define TRDOS_DISC_SIZE 655360
|
|
|
|
typedef struct
|
|
{
|
|
int disc_ready; /* Is this disk present? */
|
|
|
|
char filename[PATH_MAX]; /* The filename we used to open this disk */
|
|
int fd; /* The fd will we use to access this disk */
|
|
|
|
int ro; /* True if we have read-only access to this
|
|
disk */
|
|
|
|
libspectrum_byte trk;
|
|
|
|
} discs_type;
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
|
|
typedef struct
|
|
{
|
|
unsigned b7 : 1; /* This bit reflects the status of the Motor On output */
|
|
unsigned b6 : 1; /* disk is write-protected */
|
|
unsigned b5 : 1; /* When set, this bit indicates that the Motor Spin-Up
|
|
sequence has completed (6 revolutions) on type 1
|
|
commands. Type 2 & 3 commands, this bit indicates
|
|
record Type. 0 = Data Mark, 1 = Deleted Data Mark. */
|
|
unsigned b4 : 1; /* When set, it indicates that the desired track, sector,
|
|
or side were not found. This bit is reset when
|
|
updated. */
|
|
unsigned b3 : 1; /* If this is set, an error is found in one or more ID
|
|
fields; otherwise it indicates error in data field.
|
|
This bit is reset when updated. */
|
|
unsigned b2 : 1; /* When set, it indicates the computer did not respond to
|
|
DRQ in one byte time. This bit is reset to zero when
|
|
update. On type 1 commands, this bit reflects the
|
|
status of the TRACK 00 pin. */
|
|
unsigned b1 : 1; /* This bit is a copy of the DRQ output. When set, it
|
|
indicates the DR is full on a Read Operation or the DR
|
|
is empty on a write operation. This bit is reset to
|
|
zero when updated. On type 1 commands, this bit
|
|
indicates the status of the index pin. */
|
|
unsigned b0 : 1; /* When set, command is under execution. When reset, no
|
|
command is under execution. */
|
|
} rs_type;
|
|
|
|
#else /* #ifdef WORDS_BIGENDIAN */
|
|
|
|
typedef struct
|
|
{
|
|
unsigned b0 : 1; /* When set, command is under execution. When reset, no
|
|
command is under execution. */
|
|
unsigned b1 : 1; /* This bit is a copy of the DRQ output. When set, it
|
|
indicates the DR is full on a Read Operation or the DR
|
|
is empty on a write operation. This bit is reset to
|
|
zero when updated. On type 1 commands, this bit
|
|
indicates the status of the index pin. */
|
|
unsigned b2 : 1; /* When set, it indicates the computer did not respond to
|
|
DRQ in one byte time. This bit is reset to zero when
|
|
update. On type 1 commands, this bit reflects the
|
|
status of the TRACK 00 pin. */
|
|
unsigned b3 : 1; /* If this is set, an error is found in one or more ID
|
|
fields; otherwise it indicates error in data field.
|
|
This bit is reset when updated. */
|
|
unsigned b4 : 1; /* When set, it indicates that the desired track, sector,
|
|
or side were not found. This bit is reset when
|
|
updated. */
|
|
unsigned b5 : 1; /* When set, this bit indicates that the Motor Spin-Up
|
|
sequence has completed (6 revolutions) on type 1
|
|
commands. Type 2 & 3 commands, this bit indicates
|
|
record Type. 0 = Data Mark, 1 = Deleted Data Mark. */
|
|
unsigned b6 : 1; /* disk is write-protected */
|
|
unsigned b7 : 1; /* This bit reflects the status of the Motor On output */
|
|
} rs_type;
|
|
|
|
#endif /* #ifdef WORDS_BIGENDIAN */
|
|
|
|
# define CurrentDiscNum ( trdos_system_register & 3 )
|
|
# define CurrentDisk discs[CurrentDiscNum]
|
|
|
|
static int busy = 0;
|
|
static int index_impulse = 0;
|
|
|
|
static int towrite = 0;
|
|
static int towriteaddr;
|
|
|
|
static int pointer;
|
|
static int side;
|
|
|
|
static libspectrum_byte track[ TRDOS_DISC_SIZE ];
|
|
static libspectrum_byte *toread;
|
|
static unsigned int toread_num = 0;
|
|
static unsigned int toread_position = 0;
|
|
static libspectrum_byte six_bytes[6];
|
|
|
|
static int vg_spin;
|
|
static int trdos_direction; /* 0 - spindlewards 1 - rimwards */
|
|
static libspectrum_byte trdos_status_register; /* Betadisk status register */
|
|
static libspectrum_byte trdos_track_register; /* FDC track register */
|
|
static libspectrum_byte trdos_sector_register; /* FDC sector register */
|
|
static libspectrum_byte trdos_data_register; /* FDC data register */
|
|
static libspectrum_byte vg_portFF_in;
|
|
static libspectrum_byte trdos_system_register; /* FDC system register */
|
|
|
|
union
|
|
{
|
|
libspectrum_byte byte;
|
|
rs_type bit;
|
|
} vg_rs;
|
|
|
|
static discs_type discs[4];
|
|
|
|
int trdos_available = 0;
|
|
int trdos_active=0;
|
|
|
|
/* The template used for naming the results of the SCL->TRD conversion */
|
|
#define SCL_TMP_FILE_TEMPLATE "fuse.scl.XXXXXX"
|
|
|
|
/* The template used for the temporary copy of a .trd file */
|
|
static const char *trd_template = "fuse.trd.XXXXXX";
|
|
|
|
static int Scl2Trd( const char *oldname, int TRD );
|
|
static int insert_scl( trdos_drive_number which, const char *filename );
|
|
static int insert_trd( trdos_drive_number which, const char *filename );
|
|
static libspectrum_dword lsb2dw( libspectrum_byte *mem );
|
|
static void dw2lsb( libspectrum_byte *mem, libspectrum_dword value );
|
|
|
|
int
|
|
trdos_init( void )
|
|
{
|
|
discs[0].disc_ready = 0;
|
|
discs[1].disc_ready = 0;
|
|
discs[2].disc_ready = 0;
|
|
discs[3].disc_ready = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
trdos_reset( void )
|
|
{
|
|
trdos_active = 0;
|
|
busy = 0;
|
|
|
|
trdos_event_index( 0 );
|
|
|
|
/* We can eject disks only if they are currently present */
|
|
ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_A_EJECT,
|
|
discs[ TRDOS_DRIVE_A ].disc_ready );
|
|
ui_menu_activate( UI_MENU_ITEM_MEDIA_DISK_B_EJECT,
|
|
discs[ TRDOS_DRIVE_B ].disc_ready );
|
|
|
|
ui_statusbar_update( UI_STATUSBAR_ITEM_DISK, UI_STATUSBAR_STATE_INACTIVE );
|
|
}
|
|
|
|
void
|
|
trdos_end( void )
|
|
{
|
|
trdos_available = 0;
|
|
}
|
|
|
|
void
|
|
trdos_page( void )
|
|
{
|
|
trdos_active = 1;
|
|
machine_current->ram.romcs = 1;
|
|
machine_current->memory_map();
|
|
}
|
|
|
|
void
|
|
trdos_unpage( void )
|
|
{
|
|
trdos_active = 0;
|
|
machine_current->ram.romcs = 0;
|
|
machine_current->memory_map();
|
|
}
|
|
|
|
void
|
|
trdos_memory_map( void )
|
|
{
|
|
memory_map_read[0] = memory_map_write[0] = memory_map_romcs[0];
|
|
memory_map_read[1] = memory_map_write[1] = memory_map_romcs[1];
|
|
}
|
|
|
|
int
|
|
trdos_from_snapshot( libspectrum_snap *snap, int capabilities )
|
|
{
|
|
if( !capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_TRDOS_DISK ) return 0;
|
|
|
|
trdos_active = libspectrum_snap_beta_paged( snap );
|
|
|
|
if( trdos_active ) {
|
|
trdos_page();
|
|
} else {
|
|
trdos_unpage();
|
|
}
|
|
|
|
trdos_direction = libspectrum_snap_beta_direction( snap );
|
|
|
|
trdos_cr_write ( 0x001f, libspectrum_snap_beta_status( snap ) );
|
|
trdos_tr_write ( 0x003f, libspectrum_snap_beta_track ( snap ) );
|
|
trdos_sec_write( 0x005f, libspectrum_snap_beta_sector( snap ) );
|
|
trdos_dr_write ( 0x007f, libspectrum_snap_beta_data ( snap ) );
|
|
trdos_sp_write ( 0x00ff, libspectrum_snap_beta_system( snap ) );
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
trdos_to_snapshot( libspectrum_snap *snap )
|
|
{
|
|
libspectrum_snap_set_beta_paged( snap, trdos_active );
|
|
libspectrum_snap_set_beta_direction( snap, trdos_direction );
|
|
libspectrum_snap_set_beta_status( snap, trdos_status_register );
|
|
libspectrum_snap_set_beta_track ( snap, trdos_system_register );
|
|
libspectrum_snap_set_beta_sector( snap, trdos_track_register );
|
|
libspectrum_snap_set_beta_data ( snap, trdos_sector_register );
|
|
libspectrum_snap_set_beta_system( snap, trdos_data_register );
|
|
|
|
return 0;
|
|
}
|
|
|
|
static
|
|
void trdos_update_index_impulse( void )
|
|
{
|
|
if ( vg_spin ) {
|
|
vg_rs.bit.b1 = 0;
|
|
if ( CurrentDisk.disc_ready && index_impulse ) {
|
|
vg_rs.bit.b1 = 1;
|
|
}
|
|
}
|
|
|
|
ui_statusbar_update( UI_STATUSBAR_ITEM_DISK,
|
|
busy ? UI_STATUSBAR_STATE_ACTIVE :
|
|
UI_STATUSBAR_STATE_INACTIVE );
|
|
}
|
|
|
|
libspectrum_byte
|
|
trdos_sr_read( libspectrum_word port GCC_UNUSED, int *attached )
|
|
{
|
|
if( !trdos_active ) return 0xff;
|
|
|
|
*attached = 1;
|
|
|
|
trdos_update_index_impulse();
|
|
|
|
if ( !CurrentDisk.disc_ready ) {
|
|
return( 0x80 ); /* No disk in drive */
|
|
}
|
|
|
|
return( vg_rs.byte );
|
|
}
|
|
|
|
libspectrum_byte
|
|
trdos_tr_read( libspectrum_word port GCC_UNUSED, int *attached )
|
|
{
|
|
if( !trdos_active ) return 0xff;
|
|
|
|
*attached = 1;
|
|
|
|
trdos_update_index_impulse();
|
|
|
|
return trdos_track_register;
|
|
}
|
|
|
|
void
|
|
trdos_tr_write( libspectrum_word port GCC_UNUSED, libspectrum_byte b )
|
|
{
|
|
if( !trdos_active ) return;
|
|
|
|
trdos_track_register = b;
|
|
}
|
|
|
|
libspectrum_byte
|
|
trdos_sec_read( libspectrum_word port GCC_UNUSED, int *attached )
|
|
{
|
|
if( !trdos_active ) return 0xff;
|
|
|
|
*attached = 1;
|
|
|
|
trdos_update_index_impulse();
|
|
|
|
return trdos_sector_register;
|
|
}
|
|
|
|
void
|
|
trdos_sec_write( libspectrum_word port GCC_UNUSED, libspectrum_byte b )
|
|
{
|
|
if( !trdos_active ) return;
|
|
|
|
trdos_sector_register = b;
|
|
}
|
|
|
|
libspectrum_byte
|
|
trdos_dr_read( libspectrum_word port GCC_UNUSED, int *attached )
|
|
{
|
|
if( !trdos_active ) return 0xff;
|
|
|
|
*attached = 1;
|
|
|
|
trdos_update_index_impulse();
|
|
|
|
if( toread_position >= toread_num ) return trdos_data_register;
|
|
|
|
trdos_data_register = toread[toread_position];
|
|
|
|
if ( ( ( toread_position & 0x00ff ) == 0 ) && toread_position != 0 )
|
|
trdos_sector_register++;
|
|
|
|
if( trdos_sector_register > 16 ) trdos_sector_register = 1;
|
|
|
|
toread_position++;
|
|
|
|
if ( toread_position == toread_num ) {
|
|
vg_portFF_in = 0x80;
|
|
|
|
vg_rs.byte = 0;
|
|
} else {
|
|
vg_portFF_in = 0x40;
|
|
|
|
vg_rs.bit.b0 = 1; /* Busy */
|
|
vg_rs.bit.b1 = 1; /* DRQ copy */
|
|
}
|
|
|
|
return trdos_data_register;
|
|
}
|
|
|
|
void
|
|
trdos_dr_write( libspectrum_word port GCC_UNUSED, libspectrum_byte b )
|
|
{
|
|
if( !trdos_active ) return;
|
|
|
|
trdos_data_register = b;
|
|
|
|
if( towrite == 0 ) return;
|
|
|
|
if( lseek( CurrentDisk.fd, towriteaddr, SEEK_SET ) == -1 ) {
|
|
towrite = 0;
|
|
|
|
ui_error( UI_ERROR_ERROR,
|
|
"trdos_dr_write: seeking in '%s' failed: %s",
|
|
CurrentDisk.filename, strerror( errno ) );
|
|
return;
|
|
}
|
|
|
|
write( CurrentDisk.fd, &b, 1 );
|
|
|
|
towrite--;
|
|
towriteaddr++;
|
|
|
|
if( towrite == 0 ) {
|
|
vg_portFF_in = 0x80;
|
|
vg_rs.byte = 0;
|
|
} else {
|
|
vg_portFF_in = 0x40;
|
|
}
|
|
}
|
|
|
|
libspectrum_byte
|
|
trdos_sp_read( libspectrum_word port GCC_UNUSED, int *attached )
|
|
{
|
|
if( !trdos_active ) return 0xff;
|
|
|
|
*attached = 1;
|
|
|
|
trdos_update_index_impulse();
|
|
|
|
if ( busy == 1 ) {
|
|
return( vg_portFF_in &~ 0x40 );
|
|
}
|
|
|
|
return( vg_portFF_in );
|
|
}
|
|
|
|
void
|
|
trdos_sp_write( libspectrum_word port GCC_UNUSED, libspectrum_byte b )
|
|
{
|
|
if( !trdos_active ) return;
|
|
|
|
trdos_system_register = b;
|
|
}
|
|
|
|
static int
|
|
insert_trd( trdos_drive_number which, const char *filename )
|
|
{
|
|
int fd, error;
|
|
char tempfilename[ PATH_MAX ];
|
|
|
|
/* Make a temporary copy of the disk */
|
|
error = utils_make_temp_file( &fd, tempfilename, filename, trd_template );
|
|
if( error ) return error;
|
|
|
|
unlink( tempfilename );
|
|
|
|
discs[which].ro = 0;
|
|
discs[which].fd = fd;
|
|
strcpy( discs[which].filename, filename );
|
|
discs[which].disc_ready = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
insert_scl( trdos_drive_number which, const char *filename )
|
|
{
|
|
const char *temp_path; size_t length;
|
|
char* trd_template;
|
|
int ret;
|
|
|
|
temp_path = utils_get_temp_path();
|
|
|
|
/* +2 is for the slash between the path and the template and for the
|
|
null at the end */
|
|
length = strlen( temp_path ) + strlen( SCL_TMP_FILE_TEMPLATE ) + 2;
|
|
|
|
trd_template = malloc( length );
|
|
if( !trd_template ) {
|
|
ui_error( UI_ERROR_ERROR, "out of memory at %s:%d", __FILE__, __LINE__ );
|
|
return 1;
|
|
}
|
|
|
|
snprintf( trd_template, length, "%s/%s", temp_path, SCL_TMP_FILE_TEMPLATE );
|
|
|
|
discs[ which ].disc_ready = 0;
|
|
|
|
discs[ which ].fd = mkstemp( trd_template );
|
|
if( discs[ which ].fd == -1 ) {
|
|
ui_error( UI_ERROR_ERROR, "couldn't get a temporary filename: %s",
|
|
strerror( errno ) );
|
|
free( trd_template );
|
|
return 1;
|
|
}
|
|
|
|
/* Unlink the file so it will be removed when the fd is closed */
|
|
unlink( trd_template );
|
|
|
|
if( ( ret = Scl2Trd( filename, discs[ which ].fd ) ) ) {
|
|
close( discs[ which ].fd );
|
|
free( trd_template );
|
|
return ret;
|
|
}
|
|
|
|
strcpy( discs[which].filename, trd_template );
|
|
discs[which].disc_ready = 1;
|
|
discs[which].ro = 1;
|
|
|
|
free( trd_template );
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
trdos_disk_insert( trdos_drive_number which, const char *filename )
|
|
{
|
|
int error;
|
|
utils_file file;
|
|
libspectrum_id_t type;
|
|
libspectrum_class_t class;
|
|
|
|
if( discs[ which ].disc_ready ) {
|
|
if( trdos_disk_eject( which, 0 ) ) return 1;
|
|
}
|
|
|
|
if( utils_read_file( filename, &file ) ) return 1;
|
|
|
|
if( libspectrum_identify_file_with_class( &type, &class, filename,
|
|
file.buffer, file.length ) ) {
|
|
utils_close_file( &file );
|
|
return 1;
|
|
}
|
|
|
|
if( class != LIBSPECTRUM_CLASS_DISK_TRDOS ) {
|
|
ui_error( UI_ERROR_ERROR,
|
|
"trdos_disk_insert: file `%s' is not a TR-DOS disk", filename );
|
|
return 1;
|
|
}
|
|
|
|
if( utils_close_file( &file ) ) return 1;
|
|
|
|
switch( type ) {
|
|
case LIBSPECTRUM_ID_DISK_SCL:
|
|
error = insert_scl( which, filename );
|
|
break;
|
|
case LIBSPECTRUM_ID_DISK_TRD:
|
|
error = insert_trd( which, filename );
|
|
break;
|
|
default:
|
|
ui_error( UI_ERROR_ERROR,
|
|
"trdos_disk_insert: file `%s' is an unsupported TR-DOS disk", filename );
|
|
error = 1;
|
|
}
|
|
|
|
if( error ) return error;
|
|
|
|
/* Set the `eject' item active */
|
|
ui_menu_activate(
|
|
which == TRDOS_DRIVE_A ? UI_MENU_ITEM_MEDIA_DISK_A_EJECT :
|
|
UI_MENU_ITEM_MEDIA_DISK_B_EJECT ,
|
|
1
|
|
);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
trdos_disk_eject( trdos_drive_number which, int write )
|
|
{
|
|
int error;
|
|
|
|
if( write ) {
|
|
ui_trdos_disk_write( which );
|
|
} else {
|
|
error = close( discs[ which ].fd );
|
|
if( error ) {
|
|
ui_error( UI_ERROR_ERROR, "Error closing '%s': %s",
|
|
discs[which].filename, strerror( errno ) );
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
discs[which].disc_ready = 0;
|
|
|
|
/* Set the `eject' item inactive */
|
|
ui_menu_activate(
|
|
which == TRDOS_DRIVE_A ? UI_MENU_ITEM_MEDIA_DISK_A_EJECT :
|
|
UI_MENU_ITEM_MEDIA_DISK_B_EJECT ,
|
|
0
|
|
);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
trdos_disk_write( trdos_drive_number which, const char *filename )
|
|
{
|
|
FILE *f;
|
|
utils_file file;
|
|
int error;
|
|
ssize_t bytes_written;
|
|
|
|
f = fopen( filename, "wb" );
|
|
if( !f ) {
|
|
ui_error( UI_ERROR_ERROR, "couldn't open '%s' for writing: %s", filename,
|
|
strerror( errno ) );
|
|
}
|
|
|
|
error = utils_read_fd( discs[ which ].fd, discs[ which ].filename, &file );
|
|
if( error ) { fclose( f ); return error; }
|
|
|
|
bytes_written = fwrite( file.buffer, 1, file.length, f );
|
|
if( bytes_written != file.length ) {
|
|
ui_error( UI_ERROR_ERROR, "could write only %lu of %lu bytes to '%s'",
|
|
(unsigned long)bytes_written, (unsigned long)file.length,
|
|
filename );
|
|
utils_close_file( &file ); fclose( f );
|
|
}
|
|
|
|
error = utils_close_file( &file ); if( error ) { fclose( f ); return error; }
|
|
|
|
if( fclose( f ) ) {
|
|
ui_error( UI_ERROR_ERROR, "error closing '%s': %s", filename,
|
|
strerror( errno ) );
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
trdos_event_cmd_done( libspectrum_dword last_tstates GCC_UNUSED )
|
|
{
|
|
busy = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
trdos_event_index( libspectrum_dword last_tstates )
|
|
{
|
|
int error;
|
|
int next_tstates;
|
|
static int num_calls = 0;
|
|
|
|
if( num_calls == 0 ) {
|
|
/* schedule next call in 20ms */
|
|
next_tstates = 20 * machine_current->timings.processor_speed / 1000;
|
|
index_impulse = 1;
|
|
num_calls = 1;
|
|
} else {
|
|
/* schedule next call in 180ms */
|
|
next_tstates = 180 * machine_current->timings.processor_speed / 1000;
|
|
index_impulse = 0;
|
|
num_calls = 0;
|
|
}
|
|
|
|
error = event_add( last_tstates + next_tstates, EVENT_TYPE_TRDOS_INDEX );
|
|
if( error ) return error;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
vg_seek_delay( libspectrum_byte dst_track )
|
|
{
|
|
int error;
|
|
|
|
if( ( dst_track - CurrentDisk.trk ) > 0 ) trdos_direction = 1;
|
|
if( ( dst_track - CurrentDisk.trk ) < 0 ) trdos_direction = 0;
|
|
|
|
busy = 1;
|
|
|
|
/* FIXME: is this code really what was meant? */
|
|
if ( !CurrentDisk.disc_ready ) vg_portFF_in = 0x80;
|
|
else vg_portFF_in = 0x80;
|
|
|
|
CurrentDisk.trk = dst_track;
|
|
trdos_track_register = dst_track;
|
|
|
|
/* schedule event */
|
|
error = event_add( tstates +
|
|
machine_current->timings.processor_speed / 1000 * 20 *
|
|
abs( dst_track - CurrentDisk.trk ),
|
|
EVENT_TYPE_TRDOS_CMD_DONE );
|
|
}
|
|
|
|
static void
|
|
vg_setFlagsSeeks( void )
|
|
{
|
|
vg_rs.bit.b0 = 0;
|
|
|
|
if ( CurrentDisk.trk == 0 )
|
|
vg_rs.bit.b2 = 1;
|
|
else
|
|
vg_rs.bit.b2 = 0;
|
|
|
|
vg_rs.bit.b3 = 0;
|
|
vg_rs.bit.b4 = 0;
|
|
vg_rs.bit.b6 = CurrentDisk.ro;
|
|
vg_rs.bit.b7 = CurrentDisk.disc_ready;
|
|
}
|
|
|
|
void
|
|
trdos_cr_write( libspectrum_word port GCC_UNUSED, libspectrum_byte b )
|
|
{
|
|
int error;
|
|
|
|
if( !trdos_active ) return;
|
|
|
|
trdos_status_register = b;
|
|
|
|
side = trdos_system_register & 0x10 ? 0 : 1;
|
|
|
|
if ( (b & 0xF0) == 0xD0 ) { /* interrupt */
|
|
vg_portFF_in = 0x80;
|
|
vg_setFlagsSeeks();
|
|
|
|
return;
|
|
}
|
|
|
|
if ( (b & 0xF0) == 0x00 ) { /* seek trk0 AKA Initialisation */
|
|
vg_seek_delay( 0 );
|
|
vg_rs.bit.b5 = b & 8 ? 1 : 0;
|
|
vg_setFlagsSeeks();
|
|
if ( b & 8 ) vg_spin = 1;
|
|
|
|
return;
|
|
}
|
|
|
|
if ( (b & 0xF0) == 0x10 ) { /* seek track */
|
|
vg_seek_delay( trdos_data_register );
|
|
vg_rs.bit.b5 = b & 8 ? 1 : 0;
|
|
vg_setFlagsSeeks();
|
|
if ( b & 8 ) vg_spin = 1;
|
|
|
|
return;
|
|
}
|
|
|
|
if ( (b & 0xE0) == 0x40 ) { /* fwd */
|
|
trdos_direction = 1;
|
|
b = 0x20; /* step */
|
|
}
|
|
if ( (b & 0xE0) == 0x60 ) { /* back */
|
|
trdos_direction = 0;
|
|
b = 0x20; /* step */
|
|
}
|
|
if ( (b & 0xE0) == 0x20 ) { /* step */
|
|
if ( trdos_direction )
|
|
vg_seek_delay( ++CurrentDisk.trk );
|
|
else
|
|
vg_seek_delay( --CurrentDisk.trk );
|
|
|
|
vg_rs.bit.b5 = 1;
|
|
vg_setFlagsSeeks();
|
|
vg_spin = 1;
|
|
|
|
return;
|
|
}
|
|
|
|
if ( (b & 0xE0) == 0x80 ) { /* readsec */
|
|
vg_rs.byte = 0x81;
|
|
vg_portFF_in = 0x40;
|
|
vg_spin = 0;
|
|
|
|
if ( !CurrentDisk.disc_ready ) {
|
|
vg_rs.byte = 0x90;
|
|
vg_portFF_in = 0x80;
|
|
|
|
ui_error( UI_ERROR_WARNING, "No disk in drive %c",
|
|
'A' + CurrentDiscNum );
|
|
|
|
return;
|
|
}
|
|
|
|
if( ( trdos_sector_register == 0 ) || ( trdos_sector_register > 16 ) ) {
|
|
vg_rs.byte |= 0x10; /* sector not found */
|
|
vg_portFF_in = 0x80;
|
|
|
|
ui_error( UI_ERROR_ERROR,
|
|
"Attempt to read TRDOS sector with sector register 0x%02x",
|
|
trdos_sector_register );
|
|
|
|
return;
|
|
}
|
|
|
|
pointer = ( CurrentDisk.trk * 2 + side ) * 256 * 16 +
|
|
( trdos_sector_register - 1 ) * 256;
|
|
|
|
if( lseek( CurrentDisk.fd, pointer, SEEK_SET) == -1 ) {
|
|
ui_error( UI_ERROR_ERROR,
|
|
"trdos_cr_write: seeking in '%s' failed: %s",
|
|
CurrentDisk.filename, strerror( errno ) );
|
|
|
|
vg_rs.byte |= 0x10; /* sector not found */
|
|
vg_portFF_in = 0x80;
|
|
|
|
return;
|
|
}
|
|
|
|
if ( b & 0x10 ) {
|
|
ui_error( UI_ERROR_ERROR,
|
|
"Unimplemented TRDOS multisector read: sector register = %d",
|
|
trdos_sector_register );
|
|
|
|
read( CurrentDisk.fd, track, 256 * ( 17 - trdos_sector_register ) );
|
|
|
|
lseek( CurrentDisk.fd,
|
|
|
|
/* This line used to say:
|
|
-256 * ( ( 17 - vg_reg_sec ) + ( vg_reg_sec - 1 ) ),
|
|
which is fairly nonsensical */
|
|
-256 * 16,
|
|
|
|
SEEK_CUR );
|
|
|
|
read( CurrentDisk.fd, track, 256 * ( trdos_sector_register - 1 ) );
|
|
|
|
toread = track;
|
|
toread_num = 256 * ( 16 );
|
|
toread_position = 0;
|
|
|
|
/* vg_portFF_in=0x80; */
|
|
/* todo : Eto proverit' !!! */
|
|
} else {
|
|
if( read( CurrentDisk.fd, track, 256 ) == 256 ) {
|
|
|
|
toread = track;
|
|
toread_num = 256;
|
|
toread_position = 0;
|
|
|
|
/* schedule event */
|
|
busy = 1;
|
|
error = event_add( tstates + machine_current->timings.processor_speed
|
|
/ 1000 * 30,
|
|
EVENT_TYPE_TRDOS_CMD_DONE );
|
|
} else {
|
|
|
|
vg_rs.byte |= 0x10; /* sector not found */
|
|
vg_portFF_in = 0x80;
|
|
|
|
ui_error( UI_ERROR_ERROR, "Error reading from '%s'",
|
|
CurrentDisk.filename );
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if ( (b & 0xFB) == 0xC0 ) { /* read adr */
|
|
vg_rs.byte = 0x81;
|
|
vg_portFF_in = 0x40;
|
|
|
|
six_bytes[0] = CurrentDisk.trk;
|
|
six_bytes[1] = 0;
|
|
six_bytes[2] = trdos_sector_register;
|
|
six_bytes[3] = 1;
|
|
six_bytes[4] = 0; /* todo : crc !!! */
|
|
six_bytes[5] = 0; /* todo : crc !!! */
|
|
|
|
toread = six_bytes;
|
|
toread_num = 6;
|
|
toread_position = 0;
|
|
|
|
/* schedule event */
|
|
busy = 1;
|
|
vg_spin = 0;
|
|
|
|
error = event_add( tstates + machine_current->timings.processor_speed
|
|
/ 1000 * 30,
|
|
EVENT_TYPE_TRDOS_CMD_DONE );
|
|
|
|
return;
|
|
}
|
|
|
|
if ( (b & 0xE0) == 0xA0 ) { /* writesec */
|
|
vg_rs.byte = 0x81;
|
|
vg_portFF_in = 0x40;
|
|
vg_spin = 0;
|
|
|
|
|
|
if ( CurrentDisk.ro ) {
|
|
vg_rs.byte = 0x60;
|
|
vg_portFF_in = 0x80;
|
|
return;
|
|
}
|
|
|
|
if ( !CurrentDisk.disc_ready ) {
|
|
vg_rs.byte = 0x90;
|
|
vg_portFF_in = 0x80;
|
|
|
|
ui_error( UI_ERROR_ERROR, "No disk" );
|
|
|
|
return;
|
|
}
|
|
|
|
if ( ( trdos_sector_register == 0 ) || ( trdos_sector_register > 16 ) ) {
|
|
vg_rs.byte |= 0x10; /* sector not found */
|
|
vg_portFF_in = 0x80;
|
|
|
|
ui_error( UI_ERROR_ERROR,
|
|
"Attempt to write TRDOS sector with sector register 0x%02x",
|
|
trdos_sector_register );
|
|
|
|
return;
|
|
}
|
|
|
|
towriteaddr = ( CurrentDisk.trk * 2 + side ) * 256 * 16
|
|
+ ( trdos_sector_register - 1 ) * 256;
|
|
towrite = 256;
|
|
|
|
vg_spin = 0;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
#define TRD_NAMEOFFSET 0x08F5
|
|
#define TRD_DIRSTART 0x08E2
|
|
#define TRD_DIRLEN 32
|
|
#define TRD_MAXNAMELENGTH 8
|
|
#define BLOCKSIZE 10240
|
|
|
|
typedef union {
|
|
#ifdef WORDS_BIGENDIAN
|
|
struct { libspectrum_byte b3, b2, b1, b0; } b;
|
|
#else
|
|
struct { libspectrum_byte b0, b1, b2, b3; } b;
|
|
#endif
|
|
libspectrum_dword dword;
|
|
} lsb_dword;
|
|
|
|
static libspectrum_dword
|
|
lsb2dw( libspectrum_byte *mem )
|
|
{
|
|
return ( mem[0] + ( mem[1] * 256 ) + ( mem[2] * 256 * 256 )
|
|
+ ( mem[3] * 256 * 256 * 256 ) );
|
|
}
|
|
|
|
static void
|
|
dw2lsb( libspectrum_byte *mem, libspectrum_dword value )
|
|
{
|
|
lsb_dword ret;
|
|
|
|
ret.dword = value;
|
|
|
|
mem[0] = ret.b.b0;
|
|
mem[1] = ret.b.b1;
|
|
mem[2] = ret.b.b2;
|
|
mem[3] = ret.b.b3;
|
|
}
|
|
|
|
static int
|
|
Scl2Trd( const char *oldname, int TRD )
|
|
{
|
|
int SCL, i;
|
|
|
|
libspectrum_byte *TRDh;
|
|
|
|
libspectrum_byte *trd_free;
|
|
libspectrum_byte *trd_fsec;
|
|
libspectrum_byte *trd_ftrk;
|
|
libspectrum_byte *trd_files;
|
|
libspectrum_byte size;
|
|
|
|
char signature[8];
|
|
libspectrum_byte blocks;
|
|
libspectrum_byte headers[256][14];
|
|
void *tmpscl;
|
|
libspectrum_dword left;
|
|
libspectrum_dword fptr;
|
|
size_t x;
|
|
|
|
ssize_t written;
|
|
|
|
libspectrum_byte template[34] = {
|
|
0x01, 0x16, 0x00, 0xF0,
|
|
0x09, 0x10, 0x00, 0x00,
|
|
0x20, 0x20, 0x20, 0x20,
|
|
0x20, 0x20, 0x20, 0x20,
|
|
0x20, 0x00, 0x00, 0x64,
|
|
0x69, 0x73, 0x6B, 0x6E,
|
|
0x61, 0x6D, 0x65, 0x00,
|
|
0x00, 0x00, 0x46, 0x55
|
|
};
|
|
|
|
libspectrum_byte *mem;
|
|
|
|
mem = malloc( BLOCKSIZE );
|
|
if( !mem ) {
|
|
ui_error( UI_ERROR_ERROR, "Out of memory at %s:%d", __FILE__, __LINE__ );
|
|
return 1;
|
|
}
|
|
|
|
memset( mem, 0, BLOCKSIZE );
|
|
|
|
memcpy( &mem[TRD_DIRSTART], template, TRD_DIRLEN );
|
|
strncpy( &mem[TRD_NAMEOFFSET], "Fuse", TRD_MAXNAMELENGTH );
|
|
|
|
written = write( TRD, mem, BLOCKSIZE );
|
|
if( written != BLOCKSIZE ) {
|
|
ui_error( UI_ERROR_ERROR, "Error writing to temporary TRD file" );
|
|
free( mem );
|
|
return 1;
|
|
}
|
|
|
|
memset( mem, 0, BLOCKSIZE );
|
|
|
|
for( i = 0; i < 63; i++ ) {
|
|
|
|
written = write( TRD, mem, BLOCKSIZE );
|
|
if( written != BLOCKSIZE ) {
|
|
ui_error( UI_ERROR_ERROR, "Error writing to temporary TRD file" );
|
|
free( mem );
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
free( mem );
|
|
|
|
if( lseek( TRD, 0, SEEK_SET ) == -1 ) {
|
|
ui_error( UI_ERROR_ERROR, "Error seeking in temporary TRD file" );
|
|
return 1;
|
|
}
|
|
|
|
TRDh = malloc( 4096 );
|
|
if( !TRDh ) {
|
|
ui_error( UI_ERROR_ERROR, "Out of memory at %s:%d", __FILE__, __LINE__ );
|
|
return 1;
|
|
}
|
|
|
|
if( read( TRD, TRDh, 4096 ) != 4096 ) {
|
|
ui_error( UI_ERROR_ERROR, "Error reading from temporary TRD file" );
|
|
free( TRDh );
|
|
return 1;
|
|
}
|
|
|
|
trd_free = TRDh + 0x8E5;
|
|
trd_files = TRDh + 0x8E4;
|
|
trd_fsec = TRDh + 0x8E1;
|
|
trd_ftrk = TRDh + 0x8E2;
|
|
|
|
if( ( SCL = open( oldname, O_RDONLY | O_BINARY ) ) == -1 ) {
|
|
ui_error( UI_ERROR_ERROR, "Can't open SCL file '%s': %s", oldname,
|
|
strerror( errno ) );
|
|
free( TRDh );
|
|
return 1;
|
|
}
|
|
|
|
if( read( SCL, &signature, 8 ) != 8 ) {
|
|
ui_error( UI_ERROR_ERROR, "Error reading from '%s'", oldname );
|
|
close( SCL ); free( TRDh );
|
|
return 1;
|
|
}
|
|
|
|
if( strncasecmp( signature, "SINCLAIR", 8 ) ) {
|
|
ui_error( UI_ERROR_ERROR, "SCL file '%s' has the wrong signature",
|
|
oldname );
|
|
close( SCL ); free( TRDh );
|
|
return 1;
|
|
}
|
|
|
|
if( read( SCL, &blocks, 1 ) != 1 ) {
|
|
ui_error( UI_ERROR_ERROR, "Error reading from '%s'", oldname );
|
|
close( SCL ); free( TRDh );
|
|
return 1;
|
|
}
|
|
|
|
for( x = 0; x < blocks; x++ ) {
|
|
if( read( SCL, &(headers[x][0]), 14 ) != 14 ) {
|
|
ui_error( UI_ERROR_ERROR, "Error reading from '%s'", oldname );
|
|
close( SCL ); free( TRDh );
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
for( x = 0; x < blocks; x++ ) {
|
|
size = headers[x][13];
|
|
if( lsb2dw(trd_free) < size ) {
|
|
ui_error( UI_ERROR_ERROR,
|
|
"File is too long to fit in the image *trd_free=%u < size=%u",
|
|
lsb2dw( trd_free ), size );
|
|
goto Finish;
|
|
}
|
|
|
|
if( *trd_files > 127 ) {
|
|
ui_error( UI_ERROR_ERROR, "Image is full" );
|
|
goto Finish;
|
|
}
|
|
|
|
memcpy( TRDh + *trd_files * 16, headers[x], 14 );
|
|
|
|
memcpy( TRDh + *trd_files * 16 + 0x0E, trd_fsec, 2 );
|
|
|
|
tmpscl = malloc( 32000 );
|
|
if( !tmpscl ) {
|
|
ui_error( UI_ERROR_ERROR, "Out of memory at %s:%d", __FILE__, __LINE__ );
|
|
goto Finish;
|
|
}
|
|
|
|
left = ( headers[x][13] ) * (libspectrum_dword)256;
|
|
fptr = (*trd_ftrk) * (libspectrum_dword)4096 +
|
|
(*trd_fsec) * (libspectrum_dword)256;
|
|
|
|
if( lseek( TRD, fptr, SEEK_SET ) == -1 ) {
|
|
ui_error( UI_ERROR_ERROR, "Error seeking in temporary TRD file: %s",
|
|
strerror( errno ) );
|
|
goto Finish;
|
|
}
|
|
|
|
while ( left > 32000 ) {
|
|
read( SCL, tmpscl, 32000 );
|
|
write( TRD, tmpscl, 32000 );
|
|
left -= 32000;
|
|
}
|
|
|
|
read( SCL, tmpscl, left );
|
|
write( TRD, tmpscl, left );
|
|
|
|
free( tmpscl );
|
|
|
|
(*trd_files)++;
|
|
|
|
dw2lsb( trd_free, lsb2dw(trd_free) - size );
|
|
|
|
while ( size > 15 ) {
|
|
(*trd_ftrk)++;
|
|
size -= 16;
|
|
}
|
|
|
|
(*trd_fsec) += size;
|
|
while ( (*trd_fsec) > 15 ) {
|
|
(*trd_fsec) -= 16;
|
|
(*trd_ftrk)++;
|
|
}
|
|
}
|
|
|
|
Finish:
|
|
close( SCL );
|
|
lseek( TRD, 0L, SEEK_SET );
|
|
write( TRD, TRDh, 4096 );
|
|
free( TRDh );
|
|
|
|
return 0;
|
|
}
|