/******************************************************************************* * NewOswan * io.c: I/O ports implementaton * Based on the original Oswan-unix * Copyright (c) 2014-2021 986-Studio. All rights reserved. * ******************************************************************************/ ////////////////////////////////////////////////////////////////////////////// // // // // // // ////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#define IO_DUMP //define EEPROM_DEBUG typedef struct ioregistry_t { io_read read; /***< Read function for a specific IO port */ io_write write; /***< Write function for a specific IO port */ void *private; /***< Private data for the peripheral if needed. */ } ioregistry_t; ioregistry_t io_registry[0x100]; extern uint32_t romAddressMask; extern nec_Regs I; extern uint64_t nec_monotonicCycles; extern uint32_t sramSize; uint8_t *ws_ioRam = NULL; uint8_t ws_key_start; uint8_t ws_key_x4; uint8_t ws_key_x2; uint8_t ws_key_x1; uint8_t ws_key_x3; uint8_t ws_key_y4; uint8_t ws_key_y2; uint8_t ws_key_y1; uint8_t ws_key_y3; uint8_t ws_key_button_a; uint8_t ws_key_button_b; uint8_t ws_key_flipped; FILE *ioLogFp = NULL; //////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////// // // // // // // // //////////////////////////////////////////////////////////////////////////////// void io_reset(void) { ws_key_start = 0; ws_key_x4 = 0; ws_key_x2 = 0; ws_key_x1 = 0; ws_key_x3 = 0; ws_key_y4 = 0; ws_key_y2 = 0; ws_key_y1 = 0; ws_key_y3 = 0; ws_key_button_a = 0; ws_key_button_b = 0; int i; for (i = 0 ; i < 0x100 ; i++) { /* * 0x90 should probably be a better value as for some reason * the Swan seems to like to returh 0x90 for not connected memory/IO * Keep 0x00 for now until the whole IO "default" value is solved. */ ws_ioRam[i] = 0x00; } ws_ioRam[0xC0] = 0xFF; ws_ioRam[0xC1] = 0xFF; ws_ioRam[0xC2] = 0xFF; ws_ioRam[0xC3] = 0xFF; } //////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////// // // // // // // // //////////////////////////////////////////////////////////////////////////////// void io_init(void) { if (ws_ioRam == NULL) { ws_ioRam = (uint8_t *)malloc(0x100); } io_reset(); ws_key_flipped = 0; #ifdef IO_DUMP ioLogFp = fopen("iodump.csv", "wt"); #endif } //////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////// // // // // // // // //////////////////////////////////////////////////////////////////////////////// void io_flipControls(void) { ws_key_flipped = !ws_key_flipped; } //////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////// // // // // // // // //////////////////////////////////////////////////////////////////////////////// void io_done(void) { if (ws_ioRam == NULL) { free(ws_ioRam); } #ifdef IO_DUMP fclose(ioLogFp); #endif } //////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////// // // // // // // // //////////////////////////////////////////////////////////////////////////////// uint8_t io_readport_new(uint8_t port) { if (io_registry[port].read) { return io_registry[port].read(io_registry[port].private, port); } return 0x90; } void io_writeport_new(uint8_t port, uint8_t value) { if (io_registry[port].write) { return io_registry[port].write(io_registry[port].private, port, value); } } void register_io_hook(uint8_t port, io_read *readHook, io_write writeHook, void *pdata) { io_registry[port].read = readHook; io_registry[port].write = writeHook; io_registry[port].private = pdata; } void register_io_hook_array(uint8_t *portList, uint8_t listLen, io_read *readHook, io_write writeHook, void *pdata) { uint16_t i; for(i = 0; i < listLen; i++) { io_registry[portList[i]].read = readHook; io_registry[portList[i]].write = writeHook; io_registry[portList[i]].private = pdata; } } uint8_t io_readport(uint8_t port) { int w1, w2; uint8_t retVal = 0; switch (port) { case 0x4e: case 0x4f: case 0x50: case 0x51: case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87: case 0x88: case 0x89: case 0x8a: case 0x8b: case 0x8c: case 0x8d: case 0x8e: case 0x8f: case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: retVal = ws_audio_port_read(port); break; case 0xb5: w1 = ws_ioRam[0xb5]; if (w1 & 0x40) { w2 = 0x00; w2 = (ws_key_start << 1) | (ws_key_button_a << 2) | (ws_key_button_b << 3); retVal = (uint8_t)((w1 & 0xf0) | w2); break; } if (w1 & 0x20) { w2 = 0x00; w2 = (ws_key_x1 << 0) | (ws_key_x2 << 1) | (ws_key_x3 << 2) | (ws_key_x4 << 3); retVal = (uint8_t)((w1 & 0xf0) | w2); break; } if (w1 & 0x10) { w2 = 0x00; w2 = (ws_key_y1 << 0) | (ws_key_y2 << 1) | (ws_key_y3 << 2) | (ws_key_y4 << 3); retVal = (uint8_t)((w1 & 0xf0) | w2); } break; case 0x62: switch (ws_get_system()) { case WS_SYSTEM_AUTODETECT: case WS_SYSTEM_MONO: case WS_SYSTEM_COLOR: retVal = 0x00; break; case WS_SYSTEM_CRYSTAL: retVal = 0x80; break; } break; case 0xc0 : // ??? retVal = ((ws_ioRam[0xc0] & 0xf) | 0x20); goto exit; case 0xD0: retVal = 0; goto exit; case 0xCC: case 0xCD: retVal = 0; break; default: retVal = ws_ioRam[port]; if (port > 0xD0) { Log(TLOG_DEBUG, "io", "ReadIO(%02X) <= %02X [%04X:%04Xh];", port, retVal, I.sregs[CS], I.ip); } break; case 0xA0: case 0xAA: case 0xAB: case 0xAC: case 0xAD: retVal = ws_gpu_port_read(port); break; } if (port == 0xA0) { Log(TLOG_ALWAYS, "A0", "Hello I'm A0 and read %02X!!!", retVal); } if (port >= 0xC4) { Log(TLOG_DEBUG, "io", "ReadMBCIO(%02X) <= %02X [%04X:%04Xh];", port, retVal, I.sregs[CS], I.ip); } exit: if (port < 0xC0) { switch(port) { case 0x02: case 0x84: case 0x85: case 0x90: case 0x91: case 0xB5: break; default: Log(TLOG_DEBUG, "io", "ReadIO(%02X) <= %02X [%04X:%04Xh];", port, retVal, I.sregs[CS], I.ip); break; } } if (ioLogFp) { fprintf(ioLogFp, "%llu, R, %02X, %02X\n", nec_monotonicCycles, port, retVal); } return retVal; } //////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////// // // // // // // // //////////////////////////////////////////////////////////////////////////////// void io_writeport(uint32_t port, uint8_t value) { int unknown_io_port = 0; if (ioLogFp) { fprintf(ioLogFp, "%llu, W, %02X, %02X\n", nec_monotonicCycles, port, value); } if (port > 0x100) { port &= 0xFF; if (port > 0x100) { return; } } if ((port == 0xA0) && (ws_ioRam[port] & 0x01) && (~value & 0x01)) { value |= 0x01; } ws_ioRam[port] = value; switch (port) { /* GPU IOs */ case 0x00: Log(TLOG_DEBUG, "GPU", "Screen enabled: W2:%c W2M:%c, SW:%c, S:%c, S2:%c, S1:%c", (value & 0x20)?'Y':'N', (value & 0x10)?'I':'O', (value & 0x08)?'Y':'N', (value & 0x04)?'Y':'N', (value & 0x02)?'Y':'N', (value & 0x01)?'Y':'N'); break; case 0x04: if (ws_gpu_operatingInColor) { Log(TLOG_DEBUG, "GPU", "Sprite base: %04X", (value & 0x1F) << 9); } else { Log(TLOG_DEBUG, "GPU", "Sprite base: %04X", (value & 0x3F) << 9); } break; case 0x07: if (ws_gpu_operatingInColor) { Log(TLOG_DEBUG, "GPU", "Sprite Screen1 base: %04X", (value & 0x7) << 11); Log(TLOG_DEBUG, "GPU", "Sprite Screen2 base: %04X", (value & 0x70) << (11-4)); } else { Log(TLOG_DEBUG, "GPU", "Sprite Screen1 base: %04X", (value & 0xF) << 11); Log(TLOG_DEBUG, "GPU", "Sprite Screen2 base: %04X", (value & 0xF0) << (11-4)); } break; case 0x10: //Log(TLOG_DEBUG, "GPU", "Sprite Screen1 X scroll: %d", value); break; case 0x11: //Log(TLOG_DEBUG, "GPU", "Sprite Screen1 T scroll: %d", value); break; case 0x12: //Log(TLOG_DEBUG, "GPU", "Sprite Screen2 X scroll: %d", value); break; case 0x13: //Log(TLOG_DEBUG, "GPU", "Sprite Screen2 Y scroll: %d", value); break; case 0x01: case 0x02: case 0x03: case 0x05: case 0x06: case 0x08: case 0x09: case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x14: break; case 0x15: Log(TLOG_DEBUG, "io", "Icons %c %c %c %c %c %c %c %c", (value >> 7) & 1 ? '?' : ' ', (value >> 6) & 1 ? '?' : ' ', (value >> 5) & 1 ? '3' : ' ', (value >> 4) & 1 ? '2' : ' ', (value >> 3) & 1 ? '1' : ' ', (value >> 2) & 1 ? 'H' : ' ', (value >> 1) & 1 ? 'V' : ' ', (value >> 0) & 1 ? 'S' : ' '); break; /* Palettes ? */ case 0x1C: case 0x25: case 0x2F: case 0x38: case 0x1D: case 0x26: case 0x30: case 0x39: case 0x1E: case 0x27: case 0x31: case 0x3A: case 0x1F: case 0x28: case 0x32: case 0x3B: case 0x20: case 0x29: case 0x33: case 0x3C: case 0x21: case 0x2A: case 0x34: case 0x3E: case 0x22: case 0x2B: case 0x35: case 0x3F: case 0x23: case 0x2C: case 0x36: case 0x24: case 0x2E: case 0x37: break; /* DMAs */ case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47: break; case 0x48: // DMA // bit 7 set to start dma transfer if (value & 0x80) { uint32_t dma_start = (ws_ioRam[0x41] << 8) | (ws_ioRam[0x40]) | (ws_ioRam[0x42] << 16); uint32_t dma_dest = (ws_ioRam[0x45] << 8) | (ws_ioRam[0x44]) | (ws_ioRam[0x43] << 16); uint32_t dma_size = (ws_ioRam[0x47] << 8) | (ws_ioRam[0x46]); uint8_t dma_inc = (value & 0x01) ? -1: 1; Log(TLOG_VERBOSE, "DMA", "Starting DMA from %08X to %08X (len: %08X, inc: %d)", dma_start, dma_dest, dma_size, dma_inc); for (uint32_t ix = 0 ; ix < dma_size ; ix++) { mem_writemem20(dma_dest, mem_readmem20(dma_start)); dma_start += dma_inc; dma_dest += dma_inc; } ws_ioRam[0x47] = 0; ws_ioRam[0x46] = 0; ws_ioRam[0x41] = (uint8_t)(dma_start >> 8); ws_ioRam[0x40] = (uint8_t)(dma_start & 0xff); ws_ioRam[0x45] = (uint8_t)(dma_dest >> 8); ws_ioRam[0x44] = (uint8_t)(dma_dest & 0xff); ws_ioRam[0x48] = 0; } break; /* Audio */ case 0x4a: case 0x4b: case 0x4c: case 0x4d: case 0x4e: case 0x4f: ws_audio_port_write(port, value); break; /* DMA Start! */ case 0x52: break; /* GPU (again) */ case 0x60: #ifdef USE_PAGED_MEMORY_ACCESS if (ws_get_system() != WS_SYSTEM_MONO) { if (value & 0x80) { set_iram_access(IRAM_FULL_ACCESS); } else { set_iram_access(IRAM_LIMITED_ACCESS); } } #endif break; /* System */ case 0x62: Log(TLOG_DEBUG, "io", "HeyHo!"); break; /* Audio */ case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87: case 0x88: case 0x89: case 0x8a: case 0x8b: case 0x8c: case 0x8d: case 0x8e: case 0x8f: case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97: case 0x98: case 0x99: case 0x9A: case 0x9B: case 0x9C: case 0x9D: case 0x9E: ws_audio_port_write(port, value); break; /* Hardware */ case 0xA0: /* Force cart handshake to be set */ ws_ioRam[port] |= 0x80; if (value & 0x01) { Log(TLOG_WARNING, "A0", "Oh yeah %02X BABY", value); #ifdef USE_PAGED_MEMORY_ACCESS uint32_t romSize; uint8_t *rom = getRom(&romSize); set_memory_bank(0xF, ws_get_page_ptr(rom, romSize, (ws_ioRam[0xC0] & 0x0F << 4) + 0x0F)); #endif } break; /* Timers */ case 0xA2: case 0xA4: case 0xA5: case 0xA6: case 0xA7: case 0xA8: case 0xA9: case 0xAA: case 0xAB: break; /* Intc */ case 0xB0: case 0xB2: case 0xB4: case 0xB6: break; /* buttons */ case 0xB5: break; /* MBC */ #ifndef USE_PAGED_MEMORY_ACCESS case 0xC0: case 0xC1: case 0xC2: case 0xC3: break; #else case 0xC0: { /* page 4 to F */ uint32_t romSize; uint8_t *rom = getRom(&romSize); for (int i = 0x04 ; i < 0x10 ; i++) { set_memory_bank(i, ws_get_page_ptr(rom, romSize, (value << 4) + i)); } if (!(ws_ioRam[0xA0] & 0x01)) { set_irom_overlay(); } break; } case 0xC1: /* Sram bank */ if (sramSize > 0) { uint32_t sramSize; uint8_t *sram = getSram(&sramSize); set_memory_bank(0x1, ws_get_page_ptr(sram, sramSize, value)); } break; case 0xC2: { /* page 4 to F */ uint32_t romSize; uint8_t *rom = getRom(&romSize); /* Page 2 */ set_memory_bank(0x2, ws_get_page_ptr(rom, romSize, value)); break; } case 0xC3: { /* page 4 to F */ uint32_t romSize; uint8_t *rom = getRom(&romSize); /* Page 3 */ set_memory_bank(0x3, ws_get_page_ptr(rom, romSize, value)); break; } #endif case 0xB7: break; /* Somwthing to write there, but what? */ default: unknown_io_port = 1; } if ((ws_gpu_port_write(port, value) == 1) && (unknown_io_port == 1)) { Log(TLOG_DEBUG, "io", "WriteIO(%02X, %02X) [%04X:%04Xh];", port, value, I.sregs[CS], I.ip); } if (port >= 0xC4) { Log(TLOG_DEBUG, "io", "WriteMBCIO(%02X, %02X) [%04X:%04Xh];", port, value, I.sregs[CS], I.ip); } if (port < 0xC0) { switch(port) { case 0x05: case 0x06: case 0x10: case 0x11: case 0x12: case 0x13: case 0x1C: case 0x1D: case 0x1E: case 0x1F: case 0xB5: case 0xB6: break; default: Log(TLOG_DEBUG, "io", "WriteIO(%02X, %02X) [%04X:%04Xh];", port, value, I.sregs[CS], I.ip); break; } } }