This is true1541.c in view mode; [Download] [Up]
/* * true1541.c - Hardware-level Commodore 1541 disk drive emulation. * * Written by * Daniel Sladic (sladic@eecg.toronto.edu) * Andreas Boose (boose@unixserv.rz.fh-hannover.de) * Ettore Perazzoli (ettore@comm2000.it) * André Fachat (fachat@physik.tu-chemnitz.de) * Teemu Rantanen (tvr@cs.hut.fi) * * This file is part of VICE, the Versatile Commodore Emulator. * See README for copyright notice. * * 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. * */ /* TODO: - more accurate emulation of disk rotation. - different speeds within one track. - support for .d64 images with attached error code. - support for GCR encoded image files. - check for byte ready *within* `BVC', `BVS' and `PHP'. - serial bus handling might be faster. */ #define __1541__ #include "vice.h" #include <sys/types.h> #include <stdio.h> #include <fcntl.h> #include <ctype.h> #include <unistd.h> #include <assert.h> #include <string.h> #include <math.h> #include "true1541.h" #include "gcr.h" #include "interrupt.h" #include "vmachine.h" #include "serial.h" #include "drive.h" #include "warn.h" #include "mem.h" #include "resources.h" #include "cmdline.h" #include "memutils.h" #include "viad.h" #include "via.h" #include "cia.h" #include "utils.h" #include "ui.h" /* ------------------------------------------------------------------------- */ /* Flag: Is the true 1541 emulation turned on? */ int true1541_enabled; /* Flag: Do we emulate a SpeedDOS-compatible parallel cable? */ int true1541_parallel_cable_enabled; /* What extension policy? (See `TRUE1541_EXTEND_*' in `true1541.h'.) */ static int extend_image_policy; /* What idling method? (See `TRUE1541_IDLE_*' in `true1541.h'.) */ static int idling_method; /* Original ROM code is saved here. */ static BYTE true1541_rom_idle_trap; /* What sync factor between the CPU and the 1541? If equal to `TRUE1541_SYNC_PAL', the same as PAL machines. If equal to `TRUE1541_SYNC_NTSC', the same as NTSC machines. The sync factor is calculated as 65536 * clk_1541 / clk_[c64|vic20] where `clk_1541' is fixed to 1 MHz, while `clk_[c64|vic20]' depends on the video timing (PAL or NTSC). The pre-calculated values for PAL and NTSC are in `pal_sync_factor' and `ntsc_sync_factor'. */ static int sync_factor; /* Name of the DOS ROM. */ static char *dos_rom_name; static int set_true1541_enabled(resource_value_t v) { if ((int) v) true1541_enable(); else true1541_disable(); return 0; } static int set_true1541_parallel_cable_enabled(resource_value_t v) { true1541_parallel_cable_enabled = (int) v; return 0; } static int set_extend_image_policy(resource_value_t v) { switch ((int) v) { case TRUE1541_EXTEND_NEVER: case TRUE1541_EXTEND_ASK: case TRUE1541_EXTEND_ACCESS: extend_image_policy = (int) v; return 0; default: return -1; } } static int set_idling_method(resource_value_t v) { /* FIXME: Maybe we should call `true1541_cpu_execute()' here? */ if ((int) v != TRUE1541_IDLE_SKIP_CYCLES && (int) v != TRUE1541_IDLE_TRAP_IDLE && (int) v != TRUE1541_IDLE_NO_IDLE) return -1; if (rom_loaded) true1541_rom[0xec9b - 0xc000] = (idling_method != TRUE1541_IDLE_TRAP_IDLE) ? 0x00 : true1541_rom_idle_trap; idling_method = (int) v; return 0; } static int set_sync_factor(resource_value_t v) { switch ((int) v) { case TRUE1541_SYNC_PAL: sync_factor = (int) v; true1541_set_pal_sync_factor(); break; case TRUE1541_SYNC_NTSC: sync_factor = (int) v; true1541_set_ntsc_sync_factor(); break; default: if ((int) v > 0) { sync_factor = (int) v; true1541_set_sync_factor((int) v); } else { return -1; } } return 0; } static int set_dos_rom_name(resource_value_t v) { const char *name = (const char *) v; if (dos_rom_name == NULL) dos_rom_name = stralloc(name); else { dos_rom_name = xrealloc(dos_rom_name, strlen(name) + 1); strcpy(dos_rom_name, name); } return 0; } static resource_t resources[] = { { "True1541", RES_INTEGER, (resource_value_t) 0, (resource_value_t *) &true1541_enabled, set_true1541_enabled }, { "True1541ParallelCable", RES_INTEGER, (resource_value_t) 0, (resource_value_t *) &true1541_parallel_cable_enabled, set_true1541_parallel_cable_enabled }, { "True1541ExtendImagePolicy", RES_INTEGER, (resource_value_t) TRUE1541_EXTEND_NEVER, (resource_value_t *) &extend_image_policy, set_extend_image_policy }, { "True1541IdleMethod", RES_INTEGER, (resource_value_t) TRUE1541_IDLE_TRAP_IDLE, (resource_value_t *) &idling_method, set_idling_method }, { "True1541SyncFactor", RES_INTEGER, (resource_value_t) TRUE1541_SYNC_PAL, (resource_value_t *) &sync_factor, set_sync_factor }, { "DosName", RES_STRING, (resource_value_t) "dos1541", (resource_value_t *) &dos_rom_name, set_dos_rom_name }, { NULL } }; int true1541_init_resources(void) { return resources_register(resources); } /* ------------------------------------------------------------------------- */ /* FIXME: Maybe DosName should not be changed by `-dosname'? */ static cmdline_option_t cmdline_options[] = { { "-1541", SET_RESOURCE, 0, NULL, NULL, "True1541", (resource_value_t) 1, NULL, "Enable hardware-level 1541 emulation" }, { "+1541", SET_RESOURCE, 0, NULL, NULL, "True1541", (resource_value_t) 0, NULL, "Disable hardware-level 1541 emulation" }, { "-parallel", SET_RESOURCE, 0, NULL, NULL, "True1541ParallelCable", (resource_value_t) 1, NULL, "Enable SpeedDOS-compatible parallel cable" }, { "+parallel", SET_RESOURCE, 0, NULL, NULL, "True1541ParallelCable", (resource_value_t) 0, NULL, "Disable SpeedDOS-compatible parallel cable" }, { "-driveidle", SET_RESOURCE, 2, NULL, NULL, "True1541IdleMethod", NULL, "<method>", "Set 1541 idling method (0: no traps, 1: skip cycles, 2: trap idle)" }, { "-drivesync", SET_RESOURCE, 1, NULL, NULL, "True1541SyncFactor", NULL, "<value>", "Set 1541 sync factor to <value>" }, { "-paldrive", SET_RESOURCE, 0, NULL, NULL, "True1541SyncFactor", (resource_value_t) TRUE1541_SYNC_PAL, NULL, "Use PAL 1541 sync factor" }, { "-ntscdrive", SET_RESOURCE, 0, NULL, NULL, "True1541SyncFactor", (resource_value_t) TRUE1541_SYNC_NTSC, NULL, "Use NTSC 1541 sync factor" }, { "-dosname", SET_RESOURCE, 1, NULL, NULL, "DosName", NULL, "<name>", "Specify name of 1541 DOS ROM image name" }, { NULL } }; int true1541_init_cmdline_options(void) { return cmdline_register_options(cmdline_options); } /* ------------------------------------------------------------------------- */ #define NUM_BYTES_SECTOR_GCR 360 #define NUM_MAX_BYTES_TRACK 7928 /* RAM/ROM. */ BYTE true1541_rom[TRUE1541_ROM_SIZE]; BYTE true1541_ram[TRUE1541_RAM_SIZE]; /* LED status (zero = off). */ int true1541_led_status; /* Current half track on which the R/W head is positioned. */ int true1541_current_half_track = 36; /* If nonzero, the 1541 ROM has already been loaded. */ static int rom_loaded = 0; static int byte_ready = 1; /* Pointer to the attached disk image. */ static DRIVE *true1541_floppy; /* Disk ID. */ static BYTE diskID1, diskID2; /* Map of the sector sizes. */ extern char sector_map[43]; /* Speed zone of each track. */ extern int speed_map[42]; /* Flag: does the current need to be written out to disk? */ static int GCR_dirty_track = 0; /* GCR value being written to the disk. */ static BYTE GCR_write_value = 0x55; /* Raw GCR image of the disk. */ static BYTE GCR_data[MAX_TRACKS_1541 * NUM_MAX_BYTES_TRACK]; /* Speed zone image of the disk. */ static BYTE GCR_speed_zone[MAX_TRACKS_1541 * NUM_MAX_BYTES_TRACK]; /* Pointer to the start of the GCR data for this track. */ static BYTE *GCR_track_start_ptr = GCR_data; /* Size of the GCR data for the current track. */ static int GCR_current_track_size; /* Size of the GCR data of each track. */ static int GCR_track_size[MAX_TRACKS_1541]; /* Offset of the R/W head on the current track. */ static int GCR_head_offset; /* Speed (in bps) of the disk in the 4 disk areas. */ static int rot_speed_bps[4] = { 250000, 266667, 285714, 307692 }; /* Number of bytes per track size. */ static int raw_track_size[4] = { 6250, 6666, 7142, 7692 }; static int read_write_mode; static int byte_ready_active; /* If the user does not want to extend the disk image and `ask mode' is selected this flag gets cleared. */ static int ask_extend_disk_image; /* Tick when the disk image was attached. */ static CLOCK attach_clk = (CLOCK)0; /* Tick when the disk image was detached. */ static CLOCK detach_clk = (CLOCK)0; /* Clock speed of the PAL and NTSC versions of the connected computer. */ static CLOCK pal_cycles_per_sec; static CLOCK ntsc_cycles_per_sec; /* Warnings. */ enum true1541_warnings { WARN_GCRWRITE }; #define TRUE1541_NUM_WARNINGS (WARN_GCRWRITE + 1) static warn_t *true1541_warn; #define GCR_OFFSET(track, sector) ((track - 1) * NUM_MAX_BYTES_TRACK \ + sector * NUM_BYTES_SECTOR_GCR) static void GCR_data_writeback(void); static void initialize_rotation(void); static void true1541_extend_disk_image(void); /* ------------------------------------------------------------------------- */ /* Disk image handling. */ static void read_image_d64(void) { BYTE buffer[260], *ptr, chksum; int rc, i; int track, sector; if (!true1541_floppy) return; buffer[0] = 0x07; buffer[258] = buffer[259] = 0; /* Since the D64 format does not provide the actual track sizes or speed zones, we set them to standard values. */ for (track = 0; track < MAX_TRACKS_1541; track++) { GCR_track_size[track] = raw_track_size[speed_map[track]]; memset(GCR_speed_zone, speed_map[track], NUM_MAX_BYTES_TRACK); } true1541_set_half_track(true1541_current_half_track); for (track = 1; track <= true1541_floppy->NumTracks; track++) { ptr = GCR_data + GCR_OFFSET(track, 0); /* Clear track to avoid read errors. */ memset(ptr, 0xff, NUM_MAX_BYTES_TRACK); for (sector = 0; sector < sector_map[track]; sector++) { ptr = GCR_data + GCR_OFFSET(track, sector); rc = floppy_read_block(true1541_floppy->ActiveFd, true1541_floppy->ImageFormat, buffer + 1, track, sector, true1541_floppy->D64_Header); if (rc < 0) { printf("1541: error reading T:%d S:%d from the disk image\n", track, sector); /* FIXME: could be handled better. */ } else { chksum = buffer[1]; for (i = 2; i < 257; i++) chksum ^= buffer[i]; buffer[257] = chksum; convert_sector_to_GCR(buffer, ptr, track, sector, diskID1, diskID2); } } } } static int read_image_gcr(void) { int track, track_len, zone_len, i, NumTracks; BYTE len[2], comp_speed[NUM_MAX_BYTES_TRACK / 4]; BYTE *track_data, *zone_data; DWORD gcr_track_p[MAX_TRACKS_1541 * 2]; DWORD gcr_speed_p[MAX_TRACKS_1541 * 2]; off_t offset; NumTracks = true1541_floppy->NumTracks; lseek(true1541_floppy->ActiveFd, 12, SEEK_SET); if (read_dword(true1541_floppy->ActiveFd, gcr_track_p, NumTracks * 8) < 0) { fprintf(stderr, "1541: Could not read GCR disk image.\n"); return 0; } lseek(true1541_floppy->ActiveFd, 12 + NumTracks * 8, SEEK_SET); if (read_dword(true1541_floppy->ActiveFd, gcr_speed_p, NumTracks * 8) < 0) { fprintf(stderr, "1541: Could not read GCR disk image.\n"); return 0; } for (track = 0; track < MAX_TRACKS_1541; track++) { track_data = GCR_data + track * NUM_MAX_BYTES_TRACK; zone_data = GCR_speed_zone + track * NUM_MAX_BYTES_TRACK; memset(track_data, 0xff, NUM_MAX_BYTES_TRACK); memset(zone_data, 0x00, NUM_MAX_BYTES_TRACK / 4); GCR_track_size[track] = 6250; if (track <= NumTracks && gcr_track_p[track * 2] != 0) { offset = gcr_track_p[track * 2]; lseek(true1541_floppy->ActiveFd, offset, SEEK_SET); if (read(true1541_floppy->ActiveFd, (char *)len, 2) < 2) { fprintf(stderr, "1541: Could not read GCR disk image.\n"); return 0; } track_len = len[0] + len[1] * 256; if (track_len < 5000 || track_len > 7928) { fprintf(stderr, "1541: Track field length %i is not supported.\n", track_len); return 0; } GCR_track_size[track] = track_len; lseek(true1541_floppy->ActiveFd, offset + 2, SEEK_SET); if (read(true1541_floppy->ActiveFd, (char *)track_data, track_len) < track_len) { fprintf(stderr, "1541: Could not read GCR disk image.\n"); return 0; } zone_len = (track_len + 3) / 4; if (gcr_speed_p[track * 2] > 3) { offset = gcr_speed_p[track * 2]; lseek(true1541_floppy->ActiveFd, offset, SEEK_SET); if (read(true1541_floppy->ActiveFd, (char *)comp_speed, zone_len) < zone_len) { fprintf(stderr, "1541: Could not read GCR disk image.\n"); return 0; } for (i = 0; i < zone_len; i++) { zone_data[i * 4] = comp_speed[i] & 3; zone_data[i * 4 + 1] = (comp_speed[i] >> 2) & 3; zone_data[i * 4 + 2] = (comp_speed[i] >> 4) & 3; zone_data[i * 4 + 3] = (comp_speed[i] >> 6) & 3; } } else { memset(zone_data, gcr_speed_p[track * 2], NUM_MAX_BYTES_TRACK); } } } return 1; } static void write_track_gcr(int track) { int gap, i, NumTracks; BYTE len[2]; DWORD gcr_track_p[MAX_TRACKS_1541 * 2]; DWORD gcr_speed_p[MAX_TRACKS_1541 * 2]; off_t offset; NumTracks = true1541_floppy->NumTracks; lseek(true1541_floppy->ActiveFd, 12, SEEK_SET); if (read_dword(true1541_floppy->ActiveFd, gcr_track_p, NumTracks * 8) < 0) { fprintf(stderr, "1541: Could not read GCR disk image header.\n"); return; } lseek(true1541_floppy->ActiveFd, 12 + NumTracks * 8, SEEK_SET); if (read_dword(true1541_floppy->ActiveFd, gcr_speed_p, NumTracks * 8) < 0) { fprintf(stderr, "1541: Could not read GCR disk image header.\n"); return; } if (gcr_track_p[(track - 1) * 2] == 0) { offset = lseek(true1541_floppy->ActiveFd, 0, SEEK_END); if (offset < 0) { fprintf(stderr, "1541: Could not extend GCR disk image.\n"); return; } gcr_track_p[(track - 1) * 2] = offset; } offset = gcr_track_p[(track - 1) * 2]; len[0] = GCR_track_size[track - 1] % 256; len[1] = GCR_track_size[track - 1] / 256; if (lseek(true1541_floppy->ActiveFd, offset, SEEK_SET) < 0 || write(true1541_floppy->ActiveFd, (char *)len, 2) < 0) { fprintf(stderr, "1541: Could not write GCR disk image.\n"); return; } /* Clear gap between the end of the actual track and the start of the next track. */ gap = NUM_MAX_BYTES_TRACK - GCR_track_size[track - 1]; if (gap > 0) memset(GCR_track_start_ptr + GCR_track_size[track - 1], 0, gap); if (lseek(true1541_floppy->ActiveFd, offset + 2, SEEK_SET) < 0 || write(true1541_floppy->ActiveFd, (char *)GCR_track_start_ptr, NUM_MAX_BYTES_TRACK) < 0) { fprintf(stderr, "1541: Could not write GCR disk image.\n"); return; } for (i = 0; (GCR_speed_zone[(track - 1) * NUM_MAX_BYTES_TRACK] == GCR_speed_zone[(track - 1) * NUM_MAX_BYTES_TRACK + i]) && i < NUM_MAX_BYTES_TRACK; i++); if (i < GCR_track_size[track - 1]) { /* This will change soon. */ fprintf(stderr, "1541: Saving different speed zones is not supported yet.\n"); return; } if (gcr_speed_p[(track - 1) * 2] >= 4) { /* This will change soon. */ fprintf(stderr, "1541: Adding new speed zones is not supported yet.\n"); return; } offset = 12 + NumTracks * 8 + (track - 1) * 8; if (lseek(true1541_floppy->ActiveFd, offset, SEEK_SET) < 0 || write_dword(true1541_floppy->ActiveFd, &gcr_speed_p[(track - 1) * 2], 4) < 0) { fprintf(stderr, "1541: Could not write GCR disk image.\n"); return; } #if 0 /* We do not support writing different speeds yet. */ for (i = 0; i < (NUM_MAX_BYTES_TRACK / 4); i++) zone_len = (GCR_track_size[track - 1] + 3) / 4; zone_data = GCR_speed_zone + (track - 1) * NUM_MAX_BYTES_TRACK; if (gap > 0) memset(zone_data + GCR_track_size[track - 1], 0, gap); for (i = 0; i < (NUM_MAX_BYTES_TRACK / 4); i++) comp_speed[i] = (zone_data[i * 4] | (zone_data[i * 4 + 1] << 2) | (zone_data[i * 4 + 2] << 4) | (zone_data[i * 4 + 3] << 6)); if (lseek(true1541_floppy->ActiveFd, offset, SEEK_SET) < 0 || write(true1541_floppy->ActiveFd, (char *)comp_speed, NUM_MAX_BYTES_TRACK / 4) < 0) { fprintf(stderr, "1541: Could not write GCR disk image"); return; } #endif } static int setID(void) { BYTE buffer[256]; int rc; if (!true1541_floppy) return -1; rc = floppy_read_block(true1541_floppy->ActiveFd, true1541_floppy->ImageFormat, buffer, 18, 0, true1541_floppy->D64_Header); if (rc >= 0) { diskID1 = buffer[0xa2]; diskID2 = buffer[0xa3]; } return rc; } static BYTE *GCR_find_sector_header(int track, int sector) { BYTE *offset = GCR_track_start_ptr; BYTE *GCR_track_end = GCR_track_start_ptr + GCR_current_track_size; char GCR_header[5], header_data[4]; int i, sync_count = 0, wrap_over = 0; while ((offset < GCR_track_end) && !wrap_over) { while (*offset != 0xff) { offset++; if (offset >= GCR_track_end) return NULL; } while (*offset == 0xff) { offset++; if (offset == GCR_track_end) { offset = GCR_track_start_ptr; wrap_over = 1; } /* Check for killer tracks. */ if((++sync_count) >= GCR_current_track_size) return NULL; } for (i=0; i < 5; i++) { GCR_header[i] = *(offset++); if (offset >= GCR_track_end) { offset = GCR_track_start_ptr; wrap_over = 1; } } convert_GCR_to_4bytes(GCR_header, header_data); if (header_data[0] == 0x08) { /* FIXME: Add some sanity checks here. */ if (header_data[2] == sector && header_data[3] == track) return offset; } } return NULL; } static BYTE *GCR_find_sector_data(BYTE *offset) { BYTE *GCR_track_end = GCR_track_start_ptr + GCR_current_track_size; int header = 0; while (*offset != 0xff) { offset++; if (offset >= GCR_track_end) offset = GCR_track_start_ptr; header++; if (header >= 500) return NULL; } while (*offset == 0xff) { offset++; if (offset == GCR_track_end) offset = GCR_track_start_ptr; } return offset; } /* ------------------------------------------------------------------------- */ /* Initialize the hardware-level 1541 emulation (should be called at least once before anything else). Return 0 on success, -1 on error. */ int true1541_init(CLOCK pal_hz, CLOCK ntsc_hz) { int track; pal_cycles_per_sec = pal_hz; ntsc_cycles_per_sec = ntsc_hz; if (rom_loaded) return 0; true1541_warn = warn_init("1541", TRUE1541_NUM_WARNINGS); /* Load the ROMs. */ if (mem_load_sys_file(dos_rom_name, true1541_rom, TRUE1541_ROM_SIZE, TRUE1541_ROM_SIZE) < 0) { fprintf(stderr, "1541: Warning: ROM image not loaded; hardware-level " "emulation is not available.\n"); true1541_enabled = 0; return -1; } /* Calculate ROM checksum. */ { unsigned long s; int i; for (i = 0, s = 0; i < TRUE1541_ROM_SIZE; i++) s += true1541_rom[i]; if (s != TRUE1541_ROM_CHECKSUM) fprintf(stderr, "1541: Warning: unknown ROM image. Sum: %lu\n", s); } printf("1541: ROM loaded successfully.\n"); rom_loaded = 1; /* Remove the ROM check. */ true1541_rom[0xeae4 - 0xc000] = 0xea; true1541_rom[0xeae5 - 0xc000] = 0xea; true1541_rom[0xeae8 - 0xc000] = 0xea; true1541_rom[0xeae9 - 0xc000] = 0xea; /* Trap the idle loop. */ true1541_rom_idle_trap = true1541_rom[0xec9b - 0xc000]; if (idling_method == TRUE1541_IDLE_TRAP_IDLE) true1541_rom[0xec9b - 0xc000] = 0x00; for (track = 0; track < MAX_TRACKS_1541; track++) GCR_track_size[track] = raw_track_size[speed_map[track]]; /* Position the R/W head on the directory track. */ true1541_set_half_track(36); initialize_rotation(); true1541_cpu_init(); /* Make sure the sync factor is acknowledged correctly. */ set_sync_factor((resource_value_t) sync_factor); /* Make sure the traps are moved as needed. */ if (true1541_enabled) true1541_enable(); return 0; } /* Activate full 1541 emulation. */ int true1541_enable(void) { /* This must come first, because this might be called before the true 1541 initialization. */ true1541_enabled = 1; if (!rom_loaded) return -1; /* Always disable kernal traps. */ serial_remove_traps(); if (true1541_floppy != NULL) true1541_attach_floppy(true1541_floppy); true1541_cpu_wake_up(); ui_toggle_drive_status(1); return 0; } /* Disable full 1541 emulation. */ void true1541_disable(void) { /* This must come first, because this might be called before the true 1541 initialization. */ true1541_enabled = 0; if (rom_loaded) serial_install_traps(); true1541_cpu_sleep(); GCR_data_writeback(); ui_toggle_drive_status(0); } void true1541_reset(void) { true1541_cpu_reset(); warn_reset(true1541_warn); } /* ------------------------------------------------------------------------- */ static int have_new_disk = 0; /* used for disk change detection */ /* Attach a disk image to the true 1541 emulation. */ int true1541_attach_floppy(DRIVE *floppy) { if (floppy->ImageFormat != 1541) return -1; true1541_floppy = floppy; have_new_disk = 1; attach_clk = true1541_clk; ask_extend_disk_image = 1; if (true1541_floppy->GCR_Header != 0) { if (!read_image_gcr()) return -1; } else { if (setID() >= 0) { read_image_d64(); return 0; } else { return -1; } } return 0; } /* Detach a disk image from the true 1541 emulation. */ int true1541_detach_floppy(DRIVE *floppy) { if (floppy != true1541_floppy) { /* Shouldn't happen. */ fprintf(stderr, "Whaaat? Attempt for bogus true1541 detachment!\n"); return -1; } else if (true1541_floppy != NULL) { GCR_data_writeback(); detach_clk = true1541_clk; true1541_floppy = NULL; memset(GCR_data, 0, sizeof(GCR_data)); } return 0; } /* ------------------------------------------------------------------------- */ static BYTE GCR_read; static unsigned long bits_moved = 0, accum = 0; static int finish_byte = 0, last_mode = 1; static CLOCK rotation_last_clk = 0L; #define ROTATION_TABLE_SIZE 0x1000 #define ACCUM_MAX 0x10000 struct _rotation_table { unsigned long bits; unsigned long accum; }; struct _rotation_table rotation_table[4][ROTATION_TABLE_SIZE]; struct _rotation_table *rotation_table_ptr = rotation_table[0]; /* Initialization. */ static void initialize_rotation(void) { int i, j; for (i = 0; i < 4; i++) { int speed = rot_speed_bps[i]; for (j = 0; j < ROTATION_TABLE_SIZE; j++) { double bits = (double)j * (double)speed / 1000000.0; rotation_table[i][j].bits = (unsigned long)bits; rotation_table[i][j].accum = ((bits - (unsigned long)bits) * ACCUM_MAX); } } bits_moved = accum = 0; } /* Set the `byte ready' bit. */ inline void true1541_set_byte_ready(int val) { byte_ready = val; } /* Rotate the disk according to the current value of `true1541_clk'. If `mode_change' is non-zero, there has been a Read -> Write mode switch. */ void true1541_rotate_disk(int mode_change) { unsigned long new_bits; if (mode_change) { finish_byte = 1; return; } /* If the drive's motor is off or byte ready is disabled do nothing. */ if (byte_ready_active != 0x06) return; /* Calculate the number of bits that have passed under the R/W head since the last time. */ { CLOCK delta = true1541_clk - rotation_last_clk; new_bits = 0; while (delta > 0) { if (delta >= ROTATION_TABLE_SIZE) { struct _rotation_table *p = (rotation_table_ptr + ROTATION_TABLE_SIZE - 1); new_bits += p->bits; accum += p->accum; delta -= ROTATION_TABLE_SIZE - 1; } else { struct _rotation_table *p = rotation_table_ptr + delta; new_bits += p->bits; accum += p->accum; delta = 0; } if (accum >= ACCUM_MAX) { accum -= ACCUM_MAX; new_bits++; } } } if (bits_moved + new_bits >= 8) { bits_moved += new_bits; rotation_last_clk = true1541_clk; if (finish_byte) { if (last_mode == 0) { /* write */ GCR_dirty_track = 1; if (bits_moved >= 8) { GCR_track_start_ptr[GCR_head_offset] = GCR_write_value; GCR_head_offset = ((GCR_head_offset + 1) % GCR_current_track_size); bits_moved -= 8; } } else { /* read */ if (bits_moved >= 8) { GCR_head_offset = ((GCR_head_offset + 1) % GCR_current_track_size); bits_moved -= 8; GCR_read = GCR_track_start_ptr[GCR_head_offset]; } } finish_byte = 0; last_mode = read_write_mode; } if (last_mode == 0) { /* write */ GCR_dirty_track = 1; while (bits_moved >= 8) { GCR_track_start_ptr[GCR_head_offset] = GCR_write_value; GCR_head_offset = ((GCR_head_offset + 1) % GCR_current_track_size); bits_moved -= 8; } } else { /* read */ GCR_head_offset = ((GCR_head_offset + bits_moved / 8) % GCR_current_track_size); bits_moved %= 8; GCR_read = GCR_track_start_ptr[GCR_head_offset]; } if (!true1541_sync_found()) true1541_set_byte_ready(1); } /* if (bits_moved + new_bits >= 8) */ } /* ------------------------------------------------------------------------- */ /* This prevents the CLOCK counters `rotation_last_clk', `attach_clk' and `detach_clk' from overflowing. */ void true1541_prevent_clk_overflow(CLOCK sub) { sub = true1541_cpu_prevent_clk_overflow(sub); if (sub > 0) { true1541_rotate_disk(0); rotation_last_clk -= sub; if (attach_clk > (CLOCK) 0) attach_clk -= sub; if (detach_clk > (CLOCK) 0) detach_clk -= sub; } } /* Read a GCR byte from the disk. */ BYTE true1541_read_disk_byte(void) { BYTE val; if (attach_clk != (CLOCK)0) { if (true1541_clk - attach_clk < TRUE1541_ATTACH_DELAY) return 0; attach_clk = (CLOCK)0; } true1541_rotate_disk(0); val = GCR_read; return val; } int true1541_byte_ready(void) { if(byte_ready_active) { true1541_rotate_disk(0); return byte_ready; } else { return 0; } } /* Return non-zero if the Sync mark is found. It is required to call true1541_rotate_disk() to update GCR_head_offset first. */ int true1541_sync_found(void) { BYTE val = GCR_track_start_ptr[GCR_head_offset]; if (val != 0xff || last_mode == 0) { return 0; } else { int next_head_offset = (GCR_head_offset > 0 ? GCR_head_offset - 1 : GCR_current_track_size - 1); if (GCR_track_start_ptr[next_head_offset] != 0xff) return 0; /* As the current rotation code cannot cope with non byte aligned writes, do not change `bits_moved'! */ /* bits_moved = 0; */ return 1; } } /* Move the head to half track `num'. */ void true1541_set_half_track(int num) { if (num > 84) num = 84; else if (num < 2) num = 2; true1541_current_half_track = num; GCR_track_start_ptr = (GCR_data + ((true1541_current_half_track / 2 - 1) * NUM_MAX_BYTES_TRACK)); GCR_current_track_size = GCR_track_size[true1541_current_half_track / 2 - 1]; GCR_head_offset = 0; } /* Increment the head position by `step' half-tracks. Valid values for `step' are `+1' and `-1'. */ void true1541_move_head(int step) { GCR_data_writeback(); true1541_set_half_track(true1541_current_half_track + step); ui_display_drive_track((double)true1541_current_half_track / 2.0); } /* Write one GCR byte to the disk. */ void true1541_write_gcr(BYTE val) { if (true1541_floppy == NULL) return; true1541_rotate_disk(0); GCR_write_value = val; } /* Return the write protect sense status. */ int true1541_write_protect_sense(void) { /* Toggle the write protection bit if the disk was detached. */ if (detach_clk != (CLOCK)0) { if (true1541_clk - detach_clk < TRUE1541_DETACH_DELAY) return 0; detach_clk = (CLOCK)0; } if ((attach_clk != (CLOCK)0) && (true1541_clk - attach_clk < TRUE1541_ATTACH_DELAY)) return 0; if (true1541_floppy == NULL) { /* No disk in drive, write protection is on. */ return 1; } else if (have_new_disk) { /* Disk has changed, make sure the drive sees at least one change in the write protect status. */ have_new_disk = 0; return !true1541_floppy->ReadOnly; } else { return true1541_floppy->ReadOnly; } } static void GCR_data_writeback(void) { int rc, extend, track, sector; BYTE buffer[260], *offset; track = true1541_current_half_track / 2; if (!GCR_dirty_track) return; if (true1541_floppy->GCR_Header != 0) { write_track_gcr(track); GCR_dirty_track = 0; return; } if (track > EXT_TRACKS_1541) return; if (track > true1541_floppy->NumTracks) { switch (extend_image_policy) { case TRUE1541_EXTEND_NEVER: ask_extend_disk_image = 1; return; case TRUE1541_EXTEND_ASK: if (ask_extend_disk_image == 1) { extend = ui_extend_image_dialog(); if (extend == 0) { ask_extend_disk_image = 0; return; } else { true1541_extend_disk_image(); } } else { return; } break; case TRUE1541_EXTEND_ACCESS: ask_extend_disk_image = 1; true1541_extend_disk_image(); break; } } GCR_dirty_track = 0; for (sector = 0; sector < sector_map[track]; sector++) { offset = GCR_find_sector_header(track, sector); if (offset == NULL) fprintf(stderr, "1541: Could not find header of T:%d S:%d.\n", track, sector); else { offset = GCR_find_sector_data(offset); if (offset == NULL) fprintf(stderr, "1541: Could not find data sync of T:%d S:%d.\n", track, sector); else { convert_GCR_to_sector(buffer, offset, GCR_track_start_ptr, GCR_current_track_size); if (buffer[0] != 0x7) fprintf(stderr, "1541: Could not find data block id of T:%d S:%d.\n", track, sector); else { rc = floppy_write_block(true1541_floppy->ActiveFd, true1541_floppy->ImageFormat, buffer + 1, track, sector, true1541_floppy->D64_Header); if (rc < 0) fprintf(stderr, "1541: Could not update T:%d S:%d.\n", track, sector); } } } } } void true1541_extend_disk_image(void) { int rc, track, sector; BYTE buffer[256]; true1541_floppy->NumTracks = EXT_TRACKS_1541; true1541_floppy->NumBlocks = EXT_BLOCKS_1541; memset(buffer, 0, 256); for (track = NUM_TRACKS_1541 + 1; track <= EXT_TRACKS_1541; track++) { for (sector = 0; sector < sector_map[track]; sector++) { rc = floppy_write_block(true1541_floppy->ActiveFd, true1541_floppy->ImageFormat, buffer, track, sector, true1541_floppy->D64_Header); if (rc < 0) fprintf(stderr, "1541: Could not update T:%d S:%d.\n", track, sector); } } } void true1541_update_zone_bits(int zone) { rotation_table_ptr = rotation_table[zone]; } void true1541_update_viad2_pcr(int pcrval) { read_write_mode = pcrval & 0x20; byte_ready_active = (byte_ready_active & ~0x02) | (pcrval & 0x02); } void true1541_motor_control(int flag) { byte_ready_active = (byte_ready_active & ~0x04) | (flag & 0x04); } BYTE true1541_read_viad2_prb(void) { true1541_rotate_disk(0); return (true1541_sync_found() ? 0 : 0x80) | (true1541_write_protect_sense() ? 0 : 0x10); } /* ------------------------------------------------------------------------- */ /* Handle a ROM trap. */ int true1541_trap_handler(void) { if (MOS6510_REGS_GET_PC(&true1541_cpu_regs) == 0xec9b) { /* Idle loop */ MOS6510_REGS_SET_PC(&true1541_cpu_regs, 0xebff); if (idling_method == TRUE1541_IDLE_TRAP_IDLE) true1541_clk = next_alarm_clk(&true1541_int_status); } else return 1; return 0; } /* ------------------------------------------------------------------------- */ /* Set the sync factor between the computer and the 1541. */ void true1541_set_sync_factor(unsigned int factor) { true1541_cpu_set_sync_factor(factor); } void true1541_set_pal_sync_factor(void) { if (pal_cycles_per_sec != 0.0) { int sync_factor = (int) floor(65536.0 * (1000000.0 / ((double)pal_cycles_per_sec))); true1541_set_sync_factor(sync_factor); } } void true1541_set_ntsc_sync_factor(void) { if (ntsc_cycles_per_sec != 0.0) { int sync_factor = (int) floor(65536.0 * (1000000.0 / ((double)ntsc_cycles_per_sec))); true1541_cpu_set_sync_factor(sync_factor); } } /* ------------------------------------------------------------------------- */ /* Update the status bar in the UI. */ void true1541_update_ui_status(void) { static int old_led_status = 0; static int old_half_track = 0; int my_led_status; if (!true1541_enabled) { if (old_led_status >= 0) { old_led_status = old_half_track = -1; ui_toggle_drive_status(0); } return; } /* Actually update the LED status only if the `trap idle' idling method is being used, as the LED status could be incorrect otherwise. */ if (idling_method != TRUE1541_IDLE_SKIP_CYCLES) my_led_status = true1541_led_status ? 1 : 0; else my_led_status = 0; if (my_led_status != old_led_status) { ui_display_drive_led(my_led_status); old_led_status = my_led_status; } if (true1541_current_half_track != old_half_track) { old_half_track = true1541_current_half_track; ui_display_drive_track((float) true1541_current_half_track / 2.0); } } /* This is called at every vsync. */ void true1541_vsync_hook(void) { true1541_update_ui_status(); if (!true1541_enabled) return; if (idling_method != TRUE1541_IDLE_SKIP_CYCLES) true1541_cpu_execute(); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.