From 38fe1ce7c4587ad8f0398f52df4504e6c76fe546 Mon Sep 17 00:00:00 2001 From: Jasmine Iwanek Date: Wed, 29 Sep 2021 04:41:46 -0400 Subject: [PATCH 01/56] FIC 486-VIP-IO2 supports PS/2 Mouse --- src/machine/machine_table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index 4f65bb383..115e5130e 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -242,7 +242,7 @@ const machine_t machines[] = { { "[UMC 8881] A-Trend ATC-1415", "atc1415", MACHINE_TYPE_486_S3, CPU_PKG_SOCKET3, 0, 0, 0, 0, 0, 0, 0, MACHINE_PCI | MACHINE_IDE_DUAL, 1024, 65536, 1024, 255, machine_at_atc1415_init, NULL }, { "[UMC 8881] ECS Elite UM8810PAIO", "ecs486", MACHINE_TYPE_486_S3, CPU_PKG_SOCKET3, 0, 0, 0, 0, 0, 0, 0, MACHINE_PCI | MACHINE_IDE_DUAL, 1024, 131072, 1024, 255, machine_at_ecs486_init, NULL }, { "[UMC 8881] Shuttle HOT-433A", "hot433", MACHINE_TYPE_486_S3, CPU_PKG_SOCKET3, 0, 0, 0, 0, 0, 0, 0, MACHINE_PCI | MACHINE_IDE_DUAL, 1024, 262144, 1024, 255, machine_at_hot433_init, NULL }, - { "[VIA VT82C496G] FIC VIP-IO2", "486vipio2", MACHINE_TYPE_486_S3, CPU_PKG_SOCKET3, 0, 0, 0, 0, 0, 0, 0, MACHINE_PCIV | MACHINE_IDE_DUAL, 1024, 131072, 1024, 255, machine_at_486vipio2_init, NULL }, + { "[VIA VT82C496G] FIC VIP-IO2", "486vipio2", MACHINE_TYPE_486_S3, CPU_PKG_SOCKET3, 0, 0, 0, 0, 0, 0, 0, MACHINE_PCIV | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 1024, 131072, 1024, 255, machine_at_486vipio2_init, NULL }, /* 486 machines - Miscellaneous */ /* 486 machines with just the ISA slot */ From 2ba21b3b86fcbe82ab5abc68439f84cd77f55a4c Mon Sep 17 00:00:00 2001 From: Jasmine Iwanek Date: Wed, 29 Sep 2021 05:03:27 -0400 Subject: [PATCH 02/56] support FDC_INTERNAL on 486-VIP-IO2 --- src/machine/m_at_386dx_486.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/machine/m_at_386dx_486.c b/src/machine/m_at_386dx_486.c index 03ba605f3..f5c7107f4 100644 --- a/src/machine/m_at_386dx_486.c +++ b/src/machine/m_at_386dx_486.c @@ -1105,6 +1105,9 @@ machine_at_486vipio2_init(const machine_t *model) device_add(&keyboard_ps2_ami_pci_device); device_add(&sst_flash_29ee010_device); + if (fdc_type == FDC_INTERNAL) + device_add(&fdc_at_device); + return ret; } From 734d7505ffc3530da8628fa4d9cbbb492cb2408e Mon Sep 17 00:00:00 2001 From: Jasmine Iwanek Date: Wed, 29 Sep 2021 14:59:54 -0400 Subject: [PATCH 03/56] Revert "support FDC_INTERNAL on 486-VIP-IO2" This reverts commit 2ba21b3b86fcbe82ab5abc68439f84cd77f55a4c. --- src/machine/m_at_386dx_486.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/machine/m_at_386dx_486.c b/src/machine/m_at_386dx_486.c index f5c7107f4..03ba605f3 100644 --- a/src/machine/m_at_386dx_486.c +++ b/src/machine/m_at_386dx_486.c @@ -1105,9 +1105,6 @@ machine_at_486vipio2_init(const machine_t *model) device_add(&keyboard_ps2_ami_pci_device); device_add(&sst_flash_29ee010_device); - if (fdc_type == FDC_INTERNAL) - device_add(&fdc_at_device); - return ret; } From 8572739a551d63fff670bc18a893fabc1b0f6116 Mon Sep 17 00:00:00 2001 From: TC1995 Date: Tue, 5 Oct 2021 22:24:26 +0200 Subject: [PATCH 04/56] S3 changes part 8: Properly fixed text/graphics mode of the S3 pre-ViRGE cards. PIX TRANS cleanups. Added a sanity check to banking, per bit 0 of CRTC31. Initial implementation of Enhanced 4-bit mode. 911/924 chips use CRTC43 bit 3 for enabling 15/16bpp mode. fb_only variable used correctly as of now (depending if on 4bit or 8bit+ modes). S3 ViRGE changes part 2: Made the Trio3D/2X AGP use the PCI_ADD_AGP part accordingly RAMDAC changes: Sierra 11483 and 11487 don't have an RS2 signal so the four times reading scheme of 0x3c6 is used instead, per documentation. Fixed AT&T 49x bpp selection. Other changes: Fixed remaining rendering issues with the Radius SVGA HT209 card. --- src/video/vid_att20c49x_ramdac.c | 4 +- src/video/vid_ht216.c | 90 ++++++----- src/video/vid_s3.c | 269 +++++++++++++++---------------- src/video/vid_s3_virge.c | 2 +- src/video/vid_sc1148x_ramdac.c | 149 ++++++----------- 5 files changed, 224 insertions(+), 290 deletions(-) diff --git a/src/video/vid_att20c49x_ramdac.c b/src/video/vid_att20c49x_ramdac.c index ce6379f1d..4c09f1e8f 100644 --- a/src/video/vid_att20c49x_ramdac.c +++ b/src/video/vid_att20c49x_ramdac.c @@ -50,7 +50,7 @@ att49x_ramdac_control(uint8_t val, void *p, svga_t *svga) { att49x_ramdac_t *ramdac = (att49x_ramdac_t *) p; ramdac->ctrl = val; - switch (ramdac->ctrl >> 4) { + switch ((ramdac->ctrl >> 5) & 7) { case 0: case 1: case 2: @@ -62,11 +62,9 @@ att49x_ramdac_control(uint8_t val, void *p, svga_t *svga) svga->bpp = 15; break; case 6: - case 0xc: svga->bpp = 16; break; case 7: - case 0xe: svga->bpp = 24; break; } diff --git a/src/video/vid_ht216.c b/src/video/vid_ht216.c index 3f12bdfdc..8264f30ca 100644 --- a/src/video/vid_ht216.c +++ b/src/video/vid_ht216.c @@ -618,52 +618,62 @@ ht216_recalctimings(svga_t *svga) ht216->adjust_cursor = 0; - if (svga->crtc[0x17] == 0xeb) { - svga->rowoffset <<= 1; - svga->render = svga_render_2bpp_headland_highres; - } - - if (svga->bpp == 8) { - ht216_log("regC8 = %02x, gdcreg5 bit 6 = %02x, no lowres = %02x, regf8 bit 7 = %02x, regfc = %02x\n", ht216->ht_regs[0xc8] & HT_REG_C8_E256, svga->gdcreg[5] & 0x40, !svga->lowres, ht216->ht_regs[0xf6] & 0x80, ht216->ht_regs[0xfc] & HT_REG_FC_ECOLRE); - if (((ht216->ht_regs[0xc8] & HT_REG_C8_E256) || (svga->gdcreg[5] & 0x40)) && (!svga->lowres || (ht216->ht_regs[0xf6] & 0x80))) { - if (high_res_256) { - svga->hdisp >>= 1; - ht216->adjust_cursor = 1; - } - svga->render = svga_render_8bpp_highres; - } else if (svga->lowres) { - if (high_res_256) { - svga->hdisp >>= 1; - ht216->adjust_cursor = 1; - svga->render = svga_render_8bpp_highres; + if (!svga->scrblank && svga->attr_palette_enable) { + if (!(svga->gdcreg[6] & 1) && !(svga->attrregs[0x10] & 1)) { /*Text mode*/ + if (svga->seqregs[1] & 8) /*40 column*/ { + svga->render = svga_render_text_40; } else { - ht216_log("8bpp low, packed = %02x, chain4 = %02x\n", svga->packed_chain4, svga->chain4); - svga->render = svga_render_8bpp_lowres; + svga->render = svga_render_text_80; } - } else if (ht216->ht_regs[0xfc] & HT_REG_FC_ECOLRE) { - if (ht216->id == 0x7152) { - svga->hdisp = svga->crtc[1] - ((svga->crtc[5] & 0x60) >> 5); - if (!(svga->crtc[1] & 1)) - svga->hdisp--; - svga->hdisp++; - svga->hdisp *= (svga->seqregs[1] & 8) ? 16 : 8; - svga->rowoffset <<= 1; - if ((svga->crtc[0x17] & 0x60) == 0x20) /*Would result in a garbled screen with trailing cursor glitches*/ - svga->force_byte_mode = 1; - else - svga->force_byte_mode = 0; - } - svga->render = svga_render_8bpp_highres; - } - } else if (svga->bpp == 15) { + } else { + if (svga->crtc[0x17] == 0xeb) { svga->rowoffset <<= 1; - svga->hdisp >>= 1; - svga->render = svga_render_15bpp_highres; - } + svga->render = svga_render_2bpp_headland_highres; + } + if (svga->bpp == 8) { + ht216_log("regC8 = %02x, gdcreg5 bit 6 = %02x, no lowres = %02x, regf8 bit 7 = %02x, regfc = %02x\n", ht216->ht_regs[0xc8] & HT_REG_C8_E256, svga->gdcreg[5] & 0x40, !svga->lowres, ht216->ht_regs[0xf6] & 0x80, ht216->ht_regs[0xfc] & HT_REG_FC_ECOLRE); + if (((ht216->ht_regs[0xc8] & HT_REG_C8_E256) || (svga->gdcreg[5] & 0x40)) && (!svga->lowres || (ht216->ht_regs[0xf6] & 0x80))) { + if (high_res_256) { + svga->hdisp >>= 1; + ht216->adjust_cursor = 1; + } + svga->render = svga_render_8bpp_highres; + } else if (svga->lowres) { + if (high_res_256) { + svga->hdisp >>= 1; + ht216->adjust_cursor = 1; + svga->render = svga_render_8bpp_highres; + } else { + ht216_log("8bpp low, packed = %02x, chain4 = %02x\n", svga->packed_chain4, svga->chain4); + svga->render = svga_render_8bpp_lowres; + } + } else if (ht216->ht_regs[0xfc] & HT_REG_FC_ECOLRE) { + if (ht216->id == 0x7152) { + svga->hdisp = svga->crtc[1] - ((svga->crtc[5] & 0x60) >> 5); + if (!(svga->crtc[1] & 1)) + svga->hdisp--; + svga->hdisp++; + svga->hdisp *= (svga->seqregs[1] & 8) ? 16 : 8; + svga->rowoffset <<= 1; + if ((svga->crtc[0x17] & 0x60) == 0x20) /*Would result in a garbled screen with trailing cursor glitches*/ + svga->crtc[0x17] |= 0x40; + } + svga->render = svga_render_8bpp_highres; + } + } else if (svga->bpp == 15) { + svga->rowoffset <<= 1; + svga->hdisp >>= 1; + if ((svga->crtc[0x17] & 0x60) == 0x20) /*Would result in a garbled screen with trailing cursor glitches*/ + svga->crtc[0x17] |= 0x40; + svga->render = svga_render_15bpp_highres; + } + } + } + svga->ma_latch |= ((ht216->ht_regs[0xf6] & 0x30) << 14); - if (svga->crtc[0x17] == 0xeb) /*Looks like that 1024x768 mono mode expects 512K of video memory*/ + if (svga->crtc[0x17] == 0xeb) /*Looks like 1024x768 mono mode expects 512K of video memory*/ svga->vram_display_mask = 0x7ffff; else svga->vram_display_mask = (ht216->ht_regs[0xf6] & 0x40) ? ht216->vram_mask : 0x3ffff; diff --git a/src/video/vid_s3.c b/src/video/vid_s3.c index f305a2f59..d516ebe70 100644 --- a/src/video/vid_s3.c +++ b/src/video/vid_s3.c @@ -385,7 +385,8 @@ static uint32_t s3_accel_in_l(uint16_t port, void *p); static uint8_t s3_pci_read(int func, int addr, void *p); static void s3_pci_write(int func, int addr, uint8_t val, void *p); -/*Remap address for chain-4/doubleword style layout*/ +/*Remap address for chain-4/doubleword style layout. + These will stay for convenience.*/ static __inline uint32_t dword_remap(svga_t *svga, uint32_t in_addr) { @@ -516,8 +517,8 @@ static void s3_accel_out_pixtrans_w(s3_t *s3, uint16_t val) { svga_t *svga = &s3->svga; - - if (s3->accel.cmd & 0x100) { + + if (s3_cpu_src(s3)) { switch (s3->accel.cmd & 0x600) { case 0x000: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { @@ -583,7 +584,7 @@ s3_accel_out_pixtrans_w(s3_t *s3, uint16_t val) static void s3_accel_out_pixtrans_l(s3_t *s3, uint32_t val) { - if (s3->accel.cmd & 0x100) { + if (s3_cpu_src(s3)) { switch (s3->accel.cmd & 0x600) { case 0x000: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { @@ -1276,7 +1277,7 @@ static void s3_accel_out_fifo_w(s3_t *s3, uint16_t port, uint16_t val) { if (port != 0x9ee8 && port != 0x9d48) { - if (port == 0xb2e8) { + if (port == 0xb2e8 || port == 0xb148) { s3->accel.b2e8_pix = 1; } else { s3->accel.b2e8_pix = 0; @@ -1305,7 +1306,7 @@ s3_accel_out_fifo_w(s3_t *s3, uint16_t port, uint16_t val) static void s3_accel_out_fifo_l(s3_t *s3, uint16_t port, uint32_t val) { - if (port == 0xb2e8) { + if (port == 0xb2e8 || port == 0xb148) { s3->accel.b2e8_pix = 1; } else { s3->accel.b2e8_pix = 0; @@ -1422,15 +1423,7 @@ s3_accel_write_fifo(s3_t *s3, uint32_t addr, uint8_t val) if (svga->crtc[0x53] & 0x08) { if ((addr & 0x1ffff) < 0x8000) { - if (s3->accel.cmd & 0x100) { - if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { - if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) - s3_accel_start(8, 1, val | (val << 8) | (val << 16) | (val << 24), 0, s3); - else - s3_accel_start(1, 1, 0xffffffff, val | (val << 8) | (val << 16) | (val << 24), s3); - } else - s3_accel_start(1, 1, 0xffffffff, val | (val << 8) | (val << 16) | (val << 24), s3); - } + s3_accel_out_fifo(s3, 0xe2e8 + (addr & 3), val); } else { switch (addr & 0x1ffff) { case 0x83b0: case 0x83b1: case 0x83b2: case 0x83b3: @@ -1472,25 +1465,7 @@ s3_accel_write_fifo(s3_t *s3, uint32_t addr, uint8_t val) if (addr & 0x8000) { s3_accel_out_fifo(s3, addr & 0xffff, val); } else { - if (s3->accel.cmd & 0x100) { - if ((s3->accel.cmd & 0x600) == 0x200) { - if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { - if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) - s3_accel_start(16, 1, val | (val << 8) | (val << 16) | (val << 24), 0, s3); - else - s3_accel_start(2, 1, 0xffffffff, val | (val << 8) | (val << 16) | (val << 24), s3); - } else - s3_accel_start(2, 1, 0xffffffff, val | (val << 8) | (val << 16) | (val << 24), s3); - } else { - if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { - if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) - s3_accel_start(8, 1, val | (val << 8) | (val << 16) | (val << 24), 0, s3); - else - s3_accel_start(1, 1, 0xffffffff, val | (val << 8) | (val << 16) | (val << 24), s3); - } else - s3_accel_start(1, 1, 0xffffffff, val | (val << 8) | (val << 16) | (val << 24), s3); - } - } + s3_accel_out_fifo(s3, 0xe2e8 + (addr & 3), val); } } } @@ -2383,10 +2358,13 @@ s3_out(uint16_t addr, uint8_t val, void *p) } if (svga->seqaddr == 4) /*Chain-4 - update banking*/ { - if (val & 8) - svga->write_bank = svga->read_bank = s3->bank << 16; - else - svga->write_bank = svga->read_bank = s3->bank << 14; + svga->chain4 = !!(val & 8); + if (svga->crtc[0x31] & 1) { + if (svga->chain4) + svga->write_bank = svga->read_bank = s3->bank << 16; + else + svga->write_bank = svga->read_bank = s3->bank << 14; + } } else if (svga->seqaddr == 9) { svga->seqregs[svga->seqaddr] = val & 0x80; s3_io_set(s3); @@ -2428,9 +2406,9 @@ s3_out(uint16_t addr, uint8_t val, void *p) tvp3026_ramdac_out(addr, rs2, rs3, val, svga->ramdac, svga); } else if (((s3->chip == S3_86C801) || (s3->chip == S3_86C805)) && (s3->card_type != S3_MIROCRYSTAL10SD_805 && s3->card_type != S3_MIROCRYSTAL8S_805)) att49x_ramdac_out(addr, rs2, val, svga->ramdac, svga); - else if (s3->chip <= S3_86C924) - sc1148x_ramdac_out(addr, 0, val, svga->ramdac, svga); - else + else if (s3->chip <= S3_86C924) { + sc1148x_ramdac_out(addr, rs2, val, svga->ramdac, svga); + } else sdac_ramdac_out(addr, rs2, val, svga->ramdac, svga); return; @@ -2459,8 +2437,6 @@ s3_out(uint16_t addr, uint8_t val, void *p) { case 0x31: s3->ma_ext = (s3->ma_ext & 0x1c) | ((val & 0x30) >> 4); - if (!svga->packed_chain4) - svga->force_dword_mode = val & 0x08; break; case 0x32: if ((svga->crtc[0x31] & 0x30) && (svga->crtc[0x51] & 0x01) && (val & 0x40)) @@ -2508,11 +2484,14 @@ s3_out(uint16_t addr, uint8_t val, void *p) case 0x35: s3->bank = (s3->bank & 0x70) | (val & 0xf); - if (svga->chain4) - svga->write_bank = svga->read_bank = s3->bank << 16; - else - svga->write_bank = svga->read_bank = s3->bank << 14; + if (svga->crtc[0x31] & 1) { + if (svga->chain4) + svga->write_bank = svga->read_bank = s3->bank << 16; + else + svga->write_bank = svga->read_bank = s3->bank << 14; + } break; + case 0x51: if (s3->chip == S3_86C801 || s3->chip == S3_86C805) { s3->bank = (s3->bank & 0x6f) | ((val & 0x4) << 2); @@ -2521,19 +2500,23 @@ s3_out(uint16_t addr, uint8_t val, void *p) s3->bank = (s3->bank & 0x4f) | ((val & 0xc) << 2); s3->ma_ext = (s3->ma_ext & ~0xc) | ((val & 3) << 2); } - if (svga->chain4) - svga->write_bank = svga->read_bank = s3->bank << 16; - else - svga->write_bank = svga->read_bank = s3->bank << 14; + if (svga->crtc[0x31] & 1) { + if (svga->chain4) + svga->write_bank = svga->read_bank = s3->bank << 16; + else + svga->write_bank = svga->read_bank = s3->bank << 14; + } break; case 0x6a: if (s3->chip >= S3_VISION964) { s3->bank = val; - if (svga->chain4) - svga->write_bank = svga->read_bank = s3->bank << 16; - else - svga->write_bank = svga->read_bank = s3->bank << 14; + if (svga->crtc[0x31] & 1) { + if (svga->chain4) + svga->write_bank = svga->read_bank = s3->bank << 16; + else + svga->write_bank = svga->read_bank = s3->bank << 14; + } } break; @@ -2714,7 +2697,7 @@ s3_in(uint16_t addr, void *p) } else if (((s3->chip == S3_86C801) || (s3->chip == S3_86C805)) && (s3->card_type != S3_MIROCRYSTAL10SD_805 && s3->card_type != S3_MIROCRYSTAL8S_805)) return att49x_ramdac_in(addr, rs2, svga->ramdac, svga); else if (s3->chip <= S3_86C924) - return sc1148x_ramdac_in(addr, 0, svga->ramdac, svga); + return sc1148x_ramdac_in(addr, rs2, svga->ramdac, svga); else return sdac_ramdac_in(addr, rs2, svga->ramdac, svga); break; @@ -2781,11 +2764,11 @@ static void s3_recalctimings(svga_t *svga) if (!svga->scrblank && svga->attr_palette_enable) { if ((svga->gdcreg[6] & 1) || (svga->attrregs[0x10] & 1)) { - if (svga->crtc[0x3a] & 0x10) /*256+ color register*/ + if (svga->crtc[0x3a] & 0x10) { /*256+ color register*/ svga->gdcreg[5] |= 0x40; + } } } - svga->ma_latch |= (s3->ma_ext << 16); if (s3->chip >= S3_86C928) { svga->hdisp = svga->hdisp_old; @@ -2834,7 +2817,7 @@ static void s3_recalctimings(svga_t *svga) if (s3->card_type == S3_MIROCRYSTAL10SD_805 || s3->card_type == S3_MIROCRYSTAL20SD_864 || s3->card_type == S3_MIROCRYSTAL20SV_964 || s3->card_type == S3_SPEA_MIRAGE_86C801 || s3->card_type == S3_SPEA_MIRAGE_86C805 || s3->card_type == S3_MIROCRYSTAL8S_805) { - if (svga->bpp != 32) { + if (svga->bpp != 32) { if (svga->crtc[0x31] & 2) /*This is needed if the pixel width gets set with delays*/ s3->width = 2048; } @@ -2844,8 +2827,12 @@ static void s3_recalctimings(svga_t *svga) s3->width = 1024; } } + + if ((svga->crtc[0x43] & 0x08) && (s3->bpp == 0)) + s3->width = 1024; if ((svga->gdcreg[5] & 0x40) && (svga->crtc[0x3a] & 0x10)) { + svga->fb_only = 1; switch (svga->bpp) { case 8: svga->render = svga_render_8bpp_highres; @@ -2867,7 +2854,7 @@ static void s3_recalctimings(svga_t *svga) if (svga->hdisp != 1408) svga->hdisp = s3->width; if (s3->card_type == S3_MIROCRYSTAL20SD_864) { - if (s3->width == 2048) { + if (s3->width == 2048 || s3->width == 1600 || s3->width == 800) { switch (svga->dispend) { case 400: case 480: @@ -2879,6 +2866,8 @@ static void s3_recalctimings(svga_t *svga) break; case 600: + if (s3->width == 1600) + s3->width = 800; svga->hdisp = 800; break; @@ -2906,8 +2895,6 @@ static void s3_recalctimings(svga_t *svga) break; case 15: svga->render = svga_render_15bpp_highres; - if (s3->chip <= S3_86C924) - svga->rowoffset >>= 1; if ((s3->chip != S3_VISION964) && (s3->card_type != S3_SPEA_MIRAGE_86C801) && (s3->card_type != S3_SPEA_MIRAGE_86C805)) { if (s3->chip == S3_86C928) @@ -2933,8 +2920,6 @@ static void s3_recalctimings(svga_t *svga) break; case 16: svga->render = svga_render_16bpp_highres; - if (s3->chip <= S3_86C924) - svga->rowoffset >>= 1; if ((s3->chip != S3_VISION964) && (s3->card_type != S3_SPEA_MIRAGE_86C801) && (s3->card_type != S3_SPEA_MIRAGE_86C805)) { if (s3->chip == S3_86C928) @@ -2974,7 +2959,7 @@ static void s3_recalctimings(svga_t *svga) } break; case 32: - svga->render = svga_render_32bpp_highres; + svga->render = svga_render_32bpp_highres; if ((s3->chip < S3_TRIO32) && (s3->chip != S3_VISION964) && (s3->chip != S3_VISION968) && (s3->chip != S3_86C928)) { if (s3->chip == S3_VISION868) @@ -2989,7 +2974,7 @@ static void s3_recalctimings(svga_t *svga) s3->card_type == S3_SPEA_MERCURY_P64V) { svga->hdisp = s3->width; if (s3->card_type == S3_MIROCRYSTAL20SD_864 || s3->card_type == S3_MIROCRYSTAL20SV_964) { - if ((svga->crtc[0x31] & 2) && (svga->crtc[0x50] & 0xc1) != 0x00) { + if (s3->width == 800 || s3->width == 1024 || s3->width == 1600) { switch (svga->dispend) { case 400: case 480: @@ -2997,22 +2982,53 @@ static void s3_recalctimings(svga_t *svga) break; case 576: + if (s3->width == 1600) + s3->width = 800; svga->hdisp = 768; break; case 600: + if (s3->width == 1600) + s3->width = 800; svga->hdisp = 800; break; - } + } } } } break; } } else { - if (svga->gdcreg[5] & 0x40) { - if (!svga->lowres) - svga->rowoffset <<= 1; + svga->fb_only = 0; + if (!svga->scrblank && svga->attr_palette_enable) { + if (!(svga->gdcreg[6] & 1) && !(svga->attrregs[0x10] & 1)) { /*Text mode*/ + if (svga->seqregs[1] & 8) /*40 column*/ { + svga->render = svga_render_text_40; + } else { + svga->render = svga_render_text_80; + } + } else { + if ((svga->crtc[0x31] & 0x08) && ((svga->gdcreg[5] & 0x60) == 0x00)) { + svga->render = svga_render_8bpp_highres; /*Enhanced 4bpp mode, just like the 8bpp mode per spec.*/ + if (svga->hdisp <= 1024) + s3->width = 1024; + } else { + switch (svga->gdcreg[5] & 0x60) { + case 0x00: + if (svga->seqregs[1] & 8) /*Low res (320)*/ + svga->render = svga_render_4bpp_lowres; + else + svga->render = svga_render_4bpp_highres; + break; + case 0x20: /*4 colours*/ + if (svga->seqregs[1] & 8) /*Low res (320)*/ + svga->render = svga_render_2bpp_lowres; + else + svga->render = svga_render_2bpp_highres; + break; + } + } + } } } } @@ -3028,9 +3044,7 @@ static void s3_trio64v_recalctimings(svga_t *svga) svga->gdcreg[5] |= 0x40; } } - svga->hdisp = svga->hdisp_old; - if (svga->crtc[0x5d] & 0x01) svga->htotal |= 0x100; if (svga->crtc[0x5d] & 0x02) { svga->hdisp_time |= 0x100; @@ -3207,24 +3221,24 @@ s3_updatemapping(s3_t *s3) s3->linear_base &= ~(s3->linear_size - 1); if (s3->linear_base == 0xa0000) { mem_mapping_disable(&s3->linear_mapping); - if (!(svga->crtc[0x53] & 0x10)) - { - mem_mapping_set_addr(&svga->mapping, 0xa0000, 0x10000); + if (!(svga->crtc[0x53] & 0x10)) { + mem_mapping_set_addr(&svga->mapping, s3->linear_base, 0x10000); svga->banked_mask = 0xffff; } } else { if (s3->chip >= S3_TRIO64V) { s3->linear_base &= 0xfc000000; - svga->fb_only = 1; } else if (s3->chip == S3_VISION968 || s3->chip == S3_VISION868) { s3->linear_base &= 0xfe000000; - svga->fb_only = 1; } + mem_mapping_set_addr(&s3->linear_mapping, s3->linear_base, s3->linear_size); } - } else { + if (s3->chip >= S3_TRIO64V) + svga->fb_only = 1; + } else { mem_mapping_disable(&s3->linear_mapping); - if (s3->chip >= S3_TRIO64V || s3->chip == S3_VISION968 || s3->chip == S3_VISION868) + if (s3->chip >= S3_TRIO64V) svga->fb_only = 0; } @@ -3236,8 +3250,9 @@ s3_updatemapping(s3_t *s3) mem_mapping_set_addr(&s3->mmio_mapping, 0xb8000, 0x8000); else mem_mapping_set_addr(&s3->mmio_mapping, 0xa0000, 0x10000); - } else + } else { mem_mapping_enable(&s3->mmio_mapping); + } } else mem_mapping_disable(&s3->mmio_mapping); @@ -3271,15 +3286,12 @@ s3_accel_out(uint16_t port, uint8_t val, void *p) s3_t *s3 = (s3_t *)p; svga_t *svga = &s3->svga; + if (!s3->enable_8514) + return; + if (port >= 0x8000) - { - if (!s3->enable_8514) - return; - s3_accel_out_fifo(s3, port, val); - } - else - { + else { switch (port) { case 0x4148: case 0x42e8: @@ -3927,28 +3939,7 @@ s3_accel_read(uint32_t addr, void *p) if (addr & 0x8000) { temp = s3_accel_in(addr & 0xffff, p); } else if (s3_cpu_dest(s3)) { - READ_PIXTRANS_BYTE_MM - - switch (s3->accel.cmd & 0x600) { - case 0x000: - if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { - if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) - s3_accel_start(8, 1, temp | (temp << 8) | (temp << 16) | (temp << 24), 0, s3); - else - s3_accel_start(1, 1, 0xffffffff, temp | (temp << 8) | (temp << 16) | (temp << 24), s3); - } else - s3_accel_start(1, 1, 0xffffffff, temp | (temp << 8) | (temp << 16) | (temp << 24), s3); - break; - case 0x200: - if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { - if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) - s3_accel_start(16, 1, temp | (temp << 8) | (temp << 16) | (temp << 24), 0, s3); - else - s3_accel_start(2, 1, 0xffffffff, temp | (temp << 8) | (temp << 16) | (temp << 24), s3); - } else - s3_accel_start(2, 1, 0xffffffff, temp | (temp << 8) | (temp << 16) | (temp << 24), s3); - break; - } + temp = s3_accel_in(0xe2e8 + (addr & 3), p); } } @@ -4223,8 +4214,8 @@ polygon_setup(s3_t *s3) } } -#define READ(addr, dat) if (s3->bpp == 0) dat = svga->vram[dword_remap(svga, addr) & s3->vram_mask]; \ - else if (s3->bpp == 1) dat = vram_w[dword_remap_w(svga, addr) & (s3->vram_mask >> 1)]; \ +#define READ(addr, dat) if (s3->bpp == 0 && !(svga->crtc[0x43] & 0x08)) dat = svga->vram[dword_remap(svga, addr) & s3->vram_mask]; \ + else if (s3->bpp == 1 || (svga->crtc[0x43] & 0x08)) dat = vram_w[dword_remap_w(svga, addr) & (s3->vram_mask >> 1)]; \ else if (s3->bpp == 2) dat = svga->vram[dword_remap(svga, addr) & s3->vram_mask]; \ else dat = vram_l[dword_remap_l(svga, addr) & (s3->vram_mask >> 2)]; @@ -4529,12 +4520,12 @@ polygon_setup(s3_t *s3) } -#define WRITE(addr, dat) if (s3->bpp == 0) \ +#define WRITE(addr, dat) if (s3->bpp == 0 && !(svga->crtc[0x43] & 0x08)) \ { \ svga->vram[dword_remap(svga, addr) & s3->vram_mask] = dat; \ svga->changedvram[(dword_remap(svga, addr) & s3->vram_mask) >> 12] = changeframecount; \ } \ - else if (s3->bpp == 1) \ + else if (s3->bpp == 1 || (svga->crtc[0x43] & 0x08)) \ { \ vram_w[dword_remap_w(svga, addr) & (s3->vram_mask >> 1)] = dat; \ svga->changedvram[(dword_remap_w(svga, addr) & (s3->vram_mask >> 1)) >> 11] = changeframecount; \ @@ -5040,13 +5031,6 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ uint32_t rd_mask = s3->accel.rd_mask; int cmd = s3->accel.cmd >> 13; uint32_t srcbase, dstbase; - - if (s3->chip <= S3_86C805) { /*Chicago 4.00.58s' s3 driver has a weird bug, not sure on real hardware*/ - if (s3->bpp == 0 && svga->bpp == 15 && s3->width == 2048) { - s3->bpp = 1; - s3->width >>= 1; - } - } if ((s3->chip >= S3_TRIO64 || s3->chip == S3_VISION968 || s3->chip == S3_VISION868) && (s3->accel.cmd & (1 << 11))) { cmd |= 8; @@ -5063,7 +5047,7 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ } else { dstbase = 0x100000 * ((s3->accel.multifunc[0xe] >> 0) & 3); } - if (s3->bpp == 1) { + if (s3->bpp == 1 || (svga->crtc[0x43] & 0x08)) { srcbase >>= 1; dstbase >>= 1; } else if (s3->bpp == 3) { @@ -5077,8 +5061,8 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ if (!cpu_input) s3->accel.dat_count = 0; - - if (cpu_input && (((s3->accel.multifunc[0xa] & 0xc0) != 0x80) || (!(s3->accel.cmd & 2)))) { + + if (cpu_input && (count <= 4)) { if ((s3->bpp == 3) && count == 2) { if (s3->accel.dat_count) { cpu_dat = ((cpu_dat & 0xffff) << 16) | s3->accel.dat_buf; @@ -5089,19 +5073,19 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ s3->accel.dat_count = 1; } } - if (s3->bpp == 1) + if (s3->bpp == 1 || (svga->crtc[0x43] & 0x08)) count >>= 1; if (s3->bpp == 3) count >>= 2; } - if (s3->bpp == 0) + if (s3->bpp == 0 && !(svga->crtc[0x43] & 0x08)) rd_mask &= 0xff; - else if (s3->bpp == 1) + else if (s3->bpp == 1 || (svga->crtc[0x43] & 0x08)) rd_mask &= 0xffff; - if (s3->bpp == 0) compare &= 0xff; - if (s3->bpp == 1) compare &= 0xffff; + if (s3->bpp == 0 && !(svga->crtc[0x43] & 0x08)) compare &= 0xff; + if (s3->bpp == 1 || (svga->crtc[0x43] & 0x08)) compare &= 0xffff; switch (s3->accel.cmd & 0x600) { @@ -5154,7 +5138,7 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ mix_dat <<= 1; mix_dat |= 1; - if (s3->bpp == 0) cpu_dat >>= 8; + if (s3->bpp == 0 && !(svga->crtc[0x43] & 0x08)) cpu_dat >>= 8; else cpu_dat >>= 16; if (!s3->accel.ssv_len) break; @@ -5229,7 +5213,7 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ mix_dat <<= 1; mix_dat |= 1; - if (s3->bpp == 0) cpu_dat >>= 8; + if (s3->bpp == 0 && !(svga->crtc[0x43] & 0x08)) cpu_dat >>= 8; else cpu_dat >>= 16; if (!s3->accel.sy) { break; @@ -5254,7 +5238,7 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ } else /*Bresenham*/ { - if (s3->accel.b2e8_pix && count == 16) { /*Stupid undocumented 0xB2E8 on 911/924*/ + if (s3->accel.b2e8_pix && s3_cpu_src(s3) && count == 16) { /*Stupid undocumented 0xB2E8 on 911/924*/ count = s3->accel.maj_axis_pcnt + 1; s3->accel.temp_cnt = 16; } @@ -5301,7 +5285,7 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ mix_dat <<= 1; mix_dat |= 1; } - if (s3->bpp == 0) cpu_dat >>= 8; + if (s3->bpp == 0 && !(svga->crtc[0x43] & 0x08)) cpu_dat >>= 8; else cpu_dat >>= 16; if (!s3->accel.sy) { @@ -5446,7 +5430,7 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ mix_dat |= 1; } - if (s3->bpp == 0) + if (s3->bpp == 0 && !(svga->crtc[0x43] & 0x08)) cpu_dat >>= 8; else cpu_dat >>= 16; @@ -5474,6 +5458,10 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ s3->accel.sy--; if (cpu_input) { + if (s3->accel.b2e8_pix) { + s3->accel.cur_x = s3->accel.cx; + s3->accel.cur_y = s3->accel.cy; + } return; } if (s3->accel.sy < 0) { @@ -5620,7 +5608,6 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ frgd_mix = (s3->accel.frgd_mix >> 5) & 3; bkgd_mix = (s3->accel.bkgd_mix >> 5) & 3; - if (!cpu_input && frgd_mix == 3 && !vram_mask && !compare_mode && (s3->accel.cmd & 0xa0) == 0xa0 && (s3->accel.frgd_mix & 0xf) == 7 && (s3->accel.bkgd_mix & 0xf) == 7) @@ -5705,7 +5692,7 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ mix_dat <<= 1; mix_dat |= 1; - if (s3->bpp == 0) cpu_dat >>= 8; + if (s3->bpp == 0 && !(svga->crtc[0x43] & 0x08)) cpu_dat >>= 8; else cpu_dat >>= 16; if (s3->accel.cmd & 0x20) @@ -6833,7 +6820,6 @@ static void *s3_init(const device_t *info) s3->id_ext_pci = 0; s3->packed_mmio = 0; svga->crtc[0x5a] = 0x0a; - svga->ramdac = device_add(&bt485_ramdac_device); svga->clock_gen = device_add(&icd2061_device); svga->getclock = icd2061_getclock; @@ -6843,14 +6829,14 @@ static void *s3_init(const device_t *info) case S3_PHOENIX_VISION864: case S3_MIROCRYSTAL20SD_864: svga->decode_mask = (8 << 20) - 1; - if (info->local == S3_PARADISE_BAHAMAS64) + if (info->local == S3_PARADISE_BAHAMAS64 || info->local == S3_MIROCRYSTAL20SD_864) stepping = 0xc0; /*Vision864*/ else stepping = 0xc1; /*Vision864P*/ s3->id = stepping; s3->id_ext = s3->id_ext_pci = stepping; s3->packed_mmio = 0; - + svga->crtc[0x5a] = 0x0a; svga->ramdac = device_add(&sdac_ramdac_device); svga->clock_gen = svga->ramdac; svga->getclock = sdac_getclock; @@ -6965,7 +6951,7 @@ static void *s3_init(const device_t *info) case S3_TRIO64V2_DX: svga->decode_mask = (4 << 20) - 1; - s3->id = 0xe1; /*Trio64V2/DX*/ + s3->id = 0xe1; /*Trio64V2*/ s3->id_ext = s3->id_ext_pci = 0x01; s3->packed_mmio = 1; svga->crtc[0x53] = 0x08; @@ -6986,9 +6972,8 @@ static void *s3_init(const device_t *info) default: return NULL; } - - if (s3->chip >= S3_TRIO64V || s3->chip == S3_VISION868 || s3->chip == S3_VISION968) - svga->packed_chain4 = 1; + + svga->packed_chain4 = 1; if (s3->pci) s3->card = pci_add_card(PCI_ADD_VIDEO, s3_pci_read, s3_pci_write, s3); diff --git a/src/video/vid_s3_virge.c b/src/video/vid_s3_virge.c index e729f16d3..43d26a559 100644 --- a/src/video/vid_s3_virge.c +++ b/src/video/vid_s3_virge.c @@ -3976,7 +3976,7 @@ static void *s3_virge_init(const device_t *info) memset(virge->dmabuffer, 0, 65536); - virge->card = pci_add_card(PCI_ADD_VIDEO, s3_virge_pci_read, s3_virge_pci_write, virge); + virge->card = pci_add_card((info->flags & DEVICE_AGP) ? PCI_ADD_AGP : PCI_ADD_VIDEO, s3_virge_pci_read, s3_virge_pci_write, virge); virge->i2c = i2c_gpio_init("ddc_s3_virge"); virge->ddc = ddc_init(i2c_gpio_get_bus(virge->i2c)); diff --git a/src/video/vid_sc1148x_ramdac.c b/src/video/vid_sc1148x_ramdac.c index 88f81d77c..06df1188b 100644 --- a/src/video/vid_sc1148x_ramdac.c +++ b/src/video/vid_sc1148x_ramdac.c @@ -33,6 +33,7 @@ typedef struct { int type; int state; + int rs2; uint8_t ctrl; } sc1148x_ramdac_t; @@ -43,61 +44,32 @@ sc1148x_ramdac_out(uint16_t addr, int rs2, uint8_t val, void *p, svga_t *svga) sc1148x_ramdac_t *ramdac = (sc1148x_ramdac_t *) p; uint8_t rs = (addr & 0x03); rs |= ((!!rs2) << 2); - - if (ramdac->type == 0 || ramdac->type == 3) { - switch (addr) { - case 0x3c6: - if (ramdac->state == 4) { - ramdac->state = 0; - ramdac->ctrl = val; - if (ramdac->ctrl & 0x80) { + + switch (addr) { + case 0x3c6: + if (ramdac->state == 4) { + ramdac->state = 0; + ramdac->ctrl = val; + if (ramdac->ctrl & 0xa0) { + if ((ramdac->ctrl & 0x40) && (ramdac->type == 1)) + svga->bpp = 16; + else svga->bpp = 15; - } else { - svga->bpp = 8; - } - svga_recalctimings(svga); - return; + } else { + svga->bpp = 8; } - ramdac->state = 0; - break; - - case 0x3c7: - case 0x3c8: - case 0x3c9: - ramdac->state = 0; - svga_out(addr, val, svga); - break; + svga_recalctimings(svga); + return; } - } else { - switch (rs) { - case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - case 0x07: - svga_out(addr, val, svga); - ramdac->state = 0; - break; - case 0x06: - if (ramdac->state == 4) { - ramdac->state = 0; - ramdac->ctrl = val; - if (ramdac->ctrl & 0x80) { - if ((ramdac->ctrl & 0x40) && (ramdac->type == 1)) - svga->bpp = 16; - else - svga->bpp = 15; - } else { - svga->bpp = 8; - } - svga_recalctimings(svga); - return; - } - ramdac->state = 0; - break; - } + ramdac->state = 0; + break; + + case 0x3c7: + case 0x3c8: + case 0x3c9: + ramdac->state = 0; + svga_out(addr, val, svga); + break; } } @@ -110,51 +82,28 @@ sc1148x_ramdac_in(uint16_t addr, int rs2, void *p, svga_t *svga) uint8_t rs = (addr & 0x03); rs |= ((!!rs2) << 2); - if (ramdac->type == 0 || ramdac->type == 3) { - switch (addr) { - case 0x3c6: - if (ramdac->state == 4) { - ramdac->state = 0; - temp = ramdac->ctrl; - return temp; - } - ramdac->state++; - break; - - case 0x3c7: - case 0x3c8: - case 0x3c9: + switch (addr) { + case 0x3c6: + if (ramdac->state == 4) { ramdac->state = 0; - temp = svga_in(addr, svga); - break; + temp = ramdac->ctrl; + if (ramdac->type == 1) { + if (((ramdac->ctrl >> 5) == 1) || ((ramdac->ctrl >> 5) == 3)) + temp |= 1; + else + temp &= ~1; + } + return temp; } - } else { - switch (rs) { - case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - case 0x07: - temp = svga_in(addr, svga); - ramdac->state = 0; - break; - case 0x06: - if (ramdac->state == 4) { - ramdac->state = 0; - temp = ramdac->ctrl; - if (ramdac->type == 1) { - if (((ramdac->ctrl >> 4) == 1) || ((ramdac->ctrl >> 4) == 3)) - temp |= 1; - else - temp &= ~1; - } - return temp; - } - ramdac->state++; - break; - } + ramdac->state++; + break; + + case 0x3c7: + case 0x3c8: + case 0x3c9: + temp = svga_in(addr, svga); + ramdac->state = 0; + break; } return temp; @@ -198,18 +147,10 @@ const device_t sc11487_ramdac_device = NULL, { NULL }, NULL, NULL }; -const device_t sc11484_ramdac_device = -{ - "Sierra SC11484 RAMDAC", - 0, 2, - sc1148x_ramdac_init, sc1148x_ramdac_close, - NULL, { NULL }, NULL, NULL -}; - const device_t sc11484_nors2_ramdac_device = { "Sierra SC11484 RAMDAC (no RS2 signal)", - 0, 3, + 0, 2, sc1148x_ramdac_init, sc1148x_ramdac_close, NULL, { NULL }, NULL, NULL }; \ No newline at end of file From 46807540edc03a87d593b58f88a07ed6e3190bd7 Mon Sep 17 00:00:00 2001 From: OBattler Date: Wed, 6 Oct 2021 02:26:30 +0200 Subject: [PATCH 05/56] Rewritten renderer blitting, fixes flickering when inversion or grayscale is used. --- src/config.c | 41 +++++++++++++++--------------- src/include/86box/video.h | 4 +++ src/unix/unix_sdl.c | 5 +++- src/video/video.c | 52 +++++++++++++++++---------------------- src/vnc.c | 5 +++- src/win/win_opengl.c | 5 +++- src/win/win_sdl.c | 23 ++++++++++------- src/win/win_ui.c | 2 ++ 8 files changed, 76 insertions(+), 61 deletions(-) diff --git a/src/config.c b/src/config.c index e0928cabe..7323b6a81 100644 --- a/src/config.c +++ b/src/config.c @@ -2056,28 +2056,29 @@ config_load(void) cassette_ui_writeprot = 0; config_log("Config file not present or invalid!\n"); - return; + } else { + load_general(); /* General */ + load_machine(); /* Machine */ + load_video(); /* Video */ + load_input_devices(); /* Input devices */ + load_sound(); /* Sound */ + load_network(); /* Network */ + load_ports(); /* Ports (COM & LPT) */ + load_storage_controllers(); /* Storage controllers */ + load_hard_disks(); /* Hard disks */ + load_floppy_and_cdrom_drives(); /* Floppy and CD-ROM drives */ + /* TODO: Backwards compatibility, get rid of this when enough time has passed. */ + load_floppy_drives(); /* Floppy drives */ + load_other_removable_devices(); /* Other removable devices */ + load_other_peripherals(); /* Other peripherals */ + + /* Mark the configuration as changed. */ + config_changed = 1; + + config_log("Config loaded.\n\n"); } - load_general(); /* General */ - load_machine(); /* Machine */ - load_video(); /* Video */ - load_input_devices(); /* Input devices */ - load_sound(); /* Sound */ - load_network(); /* Network */ - load_ports(); /* Ports (COM & LPT) */ - load_storage_controllers(); /* Storage controllers */ - load_hard_disks(); /* Hard disks */ - load_floppy_and_cdrom_drives(); /* Floppy and CD-ROM drives */ - /* TODO: Backwards compatibility, get rid of this when enough time has passed. */ - load_floppy_drives(); /* Floppy drives */ - load_other_removable_devices(); /* Other removable devices */ - load_other_peripherals(); /* Other peripherals */ - - /* Mark the configuration as changed. */ - config_changed = 1; - - config_log("Config loaded.\n\n"); + video_copy = (video_grayscale || invert_display) ? video_transform_copy : memcpy; } diff --git a/src/include/86box/video.h b/src/include/86box/video.h index e22eda015..c8cff07f8 100644 --- a/src/include/86box/video.h +++ b/src/include/86box/video.h @@ -133,6 +133,10 @@ extern int readflash; /* Function handler pointers. */ extern void (*video_recalctimings)(void); +extern void * __cdecl (*video_copy)(void *_Dst, const void *_Src, size_t _Size); + +extern void video_screenshot(uint32_t *buf, int start_x, int start_y, int row_len); +extern void * __cdecl video_transform_copy(void *_Dst, const void *_Src, size_t _Size); /* Table functions. */ diff --git a/src/unix/unix_sdl.c b/src/unix/unix_sdl.c index a61d338e6..4c309144f 100644 --- a/src/unix/unix_sdl.c +++ b/src/unix/unix_sdl.c @@ -133,7 +133,10 @@ sdl_blit_shim(int x, int y, int w, int h) params.y = y; params.w = w; params.h = h; - if (!(!sdl_enabled || (x < 0) || (y < 0) || (w <= 0) || (h <= 0) || (w > 2048) || (h > 2048) || (buffer32 == NULL) || (sdl_render == NULL) || (sdl_tex == NULL))) memcpy(interpixels, &(buffer32->line[y][x]), h * (2048 + 64) * sizeof(uint32_t)); + if (!(!sdl_enabled || (x < 0) || (y < 0) || (w <= 0) || (h <= 0) || (w > 2048) || (h > 2048) || (buffer32 == NULL) || (sdl_render == NULL) || (sdl_tex == NULL))) + video_copy(interpixels, &(buffer32->line[y][x]), h * (2048 + 64) * sizeof(uint32_t)); + if (screenshots) + video_screenshot(interpixels, 0, 0, (2048 + 64)); blitreq = 1; video_blit_complete(); } diff --git a/src/video/video.c b/src/video/video.c index 1258b1824..332f8bbaf 100644 --- a/src/video/video.c +++ b/src/video/video.c @@ -114,6 +114,8 @@ static const video_timings_t *vid_timings; static uint32_t cga_2_table[16]; static uint8_t thread_run = 0; +void * __cdecl (*video_copy)(void *_Dst, const void *_Src, size_t _Size) = memcpy; + PALETTE cgapal = { {0,0,0}, {0,42,0}, {42,0,0}, {42,21,0}, @@ -317,7 +319,7 @@ static png_infop info_ptr; static void -video_take_screenshot(const char *fn) +video_take_screenshot(const char *fn, uint32_t *buf, int start_x, int start_y, int row_len) { int i, x, y; png_bytep *b_rgb = NULL; @@ -363,10 +365,10 @@ video_take_screenshot(const char *fn) for (y = 0; y < blit_data.h; ++y) { b_rgb[y] = (png_byte *) malloc(png_get_rowbytes(png_ptr, info_ptr)); for (x = 0; x < blit_data.w; ++x) { - if (buffer32 == NULL) + if (buf == NULL) memset(&(b_rgb[y][x * 3]), 0x00, 3); else { - temp = buffer32->line[blit_data.y + y][blit_data.x + x]; + temp = buf[((start_y + y) * row_len) + start_x + x]; b_rgb[y][x * 3] = (temp >> 16) & 0xff; b_rgb[y][(x * 3) + 1] = (temp >> 8) & 0xff; b_rgb[y][(x * 3) + 2] = temp & 0xff; @@ -390,8 +392,8 @@ video_take_screenshot(const char *fn) } -static void -video_screenshot(void) +void +video_screenshot(uint32_t *buf, int start_x, int start_y, int row_len) { char path[1024], fn[128]; @@ -410,50 +412,42 @@ video_screenshot(void) video_log("taking screenshot to: %s\n", path); - video_take_screenshot((const char *) path); + video_take_screenshot((const char *) path, buf, start_x, start_y, row_len); png_destroy_write_struct(&png_ptr, &info_ptr); + + screenshots--; } -static void -video_transform_copy(uint32_t *dst, uint32_t *src, int len) +void * __cdecl +video_transform_copy(void *_Dst, const void *_Src, size_t _Size) { int i; + uint32_t *dest_ex = (uint32_t *) _Dst; + uint32_t *src_ex = (uint32_t *) _Src; - if ((dst != NULL) && (src != NULL)) { - for (i = 0; i < len; i++) { - *dst = video_color_transform(*src); - dst++; - src++; + _Size /= sizeof(uint32_t); + + if ((dest_ex != NULL) && (src_ex != NULL)) { + for (i = 0; i < _Size; i++) { + *dest_ex = video_color_transform(*src_ex); + dest_ex++; + src_ex++; } } + + return _Dst; } static void blit_thread(void *param) { - int yy; - while (thread_run) { thread_wait_event(blit_data.wake_blit_thread, -1); thread_reset_event(blit_data.wake_blit_thread); MTR_BEGIN("video", "blit_thread"); - if ((video_grayscale || invert_display) && (blit_data.h > 0)) { - for (yy = 0; yy < blit_data.h; yy++) { - if (((blit_data.y + yy) >= 0) && ((blit_data.y + yy) < buffer32->h)) { - video_transform_copy(&(buffer32->line[blit_data.y + yy][blit_data.x]), &(buffer32->line[blit_data.y + yy][blit_data.x]), blit_data.w); - } - } - } - - if (screenshots) { - video_screenshot(); - screenshots--; - video_log("screenshot taken, %i left\n", screenshots); - } - if (blit_func) blit_func(blit_data.x, blit_data.y, blit_data.w, blit_data.h); diff --git a/src/vnc.c b/src/vnc.c index 371e03803..363c6205d 100644 --- a/src/vnc.c +++ b/src/vnc.c @@ -179,8 +179,11 @@ vnc_blit(int x, int y, int w, int h) p = (uint32_t *)&(((uint32_t *)rfb->frameBuffer)[yy*VNC_MAX_X]); if ((y+yy) >= 0 && (y+yy) < VNC_MAX_Y) - memcpy(p, &(buffer32->line[yy]), w*sizeof(uint32_t)); + video_copy(p, &(buffer32->line[yy]), w*sizeof(uint32_t)); } + + if (screenshots) + video_screenshot((uint32_t *) rfb->frameBuffer, 0, 0, ROW_LENGTH); video_blit_complete(); diff --git a/src/win/win_opengl.c b/src/win/win_opengl.c index b2c338a31..69190b6b1 100644 --- a/src/win/win_opengl.c +++ b/src/win/win_opengl.c @@ -816,7 +816,10 @@ static void opengl_blit(int x, int y, int w, int h) return; } - memcpy(blit_info[write_pos].buffer, &(buffer32->line[y][x]), h * ROW_LENGTH * sizeof(uint32_t)); + video_copy(blit_info[write_pos].buffer, &(buffer32->line[y][x]), h * ROW_LENGTH * sizeof(uint32_t)); + + if (screenshots) + video_screenshot(blit_info[write_pos].buffer, 0, 0, ROW_LENGTH); video_blit_complete(); diff --git a/src/win/win_sdl.c b/src/win/win_sdl.c index 62a77551b..e8bdd6e0d 100644 --- a/src/win/win_sdl.c +++ b/src/win/win_sdl.c @@ -232,7 +232,8 @@ static void sdl_blit(int x, int y, int w, int h) { SDL_Rect r_src; - int ret; + void *pixeldata; + int pitch, ret; if (!sdl_enabled || (x < 0) || (y < 0) || (w <= 0) || (h <= 0) || (w > 2048) || (h > 2048) || (buffer32 == NULL) || (sdl_render == NULL) || (sdl_tex == NULL)) { video_blit_complete(); @@ -241,17 +242,21 @@ sdl_blit(int x, int y, int w, int h) SDL_LockMutex(sdl_mutex); - r_src.x = x; - r_src.y = y; - r_src.w = w; - r_src.h = h; - SDL_UpdateTexture(sdl_tex, &r_src, &(buffer32->line[y][x]), (2048 + 64) * sizeof(uint32_t)); + SDL_LockTexture(sdl_tex, 0, &pixeldata, &pitch); + + video_copy(pixeldata, &(buffer32->line[y][x]), h * (2048 + 64) * sizeof(uint32_t)); + + if (screenshots) + video_screenshot((uint32_t *) pixeldata, 0, 0, (2048 + 64)); + + SDL_UnlockTexture(sdl_tex); + video_blit_complete(); SDL_RenderClear(sdl_render); - r_src.x = x; - r_src.y = y; + r_src.x = 0; + r_src.y = 0; r_src.w = w; r_src.h = h; @@ -353,7 +358,7 @@ sdl_init_texture(void) sdl_render = SDL_CreateRenderer(sdl_win, -1, SDL_RENDERER_SOFTWARE); sdl_tex = SDL_CreateTexture(sdl_render, SDL_PIXELFORMAT_ARGB8888, - SDL_TEXTUREACCESS_STREAMING, 2048, 2048); + SDL_TEXTUREACCESS_STREAMING, (2048 + 64), (2048 + 64)); } diff --git a/src/win/win_ui.c b/src/win/win_ui.c index 6a38045c9..9b1f64d5d 100644 --- a/src/win/win_ui.c +++ b/src/win/win_ui.c @@ -822,6 +822,7 @@ MainWindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) case IDM_VID_INVERT: video_toggle_option(hmenu, &invert_display, IDM_VID_INVERT); + video_copy = (video_grayscale || invert_display) ? video_transform_copy : memcpy; break; case IDM_VID_OVERSCAN: @@ -854,6 +855,7 @@ MainWindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) case IDM_VID_GRAY_WHITE: CheckMenuItem(hmenu, IDM_VID_GRAY_RGB+video_grayscale, MF_UNCHECKED); video_grayscale = LOWORD(wParam) - IDM_VID_GRAY_RGB; + video_copy = (video_grayscale || invert_display) ? video_transform_copy : memcpy; CheckMenuItem(hmenu, IDM_VID_GRAY_RGB+video_grayscale, MF_CHECKED); device_force_redraw(); config_save(); From b7f5d4a773b0880200d75824fea8e8a7e49833be Mon Sep 17 00:00:00 2001 From: OBattler Date: Wed, 6 Oct 2021 18:12:22 +0200 Subject: [PATCH 06/56] Video 7 fixes, fixes RAMDAC 6-bit/8-bit switching on the Radius SVGA MultiView. --- src/include/86box/vid_svga.h | 3 +- src/video/vid_ht216.c | 140 +++++++++++++++++++-------------- src/video/vid_sc1148x_ramdac.c | 43 +++++----- src/video/vid_svga.c | 4 + 4 files changed, 108 insertions(+), 82 deletions(-) diff --git a/src/include/86box/vid_svga.h b/src/include/86box/vid_svga.h index 91559aa72..90282a578 100644 --- a/src/include/86box/vid_svga.h +++ b/src/include/86box/vid_svga.h @@ -23,7 +23,8 @@ #define FLAG_EXT_WRITE 4 #define FLAG_LATCH8 8 #define FLAG_NOSKEW 16 -#define FLAG_ADDR_BY16 32 +#define FLAG_ADDR_BY16 32 +#define FLAG_RAMDAC_SHIFT 64 typedef struct { diff --git a/src/video/vid_ht216.c b/src/video/vid_ht216.c index 8264f30ca..50bedb377 100644 --- a/src/video/vid_ht216.c +++ b/src/video/vid_ht216.c @@ -59,7 +59,7 @@ typedef struct ht216_t uint8_t bg_plane_sel, fg_plane_sel; uint8_t ht_regs[256]; - uint8_t extensions; + uint8_t extensions, reg_3cb; uint8_t pos_regs[8]; } ht216_t; @@ -179,7 +179,7 @@ ht216_out(uint16_t addr, uint8_t val, void *p) ht216_log("ht216 %i out %04X %02X %04X:%04X\n", svga->miscout & 1, addr, val, CS, cpu_state.pc); if (((addr & 0xfff0) == 0x3d0 || (addr & 0xfff0) == 0x3b0) && !(svga->miscout & 1)) - addr ^= 0x60; + addr ^= 0x60; switch (addr) { case 0x3c2: @@ -300,6 +300,12 @@ ht216_out(uint16_t addr, uint8_t val, void *p) svga->hwcursor.ena = !!(val & 0x80); break; + case 0xc0: + break; + + case 0xc1: + break; + case 0xc8: if ((old ^ val) & HT_REG_C8_E256) { svga->fullchange = changeframecount; @@ -313,6 +319,10 @@ ht216_out(uint16_t addr, uint8_t val, void *p) break; case 0xe0: + svga->adv_flags &= ~FLAG_RAMDAC_SHIFT; + if (val & 0x04) + svga->adv_flags |= FLAG_RAMDAC_SHIFT; + /* FALLTHROUGH */ /*Bank registers*/ case 0xe8: case 0xe9: ht216_log("HT216 reg 0x%02x write = %02x, mode = 1, chain4 = %x\n", svga->seqaddr & 0xff, val, svga->chain4); @@ -377,70 +387,76 @@ ht216_out(uint16_t addr, uint8_t val, void *p) } break; - case 0x3c6: case 0x3c7: case 0x3c8: case 0x3c9: + case 0x3c6: case 0x3c7: case 0x3c8: case 0x3c9: if (ht216->id == 0x7152) sc1148x_ramdac_out(addr, 0, val, svga->ramdac, svga); else svga_out(addr, val, svga); return; - case 0x3cf: - if (svga->gdcaddr == 5) { - svga->chain2_read = val & 0x10; - ht216_remap(ht216); - } else if (svga->gdcaddr == 6) { - if (val & 8) - svga->banked_mask = 0x7fff; - else - svga->banked_mask = 0xffff; - } - - if (svga->gdcaddr <= 8) { - svga->fast = (svga->gdcreg[8] == 0xff && !(svga->gdcreg[3] & 0x18) && - !svga->gdcreg[1]) && svga->chain4 && svga->packed_chain4; - } - break; + case 0x3cb: + if (ht216->id == 0x7152) { + ht216->reg_3cb = val; + svga_set_ramdac_type(svga, (val & 0x20) ? RAMDAC_6BIT : RAMDAC_8BIT); + } + break; - case 0x3D4: - svga->crtcreg = val & 0x3f; + case 0x3cf: + if (svga->gdcaddr == 5) { + svga->chain2_read = val & 0x10; + ht216_remap(ht216); + } else if (svga->gdcaddr == 6) { + if (val & 8) + svga->banked_mask = 0x7fff; + else + svga->banked_mask = 0xffff; + } + + if (svga->gdcaddr <= 8) { + svga->fast = (svga->gdcreg[8] == 0xff && !(svga->gdcreg[3] & 0x18) && + !svga->gdcreg[1]) && svga->chain4 && svga->packed_chain4; + } + break; + + case 0x3D4: + svga->crtcreg = val & 0x3f; + return; + case 0x3D5: + if ((svga->crtcreg < 7) && (svga->crtc[0x11] & 0x80)) return; - case 0x3D5: - if ((svga->crtcreg < 7) && (svga->crtc[0x11] & 0x80)) - return; - if ((svga->crtcreg == 7) && (svga->crtc[0x11] & 0x80)) - val = (svga->crtc[7] & ~0x10) | (val & 0x10); + if ((svga->crtcreg == 7) && (svga->crtc[0x11] & 0x80)) + val = (svga->crtc[7] & ~0x10) | (val & 0x10); - old = svga->crtc[svga->crtcreg]; - svga->crtc[svga->crtcreg] = val; + old = svga->crtc[svga->crtcreg]; + svga->crtc[svga->crtcreg] = val; - if (old != val) { - if (svga->crtcreg < 0xe || svga->crtcreg > 0x10) - { + if (old != val) { + if (svga->crtcreg < 0xe || svga->crtcreg > 0x10) { if ((svga->crtcreg == 0xc) || (svga->crtcreg == 0xd)) { - svga->fullchange = 3; + svga->fullchange = 3; svga->ma_latch = ((svga->crtc[0xc] << 8) | svga->crtc[0xd]) + ((svga->crtc[8] & 0x60) >> 5); } else { svga->fullchange = changeframecount; - svga_recalctimings(svga); + svga_recalctimings(svga); } - } } - break; + } + break; - case 0x46e8: + case 0x46e8: + if ((ht216->id == 0x7152) && ht216->isabus) + io_removehandler(0x0105, 0x0001, ht216_in, NULL, NULL, ht216_out, NULL, NULL, ht216); + io_removehandler(0x03c0, 0x0020, ht216_in, NULL, NULL, ht216_out, NULL, NULL, ht216); + mem_mapping_disable(&svga->mapping); + mem_mapping_disable(&ht216->linear_mapping); + if (val & 8) { if ((ht216->id == 0x7152) && ht216->isabus) - io_removehandler(0x0105, 0x0001, ht216_in, NULL, NULL, ht216_out, NULL, NULL, ht216); - io_removehandler(0x03c0, 0x0020, ht216_in, NULL, NULL, ht216_out, NULL, NULL, ht216); - mem_mapping_disable(&svga->mapping); - mem_mapping_disable(&ht216->linear_mapping); - if (val & 8) { - if ((ht216->id == 0x7152) && ht216->isabus) - io_sethandler(0x0105, 0x0001, ht216_in, NULL, NULL, ht216_out, NULL, NULL, ht216); - io_sethandler(0x03c0, 0x0020, ht216_in, NULL, NULL, ht216_out, NULL, NULL, ht216); - mem_mapping_enable(&svga->mapping); - ht216_remap(ht216); - } - break; + io_sethandler(0x0105, 0x0001, ht216_in, NULL, NULL, ht216_out, NULL, NULL, ht216); + io_sethandler(0x03c0, 0x0020, ht216_in, NULL, NULL, ht216_out, NULL, NULL, ht216); + mem_mapping_enable(&svga->mapping); + ht216_remap(ht216); + } + break; } svga_out(addr, val, svga); @@ -457,10 +473,10 @@ ht216_in(uint16_t addr, void *p) if (((addr & 0xfff0) == 0x3d0 || (addr & 0xfff0) == 0x3b0) && !(svga->miscout & 1)) addr ^= 0x60; - if ((ht216->id == 0x7152) && ht216->isabus) { - if (addr == 0x105) - return ht216->extensions; - } + if ((ht216->id == 0x7152) && ht216->isabus) { + if (addr == 0x105) + return ht216->extensions; + } switch (addr) { case 0x3c4: @@ -523,6 +539,11 @@ ht216_in(uint16_t addr, void *p) return sc1148x_ramdac_in(addr, 0, svga->ramdac, svga); return svga_in(addr, svga); + case 0x3cb: + if (ht216->id == 0x7152) + return ht216->reg_3cb; + break; + case 0x3cc: return svga->miscout; @@ -1400,7 +1421,7 @@ void video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_v7vga_vlb); else if (info->flags & DEVICE_MCA) video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_v7vga_mca); - else + else video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_v7vga_isa); svga_init(info, svga, ht216, mem_size, @@ -1476,11 +1497,11 @@ void mem_mapping_disable(&ht216->linear_mapping); ht216->id = info->local; - ht216->isabus = (info->flags & DEVICE_ISA); - ht216->mca = (info->flags & DEVICE_MCA); - - io_sethandler(0x03c0, 0x0020, ht216_in, NULL, NULL, ht216_out, NULL, NULL, ht216); - io_sethandler(0x46e8, 0x0001, ht216_in, NULL, NULL, ht216_out, NULL, NULL, ht216); + ht216->isabus = (info->flags & DEVICE_ISA); + ht216->mca = (info->flags & DEVICE_MCA); + + io_sethandler(0x03c0, 0x0020, ht216_in, NULL, NULL, ht216_out, NULL, NULL, ht216); + io_sethandler(0x46e8, 0x0001, ht216_in, NULL, NULL, ht216_out, NULL, NULL, ht216); svga->bpp = 8; svga->miscout = 1; @@ -1488,6 +1509,9 @@ void if (ht216->id == 0x7861) ht216->ht_regs[0xb4] = 0x08; /*32-bit DRAM bus*/ + if (ht216->id == 0x7152) + ht216->reg_3cb = 0x20; + /* Initialize the cursor pointer towards the end of its segment, needed for ht256sf.drv to work correctly when Windows 3.1 is started after boot. */ ht216->ht_regs[0x94] = 0xff; diff --git a/src/video/vid_sc1148x_ramdac.c b/src/video/vid_sc1148x_ramdac.c index 06df1188b..046624bf4 100644 --- a/src/video/vid_sc1148x_ramdac.c +++ b/src/video/vid_sc1148x_ramdac.c @@ -43,10 +43,10 @@ sc1148x_ramdac_out(uint16_t addr, int rs2, uint8_t val, void *p, svga_t *svga) { sc1148x_ramdac_t *ramdac = (sc1148x_ramdac_t *) p; uint8_t rs = (addr & 0x03); - rs |= ((!!rs2) << 2); + rs |= ((!!rs2) << 2); - switch (addr) { - case 0x3c6: + switch (addr) { + case 0x3c6: if (ramdac->state == 4) { ramdac->state = 0; ramdac->ctrl = val; @@ -55,22 +55,20 @@ sc1148x_ramdac_out(uint16_t addr, int rs2, uint8_t val, void *p, svga_t *svga) svga->bpp = 16; else svga->bpp = 15; - } else { + } else svga->bpp = 8; - } svga_recalctimings(svga); return; } ramdac->state = 0; break; - - case 0x3c7: - case 0x3c8: - case 0x3c9: + + case 0x3c7: case 0x3c8: + case 0x3c9: ramdac->state = 0; svga_out(addr, val, svga); break; - } + } } @@ -78,35 +76,34 @@ uint8_t sc1148x_ramdac_in(uint16_t addr, int rs2, void *p, svga_t *svga) { sc1148x_ramdac_t *ramdac = (sc1148x_ramdac_t *) p; - uint8_t temp = 0xff; + uint8_t ret = 0xff; uint8_t rs = (addr & 0x03); - rs |= ((!!rs2) << 2); - - switch (addr) { + rs |= ((!!rs2) << 2); + + switch (addr) { case 0x3c6: if (ramdac->state == 4) { ramdac->state = 0; - temp = ramdac->ctrl; + ret = ramdac->ctrl; if (ramdac->type == 1) { if (((ramdac->ctrl >> 5) == 1) || ((ramdac->ctrl >> 5) == 3)) - temp |= 1; + ret |= 1; else - temp &= ~1; + ret &= ~1; } - return temp; + return ret; } ramdac->state++; break; - case 0x3c7: - case 0x3c8: + case 0x3c7: case 0x3c8: case 0x3c9: - temp = svga_in(addr, svga); + ret = svga_in(addr, svga); ramdac->state = 0; break; - } + } - return temp; + return ret; } diff --git a/src/video/vid_svga.c b/src/video/vid_svga.c index b38353701..2544fd361 100644 --- a/src/video/vid_svga.c +++ b/src/video/vid_svga.c @@ -192,6 +192,8 @@ svga_out(uint16_t addr, uint8_t val, void *p) svga->dac_addr = (val + (addr & 0x01)) & 255; break; case 0x3c9: + if (svga->adv_flags & FLAG_RAMDAC_SHIFT) + val <<= 2; svga->fullchange = changeframecount; switch (svga->dac_pos) { case 0: @@ -330,6 +332,8 @@ svga_in(uint16_t addr, void *p) ret = svga->vgapal[index].b & 0x3f; break; } + if (svga->adv_flags & FLAG_RAMDAC_SHIFT) + ret >>= 2; break; case 0x3cc: ret = svga->miscout; From 90ac317a78727810d99d8a32dccc26d34331fa49 Mon Sep 17 00:00:00 2001 From: OBattler Date: Wed, 6 Oct 2021 23:09:17 +0200 Subject: [PATCH 07/56] Fixed a compile-breaking bug in vnc.c. --- src/vnc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vnc.c b/src/vnc.c index 363c6205d..440542652 100644 --- a/src/vnc.c +++ b/src/vnc.c @@ -183,7 +183,7 @@ vnc_blit(int x, int y, int w, int h) } if (screenshots) - video_screenshot((uint32_t *) rfb->frameBuffer, 0, 0, ROW_LENGTH); + video_screenshot((uint32_t *) rfb->frameBuffer, 0, 0, VNC_MAX_X); video_blit_complete(); From 6ee1fb490ac677784e3de5c88fd1cc98c2a628be Mon Sep 17 00:00:00 2001 From: OBattler Date: Wed, 6 Oct 2021 23:18:33 +0200 Subject: [PATCH 08/56] Made the video_copy stuff Unix-friendly. --- src/include/86box/video.h | 9 +++++++-- src/video/video.c | 9 +++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/include/86box/video.h b/src/include/86box/video.h index c8cff07f8..dc41a7e5c 100644 --- a/src/include/86box/video.h +++ b/src/include/86box/video.h @@ -133,10 +133,15 @@ extern int readflash; /* Function handler pointers. */ extern void (*video_recalctimings)(void); -extern void * __cdecl (*video_copy)(void *_Dst, const void *_Src, size_t _Size); - extern void video_screenshot(uint32_t *buf, int start_x, int start_y, int row_len); + +#ifdef _WIN32 +extern void * __cdecl (*video_copy)(void *_Dst, const void *_Src, size_t _Size); extern void * __cdecl video_transform_copy(void *_Dst, const void *_Src, size_t _Size); +#ele +extern void * (*video_copy)(void *__restrict _Dst, const void *__restrict _Src, size_t _Size); +extern void * video_transform_copy(void *__restrict _Dst, const void *__restrict _Src, size_t _Size); +#endif /* Table functions. */ diff --git a/src/video/video.c b/src/video/video.c index 332f8bbaf..5118b886f 100644 --- a/src/video/video.c +++ b/src/video/video.c @@ -114,7 +114,11 @@ static const video_timings_t *vid_timings; static uint32_t cga_2_table[16]; static uint8_t thread_run = 0; +#ifdef _WIN32 void * __cdecl (*video_copy)(void *_Dst, const void *_Src, size_t _Size) = memcpy; +#else +void * (*video_copy)(void *__restrict, const void *__restrict, size_t); +#endif PALETTE cgapal = { @@ -419,8 +423,13 @@ video_screenshot(uint32_t *buf, int start_x, int start_y, int row_len) } +#ifdef _WIN32 void * __cdecl video_transform_copy(void *_Dst, const void *_Src, size_t _Size) +#else +void * +video_transform_copy(void *__restrict _Dst, const void *__restrict _Src, size_t _Size) +#endif { int i; uint32_t *dest_ex = (uint32_t *) _Dst; From fc9ae687a1aa2918ae070cb6177f98c0a427b0de Mon Sep 17 00:00:00 2001 From: OBattler Date: Wed, 6 Oct 2021 23:41:36 +0200 Subject: [PATCH 09/56] Fixed video.h. --- src/include/86box/video.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/86box/video.h b/src/include/86box/video.h index dc41a7e5c..63f854fb9 100644 --- a/src/include/86box/video.h +++ b/src/include/86box/video.h @@ -138,7 +138,7 @@ extern void video_screenshot(uint32_t *buf, int start_x, int start_y, int row_le #ifdef _WIN32 extern void * __cdecl (*video_copy)(void *_Dst, const void *_Src, size_t _Size); extern void * __cdecl video_transform_copy(void *_Dst, const void *_Src, size_t _Size); -#ele +#else extern void * (*video_copy)(void *__restrict _Dst, const void *__restrict _Src, size_t _Size); extern void * video_transform_copy(void *__restrict _Dst, const void *__restrict _Src, size_t _Size); #endif From 66db0f11a48cbea17765b67e932f48c76bdc015c Mon Sep 17 00:00:00 2001 From: OBattler Date: Thu, 7 Oct 2021 01:15:02 +0200 Subject: [PATCH 10/56] Implemented reset handlers for Cirrus, S3, and S3 ViRGE cards. --- src/video/vid_cl54xx.c | 143 ++++++++++++++++++------ src/video/vid_s3.c | 230 +++++++++++++++++++++++++++++++-------- src/video/vid_s3_virge.c | 108 +++++++++++++++--- 3 files changed, 390 insertions(+), 91 deletions(-) diff --git a/src/video/vid_cl54xx.c b/src/video/vid_cl54xx.c index c1eb9ac2d..730bfe503 100644 --- a/src/video/vid_cl54xx.c +++ b/src/video/vid_cl54xx.c @@ -212,7 +212,7 @@ typedef struct gd54xx_t uint8_t fc; /* Feature Connector */ - int card; + int card, id; uint8_t pos_regs[8]; @@ -3727,6 +3727,78 @@ gd5428_mca_feedb(void *p) return 1; } +static void +gd54xx_reset(void *priv) +{ + gd54xx_t *gd54xx = (gd54xx_t *) priv; + svga_t *svga = &gd54xx->svga; + + memset(svga->crtc, 0x00, sizeof(svga->crtc)); + memset(svga->seqregs, 0x00, sizeof(svga->seqregs)); + memset(svga->gdcreg, 0x00, sizeof(svga->gdcreg)); + svga->crtc[0] = 63; + svga->crtc[6] = 255; + svga->dispontime = 1000ull << 32; + svga->dispofftime = 1000ull << 32; + svga->bpp = 8; + + io_removehandler(0x03c0, 0x0020, gd54xx_in, NULL, NULL, gd54xx_out, NULL, NULL, gd54xx); + io_sethandler(0x03c0, 0x0020, gd54xx_in, NULL, NULL, gd54xx_out, NULL, NULL, gd54xx); + + mem_mapping_disable(&gd54xx->vgablt_mapping); + if (gd54xx->has_bios) + mem_mapping_disable(&gd54xx->bios_rom.mapping); + + memset(gd54xx->pci_regs, 0x00, 256); + + gd543x_recalc_mapping(gd54xx); + + mem_mapping_set_p(&svga->mapping, gd54xx); + mem_mapping_disable(&gd54xx->mmio_mapping); + mem_mapping_disable(&gd54xx->linear_mapping); + mem_mapping_disable(&gd54xx->aperture2_mapping); + mem_mapping_disable(&gd54xx->vgablt_mapping); + + svga->hwcursor.yoff = svga->hwcursor.xoff = 0; + + if (gd54xx->id >= CIRRUS_ID_CLGD5420) { + gd54xx->vclk_n[0] = 0x4a; + gd54xx->vclk_d[0] = 0x2b; + gd54xx->vclk_n[1] = 0x5b; + gd54xx->vclk_d[1] = 0x2f; + gd54xx->vclk_n[2] = 0x45; + gd54xx->vclk_d[2] = 0x30; + gd54xx->vclk_n[3] = 0x7e; + gd54xx->vclk_d[3] = 0x33; + } else { + gd54xx->vclk_n[0] = 0x66; + gd54xx->vclk_d[0] = 0x3b; + gd54xx->vclk_n[1] = 0x5b; + gd54xx->vclk_d[1] = 0x2f; + gd54xx->vclk_n[2] = 0x45; + gd54xx->vclk_d[2] = 0x2c; + gd54xx->vclk_n[3] = 0x7e; + gd54xx->vclk_d[3] = 0x33; + } + + svga->extra_banks[1] = 0x8000; + + gd54xx->pci_regs[PCI_REG_COMMAND] = 7; + + gd54xx->pci_regs[0x30] = 0x00; + gd54xx->pci_regs[0x32] = 0x0c; + gd54xx->pci_regs[0x33] = 0x00; + + svga->crtc[0x27] = gd54xx->id; + + svga->seqregs[6] = 0x0f; + if (svga->crtc[0x27] >= CIRRUS_ID_CLGD5429) + gd54xx->unlocked = 1; + else + gd54xx->unlocked = 0; +} + + static void *gd54xx_init(const device_t *info) { @@ -3745,8 +3817,9 @@ static void gd54xx->rev = 0; gd54xx->has_bios = 1; - switch (id) { - + gd54xx->id = id; + + switch (id) { case CIRRUS_ID_CLGD5401: romfn = BIOS_GD5401_PATH; break; @@ -3921,14 +3994,17 @@ static void gd5480_vgablt_write, gd5480_vgablt_writew, NULL, NULL, MEM_MAPPING_EXTERNAL, gd54xx); } + io_sethandler(0x03c0, 0x0020, gd54xx_in, NULL, NULL, gd54xx_out, NULL, NULL, gd54xx); + + if (gd54xx->pci && id >= CIRRUS_ID_CLGD5430) + pci_add_card(PCI_ADD_VIDEO, cl_pci_read, cl_pci_write, gd54xx); + mem_mapping_set_p(&svga->mapping, gd54xx); mem_mapping_disable(&gd54xx->mmio_mapping); mem_mapping_disable(&gd54xx->linear_mapping); mem_mapping_disable(&gd54xx->aperture2_mapping); mem_mapping_disable(&gd54xx->vgablt_mapping); - io_sethandler(0x03c0, 0x0020, gd54xx_in, NULL, NULL, gd54xx_out, NULL, NULL, gd54xx); - svga->hwcursor.yoff = svga->hwcursor.xoff = 0; if (id >= CIRRUS_ID_CLGD5420) { @@ -3953,15 +4029,12 @@ static void svga->extra_banks[1] = 0x8000; - if (gd54xx->pci && id >= CIRRUS_ID_CLGD5430) - pci_add_card(PCI_ADD_VIDEO, cl_pci_read, cl_pci_write, gd54xx); - gd54xx->pci_regs[PCI_REG_COMMAND] = 7; gd54xx->pci_regs[0x30] = 0x00; gd54xx->pci_regs[0x32] = 0x0c; gd54xx->pci_regs[0x33] = 0x00; - + svga->crtc[0x27] = id; svga->seqregs[6] = 0x0f; @@ -4260,7 +4333,7 @@ const device_t gd5401_isa_device = DEVICE_ISA, CIRRUS_ID_CLGD5401, gd54xx_init, gd54xx_close, - NULL, + gd54xx_reset, { gd5401_available }, gd54xx_speed_changed, gd54xx_force_redraw, @@ -4273,7 +4346,7 @@ const device_t gd5402_isa_device = DEVICE_ISA, CIRRUS_ID_CLGD5402, gd54xx_init, gd54xx_close, - NULL, + gd54xx_reset, { gd5402_available }, gd54xx_speed_changed, gd54xx_force_redraw, @@ -4286,7 +4359,7 @@ const device_t gd5402_onboard_device = DEVICE_AT | DEVICE_ISA, CIRRUS_ID_CLGD5402 | 0x200, gd54xx_init, gd54xx_close, - NULL, + gd54xx_reset, { NULL }, gd54xx_speed_changed, gd54xx_force_redraw, @@ -4299,7 +4372,7 @@ const device_t gd5420_isa_device = DEVICE_AT | DEVICE_ISA, CIRRUS_ID_CLGD5420, gd54xx_init, gd54xx_close, - NULL, + gd54xx_reset, { gd5420_available }, gd54xx_speed_changed, gd54xx_force_redraw, @@ -4311,7 +4384,7 @@ const device_t gd5422_isa_device = { DEVICE_AT | DEVICE_ISA, CIRRUS_ID_CLGD5422, gd54xx_init, gd54xx_close, - NULL, + gd54xx_reset, { gd5422_available }, /* Common BIOS between 5422 and 5424 */ gd54xx_speed_changed, gd54xx_force_redraw, @@ -4323,7 +4396,7 @@ const device_t gd5424_vlb_device = { DEVICE_VLB, CIRRUS_ID_CLGD5424, gd54xx_init, gd54xx_close, - NULL, + gd54xx_reset, { gd5422_available }, /* Common BIOS between 5422 and 5424 */ gd54xx_speed_changed, gd54xx_force_redraw, @@ -4337,7 +4410,7 @@ const device_t gd5426_vlb_device = CIRRUS_ID_CLGD5426, gd54xx_init, gd54xx_close, - NULL, + gd54xx_reset, { gd5426_available }, gd54xx_speed_changed, gd54xx_force_redraw, @@ -4351,7 +4424,7 @@ const device_t gd5426_onboard_device = CIRRUS_ID_CLGD5426 | 0x200, gd54xx_init, gd54xx_close, - NULL, + gd54xx_reset, { NULL }, gd54xx_speed_changed, gd54xx_force_redraw, @@ -4365,7 +4438,7 @@ const device_t gd5428_isa_device = CIRRUS_ID_CLGD5428, gd54xx_init, gd54xx_close, - NULL, + gd54xx_reset, { gd5428_isa_available }, gd54xx_speed_changed, gd54xx_force_redraw, @@ -4379,7 +4452,7 @@ const device_t gd5428_vlb_device = CIRRUS_ID_CLGD5428, gd54xx_init, gd54xx_close, - NULL, + gd54xx_reset, { gd5428_available }, gd54xx_speed_changed, gd54xx_force_redraw, @@ -4393,7 +4466,7 @@ const device_t gd5428_mca_device = CIRRUS_ID_CLGD5428, gd54xx_init, gd54xx_close, - NULL, + gd54xx_reset, { gd5428_mca_available }, gd54xx_speed_changed, gd54xx_force_redraw, @@ -4407,7 +4480,7 @@ const device_t gd5428_onboard_device = CIRRUS_ID_CLGD5428, gd54xx_init, gd54xx_close, - NULL, + gd54xx_reset, { gd5428_isa_available }, gd54xx_speed_changed, gd54xx_force_redraw, @@ -4421,7 +4494,7 @@ const device_t gd5429_isa_device = CIRRUS_ID_CLGD5429, gd54xx_init, gd54xx_close, - NULL, + gd54xx_reset, { gd5429_available }, gd54xx_speed_changed, gd54xx_force_redraw, @@ -4435,7 +4508,7 @@ const device_t gd5429_vlb_device = CIRRUS_ID_CLGD5429, gd54xx_init, gd54xx_close, - NULL, + gd54xx_reset, { gd5429_available }, gd54xx_speed_changed, gd54xx_force_redraw, @@ -4449,7 +4522,7 @@ const device_t gd5430_vlb_device = CIRRUS_ID_CLGD5430, gd54xx_init, gd54xx_close, - NULL, + gd54xx_reset, { gd5430_vlb_available }, gd54xx_speed_changed, gd54xx_force_redraw, @@ -4463,7 +4536,7 @@ const device_t gd5430_pci_device = CIRRUS_ID_CLGD5430, gd54xx_init, gd54xx_close, - NULL, + gd54xx_reset, { gd5430_pci_available }, gd54xx_speed_changed, gd54xx_force_redraw, @@ -4477,7 +4550,7 @@ const device_t gd5434_isa_device = CIRRUS_ID_CLGD5434, gd54xx_init, gd54xx_close, - NULL, + gd54xx_reset, { gd5434_available }, gd54xx_speed_changed, gd54xx_force_redraw, @@ -4491,7 +4564,7 @@ const device_t gd5434_onboard_pci_device = CIRRUS_ID_CLGD5434 | 0x200, gd54xx_init, gd54xx_close, - NULL, + gd54xx_reset, { NULL }, gd54xx_speed_changed, gd54xx_force_redraw, @@ -4505,7 +4578,7 @@ const device_t gd5434_vlb_device = CIRRUS_ID_CLGD5434, gd54xx_init, gd54xx_close, - NULL, + gd54xx_reset, { gd5434_available }, gd54xx_speed_changed, gd54xx_force_redraw, @@ -4519,7 +4592,7 @@ const device_t gd5434_pci_device = CIRRUS_ID_CLGD5434, gd54xx_init, gd54xx_close, - NULL, + gd54xx_reset, { gd5434_available }, gd54xx_speed_changed, gd54xx_force_redraw, @@ -4533,7 +4606,7 @@ const device_t gd5436_pci_device = CIRRUS_ID_CLGD5436, gd54xx_init, gd54xx_close, - NULL, + gd54xx_reset, { gd5436_available }, gd54xx_speed_changed, gd54xx_force_redraw, @@ -4547,7 +4620,7 @@ const device_t gd5440_onboard_pci_device = CIRRUS_ID_CLGD5440 | 0x600, gd54xx_init, gd54xx_close, - NULL, + gd54xx_reset, { NULL }, gd54xx_speed_changed, gd54xx_force_redraw, @@ -4561,7 +4634,7 @@ const device_t gd5440_pci_device = CIRRUS_ID_CLGD5440 | 0x400, gd54xx_init, gd54xx_close, - NULL, + gd54xx_reset, { gd5440_available }, gd54xx_speed_changed, gd54xx_force_redraw, @@ -4575,7 +4648,7 @@ const device_t gd5446_pci_device = CIRRUS_ID_CLGD5446, gd54xx_init, gd54xx_close, - NULL, + gd54xx_reset, { gd5446_available }, gd54xx_speed_changed, gd54xx_force_redraw, @@ -4589,7 +4662,7 @@ const device_t gd5446_stb_pci_device = CIRRUS_ID_CLGD5446 | 0x100, gd54xx_init, gd54xx_close, - NULL, + gd54xx_reset, { gd5446_stb_available }, gd54xx_speed_changed, gd54xx_force_redraw, @@ -4603,7 +4676,7 @@ const device_t gd5480_pci_device = CIRRUS_ID_CLGD5480, gd54xx_init, gd54xx_close, - NULL, + gd54xx_reset, { gd5480_available }, gd54xx_speed_changed, gd54xx_force_redraw, diff --git a/src/video/vid_s3.c b/src/video/vid_s3.c index d516ebe70..84f49425a 100644 --- a/src/video/vid_s3.c +++ b/src/video/vid_s3.c @@ -351,6 +351,8 @@ typedef struct s3_t uint8_t thread_run, serialport; void *i2c, *ddc; + + int vram; } s3_t; #define INT_VSY (1 << 0) @@ -6382,6 +6384,147 @@ static int vram_sizes[] = 3 /*8 MB*/ }; + +static void s3_reset(void *priv) +{ + s3_t *s3 = (s3_t *) priv; + svga_t *svga = &s3->svga; + + memset(svga->crtc, 0x00, sizeof(svga->crtc)); + svga->crtc[0] = 63; + svga->crtc[6] = 255; + svga->dispontime = 1000ull << 32; + svga->dispofftime = 1000ull << 32; + svga->bpp = 8; + + if (s3->pci) + svga->crtc[0x36] = 2 | (3 << 2) | (1 << 4); + else if (s3->vlb) + svga->crtc[0x36] = 1 | (3 << 2) | (1 << 4); + else + svga->crtc[0x36] = 3 | (1 << 4); + + if (s3->chip >= S3_86C928) + svga->crtc[0x36] |= (vram_sizes[s3->vram] << 5); + else + svga->crtc[0x36] |= ((s3->vram == 1) ? 0x00 : 0x20) | 0x80; + + svga->crtc[0x37] = 1 | (7 << 5); + + if (s3->chip >= S3_86C928) + svga->crtc[0x37] |= 0x04; + + s3_io_set(s3); + + memset(s3->pci_regs, 0x00, 256); + + s3->pci_regs[PCI_REG_COMMAND] = 7; + + s3->pci_regs[0x30] = 0x00; + s3->pci_regs[0x32] = 0x0c; + s3->pci_regs[0x33] = 0x00; + + switch(s3->card_type) { + case S3_MIROCRYSTAL8S_805: + case S3_MIROCRYSTAL10SD_805: + svga->crtc[0x5a] = 0x0a; + svga->getclock = sdac_getclock; + break; + + case S3_SPEA_MIRAGE_86C801: + case S3_SPEA_MIRAGE_86C805: + svga->crtc[0x5a] = 0x0a; + break; + + case S3_PHOENIX_86C801: + case S3_PHOENIX_86C805: + svga->crtc[0x5a] = 0x0a; + break; + + case S3_METHEUS_86C928: + svga->crtc[0x5a] = 0x0a; + break; + + case S3_PARADISE_BAHAMAS64: + case S3_PHOENIX_VISION864: + case S3_MIROCRYSTAL20SD_864: + svga->crtc[0x5a] = 0x0a; + break; + + case S3_DIAMOND_STEALTH64_964: + case S3_ELSAWIN2KPROX_964: + case S3_MIROCRYSTAL20SV_964: + svga->crtc[0x5a] = 0x0a; + break; + + case S3_ELSAWIN2KPROX: + case S3_SPEA_MERCURY_P64V: + case S3_MIROVIDEO40SV_ERGO_968: + case S3_PHOENIX_VISION968: + if (s3->pci) { + svga->crtc[0x53] = 0x18; + svga->crtc[0x58] = 0x10; + svga->crtc[0x59] = 0x70; + svga->crtc[0x5a] = 0x00; + svga->crtc[0x6c] = 1; + } else { + svga->crtc[0x53] = 0x00; + svga->crtc[0x59] = 0x00; + svga->crtc[0x5a] = 0x0a; + } + break; + + case S3_PHOENIX_VISION868: + if (s3->pci) { + svga->crtc[0x53] = 0x18; + svga->crtc[0x58] = 0x10; + svga->crtc[0x59] = 0x70; + svga->crtc[0x5a] = 0x00; + svga->crtc[0x6c] = 1; + } else { + svga->crtc[0x53] = 0x00; + svga->crtc[0x59] = 0x00; + svga->crtc[0x5a] = 0x0a; + } + break; + + case S3_PHOENIX_TRIO64: + case S3_PHOENIX_TRIO64_ONBOARD: + case S3_PHOENIX_TRIO64VPLUS: + case S3_PHOENIX_TRIO64VPLUS_ONBOARD: + case S3_DIAMOND_STEALTH64_764: + case S3_SPEA_MIRAGE_P64: + case S3_NUMBER9_9FX: + if (s3->card_type == S3_PHOENIX_TRIO64VPLUS || s3->card_type == S3_PHOENIX_TRIO64VPLUS_ONBOARD) + svga->crtc[0x53] = 0x08; + break; + + case S3_TRIO64V2_DX: + svga->crtc[0x53] = 0x08; + svga->crtc[0x59] = 0x70; + svga->crtc[0x5a] = 0x00; + svga->crtc[0x6c] = 1; + s3->pci_regs[0x05] = 0; + s3->pci_regs[0x06] = 0; + s3->pci_regs[0x07] = 2; + s3->pci_regs[0x3d] = 1; + s3->pci_regs[0x3e] = 4; + s3->pci_regs[0x3f] = 0xff; + break; + } + + if (s3->has_bios) { + if (s3->pci) + mem_mapping_disable(&s3->bios_rom.mapping); + } + + s3_updatemapping(s3); + + mem_mapping_disable(&s3->mmio_mapping); + mem_mapping_disable(&s3->new_mmio_mapping); +} + + static void *s3_init(const device_t *info) { const char *bios_fn; @@ -6608,6 +6751,7 @@ static void *s3_init(const device_t *info) else vram_size = 512 << 10; s3->vram_mask = vram_size - 1; + s3->vram = vram; s3->has_bios = (bios_fn != NULL); if (s3->has_bios) { @@ -7281,7 +7425,7 @@ const device_t s3_orchid_86c911_isa_device = S3_ORCHID_86C911, s3_init, s3_close, - NULL, + s3_reset, { s3_orchid_86c911_available }, s3_speed_changed, s3_force_redraw, @@ -7295,7 +7439,7 @@ const device_t s3_diamond_stealth_vram_isa_device = S3_DIAMOND_STEALTH_VRAM, s3_init, s3_close, - NULL, + s3_reset, { s3_diamond_stealth_vram_available }, s3_speed_changed, s3_force_redraw, @@ -7309,7 +7453,7 @@ const device_t s3_ami_86c924_isa_device = S3_AMI_86C924, s3_init, s3_close, - NULL, + s3_reset, { s3_ami_86c924_available }, s3_speed_changed, s3_force_redraw, @@ -7323,7 +7467,7 @@ const device_t s3_spea_mirage_86c801_isa_device = S3_SPEA_MIRAGE_86C801, s3_init, s3_close, - NULL, + s3_reset, { s3_spea_mirage_86c801_available }, s3_speed_changed, s3_force_redraw, @@ -7337,7 +7481,7 @@ const device_t s3_spea_mirage_86c805_vlb_device = S3_SPEA_MIRAGE_86C805, s3_init, s3_close, - NULL, + s3_reset, { s3_spea_mirage_86c805_available }, s3_speed_changed, s3_force_redraw, @@ -7351,7 +7495,7 @@ const device_t s3_mirocrystal_8s_805_vlb_device = S3_MIROCRYSTAL8S_805, s3_init, s3_close, - NULL, + s3_reset, { s3_mirocrystal_8s_805_available }, s3_speed_changed, s3_force_redraw, @@ -7366,7 +7510,7 @@ const device_t s3_mirocrystal_10sd_805_vlb_device = S3_MIROCRYSTAL10SD_805, s3_init, s3_close, - NULL, + s3_reset, { s3_mirocrystal_10sd_805_available }, s3_speed_changed, s3_force_redraw, @@ -7380,7 +7524,7 @@ const device_t s3_phoenix_86c801_isa_device = S3_PHOENIX_86C801, s3_init, s3_close, - NULL, + s3_reset, { s3_phoenix_86c80x_available }, s3_speed_changed, s3_force_redraw, @@ -7394,7 +7538,7 @@ const device_t s3_phoenix_86c805_vlb_device = S3_PHOENIX_86C805, s3_init, s3_close, - NULL, + s3_reset, { s3_phoenix_86c80x_available }, s3_speed_changed, s3_force_redraw, @@ -7408,7 +7552,7 @@ const device_t s3_metheus_86c928_isa_device = S3_METHEUS_86C928, s3_init, s3_close, - NULL, + s3_reset, { s3_metheus_86c928_available }, s3_speed_changed, s3_force_redraw, @@ -7422,7 +7566,7 @@ const device_t s3_metheus_86c928_vlb_device = S3_METHEUS_86C928, s3_init, s3_close, - NULL, + s3_reset, { s3_metheus_86c928_available }, s3_speed_changed, s3_force_redraw, @@ -7436,7 +7580,7 @@ const device_t s3_mirocrystal_20sd_864_vlb_device = S3_MIROCRYSTAL20SD_864, s3_init, s3_close, - NULL, + s3_reset, { s3_mirocrystal_20sd_864_vlb_available }, s3_speed_changed, s3_force_redraw, @@ -7450,7 +7594,7 @@ const device_t s3_bahamas64_vlb_device = S3_PARADISE_BAHAMAS64, s3_init, s3_close, - NULL, + s3_reset, { s3_bahamas64_available }, s3_speed_changed, s3_force_redraw, @@ -7464,7 +7608,7 @@ const device_t s3_bahamas64_pci_device = S3_PARADISE_BAHAMAS64, s3_init, s3_close, - NULL, + s3_reset, { s3_bahamas64_available }, s3_speed_changed, s3_force_redraw, @@ -7478,7 +7622,7 @@ const device_t s3_mirocrystal_20sv_964_vlb_device = S3_MIROCRYSTAL20SV_964, s3_init, s3_close, - NULL, + s3_reset, { s3_mirocrystal_20sv_964_vlb_available }, s3_speed_changed, s3_force_redraw, @@ -7492,7 +7636,7 @@ const device_t s3_mirocrystal_20sv_964_pci_device = S3_MIROCRYSTAL20SV_964, s3_init, s3_close, - NULL, + s3_reset, { s3_mirocrystal_20sv_964_pci_available }, s3_speed_changed, s3_force_redraw, @@ -7507,7 +7651,7 @@ const device_t s3_diamond_stealth64_964_vlb_device = S3_DIAMOND_STEALTH64_964, s3_init, s3_close, - NULL, + s3_reset, { s3_diamond_stealth64_964_available }, s3_speed_changed, s3_force_redraw, @@ -7521,7 +7665,7 @@ const device_t s3_diamond_stealth64_964_pci_device = S3_DIAMOND_STEALTH64_964, s3_init, s3_close, - NULL, + s3_reset, { s3_diamond_stealth64_964_available }, s3_speed_changed, s3_force_redraw, @@ -7535,7 +7679,7 @@ const device_t s3_phoenix_vision968_pci_device = S3_PHOENIX_VISION968, s3_init, s3_close, - NULL, + s3_reset, { s3_phoenix_vision968_available }, s3_speed_changed, s3_force_redraw, @@ -7549,7 +7693,7 @@ const device_t s3_phoenix_vision968_vlb_device = S3_PHOENIX_VISION968, s3_init, s3_close, - NULL, + s3_reset, { s3_phoenix_vision968_available }, s3_speed_changed, s3_force_redraw, @@ -7563,7 +7707,7 @@ const device_t s3_mirovideo_40sv_ergo_968_pci_device = S3_MIROVIDEO40SV_ERGO_968, s3_init, s3_close, - NULL, + s3_reset, { s3_mirovideo_40sv_ergo_968_pci_available }, s3_speed_changed, s3_force_redraw, @@ -7577,7 +7721,7 @@ const device_t s3_spea_mercury_p64v_pci_device = S3_SPEA_MERCURY_P64V, s3_init, s3_close, - NULL, + s3_reset, { s3_spea_mercury_p64v_pci_available }, s3_speed_changed, s3_force_redraw, @@ -7591,7 +7735,7 @@ const device_t s3_9fx_vlb_device = S3_NUMBER9_9FX, s3_init, s3_close, - NULL, + s3_reset, { s3_9fx_available }, s3_speed_changed, s3_force_redraw, @@ -7605,7 +7749,7 @@ const device_t s3_9fx_pci_device = S3_NUMBER9_9FX, s3_init, s3_close, - NULL, + s3_reset, { s3_9fx_available }, s3_speed_changed, s3_force_redraw, @@ -7619,7 +7763,7 @@ const device_t s3_phoenix_trio32_vlb_device = S3_PHOENIX_TRIO32, s3_init, s3_close, - NULL, + s3_reset, { s3_phoenix_trio32_available }, s3_speed_changed, s3_force_redraw, @@ -7633,7 +7777,7 @@ const device_t s3_phoenix_trio32_pci_device = S3_PHOENIX_TRIO32, s3_init, s3_close, - NULL, + s3_reset, { s3_phoenix_trio32_available }, s3_speed_changed, s3_force_redraw, @@ -7647,7 +7791,7 @@ const device_t s3_diamond_stealth_se_vlb_device = S3_DIAMOND_STEALTH_SE, s3_init, s3_close, - NULL, + s3_reset, { s3_diamond_stealth_se_available }, s3_speed_changed, s3_force_redraw, @@ -7661,7 +7805,7 @@ const device_t s3_diamond_stealth_se_pci_device = S3_DIAMOND_STEALTH_SE, s3_init, s3_close, - NULL, + s3_reset, { s3_diamond_stealth_se_available }, s3_speed_changed, s3_force_redraw, @@ -7676,7 +7820,7 @@ const device_t s3_phoenix_trio64_vlb_device = S3_PHOENIX_TRIO64, s3_init, s3_close, - NULL, + s3_reset, { s3_phoenix_trio64_available }, s3_speed_changed, s3_force_redraw, @@ -7690,7 +7834,7 @@ const device_t s3_phoenix_trio64_onboard_pci_device = S3_PHOENIX_TRIO64_ONBOARD, s3_init, s3_close, - NULL, + s3_reset, { NULL }, s3_speed_changed, s3_force_redraw, @@ -7704,7 +7848,7 @@ const device_t s3_phoenix_trio64_pci_device = S3_PHOENIX_TRIO64, s3_init, s3_close, - NULL, + s3_reset, { s3_phoenix_trio64_available }, s3_speed_changed, s3_force_redraw, @@ -7718,7 +7862,7 @@ const device_t s3_phoenix_trio64vplus_onboard_pci_device = S3_PHOENIX_TRIO64VPLUS_ONBOARD, s3_init, s3_close, - NULL, + s3_reset, { NULL }, s3_speed_changed, s3_force_redraw, @@ -7732,7 +7876,7 @@ const device_t s3_phoenix_trio64vplus_pci_device = S3_PHOENIX_TRIO64VPLUS, s3_init, s3_close, - NULL, + s3_reset, { s3_phoenix_trio64vplus_available }, s3_speed_changed, s3_force_redraw, @@ -7746,7 +7890,7 @@ const device_t s3_phoenix_vision864_vlb_device = S3_PHOENIX_VISION864, s3_init, s3_close, - NULL, + s3_reset, { s3_phoenix_vision864_available }, s3_speed_changed, s3_force_redraw, @@ -7760,7 +7904,7 @@ const device_t s3_phoenix_vision864_pci_device = S3_PHOENIX_VISION864, s3_init, s3_close, - NULL, + s3_reset, { s3_phoenix_vision864_available }, s3_speed_changed, s3_force_redraw, @@ -7774,7 +7918,7 @@ const device_t s3_phoenix_vision868_vlb_device = S3_PHOENIX_VISION868, s3_init, s3_close, - NULL, + s3_reset, { s3_phoenix_vision868_available }, s3_speed_changed, s3_force_redraw, @@ -7788,7 +7932,7 @@ const device_t s3_phoenix_vision868_pci_device = S3_PHOENIX_VISION868, s3_init, s3_close, - NULL, + s3_reset, { s3_phoenix_vision868_available }, s3_speed_changed, s3_force_redraw, @@ -7802,7 +7946,7 @@ const device_t s3_diamond_stealth64_vlb_device = S3_DIAMOND_STEALTH64_764, s3_init, s3_close, - NULL, + s3_reset, { s3_diamond_stealth64_764_available }, s3_speed_changed, s3_force_redraw, @@ -7816,7 +7960,7 @@ const device_t s3_diamond_stealth64_pci_device = S3_DIAMOND_STEALTH64_764, s3_init, s3_close, - NULL, + s3_reset, { s3_diamond_stealth64_764_available }, s3_speed_changed, s3_force_redraw, @@ -7830,7 +7974,7 @@ const device_t s3_spea_mirage_p64_vlb_device = S3_SPEA_MIRAGE_P64, s3_init, s3_close, - NULL, + s3_reset, { s3_spea_mirage_p64_vlb_available }, s3_speed_changed, s3_force_redraw, @@ -7844,7 +7988,7 @@ const device_t s3_elsa_winner2000_pro_x_964_pci_device = S3_ELSAWIN2KPROX_964, s3_init, s3_close, - NULL, + s3_reset, { s3_elsa_winner2000_pro_x_964_available }, s3_speed_changed, s3_force_redraw, @@ -7858,7 +8002,7 @@ const device_t s3_elsa_winner2000_pro_x_pci_device = S3_ELSAWIN2KPROX, s3_init, s3_close, - NULL, + s3_reset, { s3_elsa_winner2000_pro_x_available }, s3_speed_changed, s3_force_redraw, @@ -7872,7 +8016,7 @@ const device_t s3_trio64v2_dx_pci_device = S3_TRIO64V2_DX, s3_init, s3_close, - NULL, + s3_reset, { s3_trio64v2_dx_available }, s3_speed_changed, s3_force_redraw, diff --git a/src/video/vid_s3_virge.c b/src/video/vid_s3_virge.c index 43d26a559..c43a1c55a 100644 --- a/src/video/vid_s3_virge.c +++ b/src/video/vid_s3_virge.c @@ -301,7 +301,7 @@ typedef struct virge_t fifo_entry_t fifo[FIFO_SIZE]; volatile int fifo_read_idx, fifo_write_idx; - int virge_busy; + int virge_busy, local; uint8_t subsys_stat, subsys_cntl, advfunc_cntl; @@ -3785,6 +3785,86 @@ static void s3_virge_pci_write(int func, int addr, uint8_t val, void *p) } } +static void s3_virge_reset(void *priv) +{ + virge_t *virge = (virge_t *) priv; + svga_t *svga = &virge->svga; + + memset(svga->crtc, 0x00, sizeof(svga->crtc)); + svga->crtc[0] = 63; + svga->crtc[6] = 255; + svga->dispontime = 1000ull << 32; + svga->dispofftime = 1000ull << 32; + svga->bpp = 8; + + io_removehandler(0x03c0, 0x0020, s3_virge_in, NULL, NULL, s3_virge_out, NULL, NULL, virge); + io_sethandler(0x03c0, 0x0020, s3_virge_in, NULL, NULL, s3_virge_out, NULL, NULL, virge); + + memset(virge->pci_regs, 0x00, 256); + + virge->pci_regs[PCI_REG_COMMAND] = 3; + virge->pci_regs[0x05] = 0; + virge->pci_regs[0x06] = 0; + virge->pci_regs[0x07] = 2; + virge->pci_regs[0x32] = 0x0c; + virge->pci_regs[0x3d] = 1; + virge->pci_regs[0x3e] = 4; + virge->pci_regs[0x3f] = 0xff; + + switch(virge->local) { + case S3_VIRGE_325: + case S3_DIAMOND_STEALTH3D_2000: + virge->svga.crtc[0x59] = 0x70; + break; + case S3_DIAMOND_STEALTH3D_3000: + virge->svga.crtc[0x59] = 0x70; + break; + case S3_VIRGE_GX2: + case S3_DIAMOND_STEALTH3D_4000: + virge->svga.crtc[0x6c] = 1; + virge->svga.crtc[0x59] = 0x70; + break; + + case S3_TRIO_3D2X: + virge->svga.crtc[0x6c] = 1; + virge->svga.crtc[0x59] = 0x70; + break; + + default: + virge->svga.crtc[0x6c] = 1; + virge->svga.crtc[0x59] = 0x70; + break; + } + + if (virge->chip >= S3_VIRGEGX2) + virge->svga.crtc[0x36] = 2 | (0 << 2) | (1 << 4) | (0 << 5); + else { + switch (virge->memory_size) { + case 2: + virge->svga.crtc[0x36] = 2 | (0 << 2) | (1 << 4) | (4 << 5); + break; + case 8: + virge->svga.crtc[0x36] = 2 | (0 << 2) | (1 << 4) | (3 << 5); + break; + case 4: + virge->svga.crtc[0x36] = 2 | (0 << 2) | (1 << 4) | (0 << 5); + break; + } + } + + virge->svga.crtc[0x37] = 1 | (7 << 5); + virge->svga.crtc[0x53] = 8; + + mem_mapping_disable(&virge->bios_rom.mapping); + + memset(virge->dmabuffer, 0, 65536); + + s3_virge_updatemapping(virge); + + mem_mapping_disable(&virge->mmio_mapping); + mem_mapping_disable(&virge->new_mmio_mapping); +} + static void *s3_virge_init(const device_t *info) { const char *bios_fn; @@ -3990,6 +4070,8 @@ static void *s3_virge_init(const device_t *info) virge->render_thread = thread_create(render_thread, virge); timer_add(&virge->tri_timer, s3_virge_tri_timer, virge, 0); + + virge->local = info->local; return virge; } @@ -4119,7 +4201,7 @@ const device_t s3_virge_325_pci_device = S3_VIRGE_325, s3_virge_init, s3_virge_close, - NULL, + s3_virge_reset, { s3_virge_325_available }, s3_virge_speed_changed, s3_virge_force_redraw, @@ -4133,7 +4215,7 @@ const device_t s3_diamond_stealth_2000_pci_device = S3_DIAMOND_STEALTH3D_2000, s3_virge_init, s3_virge_close, - NULL, + s3_virge_reset, { s3_virge_325_diamond_available }, s3_virge_speed_changed, s3_virge_force_redraw, @@ -4147,7 +4229,7 @@ const device_t s3_diamond_stealth_3000_pci_device = S3_DIAMOND_STEALTH3D_3000, s3_virge_init, s3_virge_close, - NULL, + s3_virge_reset, { s3_virge_988_diamond_available }, s3_virge_speed_changed, s3_virge_force_redraw, @@ -4161,7 +4243,7 @@ const device_t s3_virge_375_pci_device = S3_VIRGE_DX, s3_virge_init, s3_virge_close, - NULL, + s3_virge_reset, { s3_virge_375_available }, s3_virge_speed_changed, s3_virge_force_redraw, @@ -4175,7 +4257,7 @@ const device_t s3_diamond_stealth_2000pro_pci_device = S3_DIAMOND_STEALTH3D_2000PRO, s3_virge_init, s3_virge_close, - NULL, + s3_virge_reset, { s3_virge_375_diamond_available }, s3_virge_speed_changed, s3_virge_force_redraw, @@ -4189,7 +4271,7 @@ const device_t s3_virge_385_pci_device = S3_VIRGE_GX, s3_virge_init, s3_virge_close, - NULL, + s3_virge_reset, { s3_virge_385_available }, s3_virge_speed_changed, s3_virge_force_redraw, @@ -4203,7 +4285,7 @@ const device_t s3_virge_357_pci_device = S3_VIRGE_GX2, s3_virge_init, s3_virge_close, - NULL, + s3_virge_reset, { s3_virge_357_available }, s3_virge_speed_changed, s3_virge_force_redraw, @@ -4217,7 +4299,7 @@ const device_t s3_virge_357_agp_device = S3_VIRGE_GX2, s3_virge_init, s3_virge_close, - NULL, + s3_virge_reset, { s3_virge_357_available }, s3_virge_speed_changed, s3_virge_force_redraw, @@ -4231,7 +4313,7 @@ const device_t s3_diamond_stealth_4000_pci_device = S3_DIAMOND_STEALTH3D_4000, s3_virge_init, s3_virge_close, - NULL, + s3_virge_reset, { s3_virge_357_diamond_available }, s3_virge_speed_changed, s3_virge_force_redraw, @@ -4245,7 +4327,7 @@ const device_t s3_diamond_stealth_4000_agp_device = S3_DIAMOND_STEALTH3D_4000, s3_virge_init, s3_virge_close, - NULL, + s3_virge_reset, { s3_virge_357_diamond_available }, s3_virge_speed_changed, s3_virge_force_redraw, @@ -4259,7 +4341,7 @@ const device_t s3_trio3d2x_pci_device = S3_TRIO_3D2X, s3_virge_init, s3_virge_close, - NULL, + s3_virge_reset, { s3_trio3d2x_available }, s3_virge_speed_changed, s3_virge_force_redraw, @@ -4273,7 +4355,7 @@ const device_t s3_trio3d2x_agp_device = S3_TRIO_3D2X, s3_virge_init, s3_virge_close, - NULL, + s3_virge_reset, { s3_trio3d2x_available }, s3_virge_speed_changed, s3_virge_force_redraw, From ce5a1ed05477d0e89e3658ba6acaba7b5490a609 Mon Sep 17 00:00:00 2001 From: dob205 Date: Thu, 7 Oct 2021 10:00:11 +0200 Subject: [PATCH 11/56] Fixes for library search on macOS Fixes the screenshot function on macOS if the user installed libpng via homebrew. Previously it would link to older libraries if those were installed, while at runtime it decided to run the version installed by homebrew. --- src/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a2bead497..47cb558db 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -63,6 +63,11 @@ if(MINGW) set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" ".dll.a") endif() +if(APPLE) + # Force using the newest library if it's installed by homebrew + set(CMAKE_FIND_FRAMEWORK LAST) +endif() + find_package(Freetype REQUIRED) include_directories(${FREETYPE_INCLUDE_DIRS}) From 3d5096753de45d03d34066129d7f78b686002a6c Mon Sep 17 00:00:00 2001 From: OBattler Date: Thu, 7 Oct 2021 15:44:56 +0200 Subject: [PATCH 12/56] Fixed the Cirrus Logic CL-GD 54xx reset handler to avoid excess mapping disables on non-PCI cards. --- src/video/vid_cl54xx.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/video/vid_cl54xx.c b/src/video/vid_cl54xx.c index 730bfe503..7ba2efb3c 100644 --- a/src/video/vid_cl54xx.c +++ b/src/video/vid_cl54xx.c @@ -3746,19 +3746,20 @@ gd54xx_reset(void *priv) io_sethandler(0x03c0, 0x0020, gd54xx_in, NULL, NULL, gd54xx_out, NULL, NULL, gd54xx); mem_mapping_disable(&gd54xx->vgablt_mapping); - if (gd54xx->has_bios) + if (gd54xx->has_bios && gd54xx->pci) mem_mapping_disable(&gd54xx->bios_rom.mapping); memset(gd54xx->pci_regs, 0x00, 256); - gd543x_recalc_mapping(gd54xx); - mem_mapping_set_p(&svga->mapping, gd54xx); mem_mapping_disable(&gd54xx->mmio_mapping); mem_mapping_disable(&gd54xx->linear_mapping); mem_mapping_disable(&gd54xx->aperture2_mapping); mem_mapping_disable(&gd54xx->vgablt_mapping); + gd543x_recalc_mapping(gd54xx); + gd54xx_recalc_banking(gd54xx); + svga->hwcursor.yoff = svga->hwcursor.xoff = 0; if (gd54xx->id >= CIRRUS_ID_CLGD5420) { From 430592ef2b20b5a32ee00e1e7e4fda56b0b7055a Mon Sep 17 00:00:00 2001 From: OBattler Date: Thu, 7 Oct 2021 15:48:21 +0200 Subject: [PATCH 13/56] ACPI suspend now supports suspend type 6 (officially reserved but used by eg. the ASUS P2B-LS) for soft power off. --- src/acpi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/acpi.c b/src/acpi.c index ef336105f..a287b15d5 100644 --- a/src/acpi.c +++ b/src/acpi.c @@ -646,6 +646,7 @@ acpi_reg_write_common_regs(int size, uint16_t addr, uint8_t val, void *p) sus_typ = (val >> 2) & 7; switch (sus_typ) { case 0: + case 6: /* Reserved according to the datasheet but used by eg. the ASUS P2B-LS. */ /* Soft power off. */ plat_power_off(); break; From f25137983019b42068cdad494d67541809e79bc4 Mon Sep 17 00:00:00 2001 From: dob205 Date: Mon, 11 Oct 2021 08:40:22 +0200 Subject: [PATCH 14/56] Adding the new icons for 86Box on Windows, part 1 of 2 Adding the new icons for 86Box on Windows, part 1 of 2: adding the .ico files themselves --- src/win/icons/86Box-gray.ico | Bin 0 -> 145162 bytes src/win/icons/86Box-green.ico | Bin 0 -> 145278 bytes src/win/icons/86Box-red.ico | Bin 0 -> 148122 bytes src/win/icons/86Box-yellow.ico | Bin 0 -> 129726 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/win/icons/86Box-gray.ico create mode 100644 src/win/icons/86Box-green.ico create mode 100644 src/win/icons/86Box-red.ico create mode 100644 src/win/icons/86Box-yellow.ico diff --git a/src/win/icons/86Box-gray.ico b/src/win/icons/86Box-gray.ico new file mode 100644 index 0000000000000000000000000000000000000000..c9cbad17d65f5d02048382355a4084816e9d7a76 GIT binary patch literal 145162 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 E1CYaK{r~^~ literal 0 HcmV?d00001 diff --git a/src/win/icons/86Box-yellow.ico b/src/win/icons/86Box-yellow.ico new file mode 100644 index 0000000000000000000000000000000000000000..c3822c89b28b16152251d84429c52e9eaddd3f26 GIT binary patch literal 129726 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 TiJm|Ny>#;zFhYkN=y3l5+Y;&m literal 0 HcmV?d00001 From c9be78ac0121df2f2879026f6b69d1d776c9b910 Mon Sep 17 00:00:00 2001 From: dob205 Date: Mon, 11 Oct 2021 08:52:10 +0200 Subject: [PATCH 15/56] Adding the new icons for 86Box on Windows, part 2 of 2 Adding the new icons for 86Box on Windows, part 2 of 2: updating the RC file depending on the build flag in MSYS and CMake --- src/win/86Box.rc | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/win/86Box.rc b/src/win/86Box.rc index 9f98c9350..05990324f 100644 --- a/src/win/86Box.rc +++ b/src/win/86Box.rc @@ -825,12 +825,19 @@ END // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. +// defining the icons depending on the build status #ifdef RELEASE_BUILD -/* Icon by Devcore - https://commons.wikimedia.org/wiki/File:Icon_PC_256x256.png */ - 10 ICON DISCARDABLE ICON_PATH "icons/86Box-RB.ico" +/* Icon by OBattler and laciba96 (green for release builds)*/ + 10 ICON DISCARDABLE ICON_PATH "icons/86Box-green.ico" +#elif BETA_BUILD +/* Icon by OBattler and laciba96 (yellow for beta builds done by Jenkins)*/ + 10 ICON DISCARDABLE ICON_PATH "icons/86Box-yellow.ico" +#elif ALPHA_BUILD +/* Icon by OBattler and laciba96 (red for alpha builds done by Jenkins)*/ + 10 ICON DISCARDABLE ICON_PATH "icons/86Box-red.ico" #else -/* Icon by Devcore - https://commons.wikimedia.org/wiki/File:Icon_PC2_256x256.png */ - 10 ICON DISCARDABLE ICON_PATH "icons/86Box.ico" +/* Icon by OBattler and laciba96 (gray for builds of branches and from the git master)*/ + 10 ICON DISCARDABLE ICON_PATH "icons/86Box-gray.ico" #endif 16 ICON DISCARDABLE ICON_PATH "icons/floppy_525.ico" 17 ICON DISCARDABLE ICON_PATH "icons/floppy_525_active.ico" From ca1a28f0e60914286b358f3dfd9ddce6dd52f082 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Wed, 13 Oct 2021 23:41:02 +0600 Subject: [PATCH 16/56] Fix message box display on Unix --- src/unix/unix.c | 48 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/src/unix/unix.c b/src/unix/unix.c index d15572dcd..8db22f39a 100644 --- a/src/unix/unix.c +++ b/src/unix/unix.c @@ -563,22 +563,48 @@ do_stop(void) int ui_msgbox(int flags, void *message) { - return ui_msgbox_header(flags, message, NULL); + return ui_msgbox_header(flags, NULL, message); } -int ui_msgbox_header(int flags, void *message, void* header) +int ui_msgbox_header(int flags, void *header, void* message) { - if (!header) header = L"86Box"; + SDL_MessageBoxData msgdata; + SDL_MessageBoxButtonData msgbtn; + if (!header) header = (flags & MBX_ANSI) ? "86Box" : L"86Box"; + if (header <= (void*)7168) header = plat_get_string(header); + if (message <= (void*)7168) message = plat_get_string(message); + msgbtn.buttonid = 1; + msgbtn.text = "OK"; + msgbtn.flags = 0; + memset(&msgdata, 0, sizeof(SDL_MessageBoxData)); + msgdata.numbuttons = 1; + msgdata.buttons = &msgbtn; + int msgflags = 0; + if (msgflags & MBX_FATAL) msgflags |= SDL_MESSAGEBOX_ERROR; + else if (msgflags & MBX_ERROR || msgflags & MBX_WARNING) msgflags |= SDL_MESSAGEBOX_WARNING; + else msgflags |= SDL_MESSAGEBOX_INFORMATION; + msgdata.flags = msgflags; if (flags & MBX_ANSI) { - fwprintf(stderr, L"%s\n", header); - fprintf(stderr, "==========================\n" - "%s\n", message); - return 0; + int button = 0; + msgdata.title = header; + msgdata.message = message; + SDL_ShowMessageBox(&msgdata, &button); + return button; } - fwprintf(stderr, L"%s\n", header); - fwprintf(stderr, L"==========================\n" - L"%s\n", plat_get_string(message)); + else + { + int button = 0; + char *res = SDL_iconv_string("UTF-8", sizeof(wchar_t) == 2 ? "UTF-16LE" : "UTF-32LE", (char *)message, wcslen(message) * sizeof(wchar_t) + sizeof(wchar_t)); + char *res2 = SDL_iconv_string("UTF-8", sizeof(wchar_t) == 2 ? "UTF-16LE" : "UTF-32LE", (char *)header, wcslen(header) * sizeof(wchar_t) + sizeof(wchar_t)); + msgdata.message = res; + msgdata.title = res2; + SDL_ShowMessageBox(&msgdata, &button); + free(res); + free(res2); + return button; + } + return 0; } @@ -973,7 +999,7 @@ int main(int argc, char** argv) SDL_Init(0); pc_init(argc, argv); if (! pc_init_modules()) { - fprintf(stderr, "No ROMs found.\n"); + ui_msgbox_header(MBX_FATAL, L"No ROMs found.", L"86Box could not find any usable ROM images.\n\nPlease download a ROM set and extract it into the \"roms\" directory."); SDL_Quit(); return 6; } From 16a8c4cb2582d55c68435ebab7077a381800ed5c Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Sat, 16 Oct 2021 00:14:26 +0600 Subject: [PATCH 17/56] Add more missing strings to Unix backend --- src/unix/unix.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/unix/unix.c b/src/unix/unix.c index 8db22f39a..0dc966802 100644 --- a/src/unix/unix.c +++ b/src/unix/unix.c @@ -249,6 +249,12 @@ wchar_t* plat_get_string(int i) return L"Make sure libpcap is installed and that you are on a libpcap-compatible network connection."; case IDS_2114: return L"Unable to initialize Ghostscript"; + case IDS_2063: + return L"Machine \"%hs\" is not available due to missing ROMs in the roms/machines directory. Switching to an available machine."; + case IDS_2064: + return L"Video card \"%hs\" is not available due to missing ROMs in the roms/video directory. Switching to an available video card."; + case IDS_2128: + return L"Hardware not available"; } return L""; } From 33de2779008a13bbeb840c6620bacf2ace19892f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laci=20b=C3=A1?= <84271678+laciba96@users.noreply.github.com> Date: Sat, 16 Oct 2021 18:18:34 +0200 Subject: [PATCH 18/56] Fixing the main window's icon in the title bar --- src/win/win_ui.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/win/win_ui.c b/src/win/win_ui.c index 9b1f64d5d..a099489aa 100644 --- a/src/win/win_ui.c +++ b/src/win/win_ui.c @@ -1304,13 +1304,19 @@ ui_init(int nCmdShow) wincl.lpfnWndProc = MainWindowProcedure; wincl.style = CS_DBLCLKS; /* Catch double-clicks */ wincl.cbSize = sizeof(WNDCLASSEX); - wincl.hIcon = LoadIcon(hinstance, (LPCTSTR)10); - wincl.hIconSm = LoadIcon(hinstance, (LPCTSTR)10); + wincl.hIcon = NULL; + wincl.hIconSm = NULL; wincl.hCursor = NULL; wincl.lpszMenuName = NULL; wincl.cbClsExtra = 0; wincl.cbWndExtra = 0; wincl.hbrBackground = CreateSolidBrush(RGB(0,0,0)); + + /* Load proper icons */ + wchar_t path[MAX_PATH + 1] = {0}; + GetModuleFileNameW(hinstance, path, MAX_PATH); + ExtractIconExW(path, 0, &wincl.hIcon, &wincl.hIconSm, 1); + if (! RegisterClassEx(&wincl)) return(2); wincl.lpszClassName = SUB_CLASS_NAME; From 8b5e39ceed7d887071a6f46c8c687cc3f5045751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laci=20b=C3=A1?= <84271678+laciba96@users.noreply.github.com> Date: Sat, 16 Oct 2021 18:38:32 +0200 Subject: [PATCH 19/56] Merge dynarec fixes from Cacodemon345's branch Get rid of mprotect call entirely on x64 dynamic recompiler Use mmap whenever possible on old dynamic recompiler --- src/codegen/codegen_x86-64.c | 21 +++------------------ src/codegen/codegen_x86.c | 8 +++++--- 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/src/codegen/codegen_x86-64.c b/src/codegen/codegen_x86-64.c index e3e6ced89..289411b37 100644 --- a/src/codegen/codegen_x86-64.c +++ b/src/codegen/codegen_x86-64.c @@ -21,7 +21,7 @@ #include "codegen_ops.h" #include "codegen_ops_x86-64.h" -#if defined(__linux__) || defined(__APPLE__) +#if defined(__unix__) || defined(__APPLE__) #include #include #endif @@ -63,16 +63,11 @@ static int last_ssegs; void codegen_init() { int c; - -#if defined(__linux__) || defined(__APPLE__) - void *start; - size_t len; - long pagesize = sysconf(_SC_PAGESIZE); - long pagemask = ~(pagesize - 1); -#endif #if _WIN64 codeblock = VirtualAlloc(NULL, BLOCK_SIZE * sizeof(codeblock_t), MEM_COMMIT, PAGE_EXECUTE_READWRITE); +#elif defined(__unix__) || defined(__APPLE__) + codeblock = mmap(NULL, BLOCK_SIZE * sizeof(codeblock_t), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, 0, 0); #else codeblock = malloc(BLOCK_SIZE * sizeof(codeblock_t)); #endif @@ -83,16 +78,6 @@ void codegen_init() for (c = 0; c < BLOCK_SIZE; c++) codeblock[c].valid = 0; - -#if defined(__linux__) || defined(__APPLE__) - start = (void *)((long)codeblock & pagemask); - len = ((BLOCK_SIZE * sizeof(codeblock_t)) + pagesize) & pagemask; - if (mprotect(start, len, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) - { - perror("mprotect"); - exit(-1); - } -#endif } void codegen_reset() diff --git a/src/codegen/codegen_x86.c b/src/codegen/codegen_x86.c index beb245963..f0b5169d4 100644 --- a/src/codegen/codegen_x86.c +++ b/src/codegen/codegen_x86.c @@ -61,7 +61,7 @@ #include "codegen_ops.h" #include "codegen_ops_x86.h" -#ifdef __linux__ +#ifdef __unix__ #include #include #endif @@ -1173,7 +1173,7 @@ static uint32_t gen_MEM_CHECK_WRITE_L() void codegen_init() { -#ifdef __linux__ +#ifdef __unix__ void *start; size_t len; long pagesize = sysconf(_SC_PAGESIZE); @@ -1182,6 +1182,8 @@ void codegen_init() #ifdef _WIN32 codeblock = VirtualAlloc(NULL, (BLOCK_SIZE+1) * sizeof(codeblock_t), MEM_COMMIT, PAGE_EXECUTE_READWRITE); +#elif defined __unix__ + codeblock = mmap(NULL, (BLOCK_SIZE+1) * sizeof(codeblock_t), PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, 0, 0); #else codeblock = malloc((BLOCK_SIZE+1) * sizeof(codeblock_t)); #endif @@ -1190,7 +1192,7 @@ void codegen_init() memset(codeblock, 0, (BLOCK_SIZE+1) * sizeof(codeblock_t)); memset(codeblock_hash, 0, HASH_SIZE * sizeof(codeblock_t *)); -#ifdef __linux__ +#ifdef __unix__ start = (void *)((long)codeblock & pagemask); len = (((BLOCK_SIZE+1) * sizeof(codeblock_t)) + pagesize) & pagemask; if (mprotect(start, len, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) From 17f7fa343721d9a0766d0c3e20835fa990899db6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laci=20b=C3=A1?= <84271678+laciba96@users.noreply.github.com> Date: Sat, 16 Oct 2021 19:43:50 +0200 Subject: [PATCH 20/56] Merge further fixes to the 32-bit recompiler Remove mprotect usages in 32-bit x86 dynamic recompiler --- src/codegen/codegen_x86.c | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/src/codegen/codegen_x86.c b/src/codegen/codegen_x86.c index f0b5169d4..b4ba9bc9f 100644 --- a/src/codegen/codegen_x86.c +++ b/src/codegen/codegen_x86.c @@ -1173,17 +1173,10 @@ static uint32_t gen_MEM_CHECK_WRITE_L() void codegen_init() { -#ifdef __unix__ - void *start; - size_t len; - long pagesize = sysconf(_SC_PAGESIZE); - long pagemask = ~(pagesize - 1); -#endif - #ifdef _WIN32 codeblock = VirtualAlloc(NULL, (BLOCK_SIZE+1) * sizeof(codeblock_t), MEM_COMMIT, PAGE_EXECUTE_READWRITE); #elif defined __unix__ - codeblock = mmap(NULL, (BLOCK_SIZE+1) * sizeof(codeblock_t), PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, 0, 0); + codeblock = mmap(NULL, (BLOCK_SIZE+1) * sizeof(codeblock_t), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, 0, 0); #else codeblock = malloc((BLOCK_SIZE+1) * sizeof(codeblock_t)); #endif @@ -1192,16 +1185,6 @@ void codegen_init() memset(codeblock, 0, (BLOCK_SIZE+1) * sizeof(codeblock_t)); memset(codeblock_hash, 0, HASH_SIZE * sizeof(codeblock_t *)); -#ifdef __unix__ - start = (void *)((long)codeblock & pagemask); - len = (((BLOCK_SIZE+1) * sizeof(codeblock_t)) + pagesize) & pagemask; - if (mprotect(start, len, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) - { - perror("mprotect"); - exit(-1); - } -#endif - block_current = BLOCK_SIZE; block_pos = 0; mem_abrt_rout = (uint32_t)&codeblock[block_current].data[block_pos]; From ce6b7ea07cebcb5e31f0c3b85dbc1969207a1298 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Sat, 16 Oct 2021 19:13:26 -0300 Subject: [PATCH 21/56] Fix GA-6GXU fan speeds --- src/machine/m_at_slot2.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/machine/m_at_slot2.c b/src/machine/m_at_slot2.c index cf7a29acf..bcce1d8c0 100644 --- a/src/machine/m_at_slot2.c +++ b/src/machine/m_at_slot2.c @@ -67,10 +67,7 @@ machine_at_6gxu_init(const machine_t *model) device_add(&w83977ef_device); device_add(&sst_flash_39sf020_device); spd_register(SPD_TYPE_SDRAM, 0xF, 512); - device_add(&w83782d_device); /* fans: ???, ???, System; temperatures: System, CPU, unused */ - hwm_values.fans[0] = 2000; - hwm_values.fans[1] = 2500; - hwm_values.fans[2] = 3000; + device_add(&w83782d_device); /* fans: CPU, Power, System; temperatures: System, CPU, unused */ hwm_values.temperatures[2] = 0; /* unused */ hwm_values.voltages[1] = 1500; /* VGTL */ From 9a1dd306d20d3ff0e9e36313bf83828f73a22248 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Sat, 16 Oct 2021 19:15:30 -0300 Subject: [PATCH 22/56] LM78: Add support for the additional fan divisor bit on W83782D --- src/device/hwm_lm78.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/device/hwm_lm78.c b/src/device/hwm_lm78.c index e849190cf..b9ed2e699 100644 --- a/src/device/hwm_lm78.c +++ b/src/device/hwm_lm78.c @@ -37,8 +37,8 @@ #define LM78_AS99127F_REV1 0x040000 #define LM78_AS99127F_REV2 0x080000 #define LM78_W83782D 0x100000 -#define LM78_AS99127F (LM78_AS99127F_REV1 | LM78_AS99127F_REV2) /* special mask covering both _REV1 and _REV2 */ -#define LM78_WINBOND (LM78_W83781D | LM78_AS99127F | LM78_W83782D) /* special mask covering all Winbond variants */ +#define LM78_AS99127F (LM78_AS99127F_REV1 | LM78_AS99127F_REV2) /* mask covering both _REV1 and _REV2 */ +#define LM78_WINBOND (LM78_W83781D | LM78_AS99127F | LM78_W83782D) /* mask covering all Winbond variants */ #define LM78_WINBOND_VENDOR_ID ((dev->local & LM78_AS99127F_REV1) ? 0x12c3 : 0x5ca3) #define LM78_WINBOND_BANK (dev->regs[0x4e] & 0x07) @@ -363,9 +363,12 @@ lm78_read(lm78_t *dev, uint8_t reg, uint8_t bank) ret = LM78_VOLTAGE_TO_REG(dev->values->voltages[7 + masked_reg]); else if (masked_reg == 0x27) /* temperature */ ret = dev->values->temperatures[0]; - else if ((masked_reg >= 0x28) && (masked_reg <= 0x2a)) /* fan speeds */ - ret = LM78_RPM_TO_REG(dev->values->fans[reg & 3], 1 << ((dev->regs[((reg & 3) == 2) ? 0x4b : 0x47] >> ((reg & 3) ? 6 : 4)) & 0x3)); - else if ((reg == 0x4f) && (dev->local & LM78_WINBOND)) /* two-byte vendor ID register */ + else if ((masked_reg >= 0x28) && (masked_reg <= 0x2a)) { /* fan speeds */ + ret = (dev->regs[((reg & 3) == 2) ? 0x4b : 0x47] >> ((reg & 3) ? 6 : 4)) & 0x03; /* bits [1:0] */ + if (dev->local & LM78_W83782D) + ret |= (dev->regs[0x5d] >> (3 + (reg & 3))) & 0x04; /* bit 2 */ + ret = LM78_RPM_TO_REG(dev->values->fans[reg & 3], 1 << ret); + } else if ((reg == 0x4f) && (dev->local & LM78_WINBOND)) /* two-byte vendor ID register */ ret = (dev->regs[0x4e] & 0x80) ? (uint8_t) (LM78_WINBOND_VENDOR_ID >> 8) : (uint8_t) LM78_WINBOND_VENDOR_ID; else ret = dev->regs[masked_reg]; From 128f32961d2dcfa4e9eaeea9cbde37853f1f2901 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Sat, 16 Oct 2021 19:18:09 -0300 Subject: [PATCH 23/56] Replace WCF-681 with BCM GT694VA --- src/acpi.c | 14 +++++--------- src/include/86box/machine.h | 2 +- src/machine/m_at_socket370.c | 15 ++++++++------- src/machine/machine_table.c | 8 ++++---- 4 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/acpi.c b/src/acpi.c index a287b15d5..1398edc4c 100644 --- a/src/acpi.c +++ b/src/acpi.c @@ -1568,22 +1568,18 @@ acpi_reset(void *priv) ASUS P3V4X: - Bit 15: 80-conductor cable on secondary IDE channel (active low) - Bit 5: 80-conductor cable on primary IDE channel (active low) - AEWIN WCF-681: - - Bit 3: 80-conductor cable on primary IDE channel (active low) - - Bit 2: 80-conductor cable on secondary IDE channel (active low) + BCM GT694VA: + - Bit 19: 80-conductor cable on secondary IDE channel (active low) + - Bit 17: 80-conductor cable on primary IDE channel (active low) ASUS CUV4X-LS: - Bit 2: 80-conductor cable on secondary IDE channel (active low) - Bit 1: 80-conductor cable on primary IDE channel (active low) Acorp 6VIA90AP: - Bit 3: 80-conductor cable on secondary IDE channel (active low) - Bit 1: 80-conductor cable on primary IDE channel (active low) */ - dev->regs.gpi_val = 0xffff7fc1; - if (!strcmp(machines[machine].internal_name, "ficva503a")) + dev->regs.gpi_val = 0xfff57fc1; + if (!strcmp(machines[machine].internal_name, "ficva503a") || !strcmp(machines[machine].internal_name, "6via90ap")) dev->regs.gpi_val |= 0x00000004; - if (!strcmp(machines[machine].internal_name, "6via90ap")) - dev->regs.gpi_val |= 0x00000004; - // dev->regs.gpi_val = 0xffffffe5; - // dev->regs.gpi_val = 0x00000004; } /* Power on always generates a resume event. */ diff --git a/src/include/86box/machine.h b/src/include/86box/machine.h index 69ba7645e..47f5008a8 100644 --- a/src/include/86box/machine.h +++ b/src/include/86box/machine.h @@ -559,7 +559,7 @@ extern int machine_at_awo671r_init(const machine_t *); extern int machine_at_63a_init(const machine_t *); extern int machine_at_s370sba_init(const machine_t *); extern int machine_at_apas3_init(const machine_t *); -extern int machine_at_wcf681_init(const machine_t *); +extern int machine_at_gt694va_init(const machine_t *); extern int machine_at_cuv4xls_init(const machine_t *); extern int machine_at_6via90ap_init(const machine_t *); extern int machine_at_603tcf_init(const machine_t *); diff --git a/src/machine/m_at_socket370.c b/src/machine/m_at_socket370.c index 14f5e0dca..0b0b72142 100644 --- a/src/machine/m_at_socket370.c +++ b/src/machine/m_at_socket370.c @@ -343,11 +343,11 @@ machine_at_apas3_init(const machine_t *model) int -machine_at_wcf681_init(const machine_t *model) +machine_at_gt694va_init(const machine_t *model) { int ret; - ret = bios_load_linear("roms/machines/wcf681/681osda2.bin", + ret = bios_load_linear("roms/machines/gt694va/21071100.bin", 0x000c0000, 262144, 0); if (bios_only || !ret) @@ -366,15 +366,16 @@ machine_at_wcf681_init(const machine_t *model) pci_register_slot(0x01, PCI_CARD_AGPBRIDGE, 1, 2, 3, 4); device_add(&via_apro133a_device); device_add(&via_vt82c596b_device); - device_add(&w83977tf_device); + device_add(&w83977ef_device); device_add(&keyboard_ps2_ami_pci_device); device_add(&sst_flash_39sf020_device); - spd_register(SPD_TYPE_SDRAM, 0x3, 512); - device_add(&w83781d_device); /* fans: CPU, unused, unused; temperatures: System, unused, CPU */ - hwm_values.voltages[1] = 2500; /* +2.5V */ + spd_register(SPD_TYPE_SDRAM, 0x7, 512); + device_add(&w83782d_device); /* fans: CPU, unused, unused; temperatures: System, CPU1, unused */ + hwm_values.voltages[1] = 1500; /* IN1 (unknown purpose, assumed Vtt) */ + hwm_values.fans[0] = 4500; /* BIOS does not display <4411 RPM */ hwm_values.fans[1] = 0; /* unused */ hwm_values.fans[2] = 0; /* unused */ - hwm_values.temperatures[1] = 0; /* unused */ + hwm_values.temperatures[2] = 0; /* unused */ return ret; } diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index 115e5130e..dab4bd26f 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -459,12 +459,12 @@ const machine_t machines[] = { { "[SMSC VictoryBX-66] A-Trend ATC7020BXII","atc7020bxii", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 133333333, 1300, 3500, 1.5, 8.0, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192,1048576, 8192, 255, machine_at_atc7020bxii_init, NULL }, /* VIA Apollo Pro */ - { "[VIA Apollo Pro] PC Partner APAS3", "apas3", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 100000000, 1800, 3500, 1.5, 8.0, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192, 786432, 8192, 255, machine_at_apas3_init, NULL }, - { "[VIA Apollo Pro133] ECS P6BAP", "p6bap", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 150000000, 1300, 3500, 1.5, 8.0, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192,1572864, 8192, 255, machine_at_p6bap_init, NULL }, - { "[VIA Apollo Pro133A] AEWIN WCF-681", "wcf681", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 133333333, 1300, 3500, 1.5, 8.0, /* limits assumed */ MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192,1048576, 8192, 255, machine_at_wcf681_init, NULL }, + { "[VIA Apollo Pro] PC Partner APAS3", "apas3", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 100000000, 1800, 3500, 1.5, 8.0, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192, 786432, 8192, 255, machine_at_apas3_init, NULL }, + { "[VIA Apollo Pro133] ECS P6BAP", "p6bap", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 150000000, 1300, 3500, 1.5, 8.0, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192,1572864, 8192, 255, machine_at_p6bap_init, NULL }, + { "[VIA Apollo Pro133A] BCM GT694VA", "gt694va", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 133333333, 1300, 3500, 1.5, 8.0, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192,1048576, 8192, 255, machine_at_gt694va_init, NULL }, { "[VIA Apollo Pro133A] ASUS CUV4X-LS", "cuv4xls", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 150000000, 1300, 3500, 1.5, 8.0, (MACHINE_AGP & ~MACHINE_AT) | MACHINE_BUS_PS2 | MACHINE_BUS_AC97 | MACHINE_IDE_DUAL,16384,1572864, 8192, 255, machine_at_cuv4xls_init, NULL }, { "[VIA Apollo Pro133A] Acorp 6VIA90AP", "6via90ap", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 150000000, 1300, 3500, MACHINE_MULTIPLIER_FIXED, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL | MACHINE_GAMEPORT, 8192,1572864, 8192, 255, machine_at_6via90ap_init, NULL }, - { "[VIA Apollo ProMedia] Jetway 603TCF", "603tcf", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 150000000, 1300, 3500, 1.5, 8.0, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192,1048576, 8192, 255, machine_at_603tcf_init, NULL }, + { "[VIA Apollo ProMedia] Jetway 603TCF", "603tcf", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 150000000, 1300, 3500, 1.5, 8.0, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192,1048576, 8192, 255, machine_at_603tcf_init, NULL }, /* Miscellaneous/Fake/Hypervisor machines */ { "[i440BX] Microsoft Virtual PC 2007", "vpc2007", MACHINE_TYPE_MISC, CPU_PKG_SLOT1, CPU_BLOCK(CPU_PENTIUM2, CPU_CYRIX3S), 0, 0, 0, 0, 0, 0, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192,1048576, 8192, 255, machine_at_vpc2007_init, NULL }, From b6bca8ae60b918d0d8e1f677a198d481af3616fa Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Sat, 16 Oct 2021 19:19:04 -0300 Subject: [PATCH 24/56] Remove VT8601 machine per machine_and_kb branch --- src/include/86box/machine.h | 1 - src/machine/m_at_socket370.c | 36 ------------------------------------ src/machine/machine_table.c | 1 - 3 files changed, 38 deletions(-) diff --git a/src/include/86box/machine.h b/src/include/86box/machine.h index 47f5008a8..e998545fa 100644 --- a/src/include/86box/machine.h +++ b/src/include/86box/machine.h @@ -562,7 +562,6 @@ extern int machine_at_apas3_init(const machine_t *); extern int machine_at_gt694va_init(const machine_t *); extern int machine_at_cuv4xls_init(const machine_t *); extern int machine_at_6via90ap_init(const machine_t *); -extern int machine_at_603tcf_init(const machine_t *); extern int machine_at_trinity371_init(const machine_t *); extern int machine_at_p6bap_init(const machine_t *); diff --git a/src/machine/m_at_socket370.c b/src/machine/m_at_socket370.c index 0b0b72142..87f5cd92e 100644 --- a/src/machine/m_at_socket370.c +++ b/src/machine/m_at_socket370.c @@ -457,39 +457,3 @@ machine_at_6via90ap_init(const machine_t *model) return ret; } - - -int -machine_at_603tcf_init(const machine_t *model) -{ - int ret; - - ret = bios_load_linear("roms/machines/603tcf/603tcfA4.BIN", - 0x000c0000, 262144, 0); - - if (bios_only || !ret) - return ret; - - machine_at_common_init_ex(model, 2); - - pci_init(PCI_CONFIG_TYPE_1); - pci_register_slot(0x00, PCI_CARD_NORTHBRIDGE, 0, 0, 0, 0); - pci_register_slot(0x07, PCI_CARD_SOUTHBRIDGE, 1, 2, 0, 0); - pci_register_slot(0x08, PCI_CARD_NORMAL, 1, 2, 3, 4); - pci_register_slot(0x09, PCI_CARD_NORMAL, 2, 3, 4, 1); - pci_register_slot(0x0A, PCI_CARD_NORMAL, 3, 4, 1, 2); - pci_register_slot(0x0B, PCI_CARD_NORMAL, 4, 1, 2, 3); - pci_register_slot(0x01, PCI_CARD_AGPBRIDGE, 1, 2, 3, 4); - device_add(&via_vt8601_device); - device_add(&via_vt82c686b_device); - device_add(&via_vt82c686_sio_device); - device_add(&keyboard_ps2_ami_pci_device); - device_add(&sst_flash_39sf020_device); - spd_register(SPD_TYPE_SDRAM, 0x3, 512); - device_add(&via_vt82c686_hwm_device); /* fans: 1, 2; temperatures: CPU, System, unused */ - hwm_values.temperatures[0] += 2; /* CPU offset */ - hwm_values.temperatures[1] += 2; /* System offset */ - hwm_values.temperatures[2] = 0; /* unused */ - - return ret; -} diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index dab4bd26f..6f58dff4a 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -464,7 +464,6 @@ const machine_t machines[] = { { "[VIA Apollo Pro133A] BCM GT694VA", "gt694va", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 133333333, 1300, 3500, 1.5, 8.0, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192,1048576, 8192, 255, machine_at_gt694va_init, NULL }, { "[VIA Apollo Pro133A] ASUS CUV4X-LS", "cuv4xls", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 150000000, 1300, 3500, 1.5, 8.0, (MACHINE_AGP & ~MACHINE_AT) | MACHINE_BUS_PS2 | MACHINE_BUS_AC97 | MACHINE_IDE_DUAL,16384,1572864, 8192, 255, machine_at_cuv4xls_init, NULL }, { "[VIA Apollo Pro133A] Acorp 6VIA90AP", "6via90ap", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 150000000, 1300, 3500, MACHINE_MULTIPLIER_FIXED, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL | MACHINE_GAMEPORT, 8192,1572864, 8192, 255, machine_at_6via90ap_init, NULL }, - { "[VIA Apollo ProMedia] Jetway 603TCF", "603tcf", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 150000000, 1300, 3500, 1.5, 8.0, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192,1048576, 8192, 255, machine_at_603tcf_init, NULL }, /* Miscellaneous/Fake/Hypervisor machines */ { "[i440BX] Microsoft Virtual PC 2007", "vpc2007", MACHINE_TYPE_MISC, CPU_PKG_SLOT1, CPU_BLOCK(CPU_PENTIUM2, CPU_CYRIX3S), 0, 0, 0, 0, 0, 0, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192,1048576, 8192, 255, machine_at_vpc2007_init, NULL }, From 2416fec4a2d4e081531c4d81821961cde47d7c79 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Sat, 16 Oct 2021 19:50:32 -0300 Subject: [PATCH 25/56] Add 3 GB RAM support to SPD and win_settings --- src/mem/spd.c | 26 +++++++++++++++++++++----- src/win/win_settings.c | 7 ++++++- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/mem/spd.c b/src/mem/spd.c index 32b49a101..b5400de4b 100644 --- a/src/mem/spd.c +++ b/src/mem/spd.c @@ -167,6 +167,22 @@ spd_populate(uint16_t *rows, uint8_t slot_count, uint16_t total_size, uint16_t m } +static int +spd_write_part_no(char *part_no, char *type, uint16_t size) +{ + char size_unit; + + if (size >= 1024) { + size_unit = 'G'; + size >>= 10; + } else { + size_unit = 'M'; + } + + return sprintf(part_no, EMU_NAME "-%s-%03d%c", type, size, size_unit); +} + + void spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size) { @@ -204,7 +220,7 @@ spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size) /* Register SPD devices and populate their data according to the rows. */ row = 0; - for (slot = 0; slot < SPD_MAX_SLOTS && rows[row]; slot++) { + for (slot = 0; (slot < SPD_MAX_SLOTS) && rows[row]; slot++) { if (!(slot_mask & (1 << slot))) continue; /* slot disabled */ @@ -249,8 +265,8 @@ spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size) edo_data->dram_width = 8; edo_data->spd_rev = 0x12; - sprintf(edo_data->part_no, EMU_NAME "-%s-%03dM", (ram_type == SPD_TYPE_FPM) ? "FPM" : "EDO", rows[row]); - for (i = strlen(edo_data->part_no); i < sizeof(edo_data->part_no); i++) + for (i = spd_write_part_no(edo_data->part_no, (ram_type == SPD_TYPE_FPM) ? "FPM" : "EDO", rows[row]); + i < sizeof(edo_data->part_no); i++) edo_data->part_no[i] = ' '; /* part number should be space-padded */ edo_data->rev_code[0] = BCD8(EMU_VERSION_MAJ); edo_data->rev_code[1] = BCD8(EMU_VERSION_MIN); @@ -303,8 +319,8 @@ spd_register(uint8_t ram_type, uint8_t slot_mask, uint16_t max_module_size) sdram_data->ca_hold = sdram_data->data_hold = 0x08; sdram_data->spd_rev = 0x12; - sprintf(sdram_data->part_no, EMU_NAME "-SDR-%03dM", rows[row]); - for (i = strlen(sdram_data->part_no); i < sizeof(sdram_data->part_no); i++) + for (i = spd_write_part_no(sdram_data->part_no, "SDR", rows[row]); + i < sizeof(sdram_data->part_no); i++) sdram_data->part_no[i] = ' '; /* part number should be space-padded */ sdram_data->rev_code[0] = BCD8(EMU_VERSION_MAJ); sdram_data->rev_code[1] = BCD8(EMU_VERSION_MIN); diff --git a/src/win/win_settings.c b/src/win/win_settings.c index 7e90ef1ba..4b538f31b 100644 --- a/src/win/win_settings.c +++ b/src/win/win_settings.c @@ -807,7 +807,12 @@ win_settings_machine_recalc_machine(HWND hdlg) } else { /* MB granularity */ h = GetDlgItem(hdlg, IDC_MEMSPIN); - SendMessage(h, UDM_SETRANGE, 0, (machines[temp_machine].min_ram << 6) | machines[temp_machine].max_ram >> 10); +#if (!(defined __amd64__ || defined _M_X64 || defined __aarch64__ || defined _M_ARM64)) + i = MIN(machines[temp_machine].max_ram, 2097152); +#else + i = MIN(machines[temp_machine].max_ram, 3145728); +#endif + SendMessage(h, UDM_SETRANGE, 0, (machines[temp_machine].min_ram << 6) | (i >> 10)); accel.nSec = 0; accel.nInc = machines[temp_machine].ram_granularity >> 10; From e25482db1150ae4d22207d237b6516600d3c06e0 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Sat, 16 Oct 2021 19:50:47 -0300 Subject: [PATCH 26/56] Add 3 GB RAM support to 694X machines --- src/machine/m_at_socket370.c | 6 +++--- src/machine/machine_table.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/machine/m_at_socket370.c b/src/machine/m_at_socket370.c index 87f5cd92e..acab29443 100644 --- a/src/machine/m_at_socket370.c +++ b/src/machine/m_at_socket370.c @@ -369,7 +369,7 @@ machine_at_gt694va_init(const machine_t *model) device_add(&w83977ef_device); device_add(&keyboard_ps2_ami_pci_device); device_add(&sst_flash_39sf020_device); - spd_register(SPD_TYPE_SDRAM, 0x7, 512); + spd_register(SPD_TYPE_SDRAM, 0x7, 1024); device_add(&w83782d_device); /* fans: CPU, unused, unused; temperatures: System, CPU1, unused */ hwm_values.voltages[1] = 1500; /* IN1 (unknown purpose, assumed Vtt) */ hwm_values.fans[0] = 4500; /* BIOS does not display <4411 RPM */ @@ -412,7 +412,7 @@ machine_at_cuv4xls_init(const machine_t *model) device_add(&keyboard_ps2_ami_pci_device); device_add(ics9xxx_get(ICS9250_18)); device_add(&sst_flash_39sf020_device); - spd_register(SPD_TYPE_SDRAM, 0xF, 512); + spd_register(SPD_TYPE_SDRAM, 0xF, 1024); device_add(&as99127f_device); /* fans: Chassis, CPU, Power; temperatures: MB, JTPWR, CPU */ return ret; @@ -447,7 +447,7 @@ machine_at_6via90ap_init(const machine_t *model) device_add(&keyboard_ps2_ami_pci_device); device_add(ics9xxx_get(ICS9250_18)); device_add(&sst_flash_39sf020_device); - spd_register(SPD_TYPE_SDRAM, 0x7, 512); + spd_register(SPD_TYPE_SDRAM, 0x7, 1024); device_add(&via_vt82c686_hwm_device); /* fans: CPU1, CPU2; temperatures: CPU, System, unused */ hwm_values.temperatures[0] += 2; /* CPU offset */ hwm_values.temperatures[1] += 2; /* System offset */ diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index 6f58dff4a..e5f1ea66d 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -461,9 +461,9 @@ const machine_t machines[] = { /* VIA Apollo Pro */ { "[VIA Apollo Pro] PC Partner APAS3", "apas3", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 100000000, 1800, 3500, 1.5, 8.0, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192, 786432, 8192, 255, machine_at_apas3_init, NULL }, { "[VIA Apollo Pro133] ECS P6BAP", "p6bap", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 150000000, 1300, 3500, 1.5, 8.0, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192,1572864, 8192, 255, machine_at_p6bap_init, NULL }, - { "[VIA Apollo Pro133A] BCM GT694VA", "gt694va", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 133333333, 1300, 3500, 1.5, 8.0, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192,1048576, 8192, 255, machine_at_gt694va_init, NULL }, - { "[VIA Apollo Pro133A] ASUS CUV4X-LS", "cuv4xls", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 150000000, 1300, 3500, 1.5, 8.0, (MACHINE_AGP & ~MACHINE_AT) | MACHINE_BUS_PS2 | MACHINE_BUS_AC97 | MACHINE_IDE_DUAL,16384,1572864, 8192, 255, machine_at_cuv4xls_init, NULL }, - { "[VIA Apollo Pro133A] Acorp 6VIA90AP", "6via90ap", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 150000000, 1300, 3500, MACHINE_MULTIPLIER_FIXED, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL | MACHINE_GAMEPORT, 8192,1572864, 8192, 255, machine_at_6via90ap_init, NULL }, + { "[VIA Apollo Pro133A] BCM GT694VA", "gt694va", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 133333333, 1300, 3500, 1.5, 8.0, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 16384,3145728, 8192, 255, machine_at_gt694va_init, NULL }, + { "[VIA Apollo Pro133A] ASUS CUV4X-LS", "cuv4xls", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 150000000, 1300, 3500, 1.5, 8.0, (MACHINE_AGP & ~MACHINE_AT) | MACHINE_BUS_PS2 | MACHINE_BUS_AC97 | MACHINE_IDE_DUAL,16384,4194304, 8192, 255, machine_at_cuv4xls_init, NULL }, + { "[VIA Apollo Pro133A] Acorp 6VIA90AP", "6via90ap", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 150000000, 1300, 3500, MACHINE_MULTIPLIER_FIXED, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL | MACHINE_GAMEPORT, 16384,3145728, 8192, 255, machine_at_6via90ap_init, NULL }, /* Miscellaneous/Fake/Hypervisor machines */ { "[i440BX] Microsoft Virtual PC 2007", "vpc2007", MACHINE_TYPE_MISC, CPU_PKG_SLOT1, CPU_BLOCK(CPU_PENTIUM2, CPU_CYRIX3S), 0, 0, 0, 0, 0, 0, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192,1048576, 8192, 255, machine_at_vpc2007_init, NULL }, From 5bf8aa2d58b74fc1fe8e4f966353ed93eb78dc4a Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Sat, 16 Oct 2021 20:12:49 -0300 Subject: [PATCH 27/56] Port plat_m(un)map from the VFIO branch --- src/include/86box/plat.h | 2 + src/include/86box/vid_voodoo_codegen_x86-64.h | 23 +------- src/include/86box/vid_voodoo_codegen_x86.h | 22 +------ src/mem/mem.c | 57 ++++++++++--------- src/unix/unix.c | 15 ++++- src/win/win.c | 16 +++++- 6 files changed, 66 insertions(+), 69 deletions(-) diff --git a/src/include/86box/plat.h b/src/include/86box/plat.h index 95a74f65f..d413add7d 100644 --- a/src/include/86box/plat.h +++ b/src/include/86box/plat.h @@ -105,6 +105,8 @@ extern void plat_path_slash(char *path); extern int plat_path_abs(char *path); extern int plat_dir_check(char *path); extern int plat_dir_create(char *path); +extern void *plat_mmap(size_t size, uint8_t executable); +extern void plat_munmap(void *ptr, size_t size); extern uint64_t plat_timer_read(void); extern uint32_t plat_get_ticks(void); extern uint32_t plat_get_micro_ticks(void); diff --git a/src/include/86box/vid_voodoo_codegen_x86-64.h b/src/include/86box/vid_voodoo_codegen_x86-64.h index 4e6cbcaf2..443edc2cd 100644 --- a/src/include/86box/vid_voodoo_codegen_x86-64.h +++ b/src/include/86box/vid_voodoo_codegen_x86-64.h @@ -5,16 +5,6 @@ fbzColorPath */ -#if defined(__linux__) || defined(__APPLE__) -#include -#include -#endif -#if _WIN64 -#define BITMAP windows_BITMAP -#include -#undef BITMAP -#endif - #ifdef _MSC_VER #include #else @@ -3432,11 +3422,7 @@ void voodoo_codegen_init(voodoo_t *voodoo) { int c; -#if _WIN64 - voodoo->codegen_data = VirtualAlloc(NULL, sizeof(voodoo_x86_data_t) * BLOCK_NUM * 4, MEM_COMMIT, PAGE_EXECUTE_READWRITE); -#else - voodoo->codegen_data = mmap(0, sizeof(voodoo_x86_data_t) * BLOCK_NUM*4, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, 0, 0); -#endif + voodoo->codegen_data = plat_mmap(sizeof(voodoo_x86_data_t) * BLOCK_NUM*4, 1); for (c = 0; c < 256; c++) { @@ -3462,10 +3448,5 @@ void voodoo_codegen_init(voodoo_t *voodoo) void voodoo_codegen_close(voodoo_t *voodoo) { -#if _WIN64 - VirtualFree(voodoo->codegen_data, 0, MEM_RELEASE); -#else - munmap(voodoo->codegen_data, sizeof(voodoo_x86_data_t) * BLOCK_NUM*4); -#endif + plat_munmap(voodoo->codegen_data, sizeof(voodoo_x86_data_t) * BLOCK_NUM*4); } - diff --git a/src/include/86box/vid_voodoo_codegen_x86.h b/src/include/86box/vid_voodoo_codegen_x86.h index f84ee00aa..d54a7e683 100644 --- a/src/include/86box/vid_voodoo_codegen_x86.h +++ b/src/include/86box/vid_voodoo_codegen_x86.h @@ -5,16 +5,6 @@ fbzColorPath */ -#if defined(__linux__) || defined(__APPLE__) -#include -#include -#endif -#if defined WIN32 || defined _WIN32 || defined _WIN32 -#define BITMAP windows_BITMAP -#include -#undef BITMAP -#endif - #ifdef _MSC_VER #include #else @@ -3378,11 +3368,7 @@ void voodoo_codegen_init(voodoo_t *voodoo) long pagemask = ~(pagesize - 1); #endif -#if defined WIN32 || defined _WIN32 || defined _WIN32 - voodoo->codegen_data = VirtualAlloc(NULL, sizeof(voodoo_x86_data_t) * BLOCK_NUM*4, MEM_COMMIT, PAGE_EXECUTE_READWRITE); -#else - voodoo->codegen_data = mmap(0, sizeof(voodoo_x86_data_t) * BLOCK_NUM*4, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, 0, 0); -#endif + voodoo->codegen_data = plat_mmap(sizeof(voodoo_x86_data_t) * BLOCK_NUM*4, 1); for (c = 0; c < 256; c++) { @@ -3408,9 +3394,5 @@ void voodoo_codegen_init(voodoo_t *voodoo) void voodoo_codegen_close(voodoo_t *voodoo) { -#if defined WIN32 || defined _WIN32 || defined _WIN32 - VirtualFree(voodoo->codegen_data, 0, MEM_RELEASE); -#else - munmap(voodoo->codegen_data, sizeof(voodoo_x86_data_t) * BLOCK_NUM*4); -#endif + plat_munmap(voodoo->codegen_data, sizeof(voodoo_x86_data_t) * BLOCK_NUM*4); } diff --git a/src/mem/mem.c b/src/mem/mem.c index 4a3a9145f..75ddb8c53 100644 --- a/src/mem/mem.c +++ b/src/mem/mem.c @@ -34,6 +34,7 @@ #include <86box/config.h> #include <86box/io.h> #include <86box/mem.h> +#include <86box/plat.h> #include <86box/rom.h> #ifdef USE_DYNAREC # include "codegen_public.h" @@ -128,6 +129,11 @@ static uint8_t *_mem_exec[MEM_MAPPINGS_NO]; static uint8_t ff_pccache[4] = { 0xff, 0xff, 0xff, 0xff }; static mem_state_t _mem_state[MEM_MAPPINGS_NO]; static uint32_t remap_start_addr; +#if (!(defined __amd64__ || defined _M_X64 || defined __aarch64__ || defined _M_ARM64)) +static size_t ram_size = 0, ram2_size = 0; +#else +static size_t ram_size = 0; +#endif #ifdef ENABLE_MEM_LOG @@ -1590,7 +1596,7 @@ do_mmutranslate(uint32_t addr, uint32_t *a64, int num, int write) a64[i] = (uint64_t) addr; for (i = 0; i < num; i++) { - if (cr0 >> 31) { + if (cr0 >> 31) { if (write && ((i == 0) || !(addr & 0xfff))) cond = (!page_lookup[addr >> 12] || !page_lookup[addr >> 12]->write_b); @@ -1660,7 +1666,7 @@ mem_readw_phys(uint32_t addr) p = (uint16_t *) &(map->exec[addr - map->base]); ret = *p; } else if (((addr & MEM_GRANULARITY_MASK) <= MEM_GRANULARITY_HBOUND) && (map && map->read_w)) - ret = map->read_w(addr, map->p); + ret = map->read_w(addr, map->p); else { ret = mem_readb_phys(addr + 1) << 8; ret |= mem_readb_phys(addr); @@ -1682,7 +1688,7 @@ mem_readl_phys(uint32_t addr) p = (uint32_t *) &(map->exec[addr - map->base]); ret = *p; } else if (((addr & MEM_GRANULARITY_MASK) <= MEM_GRANULARITY_QBOUND) && (map && map->read_l)) - ret = map->read_l(addr, map->p); + ret = map->read_l(addr, map->p); else { ret = mem_readw_phys(addr + 2) << 16; ret |= mem_readw_phys(addr); @@ -1740,7 +1746,7 @@ mem_writew_phys(uint32_t addr, uint16_t val) p = (uint16_t *) &(map->exec[addr - map->base]); *p = val; } else if (((addr & MEM_GRANULARITY_MASK) <= MEM_GRANULARITY_HBOUND) && (map && map->write_w)) - map->write_w(addr, val, map->p); + map->write_w(addr, val, map->p); else { mem_writeb_phys(addr, val & 0xff); mem_writeb_phys(addr + 1, (val >> 8) & 0xff); @@ -1760,7 +1766,7 @@ mem_writel_phys(uint32_t addr, uint32_t val) p = (uint32_t *) &(map->exec[addr - map->base]); *p = val; } else if (((addr & MEM_GRANULARITY_MASK) <= MEM_GRANULARITY_QBOUND) && (map && map->write_l)) - map->write_l(addr, val, map->p); + map->write_l(addr, val, map->p); else { mem_writew_phys(addr, val & 0xffff); mem_writew_phys(addr + 2, (val >> 16) & 0xffff); @@ -2606,30 +2612,34 @@ mem_reset(void) } if (ram != NULL) { - free(ram); + plat_munmap(ram, ram_size); ram = NULL; + ram_size = 0; } #if (!(defined __amd64__ || defined _M_X64 || defined __aarch64__ || defined _M_ARM64)) if (ram2 != NULL) { - free(ram2); + plat_munmap(ram2, ram2_size); ram2 = NULL; + ram2_size = 0; } -#endif if (mem_size > 2097152) - fatal("Attempting to use more than 2 GB of emulated RAM\n"); + mem_size = 2097152; +#endif m = 1024UL * mem_size; #if (!(defined __amd64__ || defined _M_X64 || defined __aarch64__ || defined _M_ARM64)) if (mem_size > 1048576) { - ram = (uint8_t *)malloc(1 << 30); /* allocate and clear the RAM block of the first 1 GB */ + ram_size = 1 << 30; + ram = (uint8_t *) plat_mmap(ram_size, 0); /* allocate and clear the RAM block of the first 1 GB */ if (ram == NULL) { fatal("Failed to allocate primary RAM block. Make sure you have enough RAM available.\n"); return; } - memset(ram, 0x00, (1 << 30)); - ram2 = (uint8_t *)malloc(m - (1 << 30)); /* allocate and clear the RAM block above 1 GB */ + memset(ram, 0x00, ram_size); + ram2_size = m - (1 << 30); + ram2 = (uint8_t *) plat_mmap(ram2_size, 0); /* allocate and clear the RAM block above 1 GB */ if (ram2 == NULL) { if (config_changed == 2) fatal(EMU_NAME " must be restarted for the memory amount change to be applied.\n"); @@ -2637,25 +2647,20 @@ mem_reset(void) fatal("Failed to allocate secondary RAM block. Make sure you have enough RAM available.\n"); return; } - memset(ram2, 0x00, m - (1 << 30)); - } else { - ram = (uint8_t *)malloc(m); /* allocate and clear the RAM block */ + memset(ram2, 0x00, ram2_size); + } else +#endif + { + ram_size = m; + ram = (uint8_t *) plat_mmap(ram_size, 0); /* allocate and clear the RAM block */ if (ram == NULL) { fatal("Failed to allocate RAM block. Make sure you have enough RAM available.\n"); return; } - memset(ram, 0x00, m); + memset(ram, 0x00, ram_size); + if (mem_size > 1048576) + ram2 = &(ram[1 << 30]); } -#else - ram = (uint8_t *)malloc(m); /* allocate and clear the RAM block */ - if (ram == NULL) { - fatal("Failed to allocate RAM block. Make sure you have enough RAM available.\n"); - return; - } - memset(ram, 0x00, m); - if (mem_size > 1048576) - ram2 = &(ram[1 << 30]); -#endif /* * Allocate the page table based on how much RAM we have. diff --git a/src/unix/unix.c b/src/unix/unix.c index 0dc966802..d0c3dfa73 100644 --- a/src/unix/unix.c +++ b/src/unix/unix.c @@ -302,7 +302,7 @@ plat_get_basename(const char *path) while (c > 0) { if (path[c] == '/') - return((char *)&path[c]); + return((char *)&path[c + 1]); c--; } @@ -366,6 +366,19 @@ plat_dir_create(char *path) return mkdir(path, S_IRWXU); } +void * +plat_mmap(size_t size, uint8_t executable) +{ + void *ret = mmap(0, size, PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0), MAP_ANON | MAP_PRIVATE, 0, 0); + return (ret < 0) ? NULL : ret; +} + +void +plat_munmap(void *ptr, size_t size) +{ + munmap(ptr, size); +} + uint64_t plat_timer_read(void) { diff --git a/src/win/win.c b/src/win/win.c index 45d32bd8e..6e397b98f 100644 --- a/src/win/win.c +++ b/src/win/win.c @@ -728,7 +728,7 @@ plat_get_basename(const char *path) while (c > 0) { if (path[c] == '/' || path[c] == '\\') - return((char *)&path[c]); + return((char *)&path[c + 1]); c--; } @@ -858,6 +858,20 @@ plat_dir_create(char *path) } +void * +plat_mmap(size_t size, uint8_t executable) +{ + return VirtualAlloc(NULL, size, MEM_COMMIT, executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE); +} + + +void +plat_munmap(void *ptr, size_t size) +{ + VirtualFree(ptr, 0, MEM_RELEASE); +} + + uint64_t plat_timer_read(void) { From 5d0b068ec03d9ae43043da2555487ae38faf0388 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Sat, 16 Oct 2021 20:22:12 -0300 Subject: [PATCH 28/56] Remove SiS 5598 machines and code as agreed upon --- src/chipset/CMakeLists.txt | 2 +- src/chipset/sis_5598.c | 969 ------------------------------------ src/include/86box/machine.h | 3 - src/machine/m_at_socket7.c | 52 -- src/machine/machine_table.c | 4 - src/win/Makefile.mingw | 2 +- 6 files changed, 2 insertions(+), 1030 deletions(-) delete mode 100644 src/chipset/sis_5598.c diff --git a/src/chipset/CMakeLists.txt b/src/chipset/CMakeLists.txt index 12006a878..f506ecc42 100644 --- a/src/chipset/CMakeLists.txt +++ b/src/chipset/CMakeLists.txt @@ -16,7 +16,7 @@ add_library(chipset OBJECT 82c100.c acc2168.c cs8230.c ali1217.c ali1429.c ali1489.c et6000.c headland.c intel_82335.c cs4031.c intel_420ex.c intel_4x0.c intel_sio.c intel_piix.c ../ioapic.c neat.c opti283.c opti291.c opti495.c opti822.c opti895.c opti5x7.c scamp.c scat.c - sis_85c310.c sis_85c4xx.c sis_85c496.c sis_85c50x.c sis_5511.c sis_5571.c sis_5598.c + sis_85c310.c sis_85c4xx.c sis_85c496.c sis_85c50x.c sis_5511.c sis_5571.c umc_8886.c umc_8890.c umc_hb4.c via_vt82c49x.c via_vt82c505.c sis_85c310.c sis_85c4xx.c sis_85c496.c sis_85c50x.c gc100.c stpc.c diff --git a/src/chipset/sis_5598.c b/src/chipset/sis_5598.c deleted file mode 100644 index e37074d6f..000000000 --- a/src/chipset/sis_5598.c +++ /dev/null @@ -1,969 +0,0 @@ -/* - * 86Box A hypervisor and IBM PC system emulator that specializes in - * running old operating systems and software designed for IBM - * PC systems and compatibles from 1981 through fairly recent - * system designs based on the PCI bus. - * - * This file is part of the 86Box distribution. - * - * Implementation of the SiS 5597/5598 Pentium PCI/ISA Chipset. - * - * - * - * Authors: Tiseno100, - * - * Copyright 2021 Tiseno100. - */ - -#include -#include -#include -#include -#include -#include -#define HAVE_STDARG_H -#include <86box/86box.h> -#include "cpu.h" -#include <86box/timer.h> -#include <86box/io.h> -#include <86box/device.h> -#include <86box/apm.h> -#include <86box/nvr.h> - -#include <86box/acpi.h> -#include <86box/ddma.h> -#include <86box/hdc.h> -#include <86box/hdc_ide.h> -#include <86box/hdc_ide_sff8038i.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/port_92.h> -#include <86box/smram.h> -#include <86box/usb.h> - -#include <86box/chipset.h> - -/* ACPI Flags */ -#define ACPI_BASE ((dev->pci_conf_sb[0][0x91] << 8) | dev->pci_conf_sb[0][0x90]) -#define ACPI_EN (dev->pci_conf_sb[0][0x40] & 0x80) - -/* DIMM */ -#define DIMM_BANK0 dev->pci_conf[0x60] -#define DIMM_BANK1 dev->pci_conf[0x61] -#define DIMM_BANK_ENABLE dev->pci_conf[0x63] - -/* IDE Flags (1 Native / 0 Compatibility)*/ -#define PRIMARY_COMP_NAT_SWITCH (dev->pci_conf_sb[1][9] & 1) -#define SECONDARY_COMP_NAT_SWITCH (dev->pci_conf_sb[1][9] & 4) -#define PRIMARY_NATIVE_BASE (dev->pci_conf_sb[1][0x11] << 8) | (dev->pci_conf_sb[1][0x10] & 0xf8) -#define PRIMARY_NATIVE_SIDE (((dev->pci_conf_sb[1][0x15] << 8) | (dev->pci_conf_sb[1][0x14] & 0xfc)) + 2) -#define SECONDARY_NATIVE_BASE (dev->pci_conf_sb[1][0x19] << 8) | (dev->pci_conf_sb[1][0x18] & 0xf8) -#define SECONDARY_NATIVE_SIDE (((dev->pci_conf_sb[1][0x1d] << 8) | (dev->pci_conf_sb[1][0x1c] & 0xfc)) + 2) -#define BUS_MASTER_BASE ((dev->pci_conf_sb[1][0x20] & 0xf0) | (dev->pci_conf_sb[1][0x21] << 8)) - -#ifdef ENABLE_SIS_5598_LOG -int sis_5598_do_log = ENABLE_SIS_5598_LOG; -static void -sis_5598_log(const char *fmt, ...) -{ - va_list ap; - - if (sis_5598_do_log) - { - va_start(ap, fmt); - pclog_ex(fmt, ap); - va_end(ap); - } -} -#else -#define sis_5598_log(fmt, ...) -#endif - -typedef struct sis_5598_t -{ - acpi_t *acpi; - ddma_t *ddma; - nvr_t *nvr; - sff8038i_t *ide_drive[2]; - smram_t *smram; - port_92_t *port_92; - usb_t *usb; - - int nb_device_id, sb_device_id; - uint8_t pci_conf[256], pci_conf_sb[3][256]; -} sis_5598_t; - -void sis_5598_dimm_programming(sis_5598_t *dev) -{ -/* -Based completely off the PC Chips M571 Manual -Configurations are forced and don't work as intended -*/ - switch (mem_size >> 10) - { - case 8: - DIMM_BANK_ENABLE = 1; - DIMM_BANK0 = 0xc0; - break; - case 16: - DIMM_BANK_ENABLE = 1; - DIMM_BANK0 = 0xc0; - DIMM_BANK1 = 0xc0; - break; - case 24: - DIMM_BANK_ENABLE = 1; - DIMM_BANK0 = 0xc2; - DIMM_BANK1 = 0xc0; - break; - case 32: - DIMM_BANK_ENABLE = 1; - DIMM_BANK0 = 0xc2; - DIMM_BANK1 = 0xc2; - break; - case 40: - DIMM_BANK_ENABLE = 1; - DIMM_BANK0 = 0xc8; - DIMM_BANK1 = 0xc0; - break; - case 48: - DIMM_BANK_ENABLE = 1; - DIMM_BANK0 = 0xc8; - DIMM_BANK1 = 0xc2; - break; - case 56: /* Unintended */ - case 64: - DIMM_BANK_ENABLE = 1; - DIMM_BANK0 = 0xc8; - DIMM_BANK1 = 0xc8; - break; - case 72: - DIMM_BANK_ENABLE = 1; - DIMM_BANK0 = 0xc6; - DIMM_BANK1 = 0xc0; - break; - case 80: - DIMM_BANK_ENABLE = 1; - DIMM_BANK0 = 0xc6; - DIMM_BANK1 = 0xc2; - break; - case 88: /* Unintended */ - case 96: - DIMM_BANK_ENABLE = 1; - DIMM_BANK0 = 0xc6; - DIMM_BANK1 = 0xc8; - break; - case 104: /* Unintended */ - case 112: /* Unintended */ - case 120: /* Unintended */ - case 128: - DIMM_BANK_ENABLE = 1; - DIMM_BANK0 = 0xc6; - DIMM_BANK1 = 0xc6; - break; - case 136: - DIMM_BANK_ENABLE = 1; - DIMM_BANK0 = 10 | 0xca; - DIMM_BANK1 = 0xc0; - break; - case 144: - DIMM_BANK_ENABLE = 1; - DIMM_BANK0 = 10 | 0xca; - DIMM_BANK1 = 2 | 0xc2; - break; - case 152: /* Unintended */ - case 160: - DIMM_BANK_ENABLE = 1; - DIMM_BANK0 = 10 | 0xca; - DIMM_BANK1 = 8 | 0xc8; - break; - case 168: /* Unintended */ - case 176: /* Unintended */ - case 184: /* Unintended */ - case 192: - DIMM_BANK_ENABLE = 1; - DIMM_BANK0 = 10 | 0xca; - DIMM_BANK1 = 6 | 0xc6; - break; - case 200: /* Unintended */ - case 208: /* Unintended */ - case 216: /* Unintended */ - case 224: /* Unintended */ - case 232: /* Unintended */ - case 240: /* Unintended */ - case 248: /* Unintended */ - case 256: - DIMM_BANK_ENABLE = 1; - DIMM_BANK0 = 10 | 0xca; - DIMM_BANK1 = 10 | 0xca; - break; - } -} - -void sis_5598_shadow(int cur_reg, sis_5598_t *dev) -{ - if (cur_reg == 0x76) - { - mem_set_mem_state_both(0xf0000, 0x10000, ((dev->pci_conf[cur_reg] & 0x80) ? MEM_READ_INTERNAL : MEM_READ_EXTANY) | ((dev->pci_conf[cur_reg] & 0x20) ? MEM_WRITE_INTERNAL : MEM_WRITE_EXTANY)); - } - else - { - mem_set_mem_state_both(0xc0000 + ((cur_reg & 7) * 0x8000), 0x4000, ((dev->pci_conf[cur_reg] & 0x80) ? MEM_READ_INTERNAL : MEM_READ_EXTANY) | ((dev->pci_conf[cur_reg] & 0x20) ? MEM_WRITE_INTERNAL : MEM_WRITE_EXTANY)); - mem_set_mem_state_both(0xc4000 + ((cur_reg & 7) * 0x8000), 0x4000, ((dev->pci_conf[cur_reg] & 8) ? MEM_READ_INTERNAL : MEM_READ_EXTANY) | ((dev->pci_conf[cur_reg] & 2) ? MEM_WRITE_INTERNAL : MEM_WRITE_EXTANY)); - } - flushmmucache_nopc(); -} - -void sis_5598_smram(sis_5598_t *dev) -{ - smram_disable_all(); - - switch ((dev->pci_conf[0xa3] & 0xc0) >> 6) - { - case 0: - if (dev->pci_conf[0x74] == 0) - smram_enable(dev->smram, 0x000e0000, 0x000e0000, 0x8000, dev->pci_conf[0xa3] & 0x10, 1); - break; - case 1: - if (dev->pci_conf[0x74] == 0) - smram_enable(dev->smram, 0x000e0000, 0x000a0000, 0x10000, dev->pci_conf[0xa3] & 0x10, 1); - break; - case 2: - if (dev->pci_conf[0x74] == 0) - smram_enable(dev->smram, 0x000e0000, 0x000b0000, 0x10000, dev->pci_conf[0xa3] & 0x10, 1); - break; - case 3: - smram_enable(dev->smram, 0x000a0000, 0x000a0000, 0x10000, dev->pci_conf[0xa3] & 0x10, 1); - break; - } - - flushmmucache(); -} - -void sis_5598_ddma_update(sis_5598_t *dev) -{ - for (int i = 0; i < 8; i++) - if (i != 4) - ddma_update_io_mapping(dev->ddma, i, dev->pci_conf_sb[0][0x80] >> 4, dev->pci_conf_sb[0][0x81], dev->pci_conf_sb[0][0x80] & 1); -} - -void sis_5598_ide_handler(sis_5598_t *dev) -{ - ide_pri_disable(); - ide_sec_disable(); - if (dev->pci_conf_sb[1][4] & 1) - { - if (dev->pci_conf_sb[1][0x4a] & 4) - { - ide_set_base(0, PRIMARY_COMP_NAT_SWITCH ? PRIMARY_NATIVE_BASE : 0x1f0); - ide_set_side(0, PRIMARY_COMP_NAT_SWITCH ? PRIMARY_NATIVE_SIDE : 0x3f6); - ide_pri_enable(); - } - if (dev->pci_conf_sb[1][0x4a] & 2) - { - ide_set_base(1, SECONDARY_COMP_NAT_SWITCH ? SECONDARY_NATIVE_BASE : 0x170); - ide_set_side(1, SECONDARY_COMP_NAT_SWITCH ? SECONDARY_NATIVE_SIDE : 0x376); - ide_sec_enable(); - } - } -} - -void sis_5598_bm_handler(sis_5598_t *dev) -{ - sff_bus_master_handler(dev->ide_drive[0], dev->pci_conf_sb[1][4] & 4, BUS_MASTER_BASE); - sff_bus_master_handler(dev->ide_drive[1], dev->pci_conf_sb[1][4] & 4, BUS_MASTER_BASE + 8); -} - -static void -sis_5597_write(int func, int addr, uint8_t val, void *priv) -{ - sis_5598_t *dev = (sis_5598_t *)priv; - - switch (addr) - { - case 0x04: /* Command */ - dev->pci_conf[addr] = val & 3; - break; - - case 0x05: /* Command */ - dev->pci_conf[addr] = val & 2; - break; - - case 0x07: /* Status */ - dev->pci_conf[addr] &= val & 0xb9; - break; - - case 0x0d: /* Master latency timer */ - dev->pci_conf[addr] = val; - break; - - case 0x50: /* Host Interface and DRAM arbiter */ - dev->pci_conf[addr] = val & 0xfc; - break; - - case 0x51: /* L2 Cache Controller */ - dev->pci_conf[addr] = (val & 0xcf) | 0x20; /* 512KB L2 Cache Installed */ - cpu_cache_ext_enabled = !!(val & 0x40); - cpu_update_waitstates(); - break; - - case 0x52: /* Control Register */ - dev->pci_conf[addr] = val & 0xe3; - break; - - case 0x53: /* DRAM Control Register */ - case 0x54: /* DRAM Control Register 0*/ - dev->pci_conf[addr] = val; - break; - - case 0x55: /* FPM/EDO DRAM Control Register 1 */ - dev->pci_conf[addr] = val & 0xfe; - break; - - case 0x56: /* Memory Data Latch Enable (MDLE) Delay Control Register */ - case 0x57: /* SDRAM Control Register */ - dev->pci_conf[addr] = val; - break; - - case 0x58: - dev->pci_conf[addr] = val & 0xfc; - break; - - case 0x59: /* DRAM signals driving current Control */ - dev->pci_conf[addr] = val; - break; - - case 0x5a: /* PCI signals driving current Control */ - dev->pci_conf[addr] = val & 3; - break; - - case 0x6c: /* Integrated VGA Controller Control */ - dev->pci_conf[addr] = 0; /* Kill the Integrated GPU */ - break; - - case 0x6d: /* Starting Address of Shared Memory Hole HA[28:23] */ - dev->pci_conf[addr] = val & 2; - break; - - case 0x6e: - dev->pci_conf[addr] = val & 0xc0; - break; - - case 0x70: /* shadow RAM Registers */ - case 0x71: /* shadow RAM Registers */ - case 0x72: /* shadow RAM Registers */ - case 0x73: /* shadow RAM Registers */ - case 0x74: /* shadow RAM Registers */ - case 0x75: /* shadow RAM Registers */ - case 0x76: /* Attribute of shadow RAM for BIOS area */ - dev->pci_conf[addr] = (addr == 0x76) ? (val & 0xe4) : (val & 0xee); - sis_5598_shadow(addr, dev); - break; - - case 0x77: /* Characteristics of non-cacheable area */ - dev->pci_conf[addr] = val & 0x0f; - break; - - case 0x78: /* Allocation of Non-Cacheable area I */ - case 0x79: - case 0x7a: /* Allocation of Non-Cacheable area II */ - case 0x7b: - dev->pci_conf[addr] = val; - break; - - case 0x80: /* PCI master characteristics */ - dev->pci_conf[addr] = val & 0xfe; - break; - - case 0x81: - dev->pci_conf[addr] = val & 0xbe; - break; - - case 0x82: - dev->pci_conf[addr] = val; - break; - - case 0x83: /* CPU to PCI characteristics */ - dev->pci_conf[addr] = val; - port_92_set_features(dev->port_92, !!(val & 0x40), !!(val & 0x80)); - break; - - case 0x84: /* PCI grant timer */ - case 0x85: - case 0x86: /* CPU idle timer */ - dev->pci_conf[addr] = val; - break; - - case 0x87: /* Miscellaneous register */ - dev->pci_conf[addr] = val & 0xfc; - break; - - case 0x88: /* Base address of fast back-to-back area */ - case 0x89: - dev->pci_conf[addr] = val; - break; - - case 0x8a: /* Size of fast back-to-back area */ - case 0x8b: - case 0x90: /* Legacy PMU control register */ - case 0x91: /* Address trap for Legacy PMU function */ - case 0x92: - dev->pci_conf[addr] = val; - break; - - case 0x93: /* STPCLK# and APM SMI control */ - dev->pci_conf[addr] = val; - if ((dev->pci_conf[0x9b] & 1) && (val & 1)) - { - smi_line = 1; - dev->pci_conf[0x9d] |= 1; - } - break; - - case 0x94: /* Cyrix 6x86 and PMU function control */ - dev->pci_conf[addr] = val & 0xf8; - break; - - case 0x95: - dev->pci_conf[addr] = val & 0xfb; - break; - - case 0x96: /* Time slot and Programmable 10-bit I/O port definition */ - dev->pci_conf[addr] = val & 0xfb; - break; - - case 0x97: /* Programmable 10-bit I/O port address bits A9~A2 */ - case 0x98: /* Programmable 16-bit I/O port */ - case 0x99: - case 0x9a: /* System Standby Timer events control */ - case 0x9b: /* Monitor Standdby Timer events control */ - case 0x9c: /* SMI Request events status 0 */ - case 0x9d: /* SMI Request events status 1 */ - case 0x9e: /* STPCLK# Assertion Timer */ - case 0x9f: /* STPCLK# De-assertion Timer */ - case 0xa0: /* Monitor Standby Timer */ - case 0xa1: - case 0xa2: /* System Standby Time */ - dev->pci_conf[addr] = val; - break; - - case 0xa3: /* SMRAM access control and Power supply control */ - dev->pci_conf[addr] = val & 0xd0; - sis_5598_smram(dev); - break; - } - - sis_5598_log("SiS 5597: dev->regs[%02x] = %02x POST: %02x\n", addr, dev->pci_conf[addr], inb(0x80)); -} - -static uint8_t -sis_5597_read(int func, int addr, void *priv) -{ - sis_5598_t *dev = (sis_5598_t *)priv; - sis_5598_log("SiS 5597: dev->regs[%02x] (%02x) POST: %02x\n", addr, dev->pci_conf[addr], inb(0x80)); - return dev->pci_conf[addr]; -} - -void sis_5598_pcitoisa_write(int addr, uint8_t val, sis_5598_t *dev) -{ - switch (addr) - { - case 0x04: /* Command Port */ - dev->pci_conf_sb[0][addr] = val & 0x0f; - break; - - case 0x07: /* Status */ - dev->pci_conf_sb[0][addr] &= val & 0x3f; - break; - - case 0x0d: /* Master latency timer */ - dev->pci_conf_sb[0][addr] = val; - break; - - case 0x40: /* BIOS Control Register */ - dev->pci_conf_sb[0][addr] = val; - acpi_update_io_mapping(dev->acpi, ACPI_BASE, ACPI_EN); - break; - - case 0x41: /* INTA#/INTB#INTC# Remapping Control Register */ - case 0x42: - case 0x43: - case 0x44: /* INTD# Remapping Control Register */ - dev->pci_conf_sb[0][addr] = val & ((addr == 0x44) ? 0x9f : 0x8f); - pci_set_irq_routing(addr & 7, (val & 0x80) ? (val & 0x0f) : PCI_IRQ_DISABLED); - break; - - case 0x45: - dev->pci_conf_sb[0][addr] = val & 0xfc; - switch ((val & 0xc0) >> 6) - { - case 0: - cpu_set_isa_speed(7159091); - break; - case 1: - cpu_set_isa_pci_div(4); - break; - case 2: - cpu_set_isa_pci_div(3); - break; - } - - break; - - case 0x46: - dev->pci_conf_sb[0][addr] = val; - break; - - case 0x47: /* DMA Clock and Wait State Control Register */ - dev->pci_conf_sb[0][addr] = val & 0x7f; - break; - - case 0x48: /* ISA Master/DMA Memory Cycle Control Register 1 */ - case 0x49: /* ISA Master/DMA Memory Cycle Control Register 2 */ - case 0x4a: /* ISA Master/DMA Memory Cycle Control Register 3 */ - case 0x4b: /* ISA Master/DMA Memory Cycle Control Register 4 */ - case 0x4c: /* 4Ch/4Dh/4Eh/4Fh Initialization Command Word 1/2/3/4 Mirror Register I */ - case 0x4d: /* 4Ch/4Dh/4Eh/4Fh Initialization Command Word 1/2/3/4 Mirror Register I */ - case 0x4e: /* 4Ch/4Dh/4Eh/4Fh Initialization Command Word 1/2/3/4 Mirror Register I */ - case 0x4f: /* 4Ch/4Dh/4Eh/4Fh Initialization Command Word 1/2/3/4 Mirror Register I */ - case 0x50: /* Initialization Command Word 1/2/3/4 mirror Register II */ - case 0x51: /* Initialization Command Word 1/2/3/4 mirror Register II */ - case 0x52: /* Initialization Command Word 1/2/3/4 mirror Register II */ - case 0x53: /* Initialization Command Word 1/2/3/4 mirror Register II */ - case 0x54: /* Operational Control Word 2/3 Mirror Register I */ - case 0x55: - case 0x56: /* Operational Control Word 2/3 Mirror Register II */ - case 0x57: - case 0x58: /* Counter Access Ports Mirror Register 0 */ - case 0x59: - case 0x5a: - case 0x5b: - case 0x5c: - case 0x5d: - case 0x5e: - dev->pci_conf_sb[0][addr] = val; - break; - - case 0x5f: - dev->pci_conf_sb[0][addr] = val & 0x3f; - break; - - case 0x60: /* Mirror port */ - dev->pci_conf_sb[0][addr] = (uint8_t)inb(0x70); - break; - - case 0x61: /* IDEIRQ Remapping Control Register */ - dev->pci_conf_sb[0][addr] = val & 0xcf; - if (val & 0x80) - { - sff_set_irq_line(dev->ide_drive[0], val & 0x0f); - sff_set_irq_line(dev->ide_drive[1], val & 0x0f); - } - break; - - case 0x62: /* USBIRQ Remapping Control Register */ - case 0x63: /* GPCS0 Control Register */ - case 0x64: /* GPCS1 Control Register */ - case 0x65: /* GPCS0 Output Mode Control Register */ - case 0x66: - case 0x67: /* GPCS1 Output Mode Control Register */ - case 0x68: - dev->pci_conf_sb[0][addr] = val; - break; - - case 0x69: /* GPCS0/1 De-Bounce Control Register */ - dev->pci_conf_sb[0][addr] = val & 0xdf; - break; - - case 0x6a: /* ACPI/SCI IRQ Remapping Control Register */ - dev->pci_conf_sb[0][addr] = val; - if (val & 0x80) - acpi_set_irq_line(dev->acpi, val & 0x0f); - break; - - case 0x6b: - dev->pci_conf_sb[0][addr] = val; - break; - - case 0x6c: - dev->pci_conf_sb[0][addr] = val & 0xfe; - break; - - case 0x6d: - case 0x6e: /* Software-Controlled Interrupt Request, Channels 7-0 */ - case 0x6f: /* Software-Controlled Interrupt Request, channels 15-8 */ - case 0x70: - dev->pci_conf_sb[0][addr] = val; - break; - - case 0x71: /* Type-F DMA Control Register */ - dev->pci_conf_sb[0][addr] = val & 0xef; - break; - - case 0x72: /* SMI Triggered By IRQ Control */ - dev->pci_conf_sb[0][addr] = val & 0xfa; - break; - - case 0x73: /* SMI Triggered By IRQ Control */ - dev->pci_conf_sb[0][addr] = val; - break; - - case 0x74: /* System Standby Timer Reload, System Standby State Exit And Throttling State Exit Control */ - dev->pci_conf_sb[0][addr] = val & 0xfb; - break; - - case 0x75: /* System Standby Timer Reload, System Standby State Exit And Throttling State Exit Control */ - dev->pci_conf_sb[0][addr] = val; - break; - - case 0x76: /* Monitor Standby Timer Reload And Monitor Standby State Exit Control */ - dev->pci_conf_sb[0][addr] = val & 0xfb; - break; - - case 0x77: /* Monitor Standby Timer Reload And Monitor Standby State Exit Control */ - dev->pci_conf_sb[0][addr] = val; - break; - - case 0x80: /* DDMA Control Register */ - case 0x81: - dev->pci_conf_sb[0][addr] = val & ((addr == 0x81) ? 0xff : 0xf1); - sis_5598_ddma_update(dev); - break; - - case 0x84: - dev->pci_conf_sb[0][addr] = val & 0xef; - break; - - case 0x88: - dev->pci_conf_sb[0][addr] = val; - break; - - case 0x89: /* Serial Interrupt Enable Register 1 */ - dev->pci_conf_sb[0][addr] = val & 0x7e; - break; - - case 0x8a: /* Serial Interrupt Enable Register 2 */ - dev->pci_conf_sb[0][addr] = val & 0xef; - break; - - case 0x90: /* ACPI Base Address Register */ - case 0x91: /* ACPI Base Address Register */ - dev->pci_conf_sb[0][addr] = val; - acpi_update_io_mapping(dev->acpi, ACPI_BASE, ACPI_EN); - break; - } -} - -void sis_5598_ide_write(int addr, uint8_t val, sis_5598_t *dev) -{ - switch (addr) - { - case 0x04: /* Command */ - dev->pci_conf_sb[1][addr] = val & 7; - sis_5598_ide_handler(dev); - sis_5598_bm_handler(dev); - break; - - case 0x06: /* Status */ - dev->pci_conf_sb[1][addr] = val & 0x20; - break; - - case 0x07: /* Status */ - dev->pci_conf_sb[1][addr] = val & 0x3c; - break; - - case 0x0d: /* Latency Timer */ - dev->pci_conf_sb[1][addr] = val; - break; - - case 0x09: /* Programming Interface Byte */ - case 0x10: /* Primary Channel Command Block Base Address Register */ - case 0x11: /* Primary Channel Command Block Base Address Register */ - case 0x12: /* Primary Channel Command Block Base Address Register */ - case 0x13: /* Primary Channel Command Block Base Address Register */ - case 0x14: /* Primary Channel Control Block Base Address Register */ - case 0x15: /* Primary Channel Control Block Base Address Register */ - case 0x16: /* Primary Channel Control Block Base Address Register */ - case 0x17: /* Primary Channel Control Block Base Address Register */ - case 0x18: /* Secondary Channel Command Block Base Address Register */ - case 0x19: /* Secondary Channel Command Block Base Address Register */ - case 0x1a: /* Secondary Channel Command Block Base Address Register */ - case 0x1b: /* Secondary Channel Command Block Base Address Register */ - case 0x1c: /* Secondary Channel Control Block Base Address Register */ - case 0x1d: /* Secondary Channel Control Block Base Address Register */ - case 0x1e: /* Secondary Channel Control Block Base Address Register */ - case 0x1f: /* Secondary Channel Control Block Base Address Register */ - dev->pci_conf_sb[1][addr] = val & ((addr == 9) ? 0x0f : 0xff); - sis_5598_ide_handler(dev); - break; - - case 0x20: /* Bus Master IDE Control Register Base Address */ - case 0x21: /* Bus Master IDE Control Register Base Address */ - case 0x22: /* Bus Master IDE Control Register Base Address */ - case 0x23: /* Bus Master IDE Control Register Base Address */ - dev->pci_conf_sb[1][addr] = val; - sis_5598_bm_handler(dev); - break; - - case 0x2c: /* Subsystem ID */ - dev->pci_conf_sb[1][addr] = val; - break; - - case 0x30: /* Expansion ROM Base Address */ - case 0x31: /* Expansion ROM Base Address */ - case 0x32: /* Expansion ROM Base Address */ - case 0x33: /* Expansion ROM Base Address */ - dev->pci_conf_sb[1][addr] = val; - break; - - case 0x40: /* IDE Primary Channel/Master Drive Data Recovery Time Control */ - dev->pci_conf_sb[1][addr] = val & 0xcf; - break; - - case 0x41: /* IDE Primary Channel/Master Drive Control */ - dev->pci_conf_sb[1][addr] = val & 0xe7; - break; - - case 0x42: /* IDE Primary Channel/Slave Drive Data Recovery Time Control */ - dev->pci_conf_sb[1][addr] = val & 0x0f; - break; - - case 0x43: /* IDE Primary Channel/Slave Drive Data Active Time Control */ - case 0x44: /* IDE Secondary Channel/Master Drive Data Recovery Time Control */ - case 0x45: /* IDE Secondary Channel/Master Drive Data Active Time Control */ - dev->pci_conf_sb[1][addr] = val & 0xe7; - break; - - case 0x46: /* IDE Secondary Channel/Slave Drive Data Recovery Time Control */ - dev->pci_conf_sb[1][addr] = val & 0x0f; - break; - - case 0x47: /* IDE Secondary Channel/Slave Drive Data Active Time Control */ - dev->pci_conf_sb[1][addr] = val & 0xe7; - break; - - case 0x48: /* IDE Command Recovery Time Control */ - case 0x49: /* IDE Command Active Time Control */ - dev->pci_conf_sb[1][addr] = val & 0x0f; - break; - - case 0x4a: /* IDE General Control Register 0 */ - dev->pci_conf_sb[1][addr] = val; - sis_5598_ide_handler(dev); - break; - - case 0x4b: /* IDE General Control register 1 */ - case 0x4c: /* Prefetch Count of Primary Channel */ - case 0x4d: - case 0x4e: /* Prefetch Count of Secondary Channel */ - case 0x4f: - case 0x50: /* IDE minimum accessed time register */ - case 0x51: - dev->pci_conf_sb[1][addr] = val; - break; - - case 0x52: /* IDE Miscellaneous Control Register */ - dev->pci_conf_sb[1][addr] = val & 0x0f; - break; - } -} - -void sis_5598_usb_write(int addr, uint8_t val, sis_5598_t *dev) -{ - switch (addr) - { - case 0x04: /* Command */ - dev->pci_conf_sb[2][addr] = val; - ohci_update_mem_mapping(dev->usb, dev->pci_conf_sb[2][0x11], dev->pci_conf_sb[2][0x12], dev->pci_conf_sb[2][0x13], dev->pci_conf_sb[2][4] & 1); - break; - - case 0x05: /* Command */ - dev->pci_conf_sb[2][addr] = val & 3; - break; - - case 0x06: /* Status */ - dev->pci_conf_sb[2][addr] &= val & 0xf0; - break; - - case 0x07: /* Status */ - dev->pci_conf_sb[2][addr] &= val; - break; - - case 0x0d: /* Latency Timer */ - dev->pci_conf_sb[2][addr] = val; - break; - - case 0x11: /* USB Memory Space Base Address Register */ - case 0x12: /* USB Memory Space Base Address Register */ - case 0x13: /* USB Memory Space Base Address Register */ - dev->pci_conf_sb[2][addr] = val & ((addr == 0x11) ? 0x0f : 0xff); - ohci_update_mem_mapping(dev->usb, dev->pci_conf_sb[2][0x11], dev->pci_conf_sb[2][0x12], dev->pci_conf_sb[2][0x13], dev->pci_conf_sb[2][4] & 1); - break; - - case 0x3c: /* Interrupt Line */ - case 0x3d: /* Interrupt Pin */ - case 0x3e: /* Minimum Grant Time */ - case 0x3f: /* Maximum Latency Time */ - dev->pci_conf_sb[2][addr] = val; - break; - } -} - -static void -sis_5598_write(int func, int addr, uint8_t val, void *priv) -{ - sis_5598_t *dev = (sis_5598_t *)priv; - switch (func) - { - case 0: - sis_5598_pcitoisa_write(addr, val, dev); - break; - case 1: - sis_5598_ide_write(addr, val, dev); - break; - case 2: - sis_5598_usb_write(addr, val, dev); - break; - } - sis_5598_log("SiS 5598: dev->regs[%02x][%02x] = %02x POST: %02x\n", func, addr, dev->pci_conf_sb[func][addr], inb(0x80)); -} - -static uint8_t -sis_5598_read(int func, int addr, void *priv) -{ - sis_5598_t *dev = (sis_5598_t *)priv; - if ((func >= 0) && (func <= 2)) - { - sis_5598_log("SiS 5598: dev->regs[%02x][%02x] (%02x) POST: %02x\n", func, addr, dev->pci_conf_sb[func][addr], inb(0x80)); - return dev->pci_conf_sb[func][addr]; - } - else - return 0xff; -} - -static void -sis_5598_defaults(sis_5598_t *dev) -{ - dev->pci_conf[0x00] = 0x39; /* SiS */ - dev->pci_conf[0x01] = 0x10; - dev->pci_conf[0x02] = 0x97; /* 5597 */ - dev->pci_conf[0x03] = 0x55; - dev->pci_conf[0x08] = 4; - dev->pci_conf[0x0b] = 6; - dev->pci_conf[0x0d] = 0xff; - dev->pci_conf[0x9e] = 0xff; - dev->pci_conf[0x9f] = 0xff; - dev->pci_conf[0xa0] = 0xff; - - dev->pci_conf_sb[0][0x00] = 0x39; /* SiS */ - dev->pci_conf_sb[0][0x01] = 0x10; - dev->pci_conf_sb[0][0x02] = 8; /* 5598 */ - dev->pci_conf_sb[0][0x08] = 1; - dev->pci_conf_sb[0][0x0a] = 1; - dev->pci_conf_sb[0][0x0b] = 6; - dev->pci_conf_sb[0][0x0d] = 0xff; - dev->pci_conf_sb[0][0x0e] = 0x30; - dev->pci_conf_sb[0][0x0f] = 0x30; - dev->pci_conf_sb[0][0x48] = 1; - dev->pci_conf_sb[0][0x4a] = 0x10; - dev->pci_conf_sb[0][0x4b] = 0x0f; - dev->pci_conf_sb[0][0x6d] = 0x19; - dev->pci_conf_sb[0][0x70] = 0x12; - - dev->pci_conf_sb[1][0x00] = 0x39; /* SiS */ - dev->pci_conf_sb[1][0x01] = 0x10; - dev->pci_conf_sb[1][0x02] = 0x13; /* 5513 */ - dev->pci_conf_sb[1][0x03] = 0x55; - dev->pci_conf_sb[1][0x08] = 0xd0; - dev->pci_conf_sb[0][0x09] = 0x80; - dev->pci_conf_sb[1][0x0a] = 1; - dev->pci_conf_sb[1][0x0b] = 1; - - dev->pci_conf_sb[2][0x00] = 0x39; /* SiS */ - dev->pci_conf_sb[2][0x01] = 0x10; - dev->pci_conf_sb[2][0x02] = 1; /* 7710 */ - dev->pci_conf_sb[2][0x03] = 0x70; - dev->pci_conf_sb[2][0x06] = 2; - dev->pci_conf_sb[2][0x07] = 0x80; - dev->pci_conf_sb[2][0x08] = 0xe0; - dev->pci_conf_sb[2][0x09] = 0x10; - dev->pci_conf_sb[2][0x0a] = 3; - dev->pci_conf_sb[2][0x0b] = 0x0c; - dev->pci_conf_sb[2][0x0e] = 0x10; - dev->pci_conf_sb[2][0x3d] = 1; -} - -static void -sis_5598_reset(void *priv) -{ - sis_5598_t *dev = (sis_5598_t *)priv; - - /* Program defaults */ - sis_5598_defaults(dev); - - /* Set up ACPI */ - acpi_set_slot(dev->acpi, dev->sb_device_id); - acpi_set_nvr(dev->acpi, dev->nvr); - - /* Set up IDE */ - sff_set_slot(dev->ide_drive[0], dev->sb_device_id); - sff_set_slot(dev->ide_drive[1], dev->sb_device_id); - sff_bus_master_reset(dev->ide_drive[0], BUS_MASTER_BASE); - sff_bus_master_reset(dev->ide_drive[1], BUS_MASTER_BASE + 8); -} - -static void -sis_5598_close(void *priv) -{ - sis_5598_t *dev = (sis_5598_t *)priv; - - smram_del(dev->smram); - free(dev); -} - -static void * -sis_5598_init(const device_t *info) -{ - sis_5598_t *dev = (sis_5598_t *)malloc(sizeof(sis_5598_t)); - memset(dev, 0, sizeof(sis_5598_t)); - dev->nb_device_id = pci_add_card(PCI_ADD_NORTHBRIDGE, sis_5597_read, sis_5597_write, dev); /* Device 0: SiS 5597 */ - dev->sb_device_id = pci_add_card(PCI_ADD_SOUTHBRIDGE, sis_5598_read, sis_5598_write, dev); /* Device 1: SiS 5598 */ - - /* ACPI */ - dev->acpi = device_add(&acpi_sis_device); - dev->nvr = device_add(&at_nvr_device); - - /* DDMA */ - dev->ddma = device_add(&ddma_device); - - /* RAM Bank Programming */ - sis_5598_dimm_programming(dev); - - /* SFF IDE */ - dev->ide_drive[0] = device_add_inst(&sff8038i_device, 1); - dev->ide_drive[1] = device_add_inst(&sff8038i_device, 2); - - /* SMRAM */ - dev->smram = smram_add(); - - /* Port 92 */ - dev->port_92 = device_add(&port_92_pci_device); - - /* USB */ - dev->usb = device_add(&usb_device); - - sis_5598_reset(dev); - - return dev; -} - -const device_t sis_5598_device = { - "SiS 5597/5598", - DEVICE_PCI, - 0, - sis_5598_init, - sis_5598_close, - sis_5598_reset, - {NULL}, - NULL, - NULL, - NULL}; diff --git a/src/include/86box/machine.h b/src/include/86box/machine.h index e998545fa..7dc06936e 100644 --- a/src/include/86box/machine.h +++ b/src/include/86box/machine.h @@ -481,9 +481,6 @@ extern int machine_at_ficva502_init(const machine_t *); extern int machine_at_ficpa2012_init(const machine_t *); -extern int machine_at_sp97xv_init(const machine_t *); -extern int machine_at_m571_init(const machine_t *); - #ifdef EMU_DEVICE_H extern const device_t *at_thor_get_device(void); extern const device_t *at_pb640_get_device(void); diff --git a/src/machine/m_at_socket7.c b/src/machine/m_at_socket7.c index 5ba8a69a3..2edf96595 100644 --- a/src/machine/m_at_socket7.c +++ b/src/machine/m_at_socket7.c @@ -1312,55 +1312,3 @@ machine_at_ficpa2012_init(const machine_t *model) return ret; } - -int -machine_at_sp97xv_init(const machine_t *model) -{ - int ret; - - ret = bios_load_linear("roms/machines/sp97xv/0109XV.005", - 0x000e0000, 131072, 0); - - if (bios_only || !ret) - return ret; - - machine_at_common_init_ex(model, 2); - - pci_init(PCI_CONFIG_TYPE_1); - pci_register_slot(0x00, PCI_CARD_NORTHBRIDGE, 0, 0, 0, 0); - pci_register_slot(0x01, PCI_CARD_SOUTHBRIDGE, 1, 2, 3, 4); - device_add(&sis_5598_device); - device_add(&keyboard_ps2_ami_pci_device); - device_add(&w83877f_device); - device_add(&sst_flash_29ee010_device); - - return ret; -} - -int -machine_at_m571_init(const machine_t *model) -{ - int ret; - - ret = bios_load_linear("roms/machines/m571/2k0621s.rom", - 0x000c0000, 262144, 0); - - if (bios_only || !ret) - return ret; - - machine_at_common_init_ex(model, 2); - - pci_init(PCI_CONFIG_TYPE_1); - pci_register_slot(0x00, PCI_CARD_NORTHBRIDGE, 0, 0, 0, 0); - pci_register_slot(0x01, PCI_CARD_SOUTHBRIDGE, 1, 2, 3, 4); - pci_register_slot(0x09, PCI_CARD_NORMAL, 1, 2, 3, 4); - pci_register_slot(0x0B, PCI_CARD_NORMAL, 2, 3, 4, 1); - pci_register_slot(0x0D, PCI_CARD_NORMAL, 3, 4, 1, 2); - pci_register_slot(0x0F, PCI_CARD_NORMAL, 4, 1, 2, 3); - device_add(&sis_5598_device); - device_add(&keyboard_ps2_ami_pci_device); - device_add(&it8661f_device); - device_add(&sst_flash_29ee020_device); - - return ret; -} diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index e5f1ea66d..b1e6dfe92 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -364,10 +364,6 @@ const machine_t machines[] = { { "[SiS 5571] Rise R534F", "r534f", MACHINE_TYPE_SOCKET7, CPU_PKG_SOCKET5_7, 0, 50000000, 66666667, 2500, 3520, 1.5, 3.0, MACHINE_PCI | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192, 393216, 8192, 127, machine_at_r534f_init, NULL }, { "[SiS 5571] MSI MS-5146", "ms5146", MACHINE_TYPE_SOCKET7, CPU_PKG_SOCKET5_7, 0, 50000000, 66666667, 2500, 3520, 1.5, 3.0, MACHINE_PCI | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192, 262144, 8192, 127, machine_at_ms5146_init, NULL }, - /* SiS 5598 */ - { "[SiS 5598] ASUS SP97-XV", "sp97xv", MACHINE_TYPE_SOCKET7, CPU_PKG_SOCKET5_7, 0, 60000000, 66666667, 2100, 3200, 1.5, 2.5, MACHINE_PCI | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192, 262144, 8192, 255, machine_at_sp97xv_init, NULL }, - { "[SiS 5598] PC Chips M571", "m571", MACHINE_TYPE_SOCKET7, CPU_PKG_SOCKET5_7, 0, 50000000, 75000000, 2500, 3500, 1.5, 3.5, MACHINE_PCI | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192, 262144, 8192, 255, machine_at_m571_init, NULL }, - /* ALi ALADDiN IV */ #if defined(DEV_BRANCH) && defined(USE_M154X) { "[ALi ALADDiN IV] PC Chips M560", "m560", MACHINE_TYPE_SOCKET7, CPU_PKG_SOCKET5_7, 0, 50000000, 83333333, 2500, 3520, 1.5, 3.0, MACHINE_PCI | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192, 262144, 8192, 255, machine_at_m560_init, NULL }, diff --git a/src/win/Makefile.mingw b/src/win/Makefile.mingw index f40ae87f7..90ef6136a 100644 --- a/src/win/Makefile.mingw +++ b/src/win/Makefile.mingw @@ -614,7 +614,7 @@ CHIPSETOBJ := 82c100.o acc2168.o cs8230.o ali1217.o ali1429.o ali1489.o et6000.o intel_420ex.o intel_4x0.o intel_sio.o intel_piix.o ioapic.o \ neat.o opti495.o opti822.o opti895.o opti5x7.o scamp.o scat.o via_vt82c49x.o via_vt82c505.o \ gc100.o \ - sis_85c310.o sis_85c4xx.o sis_85c496.o sis_85c50x.o sis_5511.o sis_5571.o sis_5598.o stpc.o opti283.o opti291.o \ + sis_85c310.o sis_85c4xx.o sis_85c496.o sis_85c50x.o sis_5511.o sis_5571.o stpc.o opti283.o opti291.o \ umc_8886.o umc_8890.o umc_hb4.o \ via_apollo.o via_pipc.o wd76c10.o vl82c480.o From 7f8c6635b3339973e052990769f01a672429e7d9 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Sat, 16 Oct 2021 21:20:06 -0300 Subject: [PATCH 29/56] Fix the GT694VA's PCI slots --- src/machine/m_at_socket370.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/machine/m_at_socket370.c b/src/machine/m_at_socket370.c index acab29443..4d8b5c5a5 100644 --- a/src/machine/m_at_socket370.c +++ b/src/machine/m_at_socket370.c @@ -357,12 +357,11 @@ machine_at_gt694va_init(const machine_t *model) pci_init(PCI_CONFIG_TYPE_1); pci_register_slot(0x00, PCI_CARD_NORTHBRIDGE, 0, 0, 0, 0); - pci_register_slot(0x07, PCI_CARD_SOUTHBRIDGE, 1, 2, 0, 0); - pci_register_slot(0x14, PCI_CARD_NORMAL, 2, 3, 4, 1); - pci_register_slot(0x13, PCI_CARD_NORMAL, 3, 4, 1, 2); - pci_register_slot(0x12, PCI_CARD_NORMAL, 4, 1, 2, 3); - pci_register_slot(0x11, PCI_CARD_NORMAL, 1, 2, 3, 4); - pci_register_slot(0x10, PCI_CARD_NORMAL, 2, 3, 4, 1); + pci_register_slot(0x07, PCI_CARD_SOUTHBRIDGE, 0, 0, 3, 4); + pci_register_slot(0x0D, PCI_CARD_NORMAL, 4, 1, 2, 3); + pci_register_slot(0x0F, PCI_CARD_NORMAL, 3, 4, 1, 2); + pci_register_slot(0x11, PCI_CARD_NORMAL, 2, 3, 4, 1); + pci_register_slot(0x13, PCI_CARD_NORMAL, 1, 2, 3, 4); pci_register_slot(0x01, PCI_CARD_AGPBRIDGE, 1, 2, 3, 4); device_add(&via_apro133a_device); device_add(&via_vt82c596b_device); From 6bf299cd417c703b8ddb15c5556b836e5d956016 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Sat, 16 Oct 2021 21:36:58 -0300 Subject: [PATCH 30/56] Implement undocumented PMCNTRL mirrors on VIA 596 (unconfirmed), 686 (confirmed) and PIIX4 (unconfirmed) ACPI --- src/acpi.c | 46 ++++++++++++++++++++++++++++++---------- src/chipset/intel_piix.c | 8 +++++-- src/chipset/via_pipc.c | 1 + 3 files changed, 42 insertions(+), 13 deletions(-) diff --git a/src/acpi.c b/src/acpi.c index 1398edc4c..6f5471faa 100644 --- a/src/acpi.c +++ b/src/acpi.c @@ -1174,9 +1174,9 @@ acpi_reg_write_common(int size, uint16_t addr, uint8_t val, void *p) { acpi_t *dev = (acpi_t *) p; - if (dev->vendor == VEN_ALI) + if (dev->vendor == VEN_ALI) acpi_reg_write_ali(size, addr, val, p); - if (dev->vendor == VEN_VIA) + else if (dev->vendor == VEN_VIA) acpi_reg_write_via(size, addr, val, p); else if (dev->vendor == VEN_VIA_596B) acpi_reg_write_via_596b(size, addr, val, p); @@ -1195,7 +1195,11 @@ acpi_aux_reg_read_common(int size, uint16_t addr, void *p) acpi_t *dev = (acpi_t *) p; uint8_t ret = 0xff; - if (dev->vendor == VEN_SMC) + if (dev->vendor == VEN_VIA_596B) + ret = acpi_reg_read_via_596b(size, addr - (0xf0 - 0x04), p); + else if (dev->vendor == VEN_INTEL) + ret = acpi_reg_read_intel(size, addr - (0x40 - 0x04), p); + else if (dev->vendor == VEN_SMC) ret = acpi_aux_reg_read_smc(size, addr, p); return ret; @@ -1207,7 +1211,11 @@ acpi_aux_reg_write_common(int size, uint16_t addr, uint8_t val, void *p) { acpi_t *dev = (acpi_t *) p; - if (dev->vendor == VEN_SMC) + if (dev->vendor == VEN_VIA_596B) + acpi_reg_write_via_596b(size, addr - (0xf0 - 0x04), val, p); + else if (dev->vendor == VEN_INTEL) + acpi_reg_write_intel(size, addr - (0x40 - 0x04), val, p); + else if (dev->vendor == VEN_SMC) acpi_aux_reg_write_smc(size, addr, val, p); } @@ -1256,7 +1264,7 @@ acpi_reg_read(uint16_t addr, void *p) static uint32_t -acpi_aux_read_readl(uint16_t addr, void *p) +acpi_aux_reg_readl(uint16_t addr, void *p) { uint32_t ret = 0x00000000; @@ -1272,7 +1280,7 @@ acpi_aux_read_readl(uint16_t addr, void *p) static uint16_t -acpi_aux_read_readw(uint16_t addr, void *p) +acpi_aux_reg_readw(uint16_t addr, void *p) { uint16_t ret = 0x0000; @@ -1286,7 +1294,7 @@ acpi_aux_read_readw(uint16_t addr, void *p) static uint8_t -acpi_aux_read_read(uint16_t addr, void *p) +acpi_aux_reg_read(uint16_t addr, void *p) { uint8_t ret = 0x00; @@ -1396,17 +1404,33 @@ acpi_update_io_mapping(acpi_t *dev, uint32_t base, int chipset_en) void acpi_update_aux_io_mapping(acpi_t *dev, uint32_t base, int chipset_en) { + int size; + + switch (dev->vendor) { + case VEN_INTEL: + case VEN_VIA_596B: + /* Undocumented mirror of PMCNTRL. */ + size = 0x001; + break; + case VEN_SMC: + size = 0x008; + break; + default: + size = 0x000; + break; + } + if (dev->aux_io_base != 0x0000) { - io_removehandler(dev->aux_io_base, 0x08, - acpi_aux_read_read, acpi_aux_read_readw, acpi_aux_read_readl, + io_removehandler(dev->aux_io_base, size, + acpi_aux_reg_read, acpi_aux_reg_readw, acpi_aux_reg_readl, acpi_aux_reg_write, acpi_aux_reg_writew, acpi_aux_reg_writel, dev); } dev->aux_io_base = base; if (chipset_en && (dev->aux_io_base != 0x0000)) { - io_sethandler(dev->aux_io_base, 0x08, - acpi_aux_read_read, acpi_aux_read_readw, acpi_aux_read_readl, + io_sethandler(dev->aux_io_base, size, + acpi_aux_reg_read, acpi_aux_reg_readw, acpi_aux_reg_readl, acpi_aux_reg_write, acpi_aux_reg_writew, acpi_aux_reg_writel, dev); } } diff --git a/src/chipset/intel_piix.c b/src/chipset/intel_piix.c index a865e045a..032a94351 100644 --- a/src/chipset/intel_piix.c +++ b/src/chipset/intel_piix.c @@ -813,11 +813,13 @@ piix_write(int func, int addr, uint8_t val, void *priv) fregs[0x40] = (val & 0xc0) | 1; dev->acpi_io_base = (dev->regs[3][0x41] << 8) | (dev->regs[3][0x40] & 0xc0); acpi_update_io_mapping(dev->acpi, dev->acpi_io_base, (dev->regs[3][0x80] & 0x01)); + acpi_update_aux_io_mapping(dev->acpi, dev->acpi_io_base + 0x40, (dev->regs[3][0x80] & 0x01)); break; case 0x41: fregs[0x41] = val; dev->acpi_io_base = (dev->regs[3][0x41] << 8) | (dev->regs[3][0x40] & 0xc0); acpi_update_io_mapping(dev->acpi, dev->acpi_io_base, (dev->regs[3][0x80] & 0x01)); + acpi_update_aux_io_mapping(dev->acpi, dev->acpi_io_base + 0x40, (dev->regs[3][0x80] & 0x01)); break; case 0x44: case 0x45: case 0x46: case 0x47: case 0x48: case 0x49: @@ -844,10 +846,12 @@ piix_write(int func, int addr, uint8_t val, void *priv) break; case 0x4f: case 0x80: case 0xd2: fregs[addr] = val & 0x0f; - if (addr == 0x80) + if (addr == 0x80) { acpi_update_io_mapping(dev->acpi, dev->acpi_io_base, (dev->regs[3][0x80] & 0x01)); - else if (addr == 0xd2) + acpi_update_aux_io_mapping(dev->acpi, dev->acpi_io_base + 0x40, (dev->regs[3][0x80] & 0x01)); + } else if (addr == 0xd2) { smbus_update_io_mapping(dev); + } break; case 0x50: fregs[addr] = val & 0x3f; diff --git a/src/chipset/via_pipc.c b/src/chipset/via_pipc.c index 41667edfd..d609f47b5 100644 --- a/src/chipset/via_pipc.c +++ b/src/chipset/via_pipc.c @@ -1128,6 +1128,7 @@ pipc_write(int func, int addr, uint8_t val, void *priv) c -= 0x400; acpi_set_timer32(dev->acpi, dev->power_regs[0x41] & 0x08); acpi_update_io_mapping(dev->acpi, c, dev->power_regs[0x41] & 0x80); + acpi_update_aux_io_mapping(dev->acpi, c + 0xf0, dev->power_regs[0x41] & 0x80); break; case 0x42: From a041469ff8b2fd302d49815e6cbb1600848e51d1 Mon Sep 17 00:00:00 2001 From: Ompronce <88358700+Ompronce@users.noreply.github.com> Date: Sun, 17 Oct 2021 23:37:10 -0400 Subject: [PATCH 31/56] Added alternate Tandy PSSJ I/O port at 1E0h (used in Tandy 1000 RSX) --- src/sound/snd_pssj.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/sound/snd_pssj.c b/src/sound/snd_pssj.c index bdf013e2e..4eab5c856 100644 --- a/src/sound/snd_pssj.c +++ b/src/sound/snd_pssj.c @@ -201,6 +201,20 @@ void *pssj_init(const device_t *info) return pssj; } +void *pssj_1e0_init(const device_t *info) +{ + pssj_t *pssj = malloc(sizeof(pssj_t)); + memset(pssj, 0, sizeof(pssj_t)); + + sn76489_init(&pssj->sn76489, 0x01e0, 0x0004, PSSJ, 3579545); + + io_sethandler(0x01E4, 0x0004, pssj_read, NULL, NULL, pssj_write, NULL, NULL, pssj); + timer_add(&pssj->timer_count, pssj_callback, pssj, pssj->enable); + sound_add_handler(pssj_get_buffer, pssj); + + return pssj; +} + void pssj_close(void *p) { pssj_t *pssj = (pssj_t *)p; @@ -219,3 +233,15 @@ const device_t pssj_device = NULL, NULL }; + +const device_t pssj_1e0_device = +{ + "Tandy PSSJ (port 1e0h)", + 0, 0, + pssj_1e0_init, + pssj_close, + NULL, + { NULL }, + NULL, + NULL +}; From 549a477d19c92aa71c560066181f490181293b52 Mon Sep 17 00:00:00 2001 From: Ompronce <88358700+Ompronce@users.noreply.github.com> Date: Mon, 18 Oct 2021 00:32:21 -0400 Subject: [PATCH 32/56] Restored SN76489 back to IBM PCjr, as SN76496 and SN76489 are identical. --- src/machine/m_pcjr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/machine/m_pcjr.c b/src/machine/m_pcjr.c index c54c7f0c7..35ddfe6da 100644 --- a/src/machine/m_pcjr.c +++ b/src/machine/m_pcjr.c @@ -859,8 +859,8 @@ machine_pcjr_init(const machine_t *model) keyboard_set_table(scancode_xt); keyboard_send = kbd_adddata_ex; - /* Technically it's the SN76496N, but the NCR 8496 is a drop-in replacement for it. */ - device_add(&ncr8496_device); + /* Technically it's the SN76496N, but the SN76489 is identical to the SN76496N. */ + device_add(&sn76489_device); nmi_mask = 0x80; From 878c92bf7f9d0d2b214a30273f0d7f4892d4e040 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Mon, 18 Oct 2021 15:05:38 -0300 Subject: [PATCH 33/56] ACPI: Rework suspend types and remove bogus PMCNTRL mirrors (those are SMI traps instead) --- src/acpi.c | 107 +++++++++++++++++++++++++++------------ src/chipset/intel_piix.c | 8 +-- src/chipset/via_pipc.c | 1 - src/include/86box/acpi.h | 10 +++- 4 files changed, 86 insertions(+), 40 deletions(-) diff --git a/src/acpi.c b/src/acpi.c index 6f5471faa..a7301304c 100644 --- a/src/acpi.c +++ b/src/acpi.c @@ -37,6 +37,7 @@ #include <86box/acpi.h> #include <86box/machine.h> #include <86box/i2c.h> +#include <86box/ui.h> int acpi_rtc_status = 0; @@ -643,38 +644,46 @@ acpi_reg_write_common_regs(int size, uint16_t addr, uint8_t val, void *p) case 0x04: case 0x05: /* PMCNTRL - Power Management Control Register (IO) */ if ((addr == 0x05) && (val & 0x20)) { - sus_typ = (val >> 2) & 7; - switch (sus_typ) { - case 0: - case 6: /* Reserved according to the datasheet but used by eg. the ASUS P2B-LS. */ - /* Soft power off. */ - plat_power_off(); - break; - case 1: + sus_typ = dev->suspend_types[(val >> 2) & 7]; + + if (sus_typ & SUS_POWER_OFF) { + /* Soft power off. */ + plat_power_off(); + return; + } + + if (sus_typ & SUS_SUSPEND) { + if (sus_typ & SUS_NVR) { /* Suspend to RAM. */ nvr_reg_write(0x000f, 0xff, dev->nvr); + } - /* Do a hard reset. */ + if (sus_typ & SUS_RESET_PCI) device_reset_all_pci(); + if (sus_typ & SUS_RESET_CPU) cpu_alt_reset = 0; + if (sus_typ & SUS_RESET_PCI) { pci_reset(); keyboard_at_reset(); mem_a20_alt = 0; mem_a20_recalc(); + } + if (sus_typ & (SUS_RESET_CPU | SUS_RESET_CACHE)) flushmmucache(); + if (sus_typ & SUS_RESET_CPU) resetx86(); - break; - default: - dev->regs.pmcntrl = ((dev->regs.pmcntrl & ~(0xff << shift16)) | (val << shift16)) & 0x3f07 /* 0x3c07 */; - break; + + /* Resume immediately as a power button is not implemented yet. */ + ui_msgbox_ex(MBX_INFO, L"Sleep mode", L"Press OK to resume the emulated machine.", NULL, NULL, NULL); + dev->regs.pmsts |= 0x8000; } - } else - dev->regs.pmcntrl = ((dev->regs.pmcntrl & ~(0xff << shift16)) | (val << shift16)) & 0x3f07 /* 0x3c07 */; + } + dev->regs.pmcntrl = ((dev->regs.pmcntrl & ~(0xff << shift16)) | (val << shift16)) & 0x3f07 /* 0x3c07 */; break; } } @@ -1195,11 +1204,7 @@ acpi_aux_reg_read_common(int size, uint16_t addr, void *p) acpi_t *dev = (acpi_t *) p; uint8_t ret = 0xff; - if (dev->vendor == VEN_VIA_596B) - ret = acpi_reg_read_via_596b(size, addr - (0xf0 - 0x04), p); - else if (dev->vendor == VEN_INTEL) - ret = acpi_reg_read_intel(size, addr - (0x40 - 0x04), p); - else if (dev->vendor == VEN_SMC) + if (dev->vendor == VEN_SMC) ret = acpi_aux_reg_read_smc(size, addr, p); return ret; @@ -1211,11 +1216,7 @@ acpi_aux_reg_write_common(int size, uint16_t addr, uint8_t val, void *p) { acpi_t *dev = (acpi_t *) p; - if (dev->vendor == VEN_VIA_596B) - acpi_reg_write_via_596b(size, addr - (0xf0 - 0x04), val, p); - else if (dev->vendor == VEN_INTEL) - acpi_reg_write_intel(size, addr - (0x40 - 0x04), val, p); - else if (dev->vendor == VEN_SMC) + if (dev->vendor == VEN_SMC) acpi_aux_reg_write_smc(size, addr, val, p); } @@ -1273,7 +1274,7 @@ acpi_aux_reg_readl(uint16_t addr, void *p) ret |= (acpi_aux_reg_read_common(4, addr + 2, p) << 16); ret |= (acpi_aux_reg_read_common(4, addr + 3, p) << 24); - acpi_log("ACPI: Read L %08X from %04X\n", ret, addr); + acpi_log("ACPI: Read Aux L %08X from %04X\n", ret, addr); return ret; } @@ -1287,7 +1288,7 @@ acpi_aux_reg_readw(uint16_t addr, void *p) ret = acpi_aux_reg_read_common(2, addr, p); ret |= (acpi_aux_reg_read_common(2, addr + 1, p) << 8); - acpi_log("ACPI: Read W %08X from %04X\n", ret, addr); + acpi_log("ACPI: Read Aux W %04X from %04X\n", ret, addr); return ret; } @@ -1300,6 +1301,8 @@ acpi_aux_reg_read(uint16_t addr, void *p) ret = acpi_aux_reg_read_common(1, addr, p); + acpi_log("ACPI: Read Aux B %02X from %04X\n", ret, addr); + return ret; } @@ -1338,6 +1341,8 @@ acpi_reg_write(uint16_t addr, uint8_t val, void *p) static void acpi_aux_reg_writel(uint16_t addr, uint32_t val, void *p) { + acpi_log("ACPI: Write Aux L %08X to %04X\n", val, addr); + acpi_aux_reg_write_common(4, addr, val & 0xff, p); acpi_aux_reg_write_common(4, addr + 1, (val >> 8) & 0xff, p); acpi_aux_reg_write_common(4, addr + 2, (val >> 16) & 0xff, p); @@ -1348,6 +1353,8 @@ acpi_aux_reg_writel(uint16_t addr, uint32_t val, void *p) static void acpi_aux_reg_writew(uint16_t addr, uint16_t val, void *p) { + acpi_log("ACPI: Write Aux W %04X to %04X\n", val, addr); + acpi_aux_reg_write_common(2, addr, val & 0xff, p); acpi_aux_reg_write_common(2, addr + 1, (val >> 8) & 0xff, p); } @@ -1356,6 +1363,8 @@ acpi_aux_reg_writew(uint16_t addr, uint16_t val, void *p) static void acpi_aux_reg_write(uint16_t addr, uint8_t val, void *p) { + acpi_log("ACPI: Write Aux B %02X to %04X\n", val, addr); + acpi_aux_reg_write_common(1, addr, val, p); } @@ -1385,6 +1394,8 @@ acpi_update_io_mapping(acpi_t *dev, uint32_t base, int chipset_en) break; } + acpi_log("ACPI: Update I/O %04X to %04X (%sabled)\n", dev->io_base, base, chipset_en ? "en" : "dis"); + if (dev->io_base != 0x0000) { io_removehandler(dev->io_base, size, acpi_reg_read, acpi_reg_readw, acpi_reg_readl, @@ -1407,11 +1418,6 @@ acpi_update_aux_io_mapping(acpi_t *dev, uint32_t base, int chipset_en) int size; switch (dev->vendor) { - case VEN_INTEL: - case VEN_VIA_596B: - /* Undocumented mirror of PMCNTRL. */ - size = 0x001; - break; case VEN_SMC: size = 0x008; break; @@ -1420,6 +1426,8 @@ acpi_update_aux_io_mapping(acpi_t *dev, uint32_t base, int chipset_en) break; } + acpi_log("ACPI: Update Aux I/O %04X to %04X (%sabled)\n", dev->aux_io_base, base, chipset_en ? "en" : "dis"); + if (dev->aux_io_base != 0x0000) { io_removehandler(dev->aux_io_base, size, acpi_aux_reg_read, acpi_aux_reg_readw, acpi_aux_reg_readl, @@ -1661,6 +1669,41 @@ acpi_init(const device_t *info) i2c_smbus = i2c_gpio_get_bus(dev->i2c); } + switch (dev->vendor) { + case VEN_ALI: + dev->suspend_types[0] = SUS_POWER_OFF; + dev->suspend_types[1] = SUS_POWER_OFF; + dev->suspend_types[2] = SUS_SUSPEND | SUS_NVR | SUS_RESET_CPU | SUS_RESET_PCI; + dev->suspend_types[3] = SUS_SUSPEND; + break; + + case VEN_VIA: + dev->suspend_types[0] = SUS_POWER_OFF; + dev->suspend_types[2] = SUS_SUSPEND; + break; + + case VEN_VIA_596B: + dev->suspend_types[1] = SUS_SUSPEND | SUS_NVR | SUS_RESET_CPU | SUS_RESET_PCI; + dev->suspend_types[2] = SUS_POWER_OFF; + dev->suspend_types[4] = SUS_SUSPEND; + dev->suspend_types[5] = SUS_SUSPEND | SUS_RESET_CPU; + dev->suspend_types[6] = SUS_SUSPEND | SUS_RESET_CPU | SUS_RESET_PCI; + break; + + case VEN_INTEL: + dev->suspend_types[0] = SUS_POWER_OFF; + dev->suspend_types[1] = SUS_SUSPEND | SUS_NVR | SUS_RESET_CPU | SUS_RESET_PCI; + dev->suspend_types[2] = SUS_SUSPEND | SUS_RESET_CPU; + dev->suspend_types[3] = SUS_SUSPEND | SUS_RESET_CACHE; + dev->suspend_types[4] = SUS_SUSPEND; + break; + + case VEN_SIS: + dev->suspend_types[0] = SUS_SUSPEND; + dev->suspend_types[4] = SUS_POWER_OFF; + break; + } + timer_add(&dev->timer, acpi_timer_count, dev, 0); timer_set_delay_u64(&dev->timer, ACPICONST); diff --git a/src/chipset/intel_piix.c b/src/chipset/intel_piix.c index 032a94351..a865e045a 100644 --- a/src/chipset/intel_piix.c +++ b/src/chipset/intel_piix.c @@ -813,13 +813,11 @@ piix_write(int func, int addr, uint8_t val, void *priv) fregs[0x40] = (val & 0xc0) | 1; dev->acpi_io_base = (dev->regs[3][0x41] << 8) | (dev->regs[3][0x40] & 0xc0); acpi_update_io_mapping(dev->acpi, dev->acpi_io_base, (dev->regs[3][0x80] & 0x01)); - acpi_update_aux_io_mapping(dev->acpi, dev->acpi_io_base + 0x40, (dev->regs[3][0x80] & 0x01)); break; case 0x41: fregs[0x41] = val; dev->acpi_io_base = (dev->regs[3][0x41] << 8) | (dev->regs[3][0x40] & 0xc0); acpi_update_io_mapping(dev->acpi, dev->acpi_io_base, (dev->regs[3][0x80] & 0x01)); - acpi_update_aux_io_mapping(dev->acpi, dev->acpi_io_base + 0x40, (dev->regs[3][0x80] & 0x01)); break; case 0x44: case 0x45: case 0x46: case 0x47: case 0x48: case 0x49: @@ -846,12 +844,10 @@ piix_write(int func, int addr, uint8_t val, void *priv) break; case 0x4f: case 0x80: case 0xd2: fregs[addr] = val & 0x0f; - if (addr == 0x80) { + if (addr == 0x80) acpi_update_io_mapping(dev->acpi, dev->acpi_io_base, (dev->regs[3][0x80] & 0x01)); - acpi_update_aux_io_mapping(dev->acpi, dev->acpi_io_base + 0x40, (dev->regs[3][0x80] & 0x01)); - } else if (addr == 0xd2) { + else if (addr == 0xd2) smbus_update_io_mapping(dev); - } break; case 0x50: fregs[addr] = val & 0x3f; diff --git a/src/chipset/via_pipc.c b/src/chipset/via_pipc.c index d609f47b5..41667edfd 100644 --- a/src/chipset/via_pipc.c +++ b/src/chipset/via_pipc.c @@ -1128,7 +1128,6 @@ pipc_write(int func, int addr, uint8_t val, void *priv) c -= 0x400; acpi_set_timer32(dev->acpi, dev->power_regs[0x41] & 0x08); acpi_update_io_mapping(dev->acpi, c, dev->power_regs[0x41] & 0x80); - acpi_update_aux_io_mapping(dev->acpi, c + 0xf0, dev->power_regs[0x41] & 0x80); break; case 0x42: diff --git a/src/include/86box/acpi.h b/src/include/86box/acpi.h index 08019d352..4637262f8 100644 --- a/src/include/86box/acpi.h +++ b/src/include/86box/acpi.h @@ -39,6 +39,13 @@ extern "C" { #define SCI_EN (1 << 0) #define SUS_EN (1 << 13) +#define SUS_POWER_OFF (1 << 0) +#define SUS_SUSPEND (1 << 1) +#define SUS_NVR (1 << 2) +#define SUS_RESET_CPU (1 << 3) +#define SUS_RESET_CACHE (1 << 4) +#define SUS_RESET_PCI (1 << 5) + #define ACPI_ENABLE 0xf1 #define ACPI_DISABLE 0xf0 @@ -79,7 +86,8 @@ typedef struct { acpi_regs_t regs; uint8_t gpireg2_default, pad[3], - gporeg_default[4]; + gporeg_default[4], + suspend_types[8]; uint16_t io_base, aux_io_base; int vendor, slot, irq_mode, From 6dd744141c857e574d68db6841a5a7eb27bb797d Mon Sep 17 00:00:00 2001 From: "Andreas J. Reichel" Date: Sun, 17 Oct 2021 21:17:57 +0200 Subject: [PATCH 34/56] Make it build in arch linux and Debian * Probably unresolved external due to newer gcc, thus added libsdl to libui.a as well. * Make it work for all distros, which either have SDL2::SDL2 or ${SDL2_LIBRARIES}, provided by sdl2-config or FindSDL2.cmake. Signed-off-by: Andreas J. Reichel --- src/CMakeLists.txt | 6 +++++- src/unix/CMakeLists.txt | 14 +++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 47cb558db..49025d4dd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -82,7 +82,11 @@ if(MINGW) elseif(WIN32) target_link_libraries(86Box SDL2::SDL2) else() - target_link_libraries(86Box ${SDL2_LIBRARIES}) + if (TARGET SDL2::SDL2) + target_link_libraries(86Box SDL2::SDL2) + else() + target_link_libraries(86Box ${SDL2_LIBRARIES}) + endif() endif() find_package(PNG REQUIRED) diff --git a/src/unix/CMakeLists.txt b/src/unix/CMakeLists.txt index d0524e38b..e21265370 100644 --- a/src/unix/CMakeLists.txt +++ b/src/unix/CMakeLists.txt @@ -12,10 +12,22 @@ add_library(plat STATIC ${PLAT_SOURCES} unix_thread.c) add_library(ui STATIC unix.c unix_sdl.c unix_cdrom.c) target_compile_definitions(ui PUBLIC _FILE_OFFSET_BITS=64) target_link_libraries(ui dl) + +find_package(SDL2 REQUIRED) +include_directories(${SDL2_INCLUDE_DIRS}) +if(MINGW) + target_link_libraries(ui SDL2::SDL2-static) +else() + if (TARGET SDL2::SDL2) + target_link_libraries(ui SDL2::SDL2) + else() + target_link_libraries(ui ${SDL2_LIBRARIES}) + endif() +endif() if (ALSA_FOUND) target_link_libraries(plat ALSA::ALSA) endif() set(THREADS_PREFER_PTHREAD_FLAG TRUE) find_package(Threads REQUIRED) -target_link_libraries(86Box Threads::Threads) \ No newline at end of file +target_link_libraries(86Box Threads::Threads) From e05aab152a18e2054651f7b210190dd2e6e29e75 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Mon, 18 Oct 2021 21:58:44 -0300 Subject: [PATCH 35/56] Implement I/O port traps on PIIX and VIA ACPI --- src/acpi.c | 103 +++++++++------ src/chipset/intel_piix.c | 164 +++++++++++++++++++++++- src/chipset/via_pipc.c | 264 ++++++++++++++++++++++++++++++++++++--- src/include/86box/acpi.h | 9 +- src/include/86box/io.h | 5 + src/io.c | 136 ++++++++++++++++++-- 6 files changed, 617 insertions(+), 64 deletions(-) diff --git a/src/acpi.c b/src/acpi.c index a7301304c..da7c48796 100644 --- a/src/acpi.c +++ b/src/acpi.c @@ -63,13 +63,10 @@ acpi_log(const char *fmt, ...) #endif -static void -acpi_update_irq(void *priv) +void +acpi_update_irq(acpi_t *dev) { - acpi_t *dev = (acpi_t *) priv; - int sci_level; - - sci_level = (dev->regs.pmsts & dev->regs.pmen) & (RTC_EN | PWRBTN_EN | GBL_EN | TMROF_EN); + int sci_level = (dev->regs.pmsts & dev->regs.pmen) & (RTC_EN | PWRBTN_EN | GBL_EN | TMROF_EN); if (dev->vendor == VEN_SMC) sci_level |= (dev->regs.pmsts & BM_STS); @@ -87,11 +84,9 @@ acpi_update_irq(void *priv) } -static void -acpi_raise_smi(void *priv) +void +acpi_raise_smi(acpi_t *dev) { - acpi_t *dev = (acpi_t *) priv; - if (dev->regs.glbctl & 0x01) { if ((dev->vendor == VEN_VIA) || (dev->vendor == VEN_VIA_596B)) { if ((!dev->regs.smi_lock || !dev->regs.smi_active)) { @@ -530,10 +525,11 @@ acpi_reg_read_via_596b(int size, uint16_t addr, void *p) shift32 = (addr & 3) << 3; switch (addr) { - case 0x42: - /* GPIO port Output Value */ - if (size == 1) - ret = dev->regs.gpio_val & 0x13; + case 0x40: /* Extended I/O Trap Status (686A/B) */ + ret = dev->regs.extiotrapsts; + break; + case 0x42: /* Extended I/O Trap Enable (686A/B) */ + ret = dev->regs.extiotrapen; break; case 0x44: case 0x45: /* External SMI Input Value */ @@ -817,6 +813,8 @@ acpi_reg_write_intel(int size, uint16_t addr, uint8_t val, void *p) case 0x2c: case 0x2d: case 0x2e: case 0x2f: /* DEVCTL - Device Control Register (IO) */ dev->regs.devctl = ((dev->regs.devctl & ~(0xff << shift32)) | (val << shift32)) & 0x0fffffff; + if (dev->trap_update) + dev->trap_update(dev->trap_priv); break; case 0x34: case 0x35: case 0x36: case 0x37: /* GPOREG - General Purpose Output Register (IO) */ @@ -957,14 +955,6 @@ acpi_reg_write_via_common(int size, uint16_t addr, uint8_t val, void *p) /* Power Supply Control */ dev->regs.pscntrl = ((dev->regs.pscntrl & ~(0xff << shift16)) | (val << shift16)) & 0x0701; break; - case 0x28: case 0x29: - /* GLBSTS - Global Status Register (IO) */ - dev->regs.glbsts &= ~((val << shift16) & 0x007f); - break; - case 0x2a: case 0x2b: - /* GLBEN - Global Enable Register (IO) */ - dev->regs.glben = ((dev->regs.glben & ~(0xff << shift16)) | (val << shift16)) & 0x007f; - break; case 0x2c: /* GLBCTL - Global Control Register (IO) */ dev->regs.glbctl = (dev->regs.glbctl & ~0xff) | (val & 0xff); @@ -991,14 +981,6 @@ acpi_reg_write_via_common(int size, uint16_t addr, uint8_t val, void *p) acpi_raise_smi(dev); } break; - case 0x30: case 0x31: case 0x32: case 0x33: - /* Primary Activity Detect Status */ - dev->regs.padsts &= ~((val << shift32) & 0x000000fd); - break; - case 0x34: case 0x35: case 0x36: case 0x37: - /* Primary Activity Detect Enable */ - dev->regs.paden = ((dev->regs.paden & ~(0xff << shift32)) | (val << shift32)) & 0x000000fd; - break; case 0x38: case 0x39: case 0x3a: case 0x3b: /* GP Timer Reload Enable */ dev->regs.gptren = ((dev->regs.gptren & ~(0xff << shift32)) | (val << shift32)) & 0x000000d9; @@ -1030,13 +1012,32 @@ static void acpi_reg_write_via(int size, uint16_t addr, uint8_t val, void *p) { acpi_t *dev = (acpi_t *) p; - int shift16; + int shift16, shift32; addr &= 0xff; acpi_log("(%i) ACPI Write (%i) %02X: %02X\n", in_smm, size, addr, val); shift16 = (addr & 1) << 3; + shift32 = (addr & 3) << 3; switch (addr) { + case 0x28: case 0x29: + /* GLBSTS - Global Status Register (IO) */ + dev->regs.glbsts &= ~((val << shift16) & 0x007f); + break; + case 0x2a: case 0x2b: + /* GLBEN - Global Enable Register (IO) */ + dev->regs.glben = ((dev->regs.glben & ~(0xff << shift16)) | (val << shift16)) & 0x007f; + break; + case 0x30: case 0x31: case 0x32: case 0x33: + /* Primary Activity Detect Status */ + dev->regs.padsts &= ~((val << shift32) & 0x000000fd); + break; + case 0x34: case 0x35: case 0x36: case 0x37: + /* Primary Activity Detect Enable */ + dev->regs.paden = ((dev->regs.paden & ~(0xff << shift32)) | (val << shift32)) & 0x000000fd; + if (dev->trap_update) + dev->trap_update(dev->trap_priv); + break; case 0x40: /* GPIO Direction Control */ if (size == 1) { @@ -1066,17 +1067,37 @@ static void acpi_reg_write_via_596b(int size, uint16_t addr, uint8_t val, void *p) { acpi_t *dev = (acpi_t *) p; - int shift32; + int shift16, shift32; addr &= 0x7f; acpi_log("(%i) ACPI Write (%i) %02X: %02X\n", in_smm, size, addr, val); + shift16 = (addr & 1) << 3; shift32 = (addr & 3) << 3; switch (addr) { - case 0x42: - /* GPIO port Output Value */ - if (size == 1) - dev->regs.gpio_val = val & 0x13; + case 0x28: case 0x29: + /* GLBSTS - Global Status Register (IO) */ + dev->regs.glbsts &= ~((val << shift16) & 0xfdff); + break; + case 0x2a: case 0x2b: + /* GLBEN - Global Enable Register (IO) */ + dev->regs.glben = ((dev->regs.glben & ~(0xff << shift16)) | (val << shift16)) & 0xfdff; + break; + case 0x30: case 0x31: case 0x32: case 0x33: + /* Primary Activity Detect Status */ + dev->regs.padsts &= ~((val << shift32) & 0x000007ff); + break; + case 0x34: case 0x35: case 0x36: case 0x37: + /* Primary Activity Detect Enable */ + dev->regs.paden = ((dev->regs.paden & ~(0xff << shift32)) | (val << shift32)) & 0x000007ff; + if (dev->trap_update) + dev->trap_update(dev->trap_priv); + break; + case 0x40: /* Extended I/O Trap Status (686A/B) */ + dev->regs.extiotrapsts &= ~(val & 0x13); + break; + case 0x42: /* Extended I/O Trap Enable (686A/B) */ + dev->regs.extiotrapen = val & 0x13; break; case 0x4c: case 0x4d: case 0x4e: case 0x4f: /* GPO Port Output Value */ @@ -1534,6 +1555,14 @@ acpi_set_nvr(acpi_t *dev, nvr_t *nvr) } +void +acpi_set_trap_update(acpi_t *dev, void (*update)(void *priv), void *priv) +{ + dev->trap_update = update; + dev->trap_priv = priv; +} + + static void acpi_apm_out(uint16_t port, uint8_t val, void *p) { @@ -1771,7 +1800,7 @@ const device_t acpi_via_device = const device_t acpi_via_596b_device = { - "VIA ACPI (VT82C596B)", + "VIA VT82C596 ACPI", DEVICE_PCI, VEN_VIA_596B, acpi_init, diff --git a/src/chipset/intel_piix.c b/src/chipset/intel_piix.c index a865e045a..d36109ffd 100644 --- a/src/chipset/intel_piix.c +++ b/src/chipset/intel_piix.c @@ -49,8 +49,14 @@ #include <86box/chipset.h> -typedef struct -{ +typedef struct { + struct _piix_ *dev; + void *trap; + uint8_t dev_id; + uint32_t *sts_reg, *en_reg, sts_mask, en_mask; +} piix_io_trap_t; + +typedef struct _piix_ { uint8_t cur_readout_reg, rev, type, func_shift, max_func, pci_slot, @@ -66,6 +72,7 @@ typedef struct ddma_t * ddma; usb_t * usb; acpi_t * acpi; + piix_io_trap_t io_traps[26]; port_92_t * port_92; pc_timer_t fast_off_timer; } piix_t; @@ -265,6 +272,148 @@ nvr_update_io_mapping(piix_t *dev) } +static void +piix_trap_io(int size, uint16_t addr, uint8_t write, uint8_t val, void *priv) +{ + piix_io_trap_t *trap = (piix_io_trap_t *) priv; + + if (*(trap->en_reg) & trap->en_mask) { + *(trap->sts_reg) |= trap->sts_mask; + acpi_raise_smi(trap->dev->acpi); + } +} + + +static void +piix_trap_update_devctl(piix_t *dev, uint8_t trap_id, uint8_t dev_id, + uint32_t devctl_mask, uint8_t enable, + uint16_t addr, uint16_t size) +{ + piix_io_trap_t *trap = &dev->io_traps[trap_id]; + enable = (dev->acpi->regs.devctl & devctl_mask) && enable; + + /* Set up Device I/O traps dynamically. */ + if (enable && !trap->trap) { + trap->dev = dev; + trap->trap = io_trap_add(piix_trap_io, trap); + trap->dev_id = dev_id; + trap->sts_reg = &dev->acpi->regs.devsts; + trap->sts_mask = 0x00010000 << dev_id; + trap->en_reg = &dev->acpi->regs.devctl; + trap->en_mask = devctl_mask; + } + +//#ifdef ENABLE_PIIX_LOG + if ((dev_id == 9) || (dev_id == 10) || (dev_id == 12) || (dev_id == 13)) + pclog("PIIX: Mapping trap device %d to %04X-%04X (enable %d)\n", dev_id, addr, addr + size - 1, enable); +//#endif + + /* Remap I/O trap. */ + io_trap_remap(trap->trap, enable, addr, size); +} + + +static void +piix_trap_update(void *priv) +{ + piix_t *dev = (piix_t *) priv; + uint8_t trap_id = 0, *fregs = dev->regs[3]; + uint16_t temp; + + piix_trap_update_devctl(dev, trap_id++, 0, 0x00000002, 1, 0x1f0, 8); + piix_trap_update_devctl(dev, trap_id++, 0, 0x00000002, 1, 0x3f6, 1); + + piix_trap_update_devctl(dev, trap_id++, 1, 0x00000008, 1, 0x1f0, 8); + piix_trap_update_devctl(dev, trap_id++, 1, 0x00000008, 1, 0x3f6, 1); + + piix_trap_update_devctl(dev, trap_id++, 2, 0x00000020, 1, 0x170, 8); + piix_trap_update_devctl(dev, trap_id++, 2, 0x00000020, 1, 0x376, 1); + + piix_trap_update_devctl(dev, trap_id++, 3, 0x00000080, 1, 0x170, 8); + piix_trap_update_devctl(dev, trap_id++, 3, 0x00000080, 1, 0x376, 1); + + piix_trap_update_devctl(dev, trap_id++, 4, 0x00000200, fregs[0x5c] & 0x08, 0x220 + (0x20 * ((fregs[0x5c] >> 5) & 0x03)), 20); + piix_trap_update_devctl(dev, trap_id++, 4, 0x00000200, fregs[0x5c] & 0x10, 0x200, 8); + piix_trap_update_devctl(dev, trap_id++, 4, 0x00000200, fregs[0x5c] & 0x08, 0x388, 4); + switch (fregs[0x5d] & 0x03) { + case 0x00: temp = 0x530; break; + case 0x01: temp = 0x604; break; + case 0x02: temp = 0xe80; break; + default: temp = 0xf40; break; + } + piix_trap_update_devctl(dev, trap_id++, 4, 0x00000200, fregs[0x5c] & 0x80, temp, 8); + piix_trap_update_devctl(dev, trap_id++, 4, 0x00000200, fregs[0x5c] & 0x01, 0x300 + (0x10 * ((fregs[0x5c] >> 1) & 0x03)), 4); + + piix_trap_update_devctl(dev, trap_id++, 5, 0x00000800, fregs[0x51] & 0x10, 0x370 + (0x80 * !(fregs[0x63] & 0x10)), 6); + piix_trap_update_devctl(dev, trap_id++, 5, 0x00000800, fregs[0x51] & 0x10, 0x377 + (0x80 * !(fregs[0x63] & 0x10)), 1); + + switch (fregs[0x67] & 0x07) { + case 0x00: temp = 0x3f8; break; + case 0x01: temp = 0x2f8; break; + case 0x02: temp = 0x220; break; + case 0x03: temp = 0x228; break; + case 0x04: temp = 0x238; break; + case 0x05: temp = 0x2e8; break; + case 0x06: temp = 0x338; break; + default: temp = 0x3e8; break; + } + piix_trap_update_devctl(dev, trap_id++, 6, 0x00002000, fregs[0x51] & 0x40, temp, 8); + + switch (fregs[0x67] & 0x70) { + case 0x00: temp = 0x3f8; break; + case 0x10: temp = 0x2f8; break; + case 0x20: temp = 0x220; break; + case 0x30: temp = 0x228; break; + case 0x40: temp = 0x238; break; + case 0x50: temp = 0x2e8; break; + case 0x60: temp = 0x338; break; + default: temp = 0x3e8; break; + } + piix_trap_update_devctl(dev, trap_id++, 7, 0x00008000, fregs[0x52] & 0x01, temp, 8); + + switch (fregs[0x63] & 0x06) { + case 0x00: + piix_trap_update_devctl(dev, trap_id++, 8, 0x00020000, fregs[0x52] & 0x04, 0x3bc, 4); + piix_trap_update_devctl(dev, trap_id++, 8, 0x00020000, fregs[0x52] & 0x04, 0x7bc, 3); + break; + + case 0x02: + piix_trap_update_devctl(dev, trap_id++, 8, 0x00020000, fregs[0x52] & 0x04, 0x378, 8); + piix_trap_update_devctl(dev, trap_id++, 8, 0x00020000, fregs[0x52] & 0x04, 0x778, 3); + break; + + case 0x04: + piix_trap_update_devctl(dev, trap_id++, 8, 0x00020000, fregs[0x52] & 0x04, 0x278, 8); + piix_trap_update_devctl(dev, trap_id++, 8, 0x00020000, fregs[0x52] & 0x04, 0x678, 3); + break; + + default: + piix_trap_update_devctl(dev, trap_id++, 8, 0x00020000, fregs[0x52] & 0x04, 0, 0); + piix_trap_update_devctl(dev, trap_id++, 8, 0x00020000, fregs[0x52] & 0x04, 0, 0); + break; + } + + temp = fregs[0x62] & 0x0f; + piix_trap_update_devctl(dev, trap_id++, 9, 0x00080000, fregs[0x62] & 0x20, (fregs[0x60] | (fregs[0x61] << 8)) & ~temp, temp + 1); + + temp = fregs[0x66] & 0x0f; + piix_trap_update_devctl(dev, trap_id++, 10, 0x00200000, fregs[0x66] & 0x20, (fregs[0x64] | (fregs[0x65] << 8)) & ~temp, temp + 1); + + piix_trap_update_devctl(dev, trap_id++, 11, 0x00800000, fregs[0x5f] & 0x04, 0x3b0, 48); + piix_trap_update_devctl(dev, trap_id++, 11, 0x00800000, fregs[0x5f] & 0x10, 0x60, 1); + piix_trap_update_devctl(dev, trap_id++, 11, 0x00800000, fregs[0x5f] & 0x10, 0x64, 1); + /* [A0000:BFFFF] memory trap not implemented. */ + + temp = fregs[0x6a] & 0x0f; + piix_trap_update_devctl(dev, trap_id++, 12, 0x01000000, fregs[0x6a] & 0x10, (fregs[0x68] | (fregs[0x69] << 8)) & ~temp, temp + 1); + /* Programmable memory trap not implemented. */ + + temp = fregs[0x72] & 0x0f; + piix_trap_update_devctl(dev, trap_id++, 13, 0x02000000, fregs[0x72] & 0x10, (fregs[0x70] | (fregs[0x71] << 8)) & ~temp, temp + 1); + /* Programmable memory trap not implemented. */ +} + + static void piix_write(int func, int addr, uint8_t val, void *priv) { @@ -835,6 +984,10 @@ piix_write(int func, int addr, uint8_t val, void *priv) case 0xd3: case 0xd4: case 0xd5: fregs[addr] = val; + if ((addr == 0x5c) || (addr == 0x60) || (addr == 0x61) || (addr == 0x62) || + (addr == 0x64) || (addr == 0x65) || (addr == 0x68) || (addr == 0x69) || + (addr == 0x70) || (addr == 0x71)) + piix_trap_update(dev); break; case 0x4a: fregs[addr] = val & 0x73; @@ -854,9 +1007,11 @@ piix_write(int func, int addr, uint8_t val, void *priv) break; case 0x51: fregs[addr] = val & 0x58; + piix_trap_update(dev); break; case 0x52: fregs[addr] = val & 0x7f; + piix_trap_update(dev); break; case 0x58: fregs[addr] = val & 0x77; @@ -867,12 +1022,16 @@ piix_write(int func, int addr, uint8_t val, void *priv) break; case 0x63: fregs[addr] = val & 0xf7; + piix_trap_update(dev); break; case 0x66: fregs[addr] = val & 0xef; + piix_trap_update(dev); break; case 0x6a: case 0x72: case 0x7a: case 0x7e: fregs[addr] = val & 0x1f; + if ((addr == 0x6a) || (addr == 0x72)) + piix_trap_update(dev); break; case 0x6d: case 0x75: fregs[addr] = val & 0x80; @@ -1297,6 +1456,7 @@ static void acpi_set_slot(dev->acpi, dev->pci_slot); acpi_set_nvr(dev->acpi, dev->nvr); acpi_set_gpireg2_default(dev->acpi, (dev->type > 4) ? 0xf1 : 0xdd); + acpi_set_trap_update(dev->acpi, piix_trap_update, dev); dev->ddma = device_add(&ddma_device); } else diff --git a/src/chipset/via_pipc.c b/src/chipset/via_pipc.c index 41667edfd..863388e2d 100644 --- a/src/chipset/via_pipc.c +++ b/src/chipset/via_pipc.c @@ -67,23 +67,69 @@ #define VIA_PIPC_8231 0x82311000 -typedef struct -{ +enum { + TRAP_DRQ = 0, + TRAP_PIRQ, + TRAP_PIDE_MAIN, + TRAP_PIDE_SIDE, + TRAP_SIDE_MAIN, + TRAP_SIDE_SIDE, + TRAP_FLP_MAIN, + TRAP_FLP_SIDE, + TRAP_COM1, + TRAP_COM3, + TRAP_COM2, + TRAP_COM4, + TRAP_LPT_LPT1, + TRAP_LPT_LPT2, + TRAP_VGA, + TRAP_KBC, + TRAP_AUD_MIDI_0, + TRAP_AUD_MIDI_1, + TRAP_AUD_MIDI_2, + TRAP_AUD_MIDI_3, + TRAP_AUD_SB_0, + TRAP_AUD_SB_1, + TRAP_AUD_SB_2, + TRAP_AUD_SB_3, + TRAP_AUD_GAME, + TRAP_AUD_WSS_0, + TRAP_AUD_WSS_1, + TRAP_AUD_WSS_2, + TRAP_AUD_WSS_3, + TRAP_GR0, + TRAP_GR1, + TRAP_GR2, + TRAP_GR3, + TRAP_MAX +}; + +typedef struct { + struct _pipc_ *dev; + void *trap; + uint32_t *sts_reg, *en_reg, mask; +} pipc_io_trap_t; + +typedef struct _pipc_ { uint32_t local; - uint8_t max_func; + uint8_t max_func, max_pcs; uint8_t pci_isa_regs[256], ide_regs[256], usb_regs[2][256], power_regs[256], ac97_regs[2][256], fmnmi_regs[4]; + sff8038i_t *bm[2]; nvr_t *nvr; int nvr_enabled, slot; ddma_t *ddma; smbus_piix4_t *smbus; usb_t *usb[2]; + acpi_t *acpi; + pipc_io_trap_t io_traps[TRAP_MAX]; + void *gameport, *ac97; sb_t *sb; uint16_t midigame_base, sb_base, fmnmi_base; @@ -117,6 +163,32 @@ static uint8_t pipc_read(int func, int addr, void *priv); static void pipc_write(int func, int addr, uint8_t val, void *priv); +static void +pipc_trap_io_pact(int size, uint16_t addr, uint8_t write, uint8_t val, void *priv) +{ + pipc_io_trap_t *trap = (pipc_io_trap_t *) priv; + + if (*(trap->en_reg) & trap->mask) { + *(trap->sts_reg) |= trap->mask; + trap->dev->acpi->regs.glbsts |= 0x0001; + if (trap->dev->acpi->regs.glben & 0x0001) + acpi_raise_smi(trap->dev->acpi); + } +} + + +static void +pipc_io_trap_glb(int size, uint16_t addr, uint8_t write, uint8_t val, void *priv) +{ + pipc_io_trap_t *trap = (pipc_io_trap_t *) priv; + + if (*(trap->en_reg) & trap->mask) { + *(trap->sts_reg) |= trap->mask; + acpi_raise_smi(trap->dev->acpi); + } +} + + static void pipc_reset_hard(void *priv) { @@ -136,7 +208,7 @@ pipc_reset_hard(void *priv) memset(dev->power_regs, 0, 256); memset(dev->ac97_regs, 0, 512); - /* PCI-ISA bridge registers */ + /* PCI-ISA bridge registers. */ dev->pci_isa_regs[0x00] = 0x06; dev->pci_isa_regs[0x01] = 0x11; dev->pci_isa_regs[0x02] = dev->local >> 16; dev->pci_isa_regs[0x03] = dev->local >> 24; @@ -164,7 +236,9 @@ pipc_reset_hard(void *priv) pic_set_shadow(0); - /* IDE registers */ + dev->max_pcs = (dev->local >= VIA_PIPC_686A) ? 3 : 1; + + /* IDE registers. */ dev->max_func++; dev->ide_regs[0x00] = 0x06; dev->ide_regs[0x01] = 0x11; dev->ide_regs[0x02] = 0x71; dev->ide_regs[0x03] = 0x05; @@ -212,7 +286,7 @@ pipc_reset_hard(void *priv) dev->ide_regs[0xc2] = 0x02; } - /* USB registers */ + /* USB registers. */ for (i = 0; i <= (dev->local >= VIA_PIPC_686A); i++) { dev->max_func++; dev->usb_regs[i][0x00] = 0x06; dev->usb_regs[i][0x01] = 0x11; @@ -260,7 +334,7 @@ pipc_reset_hard(void *priv) dev->usb_regs[i][0xc1] = 0x20; } - /* power management registers */ + /* Power management registers. */ if (dev->acpi) { dev->max_func++; dev->power_regs[0x00] = 0x06; dev->power_regs[0x01] = 0x11; @@ -317,9 +391,26 @@ pipc_reset_hard(void *priv) dev->power_regs[0x80] = 0x01; else if (dev->local >= VIA_PIPC_596B) dev->power_regs[0x90] = 0x01; + + /* Set up PCS I/O traps. */ + pipc_io_trap_t *trap; + for (i = 0; i <= dev->max_pcs; i++) { + trap = &dev->io_traps[TRAP_GR0 + i]; + trap->dev = dev; + trap->trap = io_trap_add(pipc_io_trap_glb, trap); + if (i & 2) { + trap->sts_reg = (uint32_t *) &dev->acpi->regs.extiotrapsts; + trap->en_reg = (uint32_t *) &dev->acpi->regs.extiotrapen; + trap->mask = 0x01 << (i & 1); + } else { + trap->sts_reg = &dev->acpi->regs.glbsts; + trap->en_reg = &dev->acpi->regs.glben; + trap->mask = 0x4000 << i; + } + } } - /* AC97/MC97 registers */ + /* AC97/MC97 registers. */ if (dev->local >= VIA_PIPC_686A) { for (i = 0; i <= 1; i++) { dev->max_func++; @@ -459,6 +550,139 @@ pipc_bus_master_handlers(pipc_t *dev) } +static void +pipc_pcs_update(pipc_t *dev) +{ + uint8_t i, io_base_reg, io_mask_reg, io_mask_shift, enable; + uint16_t io_base, io_mask; + + for (i = 0; i <= dev->max_pcs; i++) { + if (i & 2) { + io_base_reg = 0x8c; + io_mask_reg = 0x8a; + } else { + io_base_reg = 0x78; + io_mask_reg = 0x80; + } + io_base_reg |= (i & 1) << 1; + io_mask_shift = (i & 1) << 2; + + if (dev->local <= VIA_PIPC_596B) + enable = dev->pci_isa_regs[0x76] & (0x10 << i); + else + enable = dev->pci_isa_regs[0x8b] & (0x01 << i); + + io_base = dev->pci_isa_regs[io_base_reg] | (dev->pci_isa_regs[io_base_reg | 1] << 8); + io_mask = (dev->pci_isa_regs[io_mask_reg] >> io_mask_shift) & 0x000f; + + pipc_log("PIPC: Mapping PCS%d to %04X-%04X (enable %d)\n", i, io_base, io_base + io_mask, enable); + io_trap_remap(dev->io_traps[TRAP_GR0 + i].trap, enable, io_base & ~io_mask, io_mask + 1); + } +} + + +static void +pipc_trap_update_paden(pipc_t *dev, uint8_t trap_id, + uint32_t paden_mask, uint8_t enable, + uint16_t addr, uint16_t size) +{ + pipc_io_trap_t *trap = &dev->io_traps[trap_id]; + enable = (dev->acpi->regs.paden & paden_mask) && enable; + + /* Set up Primary Activity Detect I/O traps dynamically. */ + if (enable && !trap->trap) { + trap->dev = dev; + trap->trap = io_trap_add(pipc_trap_io_pact, trap); + trap->sts_reg = &dev->acpi->regs.padsts; + trap->en_reg = &dev->acpi->regs.paden; + trap->mask = paden_mask; + } + + /* Remap I/O trap. */ + io_trap_remap(trap->trap, enable, addr, size); +} + + +static void +pipc_trap_update_586(void *priv) +{ + pipc_t *dev = (pipc_t *) priv; + + /* TRAP_DRQ (00000001) and TRAP_PIRQ (00000002) not implemented. */ + + pipc_trap_update_paden(dev, TRAP_PIDE_MAIN, 0x00000008, 1, 0x1f0, 8); + pipc_trap_update_paden(dev, TRAP_SIDE_MAIN, 0x00000008, 1, 0x170, 8); + pipc_trap_update_paden(dev, TRAP_FLP_MAIN, 0x00000008, 1, 0x3f5, 1); + + pipc_trap_update_paden(dev, TRAP_VGA, 0x00000010, 1, 0x3b0, 48); + /* [A0000:BFFFF] memory trap not implemented. */ + + pipc_trap_update_paden(dev, TRAP_LPT_LPT1, 0x00000020, 1, 0x378, 8); + pipc_trap_update_paden(dev, TRAP_LPT_LPT2, 0x00000020, 1, 0x278, 8); + + pipc_trap_update_paden(dev, TRAP_COM1, 0x00000040, 1, 0x3f8, 8); + pipc_trap_update_paden(dev, TRAP_COM2, 0x00000040, 1, 0x2f8, 8); + pipc_trap_update_paden(dev, TRAP_COM3, 0x00000040, 1, 0x3e8, 8); + pipc_trap_update_paden(dev, TRAP_COM4, 0x00000040, 1, 0x2e8, 8); + + pipc_trap_update_paden(dev, TRAP_KBC, 0x00000080, 1, 0x60, 1); +} + + +static void +pipc_trap_update_596(void *priv) +{ + pipc_t *dev = (pipc_t *) priv; + int i; + + /* TRAP_DRQ (00000001) and TRAP_PIRQ (00000002) not implemented. */ + + pipc_trap_update_paden(dev, TRAP_PIDE_MAIN, 0x00000004, 1, 0x1f0, 8); + pipc_trap_update_paden(dev, TRAP_PIDE_SIDE, 0x00000004, 1, 0x3f6, 1); + + pipc_trap_update_paden(dev, TRAP_SIDE_MAIN, 0x00000008, 1, 0x170, 8); + pipc_trap_update_paden(dev, TRAP_SIDE_SIDE, 0x00000008, 1, 0x376, 1); + + pipc_trap_update_paden(dev, TRAP_FLP_MAIN, 0x00000010, 1, 0x3f0, 6); + pipc_trap_update_paden(dev, TRAP_FLP_SIDE, 0x00000010, 1, 0x3f7, 1); + + pipc_trap_update_paden(dev, TRAP_COM1, 0x00000020, 1, 0x3f8, 8); + pipc_trap_update_paden(dev, TRAP_COM3, 0x00000020, 1, 0x3e8, 8); + + pipc_trap_update_paden(dev, TRAP_COM2, 0x00000040, 1, 0x2f8, 8); + pipc_trap_update_paden(dev, TRAP_COM4, 0x00000040, 1, 0x2e8, 8); + + pipc_trap_update_paden(dev, TRAP_LPT_LPT1, 0x00000080, 1, 0x378, 8); + pipc_trap_update_paden(dev, TRAP_LPT_LPT2, 0x00000080, 1, 0x278, 8); + + pipc_trap_update_paden(dev, TRAP_VGA, 0x00000100, 1, 0x3b0, 48); + /* [A0000:BFFFF] memory trap not implemented. */ + + pipc_trap_update_paden(dev, TRAP_KBC, 0x00000200, 1, 0x60, 1); + + /* The following traps are poorly documented and assumed to operate on all ranges allowed + by the Positive Decoding Control registers. I couldn't probe this behavior on hardware. + It's better to be safe and cover all of them than to assume Intel-like behavior (one range). */ + + for (i = 0; i < 3; i++) { + pipc_trap_update_paden(dev, TRAP_AUD_MIDI_0 + i, + 0x00000400, (dev->local <= VIA_PIPC_596B) || (dev->power_regs[0x40] & 0x01), + 0x300 + (0x10 * i), 4); + + pipc_trap_update_paden(dev, TRAP_AUD_SB_0 + i, + 0x00000400, (dev->local <= VIA_PIPC_596B) || (dev->power_regs[0x40] & 0x02), + 0x220 + (0x20 * i), 20); + } + + pipc_trap_update_paden(dev, TRAP_AUD_GAME, 0x00000400, (dev->local <= VIA_PIPC_596B) || (dev->power_regs[0x40] & 0x04), 0x200, 8); + + pipc_trap_update_paden(dev, TRAP_AUD_WSS_0, 0x00000400, (dev->local <= VIA_PIPC_596B) || (dev->power_regs[0x40] & 0x08), 0x530, 8); + pipc_trap_update_paden(dev, TRAP_AUD_WSS_1, 0x00000400, (dev->local <= VIA_PIPC_596B) || (dev->power_regs[0x40] & 0x08), 0x604, 8); + pipc_trap_update_paden(dev, TRAP_AUD_WSS_2, 0x00000400, (dev->local <= VIA_PIPC_596B) || (dev->power_regs[0x40] & 0x08), 0xe80, 8); + pipc_trap_update_paden(dev, TRAP_AUD_WSS_3, 0x00000400, (dev->local <= VIA_PIPC_596B) || (dev->power_regs[0x40] & 0x08), 0xf40, 8); +} + + static void pipc_sgd_handlers(pipc_t *dev, uint8_t modem) { @@ -882,17 +1106,21 @@ pipc_write(int func, int addr, uint8_t val, void *priv) dev->pci_isa_regs[(addr - 0x44)] = val; break; + case 0x74: case 0x8b: + case 0x78: case 0x79: case 0x7a: case 0x7b: + case 0x8c: case 0x8d: case 0x8e: case 0x8f: + case 0x80: case 0x8a: + dev->pci_isa_regs[addr] = val; + pipc_pcs_update(dev); + break; + case 0x77: - if (val & 0x10) + if ((dev->local >= VIA_PIPC_686A) && (val & 0x10)) pclog("PIPC: Warning: Internal I/O APIC enabled.\n"); nvr_via_wp_set(!!(val & 0x04), 0x32, dev->nvr); nvr_via_wp_set(!!(val & 0x02), 0x0d, dev->nvr); break; - case 0x80: case 0x86: case 0x87: - dev->pci_isa_regs[addr] &= ~(val); - break; - default: dev->pci_isa_regs[addr] = val; break; @@ -1326,10 +1554,13 @@ pipc_init(const device_t *info) else if (dev->local >= VIA_PIPC_596A) dev->smbus = device_add(&piix4_smbus_device); - if (dev->local >= VIA_PIPC_596A) + if (dev->local >= VIA_PIPC_596A) { dev->acpi = device_add(&acpi_via_596b_device); - else if (dev->local >= VIA_PIPC_586B) + acpi_set_trap_update(dev->acpi, pipc_trap_update_596, dev); + } else if (dev->local >= VIA_PIPC_586B) { dev->acpi = device_add(&acpi_via_device); + acpi_set_trap_update(dev->acpi, pipc_trap_update_586, dev); + } dev->usb[0] = device_add_inst(&usb_device, 1); if (dev->local >= VIA_PIPC_686A) { @@ -1383,6 +1614,9 @@ pipc_close(void *p) pipc_log("PIPC: close()\n"); + for (int i = 0; i < TRAP_MAX; i++) + io_trap_remove(dev->io_traps[i].trap); + free(dev); } diff --git a/src/include/86box/acpi.h b/src/include/86box/acpi.h index 4637262f8..2cae4bb30 100644 --- a/src/include/86box/acpi.h +++ b/src/include/86box/acpi.h @@ -63,7 +63,8 @@ typedef struct smicmd, gpio_dir, gpio_val, muxcntrl, pad, timer32, smireg, - gpireg[3], gporeg[4]; + gpireg[3], gporeg[4], + extiotrapsts, extiotrapen; uint16_t pmsts, pmen, pmcntrl, gpsts, gpsts1, gpen, gpen1, gpscien, @@ -95,7 +96,8 @@ typedef struct pc_timer_t timer; nvr_t *nvr; apm_t *apm; - void *i2c; + void *i2c, + (*trap_update)(void *priv), *trap_priv; } acpi_t; @@ -111,6 +113,8 @@ extern const device_t acpi_via_596b_device; /* Functions */ +extern void acpi_update_irq(acpi_t *dev); +extern void acpi_raise_smi(acpi_t *dev); extern void acpi_update_io_mapping(acpi_t *dev, uint32_t base, int chipset_en); extern void acpi_update_aux_io_mapping(acpi_t *dev, uint32_t base, int chipset_en); extern void acpi_init_gporeg(acpi_t *dev, uint8_t val0, uint8_t val1, uint8_t val2, uint8_t val3); @@ -121,6 +125,7 @@ extern void acpi_set_irq_pin(acpi_t *dev, int irq_pin); extern void acpi_set_irq_line(acpi_t *dev, int irq_line); extern void acpi_set_gpireg2_default(acpi_t *dev, uint8_t gpireg2_default); extern void acpi_set_nvr(acpi_t *dev, nvr_t *nvr); +extern void acpi_set_trap_update(acpi_t *dev, void (*update)(void *priv), void *priv); #ifdef __cplusplus } diff --git a/src/include/86box/io.h b/src/include/86box/io.h index 6112ea8e4..c483819ce 100644 --- a/src/include/86box/io.h +++ b/src/include/86box/io.h @@ -111,5 +111,10 @@ extern void outw(uint16_t port, uint16_t val); extern uint32_t inl(uint16_t port); extern void outl(uint16_t port, uint32_t val); +extern void *io_trap_add(void (*func)(int size, uint16_t addr, uint8_t write, uint8_t val, void *priv), + void *priv); +extern void io_trap_remap(void *handle, int enable, uint16_t addr, uint16_t size); +extern void io_trap_remove(void *handle); + #endif /*EMU_IO_H*/ diff --git a/src/io.c b/src/io.c index 2751cc639..0d3d1bb91 100644 --- a/src/io.c +++ b/src/io.c @@ -35,19 +35,26 @@ typedef struct _io_ { - uint8_t (*inb)(uint16_t addr, void *priv); - uint16_t (*inw)(uint16_t addr, void *priv); - uint32_t (*inl)(uint16_t addr, void *priv); + uint8_t (*inb)(uint16_t addr, void *priv); + uint16_t (*inw)(uint16_t addr, void *priv); + uint32_t (*inl)(uint16_t addr, void *priv); - void (*outb)(uint16_t addr, uint8_t val, void *priv); - void (*outw)(uint16_t addr, uint16_t val, void *priv); - void (*outl)(uint16_t addr, uint32_t val, void *priv); + void (*outb)(uint16_t addr, uint8_t val, void *priv); + void (*outw)(uint16_t addr, uint16_t val, void *priv); + void (*outl)(uint16_t addr, uint32_t val, void *priv); - void *priv; + void *priv; - struct _io_ *prev, *next; + struct _io_ *prev, *next; } io_t; +typedef struct { + uint8_t enable; + uint16_t base, size; + void (*func)(int size, uint16_t addr, uint8_t write, uint8_t val, void *priv), + *priv; +} io_trap_t; + int initialized = 0; io_t *io[NPORTS], *io_last[NPORTS]; @@ -583,3 +590,116 @@ outl(uint16_t port, uint32_t val) return; } + + +static uint8_t +io_trap_readb(uint16_t addr, void *priv) +{ + io_trap_t *trap = (io_trap_t *) priv; + trap->func(1, addr, 0, 0, trap->priv); + return 0xff; +} + + +static uint16_t +io_trap_readw(uint16_t addr, void *priv) +{ + io_trap_t *trap = (io_trap_t *) priv; + trap->func(2, addr, 0, 0, trap->priv); + return 0xffff; +} + + +static uint32_t +io_trap_readl(uint16_t addr, void *priv) +{ + io_trap_t *trap = (io_trap_t *) priv; + trap->func(4, addr, 0, 0, trap->priv); + return 0xffffffff; +} + + +static void +io_trap_writeb(uint16_t addr, uint8_t val, void *priv) +{ + io_trap_t *trap = (io_trap_t *) priv; + trap->func(1, addr, 1, val, trap->priv); +} + + +static void +io_trap_writew(uint16_t addr, uint16_t val, void *priv) +{ + io_trap_t *trap = (io_trap_t *) priv; + trap->func(2, addr, 1, val, trap->priv); +} + + +static void +io_trap_writel(uint16_t addr, uint32_t val, void *priv) +{ + io_trap_t *trap = (io_trap_t *) priv; + trap->func(4, addr, 1, val, trap->priv); +} + + +void * +io_trap_add(void (*func)(int size, uint16_t addr, uint8_t write, uint8_t val, void *priv), + void *priv) +{ + /* Instantiate new I/O trap. */ + io_trap_t *trap = (io_trap_t *) malloc(sizeof(io_trap_t)); + trap->enable = 0; + trap->base = trap->size = 0; + trap->func = func; + trap->priv = priv; + + return trap; +} + + +void +io_trap_remap(void *handle, int enable, uint16_t addr, uint16_t size) +{ + io_trap_t *trap = (io_trap_t *) handle; + if (!trap) + return; + + io_log("I/O: Remapping trap from %04X-%04X (enable %d) to %04X-%04X (enable %d)\n", + trap->base, trap->base + trap->size, trap->enable, addr, addr + size, enable); + + /* Remove old I/O mapping. */ + if (trap->enable && trap->base && trap->size) { + io_removehandler(trap->base, trap->size, + io_trap_readb, io_trap_readw, io_trap_readl, + io_trap_writeb, io_trap_writew, io_trap_writel, + trap); + } + + /* Set trap enable flag, base address and size. */ + trap->enable = !!enable; + trap->base = addr; + trap->size = size; + + /* Add new I/O mapping. */ + if (trap->enable && trap->base && trap->size) { + io_sethandler(trap->base, trap->size, + io_trap_readb, io_trap_readw, io_trap_readl, + io_trap_writeb, io_trap_writew, io_trap_writel, + trap); + } +} + + +void +io_trap_remove(void *handle) +{ + io_trap_t *trap = (io_trap_t *) handle; + if (!trap) + return; + + /* Unmap I/O trap before freeing it. */ + io_trap_remap(trap, 0, 0, 0); + + free(trap); +} From 77244eda3e76d49ceaf23fb09213d517da426617 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Mon, 18 Oct 2021 22:29:23 -0300 Subject: [PATCH 36/56] ACPI: Make sleep mode pause emulation instead --- src/acpi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/acpi.c b/src/acpi.c index da7c48796..653b7896c 100644 --- a/src/acpi.c +++ b/src/acpi.c @@ -37,7 +37,6 @@ #include <86box/acpi.h> #include <86box/machine.h> #include <86box/i2c.h> -#include <86box/ui.h> int acpi_rtc_status = 0; @@ -674,8 +673,9 @@ acpi_reg_write_common_regs(int size, uint16_t addr, uint8_t val, void *p) if (sus_typ & SUS_RESET_CPU) resetx86(); - /* Resume immediately as a power button is not implemented yet. */ - ui_msgbox_ex(MBX_INFO, L"Sleep mode", L"Press OK to resume the emulated machine.", NULL, NULL, NULL); + /* Pause emulation and trigger a resume event immediately, + as the UI doesn't have a power button implemented yet. */ + plat_pause(1); dev->regs.pmsts |= 0x8000; } } From 8dd26d5776caebd6c1d65596bed9670ba4a649f2 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Mon, 18 Oct 2021 23:29:30 -0300 Subject: [PATCH 37/56] Fix some logging stuff --- src/chipset/intel_piix.c | 6 +++--- src/io.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/chipset/intel_piix.c b/src/chipset/intel_piix.c index d36109ffd..bff891bea 100644 --- a/src/chipset/intel_piix.c +++ b/src/chipset/intel_piix.c @@ -303,10 +303,10 @@ piix_trap_update_devctl(piix_t *dev, uint8_t trap_id, uint8_t dev_id, trap->en_mask = devctl_mask; } -//#ifdef ENABLE_PIIX_LOG +#ifdef ENABLE_PIIX_LOG if ((dev_id == 9) || (dev_id == 10) || (dev_id == 12) || (dev_id == 13)) - pclog("PIIX: Mapping trap device %d to %04X-%04X (enable %d)\n", dev_id, addr, addr + size - 1, enable); -//#endif + piix_log("PIIX: Mapping trap device %d to %04X-%04X (enable %d)\n", dev_id, addr, addr + size - 1, enable); +#endif /* Remap I/O trap. */ io_trap_remap(trap->trap, enable, addr, size); diff --git a/src/io.c b/src/io.c index 0d3d1bb91..d704a036a 100644 --- a/src/io.c +++ b/src/io.c @@ -666,7 +666,7 @@ io_trap_remap(void *handle, int enable, uint16_t addr, uint16_t size) return; io_log("I/O: Remapping trap from %04X-%04X (enable %d) to %04X-%04X (enable %d)\n", - trap->base, trap->base + trap->size, trap->enable, addr, addr + size, enable); + trap->base, trap->base + trap->size - 1, trap->enable, addr, addr + size - 1, enable); /* Remove old I/O mapping. */ if (trap->enable && trap->base && trap->size) { From 5ae6530ce526d0d75d667d4c6d1b9b84b3f98098 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Mon, 18 Oct 2021 23:29:51 -0300 Subject: [PATCH 38/56] ACPI: Fix resume issue with P2B-LS --- src/acpi.c | 21 ++++++++++++++++++--- src/include/86box/acpi.h | 2 +- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/acpi.c b/src/acpi.c index 653b7896c..9b0ba6734 100644 --- a/src/acpi.c +++ b/src/acpi.c @@ -673,10 +673,10 @@ acpi_reg_write_common_regs(int size, uint16_t addr, uint8_t val, void *p) if (sus_typ & SUS_RESET_CPU) resetx86(); - /* Pause emulation and trigger a resume event immediately, - as the UI doesn't have a power button implemented yet. */ + /* Since the UI doesn't have a power button at the moment, pause emulation, + then trigger a resume event so that the system resumes after unpausing. */ plat_pause(1); - dev->regs.pmsts |= 0x8000; + timer_set_delay_u64(&dev->resume_timer, 50 * TIMER_USEC); } } dev->regs.pmcntrl = ((dev->regs.pmcntrl & ~(0xff << shift16)) | (val << shift16)) & 0x3f07 /* 0x3c07 */; @@ -1491,6 +1491,20 @@ acpi_timer_count(void *priv) } +static void +acpi_timer_resume(void *priv) +{ + acpi_t *dev = (acpi_t *) priv; + + dev->regs.pmsts |= 0x8000; + + /* Nasty workaround for ASUS P2B-LS and potentially others, where the PMCNTRL + SMI trap handler clears the resume bit before returning control to the OS. */ + if (in_smm) + timer_set_delay_u64(&dev->resume_timer, 50 * TIMER_USEC); +} + + void acpi_init_gporeg(acpi_t *dev, uint8_t val0, uint8_t val1, uint8_t val2, uint8_t val3) { @@ -1735,6 +1749,7 @@ acpi_init(const device_t *info) timer_add(&dev->timer, acpi_timer_count, dev, 0); timer_set_delay_u64(&dev->timer, ACPICONST); + timer_add(&dev->resume_timer, acpi_timer_resume, dev, 0); acpi_reset(dev); diff --git a/src/include/86box/acpi.h b/src/include/86box/acpi.h index 2cae4bb30..91c63b74e 100644 --- a/src/include/86box/acpi.h +++ b/src/include/86box/acpi.h @@ -93,7 +93,7 @@ typedef struct int vendor, slot, irq_mode, irq_pin, irq_line; - pc_timer_t timer; + pc_timer_t timer, resume_timer; nvr_t *nvr; apm_t *apm; void *i2c, From 8e823f125c6a49f64778e4a53716449f162d2531 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Mon, 18 Oct 2021 23:56:40 -0300 Subject: [PATCH 39/56] PIIX: Remove I/O traps on close --- src/chipset/intel_piix.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/chipset/intel_piix.c b/src/chipset/intel_piix.c index bff891bea..aeed83ab5 100644 --- a/src/chipset/intel_piix.c +++ b/src/chipset/intel_piix.c @@ -1397,11 +1397,14 @@ piix_reset(void *p) static void -piix_close(void *p) +piix_close(void *priv) { - piix_t *piix = (piix_t *)p; + piix_t *dev = (piix_t *) priv; - free(piix); + for (int i = 0; i < (sizeof(dev->io_traps) / sizeof(dev->io_traps[0])); i++) + io_trap_remove(dev->io_traps[i].trap); + + free(dev); } From 04bea78e5edf10009265d173eced91866d8b9a0e Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Tue, 19 Oct 2021 00:17:27 -0300 Subject: [PATCH 40/56] PIIX: Implement drive separation on IDE I/O traps --- src/chipset/intel_piix.c | 117 ++++++++++++++++++++++----------------- src/chipset/via_pipc.c | 2 +- 2 files changed, 66 insertions(+), 53 deletions(-) diff --git a/src/chipset/intel_piix.c b/src/chipset/intel_piix.c index aeed83ab5..095dd5d36 100644 --- a/src/chipset/intel_piix.c +++ b/src/chipset/intel_piix.c @@ -41,6 +41,8 @@ #include <86box/pic.h> #include <86box/pit.h> #include <86box/port_92.h> +#include <86box/scsi_device.h> +#include <86box/hdc.h> #include <86box/hdc_ide.h> #include <86box/hdc_ide_sff8038i.h> #include <86box/usb.h> @@ -284,6 +286,17 @@ piix_trap_io(int size, uint16_t addr, uint8_t write, uint8_t val, void *priv) } +static void +piix_trap_io_ide(int size, uint16_t addr, uint8_t write, uint8_t val, void *priv) +{ + piix_io_trap_t *trap = (piix_io_trap_t *) priv; + + /* IDE traps are per drive, not per channel. */ + if (ide_drives[trap->dev_id]->selected) + piix_trap_io(size, addr, write, val, priv); +} + + static void piix_trap_update_devctl(piix_t *dev, uint8_t trap_id, uint8_t dev_id, uint32_t devctl_mask, uint8_t enable, @@ -294,8 +307,8 @@ piix_trap_update_devctl(piix_t *dev, uint8_t trap_id, uint8_t dev_id, /* Set up Device I/O traps dynamically. */ if (enable && !trap->trap) { - trap->dev = dev; - trap->trap = io_trap_add(piix_trap_io, trap); + trap->dev = dev; + trap->trap = io_trap_add((dev_id <= 3) ? piix_trap_io_ide : piix_trap_io, trap); trap->dev_id = dev_id; trap->sts_reg = &dev->acpi->regs.devsts; trap->sts_mask = 0x00010000 << dev_id; @@ -348,49 +361,49 @@ piix_trap_update(void *priv) piix_trap_update_devctl(dev, trap_id++, 5, 0x00000800, fregs[0x51] & 0x10, 0x377 + (0x80 * !(fregs[0x63] & 0x10)), 1); switch (fregs[0x67] & 0x07) { - case 0x00: temp = 0x3f8; break; - case 0x01: temp = 0x2f8; break; - case 0x02: temp = 0x220; break; - case 0x03: temp = 0x228; break; - case 0x04: temp = 0x238; break; - case 0x05: temp = 0x2e8; break; - case 0x06: temp = 0x338; break; - default: temp = 0x3e8; break; + case 0x00: temp = 0x3f8; break; + case 0x01: temp = 0x2f8; break; + case 0x02: temp = 0x220; break; + case 0x03: temp = 0x228; break; + case 0x04: temp = 0x238; break; + case 0x05: temp = 0x2e8; break; + case 0x06: temp = 0x338; break; + default: temp = 0x3e8; break; } piix_trap_update_devctl(dev, trap_id++, 6, 0x00002000, fregs[0x51] & 0x40, temp, 8); switch (fregs[0x67] & 0x70) { - case 0x00: temp = 0x3f8; break; - case 0x10: temp = 0x2f8; break; - case 0x20: temp = 0x220; break; - case 0x30: temp = 0x228; break; - case 0x40: temp = 0x238; break; - case 0x50: temp = 0x2e8; break; - case 0x60: temp = 0x338; break; - default: temp = 0x3e8; break; + case 0x00: temp = 0x3f8; break; + case 0x10: temp = 0x2f8; break; + case 0x20: temp = 0x220; break; + case 0x30: temp = 0x228; break; + case 0x40: temp = 0x238; break; + case 0x50: temp = 0x2e8; break; + case 0x60: temp = 0x338; break; + default: temp = 0x3e8; break; } piix_trap_update_devctl(dev, trap_id++, 7, 0x00008000, fregs[0x52] & 0x01, temp, 8); switch (fregs[0x63] & 0x06) { - case 0x00: - piix_trap_update_devctl(dev, trap_id++, 8, 0x00020000, fregs[0x52] & 0x04, 0x3bc, 4); - piix_trap_update_devctl(dev, trap_id++, 8, 0x00020000, fregs[0x52] & 0x04, 0x7bc, 3); - break; + case 0x00: + piix_trap_update_devctl(dev, trap_id++, 8, 0x00020000, fregs[0x52] & 0x04, 0x3bc, 4); + piix_trap_update_devctl(dev, trap_id++, 8, 0x00020000, fregs[0x52] & 0x04, 0x7bc, 3); + break; - case 0x02: - piix_trap_update_devctl(dev, trap_id++, 8, 0x00020000, fregs[0x52] & 0x04, 0x378, 8); - piix_trap_update_devctl(dev, trap_id++, 8, 0x00020000, fregs[0x52] & 0x04, 0x778, 3); - break; + case 0x02: + piix_trap_update_devctl(dev, trap_id++, 8, 0x00020000, fregs[0x52] & 0x04, 0x378, 8); + piix_trap_update_devctl(dev, trap_id++, 8, 0x00020000, fregs[0x52] & 0x04, 0x778, 3); + break; - case 0x04: - piix_trap_update_devctl(dev, trap_id++, 8, 0x00020000, fregs[0x52] & 0x04, 0x278, 8); - piix_trap_update_devctl(dev, trap_id++, 8, 0x00020000, fregs[0x52] & 0x04, 0x678, 3); - break; + case 0x04: + piix_trap_update_devctl(dev, trap_id++, 8, 0x00020000, fregs[0x52] & 0x04, 0x278, 8); + piix_trap_update_devctl(dev, trap_id++, 8, 0x00020000, fregs[0x52] & 0x04, 0x678, 3); + break; - default: - piix_trap_update_devctl(dev, trap_id++, 8, 0x00020000, fregs[0x52] & 0x04, 0, 0); - piix_trap_update_devctl(dev, trap_id++, 8, 0x00020000, fregs[0x52] & 0x04, 0, 0); - break; + default: + piix_trap_update_devctl(dev, trap_id++, 8, 0x00020000, fregs[0x52] & 0x04, 0, 0); + piix_trap_update_devctl(dev, trap_id++, 8, 0x00020000, fregs[0x52] & 0x04, 0, 0); + break; } temp = fregs[0x62] & 0x0f; @@ -1059,7 +1072,7 @@ piix_read(int func, int addr, void *priv) /* Return on unsupported function. */ if ((func <= dev->max_func) || ((func == 1) && (dev->max_func == 0))) { - fregs = (uint8_t *) dev->regs[func]; + fregs = (uint8_t *) dev->regs[func]; ret = fregs[addr]; if ((func == 0) && (addr == 0x4e)) ret |= keyboard_at_get_mouse_scan(); @@ -1147,21 +1160,21 @@ piix_reset_hard(piix_t *dev) /* Clear all 4 functions' arrays and set their vendor and device ID's. */ for (i = 0; i < 4; i++) { - memset(dev->regs[i], 0, 256); - if (dev->type == 5) { - dev->regs[i][0x00] = 0x55; dev->regs[i][0x01] = 0x10; /* SMSC/EFAR */ - if (i == 1) { /* IDE controller is 9130, breaking convention */ - dev->regs[i][0x02] = 0x30; - dev->regs[i][0x03] = 0x91; - } else { - dev->regs[i][0x02] = (dev->func0_id & 0xff) + (i << dev->func_shift); - dev->regs[i][0x03] = (dev->func0_id >> 8); - } - } else { - dev->regs[i][0x00] = 0x86; dev->regs[i][0x01] = 0x80; /* Intel */ - dev->regs[i][0x02] = (dev->func0_id & 0xff) + (i << dev->func_shift); - dev->regs[i][0x03] = (dev->func0_id >> 8); - } + memset(dev->regs[i], 0, 256); + if (dev->type == 5) { + dev->regs[i][0x00] = 0x55; dev->regs[i][0x01] = 0x10; /* SMSC/EFAR */ + if (i == 1) { /* IDE controller is 9130, breaking convention */ + dev->regs[i][0x02] = 0x30; + dev->regs[i][0x03] = 0x91; + } else { + dev->regs[i][0x02] = (dev->func0_id & 0xff) + (i << dev->func_shift); + dev->regs[i][0x03] = (dev->func0_id >> 8); + } + } else { + dev->regs[i][0x00] = 0x86; dev->regs[i][0x01] = 0x80; /* Intel */ + dev->regs[i][0x02] = (dev->func0_id & 0xff) + (i << dev->func_shift); + dev->regs[i][0x03] = (dev->func0_id >> 8); + } } /* Function 0: PCI to ISA Bridge */ @@ -1172,7 +1185,7 @@ piix_reset_hard(piix_t *dev) if (dev->type == 4) fregs[0x08] = (dev->rev & 0x08) ? 0x02 : (dev->rev & 0x07); else - fregs[0x08] = dev->rev; + fregs[0x08] = dev->rev; fregs[0x09] = 0x00; fregs[0x0a] = 0x01; fregs[0x0b] = 0x06; fregs[0x0e] = ((dev->type > 1) || (dev->rev != 2)) ? 0x80 : 0x00; @@ -1476,7 +1489,7 @@ static void /* On PIIX4, PIIX4E, and SMSC, APM is added by the ACPI device. */ if (dev->type < 4) { - dev->apm = device_add(&apm_pci_device); + dev->apm = device_add(&apm_pci_device); /* APM intercept handler to update PIIX/PIIX3 and PIIX4/4E/SMSC ACPI SMI status on APM SMI. */ io_sethandler(0x00b2, 0x0001, NULL, NULL, NULL, piix_apm_out, NULL, NULL, dev); } diff --git a/src/chipset/via_pipc.c b/src/chipset/via_pipc.c index 863388e2d..df3b26a8e 100644 --- a/src/chipset/via_pipc.c +++ b/src/chipset/via_pipc.c @@ -591,7 +591,7 @@ pipc_trap_update_paden(pipc_t *dev, uint8_t trap_id, /* Set up Primary Activity Detect I/O traps dynamically. */ if (enable && !trap->trap) { - trap->dev = dev; + trap->dev = dev; trap->trap = io_trap_add(pipc_trap_io_pact, trap); trap->sts_reg = &dev->acpi->regs.padsts; trap->en_reg = &dev->acpi->regs.paden; From 6fc0890f233d546eba310c8c77ab450ba1001f0a Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Tue, 19 Oct 2021 12:22:13 -0300 Subject: [PATCH 41/56] Remove underscore from Multitech PC500 --- src/include/86box/machine.h | 2 +- src/machine/m_xt.c | 4 ++-- src/machine/machine_table.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/include/86box/machine.h b/src/include/86box/machine.h index 7dc06936e..50cedccca 100644 --- a/src/include/86box/machine.h +++ b/src/include/86box/machine.h @@ -647,7 +647,7 @@ extern int machine_xt_pc4i_init(const machine_t *); extern int machine_xt_mpc1600_init(const machine_t *); extern int machine_xt_pcspirit_init(const machine_t *); extern int machine_xt_pc700_init(const machine_t *); -extern int machine_xt_multitechpc500_init(const machine_t *); +extern int machine_xt_pc500_init(const machine_t *); extern int machine_xt_iskra3104_init(const machine_t *); diff --git a/src/machine/m_xt.c b/src/machine/m_xt.c index ce251eda7..6e68c81bf 100644 --- a/src/machine/m_xt.c +++ b/src/machine/m_xt.c @@ -427,11 +427,11 @@ machine_xt_pc700_init(const machine_t *model) int -machine_xt_multitechpc500_init(const machine_t* model) +machine_xt_pc500_init(const machine_t* model) { int ret; - ret = bios_load_linear("roms/machines/multitech_pc500/rom404.bin", + ret = bios_load_linear("roms/machines/pc500/rom404.bin", 0x000f8000, 32768, 0); if (bios_only || !ret) diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index b1e6dfe92..2d695c567 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -73,7 +73,7 @@ const machine_t machines[] = { { "[8088] Eagle PC Spirit", "pcspirit", MACHINE_TYPE_8088, CPU_PKG_8088, 0, 0, 0, 0, 0, 0, 0, MACHINE_PC, 128, 640, 64, 0, machine_xt_pcspirit_init, NULL }, { "[8088] Generic XT clone", "genxt", MACHINE_TYPE_8088, CPU_PKG_8088, 0, 0, 0, 0, 0, 0, 0, MACHINE_PC, 64, 640, 64, 0, machine_genxt_init, NULL }, { "[8088] Juko ST", "jukopc", MACHINE_TYPE_8088, CPU_PKG_8088, 0, 0, 0, 0, 0, 0, 0, MACHINE_PC, 64, 640, 64, 0, machine_xt_jukopc_init, NULL }, - { "[8088] Multitech PC-500", "multitech_pc500", MACHINE_TYPE_8088, CPU_PKG_8088, 0, 0, 0, 0, 0, 0, 0, MACHINE_PC, 128, 640, 64, 0, machine_xt_multitechpc500_init, NULL }, + { "[8088] Multitech PC-500", "pc500", MACHINE_TYPE_8088, CPU_PKG_8088, 0, 0, 0, 0, 0, 0, 0, MACHINE_PC, 128, 640, 64, 0, machine_xt_pc500_init, NULL }, { "[8088] Multitech PC-700", "pc700", MACHINE_TYPE_8088, CPU_PKG_8088, 0, 0, 0, 0, 0, 0, 0, MACHINE_PC, 128, 640, 64, 0, machine_xt_pc700_init, NULL }, { "[8088] NCR PC4i", "pc4i", MACHINE_TYPE_8088, CPU_PKG_8088, 0, 0, 0, 0, 0, 0, 0, MACHINE_PC, 256, 640, 256, 0, machine_xt_pc4i_init, NULL }, { "[8088] Olivetti M19", "m19", MACHINE_TYPE_8088, CPU_PKG_8088, 0, 4772728, 7159092, 0, 0, 0, 0, MACHINE_PC | MACHINE_VIDEO_FIXED, 256, 640, 256, 0, machine_xt_m19_init, m19_get_device }, From 9f0d240c7aac3dea2103ba643d07e9b02a47213e Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Tue, 19 Oct 2021 12:29:06 -0300 Subject: [PATCH 42/56] Port ES1371 and ICS fixes from VFIO branch --- src/device/clock_ics9xxx.c | 10 +++++----- src/include/86box/clock.h | 6 ------ src/machine/m_at_slot2.c | 5 ++++- src/sound/snd_audiopci.c | 5 +++++ 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/device/clock_ics9xxx.c b/src/device/clock_ics9xxx.c index df7aaf5fa..9f7e790af 100644 --- a/src/device/clock_ics9xxx.c +++ b/src/device/clock_ics9xxx.c @@ -124,7 +124,7 @@ static const ics9xxx_model_t ics9xxx_models[] = { .hw_select = {0, 3}, .frequencies_ref = ICS9250_08 ICS9xxx_MODEL_END() -#if 0 +#ifdef ENABLE_ICS9xxx_DETECT ICS9xxx_MODEL(ICS9248_81) .max_reg = 5, .regs = {0x82, 0xfe, 0x7f, 0xff, 0xff, 0xb7}, @@ -522,7 +522,7 @@ static const ics9xxx_model_t ics9xxx_models[] = { ICS9xxx_MODEL(ICS9250_08) .max_reg = 5, .regs = {0x00, 0xff, 0xff, 0xff, 0x6d, 0xbf}, - .fs_regs = {{0, 4, 4, 7}, {0, 5, 4, 4}, {0, 6, 5, 6}, {0, 7, 4, 1}, {-1, -1, -1, -1}}, + .fs_regs = {{0, 4, 4, 7}, {0, 5, 4, 4}, {0, 6, 5, 6}, {0, 2, 4, 1}, {-1, -1, -1, -1}}, .hw_select = {0, 3}, .frequencies = (const ics9xxx_frequency_t[]) { {.bus = 12400, .pci_div = 3}, @@ -544,7 +544,7 @@ static const ics9xxx_model_t ics9xxx_models[] = { {0} } ICS9xxx_MODEL_END() -#if 0 +#ifdef ENABLE_ICS9xxx_DETECT ICS9xxx_MODEL(ICS9250_10) .max_reg = 5, .regs = {0x1f, 0xff, 0xfe, 0x00, 0x00, 0x06}, @@ -703,7 +703,7 @@ static const ics9xxx_model_t ics9xxx_models[] = { {0} } ICS9xxx_MODEL_END() -#if 0 +#ifdef ENABLE_ICS9xxx_DETECT ICS9xxx_MODEL(ICS9250_19) .max_reg = 5, .regs = {0x02, 0xff, 0xff, 0xff, 0x6d, 0xbf}, @@ -1199,7 +1199,7 @@ ics9xxx_init(const device_t *info) uint8_t i; #ifdef ENABLE_ICS9xxx_DETECT - for (i = 0; i < ICS9xxx_MAX; i++) { + for (i = ICS9xxx_xx + 1; i < ICS9xxx_MAX; i++) { if (ics9xxx_models[i].frequencies_ref || !ics9xxx_models[i].name) continue; for (uint8_t j = 0; j < i; j++) { diff --git a/src/include/86box/clock.h b/src/include/86box/clock.h index b1a3df478..7d2be9f05 100644 --- a/src/include/86box/clock.h +++ b/src/include/86box/clock.h @@ -22,7 +22,6 @@ enum { ICS9xxx_xx, ICS9150_08, ICS9248_39, -#if 0 ICS9248_81, ICS9248_95, ICS9248_98, @@ -35,16 +34,12 @@ enum { ICS9248_143, ICS9248_151, ICS9248_192, -#endif ICS9250_08, -#if 0 ICS9250_10, ICS9250_13, ICS9250_14, ICS9250_16, -#endif ICS9250_18, -#if 0 ICS9250_19, ICS9250_23, ICS9250_25, @@ -56,7 +51,6 @@ enum { ICS9250_32, ICS9250_38, ICS9250_50, -#endif ICS9xxx_MAX }; diff --git a/src/machine/m_at_slot2.c b/src/machine/m_at_slot2.c index bcce1d8c0..84c789990 100644 --- a/src/machine/m_at_slot2.c +++ b/src/machine/m_at_slot2.c @@ -9,7 +9,8 @@ * Implementation of Slot 2 machines. * * Slot 2 is quite a rare type of Slot. Used mostly by Pentium II & III Xeons - * These boards were also capable to take Slot 1 CPU's using Slot 2 to 1 adapters. + * + * * * Authors: Miran Grca, * @@ -35,6 +36,7 @@ #include <86box/hwm.h> #include <86box/spd.h> #include <86box/video.h> +#include <86box/clock.h> #include "cpu.h" #include <86box/machine.h> @@ -140,6 +142,7 @@ machine_at_fw6400gx_init(const machine_t *model) device_add(&piix4e_device); device_add(&keyboard_ps2_ami_pci_device); device_add(&pc87309_15c_device); + device_add(ics9xxx_get(ICS9250_08)); device_add(&sst_flash_29ee020_device); spd_register(SPD_TYPE_SDRAM, 0xF, 512); device_add(&w83781d_device); /* fans: Chassis, Power, CPU; temperatures: System, CPU, unused */ diff --git a/src/sound/snd_audiopci.c b/src/sound/snd_audiopci.c index b081d7632..4956f7742 100644 --- a/src/sound/snd_audiopci.c +++ b/src/sound/snd_audiopci.c @@ -145,6 +145,8 @@ typedef struct { #define INT_DAC2_EN (1<<5) #define INT_UART_EN (1<<3) +#define SI_P2_PAUSE (1<<12) +#define SI_P1_PAUSE (1<<11) #define SI_P2_INTR_EN (1<<9) #define SI_P1_INTR_EN (1<<8) @@ -1507,6 +1509,9 @@ es1371_pci_write(int func, int addr, uint8_t val, void *p) static void es1371_fetch(es1371_t *dev, int dac_nr) { + if (dev->si_cr & (dac_nr ? SI_P2_PAUSE : SI_P1_PAUSE)) + return; + int format = dac_nr ? ((dev->si_cr >> 2) & 3) : (dev->si_cr & 3); int pos = dev->dac[dac_nr].buffer_pos & 63; int c; From e8d9b51680481ceeccc5d0d62659fd702d15cdab Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Tue, 19 Oct 2021 12:40:08 -0300 Subject: [PATCH 43/56] Remove SiS 5598 from include as well --- src/include/86box/chipset.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/include/86box/chipset.h b/src/include/86box/chipset.h index 0c0e36299..52c841cd0 100644 --- a/src/include/86box/chipset.h +++ b/src/include/86box/chipset.h @@ -117,7 +117,6 @@ extern const device_t sis_85c496_ls486e_device; extern const device_t sis_85c50x_device; extern const device_t sis_5511_device; extern const device_t sis_5571_device; -extern const device_t sis_5598_device; /* ST */ extern const device_t stpc_client_device; From f1a74ac36d8bc78f2ee725eabab7d36d4d20d117 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Tue, 19 Oct 2021 23:44:47 +0600 Subject: [PATCH 44/56] Fix plat_mmap on macOS platform --- src/unix/unix.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/unix/unix.c b/src/unix/unix.c index d0c3dfa73..ef3491c05 100644 --- a/src/unix/unix.c +++ b/src/unix/unix.c @@ -369,7 +369,11 @@ plat_dir_create(char *path) void * plat_mmap(size_t size, uint8_t executable) { +#if defined __APPLE__ && defined MAP_JIT + void *ret = mmap(0, size, PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0), MAP_ANON | MAP_PRIVATE | (executable ? MAP_JIT : 0), 0, 0); +#else void *ret = mmap(0, size, PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0), MAP_ANON | MAP_PRIVATE, 0, 0); +#endif return (ret < 0) ? NULL : ret; } From 8ae76614df6d78eb893b8e9e136e27fcedd0fdb7 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Wed, 20 Oct 2021 00:37:23 +0600 Subject: [PATCH 45/56] Fix compile error --- src/unix/unix.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/unix/unix.c b/src/unix/unix.c index ef3491c05..286b937bd 100644 --- a/src/unix/unix.c +++ b/src/unix/unix.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include From f1fd28897f8a5c8b2f803a744d68c6341793e1e8 Mon Sep 17 00:00:00 2001 From: Ompronce <88358700+Ompronce@users.noreply.github.com> Date: Wed, 20 Oct 2021 19:09:37 -0400 Subject: [PATCH 46/56] Added gameport at port 201h to Innovation SSI-2001 sound card --- src/sound/snd_ssi2001.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sound/snd_ssi2001.c b/src/sound/snd_ssi2001.c index bd4e70ea9..55740fac9 100644 --- a/src/sound/snd_ssi2001.c +++ b/src/sound/snd_ssi2001.c @@ -8,6 +8,7 @@ #include <86box/86box.h> #include <86box/io.h> #include <86box/device.h> +#include <86box/gameport.h> #include <86box/sound.h> #include <86box/snd_resid.h> @@ -17,6 +18,7 @@ typedef struct ssi2001_t void *psid; int16_t buffer[SOUNDBUFLEN * 2]; int pos; + int gameport_enabled; } ssi2001_t; static void ssi2001_update(ssi2001_t *ssi2001) @@ -66,7 +68,10 @@ void *ssi2001_init(const device_t *info) ssi2001->psid = sid_init(); sid_reset(ssi2001->psid); uint16_t addr = device_get_config_hex16("base"); + ssi2001->gameport_enabled = device_get_config_int("gameport"); io_sethandler(addr, 0x0020, ssi2001_read, NULL, NULL, ssi2001_write, NULL, NULL, ssi2001); + if (ssi2001->gameport_enabled) + gameport_remap(gameport_add(&gameport_201_device), 0x201); sound_add_handler(ssi2001_get_buffer, ssi2001); return ssi2001; } @@ -102,6 +107,9 @@ static const device_config_t ssi2001_config[] = } } }, + { + "gameport", "Enable Game port", CONFIG_BINARY, "", 1 + }, { "", "", -1 } From 85f810aa22a806114946671aee6fdae3a6724a8f Mon Sep 17 00:00:00 2001 From: OBattler Date: Thu, 21 Oct 2021 21:18:20 +0200 Subject: [PATCH 47/56] The Virtual PC 2007 machine now correctly uses the version of 440BX without AGP. --- src/chipset/intel_4x0.c | 15 ++++++++++++++- src/include/86box/chipset.h | 1 + src/machine/m_at_misc.c | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/chipset/intel_4x0.c b/src/chipset/intel_4x0.c index 0b157f4dd..364030a28 100644 --- a/src/chipset/intel_4x0.c +++ b/src/chipset/intel_4x0.c @@ -1779,7 +1779,6 @@ const device_t i440ex_device = NULL }; - const device_t i440bx_device = { "Intel 82443BX", @@ -1794,6 +1793,20 @@ const device_t i440bx_device = NULL }; +const device_t i440bx_no_agp_device = +{ + "Intel 82443BX", + DEVICE_PCI, + 0x8200 | INTEL_440BX, + i4x0_init, + i4x0_close, + i4x0_reset, + { NULL }, + NULL, + NULL, + NULL +}; + const device_t i440gx_device = { "Intel 82443GX", diff --git a/src/include/86box/chipset.h b/src/include/86box/chipset.h index 52c841cd0..3ea2e52f8 100644 --- a/src/include/86box/chipset.h +++ b/src/include/86box/chipset.h @@ -75,6 +75,7 @@ extern const device_t i440fx_device; extern const device_t i440lx_device; extern const device_t i440ex_device; extern const device_t i440bx_device; +extern const device_t i440bx_no_agp_device; extern const device_t i440gx_device; extern const device_t i440zx_device; diff --git a/src/machine/m_at_misc.c b/src/machine/m_at_misc.c index aff270d95..f96523bf3 100644 --- a/src/machine/m_at_misc.c +++ b/src/machine/m_at_misc.c @@ -63,7 +63,7 @@ machine_at_vpc2007_init(const machine_t *model) pci_register_slot(0x0D, PCI_CARD_NORMAL, 1, 2, 3, 4); pci_register_slot(0x0E, PCI_CARD_NORMAL, 1, 2, 3, 4); pci_register_slot(0x0F, PCI_CARD_NORMAL, 1, 2, 3, 4); - device_add(&i440bx_device); + device_add(&i440bx_no_agp_device); device_add(&piix4e_device); device_add(&w83977f_370_device); device_add(&keyboard_ps2_ami_pci_device); From 066deef986706718c3b70dc323fc38f7a6790c74 Mon Sep 17 00:00:00 2001 From: OBattler Date: Thu, 21 Oct 2021 21:20:00 +0200 Subject: [PATCH 48/56] The version of 440BX without AGP now has revision 0x03. --- src/chipset/intel_4x0.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chipset/intel_4x0.c b/src/chipset/intel_4x0.c index 364030a28..83eabb5e8 100644 --- a/src/chipset/intel_4x0.c +++ b/src/chipset/intel_4x0.c @@ -1507,7 +1507,7 @@ static void regs[0x02] = (regs[0x7a] & 0x02) ? 0x92 : 0x90; regs[0x03] = 0x71; /* 82443BX */ regs[0x06] = (regs[0x7a] & 0x02) ? 0x00 : 0x10; - regs[0x08] = 0x02; + regs[0x08] = (regs[0x7a] & 0x02) ? 0x03 : 0x02; regs[0x10] = 0x08; regs[0x34] = (regs[0x7a] & 0x02) ? 0x00 : 0xa0; if (cpu_busspeed <= 66666667) From 5b689e4af7bf854bc8c4b33d53d9a67384e81bca Mon Sep 17 00:00:00 2001 From: OBattler Date: Thu, 21 Oct 2021 22:26:27 +0200 Subject: [PATCH 49/56] The Virtual PC 2007 machine no longer has the AGP flag. --- src/machine/machine_table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index 2d695c567..9dd5db1fa 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -462,7 +462,7 @@ const machine_t machines[] = { { "[VIA Apollo Pro133A] Acorp 6VIA90AP", "6via90ap", MACHINE_TYPE_SOCKET370, CPU_PKG_SOCKET370, 0, 66666667, 150000000, 1300, 3500, MACHINE_MULTIPLIER_FIXED, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL | MACHINE_GAMEPORT, 16384,3145728, 8192, 255, machine_at_6via90ap_init, NULL }, /* Miscellaneous/Fake/Hypervisor machines */ - { "[i440BX] Microsoft Virtual PC 2007", "vpc2007", MACHINE_TYPE_MISC, CPU_PKG_SLOT1, CPU_BLOCK(CPU_PENTIUM2, CPU_CYRIX3S), 0, 0, 0, 0, 0, 0, MACHINE_AGP | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192,1048576, 8192, 255, machine_at_vpc2007_init, NULL }, + { "[i440BX] Microsoft Virtual PC 2007", "vpc2007", MACHINE_TYPE_MISC, CPU_PKG_SLOT1, CPU_BLOCK(CPU_PENTIUM2, CPU_CYRIX3S), 0, 0, 0, 0, 0, 0, MACHINE_PCI | MACHINE_BUS_PS2 | MACHINE_IDE_DUAL, 8192,1048576, 8192, 255, machine_at_vpc2007_init, NULL }, { NULL, NULL, MACHINE_TYPE_NONE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL } }; From 83ad3e9f704f5b978656bde59ef71c6ae7fac76d Mon Sep 17 00:00:00 2001 From: OBattler Date: Fri, 22 Oct 2021 21:48:15 +0200 Subject: [PATCH 50/56] Moved the IDE slot to the top on the P64RP4 to ensure the CMD640 is added to it. --- src/machine/m_at_socket8.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/machine/m_at_socket8.c b/src/machine/m_at_socket8.c index 1aa892415..318e8dfea 100644 --- a/src/machine/m_at_socket8.c +++ b/src/machine/m_at_socket8.c @@ -55,11 +55,11 @@ machine_at_p6rp4_init(const machine_t *model) pci_register_slot(0x19, PCI_CARD_NORTHBRIDGE, 0, 0, 0, 0); pci_register_slot(0x12, PCI_CARD_NORTHBRIDGE, 0, 0, 0, 0); pci_register_slot(0x02, PCI_CARD_SOUTHBRIDGE, 0, 0, 0, 0); + pci_register_slot(0x08, PCI_CARD_IDE, 0, 0, 0, 0); pci_register_slot(0x07, PCI_CARD_NORMAL, 1, 2, 3, 4); pci_register_slot(0x06, PCI_CARD_NORMAL, 2, 3, 4, 1); pci_register_slot(0x05, PCI_CARD_NORMAL, 3, 4, 1, 2); pci_register_slot(0x04, PCI_CARD_NORMAL, 4, 1, 2, 3); - pci_register_slot(0x08, PCI_CARD_IDE, 0, 0, 0, 0); device_add(&i450kx_device); device_add(&sio_zb_device); device_add(&keyboard_ps2_ami_pci_device); From d61e16d91311131496a30545f39ad193ebb68427 Mon Sep 17 00:00:00 2001 From: Ompronce <88358700+Ompronce@users.noreply.github.com> Date: Fri, 22 Oct 2021 21:48:20 -0400 Subject: [PATCH 51/56] Added correct IRQs and port ranges for standalone ISA MPU-401 as per Roland documentation --- src/sound/snd_mpu401.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/sound/snd_mpu401.c b/src/sound/snd_mpu401.c index b407ceea9..95cdb1a52 100644 --- a/src/sound/snd_mpu401.c +++ b/src/sound/snd_mpu401.c @@ -1805,22 +1805,43 @@ static const device_config_t mpu401_standalone_config[] = { "base", "MPU-401 Address", CONFIG_HEX16, "", 0x330, "", { 0 }, { + { + "0x220", 0x220 + }, + { + "0x230", 0x230 + }, + { + "0x240", 0x240 + }, + { + "0x250", 0x250 + }, { "0x300", 0x300 }, + { + "0x320", 0x320 + }, { "0x330", 0x330 }, + { + "0x340", 0x340 + }, + { + "0x350", 0x350 + }, { "" } } }, { - "irq", "MPU-401 IRQ", CONFIG_SELECTION, "", 9, "", { 0 }, + "irq", "MPU-401 IRQ", CONFIG_SELECTION, "", 2, "", { 0 }, { { - "IRQ 9", 9 + "IRQ 2", 2 }, { "IRQ 3", 3 @@ -1832,10 +1853,10 @@ static const device_config_t mpu401_standalone_config[] = "IRQ 5", 5 }, { - "IRQ 7", 7 + "IRQ 6", 6 }, { - "IRQ 10", 10 + "IRQ 7", 7 }, { "" From 61273f5c1ff7460b0f12b036053562fb379461a5 Mon Sep 17 00:00:00 2001 From: TC1995 Date: Sun, 24 Oct 2021 19:06:05 +0200 Subject: [PATCH 52/56] Video changes: Improved the banking of the ATI 28800-5 cards (VGA Charger and VGA Wonder XL). Improved the skew and horizontal display of some of the ET4000W32P cards as well as the cursor. Made the Oak OTI 077 and PVGA WD90c30 cards use the Sierra 11487 (actually a clone is used in the real cards). For the WD90c30, changed the way the hack is involved. Reverted some changes of the S3 Vision/Trio that originally made glitches, now the glitches are gone and the accelerator renders fine. Re-organized the Sierra 1148x RAMDAC's and added the 11486 (Mark 1). MCA SVGA cards use the full 32-bit mapping. --- src/include/86box/vid_svga.h | 2 +- src/video/vid_ati28800.c | 205 +++++++++++-------- src/video/vid_et4000w32.c | 134 +++++++----- src/video/vid_oak_oti.c | 25 +++ src/video/vid_paradise.c | 45 +++- src/video/vid_s3.c | 363 +++++++++++++++++++-------------- src/video/vid_sc1148x_ramdac.c | 84 +++++--- src/video/vid_svga.c | 2 +- src/video/vid_table.c | 8 +- 9 files changed, 551 insertions(+), 317 deletions(-) diff --git a/src/include/86box/vid_svga.h b/src/include/86box/vid_svga.h index 90282a578..30d30254c 100644 --- a/src/include/86box/vid_svga.h +++ b/src/include/86box/vid_svga.h @@ -290,7 +290,7 @@ extern const device_t icd2061_device; extern const device_t ics9161_device; extern const device_t sc11483_ramdac_device; extern const device_t sc11487_ramdac_device; -extern const device_t sc11484_ramdac_device; +extern const device_t sc11486_ramdac_device; extern const device_t sc11484_nors2_ramdac_device; extern const device_t sc1502x_ramdac_device; extern const device_t sdac_ramdac_device; diff --git a/src/video/vid_ati28800.c b/src/video/vid_ati28800.c index d6630ccf8..565418e84 100644 --- a/src/video/vid_ati28800.c +++ b/src/video/vid_ati28800.c @@ -59,6 +59,7 @@ #endif #define BIOS_ROM_PATH "roms/video/ati28800/bios.bin" +#define BIOS_VGAXL_ROM_PATH "roms/video/ati28800/ATI_VGAWonder_XL.bin" typedef struct ati28800_t @@ -82,6 +83,8 @@ typedef struct ati28800_t int get_korean_font_index; uint16_t get_korean_font_base; int ksc5601_mode_enabled; + + int type, type_korean; } ati28800_t; @@ -111,7 +114,6 @@ ati28800_log(const char *fmt, ...) static void ati28800_recalctimings(svga_t *svga); - static void ati28800_out(uint16_t addr, uint8_t val, void *p) { @@ -134,37 +136,36 @@ ati28800_out(uint16_t addr, uint8_t val, void *p) ati28800_log("ATI 28800 write reg=0x%02X, val=0x%02X\n", ati28800->index, val); switch (ati28800->index) { case 0xa3: - ati28800->regs[0xa3] = val & 0x1f; - svga_recalctimings(svga); + if ((old ^ val) & 0x10) + svga_recalctimings(svga); break; - case 0xa6: - ati28800->regs[0xa6] = val & 0xc9; - break; - case 0xab: - ati28800->regs[0xab] = val & 0xdf; + case 0xa7: + if ((old ^ val) & 0x80) + svga_recalctimings(svga); break; case 0xb0: - ati28800->regs[0xb0] = val & 0x7d; - svga_recalctimings(svga); - break; - case 0xb1: - ati28800->regs[0xb0] = val & 0x7f; + if ((old ^ val) & 0x60) + svga_recalctimings(svga); break; case 0xb2: + case 0xbe: if (ati28800->regs[0xbe] & 0x08) { /* Read/write bank mode */ - svga->read_bank = (((val & 0x01) << 3) | ((val & 0xe0) >> 5)) * 0x10000; - svga->write_bank = ((val & 0x1e) >> 1) * 0x10000; + svga->read_bank = (((ati28800->regs[0xb2] & 0x01) << 3) | ((ati28800->regs[0xb2] & 0xe0) >> 5)) * 0x10000; + svga->write_bank = ((ati28800->regs[0xb2] & 0x1e) >> 1) * 0x10000; } else { /* Single bank mode */ - svga->read_bank = ((val & 0x1e) >> 1) * 0x10000; - svga->write_bank = ((val & 0x1e) >> 1) * 0x10000; + svga->read_bank = ((ati28800->regs[0xb2] & 0x1e) >> 1) * 0x10000; + svga->write_bank = ((ati28800->regs[0xb2] & 0x1e) >> 1) * 0x10000; + } + if (ati28800->index == 0xbe) { + if ((old ^ val) & 0x10) + svga_recalctimings(svga); } break; case 0xb3: - ati28800->regs[0xb3] = val & 0xef; ati_eeprom_write(&ati28800->eeprom, val & 8, val & 2, val & 1); break; case 0xb6: - if ((old ^ val) & 0x10) + if ((old ^ val) & 0x11) svga_recalctimings(svga); break; case 0xb8: @@ -179,7 +180,10 @@ ati28800_out(uint16_t addr, uint8_t val, void *p) break; case 0x3C6: case 0x3C7: case 0x3C8: case 0x3C9: - sc1502x_ramdac_out(addr, val, svga->ramdac, svga); + if (ati28800->type == 1) + sc1148x_ramdac_out(addr, 0, val, svga->ramdac, svga); + else + svga_out(addr, val, svga); return; case 0x3D4: @@ -190,12 +194,6 @@ ati28800_out(uint16_t addr, uint8_t val, void *p) return; if ((svga->crtcreg == 7) && (svga->crtc[0x11] & 0x80)) val = (svga->crtc[7] & ~0x10) | (val & 0x10); - if ((ati28800->regs[0xb4] & 0x10) && ((svga->crtcreg == 0x0a) || (svga->crtcreg == 0x0b))) - return; - if ((ati28800->regs[0xb4] & 0x20) && ((svga->crtc[0x08] & 0x7f) && (svga->crtc[0x14] & 0x1f))) - return; - if ((ati28800->regs[0xb4] & 0x40) && ((svga->crtcreg <= 0x06) && (svga->crtc[0x07] & 0x10) != 0x10)) - return; old = svga->crtc[svga->crtcreg]; svga->crtc[svga->crtcreg] = val; @@ -299,22 +297,23 @@ ati28800_in(uint16_t addr, void *p) break; case 0x1cf: switch (ati28800->index) { - case 0xa0: - temp = 0x10; - break; case 0xaa: temp = ati28800->id; break; case 0xb0: - if (ati28800->memory == 1024) - temp = 0x08; - else if (ati28800->memory == 512) - temp = 0x10; - else - temp = 0x00; + temp = ati28800->regs[0xb0] | 0x80; + if (ati28800->memory == 1024) { + temp &= ~0x10; + temp |= 0x08; + } else if (ati28800->memory == 512) { + temp |= 0x10; + temp &= ~0x08; + } else { + temp &= ~0x18; + } break; case 0xb7: - temp = ati28800->regs[ati28800->index] & ~8; + temp = ati28800->regs[0xb7] & ~8; if (ati_eeprom_read(&ati28800->eeprom)) temp |= 8; break; @@ -333,7 +332,9 @@ ati28800_in(uint16_t addr, void *p) break; case 0x3C6: case 0x3C7: case 0x3C8: case 0x3C9: - return sc1502x_ramdac_in(addr, svga->ramdac, svga); + if (ati28800->type == 1) + return sc1148x_ramdac_in(addr, 0, svga->ramdac, svga); + return svga_in(addr, svga); case 0x3D4: temp = svga->crtcreg; @@ -401,8 +402,14 @@ ati28800_recalctimings(svga_t *svga) { ati28800_t *ati28800 = (ati28800_t *)svga->p; - switch (((ati28800->regs[0xbe] & 0x10) >> 1) | ((ati28800->regs[0xb9] & 2) << 1) | - ((svga->miscout & 0x0C) >> 2)) { + if (ati28800->regs[0xa3] & 0x10) + svga->ma_latch |= 0x10000; + + if (ati28800->regs[0xb0] & 0x40) + svga->ma_latch |= 0x20000; + + switch (((ati28800->regs[0xbe] & 0x10) >> 1) | ((ati28800->regs[0xb9] & 2) << 1) | + ((svga->miscout & 0x0C) >> 2)) { case 0x00: svga->clock = (cpuclock * (double)(1ull << 32)) / 42954000.0; break; case 0x01: svga->clock = (cpuclock * (double)(1ull << 32)) / 48771000.0; break; case 0x02: ati28800_log ("clock 2\n"); break; @@ -420,38 +427,69 @@ ati28800_recalctimings(svga_t *svga) case 0x0E: svga->clock = (cpuclock * (double)(1ull << 32)) / 75000000.0; break; case 0x0F: svga->clock = (cpuclock * (double)(1ull << 32)) / 65000000.0; break; default: break; - } + } - if (ati28800->regs[0xb8] & 0x40) + if (ati28800->regs[0xb8] & 0x40) svga->clock *= 2; - if (ati28800->regs[0xa3] & 0x10) - svga->ma |= 0x10000; + if (ati28800->regs[0xa7] & 0x80) + svga->clock *= 3; - if (ati28800->regs[0xb0] & 0x40) - svga->ma |= 0x20000; - - if (ati28800->regs[0xb6] & 0x10) { + if (ati28800->regs[0xb6] & 0x10) { svga->hdisp <<= 1; svga->htotal <<= 1; svga->rowoffset <<= 1; - } - - if (!svga->scrblank && (ati28800->regs[0xb0] & 0x20)) { /* Extended 256 colour modes */ - switch (svga->bpp) { - case 8: - svga->render = svga_render_8bpp_highres; - svga->rowoffset <<= 1; - svga->ma <<= 1; - break; - case 15: - svga->render = svga_render_15bpp_highres; - svga->hdisp >>= 1; - svga->rowoffset <<= 1; - svga->ma <<= 1; - break; + svga->gdcreg[5] &= ~0x40; } - } + + if (ati28800->regs[0xb0] & 0x20) { + svga->gdcreg[5] |= 0x40; + } + + if (!svga->scrblank && svga->attr_palette_enable) { + if ((svga->gdcreg[6] & 1) || (svga->attrregs[0x10] & 1)) { + switch (svga->gdcreg[5] & 0x60) { + case 0x00: + if (svga->seqregs[1] & 8) /*Low res (320)*/ + svga->render = svga_render_4bpp_lowres; + else + svga->render = svga_render_4bpp_highres; + break; + case 0x20: /*4 colours*/ + if (svga->seqregs[1] & 8) /*Low res (320)*/ + svga->render = svga_render_2bpp_lowres; + else + svga->render = svga_render_2bpp_highres; + break; + case 0x40: case 0x60: /*256+ colours*/ + switch (svga->bpp) { + case 8: + svga->map8 = svga->pallook; + if (svga->lowres) + svga->render = svga_render_8bpp_lowres; + else { + svga->render = svga_render_8bpp_highres; + svga->rowoffset <<= 1; + svga->ma_latch <<= 1; + } + break; + case 15: + if (svga->lowres) + svga->render = svga_render_15bpp_lowres; + else { + svga->render = svga_render_15bpp_highres; + svga->hdisp >>= 1; + svga->rowoffset <<= 1; + svga->ma_latch <<= 1; + } + break; + } + break; + } + } + } + + svga->vram_display_mask = (ati28800->regs[0xb6] & 1) ? ((ati28800->memory << 10) - 1) : 0x3ffff; } @@ -466,14 +504,15 @@ ati28800k_recalctimings(svga_t *svga) svga->render = svga_render_text_80_ksc5601; } - void * ati28800k_init(const device_t *info) { ati28800_t *ati28800 = (ati28800_t *) malloc(sizeof(ati28800_t)); memset(ati28800, 0, sizeof(ati28800_t)); - if (info->local == 0) { + ati28800->type_korean = info->local; + + if (ati28800->type_korean == 0) { ati28800->memory = device_get_config_int("memory"); video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_ati28800); } else { @@ -489,7 +528,7 @@ ati28800k_init(const device_t *info) ati28800->in_get_korean_font_kind_set = 0; ati28800->ksc5601_mode_enabled = 0; - switch(info->local) { + switch(ati28800->type_korean) { case 0: default: rom_init(&ati28800->bios_rom, BIOS_ATIKOR_PATH, 0xc0000, 0x8000, 0x7fff, 0, MEM_MAPPING_EXTERNAL); @@ -512,12 +551,12 @@ ati28800k_init(const device_t *info) NULL, NULL); - ati28800->svga.ramdac = device_add(&sc1502x_ramdac_device); - io_sethandler(0x01ce, 0x0002, ati28800k_in, NULL, NULL, ati28800k_out, NULL, NULL, ati28800); io_sethandler(0x03c0, 0x0020, ati28800k_in, NULL, NULL, ati28800k_out, NULL, NULL, ati28800); ati28800->svga.miscout = 1; + ati28800->svga.bpp = 8; + ati28800->svga.packed_chain4 = 1; ati28800->svga.ksc5601_sbyte_mask = 0; ati28800->svga.ksc5601_udc_area_msb[0] = 0xC9; ati28800->svga.ksc5601_udc_area_msb[1] = 0xFE; @@ -541,14 +580,16 @@ ati28800_init(const device_t *info) ati28800->memory = device_get_config_int("memory"); - switch(info->local) { + ati28800->type = info->local; + + switch(ati28800->type) { case VGAWONDERXL: - ati28800->id = 6; - rom_init_interleaved(&ati28800->bios_rom, - BIOS_VGAXL_EVEN_PATH, - BIOS_VGAXL_ODD_PATH, - 0xc0000, 0x10000, 0xffff, - 0, MEM_MAPPING_EXTERNAL); + ati28800->id = 5; + rom_init(&ati28800->bios_rom, + BIOS_VGAXL_ROM_PATH, + 0xc0000, 0x8000, 0x7fff, + 0, MEM_MAPPING_EXTERNAL); + ati28800->svga.ramdac = device_add(&sc11486_ramdac_device); break; #if defined(DEV_BRANCH) && defined(USE_XL24) @@ -576,8 +617,6 @@ ati28800_init(const device_t *info) ati28800_in, ati28800_out, NULL, NULL); - - ati28800->svga.ramdac = device_add(&sc1502x_ramdac_device); io_sethandler(0x01ce, 2, ati28800_in, NULL, NULL, @@ -587,8 +626,10 @@ ati28800_init(const device_t *info) ati28800_out, NULL, NULL, ati28800); ati28800->svga.miscout = 1; + ati28800->svga.bpp = 8; + ati28800->svga.packed_chain4 = 1; - switch (info->local) { + switch (ati28800->type) { case VGAWONDERXL: ati_eeprom_load(&ati28800->eeprom, "ati28800xl.nvr", 0); break; @@ -616,7 +657,7 @@ ati28800_available(void) static int -ati28800k_available() +ati28800k_available(void) { return ((rom_present(BIOS_ATIKOR_PATH) && rom_present(FONT_ATIKOR_PATH))); } @@ -625,7 +666,7 @@ ati28800k_available() static int compaq_ati28800_available(void) { - return((rom_present(BIOS_VGAXL_EVEN_PATH) && rom_present(BIOS_VGAXL_ODD_PATH))); + return((rom_present(BIOS_VGAXL_ROM_PATH))); } @@ -719,7 +760,7 @@ static const device_config_t ati28800_wonderxl_config[] = const device_t ati28800_device = { - "ATI-28800", + "ATI 28800-5 (ATI VGA Charger)", DEVICE_ISA, 0, ati28800_init, ati28800_close, NULL, @@ -765,7 +806,7 @@ const device_t ati28800k_spc6033p_device = const device_t compaq_ati28800_device = { - "Compaq ATI-28800", + "ATI 28800-5 (ATI VGA Wonder XL)", DEVICE_ISA, VGAWONDERXL, ati28800_init, ati28800_close, NULL, diff --git a/src/video/vid_et4000w32.c b/src/video/vid_et4000w32.c index 36f8b67fb..233783439 100644 --- a/src/video/vid_et4000w32.c +++ b/src/video/vid_et4000w32.c @@ -97,11 +97,12 @@ typedef struct et4000w32p_t uint32_t pattern_addr, source_addr, dest_addr, mix_addr; } queued, internal; + uint8_t osr; uint8_t status; int pattern_x, source_x, pattern_x_back, source_x_back, pattern_y, source_y, cpu_dat_pos, pix_pos, - cpu_input_num; + cpu_input_num, queue; uint32_t pattern_addr, source_addr, dest_addr, mix_addr, pattern_back, source_back, dest_back, mix_back, @@ -119,9 +120,9 @@ typedef struct et4000w32p_t static int et4000w32_vbus[4] = {1, 2, 4, 4}; -static int et4000w32_max_x[8] = {0, 0, 4, 8, 16, 32, 64, 0x70000000}; -static int et4000w32_wrap_x[8] = {0, 0, 3, 7, 15, 31, 63, 0xffffffff}; -static int et4000w32_wrap_y[8] = {1, 2, 4, 8, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}; +static int et4000w32_max_x[8] = {0,0,4,8,0x10,0x20,0x40,0x70000000}; +static int et4000w32_wrap_x[8] = {0,0,3,7,0x0F,0x1F,0x3F,~0}; +static int et4000w32_wrap_y[8] = {1,2,4,8,~0,~0,~0,~0}; static video_timings_t timing_et4000w32_vlb = {VIDEO_BUS, 4, 4, 4, 10, 10, 10}; static video_timings_t timing_et4000w32_pci = {VIDEO_PCI, 4, 4, 4, 10, 10, 10}; @@ -271,14 +272,24 @@ et4000w32p_out(uint16_t addr, uint8_t val, void *p) switch (svga->bpp) { case 8: svga->hwcursor.xoff += 32; - break; + break; } } if (svga->hwcursor.xsize == 128) { svga->hwcursor.xoff &= 0x7f; svga->hwcursor.yoff &= 0x7f; + if (et4000->type > ET4000W32P_REVC) { + if (svga->bpp == 24) { + et4000->adjust_cursor = 2; + } + } } else { + if (et4000->type > ET4000W32P_REVC) { + if (svga->bpp == 24 && et4000->adjust_cursor) { + et4000->adjust_cursor = 0; + } + } svga->hwcursor.xoff &= 0x3f; svga->hwcursor.yoff &= 0x3f; } @@ -331,10 +342,15 @@ et4000w32p_in(uint16_t addr, void *p) case 0x214B: case 0x215B: case 0x216B: case 0x217B: if (et4000->index == 0xec) return (et4000->regs[0xec] & 0xf) | (et4000->rev << 4); - if (et4000->index == 0xee) { /* Preliminary implementation */ - if (svga->bpp == 8) - return 3; - else if (svga->bpp == 16) + if (et4000->index == 0xee) { + if (svga->bpp == 8) { + if ((svga->gdcreg[5] & 0x60) >= 0x40) + return 3; + else if ((svga->gdcreg[5] & 0x60) == 0x20) + return 1; + else + return 2; + } else if (svga->bpp == 15 || svga->bpp == 16) return 4; else break; @@ -409,13 +425,21 @@ et4000w32p_recalctimings(svga_t *svga) switch (svga->bpp) { case 15: case 16: svga->hdisp >>= 1; - if (et4000->type <= ET4000W32P_REVC) - et4000->adjust_cursor = 1; + if (et4000->type <= ET4000W32P_REVC) { + if (et4000->type == ET4000W32P_REVC) { + if (svga->hdisp != 1024) + et4000->adjust_cursor = 1; + } else + et4000->adjust_cursor = 1; + } break; case 24: svga->hdisp /= 3; if (et4000->type <= ET4000W32P_REVC) et4000->adjust_cursor = 2; + if (et4000->type == ET4000W32P_DIAMOND && (svga->hdisp == 640/2 || svga->hdisp == 1232)) { + svga->hdisp = 640; + } break; } @@ -569,45 +593,51 @@ et4000w32p_accel_write_fifo(et4000w32p_t *et4000, uint32_t addr, uint8_t val) case 0x7f80: et4000->acl.queued.pattern_addr = (et4000->acl.queued.pattern_addr & 0xFFFFFF00) | val; break; case 0x7f81: et4000->acl.queued.pattern_addr = (et4000->acl.queued.pattern_addr & 0xFFFF00FF) | (val << 8); break; case 0x7f82: et4000->acl.queued.pattern_addr = (et4000->acl.queued.pattern_addr & 0xFF00FFFF) | (val << 16); break; - case 0x7f83: et4000->acl.queued.pattern_addr = (et4000->acl.queued.pattern_addr & 0x00FFFFFF) | (val << 24); break; + case 0x7f83: et4000->acl.queued.pattern_addr = (et4000->acl.queued.pattern_addr & 0x00FFFFFF) | (val << 24); et4000->acl.queue++; break; case 0x7f84: et4000->acl.queued.source_addr = (et4000->acl.queued.source_addr & 0xFFFFFF00) | val; break; case 0x7f85: et4000->acl.queued.source_addr = (et4000->acl.queued.source_addr & 0xFFFF00FF) | (val << 8); break; case 0x7f86: et4000->acl.queued.source_addr = (et4000->acl.queued.source_addr & 0xFF00FFFF) | (val << 16); break; - case 0x7f87: et4000->acl.queued.source_addr = (et4000->acl.queued.source_addr & 0x00FFFFFF) | (val << 24); break; + case 0x7f87: et4000->acl.queued.source_addr = (et4000->acl.queued.source_addr & 0x00FFFFFF) | (val << 24); et4000->acl.queue++; break; case 0x7f88: et4000->acl.queued.pattern_off = (et4000->acl.queued.pattern_off & 0xFF00) | val; break; - case 0x7f89: et4000->acl.queued.pattern_off = (et4000->acl.queued.pattern_off & 0x00FF) | (val << 8); break; + case 0x7f89: et4000->acl.queued.pattern_off = (et4000->acl.queued.pattern_off & 0x00FF) | (val << 8); et4000->acl.queue++; break; case 0x7f8a: et4000->acl.queued.source_off = (et4000->acl.queued.source_off & 0xFF00) | val; break; - case 0x7f8b: et4000->acl.queued.source_off = (et4000->acl.queued.source_off & 0x00FF) | (val << 8); break; + case 0x7f8b: et4000->acl.queued.source_off = (et4000->acl.queued.source_off & 0x00FF) | (val << 8); et4000->acl.queue++;break; case 0x7f8c: et4000->acl.queued.dest_off = (et4000->acl.queued.dest_off & 0xFF00) | val; break; - case 0x7f8d: et4000->acl.queued.dest_off = (et4000->acl.queued.dest_off & 0x00FF) | (val << 8); break; + case 0x7f8d: et4000->acl.queued.dest_off = (et4000->acl.queued.dest_off & 0x00FF) | (val << 8); et4000->acl.queue++; break; case 0x7f8e: + et4000->acl.queue++; if (et4000->type >= ET4000W32P_REVC) et4000->acl.queued.pixel_depth = val; else et4000->acl.queued.vbus = val; break; - case 0x7f8f: et4000->acl.queued.xy_dir = val; break; - case 0x7f90: et4000->acl.queued.pattern_wrap = val; break; - case 0x7f92: et4000->acl.queued.source_wrap = val; break; + case 0x7f8f: et4000->acl.queued.xy_dir = val; et4000->acl.queue++; break; + case 0x7f90: et4000->acl.queued.pattern_wrap = val; et4000->acl.queue++; break; + case 0x7f92: et4000->acl.queued.source_wrap = val; et4000->acl.queue++; break; case 0x7f98: et4000->acl.queued.count_x = (et4000->acl.queued.count_x & 0xFF00) | val; break; - case 0x7f99: et4000->acl.queued.count_x = (et4000->acl.queued.count_x & 0x00FF) | (val << 8); break; + case 0x7f99: et4000->acl.queued.count_x = (et4000->acl.queued.count_x & 0x00FF) | (val << 8); et4000->acl.queue++; break; case 0x7f9a: et4000->acl.queued.count_y = (et4000->acl.queued.count_y & 0xFF00) | val; break; - case 0x7f9b: et4000->acl.queued.count_y = (et4000->acl.queued.count_y & 0x00FF) | (val << 8); break; - case 0x7f9c: et4000->acl.queued.ctrl_routing = val; break; - case 0x7f9d: et4000->acl.queued.ctrl_reload = val; break; - case 0x7f9e: et4000->acl.queued.rop_bg = val; break; - case 0x7f9f: et4000->acl.queued.rop_fg = val; break; + case 0x7f9b: et4000->acl.queued.count_y = (et4000->acl.queued.count_y & 0x00FF) | (val << 8); et4000->acl.queue++; break; + case 0x7f9c: et4000->acl.queued.ctrl_routing = val; et4000->acl.queue++; break; + case 0x7f9d: et4000->acl.queued.ctrl_reload = val; et4000->acl.queue++; break; + case 0x7f9e: et4000->acl.queued.rop_bg = val; et4000->acl.queue++; break; + case 0x7f9f: et4000->acl.queued.rop_fg = val; et4000->acl.queue++; break; case 0x7fa0: et4000->acl.queued.dest_addr = (et4000->acl.queued.dest_addr & 0xFFFFFF00) | val; break; case 0x7fa1: et4000->acl.queued.dest_addr = (et4000->acl.queued.dest_addr & 0xFFFF00FF) | (val << 8); break; case 0x7fa2: et4000->acl.queued.dest_addr = (et4000->acl.queued.dest_addr & 0xFF00FFFF) | (val << 16); break; case 0x7fa3: et4000->acl.queued.dest_addr = (et4000->acl.queued.dest_addr & 0x00FFFFFF) | (val << 24); + et4000->acl.queue++; et4000->acl.internal = et4000->acl.queued; if (et4000->type >= ET4000W32P_REVC) { - et4000w32p_blit_start(et4000); - if (!(et4000->acl.queued.ctrl_routing & 0x43)) - et4000w32p_blit(0xffffff, ~0, 0, 0, et4000); - if ((et4000->acl.queued.ctrl_routing & 0x40) && !(et4000->acl.internal.ctrl_routing & 3)) - et4000w32p_blit(4, ~0, 0, 0, et4000); + if (et4000->acl.osr & 0x10) { + et4000w32p_blit_start(et4000); + if (!(et4000->acl.queued.ctrl_routing & 0x43)) { + et4000w32p_blit(0xffffff, ~0, 0, 0, et4000); + } + if ((et4000->acl.queued.ctrl_routing & 0x40) && !(et4000->acl.internal.ctrl_routing & 3)) { + et4000w32p_blit(4, ~0, 0, 0, et4000); + } + } } else { et4000w32_blit_start(et4000); et4000->acl.cpu_input_num = 0; @@ -618,15 +648,15 @@ et4000w32p_accel_write_fifo(et4000w32p_t *et4000, uint32_t addr, uint8_t val) case 0x7fa4: et4000->acl.queued.mix_addr = (et4000->acl.queued.mix_addr & 0xFFFFFF00) | val; break; case 0x7fa5: et4000->acl.queued.mix_addr = (et4000->acl.queued.mix_addr & 0xFFFF00FF) | (val << 8); break; case 0x7fa6: et4000->acl.queued.mix_addr = (et4000->acl.queued.mix_addr & 0xFF00FFFF) | (val << 16); break; - case 0x7fa7: et4000->acl.queued.mix_addr = (et4000->acl.queued.mix_addr & 0x00FFFFFF) | (val << 24); break; + case 0x7fa7: et4000->acl.queued.mix_addr = (et4000->acl.queued.mix_addr & 0x00FFFFFF) | (val << 24); et4000->acl.queue++; break; case 0x7fa8: et4000->acl.queued.mix_off = (et4000->acl.queued.mix_off & 0xFF00) | val; break; - case 0x7fa9: et4000->acl.queued.mix_off = (et4000->acl.queued.mix_off & 0x00FF) | (val << 8); break; + case 0x7fa9: et4000->acl.queued.mix_off = (et4000->acl.queued.mix_off & 0x00FF) | (val << 8); et4000->acl.queue++; break; case 0x7faa: et4000->acl.queued.error = (et4000->acl.queued.error & 0xFF00) | val; break; - case 0x7fab: et4000->acl.queued.error = (et4000->acl.queued.error & 0x00FF) | (val << 8); break; + case 0x7fab: et4000->acl.queued.error = (et4000->acl.queued.error & 0x00FF) | (val << 8); et4000->acl.queue++; break; case 0x7fac: et4000->acl.queued.dmin = (et4000->acl.queued.dmin & 0xFF00) | val; break; - case 0x7fad: et4000->acl.queued.dmin = (et4000->acl.queued.dmin & 0x00FF) | (val << 8); break; + case 0x7fad: et4000->acl.queued.dmin = (et4000->acl.queued.dmin & 0x00FF) | (val << 8); et4000->acl.queue++; break; case 0x7fae: et4000->acl.queued.dmaj = (et4000->acl.queued.dmaj & 0xFF00) | val; break; - case 0x7faf: et4000->acl.queued.dmaj = (et4000->acl.queued.dmaj & 0x00FF) | (val << 8); break; + case 0x7faf: et4000->acl.queued.dmaj = (et4000->acl.queued.dmaj & 0x00FF) | (val << 8); et4000->acl.queue++; break; } } @@ -638,17 +668,17 @@ et4000w32p_accel_write_mmu(et4000w32p_t *et4000, uint32_t addr, uint8_t val) if (!(et4000->acl.status & ACL_XYST)) return; if (et4000->acl.internal.ctrl_routing & 3) { + et4000->acl.queue++; if ((et4000->acl.internal.ctrl_routing & 3) == 2) { - if (et4000->acl.mix_addr & 7) - et4000w32p_blit(8 - (et4000->acl.mix_addr & 7), val >> (et4000->acl.mix_addr & 7), 0, 1, et4000); - else - et4000w32p_blit(8, val, 0, 1, et4000); + et4000w32p_blit(8 - (et4000->acl.mix_addr & 7), val >> (et4000->acl.mix_addr & 7), 0, 1, et4000); } - else if ((et4000->acl.internal.ctrl_routing & 3) == 1) + else if ((et4000->acl.internal.ctrl_routing & 3) == 1) { et4000w32p_blit(1, ~0, val, 2, et4000); + } } } else { if (!(et4000->acl.status & ACL_XYST)) { + et4000->acl.queue++; et4000->acl.queued.dest_addr = (addr & 0x1FFF) + et4000->mmu.base[et4000->bank]; et4000->acl.internal = et4000->acl.queued; et4000w32_blit_start(et4000); @@ -658,6 +688,7 @@ et4000w32p_accel_write_mmu(et4000w32p_t *et4000, uint32_t addr, uint8_t val) } if (et4000->acl.internal.ctrl_routing & 7) { + et4000->acl.queue++; et4000->acl.cpu_input = (et4000->acl.cpu_input &~ (0xFF << (et4000->acl.cpu_input_num << 3))) | (val << (et4000->acl.cpu_input_num << 3)); et4000->acl.cpu_input_num++; @@ -716,6 +747,7 @@ et4000w32p_mmu_write(uint32_t addr, uint8_t val, void *p) case 0x7f0a: et4000->mmu.base[2] = (et4000->mmu.base[2] & 0xFF00FFFF) | (val << 16); break; case 0x7f0b: et4000->mmu.base[2] = (et4000->mmu.base[2] & 0x00FFFFFF) | (val << 24); break; case 0x7f13: et4000->mmu.ctrl = val; break; + case 0x7f31: et4000->acl.osr = val; break; } break; } @@ -771,13 +803,23 @@ et4000w32p_mmu_read(uint32_t addr, void *p) case 0x7f13: return et4000->mmu.ctrl; case 0x7f36: - if (et4000->type >= ET4000W32P_REVC) { + if (et4000->type >= ET4000W32P_REVC) { + if (et4000->acl.queue) { + et4000->acl.status |= ACL_RDST; + et4000->acl.queue = 0; + } else + et4000->acl.status &= ~ACL_RDST; + temp = et4000->acl.status; - temp &= ~(ACL_RDST | ACL_WRST); - if (temp == ACL_XYST && (et4000->acl.internal.ctrl_routing == 1 || et4000->acl.internal.ctrl_routing == 2)) - temp |= ACL_RDST; } else { et4000->acl.status &= ~(ACL_XYST | ACL_SSO); + + if (et4000->acl.queue) { + et4000->acl.status |= ACL_RDST; + et4000->acl.queue = 0; + } else + et4000->acl.status &= ~ACL_RDST; + temp = et4000->acl.status; } return temp; @@ -883,7 +925,7 @@ et4000w32p_blit_start(et4000w32p_t *et4000) et4000->acl.status |= ACL_XYST; et4000w32_log("ACL status XYST set\n"); if ((!(et4000->acl.internal.ctrl_routing & 7) || (et4000->acl.internal.ctrl_routing & 4)) && !(et4000->acl.internal.ctrl_routing & 0x40)) - et4000->acl.status |= ACL_SSO; + et4000->acl.status |= ACL_SSO; if (et4000w32_wrap_x[et4000->acl.internal.pattern_wrap & 7]) { et4000->acl.pattern_x = et4000->acl.pattern_addr & et4000w32_wrap_x[et4000->acl.internal.pattern_wrap & 7]; @@ -997,7 +1039,7 @@ et4000w32p_blit(int count, uint32_t mix, uint32_t sdat, int cpu_input, et4000w32 if (!(et4000->acl.status & ACL_XYST) && (et4000->type >= ET4000W32P_REVC)) return; - if (et4000->acl.internal.xy_dir & 0x80){ /* Line draw */ + if (et4000->acl.internal.xy_dir & 0x80) { /* Line draw */ while (count--) { et4000w32_log("%i,%i : ", et4000->acl.internal.pos_x, et4000->acl.internal.pos_y); pattern = svga->vram[(et4000->acl.pattern_addr + et4000->acl.pattern_x) & et4000->vram_mask]; diff --git a/src/video/vid_oak_oti.c b/src/video/vid_oak_oti.c index 892967837..941359987 100644 --- a/src/video/vid_oak_oti.c +++ b/src/video/vid_oak_oti.c @@ -28,6 +28,7 @@ #include <86box/device.h> #include <86box/video.h> #include <86box/vid_svga.h> +#include <86box/vid_svga_render.h> #define BIOS_037C_PATH "roms/video/oti/bios.bin" #define BIOS_067_AMA932J_PATH "roms/machines/ama932j/oti067.bin" @@ -85,6 +86,14 @@ oti_out(uint16_t addr, uint8_t val, void *p) return; } else break; + break; + + case 0x3c6: case 0x3c7: case 0x3c8: case 0x3c9: + if (oti->chip_id == OTI_077) + sc1148x_ramdac_out(addr, 0, val, svga->ramdac, svga); + else + svga_out(addr, val, svga); + return; case 0x3D4: if (oti->chip_id) @@ -216,6 +225,11 @@ oti_in(uint16_t addr, void *p) temp = oti->enable_register; break; + case 0x3c6: case 0x3c7: case 0x3c8: case 0x3c9: + if (oti->chip_id == OTI_077) + return sc1148x_ramdac_in(addr, 0, svga->ramdac, svga); + return svga_in(addr, svga); + case 0x3CF: return svga->gdcreg[svga->gdcaddr & 0xf]; @@ -343,6 +357,14 @@ oti_recalctimings(svga_t *svga) if ((oti->regs[0x0d] & 0x0c) && !(oti->regs[0x0d] & 0x10)) svga->rowoffset <<= 1; svga->interlace = oti->regs[0x14] & 0x80; + + if (svga->bpp == 16) { + svga->render = svga_render_16bpp_highres; + svga->hdisp >>= 1; + } else if (svga->bpp == 15) { + svga->render = svga_render_15bpp_highres; + svga->hdisp >>= 1; + } } @@ -406,6 +428,9 @@ oti_init(const device_t *info) svga_init(info, &oti->svga, oti, oti->vram_size << 10, oti_recalctimings, oti_in, oti_out, NULL, NULL); + if (oti->chip_id == OTI_077) + oti->svga.ramdac = device_add(&sc11487_ramdac_device); /*Actually a 82c487, probably a clone.*/ + io_sethandler(0x03c0, 32, oti_in, NULL, NULL, oti_out, NULL, NULL, oti); diff --git a/src/video/vid_paradise.c b/src/video/vid_paradise.c index edb732032..5fd0aada0 100644 --- a/src/video/vid_paradise.c +++ b/src/video/vid_paradise.c @@ -54,6 +54,7 @@ typedef struct paradise_t uint32_t read_bank[4], write_bank[4]; int interlace; + int check; struct { uint8_t reg_block_ptr; @@ -105,6 +106,13 @@ void paradise_out(uint16_t addr, uint8_t val, void *p) } break; + case 0x3c6: case 0x3c7: case 0x3c8: case 0x3c9: + if (paradise->type == WD90C30) + sc1148x_ramdac_out(addr, 0, val, svga->ramdac, svga); + else + svga_out(addr, val, svga); + return; + case 0x3cf: if (svga->gdcaddr >= 9 && svga->gdcaddr <= 0x0e) { if ((paradise->pr5 & 7) != 5) @@ -215,7 +223,12 @@ uint8_t paradise_in(uint16_t addr, void *p) return svga->seqregs[svga->seqaddr & 0x1f]; } break; - + + case 0x3c6: case 0x3c7: case 0x3c8: case 0x3c9: + if (paradise->type == WD90C30) + return sc1148x_ramdac_in(addr, 0, svga->ramdac, svga); + return svga_in(addr, svga); + case 0x3cf: if (svga->gdcaddr >= 9 && svga->gdcaddr <= 0x0e) { if ((paradise->pr5 & 7) != 5) @@ -232,8 +245,11 @@ uint8_t paradise_in(uint16_t addr, void *p) paradise->pr1 &= ~0x40; } else if (paradise->vram_mask == (1024 << 10) - 1) { paradise->pr1 |= 0xc0; - if (svga->bpp >= 8 && !svga->lowres) /*Horrible tweak, but needed to get around black corruption in 1M mode*/ + /*The following is a horrible tweak, but needed to get around black corruption in 1M mode*/ + if (svga->bpp >= 8 && (svga->gdcreg[0x0e] & 0x01) && paradise->check) paradise->pr1 &= ~0x40; + else if (!(svga->gdcreg[0x0e] & 0x01) && !(svga->crtc[0x14] & 0x40) && paradise->check) + paradise->check = 0; } return paradise->pr1; case 6: @@ -336,9 +352,25 @@ void paradise_recalctimings(svga_t *svga) svga->interlace = 1; } } - - if (svga->bpp >= 8 && !svga->lowres) - svga->render = svga_render_8bpp_highres; + + if (paradise->type < WD90C30) { + if (svga->bpp >= 8 && !svga->lowres) + svga->render = svga_render_8bpp_highres; + } else { + if (svga->bpp >= 8 && !svga->lowres) { + if (svga->bpp == 16) { + svga->render = svga_render_16bpp_highres; + svga->hdisp >>= 1; + } else if (svga->bpp == 15) { + svga->render = svga_render_15bpp_highres; + svga->hdisp >>= 1; + } else { + if ((svga->crtc[0x17] == 0xc2) && (svga->crtc[0x14] & 0x40)) + paradise->check = 1; + svga->render = svga_render_8bpp_highres; + } + } + } } static void paradise_write(uint32_t addr, uint8_t val, void *p) @@ -348,7 +380,7 @@ static void paradise_write(uint32_t addr, uint8_t val, void *p) addr &= svga->banked_mask; addr = (addr & 0x7fff) + paradise->write_bank[(addr >> 15) & 3]; - + svga_write_linear(addr, val, svga); } static void paradise_writew(uint32_t addr, uint16_t val, void *p) @@ -421,6 +453,7 @@ void *paradise_init(const device_t *info, uint32_t memsize) NULL); paradise->vram_mask = memsize - 1; svga->decode_mask = memsize - 1; + svga->ramdac = device_add(&sc11487_ramdac_device); /*Actually a Winbond W82c487-80, probably a clone.*/ break; } diff --git a/src/video/vid_s3.c b/src/video/vid_s3.c index 84f49425a..7385c068a 100644 --- a/src/video/vid_s3.c +++ b/src/video/vid_s3.c @@ -238,7 +238,7 @@ typedef struct s3_t uint16_t multifunc[16]; uint8_t pix_trans[4]; int ssv_state; - + int cx, cy; int px, py; int sx, sy; @@ -347,6 +347,7 @@ typedef struct s3_t int translate; int enable_8514; + int color_16bit; volatile int busy, force_busy; uint8_t thread_run, serialport; @@ -453,7 +454,7 @@ static void s3_visionx68_video_engine_op(uint32_t cpu_dat, s3_t *s3); temp = svga->vram[dword_remap(svga, (s3->accel.dest + s3->accel.cx)) & s3->vram_mask]; \ #define READ_PIXTRANS_WORD \ - if (s3->bpp == 0) { \ + if (s3->bpp == 0 && !s3->color_16bit) { \ temp = svga->vram[dword_remap(svga, (s3->accel.dest + s3->accel.cx)) & s3->vram_mask]; \ temp |= (svga->vram[dword_remap(svga, (s3->accel.dest + s3->accel.cx + 1)) & s3->vram_mask] << 8); \ } else { \ @@ -461,7 +462,7 @@ static void s3_visionx68_video_engine_op(uint32_t cpu_dat, s3_t *s3); } #define READ_PIXTRANS_LONG \ - if (s3->bpp == 0) { \ + if (s3->bpp == 0 && !s3->color_16bit) { \ temp = svga->vram[dword_remap(svga, (s3->accel.dest + s3->accel.cx)) & s3->vram_mask]; \ temp |= (svga->vram[dword_remap(svga, (s3->accel.dest + s3->accel.cx + 1)) & s3->vram_mask] << 8); \ temp |= (svga->vram[dword_remap(svga, (s3->accel.dest + s3->accel.cx + 2)) & s3->vram_mask] << 16); \ @@ -520,7 +521,7 @@ s3_accel_out_pixtrans_w(s3_t *s3, uint16_t val) { svga_t *svga = &s3->svga; - if (s3_cpu_src(s3)) { + if (s3->accel.cmd & 0x100) { switch (s3->accel.cmd & 0x600) { case 0x000: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { @@ -530,8 +531,12 @@ s3_accel_out_pixtrans_w(s3_t *s3, uint16_t val) s3_accel_start(8, 1, val | (val << 16), 0, s3); } else s3_accel_start(1, 1, 0xffffffff, val | (val << 16), s3); - } else - s3_accel_start(1, 1, 0xffffffff, val | (val << 16), s3); + } else { + if (s3->color_16bit) + s3_accel_start(2, 1, 0xffffffff, val | (val << 16), s3); + else + s3_accel_start(1, 1, 0xffffffff, val | (val << 16), s3); + } break; case 0x200: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { @@ -586,7 +591,7 @@ s3_accel_out_pixtrans_w(s3_t *s3, uint16_t val) static void s3_accel_out_pixtrans_l(s3_t *s3, uint32_t val) { - if (s3_cpu_src(s3)) { + if (s3->accel.cmd & 0x100) { switch (s3->accel.cmd & 0x600) { case 0x000: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { @@ -700,12 +705,14 @@ s3_accel_out_fifo(s3_t *s3, uint16_t port, uint8_t val) s3->accel.cur_x2 = (s3->accel.cur_x2 & 0xff) | ((val & 0x0f) << 8); s3->accel.poly_cx2 = s3->accel.cur_x2 << 20; break; - + + case 0xcae8: case 0x8948: case 0x8ae8: s3->accel.draw_fifo_slot++; s3->accel.desty_axstp = (s3->accel.desty_axstp & 0x3f00) | val; s3->accel.point_1_updated = 1; break; + case 0xcae9: case 0x8949: case 0x8ae9: s3->accel.desty_axstp = (s3->accel.desty_axstp & 0xff) | ((val & 0x3f) << 8); if (val & 0x20) @@ -805,7 +812,7 @@ s3_accel_out_fifo(s3_t *s3, uint16_t port, uint8_t val) s3->accel.short_stroke = (s3->accel.short_stroke & 0xff00) | val; break; case 0x9d49: case 0x9ee9: - s3->accel.short_stroke = (s3->accel.short_stroke & 0xff) | (val << 8); + s3->accel.short_stroke = (s3->accel.short_stroke & 0xff) | (val << 8); s3->accel.ssv_state = 1; s3->accel.cx = s3->accel.cur_x; @@ -1015,7 +1022,7 @@ s3_accel_out_fifo(s3_t *s3, uint16_t port, uint8_t val) s3->accel.setup_fifo_slot++; s3->accel.frgd_mix = val; break; - + case 0xbd48: case 0xbee8: s3->accel.multifunc_cntl = (s3->accel.multifunc_cntl & 0xff00) | val; break; @@ -1138,8 +1145,12 @@ s3_accel_out_fifo(s3_t *s3, uint16_t port, uint8_t val) s3_accel_start(8, 1, s3->accel.pix_trans[0], 0, s3); else s3_accel_start(1, 1, 0xffffffff, s3->accel.pix_trans[0], s3); - } else - s3_accel_start(1, 1, 0xffffffff, s3->accel.pix_trans[0], s3); + } else { + if (s3->color_16bit) + s3_accel_start(2, 1, 0xffffffff, s3->accel.pix_trans[0], s3); + else + s3_accel_start(1, 1, 0xffffffff, s3->accel.pix_trans[0], s3); + } } } break; @@ -1156,8 +1167,9 @@ s3_accel_out_fifo(s3_t *s3, uint16_t port, uint8_t val) s3_accel_start(8, 1, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8), 0, s3); else s3_accel_start(1, 1, 0xffffffff, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8), s3); - } else + } else { s3_accel_start(1, 1, 0xffffffff, s3->accel.pix_trans[0] | (s3->accel.pix_trans[1] << 8), s3); + } break; case 0x200: /*Windows 95's built-in driver expects this to be loaded regardless of the byte swap bit (0xE2E9) in the 86c928*/ @@ -1313,6 +1325,7 @@ s3_accel_out_fifo_l(s3_t *s3, uint16_t port, uint32_t val) } else { s3->accel.b2e8_pix = 0; } + s3_accel_out_pixtrans_l(s3, val); } @@ -1467,8 +1480,26 @@ s3_accel_write_fifo(s3_t *s3, uint32_t addr, uint8_t val) if (addr & 0x8000) { s3_accel_out_fifo(s3, addr & 0xffff, val); } else { - s3_accel_out_fifo(s3, 0xe2e8 + (addr & 3), val); - } + if (s3->accel.cmd & 0x100) { + if ((s3->accel.cmd & 0x600) == 0x200) { + if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { + if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) + s3_accel_start(16, 1, val | (val << 8) | (val << 16) | (val << 24), 0, s3); + else + s3_accel_start(2, 1, 0xffffffff, val | (val << 8) | (val << 16) | (val << 24), s3); + } else + s3_accel_start(2, 1, 0xffffffff, val | (val << 8) | (val << 16) | (val << 24), s3); + } else { + if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { + if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) + s3_accel_start(8, 1, val | (val << 8) | (val << 16) | (val << 24), 0, s3); + else + s3_accel_start(1, 1, 0xffffffff, val | (val << 8) | (val << 16) | (val << 24), s3); + } else + s3_accel_start(1, 1, 0xffffffff, val | (val << 8) | (val << 16) | (val << 24), s3); + } + } + } } } @@ -2199,11 +2230,13 @@ s3_io_remove(s3_t *s3) io_removehandler(0xb6e8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0xbae8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0xbee8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); + io_removehandler(0xcae8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0xd2e8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0xe2e8, 0x0004, s3_accel_in, s3_accel_in_w, s3_accel_in_l, s3_accel_out, s3_accel_out_w, s3_accel_out_l, s3); io_removehandler(0xe6e8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0xeae8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_removehandler(0xeee8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); + io_removehandler(0xfee8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); s3_io_remove_alt(s3); } @@ -2316,13 +2349,15 @@ s3_io_set(s3_t *s3) io_sethandler(0xb6e8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0xbae8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0xbee8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); + io_sethandler(0xcae8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0xe2e8, 0x0004, s3_accel_in, s3_accel_in_w, s3_accel_in_l, s3_accel_out, s3_accel_out_w, s3_accel_out_l, s3); if (s3->chip == S3_VISION968 || s3->chip == S3_VISION868) { io_sethandler(0xd2e8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0xe6e8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0xeae8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); io_sethandler(0xeee8, 0x0004, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); - } + } + io_sethandler(0xfee8, 0x0002, s3_accel_in, NULL, NULL, s3_accel_out, NULL, NULL, s3); s3_io_set_alt(s3); } @@ -2360,13 +2395,10 @@ s3_out(uint16_t addr, uint8_t val, void *p) } if (svga->seqaddr == 4) /*Chain-4 - update banking*/ { - svga->chain4 = !!(val & 8); - if (svga->crtc[0x31] & 1) { - if (svga->chain4) - svga->write_bank = svga->read_bank = s3->bank << 16; - else - svga->write_bank = svga->read_bank = s3->bank << 14; - } + if (val & 0x08) + svga->write_bank = svga->read_bank = s3->bank << 16; + else + svga->write_bank = svga->read_bank = s3->bank << 14; } else if (svga->seqaddr == 9) { svga->seqregs[svga->seqaddr] = val & 0x80; s3_io_set(s3); @@ -2430,7 +2462,9 @@ s3_out(uint16_t addr, uint8_t val, void *p) return; if ((svga->crtcreg == 0x36) && (svga->crtc[0x39] != 0xa5)) return; - if ((s3->chip == S3_TRIO64V2) && svga->crtcreg >= 0x80) + if ((s3->chip == S3_TRIO64V2) && (svga->crtcreg >= 0x80)) + return; + if ((s3->chip <= S3_86C924) && (svga->crtcreg >= 0x50)) return; old = svga->crtc[svga->crtcreg]; svga->crtc[svga->crtcreg] = val; @@ -2486,12 +2520,10 @@ s3_out(uint16_t addr, uint8_t val, void *p) case 0x35: s3->bank = (s3->bank & 0x70) | (val & 0xf); - if (svga->crtc[0x31] & 1) { - if (svga->chain4) - svga->write_bank = svga->read_bank = s3->bank << 16; - else - svga->write_bank = svga->read_bank = s3->bank << 14; - } + if (svga->chain4) + svga->write_bank = svga->read_bank = s3->bank << 16; + else + svga->write_bank = svga->read_bank = s3->bank << 14; break; case 0x51: @@ -2502,23 +2534,19 @@ s3_out(uint16_t addr, uint8_t val, void *p) s3->bank = (s3->bank & 0x4f) | ((val & 0xc) << 2); s3->ma_ext = (s3->ma_ext & ~0xc) | ((val & 3) << 2); } - if (svga->crtc[0x31] & 1) { - if (svga->chain4) - svga->write_bank = svga->read_bank = s3->bank << 16; - else - svga->write_bank = svga->read_bank = s3->bank << 14; - } + if (svga->chain4) + svga->write_bank = svga->read_bank = s3->bank << 16; + else + svga->write_bank = svga->read_bank = s3->bank << 14; break; case 0x6a: if (s3->chip >= S3_VISION964) { s3->bank = val; - if (svga->crtc[0x31] & 1) { - if (svga->chain4) - svga->write_bank = svga->read_bank = s3->bank << 16; - else - svga->write_bank = svga->read_bank = s3->bank << 14; - } + if (svga->chain4) + svga->write_bank = svga->read_bank = s3->bank << 16; + else + svga->write_bank = svga->read_bank = s3->bank << 14; } break; @@ -2771,6 +2799,7 @@ static void s3_recalctimings(svga_t *svga) } } } + svga->ma_latch |= (s3->ma_ext << 16); if (s3->chip >= S3_86C928) { svga->hdisp = svga->hdisp_old; @@ -2830,8 +2859,18 @@ static void s3_recalctimings(svga_t *svga) } } - if ((svga->crtc[0x43] & 0x08) && (s3->bpp == 0)) + if ((svga->crtc[0x43] & 0x08) && (s3->color_16bit == 0) && (s3->chip <= S3_86C805)) { + s3->color_16bit = 1; s3->width = 1024; + } else if (!(svga->crtc[0x43] & 0x08) && (s3->color_16bit == 1) && (s3->chip <= S3_86C805)) { + s3->color_16bit = 0; + if (s3->chip <= S3_86C924) { + if (s3->accel.advfunc_cntl & 4) + s3->width = 1024; + else + s3->width = 640; + } + } if ((svga->gdcreg[5] & 0x40) && (svga->crtc[0x3a] & 0x10)) { svga->fb_only = 1; @@ -3002,35 +3041,17 @@ static void s3_recalctimings(svga_t *svga) } } else { svga->fb_only = 0; - if (!svga->scrblank && svga->attr_palette_enable) { - if (!(svga->gdcreg[6] & 1) && !(svga->attrregs[0x10] & 1)) { /*Text mode*/ - if (svga->seqregs[1] & 8) /*40 column*/ { - svga->render = svga_render_text_40; - } else { - svga->render = svga_render_text_80; - } - } else { - if ((svga->crtc[0x31] & 0x08) && ((svga->gdcreg[5] & 0x60) == 0x00)) { + if ((svga->gdcreg[6] & 1) || (svga->attrregs[0x10] & 1)) { + if ((svga->crtc[0x31] & 0x08) && ((svga->gdcreg[5] & 0x60) == 0x00)) { + if (svga->bpp == 8) { svga->render = svga_render_8bpp_highres; /*Enhanced 4bpp mode, just like the 8bpp mode per spec.*/ if (svga->hdisp <= 1024) s3->width = 1024; - } else { - switch (svga->gdcreg[5] & 0x60) { - case 0x00: - if (svga->seqregs[1] & 8) /*Low res (320)*/ - svga->render = svga_render_4bpp_lowres; - else - svga->render = svga_render_4bpp_highres; - break; - case 0x20: /*4 colours*/ - if (svga->seqregs[1] & 8) /*Low res (320)*/ - svga->render = svga_render_2bpp_lowres; - else - svga->render = svga_render_2bpp_highres; - break; - } } } + } else { + if (s3->chip <= S3_86C924) + s3->width = 1024; } } } @@ -3069,7 +3090,9 @@ static void s3_trio64v_recalctimings(svga_t *svga) if (!svga->rowoffset) svga->rowoffset = 256; svga->lowres = !((svga->gdcreg[5] & 0x40) && (svga->crtc[0x3a] & 0x10)); + if ((svga->gdcreg[5] & 0x40) && (svga->crtc[0x3a] & 0x10)) { + svga->fb_only = 1; switch (svga->bpp) { case 8: svga->render = svga_render_8bpp_highres; @@ -3090,10 +3113,13 @@ static void s3_trio64v_recalctimings(svga_t *svga) svga->render = svga_render_32bpp_highres; break; } - } + } else + svga->fb_only = 0; } else /*Streams mode*/ { + svga->fb_only = 1; + if (s3->streams.buffer_ctrl & 1) svga->ma_latch = s3->streams.pri_fb1 >> 2; else @@ -3179,7 +3205,7 @@ s3_updatemapping(s3_t *s3) svga->banked_mask = 0x7fff; break; } - + if (s3->chip >= S3_86C928) { s3->linear_base = (svga->crtc[0x5a] << 16) | (svga->crtc[0x59] << 24); @@ -3236,12 +3262,8 @@ s3_updatemapping(s3_t *s3) mem_mapping_set_addr(&s3->linear_mapping, s3->linear_base, s3->linear_size); } - if (s3->chip >= S3_TRIO64V) - svga->fb_only = 1; } else { mem_mapping_disable(&s3->linear_mapping); - if (s3->chip >= S3_TRIO64V) - svga->fb_only = 0; } /* Memory mapped I/O. */ @@ -3255,9 +3277,10 @@ s3_updatemapping(s3_t *s3) } else { mem_mapping_enable(&s3->mmio_mapping); } - } else + } else { mem_mapping_disable(&s3->mmio_mapping); - + } + /* New MMIO. */ if (svga->crtc[0x53] & 0x08) mem_mapping_set_addr(&s3->new_mmio_mapping, s3->linear_base + 0x1000000, 0x20000); @@ -3288,12 +3311,12 @@ s3_accel_out(uint16_t port, uint8_t val, void *p) s3_t *s3 = (s3_t *)p; svga_t *svga = &s3->svga; - if (!s3->enable_8514) - return; + if (port >= 0x8000) { + if (!s3->enable_8514) + return; - if (port >= 0x8000) s3_accel_out_fifo(s3, port, val); - else { + } else { switch (port) { case 0x4148: case 0x42e8: @@ -3313,8 +3336,12 @@ s3_accel_out(uint16_t port, uint8_t val, void *p) s3->width = (val & 4) ? 1600 : 800; svga->fullchange = changeframecount; svga_recalctimings(svga); + } else if (s3->chip <= S3_86C805) { + svga->fullchange = changeframecount; + svga_recalctimings(svga); } - s3_updatemapping(s3); + if (s3->chip > S3_86C924) + s3_updatemapping(s3); break; } } @@ -3778,21 +3805,28 @@ s3_accel_in_w(uint16_t port, void *p) if (s3->accel.cmd & 0x1000) temp = (temp >> 8) | (temp << 8); s3_accel_start(8, 1, temp | (temp << 16), 0, s3); - } else + } else { s3_accel_start(1, 1, 0xffffffff, temp | (temp << 16), s3); - } else - s3_accel_start(1, 1, 0xffffffff, temp | (temp << 16), s3); + } + } else { + if (s3->color_16bit) { + s3_accel_start(2, 1, 0xffffffff, temp | (temp << 16), s3); + } else { + s3_accel_start(1, 1, 0xffffffff, temp | (temp << 16), s3); + } + } break; case 0x200: if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) { if (s3->accel.cmd & 0x1000) - temp = (temp >> 8) | (temp << 8); + temp = (temp >> 8) | (temp << 8); s3_accel_start(16, 1, temp | (temp << 16), 0, s3); } else s3_accel_start(2, 1, 0xffffffff, temp | (temp << 16), s3); - } else + } else { s3_accel_start(2, 1, 0xffffffff, temp | (temp << 16), s3); + } break; } } @@ -3941,7 +3975,28 @@ s3_accel_read(uint32_t addr, void *p) if (addr & 0x8000) { temp = s3_accel_in(addr & 0xffff, p); } else if (s3_cpu_dest(s3)) { - temp = s3_accel_in(0xe2e8 + (addr & 3), p); + READ_PIXTRANS_BYTE_MM + + switch (s3->accel.cmd & 0x600) { + case 0x000: + if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { + if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) + s3_accel_start(8, 1, temp | (temp << 8) | (temp << 16) | (temp << 24), 0, s3); + else + s3_accel_start(1, 1, 0xffffffff, temp | (temp << 8) | (temp << 16) | (temp << 24), s3); + } else + s3_accel_start(1, 1, 0xffffffff, temp | (temp << 8) | (temp << 16) | (temp << 24), s3); + break; + case 0x200: + if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { + if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) + s3_accel_start(16, 1, temp | (temp << 8) | (temp << 16) | (temp << 24), 0, s3); + else + s3_accel_start(2, 1, 0xffffffff, temp | (temp << 8) | (temp << 16) | (temp << 24), s3); + } else + s3_accel_start(2, 1, 0xffffffff, temp | (temp << 8) | (temp << 16) | (temp << 24), s3); + break; + } } } @@ -4216,8 +4271,8 @@ polygon_setup(s3_t *s3) } } -#define READ(addr, dat) if (s3->bpp == 0 && !(svga->crtc[0x43] & 0x08)) dat = svga->vram[dword_remap(svga, addr) & s3->vram_mask]; \ - else if (s3->bpp == 1 || (svga->crtc[0x43] & 0x08)) dat = vram_w[dword_remap_w(svga, addr) & (s3->vram_mask >> 1)]; \ +#define READ(addr, dat) if (s3->bpp == 0 && !s3->color_16bit) dat = svga->vram[dword_remap(svga, addr) & s3->vram_mask]; \ + else if (s3->bpp == 1 || s3->color_16bit) dat = vram_w[dword_remap_w(svga, addr) & (s3->vram_mask >> 1)]; \ else if (s3->bpp == 2) dat = svga->vram[dword_remap(svga, addr) & s3->vram_mask]; \ else dat = vram_l[dword_remap_l(svga, addr) & (s3->vram_mask >> 2)]; @@ -4522,12 +4577,12 @@ polygon_setup(s3_t *s3) } -#define WRITE(addr, dat) if (s3->bpp == 0 && !(svga->crtc[0x43] & 0x08)) \ +#define WRITE(addr, dat) if (s3->bpp == 0 && !s3->color_16bit) \ { \ - svga->vram[dword_remap(svga, addr) & s3->vram_mask] = dat; \ + svga->vram[dword_remap(svga, addr) & s3->vram_mask] = dat; \ svga->changedvram[(dword_remap(svga, addr) & s3->vram_mask) >> 12] = changeframecount; \ } \ - else if (s3->bpp == 1 || (svga->crtc[0x43] & 0x08)) \ + else if (s3->bpp == 1 || s3->color_16bit) \ { \ vram_w[dword_remap_w(svga, addr) & (s3->vram_mask >> 1)] = dat; \ svga->changedvram[(dword_remap_w(svga, addr) & (s3->vram_mask >> 1)) >> 11] = changeframecount; \ @@ -5038,33 +5093,33 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ cmd |= 8; } - // SRC-BASE/DST-BASE - if ((s3->accel.multifunc[0xd] >> 4) & 7) { - srcbase = 0x100000 * ((s3->accel.multifunc[0xd] >> 4) & 3); - } else { - srcbase = 0x100000 * ((s3->accel.multifunc[0xe] >> 2) & 3); - } - if ((s3->accel.multifunc[0xd] >> 0) & 7) { - dstbase = 0x100000 * ((s3->accel.multifunc[0xd] >> 0) & 3); - } else { - dstbase = 0x100000 * ((s3->accel.multifunc[0xe] >> 0) & 3); - } - if (s3->bpp == 1 || (svga->crtc[0x43] & 0x08)) { - srcbase >>= 1; - dstbase >>= 1; - } else if (s3->bpp == 3) { - srcbase >>= 2; - dstbase >>= 2; - } + // SRC-BASE/DST-BASE + if ((s3->accel.multifunc[0xd] >> 4) & 7) { + srcbase = 0x100000 * ((s3->accel.multifunc[0xd] >> 4) & 3); + } else { + srcbase = 0x100000 * ((s3->accel.multifunc[0xe] >> 2) & 3); + } + if ((s3->accel.multifunc[0xd] >> 0) & 7) { + dstbase = 0x100000 * ((s3->accel.multifunc[0xd] >> 0) & 3); + } else { + dstbase = 0x100000 * ((s3->accel.multifunc[0xe] >> 0) & 3); + } + if (s3->bpp == 1) { + srcbase >>= 1; + dstbase >>= 1; + } else if (s3->bpp == 3) { + srcbase >>= 2; + dstbase >>= 2; + } - if ((s3->accel.cmd & 0x100) && ((s3_cpu_src(s3) || (s3_cpu_dest(s3))) && !cpu_input)) { + if ((s3->accel.cmd & 0x100) && ((s3_cpu_src(s3) || (s3_cpu_dest(s3)))) && (!cpu_input || (s3_enable_fifo(s3) == 0))) { s3->force_busy = 1; } if (!cpu_input) s3->accel.dat_count = 0; - if (cpu_input && (count <= 4)) { + if (cpu_input && (((s3->accel.multifunc[0xa] & 0xc0) != 0x80) || (!(s3->accel.cmd & 2)))) { if ((s3->bpp == 3) && count == 2) { if (s3->accel.dat_count) { cpu_dat = ((cpu_dat & 0xffff) << 16) | s3->accel.dat_buf; @@ -5075,19 +5130,19 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ s3->accel.dat_count = 1; } } - if (s3->bpp == 1 || (svga->crtc[0x43] & 0x08)) + if (s3->bpp == 1 || s3->color_16bit) count >>= 1; if (s3->bpp == 3) count >>= 2; } - if (s3->bpp == 0 && !(svga->crtc[0x43] & 0x08)) + if (s3->bpp == 0 && !s3->color_16bit) rd_mask &= 0xff; - else if (s3->bpp == 1 || (svga->crtc[0x43] & 0x08)) + else if (s3->bpp == 1 || s3->color_16bit) rd_mask &= 0xffff; - if (s3->bpp == 0 && !(svga->crtc[0x43] & 0x08)) compare &= 0xff; - if (s3->bpp == 1 || (svga->crtc[0x43] & 0x08)) compare &= 0xffff; + if (s3->bpp == 0 && !s3->color_16bit) compare &= 0xff; + if (s3->bpp == 1 || s3->color_16bit) compare &= 0xffff; switch (s3->accel.cmd & 0x600) { @@ -5100,6 +5155,7 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ /*Bit 4 of the Command register is the draw yes bit, which enables writing to memory/reading from memory when enabled. When this bit is disabled, no writing to memory/reading from memory is allowed. (This bit is almost meaningless on the NOP command)*/ + switch (cmd) { case 0: /*NOP (Short Stroke Vectors)*/ @@ -5140,7 +5196,7 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ mix_dat <<= 1; mix_dat |= 1; - if (s3->bpp == 0 && !(svga->crtc[0x43] & 0x08)) cpu_dat >>= 8; + if (s3->bpp == 0) cpu_dat >>= 8; else cpu_dat >>= 16; if (!s3->accel.ssv_len) break; @@ -5173,14 +5229,13 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ if (s3->accel.cur_y_bit12) s3->accel.cy |= ~0xfff; s3->accel.sy = s3->accel.maj_axis_pcnt; - + s3_fifo_slots(s3); - + if (s3_cpu_src(s3)) { return; /*Wait for data from CPU*/ } } - frgd_mix = (s3->accel.frgd_mix >> 5) & 3; bkgd_mix = (s3->accel.bkgd_mix >> 5) & 3; @@ -5199,24 +5254,26 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ case 3: src_dat = 0; break; } - if (((compare_mode == 2 && src_dat != compare) || + if ((compare_mode == 2 && src_dat != compare) || (compare_mode == 3 && src_dat == compare) || - compare_mode < 2)) + compare_mode < 2) { READ((s3->accel.cy * s3->width) + s3->accel.cx, dest_dat); - + MIX - if (s3->accel.cmd & 0x10) { - WRITE((s3->accel.cy * s3->width) + s3->accel.cx, dest_dat); - } + WRITE((s3->accel.cy * s3->width) + s3->accel.cx, dest_dat); } } mix_dat <<= 1; mix_dat |= 1; - if (s3->bpp == 0 && !(svga->crtc[0x43] & 0x08)) cpu_dat >>= 8; - else cpu_dat >>= 16; + if (s3->bpp == 0 && !s3->color_16bit) + cpu_dat >>= 8; + else { + cpu_dat >>= 16; + } + if (!s3->accel.sy) { break; } @@ -5232,7 +5289,6 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ case 0xc0: s3->accel.cy++; break; case 0xe0: s3->accel.cx++; s3->accel.cy++; break; } - s3->accel.sy--; } s3->accel.cur_x = s3->accel.cx; @@ -5263,17 +5319,15 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ case 3: src_dat = 0; break; } - if (((compare_mode == 2 && src_dat != compare) || + if ((compare_mode == 2 && src_dat != compare) || (compare_mode == 3 && src_dat == compare) || - compare_mode < 2)) + compare_mode < 2) { READ((s3->accel.cy * s3->width) + s3->accel.cx, dest_dat); MIX - if (s3->accel.cmd & 0x10) { - WRITE((s3->accel.cy * s3->width) + s3->accel.cx, dest_dat); - } + WRITE((s3->accel.cy * s3->width) + s3->accel.cx, dest_dat); } } @@ -5287,13 +5341,16 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ mix_dat <<= 1; mix_dat |= 1; } - if (s3->bpp == 0 && !(svga->crtc[0x43] & 0x08)) cpu_dat >>= 8; - else cpu_dat >>= 16; + if (s3->bpp == 0 && !s3->color_16bit) + cpu_dat >>= 8; + else { + cpu_dat >>= 16; + } if (!s3->accel.sy) { break; } - + if (s3->accel.err_term >= s3->accel.maj_axis_pcnt) { s3->accel.err_term += s3->accel.destx_distp; /*Step minor axis*/ @@ -5324,7 +5381,6 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ case 0xc0: s3->accel.cy++; break; case 0xe0: s3->accel.cy++; break; } - s3->accel.sy--; } s3->accel.cur_x = s3->accel.cx; @@ -5432,10 +5488,11 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ mix_dat |= 1; } - if (s3->bpp == 0 && !(svga->crtc[0x43] & 0x08)) + if (s3->bpp == 0 && !s3->color_16bit) cpu_dat >>= 8; - else + else { cpu_dat >>= 16; + } if (s3->accel.cmd & 0x20) s3->accel.cx++; @@ -5462,7 +5519,7 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ if (cpu_input) { if (s3->accel.b2e8_pix) { s3->accel.cur_x = s3->accel.cx; - s3->accel.cur_y = s3->accel.cy; + s3->accel.cur_y = s3->accel.cy; } return; } @@ -5694,8 +5751,11 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ mix_dat <<= 1; mix_dat |= 1; - if (s3->bpp == 0 && !(svga->crtc[0x43] & 0x08)) cpu_dat >>= 8; - else cpu_dat >>= 16; + if (s3->bpp == 0 && !s3->color_16bit) + cpu_dat >>= 8; + else { + cpu_dat >>= 16; + } if (s3->accel.cmd & 0x20) { @@ -6384,7 +6444,6 @@ static int vram_sizes[] = 3 /*8 MB*/ }; - static void s3_reset(void *priv) { s3_t *s3 = (s3_t *) priv; @@ -6403,14 +6462,14 @@ static void s3_reset(void *priv) svga->crtc[0x36] = 1 | (3 << 2) | (1 << 4); else svga->crtc[0x36] = 3 | (1 << 4); - + if (s3->chip >= S3_86C928) svga->crtc[0x36] |= (vram_sizes[s3->vram] << 5); else svga->crtc[0x36] |= ((s3->vram == 1) ? 0x00 : 0x20) | 0x80; - + svga->crtc[0x37] = 1 | (7 << 5); - + if (s3->chip >= S3_86C928) svga->crtc[0x37] |= 0x04; @@ -6440,11 +6499,11 @@ static void s3_reset(void *priv) case S3_PHOENIX_86C805: svga->crtc[0x5a] = 0x0a; break; - + case S3_METHEUS_86C928: svga->crtc[0x5a] = 0x0a; break; - + case S3_PARADISE_BAHAMAS64: case S3_PHOENIX_VISION864: case S3_MIROCRYSTAL20SD_864: @@ -6857,8 +6916,10 @@ static void *s3_init(const device_t *info) if (chip >= S3_86C928) svga->crtc[0x36] |= (vram_sizes[vram] << 5); - else - svga->crtc[0x36] |= ((vram == 1) ? 0x00 : 0x20) | 0x80; + else { + svga->crtc[0x36] |= ((vram == 1) ? 0x00 : 0x20) | 0x98; + svga->crtc[0x41] = (vram == 1) ? 0x10 : 0x00; + } svga->crtc[0x37] = 1 | (7 << 5); diff --git a/src/video/vid_sc1148x_ramdac.c b/src/video/vid_sc1148x_ramdac.c index 046624bf4..d23d00efa 100644 --- a/src/video/vid_sc1148x_ramdac.c +++ b/src/video/vid_sc1148x_ramdac.c @@ -6,7 +6,7 @@ * * This file is part of the 86Box distribution. * - * Emulation of Sierra SC11483 and SC11487 RAMDACs. + * Emulation of Sierra SC1148x RAMDACs and clones (e.g.: Winbond). * * Used by the S3 911 and 924 chips. * @@ -43,24 +43,45 @@ sc1148x_ramdac_out(uint16_t addr, int rs2, uint8_t val, void *p, svga_t *svga) { sc1148x_ramdac_t *ramdac = (sc1148x_ramdac_t *) p; uint8_t rs = (addr & 0x03); - rs |= ((!!rs2) << 2); + rs |= ((!!rs2) << 2); + int oldbpp = 0; switch (addr) { case 0x3c6: - if (ramdac->state == 4) { - ramdac->state = 0; - ramdac->ctrl = val; - if (ramdac->ctrl & 0xa0) { - if ((ramdac->ctrl & 0x40) && (ramdac->type == 1)) - svga->bpp = 16; - else - svga->bpp = 15; - } else - svga->bpp = 8; - svga_recalctimings(svga); - return; + switch (ramdac->state) { + case 4: + ramdac->state = 0; + if (val == 0xff) + break; + ramdac->ctrl = val; + ramdac->ctrl = (ramdac->ctrl & ~1) | ((((val >> 2) ^ val) & (val & 0x20)) >> 5); + oldbpp = svga->bpp; + switch (ramdac->type) { + case 0: /* Sierra Mark 2 (11483)*/ + case 2: /* Sierra Mark 2 (11484)*/ + case 3: /* Sierra Mark 1 (11486)*/ + if (val & 0xa0) { + svga->bpp = 15; + } else if (val == 0x00) + svga->bpp = 8; + break; + case 1: /* Sierra Mark 3 (11487)*/ + if (val & 0xa0) { + if (val & 0x40) + svga->bpp = 16; + else + svga->bpp = 15; + } else if (val == 0x00) + svga->bpp = 8; + break; + } + if (oldbpp != svga->bpp) + svga_recalctimings(svga); + return; + default: + svga_out(addr, val, svga); + break; } - ramdac->state = 0; break; case 0x3c7: case 0x3c8: @@ -82,18 +103,21 @@ sc1148x_ramdac_in(uint16_t addr, int rs2, void *p, svga_t *svga) switch (addr) { case 0x3c6: - if (ramdac->state == 4) { - ramdac->state = 0; - ret = ramdac->ctrl; - if (ramdac->type == 1) { - if (((ramdac->ctrl >> 5) == 1) || ((ramdac->ctrl >> 5) == 3)) - ret |= 1; - else - ret &= ~1; - } - return ret; + switch (ramdac->state) { + case 1: + case 2: case 3: + ret = 0x00; + ramdac->state++; + break; + case 4: + ret = ramdac->ctrl; + ret = (ret & ~0x18) | (svga->dac_mask & 0x18); + break; + default: + ret = svga_in(addr, svga); + ramdac->state++; + break; } - ramdac->state++; break; case 0x3c7: case 0x3c8: @@ -150,4 +174,12 @@ const device_t sc11484_nors2_ramdac_device = 0, 2, sc1148x_ramdac_init, sc1148x_ramdac_close, NULL, { NULL }, NULL, NULL +}; + +const device_t sc11486_ramdac_device = +{ + "Sierra SC11486 RAMDAC", + 0, 3, + sc1148x_ramdac_init, sc1148x_ramdac_close, + NULL, { NULL }, NULL, NULL }; \ No newline at end of file diff --git a/src/video/vid_svga.c b/src/video/vid_svga.c index 2544fd361..b1d446829 100644 --- a/src/video/vid_svga.c +++ b/src/video/vid_svga.c @@ -953,7 +953,7 @@ svga_init(const device_t *info, svga_t *svga, void *p, int memsize, svga->translate_address = NULL; svga->ksc5601_english_font_type = 0; - if ((info->flags & DEVICE_PCI) || (info->flags & DEVICE_VLB)) { + if ((info->flags & DEVICE_PCI) || (info->flags & DEVICE_VLB) || (info->flags & DEVICE_MCA)) { mem_mapping_add(&svga->mapping, 0xa0000, 0x20000, svga_read, svga_readw, svga_readl, svga_write, svga_writew, svga_writel, diff --git a/src/video/vid_table.c b/src/video/vid_table.c index 72ca625d4..9b457326e 100644 --- a/src/video/vid_table.c +++ b/src/video/vid_table.c @@ -58,12 +58,13 @@ video_cards[] = { { "ati28800k", &ati28800k_device }, { "ati18800v", &ati18800_vga88_device }, { "ati28800", &ati28800_device }, + { "compaq_ati28800", &compaq_ati28800_device }, +#if defined(DEV_BRANCH) && defined(USE_XL24) + { "ati28800w", &ati28800_wonderxl24_device }, +#endif { "ati18800", &ati18800_device }, #if defined(DEV_BRANCH) && defined(USE_VGAWONDER) { "ati18800w", &ati18800_wonder_device }, -#endif -#if defined(DEV_BRANCH) && defined(USE_XL24) - { "ati28800w", &ati28800_wonderxl24_device }, #endif { "cga", &cga_device }, { "superega", &sega_device }, @@ -74,7 +75,6 @@ video_cards[] = { { "cl_gd5428_isa", &gd5428_isa_device }, { "cl_gd5429_isa", &gd5429_isa_device }, { "cl_gd5434_isa", &gd5434_isa_device }, - { "compaq_ati28800", &compaq_ati28800_device }, { "compaq_cga", &compaq_cga_device }, { "compaq_cga_2", &compaq_cga_2_device }, { "compaq_ega", &cpqega_device }, From 666becd27afda4d280ea80bffe9e50f79deaa7b8 Mon Sep 17 00:00:00 2001 From: Ompronce <88358700+Ompronce@users.noreply.github.com> Date: Mon, 25 Oct 2021 16:18:24 -0400 Subject: [PATCH 53/56] Prevent the selection of certain sound cards on machines without ISA or ISA16 --- src/win/win_settings.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/win/win_settings.c b/src/win/win_settings.c index 4b538f31b..9d729342c 100644 --- a/src/win/win_settings.c +++ b/src/win/win_settings.c @@ -1380,13 +1380,15 @@ win_settings_sound_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) settings_set_check(hdlg, IDC_CHECK_MPU401, temp_mpu401); settings_enable_window(hdlg, IDC_CHECK_MPU401, mpu401_standalone_allow()); settings_enable_window(hdlg, IDC_CONFIGURE_MPU401, mpu401_standalone_allow() && temp_mpu401); + settings_enable_window(hdlg, IDC_CHECK_CMS, (machines[temp_machine].flags & MACHINE_BUS_ISA)); settings_set_check(hdlg, IDC_CHECK_CMS, temp_GAMEBLASTER); - settings_enable_window(hdlg, IDC_CONFIGURE_CMS, temp_GAMEBLASTER); + settings_enable_window(hdlg, IDC_CONFIGURE_CMS, (machines[temp_machine].flags & MACHINE_BUS_ISA) && temp_GAMEBLASTER); + settings_enable_window(hdlg, IDC_CHECK_GUS, (machines[temp_machine].flags & MACHINE_BUS_ISA16)); settings_set_check(hdlg, IDC_CHECK_GUS, temp_GUS); - settings_enable_window(hdlg, IDC_CONFIGURE_GUS, temp_GUS); + settings_enable_window(hdlg, IDC_CONFIGURE_GUS, (machines[temp_machine].flags & MACHINE_BUS_ISA16) && temp_GUS); + settings_enable_window(hdlg, IDC_CHECK_SSI, (machines[temp_machine].flags & MACHINE_BUS_ISA)); settings_set_check(hdlg, IDC_CHECK_SSI, temp_SSI2001); - settings_enable_window(hdlg, IDC_CONFIGURE_SSI, temp_SSI2001); - settings_set_check(hdlg, IDC_CHECK_FLOAT, temp_float); + settings_enable_window(hdlg, IDC_CONFIGURE_SSI, (machines[temp_machine].flags & MACHINE_BUS_ISA) && temp_SSI2001); free(lptsTemp); From ce4814c2a0111807787bb119a72dcedafc3311c0 Mon Sep 17 00:00:00 2001 From: Ompronce <88358700+Ompronce@users.noreply.github.com> Date: Mon, 25 Oct 2021 16:19:31 -0400 Subject: [PATCH 54/56] Prevent the selection of certain sound cards on machines without ISA or ISA16 --- src/sound/snd_cms.c | 2 +- src/sound/snd_gus.c | 2 +- src/sound/snd_ssi2001.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sound/snd_cms.c b/src/sound/snd_cms.c index 631e00c6a..15a065776 100644 --- a/src/sound/snd_cms.c +++ b/src/sound/snd_cms.c @@ -207,7 +207,7 @@ static const device_config_t cms_config[] = const device_t cms_device = { "Creative Music System / Game Blaster", - 0, 0, + DEVICE_ISA, 0, cms_init, cms_close, NULL, { NULL }, NULL, NULL, cms_config diff --git a/src/sound/snd_gus.c b/src/sound/snd_gus.c index fe94ebf2f..003b93b58 100644 --- a/src/sound/snd_gus.c +++ b/src/sound/snd_gus.c @@ -1315,7 +1315,7 @@ static const device_config_t gus_config[] = { const device_t gus_device = { "Gravis UltraSound", - DEVICE_ISA, + DEVICE_ISA | DEVICE_AT, 0, gus_init, gus_close, NULL, { NULL }, diff --git a/src/sound/snd_ssi2001.c b/src/sound/snd_ssi2001.c index 55740fac9..400700131 100644 --- a/src/sound/snd_ssi2001.c +++ b/src/sound/snd_ssi2001.c @@ -118,7 +118,7 @@ static const device_config_t ssi2001_config[] = const device_t ssi2001_device = { "Innovation SSI-2001", - 0, 0, + DEVICE_ISA, 0, ssi2001_init, ssi2001_close, NULL, { NULL }, NULL, NULL, ssi2001_config From 6b9802f0d9a0a9e83138251b379bd201e19fe1b1 Mon Sep 17 00:00:00 2001 From: Ompronce <88358700+Ompronce@users.noreply.github.com> Date: Mon, 25 Oct 2021 16:26:26 -0400 Subject: [PATCH 55/56] Corrected accidental removal of "IDC_CHECK_FLOAT" --- src/win/win_settings.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/win/win_settings.c b/src/win/win_settings.c index 9d729342c..668487d00 100644 --- a/src/win/win_settings.c +++ b/src/win/win_settings.c @@ -1389,6 +1389,7 @@ win_settings_sound_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) settings_enable_window(hdlg, IDC_CHECK_SSI, (machines[temp_machine].flags & MACHINE_BUS_ISA)); settings_set_check(hdlg, IDC_CHECK_SSI, temp_SSI2001); settings_enable_window(hdlg, IDC_CONFIGURE_SSI, (machines[temp_machine].flags & MACHINE_BUS_ISA) && temp_SSI2001); + settings_set_check(hdlg, IDC_CHECK_FLOAT, temp_float); free(lptsTemp); From 355c26efa868df66b2d531beaffc8c4327bae646 Mon Sep 17 00:00:00 2001 From: OBattler Date: Tue, 26 Oct 2021 03:09:20 +0200 Subject: [PATCH 56/56] S3 fixes. --- src/video/vid_s3.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/video/vid_s3.c b/src/video/vid_s3.c index 7385c068a..c04a00b78 100644 --- a/src/video/vid_s3.c +++ b/src/video/vid_s3.c @@ -1438,7 +1438,15 @@ s3_accel_write_fifo(s3_t *s3, uint32_t addr, uint8_t val) if (svga->crtc[0x53] & 0x08) { if ((addr & 0x1ffff) < 0x8000) { - s3_accel_out_fifo(s3, 0xe2e8 + (addr & 3), val); + if (s3->accel.cmd & 0x100) { + if (((s3->accel.multifunc[0xa] & 0xc0) == 0x80) || (s3->accel.cmd & 2)) { + if (((s3->accel.frgd_mix & 0x60) != 0x40) || ((s3->accel.bkgd_mix & 0x60) != 0x40)) + s3_accel_start(8, 1, val | (val << 8) | (val << 16) | (val << 24), 0, s3); + else + s3_accel_start(1, 1, 0xffffffff, val | (val << 8) | (val << 16) | (val << 24), s3); + } else + s3_accel_start(1, 1, 0xffffffff, val | (val << 8) | (val << 16) | (val << 24), s3); + } } else { switch (addr & 0x1ffff) { case 0x83b0: case 0x83b1: case 0x83b2: case 0x83b3: @@ -3041,17 +3049,19 @@ static void s3_recalctimings(svga_t *svga) } } else { svga->fb_only = 0; - if ((svga->gdcreg[6] & 1) || (svga->attrregs[0x10] & 1)) { - if ((svga->crtc[0x31] & 0x08) && ((svga->gdcreg[5] & 0x60) == 0x00)) { - if (svga->bpp == 8) { - svga->render = svga_render_8bpp_highres; /*Enhanced 4bpp mode, just like the 8bpp mode per spec.*/ - if (svga->hdisp <= 1024) - s3->width = 1024; + if (!svga->scrblank && svga->attr_palette_enable) { + if ((svga->gdcreg[6] & 1) || (svga->attrregs[0x10] & 1)) { + if ((svga->crtc[0x31] & 0x08) && ((svga->gdcreg[5] & 0x60) == 0x00)) { + if (svga->bpp == 8) { + svga->render = svga_render_8bpp_highres; /*Enhanced 4bpp mode, just like the 8bpp mode per spec.*/ + if (svga->hdisp <= 1024) + s3->width = 1024; + } } + } else { + if (s3->chip <= S3_86C924) + s3->width = 1024; } - } else { - if (s3->chip <= S3_86C924) - s3->width = 1024; } } }