diff --git a/src/core/cpu_inline.h b/src/core/cpu_inline.h index 10a1396c3..a879aad20 100644 --- a/src/core/cpu_inline.h +++ b/src/core/cpu_inline.h @@ -633,25 +633,23 @@ INLINE static BYTE fds_rd_mem(BYTE nidx, WORD address, BYTE made_tick) { // |||+------ CRC control (0: CRC passed; 1: CRC error) // |+-------- End of Head (1 when disk head is on the most inner track) // +--------- Disk Data Read/Write Enable (1 when disk is readable/writable) - - // azzero - nes[nidx].c.cpu.openbus = 0; // bit 0 (timer irq) - nes[nidx].c.cpu.openbus |= fds.drive.irq_timer_high; + nes[nidx].c.cpu.openbus = fds.drive.irq_timer_high; // bit 1 (trasfer flag) nes[nidx].c.cpu.openbus |= fds.drive.transfer_flag; // bit 2 e 3 non settati // TODO : bit 4 (CRC control : 0 passato, 1 errore) + nes[nidx].c.cpu.openbus |= (fds.drive.crc ? 0x10 : 0x00); // bit 5 non settato - // TODO : bit 6 (end of head) + // bit 6 (end of head) nes[nidx].c.cpu.openbus |= fds.drive.end_of_head; - //fds.drive.end_of_head = FALSE; - // TODO : bit 7 (disk data read/write enable (1 when disk is readable/writable) - fds.drive.transfer_flag = FALSE; + // bit 7 (disk data read/write enable (1 when disk is readable/writable)) + nes[nidx].c.cpu.openbus |= fds.drive.data_available; // devo disabilitare sia il timer IRQ ... fds.drive.irq_timer_high = FALSE; nes[nidx].c.irq.high &= ~FDS_TIMER_IRQ; // che il disk IRQ + fds.drive.transfer_flag = FALSE; nes[nidx].c.irq.high &= ~FDS_DISK_IRQ; #if !defined (RELEASE) //printf("0x%04X 0x%02X %d\n", address, nes[nidx].c.cpu.openbus, nes[nidx].c.irq.high); @@ -659,15 +657,16 @@ INLINE static BYTE fds_rd_mem(BYTE nidx, WORD address, BYTE made_tick) { return (TRUE); } if (address == 0x4031) { - nes[nidx].c.cpu.openbus = fds.drive.data_readed; + nes[nidx].c.cpu.openbus = fds.drive.data_io; + fds.drive.data_available = FALSE; + // devo disabilitare il disk IRQ + fds.drive.transfer_flag = FALSE; + nes[nidx].c.irq.high &= ~FDS_DISK_IRQ; #if !defined (RELEASE) //printf("0x%04X 0x%02X [0x%04X] 0x%04X %d %d\n", address, nes[nidx].c.cpu.openbus, // fds.info.sides[fds.drive.side_inserted].data[fds.drive.disk_position], nes[nidx].c.cpu.opcode_PC, // fds.drive.disk_position, nes[nidx].c.irq.high); #endif - fds.drive.transfer_flag = FALSE; - // devo disabilitare il disk IRQ - nes[nidx].c.irq.high &= ~FDS_DISK_IRQ; return (TRUE); } if (address == 0x4032) { @@ -685,33 +684,42 @@ INLINE static BYTE fds_rd_mem(BYTE nidx, WORD address, BYTE made_tick) { } else if (fds.drive.end_of_head | fds.drive.transfer_reset) { nes[nidx].c.cpu.openbus |= 0x02; } + nes[nidx].c.cpu.openbus |= fds.info.write_protected; + #if !defined (RELEASE) //printf("%5d - %3d - %3d : 0x%04X 0x%02X %d %d %d %d\n", // nes[nidx].p.ppu.frames, // nes[nidx].p.ppu.frame_y, // nes[nidx].p.ppu.frame_x, // address, nes[nidx].c.cpu.openbus, fds.drive.disk_ejected, fds.drive.scan, - // fds.drive.delay, fds.drive.disk_position); + // fds.drive.delay_insert, fds.drive.disk_position); #endif - if (fds_auto_insert_enabled()) { - if ((nes[nidx].p.ppu.frames - fds.auto_insert.r4032.frames) < 100) { - if ((++fds.auto_insert.r4032.checks > FDS_AUTOINSERT_R4032_MAX_CHECKS) && - (fds.auto_insert.delay.side == -1) && - (fds.auto_insert.delay.eject == -1) && - (fds.auto_insert.delay.dummy == -1)) { - // FDS interessati : - // - Ao no Senritsu (1987)(Gakken)(J).fds - // - Bishojou SF Alien Battle (19xx)(Hacker International)(J)(Unl)[b].fds - // senza questo controllo entrambi gli fds potrebbero rimanere in attesa - // del cambio side senza che l'auto insert avvenga visto che l'END_OF_HEAD - // e' stato raggiunto prima. - fds.auto_insert.r4032.checks = 0; - fds.auto_insert.delay.eject = FDS_OP_SIDE_DELAY; + if (fds_auto_insert_enabled() && !fds.auto_insert.r4032.disabled && !fds.auto_insert.delay.dummy) { + if (!fds.auto_insert.r4032.frames) { + fds.auto_insert.r4032.frames = nes[nidx].p.ppu.frames; + } + { + int diff = (nes[nidx].p.ppu.frames - fds.auto_insert.r4032.frames); + + if (diff > 10) { + if (diff < 70) { + if (!fds.drive.scan && (++fds.auto_insert.r4032.checks > FDS_AUTOINSERT_R4032_MAX_CHECKS)) { + // - 19 Neunzehn (1988)(Soft Pro)(J).fds (il controllo del r4032 e' mooooolto lento) + // - Dandy (19xx)(Pony Canyon)(J).fds + // - Zelda no Densetsu - The Hyrule Fantasy (1986)(Nintendo)(J).fds + // - Ao no Senritsu (1987)(Gakken)(J).fds + // - Bishojou SF Alien Battle (19xx)(Hacker International)(J)(Unl)[b].fds + fds_disk_op(FDS_DISK_EJECT, 0, TRUE); + gui_update_fds_menu(); + fds.auto_insert.delay.dummy = fds.info.cycles_dummy_delay; + } + fds.auto_insert.r4032.frames = nes[nidx].p.ppu.frames; + } else { + fds.auto_insert.r4032.frames = 0; + fds.auto_insert.r4032.checks = 0; + } } - } else { - fds.auto_insert.r4032.checks = 0; } - fds.auto_insert.r4032.frames = nes[nidx].p.ppu.frames; } return (TRUE); } @@ -1808,15 +1816,6 @@ INLINE static BYTE fds_wr_mem(BYTE nidx, WORD address, BYTE value) { if ((address >= 0x4020) && (address <= 0x4026)) { // eseguo un tick hardware tick_hw(nidx, 1); -#if !defined (RELEASE) - //if (address == 0x4025) { - // printf("0x%04X 0x%02X %d\n", address, value, fds.drive.enabled_dsk_reg); - //} else { - // if (fds.drive.disk_position) - // printf("0x%04X 0x%02X 0x%04X %d 0x%02X %d\n", address, value, nes[nidx].c.cpu.opcode_PC, - // fds.drive.disk_position - 1, fds.side.data[fds.drive.disk_position - 1], nes[nidx].p.ppu.frames); - //} -#endif if (address == 0x4020) { // 7 bit 0 // --------- @@ -1832,7 +1831,7 @@ INLINE static BYTE fds_wr_mem(BYTE nidx, WORD address, BYTE value) { // LLLL LLLL // |||| |||| // ++++-++++- 8 MSB of IRQ timer - fds.drive.irq_timer_reload = (value << 8) | (fds.drive.irq_timer_reload & 0x00FF); + fds.drive.irq_timer_reload = (fds.drive.irq_timer_reload & 0x00FF) | (value << 8); return (TRUE); } if (address == 0x4022) { @@ -1845,12 +1844,12 @@ INLINE static BYTE fds_wr_mem(BYTE nidx, WORD address, BYTE value) { if (fds.drive.enabled_dsk_reg) { fds.drive.irq_timer_reload_enabled = value & 0x01; fds.drive.irq_timer_enabled = value & 0x02; - } - if (fds.drive.irq_timer_enabled) { - fds.drive.irq_timer_counter = fds.drive.irq_timer_reload; - } else { - fds.drive.irq_timer_high = FALSE; - nes[nidx].c.irq.high &= ~FDS_TIMER_IRQ; + if (fds.drive.irq_timer_enabled) { + fds.drive.irq_timer_counter = fds.drive.irq_timer_reload; + } else { + fds.drive.irq_timer_high = FALSE; + nes[nidx].c.irq.high &= ~FDS_TIMER_IRQ; + } } return (TRUE); } @@ -1866,7 +1865,6 @@ INLINE static BYTE fds_wr_mem(BYTE nidx, WORD address, BYTE value) { #endif fds.drive.enabled_dsk_reg = value & 0x01; fds.drive.enabled_snd_reg = value & 0x02; - if (!fds.drive.enabled_dsk_reg) { fds.drive.irq_timer_high = FALSE; nes[nidx].c.irq.high &= ~FDS_TIMER_IRQ; @@ -1874,9 +1872,19 @@ INLINE static BYTE fds_wr_mem(BYTE nidx, WORD address, BYTE value) { return (TRUE); } if (address == 0x4024) { - fds.drive.data_to_write = value; + if (fds.drive.enabled_dsk_reg) { + fds.drive.data_io = value; + fds.drive.data_available = FALSE; + } fds.drive.transfer_flag = FALSE; nes[nidx].c.irq.high &= ~FDS_DISK_IRQ; +#if !defined (RELEASE) + //printf("%5d - %3d - %3d : 0x%04X 0x%02X %d %d\n", + // nes[nidx].p.ppu.frames, + // nes[nidx].p.ppu.frame_y, + // nes[nidx].p.ppu.frame_x, + // address, value, fds.drive.disk_position, fds.drive.io_mode); +#endif return (TRUE); } if (address == 0x4025) { @@ -1904,32 +1912,43 @@ INLINE static BYTE fds_wr_mem(BYTE nidx, WORD address, BYTE value) { // nes[nidx].p.ppu.frames, // nes[nidx].p.ppu.frame_y, // nes[nidx].p.ppu.frame_x, - // address, value, fds.drive.disk_ejected, fds.drive.scan, - // fds.drive.delay, fds.drive.disk_position); + // address, value, fds.drive.motor_on, fds.drive.scan, + // fds.drive.delay_insert, fds.drive.disk_position); #endif - if (!fds.drive.enabled_dsk_reg) { - return (TRUE); - } - - fds.drive.motor_on = value & 0x01; - fds.drive.transfer_reset = value & 0x02; + if (fds.drive.enabled_dsk_reg) { + fds.drive.motor_on = value & 0x01; + if (!fds.drive.transfer_reset && (value & 0x02)) { + fds.drive.motor_started = TRUE; + } + fds.drive.transfer_reset = value & 0x02; + if (!fds.drive.transfer_reset || !fds.drive.motor_on) { + fds.drive.motor_started = FALSE; + } + fds.drive.io_mode = value & 0x04; + fds.drive.mirroring = value & 0x08; + if (fds.drive.mirroring) { + mirroring_H(nidx); + } else { + mirroring_V(nidx); + } + // If we are writing and a CRC value is about to be written (rising edge on bit 4), + // feed two empty bytes to the CRC routine to yield the correct value. + if (!fds.drive.crc_control && (value & 0x10) && !fds.drive.io_mode) { + fds.drive.crc = fds_crc_byte(fds.drive.crc, FDS_DISK_GAP); + fds.drive.crc = fds_crc_byte(fds.drive.crc, FDS_DISK_GAP); + } + fds.drive.crc_control = value & 0x10; + fds.drive.unknow = value & 0x20; + fds.drive.drive_ready = value & 0x40; + if (!fds.drive.drive_ready) { + fds.drive.mark_finded = FALSE; + } + fds.drive.irq_disk_enabled = value & 0x80; -// TODO : penso che sia corretto -// if (fds.drive.read_mode != (value & 0x04)) { -// fds.drive.gap_ended = FALSE; -// } - fds.drive.read_mode = value & 0x04; - fds.drive.mirroring = value & 0x08; - if (fds.drive.mirroring) { - mirroring_H(nidx); - } else { - mirroring_V(nidx); + // "Akuu Senki Raijin (Japan) (Disk Writer)" (sporcare lo screen). + fds.drive.delay_8bit = fds.info.cycles_8bit_delay; } - fds.drive.crc_control = value & 0x10; - fds.drive.unknow = value & 0x20; - fds.drive.drive_ready = value & 0x40; - fds.drive.irq_disk_enabled = value & 0x80; - + fds.drive.transfer_flag = FALSE; nes[nidx].c.irq.high &= ~FDS_DISK_IRQ; return (TRUE); } diff --git a/src/core/fds.c b/src/core/fds.c index 2f1d23a43..47126ef64 100644 --- a/src/core/fds.c +++ b/src/core/fds.c @@ -28,7 +28,6 @@ #include "../../c++/crc/crc.h" #define BIOSFILE "disksys.rom" -#define DIFFVERSION 2 typedef struct _fds_sinfo { struct _fds_side_block1 { @@ -59,18 +58,46 @@ typedef struct _fds_sinfo { } _fds_sinfo; typedef struct _fds_diff_ele { BYTE side; - WORD value; + BYTE value; uint32_t position; } _fds_diff_ele; - -void fds_to_image(void); +typedef struct _fds_control_autoinsert { + char name[4]; + uint32_t crc32prg[4]; + BYTE disable_autoinsert; + BYTE disable_r4032; + BYTE disable_end_of_head; +} _fds_control_autoinsert; +typedef struct _fds_info_block { + uint32_t blength; + WORD faddress; + WORD flength; + WORD flength_new; + WORD flength_cpu; + WORD flength_ppu; + BYTE ftype; + BYTE files; + BYTE bl1; + BYTE stop; + int count2000; + BYTE magic_card_trainer; + BYTE quick_hunter; + BYTE ouji; + BYTE kgk; +} _fds_info_block; + +BYTE fds_to_image(void); +BYTE fds_examine_block(const BYTE *src, uint32_t position, _fds_info_block *fb); void fds_control_autoinsert(_fds_sinfo *sinfo); void fds_diff_file_name(uTCHAR *dst, size_t lenght); void fds_image_sinfo(BYTE side, _fds_sinfo *sinfo); -void fds_image_memset(WORD *dst, WORD value, uint32_t lenght); -void fds_image_memcpy(const BYTE *src, WORD *dst, uint32_t lenght); -void fds_image_memcpy_ASCII(const WORD *src, BYTE *dst, size_t lenght); -WORD fds_block_crc(const WORD *src, uint32_t lenght); +void fds_image_memset(BYTE *dst, BYTE value, uint32_t lenght); +void fds_image_memcpy(const BYTE *src, BYTE *dst, uint32_t lenght); +void fds_image_memcpy_ASCII(const BYTE *src, BYTE *dst, size_t lenght); +WORD fds_crc_block(const BYTE *src, uint32_t lenght); +BYTE *fds_from_image_to_mem(BYTE format, BYTE type, size_t *size); +BYTE *fds_read_ips(BYTE *data, const size_t size, const char *file); +BYTE fds_create_ips(const BYTE *d1, const size_t size1, const BYTE *d2, const size_t size2, const char *file); _fds fds; @@ -89,20 +116,32 @@ void fds_init(void) { void fds_quit(void) { if (fds.info.image) { // se richiesto, sovrascrivo il file originale - if ((cfg->fds_write_mode == FDS_WR_ORIGINAL_FILE) && fds.info.writings_occurred) { - fds_from_image(info.rom.file, info.format, fds.info.type); + if (fds.info.writings_occurred) { + uTCHAR file[LENGTH_FILE_NAME_LONG]; + + fds_diff_file_name(&file[0], usizeof(file)); + if (cfg->fds_write_mode == FDS_WR_ORIGINAL_FILE) { + fds_from_image_to_file(info.rom.file, fds.info.format, fds.info.type); + } else { + size_t size = 0; + BYTE *mfds = fds_from_image_to_mem(fds.info.format, fds.info.type, &size); + + if (!mfds || fds_create_ips(fds.info.data, fds.info.total_size, mfds, size, file)) { + log_error(uL("FDS;error on writing diff file")); + } + if (mfds) { + free(mfds); + } + } } free(fds.info.image); } if (fds.info.data) { free(fds.info.data); } - if (fds.info.diff) { - fclose(fds.info.diff); - } fds_init(); } -BYTE fds_load_rom(BYTE type) { +BYTE fds_load_rom(BYTE format) { _rom_mem rom; unsigned int i = 0; @@ -111,7 +150,7 @@ BYTE fds_load_rom(BYTE type) { uTCHAR rom_ext[2][10] = { uL(".fds\0"), uL(".FDS\0") }; FILE *fp = NULL; - if (type == QD_FORMAT) { + if (format == QD_FORMAT) { ustrncpy(&rom_ext[0][0], uL(".qd\0"), usizeof(rom_ext[0])); ustrncpy(&rom_ext[0][0], uL(".QD\0"), usizeof(rom_ext[0])); } @@ -195,16 +234,21 @@ BYTE fds_load_rom(BYTE type) { rom.position = 0; } - info.format = type; + info.format = fds.info.format = format; info.number_of_nes = 1; - fds.info.expcted_side = fds.info.total_sides; + fds.info.expcted_sides = fds.info.total_sides; + fds.info.write_protected = 0x00; + fds.info.cycles_8bit_delay = emu_ms_to_cpu_cycles(FDS_8BIT_MS_DELAY); + fds.info.cycles_dummy_delay = emu_ms_to_cpu_cycles(FDS_OP_SIDE_MS_DELAY); // converto nel mio formato immagine - fds_to_image(); + if (fds_to_image()) { + return (EXIT_ERROR); + } // inserisco il primo fds.info.frame_insert = nes[0].p.ppu.frames; - fds.info.bios_first_run = !cfg->fds_disk1sideA_at_reset; + fds.info.bios.first_run = !cfg->fds_disk1sideA_at_reset; fds_disk_op(cfg->fds_disk1sideA_at_reset ? FDS_DISK_SELECT_AND_INSERT : FDS_DISK_SELECT, 0, FALSE); info.cpu_rw_extern = TRUE; @@ -230,49 +274,54 @@ BYTE fds_load_rom(BYTE type) { return (EXIT_OK); } BYTE fds_load_bios(void) { - uTCHAR bios_file[LENGTH_FILE_NAME_LONG], *lastSlash = NULL; + uTCHAR *lastSlash = NULL; FILE *bios = NULL; // ordine di ricerca: // 1) file specificato dall'utente - usnprintf(bios_file, usizeof(bios_file), uL("" uPs("")), cfg->fds_bios_file); - bios = ufopen(bios_file, uL("rb")); + umemset(fds.info.bios.file, 0x00, usizeof(fds.info.bios.file)); + usnprintf(fds.info.bios.file, usizeof(fds.info.bios.file), uL("" uPs("")), cfg->fds_bios_file); + bios = ufopen(fds.info.bios.file, uL("rb")); if (bios) { goto fds_load_bios_founded; } // 2) directory di lavoro - ustrncpy(bios_file, uL("" BIOSFILE), usizeof(bios_file)); - bios = ufopen(bios_file, uL("rb")); + umemset(fds.info.bios.file, 0x00, usizeof(fds.info.bios.file)); + ustrncpy(fds.info.bios.file, uL("" BIOSFILE), usizeof(fds.info.bios.file)); + bios = ufopen(fds.info.bios.file, uL("rb")); if (bios) { goto fds_load_bios_founded; } // 3) directory contenente il file fds - ustrncpy(bios_file, info.rom.file, usizeof(bios_file)); + umemset(fds.info.bios.file, 0x00, usizeof(fds.info.bios.file)); + ustrncpy(fds.info.bios.file, info.rom.file, usizeof(fds.info.bios.file)); // rintraccio l'ultimo '.' nel nome #if defined (_WIN32) - lastSlash = ustrrchr(bios_file, uL('\\')); + lastSlash = ustrrchr(fds.info.bios.file, uL('\\')); if (lastSlash) { (*(lastSlash + 1)) = 0x00; } #else - lastSlash = ustrrchr(bios_file, uL('/')); + lastSlash = ustrrchr(fds.info.bios.file, uL('/')); if (lastSlash) { (*(lastSlash + 1)) = 0x00; } #endif // aggiungo il nome del file - ustrcat(bios_file, uL("" BIOSFILE)); - bios = ufopen(bios_file, uL("rb")); + ustrcat(fds.info.bios.file, uL("" BIOSFILE)); + bios = ufopen(fds.info.bios.file, uL("rb")); if (bios) { goto fds_load_bios_founded; } // 4) directory puNES/bios - usnprintf(bios_file, usizeof(bios_file), uL("" uPs("") BIOS_FOLDER "/" BIOSFILE), gui_data_folder()); - bios = ufopen(bios_file, uL("rb")); + umemset(fds.info.bios.file, 0x00, usizeof(fds.info.bios.file)); + usnprintf(fds.info.bios.file, usizeof(fds.info.bios.file), + uL("" uPs("") BIOS_FOLDER "/" BIOSFILE), gui_data_folder()); + bios = ufopen(fds.info.bios.file, uL("rb")); if (bios) { goto fds_load_bios_founded; } @@ -295,33 +344,50 @@ BYTE fds_load_bios(void) { fclose(bios); + fds.info.bios.crc32 = emu_crc32((void *)prgrom_pnt(), prgrom_size()); + return (EXIT_OK); } void fds_info(void) { - log_info_box_open(uL("sides;")); - if (fds.info.expcted_side != fds.info.total_sides) { - log_close_box(uL("expected %d, finded %d"), fds.info.expcted_side, fds.info.total_sides); + uTCHAR buffer[LENGTH_FILE_NAME_LONG]; + + log_info_open(uL("bios;")); + umemset(buffer, 0x00, usizeof(buffer)); + gui_utf_basename((uTCHAR *)fds.info.bios.file, buffer, usizeof(buffer) - 1); + log_close(uL("" uPs("") ", crc32 : 0x%08X"), buffer, fds.info.bios.crc32); + + log_info_box_open(uL("folder;")); + umemset(buffer, 0x00, usizeof(buffer)); + gui_utf_dirname((uTCHAR *)fds.info.bios.file, buffer, usizeof(buffer) - 1); + log_close_box(uL("" uPs("")), buffer); + + log_info_open(uL("sides;")); + if (fds.info.expcted_sides != fds.info.total_sides) { + log_close(uL("expected %d, finded %d"), fds.info.expcted_sides, fds.info.total_sides); } else { - log_close_box(uL("%d"), fds.info.total_sides); + log_close(uL("%d"), fds.info.total_sides); } for (int side = 0; side < fds.info.total_sides; side++) { _fds_sinfo sinfo = { 0 }; fds_image_sinfo(side, &sinfo); - log_info_box(uL("FDS side %d;prg crc 0x%08X, vrt size %d, files %3d, counted %3d"), side, + log_info_box(uL("FDS side %d;prg crc32 0x%08X, vrt size %d, files %3d, counted %3d"), side, sinfo.crc32prg, fds.info.sides[side].size, sinfo.block2.files, sinfo.counted_files); // controllo se disabilitare l'autoinsert fds_control_autoinsert(&sinfo); +#if !defined (RELEASE) + fds_info_side(side); +#endif } } void fds_info_side(BYTE side) { _fds_sinfo sinfo = { 0 }; fds_image_sinfo(side, &sinfo); - log_info(uL("FDS side %d;disk %d, side %X, name %3s, version %d, vsize %d, prg crc 0x%08X"), side, + log_info(uL("FDS side %d;disk %d, side %X, name %3s, version %d, vsize %d, prg crc32 0x%08X"), side, sinfo.block1.dnumber, sinfo.block1.snumber + 0x0A, sinfo.block1.name, @@ -334,7 +400,7 @@ void fds_info_side(BYTE side) { sinfo.block2.files, sinfo.counted_files); for (uint32_t i = 0; i < sinfo.counted_files; i++) { - log_info_box(uL("file %d;name %8s, size %5d, crc 0x%08X, 0x%04X (b3 : %5d) (b4 : %5d)"), i, + log_info_box(uL("file %d;name %8s, size %5d, crc32 0x%08X, 0x%04X (b3 : %5d) (b4 : %5d)"), i, sinfo.file[i].block3.name, sinfo.file[i].block3.length, sinfo.file[i].block4.crc32, @@ -352,6 +418,8 @@ void fds_disk_op(WORD type, BYTE side_to_insert, BYTE quiet) { switch (type) { case FDS_DISK_EJECT: fds.drive.disk_ejected = TRUE; + fds.drive.scan = FALSE; + fds.drive.motor_started = FALSE; fds.auto_insert.r4032.frames = 0; fds.auto_insert.r4032.checks = 0; if (!quiet) { @@ -365,11 +433,13 @@ void fds_disk_op(WORD type, BYTE side_to_insert, BYTE quiet) { } return; } - fds.info.bios_first_run = FALSE; + fds.info.bios.first_run = FALSE; fds.drive.disk_position = 0; - fds.drive.gap_ended = FALSE; + fds.drive.mark_finded = FALSE; fds.drive.disk_ejected = FALSE; - fds.drive.at_least_one_scan = FALSE; + fds.drive.scan = FALSE; + fds.drive.motor_started = FALSE; + fds.drive.delay_insert = 32768; fds.auto_insert.r4032.frames = 0; fds.auto_insert.r4032.checks = 0; if (!quiet) { @@ -415,90 +485,14 @@ void fds_disk_op(WORD type, BYTE side_to_insert, BYTE quiet) { break; } } -void fds_diff_op(BYTE side, BYTE mode, uint32_t position, WORD value) { - if (!fds.info.diff) { - uTCHAR file[LENGTH_FILE_NAME_LONG]; - uint32_t version = 0; - - fds_diff_file_name(&file[0], usizeof(file)); - fds.info.diff = ufopen(file, uL("r+b")); - if ((mode == FDS_OP_WRITE) && !fds.info.diff) { - // creo il file - fds.info.diff = ufopen(file, uL("a+b")); - if (fds.info.diff) { - // lo chiudo - fclose(fds.info.diff); - // lo riapro in modalita' rb+ - fds.info.diff = ufopen(file, uL("r+b")); - } - } - if (fds.info.diff) { - // leggo la versione del file - if ((fread(&version, sizeof(uint32_t), 1, fds.info.diff) < 1) || (version < DIFFVERSION)) { - fclose(fds.info.diff); - fds.info.diff = ufopen(file, uL("w+b")); - } - } - } - - if (!fds.info.diff) { - return; - } - - rewind(fds.info.diff); - fds.info.writings_occurred = TRUE; - - if (mode == FDS_OP_WRITE) { - _fds_diff_ele in = { 0 }, out = { 0 }; - uint32_t version = DIFFVERSION; - - // salvo la versione - if (fwrite(&version, sizeof(uint32_t), 1, fds.info.diff) < 1) { - fprintf(stderr, "error on write version fds diff file\n"); - } - // senza questo in windows non funziona correttamente - fflush(fds.info.diff); - - out.side = side; - out.position = position; - out.value = value; - - while (fread(&in, sizeof(_fds_diff_ele), 1, fds.info.diff)) { - if ((in.position == out.position) && (in.side == out.side)) { - fseek(fds.info.diff, ftell(fds.info.diff) - (long)sizeof(_fds_diff_ele), SEEK_SET); - break; - } - } - - if (fwrite(&out, sizeof(_fds_diff_ele), 1, fds.info.diff) < 1) { - log_error(uL("FDS;error on writing diff file")); - } - // senza questo in windows non funziona correttamente - fflush(fds.info.diff); - } else if (mode == FDS_OP_READ) { - WORD *dst = &fds.info.image[side * fds_image_side_size()]; - _fds_diff_ele ele = { 0 }; - uint32_t version = 0; - - // leggo la versione del file - if (fread(&version, sizeof(uint32_t), 1, fds.info.diff) < 1) { - log_error(uL("FDS;error on reading version diff file")); - } - - while (fread(&ele, sizeof(_fds_diff_ele), 1, fds.info.diff)) { - if (ele.side == side) { - dst[ele.position] = ele.value; - } - } - - fclose(fds.info.diff); - fds.info.diff = NULL; - } -} -BYTE fds_from_image(uTCHAR *file, BYTE format, BYTE type) { - uint32_t flength = 0; +BYTE fds_from_image_to_file(uTCHAR *file, BYTE format, BYTE type) { + size_t size = 0; + BYTE *mfds = fds_from_image_to_mem(format, type, &size); FILE *fp = NULL; + if (!mfds) { + return (EXIT_ERROR); + } // creo il file fp = ufopen(file, uL("a+b")); if (fp) { @@ -507,92 +501,19 @@ BYTE fds_from_image(uTCHAR *file, BYTE format, BYTE type) { // lo riapro in modalita' rb+ fp = ufopen(file, uL("w+b")); } else { + free(mfds); return (EXIT_ERROR); } - - if (type == FDS_FORMAT_FDS) { - fwrite("FDS", 3, 1, fp); - fputc(0x1A, fp); - fputc(fds.info.total_sides, fp); - for (int i = 0; i < 11; i++) { - fputc(0, fp); - } - } - - for (BYTE side = 0; side < fds.info.total_sides; side++) { - _fds_info_side *is = &fds.info.sides[side]; - const WORD *src = is->data; - uint32_t position = 0, size = 0; - WORD crc = 0; - - for (position = 0; position < fds_disk_side_size_format(format);) { - WORD block = src[position]; - uint32_t blength = 0; - BYTE stop = FALSE; - - switch (block) { - case BL_DISK_INFO: - // le info sul disco - blength = 56; - crc = fds_block_crc(&src[position], blength); - break; - case BL_FILE_AMOUNT: - // il numero dei file immagazzinati nel disco - blength = 2; - crc = fds_block_crc(&src[position], blength); - break; - case BL_FILE_HEADER: - // l'header del file - flength = (src[position + 14] << 8) | src[position + 13]; - blength = 16; - crc = fds_block_crc(&src[position], blength); - break; - case BL_FILE_DATA: - // il contenuto del file - blength = flength + 1; - crc = fds_block_crc(&src[position], blength); - break; - case FDS_DISK_GAP: - case FDS_DISK_BLOCK_MARK: - case FDS_DISK_CRC_CHAR1: - case FDS_DISK_CRC_CHAR2: - position++; - continue; - default: - stop = TRUE; - break; - } - - if (stop) { - break; - } - - if (block) { - for (unsigned int i = 0; i < blength; i++) { - fputc(src[position] & 0xFF, fp); - position++; - size++; - } - if (format == QD_FORMAT) { - fputc(((crc & 0x00FF) >> 0), fp); - fputc(((crc & 0xFF00) >> 8), fp); - size += 2; - } - } - } - while (size < fds_disk_side_size_format(format)) { - fputc(0x00, fp); - size++; - } - } + if (fwrite(mfds, size, 1, fp) < 1) { + fclose(fp); + free(mfds); + return (EXIT_ERROR); + }; fclose(fp); + free(mfds); { uTCHAR diff[LENGTH_FILE_NAME_LONG]; - if (fds.info.diff) { - fclose(fds.info.diff); - fds.info.diff = NULL; - } fds_diff_file_name(&diff[0], usizeof(diff)); uremove(diff); } @@ -611,48 +532,90 @@ BYTE fds_image_to_file(uTCHAR *file) { } else { return (EXIT_ERROR); } - fwrite(fds.info.image, fds.info.total_sides * fds_image_side_bytes(), 1, fp); + fwrite(fds.info.image, fds.info.total_sides * fds_image_side_size(), 1, fp); fclose(fp); return (EXIT_OK); } +WORD fds_crc_byte(WORD base, BYTE data) { + for (unsigned i = 0; i < 8; i++) { + BYTE bit = (data >> i) & 0x01; + BYTE carry = base & 0x01; + + base = (base >> 1) | (bit << 15); + if (carry) { + base ^= 0x8408; + } + } + return (base); +} uint32_t fds_disk_side_size_format(BYTE format) { return (format == QD_FORMAT ? DISK_QD_SIDE_SIZE : DISK_FDS_SIDE_SIZE); } uint32_t fds_disk_side_size(void) { - return (fds_disk_side_size_format(info.format)); + return (fds_disk_side_size_format(fds.info.format)); } uint32_t fds_image_side_size(void) { - return (fds_disk_side_size() + FDS_GAP_END); -} -uint32_t fds_image_side_bytes(void) { - return (fds_image_side_size() * sizeof(WORD)); + return (FDS_IMAGE_SIDE_SIZE); } -void fds_to_image(void) { - uint32_t flength = 0; +BYTE fds_to_image(void) { + BYTE *mfds = NULL, *pointer = fds.info.data; if (fds.info.image) { free(fds.info.image); fds.info.image = NULL; } + // applico la diff + { + uTCHAR file[LENGTH_FILE_NAME_LONG]; + + fds_diff_file_name(&file[0], usizeof(file)); + if (emu_file_exist(file) == EXIT_OK) { + mfds = malloc(fds.info.total_size); + if (mfds) { + memcpy(mfds, fds.info.data, fds.info.total_size); + mfds = fds_read_ips(mfds, fds.info.total_size, file); + } + if (!mfds) { + log_error(uL("FDS;error on reading diff file")); + } else { + pointer = mfds; + fds.info.writings_occurred = TRUE; + } + } + } + for (BYTE side = 0; side < fds.info.total_sides; side++) { uint32_t position = 0, size = 0; + _fds_info_block fib = { 0 }; _fds_info_side *is = NULL; const BYTE *src = NULL; - WORD *dst = NULL; - BYTE bl1 = FALSE; + BYTE *dst = NULL; + uint32_t header = 0; if (fds.info.type == FDS_FORMAT_FDS) { - position = (side * fds_disk_side_size()) + 16; - } else { - position = side * fds_disk_side_size(); + header = 16; } - fds.info.image = realloc((void * )fds.info.image, (side + 1) * fds_image_side_bytes()); - src = &fds.info.data[position]; + fds.info.protection.autodetect = TRUE; + fds.info.protection.magic_card_trainer = FALSE; + fds.info.protection.quick_hunter = FALSE; + fds.info.protection.ouji = FALSE; + fds.info.protection.kgk = FALSE; + position = side * fds_disk_side_size() + header; + + // "Bishoujo Mahjong Club (Japan) (Unl).fds" + // "Bodycon Quest I - Girls Exposed (Japan) (Unl) [T-En by DvD Translations Rev A] [n].fds" + // "Dead Zone (Japan) [T-En by Stardust Crusaders v1.00].fds" + // "Game no Tatsujin - Money Wars (Japan) (Unl).fds" + // "Golf, The - Bishoujo Classic (Japan) (Unl).fds" + // "Otocky (Japan) (Beta) (1986-04-15).fds" + // la dimensione dell'immeagine finale e' superiore a 65500 + fds.info.image = realloc((void *)fds.info.image, (side + 1) * fds_image_side_size()); + src = &pointer[position]; dst = &fds.info.image[side * fds_image_side_size()]; - memset(dst, 0x00, fds_image_side_bytes()); + memset(dst, FDS_DISK_GAP, fds_image_side_size()); is = &fds.info.sides[side]; position = 0; @@ -660,34 +623,15 @@ void fds_to_image(void) { size += FDS_GAP_START; for (position = 0; position < fds_disk_side_size();) { - BYTE block = src[position], stop = FALSE; - uint32_t blength = 1; - - switch (block) { - case BL_DISK_INFO: - // le info sul disco - blength = 56; - bl1 = TRUE; - break; - case BL_FILE_AMOUNT: - // il numero dei file immagazzinati nel disco - blength = 2; - break; - case BL_FILE_HEADER: - // l'header del file - flength = (src[position + 14] << 8) | src[position + 13]; - blength = 16; - break; - case BL_FILE_DATA: - // il contenuto del file - blength = flength + 1; - break; - default: - // nel caso il disco sia "sporco" - stop = TRUE; - break; + fds_examine_block(src, position, &fib); + fds.info.protection.magic_card_trainer = fib.magic_card_trainer; + fds.info.protection.quick_hunter = fib.quick_hunter; + fds.info.protection.ouji = fib.ouji; + fds.info.protection.kgk = fib.kgk; + + if (fds.info.protection.quick_hunter | fds.info.protection.kgk | fds.info.protection.ouji) { + fds.info.write_protected = 0x04; } - // in "Tobidase Daisakusen (1987)(Square)(J).fds" esiste un file nascosto // esattamente dopo l'ultimo file "riconosciuto" dal file system. // Il vecchio controllo che facevo per riconoscere i dischi "sporchi" @@ -698,73 +642,249 @@ void fds_to_image(void) { // blocchi riconosciuti allora considero l'analisi del disco completa e // tralascio tutto quello che sta dopo (in questo modo funziona anche // "Akumajou Dracula v1.02 (1986)(Konami)(J).fds" il cui disco e' "sporco"). - if (stop) { + if (fib.stop) { + // gestione trainer e protezioni (blocco aggiuntivo) + if (fds.info.protection.magic_card_trainer || fds.info.protection.kgk || fds.info.protection.quick_hunter) { + BYTE btype = fds.info.protection.magic_card_trainer + ? 0x05 + : fds.info.protection.kgk ? 0x12 : fds.info.protection.quick_hunter ? 0x00 : 0x00; + uint32_t cpos = 0, cblength = 0; + WORD crc = 0; + + fib.blength = fds.info.protection.magic_card_trainer + ? 0x200 + : fds.info.protection.kgk ? 0x500 : fds.info.protection.quick_hunter ? 0x3000 : 0; + // indico l'inizio del blocco + fds_image_memset(&dst[size], FDS_DISK_BLOCK_MARK, 1); + size += 1; + // inizilizzo le variabili per il calcolo del crc + cpos = size; + cblength = fib.blength; + // nell'fds originale manca il tipo di blocco (0x00) + if (fds.info.protection.quick_hunter) { + fds_image_memset(&dst[size], btype, 1); + size += 1; + cblength += 1; + } + // copio il blocco + fds_image_memcpy(&src[position], &dst[size], fib.blength); + size += fib.blength; + // crc + crc = fds_crc_block(&dst[cpos], cblength); + fds_image_memset(&dst[size], (crc >> 0), 1); + size += 1; + fds_image_memset(&dst[size], (crc >> 8), 1); + size += 1; + } break; } - if (block) { + if (src[position]) { + uint32_t cpos = 0, cblength = 0; + WORD crc = 0; + // indico l'inizio del blocco fds_image_memset(&dst[size], FDS_DISK_BLOCK_MARK, 1); size += 1; - // copio i dati - fds_image_memcpy(&src[position], &dst[size], blength); - size += blength; - // dummy CRC - fds_image_memset(&dst[size], FDS_DISK_CRC_CHAR1, 1); + // inizilizzo le variabili per il calcolo del crc + cpos = size; + cblength = fib.blength; + // copio il blocco + fds_image_memcpy(&src[position], &dst[size], fib.blength); + size += fib.blength; + // crc + crc = fds_crc_block(&dst[cpos], cblength); + fds_image_memset(&dst[size], (crc >> 0), 1); size += 1; - fds_image_memset(&dst[size], FDS_DISK_CRC_CHAR2, 1); + fds_image_memset(&dst[size], (crc >> 8), 1); size += 1; - // 1016 bit di gap alla fine di ogni blocco. - // Note : con 976 funziona correttamente la read del disco ma non e' - // sufficiente per la write. + // inizializzo il blocco di gap fds_image_memset(&dst[size], FDS_DISK_GAP, FDS_GAP_BLOCK); size += FDS_GAP_BLOCK; + + // in caso di write di un file questo altro blocco di gap serve per ottenere il + // il giusto posizionamento (nell'immagine) al blocco dei dati del file (0x04): + // - scrittura blocco header (0x03) + // - scrittura crc + // - il bios scrive altri 4 byte (0x00) dopo il crc + // - scrive 121 byte di gap + // - scrive il blocco dei dati (0x04) + // - scrittura crc + // - il bios scrive altri 4 byte (0x00) dopo il crc + // - scrive 121 byte di gap + if (src[position] == BL_FILE_HEADER) { + fds_image_memset(&dst[size], FDS_DISK_GAP, FDS_GAP_FILE_BLOCK); + size += FDS_GAP_FILE_BLOCK; + } } - position += (blength + (info.format == QD_FORMAT ? 2 : 0)); + position += (fib.blength + (fds.info.format == QD_FORMAT ? 2 : 0)); } - if (!bl1) { + if (!fib.bl1) { fds.info.total_sides = side; if (!fds.info.total_sides) { free(fds.info.image); fds.info.image = NULL; } else { - fds.info.image = realloc((void * )fds.info.image, fds.info.total_sides * fds_image_side_bytes()); + fds.info.image = realloc((void *)fds.info.image, fds.info.total_sides * fds_image_side_size()); } continue; } is->side = side; - is->last_position = size; - if (size < fds_disk_side_size()) { - fds_image_memset(&dst[size], 0x0000, fds_disk_side_size() - size); - size += (fds_disk_side_size() - size); + if (size < fds_image_side_size()) { + fds_image_memset(&dst[size], FDS_DISK_GAP, fds_image_side_size() - size); + size += (fds_image_side_size() - size); } - fds_image_memset(&dst[size], FDS_DISK_GAP, FDS_GAP_END); - size += FDS_GAP_END; - is->size = size; - fds_diff_op(side, FDS_OP_READ, 0, 0); } // ultimo passaggio for (BYTE side = 0; side < fds.info.total_sides; side++) { fds.info.sides[side].data = &fds.info.image[side * fds_image_side_size()]; } + if (mfds) { + free(mfds); + } + return (EXIT_OK); +} +BYTE fds_examine_block(const BYTE *src, uint32_t position, _fds_info_block *fib) { + switch (src[position]) { + case BL_DISK_INFO: + // informazioni sul disco + // "Jingorou (Japan) (Unl).fds" e "Graphic Editor Hokusai - Ver 1.2 (Japan) (Unl).fds" + fib->ouji = fds.info.protection.autodetect && !strncmp((void *)&src[position + 0x10], "OUJI", 4); + fib->kgk = fds.info.protection.autodetect && !strncmp((void *)&src[position + 0x10], "KGK ", 4); + fib->magic_card_trainer = FALSE; + fib->count2000 = 0; + fib->blength = 56; + fib->bl1 = TRUE; + return (TRUE); + case BL_FILE_AMOUNT: + // il numero dei file nel disco + fib->files = src[position + 1]; + fib->blength = 2; + return (TRUE); + case BL_FILE_HEADER: + // header del file + fib->faddress = (src[position + 0x0C] << 8) | src[position + 0x0B]; + fib->flength = (src[position + 0x0E] << 8) | src[position + 0x0D]; + fib->ftype = src[position + 0x0F] & 0x03; + if (fib->flength_new && fib->files) { + fib->flength = fib->ftype != 0 ? (fib->flength_ppu ? fib->flength_ppu : fib->flength) : fib->flength_cpu; + } else if (fib->ouji && (fib->ftype == 0) && (fib->faddress == 0x2000) && (fib->flength == 1) && + (++fib->count2000 >= 3)) { + fib->flength = fib->files ? 0xC000 : 0xE000; + } + if (fib->files) { + fib->files--; + } + // "Quick Hunter (Japan) (Unl).fds" + if (!fib->files && (fib->faddress == 0x2000) && (fib->flength == 0x900) && (fib->ftype == 0) && + !strncmp((void *)&src[position + 0x03], "KIYONO.", 7)) { + fib->quick_hunter = TRUE; + } + fib->blength = 16; + return (TRUE); + case BL_FILE_DATA: + // il contenuto del file + if (fds.info.protection.autodetect && (fib->ftype == 0) && (fib->faddress == 0x4FFF) && (fib->flength == 8)) { + fib->flength_new = src[position + 0x01] & 0x80; + fib->magic_card_trainer = (src[position + 0x01] != 0xFE) && ((src[position + 0x01] & 0xC0) == 0xC0) && + (src[position + 0x08] == 0x00); + fib->flength_cpu = 0x8000; + switch (src[position + 0x02] >> 5) { + case 4: + case 5: + fib->flength_ppu = 32768; + break; + case 6: + fib->flength_ppu = 16384; + break; + case 7: + fib->flength_ppu = 8192; + break; + default: + fib->flength_ppu = 0; + break; + } + } + fib->blength = fib->flength + 1; + return (TRUE); + default: + fib->blength = 0; + fib->stop = TRUE; + return (FALSE); + } } void fds_control_autoinsert(_fds_sinfo *sinfo) { - // NB : il crc32prg da controllare deve essere sempre quello - // del disco dove non vengono scritti i dati di salvataggio - if (!strncmp((char *)&sinfo->block1.name[0], "GAL", 3) && (sinfo->crc32prg == 0xC5D1EC5D)) { - // Gall Force - Eternal Story (Japan) - disk 0 side A + _fds_control_autoinsert images[] = { + // --- auto insert disabilitato + // Gall Force - Eternal Story (Japan) + { "GAL", { 0x1E9969AC, 0xED380FA3, 0x00, 0x00 }, TRUE, TRUE, TRUE }, + // Koneko Monogatari - The Adventures of Chatran (Japan) + { "KOM", { 0xE9A457BF, 0x7791A2DD, 0x00, 0x00 }, TRUE, TRUE, TRUE }, + //Koneko Monogatari - The Adventures of Chatran (Japan) (Sample) (1986-07-03) + { "KOM", { 0x8C457E04, 0xCB25E05C, 0x00, 0x00 }, TRUE, TRUE, TRUE }, + // Samurai Sword (Japan) + { "SMU", { 0xD0B342F5, 0xE46A6E3E, 0x00, 0x00 }, TRUE, TRUE, TRUE }, + // Quick Hunter (Japan) (Unl) + { "OUJ", { 0x2AD12F7F, 0xBFA73716, 0x00, 0x00 }, TRUE, TRUE, TRUE }, + + // --- auto insert r4032 disabilitato e auto insert end_of_head abilitato + // Egger Land (Japan) + { "EGL", { 0x8EE0B051, 0x71119427, 0x00, 0x00 }, FALSE, TRUE, FALSE }, + // Eggerland (1987)(Hal Laboratory)(J)[tr En] + { "EGL", { 0xCE21494E, 0x5FD1F4EB, 0x00, 0x00 }, FALSE, TRUE, FALSE }, + // Egger Land (Japan) (Rev 1) (Possible Proto) + { "EGL", { 0xBB887262, 0xC9915867, 0x00, 0x00 }, FALSE, TRUE, FALSE }, + // Igo - Kyuu Roban Taikyoku (Japan) + { "IGO", { 0x1E8B0151, 0xF7130E20, 0x00, 0x00 }, FALSE, TRUE, FALSE }, + // Puzzle Boys (Japan) (Disk Writer) + { "PUZ", { 0x2385D83D, 0x64488AFD, 0x00, 0x00 }, FALSE, TRUE, FALSE }, + // Time Twist - Rekishi no Katasumi de (1991)(Nintendo)(J) + { "TT1", { 0x6D6014C1, 0x145A90B2, 0xBFB019B9, 0x636083C1 }, FALSE, TRUE, FALSE }, + }; + + fds.auto_insert.r4032.disabled = FALSE; + fds.auto_insert.end_of_head.disabled = TRUE; + + if (fds.info.total_sides == 1) { + // auto insert disabilitato + fds.auto_insert.disabled = TRUE; + gui_overlay_info_append_msg_precompiled(40, NULL); + return; + } + if (fds.info.protection.quick_hunter) { + // auto insert disabilitato fds.auto_insert.disabled = TRUE; gui_overlay_info_append_msg_precompiled(39, NULL); - } else if (!strncmp((char *)&sinfo->block1.name[0], "KOM", 3) && - ((sinfo->crc32prg == 0x2B24787F) || (sinfo->crc32prg == 0x14F219C5))) { - // Koneko Monogatari - The Adventures of Chatran (Japan) - disk 0 side A oppure disk 0 side B + return; + } + if (ustrstr(info.rom.file, uL(" Zenpen ")) || ustrstr(info.rom.file, uL(" Kouhen "))) { + // auto insert disabilitato fds.auto_insert.disabled = TRUE; gui_overlay_info_append_msg_precompiled(39, NULL); + return; + } + for (unsigned int i = 0; i < LENGTH(images); i++) { + _fds_control_autoinsert *fca = &images[i]; + + if (!strncmp((char *)&sinfo->block1.name[0], &fca->name[0], 3) && + ((fca->crc32prg[0] && (fca->crc32prg[0] == sinfo->crc32prg)) || + (fca->crc32prg[1] && (fca->crc32prg[1] == sinfo->crc32prg)) || + (fca->crc32prg[2] && (fca->crc32prg[2] == sinfo->crc32prg)) || + (fca->crc32prg[3] && (fca->crc32prg[3] == sinfo->crc32prg)))) { + if (fca->disable_autoinsert) { + fds.auto_insert.disabled = TRUE; + gui_overlay_info_append_msg_precompiled(39, NULL); + continue; + } + fds.auto_insert.r4032.disabled = fca->disable_r4032; + fds.auto_insert.end_of_head.disabled = fca->disable_end_of_head; + break; + } } } void fds_diff_file_name(uTCHAR *dst, size_t lenght) { @@ -773,7 +893,7 @@ void fds_diff_file_name(uTCHAR *dst, size_t lenght) { umemset(dst, 0x00, lenght); gui_utf_basename(info.rom.file, basename, usizeof(basename)); usnprintf(dst, lenght, uL("" uPs("") DIFF_FOLDER "/" uPs("")), gui_data_folder(), basename); - usnprintf(ext, usizeof(ext), uL("" uPs("")), (info.format == QD_FORMAT ? uL(".diq") : uL(".dif"))); + usnprintf(ext, usizeof(ext), uL("" uPs("")), (fds.info.format == QD_FORMAT ? uL(".ipq") : uL(".ipf"))); // rintraccio l'ultimo '.' nel nome last_dot = ustrrchr(dst, uL('.')); @@ -787,12 +907,12 @@ void fds_diff_file_name(uTCHAR *dst, size_t lenght) { void fds_image_sinfo(BYTE side, _fds_sinfo *sinfo) { _fds_info_side *is = &fds.info.sides[side]; uint32_t size = 0, position = 0, flength = 0; - const WORD *src = is->data; + const BYTE *src = is->data; memset(sinfo, 0x00, sizeof(_fds_sinfo)); for (position = 0; position < fds_disk_side_size();) { - WORD block = src[position]; + BYTE block = src[position]; uint32_t blength = 1; BYTE stop = FALSE; @@ -807,7 +927,7 @@ void fds_image_sinfo(BYTE side, _fds_sinfo *sinfo) { break; case BL_FILE_HEADER: // l'header del file - flength = (src[position + 14] << 8) | src[position + 13]; + flength = (src[position + 0x0E] << 8) | src[position + 0x0D]; blength = 16; break; case BL_FILE_DATA: @@ -816,15 +936,10 @@ void fds_image_sinfo(BYTE side, _fds_sinfo *sinfo) { break; case FDS_DISK_GAP: case FDS_DISK_BLOCK_MARK: - case FDS_DISK_CRC_CHAR1: - case FDS_DISK_CRC_CHAR2: size++; position++; continue; default: - if (is->last_position < position) { - is->last_position = position; - } stop = TRUE; break; } @@ -840,8 +955,7 @@ void fds_image_sinfo(BYTE side, _fds_sinfo *sinfo) { case 1: sinfo->block1.position = size; // name - fds_image_memcpy_ASCII(&src[position + 0x10], &sinfo->block1.name[0], - sizeof(sinfo->block1.name)); + fds_image_memcpy_ASCII(&src[position + 0x10], &sinfo->block1.name[0], sizeof(sinfo->block1.name)); // game version sinfo->block1.gversion = src[position + 0x14]; // side number @@ -862,12 +976,12 @@ void fds_image_sinfo(BYTE side, _fds_sinfo *sinfo) { sizeof(sinfo->file[file].block3.name)); // type sinfo->file[file].block3.type = src[position + 0x0F]; - sinfo->crc32prg = emu_crc32_continue((void *)&src[position], blength * sizeof(WORD), sinfo->crc32prg); + sinfo->crc32prg = emu_crc32_continue((void *)&src[position], blength, sinfo->crc32prg); break; case 4: sinfo->file[file].block4.position = size; - sinfo->file[file].block4.crc32 = emu_crc32((void *)&src[position + 1], flength * sizeof(WORD)); - sinfo->crc32prg = emu_crc32_continue((void *)&src[position], blength * sizeof(WORD), sinfo->crc32prg); + sinfo->file[file].block4.crc32 = emu_crc32((void *)&src[position + 1], flength); + sinfo->crc32prg = emu_crc32_continue((void *)&src[position], blength, sinfo->crc32prg); sinfo->counted_files++; break; default: @@ -875,20 +989,23 @@ void fds_image_sinfo(BYTE side, _fds_sinfo *sinfo) { } size += blength; position += blength; + // crc + size += 2; + position += 2; } } } -void fds_image_memset(WORD *dst, WORD value, uint32_t lenght) { +void fds_image_memset(BYTE *dst, BYTE value, uint32_t lenght) { for (uint32_t i = 0; i < lenght; i++) { (*dst++) = value; } } -void fds_image_memcpy(const BYTE *src, WORD *dst, uint32_t lenght) { +void fds_image_memcpy(const BYTE *src, BYTE *dst, uint32_t lenght) { for (uint32_t i = 0; i < lenght; i++) { (*dst++) = (*src++); } } -void fds_image_memcpy_ASCII(const WORD *src, BYTE *dst, size_t lenght) { +void fds_image_memcpy_ASCII(const BYTE *src, BYTE *dst, size_t lenght) { memset(dst, 0x00, lenght); for (size_t i = 0; i < (lenght - 1); i++) { BYTE ch = src[i] & 0xFF; @@ -896,25 +1013,304 @@ void fds_image_memcpy_ASCII(const WORD *src, BYTE *dst, size_t lenght) { dst[i] = ((ch >= 0x20) && (ch <= 0x7E)) ? ch : 0x20; } } -WORD fds_block_crc(const WORD *src, uint32_t lenght) { +WORD fds_crc_block(const BYTE *src, uint32_t lenght) { // Do not include any existing checksum, not even the blank checksums 00 00 or FF FF. // The formula will automatically count 2 0x00 bytes without the programmer adding them manually. // Also, do not include the gap terminator (0x80) in the data. // If you wish to do so, change sum to 0x0000. - WORD sum = 0x8000; + WORD crc = 0x8000; + + for (uint32_t i = 0; i < (lenght + 2); i++) { + crc = fds_crc_byte(crc, (i < lenght ? src[i]: 0x00)); + } + return (crc); +} +BYTE *fds_from_image_to_mem(BYTE format, BYTE type, size_t *size) { + BYTE *mfds = NULL; + + (*size) = (type == FDS_FORMAT_FDS ? 16 : 0) + fds_disk_side_size_format(format) * fds.info.total_sides; + + // alloco la zona di memoria + mfds = malloc((*size)); + if (!mfds) { + (*size) = 0; + return (mfds); + } + memset(mfds, 0x00, (*size)); + + if (type == FDS_FORMAT_FDS) { + memcpy(&mfds[0], "FDS", 3); + mfds[3] = 0x1A; + mfds[4] = fds.info.total_sides; + } + + for (BYTE side = 0; side < fds.info.total_sides; side++) { + uint32_t length = 0, position = 0, total_size = 0; + _fds_info_side *is = &fds.info.sides[side]; + const BYTE *src = is->data; + _fds_info_block fib = { 0 }; + WORD crc = 0; + + length = (type == FDS_FORMAT_FDS ? 16 : 0) + (fds_disk_side_size_format(format) * side); + + for (position = 0; position < fds_image_side_size();) { + if ((src[position] == FDS_DISK_GAP) || (src[position] == FDS_DISK_BLOCK_MARK)) { + position++; + continue; + } + if (fds_examine_block(src, position, &fib)) { + crc = fds_crc_block(&src[position], fib.blength); + } else { + break; + } + + if (src[position]) { + if ((total_size + fib.blength + (format == QD_FORMAT ? 2 : 0)) < fds_disk_side_size_format(format)) { + // troncato + for (unsigned int i = 0; i < fib.blength; i++) { + mfds[length] = src[position]; + length++; + position++; + total_size++; + } + if (format == QD_FORMAT) { + mfds[length] = (crc >> 0); + mfds[length + 1] = (crc >> 8); + length += 2; + total_size += 2; + } + position += 2; + } else { + break; + } + } + } + } + return (mfds); +} +BYTE *fds_read_ips(BYTE *data, size_t size, const char *file) { + size_t fsize = 0, fpos = 0, mpos = 0; + FILE *fp = fopen(file, "rb"); + uint32_t counter = 0; + BYTE rc = EXIT_OK; + char header[5]; + + if (!fp || !data) { + return (NULL); + } + + fseek(fp, 0L, SEEK_END); + fsize = ftell(fp); + fseek(fp, 0L, SEEK_SET); + + if (fsize >= 0x1000000) { + fclose(fp); + free(data); + return (NULL); + } + if ((fread(&header[0], 1, sizeof(header), fp) < sizeof(header)) || strncmp((void *)header, "PATCH", 5)) { + fclose(fp); + free(data); + return (NULL); + } + fpos += sizeof(header); + + // Itera attraverso le patch + while ((fpos < fsize) && (mpos < size)) { + size_t offset = 0, length = 0; + BYTE address[3], len[2]; + BYTE rle = FALSE, ch = 0; + + // Leggo l'offset dalla patch corrente + if (fread(&address[0], 1, sizeof(address), fp) < sizeof(address)) { + rc = EXIT_ERROR; + break; + } + fpos += sizeof(address); + offset = (address[0] << 16) | (address[1] << 8) | address[2]; + + // EOF + if (offset == 0x454F46) { + break; + } - for (uint32_t byte_index = 0; byte_index < (lenght + 2); byte_index++) { - BYTE byte = byte_index < lenght ? src[byte_index] & 0x00FF: 0x00; + // Leggo la lunghezza dalla patch corrente + if (fread(&len[0], 1, sizeof(len), fp) < sizeof(len)) { + rc = EXIT_ERROR; + break; + } + fpos += sizeof(len); + length = (len[0] << 8) | len[1]; - for(unsigned bit_index = 0; bit_index < 8; bit_index++) { - BYTE bit = (byte >> bit_index) & 0x01; - BYTE carry = sum & 0x01; + // Se la lunghezza e' 0, e' un blocco RLE + if (!length) { + rle = TRUE; + + // Leggo la lunghezza RLE a 16 bit + if (fread(&len[0], 1, sizeof(len), fp) < sizeof(len)) { + rc = EXIT_ERROR; + break; + } + fpos += sizeof(len); + length = (len[0] << 8) | len[1]; - sum = (sum >> 1) | (bit << 15); - if (carry) { - sum ^= 0x8408; + // Leggo il byte da ripetere + if (fread(&ch, 1, sizeof(ch), fp) < sizeof(ch)) { + rc = EXIT_ERROR; + break; } + fpos += sizeof(ch); } + + if ((offset + length) >= size) { + size_t old_size = size; + BYTE *new_blk = NULL; + + size = (offset + length); + new_blk = (BYTE *)realloc(data, size); + if (!new_blk) { + rc = EXIT_ERROR; + break; + } + memset(new_blk + old_size, 0x00, size - old_size); + data = new_blk; + } + + if (rle) { + // Applico la ripetizione dei dati in memoria + for (size_t i = 0; i < length; ++i) { + data[offset + i] = ch; + } + } else { + // Leggo i dati da applicare dalla patch + if (fread(&data[offset], 1, length, fp) < length) { + rc = EXIT_ERROR; + break; + } + fpos += length; + } + counter++; + mpos = offset + length; + } + fclose(fp); + if ((rc == EXIT_ERROR) || !counter) { + free(data); + data = NULL; + } + return (data); +} +BYTE fds_create_ips(const BYTE *d1, const size_t size1, const BYTE *d2, const size_t size2, const char *file) { + FILE *fp = fopen(file, "w+b"); + uint32_t counter = 0; + size_t i = 0, fsize = 0; + + if (!d1 || !d2 || !fp) { + return (EXIT_ERROR); } - return (sum); + fwrite("PATCH", 5, 1, fp); + fsize += 5; + + while ((i < size1) || (i < size2)) { + size_t length = 0; + BYTE rle = TRUE; + + if (i < size1) { + if (d1[i] == d2[i]) { + i++; + continue; + } + while (((i + length) < size1) && ((i + length) < size2) && (length < 65535)) { + if (d1[i + length] == d2[i + length]) { + break; + } + if (!rle && (length >= 3) && ((length + 3) < size1)) { + if ((d1[i + length + 0] != d2[i + length + 0]) && + (d1[i + length + 1] != d2[i + length + 1]) && + (d1[i + length + 2] != d2[i + length + 2]) && + (d2[i + length + 0] == d2[i + length + 1]) && + (d2[i + length + 1] == d2[i + length + 2])) { + break; + } + } + if (rle && (length >= 1) && (d2[i + length] != d2[i])) { + if (length < 3) { + rle = FALSE; + continue; + } + break; + } + length++; + } + } + + if (!length) { + while (((i + length) < size2) && (length < 65535)) { + if (!rle && (length >= 3) && ((length + 3) < size2)) { + if ((d2[i + length + 0] == d2[i + length + 1]) && + (d2[i + length + 1] == d2[i + length + 2])) { + break; + } + } + if (rle && (length >= 1) && (d2[i + length] != d2[i])) { + if (length < 3) { + rle = FALSE; + continue; + } + break; + } + length++; + } + } + + if (length == 1) { + rle = FALSE; + } + + { + // Big Endian + BYTE address[3] = { i >> 16, i >> 8, i & 0xFF }; + BYTE len[2] = { length >> 8, length & 0xFF }; + BYTE rlen[2] = { 0x00, 0x00 }; + + if (rle) { + rlen[0] = len[0]; + rlen[1] = len[1]; + len[0] = 0x00; + len[1] = 0x00; + } + // Scrivo le istruzioni di patching nel file IPS + fputc(address[0], fp); + fputc(address[1], fp); + fputc(address[2], fp); + fputc(len[0], fp); + fputc(len[1], fp); + fsize += 5; + if (rle) { + fputc(rlen[0], fp); + fputc(rlen[1], fp); + fputc(d2[i], fp); + fsize += 3; + } else { + fwrite(&d2[i], 1, length, fp); + fsize += length; + } + + // forzo la scrittura del file + fflush(fp); + + if (fsize >= 0x1000000) { + log_error(uL("ips;error on writing file, too large (max 16MB)")); + break; + } + } + + counter++; + i += length; + } + fclose(fp); + + if (!counter) { + remove(file); + } + return (EXIT_OK); } diff --git a/src/core/fds.h b/src/core/fds.h index 3e6f46856..4e05dcd38 100644 --- a/src/core/fds.h +++ b/src/core/fds.h @@ -19,7 +19,6 @@ #ifndef FDS_H_ #define FDS_H_ -#include #include "common.h" enum fds_formats { FDS_FORMAT_RAW, FDS_FORMAT_FDS }; @@ -28,65 +27,76 @@ enum fds_operations { FDS_OP_NONE, FDS_OP_READ, FDS_OP_WRITE }; enum fds_disk_operations { FDS_DISK_INSERT, FDS_DISK_EJECT, - // e' importante che tutte le modalita' - // SELECT siano dopo la FDS_DISK_SELECT. + // e' importante che tutte le modalita' SELECT siano dopo la FDS_DISK_SELECT. FDS_DISK_SELECT, FDS_DISK_SELECT_AND_INSERT, FDS_DISK_SELECT_FROM_REWIND }; enum fds_gaps { FDS_GAP_START = 28300 / 8, - // 1016 bit di gap alla fine di ogni blocco. - // Note : con 976 funziona correttamente la read del disco ma non e' - // sufficiente per la write. - //FDS_GAP_BLOCK = 1016 / 8, //976 / 8, FDS_GAP_BLOCK = 976 / 8, - FDS_GAP_END = 0 + FDS_GAP_FILE_BLOCK = 32 / 8 }; enum fds_block_type { BL_DISK_INFO = 1, BL_FILE_AMOUNT, BL_FILE_HEADER, BL_FILE_DATA, - DISK_FDS_SIDE_SIZE = 65500, - DISK_QD_SIDE_SIZE = 65536 }; enum fds_misc { - //FDS_8BIT_DELAY = 149, //20 * 8, - FDS_8BIT_DELAY = 22 * 8, - FDS_DISK_GAP = 0x0100, - FDS_DISK_BLOCK_MARK = 0x0180, - FDS_DISK_CRC_CHAR1 = 0x0155, - FDS_DISK_CRC_CHAR2 = 0x01AA, - FDS_OP_SIDE_DELAY = 2800000, - FDS_AUTOINSERT_OP_SIDE_DELAY = 100, - FDS_AUTOINSERT_R4032_MAX_CHECKS = 150 + FDS_DISK_GAP = 0x00, + FDS_DISK_BLOCK_MARK = 0x80, + // Aspic (1988)(Bothtec)(J) necessita di almeno 1500 ms + // Pulsar no Hikari - Space Wars Simulation (Japan) di almeno 1600 ms + FDS_OP_SIDE_MS_DELAY = 1600, + FDS_AUTOINSERT_R4032_MAX_CHECKS = 7, + FDS_MIN_LAG_FRAMES = 20, + FDS_IMAGE_SIDE_SIZE = 75500, + DISK_FDS_SIDE_SIZE = 65500, + DISK_QD_SIDE_SIZE = 65536 }; -#define fds_auto_insert_enabled() (cfg->fds_switch_side_automatically & !fds.auto_insert.disabled & !fds.info.bios_first_run) +// 147 cicli (secondo vari test è questo il numero preciso) +#define FDS_8BIT_MS_DELAY 0.0825f +#define fds_auto_insert_enabled() (cfg->fds_switch_side_automatically & !fds.auto_insert.disabled & !fds.info.bios.first_run) #define fds_reset_envelope_counter(env) (fds.snd.envelope.speed << 3) * (fds.snd.env.speed + 1) #define fds_sweep_bias(val) (SBYTE)((val & 0x7F) << 1) / 2; typedef struct _fds_info_side { BYTE side; - WORD *data; + BYTE *data; uint32_t size; - uint32_t last_position; } _fds_info_side; +typedef struct _fds_info_bios { + uTCHAR file[LENGTH_FILE_NAME_LONG]; + uint32_t crc32; + BYTE first_run; +} _fds_info_bios; +typedef struct _fds_info_protection { + BYTE autodetect; + BYTE magic_card_trainer; + BYTE quick_hunter; + BYTE ouji; + BYTE kgk; +} _fds_info_protection; typedef struct _fds { struct _fds_info { BYTE enabled; BYTE *data; - WORD *image; - FILE *diff; + BYTE *image; + BYTE write_protected; BYTE writings_occurred; BYTE total_sides; - BYTE expcted_side; + BYTE expcted_sides; + BYTE format; BYTE type; uint32_t total_size; BYTE last_operation; - BYTE bios_first_run; BYTE frame_insert; + uint32_t cycles_8bit_delay; + uint32_t cycles_dummy_delay; + _fds_info_protection protection; + _fds_info_bios bios; _fds_info_side sides[20]; } info; struct _fds_side { @@ -99,30 +109,28 @@ typedef struct _fds { // le variabili da salvare nei savestate struct _fds_drive { uint32_t disk_position; - uint32_t delay; + uint32_t delay_insert; + uint32_t delay_8bit; BYTE disk_ejected; BYTE side_inserted; - BYTE gap_ended; + BYTE mark_finded; BYTE end_of_head; BYTE scan; - BYTE crc_char; + BYTE crc_control; + WORD crc; BYTE enabled_dsk_reg; BYTE enabled_snd_reg; - BYTE data_readed; - BYTE data_to_write; - // anche se continuo a salvarlo nel save_slot.c, questa - // variabile non e' piu' utilizzata. quindi se servisse - // potrebbe essere riciclata per qualche altra cosa. + BYTE data_io; + BYTE data_available; BYTE transfer_flag; - BYTE motor_on; BYTE transfer_reset; - BYTE read_mode; + BYTE motor_on; + BYTE motor_started; + BYTE io_mode; BYTE mirroring; - BYTE crc_control; BYTE unknow; BYTE drive_ready; BYTE irq_disk_enabled; - BYTE at_least_one_scan; BYTE irq_timer_enabled; BYTE irq_timer_reload_enabled; BYTE irq_timer_high; @@ -189,20 +197,20 @@ typedef struct _fds { // auto insert struct _fds_auto_insert { struct _fds_auto_insert_r4032 { + BYTE disabled; uint32_t frames; uint32_t checks; } r4032; + struct _fds_auto_insert_end_of_head { + BYTE disabled; + } end_of_head; struct _fds_auto_insert_delay { - int32_t eject; int32_t dummy; - int32_t side; } delay; struct _fds_auto_insert_rE445 { BYTE in_run; - BYTE count; } rE445; BYTE disabled; - BYTE new_side; BYTE in_game; } auto_insert; } _fds; @@ -217,18 +225,17 @@ extern _fds fds; EXTERNC void fds_init(void); EXTERNC void fds_quit(void); -EXTERNC BYTE fds_load_rom(BYTE type); +EXTERNC BYTE fds_load_rom(BYTE format); EXTERNC BYTE fds_load_bios(void); EXTERNC void fds_info(void); EXTERNC void fds_info_side(BYTE side); EXTERNC void fds_disk_op(WORD type, BYTE side_to_insert, BYTE quiet); -EXTERNC void fds_diff_op(BYTE side, BYTE mode, uint32_t position, WORD value); -EXTERNC BYTE fds_from_image(uTCHAR *file, BYTE format, BYTE type); +EXTERNC BYTE fds_from_image_to_file(uTCHAR *file, BYTE format, BYTE type); EXTERNC BYTE fds_image_to_file(uTCHAR *file); +EXTERNC WORD fds_crc_byte(WORD base, BYTE data); EXTERNC uint32_t fds_disk_side_size_format(BYTE format); EXTERNC uint32_t fds_disk_side_size(void); EXTERNC uint32_t fds_image_side_size(void); -EXTERNC uint32_t fds_image_side_bytes(void); #undef EXTERNC diff --git a/src/core/mappers/mapper_FDS.c b/src/core/mappers/mapper_FDS.c index 8d05a6fa9..6c06ee9a7 100644 --- a/src/core/mappers/mapper_FDS.c +++ b/src/core/mappers/mapper_FDS.c @@ -28,8 +28,6 @@ #include "conf.h" #include "gui.h" -enum { TRANSFERED_8BIT = 0x02, END_OF_HEAD = 0x40, MIN_LAG_FRAMES = 20 }; - static const SBYTE modulation_table[8] = { 0, 1, 2, 4, 8, -4, -2, -1 }; static const BYTE volume_wave[4] = { 36, 24, 17, 14 }; @@ -40,9 +38,9 @@ void map_init_FDS(void) { EXTCL_APU_TICK(FDS); memset (&fds.auto_insert, 0x00, sizeof(fds.auto_insert)); - fds.auto_insert.delay.eject = -1; - fds.auto_insert.delay.dummy = -1; - fds.auto_insert.delay.side = -1; + fds.drive.transfer_reset = 0x02; + fds.drive.io_mode = 0x04; + fds.drive.drive_ready = 0x40; if (cfg->fds_disk1sideA_at_reset) { fds_disk_op(FDS_DISK_EJECT, 0, TRUE); @@ -76,22 +74,24 @@ void extcl_after_mapper_init_FDS(void) { memmap_prgrom_8k(0, MMCPU(0xE000), 0); } BYTE extcl_cpu_rd_mem_FDS(BYTE nidx, WORD address, UNUSED(BYTE openbus)) { - // 0xE18B : NMI entry point - // [$0100]: PC action on NMI. set to $C0 on reset - // When NMI occurs while $100 & $C0 != 0, it typically means that the game is starting. - if ((address == 0xE18B) & !fds.auto_insert.in_game & ((cpu_rd_mem_dbg(nidx, 0x100) & 0xC0) != 0)) { - fds.auto_insert.in_game = TRUE; - } else if (address == 0xE445) { - // Address : 0xE445 - // Name : CheckDiskHeader - // Input parameters : Pointer to 10 byte string at $00 - // Description : Compares the first 10 bytes on the disk coming after the FDS string, to 10 - // bytes pointed to by Ptr($00). To bypass the checking of any byte, a -1 can be placed in the - // equivelant place in the compare string. Otherwise, if the comparison fails, an appropriate error - // will be generated. - if (fds_auto_insert_enabled() & !fds.auto_insert.rE445.in_run) { - // i primi due passaggi sono del bios e li ignoro - if (fds.auto_insert.rE445.count > 1) { + switch (address) { + case 0xE188: + // 0xE18B : NMI entry point + // [$0100]: PC action on NMI. set to $C0 on reset + // When NMI occurs while $100 & $C0 != 0, it typically means that the game is starting. + if (!fds.auto_insert.in_game & ((cpu_rd_mem_dbg(nidx, 0x100) & 0xC0) != 0)) { + fds.auto_insert.in_game = TRUE; + } + break; + case 0xE445: + // Address : 0xE445 + // Name : CheckDiskHeader + // Input parameters : Pointer to 10 byte string at $00 + // Description : Compares the first 10 bytes on the disk coming after the FDS string, to 10 + // bytes pointed to by Ptr($00). To bypass the checking of any byte, a -1 can be placed in the + // equivelant place in the compare string. Otherwise, if the comparison fails, an appropriate error + // will be generated. + if (fds_auto_insert_enabled() & !fds.auto_insert.rE445.in_run) { WORD adr = cpu_rd_mem_dbg(nidx, 0) | (cpu_rd_mem_dbg(nidx, 1) << 8); BYTE string[10], side = 0xFF; uint32_t position = 0; @@ -108,7 +108,6 @@ BYTE extcl_cpu_rd_mem_FDS(BYTE nidx, WORD address, UNUSED(BYTE openbus)) { BYTE finded = TRUE; position = (a * fds_disk_side_size()); - if (fds.info.type == FDS_FORMAT_FDS) { position += 16; } @@ -145,61 +144,63 @@ BYTE extcl_cpu_rd_mem_FDS(BYTE nidx, WORD address, UNUSED(BYTE openbus)) { } else if (count == 1) { if ((side != fds.drive.side_inserted) || fds.drive.disk_ejected) { fds.auto_insert.rE445.in_run = TRUE; - fds.auto_insert.new_side = side; - fds.auto_insert.delay.side = FDS_AUTOINSERT_OP_SIDE_DELAY; + fds.side.change.new_side = side; + fds.side.change.delay = emu_ms_to_cpu_cycles(1); + fds.auto_insert.delay.dummy = 0; + if (!fds.drive.disk_ejected) { + fds_disk_op(FDS_DISK_EJECT, 0, TRUE); + gui_update_fds_menu(); + } } if (side > 0) { fds.auto_insert.in_game = TRUE; } - fds.auto_insert.delay.eject = -1; - fds.auto_insert.delay.dummy = -1; + fds.auto_insert.delay.dummy = 0; } - } else { - fds.auto_insert.rE445.count++; } - } + break; + case 0xEF44: + // Wait after disk insertion + if (cfg->fds_fast_forward) { + nes[nidx].c.cpu.PC.w += 2; + address = nes[nidx].c.cpu.PC.w - 1; + } + break; + case 0xEFAF: + // Wait license + if (cfg->fds_fast_forward) { + nes[nidx].c.cpu.AR = 0; + nes[nidx].c.cpu.PC.w += 2; + address = nes[nidx].c.cpu.PC.w - 1; + } + break; + case 0xF46E: + // License check + if (cfg->fds_fast_forward) { + nes[nidx].c.cpu.PC.w += 2; + address = nes[nidx].c.cpu.PC.w - 1; + } + break; } return (prgrom_rd(nidx, address)); } void extcl_cpu_every_cycle_FDS(BYTE nidx) { BYTE max_speed = cfg->fds_fast_forward & - ((fds.drive.scan & (info.lag_frame.consecutive > MIN_LAG_FRAMES)) | !fds.auto_insert.in_game); - WORD data = 0; + ((fds.side.change.delay | fds.drive.delay_insert) || !fds.auto_insert.in_game || + (fds.drive.scan & (info.lag_frame.consecutive > FDS_MIN_LAG_FRAMES)) || + (fds.auto_insert.r4032.checks > 5)); // auto insert - if (fds_auto_insert_enabled()) { -#define df_max_speed (cfg->fds_fast_forward & (info.lag_frame.consecutive > MIN_LAG_FRAMES)) - if (fds.auto_insert.delay.eject > 0) { - fds.auto_insert.delay.eject--; - max_speed = df_max_speed & (fds.auto_insert.delay.eject > 0); - } else if (fds.auto_insert.delay.dummy > 0) { - if (--fds.auto_insert.delay.dummy == 0) { - fds.auto_insert.delay.dummy = -1; - fds_disk_op(FDS_DISK_INSERT, 0, TRUE); - gui_update_fds_menu(); - } - max_speed = df_max_speed & (fds.auto_insert.delay.dummy > 0); - } else if (fds.auto_insert.delay.side > 0) { - if (--fds.auto_insert.delay.side == 0) { - fds.auto_insert.delay.side = -1; - fds.side.change.new_side = fds.auto_insert.new_side; - fds.side.change.delay = FDS_AUTOINSERT_OP_SIDE_DELAY; - fds_disk_op(FDS_DISK_EJECT, 0, TRUE); - gui_update_fds_menu(); - } - max_speed = df_max_speed & (fds.auto_insert.delay.side > 0); - } - if (!fds.auto_insert.delay.eject & (fds.auto_insert.delay.dummy == -1) & (fds.auto_insert.r4032.checks > 20)) { - fds.auto_insert.delay.eject = -1; - fds.auto_insert.delay.dummy = FDS_OP_SIDE_DELAY; - fds_disk_op(FDS_DISK_EJECT, 0, TRUE); + if (fds_auto_insert_enabled() && (fds.auto_insert.delay.dummy > 0)) { + if (!(--fds.auto_insert.delay.dummy)) { + fds_disk_op(FDS_DISK_INSERT, fds.drive.side_inserted, TRUE); gui_update_fds_menu(); - max_speed = df_max_speed; + } else { + max_speed = cfg->fds_fast_forward && fds.auto_insert.end_of_head.disabled; } -#undef df_max_speed } - if (max_speed & !fds.info.bios_first_run) { + if (max_speed) { gui_max_speed_start(); } else { gui_max_speed_stop(); @@ -235,149 +236,101 @@ void extcl_cpu_every_cycle_FDS(BYTE nidx) { // no disco, no party if (fds.drive.disk_ejected) { - fds.drive.at_least_one_scan = FALSE; - if (fds.drive.delay < FDS_8BIT_DELAY) { - fds.drive.delay = FDS_8BIT_DELAY; - } return; } - // il motore non e' avviato - if (!fds.drive.motor_on) { - fds.drive.at_least_one_scan = FALSE; + if (fds.drive.delay_insert) { + fds.drive.delay_insert--; + return; + } + + if (!fds.drive.motor_on && !fds.drive.motor_started) { fds.drive.disk_position = 0; - fds.drive.gap_ended = FALSE; - // "Akuu Senki Raijin (Japan) (Disk Writer)" ne ha bisogno - // per non sporcare lo screen. - if (fds.drive.delay < FDS_8BIT_DELAY) { - fds.drive.delay = FDS_8BIT_DELAY; - } + fds.drive.mark_finded = FALSE; return; } // se c'e' un delay aspetto - if ((fds.drive.delay > 0) && --fds.drive.delay) { + if ((fds.drive.delay_8bit > 0) && --fds.drive.delay_8bit) { return; } fds.drive.scan = !fds.drive.transfer_reset; fds.info.last_operation = FDS_OP_NONE; + fds.drive.data_available = FALSE; if (fds.drive.scan) { - // se c'e' una richiesta di invio crc i prossimi due bytes lo saranno - if (!fds.drive.crc_char && fds.drive.crc_control) { - fds.drive.crc_char = 2; - } + BYTE data = 0, transfer = FALSE; + + data = fds.side.info->data[fds.drive.disk_position]; - if (fds.drive.read_mode) { + if (fds.drive.io_mode) { // read - data = fds.side.info->data[fds.drive.disk_position]; + if (fds.drive.drive_ready) { + if (fds.drive.mark_finded) { + transfer = TRUE; + fds.drive.crc = fds_crc_byte(fds.drive.crc, data); + } else if (data == FDS_DISK_BLOCK_MARK) { + fds.drive.mark_finded = TRUE; + fds.drive.crc = 0; + fds.drive.crc = fds_crc_byte(fds.drive.crc, data); + } + } } else { - // write if (!fds.drive.drive_ready) { data = FDS_DISK_GAP; - } else if (fds.drive.crc_char) { - data = FDS_DISK_CRC_CHAR1; + fds.drive.crc = 0; } else { - data = fds.side.info->data[fds.drive.disk_position]; + if (fds.drive.crc_control) { + data = fds.drive.crc >> 0; + fds.drive.crc >>= 8; + } else { + data = fds.drive.data_io; + fds.drive.crc = fds_crc_byte(fds.drive.crc, data); + } } + transfer = TRUE; } - // se non sono piu' nel gap vuol dire che ho trasferito - // 8 bit di dati quindi setto il flag corrispondente e - // se e' abilitato l'irq del disco, lo setto. - if (fds.drive.gap_ended) { - fds.drive.transfer_flag = 0x02; - fds.drive.at_least_one_scan = TRUE; + fds.auto_insert.r4032.frames = 0; + fds.auto_insert.r4032.checks = 0; + + if (transfer) { + fds.drive.data_available = 0x80; if (fds.drive.irq_disk_enabled) { + fds.drive.transfer_flag = 0x02; nes[nidx].c.irq.high |= FDS_DISK_IRQ; } - - if (fds.drive.read_mode) { - fds.drive.data_readed = data; + if (fds.drive.io_mode) { + fds.drive.data_io = data; fds.info.last_operation = FDS_OP_READ; } else { - uint32_t position = (fds.drive.disk_position - 2); - WORD *dst = &fds.side.info->data[position]; - - // quando inizia la scrittura il bios scrive sempre - // prima un GAP, seguito da un MARK seguito dal blocco che verrà chiuso dai CRC. - // il last_position devo aggiornarlo solo con i CRC e i GAP che seguono. - if (((*dst) == 0x0100) && (fds.drive.data_to_write == 0x00)) { - (*dst) = 0x0100; - } else if ((fds.drive.data_to_write == 0x80) && - (((*dst) == 0x0180) || (position == fds.side.info->last_position))) { - (*dst) = 0x0180; - if (position == fds.side.info->last_position) { - fds_diff_op(fds.side.info->side, FDS_OP_WRITE, position, (*dst)); - } - } else if (fds.drive.crc_char) { - if (fds.drive.crc_char == 2) { - (*dst) = FDS_DISK_CRC_CHAR1; - } else { - (*dst) = FDS_DISK_CRC_CHAR2; - } - if (position >= fds.side.info->last_position) { - fds_diff_op(fds.side.info->side, FDS_OP_WRITE, position, (*dst)); - fds.info.sides[fds.side.info->side].last_position = position + 1; - if ((*dst) == FDS_DISK_CRC_CHAR2) { - for (uint32_t i = 0; i < FDS_GAP_BLOCK; i++) { - uint32_t p = position + 1 + i; - - if (p < fds_disk_side_size()) { - fds.side.info->data[p] = FDS_DISK_GAP; - fds_diff_op(fds.side.info->side, FDS_OP_WRITE, p, FDS_DISK_GAP); - fds.info.sides[fds.side.info->side].last_position = p + 1; - } - } - } - } - } else { - (*dst) = fds.drive.data_to_write; - fds_diff_op(fds.side.info->side, FDS_OP_WRITE, position, fds.drive.data_to_write); - } - data = (*dst); + fds.side.info->data[fds.drive.disk_position] = data; + fds.info.writings_occurred = TRUE; fds.info.last_operation = FDS_OP_WRITE; } } - - if (data != FDS_DISK_GAP) { - fds.drive.gap_ended = TRUE; - } - - if (fds.drive.crc_char && !(--fds.drive.crc_char)) { - fds.drive.gap_ended = fds.drive.crc_control = FALSE; - } - - if (!fds.drive.drive_ready) { - fds.drive.gap_ended = FALSE; - } } - if (++fds.drive.disk_position >= fds.info.sides[fds.drive.side_inserted].size) { - fds.drive.end_of_head = END_OF_HEAD; - fds.drive.disk_position = 0; - fds.drive.gap_ended = FALSE; - fds.drive.delay = 65536 * 8; - // FDS interessati : - // - 19 Neunzehn (1988)(Soft Pro)(J).fds - // visto che il controllo del r4032 e' mooooolto lento, l'eject forzato alla fine del disco - // costringe la rom al richiamo della funzione del bios $E445. - // - Dandy (19xx)(Pony Canyon)(J).fds - // dopo aver selezionato il nome del personaggio, puo' capitare che il disco sia disinserito a causa di - // di eject e che dia un "error 01" che comunque verra' subito corretto dall'insert automatico seguente. - // - Zelda no Densetsu - The Hyrule Fantasy (1986)(Nintendo)(J).fds - // stesso discorso fatto per Dandy (19xx)(Pony Canyon)(J).fds. - if (fds_auto_insert_enabled() && !fds.drive.at_least_one_scan && - (fds.auto_insert.delay.eject == -1) && !fds.side.change.delay && - (fds.auto_insert.delay.dummy == -1)) { - fds.auto_insert.delay.eject = FDS_AUTOINSERT_OP_SIDE_DELAY; + if (fds.drive.scan || fds.drive.motor_started) { + if (++fds.drive.disk_position >= fds.info.sides[fds.drive.side_inserted].size) { + fds.drive.end_of_head = 0x40; + fds.drive.disk_position = 0; + fds.drive.transfer_reset = FALSE; + if (fds.drive.motor_started) { + fds.drive.motor_on = FALSE; + fds.drive.motor_started = FALSE; + if (fds_auto_insert_enabled() && !fds.auto_insert.end_of_head.disabled && !fds.auto_insert.delay.dummy) { + fds_disk_op(FDS_DISK_EJECT, 0, TRUE); + gui_update_fds_menu(); + fds.auto_insert.delay.dummy = fds.info.cycles_dummy_delay; + } + } + } else { + fds.drive.end_of_head = FALSE; + // il delay per riuscire a leggere i prossimi 8 bit + fds.drive.delay_8bit = fds.info.cycles_8bit_delay; } - fds.drive.at_least_one_scan = FALSE; - } else { - fds.drive.end_of_head = FALSE; - // il delay per riuscire a leggere i prossimi 8 bit - fds.drive.delay = FDS_8BIT_DELAY; } } void extcl_apu_tick_FDS(void) { diff --git a/src/core/patcher.c b/src/core/patcher.c index 68c61bc0b..4861e2253 100644 --- a/src/core/patcher.c +++ b/src/core/patcher.c @@ -312,7 +312,7 @@ static BYTE patcher_ips(_rom_mem *patch, _rom_mem *rom) { BYTE ch = 0; address = patcher_3byte(patch); - if ((address == -1) || (address == 0x454f46)) { + if ((address == -1) || (address == 0x454F46)) { break; } @@ -352,9 +352,7 @@ static BYTE patcher_ips(_rom_mem *patch, _rom_mem *rom) { } if (rle) { - SDBWORD i = 0; - - for (i = 0; i < len; i++) { + for (SDBWORD i = 0; i < len; i++) { blk[address + i] = ch; } } else { diff --git a/src/core/recording.c b/src/core/recording.c index e55ff9484..e1959d405 100644 --- a/src/core/recording.c +++ b/src/core/recording.c @@ -1412,7 +1412,7 @@ static int ffmpeg_audio_select_samplerate(const AVCodec *codec) { const int *p = NULL; if (!codec->supported_samplerates) { - switch(codec->id) { + switch (codec->id) { case AV_CODEC_ID_PCM_S16LE: case AV_CODEC_ID_PCM_S16BE: case AV_CODEC_ID_PCM_S16LE_PLANAR: @@ -1474,7 +1474,7 @@ static uint64_t ffmpeg_audio_select_channel_layout(const AVCodec *codec) { int best_nb_channels = 0; if (!codec->channel_layouts) { - switch(codec->id) { + switch (codec->id) { case AV_CODEC_ID_PCM_S16LE: case AV_CODEC_ID_PCM_S16BE: case AV_CODEC_ID_PCM_S16LE_PLANAR: @@ -1549,7 +1549,7 @@ static int ffmpeg_audio_select_channel_layout(const AVCodec *codec, AVChannelLay int best_nb_channels = 0; if (!codec->ch_layouts) { - switch(codec->id) { + switch (codec->id) { case AV_CODEC_ID_PCM_S16LE: case AV_CODEC_ID_PCM_S16BE: case AV_CODEC_ID_PCM_S16LE_PLANAR: diff --git a/src/core/save_slot.c b/src/core/save_slot.c index 6f9fb2a66..e46ed466a 100644 --- a/src/core/save_slot.c +++ b/src/core/save_slot.c @@ -511,31 +511,32 @@ BYTE save_slot_operation(BYTE mode, BYTE slot, FILE *fp) { save_slot_ele(mode, slot, dipswitch.value); if (fds.info.enabled) { - // libero la zona di memoria gia' occupata BYTE old_side_inserted = fds.drive.side_inserted; // salvo, leggo o conto quello che serve save_slot_ele(mode, slot, fds.drive.disk_position); - save_slot_ele(mode, slot, fds.drive.delay); + save_slot_ele(mode, slot, fds.drive.delay_insert); + save_slot_ele(mode, slot, fds.drive.delay_8bit); save_slot_ele(mode, slot, fds.drive.disk_ejected); save_slot_ele(mode, slot, fds.drive.side_inserted); - save_slot_ele(mode, slot, fds.drive.gap_ended); + save_slot_ele(mode, slot, fds.drive.mark_finded); + save_slot_ele(mode, slot, fds.drive.end_of_head); save_slot_ele(mode, slot, fds.drive.scan); - save_slot_ele(mode, slot, fds.drive.crc_char); + save_slot_ele(mode, slot, fds.drive.crc_control); + save_slot_ele(mode, slot, fds.drive.crc); save_slot_ele(mode, slot, fds.drive.enabled_dsk_reg); save_slot_ele(mode, slot, fds.drive.enabled_snd_reg); - save_slot_ele(mode, slot, fds.drive.data_readed); - save_slot_ele(mode, slot, fds.drive.data_to_write); + save_slot_ele(mode, slot, fds.drive.data_io); + save_slot_ele(mode, slot, fds.drive.data_available); save_slot_ele(mode, slot, fds.drive.transfer_flag); - save_slot_ele(mode, slot, fds.drive.motor_on); save_slot_ele(mode, slot, fds.drive.transfer_reset); - save_slot_ele(mode, slot, fds.drive.read_mode); + save_slot_ele(mode, slot, fds.drive.motor_on); + save_slot_ele(mode, slot, fds.drive.motor_started); + save_slot_ele(mode, slot, fds.drive.io_mode); save_slot_ele(mode, slot, fds.drive.mirroring); - save_slot_ele(mode, slot, fds.drive.crc_control); save_slot_ele(mode, slot, fds.drive.unknow); save_slot_ele(mode, slot, fds.drive.drive_ready); save_slot_ele(mode, slot, fds.drive.irq_disk_enabled); - save_slot_ele(mode, slot, fds.drive.at_least_one_scan); save_slot_ele(mode, slot, fds.drive.irq_timer_enabled); save_slot_ele(mode, slot, fds.drive.irq_timer_reload_enabled); save_slot_ele(mode, slot, fds.drive.irq_timer_high); @@ -581,15 +582,11 @@ BYTE save_slot_operation(BYTE mode, BYTE slot, FILE *fp) { save_slot_ele(mode, slot, fds.auto_insert.r4032.frames); save_slot_ele(mode, slot, fds.auto_insert.r4032.checks); - save_slot_ele(mode, slot, fds.auto_insert.delay.eject); save_slot_ele(mode, slot, fds.auto_insert.delay.dummy); - save_slot_ele(mode, slot, fds.auto_insert.delay.side); save_slot_ele(mode, slot, fds.auto_insert.rE445.in_run); - save_slot_ele(mode, slot, fds.auto_insert.rE445.count); save_slot_ele(mode, slot, fds.auto_insert.disabled); - save_slot_ele(mode, slot, fds.auto_insert.new_side); save_slot_ele(mode, slot, fds.auto_insert.in_game); save_slot_ele(mode, slot, info.lag_frame.consecutive); diff --git a/src/core/unicode_def.h b/src/core/unicode_def.h index 4a6992373..99bf94115 100644 --- a/src/core/unicode_def.h +++ b/src/core/unicode_def.h @@ -71,6 +71,7 @@ typedef wchar_t uTCHAR; #define ustrchr wcschr #define ustrdup _wcsdup #define usscanf swscanf_s +#define ustrstr wcsstr // linux, bsd #else @@ -119,6 +120,7 @@ typedef char uTCHAR; #define ustrchr strchr #define ustrdup strdup #define usscanf sscanf +#define ustrstr strstr #endif diff --git a/src/gui/designer/mainWindow.ui b/src/gui/designer/mainWindow.ui index 76d37ee80..7a56b8a07 100644 --- a/src/gui/designer/mainWindow.ui +++ b/src/gui/designer/mainWindow.ui @@ -99,7 +99,8 @@ :/icon/icons/fds_export.svgz:/icon/icons/fds_export.svgz - + + @@ -788,14 +789,14 @@ Detach &Barcode - + - to &FDS Format + to &FDS Format (with Header) - to Quick Disk Format + to &Quick Disk Format @@ -803,6 +804,11 @@ to puNES image + + + to F&DS Format (without Header) + + diff --git a/src/gui/mainWindow.cpp b/src/gui/mainWindow.cpp index cff0d18bf..0d5bb07f4 100644 --- a/src/gui/mainWindow.cpp +++ b/src/gui/mainWindow.cpp @@ -885,9 +885,10 @@ void mainWindow::connect_menu_signals(void) { connect_action(action_Disk_4_side_B, 7, SLOT(s_disk_side())); connect_action(action_Switch_sides, 0xFFF, SLOT(s_disk_side())); connect_action(action_Eject_Insert_Disk, SLOT(s_eject_disk())); - connect_action(action_Current_state_to_FDS, 0, SLOT(s_export_fds_image())); - connect_action(action_Current_state_to_Quick_Disk, 1, SLOT(s_export_fds_image())); - connect_action(action_Current_state_to_puNES_image, 2, SLOT(s_export_fds_image())); + connect_action(action_Current_state_to_FDS_with_Header, 0, SLOT(s_export_fds_image())); + connect_action(action_Current_state_to_FDS_without_Header, 1, SLOT(s_export_fds_image())); + connect_action(action_Current_state_to_Quick_Disk, 2, SLOT(s_export_fds_image())); + connect_action(action_Current_state_to_puNES_image, 3, SLOT(s_export_fds_image())); connect_action(action_Tape_Play, SLOT(s_tape_play())); connect_action(action_Tape_Record, SLOT(s_tape_record())); connect_action(action_Tape_Stop, SLOT(s_tape_stop())); @@ -1554,7 +1555,8 @@ void mainWindow::s_disk_side(void) { fds_disk_op(FDS_DISK_SELECT, side, FALSE); } else { fds.side.change.new_side = side; - fds.side.change.delay = FDS_OP_SIDE_DELAY; + fds.side.change.delay = fds.info.cycles_dummy_delay; + fds.auto_insert.delay.dummy = 0; fds_disk_op(FDS_DISK_EJECT, 0, FALSE); } @@ -1579,18 +1581,18 @@ void mainWindow::s_export_fds_image(void) { emu_thread_pause(); - switch(format) { + switch (format) { default: case 0: - format = 0; + case 1: filters.append(tr("FDS Format Disk")); filters[0].append(" (*.fds *.FDS)"); break; - case 1: + case 2: filters.append(tr("Quick Disk Format Disk")); filters[0].append(" (*.qd *.QD)"); break; - case 2: + case 3: filters.append(tr("puNES image")); filters[0].append(" (*.image)"); break; @@ -1607,22 +1609,23 @@ void mainWindow::s_export_fds_image(void) { BYTE rc = EXIT_ERROR; if (fileinfo.suffix().isEmpty()) { - switch(format) { + switch (format) { default: case 0: + case 1: fileinfo.setFile(QString(file) + ".fds"); break; - case 1: + case 2: fileinfo.setFile(QString(file) + ".qd"); break; - case 2: + case 3: fileinfo.setFile(QString(file) + ".image"); break; } } - if (format < 2) { - rc = fds_from_image(uQStringCD(fileinfo.absoluteFilePath()), - format == 0 ? FDS_FORMAT : QD_FORMAT, + if (format < 3) { + rc = fds_from_image_to_file(uQStringCD(fileinfo.absoluteFilePath()), + format == 2 ? QD_FORMAT : FDS_FORMAT, format == 0 ? FDS_FORMAT_FDS : FDS_FORMAT_RAW); } else { rc = fds_image_to_file(uQStringCD(fileinfo.absoluteFilePath())); diff --git a/src/gui/wdgOverlayUi.cpp b/src/gui/wdgOverlayUi.cpp index 4116df553..17d14cf65 100644 --- a/src/gui/wdgOverlayUi.cpp +++ b/src/gui/wdgOverlayUi.cpp @@ -122,6 +122,8 @@ static const char *info_messages_precompiled[] = { /* 38 */ QT_TRANSLATE_NOOP("overlayWidgetInfo", "[red]error[normal] on write save state"), //: Do not translate the words contained between parentheses (example: [red] or [normal]) are tags that have a specific meaning and do not traslate %1 and %2 /* 39 */ QT_TRANSLATE_NOOP("overlayWidgetInfo", "auto switch [red]disabled[normal], game not compatible"), +//: Do not translate the words contained between parentheses (example: [red] or [normal]) are tags that have a specific meaning and do not traslate %1 and %2 +/* 40 */ QT_TRANSLATE_NOOP("overlayWidgetInfo", "auto switch [red]disabled[normal], single-sided disk"), }; typedef struct _overlay_info_message {