/* ActiveGS, Copyright 2004-2016 Olivier Goguel, https://github.com/ogoguel/ActiveGS Based on Kegs, Copyright 2004 Kent Dickey, https://kegs.sourceforge.net This code is covered by the GNU GPL licence */ #include "defc.h" #include "moremem.h" #include "sim65816.h" #include "iwm.h" #include "config.h" #include "driver.h" #ifdef _MSC_VER #define snprintf _snprintf #endif //extern int g_rom_version; //extern word32 g_mem_size_base; extern byte *g_rom_fc_ff_ptr; extern byte *g_rom_cards_ptr; //extern word32 g_mem_size_exp; //extern Iwm iwm; ///extern int Verbose; //extern int g_highest_smartport_unit; // was in config.cpp int g_config_control_panel = 0; int g_config_kegs_update_needed = 0; word32 g_cfg_vbl_count = 0; int g_user_page2_shadow = 1; int g_save_cur_a2_stat = 0; const byte g_rom_c600_rom01_diffs[256] = { 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0xe2, 0x00, 0xd0, 0x50, 0x0f, 0x77, 0x5b, 0xb9, 0xc3, 0xb1, 0xb1, 0xf8, 0xcb, 0x4e, 0xb8, 0x60, 0xc7, 0x2e, 0xfc, 0xe0, 0xbf, 0x1f, 0x66, 0x37, 0x4a, 0x70, 0x55, 0x2c, 0x3c, 0xfc, 0xc2, 0xa5, 0x08, 0x29, 0xac, 0x21, 0xcc, 0x09, 0x55, 0x03, 0x17, 0x35, 0x4e, 0xe2, 0x0c, 0xe9, 0x3f, 0x9d, 0xc2, 0x06, 0x18, 0x88, 0x0d, 0x58, 0x57, 0x6d, 0x83, 0x8c, 0x22, 0xd3, 0x4f, 0x0a, 0xe5, 0xb7, 0x9f, 0x7d, 0x2c, 0x3e, 0xae, 0x7f, 0x24, 0x78, 0xfd, 0xd0, 0xb5, 0xd6, 0xe5, 0x26, 0x85, 0x3d, 0x8d, 0xc9, 0x79, 0x0c, 0x75, 0xec, 0x98, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x6c, 0x44, 0xce, 0x4c, 0x01, 0x08, 0x00, 0x00 }; extern void x_config_load_roms(); extern void x_config_load_additional_roms(); void config_load_roms() { int more_than_8mb; int changed_rom; int i; // OG load roms if (g_driver.x_config_load_roms==NULL) x_fatal_exit("missing rom"); g_driver.x_config_load_roms(); memset(&g_rom_cards_ptr[0], 0, 256*16); if (g_sim65816.g_rom_version < 0) { // No need to return ; } /* initialize c600 rom to be diffs from the real ROM, to build-in */ /* Apple II compatibility without distributing ROMs */ for(i = 0; i < 256; i++) { g_rom_cards_ptr[0x600 + i] = g_rom_fc_ff_ptr[0x3c600 + i] ^ g_rom_c600_rom01_diffs[i]; } if(g_sim65816.g_rom_version >= 3) { /* some patches */ g_rom_cards_ptr[0x61b] ^= 0x40; g_rom_cards_ptr[0x61c] ^= 0x33; g_rom_cards_ptr[0x632] ^= 0xc0; g_rom_cards_ptr[0x633] ^= 0x33; } x_config_load_additional_roms(); more_than_8mb = (g_sim65816.g_mem_size_exp > 0x800000); /* Only do the patch if users wants more than 8MB of expansion mem */ changed_rom = 0; if(g_sim65816.g_rom_version == 1) { /* make some patches to ROM 01 */ #if 0 /* 1: Patch ROM selftest to not do speed test */ printf("Patching out speed test failures from ROM 01\n"); g_rom_fc_ff_ptr[0x3785a] = 0x18; changed_rom = 1; #endif #if 0 /* 2: Patch ROM selftests not to do tests 2,4 */ /* 0 = skip, 1 = do it, test 1 is bit 0 of LSByte */ g_rom_fc_ff_ptr[0x371e9] = 0xf5; g_rom_fc_ff_ptr[0x371ea] = 0xff; changed_rom = 1; #endif if(more_than_8mb) { /* Geoff Weiss patch to use up to 14MB of RAM */ g_rom_fc_ff_ptr[0x30302] = 0xdf; g_rom_fc_ff_ptr[0x30314] = 0xdf; g_rom_fc_ff_ptr[0x3031c] = 0x00; changed_rom = 1; } /* Patch ROM selftest to not do ROM cksum if any changes*/ if(changed_rom) { g_rom_fc_ff_ptr[0x37a06] = 0x18; g_rom_fc_ff_ptr[0x37a07] = 0x18; } } else if(g_sim65816.g_rom_version == 3) { /* patch ROM 03 */ printf("Patching ROM 03 smartport bug\n"); /* 1: Patch Smartport code to fix a stupid bug */ /* that causes it to write the IWM status reg into c036, */ /* which is the system speed reg...it's "safe" since */ /* IWM status reg bit 4 must be 0 (7MHz)..., otherwise */ /* it might have turned on shadowing in all banks! */ g_rom_fc_ff_ptr[0x357c9] = 0x00; changed_rom = 1; #if 0 /* patch ROM 03 to not to speed test */ /* skip fast speed test */ g_rom_fc_ff_ptr[0x36ad7] = 0x18; g_rom_fc_ff_ptr[0x36ad8] = 0x18; changed_rom = 1; #endif #if 0 /* skip slow speed test */ g_rom_fc_ff_ptr[0x36ae7] = 0x18; g_rom_fc_ff_ptr[0x36ae8] = 0x6b; changed_rom = 1; #endif #if 0 /* 4: Patch ROM 03 selftests not to do tests 1-4 */ g_rom_fc_ff_ptr[0x364a9] = 0xf0; g_rom_fc_ff_ptr[0x364aa] = 0xff; changed_rom = 1; #endif /* ROM tests are in ff/6403-642x, where 6403 = addr of */ /* test 1, etc. */ if(more_than_8mb) { /* Geoff Weiss patch to use up to 14MB of RAM */ g_rom_fc_ff_ptr[0x30b] = 0xdf; g_rom_fc_ff_ptr[0x31d] = 0xdf; g_rom_fc_ff_ptr[0x325] = 0x00; changed_rom = 1; } if(changed_rom) { /* patch ROM 03 selftest to not do ROM cksum */ g_rom_fc_ff_ptr[0x36cb0] = 0x18; g_rom_fc_ff_ptr[0x36cb1] = 0x18; } } } Disk * cfg_get_dsk_from_slot_drive(int slot, int drive) { Disk *dsk; int max_drive; /* Get dsk */ max_drive = 2; switch(slot) { case 5: dsk = &(g_iwm.iwm.drive35[drive]); break; case 6: dsk = &(g_iwm.iwm.drive525[drive]); break; default: max_drive = MAX_C7_DISKS; dsk = &(g_iwm.iwm.smartport[drive]); break; } if(drive >= max_drive) { dsk -= drive; /* move back to drive 0 effectively */ } dsk->slot = slot; dsk->disk = drive+1; return dsk; } void insert_disk(int slot, int drive, const char *name, int ejected, int force_size, const char *partition_name, int part_num) { byte buf_2img[512]; Disk *dsk; // char *part_ptr; int size; #if 0 // char *name_ptr; char *uncomp_ptr, *system_str; int system_len; #endif // int part_len; int cmp_o, cmp_p, cmp_dot; int cmp_b, cmp_i, cmp_n; int can_write; int len; int nibs; int unix_pos; size_t name_len; int image_identified; int exp_size; int save_track; int ret; int tmp; int i; g_config_kegs_update_needed = 1; if((slot < 5) || (slot > 7)) { fatal_printf("Invalid slot for inserting disk: %d\n", slot); return; } if(drive < 0 || ((slot == 7) && (drive >= MAX_C7_DISKS)) || ((slot < 7) && (drive > 1))) { fatal_printf("Invalid drive for inserting disk: %d\n", drive); return; } dsk = cfg_get_dsk_from_slot_drive(slot, drive); // OG Do not insert same disk at same place if (( dsk->name_ptr && name) && !strcmp(dsk->name_ptr,name)) return ; #if 0 printf("Inserting disk %s (%s or %d) in slot %d, drive: %d\n", name, partition_name, part_num, slot, drive); #endif dsk->just_ejected = 0; dsk->force_size = force_size; if(dsk->fd >= 0) { eject_disk(dsk); } // OG check for valid name first if (!name) return ; /* Before opening, make sure no other mounted disk has this name */ /* If so, unmount it */ if(!ejected) { for(i = 0; i < 2; i++) { eject_named_disk(&g_iwm.iwm.drive525[i], name,partition_name); eject_named_disk(&g_iwm.iwm.drive35[i], name, partition_name); } for(i = 0; i < MAX_C7_DISKS; i++) { eject_named_disk(&g_iwm.iwm.smartport[i],name,partition_name); } } /* if(dsk->name_ptr != 0) { // free old name_ptr free(dsk->name_ptr); } */ name_len = strlen(name); strncpy(dsk->name_ptr,name,MAX_DISK_NAME); /* name_len = strlen(name); name_ptr = (char *)malloc(name_len + 1); strncpy(name_ptr, name, name_len + 1); dsk->name_ptr = name_ptr; */ // dsk->partition_name = 0; dsk->partition_name[0] = 0; if(partition_name != 0) { /* part_len = strlen(partition_name) + 1; part_ptr = (char *)malloc(part_len); strncpy(part_ptr, partition_name, part_len); dsk->partition_name = part_ptr; */ strncpy(dsk->partition_name,partition_name,MAX_DISK_NAME); } dsk->partition_num = part_num; iwm_printf("Opening up disk image named: %s\n", dsk->name_ptr); if(ejected) { /* just get out of here */ dsk->fd = -1; return; } dsk->fd = -1; #ifdef ACTIVEGSKARATEKA can_write = 0; #else can_write = 1; #endif //OG discard .GZ support #if 0 if((name_len > 3) && (strcmp(&name_ptr[name_len - 3], ".gz") == 0)) { /* it's gzip'ed, try to gunzip it, then unlink the */ /* uncompressed file */ can_write = 0; uncomp_ptr = (char *)malloc(name_len + 1); strncpy(uncomp_ptr, name_ptr, name_len + 1); uncomp_ptr[name_len - 3] = 0; system_len = 2*name_len + 100; system_str = (char *)malloc(system_len + 1); snprintf(system_str, system_len, "set -o noclobber;gunzip -c %c%s%c > %c%s%c", 0x22, name_ptr, 0x22, 0x22, uncomp_ptr, 0x22); /* 0x22 are " to allow spaces in filenames */ printf("I am uncompressing %s into %s for mounting\n", name_ptr, uncomp_ptr); ret = system(system_str); if(ret == 0) { /* successfully ran */ dsk->fd = open(uncomp_ptr, O_RDONLY | O_BINARY, 0x1b6); iwm_printf("Opening .gz file %s is fd: %d\n", uncomp_ptr, dsk->fd); /* and, unlink the temporary file */ (void)unlink(uncomp_ptr); } free(system_str); free(uncomp_ptr); /* Reduce name_len by 3 so that subsequent compares for .po */ /* look at the correct chars */ name_len -= 3; } #endif // OG if(dsk->fd < 0 && can_write) { dsk->fd = open(dsk->name_ptr, O_RDWR | O_BINARY, 0x1b6); } if(dsk->fd < 0 /*&& can_write*/) { printf("Trying to open %s read-only, errno: %d\n", dsk->name_ptr, errno); dsk->fd = open(dsk->name_ptr, O_RDONLY | O_BINARY, 0x1b6); can_write = 0; } iwm_printf("open returned: %d\n", dsk->fd); if(dsk->fd < 0) { fatal_printf("Disk image %s does not exist!\n", dsk->name_ptr); return; } if(can_write != 0) { dsk->write_prot = 0; dsk->write_through_to_unix = 1; } else { dsk->write_prot = 1; dsk->write_through_to_unix = 0; } save_track = dsk->cur_qtr_track; /* save arm position */ dsk->image_type = DSK_TYPE_PRODOS; dsk->image_start = 0; /* See if it is in 2IMG format */ ret = read(dsk->fd, (char *)&buf_2img[0], 512); size = force_size; if(size <= 0) { size = cfg_get_fd_size(dsk->fd) ; } /* Try to guess that there is a Mac Binary header of 128 bytes */ /* See if image size & 0xfff = 0x080 which indicates extra 128 bytes */ if((size & 0xfff) == 0x080) { printf("Assuming Mac Binary header on %s\n", dsk->name_ptr); dsk->image_start += 0x80; } image_identified = 0; if(buf_2img[0] == '2' && buf_2img[1] == 'I' && buf_2img[2] == 'M' && buf_2img[3] == 'G') { // OG Put back Gilles Tschopp hack to deal with corrupted 2IMG files size -= 64; size = size & -0x1000; /* It's a 2IMG disk */ #ifdef _DEBUG printf("Image named %s is in 2IMG format\n", dsk->name_ptr); #endif image_identified = 1; if(buf_2img[12] == 0) { printf("2IMG is in DOS 3.3 sector order\n"); dsk->image_type = DSK_TYPE_DOS33; } if(buf_2img[19] & 0x80) { /* disk is locked */ printf("2IMG is write protected\n"); dsk->write_prot = 1; dsk->write_through_to_unix = 0; } if((buf_2img[17] & 1) && (dsk->image_type == DSK_TYPE_DOS33)) { dsk->vol_num = buf_2img[16]; printf("Setting DOS 3.3 vol num to %d\n", dsk->vol_num); } // OG do not overwrite when 2mg size = 0 int size2img = (buf_2img[31] << 24) + (buf_2img[30] << 16) + (buf_2img[29] << 8) + buf_2img[28]; if (size2img) size = size2img; else printf(".2MG has corrupted size - fixed.\n"); unix_pos = (buf_2img[27] << 24) + (buf_2img[26] << 16) + (buf_2img[25] << 8) + buf_2img[24]; // Some 2IMG archives have the size byte reversed if(size == 0x800c00) { // Byte reversed 0x0c8000 size = 0x0c8000; } dsk->image_start = unix_pos; dsk->image_size = size; } exp_size = 800*1024; if(dsk->disk_525) { exp_size = 140*1024; } if(!image_identified) { /* See if it might be the Mac diskcopy format */ tmp = (buf_2img[0x40] << 24) + (buf_2img[0x41] << 16) + (buf_2img[0x42] << 8) + buf_2img[0x43]; if((size >= (exp_size + 0x54)) && (tmp == exp_size)) { /* It's diskcopy since data size field matches */ printf("Image named %s is in Mac diskcopy format\n", dsk->name_ptr); image_identified = 1; dsk->image_start += 0x54; dsk->image_size = exp_size; dsk->image_type = DSK_TYPE_PRODOS; /* ProDOS */ } } if(!image_identified) { /* Assume raw image */ dsk->image_size = size; dsk->image_type = DSK_TYPE_PRODOS; if(dsk->disk_525) { dsk->image_type = DSK_TYPE_DOS33; if(name_len >= 4) { cmp_o = dsk->name_ptr[name_len-1]; cmp_p = dsk->name_ptr[name_len-2]; cmp_dot = dsk->name_ptr[name_len-3]; if(cmp_dot == '.' && (cmp_p == 'p' || cmp_p == 'P') && (cmp_o == 'o' || cmp_o == 'O')) { dsk->image_type = DSK_TYPE_PRODOS; } cmp_b = dsk->name_ptr[name_len-1]; cmp_i = dsk->name_ptr[name_len-2]; cmp_n = dsk->name_ptr[name_len-3]; cmp_dot = dsk->name_ptr[name_len-4]; if(cmp_dot == '.' && (cmp_n == 'n' || cmp_n == 'N') && (cmp_i == 'i' || cmp_i == 'I') && (cmp_b == 'b' || cmp_b == 'B')) { dsk->image_type = DSK_TYPE_NIB; dsk->write_prot = 0; // OG : Remove WriteProtect on .nib !!!! TO REACTIVATE !! dsk->write_through_to_unix = 0; } } } } dsk->disk_dirty = 0; dsk->nib_pos = 0; dsk->trks = 0; if(dsk->smartport) { g_iwm.g_highest_smartport_unit = MAX(dsk->drive,g_iwm.g_highest_smartport_unit); // OG Disabled partition support #ifndef ACTIVEGS if( (partition_name != NULL && *partition_name ) || part_num >= 0) { ret = cfg_partition_find_by_name_or_num(dsk->fd, partition_name, part_num, dsk); printf("partition %s (num %d) mounted, wr_prot: %d\n", partition_name, part_num, dsk->write_prot); if(ret < 0) { close(dsk->fd); dsk->fd = -1; return; } } #endif iwm_printf("adding smartport device[%d], size:%08x, " "img_sz:%08x\n", dsk->drive, dsk->trks[0].unix_len, dsk->image_size); } else if(dsk->disk_525) { unix_pos = dsk->image_start; size = dsk->image_size; disk_set_num_tracks(dsk, 4*35); len = 0x1000; nibs = NIB_LEN_525; if(dsk->image_type == DSK_TYPE_NIB) { len = dsk->image_size / 35;; nibs = len; } if(size != 35*len) { fatal_printf("Disk 5.25 error: size is %d, not 140K. " "Will try to mount anyway\n", size, 35*len); } for(i = 0; i < 35; i++) { iwm_move_to_track(dsk, 4*i); disk_unix_to_nib(dsk, 4*i, unix_pos, len, nibs); unix_pos += len; } } else { /* disk_35 */ unix_pos = dsk->image_start; size = dsk->image_size; if(size != 800*1024) { fatal_printf("Disk 3.5 error: size is %d, not 800K. " "Will try to mount anyway\n", size); } disk_set_num_tracks(dsk, 2*80); for(i = 0; i < 2*80; i++) { iwm_move_to_track(dsk, i); len = g_track_bytes_35[i >> 5]; nibs = g_track_nibs_35[i >> 5]; iwm_printf("Trk: %d.%d = unix: %08x, %04x, %04x\n", i>>1, i & 1, unix_pos, len, nibs); disk_unix_to_nib(dsk, i, unix_pos, len, nibs); unix_pos += len; iwm_printf(" trk_len:%05x\n", dsk->trks[i].track_len); } } iwm_move_to_track(dsk, save_track); x_notify_disk_insertion(dsk->image_size,dsk->image_start,(dsk->image_type == DSK_TYPE_PRODOS),slot,drive); } void eject_named_disk(Disk *dsk, const char *name, const char *partition_name) { if(dsk->fd < 0) { return; } /* If name matches, eject the disk! */ if(!strcmp(dsk->name_ptr, name)) { /* It matches, eject it */ if((partition_name != 0) && (dsk->partition_name != 0)) { /* If both have partitions, and they differ, then */ /* don't eject. Otherwise, eject */ if(strcmp(dsk->partition_name, partition_name) != 0) { /* Don't eject */ return; } } eject_disk(dsk); } } void eject_disk_by_num(int slot, int drive) { Disk *dsk; dsk = cfg_get_dsk_from_slot_drive(slot, drive); eject_disk(dsk); } void eject_disk(Disk *dsk) { int motor_on; int i; if(dsk->fd < 0) { return; } g_config_kegs_update_needed = 1; motor_on = g_iwm.iwm.motor_on; if(g_moremem.g_c031_disk35 & 0x40) { motor_on = g_iwm.iwm.motor_on35; } if(motor_on) { halt_printf("Try eject dsk:%s, but motor_on!\n", dsk->name_ptr); } iwm_flush_disk_to_unix(dsk); printf("Ejecting disk: %s\n", dsk->name_ptr); /* Free all memory, close file */ /* free the tracks first */ if(dsk->trks != 0) { for(i = 0; i < dsk->num_tracks; i++) { if(dsk->trks[i].nib_area) { free(dsk->trks[i].nib_area); } dsk->trks[i].nib_area = 0; dsk->trks[i].track_len = 0; } free(dsk->trks); } dsk->num_tracks = 0; dsk->trks = 0; /* close file, clean up dsk struct */ close(dsk->fd); dsk->image_start = 0; dsk->image_size = 0; dsk->nib_pos = 0; dsk->disk_dirty = 0; dsk->write_through_to_unix = 0; dsk->write_prot = 1; dsk->fd = -1; dsk->just_ejected = 1; /* Leave name_ptr valid */ // OG WHY? /* if (dsk->name_ptr) { free(dsk->name_ptr); dsk->name_ptr = NULL; } */ dsk->name_ptr[0]=0; } int cfg_get_fd_size(int fd) { struct stat stat_buf; int ret; ret = fstat(fd, &stat_buf); if(ret != 0) { fprintf(stderr,"fstat returned %d on fd %d, errno: %d\n", ret, fd, errno); stat_buf.st_size = 0; } return (int)stat_buf.st_size; }