Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
1852 lines (1593 sloc)
50.5 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| ** Oricutron | |
| ** Copyright (C) 2009-2014 Peter Gordon | |
| ** | |
| ** 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, version 2 | |
| ** of the License. | |
| ** | |
| ** 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. | |
| ** | |
| ** WD17xx, Microdisc and Jasmin emulation | |
| */ | |
| /* The microdisc ROM has a bug where if no disk */ | |
| /* is inserted at boot, the disk detection routine */ | |
| /* has an unbalanced stack and there is a chance */ | |
| /* that it will end up executing garbage when you */ | |
| /* insert a disk. Euphoric has a fudge to work */ | |
| /* around this. It pretends a disk is in the drive */ | |
| /* but leaves the "read sector" command lingering */ | |
| /* until you insert a disk. Set this define to */ | |
| /* do the same fudge */ | |
| #define MICRODISC_FUDGE | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include "system.h" | |
| #include "6502.h" | |
| #include "via.h" | |
| #include "8912.h" | |
| #include "gui.h" | |
| #include "disk.h" | |
| #include "disk_pravetz.h" | |
| #include "monitor.h" | |
| #include "6551.h" | |
| #include "machine.h" | |
| #include "msgbox.h" | |
| #include "filereq.h" | |
| extern char diskfile[], diskpath[], filetmp[]; | |
| extern char telediskfile[], telediskpath[]; | |
| extern char pravdiskfile[], pravdiskpath[]; | |
| extern SDL_bool refreshdisks; | |
| #define GENERAL_DISK_DEBUG 0 | |
| #define DEBUG_SECTOR_DUMP 0 | |
| #if DEBUG_SECTOR_DUMP | |
| static char sectordumpstr[64]; | |
| static int sectordumpcount; | |
| #endif | |
| #ifdef MICRODISC_FUDGE | |
| void microdisc_setintrq( void *md ); | |
| #endif | |
| static Uint16 calc_crc( Uint16 crc, Uint8 value) | |
| { | |
| crc = ((unsigned char)(crc >> 8)) | (crc << 8); | |
| crc ^= value; | |
| crc ^= ((unsigned char)(crc & 0xff)) >> 4; | |
| crc ^= (crc << 8) << 4; | |
| crc ^= ((crc & 0xff) << 4) << 1; | |
| return crc; | |
| } | |
| // Pop up disk image information for a couple of seconds | |
| void disk_popup( struct machine *oric, int drive ) | |
| { | |
| char tmp[40]; | |
| if( !oric->diskname[drive][0] ) | |
| { | |
| do_popup( oric, "\x14\x15\x13" ); | |
| return; | |
| } | |
| sprintf( tmp, "\x14\x15""%d %-16s", drive, oric->diskname[drive] ); | |
| do_popup( oric, tmp ); | |
| } | |
| // Free a disk image and clear the pointer to it | |
| static void diskimage_free( struct machine *oric, struct diskimage **dimg ) | |
| { | |
| char *dpath, *dfile; | |
| if( !dimg ) return; | |
| if( !(*dimg) ) return; | |
| if( oric->type != MACH_TELESTRAT ) | |
| { | |
| switch (oric->drivetype) | |
| { | |
| case DRV_PRAVETZ: | |
| dpath = pravdiskpath; | |
| dfile = pravdiskfile; | |
| break; | |
| default: | |
| dpath = diskpath; | |
| dfile = diskfile; | |
| break; | |
| } | |
| } | |
| else | |
| { | |
| dpath = telediskpath; | |
| dfile = telediskfile; | |
| } | |
| if( (*dimg)->modified ) | |
| { | |
| char modmsg[128]; | |
| sprintf( modmsg, "The disk in drive %d is modified.\nWould you like to save the changes?", (*dimg)->drivenum ); | |
| if( msgbox( oric, MSGBOX_YES_NO, modmsg ) ) | |
| { | |
| sprintf( modmsg, "Save disk %d", (*dimg)->drivenum ); | |
| if( filerequester( oric, modmsg, dpath, dfile, FR_DISKSAVE ) ) | |
| { | |
| joinpath( dpath, dfile ); | |
| diskimage_save( oric, filetmp, (*dimg)->drivenum ); | |
| } | |
| } | |
| } | |
| if( (*dimg)->rawimage ) | |
| { | |
| free( (*dimg)->rawimage ); | |
| (*dimg)->rawimage = NULL; | |
| } | |
| free( *dimg ); | |
| *dimg = NULL; | |
| } | |
| // Read a raw integer from a disk image file | |
| static Uint32 diskimage_rawint( struct diskimage *dimg, Uint32 offset ) | |
| { | |
| if( !dimg ) return 0; | |
| if( ( !dimg->rawimage ) || ( dimg->rawimagelen < 4 ) ) return 0; | |
| if( offset > (dimg->rawimagelen-4) ) return 0; | |
| return (dimg->rawimage[offset+3]<<24)|(dimg->rawimage[offset+2]<<16)|(dimg->rawimage[offset+1]<<8)|dimg->rawimage[offset]; | |
| } | |
| // Allocate and initialise a disk image structure | |
| // If "rawimglen" isn't zero, it will also allocate | |
| // ram for a raw disk image | |
| static struct diskimage *diskimage_alloc( Uint32 rawimglen ) | |
| { | |
| struct diskimage *dimg; | |
| Uint8 *buf = NULL; | |
| if( rawimglen ) | |
| { | |
| // FIXME: this is temporary solution to allow | |
| // low-level formatting up to 128 track per side | |
| if( rawimglen < 128*2*6400+256 ) rawimglen = 128*2*6400+256; | |
| buf = malloc( rawimglen ); | |
| if( !buf ) return NULL; | |
| } | |
| dimg = malloc( sizeof( struct diskimage ) ); | |
| if( !dimg ) | |
| { | |
| free( buf ); | |
| return NULL; | |
| } | |
| dimg->drivenum = -1; | |
| dimg->numtracks = 0; | |
| dimg->numsides = 0; | |
| dimg->geometry = 0; | |
| dimg->cachedtrack = -1; | |
| dimg->cachedside = -1; | |
| dimg->numsectors = 0; | |
| dimg->rawimage = buf; | |
| dimg->rawimagelen = rawimglen; | |
| dimg->modified = SDL_FALSE; | |
| dimg->modified_time = 0; | |
| return dimg; | |
| } | |
| // Eject a disk from a drive | |
| void disk_eject( struct machine *oric, int drive ) | |
| { | |
| diskimage_free( oric, &oric->wddisk.disk[drive] ); | |
| oric->wddisk.disk[drive] = NULL; | |
| oric->pravetz.drv[drive].pimg = NULL; | |
| oric->pravetz.drv[drive].byte = 0; | |
| oric->pravetz.drv[drive].dirty = SDL_FALSE; | |
| oric->pravetz.drv[drive].half_track = 0; | |
| oric->diskname[drive][0] = 0; | |
| disk_popup( oric, drive ); | |
| } | |
| // Whenever a seek operation occurs, the track where the head ends up | |
| // is "cached". The track is located within the raw image file, and | |
| // all the sector address and data markers are found, and pointers | |
| // are remembered for each. | |
| void diskimage_cachetrack( struct diskimage *dimg, int track, int side ) | |
| { | |
| Uint8 *ptr, *eot; | |
| Uint32 sectorcount, n; | |
| // If this track is already cached, don't waste time doing it again | |
| if( ( dimg->cachedtrack == track ) && | |
| ( dimg->cachedside == side ) ) | |
| return; | |
| // reset cached info | |
| for( n=0; n<32; n++ ) | |
| { | |
| dimg->sector[n].id_ptr = NULL; | |
| dimg->sector[n].data_ptr = NULL; | |
| } | |
| dimg->numsectors = 0; | |
| dimg->cachedtrack = -1; | |
| dimg->cachedside = -1; | |
| if( side >= dimg->numsides ) | |
| return; | |
| // Find the start and end locations of the track within the disk image | |
| ptr = &dimg->rawimage[(side*dimg->numtracks+track)*6400+256]; | |
| eot = &ptr[6400]; | |
| // Scan through the track looking for sectors | |
| sectorcount = 0; | |
| while( ptr < eot ) | |
| { | |
| // Search for ID mark | |
| while( ((ptr+3)<eot) && !((ptr[0]==0xa1) && (ptr[1]==0xa1) && (ptr[2]==0xa1) && (ptr[3]==0xfe)) ) ptr++; | |
| ptr += 3; | |
| // Don't exceed the bounds of this track | |
| if( ptr >= eot ) break; | |
| // Store ID pointer | |
| dimg->sector[sectorcount].id_ptr = ptr; | |
| dimg->sector[sectorcount].data_ptr = NULL; | |
| sectorcount++; | |
| // Get N value | |
| n = ptr[4]; | |
| // Skip ID field and CRC | |
| ptr+=7; | |
| // Search for data ID | |
| while( (ptr<eot) && (ptr[0]!=0xfb) && (ptr[0]!=0xf8) ) ptr++; | |
| if( ptr >= eot ) break; | |
| // Store pointer | |
| dimg->sector[sectorcount-1].data_ptr = ptr; | |
| // Skip data field and ID | |
| ptr += (1<<(n+7))+3; | |
| } | |
| if( 0 < sectorcount ) | |
| { | |
| // Remember how many sectors we have successfully cached | |
| dimg->numsectors = sectorcount; | |
| dimg->cachedtrack = track; | |
| dimg->cachedside = side; | |
| } | |
| } | |
| // This saves a diskimage back to disk. | |
| // Since the disk image is always kept in standard format, there is | |
| // no processing of the image in this routine, it is just dumped from | |
| // memory back to disk. | |
| SDL_bool diskimage_save( struct machine *oric, char *fname, int drive ) | |
| { | |
| FILE *f; | |
| // Make sure there is a disk in the drive! | |
| if( !oric->wddisk.disk[drive] ) return SDL_FALSE; | |
| if( oric->drivetype == DRV_PRAVETZ ) | |
| disk_pravetz_write_image(&oric->pravetz.drv[drive]); | |
| // Open the file for writing | |
| f = fopen( fname, "wb" ); | |
| if( !f ) | |
| { | |
| do_popup( oric, "\x14\x15Save failed" ); | |
| return SDL_FALSE; | |
| } | |
| // Dump it to disk | |
| if( fwrite( oric->wddisk.disk[drive]->rawimage, oric->wddisk.disk[drive]->rawimagelen, 1, f ) != 1 ) | |
| { | |
| fclose( f ); | |
| do_popup( oric, "\x14\x15Save failed" ); | |
| return SDL_FALSE; | |
| } | |
| // All done! | |
| fclose( f ); | |
| // If we are not just overwriting the original file, remember the new filename | |
| if( fname != oric->wddisk.disk[drive]->filename ) | |
| { | |
| strncpy( oric->wddisk.disk[drive]->filename, fname, 4096+512-1 ); | |
| oric->wddisk.disk[drive]->filename[4096+512-1] = 0; | |
| } | |
| // The image in memory is no longer different to the last saved version | |
| oric->wddisk.disk[drive]->modified = SDL_FALSE; | |
| oric->wddisk.disk[drive]->modified_time = 0; | |
| // Remember to update the GUI | |
| refreshdisks = SDL_TRUE; | |
| return SDL_TRUE; | |
| } | |
| // This routine "inserts" a disk image into a virtual drive | |
| SDL_bool diskimage_load( struct machine *oric, char *fname, int drive ) | |
| { | |
| FILE *f; | |
| Uint32 len; | |
| // Open the file | |
| f = fopen( fname, "rb" ); | |
| if( !f ) return SDL_FALSE; | |
| // The file exists, so eject any currently inserted disk | |
| disk_eject( oric, drive ); | |
| // Determine the size of the disk image | |
| fseek( f, 0, SEEK_END ); | |
| len = (int)ftell( f ); | |
| fseek( f, 0, SEEK_SET ); | |
| // Empty file!? | |
| if( len <= 0 ) | |
| { | |
| fclose( f ); | |
| return SDL_FALSE; | |
| } | |
| // Allocate a new disk image structure and space for the raw image | |
| oric->wddisk.disk[drive] = diskimage_alloc( len ); | |
| if( !oric->wddisk.disk[drive] ) | |
| { | |
| do_popup( oric, "\x14\x15""Out of memory" ); | |
| fclose( f ); | |
| return SDL_FALSE; | |
| } | |
| // Read the image file into memory | |
| if( fread( oric->wddisk.disk[drive]->rawimage, len, 1, f ) != 1 ) | |
| { | |
| fclose( f ); | |
| disk_eject( oric, drive ); | |
| do_popup( oric, "\x14\x15""Read error" ); | |
| return SDL_FALSE; | |
| } | |
| fclose( f ); | |
| if( oric->drivetype == DRV_PRAVETZ ) | |
| { | |
| Uint16 t_idx; | |
| Uint16 s_idx; | |
| Uint16 sb_idx; | |
| Uint16 tb_idx; | |
| oric->pravetz.drv[drive].pimg = oric->wddisk.disk[drive]; | |
| if( drive >= 2 ) | |
| { | |
| disk_eject( oric, drive ); | |
| do_popup( oric, "\x14\x15""Invalid drive number" ); | |
| return SDL_FALSE; | |
| } | |
| if( len != 143360 ) | |
| { | |
| disk_eject( oric, drive ); | |
| do_popup( oric, "\x14\x15""Invalid pravetz disk image" ); | |
| return SDL_FALSE; | |
| } | |
| // Get some basic image info | |
| for (t_idx = 0; t_idx < PRAV_TRACKS_PER_DISK; t_idx++) | |
| { | |
| tb_idx = 0; | |
| for (s_idx = 0; s_idx < PRAV_SECTORS_PER_TRACK; s_idx++) | |
| { | |
| for (sb_idx = 0; sb_idx < PRAV_RAW_BYTES_PER_SECTOR; sb_idx++, tb_idx++) | |
| { | |
| oric->pravetz.drv[drive].image[t_idx][tb_idx] = | |
| disk_pravetz_image_raw_byte(oric, drive, t_idx, s_idx, sb_idx); | |
| } | |
| } | |
| while (tb_idx < PRAV_RAW_TRACK_SIZE) | |
| { | |
| oric->pravetz.drv[drive].image[t_idx][tb_idx] = 0xFF; | |
| tb_idx++; | |
| } | |
| } | |
| oric->pravetz.drv[drive].byte = 0; | |
| oric->pravetz.drv[drive].half_track = 0; | |
| } | |
| else | |
| { | |
| // Check for the header ID | |
| if( strncmp( (char *)oric->wddisk.disk[drive]->rawimage, "MFM_DISK", 8 ) != 0 ) | |
| { | |
| disk_eject( oric, drive ); | |
| do_popup( oric, "\x14\x15""Invalid disk image" ); | |
| return SDL_FALSE; | |
| } | |
| // Get some basic image info | |
| oric->wddisk.disk[drive]->drivenum = drive; | |
| oric->wddisk.disk[drive]->numsides = diskimage_rawint( oric->wddisk.disk[drive], 8 ); | |
| oric->wddisk.disk[drive]->numtracks = diskimage_rawint( oric->wddisk.disk[drive], 12 ); | |
| oric->wddisk.disk[drive]->geometry = diskimage_rawint( oric->wddisk.disk[drive], 16 ); | |
| // Is the disk sane!? | |
| if( ( oric->wddisk.disk[drive]->numsides < 1 ) || | |
| ( oric->wddisk.disk[drive]->numsides > 2 ) ) | |
| { | |
| disk_eject( oric, drive ); | |
| do_popup( oric, "\x14\x15""Invalid disk image" ); | |
| return SDL_FALSE; | |
| } | |
| } | |
| // Nobody has written to this disk yet | |
| oric->wddisk.disk[drive]->modified = SDL_FALSE; | |
| oric->wddisk.disk[drive]->modified_time = 0; | |
| // Remember the filename of the image for this drive | |
| strncpy( oric->wddisk.disk[drive]->filename, fname, 4096+512-1 ); | |
| oric->wddisk.disk[drive]->filename[4096+512-1] = 0; | |
| // Come up with a suitable short name for popups etc. | |
| if( strlen( fname ) > 31 ) | |
| { | |
| strncpy( oric->diskname[drive], &fname[strlen(fname)-31], 32 ); | |
| oric->diskname[drive][0] = 22; | |
| } else { | |
| strncpy( oric->diskname[drive], fname, 32 ); | |
| } | |
| oric->diskname[drive][31] = 0; | |
| // Do the popup | |
| disk_popup( oric, drive ); | |
| // Mark the disk status icons as needing a refresh | |
| refreshdisks = SDL_TRUE; | |
| return SDL_TRUE; | |
| }; | |
| // This routine does nothing. It is just used as a default for the callback routines | |
| // and is usually replaced by the microdisc/jasmin/whatever implementations. | |
| void wd17xx_dummy( void *nothing ) | |
| { | |
| } | |
| // Initialise a WD17xx controller instance | |
| void wd17xx_init( struct wd17xx *wd ) | |
| { | |
| wd->r_status = 0; | |
| wd->r_track = 0; | |
| wd->r_sector = 0; | |
| wd->r_data = 0; | |
| wd->c_drive = 0; | |
| wd->c_side = 0; | |
| wd->c_track = 0; | |
| wd->c_sector = 0; | |
| wd->last_step_in = SDL_FALSE; | |
| wd->setintrq = wd17xx_dummy; | |
| wd->clrintrq = wd17xx_dummy; | |
| wd->intrqarg = NULL; | |
| wd->setdrq = wd17xx_dummy; | |
| wd->clrdrq = wd17xx_dummy; | |
| wd->drqarg = NULL; | |
| wd->currentop = COP_NUFFINK; | |
| wd->delayedint = 0; | |
| wd->delayeddrq = 0; | |
| wd->distatus = -1; | |
| wd->ddstatus = -1; | |
| refreshdisks = SDL_TRUE; | |
| } | |
| // This routine emulates some cycles of disk activity. | |
| void wd17xx_ticktock( struct wd17xx *wd, int cycles ) | |
| { | |
| #ifdef MICRODISC_FUDGE | |
| if ((wd->currentop == COP_READ_SECTORS_FUDGE) || | |
| (wd->currentop == COP_READ_SECTOR_FUDGE)) | |
| { | |
| if (wd->disk[wd->c_drive]) | |
| { | |
| wd->curroffs = 0; | |
| wd->currsector = wd17xx_find_sector( wd, wd->r_sector ); | |
| if( !wd->currsector ) | |
| { | |
| wd->r_status = WSF_RNF; | |
| wd->clrdrq( wd->drqarg ); | |
| wd->setintrq( wd->intrqarg ); | |
| wd->currentop = COP_NUFFINK; | |
| refreshdisks = SDL_TRUE; | |
| #if GENERAL_DISK_DEBUG | |
| dbg_printf( "DISK: Sector %d not found.", wd->r_sector ); | |
| #endif | |
| } | |
| else | |
| { | |
| wd->currseclen = 1<<(wd->currsector->id_ptr[4]+7); | |
| wd->r_status = WSF_BUSY|WSF_NOTREADY; | |
| wd->delayeddrq = 60; | |
| wd->currentop = (wd->currentop == COP_READ_SECTORS_FUDGE) ? COP_READ_SECTORS : COP_READ_SECTOR; | |
| wd->crc = 0xe295; | |
| refreshdisks = SDL_TRUE; | |
| #if DEBUG_SECTOR_DUMP | |
| sectordumpcount = 0; | |
| sectordumpstr[0] = 0; | |
| #endif | |
| } | |
| } | |
| } | |
| #endif | |
| // Is there a pending INTRQ? | |
| if( wd->delayedint > 0 ) | |
| { | |
| // Count down the INTRQ timer! | |
| wd->delayedint -= cycles; | |
| // Time to assert INTRQ? | |
| if( wd->delayedint <= 0 ) | |
| { | |
| // Yep! Stop timing. | |
| wd->delayedint = 0; | |
| // Need to update the status register? | |
| if( wd->distatus != -1 ) | |
| { | |
| // Yep. Do so. | |
| wd->r_status = wd->distatus; | |
| wd->distatus = -1; | |
| } | |
| // Assert INTRQ (this function pointer is set up by the microdisc/jasmin/whatever controller) | |
| wd->setintrq( wd->intrqarg ); | |
| #if GENERAL_DISK_DEBUG | |
| dbg_printf( "DISK: Delayed INTRQ" ); | |
| #endif | |
| } | |
| } | |
| // Is there a pending DRQ? | |
| if( wd->delayeddrq > 0 ) | |
| { | |
| // Count down the DRQ timer! | |
| wd->delayeddrq -= cycles; | |
| // Time to assert DRQ? | |
| if( wd->delayeddrq <= 0 ) | |
| { | |
| // Yep! Stop timing. | |
| wd->delayeddrq = 0; | |
| // Need to update the status register? | |
| if( wd->ddstatus != -1 ) | |
| { | |
| // Yep. Do so. | |
| wd->r_status = wd->ddstatus; | |
| wd->ddstatus = -1; | |
| } | |
| // Assert DRQ | |
| wd->r_status |= WSF_DRQ; | |
| wd->setdrq( wd->drqarg ); | |
| } | |
| } | |
| } | |
| // This routine seeks to the specified track. It is used by the SEEK and STEP commands. | |
| void wd17xx_seek_track( struct wd17xx *wd, Uint8 track, SDL_bool dofudge ) | |
| { | |
| // Is there a disk in the drive? | |
| if( wd->disk[wd->c_drive] ) | |
| { | |
| // Yes. If we are trying to seek to a non-existant track, just seek as far as we can | |
| if( track >= wd->disk[wd->c_drive]->numtracks ) | |
| { | |
| track = (wd->disk[wd->c_drive]->numtracks>0)?wd->disk[wd->c_drive]->numtracks-1:0; | |
| wd->distatus = WSFI_HEADL|WSFI_SEEKERR; | |
| } | |
| else | |
| { | |
| wd->distatus = WSFI_HEADL|WSFI_PULSE; | |
| } | |
| // Cache the new track | |
| diskimage_cachetrack( wd->disk[wd->c_drive], track, wd->c_side ); | |
| // Update our status | |
| wd->c_track = track; | |
| wd->c_sector = 0; | |
| wd->r_track = track; | |
| // Assert INTRQ in 20 cycles time and update the status accordingly | |
| // (note: 20 cycles is waaaaaay faster than any real drive could seek. The actual | |
| // delay would depend how far the head had to seek, and what stepping speed was | |
| // currently set). | |
| wd->delayedint = 20; | |
| if( wd->c_track == 0 ) wd->distatus |= WSFI_TRK0; | |
| #if GENERAL_DISK_DEBUG | |
| dbg_printf( "DISK: At track %u (%u sectors)", track, wd->disk[wd->c_drive]->numsectors ); | |
| #endif | |
| return; | |
| } | |
| // No disk in drive | |
| // Set INTRQ because the operation has finished. | |
| wd->setintrq( wd->intrqarg ); | |
| wd->r_track = 0; | |
| #if GENERAL_DISK_DEBUG | |
| dbg_printf( "DISK: Seek fail" ); | |
| #endif | |
| #ifdef MICRODISC_FUDGE | |
| // Euphoric style workaround for the sedoric bug... | |
| if ((dofudge) && (wd->setintrq == microdisc_setintrq)) | |
| { | |
| wd->r_status = 0x44; | |
| return; | |
| } | |
| #endif | |
| // Set error state | |
| wd->r_status = WSF_NOTREADY|WSFI_SEEKERR; | |
| } | |
| // This routine looks for the sector with the specified ID in the current track. | |
| // It returns NULL if there is no such sector, or a structure with pointers to | |
| // the ID and data fields if the sector is found. | |
| struct mfmsector *wd17xx_find_sector( struct wd17xx *wd, Uint8 secid ) | |
| { | |
| int revs=0; | |
| struct diskimage *dimg; | |
| // Save some typing... | |
| dimg = wd->disk[wd->c_drive]; | |
| // No disk image? No sectors! | |
| if( !dimg ) return NULL; | |
| // Make sure the current track is cached | |
| diskimage_cachetrack( dimg, wd->c_track, wd->c_side ); | |
| // No sectors on this track? Someone needs to format their disk... | |
| if( dimg->numsectors < 1 ) | |
| return NULL; | |
| // We do this more realistically than we need to since this is not | |
| // a super-accurate emulation (for now). Never mind. Lets go | |
| // around the track up to two times. | |
| while( revs < 2 ) | |
| { | |
| // Move on to the next sector | |
| wd->c_sector = (wd->c_sector+1)%dimg->numsectors; | |
| // If we passed through the start of the track, set the pulse bit in the status register | |
| if( !wd->c_sector ) | |
| { | |
| revs++; | |
| wd->r_status |= WSFI_PULSE; | |
| } | |
| // Found the required sector? | |
| if( dimg->sector[wd->c_sector].id_ptr[3] == secid ) | |
| return &dimg->sector[wd->c_sector]; | |
| } | |
| // The search failed :-( | |
| #if GENERAL_DISK_DEBUG | |
| dbg_printf( "Couldn't find sector %u", secid ); | |
| #endif | |
| return NULL; | |
| } | |
| // This returns the first valid sector in the current track, or NULL if | |
| // there aren't any sectors. | |
| struct mfmsector *wd17xx_first_sector( struct wd17xx *wd ) | |
| { | |
| struct diskimage *dimg; | |
| dimg = wd->disk[wd->c_drive]; | |
| // No disk? no sector... | |
| if( !dimg ) return NULL; | |
| // Make sure the current track is cached | |
| diskimage_cachetrack( dimg, wd->c_track, wd->c_side ); | |
| // No sectors?! | |
| if( dimg->numsectors < 1 ) | |
| return NULL; | |
| // We're at the first sector! | |
| wd->c_sector = 0; | |
| wd->r_status = WSFI_PULSE; | |
| // Return the sector pointers | |
| return &dimg->sector[wd->c_sector]; | |
| } | |
| // Move on to the next sector | |
| struct mfmsector *wd17xx_next_sector( struct wd17xx *wd ) | |
| { | |
| struct diskimage *dimg; | |
| dimg = wd->disk[wd->c_drive]; | |
| // No disk? No sectors! | |
| if( !dimg ) return NULL; | |
| // Make sure the current track is cached | |
| diskimage_cachetrack( dimg, wd->c_track, wd->c_side ); | |
| // No sectors? | |
| if( dimg->numsectors < 1 ) | |
| return NULL; | |
| // Get the next sector number | |
| wd->c_sector = (wd->c_sector+1)%dimg->numsectors; | |
| // If we are at the start of the track, set the pulse bit | |
| if( !wd->c_sector ) wd->r_status |= WSFI_PULSE; | |
| // Return the sector pointers | |
| return &dimg->sector[wd->c_sector]; | |
| } | |
| // Perform a read operation on a WD17xx register | |
| unsigned char wd17xx_read( struct wd17xx *wd, unsigned short addr ) | |
| { | |
| // Which register?! | |
| switch( addr ) | |
| { | |
| case 0: // Status register | |
| wd->clrintrq( wd->intrqarg ); // Reading the status register clears INTRQ | |
| return wd->r_status; | |
| case 1: // Track register | |
| return wd->r_track; | |
| case 2: // Sector register | |
| return wd->r_sector; | |
| case 3: // Data register | |
| // What are we currently doing? | |
| switch( wd->currentop ) | |
| { | |
| #ifdef MICRODISC_FUDGE | |
| case COP_READ_SECTOR_FUDGE: | |
| case COP_READ_SECTORS_FUDGE: | |
| return 0; | |
| #endif | |
| case COP_READ_SECTOR: | |
| case COP_READ_SECTORS: | |
| // We somehow started a sector read operation without a valid sector. | |
| if( !wd->currsector ) | |
| { | |
| // Abort. | |
| wd->r_status &= ~WSF_DRQ; | |
| wd->r_status |= WSF_RNF; | |
| wd->clrdrq( wd->drqarg ); | |
| wd->currentop = COP_NUFFINK; | |
| refreshdisks = SDL_TRUE; | |
| break; | |
| } | |
| // If this is the first read of a read operation, remember the record type for later | |
| if( wd->curroffs == 0 ) wd->sectype = (wd->currsector->data_ptr[wd->curroffs++]==0xf8)?WSFR_RECTYP:0x00; | |
| // Get the next byte from the sector | |
| wd->r_data = wd->currsector->data_ptr[wd->curroffs++]; | |
| wd->crc = calc_crc( wd->crc, wd->r_data ); | |
| // Clear any previous DRQ | |
| wd->r_status &= ~WSF_DRQ; | |
| wd->clrdrq( wd->drqarg ); | |
| #if DEBUG_SECTOR_DUMP | |
| sprintf( §ordumpstr[sectordumpcount*2], "%02X", wd->r_data ); | |
| sectordumpstr[34+sectordumpcount] = ((wd->r_data>31)&&(wd->r_data<127)) ? wd->r_data : '.'; | |
| sectordumpcount++; | |
| if( sectordumpcount >= 16 ) | |
| { | |
| sectordumpstr[32] = ' '; | |
| sectordumpstr[33] = '\''; | |
| sectordumpstr[50] = '\''; | |
| sectordumpstr[51] = 0; | |
| dbg_printf( "%s", sectordumpstr ); | |
| sectordumpcount = 0; | |
| } | |
| #endif | |
| // Has the whole sector been read? | |
| if( wd->curroffs > wd->currseclen ) | |
| { | |
| // If you want to do CRC checking, wd->crc should equal (wd->currsector->data_ptr[wd_curroffs]<<8)|wd->currsector->data_ptr[wd_curroffs+1] right here | |
| #if DEBUG_SECTOR_DUMP | |
| if( sectordumpcount ) | |
| { | |
| sectordumpstr[33] = '\''; | |
| sectordumpstr[34+sectordumpcount] = '\''; | |
| sectordumpstr[35+sectordumpcount] = 0; | |
| for( sectordumpcount*=2; sectordumpcount<33; sectordumpcount++ ) | |
| sectordumpstr[sectordumpcount*2] = ' '; | |
| dbg_printf( "%s", sectordumpstr ); | |
| sectordumpcount = 0; | |
| } | |
| #endif | |
| // We've got to the end of the current sector. IF it is a multiple sector | |
| // operation, we need to move on! | |
| if( wd->currentop == COP_READ_SECTORS ) | |
| { | |
| #if GENERAL_DISK_DEBUG | |
| dbg_printf( "Next sector..." ); | |
| #endif | |
| // Get the next sector, and carry on! | |
| wd->r_sector++; | |
| wd->curroffs = 0; | |
| wd->currsector = wd17xx_find_sector( wd, wd->r_sector ); | |
| wd->crc = 0xe295; | |
| // If we hit the end of the track, thats fine, it just means the operation | |
| // is finished. | |
| if( !wd->currsector ) | |
| { | |
| wd->delayedint = 20; // Assert INTRQ in 20 cycles time | |
| wd->distatus = wd->sectype; // ...and when doing so, set the status to reflect the record type | |
| wd->currentop = COP_NUFFINK; // No longer in the middle of an operation | |
| wd->r_status &= (~WSF_DRQ); // Clear DRQ (no data to read) | |
| wd->clrdrq( wd->drqarg ); | |
| refreshdisks = SDL_TRUE; // Turn off the disk LED in the status bar | |
| break; | |
| } | |
| // We've got the next sector lined up. Assert DRQ in 180 cycles time (simulate a bit of a delay | |
| // between sectors. Note that most of these values have been pulled out of thin air and might need | |
| // adjusting for some pickier loaders). | |
| wd->delayeddrq = 180; | |
| break; | |
| } | |
| // Just reading one sector so.. | |
| wd->delayedint = 32; // INTRQ in a little while because we're finished | |
| wd->distatus = wd->sectype; // Set the status accordingly | |
| wd->currentop = COP_NUFFINK; // Finished the op | |
| wd->r_status &= (~WSF_DRQ); // Clear DRQ (no more data) | |
| wd->clrdrq( wd->drqarg ); | |
| refreshdisks = SDL_TRUE; // Turn off disk LED | |
| } else { | |
| wd->delayeddrq = 32; // More data ready. DRQ to let them know! | |
| } | |
| break; | |
| case COP_READ_TRACK: | |
| wd->r_data = wd->disk[wd->c_drive]->rawimage[(wd->c_side*wd->disk[wd->c_drive]->numtracks + wd->c_track)*6400+256 + wd->curroffs]; | |
| wd->curroffs++; | |
| wd->r_status &= ~WSF_DRQ; | |
| wd->clrdrq( wd->drqarg ); | |
| // Has the whole track been read? | |
| if( wd->curroffs >= 6400 ) | |
| { | |
| wd->delayedint = 20; | |
| wd->distatus = 0; | |
| wd->currentop = COP_NUFFINK; | |
| refreshdisks = SDL_TRUE; | |
| } else { | |
| wd->delayeddrq = 32; | |
| } | |
| break; | |
| case COP_READ_ADDRESS: | |
| if( !wd->currsector ) | |
| { | |
| wd->r_status &= ~WSF_DRQ; | |
| wd->clrdrq( wd->drqarg ); | |
| wd->currentop = COP_NUFFINK; | |
| refreshdisks = SDL_TRUE; | |
| break; | |
| } | |
| if( wd->curroffs == 0 ) wd->r_sector = wd->currsector->id_ptr[1]; | |
| wd->r_data = wd->currsector->id_ptr[++wd->curroffs]; | |
| wd->r_status &= ~WSF_DRQ; | |
| wd->clrdrq( wd->drqarg ); | |
| if( wd->curroffs >= 6 ) | |
| { | |
| wd->delayedint = 20; | |
| wd->distatus = 0; | |
| wd->currentop = COP_NUFFINK; | |
| refreshdisks = SDL_TRUE; | |
| } else { | |
| wd->delayeddrq = 32; | |
| } | |
| break; | |
| } | |
| return wd->r_data; | |
| } | |
| return 0; // ?? | |
| } | |
| static SDL_bool last_step_in = SDL_FALSE; | |
| void wd17xx_write( struct machine *oric, struct wd17xx *wd, unsigned short addr, unsigned char data ) | |
| { | |
| switch( addr ) | |
| { | |
| case 0: // Command register | |
| wd->clrintrq( wd->intrqarg ); | |
| switch( data & 0xe0 ) | |
| { | |
| case 0x00: // Restore or seek | |
| switch( data & 0x10 ) | |
| { | |
| case 0x00: // Restore (Type I) | |
| #if GENERAL_DISK_DEBUG | |
| dbg_printf( "DISK: (%04X) Restore", oric->cpu.pc-1 ); | |
| #endif | |
| wd->r_status = WSF_BUSY; | |
| if( data & 8 ) wd->r_status |= WSFI_HEADL; | |
| wd17xx_seek_track( wd, 0, oric->cpu.calcpc == 0xe3a1 ); | |
| wd->currentop = COP_NUFFINK; | |
| refreshdisks = SDL_TRUE; | |
| break; | |
| case 0x10: // Seek (Type I) | |
| #if GENERAL_DISK_DEBUG | |
| dbg_printf( "DISK: (%04X) Seek", oric->cpu.pc-1 ); | |
| #endif | |
| wd->r_status = WSF_BUSY; | |
| if( data & 8 ) wd->r_status |= WSFI_HEADL; | |
| wd17xx_seek_track( wd, wd->r_data, SDL_FALSE ); | |
| wd->currentop = COP_NUFFINK; | |
| refreshdisks = SDL_TRUE; | |
| break; | |
| } | |
| break; | |
| case 0x20: // Step (Type I) | |
| #if GENERAL_DISK_DEBUG | |
| dbg_printf( "DISK: (%04X) Step", oric->cpu.pc-1 ); | |
| #endif | |
| wd->r_status = WSF_BUSY; | |
| if( data & 8 ) wd->r_status |= WSFI_HEADL; | |
| if( last_step_in ) | |
| wd17xx_seek_track( wd, wd->c_track+1, SDL_FALSE ); | |
| else | |
| wd17xx_seek_track( wd, wd->c_track > 0 ? wd->c_track-1 : 0, SDL_FALSE ); | |
| wd->currentop = COP_NUFFINK; | |
| refreshdisks = SDL_TRUE; | |
| break; | |
| case 0x40: // Step-in (Type I) | |
| #if GENERAL_DISK_DEBUG | |
| dbg_printf( "DISK: (%04X) Step-In (%d,%d)", oric->cpu.pc-1, wd->c_track, wd->c_track+1 ); | |
| #endif | |
| wd->r_status = WSF_BUSY; | |
| if( data & 8 ) wd->r_status |= WSFI_HEADL; | |
| wd17xx_seek_track( wd, wd->c_track+1, SDL_FALSE ); | |
| last_step_in = SDL_TRUE; | |
| wd->currentop = COP_NUFFINK; | |
| refreshdisks = SDL_TRUE; | |
| break; | |
| case 0x60: // Step-out (Type I) | |
| #if GENERAL_DISK_DEBUG | |
| dbg_printf( "DISK: Step-Out" ); | |
| #endif | |
| wd->r_status = WSF_BUSY; | |
| if( data & 8 ) wd->r_status |= WSFI_HEADL; | |
| if( wd->c_track > 0 ) | |
| wd17xx_seek_track( wd, wd->c_track-1, SDL_FALSE ); | |
| last_step_in = SDL_FALSE; | |
| wd->currentop = COP_NUFFINK; | |
| refreshdisks = SDL_TRUE; | |
| break; | |
| case 0x80: // Read sector (Type II) | |
| #if GENERAL_DISK_DEBUG | |
| switch( oric->drivetype ) | |
| { | |
| case DRV_MICRODISC: | |
| dbg_printf( "DISK: (%04X) Read sector %u (CODE=%02X,ROM=%s,EPROM=%s)", oric->cpu.pc-1, wd->r_sector, data, oric->romdis?"OFF":"ON", oric->md.diskrom?"ON":"OFF" ); | |
| break; | |
| default: | |
| dbg_printf( "DISK: (%04X) Read sector %u (CODE=%02X)", oric->cpu.pc-1, wd->r_sector, data ); | |
| break; | |
| } | |
| #endif | |
| #ifdef MICRODISC_FUDGE | |
| if (!wd->disk[wd->c_drive]) | |
| { | |
| wd->currentop = (data&0x10) ? COP_READ_SECTORS_FUDGE : COP_READ_SECTOR_FUDGE; | |
| break; | |
| } | |
| #endif | |
| wd->curroffs = 0; | |
| wd->currsector = wd17xx_find_sector( wd, wd->r_sector ); | |
| if( !wd->currsector ) | |
| { | |
| wd->r_status = WSF_RNF; | |
| wd->clrdrq( wd->drqarg ); | |
| wd->setintrq( wd->intrqarg ); | |
| wd->currentop = COP_NUFFINK; | |
| refreshdisks = SDL_TRUE; | |
| #if GENERAL_DISK_DEBUG | |
| dbg_printf( "DISK: Sector %d not found.", wd->r_sector ); | |
| #endif | |
| // setemumode( oric, NULL, EM_DEBUG ); | |
| break; | |
| } | |
| wd->currseclen = 1<<(wd->currsector->id_ptr[4]+7); | |
| wd->r_status = WSF_BUSY|WSF_NOTREADY; | |
| wd->delayeddrq = 60; | |
| wd->currentop = (data&0x10) ? COP_READ_SECTORS : COP_READ_SECTOR; | |
| wd->crc = 0xe295; | |
| refreshdisks = SDL_TRUE; | |
| #if DEBUG_SECTOR_DUMP | |
| sectordumpcount = 0; | |
| sectordumpstr[0] = 0; | |
| #endif | |
| break; | |
| case 0xa0: // Write sector (Type II) | |
| #if GENERAL_DISK_DEBUG | |
| switch( oric->drivetype ) | |
| { | |
| case DRV_MICRODISC: | |
| dbg_printf( "DISK: (%04X) Write sector %u (CODE=%02X,ROM=%s,EPROM=%s)", oric->cpu.pc-1, wd->r_sector, data, oric->romdis?"OFF":"ON", oric->md.diskrom?"ON":"OFF" ); | |
| break; | |
| default: | |
| dbg_printf( "DISK: (%04X) Write sector %u (CODE=%02X)", oric->cpu.pc-1, wd->r_sector, data ); | |
| break; | |
| } | |
| #endif | |
| wd->curroffs = 0; | |
| wd->currsector = wd17xx_find_sector( wd, wd->r_sector ); | |
| if( !wd->currsector ) | |
| { | |
| wd->r_status = WSF_RNF; | |
| wd->clrdrq( wd->drqarg ); | |
| wd->setintrq( wd->intrqarg ); | |
| wd->currentop = COP_NUFFINK; | |
| refreshdisks = SDL_TRUE; | |
| #if GENERAL_DISK_DEBUG | |
| dbg_printf( "DISK: Sector %d not found.", wd->r_sector ); | |
| #endif | |
| // setemumode( oric, NULL, EM_DEBUG ); | |
| break; | |
| } | |
| wd->currseclen = 1<<(wd->currsector->id_ptr[4]+7); | |
| wd->r_status = WSF_BUSY|WSF_NOTREADY; | |
| wd->delayeddrq = 500; | |
| wd->currentop = (data&0x10) ? COP_WRITE_SECTORS : COP_WRITE_SECTOR; | |
| wd->crc = 0xe295; | |
| refreshdisks = SDL_TRUE; | |
| break; | |
| case 0xc0: // Read address / Force IRQ | |
| switch( data & 0x10 ) | |
| { | |
| case 0x00: // Read address (Type III) | |
| #if GENERAL_DISK_DEBUG | |
| dbg_printf( "DISK: (%04X) Read address", oric->cpu.pc-1 ); | |
| #endif | |
| wd->curroffs = 0; | |
| if( !wd->currsector ) | |
| wd->currsector = wd17xx_first_sector( wd ); | |
| else | |
| wd->currsector = wd17xx_next_sector( wd ); | |
| if( !wd->currsector ) | |
| { | |
| wd->r_status = WSF_RNF; | |
| wd->clrdrq( wd->drqarg ); | |
| wd->currentop = COP_NUFFINK; | |
| wd->setintrq( wd->intrqarg ); | |
| refreshdisks = SDL_TRUE; | |
| #if GENERAL_DISK_DEBUG | |
| dbg_printf( "DISK: No sectors on this track?" ); | |
| #endif | |
| break; | |
| } | |
| #if GENERAL_DISK_DEBUG | |
| dbg_printf( "DISK: %02X,%02X,%02X,%02X,%02X,%02X", | |
| wd->currsector->id_ptr[1], | |
| wd->currsector->id_ptr[2], | |
| wd->currsector->id_ptr[3], | |
| wd->currsector->id_ptr[4], | |
| wd->currsector->id_ptr[5], | |
| wd->currsector->id_ptr[6] ); | |
| #endif | |
| wd->r_status = WSF_NOTREADY|WSF_BUSY|WSF_DRQ; | |
| wd->setdrq( wd->drqarg ); | |
| wd->currentop = COP_READ_ADDRESS; | |
| refreshdisks = SDL_TRUE; | |
| break; | |
| case 0x10: // Force Interrupt (Type IV) | |
| #if GENERAL_DISK_DEBUG | |
| dbg_printf( "DISK: (%04X) Force int", oric->cpu.pc-1 ); | |
| #endif | |
| wd->r_status = 0; | |
| wd->clrdrq( wd->drqarg ); | |
| wd->setintrq( wd->intrqarg ); | |
| wd->delayedint = 0; | |
| wd->delayeddrq = 0; | |
| wd->currentop = COP_NUFFINK; | |
| refreshdisks = SDL_TRUE; | |
| break; | |
| } | |
| break; | |
| case 0xe0: // Read track / Write track | |
| switch( data & 0x10 ) | |
| { | |
| case 0x00: // Read track (Type III) | |
| #if GENERAL_DISK_DEBUG | |
| dbg_printf( "DISK: (%04X) Read track", oric->cpu.pc-1 ); | |
| #endif | |
| wd->curroffs = 0; | |
| wd->r_status = WSF_BUSY|WSF_NOTREADY; | |
| wd->delayeddrq = 60; | |
| wd->currentop = COP_READ_TRACK; | |
| refreshdisks = SDL_TRUE; | |
| break; | |
| case 0x10: // Write track (Type III) | |
| #if GENERAL_DISK_DEBUG | |
| dbg_printf( "DISK: (%04X) Write track", oric->cpu.pc-1 ); | |
| #endif | |
| wd->curroffs = 0; | |
| wd->r_status = WSF_NOTREADY|WSF_BUSY; | |
| wd->delayeddrq = 500; | |
| wd->clrdrq( wd->drqarg ); | |
| wd->clrintrq( wd->intrqarg ); | |
| wd->delayedint = 0; | |
| wd->currentop = COP_WRITE_TRACK; | |
| refreshdisks = SDL_TRUE; | |
| break; | |
| } | |
| break; | |
| } | |
| break; | |
| case 1: // Track register | |
| wd->r_track = data; | |
| break; | |
| case 2: // Sector register | |
| wd->r_sector = data; | |
| break; | |
| case 3: // Data register | |
| wd->r_data = data; | |
| switch( wd->currentop ) | |
| { | |
| case COP_WRITE_SECTOR: | |
| case COP_WRITE_SECTORS: | |
| if( !wd->currsector ) | |
| { | |
| wd->r_status &= ~WSF_DRQ; | |
| wd->r_status |= WSF_RNF; | |
| wd->clrdrq( wd->drqarg ); | |
| wd->currentop = COP_NUFFINK; | |
| refreshdisks = SDL_TRUE; | |
| break; | |
| } | |
| if( wd->curroffs == 0 ) wd->currsector->data_ptr[wd->curroffs++]=0xfb; | |
| wd->currsector->data_ptr[wd->curroffs++] = wd->r_data; | |
| wd->crc = calc_crc( wd->crc, wd->r_data ); | |
| if( !wd->disk[wd->c_drive]->modified ) refreshdisks = SDL_TRUE; | |
| wd->disk[wd->c_drive]->modified = SDL_TRUE; | |
| wd->disk[wd->c_drive]->modified_time = 0; | |
| wd->r_status &= ~WSF_DRQ; | |
| wd->clrdrq( wd->drqarg ); | |
| if( wd->curroffs > wd->currseclen ) | |
| { | |
| wd->currsector->data_ptr[wd->curroffs++] = wd->crc>>8; | |
| wd->currsector->data_ptr[wd->curroffs++] = wd->crc; | |
| if( wd->currentop == COP_WRITE_SECTORS ) | |
| { | |
| #if GENERAL_DISK_DEBUG | |
| dbg_printf( "Next sector..." ); | |
| #endif | |
| // Get the next sector, and carry on! | |
| wd->r_sector++; | |
| wd->curroffs = 0; | |
| wd->currsector = wd17xx_find_sector( wd, wd->r_sector ); | |
| wd->crc = 0xe295; | |
| if( !wd->currsector ) | |
| { | |
| wd->delayedint = 20; | |
| wd->distatus = wd->sectype; | |
| wd->currentop = COP_NUFFINK; | |
| wd->r_status &= (~WSF_DRQ); | |
| wd->clrdrq( wd->drqarg ); | |
| refreshdisks = SDL_TRUE; | |
| break; | |
| } | |
| wd->delayeddrq = 180; | |
| break; | |
| } | |
| wd->delayedint = 32; | |
| wd->distatus = wd->sectype; | |
| wd->currentop = COP_NUFFINK; | |
| wd->r_status &= (~WSF_DRQ); | |
| wd->clrdrq( wd->drqarg ); | |
| refreshdisks = SDL_TRUE; | |
| } else { | |
| wd->delayeddrq = 32; | |
| } | |
| break; | |
| case COP_WRITE_TRACK: | |
| switch(data) | |
| { | |
| // All bytes > 0xF4 are control bytes | |
| case 0xf5: | |
| // MFM: Initialize CRC generator | |
| // FM : Not allowed | |
| #if GENERAL_DISK_DEBUG | |
| dbg_printf( "\tCOP_WRITE_TRACK: %02X -> SYNC (clear crc) -> 0xA1", wd->r_data); | |
| #endif | |
| wd->crc = 0x968b; | |
| wd->r_data = 0xa1; | |
| wd->crc = calc_crc( wd->crc, wd->r_data ); | |
| // wd->crc = 0xcdb4; | |
| break; | |
| case 0xf6: | |
| // FM : Not allowed | |
| #if GENERAL_DISK_DEBUG | |
| dbg_printf( "\tCOP_WRITE_TRACK: %02X -> SYNC -> 0xC2", wd->r_data); | |
| #endif | |
| wd->r_data = 0xc2; | |
| wd->crc = calc_crc( wd->crc, wd->r_data ); | |
| break; | |
| case 0xf7: | |
| #if GENERAL_DISK_DEBUG | |
| dbg_printf( "\tCOP_WRITE_TRACK: %02X -> CRC -> %04X", wd->r_data, wd->crc); | |
| #endif | |
| wd->disk[wd->c_drive]->rawimage[(wd->c_side*wd->disk[wd->c_drive]->numtracks + wd->c_track)*6400+256 + wd->curroffs] = wd->crc >> 8; | |
| wd->curroffs++; | |
| wd->r_data = wd->crc & 0xff; | |
| break; | |
| #if GENERAL_DISK_DEBUG | |
| // MFM: 0xf8 -> 0xff: write control byte | |
| // FM : 0xf8 -> 0xfe: Initialize CRC generator | |
| case 0xf8: | |
| dbg_printf( "\tCOP_WRITE_TRACK: %02X -> Data Address Mark (deleted)", wd->r_data); | |
| wd->crc = calc_crc( wd->crc, wd->r_data ); | |
| // next bytes: data, <CRC>; | |
| break; | |
| case 0xf9: | |
| dbg_printf( "\tCOP_WRITE_TRACK: %02X -> Data Mark", wd->r_data); | |
| wd->crc = calc_crc( wd->crc, wd->r_data ); | |
| break; | |
| case 0xfa: | |
| dbg_printf( "\tCOP_WRITE_TRACK: %02X -> Data Mark", wd->r_data); | |
| wd->crc = calc_crc( wd->crc, wd->r_data ); | |
| break; | |
| case 0xfb: | |
| dbg_printf( "\tCOP_WRITE_TRACK: %02X -> Data Address Mark (normal)", wd->r_data); | |
| wd->crc = calc_crc( wd->crc, wd->r_data ); | |
| // next bytes: data, <CRC>; | |
| break; | |
| case 0xfc: | |
| dbg_printf( "\tCOP_WRITE_TRACK: %02X -> Data Mark", wd->r_data); | |
| wd->crc = calc_crc( wd->crc, wd->r_data ); | |
| break; | |
| case 0xfd: | |
| dbg_printf( "\tCOP_WRITE_TRACK: %02X -> Data Mark", wd->r_data); | |
| wd->crc = calc_crc( wd->crc, wd->r_data ); | |
| break; | |
| case 0xfe: | |
| dbg_printf( "\tCOP_WRITE_TRACK: %02X -> Index Address Mark", wd->r_data); | |
| wd->crc = calc_crc( wd->crc, wd->r_data ); | |
| // next 6 bytes: track, side, sector, size, <CRC>; | |
| break; | |
| case 0xff: | |
| dbg_printf( "\tCOP_WRITE_TRACK: %02X -> ???", wd->r_data); | |
| wd->crc = calc_crc( wd->crc, wd->r_data ); | |
| break; | |
| #endif | |
| default: | |
| wd->crc = calc_crc( wd->crc, wd->r_data ); | |
| } | |
| // Write byte to disk image | |
| #if GENERAL_DISK_DEBUG | |
| dbg_printf("\tCOP_WRITE_TRACK: write byte: %02X '%c'", wd->r_data, wd->r_data); | |
| #endif | |
| wd->disk[wd->c_drive]->rawimage[(wd->c_side*wd->disk[wd->c_drive]->numtracks + wd->c_track)*6400+256 + wd->curroffs] = wd->r_data; | |
| wd->curroffs++; | |
| wd->r_status &= ~WSF_DRQ; | |
| wd->clrdrq( wd->drqarg ); | |
| wd->disk[wd->c_drive]->modified = SDL_TRUE; | |
| wd->disk[wd->c_drive]->modified_time = 0; | |
| refreshdisks = SDL_TRUE; | |
| // Has the whole track been written? | |
| if (wd->curroffs >= 6400) | |
| { | |
| #if GENERAL_DISK_DEBUG | |
| dbg_printf("\tCOP_WRITE_TRACK: track full (%d)", wd->curroffs); | |
| #endif | |
| wd->delayedint = 32; | |
| wd->currentop = COP_NUFFINK; | |
| // wd->r_status &= (~WSF_DRQ); | |
| wd->r_status = 0; | |
| wd->clrdrq( wd->drqarg ); | |
| } | |
| else | |
| wd->delayeddrq = 64; | |
| break; | |
| } | |
| break; | |
| } | |
| } | |
| // Microdisc interface handlers | |
| void microdisc_setdrq( void *md ) | |
| { | |
| struct microdisc *mdp = (struct microdisc *)md; | |
| mdp->drq = 0; | |
| } | |
| void microdisc_clrdrq( void *md ) | |
| { | |
| struct microdisc *mdp = (struct microdisc *)md; | |
| mdp->drq = MF_DRQ; | |
| } | |
| void microdisc_setintrq( void *md ) | |
| { | |
| struct microdisc *mdp = (struct microdisc *)md; | |
| mdp->intrq = 0; //MDSF_INTRQ; | |
| if( mdp->status & MDSF_INTENA ) | |
| mdp->oric->cpu.irq |= IRQF_DISK; | |
| } | |
| void microdisc_clrintrq( void *md ) | |
| { | |
| struct microdisc *mdp = (struct microdisc *)md; | |
| mdp->intrq = MDSF_INTRQ; | |
| mdp->oric->cpu.irq &= ~IRQF_DISK; | |
| } | |
| void microdisc_init( struct microdisc *md, struct wd17xx *wd, struct machine *oric ) | |
| { | |
| wd17xx_init( wd ); | |
| wd->setintrq = microdisc_setintrq; | |
| wd->clrintrq = microdisc_clrintrq; | |
| wd->intrqarg = (void*)md; | |
| wd->setdrq = microdisc_setdrq; | |
| wd->clrdrq = microdisc_clrdrq; | |
| wd->drqarg = (void*)md; | |
| md->status = 0; | |
| md->intrq = 0; | |
| md->drq = 0; | |
| md->wd = wd; | |
| md->oric = oric; | |
| md->diskrom = SDL_TRUE; | |
| } | |
| void microdisc_free( struct microdisc *md ) | |
| { | |
| int i; | |
| for( i=0; i<MAX_DRIVES; i++ ) | |
| disk_eject( md->oric, i ); | |
| } | |
| unsigned char microdisc_read( struct microdisc *md, unsigned short addr ) | |
| { | |
| // dbg_printf( "DISK: (%04X) Read from %04X", md->oric->cpu.pc-1, addr ); | |
| if( ( addr >= 0x310 ) && ( addr < 0x314 ) ) | |
| return wd17xx_read( md->wd, addr&3 ); | |
| switch( addr ) | |
| { | |
| case 0x314: | |
| return md->intrq|0x7f; | |
| case 0x318: | |
| return md->drq|0x7f; | |
| default: | |
| break; | |
| } | |
| return via_read( &md->oric->via, addr ); | |
| } | |
| void microdisc_write( struct microdisc *md, unsigned short addr, unsigned char data ) | |
| { | |
| // dbg_printf( "DISK: (%04X) Write %02X to %04X", md->oric->cpu.pc-1, data, addr ); | |
| if( ( addr >= 0x310 ) && ( addr < 0x314 ) ) | |
| { | |
| wd17xx_write( md->oric, md->wd, addr&3, data ); | |
| return; | |
| } | |
| switch( addr ) | |
| { | |
| case 0x314: | |
| md->status = data; | |
| // Interrupts enabled, and /INTRQ == 0 ? | |
| if( ( data&MDSF_INTENA ) && ( md->intrq == 0 ) ) | |
| { | |
| md->oric->cpu.irq |= IRQF_DISK; | |
| } else { | |
| md->oric->cpu.irq &= ~IRQF_DISK; | |
| } | |
| md->wd->c_drive = (data&MDSF_DRIVE)>>5; | |
| md->wd->c_side = (data&MDSF_SIDE) ? 1 : 0; | |
| md->oric->romdis = (data&MDSF_ROMDIS) ? SDL_FALSE : SDL_TRUE; | |
| md->diskrom = (data&MDSF_EPROM) ? SDL_FALSE : SDL_TRUE; | |
| break; | |
| case 0x318: | |
| md->drq = (data&MF_DRQ); | |
| break; | |
| default: | |
| via_write( &md->oric->via, addr, data ); | |
| break; | |
| } | |
| } | |
| // Byte Drive 500 interface handlers | |
| // NOTE: Info by Ray McLaughlin | |
| // | |
| // 0x310 MOTOFF R switches the disk drive motor off. | |
| // 0x311 MOTON R switches the disk drive motor on. | |
| // NOTE: (probably)the motor is switched on | |
| // by (electronic) default on first powering up | |
| // the BD interface but it is safer to switch it | |
| // on in the boot sector (page 4) code. | |
| // | |
| // 0x312 DSTATS R bit 7 - DRQ | |
| // bit 6 - IRQ | |
| // bit 0 - motor status (): b0=0 ON, b0=1 OFF (NOTE: DOS7 only). | |
| // | |
| // 0x313 MAPOFF R/W enables the ORIC ROM and disables the overlay RAM. | |
| // | |
| // 0x314 MAPON R/W disables the ORIC ROM and enables the overlay RAM. | |
| // | |
| // 0x315 PRECMP R (probably) forwrite compensation on the FDC. | |
| // NOTE: maybe it's used in the DOS versions V2.2 & V3.1 | |
| // of the BD software but not in Ray's latest version, | |
| // perhaps it will be included. | |
| // | |
| // 0x316 SDEN R/W enables the the lower 8k of the ORIC's EPROM | |
| // (used where the ORIC has 2x8k EPROMS) and therefore | |
| // disables the overlay RAM in that memory area. | |
| // This overrides MAPON (see above). | |
| // | |
| // 0x317 DDEN R/W disables the lower 8k of the ORIC's EPROM | |
| // (used where the ORIC has 1x16k EPROM or ROM) | |
| // and therefore enables the overlay RAM when MAPON | |
| // has been accessed. | |
| // | |
| // 0x31a DRVSEL R/W bit 7,6 - get/set current drive | |
| // bit 5 - get/set current side | |
| // | |
| // 0x0380 R/W disables the BD interface ROM which covers addresses | |
| // 0xE000 to 0xFFFF but before that is done, it takes | |
| // precedence over the above for that memory space. | |
| void bd500_setdrq( void *bd ) | |
| { | |
| struct bd500 *bdp = (struct bd500 *)bd; | |
| bdp->drq = 0x80; | |
| } | |
| void bd500_clrdrq( void *bd ) | |
| { | |
| struct bd500 *bdp = (struct bd500 *)bd; | |
| bdp->drq = 0; | |
| } | |
| void bd500_setintrq( void *bd ) | |
| { | |
| struct bd500 *bdp = (struct bd500 *)bd; | |
| bdp->intrq = 0x40; | |
| } | |
| void bd500_clrintrq( void *bd ) | |
| { | |
| struct bd500 *bdp = (struct bd500 *)bd; | |
| bdp->intrq = 0; | |
| } | |
| void bd500_init( struct bd500 *bd, struct wd17xx *wd, struct machine *oric ) | |
| { | |
| wd17xx_init( wd ); | |
| wd->setintrq = bd500_setintrq; | |
| wd->clrintrq = bd500_clrintrq; | |
| wd->intrqarg = (void*)bd; | |
| wd->setdrq = bd500_setdrq; | |
| wd->clrdrq = bd500_clrdrq; | |
| wd->drqarg = (void*)bd; | |
| bd->status = 0; | |
| bd->intrq = 0; | |
| bd->drq = 0; | |
| bd->wd = wd; | |
| bd->oric = oric; | |
| bd->diskrom = SDL_TRUE; | |
| bd->motor = SDL_FALSE; | |
| } | |
| void bd500_free( struct bd500 *bd ) | |
| { | |
| int i; | |
| for( i=0; i<MAX_DRIVES; i++ ) | |
| disk_eject( bd->oric, i ); | |
| } | |
| unsigned char bd500_read( struct bd500 *bd, unsigned short addr ) | |
| { | |
| unsigned char ret = 0xff; | |
| // dbg_printf( "DISK: (%04X) Read from %04X", bd->oric->cpu.pc-1, addr ); | |
| if( ( addr >= 0x320 ) && ( addr < 0x324 ) ) | |
| return wd17xx_read( bd->wd, addr&3 ); | |
| switch( addr ) | |
| { | |
| case 0x312: | |
| ret = bd->drq | bd->intrq; | |
| if( bd->oric->dos70 ) ret |= bd->motor? 0:1; | |
| break; | |
| case 0x313: | |
| bd->oric->romdis = SDL_FALSE; | |
| break; | |
| case 0x314: | |
| bd->oric->romdis = SDL_TRUE; | |
| break; | |
| case 0x310: | |
| // MOTOFF = 0 | |
| bd->motor = SDL_FALSE; | |
| bd->wd->currentop = COP_NUFFINK; | |
| break; | |
| case 0x311: | |
| // MOTON = 1 | |
| bd->motor = SDL_TRUE; | |
| bd->wd->currentop = COP_READ_SECTOR; | |
| break; | |
| case 0x315: | |
| // PRECMP | |
| break; | |
| case 0x316: | |
| // SDEN | |
| bd->diskrom = SDL_FALSE; | |
| break; | |
| case 0x317: | |
| // DDEN | |
| // 64k/56k mode depend on ROM chips | |
| bd->diskrom = bd->oric->rom16? SDL_FALSE : SDL_TRUE; | |
| break; | |
| case 0x31a: | |
| // bits 7,6 current drive | |
| // bit 5 current side | |
| ret = ((bd->wd->c_drive&3)<<6) | ((bd->wd->c_side&1)<<5); | |
| break; | |
| case 0x0380: | |
| bd->diskrom = SDL_FALSE; | |
| break; | |
| default: | |
| break; | |
| } | |
| return ret; | |
| } | |
| void bd500_write( struct bd500 *bd, unsigned short addr, unsigned char data ) | |
| { | |
| // dbg_printf( "DISK: (%04X) Write %02X to %04X", bd->oric->cpu.pc-1, data, addr ); | |
| if( ( addr >= 0x320 ) && ( addr < 0x324 ) ) | |
| { | |
| wd17xx_write( bd->oric, bd->wd, addr&3, data ); | |
| return; | |
| } | |
| switch( addr ) | |
| { | |
| case 0x313: | |
| bd->oric->romdis = SDL_FALSE; | |
| break; | |
| case 0x314: | |
| bd->oric->romdis = SDL_TRUE; | |
| break; | |
| case 0x316: | |
| // SDEN | |
| bd->diskrom = SDL_FALSE; | |
| break; | |
| case 0x317: | |
| // DDEN | |
| // 64k/56k mode depend on ROM chips | |
| bd->diskrom = bd->oric->rom16? SDL_FALSE : SDL_TRUE; | |
| break; | |
| case 0x0380: | |
| bd->diskrom = SDL_FALSE; | |
| break; | |
| case 0x31a: | |
| bd->wd->c_side = (data>>5)&1; | |
| bd->wd->c_drive = (data>>6)&3; | |
| break; | |
| } | |
| } | |
| // Jasmin interface handlers | |
| void jasmin_setdrq( void *j ) | |
| { | |
| struct jasmin *jp = (struct jasmin *)j; | |
| jp->oric->cpu.irq |= IRQF_DISK; | |
| } | |
| void jasmin_clrdrq( void *j ) | |
| { | |
| struct jasmin *jp = (struct jasmin *)j; | |
| jp->oric->cpu.irq &= ~IRQF_DISK; | |
| } | |
| void jasmin_setintrq( void *j ) | |
| { | |
| } | |
| void jasmin_clrintrq( void *j ) | |
| { | |
| } | |
| void jasmin_init( struct jasmin *j, struct wd17xx *wd, struct machine *oric ) | |
| { | |
| wd17xx_init( wd ); | |
| wd->setintrq = jasmin_setintrq; | |
| wd->clrintrq = jasmin_clrintrq; | |
| wd->intrqarg = (void*)j; | |
| wd->setdrq = jasmin_setdrq; | |
| wd->clrdrq = jasmin_clrdrq; | |
| wd->drqarg = (void*)j; | |
| j->olay = 0; | |
| j->romdis = 0; | |
| j->wd = wd; | |
| j->oric = oric; | |
| } | |
| void jasmin_free( struct jasmin *j ) | |
| { | |
| int i; | |
| for( i=0; i<MAX_DRIVES; i++ ) | |
| disk_eject( j->oric, i ); | |
| } | |
| unsigned char jasmin_read( struct jasmin *j, unsigned short addr ) | |
| { | |
| // dbg_printf( "DISK: (%04X) Read from %04X", md->oric->cpu.pc-1, addr ); | |
| if( ( addr >= 0x3f4 ) && ( addr < 0x3f8 ) ) | |
| return wd17xx_read( j->wd, addr&3 ); | |
| switch( addr ) | |
| { | |
| case 0x3f8: // Side select | |
| return j->wd->c_side ? 1 : 0; | |
| case 0x3f9: // Disk controller reset | |
| case 0x3fc: | |
| case 0x3fd: | |
| case 0x3fe: | |
| case 0x3ff: | |
| return 0; | |
| case 0x3fa: // Overlay RAM | |
| return j->olay; | |
| case 0x3fb: | |
| return j->romdis; | |
| default: | |
| break; | |
| } | |
| return via_read( &j->oric->via, addr ); | |
| } | |
| void jasmin_write( struct jasmin *j, unsigned short addr, unsigned char data ) | |
| { | |
| if( ( addr >= 0x3f4 ) && ( addr < 0x3f8 ) ) | |
| { | |
| wd17xx_write( j->oric, j->wd, addr&3, data ); | |
| return; | |
| } | |
| switch( addr ) | |
| { | |
| case 0x3f8: // side select | |
| j->wd->c_side = data&1; | |
| break; | |
| case 0x3f9: // reset | |
| // ... | |
| break; | |
| case 0x3fa: // overlay RAM | |
| j->olay = data&1; | |
| break; | |
| case 0x3fb: // romdis | |
| j->romdis = data&1; | |
| j->oric->romdis = (data!=0) ? SDL_TRUE : SDL_FALSE; | |
| break; | |
| case 0x3fc: // Drive 0 | |
| case 0x3fd: // Drive 1 | |
| case 0x3fe: // Drive 2 | |
| case 0x3ff: // Drive 3 | |
| j->wd->c_drive = addr&3; | |
| break; | |
| default: | |
| via_write( &j->oric->via, addr, data ); | |
| break; | |
| } | |
| } | |
| // Pravetz | |
| void pravetz_init( struct pravetz *p, struct machine *oric ) | |
| { | |
| int i; | |
| p->olay = 0; | |
| p->romdis = 1; | |
| p->extension = 0; | |
| p->oric = oric; | |
| for( i=0; i<MAX_DRIVES; i++) | |
| { | |
| p->drv[i].write_ready = 0x80; | |
| p->drv[i].volume = 254; | |
| p->drv[i].select = 0; | |
| p->drv[i].motor_on = 0; | |
| p->drv[i].pimg = NULL; | |
| } | |
| } | |
| void pravetz_free( struct pravetz *p ) | |
| { | |
| int i; | |
| for( i=0; i<MAX_DRIVES; i++ ) | |
| disk_eject( p->oric, i ); | |
| } | |
| unsigned char pravetz_read( struct pravetz *p, unsigned short addr ) | |
| { | |
| unsigned char data = 0; | |
| data = disk_pravetz_read( p->oric, addr ); | |
| p->oric->wddisk.currentop = p->drv[p->oric->wddisk.c_drive].motor_on ? COP_READ_SECTOR : COP_NUFFINK; | |
| return data; | |
| } | |
| void pravetz_write( struct pravetz *p, unsigned short addr, unsigned char data ) | |
| { | |
| disk_pravetz_write( p->oric, addr, data ); | |
| p->oric->wddisk.currentop = p->drv[p->oric->wddisk.c_drive].motor_on ? COP_WRITE_SECTOR : COP_NUFFINK; | |
| } | |