#include "zx_disk.h" #include "z80.h" #include #define ZX_FDC_MODE_IDLE 0 #define ZX_FDC_MODE_COMMAND 1 #define ZX_FDC_MODE_EXECUTION 2 #define ZX_FDC_MODE_RESULT 3 #define ZX_FDC_DATA_DIRECTION_INPUT 0x00 #define ZX_FDC_DATA_DIRECTION_OUTPUT 0x40 #define ZX_FDC_COMMAND_MASK 0x1F #define ZX_FDC_COMMAND_SPECIFY 0x03 #define ZX_FDC_COMMAND_SENSE_DRIVE_STATUS 0x04 #define ZX_FDC_COMMAND_RECALIBRATE 0x07 #define ZX_FDC_COMMAND_SENSE_INTERRUPT_STATUS 0x08 #define ZX_FDC_COMMAND_SEEK 0x0F #define ZX_FDC_MAIN_DRIVE0_BUSY 1 #define ZX_FDC_MAIN_DRIVE1_BUSY 2 #define ZX_FDC_MAIN_DRIVE2_BUSY 4 #define ZX_FDC_MAIN_DRIVE3_BUSY 8 #define ZX_FDC_MAIN_BUSY 16 #define ZX_FDC_MAIN_EXEC_MODE 32 #define ZX_FDC_MAIN_DATA_DIR 64 // 0: CPU->FDC 1: FDC->CPU #define ZX_FDC_MAIN_DATA_READY 128 namespace zx_disk { uint8_t mode = ZX_FDC_MODE_IDLE; uint8_t call_count = 0; //uint8_t data_direction = 0x00; // 0x40 uint8_t fdd0busy = 0; uint8_t srt, hlt, hut, nd; bool seeking = false; uint8_t current_head = 0; uint8_t current_drive = 0; uint8_t current_track = 0; int zx_fdc_main_status_port_in(int port); int zx_fdc_data_port_in(int port); void zx_fdc_data_port_out(int port, int val); void init() { z80::connect_port(0x2ffd, 0xf002, zx_fdc_main_status_port_in, nullptr); z80::connect_port(0x3ffd, 0xf002, zx_fdc_data_port_in, zx_fdc_data_port_out); } void reset() { } uint8_t main_status_register() { const uint8_t val = (fdd0busy) | ((mode==ZX_FDC_MODE_IDLE) ? 0x00 : 0x10) | ((mode==ZX_FDC_MODE_EXECUTION) ? 0x20 : 0x00) | ((mode==ZX_FDC_MODE_RESULT) ? 0x40 : 0x00) | (0x80); return val; } int zx_fdc_main_status_port_in(int port) { printf("FDC port 0x2ffd IN\n"); return main_status_register(); } uint8_t ST0() { return current_drive | (current_head<<2) | (seeking ? 0x20 : 0x00) | (fdd0busy ? 0x00 : 0x40); } uint8_t ST3() { return current_drive | (current_head<<2) | 0x38; // ST3 } void start_command(uint8_t command); uint8_t (*process_current_command)(uint8_t) = nullptr; uint8_t process_command_unknown(uint8_t command); uint8_t process_command_specify(uint8_t command); uint8_t process_command_sense_drive_status(uint8_t command); uint8_t process_command_recalibrate(uint8_t command); uint8_t process_command_sense_interrupt_status(uint8_t command); uint8_t process_command_seek(uint8_t command); int zx_fdc_data_port_in(int port) { printf("FDC port 0x3ffd IN\n"); if (mode == ZX_FDC_MODE_COMMAND) { printf("IGNORED!\n"); return 0; } if (process_current_command) return process_current_command(0); return 0; } void zx_fdc_data_port_out(int port, int val) { printf("FDC port 0x3ffd OUT: 0x%02x\n", val); if (mode == ZX_FDC_MODE_RESULT) { printf("IGNORED!\n"); return; } if (process_current_command) process_current_command(val); else start_command(val & ZX_FDC_COMMAND_MASK); } void start_command(uint8_t command) { if ((fdd0busy) && (command != ZX_FDC_COMMAND_SENSE_INTERRUPT_STATUS)) return; mode = ZX_FDC_MODE_COMMAND; call_count = 1; switch(command) { case ZX_FDC_COMMAND_SPECIFY: process_current_command = process_command_specify; break; case ZX_FDC_COMMAND_SENSE_DRIVE_STATUS: process_current_command = process_command_sense_drive_status; break; case ZX_FDC_COMMAND_RECALIBRATE: process_current_command = process_command_recalibrate; fdd0busy = 1; break; case ZX_FDC_COMMAND_SENSE_INTERRUPT_STATUS: process_current_command = process_command_sense_interrupt_status; mode = ZX_FDC_MODE_RESULT; call_count = 0; break; case ZX_FDC_COMMAND_SEEK: process_current_command = process_command_seek; fdd0busy = 1; seeking = true; break; default: { process_current_command = process_command_unknown; mode = ZX_FDC_MODE_RESULT; call_count = 0; } } } // =================================================================== // FDC COMMAND: UNKNOWN // =================================================================== uint8_t process_command_unknown(uint8_t command) { process_current_command = nullptr; mode = ZX_FDC_MODE_IDLE; const uint8_t val = ST0(); printf("--> (returning 0x%02x)\n", val); return val; } // =================================================================== // FDC COMMAND: SPECIFY (0x03) // =================================================================== uint8_t process_command_specify(uint8_t command) { switch (call_count) { case 1: srt = (command & 0xf0) >> 4; hut = command & 0x0f; call_count++; break; case 2: hlt = (command & 0xfe) >> 1; nd = command & 0x01; call_count=0; process_current_command = nullptr; mode = ZX_FDC_MODE_IDLE; break; } return 0; } // =================================================================== // FDC COMMAND: SENSE DRIVE STATUS (0x04) // =================================================================== uint8_t process_command_sense_drive_status(uint8_t command) { switch (mode) { case ZX_FDC_MODE_COMMAND: current_head = (command & 0x4)>>2; current_drive = command & 0x3; call_count = 0; mode = ZX_FDC_MODE_RESULT; break; case ZX_FDC_MODE_RESULT: { process_current_command = nullptr; mode = ZX_FDC_MODE_IDLE; const uint8_t val = ST3(); printf("--> (returning 0x%02x)\n", val); return val; } } return 0; } // =================================================================== // FDC COMMAND: RECALIBRATE (0x07) // =================================================================== uint8_t process_command_recalibrate(uint8_t command) { switch (mode) { case ZX_FDC_MODE_COMMAND: current_drive = command & 0x3; call_count = 0; current_track = 0; process_current_command = nullptr; mode = ZX_FDC_MODE_IDLE; break; } return 0; } // =================================================================== // FDC COMMAND: SENSE INTERRUPT STATUS (0x08) // =================================================================== uint8_t process_command_sense_interrupt_status(uint8_t command) { switch (call_count) { case 0: { call_count++; const uint8_t val = ST0(); printf("--> (returning 0x%02x)\n", val); return val; } case 1: process_current_command = nullptr; fdd0busy = 0; seeking = false; call_count = 0; mode = ZX_FDC_MODE_IDLE; printf("--> (returning 0x%02x)\n", current_track); return current_track; } return 0; } // =================================================================== // FDC COMMAND: SEEK (0x0F) // =================================================================== uint8_t process_command_seek(uint8_t command) { switch (call_count) { case 1: { current_head = (command & 0x4)>>2; current_drive = command & 0x3; call_count++; break; } case 2: current_track = command; process_current_command = nullptr; call_count = 0; mode = ZX_FDC_MODE_IDLE; break; } return 0; } }