diff --git a/src/ibm.h b/src/ibm.h index 6f1c001..dad81be 100644 --- a/src/ibm.h +++ b/src/ibm.h @@ -451,6 +451,8 @@ enum ROM_PB520R, ROM_COMPAQ_PIP, ROM_XI8088, + ROM_IBMPS2_M70_TYPE3, + ROM_IBMPS2_M70_TYPE4, ROM_MAX }; diff --git a/src/mem.c b/src/mem.c index 429df08..b4bbfd1 100644 --- a/src/mem.c +++ b/src/mem.c @@ -916,6 +916,36 @@ int loadbios() } fclose(f); return 1; + + case ROM_IBMPS2_M70_TYPE3: + f = romfopen("ibmps2_m70_type3/70-a_even.bin","rb"); + ff = romfopen("ibmps2_m70_type3/70-a_odd.bin","rb"); + if (!f || !ff) + break; + for (c = 0x0000; c < 0x20000; c += 2) + { + rom[c] = getc(f); + rom[c+1] = getc(ff); + } + fclose(ff); + fclose(f); + biosmask = 0x1ffff; + return 1; + + case ROM_IBMPS2_M70_TYPE4: + f = romfopen("ibmps2_m70_type4/70-b_even.bin","rb"); + ff = romfopen("ibmps2_m70_type4/70-b_odd.bin","rb"); + if (!f || !ff) + break; + for (c = 0x0000; c < 0x20000; c += 2) + { + rom[c] = getc(f); + rom[c+1] = getc(ff); + } + fclose(ff); + fclose(f); + biosmask = 0x1ffff; + return 1; } printf("Failed to load ROM!\n"); if (f) fclose(f); diff --git a/src/model.c b/src/model.c index 630387b..b8960f7 100644 --- a/src/model.c +++ b/src/model.c @@ -82,6 +82,7 @@ void ps1_m2121_init(); void ps2_m30_286_init(); void ps2_model_50_init(); void ps2_model_55sx_init(); +void ps2_model_70_init(); void ps2_model_80_init(); void at_neat_init(); void at_scat_init(); @@ -169,6 +170,7 @@ MODEL models[] = {"[386DX] AMI 386DX clone", ROM_AMI386DX_OPTI495, "ami386dx", { {"Intel", cpus_i386DX}, {"AMD", cpus_Am386DX}, {"Cyrix", cpus_486DLC}}, MODEL_GFX_NONE|MODEL_AT|MODEL_HAS_IDE, 1, 256, 1, at_opti495_init, NULL}, {"[386DX] Compaq Deskpro 386", ROM_DESKPRO_386, "deskpro386", { {"Intel", cpus_i386DX}, {"AMD", cpus_Am386DX}, {"Cyrix", cpus_486DLC}}, MODEL_GFX_NONE|MODEL_AT, 1, 15, 1, deskpro386_init, NULL}, + {"[386DX] IBM PS/2 Model 70 (type 3)", ROM_IBMPS2_M70_TYPE3, "ibmps2_m70_type3", { {"Intel", cpus_i386DX}, {"AMD", cpus_Am386DX}, {"Cyrix", cpus_486DLC}}, MODEL_GFX_FIXED|MODEL_AT|MODEL_PS2|MODEL_MCA, 2, 16, 2, ps2_model_70_init, NULL}, {"[386DX] IBM PS/2 Model 80", ROM_IBMPS2_M80, "ibmps2_m80", { {"Intel", cpus_i386DX}, {"AMD", cpus_Am386DX}, {"Cyrix", cpus_486DLC}}, MODEL_GFX_FIXED|MODEL_AT|MODEL_PS2|MODEL_MCA, 1, 12, 1, ps2_model_80_init, NULL}, {"[386DX] MR 386DX clone", ROM_MR386DX_OPTI495, "mr386dx", { {"Intel", cpus_i386DX}, {"AMD", cpus_Am386DX}, {"Cyrix", cpus_486DLC}}, MODEL_GFX_NONE|MODEL_AT|MODEL_HAS_IDE, 1, 256, 1, at_opti495_init, NULL}, @@ -176,7 +178,8 @@ MODEL models[] = {"[486] AMI WinBIOS 486", ROM_WIN486, "win486", { {"Intel", cpus_i486}, {"AMD", cpus_Am486}, {"Cyrix", cpus_Cx486}}, MODEL_GFX_NONE|MODEL_AT|MODEL_HAS_IDE, 1, 256, 1, at_ali1429_init, NULL}, {"[486] Award SiS 496/497", ROM_SIS496, "sis496", { {"Intel", cpus_i486}, {"AMD", cpus_Am486}, {"Cyrix", cpus_Cx486}}, MODEL_GFX_NONE|MODEL_AT|MODEL_PCI|MODEL_HAS_IDE, 1, 256, 1, at_sis496_init, NULL}, {"[486] Elonex PC-425X", ROM_ELX_PC425X, "elx_pc425x", { {"Intel", cpus_i486}, {"AMD", cpus_Am486}, {"Cyrix", cpus_Cx486}}, MODEL_GFX_FIXED|MODEL_AT|MODEL_HAS_IDE, 1, 256, 1, at_sl82c460_init, NULL}, - + {"[486] IBM PS/2 Model 70 (type 4)", ROM_IBMPS2_M70_TYPE4, "ibmps2_m70_type4", { {"Intel", cpus_i486}, {"AMD", cpus_Am486}, {"Cyrix", cpus_Cx486}}, MODEL_GFX_FIXED|MODEL_AT|MODEL_PS2|MODEL_MCA, 2, 16, 2, ps2_model_70_init, NULL}, + {"[Socket 4] Intel Premiere/PCI", ROM_REVENGE, "revenge", { {"Intel", cpus_Pentium5V}, {"", NULL}, {"", NULL}}, MODEL_GFX_NONE|MODEL_AT|MODEL_PCI|MODEL_PS2|MODEL_HAS_IDE, 1, 128, 1, at_batman_init, NULL}, {"[Socket 4] Packard Bell PB520R",ROM_PB520R, "pb520r", { {"Intel", cpus_Pentium5V}, {"", NULL}, {"", NULL}}, MODEL_GFX_DISABLE_SW|MODEL_AT|MODEL_PCI|MODEL_PS2|MODEL_HAS_IDE, 1, 128, 1, at_pb520r_init, NULL}, @@ -493,6 +496,12 @@ void ps2_model_55sx_init() ps2_mca_board_model_55sx_init(); } +void ps2_model_70_init() +{ + ps2_common_init(); + ps2_mca_board_model_70_type34_init(romset == ROM_IBMPS2_M70_TYPE4); +} + void ps2_model_80_init() { ps2_common_init(); diff --git a/src/nvr.c b/src/nvr.c index 4241c43..5e44fa6 100644 --- a/src/nvr.c +++ b/src/nvr.c @@ -305,7 +305,9 @@ void loadnvr() case ROM_ZAPPA: f = nvrfopen("zappa.nvr", "rb"); nvrmask = 127; break; case ROM_PB520R: f = nvrfopen("pb520r.nvr", "rb"); nvrmask = 127; break; case ROM_XI8088: f = nvrfopen("xi8088.nvr", "rb"); nvrmask = 127; break; - + case ROM_IBMPS2_M70_TYPE3: f = nvrfopen("ibmps2_m70_type3.nvr","rb"); break; + case ROM_IBMPS2_M70_TYPE4: f = nvrfopen("ibmps2_m70_type4.nvr","rb"); break; + default: return; } if (!f) @@ -392,7 +394,9 @@ void savenvr() case ROM_ZAPPA: f = nvrfopen("zappa.nvr", "wb"); break; case ROM_PB520R: f = nvrfopen("pb520r.nvr", "wb"); break; case ROM_XI8088: f = nvrfopen("xi8088.nvr", "wb"); break; - + case ROM_IBMPS2_M70_TYPE3: f = nvrfopen("ibmps2_m70_type3.nvr","wb"); break; + case ROM_IBMPS2_M70_TYPE4: f = nvrfopen("ibmps2_m70_type4.nvr","wb"); break; + default: return; } fwrite(nvrram,128,1,f); diff --git a/src/ps2_mca.c b/src/ps2_mca.c index c482ab5..5113642 100644 --- a/src/ps2_mca.c +++ b/src/ps2_mca.c @@ -35,6 +35,7 @@ static struct mem_mapping_t shadow_mapping; mem_mapping_t split_mapping; mem_mapping_t expansion_mapping; + mem_mapping_t cache_mapping; uint8_t (*planar_read)(uint16_t port); void (*planar_write)(uint16_t port, uint8_t val); @@ -44,8 +45,94 @@ static struct uint32_t split_addr; uint8_t mem_pos_regs[8]; + + int pending_cache_miss; } ps2; +/*The model 70 type 3/4 BIOS performs cache testing. Since PCem doesn't have any + proper cache emulation, it's faked a bit here. + + Port E2 is used for cache diagnostics. Bit 7 seems to be set on a cache miss, + toggling bit 2 seems to clear this. The BIOS performs at least the following + tests : + + - Disable RAM, access low 64kb (386) / 8kb (486), execute code from cache to + access low memory and verify that there are no cache misses. + - Write to low memory using DMA, read low memory and verify that all accesses + cause cache misses. + - Read low memory, verify that first access is cache miss. Read again and + verify that second access is cache hit. + + These tests are also performed on the 486 model 70, despite there being no + external cache on this system. Port E2 seems to control the internal cache on + these systems. Presumably this port is connected to KEN#/FLUSH# on the 486. + This behaviour is required to pass the timer interrupt test on the 486 version + - the BIOS uses a fixed length loop that will terminate too early on a 486/25 + if it executes from internal cache. + + To handle this, PCem uses some basic heuristics : + - If cache is enabled but RAM is disabled, accesses to low memory go directly + to cache memory. + - Reads to cache addresses not 'valid' will set the cache miss flag, and mark + that line as valid. + - Cache flushes will clear the valid array. + - DMA via the undocumented PS/2 command 0xb will clear the valid array. + - Disabling the cache will clear the valid array. + - Disabling the cache will also mark shadowed ROM areas as using ROM timings. + This works around the timing loop mentioned above. +*/ +static uint8_t ps2_cache[65536]; +static int ps2_cache_valid[65536/8]; + +static uint8_t ps2_read_cache_ram(uint32_t addr, void *priv) +{ +// pclog("ps2_read_cache_ram: addr=%08x %i %04x:%04x\n", addr, ps2_cache_valid[addr >> 3], CS,cpu_state.pc); + if (!ps2_cache_valid[addr >> 3]) + { + ps2_cache_valid[addr >> 3] = 1; + ps2.mem_regs[2] |= 0x80; + } + else + ps2.pending_cache_miss = 0; + + return ps2_cache[addr]; +} +static uint16_t ps2_read_cache_ramw(uint32_t addr, void *priv) +{ +// pclog("ps2_read_cache_ramw: addr=%08x %i %04x:%04x\n", addr, ps2_cache_valid[addr >> 3], CS,cpu_state.pc); + if (!ps2_cache_valid[addr >> 3]) + { + ps2_cache_valid[addr >> 3] = 1; + ps2.mem_regs[2] |= 0x80; + } + else + ps2.pending_cache_miss = 0; + + return *(uint16_t *)&ps2_cache[addr]; +} +static uint32_t ps2_read_cache_raml(uint32_t addr, void *priv) +{ +// pclog("ps2_read_cache_raml: addr=%08x %i %04x:%04x\n", addr, ps2_cache_valid[addr >> 3], CS,cpu_state.pc); + if (!ps2_cache_valid[addr >> 3]) + { + ps2_cache_valid[addr >> 3] = 1; + ps2.mem_regs[2] |= 0x80; + } + else + ps2.pending_cache_miss = 0; + + return *(uint32_t *)&ps2_cache[addr]; +} +static void ps2_write_cache_ram(uint32_t addr, uint8_t val, void *priv) +{ +// pclog("ps2_write_cache_ram: addr=%08x val=%02x %04x:%04x %i\n", addr, val, CS,cpu_state.pc, ins); + ps2_cache[addr] = val; +} + +void ps2_cache_clean() +{ + memset(ps2_cache_valid, 0, sizeof(ps2_cache_valid)); +} static uint8_t ps2_read_shadow_ram(uint32_t addr, void *priv) { @@ -164,6 +251,30 @@ static uint8_t model_55sx_read(uint16_t port) return 0xff; } +static uint8_t model_70_type3_read(uint16_t port) +{ + switch (port) + { + case 0x100: + return 0xff; + case 0x101: + return 0xf9; + case 0x102: + return ps2.option[0]; + case 0x103: + return ps2.option[1]; + case 0x104: + return ps2.option[2]; + case 0x105: + return ps2.option[3]; + case 0x106: + return ps2.subaddr_lo; + case 0x107: + return ps2.subaddr_hi; + } + return 0xff; +} + static uint8_t model_80_read(uint16_t port) { switch (port) @@ -321,6 +432,55 @@ static void model_55sx_write(uint16_t port, uint8_t val) } } +static void model_70_type3_write(uint16_t port, uint8_t val) +{ + switch (port) + { + case 0x100: + break; + case 0x101: + break; + case 0x102: + lpt1_remove(); + serial1_remove(); + if (val & 0x04) + { + if (val & 0x08) + serial1_init(0x3f8, 4); + else + serial1_init(0x2f8, 3); + } + else + serial1_remove(); + if (val & 0x10) + { + switch ((val >> 5) & 3) + { + case 0: + lpt1_init(0x3bc); + break; + case 1: + lpt1_init(0x378); + break; + case 2: + lpt1_init(0x278); + break; + } + } + ps2.option[0] = val; + break; + case 0x105: + ps2.option[3] = val; + break; + case 0x106: + ps2.subaddr_lo = val; + break; + case 0x107: + ps2.subaddr_hi = val; + break; + } +} + static void model_80_write(uint16_t port, uint8_t val) { switch (port) @@ -469,14 +629,14 @@ uint8_t ps2_mca_read(uint16_t port, void *p) break; } - pclog("ps2_read: port=%04x temp=%02x\n", port, temp); +// pclog("ps2_read: port=%04x temp=%02x %04x:%04x %i\n", port, temp, CS, cpu_state.pc, ins); return temp; } static void ps2_mca_write(uint16_t port, uint8_t val, void *p) { - pclog("ps2_write: port=%04x val=%02x %04x:%04x\n", port, val, CS,cpu_state.pc); +// pclog("ps2_write: port=%04x val=%02x %04x:%04x\n", port, val, CS,cpu_state.pc); switch (port) { @@ -685,6 +845,70 @@ static void mem_encoding_write(uint16_t addr, uint8_t val, void *p) mem_encoding_update(); } +static uint8_t mem_encoding_read_cached(uint16_t addr, void *p) +{ + switch (addr) + { + case 0xe0: + return ps2.mem_regs[0]; + case 0xe1: + return ps2.mem_regs[1]; + case 0xe2: + return ps2.mem_regs[2]; + } + return 0xff; +} +static void mem_encoding_write_cached(uint16_t addr, uint8_t val, void *p) +{ + uint8_t old; + + switch (addr) + { + case 0xe0: + ps2.mem_regs[0] = val; + break; + case 0xe1: + ps2.mem_regs[1] = val; + break; + case 0xe2: + old = ps2.mem_regs[2]; + ps2.mem_regs[2] = (ps2.mem_regs[2] & 0x80) | (val & ~0x88); + if (val & 2) + { +// pclog("Clear latch - %i\n", ps2.pending_cache_miss); + if (ps2.pending_cache_miss) + ps2.mem_regs[2] |= 0x80; + else + ps2.mem_regs[2] &= ~0x80; + ps2.pending_cache_miss = 0; + } + + if ((val & 0x21) == 0x20 && (old & 0x21) != 0x20) + ps2.pending_cache_miss = 1; + if ((val & 0x21) == 0x01 && (old & 0x21) != 0x01) + ps2_cache_clean(); + if (val & 0x01) + ram_mid_mapping.flags |= MEM_MAPPING_ROM; + else + ram_mid_mapping.flags &= ~MEM_MAPPING_ROM; + break; + } +// pclog("mem_encoding_write: addr=%02x val=%02x %04x:%04x %02x %02x\n", addr, val, CS,cpu_state.pc, ps2.mem_regs[1],ps2.mem_regs[2]); + mem_encoding_update(); + if ((ps2.mem_regs[1] & 0x10) && (ps2.mem_regs[2] & 0x21) == 0x20) + { + mem_mapping_disable(&ram_low_mapping); + mem_mapping_enable(&ps2.cache_mapping); + flushmmucache(); + } + else + { + mem_mapping_disable(&ps2.cache_mapping); + mem_mapping_enable(&ram_low_mapping); + flushmmucache(); + } +} + static uint8_t ps2_mem_expansion_read(int port, void *p) { return ps2.mem_pos_regs[port & 7]; @@ -703,6 +927,116 @@ static void ps2_mem_expansion_write(int port, uint8_t val, void *p) mem_mapping_disable(&ps2.expansion_mapping); } +void ps2_mca_board_model_70_type34_init(int is_type4) +{ + ps2_mca_board_common_init(); + + mem_remap_top_256k(); + ps2.split_addr = mem_size * 1024; + mca_init(4); + + ps2.planar_read = model_70_type3_read; + ps2.planar_write = model_70_type3_write; + + device_add(&ps2_nvr_device); + + io_sethandler(0x00e0, 0x0003, mem_encoding_read_cached, NULL, NULL, mem_encoding_write_cached, NULL, NULL, NULL); + + ps2.mem_regs[1] = 2; + + switch (mem_size/1024) + { + case 2: + ps2.option[1] = 0xa6; + ps2.option[2] = 0x01; + break; + case 4: + ps2.option[1] = 0xaa; + ps2.option[2] = 0x01; + break; + case 6: + ps2.option[1] = 0xca; + ps2.option[2] = 0x01; + break; + case 8: + default: + ps2.option[1] = 0xca; + ps2.option[2] = 0x02; + break; + } + + if (is_type4) + ps2.option[2] |= 0x04; /*486 CPU*/ + + mem_mapping_add(&ps2.split_mapping, + (mem_size+256) * 1024, + 256*1024, + ps2_read_split_ram, + ps2_read_split_ramw, + ps2_read_split_raml, + ps2_write_split_ram, + ps2_write_split_ramw, + ps2_write_split_raml, + &ram[0xa0000], + MEM_MAPPING_INTERNAL, + NULL); + mem_mapping_disable(&ps2.split_mapping); + + mem_mapping_add(&ps2.cache_mapping, + 0, + is_type4 ? (8 * 1024) : (64 * 1024), + ps2_read_cache_ram, + ps2_read_cache_ramw, + ps2_read_cache_raml, + ps2_write_cache_ram, + NULL, + NULL, + ps2_cache, + MEM_MAPPING_INTERNAL, + NULL); + mem_mapping_disable(&ps2.cache_mapping); + + if (mem_size > 8192) + { + /* Only 8 MB supported on planar, create a memory expansion card for the rest */ + mem_mapping_set_addr(&ram_high_mapping, 0x100000, 0x700000); + + ps2.mem_pos_regs[0] = 0xff; + ps2.mem_pos_regs[1] = 0xfc; + + switch (mem_size/1024) + { + case 10: + ps2.mem_pos_regs[4] = 0xfe; + break; + case 12: + ps2.mem_pos_regs[4] = 0xfa; + break; + case 14: + ps2.mem_pos_regs[4] = 0xea; + break; + case 16: + ps2.mem_pos_regs[4] = 0xaa; + break; + } + + mca_add(ps2_mem_expansion_read, ps2_mem_expansion_write, NULL); + mem_mapping_add(&ps2.expansion_mapping, + 0x800000, + (mem_size - 8192)*1024, + mem_read_ram, + mem_read_ramw, + mem_read_raml, + mem_write_ram, + mem_write_ramw, + mem_write_raml, + &ram[0x800000], + MEM_MAPPING_INTERNAL, + NULL); + mem_mapping_disable(&ps2.expansion_mapping); + } +} + void ps2_mca_board_model_80_type2_init() { ps2_mca_board_common_init(); diff --git a/src/ps2_mca.h b/src/ps2_mca.h index d7a2e13..9e20914 100644 --- a/src/ps2_mca.h +++ b/src/ps2_mca.h @@ -1,3 +1,6 @@ void ps2_mca_board_model_50_init(); void ps2_mca_board_model_55sx_init(); +void ps2_mca_board_model_70_type34_init(int is_type4); void ps2_mca_board_model_80_type2_init(); + +void ps2_cache_clean(); diff --git a/src/ps2_nvr.c b/src/ps2_nvr.c index ed1896b..50829fb 100644 --- a/src/ps2_nvr.c +++ b/src/ps2_nvr.c @@ -58,7 +58,9 @@ static void *ps2_nvr_init() switch (romset) { - case ROM_IBMPS2_M80: f = nvrfopen("ibmps2_m80_sec.nvr", "rb"); break; + case ROM_IBMPS2_M70_TYPE3:f = nvrfopen("ibmps2_m70_type3_sec.nvr", "rb"); break; + case ROM_IBMPS2_M70_TYPE4:f = nvrfopen("ibmps2_m70_type4_sec.nvr", "rb"); break; + case ROM_IBMPS2_M80: f = nvrfopen("ibmps2_m80_sec.nvr", "rb"); break; } if (f) { @@ -78,7 +80,9 @@ static void ps2_nvr_close(void *p) switch (romset) { - case ROM_IBMPS2_M80: f = nvrfopen("ibmps2_m80_sec.nvr", "wb"); break; + case ROM_IBMPS2_M70_TYPE3:f = nvrfopen("ibmps2_m70_type3_sec.nvr", "wb"); break; + case ROM_IBMPS2_M70_TYPE4:f = nvrfopen("ibmps2_m70_type4_sec.nvr", "wb"); break; + case ROM_IBMPS2_M80: f = nvrfopen("ibmps2_m80_sec.nvr", "wb"); break; } if (f) { diff --git a/src/video.c b/src/video.c index 0c923b4..3a97dba 100644 --- a/src/video.c +++ b/src/video.c @@ -195,6 +195,8 @@ device_t *video_card_getdevice(int card) case ROM_IBMPS2_M30_286: case ROM_IBMPS2_M50: case ROM_IBMPS2_M55SX: + case ROM_IBMPS2_M70_TYPE3: + case ROM_IBMPS2_M70_TYPE4: case ROM_IBMPS2_M80: return &ps1vga_device; @@ -316,6 +318,8 @@ int video_is_mda() case ROM_IBMPS2_M30_286: case ROM_IBMPS2_M50: case ROM_IBMPS2_M55SX: + case ROM_IBMPS2_M70_TYPE3: + case ROM_IBMPS2_M70_TYPE4: case ROM_IBMPS2_M80: case ROM_IBMPS1_2121: case ROM_T3100E: @@ -355,6 +359,8 @@ int video_is_cga() case ROM_IBMPS2_M30_286: case ROM_IBMPS2_M50: case ROM_IBMPS2_M55SX: + case ROM_IBMPS2_M70_TYPE3: + case ROM_IBMPS2_M70_TYPE4: case ROM_IBMPS2_M80: case ROM_IBMPS1_2121: case ROM_ELX_PC425X: @@ -388,6 +394,8 @@ int video_is_ega_vga() case ROM_IBMPS2_M30_286: case ROM_IBMPS2_M50: case ROM_IBMPS2_M55SX: + case ROM_IBMPS2_M70_TYPE3: + case ROM_IBMPS2_M70_TYPE4: case ROM_IBMPS2_M80: case ROM_IBMPS1_2121: case ROM_ELX_PC425X: @@ -518,6 +526,8 @@ void video_updatetiming() case ROM_IBMPS2_M30_286: case ROM_IBMPS2_M50: case ROM_IBMPS2_M55SX: + case ROM_IBMPS2_M70_TYPE3: + case ROM_IBMPS2_M70_TYPE4: case ROM_IBMPS2_M80: timing = &timing_vga; break; @@ -683,6 +693,8 @@ void video_init() case ROM_IBMPS2_M30_286: case ROM_IBMPS2_M50: case ROM_IBMPS2_M55SX: + case ROM_IBMPS2_M70_TYPE3: + case ROM_IBMPS2_M70_TYPE4: case ROM_IBMPS2_M80: device_add(&ps1vga_device); return;