/* * Apple // emulator for Linux: C portion of Disk ][ emulation * * Copyright 1994 Alexander Jean-Claude Bottema * Copyright 1995 Stephen Lee * Copyright 1997, 1998 Aaron Culliney * Copyright 1998, 1999, 2000 Michael Deutschmann * * This software package is subject to the GNU General Public License * version 2 or later (your choice) as published by the Free Software * Foundation. * * THERE ARE NO WARRANTIES WHATSOEVER. * */ #include "common.h" #define PHASE_BYTES 3328 static unsigned char slot6_rom[256]; static int slot6_rom_loaded = 0; struct drive disk6; static int skew_table_6[16] = /* Sector skew table */ { 0,7,14,6,13,5,12,4,11,3,10,2,9,1,8,15 }; static int translate_table_6[256] = /* Translation table */ { 0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6, 0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3, 0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x01, 0x80, 0x80, 0x02, 0x03, 0x80, 0x04, 0x05, 0x06, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x07, 0x08, 0x80, 0x80, 0x80, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x80, 0x80, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x80, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x1b, 0x80, 0x1c, 0x1d, 0x1e, 0x80, 0x80, 0x80, 0x1f, 0x80, 0x80, 0x20, 0x21, 0x80, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x80, 0x80, 0x80, 0x80, 0x80, 0x29, 0x2a, 0x2b, 0x80, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x80, 0x80, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x80, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f }; /* ------------------------------------------------------------------------- c_init_6() ------------------------------------------------------------------------- */ void c_init_6() { disk6.disk[0].phase = disk6.disk[1].phase = 42; disk6.disk[0].phase_change = disk6.disk[1].phase_change = 0; disk6.motor = 1; /* motor on */ disk6.drive = 0; /* first drive active */ disk6.ddrw = 0; disk6.volume = 254; #if 0 /* BUGS!: */ file_name_6[0][1024] = '\0'; file_name_6[1][1024] = '\0'; #endif } /* ------------------------------------------------------------------------- c_eject_6() - ejects/gzips image file ------------------------------------------------------------------------- */ void c_eject_6(int drive) { int ch = -1; #define ZLIB_SUBMENU_H 7 #define ZLIB_SUBMENU_W 40 char zlibmenu[ZLIB_SUBMENU_H][ZLIB_SUBMENU_W+1] = //1. 5. 10. 15. 20. 25. 30. 35. 40. { "||||||||||||||||||||||||||||||||||||||||", "| |", "| An error occurred when attempting to |", "| compress a disk image: |", "| |", "| |", "||||||||||||||||||||||||||||||||||||||||" }; if (disk6.disk[drive].compressed) { // foo.dsk -> foo.dsk.gz const char* const err = def(disk6.disk[drive].file_name); if (err) { snprintf(&zlibmenu[4][2], 37, "%s", err); c_interface_print_submenu_centered(zlibmenu[0], ZLIB_SUBMENU_W, ZLIB_SUBMENU_H); while ((ch = c_mygetch(1)) == -1) { } } else { unlink(disk6.disk[drive].file_name); } } disk6.disk[drive].compressed = 0; disk6.disk[drive].nibblized = 0; sprintf(disk6.disk[drive].file_name, "%s", ""); if (disk6.disk[drive].fp) { fclose(disk6.disk[drive].fp); disk6.disk[drive].fp = NULL; } } /* ------------------------------------------------------------------------- c_new_diskette_6( int drive, char *filename, Tr cmpr, Tr nib ) inserts a new disk image into the appropriate drive. assumes privileged user level on entry, sets to the user when using disks. return 1 if we can't open an image file for reading. ------------------------------------------------------------------------- */ int c_new_diskette_6(int drive, char *file_name, int cmpr, int nib, int force) { struct stat buf; strcpy(disk6.disk[drive].file_name, file_name); disk6.disk[drive].file_name[1023]='\0'; disk6.disk[drive].compressed = cmpr; disk6.disk[drive].nibblized = nib; if (disk6.disk[drive].fp) { fclose(disk6.disk[drive].fp); disk6.disk[drive].fp = NULL; } if (stat(disk6.disk[drive].file_name, &buf) < 0) { disk6.disk[drive].fp = NULL; c_eject_6(drive); return 1; /* problem: disk unreadable */ } else { disk6.disk[drive].file_size = buf.st_size; if (!force) { /* Open read only */ disk6.disk[drive].fp = fopen(disk6.disk[drive].file_name, "r+"); disk6.disk[drive].is_protected = 0; } if ((disk6.disk[drive].fp == NULL) || (force)) { /* Open for read AND write */ disk6.disk[drive].fp = fopen(disk6.disk[drive].file_name, "r"); disk6.disk[drive].is_protected = 1; /* disk is write protected! */ } if (disk6.disk[drive].fp == NULL) { /* Failed to open file. */ c_eject_6(drive); return 1; /* problem: disk unreadable */ } /* seek to current head position. */ fseek(disk6.disk[drive].fp, PHASE_BYTES * disk6.disk[drive].phase, SEEK_SET); } disk6.disk[drive].sector = 0; disk6.disk[drive].run_byte = 0; return 0; /* no problem */ } /* ------------------------------------------------------------------------- c_read_nibblized_6_6() - reads a standard .nib file of length 232960 bytes. there are 70 phases positioned every 3328 bytes. ------------------------------------------------------------------------- */ unsigned char c_read_nibblized_6_6() { static unsigned char ch; if (disk6.disk[disk6.drive].phase_change) { fseek(disk6.disk[disk6.drive].fp, PHASE_BYTES * disk6.disk[disk6.drive].phase, SEEK_SET); disk6.disk[disk6.drive].phase_change = 0; } disk6.disk_byte = fgetc(disk6.disk[disk6.drive].fp); ch = disk6.disk_byte; /* track revolves... */ if (ftell(disk6.disk[disk6.drive].fp) == (PHASE_BYTES * (disk6.disk[disk6.drive].phase + 2))) { fseek(disk6.disk[disk6.drive].fp, -2 * PHASE_BYTES, SEEK_CUR); } return ch; } /* ------------------------------------------------------------------------- c_read_normal_6() ------------------------------------------------------------------------- */ unsigned char c_read_normal_6() { int position; int old_value; unsigned char value = 0; /* The run byte tells what's to do */ switch (disk6.disk[disk6.drive].run_byte) { case 0: case 1: case 2: case 3: case 4: case 5: case 20: case 21: case 22: case 23: case 24: /* Sync */ value = 0xFF; break; case 6: case 25: /* Prologue (first byte) */ value = 0xD5; break; case 7: case 26: /* Prologue (second byte) */ value = 0xAA; break; case 8: /* Prologue (third byte) */ value = 0x96; break; case 9: /* Volume (encoded) */ value = (disk6.volume >> 1) | 0xAA; disk6.checksum = disk6.volume; break; case 10: /* Volume (encoded) */ value = disk6.volume | 0xAA; break; case 11: /* Track number (encoded) */ disk6.checksum ^= (disk6.disk[disk6.drive].phase >> 1); value = (disk6.disk[disk6.drive].phase >> 2) | 0xAA; break; case 12: /* Track number (encoded) */ value = (disk6.disk[disk6.drive].phase >> 1) | 0xAA; break; case 13: /* Sector number (encoded) */ disk6.checksum ^= disk6.disk[disk6.drive].sector; value = (disk6.disk[disk6.drive].sector >> 1) | 0xAA; break; case 14: /* Sector number (encoded) */ value = disk6.disk[disk6.drive].sector | 0xAA; break; case 15: /* Checksum */ value = (disk6.checksum >> 1) | 0xAA; break; case 16: /* Checksum */ value = disk6.checksum | 0xAA; break; case 17: case 371: /* Epilogue (first byte) */ value = 0xDE; break; case 18: case 372: /* Epilogue (second byte) */ value = 0xAA; break; case 19: case 373: /* Epilogue (third byte) */ value = 0xEB; break; case 27: /* Data header */ disk6.exor_value = 0; /* Set file position variable */ disk6.disk[disk6.drive].file_pos = 256 * 16 * (disk6.disk[disk6.drive].phase >> 1) + 256 * skew_table_6[ disk6.disk[disk6.drive].sector ]; /* File large enough? */ if (disk6.disk[disk6.drive].file_pos + 255 > disk6.disk[disk6.drive].file_size) { return 0xFF; } /* Set position */ fseek( disk6.disk[disk6.drive].fp, disk6.disk[disk6.drive].file_pos, SEEK_SET ); /* Read sector */ if (fread( disk6.disk_data, 1, 256, disk6.disk[disk6.drive].fp ) != 256) { // error } disk6.disk_data[ 256 ] = disk6.disk_data[ 257 ] = 0; value = 0xAD; break; case 370: /* Checksum */ value = translate_table_6[disk6.exor_value & 0x3F]; /* Increment sector number (and wrap if necessary) */ disk6.disk[disk6.drive].sector++; if (disk6.disk[disk6.drive].sector == 16) { disk6.disk[disk6.drive].sector = 0; } break; default: position = disk6.disk[disk6.drive].run_byte - 28; if (position >= 0x56) { position -= 0x56; old_value = disk6.disk_data[ position ]; old_value = old_value >> 2; disk6.exor_value ^= old_value; value = translate_table_6[disk6.exor_value & 0x3F]; disk6.exor_value = old_value; } else { old_value = 0; old_value |= (disk6.disk_data[position] & 0x1) << 1; old_value |= (disk6.disk_data[position] & 0x2) >> 1; old_value |= (disk6.disk_data[position+0x56] & 0x1) << 3; old_value |= (disk6.disk_data[position+0x56] & 0x2) << 1; old_value |= (disk6.disk_data[position+0xAC] & 0x1) << 5; old_value |= (disk6.disk_data[position+0xAC] & 0x2) << 3; disk6.exor_value ^= old_value; value = translate_table_6[disk6.exor_value & 0x3F]; disk6.exor_value = old_value; } break; } /* End switch */ /* Continue by increasing run byte value */ disk6.disk[disk6.drive].run_byte++; if (disk6.disk[disk6.drive].run_byte > 373) { disk6.disk[disk6.drive].run_byte = 0; } disk6.disk_byte = value; return value; } /* ------------------------------------------------------------------------- c_write_nibblized_6_6() - writes a standard .nib file of length 232960 bytes. there are 70 phases positioned every 3328 bytes. ------------------------------------------------------------------------- */ void c_write_nibblized_6_6() { if (disk6.disk[disk6.drive].phase_change) { fseek(disk6.disk[disk6.drive].fp, PHASE_BYTES * disk6.disk[disk6.drive].phase, SEEK_SET); disk6.disk[disk6.drive].phase_change = 0; } fputc(disk6.disk_byte, disk6.disk[disk6.drive].fp); /* track revolves... */ if (ftell(disk6.disk[disk6.drive].fp) == (PHASE_BYTES * (disk6.disk[disk6.drive].phase + 2))) { fseek(disk6.disk[disk6.drive].fp, -2 * PHASE_BYTES, SEEK_CUR); } } /* ------------------------------------------------------------------------- c_write_normal_6() disk6.disk_byte contains the value ------------------------------------------------------------------------- */ void c_write_normal_6() { int position; int old_value; if (disk6.disk_byte == 0xD5) { disk6.disk[disk6.drive].run_byte = 6; /* Initialize run byte value */ } /* The run byte tells what's to do */ switch (disk6.disk[disk6.drive].run_byte) { case 0: case 1: case 2: case 3: case 4: case 5: case 20: case 21: case 22: case 23: case 24: /* Sync */ break; case 6: case 25: /* Prologue (first byte) */ if (disk6.disk_byte == 0xFF) { disk6.disk[disk6.drive].run_byte--; } break; case 7: case 26: /* Prologue (second byte) */ break; case 8: /* Prologue (third byte) */ if (disk6.disk_byte == 0xAD) { disk6.exor_value = 0, disk6.disk[disk6.drive].run_byte = 27; } break; case 9: case 10: /* Volume */ break; case 11: case 12: /* Track */ break; case 13: case 14: /* Sector */ break; case 15: /* Checksum */ break; case 16: /* Checksum */ break; case 17: case 371: /* Epilogue (first byte) */ break; case 18: case 372: /* Epilogue (second byte) */ break; case 19: case 373: /* Epilogue (third byte) */ break; case 27: disk6.exor_value = 0; break; case 370: /* Set file position variable */ disk6.disk[disk6.drive].file_pos = 256 * 16 * (disk6.disk[disk6.drive].phase >> 1) + 256 * skew_table_6[ disk6.disk[disk6.drive].sector ]; /* Is the file large enough? */ if (disk6.disk[disk6.drive].file_pos + 255 > disk6.disk[disk6.drive].file_size) { return; } /* Set position */ fseek( disk6.disk[disk6.drive].fp, disk6.disk[disk6.drive].file_pos, SEEK_SET ); /* Write sector */ fwrite(disk6.disk_data, 1, 256, disk6.disk[disk6.drive].fp); fflush( disk6.disk[disk6.drive].fp ); /* Increment sector number (and wrap if necessary) */ disk6.disk[disk6.drive].sector++; if (disk6.disk[disk6.drive].sector == 16) { disk6.disk[disk6.drive].sector = 0; } break; default: position = disk6.disk[disk6.drive].run_byte - 28; disk6.disk_byte = translate_table_6[ disk6.disk_byte ]; if (position >= 0x56) { position -= 0x56; disk6.disk_byte ^= disk6.exor_value; old_value = disk6.disk_byte; disk6.disk_data[position] |= (disk6.disk_byte << 2) & 0xFC; disk6.exor_value = old_value; } else { disk6.disk_byte ^= disk6.exor_value; old_value = disk6.disk_byte; disk6.disk_data[position] = (disk6.disk_byte & 0x01) << 1; disk6.disk_data[position] |= (disk6.disk_byte & 0x02) >> 1; disk6.disk_data[position + 0x56] = (disk6.disk_byte & 0x04) >> 1; disk6.disk_data[position + 0x56] |= (disk6.disk_byte & 0x08) >> 3; disk6.disk_data[position + 0xAC] = (disk6.disk_byte & 0x10) >> 3; disk6.disk_data[position + 0xAC] |= (disk6.disk_byte & 0x20) >> 5; disk6.exor_value = old_value; } break; } /* End switch */ disk6.disk[disk6.drive].run_byte++; if (disk6.disk[disk6.drive].run_byte > 373) { disk6.disk[disk6.drive].run_byte = 0; } } GLUE_C_READ(disk_read_byte) { if (disk6.ddrw) { if (disk6.disk[disk6.drive].fp == NULL) { return 0; /* Return if there is no disk in drive */ } if (disk6.disk[disk6.drive].is_protected) { return 0; /* Do not write if diskette is write protected */ } if (disk6.disk_byte < 0x96) { return 0; /* Only byte values at least 0x96 are allowed */ } (disk6.disk[disk6.drive].nibblized) ? c_write_nibblized_6_6() : c_write_normal_6(); return 0; /* ??? */ } else { if (disk6.disk[disk6.drive].fp == NULL) { return 0xFF; /* Return FF if there is no disk in drive */ } if (disk6.motor) /* Motor turned on? */ { if (disk6.motor > 99) { return 0; } else { disk6.motor++; } } /* handle nibblized_6 or regular disks */ return (disk6.disk[disk6.drive].nibblized) ? c_read_nibblized_6_6() : c_read_normal_6(); } } GLUE_C_READ(disk_read_phase) { /* * Comment from xapple2+ by Phillip Stephens: * Turn motor phases 0 to 3 on. Turning on the previous phase + 1 * increments the track position, turning on the previous phase - 1 * decrements the track position. In this scheme phase 0 and 3 are * considered to be adjacent. The previous phase number can be * computed as the track number % 4. */ switch (((ea >> 1) - disk6.disk[disk6.drive].phase) & 3) { case 1: disk6.disk[disk6.drive].phase++; break; case 3: disk6.disk[disk6.drive].phase--; break; } if (disk6.disk[disk6.drive].phase<0) { disk6.disk[disk6.drive].phase=0; } if (disk6.disk[disk6.drive].phase>69) { disk6.disk[disk6.drive].phase=69; } disk6.disk[disk6.drive].phase_change = 1; return 0; } GLUE_C_READ(disk_read_motor_off) { disk6.motor = 1; return disk6.drive; } GLUE_C_READ(disk_read_motor_on) { disk6.motor = 0; return disk6.drive; } GLUE_C_READ(disk_read_select_a) { return disk6.drive = 0; } GLUE_C_READ(disk_read_select_b) { return disk6.drive = 1; } GLUE_C_READ(disk_read_latch) { return disk6.drive; } GLUE_C_READ(disk_read_prepare_in) { disk6.ddrw = 0; return disk6.disk[disk6.drive].is_protected ? 0x80 : 0x00; } GLUE_C_READ(disk_read_prepare_out) { disk6.ddrw = 1; return disk6.drive; } GLUE_C_WRITE(disk_write_latch) { disk6.disk_byte = b; } void disk_io_initialize(unsigned int slot) { FILE *f; int i; assert(slot == 6); /* load Disk II rom */ if (!slot6_rom_loaded) { snprintf(temp, TEMPSIZE, "%s/slot6.rom", system_path); if ((f = fopen( temp, "r" )) == NULL) { printf("Cannot find file '%s'.\n",temp); exit( 0 ); } if (fread(slot6_rom, 0x100, 1, f) != 0x100) { // error } fclose(f); slot6_rom_loaded = 1; } memcpy(apple_ii_64k[0] + 0xC600, slot6_rom, 0x100); // disk softswitches // 0xC0Xi : X = slot 0x6 + 0x8 == 0xE cpu65_vmem[0xC0E0].r = cpu65_vmem[0xC0E2].r = cpu65_vmem[0xC0E4].r = cpu65_vmem[0xC0E6].r = ram_nop; cpu65_vmem[0xC0E1].r = cpu65_vmem[0xC0E3].r = cpu65_vmem[0xC0E5].r = cpu65_vmem[0xC0E7].r = disk_read_phase; cpu65_vmem[0xC0E8].r = disk_read_motor_off; cpu65_vmem[0xC0E9].r = disk_read_motor_on; cpu65_vmem[0xC0EA].r = disk_read_select_a; cpu65_vmem[0xC0EB].r = disk_read_select_b; cpu65_vmem[0xC0EC].r = disk_read_byte; cpu65_vmem[0xC0ED].r = disk_read_latch; /* read latch */ cpu65_vmem[0xC0EE].r = disk_read_prepare_in; cpu65_vmem[0xC0EF].r = disk_read_prepare_out; for (i = 0xC0E0; i < 0xC0F0; i++) { cpu65_vmem[i].w = cpu65_vmem[i].r; } cpu65_vmem[0xC0ED].w = disk_write_latch; /* write latch */ }