diff --git a/CMakeLists.txt b/CMakeLists.txt index 8422792bd..d9f92add3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,7 +35,7 @@ if(MUNT_EXTERNAL) endif() project(86Box - VERSION 4.0.2 + VERSION 4.1 DESCRIPTION "Emulator of x86-based systems" HOMEPAGE_URL "https://86box.net" LANGUAGES C CXX) diff --git a/debian/changelog b/debian/changelog index 887066097..29ec6d3af 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -86box (4.0.2) UNRELEASED; urgency=medium +86box (4.1) UNRELEASED; urgency=medium * Bump release. diff --git a/src/acpi.c b/src/acpi.c index 5dee7cefe..1bfa5b2b4 100644 --- a/src/acpi.c +++ b/src/acpi.c @@ -49,6 +49,8 @@ int acpi_enabled = 0; static double cpu_to_acpi; +static int acpi_power_on = 0; + #ifdef ENABLE_ACPI_LOG int acpi_do_log = ENABLE_ACPI_LOG; @@ -136,7 +138,7 @@ acpi_update_irq(acpi_t *dev) if (dev->vendor == VEN_SMC) sci_level |= (dev->regs.pmsts & BM_STS); - if (sci_level) { + if ((dev->regs.pmcntrl & 0x01) && sci_level) { if (dev->irq_mode == 1) pci_set_irq(dev->slot, dev->irq_pin, &dev->irq_state); else if (dev->irq_mode == 2) @@ -777,6 +779,7 @@ acpi_reg_write_common_regs(UNUSED(int size), uint16_t addr, uint8_t val, void *p acpi_t *dev = (acpi_t *) priv; int shift16; int sus_typ; + uint8_t old; addr &= 0x3f; #ifdef ENABLE_ACPI_LOG @@ -803,6 +806,7 @@ acpi_reg_write_common_regs(UNUSED(int size), uint16_t addr, uint8_t val, void *p case 0x04: case 0x05: /* PMCNTRL - Power Management Control Register (IO) */ + old = dev->regs.pmcntrl & 0xff; if ((addr == 0x05) && !!(val & 0x20) && !!(val & 4) && !!(dev->regs.smi_en & 0x00000010) && (dev->vendor == VEN_INTEL_ICH2)) { dev->regs.smi_sts |= 0x00000010; /* ICH2 Specific. Trigger an SMI if SLP_SMI_EN bit is set instead of transistioning to a Sleep State. */ acpi_raise_smi(dev, 1); @@ -848,6 +852,8 @@ acpi_reg_write_common_regs(UNUSED(int size), uint16_t addr, uint8_t val, void *p } } dev->regs.pmcntrl = ((dev->regs.pmcntrl & ~(0xff << shift16)) | (val << shift16)) & 0x3f07 /* 0x3c07 */; + if ((addr == 0x04) && ((old ^ val) & 0x01)) + acpi_update_irq(dev); break; default: @@ -911,7 +917,7 @@ acpi_reg_write_ali(int size, uint16_t addr, uint8_t val, void *priv) dev->regs.gpcntrl = ((dev->regs.gpcntrl & ~(0xff << shift32)) | (val << shift32)) & 0x00000001; break; case 0x30: - /* PM2_CNTRL - Power Management 2 Control Register( */ + /* PM2_CNTRL - Power Management 2 Control Register */ dev->regs.pmcntrl = val & 1; break; default: @@ -1878,7 +1884,10 @@ acpi_reset(void *priv) acpi_t *dev = (acpi_t *) priv; memset(&dev->regs, 0x00, sizeof(acpi_regs_t)); - dev->regs.gpireg[0] = 0xff; + /* PC Chips M773: + - Bit 3: 80-conductor cable on unknown IDE channel (active low) + - Bit 1: 80-conductor cable on unknown IDE channel (active low) */ + dev->regs.gpireg[0] = !strcmp(machine_get_internal_name(), "m773") ? 0xf5 : 0xff; dev->regs.gpireg[1] = 0xff; /* A-Trend ATC7020BXII: - Bit 3: 80-conductor cable on secondary IDE channel (active low) @@ -1912,8 +1921,11 @@ acpi_reset(void *priv) dev->regs.gpi_val |= 0x00000004; } - /* Power on always generates a resume event. */ - dev->regs.pmsts |= 0x8100; + if (acpi_power_on) { + /* Power on always generates a resume event. */ + dev->regs.pmsts |= 0x8100; + acpi_power_on = 0; + } acpi_rtc_status = 0; @@ -2028,7 +2040,9 @@ acpi_init(const device_t *info) acpi_reset(dev); - acpi_enabled = 1; + acpi_enabled = 1; + acpi_power_on = 1; + return dev; } diff --git a/src/cdrom/cdrom.c b/src/cdrom/cdrom.c index 22cefb991..40686f14a 100644 --- a/src/cdrom/cdrom.c +++ b/src/cdrom/cdrom.c @@ -1414,7 +1414,7 @@ cdrom_read_disc_info_toc(cdrom_t *dev, unsigned char *b, unsigned char track, in b[3] = ti.attr; cdrom_log("CD-ROM %i: Returned Toshiba/NEC disc information (type 2) at %02i:%02i.%02i, track=%d, m=%02i,s=%02i,f=%02i, tno=%02x.\n", dev->id, b[0], b[1], b[2], bcd2bin(track), m, s, f, ti.attr); break; - case 3: /*Undocumented on NEC CD-ROM's, from information based on sr_vendor.c from Android's source code*/ + case 3: /* Undocumented on NEC CD-ROM's, from information based on sr_vendor.c from the Linux kernel */ switch (dev->type) { case CDROM_TYPE_NEC_25_10a: case CDROM_TYPE_NEC_38_103: diff --git a/src/chipset/intel_piix.c b/src/chipset/intel_piix.c index e5b90d861..2094eefcf 100644 --- a/src/chipset/intel_piix.c +++ b/src/chipset/intel_piix.c @@ -1172,8 +1172,6 @@ piix_read(int func, int addr, void *priv) if ((func <= dev->max_func) || ((func == 1) && (dev->max_func == 0))) { fregs = (uint8_t *) dev->regs[func]; ret = fregs[addr]; - if ((func == 2) && (addr == 0xff)) - ret |= 0xef; piix_log("PIIX function %i read: %02X from %02X\n", func, ret, addr); } diff --git a/src/chipset/opti499.c b/src/chipset/opti499.c index acac4d87b..f8b878559 100644 --- a/src/chipset/opti499.c +++ b/src/chipset/opti499.c @@ -33,7 +33,7 @@ #include <86box/chipset.h> typedef struct opti499_t { - uint8_t idx, + uint8_t idx; uint8_t regs[256]; uint8_t scratch[2]; } opti499_t; diff --git a/src/chipset/sis_85c4xx.c b/src/chipset/sis_85c4xx.c index 97c989b73..cf4ff42d7 100644 --- a/src/chipset/sis_85c4xx.c +++ b/src/chipset/sis_85c4xx.c @@ -176,6 +176,8 @@ sis_85c4xx_out(uint16_t port, uint8_t val, void *priv) valxor = val ^ dev->regs[rel_reg]; if (rel_reg == 0x19) dev->regs[rel_reg] &= ~val; + else if (rel_reg == 0x00) + dev->regs[rel_reg] = (dev->regs[rel_reg] & 0x1f) | (val & 0xe0); else dev->regs[rel_reg] = val; diff --git a/src/config.c b/src/config.c index 09133c6da..b1ed7094f 100644 --- a/src/config.c +++ b/src/config.c @@ -139,6 +139,8 @@ load_general(void) rctrl_is_lalt = ini_section_get_int(cat, "rctrl_is_lalt", 0); update_icons = ini_section_get_int(cat, "update_icons", 1); + status_icons_fullscreen = !!ini_section_get_int(cat, "status_icons_fullscreen", 0); + window_remember = ini_section_get_int(cat, "window_remember", 0); if (!window_remember && !(vid_resize & 2)) @@ -1782,6 +1784,11 @@ save_general(void) else ini_section_delete_var(cat, "open_dir_usr_path"); + if (status_icons_fullscreen) + ini_section_set_int(cat, "status_icons_fullscreen", status_icons_fullscreen); + else + ini_section_delete_var(cat, "status_icons_fullscreen"); + if (video_framerate != -1) ini_section_set_int(cat, "video_gl_framerate", video_framerate); else diff --git a/src/cpu/808x.c b/src/cpu/808x.c index a24d1f07f..3572f2c9f 100644 --- a/src/cpu/808x.c +++ b/src/cpu/808x.c @@ -35,8 +35,6 @@ #include <86box/ppi.h> #include <86box/timer.h> #include <86box/gdbstub.h> -#include <86box/plat_fallthrough.h> -#include <86box/plat_unused.h> /* Is the CPU 8088 or 8086. */ int is8086 = 0; @@ -48,8 +46,7 @@ uint32_t custom_nmi_vector = 0x00000000; static uint8_t pfq[6]; /* Variables to aid with the prefetch queue operation. */ -static int biu_cycles = 0; -static int pfq_pos = 0; +static int biu_cycles = 0, pfq_pos = 0; /* The IP equivalent of the current prefetch queue position. */ static uint16_t pfq_ip; @@ -60,57 +57,18 @@ static x86seg *_opseg[4]; static int noint = 0; static int in_lock = 0; -static int cpu_alu_op; -static int pfq_size; +static int cpu_alu_op, pfq_size; -static uint32_t cpu_src = 0; -static uint32_t cpu_dest = 0; +static uint32_t cpu_src = 0, cpu_dest = 0; static uint32_t cpu_data = 0; static uint16_t last_addr = 0x0000; static uint32_t *ovr_seg = NULL; -static int prefetching = 1; -static int completed = 1; -static int in_rep = 0; -static int repeating = 0; -static int rep_c_flag = 0; -static int oldc; -static int clear_lock = 0; -static int refresh = 0; -static int cycdiff; - -static int access_code = 0; -static int hlda = 0; -static int not_ready = 0; -static int bus_request_type = 0; -static int pic_data = -1; -static int last_was_code = 0; -static uint16_t mem_data = 0; -static uint32_t mem_seg = 0; -static uint16_t mem_addr = 0; -static int schedule_fetch = 1; -static int pasv = 0; - -#define BUS_OUT 1 -#define BUS_HIGH 2 -#define BUS_WIDE 4 -#define BUS_CODE 8 -#define BUS_IO 16 -#define BUS_MEM 32 -#define BUS_PIC 64 -#define BUS_ACCESS_TYPE (BUS_CODE | BUS_IO | BUS_MEM | BUS_PIC) - -#define BUS_CYCLE (biu_cycles & 3) -#define BUS_CYCLE_T1 biu_cycles = 0 -#define BUS_CYCLE_NEXT biu_cycles = (biu_cycles + 1) & 3 - -enum { - BUS_T1 = 0, - BUS_T2, - BUS_T3, - BUS_T4 -}; +static int prefetching = 1, completed = 1; +static int in_rep = 0, repeating = 0, rep_c_flag = 0; +static int oldc, clear_lock = 0; +static int refresh = 0, cycdiff; /* Various things needed for 8087. */ #define OP_TABLE(name) ops_##name @@ -135,6 +93,7 @@ enum { wait(val, 0); \ } +#if 0 # define CLOCK_CYCLES_FPU(val) \ { \ wait(val, 0); \ @@ -153,6 +112,19 @@ enum { } # define CONCURRENCY_CYCLES(c) fpu_cycles = (c) +#else +# define CLOCK_CYCLES(val) \ + { \ + wait(val, 0); \ + } + +# define CLOCK_CYCLES_FPU(val) \ + { \ + wait(val, 0); \ + } + +# define CONCURRENCY_CYCLES(c) +#endif typedef int (*OpFn)(uint32_t fetchdat); @@ -179,7 +151,7 @@ x808x_log(const char *fmt, ...) # define x808x_log(fmt, ...) #endif -static void pfq_add(void); +static void pfq_add(int c, int add); static void set_pzs(int bits); uint16_t @@ -200,344 +172,50 @@ clock_end(void) int diff = cycdiff - cycles; /* On 808x systems, clock speed is usually crystal frequency divided by an integer. */ - tsc += ((uint64_t) diff * (xt_cpu_multi >> 32ULL)); /* Shift xt_cpu_multi by 32 bits to the right and then multiply. */ + tsc += (uint64_t) diff * ((uint64_t) xt_cpu_multi >> 32ULL); /* Shift xt_cpu_multi by 32 bits to the right and then multiply. */ if (TIMER_VAL_LESS_THAN_VAL(timer_target, (uint32_t) tsc)) timer_process(); } static void -process_timers(void) +fetch_and_bus(int c, int bus) { - clock_end(); - clock_start(); -} + if (refresh > 0) { + /* Finish the current fetch, if any. */ + cycles -= ((4 - (biu_cycles & 3)) & 3); + pfq_add((4 - (biu_cycles & 3)) & 3, 1); + /* Add 4 memory access cycles. */ + cycles -= 4; + pfq_add(4, 0); -static void -cycles_forward(int c) -{ - cycles -= c; - - if (!is286) - process_timers(); -} - -static void -bus_outb(uint16_t port, uint8_t val) -{ - int old_cycles = cycles; - - cycles--; - outb(port, val); - resub_cycles(old_cycles); -} - -static void -bus_outw(uint16_t port, uint16_t val) -{ - int old_cycles = cycles; - - cycles--; - outw(port, val); - resub_cycles(old_cycles); -} - -static uint8_t -bus_inb(uint16_t port) -{ - int old_cycles = cycles; - uint8_t ret; - - cycles--; - ret = inb(port); - resub_cycles(old_cycles); - - return ret; -} - -static uint16_t -bus_inw(uint16_t port) -{ - int old_cycles = cycles; - uint16_t ret; - - cycles--; - ret = inw(port); - resub_cycles(old_cycles); - - return ret; -} - -static void -bus_do_io(int io_type) -{ - last_was_code = 0; - - x808x_log("(%02X) bus_do_io(%02X): %04X\n", opcode, io_type, cpu_state.eaaddr); - - if (io_type & BUS_OUT) { - if (io_type & BUS_WIDE) - bus_outw((uint16_t) cpu_state.eaaddr, AX); - else if (io_type & BUS_HIGH) - bus_outb(((uint16_t) cpu_state.eaaddr + 1) & 0xffff, AH); - else - bus_outb((uint16_t) cpu_state.eaaddr, AL); - } else { - if (io_type & BUS_WIDE) - AX = bus_inw((uint16_t) cpu_state.eaaddr); - else if (io_type & BUS_HIGH) - AH = bus_inb(((uint16_t) cpu_state.eaaddr + 1) & 0xffff); - else - AL = bus_inb((uint16_t) cpu_state.eaaddr); - } - - process_timers(); -} - -static void -bus_writeb(uint32_t seg, uint32_t addr, uint8_t val) -{ - write_mem_b(seg + addr, val); -} - -static void -bus_writew(uint32_t seg, uint32_t addr, uint16_t val) -{ - write_mem_w(seg + addr, val); -} - -static uint8_t -bus_readb(uint32_t seg, uint32_t addr) -{ - uint8_t ret = read_mem_b(seg + addr); - - return ret; -} - -static uint16_t -bus_readw(uint32_t seg, uint32_t addr) -{ - uint16_t ret = read_mem_w(seg + addr); - - return ret; -} - -static void -bus_do_mem(int io_type) -{ - last_was_code = 0; - - if (io_type & BUS_OUT) { - if (io_type & BUS_WIDE) - bus_writew(mem_seg, (uint32_t) mem_addr, mem_data); - else if (io_type & BUS_HIGH) { - if (is186 && !is_nec) - bus_writeb(mem_seg, ((uint32_t) mem_addr) + 1, mem_data >> 8); - else - bus_writeb(mem_seg, (uint32_t) ((mem_addr + 1) & 0xffff), mem_data >> 8); - } else - bus_writeb(mem_seg, (uint32_t) mem_addr, mem_data & 0xff); - } else { - if (io_type & BUS_WIDE) - mem_data = bus_readw(mem_seg, (uint32_t) mem_addr); - else if (io_type & BUS_HIGH) { - if (is186 && !is_nec) - mem_data = (mem_data & 0x00ff) | (((uint16_t) bus_readb(mem_seg, ((uint32_t) mem_addr) + 1)) << 8); - else - mem_data = (mem_data & 0x00ff) | (((uint16_t) bus_readb(mem_seg, (uint32_t) ((mem_addr + 1) & 0xffff))) << 8); - } else - mem_data = (mem_data & 0xff00) | ((uint16_t) bus_readb(mem_seg, (uint32_t) mem_addr)); - } -} - -static void -run_bus_cycle(int io_type) -{ - int do_bus_access = (io_type != 0) && (!(io_type & BUS_CODE) || schedule_fetch); - - x808x_log("[%04X:%04X] %02X bus access %02X (%i)\n", CS, cpu_state.pc, opcode, io_type, do_bus_access); - - if (do_bus_access) { - if (not_ready > 0) { - x808x_log("[%04X:%04X] %02X TW x%i\n", CS, cpu_state.pc, opcode, not_ready); - cycles_forward(not_ready); - not_ready = 0; - } - - switch (BUS_CYCLE) { - case BUS_T1: - access_code = !!(io_type & BUS_CODE); - break; - case BUS_T2: - switch (io_type & BUS_ACCESS_TYPE) { - case BUS_IO: - if (io_type & BUS_OUT) - bus_do_io(io_type); - break; - case BUS_MEM: - if (io_type & BUS_OUT) - bus_do_mem(io_type); - break; - default: - break; - } - break; - case BUS_T3: - switch (io_type & BUS_ACCESS_TYPE) { - case BUS_CODE: - pfq_add(); - last_was_code = 1; - break; - case BUS_IO: - if (!(io_type & BUS_OUT)) - bus_do_io(io_type); - break; - case BUS_MEM: - if (!(io_type & BUS_OUT)) - bus_do_mem(io_type); - break; - case BUS_PIC: - pic_data = pic_irq_ack(); - last_was_code = 0; - break; - default: - break; - } - break; - default: - break; - } - } -} - -static void -run_dma_cycle(int idle) -{ - if (not_ready > 0) { - /* Subtract one not ready cycle. */ - not_ready--; - } else if (hlda > 0) { - hlda--; - /* DMAWAIT is two cycles in, the actual wait states - are inserted with one cycle of delay. */ - if (hlda == 0) { - /* Deassert READY. */ - not_ready = 6; - } - } else if ((refresh > 0) && (in_lock == 0) && (idle || (BUS_CYCLE >= BUS_T3))) { - /* Refresh pending and it's either non-bus cycle or T3-T4, - raise HLDA. */ - hlda = 2; - /* Decrease the refresh count. */ refresh--; } -} -static void -cycles_idle(int c) -{ - for (int d = 0; d < c; d++) { - x808x_log("[%04X:%04X] %02X TI\n", CS, cpu_state.pc, opcode); - - cycles_forward(1); - run_dma_cycle(1); + pfq_add(c, !bus); + if (bus < 2) { + clock_end(); + clock_start(); } } -static void -pfq_schedule(int on) -{ - schedule_fetch = on && prefetching && (pfq_pos < pfq_size); -} - -static void -cycles_biu(int bus, int init) -{ - /* T1, T2 = Nothing, T3 = Start and schedule, T4 = Nothing */ - pasv = (bus || ((BUS_CYCLE == BUS_T1) && schedule_fetch)) ? 0 : 1; - - x808x_log("cycles_biu(%i, %i): %i, %i, %i, %i\n", bus, init, prefetching, pfq_pos, pfq_size, BUS_CYCLE); - if (bus) { - /* CPU wants non-code bus access. */ - if (init) { - if (schedule_fetch) { - switch (BUS_CYCLE) { - case BUS_T1: - case BUS_T2: - BUS_CYCLE_T1; /* Simply abort the prefetch before actual scheduling, no penalty. */ - break; - case BUS_T3: - case BUS_T4: - cycles_idle(5 - BUS_CYCLE); /* Leftover BIU cycles + 2 idle cycles. */ - BUS_CYCLE_T1; /* Abort the prefetch. */ - break; - - default: - break; - } - - pfq_schedule(0); - access_code = 0; - } - } - - run_bus_cycle(bus_request_type); - } else { - /* CPU wants idle or code bus access. */ - if (schedule_fetch) - run_bus_cycle(BUS_CODE); - } - - if (BUS_CYCLE == BUS_T2) - pfq_schedule(1); - - run_dma_cycle(pasv); - - BUS_CYCLE_NEXT; -} - -static void -cycles_pasv(void) -{ - pfq_schedule(1); - - run_dma_cycle(1); -} - -/* Bus: - 0 CPU cycles without bus access. - 1 CPU cycle T1-T4, bus access. - 2 CPU cycle Tw (wait state). - 3 CPU cycle Ti (idle). - */ static void wait(int c, int bus) { - if (c < 0) - pclog("Negative cycles: %i!\n", c); - - x808x_log("[%04X:%04X] %02X %i cycles (%i)\n", CS, cpu_state.pc, opcode, c, bus); - - for (int d = 0; d < c; d++) { - x808x_log("[%04X:%04X] %02X cycle %i BIU\n", CS, cpu_state.pc, opcode, d); - if (!bus && !schedule_fetch && (BUS_CYCLE == BUS_T1)) - cycles_pasv(); - else - cycles_biu(bus, !d); - x808x_log("[%04X:%04X] %02X cycle %i EU\n", CS, cpu_state.pc, opcode, d); - cycles_forward(1); - } + cycles -= c; + fetch_and_bus(c, bus); } -/* This is for external subtraction of cycles, ie. wait states. */ +/* This is for external subtraction of cycles. */ void sub_cycles(int c) { - if (is286) - cycles -= c; - else { - if (c > 0) - cycles_idle(c); - } + if (c <= 0) + return; + + cycles -= c; + + if (!is286) + fetch_and_bus(c, 2); } void @@ -547,14 +225,9 @@ resub_cycles(int old_cycles) if (old_cycles > cycles) { cyc_diff = old_cycles - cycles; - - for (int i = 0; i < cyc_diff; i++) { - if (not_ready > 0) - not_ready--; - } + cycles = old_cycles; + sub_cycles(cyc_diff); } - - process_timers(); } #undef readmemb @@ -565,55 +238,53 @@ resub_cycles(int old_cycles) static void cpu_io(int bits, int out, uint16_t port) { + int old_cycles = cycles; + if (out) { + wait(4, 1); if (bits == 16) { if (is8086 && !(port & 1)) { - bus_request_type = BUS_IO | BUS_OUT | BUS_WIDE; - wait(4, 1); + old_cycles = cycles; + outw(port, AX); } else { - bus_request_type = BUS_IO | BUS_OUT; - wait(4, 1); - pfq_schedule(0); - bus_request_type = BUS_IO | BUS_OUT | BUS_HIGH; wait(4, 1); + old_cycles = cycles; + outb(port++, AL); + outb(port, AH); } } else { - bus_request_type = BUS_IO | BUS_OUT; - wait(4, 1); + old_cycles = cycles; + outb(port, AL); } } else { + wait(4, 1); if (bits == 16) { if (is8086 && !(port & 1)) { - bus_request_type = BUS_IO | BUS_WIDE; - wait(4, 1); + old_cycles = cycles; + AX = inw(port); } else { - bus_request_type = BUS_IO; - wait(4, 1); - pfq_schedule(0); - bus_request_type = BUS_IO | BUS_HIGH; wait(4, 1); + old_cycles = cycles; + AL = inb(port++); + AH = inb(port); } } else { - bus_request_type = BUS_IO; - wait(4, 1); + old_cycles = cycles; + AL = inb(port); } } - bus_request_type = 0; + resub_cycles(old_cycles); } /* Reads a byte from the memory and advances the BIU. */ static uint8_t -readmemb(uint32_t s, uint16_t a) +readmemb(uint32_t a) { uint8_t ret; - mem_seg = s; - mem_addr = a; - bus_request_type = BUS_MEM; wait(4, 1); - ret = mem_data & 0xff; - bus_request_type = 0; + ret = read_mem_b(a); return ret; } @@ -627,8 +298,6 @@ readmembf(uint32_t a) a = cs + (a & 0xffff); ret = read_mem_b(a); - last_was_code = 1; - return ret; } @@ -638,20 +307,14 @@ readmemw(uint32_t s, uint16_t a) { uint16_t ret; - mem_seg = s; - mem_addr = a; - if (is8086 && !(a & 1)) { - bus_request_type = BUS_MEM | BUS_WIDE; - wait(4, 1); - } else { - bus_request_type = BUS_MEM | BUS_HIGH; - wait(4, 1); - pfq_schedule(0); - bus_request_type = BUS_MEM; + wait(4, 1); + if (is8086 && !(a & 1)) + ret = read_mem_w(s + a); + else { wait(4, 1); + ret = read_mem_b(s + a); + ret |= read_mem_b(s + ((is186 && !is_nec) ? (a + 1) : (a + 1) & 0xffff)) << 8; } - ret = mem_data; - bus_request_type = 0; return ret; } @@ -663,8 +326,6 @@ readmemwf(uint16_t a) ret = read_mem_w(cs + (a & 0xffff)); - last_was_code = 1; - return ret; } @@ -674,7 +335,7 @@ readmem(uint32_t s) if (opcode & 1) return readmemw(s, cpu_state.eaaddr); else - return (uint16_t) readmemb(s, cpu_state.eaaddr); + return (uint16_t) readmemb(s + cpu_state.eaaddr); } static uint32_t @@ -696,8 +357,6 @@ readmemq(uint32_t s, uint16_t a) temp = (uint64_t) (readmeml(s, a + 4)) << 32; temp |= readmeml(s, a); - last_was_code = 0; - return temp; } @@ -707,15 +366,8 @@ writememb(uint32_t s, uint32_t a, uint8_t v) { uint32_t addr = s + a; - // if (CS == DEBUG_SEG) - // fatal("writememb(%08X, %08X, %02X)\n", s, a, v); - - mem_seg = s; - mem_addr = a; - mem_data = v; - bus_request_type = BUS_MEM | BUS_OUT; wait(4, 1); - bus_request_type = 0; + write_mem_b(addr, v); if ((addr >= 0xf0000) && (addr <= 0xfffff)) last_addr = addr & 0xffff; @@ -727,20 +379,15 @@ writememw(uint32_t s, uint32_t a, uint16_t v) { uint32_t addr = s + a; - mem_seg = s; - mem_addr = a; - mem_data = v; - if (is8086 && !(a & 1)) { - bus_request_type = BUS_MEM | BUS_OUT | BUS_WIDE; - wait(4, 1); - } else { - bus_request_type = BUS_MEM | BUS_OUT | BUS_HIGH; - wait(4, 1); - pfq_schedule(0); - bus_request_type = BUS_MEM | BUS_OUT; + wait(4, 1); + if (is8086 && !(a & 1)) + write_mem_w(addr, v); + else { + write_mem_b(addr, v & 0xff); wait(4, 1); + addr = s + ((is186 && !is_nec) ? (a + 1) : ((a + 1) & 0xffff)); + write_mem_b(addr, v >> 8); } - bus_request_type = 0; if ((addr >= 0xf0000) && (addr <= 0xfffff)) last_addr = addr & 0xffff; @@ -773,55 +420,38 @@ static void pfq_write(void) { uint16_t tempw; - /* Byte fetch on odd addres on 8086 to simulate the HL toggle. */ - int fetch_word = is8086 && !(pfq_ip & 1); - if (fetch_word && (pfq_pos < (pfq_size - 1))) { + if (is8086 && (pfq_pos < (pfq_size - 1))) { /* The 8086 fetches 2 bytes at a time, and only if there's at least 2 bytes free in the queue. */ tempw = readmemwf(pfq_ip); *(uint16_t *) &(pfq[pfq_pos]) = tempw; - pfq_ip = (pfq_ip + 2) & 0xffff; + pfq_ip += 2; pfq_pos += 2; - - if (pfq_pos >= (pfq_size - 1)) - pfq_schedule(0); - } else if (!fetch_word && (pfq_pos < pfq_size)) { + } else if (!is8086 && (pfq_pos < pfq_size)) { /* The 8088 fetches 1 byte at a time, and only if there's at least 1 byte free in the queue. */ pfq[pfq_pos] = readmembf(pfq_ip); - pfq_ip = (pfq_ip + 1) & 0xffff; + pfq_ip++; pfq_pos++; - - if (pfq_pos >= pfq_size) - pfq_schedule(0); } - - if (pfq_pos >= pfq_size) - pfq_pos = pfq_size; } static uint8_t pfq_read(void) { - uint8_t temp; + uint8_t temp, i; temp = pfq[0]; - for (int i = 0; i < (pfq_size - 1); i++) + for (i = 0; i < (pfq_size - 1); i++) pfq[i] = pfq[i + 1]; pfq_pos--; - if (pfq_pos < 0) - pfq_pos = 0; cpu_state.pc = (cpu_state.pc + 1) & 0xffff; return temp; } /* Fetches a byte from the prefetch queue, or from memory if the queue has - been drained. - - Cycles: 1 If fetching from the queue; - (4 - (biu_cycles & 3)) If fetching from the bus - fetch into the queue; - 1 If fetching from the bus - delay. */ + been drained. */ static uint8_t pfq_fetchb_common(void) { @@ -831,8 +461,7 @@ pfq_fetchb_common(void) /* Reset prefetch queue internal position. */ pfq_ip = cpu_state.pc; /* Fill the queue. */ - while (pfq_pos == 0) - wait(1, 0); + wait(4 - (biu_cycles & 3), 0); } /* Fetch. */ @@ -840,7 +469,6 @@ pfq_fetchb_common(void) return temp; } -/* The timings are above. */ static uint8_t pfq_fetchb(void) { @@ -876,36 +504,26 @@ pfq_fetch(void) /* Adds bytes to the prefetch queue based on the instruction's cycle count. */ static void -pfq_add(void) +pfq_add(int c, int add) { - if (prefetching && (pfq_pos < pfq_size)) - pfq_write(); + int d; + + if ((c <= 0) || (pfq_pos >= pfq_size)) + return; + + for (d = 0; d < c; d++) { + biu_cycles = (biu_cycles + 1) & 0x03; + if (prefetching && add && (biu_cycles == 0x00)) + pfq_write(); + } } /* Clear the prefetch queue - called on reset and on anything that affects either CS or IP. */ static void pfq_clear(void) { - pfq_pos = 0; - - BUS_CYCLE_T1; -} - -static void -pfq_do_suspend(void) -{ - while (BUS_CYCLE != BUS_T1) - wait(1, 0); - wait(1, 0); - pfq_schedule(0); - prefetching = 0; -} - -static void -pfq_suspend(void) -{ - pfq_do_suspend(); - pfq_clear(); + pfq_pos = 0; + prefetching = 0; } static void @@ -925,7 +543,7 @@ load_seg(uint16_t seg, x86seg *s) void reset_808x(int hard) { - BUS_CYCLE_T1; + biu_cycles = 0; in_rep = 0; in_lock = 0; completed = 1; @@ -944,7 +562,7 @@ reset_808x(int hard) _opseg[2] = &cpu_state.seg_ss; _opseg[3] = &cpu_state.seg_ds; - pfq_size = is8086 ? 6 : 4; + pfq_size = (is8086) ? 6 : 4; pfq_clear(); } @@ -954,25 +572,11 @@ reset_808x(int hard) cpu_state.flags |= MD_FLAG; rammask = 0xfffff; - pasv = 0; - - cpu_alu_op = 0; + prefetching = 1; + cpu_alu_op = 0; use_custom_nmi_vector = 0x00; custom_nmi_vector = 0x00000000; - - access_code = 0; - hlda = 0; - not_ready = 0; - bus_request_type = 0; - pic_data = -1; - last_was_code = 0; - mem_data = 0; - mem_seg = 0; - mem_addr = 0; - - prefetching = 1; - pfq_schedule(1); } static void @@ -980,7 +584,6 @@ set_ip(uint16_t new_ip) { pfq_ip = cpu_state.pc = new_ip; prefetching = 1; - pfq_schedule(1); } /* Memory refresh read - called by reads and writes on DMA channel 0. */ @@ -1023,11 +626,12 @@ do_mod_rm(void) if (cpu_mod == 3) return; - wait(2, 0); + wait(1, 0); if ((rmdat & 0xc7) == 0x06) { + wait(1, 0); cpu_state.eaaddr = pfq_fetchw(); easeg = ovr_seg ? *ovr_seg : ds; - wait(2, 0); + wait(1, 0); return; } else switch (cpu_rm) { @@ -1039,24 +643,17 @@ do_mod_rm(void) case 2: wait(3, 0); break; - - default: - break; } cpu_state.eaaddr = (*mod1add[0][cpu_rm]) + (*mod1add[1][cpu_rm]); easeg = ovr_seg ? *ovr_seg : *mod1seg[cpu_rm]; switch (rmdat & 0xc0) { case 0x40: - wait(2, 0); + wait(3, 0); cpu_state.eaaddr += sign_extend(pfq_fetchb()); - wait(1, 0); break; case 0x80: - wait(2, 0); + wait(3, 0); cpu_state.eaaddr += pfq_fetchw(); - wait(1, 0); - break; - default: break; } cpu_state.eaaddr &= 0xffff; @@ -1080,7 +677,7 @@ geteab(void) if (cpu_mod == 3) return (getr8(cpu_rm)); - return readmemb(easeg, cpu_state.eaaddr); + return readmemb(easeg + cpu_state.eaaddr); } /* Reads a word from the effective address. */ @@ -1124,7 +721,7 @@ read_ea(int memory_only, int bits) if (bits == 16) cpu_data = readmemw(easeg, cpu_state.eaaddr); else - cpu_data = readmemb(easeg, cpu_state.eaaddr); + cpu_data = readmemb(easeg + cpu_state.eaaddr); return; } if (!memory_only) { @@ -1142,7 +739,7 @@ read_ea2(int bits) if (bits == 16) cpu_data = readmemw(easeg, cpu_state.eaaddr); else - cpu_data = readmemb(easeg, cpu_state.eaaddr); + cpu_data = readmemb(easeg + cpu_state.eaaddr); } /* Writes a byte to the effective address. */ @@ -1151,10 +748,8 @@ seteab(uint8_t val) { if (cpu_mod == 3) { setr8(cpu_rm, val); - } else { - wait(1, 0); + } else writememb(easeg, cpu_state.eaaddr, val); - } } /* Writes a word to the effective address. */ @@ -1163,10 +758,8 @@ seteaw(uint16_t val) { if (cpu_mod == 3) cpu_state.regs[cpu_rm].w = val; - else { - wait(1, 0); + else writememw(easeg, cpu_state.eaaddr, val); - } } static void @@ -1222,129 +815,206 @@ pop(void) } static void -nearcall(uint16_t new_ip) +access(int num, int bits) { - uint16_t ret_ip = cpu_state.pc & 0xffff; - - wait(1, 0); - set_ip(new_ip); - pfq_clear(); - wait(3, 0); - push(&ret_ip); -} - -static void -farcall2(uint16_t new_cs, uint16_t new_ip) -{ - wait(3, 0); - push(&CS); - load_cs(new_cs); - wait(2, 0); - nearcall(new_ip); + switch (num) { + case 0: + case 61: + case 63: + case 64: + case 67: + case 69: + case 71: + case 72: + default: + break; + case 1: + case 6: + case 7: + case 8: + case 9: + case 17: + case 20: + case 21: + case 24: + case 28: + case 47: + case 48: + case 49: + case 50: + case 51: + case 55: + case 56: + case 62: + case 66: + case 68: + wait(1, 0); + break; + case 3: + case 11: + case 15: + case 22: + case 23: + case 25: + case 26: + case 35: + case 44: + case 45: + case 46: + case 52: + case 53: + case 54: + wait(2, 0); + break; + case 16: + case 18: + case 19: + case 27: + case 32: + case 37: + case 42: + wait(3, 0); + break; + case 10: + case 12: + case 13: + case 14: + case 29: + case 30: + case 33: + case 34: + case 39: + case 41: + case 60: + wait(4, 0); + break; + case 4: + case 70: + wait(5, 0); + break; + case 31: + case 38: + case 40: + wait(6, 0); + break; + case 5: + if (opcode == 0xcc) + wait(7, 0); + else + wait(4, 0); + break; + case 36: + wait(1, 0); + pfq_clear(); + wait(1, 0); + if (cpu_mod != 3) + wait(1, 0); + wait(3, 0); + break; + case 43: + wait(2, 0); + pfq_clear(); + wait(1, 0); + break; + case 57: + if (cpu_mod != 3) + wait(2, 0); + wait(4, 0); + break; + case 58: + if (cpu_mod != 3) + wait(1, 0); + wait(4, 0); + break; + case 59: + wait(2, 0); + pfq_clear(); + if (cpu_mod != 3) + wait(1, 0); + wait(3, 0); + break; + case 65: + wait(1, 0); + pfq_clear(); + wait(2, 0); + if (cpu_mod != 3) + wait(1, 0); + break; + } } /* Calls an interrupt. */ -/* The INTR microcode routine. */ static void -intr_routine(uint16_t intr, int skip_first) +interrupt(uint16_t addr) { - uint16_t vector = intr * 4; - uint16_t tempf = cpu_state.flags & (is_nec ? 0x8fd7 : 0x0fd7); - uint16_t new_cs; - uint16_t new_ip; + uint16_t old_cs, old_ip; + uint16_t new_cs, new_ip; + uint16_t tempf; - if (!skip_first) - wait(1, 0); - wait(2, 0); - - cpu_state.eaaddr = vector & 0xffff; - new_ip = readmemw(0, cpu_state.eaaddr); + addr <<= 2; + cpu_state.eaaddr = addr; + old_cs = CS; + access(5, 16); + new_ip = readmemw(0, cpu_state.eaaddr); wait(1, 0); cpu_state.eaaddr = (cpu_state.eaaddr + 2) & 0xffff; - new_cs = readmemw(0, cpu_state.eaaddr); - - pfq_do_suspend(); - wait(2, 0); - push(&tempf); - cpu_state.flags &= ~(I_FLAG | T_FLAG); - wait(1, 0); - - farcall2(new_cs, new_ip); -} - -static void -sw_int(uint16_t intr) -{ - uint16_t vector = intr * 4; - uint16_t tempf = cpu_state.flags & (is_nec ? 0x8fd7 : 0x0fd7); - uint16_t new_cs; - uint16_t new_ip; - uint16_t old_ip; - - wait(3, 0); - cpu_state.eaaddr = vector & 0xffff; - new_ip = readmemw(0, cpu_state.eaaddr); - wait(1, 0); - cpu_state.eaaddr = (cpu_state.eaaddr + 2) & 0xffff; - new_cs = readmemw(0, cpu_state.eaaddr); - pfq_do_suspend(); - wait(2, 0); - push(&tempf); - cpu_state.flags &= ~(I_FLAG | T_FLAG); - - /* FARCALL2 */ - wait(4, 0); - push(&CS); - load_cs(new_cs); - wait(1, 0); - - /* NEARCALL */ - old_ip = cpu_state.pc & 0xffff; - wait(2, 0); - set_ip(new_ip); + access(6, 16); + new_cs = readmemw(0, cpu_state.eaaddr); + prefetching = 0; pfq_clear(); - wait(3, 0); + ovr_seg = NULL; + access(39, 16); + tempf = cpu_state.flags & (is_nec ? 0x8fd7 : 0x0fd7); + push(&tempf); + cpu_state.flags &= ~(I_FLAG | T_FLAG); + access(40, 16); + push(&old_cs); + old_ip = cpu_state.pc; + load_cs(new_cs); + access(68, 16); + set_ip(new_ip); + access(41, 16); push(&old_ip); } -static void -int3(void) -{ - wait(4, 0); - intr_routine(3, 0); -} - void interrupt_808x(uint16_t addr) { - intr_routine(addr, 0); + interrupt(addr); } static void custom_nmi(void) { - uint16_t tempf = cpu_state.flags & (is_nec ? 0x8fd7 : 0x0fd7); - uint16_t new_cs; - uint16_t new_ip; - - wait(1, 0); - wait(2, 0); + uint16_t old_cs, old_ip; + uint16_t new_cs, new_ip; + uint16_t tempf; cpu_state.eaaddr = 0x0002; + old_cs = CS; + access(5, 16); (void) readmemw(0, cpu_state.eaaddr); new_ip = custom_nmi_vector & 0xffff; wait(1, 0); cpu_state.eaaddr = (cpu_state.eaaddr + 2) & 0xffff; + access(6, 16); (void) readmemw(0, cpu_state.eaaddr); - new_cs = custom_nmi_vector >> 16; - - pfq_do_suspend(); - wait(2, 0); + new_cs = custom_nmi_vector >> 16; + prefetching = 0; + pfq_clear(); + ovr_seg = NULL; + access(39, 16); + tempf = cpu_state.flags & (is_nec ? 0x8fd7 : 0x0fd7); push(&tempf); cpu_state.flags &= ~(I_FLAG | T_FLAG); - wait(1, 0); - - farcall2(new_cs, new_ip); + access(40, 16); + push(&old_cs); + old_ip = cpu_state.pc; + load_cs(new_cs); + access(68, 16); + set_ip(new_ip); + access(41, 16); + push(&old_ip); } static int @@ -1357,18 +1027,6 @@ irq_pending(void) return temp; } -static int -bus_pic_ack(void) -{ - int old_in_lock = in_lock; - - in_lock = 1; - bus_request_type = BUS_PIC; - wait(4, 1); - in_lock = old_in_lock; - return pic_data; -} - static void check_interrupts(void) { @@ -1376,17 +1034,15 @@ check_interrupts(void) if (irq_pending()) { if ((cpu_state.flags & T_FLAG) && !noint) { - wait(2, 0); - intr_routine(1, 0); + interrupt(1); return; } if (nmi && nmi_enable && nmi_mask) { nmi_enable = 0; - wait(2, 0); if (use_custom_nmi_vector) custom_nmi(); else - intr_routine(2, 0); + interrupt(2); #ifndef OLD_NMI_BEHAVIOR nmi = 0; #endif @@ -1396,75 +1052,65 @@ check_interrupts(void) repeating = 0; completed = 1; ovr_seg = NULL; - wait(4, 0); + wait(3, 0); /* ACK to PIC */ - temp = bus_pic_ack(); + temp = pic_irq_ack(); + wait(4, 1); wait(1, 0); /* ACK to PIC */ - temp = bus_pic_ack(); + temp = pic_irq_ack(); + wait(4, 1); wait(1, 0); in_lock = 0; clear_lock = 0; - if (BUS_CYCLE != BUS_T3) - wait(1, 0); - wait(5, 0); + wait(1, 0); /* Here is where temp should be filled, but we cheat. */ + wait(3, 0); opcode = 0x00; - intr_routine(temp, 0); + interrupt(temp); } } } -static void -rep_end(void) -{ - repeating = 0; - in_rep = 0; - completed = 1; -} - static int -rep_start(void) +rep_action(int bits) { - if (!repeating) { - wait(2, 0); + uint16_t t; - if (in_rep != 0) { - if (CX == 0) { - wait(4, 0); - rep_end(); - return 0; - } else - wait(7, 0); - } + if (in_rep == 0) + return 0; + wait(2, 0); + t = CX; + if (irq_pending() && (repeating != 0)) { + access(71, bits); + pfq_clear(); + if (is_nec && (ovr_seg != NULL)) + set_ip(cpu_state.pc - 3); + else + set_ip(cpu_state.pc - 2); + t = 0; } - - completed = 1; - return 1; -} - -static void -rep_interrupt(void) -{ - pfq_do_suspend(); - wait(4, 0); - pfq_clear(); - - if (is_nec && (ovr_seg != NULL)) - set_ip((cpu_state.pc - 3) & 0xffff); - else - set_ip((cpu_state.pc - 2) & 0xffff); - - rep_end(); + if (t == 0) { + wait(1, 0); + completed = 1; + repeating = 0; + return 1; + } + --CX; + completed = 0; + wait(2, 0); + if (!repeating) + wait(2, 0); + return 0; } static uint16_t jump(uint16_t delta) { uint16_t old_ip; - wait(1, 0); - pfq_suspend(); - cycles_idle(1); + access(67, 8); + pfq_clear(); + wait(5, 0); old_ip = cpu_state.pc; set_ip((cpu_state.pc + delta) & 0xffff); return old_ip; @@ -1486,6 +1132,8 @@ jump_near(void) static void jcc(uint8_t opcode, int cond) { + /* int8_t offset; */ + wait(1, 0); cpu_data = pfq_fetchb(); wait(1, 0); @@ -1614,14 +1262,14 @@ alu_op(int bits) case 2: if (cpu_state.flags & C_FLAG) cpu_src++; - fallthrough; + /* Fall through. */ case 0: add(bits); break; case 3: if (cpu_state.flags & C_FLAG) cpu_src++; - fallthrough; + /* Fall through. */ case 5: case 7: sub(bits); @@ -1632,9 +1280,6 @@ alu_op(int bits) case 6: bitwise(bits, (cpu_dest ^ cpu_src)); break; - - default: - break; } } @@ -1655,11 +1300,10 @@ mul(uint16_t a, uint16_t b) { int negate = 0; int bit_count = 8; - int carry; + int carry, i; uint16_t high_bit = 0x80; uint16_t size_mask; - uint16_t c; - uint16_t r; + uint16_t c, r; size_mask = (1 << bit_count) - 1; @@ -1700,7 +1344,7 @@ mul(uint16_t a, uint16_t b) a &= size_mask; carry = (a & 1) != 0; a >>= 1; - for (int i = 0; i < bit_count; ++i) { + for (i = 0; i < bit_count; ++i) { wait(7, 0); if (carry) { cpu_src = c; @@ -1761,7 +1405,7 @@ set_pzs(int bits) } static void -set_co_mul(UNUSED(int bits), int carry) +set_co_mul(int bits, int carry) { set_cf(carry); set_of(carry); @@ -1774,11 +1418,10 @@ set_co_mul(UNUSED(int bits), int carry) static int x86_div(uint16_t l, uint16_t h) { - int bit_count = 8; + int b, bit_count = 8; int negative = 0; int dividend_negative = 0; - int size_mask; - int carry; + int size_mask, carry; uint16_t r; if (opcode & 1) { @@ -1815,14 +1458,14 @@ x86_div(uint16_t l, uint16_t h) if (h >= cpu_src) { if (opcode != 0xd4) wait(1, 0); - intr_routine(0, 0); + interrupt(0); return 0; } if (opcode != 0xd4) wait(1, 0); wait(2, 0); carry = 1; - for (int b = 0; b < bit_count; ++b) { + for (b = 0; b < bit_count; ++b) { r = (l << 1) + (carry ? 1 : 0); carry = top_bit(l, bit_count); l = r; @@ -1851,7 +1494,7 @@ x86_div(uint16_t l, uint16_t h) if (top_bit(l, bit_count)) { if (cpu_mod == 3) wait(1, 0); - intr_routine(0, 0); + interrupt(0); return 0; } wait(7, 0); @@ -1893,21 +1536,10 @@ lods(int bits) if (bits == 16) cpu_data = readmemw((ovr_seg ? *ovr_seg : ds), cpu_state.eaaddr); else - cpu_data = readmemb((ovr_seg ? *ovr_seg : ds), cpu_state.eaaddr); + cpu_data = readmemb((ovr_seg ? *ovr_seg : ds) + cpu_state.eaaddr); SI = string_increment(bits); } -static void -lods_di(int bits) -{ - cpu_state.eaaddr = DI; - if (bits == 16) - cpu_data = readmemw(es, cpu_state.eaaddr); - else - cpu_data = readmemb(es, cpu_state.eaaddr); - DI = string_increment(bits); -} - static void stos(int bits) { @@ -1919,22 +1551,6 @@ stos(int bits) DI = string_increment(bits); } -static void -ins(int bits) -{ - cpu_state.eaaddr = DX; - cpu_io(bits, 0, cpu_state.eaaddr); - stos(bits); -} - -static void -outs(int bits) -{ - lods(bits); - cpu_state.eaaddr = DX; - cpu_io(bits, 1, cpu_state.eaaddr); -} - static void aa(void) { @@ -2028,82 +1644,23 @@ cpu_outw(uint16_t port, uint16_t val) return outw(port, val); } -/* The FARRET microcode routine. */ -static void -farret(int far) -{ - uint8_t far2 = !!(opcode & 0x08); - uint16_t new_cs; - uint16_t new_ip; - - wait(1, 0); - new_ip = pop(); - pfq_do_suspend(); - wait(2, 0); - - if ((!!far) != far2) - fatal("Far call distance mismatch (%i = %i)\n", !!far, far2); - - if (far) { - wait(1, 0); - new_cs = pop(); - - pfq_clear(); - wait(2, 0); - } else { - pfq_clear(); - wait(2, 0); - } - - wait(2, 0); - if (far) - load_cs(new_cs); - set_ip(new_ip); -} - /* Executes instructions up to the specified number of cycles. */ void -execx86(int32_t cycs) +execx86(int cycs) { - uint8_t temp = 0; - uint8_t temp2; - uint8_t old_af; - uint8_t nests; - uint8_t temp_val; - uint8_t temp_al; - uint8_t bit; - uint8_t handled = 0; - uint8_t odd; - uint8_t zero; - uint8_t nibbles_count; - uint8_t destcmp; - uint8_t destbyte; - uint8_t srcbyte; - uint8_t nibble_result; - uint8_t bit_length; + uint8_t temp = 0, temp2, old_af, nests; + uint8_t temp_val, temp_al, bit, handled = 0; + uint8_t odd, zero, nibbles_count, destcmp; + uint8_t destbyte, srcbyte, nibble_result, bit_length; uint8_t bit_offset; int8_t nibble_result_s; - uint16_t addr; - uint16_t tempw; - uint16_t new_cs; - uint16_t new_ip; - uint16_t tempw_int; - uint16_t size; - uint16_t tempbp; - uint16_t lowbound; - uint16_t highbound; - uint16_t regval; - uint16_t orig_sp; - uint16_t wordtopush; - uint16_t immediate; - uint16_t old_flags; - uint16_t tmpa; + uint16_t addr, tempw, new_cs, new_ip; + uint16_t tempw_int, size, tempbp, lowbound; + uint16_t highbound, regval, orig_sp, wordtopush; + uint16_t immediate, old_flags; int bits; - uint32_t i; - uint32_t carry; - uint32_t nibble; - uint32_t srcseg; - uint32_t byteaddr; + uint32_t dest_seg, i, carry, nibble; + uint32_t srcseg, byteaddr; cycles += cycs; @@ -2112,9 +1669,9 @@ execx86(int32_t cycs) if (!repeating) { cpu_state.oldpc = cpu_state.pc; - opcode = pfq_fetchb_common(); - handled = 0; - oldc = cpu_state.flags & C_FLAG; + opcode = pfq_fetchb(); + handled = 0; + oldc = cpu_state.flags & C_FLAG; if (clear_lock) { in_lock = 0; clear_lock = 0; @@ -2123,7 +1680,7 @@ execx86(int32_t cycs) } completed = 1; - x808x_log("[%04X:%04X] Opcode: %02X\n", CS, cpu_state.pc, opcode); + // pclog("[%04X:%04X] Opcode: %02X\n", CS, cpu_state.pc, opcode); if (is186) { switch (opcode) { case 0x60: /*PUSHA/PUSH R*/ @@ -2163,7 +1720,7 @@ execx86(int32_t cycs) regval = get_reg(cpu_reg); if (lowbound > regval || highbound < regval) { cpu_state.pc = cpu_state.oldpc; - intr_routine(5, 0); + interrupt(5); } handled = 1; break; @@ -2219,60 +1776,58 @@ execx86(int32_t cycs) case 0x6c: case 0x6d: /* INM dst, DW/INS dst, DX */ + bits = 8 << (opcode & 1); handled = 1; - bits = 8 << (opcode & 1); - if (rep_start()) { - ins(bits); - wait(3, 0); + if (!repeating) + wait(2, 0); - if (in_rep != 0) { - completed = 0; - repeating = 1; + if (rep_action(bits)) + break; + else if (!repeating) + wait(7, 0); - wait(1, 0); - CX--; - - if (irq_pending()) { - wait(2, 0); - rep_interrupt(); - } else { - wait(2, 0); - - if (CX == 0) - rep_end(); - else - wait(1, 0); - } - } + if (bits == 16) { + writememw(es, DI, cpu_inw(DX)); + DI += (cpu_state.flags & D_FLAG) ? -2 : 2; + } else { + wait(4, 0); + writememb(es, DI, inb(DX)); + DI += (cpu_state.flags & D_FLAG) ? -1 : 1; } + + if (in_rep == 0) + break; + + repeating = 1; + clock_end(); break; case 0x6e: case 0x6f: /* OUTM DW, src/OUTS DX, src */ - handled = 1; - bits = 8 << (opcode & 1); - if (rep_start()) { - wait(1, 0); - outs(bits); - if (in_rep != 0) { - completed = 0; - repeating = 1; + dest_seg = ovr_seg ? *ovr_seg : ds; + bits = 8 << (opcode & 1); + handled = 1; + if (!repeating) + wait(2, 0); - wait(1, 0); - if (irq_pending()) { - wait(1, 0); - rep_interrupt(); - } + if (rep_action(bits)) + break; + else if (!repeating) + wait(7, 0); - wait(1, 0); - CX--; - if (CX == 0) - rep_end(); - else - wait(1, 0); - } else - wait(1, 0); + if (bits == 16) { + cpu_outw(DX, readmemw(dest_seg, SI)); + SI += (cpu_state.flags & D_FLAG) ? -2 : 2; + } else { + wait(4, 0); + outb(DX, readmemb(dest_seg + SI)); + SI += (cpu_state.flags & D_FLAG) ? -1 : 1; } + if (in_rep == 0) + break; + + repeating = 1; + clock_end(); break; case 0xc8: /* ENTER/PREPARE */ @@ -2303,6 +1858,7 @@ execx86(int32_t cycs) do_mod_rm(); if (cpu_mod == 3) wait(1, 0); + access(53, bits); cpu_data = get_ea(); cpu_src = pfq_fetchb(); @@ -2376,14 +1932,12 @@ execx86(int32_t cycs) set_af(0); set_pzs(bits); break; - - default: - break; } if ((opcode & 2) != 0) wait(4, 0); --cpu_src; } + access(17, bits); set_ea(cpu_data); handled = 1; break; @@ -2393,9 +1947,6 @@ execx86(int32_t cycs) BP = pop(); handled = 1; break; - - default: - break; } } if (!handled) { @@ -2404,7 +1955,7 @@ execx86(int32_t cycs) case 0x0E: case 0x16: case 0x1E: /* PUSH seg */ - wait(3, 0); + access(29, 16); push(&(_opseg[(opcode >> 3) & 0x03]->seg)); break; case 0x07: @@ -2647,7 +2198,7 @@ execx86(int32_t cycs) } for (i = 0; i < bit_length; i++) { byteaddr = (es) + DI; - writememb(es, DI, (read_mem_b(byteaddr) & ~(1 << bit_offset)) | ((!!(AX & (1 << i))) << bit_offset)); + writememb(es, DI, (read_mem_b(byteaddr) & ~(1 << (bit_offset))) | ((!!(AX & (1 << i))) << bit_offset)); bit_offset++; if (bit_offset == 8) { DI++; @@ -2678,7 +2229,7 @@ execx86(int32_t cycs) AX = 0; for (i = 0; i < bit_length; i++) { byteaddr = (ds) + SI; - AX |= (!!(readmemb((ds), SI) & (1 << bit_offset))) << i; + AX |= (!!(readmemb(byteaddr) & (1 << bit_offset))) << i; bit_offset++; if (bit_offset == 8) { SI++; @@ -2696,20 +2247,23 @@ execx86(int32_t cycs) break; default: - opcode = orig_opcode; - cpu_state.pc = (cpu_state.pc - 1) & 0xffff; + opcode = orig_opcode; + cpu_state.pc--; break; } - } else { - wait(1, 0); - if (opcode == 0x0F) { - load_cs(pop()); - pfq_pos = 0; - } else - load_seg(pop(), _opseg[(opcode >> 3) & 0x03]); - /* All POP segment instructions suppress interrupts for one instruction. */ - noint = 1; - } + } else + handled = 0; + if (handled) + break; + access(22, 16); + if (opcode == 0x0F) { + load_cs(pop()); + pfq_pos = 0; + } else + load_seg(pop(), _opseg[(opcode >> 3) & 0x03]); + wait(1, 0); + /* All POP segment instructions suppress interrupts for one instruction. */ + noint = 1; break; case 0x26: /*ES:*/ @@ -2721,164 +2275,96 @@ execx86(int32_t cycs) completed = 0; break; - case 0x00: /* ADD r/m8, r8; r8, r/m8; al, imm8 */ + case 0x00: + case 0x01: case 0x02: - case 0x04: - case 0x08: /* OR r/m8, r8; r8, r/m8; al, imm8 */ - case 0x0a: - case 0x0c: - case 0x10: /* ADC r/m8, r8; r8, r/m8; al, imm8 */ - case 0x12: - case 0x14: - case 0x18: /* SBB r/m8, r8; r8, r/m8; al, imm8 */ - case 0x1a: - case 0x1c: - case 0x20: /* AND r/m8, r8; r8, r/m8; al, imm8 */ - case 0x22: - case 0x24: - case 0x28: /* SUB r/m8, r8; r8, r/m8; al, imm8 */ - case 0x2a: - case 0x2c: - case 0x30: /* XOR r/m8, r8; r8, r/m8; al, imm8 */ - case 0x32: - case 0x34: - bits = 8; - wait(1, 0); - if (opcode & 0x04) { - cpu_data = pfq_fetch(); - cpu_dest = get_accum(bits); /* AX/AL */ - cpu_src = cpu_data; - } else { - do_mod_rm(); - tempw = get_ea(); - if (opcode & 2) { - cpu_dest = get_reg(cpu_reg); - cpu_src = tempw; - } else { - cpu_dest = tempw; - cpu_src = get_reg(cpu_reg); - } - } - cpu_alu_op = (opcode >> 3) & 7; - wait(2, 0); - if (cpu_mod == 3) - wait(2, 0); - - alu_op(bits); - if (opcode & 0x04) - set_accum(bits, cpu_data); - else { - if (opcode & 2) - set_reg(cpu_reg, cpu_data); - else - set_ea(cpu_data); - } - break; - - case 0x01: /* ADD r/m16, r16; r16, r/m16; ax, imm16 */ case 0x03: - case 0x05: - case 0x09: /* OR r/m16, r16; r16, r/m16; ax, imm16 */ + case 0x08: + case 0x09: + case 0x0a: case 0x0b: - case 0x0d: - case 0x11: /* ADC r/m16, r16; r16, r/m16; ax, imm16 */ + case 0x10: + case 0x11: + case 0x12: case 0x13: - case 0x15: - case 0x19: /* SBB r/m16, r16; r16, r/m16; ax, imm16 */ + case 0x18: + case 0x19: + case 0x1a: case 0x1b: - case 0x1d: - case 0x21: /* AND r/m16, r16; r16, r/m16; ax, imm16 */ + case 0x20: + case 0x21: + case 0x22: case 0x23: - case 0x25: - case 0x29: /* SUB r/m16, r16; r16, r/m16; ax, imm16 */ + case 0x28: + case 0x29: + case 0x2a: case 0x2b: - case 0x2d: - case 0x31: /* XOR r/m16, r16; r16, r/m16; ax, imm16 */ + case 0x30: + case 0x31: + case 0x32: case 0x33: - case 0x35: - bits = 16; - wait(1, 0); - if (opcode & 0x04) { - cpu_data = pfq_fetch(); - cpu_dest = get_accum(bits); /* AX/AL */ - cpu_src = cpu_data; - } else { - do_mod_rm(); - tempw = get_ea(); - if (opcode & 2) { - cpu_dest = get_reg(cpu_reg); - cpu_src = tempw; - } else { - cpu_dest = tempw; - cpu_src = get_reg(cpu_reg); - } - } - cpu_alu_op = (opcode >> 3) & 7; - wait(2, 0); - if (cpu_mod == 3) - wait(2, 0); - - alu_op(bits); - if (opcode & 0x04) - set_accum(bits, cpu_data); - else { - if (opcode & 2) - set_reg(cpu_reg, cpu_data); - else - set_ea(cpu_data); - } - break; - - case 0x38: /* CMP r/m8, r8; r8, r/m8; al, imm8 */ + case 0x38: + case 0x39: case 0x3a: - case 0x3c: - bits = 8; - wait(1, 0); - if (opcode & 0x04) { - cpu_data = pfq_fetch(); - cpu_dest = get_accum(bits); /* AX/AL */ - cpu_src = cpu_data; - } else { - do_mod_rm(); - tempw = get_ea(); - if (opcode & 2) { - cpu_dest = get_reg(cpu_reg); - cpu_src = tempw; - } else { - cpu_dest = tempw; - cpu_src = get_reg(cpu_reg); - } - } + case 0x3b: + /* alu rm, r / r, rm */ + bits = 8 << (opcode & 1); + do_mod_rm(); + access(46, bits); + tempw = get_ea(); cpu_alu_op = (opcode >> 3) & 7; - wait(2, 0); - + if ((opcode & 2) == 0) { + cpu_dest = tempw; + cpu_src = get_reg(cpu_reg); + } else { + cpu_dest = get_reg(cpu_reg); + cpu_src = tempw; + } + if (cpu_mod != 3) + wait(2, 0); + wait(1, 0); alu_op(bits); + if (cpu_alu_op != 7) { + if ((opcode & 2) == 0) { + access(10, bits); + set_ea(cpu_data); + if (cpu_mod == 3) + wait(1, 0); + } else { + set_reg(cpu_reg, cpu_data); + wait(1, 0); + } + } else + wait(1, 0); break; - case 0x39: /* CMP r/m16, r16; r16, r/m16; ax, imm16 */ - case 0x3b: + case 0x04: + case 0x05: + case 0x0c: + case 0x0d: + case 0x14: + case 0x15: + case 0x1c: + case 0x1d: + case 0x24: + case 0x25: + case 0x2c: + case 0x2d: + case 0x34: + case 0x35: + case 0x3c: case 0x3d: - bits = 16; + /* alu A, imm */ + bits = 8 << (opcode & 1); wait(1, 0); - if (opcode & 0x04) { - cpu_data = pfq_fetch(); - cpu_dest = get_accum(bits); /* AX/AL */ - cpu_src = cpu_data; - } else { - do_mod_rm(); - tempw = get_ea(); - if (opcode & 2) { - cpu_dest = get_reg(cpu_reg); - cpu_src = tempw; - } else { - cpu_dest = tempw; - cpu_src = get_reg(cpu_reg); - } - } + cpu_data = pfq_fetch(); + cpu_dest = get_accum(bits); /* AX/AL */ + cpu_src = cpu_data; cpu_alu_op = (opcode >> 3) & 7; - wait(2, 0); - alu_op(bits); + if (cpu_alu_op != 7) + set_accum(bits, cpu_data); + wait(1, 0); break; case 0x27: /*DAA*/ @@ -2999,7 +2485,7 @@ execx86(int32_t cycs) case 0x55: case 0x56: case 0x57: - wait(3, 0); + access(30, 16); push(&(cpu_state.regs[opcode & 0x07].w)); break; case 0x58: @@ -3010,8 +2496,9 @@ execx86(int32_t cycs) case 0x5D: case 0x5E: case 0x5F: - wait(1, 0); + access(23, 16); cpu_state.regs[opcode & 0x07].w = pop(); + wait(1, 0); break; case 0x60: /*JO alias*/ @@ -3074,14 +2561,18 @@ execx86(int32_t cycs) /* alu rm, imm */ bits = 8 << (opcode & 1); do_mod_rm(); + access(47, bits); cpu_data = get_ea(); cpu_dest = cpu_data; if (cpu_mod != 3) - wait(1, 0); - wait(1, 0); - if (opcode == 0x81) + wait(3, 0); + if (opcode == 0x81) { + if (cpu_mod == 3) + wait(1, 0); cpu_src = pfq_fetchw(); - else { + } else { + if (cpu_mod == 3) + wait(1, 0); if (opcode == 0x83) cpu_src = sign_extend(pfq_fetchb()); else @@ -3091,8 +2582,7 @@ execx86(int32_t cycs) cpu_alu_op = (rmdat & 0x38) >> 3; alu_op(bits); if (cpu_alu_op != 7) { - if (cpu_mod != 3) - wait(1, 0); + access(11, bits); set_ea(cpu_data); } else { if (cpu_mod != 3) @@ -3105,10 +2595,11 @@ execx86(int32_t cycs) /* TEST rm, reg */ bits = 8 << (opcode & 1); do_mod_rm(); + access(48, bits); cpu_data = get_ea(); test(bits, cpu_data, get_reg(cpu_reg)); - if (cpu_mod != 3) - wait(1, 0); + if (cpu_mod == 3) + wait(2, 0); wait(2, 0); break; case 0x86: @@ -3116,12 +2607,12 @@ execx86(int32_t cycs) /* XCHG rm, reg */ bits = 8 << (opcode & 1); do_mod_rm(); + access(49, bits); cpu_data = get_ea(); cpu_src = get_reg(cpu_reg); set_reg(cpu_reg, cpu_data); wait(3, 0); - if (cpu_mod != 3) - wait(3, 0); + access(12, bits); set_ea(cpu_src); break; @@ -3131,8 +2622,7 @@ execx86(int32_t cycs) bits = 8 << (opcode & 1); do_mod_rm(); wait(1, 0); - if (cpu_mod != 3) - wait(2, 0); + access(13, bits); set_ea(get_reg(cpu_reg)); break; case 0x8A: @@ -3140,17 +2630,18 @@ execx86(int32_t cycs) /* MOV reg, rm */ bits = 8 << (opcode & 1); do_mod_rm(); + access(50, bits); set_reg(cpu_reg, get_ea()); wait(1, 0); if (cpu_mod != 3) - wait(1, 0); + wait(2, 0); break; case 0x8C: /*MOV w,sreg*/ do_mod_rm(); - wait(1, 0); - if (cpu_mod != 3) - wait(2, 0); + if (cpu_mod == 3) + wait(1, 0); + access(14, 16); seteaw(_opseg[(rmdat & 0x18) >> 3]->seg); break; @@ -3159,34 +2650,36 @@ execx86(int32_t cycs) cpu_state.regs[cpu_reg].w = cpu_state.eaaddr; wait(1, 0); if (cpu_mod != 3) - wait(1, 0); + wait(2, 0); break; case 0x8E: /*MOV sreg,w*/ do_mod_rm(); + access(51, 16); tempw = geteaw(); - if ((rmdat & 0x18) == 0x08) + if ((rmdat & 0x18) == 0x08) { load_cs(tempw); - else + pfq_pos = 0; + } else load_seg(tempw, _opseg[(rmdat & 0x18) >> 3]); wait(1, 0); if (cpu_mod != 3) - wait(1, 0); + wait(2, 0); if (((rmdat & 0x18) >> 3) == 2) noint = 1; break; case 0x8F: /*POPW*/ do_mod_rm(); - wait(2, 0); - cpu_src = cpu_state.eaaddr; - if (cpu_mod != 3) - wait(1, 0); wait(1, 0); + cpu_src = cpu_state.eaaddr; + access(24, 16); if (cpu_mod != 3) wait(2, 0); cpu_data = pop(); cpu_state.eaaddr = cpu_src; + wait(2, 0); + access(15, 16); seteaw(cpu_data); break; @@ -3224,14 +2717,14 @@ execx86(int32_t cycs) new_ip = pfq_fetchw(); wait(1, 0); new_cs = pfq_fetchw(); - wait(1, 0); - pfq_suspend(); + pfq_clear(); + access(31, 16); push(&(CS)); - wait(4, 0); + access(60, 16); cpu_state.oldpc = cpu_state.pc; load_cs(new_cs); set_ip(new_ip); - wait(1, 0); + access(32, 16); push((uint16_t *) &(cpu_state.oldpc)); break; case 0x9B: /*WAIT*/ @@ -3253,7 +2746,7 @@ execx86(int32_t cycs) #endif break; case 0x9C: /*PUSHF*/ - wait(4, 0); + access(33, 16); if (is_nec) tempw = (cpu_state.flags & 0x8fd7) | 0x7000; else @@ -3261,11 +2754,12 @@ execx86(int32_t cycs) push(&tempw); break; case 0x9D: /*POPF*/ - wait(1, 0); + access(25, 16); if (is_nec) cpu_state.flags = pop() | 0x8002; else cpu_state.flags = pop() | 0x0002; + wait(1, 0); break; case 0x9E: /*SAHF*/ wait(1, 0); @@ -3281,49 +2775,58 @@ execx86(int32_t cycs) case 0xA1: /* MOV A, [iw] */ bits = 8 << (opcode & 1); - wait(2, 0); + wait(1, 0); cpu_state.eaaddr = pfq_fetchw(); - set_accum(bits, readmem(ovr_seg ? *ovr_seg : ds)); + access(1, bits); + set_accum(bits, readmem((ovr_seg ? *ovr_seg : ds))); + wait(1, 0); break; case 0xA2: case 0xA3: /* MOV [iw], A */ bits = 8 << (opcode & 1); - wait(2, 0); + wait(1, 0); cpu_state.eaaddr = pfq_fetchw(); + access(7, bits); writemem((ovr_seg ? *ovr_seg : ds), get_accum(bits)); - wait(2, 0); break; case 0xA4: case 0xA5: /* MOVS */ + case 0xAC: + case 0xAD: /* LODS */ bits = 8 << (opcode & 1); - if (rep_start()) { - lods(bits); + if (!repeating) { wait(1, 0); - stos(bits); - wait(1, 0); - - if (in_rep != 0) { - completed = 0; - repeating = 1; - - CX--; - - if (irq_pending()) { - wait(2, 0); - rep_interrupt(); - } else { - wait(2, 0); - - if (CX == 0) - rep_end(); - else - wait(1, 0); - } - } else + if ((opcode & 8) == 0 && in_rep != 0) wait(1, 0); } + if (rep_action(bits)) { + wait(1, 0); + if ((opcode & 8) != 0) + wait(1, 0); + break; + } + if (in_rep != 0 && (opcode & 8) != 0) + wait(1, 0); + access(20, bits); + lods(bits); + if ((opcode & 8) == 0) { + access(27, bits); + stos(bits); + } else { + set_accum(bits, cpu_data); + if (in_rep != 0) + wait(2, 0); + } + if (in_rep == 0) { + wait(3, 0); + if ((opcode & 8) != 0) + wait(1, 0); + break; + } + repeating = 1; + clock_end(); break; case 0xA6: @@ -3331,53 +2834,40 @@ execx86(int32_t cycs) case 0xAE: case 0xAF: /* SCAS */ bits = 8 << (opcode & 1); - if (rep_start()) { - if ((opcode & 8) == 0) { - wait(1, 0); - lods(bits); - tmpa = cpu_data; - } else - tmpa = AX; + if (!repeating) + wait(1, 0); + if (rep_action(bits)) { wait(2, 0); - lods_di(bits); - cpu_src = cpu_data; - cpu_dest = tmpa; - wait(3, 0); - sub(bits); - - if (in_rep) { - uint8_t end = 0; - - completed = 0; - repeating = 1; - - wait(1, 0); - - CX--; - - if ((!!(cpu_state.flags & (rep_c_flag ? C_FLAG : Z_FLAG))) == (in_rep == 1)) { - completed = 1; - wait(1, 0); - end = 1; - } - - if (!end) { - wait(1, 0); - - if (irq_pending()) { - wait(1, 0); - rep_interrupt(); - } - - wait(1, 0); - if (CX == 0) - rep_end(); - else - wait(1, 0); - } else - wait(1, 0); - } + break; } + if (in_rep != 0) + wait(1, 0); + wait(1, 0); + cpu_dest = get_accum(bits); + if ((opcode & 8) == 0) { + access(21, bits); + lods(bits); + wait(1, 0); + cpu_dest = cpu_data; + } + access(2, bits); + cpu_state.eaaddr = DI; + cpu_data = readmem(es); + DI = string_increment(bits); + cpu_src = cpu_data; + sub(bits); + wait(2, 0); + if (in_rep == 0) { + wait(3, 0); + break; + } + if ((!!(cpu_state.flags & (rep_c_flag ? C_FLAG : Z_FLAG))) == (in_rep == 1)) { + completed = 1; + wait(4, 0); + break; + } + repeating = 1; + clock_end(); break; case 0xA8: @@ -3393,59 +2883,24 @@ execx86(int32_t cycs) case 0xAA: case 0xAB: /* STOS */ bits = 8 << (opcode & 1); - if (rep_start()) { - cpu_data = AX; + if (!repeating) { wait(1, 0); - stos(bits); - if (in_rep != 0) { - completed = 0; - repeating = 1; - - wait(1, 0); - if (irq_pending()) { - wait(1, 0); - rep_interrupt(); - } - - wait(1, 0); - CX--; - if (CX == 0) - rep_end(); - else - wait(1, 0); - } else + if (in_rep != 0) wait(1, 0); } - break; - - case 0xAC: - case 0xAD: /* LODS */ - bits = 8 << (opcode & 1); - if (rep_start()) { - lods(bits); - set_accum(bits, cpu_data); + if (rep_action(bits)) { + wait(1, 0); + break; + } + cpu_data = AX; + access(28, bits); + stos(bits); + if (in_rep == 0) { wait(3, 0); - - if (in_rep != 0) { - completed = 0; - repeating = 1; - - wait(1, 0); - CX--; - - if (irq_pending()) { - wait(2, 0); - rep_interrupt(); - } else { - wait(2, 0); - - if (CX == 0) - rep_end(); - else - wait(1, 0); - } - } + break; } + repeating = 1; + clock_end(); break; case 0xB0: @@ -3478,53 +2933,42 @@ execx86(int32_t cycs) break; case 0xC0: - case 0xC2: - /* RETN imm16 */ - bits = 8 + (opcode & 0x08); - wait(1, 0); - cpu_src = pfq_fetchw(); - wait(1, 0); - new_ip = pop(); - pfq_do_suspend(); - wait(2, 0); - pfq_clear(); - wait(3, 0); - SP += cpu_src; - set_ip(new_ip); - break; - case 0xC1: + case 0xC2: case 0xC3: - /* RETN */ - bits = 8 + (opcode & 0x08); - wait(1, 0); - cpu_src = pfq_fetchw(); - new_ip = pop(); - pfq_do_suspend(); - wait(1, 0); - pfq_clear(); - wait(2, 0); - set_ip(new_ip); - break; - case 0xC8: - case 0xCA: - /* RETF imm16 */ - bits = 8 + (opcode & 0x08); - wait(1, 0); - cpu_src = pfq_fetchw(); - farret(1); - SP += cpu_src; - wait(1, 0); - break; - case 0xC9: + case 0xCA: case 0xCB: - /* RETF */ + /* RET */ bits = 8 + (opcode & 0x08); - wait(1, 0); - wait(1, 0); - farret(1); + if ((opcode & 9) != 1) + wait(1, 0); + if (!(opcode & 1)) { + cpu_src = pfq_fetchw(); + wait(1, 0); + } + if ((opcode & 9) == 9) + wait(1, 0); + pfq_clear(); + access(26, bits); + new_ip = pop(); + wait(2, 0); + if ((opcode & 8) == 0) + new_cs = CS; + else { + access(42, bits); + new_cs = pop(); + if (opcode & 1) + wait(1, 0); + } + if (!(opcode & 1)) { + SP += cpu_src; + wait(1, 0); + } + load_cs(new_cs); + access(72, bits); + set_ip(new_ip); break; case 0xC4: @@ -3532,12 +2976,13 @@ execx86(int32_t cycs) /* LsS rw, rmd */ do_mod_rm(); bits = 16; + access(52, bits); read_ea(1, bits); cpu_state.regs[cpu_reg].w = cpu_data; - if (cpu_mod != 3) - wait(2, 0); + access(57, bits); read_ea2(bits); load_seg(cpu_data, (opcode & 0x01) ? &cpu_state.seg_ds : &cpu_state.seg_es); + wait(1, 0); break; case 0xC6: @@ -3546,37 +2991,45 @@ execx86(int32_t cycs) bits = 8 << (opcode & 1); do_mod_rm(); wait(1, 0); + if (cpu_mod != 3) + wait(2, 0); cpu_data = pfq_fetch(); - wait((opcode == 0xc6) ? 2 : 1, 0); + if (cpu_mod == 3) + wait(1, 0); + access(16, bits); set_ea(cpu_data); break; case 0xCC: /*INT 3*/ - wait(1, 0); - wait(4, 0); - int3(); + interrupt(3); break; case 0xCD: /*INT*/ wait(1, 0); - temp = pfq_fetchb(); - wait(1, 0); - sw_int(temp); + interrupt(pfq_fetchb()); break; case 0xCE: /*INTO*/ - wait(1, 0); - if (cpu_state.flags & V_FLAG) - sw_int(4); + wait(3, 0); + if (cpu_state.flags & V_FLAG) { + wait(2, 0); + interrupt(4); + } break; case 0xCF: /*IRET*/ - wait(1, 0); - wait(1, 0); - farret(1); + access(43, 8); + new_ip = pop(); + wait(3, 0); + access(44, 8); + new_cs = pop(); + load_cs(new_cs); + access(62, 8); + set_ip(new_ip); + access(45, 8); if (is_nec) cpu_state.flags = pop() | 0x8002; else cpu_state.flags = pop() | 0x0002; - wait(1, 0); + wait(5, 0); noint = 1; nmi_enable = 1; break; @@ -3588,9 +3041,10 @@ execx86(int32_t cycs) /* rot rm */ bits = 8 << (opcode & 1); do_mod_rm(); - cpu_data = get_ea(); if (cpu_mod == 3) wait(1, 0); + access(53, bits); + cpu_data = get_ea(); if ((opcode & 2) == 0) { cpu_src = 1; wait((cpu_mod != 3) ? 4 : 0, 0); @@ -3666,14 +3120,12 @@ execx86(int32_t cycs) set_af(0); set_pzs(bits); break; - - default: - break; } if ((opcode & 2) != 0) wait(4, 0); --cpu_src; } + access(17, bits); set_ea(cpu_data); break; @@ -3711,8 +3163,9 @@ execx86(int32_t cycs) break; case 0xD7: /*XLATB*/ cpu_state.eaaddr = (BX + AL) & 0xffff; - wait(4, 0); - AL = readmemb((ovr_seg ? *ovr_seg : ds), cpu_state.eaaddr); + access(4, 8); + AL = readmemb((ovr_seg ? *ovr_seg : ds) + cpu_state.eaaddr); + wait(1, 0); break; case 0xD8: @@ -3725,76 +3178,42 @@ execx86(int32_t cycs) case 0xDF: /* esc i, r, rm */ do_mod_rm(); + access(54, 16); tempw = cpu_state.pc; - geteaw(); - wait(1, 0); - if (cpu_mod != 3) - wait(1, 0); - if (hasfpu) { - if (fpu_softfloat) { - switch (opcode) { - case 0xD8: - ops_sf_fpu_8087_d8[(rmdat >> 3) & 0x1f](rmdat); - break; - case 0xD9: - ops_sf_fpu_8087_d9[rmdat & 0xff](rmdat); - break; - case 0xDA: - ops_sf_fpu_8087_da[rmdat & 0xff](rmdat); - break; - case 0xDB: - ops_sf_fpu_8087_db[rmdat & 0xff](rmdat); - break; - case 0xDC: - ops_sf_fpu_8087_dc[(rmdat >> 3) & 0x1f](rmdat); - break; - case 0xDD: - ops_sf_fpu_8087_dd[rmdat & 0xff](rmdat); - break; - case 0xDE: - ops_sf_fpu_8087_de[rmdat & 0xff](rmdat); - break; - case 0xDF: - ops_sf_fpu_8087_df[rmdat & 0xff](rmdat); - break; - - default: - break; - } - } else { - switch (opcode) { - case 0xD8: - ops_fpu_8087_d8[(rmdat >> 3) & 0x1f](rmdat); - break; - case 0xD9: - ops_fpu_8087_d9[rmdat & 0xff](rmdat); - break; - case 0xDA: - ops_fpu_8087_da[rmdat & 0xff](rmdat); - break; - case 0xDB: - ops_fpu_8087_db[rmdat & 0xff](rmdat); - break; - case 0xDC: - ops_fpu_8087_dc[(rmdat >> 3) & 0x1f](rmdat); - break; - case 0xDD: - ops_fpu_8087_dd[rmdat & 0xff](rmdat); - break; - case 0xDE: - ops_fpu_8087_de[rmdat & 0xff](rmdat); - break; - case 0xDF: - ops_fpu_8087_df[rmdat & 0xff](rmdat); - break; - - default: - break; - } + if (!hasfpu) + geteaw(); + else + switch (opcode) { + case 0xD8: + ops_fpu_8087_d8[(rmdat >> 3) & 0x1f]((uint32_t) rmdat); + break; + case 0xD9: + ops_fpu_8087_d9[rmdat & 0xff]((uint32_t) rmdat); + break; + case 0xDA: + ops_fpu_8087_da[rmdat & 0xff]((uint32_t) rmdat); + break; + case 0xDB: + ops_fpu_8087_db[rmdat & 0xff]((uint32_t) rmdat); + break; + case 0xDC: + ops_fpu_8087_dc[(rmdat >> 3) & 0x1f]((uint32_t) rmdat); + break; + case 0xDD: + ops_fpu_8087_dd[rmdat & 0xff]((uint32_t) rmdat); + break; + case 0xDE: + ops_fpu_8087_de[rmdat & 0xff]((uint32_t) rmdat); + break; + case 0xDF: + ops_fpu_8087_df[rmdat & 0xff]((uint32_t) rmdat); + break; } - } cpu_state.pc = tempw; /* Do this as the x87 code advances it, which is needed on the 286+ core, but not here. */ + wait(1, 0); + if (cpu_mod != 3) + wait(2, 0); break; case 0xE0: @@ -3818,9 +3237,6 @@ execx86(int32_t cycs) if (!(cpu_state.flags & Z_FLAG)) oldc = 0; break; - - default: - break; } } else oldc = (CX == 0); @@ -3830,46 +3246,43 @@ execx86(int32_t cycs) case 0xE4: case 0xE5: - bits = 8 << (opcode & 1); - wait(1, 0); - cpu_data = pfq_fetchb(); - cpu_state.eaaddr = cpu_data; - wait(1, 0); - cpu_io(bits, 0, cpu_state.eaaddr); - break; case 0xE6: case 0xE7: - bits = 8 << (opcode & 1); - wait(1, 0); - cpu_data = pfq_fetchb(); - cpu_state.eaaddr = cpu_data; - cpu_data = (bits == 16) ? AX : AL; - wait(2, 0); - cpu_io(bits, 1, cpu_state.eaaddr); - break; case 0xEC: case 0xED: - bits = 8 << (opcode & 1); - cpu_data = DX; - cpu_state.eaaddr = cpu_data; - wait(1, 0); - cpu_io(bits, 0, cpu_state.eaaddr); - break; case 0xEE: case 0xEF: bits = 8 << (opcode & 1); - wait(2, 0); - cpu_data = DX; + if ((opcode & 0x0e) != 0x0c) + wait(1, 0); + if ((opcode & 8) == 0) + cpu_data = pfq_fetchb(); + else + cpu_data = DX; cpu_state.eaaddr = cpu_data; - cpu_data = (bits == 16) ? AX : AL; - cpu_io(bits, 1, cpu_state.eaaddr); - wait(1, 0); + if ((opcode & 2) == 0) { + access(3, bits); + if (opcode & 1) + cpu_io(16, 0, cpu_data); + else + cpu_io(8, 0, cpu_data); + wait(1, 0); + } else { + if ((opcode & 8) == 0) + access(8, bits); + else + access(9, bits); + if (opcode & 1) + cpu_io(16, 1, cpu_data); + else + cpu_io(8, 1, cpu_data); + } break; case 0xE8: /*CALL rel 16*/ wait(1, 0); cpu_state.oldpc = jump_near(); - wait(2, 0); + access(34, 8); push((uint16_t *) &(cpu_state.oldpc)); break; case 0xE9: /*JMP rel 16*/ @@ -3879,13 +3292,12 @@ execx86(int32_t cycs) case 0xEA: /*JMP far*/ wait(1, 0); addr = pfq_fetchw(); + wait(1, 0); tempw = pfq_fetchw(); load_cs(tempw); - pfq_do_suspend(); - set_ip(addr); - wait(2, 0); + access(70, 8); pfq_clear(); - wait(1, 0); + set_ip(addr); break; case 0xEB: /*JMP rel*/ wait(1, 0); @@ -3910,23 +3322,18 @@ execx86(int32_t cycs) break; case 0xF4: /*HLT*/ - if (repeating) { + if (!repeating) { wait(1, 0); - wait(1, 0); - wait(1, 0); - if (irq_pending()) { - check_interrupts(); - wait(7, 0); - } else { - repeating = 1; - completed = 0; - } + pfq_clear(); + } + wait(1, 0); + if (irq_pending()) { + wait(cycles & 1, 0); + check_interrupts(); } else { - wait(1, 0); - pfq_do_suspend(); - wait(2, 0); repeating = 1; completed = 0; + clock_end(); } break; case 0xF5: /*CMC*/ @@ -3938,12 +3345,15 @@ execx86(int32_t cycs) case 0xF7: bits = 8 << (opcode & 1); do_mod_rm(); + access(55, bits); cpu_data = get_ea(); switch (rmdat & 0x38) { case 0x00: case 0x08: /* TEST */ wait(2, 0); + if (cpu_mod != 3) + wait(1, 0); cpu_src = pfq_fetch(); wait(1, 0); test(bits, cpu_data, cpu_src); @@ -3960,8 +3370,7 @@ execx86(int32_t cycs) cpu_dest = 0; sub(bits); } - if (cpu_mod != 3) - wait(2, 0); + access(18, bits); set_ea(cpu_data); break; case 0x20: /* MUL */ @@ -3972,19 +3381,19 @@ execx86(int32_t cycs) if (opcode & 1) { AX = cpu_data; DX = cpu_dest; - set_co_mul(bits, DX != ((AX & 0x8000) == 0 || - (rmdat & 0x38) == 0x20 ? 0 : 0xffff)); + set_co_mul(bits, DX != ((AX & 0x8000) == 0 || (rmdat & 0x38) == 0x20 ? 0 : 0xffff)); cpu_data = DX; } else { AL = (uint8_t) cpu_data; AH = (uint8_t) cpu_dest; - set_co_mul(bits, AH != ((AL & 0x80) == 0 || - (rmdat & 0x38) == 0x20 ? 0 : 0xff)); + set_co_mul(bits, AH != ((AL & 0x80) == 0 || (rmdat & 0x38) == 0x20 ? 0 : 0xff)); if (!is_nec) cpu_data = AH; } set_sf(bits); set_pf(); + if (cpu_mod != 3) + wait(1, 0); /* NOTE: When implementing the V20, care should be taken to not change the zero flag. */ if (is_nec) @@ -3992,13 +3401,12 @@ execx86(int32_t cycs) break; case 0x30: /* DIV */ case 0x38: /* IDIV */ + if (cpu_mod != 3) + wait(1, 0); cpu_src = cpu_data; if (x86_div(AL, AH)) wait(1, 0); break; - - default: - break; } break; @@ -4026,6 +3434,7 @@ execx86(int32_t cycs) /* misc */ bits = 8 << (opcode & 1); do_mod_rm(); + access(56, bits); read_ea(((rmdat & 0x38) == 0x18) || ((rmdat & 0x38) == 0x28), bits); switch (rmdat & 0x38) { case 0x00: /* INC rm */ @@ -4042,65 +3451,63 @@ execx86(int32_t cycs) do_af(); set_pzs(bits); wait(2, 0); + access(19, bits); set_ea(cpu_data); break; case 0x10: /* CALL rm */ cpu_data_opff_rm(); - wait(2, 0); - pfq_do_suspend(); - wait(4, 0); + access(63, bits); + wait(1, 0); pfq_clear(); + wait(4, 0); + if (cpu_mod != 3) + wait(1, 0); + wait(1, 0); /* Wait. */ cpu_state.oldpc = cpu_state.pc; set_ip(cpu_data); wait(2, 0); + access(35, bits); push((uint16_t *) &(cpu_state.oldpc)); break; case 0x18: /* CALL rmd */ new_ip = cpu_data; - wait(3, 0); + access(58, bits); read_ea2(bits); if (!(opcode & 1)) cpu_data |= 0xff00; new_cs = cpu_data; - wait(1, 0); - pfq_do_suspend(); - wait(3, 0); + access(36, bits); push(&(CS)); + access(64, bits); + wait(4, 0); + cpu_state.oldpc = cpu_state.pc; load_cs(new_cs); - wait(3, 0); - pfq_clear(); - wait(3, 0); - push((uint16_t *) &(cpu_state.pc)); set_ip(new_ip); + access(37, bits); + push((uint16_t *) &(cpu_state.oldpc)); break; case 0x20: /* JMP rm */ cpu_data_opff_rm(); - pfq_do_suspend(); - wait(4, 0); - pfq_clear(); + access(65, bits); set_ip(cpu_data); break; case 0x28: /* JMP rmd */ new_ip = cpu_data; - pfq_do_suspend(); - wait(4, 0); - pfq_clear(); + access(59, bits); read_ea2(bits); if (!(opcode & 1)) cpu_data |= 0xff00; new_cs = cpu_data; load_cs(new_cs); + access(66, bits); set_ip(new_ip); break; case 0x30: /* PUSH rm */ case 0x38: if (cpu_mod != 3) wait(1, 0); - wait(4, 0); - push((uint16_t *) &cpu_data); - break; - - default: + access(38, bits); + push((uint16_t *) &(cpu_data)); break; } break; diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c index 2914afff0..a5a73684f 100644 --- a/src/cpu/cpu.c +++ b/src/cpu/cpu.c @@ -1389,7 +1389,6 @@ cpu_set(void) cpu_features = CPU_FEATURE_RDTSC | CPU_FEATURE_MSR | CPU_FEATURE_CR4 | CPU_FEATURE_VME; if (cpu_s->cpu_type == CPU_PENTIUMMMX) cpu_features |= CPU_FEATURE_MMX; - msr.fcr = (1 << 8) | (1 << 9) | (1 << 12) | (1 << 16) | (1 << 19) | (1 << 21); cpu_CR4_mask = CR4_VME | CR4_PVI | CR4_TSD | CR4_DE | CR4_PSE | CR4_MCE | CR4_PCE; #ifdef USE_DYNAREC codegen_timing_set(&codegen_timing_pentium); @@ -1503,7 +1502,6 @@ cpu_set(void) cpu_features |= CPU_FEATURE_MSR | CPU_FEATURE_CR4; if (cpu_s->cpu_type == CPU_Cx6x86MX) cpu_features |= CPU_FEATURE_MMX; - msr.fcr = (1 << 8) | (1 << 9) | (1 << 12) | (1 << 16) | (1 << 19) | (1 << 21); if (cpu_s->cpu_type >= CPU_CxGX1) cpu_CR4_mask = CR4_TSD | CR4_DE | CR4_PCE; @@ -1598,7 +1596,6 @@ cpu_set(void) cpu_features |= CPU_FEATURE_3DNOW; if ((cpu_s->cpu_type == CPU_K6_2P) || (cpu_s->cpu_type == CPU_K6_3P)) cpu_features |= CPU_FEATURE_3DNOWE; - msr.fcr = (1 << 8) | (1 << 9) | (1 << 12) | (1 << 16) | (1 << 19) | (1 << 21); #if defined(DEV_BRANCH) && defined(USE_AMD_K5) cpu_CR4_mask = CR4_TSD | CR4_DE | CR4_MCE; if (cpu_s->cpu_type >= CPU_K6) { @@ -1701,7 +1698,6 @@ cpu_set(void) cpu_features = CPU_FEATURE_RDTSC | CPU_FEATURE_MSR | CPU_FEATURE_CR4 | CPU_FEATURE_VME; if (cpu_s->cpu_type >= CPU_PENTIUM2) cpu_features |= CPU_FEATURE_MMX; - msr.fcr = (1 << 8) | (1 << 9) | (1 << 12) | (1 << 16) | (1 << 19) | (1 << 21); cpu_CR4_mask = CR4_VME | CR4_PVI | CR4_TSD | CR4_DE | CR4_PSE | CR4_MCE | CR4_PAE | CR4_PCE | CR4_PGE; if (cpu_s->cpu_type == CPU_PENTIUM2D) cpu_CR4_mask |= CR4_OSFXSR; @@ -1749,8 +1745,8 @@ cpu_set(void) timing_misaligned = 2; cpu_features = CPU_FEATURE_RDTSC | CPU_FEATURE_MMX | CPU_FEATURE_MSR | CPU_FEATURE_CR4 | CPU_FEATURE_3DNOW; - msr.fcr = (1 << 8) | (1 << 9) | (1 << 12) | (1 << 16) | (1 << 18) | (1 << 19) | (1 << 20) | (1 << 21); - cpu_CR4_mask = CR4_TSD | CR4_DE | CR4_MCE | CR4_PCE; + msr.fcr = (1 << 7) | (1 << 8) | (1 << 9) | (1 << 12) | (1 << 16) | (1 << 18) | (1 << 19) | (1 << 20) | (1 << 21); + cpu_CR4_mask = CR4_TSD | CR4_DE | CR4_MCE | CR4_PCE | CR4_PGE; cpu_cyrix_alignment = 1; @@ -1816,12 +1812,13 @@ cpu_set_isa_speed(int speed) { if (speed) { cpu_isa_speed = speed; - pc_speed_changed(); } else if (cpu_busspeed >= 8000000) cpu_isa_speed = 8000000; else cpu_isa_speed = cpu_busspeed; + pc_speed_changed(); + cpu_log("cpu_set_isa_speed(%d) = %d\n", speed, cpu_isa_speed); } @@ -2373,9 +2370,14 @@ cpu_CPUID(void) EBX = ECX = 0; EDX = CPUID_FPU | CPUID_VME | CPUID_PSE | CPUID_TSC | CPUID_MSR | CPUID_PAE | CPUID_MCE | CPUID_CMPXCHG8B | CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_SEP | CPUID_CMOV; } else if (EAX == 2) { - EAX = 0x00000001; + EAX = 0x03020101; /* Instruction TLB: 4 KB pages, 4-way set associative, 32 entries + Instruction TLB: 4 MB pages, fully associative, 2 entries + Data TLB: 4 KB pages, 4-way set associative, 64 entries */ EBX = ECX = 0; - EDX = 0x00000000; + EDX = 0x06040a42; /* 2nd-level cache: 256 KB, 4-way set associative, 32-byte line size + 1st-level data cache: 8 KB, 2-way set associative, 32-byte line size + Data TLB: 4 MB pages, 4-way set associative, 8 entries + 1st-level instruction cache:8 KB, 4-way set associative, 32-byte line size */ } else EAX = EBX = ECX = EDX = 0; break; @@ -2391,9 +2393,14 @@ cpu_CPUID(void) EBX = ECX = 0; EDX = CPUID_FPU | CPUID_VME | CPUID_PSE | CPUID_TSC | CPUID_MSR | CPUID_PAE | CPUID_MCE | CPUID_CMPXCHG8B | CPUID_MMX | CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_SEP | CPUID_CMOV; } else if (EAX == 2) { - EAX = 0x00000001; + EAX = 0x03020101; /* Instruction TLB: 4 KB pages, 4-way set associative, 32 entries + Instruction TLB: 4 MB pages, fully associative, 2 entries + Data TLB: 4 KB pages, 4-way set associative, 64 entries */ EBX = ECX = 0; - EDX = 0x00000000; + EDX = 0x0c040843; /* 2nd-level cache: 512 KB, 4-way set associative, 32-byte line size + 1st-level data cache: 16 KB, 4-way set associative, 32-byte line size + Data TLB: 4 MB pages, 4-way set associative, 8 entries + 1st-level instruction cache: 16 KB, 4-way set associative, 32-byte line size */ } else EAX = EBX = ECX = EDX = 0; break; @@ -2409,9 +2416,22 @@ cpu_CPUID(void) EBX = ECX = 0; EDX = CPUID_FPU | CPUID_VME | CPUID_PSE | CPUID_TSC | CPUID_MSR | CPUID_PAE | CPUID_MCE | CPUID_CMPXCHG8B | CPUID_MMX | CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_SEP | CPUID_FXSR | CPUID_CMOV; } else if (EAX == 2) { - EAX = 0x00000001; + EAX = 0x03020101; /* Instruction TLB: 4 KB pages, 4-way set associative, 32 entries + Instruction TLB: 4 MB pages, fully associative, 2 entries + Data TLB: 4 KB pages, 4-way set associative, 64 entries */ EBX = ECX = 0; - EDX = 0x00000000; + if (cpu_f->package == CPU_PKG_SLOT2) /* Pentium II Xeon Drake */ + EDX = 0x0c040844; /* 2nd-level cache: 1 MB, 4-way set associative, 32-byte line size + 1st-level data cache: 16 KB, 4-way set associative, 32-byte line size + Data TLB: 4 MB pages, 4-way set associative, 8 entries + 1st-level instruction cache: 16 KB, 4-way set associative, 32-byte line size */ + else if (!strncmp(cpu_f->internal_name, "celeron", 7)) { /* Celeron */ + if (CPUID >= 0x660) /* Mendocino */ + EDX = 0x0c040841; /* 2nd-level cache: 128 KB, 4-way set associative, 32-byte line size */ + else /* Covington */ + EDX = 0x0c040840; /* No 2nd-level cache */ + } else /* Pentium II Deschutes and OverDrive */ + EDX = 0x0c040843; /* 2nd-level cache: 512 KB, 4-way set associative, 32-byte line size */ } else EAX = EBX = ECX = EDX = 0; break; @@ -2436,6 +2456,8 @@ cpu_CPUID(void) EDX = CPUID_FPU | CPUID_TSC | CPUID_MSR | CPUID_MCE | CPUID_MMX | CPUID_MTRR; if (cpu_has_feature(CPU_FEATURE_CX8)) EDX |= CPUID_CMPXCHG8B; + if (msr.fcr & (1 << 7)) + EDX |= CPUID_PGE; break; case 0x80000000: EAX = 0x80000005; @@ -2445,6 +2467,8 @@ cpu_CPUID(void) EDX = CPUID_FPU | CPUID_TSC | CPUID_MSR | CPUID_MCE | CPUID_MMX | CPUID_MTRR | CPUID_3DNOW; if (cpu_has_feature(CPU_FEATURE_CX8)) EDX |= CPUID_CMPXCHG8B; + if (msr.fcr & (1 << 7)) + EDX |= CPUID_PGE; break; case 0x80000002: /* Processor name string */ EAX = 0x20414956; /* VIA Samuel */ @@ -2471,6 +2495,13 @@ cpu_ven_reset(void) memset(&msr, 0, sizeof(msr)); switch (cpu_s->cpu_type) { + case CPU_WINCHIP: + case CPU_WINCHIP2: + msr.fcr = (1 << 8) | (1 << 9) | (1 << 12) | (1 << 16) | (1 << 19) | (1 << 21); + if (cpu_s->cpu_type == CPU_WINCHIP2) + msr.fcr |= (1 << 18) | (1 << 20); + break; + case CPU_K6_2P: case CPU_K6_3P: case CPU_K6_3: @@ -2491,6 +2522,11 @@ cpu_ven_reset(void) case CPU_PENTIUM2D: msr.mtrr_cap = 0x00000508ULL; break; + + case CPU_CYRIX3S: + msr.fcr = (1 << 7) | (1 << 8) | (1 << 9) | (1 << 12) | (1 << 16) | (1 << 18) | (1 << 19) | + (1 << 20) | (1 << 21); + break; } } @@ -3105,6 +3141,10 @@ cpu_WRMSR(void) cpu_features |= CPU_FEATURE_CX8; else cpu_features &= ~CPU_FEATURE_CX8; + if (EAX & (1 << 7)) + cpu_CR4_mask |= CR4_PGE; + else + cpu_CR4_mask &= ~CR4_PGE; break; case 0x1108: msr.fcr2 = EAX | ((uint64_t) EDX << 32); diff --git a/src/cpu/cpu_table.c b/src/cpu/cpu_table.c index 194089f72..947804014 100644 --- a/src/cpu/cpu_table.c +++ b/src/cpu/cpu_table.c @@ -33,39 +33,39 @@ #include <86box/machine.h> FPU fpus_none[] = { - {"None", "none", FPU_NONE}, - { NULL, NULL, 0 } + { .name = "None", .internal_name = "none", .type = FPU_NONE }, + { .name = NULL, .internal_name = NULL, .type = 0 } }; FPU fpus_8088[] = { - {"None", "none", FPU_NONE}, - { "8087", "8087", FPU_8087}, - { NULL, NULL, 0 } + { .name = "None", .internal_name = "none", .type = FPU_NONE }, + { .name = "8087", .internal_name = "8087", .type = FPU_8087 }, + { .name = NULL, .internal_name = NULL, .type = 0 } }; FPU fpus_80186[] = { - {"None", "none", FPU_NONE }, - { "8087", "8087", FPU_8087 }, - { "80187", "80187", FPU_80187}, - { NULL, NULL, 0 } + { .name = "None", .internal_name = "none", .type = FPU_NONE }, + { .name = "8087", .internal_name = "8087", .type = FPU_8087 }, + { .name = "80187", .internal_name = "80187", .type = FPU_80187 }, + { .name = NULL, .internal_name = NULL, .type = 0 } }; FPU fpus_80286[] = { - {"None", "none", FPU_NONE }, - { "287", "287", FPU_287 }, - { "287XL", "287xl", FPU_287XL}, - { NULL, NULL, 0 } + { .name = "None", .internal_name = "none", .type = FPU_NONE }, + { .name = "287", .internal_name = "287", .type = FPU_287 }, + { .name = "287XL", .internal_name = "287xl", .type = FPU_287XL }, + { .name = NULL, .internal_name = NULL, .type = 0 } }; FPU fpus_80386[] = { - {"None", "none", FPU_NONE}, - { "387", "387", FPU_387 }, - { NULL, NULL, 0 } + { .name = "None", .internal_name = "none", .type = FPU_NONE }, + { .name = "387", .internal_name = "387", .type = FPU_387 }, + { .name = NULL, .internal_name = NULL, .type = 0 } }; FPU fpus_486sx[] = { - {"None", "none", FPU_NONE }, - { "487SX", "487sx", FPU_487SX}, - { NULL, NULL, 0 } + { .name = "None", .internal_name = "none", .type = FPU_NONE }, + { .name = "487SX", .internal_name = "487sx", .type = FPU_487SX }, + { .name = NULL, .internal_name = NULL, .type = 0 } }; FPU fpus_internal[] = { - {"Internal", "internal", FPU_INTERNAL}, - { NULL, NULL, 0 } + { .name = "Internal", .internal_name = "internal", .type = FPU_INTERNAL }, + { .name = NULL, .internal_name = NULL, .type = 0 } }; const cpu_family_t cpu_families[] = { @@ -76,14 +76,128 @@ const cpu_family_t cpu_families[] = { .name = "8088", .internal_name = "8088", .cpus = (const CPU[]) { - {"4.77", CPU_8088, fpus_8088, 4772728, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 1}, - {"7.16", CPU_8088, fpus_8088, 7159092, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 1}, - {"8", CPU_8088, fpus_8088, 8000000, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 1}, -// {"9.54", CPU_8088, fpus_8088, 9545456, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 1}, - {"10", CPU_8088, fpus_8088, 10000000, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 1}, - {"12", CPU_8088, fpus_8088, 12000000, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 1}, - {"16", CPU_8088, fpus_8088, 16000000, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 1}, - {"", 0} + { + .name = "4.77", + .cpu_type = CPU_8088, + .fpus = fpus_8088, + .rspeed = 4772728, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "7.16", + .cpu_type = CPU_8088, + .fpus = fpus_8088, + .rspeed = 7159092, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "8", + .cpu_type = CPU_8088, + .fpus = fpus_8088, + .rspeed = 8000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, +#if 0 + { + .name = "9.54", + .cpu_type = CPU_8088, + .fpus = fpus_8088, + .rspeed = 9545456, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, +#endif + { + .name = "10", + .cpu_type = CPU_8088, + .fpus = fpus_8088, + .rspeed = 10000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "12", + .cpu_type = CPU_8088, + .fpus = fpus_8088, + .rspeed = 12000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "16", + .cpu_type = CPU_8088, + .fpus = fpus_8088, + .rspeed = 16000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { .name = "", 0 } } }, { .package = CPU_PKG_8088_EUROPC, @@ -91,10 +205,58 @@ const cpu_family_t cpu_families[] = { .name = "8088", .internal_name = "8088_europc", .cpus = (const CPU[]) { - {"4.77", CPU_8088, fpus_8088, 4772728, 1, 5000, 0, 0, 0, CPU_ALTERNATE_XTAL, 0,0,0,0, 1}, - {"7.16", CPU_8088, fpus_8088, 7159092, 1, 5000, 0, 0, 0, CPU_ALTERNATE_XTAL, 0,0,0,0, 1}, - {"9.54", CPU_8088, fpus_8088, 9545456, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 1}, - {"", 0} + { + .name = "4.77", + .cpu_type = CPU_8088, + .fpus = fpus_8088, + .rspeed = 4772728, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = CPU_ALTERNATE_XTAL, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "7.16", + .cpu_type = CPU_8088, + .fpus = fpus_8088, + .rspeed = 7159092, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = CPU_ALTERNATE_XTAL, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "9.54", + .cpu_type = CPU_8088, + .fpus = fpus_8088, + .rspeed = 9545456, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { .name = "", 0 } } }, { .package = CPU_PKG_8086, @@ -102,13 +264,109 @@ const cpu_family_t cpu_families[] = { .name = "8086", .internal_name = "8086", .cpus = (const CPU[]) { - {"7.16", CPU_8086, fpus_8088, 7159092, 1, 5000, 0, 0, 0, CPU_ALTERNATE_XTAL, 0,0,0,0, 1}, - {"8", CPU_8086, fpus_8088, 8000000, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 1}, - {"9.54", CPU_8086, fpus_8088, 9545456, 1, 5000, 0, 0, 0, CPU_ALTERNATE_XTAL, 0,0,0,0, 1}, - {"10", CPU_8086, fpus_8088, 10000000, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 1}, - {"12", CPU_8086, fpus_8088, 12000000, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 1}, - {"16", CPU_8086, fpus_8088, 16000000, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 2}, - {"", 0} + { + .name = "7.16", + .cpu_type = CPU_8086, + .fpus = fpus_8088, + .rspeed = 7159092, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = CPU_ALTERNATE_XTAL, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "8", + .cpu_type = CPU_8086, + .fpus = fpus_8088, + .rspeed = 8000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "9.54", + .cpu_type = CPU_8086, + .fpus = fpus_8088, + .rspeed = 9545456, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = CPU_ALTERNATE_XTAL, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "10", + .cpu_type = CPU_8086, + .fpus = fpus_8088, + .rspeed = 10000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "12", + .cpu_type = CPU_8086, + .fpus = fpus_8088, + .rspeed = 12000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "16", + .cpu_type = CPU_8086, + .fpus = fpus_8088, + .rspeed = 16000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 2 + }, + { .name = "", 0 } } }, { .package = CPU_PKG_188, @@ -116,16 +374,160 @@ const cpu_family_t cpu_families[] = { .name = "80188", .internal_name = "80188", .cpus = (const CPU[]) { - {"6", CPU_188, fpus_8088, 6000000, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 1}, - {"7.16", CPU_188, fpus_8088, 7159092, 1, 5000, 0, 0, 0, CPU_ALTERNATE_XTAL, 0,0,0,0, 1}, - {"8", CPU_188, fpus_8088, 8000000, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 1}, - {"9.54", CPU_188, fpus_8088, 9545456, 1, 5000, 0, 0, 0, CPU_ALTERNATE_XTAL, 0,0,0,0, 1}, - {"10", CPU_188, fpus_8088, 10000000, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 1}, - {"12", CPU_188, fpus_8088, 12000000, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 1}, - {"16", CPU_188, fpus_8088, 16000000, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 2}, - {"20", CPU_188, fpus_8088, 20000000, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 3}, - {"25", CPU_188, fpus_8088, 25000000, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 3}, - {"", 0} + { + .name = "6", + .cpu_type = CPU_188, + .fpus = fpus_8088, + .rspeed = 6000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "7.16", + .cpu_type = CPU_188, + .fpus = fpus_8088, + .rspeed = 7159092, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = CPU_ALTERNATE_XTAL, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "8", + .cpu_type = CPU_188, + .fpus = fpus_8088, + .rspeed = 8000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "9.54", + .cpu_type = CPU_188, + .fpus = fpus_8088, + .rspeed = 9545456, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = CPU_ALTERNATE_XTAL, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "10", + .cpu_type = CPU_188, + .fpus = fpus_8088, + .rspeed = 10000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "12", + .cpu_type = CPU_188, + .fpus = fpus_8088, + .rspeed = 12000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "16", + .cpu_type = CPU_188, + .fpus = fpus_8088, + .rspeed = 16000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 2 + }, + { + .name = "20", + .cpu_type = CPU_188, + .fpus = fpus_8088, + .rspeed = 20000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 3 + }, + { + .name = "25", + .cpu_type = CPU_188, + .fpus = fpus_8088, + .rspeed = 25000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 3 + }, + { .name = "", 0 } } }, { .package = CPU_PKG_8088, @@ -133,12 +535,92 @@ const cpu_family_t cpu_families[] = { .name = "V20", .internal_name = "necv20", .cpus = (const CPU[]) { - {"4.77", CPU_V20, fpus_8088, 4772728, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 1}, - {"7.16", CPU_V20, fpus_8088, 7159092, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 1}, - {"10", CPU_V20, fpus_8088, 10000000, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 1}, - {"12", CPU_V20, fpus_8088, 12000000, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 1}, - {"16", CPU_V20, fpus_8088, 16000000, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 2}, - {"", 0} + { + .name = "4.77", + .cpu_type = CPU_V20, + .fpus = fpus_8088, + .rspeed = 4772728, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "7.16", + .cpu_type = CPU_V20, + .fpus = fpus_8088, + .rspeed = 7159092, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "10", + .cpu_type = CPU_V20, + .fpus = fpus_8088, + .rspeed = 10000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "12", + .cpu_type = CPU_V20, + .fpus = fpus_8088, + .rspeed = 12000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "16", + .cpu_type = CPU_V20, + .fpus = fpus_8088, + .rspeed = 16000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 2 + }, + { .name = "", 0 } } }, { .package = CPU_PKG_186, @@ -146,16 +628,160 @@ const cpu_family_t cpu_families[] = { .name = "80186", .internal_name = "80186", .cpus = (const CPU[]) { - {"6", CPU_186, fpus_80186, 6000000, 1, 0, 0, 0, 0, 0, 0,0,0,0, 1}, - {"7.16", CPU_186, fpus_80186, 7159092, 1, 0, 0, 0, 0, CPU_ALTERNATE_XTAL, 0,0,0,0, 1}, - {"8", CPU_186, fpus_80186, 8000000, 1, 0, 0, 0, 0, 0, 0,0,0,0, 1}, - {"9.54", CPU_186, fpus_80186, 9545456, 1, 0, 0, 0, 0, CPU_ALTERNATE_XTAL, 0,0,0,0, 1}, - {"10", CPU_186, fpus_80186, 10000000, 1, 0, 0, 0, 0, 0, 0,0,0,0, 1}, - {"12", CPU_186, fpus_80186, 12000000, 1, 0, 0, 0, 0, 0, 0,0,0,0, 1}, - {"16", CPU_186, fpus_80186, 16000000, 1, 0, 0, 0, 0, 0, 0,0,0,0, 2}, - {"20", CPU_186, fpus_80186, 20000000, 1, 0, 0, 0, 0, 0, 0,0,0,0, 3}, - {"25", CPU_186, fpus_80186, 25000000, 1, 0, 0, 0, 0, 0, 0,0,0,0, 3}, - {"", 0} + { + .name = "6", + .cpu_type = CPU_186, + .fpus = fpus_80186, + .rspeed = 6000000, + .multi = 1, + .voltage = 0, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "7.16", + .cpu_type = CPU_186, + .fpus = fpus_80186, + .rspeed = 7159092, + .multi = 1, + .voltage = 0, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = CPU_ALTERNATE_XTAL, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "8", + .cpu_type = CPU_186, + .fpus = fpus_80186, + .rspeed = 8000000, + .multi = 1, + .voltage = 0, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "9.54", + .cpu_type = CPU_186, + .fpus = fpus_80186, + .rspeed = 9545456, + .multi = 1, + .voltage = 0, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = CPU_ALTERNATE_XTAL, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "10", + .cpu_type = CPU_186, + .fpus = fpus_80186, + .rspeed = 10000000, + .multi = 1, + .voltage = 0, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "12", + .cpu_type = CPU_186, + .fpus = fpus_80186, + .rspeed = 12000000, + .multi = 1, + .voltage = 0, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "16", + .cpu_type = CPU_186, + .fpus = fpus_80186, + .rspeed = 16000000, + .multi = 1, + .voltage = 0, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 2 + }, + { + .name = "20", + .cpu_type = CPU_186, + .fpus = fpus_80186, + .rspeed = 20000000, + .multi = 1, + .voltage = 0, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 3 + }, + { + .name = "25", + .cpu_type = CPU_186, + .fpus = fpus_80186, + .rspeed = 25000000, + .multi = 1, + .voltage = 0, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 3 + }, + { .name = "", 0 } } }, { .package = CPU_PKG_8086, @@ -163,12 +789,92 @@ const cpu_family_t cpu_families[] = { .name = "V30", .internal_name = "necv30", .cpus = (const CPU[]) { - {"5", CPU_V30, fpus_80186, 5000000, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 1}, - {"8", CPU_V30, fpus_80186, 8000000, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 1}, - {"10", CPU_V30, fpus_80186, 10000000, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 1}, - {"12", CPU_V30, fpus_80186, 12000000, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 1}, - {"16", CPU_V30, fpus_80186, 16000000, 1, 5000, 0, 0, 0, 0, 0,0,0,0, 2}, - {"", 0} + { + .name = "5", + .cpu_type = CPU_V30, + .fpus = fpus_80186, + .rspeed = 5000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "8", + .cpu_type = CPU_V30, + .fpus = fpus_80186, + .rspeed = 8000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "10", + .cpu_type = CPU_V30, + .fpus = fpus_80186, + .rspeed = 10000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "12", + .cpu_type = CPU_V30, + .fpus = fpus_80186, + .rspeed = 12000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 1 + }, + { + .name = "16", + .cpu_type = CPU_V30, + .fpus = fpus_80186, + .rspeed = 16000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 0, + .mem_write_cycles = 0, + .cache_read_cycles = 0, + .cache_write_cycles = 0, + .atclk_div = 2 + }, + { .name = "", 0 } } }, { .package = CPU_PKG_286, @@ -176,14 +882,126 @@ const cpu_family_t cpu_families[] = { .name = "80286", .internal_name = "286", .cpus = (const CPU[]) { - {"6", CPU_286, fpus_80286, 6000000, 1, 5000, 0, 0, 0, 0, 2,2,2,2, 1}, - {"8", CPU_286, fpus_80286, 8000000, 1, 5000, 0, 0, 0, 0, 2,2,2,2, 1}, - {"10", CPU_286, fpus_80286, 10000000, 1, 5000, 0, 0, 0, 0, 2,2,2,2, 1}, - {"12", CPU_286, fpus_80286, 12500000, 1, 5000, 0, 0, 0, 0, 3,3,3,3, 2}, - {"16", CPU_286, fpus_80286, 16000000, 1, 5000, 0, 0, 0, 0, 3,3,3,3, 2}, - {"20", CPU_286, fpus_80286, 20000000, 1, 5000, 0, 0, 0, 0, 4,4,4,4, 3}, - {"25", CPU_286, fpus_80286, 25000000, 1, 5000, 0, 0, 0, 0, 4,4,4,4, 3}, - {"", 0} + { + .name = "6", + .cpu_type = CPU_286, + .fpus = fpus_80286, + .rspeed = 6000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 2, + .mem_write_cycles = 2, + .cache_read_cycles = 2, + .cache_write_cycles = 2, + .atclk_div = 1 + }, + { + .name = "8", + .cpu_type = CPU_286, + .fpus = fpus_80286, + .rspeed = 8000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 2, + .mem_write_cycles = 2, + .cache_read_cycles = 2, + .cache_write_cycles = 2, + .atclk_div = 1 + }, + { + .name = "10", + .cpu_type = CPU_286, + .fpus = fpus_80286, + .rspeed = 10000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 2, + .mem_write_cycles = 2, + .cache_read_cycles = 2, + .cache_write_cycles = 2, + .atclk_div = 1 + }, + { + .name = "12", + .cpu_type = CPU_286, + .fpus = fpus_80286, + .rspeed = 12500000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 3, + .mem_write_cycles = 3, + .cache_read_cycles = 3, + .cache_write_cycles = 3, + .atclk_div = 2 + }, + { + .name = "16", + .cpu_type = CPU_286, + .fpus = fpus_80286, + .rspeed = 16000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 3, + .mem_write_cycles = 3, + .cache_read_cycles = 3, + .cache_write_cycles = 3, + .atclk_div = 2 + }, + { + .name = "20", + .cpu_type = CPU_286, + .fpus = fpus_80286, + .rspeed = 20000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 4, + .mem_write_cycles = 4, + .cache_read_cycles = 4, + .cache_write_cycles = 4, + .atclk_div = 3 + }, + { + .name = "25", + .cpu_type = CPU_286, + .fpus = fpus_80286, + .rspeed = 25000000, + .multi = 1, + .voltage = 5000, + .edx_reset = 0, + .cpuid_model = 0, + .cyrix_id = 0, + .cpu_flags = 0, + .mem_read_cycles = 4, + .mem_write_cycles = 4, + .cache_read_cycles = 4, + .cache_write_cycles = 4, + .atclk_div = 3 + }, + { .name = "", 0 } } }, { .package = CPU_PKG_386SX, @@ -1946,6 +2764,7 @@ const cpu_legacy_machine_t cpu_legacy_table[] = { { "award286", cputables_286 }, { "gw286ct", cputables_286 }, { "gdc212m", cputables_286 }, + { "super286c", cputables_286 }, { "super286tr", cputables_286 }, { "spc4200p", cputables_286 }, { "spc4216p", cputables_286 }, diff --git a/src/cpu/x86seg.c b/src/cpu/x86seg.c index eaa63f846..96061d3fa 100644 --- a/src/cpu/x86seg.c +++ b/src/cpu/x86seg.c @@ -1796,7 +1796,9 @@ pmodeiret(int is32) } if (cpu_state.flags & NT_FLAG) { + cpl_override = 1; seg = readmemw(tr.base, 0); + cpl_override = 0; addr = seg & 0xfff8; if (seg & 0x0004) { x86seg_log("TS LDT %04X %04X IRET\n", seg, gdt.limit); @@ -1809,8 +1811,8 @@ pmodeiret(int is32) } addr += gdt.base; } - cpl_override = 1; read_descriptor(addr, segdat, segdat32, 1); + cpl_override = 1; op_taskswitch286(seg, segdat, segdat[2] & 0x0800); cpl_override = 0; return; diff --git a/src/device/hwm_gl518sm.c b/src/device/hwm_gl518sm.c index 01f917b32..6ba1083d9 100644 --- a/src/device/hwm_gl518sm.c +++ b/src/device/hwm_gl518sm.c @@ -31,11 +31,13 @@ #define CLAMP(a, min, max) (((a) < (min)) ? (min) : (((a) > (max)) ? (max) : (a))) -/* Formulas and factors derived from Linux's gl518sm.c driver. */ -#define GL518SM_RPM_TO_REG(r, d) ((r) ? CLAMP((480000 + (r) * (d) / 2) / (r) * (d), 1, 255) : 0) +/* Formulas and factors derived from Linux's gl518sm.c and gl520sm.c drivers. */ +#define GL518SM_RPM_TO_REG(r, d) ((r) ? (480000 / (CLAMP((r), (480000 >> (d)) / 255, (480000 >> (d))) << (d))) : 0) #define GL518SM_VOLTAGE_TO_REG(v) ((uint8_t) round((v) / 19.0)) #define GL518SM_VDD_TO_REG(v) ((uint8_t) (((v) *4) / 95.0)) +#define GL520SM 0x100 + typedef struct gl518sm_t { uint32_t local; hwm_values_t *values; @@ -128,18 +130,22 @@ gl518sm_read(gl518sm_t *dev, uint8_t reg) switch (reg) { case 0x04: /* temperature */ - ret = (dev->values->temperatures[0] + 119) & 0xff; + ret = (dev->values->temperatures[0] + ((dev->local & GL520SM) ? 130 : 119)) & 0xff; break; case 0x07: /* fan speeds */ - ret = GL518SM_RPM_TO_REG(dev->values->fans[0], 1 << ((dev->regs[0x0f] >> 6) & 0x3)) << 8; - ret |= GL518SM_RPM_TO_REG(dev->values->fans[1], 1 << ((dev->regs[0x0f] >> 4) & 0x3)); + ret = GL518SM_RPM_TO_REG(dev->values->fans[0], (dev->regs[0x0f] >> 6) & 0x3) << 8; + ret |= GL518SM_RPM_TO_REG(dev->values->fans[1], (dev->regs[0x0f] >> 4) & 0x3); break; case 0x0d: /* VIN3 */ ret = GL518SM_VOLTAGE_TO_REG(dev->values->voltages[2]); break; + case 0x0e: /* temperature 2 */ + ret = (dev->local & GL520SM) ? ((dev->values->temperatures[1] + 130) & 0xff) : dev->regs[reg]; + break; + case 0x13: /* VIN2 */ ret = GL518SM_VOLTAGE_TO_REG(dev->values->voltages[1]); break; @@ -217,6 +223,11 @@ gl518sm_write(gl518sm_t *dev, uint8_t reg, uint16_t val) gl518sm_reset(dev); break; + case 0x0e: + if (dev->local & GL520SM) + return 0; + break; + case 0x0f: dev->regs[reg] = val & 0xf8; break; @@ -238,10 +249,16 @@ gl518sm_reset(gl518sm_t *dev) { memset(dev->regs, 0, sizeof(dev->regs)); - dev->regs[0x00] = 0x80; - dev->regs[0x01] = 0x80; /* revision 0x80 can read all voltages */ - dev->regs[0x05] = 0xc7; - dev->regs[0x06] = 0xc2; + if (dev->local & GL520SM) { + dev->regs[0x00] = 0x20; + dev->regs[0x01] = 0x00; + dev->regs[0x03] = 0x04; + } else { + dev->regs[0x00] = 0x80; + dev->regs[0x01] = 0x80; /* revision 0x80 can read all voltages */ + dev->regs[0x05] = 0xc7; + dev->regs[0x06] = 0xc2; + } dev->regs[0x08] = 0x6464; dev->regs[0x09] = 0xdac5; dev->regs[0x0a] = 0xdac5; @@ -279,7 +296,8 @@ gl518sm_init(const device_t *info) }, { /* temperatures */ - 30 /* usually CPU */ + 30, /* usually CPU */ + 30 /* GL520SM only: usually System */ }, { /* voltages */ @@ -327,3 +345,34 @@ const device_t gl518sm_2d_device = { .force_redraw = NULL, .config = NULL }; + +/* GL520SM on SMBus address 2Ch */ +const device_t gl520sm_2c_device = { + .name = "Genesys Logic GL520SM Hardware Monitor", + .internal_name = "gl520sm_2c", + .flags = DEVICE_ISA, + .local = GL520SM | 0x2c, + .init = gl518sm_init, + .close = gl518sm_close, + .reset = NULL, + { .available = NULL }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; + + +/* GL520SM on SMBus address 2Dh */ +const device_t gl520sm_2d_device = { + .name = "Genesys Logic GL520SM Hardware Monitor", + .internal_name = "gl520sm_2d", + .flags = DEVICE_ISA, + .local = GL520SM | 0x2d, + .init = gl518sm_init, + .close = gl518sm_close, + .reset = NULL, + { .available = NULL }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; diff --git a/src/device/isapnp.c b/src/device/isapnp.c index 26301b622..7b9d570bb 100644 --- a/src/device/isapnp.c +++ b/src/device/isapnp.c @@ -29,29 +29,21 @@ #include <86box/plat_unused.h> #define CHECK_CURRENT_LD() \ - if (!dev->current_ld) { \ + if (!ld) { \ isapnp_log("ISAPnP: No logical device selected\n"); \ - break; \ + goto vendor_defined; \ } -#define CHECK_CURRENT_CARD() \ - if (1) { \ - card = dev->first_card; \ - while (card) { \ - if (card->enable && (card->state == PNP_STATE_CONFIG)) \ - break; \ - card = card->next; \ - } \ - if (!card) { \ - isapnp_log("ISAPnP: No card in CONFIG state\n"); \ - break; \ - } \ +#define CHECK_CURRENT_CARD() \ + if (!card) { \ + isapnp_log("ISAPnP: No card in CONFIG state\n"); \ + break; \ } -static const uint8_t pnp_init_key[32] = { 0x6A, 0xB5, 0xDA, 0xED, 0xF6, 0xFB, 0x7D, 0xBE, - 0xDF, 0x6F, 0x37, 0x1B, 0x0D, 0x86, 0xC3, 0x61, - 0xB0, 0x58, 0x2C, 0x16, 0x8B, 0x45, 0xA2, 0xD1, - 0xE8, 0x74, 0x3A, 0x9D, 0xCE, 0xE7, 0x73, 0x39 }; +const uint8_t isapnp_init_key[32] = { 0x6A, 0xB5, 0xDA, 0xED, 0xF6, 0xFB, 0x7D, 0xBE, + 0xDF, 0x6F, 0x37, 0x1B, 0x0D, 0x86, 0xC3, 0x61, + 0xB0, 0x58, 0x2C, 0x16, 0x8B, 0x45, 0xA2, 0xD1, + 0xE8, 0x74, 0x3A, 0x9D, 0xCE, 0xE7, 0x73, 0x39 }; static const device_t isapnp_device; #ifdef ENABLE_ISAPNP_LOG @@ -95,6 +87,7 @@ typedef struct _isapnp_card_ { uint8_t enable; uint8_t state; uint8_t csn; + uint8_t ld; uint8_t id_checksum; uint8_t serial_read; uint8_t serial_read_pair; @@ -265,15 +258,13 @@ isapnp_read_rangecheck(UNUSED(uint16_t addr), void *priv) } static uint8_t -isapnp_read_data(UNUSED(uint16_t addr), void *priv) +isapnp_read_common(isapnp_t *dev, isapnp_card_t *card, isapnp_device_t *ld, uint8_t reg) { - isapnp_t *dev = (isapnp_t *) priv; - uint8_t ret = 0xff; - uint8_t bit; - uint8_t next_shift; - isapnp_card_t *card; + uint8_t ret = 0xff; + uint8_t bit; + uint8_t next_shift; - switch (dev->reg) { + switch (reg) { case 0x01: /* Serial Isolation */ card = dev->first_card; while (card) { @@ -342,78 +333,52 @@ isapnp_read_data(UNUSED(uint16_t addr), void *priv) ret = 0x00; CHECK_CURRENT_LD(); - isapnp_log("ISAPnP: Query LDN for CSN %02X device %02X\n", dev->current_ld_card->csn, dev->current_ld->number); - ret = dev->current_ld->number; + isapnp_log("ISAPnP: Query LDN for CSN %02X device %02X\n", card->csn, ld->number); + ret = ld->number; break; - case 0x20: - case 0x21: - case 0x22: - case 0x23: - case 0x24: - case 0x25: - case 0x26: - case 0x27: - case 0x28: - case 0x29: - case 0x2a: - case 0x2b: - case 0x2c: - case 0x2d: - case 0x2e: - case 0x2f: + case 0x20 ... 0x2f: + case 0x38 ... 0x3f: + case 0xa9 ... 0xff: +vendor_defined: CHECK_CURRENT_CARD(); - isapnp_log("ISAPnP: Read vendor-defined register %02X from CSN %02X\n", dev->reg, card->csn); + isapnp_log("ISAPnP: Read vendor-defined register %02X from CSN %02X device %02X\n", reg, card->csn, ld ? ld->number : -1); if (card->read_vendor_reg) - ret = card->read_vendor_reg(0, dev->reg, card->priv); - break; - - case 0x38: - case 0x39: - case 0x3a: - case 0x3b: - case 0x3c: - case 0x3d: - case 0x3e: - case 0x3f: - case 0xf0: - case 0xf1: - case 0xf2: - case 0xf3: - case 0xf4: - case 0xf5: - case 0xf6: - case 0xf7: - case 0xf8: - case 0xf9: - case 0xfa: - case 0xfb: - case 0xfc: - case 0xfd: - case 0xfe: - CHECK_CURRENT_LD(); - isapnp_log("ISAPnP: Read vendor-defined register %02X from CSN %02X device %02X\n", dev->reg, dev->current_ld_card->csn, dev->current_ld->number); - if (dev->current_ld_card->read_vendor_reg) - ret = dev->current_ld_card->read_vendor_reg(dev->current_ld->number, dev->reg, dev->current_ld_card->priv); + ret = card->read_vendor_reg(ld ? ld->number : -1, reg, card->priv); break; default: - if (dev->reg >= 0x30) { + if (reg >= 0x30) { CHECK_CURRENT_LD(); - isapnp_log("ISAPnP: Read register %02X from CSN %02X device %02X\n", dev->reg, dev->current_ld_card->csn, dev->current_ld->number); - ret = dev->current_ld->regs[dev->reg]; + isapnp_log("ISAPnP: Read register %02X from CSN %02X device %02X\n", reg, card->csn, ld->number); + ret = ld->regs[reg]; } break; } - isapnp_log("ISAPnP: read_data(%02X) = %02X\n", dev->reg, ret); + isapnp_log("ISAPnP: read_common(%02X) = %02X\n", reg, ret); return ret; } +static uint8_t +isapnp_read_data(UNUSED(uint16_t addr), void *priv) +{ + isapnp_t *dev = (isapnp_t *) priv; + isapnp_card_t *card = dev->first_card; + while (card) { + if (card->enable && (card->state == PNP_STATE_CONFIG)) + break; + card = card->next; + } + + isapnp_log("ISAPnP: read_data() => "); + return isapnp_read_common(dev, card, dev->current_ld, dev->reg); +} + static void isapnp_set_read_data(uint16_t addr, isapnp_t *dev) { @@ -445,7 +410,7 @@ isapnp_write_addr(UNUSED(uint16_t addr), uint8_t val, void *priv) if (card->state == PNP_STATE_WAIT_FOR_KEY) { /* checking only the first card should be fine */ /* Check written value against LFSR key. */ - if (val == pnp_init_key[dev->key_pos]) { + if (val == isapnp_init_key[dev->key_pos]) { dev->key_pos++; if (!dev->key_pos) { isapnp_log("ISAPnP: Key unlocked, putting cards to SLEEP\n"); @@ -462,17 +427,14 @@ isapnp_write_addr(UNUSED(uint16_t addr), uint8_t val, void *priv) } static void -isapnp_write_data(UNUSED(uint16_t addr), uint8_t val, void *priv) +isapnp_write_common(isapnp_t *dev, isapnp_card_t *card, isapnp_device_t *ld, uint8_t reg, uint8_t val) { - isapnp_t *dev = (isapnp_t *) priv; - isapnp_card_t *card; - isapnp_device_t *ld; - uint16_t io_addr; - uint16_t reset_cards = 0; + uint16_t io_addr; + uint16_t reset_cards = 0; - isapnp_log("ISAPnP: write_data(%02X)\n", val); + isapnp_log("ISAPnP: write_common(%02X, %02X)\n", reg, val); - switch (dev->reg) { + switch (reg) { case 0x00: /* Set RD_DATA Port */ isapnp_set_read_data((val << 2) | 3, dev); isapnp_log("ISAPnP: Read data port set to %04X\n", dev->read_data_addr); @@ -526,7 +488,7 @@ isapnp_write_data(UNUSED(uint16_t addr), uint8_t val, void *priv) while (card) { if (card->csn == val) { card->rom_pos = 0; - card->id_checksum = pnp_init_key[0]; + card->id_checksum = isapnp_init_key[0]; if (card->state == PNP_STATE_SLEEP) card->state = (val == 0) ? PNP_STATE_ISOLATION : PNP_STATE_CONFIG; } else { @@ -551,6 +513,7 @@ isapnp_write_data(UNUSED(uint16_t addr), uint8_t val, void *priv) case 0x07: /* Logical Device Number */ CHECK_CURRENT_CARD(); + card->ld = val; ld = card->first_ld; while (ld) { if (ld->number == val) { @@ -570,10 +533,10 @@ isapnp_write_data(UNUSED(uint16_t addr), uint8_t val, void *priv) case 0x30: /* Activate */ CHECK_CURRENT_LD(); - isapnp_log("ISAPnP: %sctivate CSN %02X device %02X\n", (val & 0x01) ? "A" : "Dea", dev->current_ld_card->csn, dev->current_ld->number); + isapnp_log("ISAPnP: %sctivate CSN %02X device %02X\n", (val & 0x01) ? "A" : "Dea", card->csn, ld->number); - dev->current_ld->regs[dev->reg] = val & 0x01; - isapnp_device_config_changed(dev->current_ld_card, dev->current_ld); + ld->regs[reg] = val & 0x01; + isapnp_device_config_changed(card, ld); break; @@ -581,80 +544,39 @@ isapnp_write_data(UNUSED(uint16_t addr), uint8_t val, void *priv) CHECK_CURRENT_LD(); for (uint8_t i = 0; i < 8; i++) { - if (!dev->current_ld->io_len[i]) + if (!ld->io_len[i]) continue; - io_addr = (dev->current_ld->regs[0x60 + (2 * i)] << 8) | dev->current_ld->regs[0x61 + (2 * i)]; - if (dev->current_ld->regs[dev->reg] & 0x02) - io_removehandler(io_addr, dev->current_ld->io_len[i], isapnp_read_rangecheck, NULL, NULL, NULL, NULL, NULL, dev->current_ld); + io_addr = (ld->regs[0x60 + (2 * i)] << 8) | ld->regs[0x61 + (2 * i)]; + if (ld->regs[reg] & 0x02) + io_removehandler(io_addr, ld->io_len[i], isapnp_read_rangecheck, NULL, NULL, NULL, NULL, NULL, ld); if (val & 0x02) - io_sethandler(io_addr, dev->current_ld->io_len[i], isapnp_read_rangecheck, NULL, NULL, NULL, NULL, NULL, dev->current_ld); + io_sethandler(io_addr, ld->io_len[i], isapnp_read_rangecheck, NULL, NULL, NULL, NULL, NULL, ld); } - dev->current_ld->regs[dev->reg] = val & 0x03; - isapnp_device_config_changed(dev->current_ld_card, dev->current_ld); + ld->regs[reg] = val & 0x03; + isapnp_device_config_changed(card, ld); break; - case 0x20: - case 0x21: - case 0x22: - case 0x23: - case 0x24: - case 0x25: - case 0x26: - case 0x27: - case 0x28: - case 0x29: - case 0x2a: - case 0x2b: - case 0x2c: - case 0x2d: - case 0x2e: - case 0x2f: + case 0x20 ... 0x2f: + case 0x38 ... 0x3f: + case 0xa9 ... 0xff: +vendor_defined: CHECK_CURRENT_CARD(); - isapnp_log("ISAPnP: Write %02X to vendor-defined register %02X on CSN %02X\n", val, dev->reg, card->csn); + isapnp_log("ISAPnP: Write %02X to vendor-defined register %02X on CSN %02X device %02X\n", val, reg, card->csn, ld ? ld->number : -1); if (card->write_vendor_reg) - card->write_vendor_reg(0, dev->reg, val, card->priv); - break; - - case 0x38: - case 0x39: - case 0x3a: - case 0x3b: - case 0x3c: - case 0x3d: - case 0x3e: - case 0x3f: - case 0xf0: - case 0xf1: - case 0xf2: - case 0xf3: - case 0xf4: - case 0xf5: - case 0xf6: - case 0xf7: - case 0xf8: - case 0xf9: - case 0xfa: - case 0xfb: - case 0xfc: - case 0xfd: - case 0xfe: - CHECK_CURRENT_LD(); - isapnp_log("ISAPnP: Write %02X to vendor-defined register %02X on CSN %02X device %02X\n", val, dev->reg, dev->current_ld_card->csn, dev->current_ld->number); - if (dev->current_ld_card->write_vendor_reg) - dev->current_ld_card->write_vendor_reg(dev->current_ld->number, dev->reg, val, dev->current_ld_card->priv); + card->write_vendor_reg(ld ? ld->number : -1, reg, val, card->priv); break; default: - if (dev->reg >= 0x40) { + if (reg >= 0x40) { CHECK_CURRENT_LD(); - isapnp_log("ISAPnP: Write %02X to register %02X on CSN %02X device %02X\n", val, dev->reg, dev->current_ld_card->csn, dev->current_ld->number); + isapnp_log("ISAPnP: Write %02X to register %02X on CSN %02X device %02X\n", val, reg, card->csn, ld->number); - switch (dev->reg) { + switch (reg) { case 0x42: case 0x4a: case 0x52: @@ -664,7 +586,7 @@ isapnp_write_data(UNUSED(uint16_t addr), uint8_t val, void *priv) case 0x94: case 0xa4: /* Read-only memory range length / upper limit bit. */ - val = (val & 0xfe) | (dev->current_ld->regs[dev->reg] & 0x01); + val = (val & 0xfe) | (ld->regs[reg] & 0x01); break; case 0x60: @@ -676,21 +598,21 @@ isapnp_write_data(UNUSED(uint16_t addr), uint8_t val, void *priv) case 0x6c: case 0x6e: /* Discard upper address bits if this I/O range can only decode 10-bit. */ - if (!(dev->current_ld->io_16bit & (1 << ((dev->reg >> 1) & 0x07)))) + if (!(ld->io_16bit & (1 << ((reg >> 1) & 0x07)))) val &= 0x03; break; case 0x71: case 0x73: /* Limit IRQ types to supported ones. */ - if ((val & 0x01) && !(dev->current_ld->irq_types & ((dev->reg == 0x71) ? 0x0c : 0xc0))) /* level, not supported = force edge */ + if ((val & 0x01) && !(ld->irq_types & ((reg == 0x71) ? 0x0c : 0xc0))) /* level, not supported = force edge */ val &= ~0x01; - else if (!(val & 0x01) && !(dev->current_ld->irq_types & ((dev->reg == 0x71) ? 0x03 : 0x30))) /* edge, not supported = force level */ + else if (!(val & 0x01) && !(ld->irq_types & ((reg == 0x71) ? 0x03 : 0x30))) /* edge, not supported = force level */ val |= 0x01; - if ((val & 0x02) && !(dev->current_ld->irq_types & ((dev->reg == 0x71) ? 0x05 : 0x50))) /* high, not supported = force low */ + if ((val & 0x02) && !(ld->irq_types & ((reg == 0x71) ? 0x05 : 0x50))) /* high, not supported = force low */ val &= ~0x02; - else if (!(val & 0x02) && !(dev->current_ld->irq_types & ((dev->reg == 0x71) ? 0x0a : 0xa0))) /* low, not supported = force high */ + else if (!(val & 0x02) && !(ld->irq_types & ((reg == 0x71) ? 0x0a : 0xa0))) /* low, not supported = force high */ val |= 0x02; break; @@ -699,13 +621,31 @@ isapnp_write_data(UNUSED(uint16_t addr), uint8_t val, void *priv) break; } - dev->current_ld->regs[dev->reg] = val; - isapnp_device_config_changed(dev->current_ld_card, dev->current_ld); + ld->regs[reg] = val; + isapnp_device_config_changed(card, ld); } break; } } +static void +isapnp_write_data(UNUSED(uint16_t addr), uint8_t val, void *priv) +{ + isapnp_t *dev = (isapnp_t *) priv; + isapnp_card_t *card = NULL; + if (!card) { + card = dev->first_card; + while (card) { + if (card->enable && (card->state == PNP_STATE_CONFIG)) + break; + card = card->next; + } + } + + isapnp_log("ISAPnP: write_data(%02X) => ", val); + isapnp_write_common(dev, card, dev->current_ld, dev->reg, val); +} + static void * isapnp_init(UNUSED(const device_t *info)) { @@ -869,7 +809,7 @@ isapnp_update_card_rom(void *priv, uint8_t *rom, uint16_t rom_size) } #ifdef ENABLE_ISAPNP_LOG - isapnp_log(" bytes, %swritable, %sread cacheable, %s, %sshadowable, %sexpansion ROM\n", + isapnp_log(" bytes, %swritable, %sread cacheable, %s, %s, %sshadowable, %sexpansion ROM\n", (card->rom[i + 3] & 0x01) ? "not " : "", (card->rom[i + 3] & 0x02) ? "not " : "", (card->rom[i + 3] & 0x04) ? "upper limit" : "range length", @@ -1135,6 +1075,32 @@ isapnp_set_csn(void *priv, uint8_t csn) card->csn_changed(card->csn, card->priv); } +uint8_t +isapnp_read_reg(void *priv, uint8_t ldn, uint8_t reg) +{ + isapnp_card_t *card = (isapnp_card_t *) priv; + isapnp_device_t *ld = card->first_ld; + while (ld) { + if (ld->number == ldn) + break; + ld = ld->next; + } + return isapnp_read_common(device_get_priv(&isapnp_device), card, ld, reg); +} + +void +isapnp_write_reg(void *priv, uint8_t ldn, uint8_t reg, uint8_t val) +{ + isapnp_card_t *card = (isapnp_card_t *) priv; + isapnp_device_t *ld = card->first_ld; + while (ld) { + if (ld->number == ldn) + break; + ld = ld->next; + } + isapnp_write_common(device_get_priv(&isapnp_device), card, ld, reg, val); +} + void isapnp_set_device_defaults(void *priv, uint8_t ldn, const isapnp_device_config_t *config) { diff --git a/src/device/keyboard_xt.c b/src/device/keyboard_xt.c index 028c58493..f65a6dffc 100644 --- a/src/device/keyboard_xt.c +++ b/src/device/keyboard_xt.c @@ -54,19 +54,22 @@ #define STAT_IFULL 0x02 #define STAT_OFULL 0x01 -// Keyboard Types -#define KBD_TYPE_PC81 0 -#define KBD_TYPE_PC82 1 -#define KBD_TYPE_XT82 2 -#define KBD_TYPE_XT86 3 -#define KBD_TYPE_COMPAQ 4 -#define KBD_TYPE_TANDY 5 -#define KBD_TYPE_TOSHIBA 6 -#define KBD_TYPE_VTECH 7 -#define KBD_TYPE_OLIVETTI 8 -#define KBD_TYPE_ZENITH 9 -#define KBD_TYPE_PRAVETZ 10 -#define KBD_TYPE_XTCLONE 11 +/* Keyboard Types */ +enum { + KBD_TYPE_PC81 = 0, + KBD_TYPE_PC82, + KBD_TYPE_XT82, + KBD_TYPE_XT86, + KBD_TYPE_COMPAQ, + KBD_TYPE_TANDY, + KBD_TYPE_TOSHIBA, + KBD_TYPE_VTECH, + KBD_TYPE_OLIVETTI, + KBD_TYPE_ZENITH, + KBD_TYPE_PRAVETZ, + KBD_TYPE_HYUNDAI, + KBD_TYPE_XTCLONE +}; typedef struct xtkbd_t { int want_irq; @@ -530,7 +533,7 @@ kbd_write(uint16_t port, uint8_t val, void *priv) switch (port) { case 0x61: /* Keyboard Control Register (aka Port B) */ - if (!(val & 0x80)) { + if (!(val & 0x80) || (kbd->type == KBD_TYPE_HYUNDAI)) { new_clock = !!(val & 0x40); if (!kbd->clock && new_clock) { key_queue_start = key_queue_end = 0; @@ -540,7 +543,7 @@ kbd_write(uint16_t port, uint8_t val, void *priv) } } kbd->pb = val; - if (!(kbd->pb & 0x80)) + if (!(kbd->pb & 0x80) || (kbd->type == KBD_TYPE_HYUNDAI)) kbd->clock = !!(kbd->pb & 0x40); ppi.pb = val; @@ -603,10 +606,10 @@ kbd_read(uint16_t port, void *priv) (kbd->type == KBD_TYPE_PC82) || (kbd->type == KBD_TYPE_PRAVETZ) || (kbd->type == KBD_TYPE_XT82) || (kbd->type == KBD_TYPE_XT86) || (kbd->type == KBD_TYPE_XTCLONE) || (kbd->type == KBD_TYPE_COMPAQ) || - (kbd->type == KBD_TYPE_ZENITH))) { + (kbd->type == KBD_TYPE_ZENITH) || (kbd->type == KBD_TYPE_HYUNDAI))) { if ((kbd->type == KBD_TYPE_PC81) || (kbd->type == KBD_TYPE_PC82) || (kbd->type == KBD_TYPE_XTCLONE) || (kbd->type == KBD_TYPE_COMPAQ) || - (kbd->type == KBD_TYPE_PRAVETZ)) + (kbd->type == KBD_TYPE_PRAVETZ) || (kbd->type == KBD_TYPE_HYUNDAI)) ret = (kbd->pd & ~0x02) | (hasfpu ? 0x02 : 0x00); else if ((kbd->type == KBD_TYPE_XT82) || (kbd->type == KBD_TYPE_XT86)) /* According to Ruud on the PCem forum, this is supposed to @@ -696,7 +699,7 @@ kbd_read(uint16_t port, void *priv) case 0x63: /* Keyboard Configuration Register (aka Port D) */ if ((kbd->type == KBD_TYPE_XT82) || (kbd->type == KBD_TYPE_XT86) || (kbd->type == KBD_TYPE_XTCLONE) || (kbd->type == KBD_TYPE_COMPAQ) || - (kbd->type == KBD_TYPE_TOSHIBA)) + (kbd->type == KBD_TYPE_TOSHIBA) || (kbd->type == KBD_TYPE_HYUNDAI)) ret = kbd->pd; break; @@ -762,7 +765,7 @@ kbd_init(const device_t *info) (kbd->type == KBD_TYPE_PRAVETZ) || (kbd->type == KBD_TYPE_XT82) || (kbd->type <= KBD_TYPE_XT86) || (kbd->type == KBD_TYPE_XTCLONE) || (kbd->type == KBD_TYPE_COMPAQ) || (kbd->type == KBD_TYPE_TOSHIBA) || - (kbd->type == KBD_TYPE_OLIVETTI)) { + (kbd->type == KBD_TYPE_OLIVETTI) || (kbd->type == KBD_TYPE_HYUNDAI)) { /* DIP switch readout: bit set = OFF, clear = ON. */ if (kbd->type == KBD_TYPE_OLIVETTI) /* Olivetti M19 @@ -781,7 +784,8 @@ kbd_init(const device_t *info) /* Switches 3, 4 - memory size. */ if ((kbd->type == KBD_TYPE_XT86) || (kbd->type == KBD_TYPE_XTCLONE) || - (kbd->type == KBD_TYPE_COMPAQ) || (kbd->type == KBD_TYPE_TOSHIBA)) { + (kbd->type == KBD_TYPE_HYUNDAI) || (kbd->type == KBD_TYPE_COMPAQ) || + (kbd->type == KBD_TYPE_TOSHIBA)) { switch (mem_size) { case 256: kbd->pd |= 0x00; @@ -1076,6 +1080,20 @@ const device_t keyboard_xt_zenith_device = { .config = NULL }; +const device_t keyboard_xt_hyundai_device = { + .name = "Hyundai XT Keyboard", + .internal_name = "keyboard_x_hyundai", + .flags = 0, + .local = KBD_TYPE_HYUNDAI, + .init = kbd_init, + .close = kbd_close, + .reset = kbd_reset, + { .available = NULL }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; + const device_t keyboard_xtclone_device = { .name = "XT (Clone) Keyboard", .internal_name = "keyboard_xtclone", diff --git a/src/disk/hdc_ide.c b/src/disk/hdc_ide.c index 2dffeeec3..2bb83b4ab 100644 --- a/src/disk/hdc_ide.c +++ b/src/disk/hdc_ide.c @@ -82,23 +82,23 @@ #define WIN_SEEK 0x70 #define WIN_DRIVE_DIAGNOSTICS 0x90 /* Execute Drive Diagnostics */ #define WIN_SPECIFY 0x91 /* Initialize Drive Parameters */ -#define WIN_PACKETCMD 0xA0 /* Send a packet command. */ -#define WIN_PIDENTIFY 0xA1 /* Identify ATAPI device */ -#define WIN_READ_MULTIPLE 0xC4 -#define WIN_WRITE_MULTIPLE 0xC5 -#define WIN_SET_MULTIPLE_MODE 0xC6 -#define WIN_READ_DMA 0xC8 -#define WIN_READ_DMA_ALT 0xC9 -#define WIN_WRITE_DMA 0xCA -#define WIN_WRITE_DMA_ALT 0xCB -#define WIN_STANDBYNOW1 0xE0 -#define WIN_IDLENOW1 0xE1 -#define WIN_SETIDLE1 0xE3 -#define WIN_CHECKPOWERMODE1 0xE5 -#define WIN_SLEEP1 0xE6 -#define WIN_IDENTIFY 0xEC /* Ask drive to identify itself */ -#define WIN_SET_FEATURES 0xEF -#define WIN_READ_NATIVE_MAX 0xF8 +#define WIN_PACKETCMD 0xa0 /* Send a packet command. */ +#define WIN_PIDENTIFY 0xa1 /* Identify ATAPI device */ +#define WIN_READ_MULTIPLE 0xc4 +#define WIN_WRITE_MULTIPLE 0xc5 +#define WIN_SET_MULTIPLE_MODE 0xc6 +#define WIN_READ_DMA 0xc8 +#define WIN_READ_DMA_ALT 0xc9 +#define WIN_WRITE_DMA 0xcA +#define WIN_WRITE_DMA_ALT 0xcB +#define WIN_STANDBYNOW1 0xe0 +#define WIN_IDLENOW1 0xe1 +#define WIN_SETIDLE1 0xe3 +#define WIN_CHECKPOWERMODE1 0xe5 +#define WIN_SLEEP1 0xe6 +#define WIN_IDENTIFY 0xeC /* Ask drive to identify itself */ +#define WIN_SET_FEATURES 0xeF +#define WIN_READ_NATIVE_MAX 0xf8 #define FEATURE_SET_TRANSFER_MODE 0x03 #define FEATURE_ENABLE_IRQ_OVERLAPPED 0x5d @@ -138,50 +138,90 @@ typedef struct ide_board_t { ide_board_t *ide_boards[IDE_BUS_MAX]; static uint8_t ide_ter_pnp_rom[] = { - 0x09, 0xf8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, /* BOX0001, serial 0, dummy checksum (filled in by isapnp_add_card) */ - 0x0a, 0x10, 0x10, /* PnP version 1.0, vendor version 1.0 */ - 0x82, 0x0e, 0x00, 'I', 'D', 'E', ' ', 'C', 'o', 'n', 't', 'r', 'o', 'l', 'l', 'e', 'r', /* ANSI identifier */ + /* BOX0001, serial 0, dummy checksum (filled in by isapnp_add_card) */ + 0x09, 0xf8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + /* PnP version 1.0, vendor version 1.0 */ + 0x0a, 0x10, 0x10, + /* ANSI identifier */ + 0x82, 0x0e, 0x00, 'I', 'D', 'E', ' ', 'C', 'o', 'n', 't', 'r', 'o', + 'l', 'l', 'e', 'r', - 0x15, 0x09, 0xf8, 0x00, 0x01, 0x00, /* logical device BOX0001 */ - 0x1c, 0x41, 0xd0, 0x06, 0x00, /* compatible device PNP0600 */ - 0x31, 0x00, /* start dependent functions, preferred */ - 0x22, 0x00, 0x08, /* IRQ 11 */ - 0x47, 0x01, 0xe8, 0x01, 0xe8, 0x01, 0x01, 0x08, /* I/O 0x1E8, decodes 16-bit, 1-byte alignment, 8 addresses */ - 0x47, 0x01, 0xee, 0x03, 0xee, 0x03, 0x01, 0x01, /* I/O 0x3EE, decodes 16-bit, 1-byte alignment, 1 address */ - 0x30, /* start dependent functions, acceptable */ - 0x22, 0xb8, 0x1e, /* IRQ 3/4/5/7/9/10/11/12 */ - 0x47, 0x01, 0xe8, 0x01, 0xe8, 0x01, 0x01, 0x08, /* I/O 0x1E8, decodes 16-bit, 1-byte alignment, 8 addresses */ - 0x47, 0x01, 0xee, 0x03, 0xee, 0x03, 0x01, 0x01, /* I/O 0x3EE, decodes 16-bit, 1-byte alignment, 1 address */ - 0x30, /* start dependent functions, acceptable */ - 0x22, 0xb8, 0x1e, /* IRQ 3/4/5/7/9/10/11/12 */ - 0x47, 0x01, 0x00, 0x01, 0xf8, 0xff, 0x08, 0x08, /* I/O 0x100-0xFFF8, decodes 16-bit, 8-byte alignment, 8 addresses */ - 0x47, 0x01, 0x00, 0x01, 0xff, 0xff, 0x01, 0x01, /* I/O 0x100-0xFFFF, decodes 16-bit, 1-byte alignment, 1 address */ - 0x38, /* end dependent functions */ + /* Logical device BOX0001 */ + 0x15, 0x09, 0xf8, 0x00, 0x01, 0x00, + /* Compatible device PNP0600 */ + 0x1c, 0x41, 0xd0, 0x06, 0x00, + /* Start dependent functions, preferred */ + 0x31, 0x00, + /* IRQ 11 */ + 0x22, 0x00, 0x08, + /* I/O 0x1E8, decodes 16-bit, 1-byte alignment, 8 addresses */ + 0x47, 0x01, 0xe8, 0x01, 0xe8, 0x01, 0x01, 0x08, + /* I/O 0x3EE, decodes 16-bit, 1-byte alignment, 1 address */ + 0x47, 0x01, 0xee, 0x03, 0xee, 0x03, 0x01, 0x01, + /* Start dependent functions, acceptable */ + 0x30, + /* IRQ 3/4/5/7/9/10/11/12 */ + 0x22, 0xb8, 0x1e, + /* I/O 0x1E8, decodes 16-bit, 1-byte alignment, 8 addresses */ + 0x47, 0x01, 0xe8, 0x01, 0xe8, 0x01, 0x01, 0x08, + /* I/O 0x3EE, decodes 16-bit, 1-byte alignment, 1 address */ + 0x47, 0x01, 0xee, 0x03, 0xee, 0x03, 0x01, 0x01, + /* Start dependent functions, acceptable */ + 0x30, + /* IRQ 3/4/5/7/9/10/11/12 */ + 0x22, 0xb8, 0x1e, + /* I/O 0x100-0xFFF8, decodes 16-bit, 8-byte alignment, 8 addresses */ + 0x47, 0x01, 0x00, 0x01, 0xf8, 0xff, 0x08, 0x08, + /* I/O 0x100-0xFFFF, decodes 16-bit, 1-byte alignment, 1 address */ + 0x47, 0x01, 0x00, 0x01, 0xff, 0xff, 0x01, 0x01, + /* End dependent functions */ + 0x38, - 0x79, 0x00 /* end tag, dummy checksum (filled in by isapnp_add_card) */ + /* End tag, dummy checksum (filled in by isapnp_add_card) */ + 0x79, 0x00 }; static uint8_t ide_qua_pnp_rom[] = { - 0x09, 0xf8, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, /* BOX0001, serial 1, dummy checksum (filled in by isapnp_add_card) */ - 0x0a, 0x10, 0x10, /* PnP version 1.0, vendor version 1.0 */ - 0x82, 0x0e, 0x00, 'I', 'D', 'E', ' ', 'C', 'o', 'n', 't', 'r', 'o', 'l', 'l', 'e', 'r', /* ANSI identifier */ + /* BOX0001, serial 1, dummy checksum (filled in by isapnp_add_card) */ + 0x09, 0xf8, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + /* PnP version 1.0, vendor version 1.0 */ + 0x0a, 0x10, 0x10, + /* ANSI identifier */ + 0x82, 0x0e, 0x00, 'I', 'D', 'E', ' ', 'C', 'o', 'n', 't', 'r', 'o', + 'l', 'l', 'e', 'r', - 0x15, 0x09, 0xf8, 0x00, 0x01, 0x00, /* logical device BOX0001 */ - 0x1c, 0x41, 0xd0, 0x06, 0x00, /* compatible device PNP0600 */ - 0x31, 0x00, /* start dependent functions, preferred */ - 0x22, 0x00, 0x04, /* IRQ 10 */ - 0x47, 0x01, 0x68, 0x01, 0x68, 0x01, 0x01, 0x08, /* I/O 0x168, decodes 16-bit, 1-byte alignment, 8 addresses */ - 0x47, 0x01, 0x6e, 0x03, 0x6e, 0x03, 0x01, 0x01, /* I/O 0x36E, decodes 16-bit, 1-byte alignment, 1 address */ - 0x30, /* start dependent functions, acceptable */ - 0x22, 0xb8, 0x1e, /* IRQ 3/4/5/7/9/10/11/12 */ - 0x47, 0x01, 0x68, 0x01, 0x68, 0x01, 0x01, 0x08, /* I/O 0x168, decodes 16-bit, 1-byte alignment, 8 addresses */ - 0x47, 0x01, 0x6e, 0x03, 0x6e, 0x03, 0x01, 0x01, /* I/O 0x36E, decodes 16-bit, 1-byte alignment, 1 address */ - 0x30, /* start dependent functions, acceptable */ - 0x22, 0xb8, 0x1e, /* IRQ 3/4/5/7/9/10/11/12 */ - 0x47, 0x01, 0x00, 0x01, 0xf8, 0xff, 0x08, 0x08, /* I/O 0x100-0xFFF8, decodes 16-bit, 8-byte alignment, 8 addresses */ - 0x47, 0x01, 0x00, 0x01, 0xff, 0xff, 0x01, 0x01, /* I/O 0x100-0xFFFF, decodes 16-bit, 1-byte alignment, 1 address */ - 0x38, /* end dependent functions */ + /* Logical device BOX0001 */ + 0x15, 0x09, 0xf8, 0x00, 0x01, 0x00, + /* Compatible device PNP0600 */ + 0x1c, 0x41, 0xd0, 0x06, 0x00, + /* Start dependent functions, preferred */ + 0x31, 0x00, + /* IRQ 10 */ + 0x22, 0x00, 0x04, + /* I/O 0x168, decodes 16-bit, 1-byte alignment, 8 addresses */ + 0x47, 0x01, 0x68, 0x01, 0x68, 0x01, 0x01, 0x08, + /* I/O 0x36E, decodes 16-bit, 1-byte alignment, 1 address */ + 0x47, 0x01, 0x6e, 0x03, 0x6e, 0x03, 0x01, 0x01, + /* Start dependent functions, acceptable */ + 0x30, + /* IRQ 3/4/5/7/9/10/11/12 */ + 0x22, 0xb8, 0x1e, + /* I/O 0x168, decodes 16-bit, 1-byte alignment, 8 addresses */ + 0x47, 0x01, 0x68, 0x01, 0x68, 0x01, 0x01, 0x08, + /* I/O 0x36E, decodes 16-bit, 1-byte alignment, 1 address */ + 0x47, 0x01, 0x6e, 0x03, 0x6e, 0x03, 0x01, 0x01, + /* Start dependent functions, acceptable */ + 0x30, + /* IRQ 3/4/5/7/9/10/11/12 */ + 0x22, 0xb8, 0x1e, + /* I/O 0x100-0xFFF8, decodes 16-bit, 8-byte alignment, 8 addresses */ + 0x47, 0x01, 0x00, 0x01, 0xf8, 0xff, 0x08, 0x08, + /* I/O 0x100-0xFFFF, decodes 16-bit, 1-byte alignment, 1 address */ + 0x47, 0x01, 0x00, 0x01, 0xff, 0xff, 0x01, 0x01, + /* End dependent functions */ + 0x38, - 0x79, 0x00 /* end tag, dummy checksum (filled in by isapnp_add_card) */ + /* End tag, dummy checksum (filled in by isapnp_add_card) */ + 0x79, 0x00 }; ide_t *ide_drives[IDE_NUM]; @@ -336,7 +376,7 @@ ide_atapi_get_period(uint8_t channel) } static void -ide_irq_update(ide_board_t *dev) +ide_irq_update(ide_board_t *dev, int log) { ide_t *ide; uint8_t set; @@ -344,7 +384,10 @@ ide_irq_update(ide_board_t *dev) if (dev == NULL) return; - ide_log("IDE %i: IRQ update (%i)\n", dev->cur_dev >> 1, dev->irq); +#ifdef ENABLE_IDE_LOG + if (log) + ide_log("IDE %i: IRQ update (%i)\n", dev->cur_dev >> 1, dev->irq); +#endif ide = ide_drives[dev->cur_dev]; set = !(ide_boards[ide->board]->devctl & 2) && ide->irqstat; @@ -356,32 +399,22 @@ ide_irq_update(ide_board_t *dev) } void -ide_irq_raise(ide_t *ide) +ide_irq(ide_t *ide, int set, int log) { if (!ide_boards[ide->board]) return; - ide_log("IDE %i: IRQ raise\n", ide->channel); +#if defined(ENABLE_IDE_LOG) && (ENABLE_IDE_LOG == 2) + ide_log("IDE %i: IRQ %s\n", ide->channel, set ? "raise" : "lower"); +#endif - ide->irqstat = 1; - ide->service = 1; + ide->irqstat = set; + + if (set) + ide->service = 1; if (ide->selected) - ide_irq_update(ide_boards[ide->board]); -} - -void -ide_irq_lower(ide_t *ide) -{ - if (!ide_boards[ide->board]) - return; - - ide_log("IDE %i: IRQ lower\n", ide->channel); - - ide->irqstat = 0; - - if (ide->selected) - ide_irq_update(ide_boards[ide->board]); + ide_irq_update(ide_boards[ide->board], log); } /** @@ -590,7 +623,7 @@ ide_identify(ide_t *ide) if (ide->type == IDE_ATAPI) ide->identify(ide, !IDE_ATAPI_IS_EARLY && !ide_boards[ide->board]->force_ata3 && (bm != NULL)); - else if (ide->type != IDE_NONE) + else if (ide->type == IDE_HDD) ide_hd_identify(ide); else { fatal("IDE IDENTIFY or IDENTIFY PACKET DEVICE on non-attached IDE device\n"); @@ -682,15 +715,15 @@ ide_get_sector(ide_t *ide) uint32_t heads; uint32_t sectors; - if (ide->lba) + if (ide->tf->lba) return (off64_t) ide->lba_addr; else { heads = ide->cfg_hpc; sectors = ide->cfg_spt; - uint8_t sector = ide->sector ? (ide->sector - 1) : 0; + uint8_t sector = ide->tf->sector ? (ide->tf->sector - 1) : 0; - return ((((off64_t) ide->tf->cylinder * heads) + (off64_t) ide->head) * sectors) + + return ((((off64_t) ide->tf->cylinder * heads) + (off64_t) ide->tf->head) * sectors) + (off64_t) sector; } } @@ -701,15 +734,15 @@ ide_get_sector(ide_t *ide) static void ide_next_sector(ide_t *ide) { - if (ide->lba) + if (ide->tf->lba) ide->lba_addr++; else { - ide->sector++; - if ((ide->sector == 0) || (ide->sector == (ide->cfg_spt + 1))) { - ide->sector = 1; - ide->head++; - if ((ide->head == 0) || (ide->head == ide->cfg_hpc)) { - ide->head = 0; + ide->tf->sector++; + if ((ide->tf->sector == 0) || (ide->tf->sector == (ide->cfg_spt + 1))) { + ide->tf->sector = 1; + ide->tf->head++; + if ((ide->tf->head == 0) || (ide->head == ide->cfg_hpc)) { + ide->tf->head = 0; ide->tf->cylinder++; } } @@ -736,13 +769,12 @@ loadhd(ide_t *ide, int d, UNUSED(const char *fn)) void ide_set_signature(ide_t *ide) { - uint16_t ide_signatures[3] = { /* 0xffff */ 0x7f7f, 0x0000, 0xeb14 }; + uint16_t ide_signatures[4] = { 0x7f7f, 0x0000, 0xeb14, 0x7f7f }; - ide->sector = 1; - ide->head = 0; - - ide->tf->secount = 1; - ide->tf->cylinder = ide_signatures[ide->type]; + ide->tf->sector = 1; + ide->tf->head = 0; + ide->tf->secount = 1; + ide->tf->cylinder = ide_signatures[ide->type & ~IDE_SHADOW]; if (ide->type == IDE_HDD) ide->drive = 0; @@ -846,16 +878,16 @@ ide_set_sector(ide_t *ide, int64_t sector_num) { unsigned int cyl; unsigned int r; - if (ide->lba) { - ide->head = (sector_num >> 24) & 0xff; + if (ide->tf->lba) { + ide->tf->head = (sector_num >> 24) & 0xff; ide->tf->cylinder = (sector_num >> 8) & 0xffff; - ide->sector = sector_num & 0xff; + ide->tf->sector = sector_num & 0xff; } else { cyl = sector_num / (hdd[ide->hdd_num].hpc * hdd[ide->hdd_num].spt); r = sector_num % (hdd[ide->hdd_num].hpc * hdd[ide->hdd_num].spt); ide->tf->cylinder = cyl & 0xffff; - ide->head = ((r / hdd[ide->hdd_num].spt) & 0x0f) & 0xff; - ide->sector = ((r % hdd[ide->hdd_num].spt) + 1) & 0xff; + ide->tf->head = ((r / hdd[ide->hdd_num].spt) & 0x0f) & 0xff; + ide->tf->sector = ((r % hdd[ide->hdd_num].spt) + 1) & 0xff; } } @@ -961,14 +993,20 @@ ide_atapi_callback(ide_t *ide) "Complete" }; char *phase; - if (ide->sc->packet_status <= PHASE_COMPLETE) - phase = phases[ide->sc->packet_status]; - else if (ide->sc->packet_status == PHASE_ERROR) - phase = "Error"; - else if (ide->sc->packet_status == PHASE_NONE) - phase = "None"; - else - phase = "Unknown"; + switch (ide->sc->packet_status) { + default: + phase = "Unknown"; + break; + case PHASE_IDLE ... PHASE_COMPLETE: + phase = phases[ide->sc->packet_status]; + break; + case PHASE_ERROR: + phase = "Error"; + break; + case PHASE_NONE: + phase = "None"; + break; + } ide_log("Phase: %02X (%s)\n", ide->sc->packet_status, phase); #endif @@ -1169,36 +1207,38 @@ ide_write_data(ide_t *ide, uint16_t val, int length) uint8_t *idebufferb = (uint8_t *) ide->buffer; uint16_t *idebufferw = ide->buffer; - if (ide->command == WIN_PACKETCMD) { - if (ide->type == IDE_ATAPI) - ide_atapi_packet_write(ide, val, length); - else - ide->tf->pos = 0; - } else { - if (length == 2) { - idebufferw[ide->tf->pos >> 1] = val & 0xffff; - ide->tf->pos += 2; + if ((ide->type != IDE_NONE) && !(ide->type & IDE_SHADOW) && ide->buffer) { + if (ide->command == WIN_PACKETCMD) { + if (ide->type == IDE_ATAPI) + ide_atapi_packet_write(ide, val, length); + else + ide->tf->pos = 0; } else { - idebufferb[ide->tf->pos] = val & 0xff; - ide->tf->pos++; - } + if (length == 2) { + idebufferw[ide->tf->pos >> 1] = val & 0xffff; + ide->tf->pos += 2; + } else { + idebufferb[ide->tf->pos] = val & 0xff; + ide->tf->pos++; + } - if (ide->tf->pos >= 512) { - ide->tf->pos = 0; - ide->tf->atastat = BSY_STAT; - double seek_time = hdd_timing_write(&hdd[ide->hdd_num], ide_get_sector(ide), 1); - double xfer_time = ide_get_xfer_time(ide, 512); - double wait_time = seek_time + xfer_time; - if (ide->command == WIN_WRITE_MULTIPLE) { - if ((ide->blockcount + 1) >= ide->blocksize || ide->tf->secount == 1) { - ide_set_callback(ide, seek_time + xfer_time + ide->pending_delay); - ide->pending_delay = 0; - } else { - ide->pending_delay += wait_time; - ide_callback(ide); - } - } else - ide_set_callback(ide, wait_time); + if (ide->tf->pos >= 512) { + ide->tf->pos = 0; + ide->tf->atastat = BSY_STAT; + double seek_time = hdd_timing_write(&hdd[ide->hdd_num], ide_get_sector(ide), 1); + double xfer_time = ide_get_xfer_time(ide, 512); + double wait_time = seek_time + xfer_time; + if (ide->command == WIN_WRITE_MULTIPLE) { + if ((ide->blockcount + 1) >= ide->blocksize || ide->tf->secount == 1) { + ide_set_callback(ide, seek_time + xfer_time + ide->pending_delay); + ide->pending_delay = 0; + } else { + ide->pending_delay += wait_time; + ide_callback(ide); + } + } else + ide_set_callback(ide, wait_time); + } } } } @@ -1214,7 +1254,9 @@ ide_writew(uint16_t addr, uint16_t val, void *priv) ch = dev->cur_dev; ide = ide_drives[ch]; +#if defined(ENABLE_IDE_LOG) && (ENABLE_IDE_LOG == 2) ide_log("ide_writew(%04X, %04X, %08X)\n", addr, val, priv); +#endif addr &= 0x7; @@ -1246,7 +1288,9 @@ ide_writel(uint16_t addr, uint32_t val, void *priv) ch = dev->cur_dev; ide = ide_drives[ch]; +#if defined(ENABLE_IDE_LOG) && (ENABLE_IDE_LOG == 2) ide_log("ide_writel(%04X, %08X, %08X)\n", addr, val, priv); +#endif addr &= 0x7; @@ -1361,22 +1405,22 @@ ide_write_devctl(UNUSED(uint16_t addr), uint8_t val, void *priv) old = dev->devctl; dev->devctl = val; if (!(val & 0x02) && (old & 0x02)) - ide_irq_update(ide_boards[ide->board]); + ide_irq_update(ide_boards[ide->board], 1); } static void ide_reset_registers(ide_t *ide) { - uint16_t ide_signatures[3] = { /* 0xffff */ 0x7f7f, 0x0000, 0xeb14 }; + uint16_t ide_signatures[4] = { 0x7f7f, 0x0000, 0xeb14, 0x7f7f }; ide->tf->atastat = DRDY_STAT | DSC_STAT; ide->tf->error = 1; ide->tf->secount = 1; - ide->tf->cylinder = ide_signatures[ide->type]; + ide->tf->cylinder = ide_signatures[ide->type & ~IDE_SHADOW]; + ide->tf->sector = 1; + ide->tf->head = 0; - ide->sector = 1; - ide->head = 0; - ide->reset = 0; + ide->reset = 0; if (ide->type == IDE_ATAPI) ide->sc->callback = 0.0; @@ -1391,7 +1435,6 @@ ide_writeb(uint16_t addr, uint8_t val, void *priv) ide_t *ide; ide_t *ide_other; int ch; - int absent = 0; int bad = 0; int reset = 0; @@ -1399,64 +1442,74 @@ ide_writeb(uint16_t addr, uint8_t val, void *priv) ide = ide_drives[ch]; ide_other = ide_drives[ch ^ 1]; - /* Absent and is master or both are absent. */ - if ((ide->type == IDE_NONE) && ((ide_drives[ch ^ 1]->type == IDE_NONE) || !(ch & 1))) - absent = 1; - /* Absent and is slave and master is present. */ - else if ((ide->type == IDE_NONE) && (ch & 1)) - absent = 2; - ide_log("ide_writeb(%04X, %02X, %08X)\n", addr, val, priv); addr &= 0x7; - if ((absent != 1) || ((addr != 0x0) && (addr != 0x7))) switch (addr) { + if ((ide->type != IDE_NONE) || ((addr != 0x0) && (addr != 0x7))) switch (addr) { case 0x0: /* Data */ - if (absent == 0) - ide_write_data(ide, val | (val << 8), 2); + ide_write_data(ide, val | (val << 8), 2); break; /* Note to self: for ATAPI, bit 0 of this is DMA if set, PIO if clear. */ case 0x1: /* Features */ - ide->tf->cylprecomp = val; - if (ide->type == IDE_ATAPI) - ide_log("ATAPI transfer mode: %s\n", (val & 1) ? "DMA" : "PIO"); + if (!(ide->tf->atastat & (BSY_STAT | DRQ_STAT))) { + ide->tf->cylprecomp = val; + if (ide->type == IDE_ATAPI) + ide_log("ATAPI transfer mode: %s\n", (val & 1) ? "DMA" : "PIO"); + } -/* The ATA-3 specification says this register is the parameter for the - command and is unclear as to whether or not it's written to both - devices at once. Writing it to both devices at once breaks CD boot - on the AMI Apollo. */ -#ifdef WRITE_PARAM_TO_BOTH_DEVICES - ide_other->tf->cylprecomp = val; -#endif - return; + if (!(ide_other->tf->atastat & (BSY_STAT | DRQ_STAT))) + ide_other->tf->cylprecomp = val; + break; case 0x2: /* Sector count */ - ide->tf->secount = val; - ide_other->tf->secount = val; + if (!(ide->tf->atastat & (BSY_STAT | DRQ_STAT))) + ide->tf->secount = val; + if (!(ide_other->tf->atastat & (BSY_STAT | DRQ_STAT))) + ide_other->tf->secount = val; break; case 0x3: /* Sector */ - ide->sector = val; - ide->lba_addr = (ide->lba_addr & 0xfffff00) | val; - ide_other->sector = val; - ide_other->lba_addr = (ide_other->lba_addr & 0xfffff00) | val; + if (!(ide->tf->atastat & (BSY_STAT | DRQ_STAT))) { + ide->tf->sector = val; + ide->lba_addr = (ide->lba_addr & 0xfffff00) | val; + } + + if (!(ide_other->tf->atastat & (BSY_STAT | DRQ_STAT))) { + ide_other->tf->sector = val; + ide_other->lba_addr = (ide_other->lba_addr & 0xfffff00) | val; + } break; case 0x4: /* Cylinder low */ - ide->tf->cylinder = (ide->tf->cylinder & 0xff00) | val; - ide->lba_addr = (ide->lba_addr & 0xfff00ff) | (val << 8); + if (ide->type & IDE_SHADOW) + break; - ide_other->tf->cylinder = (ide_other->tf->cylinder & 0xff00) | val; - ide_other->lba_addr = (ide_other->lba_addr & 0xfff00ff) | (val << 8); + if (!(ide->tf->atastat & (BSY_STAT | DRQ_STAT))) { + ide->tf->cylinder = (ide->tf->cylinder & 0xff00) | val; + ide->lba_addr = (ide->lba_addr & 0xfff00ff) | (val << 8); + } + + if (!(ide_other->tf->atastat & (BSY_STAT | DRQ_STAT))) { + ide_other->tf->cylinder = (ide_other->tf->cylinder & 0xff00) | val; + ide_other->lba_addr = (ide_other->lba_addr & 0xfff00ff) | (val << 8); + } break; case 0x5: /* Cylinder high */ - ide->tf->cylinder = (ide->tf->cylinder & 0xff) | (val << 8); - ide->lba_addr = (ide->lba_addr & 0xf00ffff) | (val << 16); + if (ide->type & IDE_SHADOW) + break; - ide_other->tf->cylinder = (ide_other->tf->cylinder & 0xff) | (val << 8); - ide_other->lba_addr = (ide_other->lba_addr & 0xf00ffff) | (val << 16); + if (!(ide->tf->atastat & (BSY_STAT | DRQ_STAT))) { + ide->tf->cylinder = (ide->tf->cylinder & 0xff) | (val << 8); + ide->lba_addr = (ide->lba_addr & 0xf00ffff) | (val << 16); + } + + if (!(ide_other->tf->atastat & (BSY_STAT | DRQ_STAT))) { + ide_other->tf->cylinder = (ide_other->tf->cylinder & 0xff) | (val << 8); + ide_other->lba_addr = (ide_other->lba_addr & 0xf00ffff) | (val << 16); + } break; case 0x6: /* Drive/Head */ @@ -1482,20 +1535,29 @@ ide_writeb(uint16_t addr, uint8_t val, void *priv) ide_set_board_callback(ide->board, 0.0); reset = 1; } else - ide_irq_update(ide_boards[ide->board]); + ide_irq_update(ide_boards[ide->board], 1); } if (!reset) { - ide->head = ide_other->head = val & 0xF; - ide->lba = ide_other->lba = val & 0x40; + if (!(ide->tf->atastat & (BSY_STAT | DRQ_STAT))) { + ide->tf->drvsel = val & 0xef; + ide->lba_addr = (ide->lba_addr & 0x0ffffff) | + (ide->tf->head << 24); + } - ide->lba_addr = (ide->lba_addr & 0x0FFFFFF) | ((val & 0xF) << 24); - ide_other->lba_addr = (ide_other->lba_addr & 0x0FFFFFF) | ((val & 0xF) << 24); + if (!(ide_other->tf->atastat & (BSY_STAT | DRQ_STAT))) { + ide_other->tf->drvsel = val & 0xef; + ide_other->lba_addr = (ide_other->lba_addr & 0x0ffffff) | + (ide->tf->head << 24); + } } break; case 0x7: /* Command register */ - if (absent != 0) + if (ide->tf->atastat & (BSY_STAT | DRQ_STAT)) + break; + + if ((ide->type == IDE_NONE) || ((ide->type & IDE_SHADOW) && (val != WIN_DRIVE_DIAGNOSTICS))) break; ide_irq_lower(ide); @@ -1721,70 +1783,70 @@ ide_read_data(ide_t *ide, int length) { const uint8_t *idebufferb = (uint8_t *) ide->buffer; const uint16_t *idebufferw = ide->buffer; - int ch = ide->channel; uint16_t ret = 0; + double seek_us; + double xfer_us; - /* Absent and is master or both are absent. */ - if ((ide->type == IDE_NONE) && ((ide_drives[ch ^ 1]->type == IDE_NONE) || !(ch & 1))) { +#if defined(ENABLE_IDE_LOG) && (ENABLE_IDE_LOG == 2) + ide_log("ide_read_data(): ch = %i, board = %i, type = %i\n", ch, + ide->board, ide->type); +#endif + + if ((ide->type == IDE_NONE) || (ide->type & IDE_SHADOW) || !ide->buffer) { if (length == 2) ret = 0xff7f; else ret = 0x7f; - /* Absent and is slave and master is present. */ - } else if ((ide->type != IDE_NONE) || !(ch & 1)) { - if (!ide->buffer) { - if (length == 2) - ret = 0xffff; - else - ret = 0xff; - } else if (ide->command == WIN_PACKETCMD) { - if (ide->type == IDE_ATAPI) - ret = ide_atapi_packet_read(ide, length); - else { - ide_log("Drive not ATAPI (position: %i)\n", ide->tf->pos); - ide->tf->pos = 0; - } + } else if (ide->command == WIN_PACKETCMD) { + if (ide->type == IDE_ATAPI) + ret = ide_atapi_packet_read(ide, length); + else { + ide_log("Drive not ATAPI (position: %i)\n", ide->tf->pos); + ide->tf->pos = 0; + } + } else { + if (length == 2) { + ret = idebufferw[ide->tf->pos >> 1]; + ide->tf->pos += 2; } else { - if (length == 2) { - ret = idebufferw[ide->tf->pos >> 1]; - ide->tf->pos += 2; - } else { - ret = idebufferb[ide->tf->pos]; - ide->tf->pos++; - } + ret = idebufferb[ide->tf->pos]; + ide->tf->pos++; + } - if (ide->tf->pos >= 512) { - ide->tf->pos = 0; - ide->tf->atastat = DRDY_STAT | DSC_STAT; - if (ide->type == IDE_ATAPI) - ide->sc->packet_status = PHASE_IDLE; + if (ide->tf->pos >= 512) { + ide->tf->pos = 0; + ide->tf->atastat = DRDY_STAT | DSC_STAT; + if (ide->type == IDE_ATAPI) + ide->sc->packet_status = PHASE_IDLE; - if ((ide->command == WIN_READ) || (ide->command == WIN_READ_NORETRY) || - (ide->command == WIN_READ_MULTIPLE)) { - ide->tf->secount--; + if ((ide->command == WIN_READ) || + (ide->command == WIN_READ_NORETRY) || + (ide->command == WIN_READ_MULTIPLE)) { + ide->tf->secount--; - if (ide->tf->secount) { - ide_next_sector(ide); - ide->tf->atastat = BSY_STAT | READY_STAT | DSC_STAT; - if (ide->command == WIN_READ_MULTIPLE) { - if (!ide->blockcount) { - uint32_t sec_count = ide->tf->secount ? ide->tf->secount : 256; - if (sec_count > ide->blocksize) - sec_count = ide->blocksize; - double seek_time = hdd_timing_read(&hdd[ide->hdd_num], - ide_get_sector(ide), sec_count); - double xfer_time = ide_get_xfer_time(ide, 512 * sec_count); - ide_set_callback(ide, seek_time + xfer_time); - } else - ide_callback(ide); - } else { - double seek_time = hdd_timing_read(&hdd[ide->hdd_num], ide_get_sector(ide), 1); - double xfer_time = ide_get_xfer_time(ide, 512); - ide_set_callback(ide, seek_time + xfer_time); - } - } else - ui_sb_update_icon(SB_HDD | hdd[ide->hdd_num].bus, 0); - } + if (ide->tf->secount) { + ide_next_sector(ide); + ide->tf->atastat = BSY_STAT | READY_STAT | DSC_STAT; + if (ide->command == WIN_READ_MULTIPLE) { + if (!ide->blockcount) { + uint32_t cnt = ide->tf->secount ? + ide->tf->secount : 256; + if (cnt > ide->blocksize) + cnt = ide->blocksize; + seek_us = hdd_timing_read(&hdd[ide->hdd_num], + ide_get_sector(ide), cnt); + xfer_us = ide_get_xfer_time(ide, 512 * cnt); + ide_set_callback(ide, seek_us + xfer_us); + } else + ide_callback(ide); + } else { + seek_us = hdd_timing_read(&hdd[ide->hdd_num], + ide_get_sector(ide), 1); + xfer_us = ide_get_xfer_time(ide, 512); + ide_set_callback(ide, seek_us + xfer_us); + } + } else + ui_sb_update_icon(SB_HDD | hdd[ide->hdd_num].bus, 0); } } } @@ -1798,11 +1860,11 @@ ide_status(ide_t *ide, ide_t *ide_other, int ch) uint8_t ret; /* Absent and is master or both are absent. */ - if ((ide->type == IDE_NONE) && ((ide_drives[ch ^ 1]->type == IDE_NONE) || !(ch & 1))) { + if (ide->type == IDE_NONE) { /* Bit 7 pulled down, all other bits pulled up, per the spec. */ ret = 0x7f; /* Absent and is slave and master is present. */ - } else if ((ide->type == IDE_NONE) && (ch & 1)) { + } else if (ide->type & IDE_SHADOW) { /* On real hardware, a slave with a present master always returns a status of 0x00. Confirmed by the ATA-3 and ATA-4 specifications. */ @@ -1821,21 +1883,11 @@ ide_readb(uint16_t addr, void *priv) { const ide_board_t *dev = (ide_board_t *) priv; int ch; - int absent = 0; ide_t *ide; - ide_t *ide_other; uint8_t ret = 0xff; ch = dev->cur_dev; ide = ide_drives[ch]; - ide_other = ide_drives[ch ^ 1]; - - /* Absent and is master or both are absent. */ - if ((ide->type == IDE_NONE) && ((ide_drives[ch ^ 1]->type == IDE_NONE) || !(ch & 1))) - absent = 1; - /* Absent and is slave and master is present. */ - else if ((ide->type == IDE_NONE) && (ch & 1)) - absent = 2; switch (addr & 0x7) { case 0x0: /* Data */ @@ -1846,7 +1898,7 @@ ide_readb(uint16_t addr, void *priv) Bit 2 = ABRT (aborted command), Bit 1 = EOM (end of media), and Bit 0 = ILI (illegal length indication). */ case 0x1: /* Error */ - if (absent == 1) + if (ide->type == IDE_NONE) ret = 0x7f; else ret = ide->tf->error; @@ -1866,52 +1918,52 @@ ide_readb(uint16_t addr, void *priv) 0 1 0 Data from host 1 0 1 Status. */ case 0x2: /* Sector count */ - if (absent == 1) + if (ide->type == IDE_NONE) ret = 0x7f; - else if (absent == 2) - ret = ide_other->tf->secount; else ret = ide->tf->secount; break; case 0x3: /* Sector */ - if (absent == 1) + if (ide->type == IDE_NONE) ret = 0x7f; - else if (absent == 2) - ret = (uint8_t) ide_other->sector; else - ret = (uint8_t) ide->sector; + ret = (uint8_t) ide->tf->sector; break; case 0x4: /* Cylinder low */ - if (absent == 1) + if (ide->type == IDE_NONE) ret = 0x7f; - else if (absent == 2) - ret = ide_other->tf->cylinder & 0xff; else ret = ide->tf->cylinder & 0xff; +#if defined(ENABLE_IDE_LOG) && (ENABLE_IDE_LOG == 2) + ide_log("Cylinder low @ board %i, channel %i: ide->type = %i, " + "ret = %02X\n", ide->board, ide->channel, ide->type, ret); +#endif break; case 0x5: /* Cylinder high */ - if (absent == 1) + if (ide->type == IDE_NONE) ret = 0x7f; - else if (absent == 2) - ret = ide_other->tf->cylinder >> 8; else ret = ide->tf->cylinder >> 8; +#if defined(ENABLE_IDE_LOG) && (ENABLE_IDE_LOG == 2) + pclog("Cylinder high @ board %i, channel %i: ide->type = %i, " + "ret = %02X\n", ide->board, ide->channel, ide->type, ret); +#endif break; case 0x6: /* Drive/Head */ - if (absent == 1) + if (ide->type == IDE_NONE) ret = 0x7f; else - ret = (uint8_t) (ide->head | ((ch & 1) ? 0x10 : 0) | (ide->lba ? 0x40 : 0) | 0xa0); + ret = ide->tf->drvsel | ((ch & 1) ? 0xb0 : 0xa0); break; /* For ATAPI: Bit 5 is DMA ready, but without overlapped or interlaved DMA, it is DF (drive fault). */ case 0x7: /* Status */ - ide_irq_lower(ide); + ide_irq(ide, 0, 0); ret = ide_status(ide, ide_drives[ch ^ 1], ch); break; @@ -1969,7 +2021,9 @@ ide_readw(uint16_t addr, void *priv) break; } +#if defined(ENABLE_IDE_LOG) && (ENABLE_IDE_LOG == 2) ide_log("ide_readw(%04X, %08X) = %04X\n", addr, priv, ret); +#endif return ret; } @@ -2002,7 +2056,9 @@ ide_readl(uint16_t addr, void *priv) break; } +#if defined(ENABLE_IDE_LOG) && (ENABLE_IDE_LOG == 2) ide_log("ide_readl(%04X, %08X) = %04X\n", addr, priv, ret); +#endif return ret; } @@ -2012,9 +2068,7 @@ ide_board_callback(void *priv) ide_board_t *dev = (ide_board_t *) priv; ide_t *ide; -#ifdef ENABLE_IDE_LOG ide_log("ide_board_callback(%i)\n", dev->cur_dev >> 1); -#endif for (uint8_t i = 0; i < 2; i++) { ide = dev->ide[i]; @@ -2060,13 +2114,13 @@ ide_callback(void *priv) ide_log("ide_callback(%i): %02X\n", ide->channel, ide->command); switch (ide->command) { - case WIN_SEEK ... 0x7F: - chk_chs = !ide->lba; + case WIN_SEEK ... 0x7f: + chk_chs = !ide->tf->lba; if (ide->type == IDE_ATAPI) atapi_error_no_ready(ide); else { - if (chk_chs && ((ide->tf->cylinder >= ide->tracks) || (ide->head >= ide->hpc) || - !ide->sector || (ide->sector > ide->spt))) + if (chk_chs && ((ide->tf->cylinder >= ide->tracks) || (ide->tf->head >= ide->hpc) || + !ide->tf->sector || (ide->tf->sector > ide->spt))) err = IDNF_ERR; else { ide->tf->atastat = DRDY_STAT | DSC_STAT; @@ -2075,7 +2129,7 @@ ide_callback(void *priv) } break; - case WIN_RECAL ... 0x1F: + case WIN_RECAL ... 0x1f: if (ide->type == IDE_ATAPI) atapi_error_no_ready(ide); else { @@ -2091,7 +2145,7 @@ ide_callback(void *priv) ide->tf->error = 1; /*Device passed*/ ide->tf->secount = 1; - ide->sector = 1; + ide->tf->sector = 1; ide_set_signature(ide); @@ -2129,7 +2183,7 @@ ide_callback(void *priv) if (ide->type == IDE_ATAPI) { ide_set_signature(ide); err = ABRT_ERR; - } else if (!ide->lba && (ide->cfg_spt == 0)) + } else if (!ide->tf->lba && (ide->cfg_spt == 0)) err = IDNF_ERR; else { if (ide->do_initial_read) { @@ -2157,7 +2211,7 @@ ide_callback(void *priv) if ((ide->type == IDE_ATAPI) || ide_boards[ide->board]->force_ata3 || (bm == NULL)) { ide_log("IDE %i: DMA read aborted (bad device or board)\n", ide->channel); err = ABRT_ERR; - } else if (!ide->lba && (ide->cfg_spt == 0)) { + } else if (!ide->tf->lba && (ide->cfg_spt == 0)) { ide_log("IDE %i: DMA read aborted (SPECIFY failed)\n", ide->channel); err = IDNF_ERR; } else { @@ -2207,7 +2261,7 @@ ide_callback(void *priv) mand error. */ if ((ide->type == IDE_ATAPI) || !ide->blocksize) err = ABRT_ERR; - else if (!ide->lba && (ide->cfg_spt == 0)) + else if (!ide->tf->lba && (ide->cfg_spt == 0)) err = IDNF_ERR; else { if (ide->do_initial_read) { @@ -2235,7 +2289,7 @@ ide_callback(void *priv) case WIN_WRITE_NORETRY: if (ide->type == IDE_ATAPI) err = ABRT_ERR; - else if (!ide->lba && (ide->cfg_spt == 0)) + else if (!ide->tf->lba && (ide->cfg_spt == 0)) err = IDNF_ERR; else { hdd_image_write(ide->hdd_num, ide_get_sector(ide), 1, (uint8_t *) ide->buffer); @@ -2258,7 +2312,7 @@ ide_callback(void *priv) if ((ide->type == IDE_ATAPI) || ide_boards[ide->board]->force_ata3 || (bm == NULL)) { ide_log("IDE %i: DMA write aborted (bad device type or board)\n", ide->channel); err = ABRT_ERR; - } else if (!ide->lba && (ide->cfg_spt == 0)) { + } else if (!ide->tf->lba && (ide->cfg_spt == 0)) { ide_log("IDE %i: DMA write aborted (SPECIFY failed)\n", ide->channel); err = IDNF_ERR; } else { @@ -2307,7 +2361,7 @@ ide_callback(void *priv) mand error. */ if ((ide->type == IDE_ATAPI) || !ide->blocksize) err = ABRT_ERR; - else if (!ide->lba && (ide->cfg_spt == 0)) + else if (!ide->tf->lba && (ide->cfg_spt == 0)) err = IDNF_ERR; else { hdd_image_write(ide->hdd_num, ide_get_sector(ide), 1, (uint8_t *) ide->buffer); @@ -2332,7 +2386,7 @@ ide_callback(void *priv) case WIN_VERIFY_ONCE: if (ide->type == IDE_ATAPI) err = ABRT_ERR; - else if (!ide->lba && (ide->cfg_spt == 0)) + else if (!ide->tf->lba && (ide->cfg_spt == 0)) err = IDNF_ERR; else { ide->tf->pos = 0; @@ -2345,7 +2399,7 @@ ide_callback(void *priv) case WIN_FORMAT: if (ide->type == IDE_ATAPI) err = ABRT_ERR; - else if (!ide->lba && (ide->cfg_spt == 0)) + else if (!ide->tf->lba && (ide->cfg_spt == 0)) err = IDNF_ERR; else { hdd_image_zero(ide->hdd_num, ide_get_sector(ide), ide->tf->secount); @@ -2364,7 +2418,7 @@ ide_callback(void *priv) if (ide->cfg_spt == 0) { /* Only accept after RESET or DIAG. */ ide->cfg_spt = ide->tf->secount; - ide->cfg_hpc = ide->head + 1; + ide->cfg_hpc = ide->tf->head + 1; } ide->command = 0x00; ide->tf->atastat = DRDY_STAT | DSC_STAT; @@ -2597,7 +2651,7 @@ ide_board_close(int board) if (dev->type == IDE_ATAPI) dev->tf->atastat = DRDY_STAT | DSC_STAT; - else if (dev->tf != NULL) { + else if (!(dev->type & IDE_SHADOW) && (dev->tf != NULL)) { free(dev->tf); dev->tf = NULL; } @@ -2863,8 +2917,14 @@ ide_drive_reset(int d) { ide_log("Resetting IDE drive %i...\n", d); + if ((d & 1) && (ide_drives[d]->type == IDE_NONE) && (ide_drives[d ^ 1]->type != IDE_NONE)) { + ide_drives[d]->type = ide_drives[d ^ 1]->type | IDE_SHADOW; + free(ide_drives[d]->tf); + ide_drives[d]->tf = ide_drives[d ^ 1]->tf; + } else + ide_drives[d]->tf->atastat = DRDY_STAT | DSC_STAT; + ide_drives[d]->channel = d; - ide_drives[d]->tf->atastat = DRDY_STAT | DSC_STAT; ide_drives[d]->service = 0; ide_drives[d]->board = d >> 1; ide_drives[d]->selected = !(d & 1); diff --git a/src/disk/hdc_ide_sff8038i.c b/src/disk/hdc_ide_sff8038i.c index 40cca95a9..3f43f80e6 100644 --- a/src/disk/hdc_ide_sff8038i.c +++ b/src/disk/hdc_ide_sff8038i.c @@ -27,7 +27,9 @@ #define HAVE_STDARG_H #include <86box/86box.h> #include <86box/cdrom.h> +#include <86box/hdd.h> #include <86box/scsi_device.h> +#include <86box/scsi_disk.h> #include <86box/scsi_cdrom.h> #include <86box/dma.h> #include <86box/io.h> @@ -389,7 +391,6 @@ sff_bus_master_set_irq(uint8_t status, void *priv) { sff8038i_t *dev = (sff8038i_t *) priv; uint8_t irq = !!(status & 0x04); - int irq_shift = 0; if (!(dev->status & 0x04) || (status & 0x04)) dev->status = (dev->status & ~0x04) | status; @@ -410,16 +411,12 @@ sff_bus_master_set_irq(uint8_t status, void *priv) else pci_clear_irq(dev->slot, dev->irq_pin, &dev->irq_state); break; - case IRQ_MODE_MIRQ_0: - case IRQ_MODE_MIRQ_1: - /* MIRQ 0 or 1. */ - case IRQ_MODE_MIRQ_2: - case IRQ_MODE_MIRQ_3: - /* MIRQ 2 or 3. */ + case IRQ_MODE_MIRQ_0 ... IRQ_MODE_MIRQ_3: + /* MIRQ 0, 1, 2, or 3. */ if (irq) - pci_set_mirq((dev->irq_mode & 3) + irq_shift, 0, &dev->irq_state); + pci_set_mirq(dev->irq_mode & 3, 0, &dev->irq_state); else - pci_clear_mirq((dev->irq_mode & 3) + irq_shift, 0, &dev->irq_state); + pci_clear_mirq(dev->irq_mode & 3, 0, &dev->irq_state); break; /* TODO: Redo this as a MIRQ. */ case IRQ_MODE_PCI_IRQ_LINE: @@ -477,6 +474,10 @@ sff_reset(void *priv) sff_log("SFF8038i: Reset\n"); #endif + for (uint8_t i = 0; i < HDD_NUM; i++) { + if ((hdd[i].bus == HDD_BUS_ATAPI) && (hdd[i].ide_channel < 4) && hdd[i].priv) + scsi_disk_reset((scsi_common_t *) hdd[i].priv); + } for (uint8_t i = 0; i < CDROM_NUM; i++) { if ((cdrom[i].bus_type == CDROM_BUS_ATAPI) && (cdrom[i].ide_channel < 4) && cdrom[i].priv) scsi_cdrom_reset((scsi_common_t *) cdrom[i].priv); @@ -522,21 +523,15 @@ sff_set_irq_mode(sff8038i_t *dev, int irq_mode) default: case IRQ_MODE_LEGACY: /* Legacy IRQ mode. */ - sff_log("[%08X] Setting IRQ mode to legacy IRQ %i\n", dev, 14 + channel); + sff_log("[%08X] Setting IRQ mode to legacy IRQ %i\n", dev, dev->irq_line); break; case IRQ_MODE_PCI_IRQ_PIN: /* Native PCI IRQ mode with interrupt pin. */ sff_log("[%08X] Setting IRQ mode to native PCI INT%c\n", dev, 0x40 + dev->irq_pin); break; - case IRQ_MODE_MIRQ_0: - case IRQ_MODE_MIRQ_1: - /* MIRQ 0 or 1. */ - sff_log("[%08X] Setting IRQ mode to PCI MIRQ%i\n", dev, irq_mode & 1); - break; - case IRQ_MODE_MIRQ_2: - case IRQ_MODE_MIRQ_3: - /* MIRQ 0 or 1. */ - sff_log("[%08X] Setting IRQ mode to PCI MIRQ%i\n", dev, (irq_mode & 1) + 1); + case IRQ_MODE_MIRQ_0 ... IRQ_MODE_MIRQ_3: + /* MIRQ 0, 1, 2, or 3. */ + sff_log("[%08X] Setting IRQ mode to PCI MIRQ%i\n", dev, dev->irq_mode & 3); break; case IRQ_MODE_PCI_IRQ_LINE: /* Native PCI IRQ mode with specified interrupt line. */ diff --git a/src/disk/mo.c b/src/disk/mo.c index ad781a8ac..27bf7e5e0 100644 --- a/src/disk/mo.c +++ b/src/disk/mo.c @@ -2147,6 +2147,9 @@ mo_hard_reset(void) dev = (mo_t *) mo_drives[c].priv; + if (dev->tf == NULL) + continue; + dev->id = c; dev->drv = &mo_drives[c]; diff --git a/src/disk/zip.c b/src/disk/zip.c index c498383e8..887a9c68e 100644 --- a/src/disk/zip.c +++ b/src/disk/zip.c @@ -2386,6 +2386,9 @@ zip_hard_reset(void) dev = (zip_t *) zip_drives[c].priv; + if (dev->tf == NULL) + continue; + dev->id = c; dev->drv = &zip_drives[c]; diff --git a/src/include/86box/hdc_ide.h b/src/include/86box/hdc_ide.h index b1bff13b3..291dec303 100644 --- a/src/include/86box/hdc_ide.h +++ b/src/include/86box/hdc_ide.h @@ -36,9 +36,13 @@ #define HDC_QUATERNARY_IRQ 10 enum { - IDE_NONE = 0, - IDE_HDD, - IDE_ATAPI + IDE_NONE = 0, /* Absent master or both. */ + IDE_HDD, /* Hard disk. */ + IDE_ATAPI, /* ATAPI device. */ + IDE_RESERVED, /* Reserved, do not use. */ + IDE_SHADOW, /* Shadow flag, do not assign on is own. */ + IDE_HDD_SHADOW, /* Shadow of a hard disk. */ + IDE_ATAPI_SHADOW /* Shadow of an ATAPI device. */ }; typedef struct ide_tf_s { @@ -59,28 +63,26 @@ typedef struct ide_tf_s { uint8_t status; }; uint8_t error; - uint16_t pad; + uint8_t sector; + union { + uint8_t drvsel; + struct { + uint8_t head :4; + uint8_t pad :2; + uint8_t lba :1; + uint8_t pad0 :1; + }; + }; uint32_t pos; } ide_tf_t; #ifdef _TIMER_H_ typedef struct ide_s { -#ifdef ANCIENT_CODE - /* Task file. */ - uint8_t cylprecomp; - uint8_t secount; - uint16_t cylinder; - uint8_t atastat; - uint8_t error; - uint16_t pad; - uint32_t pos; -#endif - /* The rest. */ uint8_t selected; uint8_t command; uint8_t head; - uint8_t sector; + uint8_t pad; int type; int board; int irqstat; @@ -90,7 +92,6 @@ typedef struct ide_s { int hdd_num; int channel; int sector_pos; - int lba; int reset; int mdma_mode; int do_initial_read; @@ -179,8 +180,7 @@ extern int ide_qua_enabled; #ifdef SCSI_DEVICE_H extern ide_t *ide_get_drive(int ch); -extern void ide_irq_raise(ide_t *ide); -extern void ide_irq_lower(ide_t *ide); +extern void ide_irq(ide_t *ide, int set, int log); extern void ide_allocate_buffer(ide_t *dev); extern void ide_atapi_attach(ide_t *dev); #endif @@ -224,6 +224,9 @@ extern uint8_t ide_read_ali_75(void); extern uint8_t ide_read_ali_76(void); /* Legacy #define's. */ +#define ide_irq_raise(ide) ide_irq(ide, 1, 1) +#define ide_irq_lower(ide) ide_irq(ide, 0, 1) + #define ide_set_base(board, port) ide_set_base_addr(board, 0, port) #define ide_set_side(board, port) ide_set_base_addr(board, 1, port) diff --git a/src/include/86box/hwm.h b/src/include/86box/hwm.h index 636616388..7899763e4 100644 --- a/src/include/86box/hwm.h +++ b/src/include/86box/hwm.h @@ -71,6 +71,8 @@ extern const device_t w83782d_device; extern const device_t gl518sm_2c_device; extern const device_t gl518sm_2d_device; +extern const device_t gl520sm_2c_device; +extern const device_t gl520sm_2d_device; extern const device_t via_vt82c686_hwm_device; diff --git a/src/include/86box/isapnp.h b/src/include/86box/isapnp.h index 71c1bb29a..5e48b7c18 100644 --- a/src/include/86box/isapnp.h +++ b/src/include/86box/isapnp.h @@ -54,17 +54,21 @@ typedef struct isapnp_device_config_t { } dma[2]; } isapnp_device_config_t; -void *isapnp_add_card(uint8_t *rom, uint16_t rom_size, - void (*config_changed)(uint8_t ld, isapnp_device_config_t *config, void *priv), - void (*csn_changed)(uint8_t csn, void *priv), - uint8_t (*read_vendor_reg)(uint8_t ld, uint8_t reg, void *priv), - void (*write_vendor_reg)(uint8_t ld, uint8_t reg, uint8_t val, void *priv), - void *priv); -void isapnp_update_card_rom(void *priv, uint8_t *rom, uint16_t rom_size); -void isapnp_enable_card(void *priv, uint8_t enable); -void isapnp_set_csn(void *priv, uint8_t csn); -void isapnp_set_device_defaults(void *priv, uint8_t ldn, const isapnp_device_config_t *config); -void isapnp_reset_card(void *priv); -void isapnp_reset_device(void *priv, uint8_t ld); +extern const uint8_t isapnp_init_key[32]; + +void *isapnp_add_card(uint8_t *rom, uint16_t rom_size, + void (*config_changed)(uint8_t ld, isapnp_device_config_t *config, void *priv), + void (*csn_changed)(uint8_t csn, void *priv), + uint8_t (*read_vendor_reg)(uint8_t ld, uint8_t reg, void *priv), + void (*write_vendor_reg)(uint8_t ld, uint8_t reg, uint8_t val, void *priv), + void *priv); +void isapnp_update_card_rom(void *priv, uint8_t *rom, uint16_t rom_size); +void isapnp_enable_card(void *priv, uint8_t enable); +void isapnp_set_csn(void *priv, uint8_t csn); +uint8_t isapnp_read_reg(void *priv, uint8_t ldn, uint8_t reg); +void isapnp_write_reg(void *priv, uint8_t ldn, uint8_t reg, uint8_t val); +void isapnp_set_device_defaults(void *priv, uint8_t ldn, const isapnp_device_config_t *config); +void isapnp_reset_card(void *priv); +void isapnp_reset_device(void *priv, uint8_t ld); #endif /*EMU_ISAPNP_H*/ diff --git a/src/include/86box/keyboard.h b/src/include/86box/keyboard.h index 0a2a038ec..a4b079760 100644 --- a/src/include/86box/keyboard.h +++ b/src/include/86box/keyboard.h @@ -226,6 +226,7 @@ extern const device_t keyboard_xt_lxt3_device; # endif /*defined(DEV_BRANCH) && defined(USE_LASERXT) */ extern const device_t keyboard_xt_olivetti_device; extern const device_t keyboard_xt_zenith_device; +extern const device_t keyboard_xt_hyundai_device; extern const device_t keyboard_xtclone_device; extern const device_t keyboard_at_device; extern const device_t keyboard_at_siemens_device; diff --git a/src/include/86box/machine.h b/src/include/86box/machine.h index ecab253d0..1acb7bb57 100644 --- a/src/include/86box/machine.h +++ b/src/include/86box/machine.h @@ -447,6 +447,7 @@ extern int machine_at_quadt386sx_init(const machine_t *); extern int machine_at_award286_init(const machine_t *); extern int machine_at_gdc212m_init(const machine_t *); extern int machine_at_gw286ct_init(const machine_t *); +extern int machine_at_super286c_init(const machine_t *); extern int machine_at_super286tr_init(const machine_t *); extern int machine_at_spc4200p_init(const machine_t *); extern int machine_at_spc4216p_init(const machine_t *); @@ -600,6 +601,7 @@ extern int machine_at_p5sp4_init(const machine_t *); /* m_at_socket5.c */ extern int machine_at_plato_init(const machine_t *); +extern int machine_at_dellplato_init(const machine_t *); extern int machine_at_ambradp90_init(const machine_t *); extern int machine_at_430nx_init(const machine_t *); @@ -673,6 +675,7 @@ extern int machine_at_tx97_init(const machine_t *); extern int machine_at_an430tx_init(const machine_t *); #endif extern int machine_at_ym430tx_init(const machine_t *); +extern int machine_at_thunderbolt_init(const machine_t *); extern int machine_at_mb540n_init(const machine_t *); extern int machine_at_56a5_init(const machine_t *); extern int machine_at_p5mms98_init(const machine_t *); @@ -705,6 +708,7 @@ extern int machine_at_aurora_init(const machine_t *); extern int machine_at_686nx_init(const machine_t *); extern int machine_at_acerv60n_init(const machine_t *); extern int machine_at_vs440fx_init(const machine_t *); +extern int machine_at_gw2kvenus_init(const machine_t *); extern int machine_at_ap440fx_init(const machine_t *); extern int machine_at_mb600n_init(const machine_t *); extern int machine_at_8600ttc_init(const machine_t *); @@ -753,6 +757,7 @@ extern int machine_at_s370slm_init(const machine_t *); extern int machine_at_cubx_init(const machine_t *); extern int machine_at_atc7020bxii_init(const machine_t *); +extern int machine_at_m773_init(const machine_t *); extern int machine_at_ambx133_init(const machine_t *); extern int machine_at_awo671r_init(const machine_t *); extern int machine_at_63a1_init(const machine_t *); diff --git a/src/include/86box/midi.h b/src/include/86box/midi.h index 97ed1f6ca..4bc678817 100644 --- a/src/include/86box/midi.h +++ b/src/include/86box/midi.h @@ -102,6 +102,7 @@ extern void midi_in_sysex(uint8_t *buffer, uint32_t len); #ifdef EMU_DEVICE_H extern const device_t rtmidi_output_device; extern const device_t rtmidi_input_device; +extern const device_t opl4_midi_device; # ifdef USE_FLUIDSYNTH extern const device_t fluidsynth_device; # endif diff --git a/src/include/86box/net_eeprom_nmc93cxx.h b/src/include/86box/net_eeprom_nmc93cxx.h new file mode 100644 index 000000000..f5260d1ed --- /dev/null +++ b/src/include/86box/net_eeprom_nmc93cxx.h @@ -0,0 +1,19 @@ +struct nmc93cxx_eeprom_t; +typedef struct nmc93cxx_eeprom_t nmc93cxx_eeprom_t; + +typedef struct nmc93cxx_eeprom_params_t { + uint16_t nwords; + char *filename; + uint16_t *default_content; +} nmc93cxx_eeprom_params_t; + +/* Read from the EEPROM. */ +uint16_t nmc93cxx_eeprom_read(nmc93cxx_eeprom_t *eeprom); + +/* Write to the EEPROM. */ +void nmc93cxx_eeprom_write(nmc93cxx_eeprom_t *eeprom, int eecs, int eesk, int eedi); + +/* Get EEPROM data array. */ +uint16_t *nmc93cxx_eeprom_data(nmc93cxx_eeprom_t *eeprom); + +extern const device_t nmc93cxx_device; diff --git a/src/include/86box/net_rtl8139.h b/src/include/86box/net_rtl8139.h new file mode 100644 index 000000000..f44d0facb --- /dev/null +++ b/src/include/86box/net_rtl8139.h @@ -0,0 +1 @@ +extern const device_t rtl8139c_plus_device; diff --git a/src/include/86box/net_tulip.h b/src/include/86box/net_tulip.h new file mode 100644 index 000000000..3e14deeea --- /dev/null +++ b/src/include/86box/net_tulip.h @@ -0,0 +1,3 @@ +extern const device_t dec_tulip_device; +extern const device_t dec_tulip_21140_device; +extern const device_t dec_tulip_21140_vpc_device; diff --git a/src/include/86box/opl4_defines.h b/src/include/86box/opl4_defines.h new file mode 100644 index 000000000..248b1f729 --- /dev/null +++ b/src/include/86box/opl4_defines.h @@ -0,0 +1,101 @@ +/* + * RoboPlay for MSX + * Copyright (C) 2020 by RoboSoft Inc. + * + * opl4_defines.h + * + */ + +#ifndef __OPL4_DEFINES_H +#define __OPL4_DEFINES_H + +/* + * Register numbers + */ + +#define OPL4_REG_TEST0 0x00 +#define OPL4_REG_TEST1 0x01 + +#define OPL4_REG_MEMORY_CONFIGURATION 0x02 +#define OPL4_MODE_BIT 0x01 +#define OPL4_MTYPE_BIT 0x02 +#define OPL4_TONE_HEADER_MASK 0x1C +#define OPL4_DEVICE_ID_MASK 0xE0 + +#define OPL4_REG_MEMORY_ADDRESS_HIGH 0x03 +#define OPL4_REG_MEMORY_ADDRESS_MID 0x04 +#define OPL4_REG_MEMORY_ADDRESS_LOW 0x05 +#define OPL4_REG_MEMORY_DATA 0x06 + +/* + * Offsets to the register banks for voices. To get the + * register number just add the voice number to the bank offset. + * + * Wave Table Number low bits (0x08 to 0x1F) + */ +#define OPL4_REG_TONE_NUMBER 0x08 + +/* Wave Table Number high bit, F-Number low bits (0x20 to 0x37) */ +#define OPL4_REG_F_NUMBER 0x20 +#define OPL4_TONE_NUMBER_BIT8 0x01 +#define OPL4_F_NUMBER_LOW_MASK 0xFE + +/* F-Number high bits, Octave, Pseudo-Reverb (0x38 to 0x4F) */ +#define OPL4_REG_OCTAVE 0x38 +#define OPL4_F_NUMBER_HIGH_MASK 0x07 +#define OPL4_BLOCK_MASK 0xF0 +#define OPL4_PSEUDO_REVERB_BIT 0x08 + +/* Total Level, Level Direct (0x50 to 0x67) */ +#define OPL4_REG_LEVEL 0x50 +#define OPL4_TOTAL_LEVEL_MASK 0xFE +#define OPL4_LEVEL_DIRECT_BIT 0x01 + +/* Key On, Damp, LFO RST, CH, Panpot (0x68 to 0x7F) */ +#define OPL4_REG_MISC 0x68 +#define OPL4_KEY_ON_BIT 0x80 +#define OPL4_DAMP_BIT 0x40 +#define OPL4_LFO_RESET_BIT 0x20 +#define OPL4_OUTPUT_CHANNEL_BIT 0x10 +#define OPL4_PAN_POT_MASK 0x0F + +/* LFO, VIB (0x80 to 0x97) */ +#define OPL4_REG_LFO_VIBRATO 0x80 +#define OPL4_LFO_FREQUENCY_MASK 0x38 +#define OPL4_VIBRATO_DEPTH_MASK 0x07 +#define OPL4_CHORUS_SEND_MASK 0xC0 + +/* Attack / Decay 1 rate (0x98 to 0xAF) */ +#define OPL4_REG_ATTACK_DECAY1 0x98 +#define OPL4_ATTACK_RATE_MASK 0xF0 +#define OPL4_DECAY1_RATE_MASK 0x0F + +/* Decay level / 2 rate (0xB0 to 0xC7) */ +#define OPL4_REG_LEVEL_DECAY2 0xB0 +#define OPL4_DECAY_LEVEL_MASK 0xF0 +#define OPL4_DECAY2_RATE_MASK 0x0F + +/* Release rate / Rate correction (0xC8 to 0xDF) */ +#define OPL4_REG_RELEASE_CORRECTION 0xC8 +#define OPL4_RELEASE_RATE_MASK 0x0F +#define OPL4_RATE_INTERPOLATION_MASK 0xF0 + +/* AM (0xE0 to 0xF7) */ +#define OPL4_REG_TREMOLO 0xE0 +#define OPL4_TREMOLO_DEPTH_MASK 0x07 +#define OPL4_REVERB_SEND_MASK 0xE0 + +/* Mixer */ +#define OPL4_REG_MIX_CONTROL_FM 0xF8 +#define OPL4_REG_MIX_CONTROL_PCM 0xF9 +#define OPL4_MIX_LEFT_MASK 0x07 +#define OPL4_MIX_RIGHT_MASK 0x38 + +#define OPL4_REG_ATC 0xFA +#define OPL4_ATC_BIT 0x01 + +/* Bits in the OPL4 Status register */ +#define OPL4_STATUS_BUSY 0x01 +#define OPL4_STATUS_LOAD 0x02 + +#endif /* __OPL4_DEFINES_H */ diff --git a/src/include/86box/plat.h b/src/include/86box/plat.h index 1552b032f..0d5b17a3c 100644 --- a/src/include/86box/plat.h +++ b/src/include/86box/plat.h @@ -107,6 +107,7 @@ extern int infocus; extern char emu_version[200]; /* version ID string */ extern int rctrl_is_lalt; extern int update_icons; +extern int status_icons_fullscreen; extern int kbd_req_capture; extern int hide_status_bar; diff --git a/src/include/86box/scsi_disk.h b/src/include/86box/scsi_disk.h index a62bc9e20..eb4dc69a4 100644 --- a/src/include/86box/scsi_disk.h +++ b/src/include/86box/scsi_disk.h @@ -68,6 +68,8 @@ typedef struct scsi_disk_t { extern scsi_disk_t *scsi_disk[HDD_NUM]; +extern void scsi_disk_reset(scsi_common_t *sc); + extern void scsi_disk_hard_reset(void); extern void scsi_disk_close(void); diff --git a/src/include/86box/sio.h b/src/include/86box/sio.h index 11c0d4ae6..da6dd165d 100644 --- a/src/include/86box/sio.h +++ b/src/include/86box/sio.h @@ -42,6 +42,7 @@ extern const device_t fdc37c935_device; extern const device_t fdc37m60x_device; extern const device_t fdc37m60x_370_device; extern const device_t it8661f_device; +extern const device_t it8671f_device; extern const device_t i82091aa_device; extern const device_t i82091aa_398_device; extern const device_t i82091aa_ide_pri_device; @@ -79,6 +80,8 @@ extern const device_t ps1_m2133_sio; extern const device_t sio_detect_device; #endif extern const device_t um8669f_device; +extern const device_t um8669f_ide_device; +extern const device_t um8669f_ide_sec_device; extern const device_t via_vt82c686_sio_device; extern const device_t w83627hf_device; extern const device_t w83627hf_no_hwm_device; diff --git a/src/include/86box/snd_opl.h b/src/include/86box/snd_opl.h index e516b95f9..0d89589c4 100644 --- a/src/include/86box/snd_opl.h +++ b/src/include/86box/snd_opl.h @@ -38,6 +38,7 @@ typedef struct fm_drv_t { void (*reset_buffer)(void *priv); void (*set_do_cycles)(void *priv, int8_t do_cycles); void *priv; + void (*generate)(void *priv, int32_t *data, uint32_t num_samples); /* daughterboard only. */ } fm_drv_t; extern uint8_t fm_driver_get(int chip_id, fm_drv_t *drv); diff --git a/src/include/86box/vid_8514a.h b/src/include/86box/vid_8514a.h index 983e98cd1..da4e9e700 100644 --- a/src/include/86box/vid_8514a.h +++ b/src/include/86box/vid_8514a.h @@ -41,7 +41,7 @@ typedef struct ibm8514_t { int type; int local; int bpp; - int on; + int on[2]; int accel_bpp; uint32_t vram_size; @@ -64,7 +64,7 @@ typedef struct ibm8514_t { struct { uint16_t subsys_cntl; uint16_t setup_md; - uint8_t advfunc_cntl; + uint16_t advfunc_cntl; uint8_t ext_advfunc_cntl; uint16_t cur_y; uint16_t cur_y_bitres; diff --git a/src/include_make/86box/version.h b/src/include_make/86box/version.h index 4b6a8d5d0..9a175be24 100644 --- a/src/include_make/86box/version.h +++ b/src/include_make/86box/version.h @@ -22,12 +22,12 @@ #define EMU_NAME "86Box" #define EMU_NAME_W LSTR(EMU_NAME) -#define EMU_VERSION "4.0.2" +#define EMU_VERSION "4.1" #define EMU_VERSION_W LSTR(EMU_VERSION) #define EMU_VERSION_EX "3.50" /* frozen due to IDE re-detection behavior on Windows */ #define EMU_VERSION_MAJ 4 -#define EMU_VERSION_MIN 0 -#define EMU_VERSION_PATCH 2 +#define EMU_VERSION_MIN 1 +#define EMU_VERSION_PATCH 0 #define EMU_BUILD_NUM 0 @@ -42,7 +42,7 @@ #define EMU_ROMS_URL "https://github.com/86Box/roms/releases/latest" #define EMU_ROMS_URL_W LSTR(EMU_ROMS_URL) #ifdef RELEASE_BUILD -# define EMU_DOCS_URL "https://86box.readthedocs.io/en/v4.0/" +# define EMU_DOCS_URL "https://86box.readthedocs.io/en/v4.1/" #else # define EMU_DOCS_URL "https://86box.readthedocs.io" #endif diff --git a/src/machine/m_at_286_386sx.c b/src/machine/m_at_286_386sx.c index c7b1acb0c..79a82595f 100644 --- a/src/machine/m_at_286_386sx.c +++ b/src/machine/m_at_286_386sx.c @@ -348,6 +348,29 @@ machine_at_gw286ct_init(const machine_t *model) return ret; } +int +machine_at_super286c_init(const machine_t *model) +{ + int ret; + + ret = bios_load_linear("roms/machines/super286c/hyundai_award286.bin", + 0x000f0000, 65536, 0); + + if (bios_only || !ret) + return ret; + + machine_at_common_init(model); + + device_add(&keyboard_at_ami_device); + + if (fdc_type == FDC_INTERNAL) + device_add(&fdc_at_device); + + device_add(&neat_device); + + return ret; +} + int machine_at_super286tr_init(const machine_t *model) { diff --git a/src/machine/m_at_socket370.c b/src/machine/m_at_socket370.c index 0ca90433d..c6ec7c907 100644 --- a/src/machine/m_at_socket370.c +++ b/src/machine/m_at_socket370.c @@ -173,9 +173,8 @@ machine_at_p6bat_init(const machine_t *model) device_add(&sst_flash_39sf020_device); spd_register(SPD_TYPE_SDRAM, 0x7, 256); - if (sound_card_current[0] == SOUND_INTERNAL) { + if (sound_card_current[0] == SOUND_INTERNAL) device_add(&cmi8738_onboard_device); - } return ret; } @@ -248,6 +247,46 @@ machine_at_atc7020bxii_init(const machine_t *model) return ret; } +int +machine_at_m773_init(const machine_t *model) +{ + int ret; + + ret = bios_load_linear("roms/machines/m773/010504s.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(0x07, PCI_CARD_SOUTHBRIDGE, 1, 2, 3, 4); + pci_register_slot(0x0C, PCI_CARD_SOUND, 4, 3, 0, 0); + pci_register_slot(0x09, PCI_CARD_NORMAL, 1, 2, 3, 4); + pci_register_slot(0x0A, PCI_CARD_NORMAL, 2, 3, 4, 1); + pci_register_slot(0x0B, PCI_CARD_NORMAL, 3, 4, 1, 2); + pci_register_slot(0x0D, PCI_CARD_NORMAL, 4, 1, 2, 3); + pci_register_slot(0x01, PCI_CARD_AGPBRIDGE, 1, 2, 3, 4); + device_add(&i440bx_device); + device_add(&slc90e66_device); + device_add(&keyboard_ps2_ami_pci_device); + device_add(&it8671f_device); + device_add(&sst_flash_39sf020_device); + spd_register(SPD_TYPE_SDRAM, 0x3, 256); + device_add(&gl520sm_2d_device); /* fans: CPU, Chassis; temperature: System */ + hwm_values.temperatures[0] += 2; /* System offset */ + hwm_values.temperatures[1] += 2; /* CPU offset */ + hwm_values.voltages[0] = 3300; /* Vcore and 3.3V are swapped */ + hwm_values.voltages[2] = hwm_get_vcore(); + + if (sound_card_current[0] == SOUND_INTERNAL) + device_add(&cmi8738_onboard_device); + + return ret; +} + int machine_at_ambx133_init(const machine_t *model) { diff --git a/src/machine/m_at_socket5.c b/src/machine/m_at_socket5.c index 5a336da24..0b67976af 100644 --- a/src/machine/m_at_socket5.c +++ b/src/machine/m_at_socket5.c @@ -59,6 +59,25 @@ machine_at_plato_init(const machine_t *model) return ret; } +int +machine_at_dellplato_init(const machine_t *model) +{ + int ret; + + ret = bios_load_linear_combined("roms/machines/dellplato/1016AX1J.bio", + "roms/machines/dellplato/1016AX1J.bi1", + 0x1d000, 128); + + if (bios_only || !ret) + return ret; + + machine_at_premiere_common_init(model, PCI_CAN_SWITCH_TYPE); + + device_add(&i430nx_device); + + return ret; +} + int machine_at_ambradp90_init(const machine_t *model) { diff --git a/src/machine/m_at_socket7.c b/src/machine/m_at_socket7.c index f600710af..fa81b40db 100644 --- a/src/machine/m_at_socket7.c +++ b/src/machine/m_at_socket7.c @@ -1197,3 +1197,33 @@ machine_at_ms5164_init(const machine_t *model) return ret; } + +int +machine_at_thunderbolt_init(const machine_t *model) +{ + int ret; + + ret = bios_load_linear("roms/machines/thunderbolt/tbolt-01.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(0x07, PCI_CARD_SOUTHBRIDGE, 0, 1, 2, 3); /* PIIX4 */ + pci_register_slot(0x11, PCI_CARD_NORMAL, 0, 1, 2, 3); + pci_register_slot(0x12, PCI_CARD_NORMAL, 1, 2, 3, 0); + pci_register_slot(0x13, PCI_CARD_NORMAL, 2, 3, 0, 1); + pci_register_slot(0x14, PCI_CARD_NORMAL, 3, 0, 1, 2); + device_add(&i430tx_device); + device_add(&piix4_device); + device_add(&keyboard_ps2_ami_pci_device); + device_add(&fdc37c935_device); + device_add(&intel_flash_bxt_device); + spd_register(SPD_TYPE_SDRAM, 0x3, 128); + + return ret; +} diff --git a/src/machine/m_at_socket8.c b/src/machine/m_at_socket8.c index c8e507335..628206a61 100644 --- a/src/machine/m_at_socket8.c +++ b/src/machine/m_at_socket8.c @@ -195,6 +195,40 @@ machine_at_vs440fx_init(const machine_t *model) return ret; } +int +machine_at_gw2kvenus_init(const machine_t *model) +{ + int ret; + + ret = bios_load_linear_combined2("roms/machines/gw2kvenus/1011CS1T.BIO", + "roms/machines/gw2kvenus/1011CS1T.BI1", + "roms/machines/gw2kvenus/1011CS1T.BI2", + "roms/machines/gw2kvenus/1011CS1T.BI3", + "roms/machines/gw2kvenus/1011CS1T.RCV", + 0x3a000, 128); + + if (bios_only || !ret) + return ret; + + machine_at_common_init(model); + + pci_init(PCI_CONFIG_TYPE_1); + pci_register_slot(0x00, PCI_CARD_NORTHBRIDGE, 0, 0, 0, 0); + pci_register_slot(0x0B, PCI_CARD_NORMAL, 1, 2, 3, 4); + pci_register_slot(0x0F, PCI_CARD_NORMAL, 4, 1, 2, 3); + pci_register_slot(0x11, PCI_CARD_NORMAL, 3, 4, 1, 2); + pci_register_slot(0x13, PCI_CARD_NORMAL, 2, 3, 4, 1); + pci_register_slot(0x07, PCI_CARD_SOUTHBRIDGE, 0, 0, 0, 0); + device_add(&i440fx_device); + device_add(&piix3_device); + device_add(&keyboard_ps2_intel_ami_pci_device); + device_add(&pc87307_device); + + device_add(&intel_flash_bxt_ami_device); + + return ret; +} + int machine_at_ap440fx_init(const machine_t *model) { diff --git a/src/machine/m_xt.c b/src/machine/m_xt.c index fe519c65b..a374b58f3 100644 --- a/src/machine/m_xt.c +++ b/src/machine/m_xt.c @@ -493,6 +493,14 @@ machine_xt_vendex_init(const machine_t *model) return ret; } +static void +machine_xt_hyundai_common_init(const machine_t *model) +{ + device_add(&keyboard_xt_hyundai_device); + + machine_xt_common_init(model); +} + int machine_xt_super16t_init(const machine_t *model) { @@ -504,7 +512,7 @@ machine_xt_super16t_init(const machine_t *model) if (bios_only || !ret) return ret; - machine_xt_clone_init(model); + machine_xt_hyundai_common_init(model); /* On-board FDC cannot be disabled */ device_add(&fdc_xt_device); @@ -523,7 +531,7 @@ machine_xt_super16te_init(const machine_t *model) if (bios_only || !ret) return ret; - machine_xt_clone_init(model); + machine_xt_hyundai_common_init(model); /* On-board FDC cannot be disabled */ device_add(&fdc_xt_device); diff --git a/src/machine/m_xt_zenith.c b/src/machine/m_xt_zenith.c index 0e0f9b9b0..0da091917 100644 --- a/src/machine/m_xt_zenith.c +++ b/src/machine/m_xt_zenith.c @@ -112,12 +112,8 @@ static const device_t zenith_scratchpad_device = { void machine_zenith_init(const machine_t *model) { - machine_common_init(model); - if (fdc_type == FDC_INTERNAL) - device_add(&fdc_xt_device); - device_add(&zenith_scratchpad_device); pit_devs[0].set_out_func(pit_devs[0].data, 1, pit_refresh_timer_xt); @@ -144,6 +140,9 @@ machine_xt_z184_init(const machine_t *model) machine_zenith_init(model); + if (fdc_type == FDC_INTERNAL) + device_add(&fdc_xt_device); + lpt1_remove(); /* only one parallel port */ lpt2_remove(); lpt1_init(0x278); @@ -171,6 +170,9 @@ machine_xt_z151_init(const machine_t *model) machine_zenith_init(model); + if (fdc_type == FDC_INTERNAL) + device_add(&fdc_xt_tandy_device); + return ret; } @@ -191,6 +193,9 @@ machine_xt_z159_init(const machine_t *model) machine_zenith_init(model); + if (fdc_type == FDC_INTERNAL) + device_add(&fdc_xt_tandy_device); + /* parallel port is on the memory board */ lpt1_remove(); /* only one parallel port */ lpt2_remove(); diff --git a/src/machine/machine.c b/src/machine/machine.c index a21d8115c..c4ace6b4f 100644 --- a/src/machine/machine.c +++ b/src/machine/machine.c @@ -168,13 +168,15 @@ pit_irq0_timer(int new_out, int old_out) void machine_common_init(UNUSED(const machine_t *model)) { + uint8_t cpu_requires_fast_pit = is486 || (is8086 && (cpu_s->rspeed >= 8000000)); + /* System devices first. */ pic_init(); dma_init(); int pit_type = IS_AT(machine) ? PIT_8254 : PIT_8253; /* Select fast PIT if needed */ - if (((pit_mode == -1) && is486) || (pit_mode == 1)) + if (((pit_mode == -1) && cpu_requires_fast_pit) || (pit_mode == 1)) pit_type += 2; pit_common_init(pit_type, pit_irq0_timer, NULL); diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index 3eb1f351e..7d21a7b33 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -164,7 +164,6 @@ const machine_filter_t machine_chipsets[] = { }; /* Machines to add before machine freeze: - - PCChips M773 (440BX + SMSC with AMI BIOS); - TMC Mycomp PCI54ST; - Zeos Quadtel 486. @@ -1085,7 +1084,7 @@ const machine_t machines[] = { .ram = { .min = 256, .max = 640, - .step = 256 + .step = 128 }, .nvrmask = 0, .kbc_device = &keyboard_xt_olivetti_device, @@ -1163,7 +1162,7 @@ const machine_t machines[] = { .ram = { .min = 256, .max = 640, - .step = 256 + .step = 128 }, .nvrmask = 0, .kbc_device = &keyboard_xtclone_device, @@ -1319,7 +1318,7 @@ const machine_t machines[] = { .ram = { .min = 256, .max = 640, - .step = 256 + .step = 128 }, .nvrmask = 0, .kbc_device = &keyboard_xtclone_device, @@ -3240,6 +3239,46 @@ const machine_t machines[] = { .snd_device = NULL, .net_device = NULL }, + /* has an Award-branded KBC controller */ + { + .name = "[NEAT] Hyundai Super-286C", + .internal_name = "super286c", + .type = MACHINE_TYPE_286, + .chipset = MACHINE_CHIPSET_NEAT, + .init = machine_at_super286c_init, + .p1_handler = NULL, + .gpio_handler = NULL, + .available_flag = MACHINE_AVAILABLE, + .gpio_acpi_handler = NULL, + .cpu = { + .package = CPU_PKG_286, + .block = CPU_BLOCK_NONE, + .min_bus = 0, + .max_bus = 0, + .min_voltage = 0, + .max_voltage = 0, + .min_multi = 0, + .max_multi = 0 + }, + .bus_flags = MACHINE_AT, + .flags = MACHINE_FLAGS_NONE, + .ram = { + .min = 512, + .max = 1024, + .step = 128 + }, + .nvrmask = 127, + .kbc_device = NULL, + .kbc_p1 = 0xff, + .gpio = 0xffffffff, + .gpio_acpi = 0xffffffff, + .device = NULL, + .fdc_device = NULL, + .sio_device = NULL, + .vid_device = NULL, + .snd_device = NULL, + .net_device = NULL + }, /* Has IBM AT KBC firmware. */ { .name = "[NEAT] NCR 3302", @@ -8294,6 +8333,46 @@ const machine_t machines[] = { .snd_device = NULL, .net_device = NULL }, + /* Same as Intel Premiere PCI/II, but with a Dell OEM BIOS */ + { + .name = "[i430NX] Dell Dimension XPS Pxxx", + .internal_name = "dellplato", + .type = MACHINE_TYPE_SOCKET5, + .chipset = MACHINE_CHIPSET_INTEL_430NX, + .init = machine_at_dellplato_init, + .p1_handler = NULL, + .gpio_handler = NULL, + .available_flag = MACHINE_AVAILABLE, + .gpio_acpi_handler = NULL, + .cpu = { + .package = CPU_PKG_SOCKET5_7, + .block = CPU_BLOCK_NONE, + .min_bus = 50000000, + .max_bus = 66666667, + .min_voltage = 3520, + .max_voltage = 3520, + .min_multi = 1.5, + .max_multi = 1.5 + }, + .bus_flags = MACHINE_PS2_PCI, + .flags = MACHINE_IDE_DUAL | MACHINE_APM | MACHINE_ACPI, + .ram = { + .min = 2048, + .max = 131072, + .step = 2048 + }, + .nvrmask = 127, + .kbc_device = NULL, + .kbc_p1 = 0xff, + .gpio = 0xffffffff, + .gpio_acpi = 0xffffffff, + .device = NULL, + .fdc_device = NULL, + .sio_device = NULL, + .vid_device = NULL, + .snd_device = NULL, + .net_device = NULL + }, /* This has the Phoenix MultiKey KBC firmware. This is basically an Intel Premiere/PCI II with a fancier POST screen. */ { @@ -10629,6 +10708,46 @@ const machine_t machines[] = { .vid_device = NULL, .snd_device = NULL, .net_device = NULL + }, + /* PhoenixBIOS 4.0 Rel 6.0 for 430TX, most likely has AMI KBC of some sort. Also has onboard Yamaha YMF701 which can't be emulated yet. */ + { + .name = "[i430TX] Micronics Thunderbolt", + .internal_name = "thunderbolt", + .type = MACHINE_TYPE_SOCKET7, + .chipset = MACHINE_CHIPSET_INTEL_430TX, + .init = machine_at_thunderbolt_init, + .p1_handler = NULL, + .gpio_handler = NULL, + .available_flag = MACHINE_AVAILABLE, + .gpio_acpi_handler = NULL, + .cpu = { + .package = CPU_PKG_SOCKET5_7, + .block = CPU_BLOCK(CPU_WINCHIP, CPU_WINCHIP2), + .min_bus = 50000000, + .max_bus = 66666667, + .min_voltage = 2500, + .max_voltage = 3520, + .min_multi = 1.5, + .max_multi = 3.0 + }, + .bus_flags = MACHINE_PS2_PCI, + .flags = MACHINE_IDE_DUAL | MACHINE_APM | MACHINE_ACPI, + .ram = { + .min = 8192, + .max = 262144, + .step = 8192 + }, + .nvrmask = 255, + .kbc_device = NULL, + .kbc_p1 = 0xff, + .gpio = 0xffffffff, + .gpio_acpi = 0xffffffff, + .device = NULL, + .fdc_device = NULL, + .sio_device = NULL, + .vid_device = NULL, + .snd_device = NULL, + .net_device = NULL }, /* The BIOS sends KBC command BB and expects it to output a byte, which is AMI KBC behavior. */ { @@ -11619,6 +11738,46 @@ const machine_t machines[] = { .snd_device = NULL, .net_device = NULL }, + /* It's a Intel VS440FX with a Gateway 2000 OEM BIOS */ + { + .name = "[i440FX] Gateway 2000 Venus", + .internal_name = "gw2kvenus", + .type = MACHINE_TYPE_SOCKET8, + .chipset = MACHINE_CHIPSET_INTEL_440FX, + .init = machine_at_gw2kvenus_init, + .p1_handler = NULL, + .gpio_handler = NULL, + .available_flag = MACHINE_AVAILABLE, + .gpio_acpi_handler = NULL, + .cpu = { + .package = CPU_PKG_SOCKET8, + .block = CPU_BLOCK_NONE, + .min_bus = 60000000, + .max_bus = 66666667, + .min_voltage = 2100, + .max_voltage = 3500, + .min_multi = 2.0, + .max_multi = 3.5 + }, + .bus_flags = MACHINE_PS2_PCI, + .flags = MACHINE_IDE_DUAL | MACHINE_APM | MACHINE_ACPI, + .ram = { + .min = 8192, + .max = 524288, + .step = 8192 + }, + .nvrmask = 127, + .kbc_device = NULL, + .kbc_p1 = 0xff, + .gpio = 0xffffffff, + .gpio_acpi = 0xffffffff, + .device = NULL, + .fdc_device = NULL, + .sio_device = NULL, + .vid_device = NULL, + .snd_device = NULL, + .net_device = NULL + }, /* Has the SMC FDC73C935's on-chip KBC with Phoenix MultiKey firmware. */ { .name = "[i440FX] Micronics M6Mi", @@ -13047,6 +13206,47 @@ const machine_t machines[] = { .snd_device = NULL, .net_device = NULL }, + /* Has an ITE IT8671F Super I/O chip with on-chip KBC with AMIKey-2 KBC + firmware. */ + { + .name = "[SMSC VictoryBX-66] PC Chips M773", + .internal_name = "m773", + .type = MACHINE_TYPE_SOCKET370, + .chipset = MACHINE_CHIPSET_SMSC_VICTORYBX_66, + .init = machine_at_m773_init, + .p1_handler = NULL, + .gpio_handler = NULL, + .available_flag = MACHINE_AVAILABLE, + .gpio_acpi_handler = NULL, + .cpu = { + .package = CPU_PKG_SOCKET370, + .block = CPU_BLOCK_NONE, + .min_bus = 66666667, + .max_bus = 133333333, + .min_voltage = 1300, + .max_voltage = 3500, + .min_multi = 1.5, + .max_multi = 8.0 + }, + .bus_flags = MACHINE_PS2_AGP, + .flags = MACHINE_IDE_DUAL | MACHINE_SOUND | MACHINE_APM | MACHINE_ACPI, + .ram = { + .min = 8192, + .max = 524288, + .step = 8192 + }, + .nvrmask = 255, + .kbc_device = NULL, + .kbc_p1 = 0xff, + .gpio = 0xffffffff, + .gpio_acpi = 0xffffffff, + .device = NULL, + .fdc_device = NULL, + .sio_device = NULL, + .vid_device = NULL, + .snd_device = &cmi8738_onboard_device, + .net_device = NULL + }, /* VIA Apollo Pro */ /* Has the VIA VT82C586B southbridge with on-chip KBC identical to the VIA diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt index ce1bba325..b0ba913d5 100644 --- a/src/network/CMakeLists.txt +++ b/src/network/CMakeLists.txt @@ -14,7 +14,8 @@ # set(net_sources) list(APPEND net_sources network.c net_pcap.c net_slirp.c net_dp8390.c net_3c501.c - net_3c503.c net_ne2000.c net_pcnet.c net_wd8003.c net_plip.c net_event.c net_null.c) + net_3c503.c net_ne2000.c net_pcnet.c net_wd8003.c net_plip.c net_event.c net_null.c + net_eeprom_nmc93cxx.c net_tulip.c net_rtl8139.c net_l80225.c) find_package(PkgConfig REQUIRED) pkg_check_modules(SLIRP REQUIRED IMPORTED_TARGET slirp) diff --git a/src/network/net_eeprom_nmc93cxx.c b/src/network/net_eeprom_nmc93cxx.c new file mode 100644 index 000000000..6e5b9c60c --- /dev/null +++ b/src/network/net_eeprom_nmc93cxx.c @@ -0,0 +1,289 @@ +/* + * 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. + * + * Emulation of National Semiconductors NMC93Cxx EEPROMs. + * + * + * Authors: Cacodemon345 + * + * Copyright 2023 Cacodemon345 + */ + +/* Ported over from QEMU */ + +#include +#include +#include +#include +#include + +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/timer.h> +#include <86box/nvr.h> +#include <86box/net_eeprom_nmc93cxx.h> +#include <86box/plat_unused.h> + +struct nmc93cxx_eeprom_t { + uint8_t tick; + uint8_t address; + uint8_t command; + uint8_t writable; + + uint8_t eecs; + uint8_t eesk; + uint8_t eedo; + + uint8_t addrbits; + uint16_t size; + uint16_t data; + char filename[1024]; + uint16_t contents[]; +}; + +typedef struct nmc93cxx_eeprom_t nmc93cxx_eeprom_t; + +#ifdef ENABLE_NMC93CXX_EEPROM_LOG +int nmc93cxx_eeprom_do_log = ENABLE_NMC93CXX_EEPROM_LOG; + +static void +nmc93cxx_eeprom_log(int lvl, const char *fmt, ...) +{ + va_list ap; + + if (nmc93cxx_eeprom_do_log >= lvl) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +# define nmc93cxx_eeprom_log(lvl, fmt, ...) +#endif + +static void * +nmc93cxx_eeprom_init_params(UNUSED(const device_t *info), void *params) +{ + uint16_t nwords = 64; + uint8_t addrbits = 6; + uint8_t filldefault = 1; + nmc93cxx_eeprom_params_t *params_details = (nmc93cxx_eeprom_params_t *) params; + nmc93cxx_eeprom_t *eeprom = NULL; + if (!params) + return NULL; + + nwords = params_details->nwords; + + switch (nwords) { + case 16: + case 64: + addrbits = 6; + break; + case 128: + case 256: + addrbits = 8; + break; + default: + nwords = 64; + addrbits = 6; + break; + } + eeprom = calloc(1, sizeof(nmc93cxx_eeprom_t) + ((nwords + 1) * 2)); + if (!eeprom) + return NULL; + eeprom->size = nwords; + eeprom->addrbits = addrbits; + /* Output DO is tristate, read results in 1. */ + eeprom->eedo = 1; + + if (params_details->filename) { + FILE *fp = nvr_fopen(params_details->filename, "rb"); + strncpy(eeprom->filename, params_details->filename, sizeof(eeprom->filename) - 1); + if (fp) { + filldefault = !fread(eeprom->contents, sizeof(uint16_t), nwords, fp); + fclose(fp); + } + } + + if (filldefault) { + memcpy(eeprom->contents, params_details->default_content, nwords * sizeof(uint16_t)); + } + + return eeprom; +} + +void +nmc93cxx_eeprom_write(nmc93cxx_eeprom_t *eeprom, int eecs, int eesk, int eedi) +{ + uint8_t tick = eeprom->tick; + uint8_t eedo = eeprom->eedo; + uint16_t address = eeprom->address; + uint8_t command = eeprom->command; + + nmc93cxx_eeprom_log(1, "CS=%u SK=%u DI=%u DO=%u, tick = %u\n", + eecs, eesk, eedi, eedo, tick); + + if (!eeprom->eecs && eecs) { + /* Start chip select cycle. */ + nmc93cxx_eeprom_log(1, "Cycle start, waiting for 1st start bit (0)\n"); + tick = 0; + command = 0x0; + address = 0x0; + } else if (eeprom->eecs && !eecs) { + /* End chip select cycle. This triggers write / erase. */ + if (eeprom->writable) { + uint8_t subcommand = address >> (eeprom->addrbits - 2); + if (command == 0 && subcommand == 2) { + /* Erase all. */ + for (address = 0; address < eeprom->size; address++) { + eeprom->contents[address] = 0xffff; + } + } else if (command == 3) { + /* Erase word. */ + eeprom->contents[address] = 0xffff; + } else if (tick >= 2 + 2 + eeprom->addrbits + 16) { + if (command == 1) { + /* Write word. */ + eeprom->contents[address] &= eeprom->data; + } else if (command == 0 && subcommand == 1) { + /* Write all. */ + for (address = 0; address < eeprom->size; address++) { + eeprom->contents[address] &= eeprom->data; + } + } + } + } + /* Output DO is tristate, read results in 1. */ + eedo = 1; + } else if (eecs && !eeprom->eesk && eesk) { + /* Raising edge of clock shifts data in. */ + if (tick == 0) { + /* Wait for 1st start bit. */ + if (eedi == 0) { + nmc93cxx_eeprom_log(1, "Got correct 1st start bit, waiting for 2nd start bit (1)\n"); + tick++; + } else { + nmc93cxx_eeprom_log(1, "wrong 1st start bit (is 1, should be 0)\n"); + tick = 2; +#if 0 + ~ assert(!"wrong start bit"); +#endif + } + } else if (tick == 1) { + /* Wait for 2nd start bit. */ + if (eedi != 0) { + nmc93cxx_eeprom_log(1, "Got correct 2nd start bit, getting command + address\n"); + tick++; + } else { + nmc93cxx_eeprom_log(1, "1st start bit is longer than needed\n"); + } + } else if (tick < 2 + 2) { + /* Got 2 start bits, transfer 2 opcode bits. */ + tick++; + command <<= 1; + if (eedi) { + command += 1; + } + } else if (tick < 2 + 2 + eeprom->addrbits) { + /* Got 2 start bits and 2 opcode bits, transfer all address bits. */ + tick++; + address = ((address << 1) | eedi); + if (tick == 2 + 2 + eeprom->addrbits) { + nmc93cxx_eeprom_log(1, "%s command, address = 0x%02x (value 0x%04x)\n", + opstring[command], address, eeprom->contents[address]); + if (command == 2) { + eedo = 0; + } + address = address % eeprom->size; + if (command == 0) { + /* Command code in upper 2 bits of address. */ + switch (address >> (eeprom->addrbits - 2)) { + case 0: + nmc93cxx_eeprom_log(1, "write disable command\n"); + eeprom->writable = 0; + break; + case 1: + nmc93cxx_eeprom_log(1, "write all command\n"); + break; + case 2: + nmc93cxx_eeprom_log(1, "erase all command\n"); + break; + case 3: + nmc93cxx_eeprom_log(1, "write enable command\n"); + eeprom->writable = 1; + break; + + default: + break; + } + } else { + /* Read, write or erase word. */ + eeprom->data = eeprom->contents[address]; + } + } + } else if (tick < 2 + 2 + eeprom->addrbits + 16) { + /* Transfer 16 data bits. */ + tick++; + if (command == 2) { + /* Read word. */ + eedo = ((eeprom->data & 0x8000) != 0); + } + eeprom->data <<= 1; + eeprom->data += eedi; + } else { + nmc93cxx_eeprom_log(1, "additional unneeded tick, not processed\n"); + } + } + /* Save status of EEPROM. */ + eeprom->tick = tick; + eeprom->eecs = eecs; + eeprom->eesk = eesk; + eeprom->eedo = eedo; + eeprom->address = address; + eeprom->command = command; +} + +uint16_t +nmc93cxx_eeprom_read(nmc93cxx_eeprom_t *eeprom) +{ + /* Return status of pin DO (0 or 1). */ + return eeprom->eedo; +} + +static void +nmc93cxx_eeprom_close(void *priv) +{ + nmc93cxx_eeprom_t *eeprom = (nmc93cxx_eeprom_t *) priv; + FILE *fp = nvr_fopen(eeprom->filename, "wb"); + if (fp) { + fwrite(eeprom->contents, 2, eeprom->size, fp); + fclose(fp); + } + free(priv); +} + +uint16_t * +nmc93cxx_eeprom_data(nmc93cxx_eeprom_t *eeprom) +{ + /* Get EEPROM data array. */ + return &eeprom->contents[0]; +} + +const device_t nmc93cxx_device = { + .name = "National Semiconductor NMC93Cxx", + .internal_name = "nmc93cxx", + .flags = DEVICE_EXTPARAMS, + .local = 0, + .init_ext = nmc93cxx_eeprom_init_params, + .close = nmc93cxx_eeprom_close, + .reset = NULL, + { .available = NULL }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; diff --git a/src/network/net_l80225.c b/src/network/net_l80225.c new file mode 100644 index 000000000..6493edec6 --- /dev/null +++ b/src/network/net_l80225.c @@ -0,0 +1,41 @@ +#include +#include +#include +#include +#include + +#include <86box/86box.h> +#include <86box/timer.h> +#include <86box/pci.h> +#include <86box/io.h> +#include <86box/mem.h> +#include <86box/dma.h> +#include <86box/device.h> +#include <86box/thread.h> +#include <86box/network.h> + +uint16_t +l80225_mii_readw(uint16_t *regs, uint16_t addr) +{ + switch (addr) { + case 0x1: + return 0x782D; + case 0x2: + return 0b10110; + case 0x3: + return 0xF830; + case 0x5: + return 0x41E1; + case 0x18: + return 0xC0; + default: + return regs[addr]; + } + return 0; +} + +void +l80225_mii_writew(uint16_t *regs, uint16_t addr, uint16_t val) +{ + regs[addr] = val; +} diff --git a/src/network/net_rtl8139.c b/src/network/net_rtl8139.c new file mode 100644 index 000000000..d1e14fb12 --- /dev/null +++ b/src/network/net_rtl8139.c @@ -0,0 +1,3411 @@ +/* + * 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. + * + * Emulation of Realtek RTL8139C+ NIC. + * + * Authors: Igor Kovalenko, + * Mark Malakanov, + * Jurgen Lock, + * Frediano Ziglio, + * Benjamin Poirier. + * Cacodemon345, + * + * Copyright 2006-2023 Igor Kovalenko. + * Copyright 2006-2023 Mark Malakanov. + * Copyright 2006-2023 Jurgen Lock. + * Copyright 2010-2023 Frediano Ziglio. + * Copyright 2011-2023 Benjamin Poirier. + * Copyright 2023 Cacodemon345. + */ +#include +#include +#include +#include +#include +#include + +#include <86box/86box.h> +#include <86box/timer.h> +#include <86box/pci.h> +#include <86box/random.h> +#include <86box/io.h> +#include <86box/mem.h> +#include <86box/dma.h> +#include <86box/device.h> +#include <86box/thread.h> +#include <86box/network.h> +#include <86box/bswap.h> +#include <86box/nvr.h> +#include "cpu.h" +#include <86box/net_rtl8139.h> +#include <86box/plat_unused.h> + +#define PCI_PERIOD 30 /* 30 ns period = 33.333333 Mhz frequency */ + +#define SET_MASKED(input, mask, curr) \ + (((input) & ~(mask)) | ((curr) & (mask))) + +/* arg % size for size which is a power of 2 */ +#define MOD2(input, size) \ + ((input) & (size - 1)) + +#define ETHER_TYPE_LEN 2 + +#define VLAN_TCI_LEN 2 +#define VLAN_HLEN (ETHER_TYPE_LEN + VLAN_TCI_LEN) + +#ifdef ENABLE_RTL8139_LOG +int rtl8139_do_log = ENABLE_RTL8139_LOG; + +static void +rtl8139_log(const char *fmt, ...) +{ + va_list ap; + + if (rtl8139_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +# define rtl8139_log(fmt, ...) +#endif + +struct RTL8139State; +typedef struct RTL8139State RTL8139State; + +/* Symbolic offsets to registers. */ +enum RTL8139_registers { + MAC0 = 0, /* Ethernet hardware address. */ + MAR0 = 8, /* Multicast filter. */ + TxStatus0 = 0x10, /* Transmit status (Four 32bit registers). C mode only */ + /* Dump Tally Conter control register(64bit). C+ mode only */ + TxAddr0 = 0x20, /* Tx descriptors (also four 32bit). */ + RxBuf = 0x30, + ChipCmd = 0x37, + RxBufPtr = 0x38, + RxBufAddr = 0x3A, + IntrMask = 0x3C, + IntrStatus = 0x3E, + TxConfig = 0x40, + RxConfig = 0x44, + Timer = 0x48, /* A general-purpose counter. */ + RxMissed = 0x4C, /* 24 bits valid, write clears. */ + Cfg9346 = 0x50, + Config0 = 0x51, + Config1 = 0x52, + FlashReg = 0x54, + MediaStatus = 0x58, + Config3 = 0x59, + Config4 = 0x5A, /* absent on RTL-8139A */ + HltClk = 0x5B, + MultiIntr = 0x5C, + PCIRevisionID = 0x5E, + TxSummary = 0x60, /* TSAD register. Transmit Status of All Descriptors*/ + BasicModeCtrl = 0x62, + BasicModeStatus = 0x64, + NWayAdvert = 0x66, + NWayLPAR = 0x68, + NWayExpansion = 0x6A, + /* Undocumented registers, but required for proper operation. */ + FIFOTMS = 0x70, /* FIFO Control and test. */ + CSCR = 0x74, /* Chip Status and Configuration Register. */ + PARA78 = 0x78, + PARA7c = 0x7c, /* Magic transceiver parameter register. */ + Config5 = 0xD8, /* absent on RTL-8139A */ + /* C+ mode */ + TxPoll = 0xD9, /* Tell chip to check Tx descriptors for work */ + RxMaxSize = 0xDA, /* Max size of an Rx packet (8169 only) */ + CpCmd = 0xE0, /* C+ Command register (C+ mode only) */ + IntrMitigate = 0xE2, /* rx/tx interrupt mitigation control */ + RxRingAddrLO = 0xE4, /* 64-bit start addr of Rx ring */ + RxRingAddrHI = 0xE8, /* 64-bit start addr of Rx ring */ + TxThresh = 0xEC, /* Early Tx threshold */ +}; + +enum ClearBitMasks { + MultiIntrClear = 0xF000, + ChipCmdClear = 0xE2, + Config1Clear = (1 << 7) | (1 << 6) | (1 << 3) | (1 << 2) | (1 << 1), +}; + +enum ChipCmdBits { + CmdReset = 0x10, + CmdRxEnb = 0x08, + CmdTxEnb = 0x04, + RxBufEmpty = 0x01, +}; + +/* C+ mode */ +enum CplusCmdBits { + CPlusRxVLAN = 0x0040, /* enable receive VLAN detagging */ + CPlusRxChkSum = 0x0020, /* enable receive checksum offloading */ + CPlusRxEnb = 0x0002, + CPlusTxEnb = 0x0001, +}; + +/* Interrupt register bits, using my own meaningful names. */ +enum IntrStatusBits { + PCIErr = 0x8000, + PCSTimeout = 0x4000, + RxFIFOOver = 0x40, + RxUnderrun = 0x20, /* Packet Underrun / Link Change */ + RxOverflow = 0x10, + TxErr = 0x08, + TxOK = 0x04, + RxErr = 0x02, + RxOK = 0x01, + + RxAckBits = RxFIFOOver | RxOverflow | RxOK, +}; + +enum TxStatusBits { + TxHostOwns = 0x2000, + TxUnderrun = 0x4000, + TxStatOK = 0x8000, + TxOutOfWindow = 0x20000000, + TxAborted = 0x40000000, + TxCarrierLost = 0x80000000, +}; +enum RxStatusBits { + RxMulticast = 0x8000, + RxPhysical = 0x4000, + RxBroadcast = 0x2000, + RxBadSymbol = 0x0020, + RxRunt = 0x0010, + RxTooLong = 0x0008, + RxCRCErr = 0x0004, + RxBadAlign = 0x0002, + RxStatusOK = 0x0001, +}; + +/* Bits in RxConfig. */ +enum rx_mode_bits { + AcceptErr = 0x20, + AcceptRunt = 0x10, + AcceptBroadcast = 0x08, + AcceptMulticast = 0x04, + AcceptMyPhys = 0x02, + AcceptAllPhys = 0x01, +}; + +/* Bits in TxConfig. */ +enum tx_config_bits { + + /* Interframe Gap Time. Only TxIFG96 doesn't violate IEEE 802.3 */ + TxIFGShift = 24, + TxIFG84 = (0 << TxIFGShift), /* 8.4us / 840ns (10 / 100Mbps) */ + TxIFG88 = (1 << TxIFGShift), /* 8.8us / 880ns (10 / 100Mbps) */ + TxIFG92 = (2 << TxIFGShift), /* 9.2us / 920ns (10 / 100Mbps) */ + TxIFG96 = (3 << TxIFGShift), /* 9.6us / 960ns (10 / 100Mbps) */ + + TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */ + TxCRC = (1 << 16), /* DISABLE appending CRC to end of Tx packets */ + TxClearAbt = (1 << 0), /* Clear abort (WO) */ + TxDMAShift = 8, /* DMA burst value (0-7) is shifted this many bits */ + TxRetryShift = 4, /* TXRR value (0-15) is shifted this many bits */ + + TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */ +}; + +/* Transmit Status of All Descriptors (TSAD) Register */ +enum TSAD_bits { + TSAD_TOK3 = 1 << 15, // TOK bit of Descriptor 3 + TSAD_TOK2 = 1 << 14, // TOK bit of Descriptor 2 + TSAD_TOK1 = 1 << 13, // TOK bit of Descriptor 1 + TSAD_TOK0 = 1 << 12, // TOK bit of Descriptor 0 + TSAD_TUN3 = 1 << 11, // TUN bit of Descriptor 3 + TSAD_TUN2 = 1 << 10, // TUN bit of Descriptor 2 + TSAD_TUN1 = 1 << 9, // TUN bit of Descriptor 1 + TSAD_TUN0 = 1 << 8, // TUN bit of Descriptor 0 + TSAD_TABT3 = 1 << 07, // TABT bit of Descriptor 3 + TSAD_TABT2 = 1 << 06, // TABT bit of Descriptor 2 + TSAD_TABT1 = 1 << 05, // TABT bit of Descriptor 1 + TSAD_TABT0 = 1 << 04, // TABT bit of Descriptor 0 + TSAD_OWN3 = 1 << 03, // OWN bit of Descriptor 3 + TSAD_OWN2 = 1 << 02, // OWN bit of Descriptor 2 + TSAD_OWN1 = 1 << 01, // OWN bit of Descriptor 1 + TSAD_OWN0 = 1 << 00, // OWN bit of Descriptor 0 +}; + +/* Bits in Config1 */ +enum Config1Bits { + Cfg1_PM_Enable = 0x01, + Cfg1_VPD_Enable = 0x02, + Cfg1_PIO = 0x04, + Cfg1_MMIO = 0x08, + LWAKE = 0x10, /* not on 8139, 8139A */ + Cfg1_Driver_Load = 0x20, + Cfg1_LED0 = 0x40, + Cfg1_LED1 = 0x80, + SLEEP = (1 << 1), /* only on 8139, 8139A */ + PWRDN = (1 << 0), /* only on 8139, 8139A */ +}; + +/* Bits in Config3 */ +enum Config3Bits { + Cfg3_FBtBEn = (1 << 0), /* 1 = Fast Back to Back */ + Cfg3_FuncRegEn = (1 << 1), /* 1 = enable CardBus Function registers */ + Cfg3_CLKRUN_En = (1 << 2), /* 1 = enable CLKRUN */ + Cfg3_CardB_En = (1 << 3), /* 1 = enable CardBus registers */ + Cfg3_LinkUp = (1 << 4), /* 1 = wake up on link up */ + Cfg3_Magic = (1 << 5), /* 1 = wake up on Magic Packet (tm) */ + Cfg3_PARM_En = (1 << 6), /* 0 = software can set twister parameters */ + Cfg3_GNTSel = (1 << 7), /* 1 = delay 1 clock from PCI GNT signal */ +}; + +/* Bits in Config4 */ +enum Config4Bits { + LWPTN = (1 << 2), /* not on 8139, 8139A */ +}; + +/* Bits in Config5 */ +enum Config5Bits { + Cfg5_PME_STS = (1 << 0), /* 1 = PCI reset resets PME_Status */ + Cfg5_LANWake = (1 << 1), /* 1 = enable LANWake signal */ + Cfg5_LDPS = (1 << 2), /* 0 = save power when link is down */ + Cfg5_FIFOAddrPtr = (1 << 3), /* Realtek internal SRAM testing */ + Cfg5_UWF = (1 << 4), /* 1 = accept unicast wakeup frame */ + Cfg5_MWF = (1 << 5), /* 1 = accept multicast wakeup frame */ + Cfg5_BWF = (1 << 6), /* 1 = accept broadcast wakeup frame */ +}; + +enum RxConfigBits { + /* rx fifo threshold */ + RxCfgFIFOShift = 13, + RxCfgFIFONone = (7 << RxCfgFIFOShift), + + /* Max DMA burst */ + RxCfgDMAShift = 8, + RxCfgDMAUnlimited = (7 << RxCfgDMAShift), + + /* rx ring buffer length */ + RxCfgRcv8K = 0, + RxCfgRcv16K = (1 << 11), + RxCfgRcv32K = (1 << 12), + RxCfgRcv64K = (1 << 11) | (1 << 12), + + /* Disable packet wrap at end of Rx buffer. (not possible with 64k) */ + RxNoWrap = (1 << 7), +}; + +/* Twister tuning parameters from RealTek. + Completely undocumented, but required to tune bad links on some boards. */ +#if 0 +enum CSCRBits { + CSCR_LinkOKBit = 0x0400, + CSCR_LinkChangeBit = 0x0800, + CSCR_LinkStatusBits = 0x0f000, + CSCR_LinkDownOffCmd = 0x003c0, + CSCR_LinkDownCmd = 0x0f3c0, +#endif +enum CSCRBits { + CSCR_Testfun = 1 << 15, /* 1 = Auto-neg speeds up internal timer, WO, def 0 */ + CSCR_LD = 1 << 9, /* Active low TPI link disable signal. When low, TPI still transmits link pulses and TPI stays in good link state. def 1*/ + CSCR_HEART_BIT = 1 << 8, /* 1 = HEART BEAT enable, 0 = HEART BEAT disable. HEART BEAT function is only valid in 10Mbps mode. def 1*/ + CSCR_JBEN = 1 << 7, /* 1 = enable jabber function. 0 = disable jabber function, def 1*/ + CSCR_F_LINK_100 = 1 << 6, /* Used to login force good link in 100Mbps for diagnostic purposes. 1 = DISABLE, 0 = ENABLE. def 1*/ + CSCR_F_Connect = 1 << 5, /* Assertion of this bit forces the disconnect function to be bypassed. def 0*/ + CSCR_Con_status = 1 << 3, /* This bit indicates the status of the connection. 1 = valid connected link detected; 0 = disconnected link detected. RO def 0*/ + CSCR_Con_status_En = 1 << 2, /* Assertion of this bit configures LED1 pin to indicate connection status. def 0*/ + CSCR_PASS_SCR = 1 << 0, /* Bypass Scramble, def 0*/ +}; + +enum Cfg9346Bits { + Cfg9346_Normal = 0x00, + Cfg9346_Autoload = 0x40, + Cfg9346_Programming = 0x80, + Cfg9346_ConfigWrite = 0xC0, +}; + +typedef enum { + CH_8139 = 0, + CH_8139_K, + CH_8139A, + CH_8139A_G, + CH_8139B, + CH_8130, + CH_8139C, + CH_8100, + CH_8100B_8139D, + CH_8101, +} chip_t; + +enum chip_flags { + HasHltClk = (1 << 0), + HasLWake = (1 << 1), +}; + +#define HW_REVID(b30, b29, b28, b27, b26, b23, b22) \ + (b30 << 30 | b29 << 29 | b28 << 28 | b27 << 27 | b26 << 26 | b23 << 23 | b22 << 22) +#define HW_REVID_MASK HW_REVID(1, 1, 1, 1, 1, 1, 1) + +#define RTL8139_PCI_REVID_8139 0x10 +#define RTL8139_PCI_REVID_8139CPLUS 0x20 + +#define RTL8139_PCI_REVID RTL8139_PCI_REVID_8139CPLUS + +/* Size is 64 * 16bit words */ +#define EEPROM_9346_ADDR_BITS 6 +#define EEPROM_9346_SIZE (1 << EEPROM_9346_ADDR_BITS) +#define EEPROM_9346_ADDR_MASK (EEPROM_9346_SIZE - 1) + +enum Chip9346Operation { + Chip9346_op_mask = 0xc0, /* 10 zzzzzz */ + Chip9346_op_read = 0x80, /* 10 AAAAAA */ + Chip9346_op_write = 0x40, /* 01 AAAAAA D(15)..D(0) */ + Chip9346_op_ext_mask = 0xf0, /* 11 zzzzzz */ + Chip9346_op_write_enable = 0x30, /* 00 11zzzz */ + Chip9346_op_write_all = 0x10, /* 00 01zzzz */ + Chip9346_op_write_disable = 0x00, /* 00 00zzzz */ +}; + +enum Chip9346Mode { + Chip9346_none = 0, + Chip9346_enter_command_mode, + Chip9346_read_command, + Chip9346_data_read, /* from output register */ + Chip9346_data_write, /* to input register, then to contents at specified address */ + Chip9346_data_write_all, /* to input register, then filling contents */ +}; + +typedef struct EEprom9346 { + uint16_t contents[EEPROM_9346_SIZE]; + int mode; + uint32_t tick; + uint8_t address; + uint16_t input; + uint16_t output; + + uint8_t eecs; + uint8_t eesk; + uint8_t eedi; + uint8_t eedo; +} EEprom9346; + +#pragma pack(push, 1) +typedef struct RTL8139TallyCounters { + /* Tally counters */ + uint64_t TxOk; + uint64_t RxOk; + uint64_t TxERR; + uint32_t RxERR; + uint16_t MissPkt; + uint16_t FAE; + uint32_t Tx1Col; + uint32_t TxMCol; + uint64_t RxOkPhy; + uint64_t RxOkBrd; + uint32_t RxOkMul; + uint16_t TxAbt; + uint16_t TxUndrn; +} RTL8139TallyCounters; +#pragma pack(pop) + +/* Clears all tally counters */ +static void RTL8139TallyCounters_clear(RTL8139TallyCounters *counters); + +struct RTL8139State { + /*< private >*/ + uint8_t pci_slot; + uint8_t inst; + uint8_t irq_state; + uint8_t pad; + /*< public >*/ + + uint8_t phys[8]; /* mac address */ + uint8_t mult[8]; /* multicast mask array */ + uint8_t pci_conf[256]; + + uint32_t TxStatus[4]; /* TxStatus0 in C mode*/ /* also DTCCR[0] and DTCCR[1] in C+ mode */ + uint32_t TxAddr[4]; /* TxAddr0 */ + uint32_t RxBuf; /* Receive buffer */ + uint32_t RxBufferSize; /* internal variable, receive ring buffer size in C mode */ + uint32_t RxBufPtr; + uint32_t RxBufAddr; + + uint16_t IntrStatus; + uint16_t IntrMask; + + uint32_t TxConfig; + uint32_t RxConfig; + uint32_t RxMissed; + + uint16_t CSCR; + + uint8_t Cfg9346; + uint8_t Config0; + uint8_t Config1; + uint8_t Config3; + uint8_t Config4; + uint8_t Config5; + + uint8_t clock_enabled; + uint8_t bChipCmdState; + + uint16_t MultiIntr; + + uint16_t BasicModeCtrl; + uint16_t BasicModeStatus; + uint16_t NWayAdvert; + uint16_t NWayLPAR; + uint16_t NWayExpansion; + + uint16_t CpCmd; + + uint8_t TxThresh; + uint8_t pci_latency; + + netcard_t *nic; + + /* C ring mode */ + uint32_t currTxDesc; + + /* C+ mode */ + uint32_t cplus_enabled; + + uint32_t currCPlusRxDesc; + uint32_t currCPlusTxDesc; + + uint32_t RxRingAddrLO; + uint32_t RxRingAddrHI; + + EEprom9346 eeprom; + + uint32_t TCTR; + uint32_t TimerInt; + int64_t TCTR_base; + + /* Tally counters */ + RTL8139TallyCounters tally_counters; + + /* Non-persistent data */ + uint8_t *cplus_txbuffer; + int cplus_txbuffer_len; + int cplus_txbuffer_offset; + + /* PCI interrupt timer */ + pc_timer_t timer; + + mem_mapping_t bar_mem; + + /* Support migration to/from old versions */ + int rtl8139_mmio_io_addr_dummy; +}; + +/* Writes tally counters to memory via DMA */ +static void RTL8139TallyCounters_dma_write(RTL8139State *s, uint32_t tc_addr); + +static void +prom9346_decode_command(EEprom9346 *eeprom, uint8_t command) +{ + rtl8139_log("eeprom command 0x%02x\n", command); + + switch (command & Chip9346_op_mask) { + case Chip9346_op_read: + { + eeprom->address = command & EEPROM_9346_ADDR_MASK; + eeprom->output = eeprom->contents[eeprom->address]; + eeprom->eedo = 0; + eeprom->tick = 0; + eeprom->mode = Chip9346_data_read; + rtl8139_log("eeprom read from address 0x%02x data=0x%04x\n", + eeprom->address, eeprom->output); + } + break; + + case Chip9346_op_write: + { + eeprom->address = command & EEPROM_9346_ADDR_MASK; + eeprom->input = 0; + eeprom->tick = 0; + eeprom->mode = Chip9346_none; /* Chip9346_data_write */ + rtl8139_log("eeprom begin write to address 0x%02x\n", + eeprom->address); + } + break; + default: + eeprom->mode = Chip9346_none; + switch (command & Chip9346_op_ext_mask) { + case Chip9346_op_write_enable: + rtl8139_log("eeprom write enabled\n"); + break; + case Chip9346_op_write_all: + rtl8139_log("eeprom begin write all\n"); + break; + case Chip9346_op_write_disable: + rtl8139_log("eeprom write disabled\n"); + break; + + default: + break; + } + break; + } +} + +static void +prom9346_shift_clock(EEprom9346 *eeprom) +{ + int bit = eeprom->eedi ? 1 : 0; + + ++eeprom->tick; + + rtl8139_log("eeprom: tick %d eedi=%d eedo=%d\n", eeprom->tick, eeprom->eedi, + eeprom->eedo); + + switch (eeprom->mode) { + case Chip9346_enter_command_mode: + if (bit) { + eeprom->mode = Chip9346_read_command; + eeprom->tick = 0; + eeprom->input = 0; + rtl8139_log("eeprom: +++ synchronized, begin command read\n"); + } + break; + + case Chip9346_read_command: + eeprom->input = (eeprom->input << 1) | (bit & 1); + if (eeprom->tick == 8) { + prom9346_decode_command(eeprom, eeprom->input & 0xff); + } + break; + + case Chip9346_data_read: + eeprom->eedo = (eeprom->output & 0x8000) ? 1 : 0; + eeprom->output <<= 1; + if (eeprom->tick == 16) { +#if 1 + // the FreeBSD drivers (rl and re) don't explicitly toggle + // CS between reads (or does setting Cfg9346 to 0 count too?), + // so we need to enter wait-for-command state here + eeprom->mode = Chip9346_enter_command_mode; + eeprom->input = 0; + eeprom->tick = 0; + + rtl8139_log("eeprom: +++ end of read, awaiting next command\n"); +#else + // original behaviour + ++eeprom->address; + eeprom->address &= EEPROM_9346_ADDR_MASK; + eeprom->output = eeprom->contents[eeprom->address]; + eeprom->tick = 0; + + rtl8139_log("eeprom: +++ read next address 0x%02x data=0x%04x\n", + eeprom->address, eeprom->output); +#endif + } + break; + + case Chip9346_data_write: + eeprom->input = (eeprom->input << 1) | (bit & 1); + if (eeprom->tick == 16) { + rtl8139_log("eeprom write to address 0x%02x data=0x%04x\n", + eeprom->address, eeprom->input); + + eeprom->contents[eeprom->address] = eeprom->input; + eeprom->mode = Chip9346_none; /* waiting for next command after CS cycle */ + eeprom->tick = 0; + eeprom->input = 0; + } + break; + + case Chip9346_data_write_all: + eeprom->input = (eeprom->input << 1) | (bit & 1); + if (eeprom->tick == 16) { + for (int i = 0; i < EEPROM_9346_SIZE; i++) { + eeprom->contents[i] = eeprom->input; + } + rtl8139_log("eeprom filled with data=0x%04x\n", eeprom->input); + + eeprom->mode = Chip9346_enter_command_mode; + eeprom->tick = 0; + eeprom->input = 0; + } + break; + + default: + break; + } +} + +static int +prom9346_get_wire(RTL8139State *s) +{ + const EEprom9346 *eeprom = &s->eeprom; + if (!eeprom->eecs) + return 0; + + return eeprom->eedo; +} + +/* FIXME: This should be merged into/replaced by eeprom93xx.c. */ +static void +prom9346_set_wire(RTL8139State *s, int eecs, int eesk, int eedi) +{ + EEprom9346 *eeprom = &s->eeprom; + uint8_t old_eecs = eeprom->eecs; + uint8_t old_eesk = eeprom->eesk; + + eeprom->eecs = eecs; + eeprom->eesk = eesk; + eeprom->eedi = eedi; + + rtl8139_log("eeprom: +++ wires CS=%d SK=%d DI=%d DO=%d\n", eeprom->eecs, + eeprom->eesk, eeprom->eedi, eeprom->eedo); + + if (!old_eecs && eecs) { + /* Synchronize start */ + eeprom->tick = 0; + eeprom->input = 0; + eeprom->output = 0; + eeprom->mode = Chip9346_enter_command_mode; + + rtl8139_log("=== eeprom: begin access, enter command mode\n"); + } + + if (!eecs) { + rtl8139_log("=== eeprom: end access\n"); + return; + } + + if (!old_eesk && eesk) { + /* SK front rules */ + prom9346_shift_clock(eeprom); + } +} + +static void +rtl8139_update_irq(RTL8139State *s) +{ + uint8_t d = s->pci_slot; + int isr; + + isr = (s->IntrStatus & s->IntrMask) & 0xffff; + + rtl8139_log("Set IRQ to %d (%04x %04x)\n", isr ? 1 : 0, s->IntrStatus, + s->IntrMask); + + if (isr != 0) + pci_set_irq(d, PCI_INTA, &s->irq_state); + else + pci_clear_irq(d, PCI_INTA, &s->irq_state); +} + +static int +rtl8139_RxWrap(RTL8139State *s) +{ + /* wrapping enabled; assume 1.5k more buffer space if size < 65536 */ + return (s->RxConfig & (1 << 7)); +} + +static int +rtl8139_receiver_enabled(RTL8139State *s) +{ + return s->bChipCmdState & CmdRxEnb; +} + +static int +rtl8139_transmitter_enabled(RTL8139State *s) +{ + return s->bChipCmdState & CmdTxEnb; +} + +static int +rtl8139_cp_receiver_enabled(RTL8139State *s) +{ + return s->CpCmd & CPlusRxEnb; +} + +static int +rtl8139_cp_transmitter_enabled(RTL8139State *s) +{ + return s->CpCmd & CPlusTxEnb; +} + +static void +rtl8139_write_buffer(RTL8139State *s, const void *buf, int size) +{ + if (s->RxBufAddr + size > s->RxBufferSize) { + int wrapped = MOD2(s->RxBufAddr + size, s->RxBufferSize); + + /* write packet data */ + if (wrapped && !(s->RxBufferSize < 65536 && rtl8139_RxWrap(s))) { + rtl8139_log(">>> rx packet wrapped in buffer at %d\n", size - wrapped); + + if (size > wrapped) { + dma_bm_write(s->RxBuf + s->RxBufAddr, + buf, size - wrapped, 1); + } + + /* reset buffer pointer */ + s->RxBufAddr = 0; + + dma_bm_write(s->RxBuf + s->RxBufAddr, + buf + (size - wrapped), wrapped, 1); + + s->RxBufAddr = wrapped; + + return; + } + } + + /* non-wrapping path or overwrapping enabled */ + dma_bm_write(s->RxBuf + s->RxBufAddr, buf, size, 1); + + s->RxBufAddr += size; +} + +static __inline uint32_t +rtl8139_addr64(uint32_t low, UNUSED(uint32_t high)) +{ + return low; +} + +/* Workaround for buggy guest driver such as linux who allocates rx + * rings after the receiver were enabled. */ +static bool +rtl8139_cp_rx_valid(RTL8139State *s) +{ + return !(s->RxRingAddrLO == 0 && s->RxRingAddrHI == 0); +} + +static bool +rtl8139_can_receive(RTL8139State *s) +{ + int avail; + + /* Receive (drop) packets if card is disabled. */ + if (!s->clock_enabled) { + return true; + } + if (!rtl8139_receiver_enabled(s)) { + return true; + } + + if (rtl8139_cp_receiver_enabled(s) && rtl8139_cp_rx_valid(s)) { + /* ??? Flow control not implemented in c+ mode. + This is a hack to work around slirp deficiencies anyway. */ + return true; + } + + avail = MOD2(s->RxBufferSize + s->RxBufPtr - s->RxBufAddr, + s->RxBufferSize); + return avail == 0 || avail >= 1514 || (s->IntrMask & RxOverflow); +} + +/* From FreeBSD */ +/* XXX: optimize */ +static uint32_t +net_crc32(const uint8_t *p, int len) +{ + uint32_t crc; + int carry; + uint8_t b; + + crc = 0xffffffff; + for (int i = 0; i < len; i++) { + b = *p++; + for (uint8_t j = 0; j < 8; j++) { + carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01); + crc <<= 1; + b >>= 1; + if (carry) { + crc = ((crc ^ 0x04c11db6) | carry); + } + } + } + + return crc; +} + +uint32_t +net_crc32_le(const uint8_t *p, int len) +{ + uint32_t crc; + int carry; + uint8_t b; + + crc = 0xffffffff; + for (int i = 0; i < len; i++) { + b = *p++; + for (uint8_t j = 0; j < 8; j++) { + carry = (crc & 0x1) ^ (b & 0x01); + crc >>= 1; + b >>= 1; + if (carry) { + crc ^= 0xedb88320; + } + } + } + + return crc; +} + +#define ETH_ALEN 6 +static int +rtl8139_do_receive(void *priv, uint8_t *buf, int size_) +{ + RTL8139State *s = (RTL8139State *) priv; + /* size is the length of the buffer passed to the driver */ + size_t size = size_; + const uint8_t *dot1q_buf = NULL; + + uint32_t packet_header = 0; + uint8_t buf1[60 + VLAN_HLEN]; + + static const uint8_t broadcast_macaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + rtl8139_log(">>> received len=%u\n", (uint32_t) size); + + if (!rtl8139_can_receive(s)) + return 0; + + /* test if board clock is stopped */ + if (!s->clock_enabled) { + rtl8139_log("stopped ==========================\n"); + return -1; + } + + /* first check if receiver is enabled */ + + if (!rtl8139_receiver_enabled(s)) { + rtl8139_log("receiver disabled ================\n"); + return -1; + } + + /* XXX: check this */ + if (s->RxConfig & AcceptAllPhys) { + /* promiscuous: receive all */ + rtl8139_log(">>> packet received in promiscuous mode\n"); + + } else { + if (!memcmp(buf, broadcast_macaddr, 6)) { + /* broadcast address */ + if (!(s->RxConfig & AcceptBroadcast)) { + rtl8139_log(">>> broadcast packet rejected\n"); + + /* update tally counter */ + ++s->tally_counters.RxERR; + + return size; + } + + packet_header |= RxBroadcast; + + rtl8139_log(">>> broadcast packet received\n"); + + /* update tally counter */ + ++s->tally_counters.RxOkBrd; + + } else if (buf[0] & 0x01) { + /* multicast */ + if (!(s->RxConfig & AcceptMulticast)) { + rtl8139_log(">>> multicast packet rejected\n"); + + /* update tally counter */ + ++s->tally_counters.RxERR; + + return size; + } + + int mcast_idx = net_crc32(buf, ETH_ALEN) >> 26; + + if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7)))) { + rtl8139_log(">>> multicast address mismatch\n"); + + /* update tally counter */ + ++s->tally_counters.RxERR; + + return size; + } + + packet_header |= RxMulticast; + + rtl8139_log(">>> multicast packet received\n"); + + /* update tally counter */ + ++s->tally_counters.RxOkMul; + + } else if (s->phys[0] == buf[0] && s->phys[1] == buf[1] && s->phys[2] == buf[2] && s->phys[3] == buf[3] && s->phys[4] == buf[4] && s->phys[5] == buf[5]) { + /* match */ + if (!(s->RxConfig & AcceptMyPhys)) { + rtl8139_log(">>> rejecting physical address matching packet\n"); + + /* update tally counter */ + ++s->tally_counters.RxERR; + + return size; + } + + packet_header |= RxPhysical; + + rtl8139_log(">>> physical address matching packet received\n"); + + /* update tally counter */ + ++s->tally_counters.RxOkPhy; + + } else { + + rtl8139_log(">>> unknown packet\n"); + + /* update tally counter */ + ++s->tally_counters.RxERR; + + return size; + } + } + + if (size < 60 + VLAN_HLEN) { + memcpy(buf1, buf, size); + memset(buf1 + size, 0, 60 + VLAN_HLEN - size); + buf = buf1; + if (size < 60) { + size = 60; + } + } + + if (rtl8139_cp_receiver_enabled(s)) { + if (!rtl8139_cp_rx_valid(s)) { + return size; + } + + rtl8139_log("in C+ Rx mode ================\n"); + + /* begin C+ receiver mode */ + +/* w0 ownership flag */ +#define CP_RX_OWN (1 << 31) +/* w0 end of ring flag */ +#define CP_RX_EOR (1 << 30) +/* w0 bits 0...12 : buffer size */ +#define CP_RX_BUFFER_SIZE_MASK ((1 << 13) - 1) +/* w1 tag available flag */ +#define CP_RX_TAVA (1 << 16) +/* w1 bits 0...15 : VLAN tag */ +#define CP_RX_VLAN_TAG_MASK ((1 << 16) - 1) + /* w2 low 32bit of Rx buffer ptr */ + /* w3 high 32bit of Rx buffer ptr */ + + int descriptor = s->currCPlusRxDesc; + uint32_t cplus_rx_ring_desc; + + cplus_rx_ring_desc = rtl8139_addr64(s->RxRingAddrLO, s->RxRingAddrHI); + cplus_rx_ring_desc += 16 * descriptor; + + rtl8139_log("+++ C+ mode reading RX descriptor %d from host memory at " + "%08x %08x = " + "0x%8X" + "\n", + descriptor, s->RxRingAddrHI, + s->RxRingAddrLO, cplus_rx_ring_desc); + + uint32_t val; + uint32_t rxdw0; + uint32_t rxdw1; + uint32_t rxbufLO; + uint32_t rxbufHI; + + dma_bm_read(cplus_rx_ring_desc, (uint8_t *) &val, 4, 4); + rxdw0 = val; + dma_bm_read(cplus_rx_ring_desc + 4, (uint8_t *) &val, 4, 4); + rxdw1 = val; + dma_bm_read(cplus_rx_ring_desc + 8, (uint8_t *) &val, 4, 4); + rxbufLO = val; + dma_bm_read(cplus_rx_ring_desc + 12, (uint8_t *) &val, 4, 4); + rxbufHI = val; + + rtl8139_log("+++ C+ mode RX descriptor %d %08x %08x %08x %08x\n", + descriptor, rxdw0, rxdw1, rxbufLO, rxbufHI); + + if (!(rxdw0 & CP_RX_OWN)) { + rtl8139_log("C+ Rx mode : descriptor %d is owned by host\n", + descriptor); + + s->IntrStatus |= RxOverflow; + ++s->RxMissed; + + /* update tally counter */ + ++s->tally_counters.RxERR; + ++s->tally_counters.MissPkt; + + rtl8139_update_irq(s); + return size_; + } + + uint32_t rx_space = rxdw0 & CP_RX_BUFFER_SIZE_MASK; + + /* write VLAN info to descriptor variables. */ + if (s->CpCmd & CPlusRxVLAN && bswap16(*((uint16_t *) &buf[ETH_ALEN * 2])) == 0x8100) { + dot1q_buf = &buf[ETH_ALEN * 2]; + size -= VLAN_HLEN; + /* if too small buffer, use the tailroom added duing expansion */ + if (size < 60) { + size = 60; + } + + rxdw1 &= ~CP_RX_VLAN_TAG_MASK; + /* BE + ~le_to_cpu()~ + cpu_to_le() = BE */ + rxdw1 |= CP_RX_TAVA | *((uint16_t *) (&dot1q_buf[ETHER_TYPE_LEN])); + + rtl8139_log("C+ Rx mode : extracted vlan tag with tci: " + "%u\n", + bswap16(*((uint16_t *) &dot1q_buf[ETHER_TYPE_LEN]))); + } else { + /* reset VLAN tag flag */ + rxdw1 &= ~CP_RX_TAVA; + } + + /* TODO: scatter the packet over available receive ring descriptors space */ + + if (size + 4 > rx_space) { + rtl8139_log("C+ Rx mode : descriptor %d size %d received %u + 4\n", + descriptor, rx_space, (uint32_t) size); + + s->IntrStatus |= RxOverflow; + ++s->RxMissed; + + /* update tally counter */ + ++s->tally_counters.RxERR; + ++s->tally_counters.MissPkt; + + rtl8139_update_irq(s); + return size_; + } + + uint32_t rx_addr = rtl8139_addr64(rxbufLO, rxbufHI); + + /* receive/copy to target memory */ + if (dot1q_buf) { + dma_bm_write(rx_addr, buf, 2 * ETH_ALEN, 1); + dma_bm_write(rx_addr + 2 * ETH_ALEN, + buf + 2 * ETH_ALEN + VLAN_HLEN, + size - 2 * ETH_ALEN, 1); + } else { + dma_bm_write(rx_addr, buf, size, 1); + } + + if (s->CpCmd & CPlusRxChkSum) { + /* do some packet checksumming */ + } + + /* write checksum */ + val = (net_crc32_le(buf, size_)); + dma_bm_write(rx_addr + size, (uint8_t *) &val, 4, 4); + +/* first segment of received packet flag */ +#define CP_RX_STATUS_FS (1 << 29) +/* last segment of received packet flag */ +#define CP_RX_STATUS_LS (1 << 28) +/* multicast packet flag */ +#define CP_RX_STATUS_MAR (1 << 26) +/* physical-matching packet flag */ +#define CP_RX_STATUS_PAM (1 << 25) +/* broadcast packet flag */ +#define CP_RX_STATUS_BAR (1 << 24) +/* runt packet flag */ +#define CP_RX_STATUS_RUNT (1 << 19) +/* crc error flag */ +#define CP_RX_STATUS_CRC (1 << 18) +/* IP checksum error flag */ +#define CP_RX_STATUS_IPF (1 << 15) +/* UDP checksum error flag */ +#define CP_RX_STATUS_UDPF (1 << 14) +/* TCP checksum error flag */ +#define CP_RX_STATUS_TCPF (1 << 13) + + /* transfer ownership to target */ + rxdw0 &= ~CP_RX_OWN; + + /* set first segment bit */ + rxdw0 |= CP_RX_STATUS_FS; + + /* set last segment bit */ + rxdw0 |= CP_RX_STATUS_LS; + + /* set received packet type flags */ + if (packet_header & RxBroadcast) + rxdw0 |= CP_RX_STATUS_BAR; + if (packet_header & RxMulticast) + rxdw0 |= CP_RX_STATUS_MAR; + if (packet_header & RxPhysical) + rxdw0 |= CP_RX_STATUS_PAM; + + /* set received size */ + rxdw0 &= ~CP_RX_BUFFER_SIZE_MASK; + rxdw0 |= (size + 4); + + /* update ring data */ + val = rxdw0; + dma_bm_write(cplus_rx_ring_desc, (uint8_t *) &val, 4, 4); + val = rxdw1; + dma_bm_write(cplus_rx_ring_desc + 4, (uint8_t *) &val, 4, 4); + + /* update tally counter */ + ++s->tally_counters.RxOk; + + /* seek to next Rx descriptor */ + if (rxdw0 & CP_RX_EOR) { + s->currCPlusRxDesc = 0; + } else { + ++s->currCPlusRxDesc; + } + + rtl8139_log("done C+ Rx mode ----------------\n"); + + } else { + rtl8139_log("in ring Rx mode ================\n"); + + /* begin ring receiver mode */ + int avail = MOD2(s->RxBufferSize + s->RxBufPtr - s->RxBufAddr, s->RxBufferSize); + + /* if receiver buffer is empty then avail == 0 */ + +#define RX_ALIGN(x) (((x) + 3) & ~0x3) + + if (avail != 0 && RX_ALIGN(size + 8) >= avail) { + rtl8139_log("rx overflow: rx buffer length %d head 0x%04x " + "read 0x%04x === available 0x%04x need 0x%04zx\n", + s->RxBufferSize, s->RxBufAddr, s->RxBufPtr, avail, size + 8); + + s->IntrStatus |= RxOverflow; + ++s->RxMissed; + rtl8139_update_irq(s); + return 0; + } + + packet_header |= RxStatusOK; + + packet_header |= (((size + 4) << 16) & 0xffff0000); + + /* write header */ + uint32_t val = packet_header; + + rtl8139_write_buffer(s, (uint8_t *) &val, 4); + + rtl8139_write_buffer(s, buf, size); + + /* write checksum */ + val = (net_crc32_le(buf, size)); + rtl8139_write_buffer(s, (uint8_t *) &val, 4); + + /* correct buffer write pointer */ + s->RxBufAddr = MOD2(RX_ALIGN(s->RxBufAddr), s->RxBufferSize); + + /* now we can signal we have received something */ + + rtl8139_log("received: rx buffer length %d head 0x%04x read 0x%04x\n", + s->RxBufferSize, s->RxBufAddr, s->RxBufPtr); + } + + s->IntrStatus |= RxOK; + + { + rtl8139_update_irq(s); + } + + return size_; +} + +static void +rtl8139_reset_rxring(RTL8139State *s, uint32_t bufferSize) +{ + s->RxBufferSize = bufferSize; + s->RxBufPtr = 0; + s->RxBufAddr = 0; +} + +static void +rtl8139_reset_phy(RTL8139State *s) +{ + s->BasicModeStatus = 0x7809; + s->BasicModeStatus |= 0x0020; /* autonegotiation completed */ + /* preserve link state */ + s->BasicModeStatus |= (net_cards_conf[s->nic->card_num].link_state & NET_LINK_DOWN) ? 0 : 0x04; + + s->NWayAdvert = 0x05e1; /* all modes, full duplex */ + s->NWayLPAR = 0x05e1; /* all modes, full duplex */ + s->NWayExpansion = 0x0001; /* autonegotiation supported */ + + s->CSCR = CSCR_F_LINK_100 | CSCR_HEART_BIT | CSCR_LD; +} + +static void +rtl8139_reset(void *priv) +{ + RTL8139State *s = (RTL8139State *) priv; + + /* reset interrupt mask */ + s->IntrStatus = 0; + s->IntrMask = 0; + + rtl8139_update_irq(s); + + /* mark all status registers as owned by host */ + for (uint8_t i = 0; i < 4; ++i) { + s->TxStatus[i] = TxHostOwns; + } + + s->currTxDesc = 0; + s->currCPlusRxDesc = 0; + s->currCPlusTxDesc = 0; + + s->RxRingAddrLO = 0; + s->RxRingAddrHI = 0; + + s->RxBuf = 0; + + rtl8139_reset_rxring(s, 8192); + + /* ACK the reset */ + s->TxConfig = 0; + +#if 0 +// s->TxConfig |= HW_REVID(1, 0, 0, 0, 0, 0, 0); // RTL-8139 HasHltClk + s->clock_enabled = 0; +#else + s->TxConfig |= HW_REVID(1, 1, 1, 0, 1, 1, 0); // RTL-8139C+ HasLWake + s->clock_enabled = 1; +#endif + + s->bChipCmdState = CmdReset; /* RxBufEmpty bit is calculated on read from ChipCmd */ + + /* set initial state data */ + s->Config0 = 0x0; /* No boot ROM */ + s->Config1 = 0xC; /* IO mapped and MEM mapped registers available */ + s->Config3 = 0x1; /* fast back-to-back compatible */ + s->Config5 = 0x0; + + s->CpCmd = 0x0; /* reset C+ mode */ + s->cplus_enabled = 0; + +#if 0 + s->BasicModeCtrl = 0x3100; // 100Mbps, full duplex, autonegotiation + s->BasicModeCtrl = 0x2100; // 100Mbps, full duplex +#endif + s->BasicModeCtrl = 0x1000; // autonegotiation + + rtl8139_reset_phy(s); + + /* also reset timer and disable timer interrupt */ + s->TCTR = 0; + s->TimerInt = 0; + s->TCTR_base = 0; +#if 0 + rtl8139_set_next_tctr_time(s); +#endif + + /* reset tally counters */ + RTL8139TallyCounters_clear(&s->tally_counters); +} + +static void +RTL8139TallyCounters_clear(RTL8139TallyCounters *counters) +{ + counters->TxOk = 0; + counters->RxOk = 0; + counters->TxERR = 0; + counters->RxERR = 0; + counters->MissPkt = 0; + counters->FAE = 0; + counters->Tx1Col = 0; + counters->TxMCol = 0; + counters->RxOkPhy = 0; + counters->RxOkBrd = 0; + counters->RxOkMul = 0; + counters->TxAbt = 0; + counters->TxUndrn = 0; +} + +static void +RTL8139TallyCounters_dma_write(RTL8139State *s, uint32_t tc_addr) +{ + RTL8139TallyCounters *tally_counters = &s->tally_counters; + + dma_bm_write(tc_addr, (uint8_t *) tally_counters, sizeof(RTL8139TallyCounters), 1); +} + +static void +rtl8139_ChipCmd_write(RTL8139State *s, uint32_t val) +{ + val &= 0xff; + + rtl8139_log("ChipCmd write val=0x%08x\n", val); + + if (val & CmdReset) { + rtl8139_log("ChipCmd reset\n"); + rtl8139_reset(s); + } + if (val & CmdRxEnb) { + rtl8139_log("ChipCmd enable receiver\n"); + + s->currCPlusRxDesc = 0; + } + if (val & CmdTxEnb) { + rtl8139_log("ChipCmd enable transmitter\n"); + + s->currCPlusTxDesc = 0; + } + + /* mask unwritable bits */ + val = SET_MASKED(val, 0xe3, s->bChipCmdState); + + /* Deassert reset pin before next read */ + val &= ~CmdReset; + + s->bChipCmdState = val; +} + +static int +rtl8139_RxBufferEmpty(RTL8139State *s) +{ + int unread = MOD2(s->RxBufferSize + s->RxBufAddr - s->RxBufPtr, s->RxBufferSize); + + if (unread != 0) { + rtl8139_log("receiver buffer data available 0x%04x\n", unread); + return 0; + } + + rtl8139_log("receiver buffer is empty\n"); + + return 1; +} + +static uint32_t +rtl8139_ChipCmd_read(RTL8139State *s) +{ + uint32_t ret = s->bChipCmdState; + + if (rtl8139_RxBufferEmpty(s)) + ret |= RxBufEmpty; + + rtl8139_log("ChipCmd read val=0x%04x\n", ret); + + return ret; +} + +static void +rtl8139_CpCmd_write(RTL8139State *s, uint32_t val) +{ + val &= 0xffff; + + rtl8139_log("C+ command register write(w) val=0x%04x\n", val); + + s->cplus_enabled = 1; + + /* mask unwritable bits */ + val = SET_MASKED(val, 0xff84, s->CpCmd); + + s->CpCmd = val; +} + +static uint32_t +rtl8139_CpCmd_read(RTL8139State *s) +{ + uint32_t ret = s->CpCmd; + + rtl8139_log("C+ command register read(w) val=0x%04x\n", ret); + + return ret; +} + +static void +rtl8139_IntrMitigate_write(UNUSED(RTL8139State *s), uint32_t val) +{ + rtl8139_log("C+ IntrMitigate register write(w) val=0x%04x\n", val); +} + +static uint32_t +rtl8139_IntrMitigate_read(UNUSED(RTL8139State *s)) +{ + uint32_t ret = 0; + + rtl8139_log("C+ IntrMitigate register read(w) val=0x%04x\n", ret); + + return ret; +} + +static int +rtl8139_config_writable(RTL8139State *s) +{ + if ((s->Cfg9346 & Chip9346_op_mask) == Cfg9346_ConfigWrite) { + return 1; + } + + rtl8139_log("Configuration registers are write-protected\n"); + + return 0; +} + +static void +rtl8139_BasicModeCtrl_write(RTL8139State *s, uint32_t val) +{ + val &= 0xffff; + + rtl8139_log("BasicModeCtrl register write(w) val=0x%04x\n", val); + + /* mask unwritable bits */ + uint32_t mask = 0xccff; + + if (1 || !rtl8139_config_writable(s)) { + /* Speed setting and autonegotiation enable bits are read-only */ + mask |= 0x3000; + /* Duplex mode setting is read-only */ + mask |= 0x0100; + } + + if (val & 0x8000) { + /* Reset PHY */ + rtl8139_reset_phy(s); + } + + val = SET_MASKED(val, mask, s->BasicModeCtrl); + + s->BasicModeCtrl = val; +} + +static uint32_t +rtl8139_BasicModeCtrl_read(RTL8139State *s) +{ + uint32_t ret = s->BasicModeCtrl; + + rtl8139_log("BasicModeCtrl register read(w) val=0x%04x\n", ret); + + return ret; +} + +static void +rtl8139_BasicModeStatus_write(RTL8139State *s, uint32_t val) +{ + val &= 0xffff; + + rtl8139_log("BasicModeStatus register write(w) val=0x%04x\n", val); + + /* mask unwritable bits */ + val = SET_MASKED(val, 0xff3f, s->BasicModeStatus); + + s->BasicModeStatus = val; +} + +static uint32_t +rtl8139_BasicModeStatus_read(RTL8139State *s) +{ + uint32_t ret = s->BasicModeStatus; + + rtl8139_log("BasicModeStatus register read(w) val=0x%04x\n", ret); + + return ret; +} + +static void +rtl8139_Cfg9346_write(RTL8139State *s, uint32_t val) +{ + val &= 0xff; + + rtl8139_log("Cfg9346 write val=0x%02x\n", val); + + /* mask unwritable bits */ + val = SET_MASKED(val, 0x31, s->Cfg9346); + + uint32_t opmode = val & 0xc0; + uint32_t eeprom_val = val & 0xf; + + if (opmode == 0x80) { + /* eeprom access */ + int eecs = (eeprom_val & 0x08) ? 1 : 0; + int eesk = (eeprom_val & 0x04) ? 1 : 0; + int eedi = (eeprom_val & 0x02) ? 1 : 0; + prom9346_set_wire(s, eecs, eesk, eedi); + } else if (opmode == 0x40) { + /* Reset. */ + val = 0; + rtl8139_reset(s); + } + + s->Cfg9346 = val; +} + +static uint32_t +rtl8139_Cfg9346_read(RTL8139State *s) +{ + uint32_t ret = s->Cfg9346; + + uint32_t opmode = ret & 0xc0; + + if (opmode == 0x80) { + /* eeprom access */ + int eedo = prom9346_get_wire(s); + if (eedo) { + ret |= 0x01; + } else { + ret &= ~0x01; + } + } + + rtl8139_log("Cfg9346 read val=0x%02x\n", ret); + + return ret; +} + +static void +rtl8139_Config0_write(RTL8139State *s, uint32_t val) +{ + val &= 0xff; + + rtl8139_log("Config0 write val=0x%02x\n", val); + + if (!rtl8139_config_writable(s)) { + return; + } + + /* mask unwritable bits */ + val = SET_MASKED(val, 0xf8, s->Config0); + + s->Config0 = val; +} + +static uint32_t +rtl8139_Config0_read(RTL8139State *s) +{ + uint32_t ret = s->Config0; + + rtl8139_log("Config0 read val=0x%02x\n", ret); + + return ret; +} + +static void +rtl8139_Config1_write(RTL8139State *s, uint32_t val) +{ + val &= 0xff; + + rtl8139_log("Config1 write val=0x%02x\n", val); + + if (!rtl8139_config_writable(s)) { + return; + } + + /* mask unwritable bits */ + val = SET_MASKED(val, 0xC, s->Config1); + + s->Config1 = val; +} + +static uint32_t +rtl8139_Config1_read(RTL8139State *s) +{ + uint32_t ret = s->Config1; + + rtl8139_log("Config1 read val=0x%02x\n", ret); + + return ret; +} + +static void +rtl8139_Config3_write(RTL8139State *s, uint32_t val) +{ + val &= 0xff; + + rtl8139_log("Config3 write val=0x%02x\n", val); + + if (!rtl8139_config_writable(s)) { + return; + } + + /* mask unwritable bits */ + val = SET_MASKED(val, 0x8F, s->Config3); + + s->Config3 = val; +} + +static uint32_t +rtl8139_Config3_read(RTL8139State *s) +{ + uint32_t ret = s->Config3; + + rtl8139_log("Config3 read val=0x%02x\n", ret); + + return ret; +} + +static void +rtl8139_Config4_write(RTL8139State *s, uint32_t val) +{ + val &= 0xff; + + rtl8139_log("Config4 write val=0x%02x\n", val); + + if (!rtl8139_config_writable(s)) { + return; + } + + /* mask unwritable bits */ + val = SET_MASKED(val, 0x0a, s->Config4); + + s->Config4 = val; +} + +static uint32_t +rtl8139_Config4_read(RTL8139State *s) +{ + uint32_t ret = s->Config4; + + rtl8139_log("Config4 read val=0x%02x\n", ret); + + return ret; +} + +static void +rtl8139_Config5_write(RTL8139State *s, uint32_t val) +{ + val &= 0xff; + + rtl8139_log("Config5 write val=0x%02x\n", val); + + /* mask unwritable bits */ + val = SET_MASKED(val, 0x80, s->Config5); + + s->Config5 = val; +} + +static uint32_t +rtl8139_Config5_read(RTL8139State *s) +{ + uint32_t ret = s->Config5; + + rtl8139_log("Config5 read val=0x%02x\n", ret); + + return ret; +} + +static void +rtl8139_TxConfig_write(RTL8139State *s, uint32_t val) +{ + if (!rtl8139_transmitter_enabled(s)) { + rtl8139_log("transmitter disabled; no TxConfig write val=0x%08x\n", val); + return; + } + + rtl8139_log("TxConfig write val=0x%08x\n", val); + + val = SET_MASKED(val, TxVersionMask | 0x8070f80f, s->TxConfig); + + s->TxConfig = val; +} + +static void +rtl8139_TxConfig_writeb(RTL8139State *s, uint32_t val) +{ + rtl8139_log("RTL8139C TxConfig via write(b) val=0x%02x\n", val); + + uint32_t tc = s->TxConfig; + + tc &= 0xFFFFFF00; + tc |= (val & 0x000000FF); + rtl8139_TxConfig_write(s, tc); +} + +static uint32_t +rtl8139_TxConfig_read(RTL8139State *s) +{ + uint32_t ret = s->TxConfig; + + rtl8139_log("TxConfig read val=0x%04x\n", ret); + + return ret; +} + +static void +rtl8139_RxConfig_write(RTL8139State *s, uint32_t val) +{ + rtl8139_log("RxConfig write val=0x%08x\n", val); + + /* mask unwritable bits */ + val = SET_MASKED(val, 0xf0fc0040, s->RxConfig); + + s->RxConfig = val; + + /* reset buffer size and read/write pointers */ + rtl8139_reset_rxring(s, 8192 << ((s->RxConfig >> 11) & 0x3)); + + rtl8139_log("RxConfig write reset buffer size to %d\n", s->RxBufferSize); +} + +static uint32_t +rtl8139_RxConfig_read(RTL8139State *s) +{ + uint32_t ret = s->RxConfig; + + rtl8139_log("RxConfig read val=0x%08x\n", ret); + + return ret; +} + +void +rtl8139_network_rx_put(netcard_t *card, uint8_t *bufp, int len) +{ + (void) network_rx_put(card, bufp, len); +} + +static void +rtl8139_transfer_frame(RTL8139State *s, uint8_t *buf, int size, + UNUSED(int do_interrupt), const uint8_t *dot1q_buf) +{ + void (*network_func)(netcard_t *, uint8_t *, int) = (TxLoopBack == (s->TxConfig & TxLoopBack)) ? rtl8139_network_rx_put : network_tx; + if (!size) { + rtl8139_log("+++ empty ethernet frame\n"); + return; + } + + if (dot1q_buf && size >= ETH_ALEN * 2) { + network_func(s->nic, buf, ETH_ALEN * 2); + network_func(s->nic, (uint8_t *) dot1q_buf, VLAN_HLEN); + network_func(s->nic, buf + ETH_ALEN * 2, size - ETH_ALEN * 2); + return; + } + + network_func(s->nic, buf, size); + return; +} + +static int +rtl8139_transmit_one(RTL8139State *s, int descriptor) +{ + int txsize = s->TxStatus[descriptor] & 0x1fff; + uint8_t txbuffer[0x2000]; + + if (!rtl8139_transmitter_enabled(s)) { + rtl8139_log("+++ cannot transmit from descriptor %d: transmitter " + "disabled\n", descriptor); + return 0; + } + + if (s->TxStatus[descriptor] & TxHostOwns) { + rtl8139_log("+++ cannot transmit from descriptor %d: owned by host " + "(%08x)\n", descriptor, s->TxStatus[descriptor]); + return 0; + } + + rtl8139_log("+++ transmitting from descriptor %d\n", descriptor); + + rtl8139_log("+++ transmit reading %d bytes from host memory at 0x%08x\n", + txsize, s->TxAddr[descriptor]); + + dma_bm_read(s->TxAddr[descriptor], txbuffer, txsize, 1); + + /* Mark descriptor as transferred */ + s->TxStatus[descriptor] |= TxHostOwns; + s->TxStatus[descriptor] |= TxStatOK; + + rtl8139_transfer_frame(s, txbuffer, txsize, 0, NULL); + + rtl8139_log("+++ transmitted %d bytes from descriptor %d\n", txsize, + descriptor); + + /* update interrupt */ + s->IntrStatus |= TxOK; + rtl8139_update_irq(s); + + return 1; +} + +#define TCP_HEADER_CLEAR_FLAGS(tcp, off) ((tcp)->th_offset_flags &= cpu_to_be16(~TCP_FLAGS_ONLY(off))) + +/* produces ones' complement sum of data */ +static uint16_t +ones_complement_sum(uint8_t *data, size_t len) +{ + uint32_t result = 0; + + for (; len > 1; data += 2, len -= 2) { + result += *(uint16_t *) data; + } + + /* add the remainder byte */ + if (len) { + uint8_t odd[2] = { *data, 0 }; + result += *(uint16_t *) odd; + } + + while (result >> 16) + result = (result & 0xffff) + (result >> 16); + + return result; +} + +static uint16_t +ip_checksum(void *data, size_t len) +{ + return ~ones_complement_sum((uint8_t *) data, len); +} + +/* TODO: Replace these with equivalents in 86Box if applicable. */ +struct ip_header { + uint8_t ip_ver_len; /* version and header length */ + uint8_t ip_tos; /* type of service */ + uint16_t ip_len; /* total length */ + uint16_t ip_id; /* identification */ + uint16_t ip_off; /* fragment offset field */ + uint8_t ip_ttl; /* time to live */ + uint8_t ip_p; /* protocol */ + uint16_t ip_sum; /* checksum */ + uint32_t ip_src, ip_dst; /* source and destination address */ +}; + +typedef struct tcp_header { + uint16_t th_sport; /* source port */ + uint16_t th_dport; /* destination port */ + uint32_t th_seq; /* sequence number */ + uint32_t th_ack; /* acknowledgment number */ + uint16_t th_offset_flags; /* data offset, reserved 6 bits, */ + /* TCP protocol flags */ + uint16_t th_win; /* window */ + uint16_t th_sum; /* checksum */ + uint16_t th_urp; /* urgent pointer */ +} tcp_header; + +typedef struct ip_pseudo_header { + uint32_t ip_src; + uint32_t ip_dst; + uint8_t zeros; + uint8_t ip_proto; + uint16_t ip_payload; +} ip_pseudo_header; + +typedef struct udp_header { + uint16_t uh_sport; /* source port */ + uint16_t uh_dport; /* destination port */ + uint16_t uh_ulen; /* udp length */ + uint16_t uh_sum; /* udp checksum */ +} udp_header; + +#define ETH_HLEN 14 +#define ETH_P_IP (0x0800) +#define IP_PROTO_TCP (6) +#define IP_PROTO_UDP (17) +#define IP_HEADER_VERSION_4 (4) +#define TH_PUSH 0x08 +#define TH_FIN 0x01 + +#define IP_HDR_GET_LEN(p) \ + ((*(uint8_t *) ((p + __builtin_offsetof(struct ip_header, ip_ver_len))) & 0x0F) << 2) + +#define IP_HEADER_VERSION(ip) \ + (((ip)->ip_ver_len >> 4) & 0xf) + +#define TCP_HEADER_DATA_OFFSET(tcp) \ + (((be16_to_cpu((tcp)->th_offset_flags) >> 12) & 0xf) << 2) + +static int +rtl8139_cplus_transmit_one(RTL8139State *s) +{ + if (!rtl8139_transmitter_enabled(s)) { + rtl8139_log("+++ C+ mode: transmitter disabled\n"); + return 0; + } + + if (!rtl8139_cp_transmitter_enabled(s)) { + rtl8139_log("+++ C+ mode: C+ transmitter disabled\n"); + return 0; + } + int descriptor = s->currCPlusTxDesc; + + uint32_t cplus_tx_ring_desc = rtl8139_addr64(s->TxAddr[0], s->TxAddr[1]); + + /* Normal priority ring */ + cplus_tx_ring_desc += 16 * descriptor; + + rtl8139_log("+++ C+ mode reading TX descriptor %d from host memory at " + "%08x %08x = 0x%08X\n", descriptor, s->TxAddr[1], + s->TxAddr[0], cplus_tx_ring_desc); + + uint32_t val; + uint32_t txdw0; + uint32_t txdw1; + uint32_t txbufLO; + uint32_t txbufHI; + + dma_bm_read(cplus_tx_ring_desc, (uint8_t *) &val, 4, 4); + txdw0 = le32_to_cpu(val); + dma_bm_read(cplus_tx_ring_desc + 4, (uint8_t *) &val, 4, 4); + txdw1 = le32_to_cpu(val); + dma_bm_read(cplus_tx_ring_desc + 8, (uint8_t *) &val, 4, 4); + txbufLO = le32_to_cpu(val); + dma_bm_read(cplus_tx_ring_desc + 12, (uint8_t *) &val, 4, 4); + txbufHI = le32_to_cpu(val); + + rtl8139_log("+++ C+ mode TX descriptor %d %08x %08x %08x %08x\n", descriptor, + txdw0, txdw1, txbufLO, txbufHI); + +/* w0 ownership flag */ +#define CP_TX_OWN (1 << 31) +/* w0 end of ring flag */ +#define CP_TX_EOR (1 << 30) +/* first segment of received packet flag */ +#define CP_TX_FS (1 << 29) +/* last segment of received packet flag */ +#define CP_TX_LS (1 << 28) +/* large send packet flag */ +#define CP_TX_LGSEN (1 << 27) +/* large send MSS mask, bits 16...26 */ +#define CP_TC_LGSEN_MSS_SHIFT 16 +#define CP_TC_LGSEN_MSS_MASK ((1 << 11) - 1) + +/* IP checksum offload flag */ +#define CP_TX_IPCS (1 << 18) +/* UDP checksum offload flag */ +#define CP_TX_UDPCS (1 << 17) +/* TCP checksum offload flag */ +#define CP_TX_TCPCS (1 << 16) + +/* w0 bits 0...15 : buffer size */ +#define CP_TX_BUFFER_SIZE (1 << 16) +#define CP_TX_BUFFER_SIZE_MASK (CP_TX_BUFFER_SIZE - 1) +/* w1 add tag flag */ +#define CP_TX_TAGC (1 << 17) +/* w1 bits 0...15 : VLAN tag (big endian) */ +#define CP_TX_VLAN_TAG_MASK ((1 << 16) - 1) +/* w2 low 32bit of Rx buffer ptr */ +/* w3 high 32bit of Rx buffer ptr */ + +/* set after transmission */ +/* FIFO underrun flag */ +#define CP_TX_STATUS_UNF (1 << 25) +/* transmit error summary flag, valid if set any of three below */ +#define CP_TX_STATUS_TES (1 << 23) +/* out-of-window collision flag */ +#define CP_TX_STATUS_OWC (1 << 22) +/* link failure flag */ +#define CP_TX_STATUS_LNKF (1 << 21) +/* excessive collisions flag */ +#define CP_TX_STATUS_EXC (1 << 20) + + if (!(txdw0 & CP_TX_OWN)) { + rtl8139_log("C+ Tx mode : descriptor %d is owned by host\n", descriptor); + return 0; + } + + rtl8139_log("+++ C+ Tx mode : transmitting from descriptor %d\n", descriptor); + + if (txdw0 & CP_TX_FS) { + rtl8139_log("+++ C+ Tx mode : descriptor %d is first segment " + "descriptor\n", descriptor); + + /* reset internal buffer offset */ + s->cplus_txbuffer_offset = 0; + } + + int txsize = txdw0 & CP_TX_BUFFER_SIZE_MASK; + uint32_t tx_addr = rtl8139_addr64(txbufLO, txbufHI); + + /* make sure we have enough space to assemble the packet */ + if (!s->cplus_txbuffer) { + s->cplus_txbuffer_len = CP_TX_BUFFER_SIZE; + s->cplus_txbuffer = calloc(s->cplus_txbuffer_len, 1); + s->cplus_txbuffer_offset = 0; + + rtl8139_log("+++ C+ mode transmission buffer allocated space %d\n", + s->cplus_txbuffer_len); + } + + if (s->cplus_txbuffer_offset + txsize >= s->cplus_txbuffer_len) { + /* The spec didn't tell the maximum size, stick to CP_TX_BUFFER_SIZE */ + txsize = s->cplus_txbuffer_len - s->cplus_txbuffer_offset; + rtl8139_log("+++ C+ mode transmission buffer overrun, truncated descriptor" + "length to %d\n", txsize); + } + + /* append more data to the packet */ + + rtl8139_log("+++ C+ mode transmit reading %d bytes from host memory at " + "%08X to offset %d\n", txsize, tx_addr, s->cplus_txbuffer_offset); + + dma_bm_read(tx_addr, + s->cplus_txbuffer + s->cplus_txbuffer_offset, txsize, 1); + s->cplus_txbuffer_offset += txsize; + + /* seek to next Rx descriptor */ + if (txdw0 & CP_TX_EOR) { + s->currCPlusTxDesc = 0; + } else { + ++s->currCPlusTxDesc; + if (s->currCPlusTxDesc >= 64) + s->currCPlusTxDesc = 0; + } + + /* Build the Tx Status Descriptor */ + uint32_t tx_status = txdw0; + + /* transfer ownership to target */ + tx_status &= ~CP_TX_OWN; + + /* reset error indicator bits */ + tx_status &= ~CP_TX_STATUS_UNF; + tx_status &= ~CP_TX_STATUS_TES; + tx_status &= ~CP_TX_STATUS_OWC; + tx_status &= ~CP_TX_STATUS_LNKF; + tx_status &= ~CP_TX_STATUS_EXC; + + /* update ring data */ + val = cpu_to_le32(tx_status); + dma_bm_write(cplus_tx_ring_desc, (uint8_t *) &val, 4, 4); + + /* Now decide if descriptor being processed is holding the last segment of packet */ + if (txdw0 & CP_TX_LS) { + uint8_t dot1q_buffer_space[VLAN_HLEN]; + uint16_t *dot1q_buffer; + + rtl8139_log("+++ C+ Tx mode : descriptor %d is last segment descriptor\n", + descriptor); + + /* can transfer fully assembled packet */ + + uint8_t *saved_buffer = s->cplus_txbuffer; + int saved_size = s->cplus_txbuffer_offset; + int saved_buffer_len = s->cplus_txbuffer_len; + + /* create vlan tag */ + if (txdw1 & CP_TX_TAGC) { + /* the vlan tag is in BE byte order in the descriptor + * BE + le_to_cpu() + ~swap()~ = cpu */ + rtl8139_log("+++ C+ Tx mode : inserting vlan tag with " + "tci: %u\n", bswap16(txdw1 & CP_TX_VLAN_TAG_MASK)); + + dot1q_buffer = (uint16_t *) dot1q_buffer_space; + dot1q_buffer[0] = cpu_to_be16(0x8100); + /* BE + le_to_cpu() + ~cpu_to_le()~ = BE */ + dot1q_buffer[1] = cpu_to_le16(txdw1 & CP_TX_VLAN_TAG_MASK); + } else { + dot1q_buffer = NULL; + } + + /* reset the card space to protect from recursive call */ + s->cplus_txbuffer = NULL; + s->cplus_txbuffer_offset = 0; + s->cplus_txbuffer_len = 0; + + if (txdw0 & (CP_TX_IPCS | CP_TX_UDPCS | CP_TX_TCPCS | CP_TX_LGSEN)) { + rtl8139_log("+++ C+ mode offloaded task checksum\n"); + + /* Large enough for Ethernet and IP headers? */ + if (saved_size < ETH_HLEN + sizeof(struct ip_header)) { + goto skip_offload; + } + + /* ip packet header */ + struct ip_header *ip = NULL; + int hlen = 0; + uint8_t ip_protocol = 0; + uint16_t ip_data_len = 0; + + uint8_t *eth_payload_data = NULL; + size_t eth_payload_len = 0; + + int proto = be16_to_cpu(*(uint16_t *) (saved_buffer + 12)); + if (proto != ETH_P_IP) { + goto skip_offload; + } + + rtl8139_log("+++ C+ mode has IP packet\n"); + + /* Note on memory alignment: eth_payload_data is 16-bit aligned + * since saved_buffer is allocated with g_malloc() and ETH_HLEN is + * even. 32-bit accesses must use ldl/stl wrappers to avoid + * unaligned accesses. + */ + eth_payload_data = saved_buffer + ETH_HLEN; + eth_payload_len = saved_size - ETH_HLEN; + + ip = (struct ip_header *) eth_payload_data; + + if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) { + rtl8139_log("+++ C+ mode packet has bad IP version %d " + "expected %d\n", IP_HEADER_VERSION(ip), + IP_HEADER_VERSION_4); + goto skip_offload; + } + + hlen = IP_HDR_GET_LEN(ip); + if (hlen < sizeof(struct ip_header) || hlen > eth_payload_len) { + goto skip_offload; + } + + ip_protocol = ip->ip_p; + + ip_data_len = be16_to_cpu(ip->ip_len); + if (ip_data_len < hlen || ip_data_len > eth_payload_len) { + goto skip_offload; + } + ip_data_len -= hlen; + + if (!(txdw0 & CP_TX_LGSEN) && (txdw0 & CP_TX_IPCS)) { + rtl8139_log("+++ C+ mode need IP checksum\n"); + + ip->ip_sum = 0; + ip->ip_sum = ip_checksum(ip, hlen); + rtl8139_log("+++ C+ mode IP header len=%d checksum=%04x\n", + hlen, ip->ip_sum); + } + + if ((txdw0 & CP_TX_LGSEN) && ip_protocol == IP_PROTO_TCP) { + /* Large enough for the TCP header? */ + if (ip_data_len < sizeof(tcp_header)) { + goto skip_offload; + } + + int large_send_mss = (txdw0 >> CP_TC_LGSEN_MSS_SHIFT) & CP_TC_LGSEN_MSS_MASK; + if (large_send_mss == 0) { + goto skip_offload; + } + + rtl8139_log("+++ C+ mode offloaded task TSO IP data %d " + "frame data %d specified MSS=%d\n", + ip_data_len, saved_size - ETH_HLEN, large_send_mss); + + /* maximum IP header length is 60 bytes */ + uint8_t saved_ip_header[60]; + + /* save IP header template; data area is used in tcp checksum calculation */ + memcpy(saved_ip_header, eth_payload_data, hlen); + + /* a placeholder for checksum calculation routine in tcp case */ + uint8_t *data_to_checksum = eth_payload_data + hlen - 12; +#if 0 + size_t data_to_checksum_len = eth_payload_len - hlen + 12; +#endif + + /* pointer to TCP header */ + tcp_header *p_tcp_hdr = (tcp_header *) (eth_payload_data + hlen); + + int tcp_hlen = TCP_HEADER_DATA_OFFSET(p_tcp_hdr); + + /* Invalid TCP data offset? */ + if (tcp_hlen < sizeof(tcp_header) || tcp_hlen > ip_data_len) { + goto skip_offload; + } + + int tcp_data_len = ip_data_len - tcp_hlen; + + rtl8139_log("+++ C+ mode TSO IP data len %d TCP hlen %d TCP " + "data len %d\n", ip_data_len, tcp_hlen, tcp_data_len); + + /* note the cycle below overwrites IP header data, + but restores it from saved_ip_header before sending packet */ + + int is_last_frame = 0; + + for (int tcp_send_offset = 0; tcp_send_offset < tcp_data_len; tcp_send_offset += large_send_mss) { + uint16_t chunk_size = large_send_mss; + + /* check if this is the last frame */ + if (tcp_send_offset + large_send_mss >= tcp_data_len) { + is_last_frame = 1; + chunk_size = tcp_data_len - tcp_send_offset; + } + +#if 0 + rtl8139_log("+++ C+ mode TSO TCP seqno %08x\n", + ldl_be_p(&p_tcp_hdr->th_seq)); +#endif + + /* add 4 TCP pseudoheader fields */ + /* copy IP source and destination fields */ + memcpy(data_to_checksum, saved_ip_header + 12, 8); + + rtl8139_log("+++ C+ mode TSO calculating TCP checksum for " + "packet with %d bytes data\n", tcp_hlen + chunk_size); + + if (tcp_send_offset) { + memcpy((uint8_t *) p_tcp_hdr + tcp_hlen, (uint8_t *) p_tcp_hdr + tcp_hlen + tcp_send_offset, chunk_size); + } + +#define TCP_FLAGS_ONLY(flags) ((flags) &0x3f) +#define TCP_HEADER_CLEAR_FLAGS(tcp, off) ((tcp)->th_offset_flags &= cpu_to_be16(~TCP_FLAGS_ONLY(off))) + /* keep PUSH and FIN flags only for the last frame */ + if (!is_last_frame) { + TCP_HEADER_CLEAR_FLAGS(p_tcp_hdr, TH_PUSH | TH_FIN); + } + + /* recalculate TCP checksum */ + ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *) data_to_checksum; + p_tcpip_hdr->zeros = 0; + p_tcpip_hdr->ip_proto = IP_PROTO_TCP; + p_tcpip_hdr->ip_payload = cpu_to_be16(tcp_hlen + chunk_size); + + p_tcp_hdr->th_sum = 0; + + int tcp_checksum = ip_checksum(data_to_checksum, tcp_hlen + chunk_size + 12); + rtl8139_log("+++ C+ mode TSO TCP checksum %04x\n", tcp_checksum); + + p_tcp_hdr->th_sum = tcp_checksum; + + /* restore IP header */ + memcpy(eth_payload_data, saved_ip_header, hlen); + + /* set IP data length and recalculate IP checksum */ + ip->ip_len = cpu_to_be16(hlen + tcp_hlen + chunk_size); + + /* increment IP id for subsequent frames */ + ip->ip_id = cpu_to_be16(tcp_send_offset / large_send_mss + be16_to_cpu(ip->ip_id)); + + ip->ip_sum = 0; + ip->ip_sum = ip_checksum(eth_payload_data, hlen); + rtl8139_log("+++ C+ mode TSO IP header len=%d checksum=%04x\n", + hlen, ip->ip_sum); + + int tso_send_size = ETH_HLEN + hlen + tcp_hlen + chunk_size; + rtl8139_log("+++ C+ mode TSO transferring packet size %d\n", + tso_send_size); + rtl8139_transfer_frame(s, saved_buffer, tso_send_size, + 0, (uint8_t *) dot1q_buffer); + + /* add transferred count to TCP sequence number */ +#if 0 + stl_be_p(&p_tcp_hdr->th_seq, + chunk_size + ldl_be_p(&p_tcp_hdr->th_seq)); +#endif + p_tcp_hdr->th_seq = bswap32(chunk_size + bswap32(p_tcp_hdr->th_seq)); + } + + /* Stop sending this frame */ + saved_size = 0; + } else if (!(txdw0 & CP_TX_LGSEN) && (txdw0 & (CP_TX_TCPCS | CP_TX_UDPCS))) { + rtl8139_log("+++ C+ mode need TCP or UDP checksum\n"); + + /* maximum IP header length is 60 bytes */ + uint8_t saved_ip_header[60]; + memcpy(saved_ip_header, eth_payload_data, hlen); + + uint8_t *data_to_checksum = eth_payload_data + hlen - 12; +#if 0 + size_t data_to_checksum_len = eth_payload_len - hlen + 12; +#endif + + /* add 4 TCP pseudoheader fields */ + /* copy IP source and destination fields */ + memcpy(data_to_checksum, saved_ip_header + 12, 8); + + if ((txdw0 & CP_TX_TCPCS) && ip_protocol == IP_PROTO_TCP) { + rtl8139_log("+++ C+ mode calculating TCP checksum for " + "packet with %d bytes data\n", ip_data_len); + + ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *) data_to_checksum; + p_tcpip_hdr->zeros = 0; + p_tcpip_hdr->ip_proto = IP_PROTO_TCP; + p_tcpip_hdr->ip_payload = cpu_to_be16(ip_data_len); + + tcp_header *p_tcp_hdr = (tcp_header *) (data_to_checksum + 12); + + p_tcp_hdr->th_sum = 0; + + int tcp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12); + rtl8139_log("+++ C+ mode TCP checksum %04x\n", tcp_checksum); + + p_tcp_hdr->th_sum = tcp_checksum; + } else if ((txdw0 & CP_TX_UDPCS) && ip_protocol == IP_PROTO_UDP) { + rtl8139_log("+++ C+ mode calculating UDP checksum for " + "packet with %d bytes data\n", ip_data_len); + + ip_pseudo_header *p_udpip_hdr = (ip_pseudo_header *) data_to_checksum; + p_udpip_hdr->zeros = 0; + p_udpip_hdr->ip_proto = IP_PROTO_UDP; + p_udpip_hdr->ip_payload = cpu_to_be16(ip_data_len); + + udp_header *p_udp_hdr = (udp_header *) (data_to_checksum + 12); + + p_udp_hdr->uh_sum = 0; + + int udp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12); + rtl8139_log("+++ C+ mode UDP checksum %04x\n", udp_checksum); + + p_udp_hdr->uh_sum = udp_checksum; + } + + /* restore IP header */ + memcpy(eth_payload_data, saved_ip_header, hlen); + } + } + +skip_offload: + /* update tally counter */ + ++s->tally_counters.TxOk; + + rtl8139_log("+++ C+ mode transmitting %d bytes packet\n", saved_size); + + rtl8139_transfer_frame(s, saved_buffer, saved_size, 1, + (uint8_t *) dot1q_buffer); + + /* restore card space if there was no recursion and reset offset */ + if (!s->cplus_txbuffer) { + s->cplus_txbuffer = saved_buffer; + s->cplus_txbuffer_len = saved_buffer_len; + s->cplus_txbuffer_offset = 0; + } else + free(saved_buffer); + } else { + rtl8139_log("+++ C+ mode transmission continue to next descriptor\n"); + } + + return 1; +} + +static void +rtl8139_cplus_transmit(RTL8139State *s) +{ + int txcount = 0; + + while (txcount < 64 && rtl8139_cplus_transmit_one(s)) { + ++txcount; + } + + /* Mark transfer completed */ + if (!txcount) { + rtl8139_log("C+ mode : transmitter queue stalled, current TxDesc = %d\n", + s->currCPlusTxDesc); + } else { + /* update interrupt status */ + s->IntrStatus |= TxOK; + rtl8139_update_irq(s); + } +} + +static void +rtl8139_transmit(RTL8139State *s) +{ + int descriptor = s->currTxDesc; + int txcount = 0; + + /*while*/ + if (rtl8139_transmit_one(s, descriptor)) { + ++s->currTxDesc; + s->currTxDesc %= 4; + ++txcount; + } + + /* Mark transfer completed */ + if (!txcount) { + rtl8139_log("transmitter queue stalled, current TxDesc = %d\n", + s->currTxDesc); + } +} + +static void +rtl8139_TxStatus_write(RTL8139State *s, uint32_t txRegOffset, uint32_t val) +{ + int descriptor = txRegOffset / 4; + + /* handle C+ transmit mode register configuration */ + + if (s->cplus_enabled) { + rtl8139_log("RTL8139C+ DTCCR write offset=0x%x val=0x%08x " + "descriptor=%d\n", txRegOffset, val, descriptor); + + /* handle Dump Tally Counters command */ + s->TxStatus[descriptor] = val; + + if (descriptor == 0 && (val & 0x8)) { + uint32_t tc_addr = rtl8139_addr64(s->TxStatus[0] & ~0x3f, s->TxStatus[1]); + + /* dump tally counters to specified memory location */ + RTL8139TallyCounters_dma_write(s, tc_addr); + + /* mark dump completed */ + s->TxStatus[0] &= ~0x8; + } + + return; + } + + rtl8139_log("TxStatus write offset=0x%x val=0x%08x descriptor=%d\n", + txRegOffset, val, descriptor); + + /* mask only reserved bits */ + val &= ~0xff00c000; /* these bits are reset on write */ + val = SET_MASKED(val, 0x00c00000, s->TxStatus[descriptor]); + + s->TxStatus[descriptor] = val; + + /* attempt to start transmission */ + rtl8139_transmit(s); +} + +static uint32_t +rtl8139_TxStatus_TxAddr_read(UNUSED(RTL8139State *s), uint32_t regs[], + uint32_t base, uint8_t addr, + int size) +{ + uint32_t reg = (addr - base) / 4; + uint32_t offset = addr & 0x3; + uint32_t ret = 0; + + if (addr & (size - 1)) { + rtl8139_log("not implemented read for TxStatus/TxAddr " + "addr=0x%x size=0x%x\n", addr, size); + return ret; + } + + switch (size) { + case 1: /* fall through */ + case 2: /* fall through */ + case 4: + ret = (regs[reg] >> offset * 8) & (((uint64_t) 1 << (size * 8)) - 1); + rtl8139_log("TxStatus/TxAddr[%d] read addr=0x%x size=0x%x val=0x%08x\n", + reg, addr, size, ret); + break; + + default: + rtl8139_log("unsupported size 0x%x of TxStatus/TxAddr reading\n", size); + break; + } + + return ret; +} + +static uint16_t +rtl8139_TSAD_read(RTL8139State *s) +{ + uint16_t ret = 0; + + /* Simulate TSAD, it is read only anyway */ + + ret = ((s->TxStatus[3] & TxStatOK) ? TSAD_TOK3 : 0) + | ((s->TxStatus[2] & TxStatOK) ? TSAD_TOK2 : 0) + | ((s->TxStatus[1] & TxStatOK) ? TSAD_TOK1 : 0) + | ((s->TxStatus[0] & TxStatOK) ? TSAD_TOK0 : 0) + + | ((s->TxStatus[3] & TxUnderrun) ? TSAD_TUN3 : 0) + | ((s->TxStatus[2] & TxUnderrun) ? TSAD_TUN2 : 0) + | ((s->TxStatus[1] & TxUnderrun) ? TSAD_TUN1 : 0) + | ((s->TxStatus[0] & TxUnderrun) ? TSAD_TUN0 : 0) + + | ((s->TxStatus[3] & TxAborted) ? TSAD_TABT3 : 0) + | ((s->TxStatus[2] & TxAborted) ? TSAD_TABT2 : 0) + | ((s->TxStatus[1] & TxAborted) ? TSAD_TABT1 : 0) + | ((s->TxStatus[0] & TxAborted) ? TSAD_TABT0 : 0) + + | ((s->TxStatus[3] & TxHostOwns) ? TSAD_OWN3 : 0) + | ((s->TxStatus[2] & TxHostOwns) ? TSAD_OWN2 : 0) + | ((s->TxStatus[1] & TxHostOwns) ? TSAD_OWN1 : 0) + | ((s->TxStatus[0] & TxHostOwns) ? TSAD_OWN0 : 0); + + rtl8139_log("TSAD read val=0x%04x\n", ret); + + return ret; +} + +static uint16_t +rtl8139_CSCR_read(RTL8139State *s) +{ + uint16_t ret = s->CSCR; + + rtl8139_log("CSCR read val=0x%04x\n", ret); + + return ret; +} + +static void +rtl8139_TxAddr_write(RTL8139State *s, uint32_t txAddrOffset, uint32_t val) +{ + rtl8139_log("TxAddr write offset=0x%x val=0x%08x\n", txAddrOffset, val); + + s->TxAddr[txAddrOffset / 4] = val; +} + +static uint32_t +rtl8139_TxAddr_read(RTL8139State *s, uint32_t txAddrOffset) +{ + uint32_t ret = s->TxAddr[txAddrOffset / 4]; + + rtl8139_log("TxAddr read offset=0x%x val=0x%08x\n", txAddrOffset, ret); + + return ret; +} + +static void +rtl8139_RxBufPtr_write(RTL8139State *s, uint32_t val) +{ + rtl8139_log("RxBufPtr write val=0x%04x\n", val); + + /* this value is off by 16 */ + s->RxBufPtr = MOD2(val + 0x10, s->RxBufferSize); + + rtl8139_log(" CAPR write: rx buffer length %d head 0x%04x read 0x%04x\n", + s->RxBufferSize, s->RxBufAddr, s->RxBufPtr); +} + +static uint32_t +rtl8139_RxBufPtr_read(RTL8139State *s) +{ + /* this value is off by 16 */ + uint32_t ret = s->RxBufPtr - 0x10; + + rtl8139_log("RxBufPtr read val=0x%04x\n", ret); + + return ret; +} + +static uint32_t +rtl8139_RxBufAddr_read(RTL8139State *s) +{ + /* this value is NOT off by 16 */ + uint32_t ret = s->RxBufAddr; + + rtl8139_log("RxBufAddr read val=0x%04x\n", ret); + + return ret; +} + +static void +rtl8139_RxBuf_write(RTL8139State *s, uint32_t val) +{ + rtl8139_log("RxBuf write val=0x%08x\n", val); + + s->RxBuf = val; + + /* may need to reset rxring here */ +} + +static uint32_t +rtl8139_RxBuf_read(RTL8139State *s) +{ + uint32_t ret = s->RxBuf; + + rtl8139_log("RxBuf read val=0x%08x\n", ret); + + return ret; +} + +static void +rtl8139_IntrMask_write(RTL8139State *s, uint32_t val) +{ + rtl8139_log("IntrMask write(w) val=0x%04x\n", val); + + /* mask unwritable bits */ + val = SET_MASKED(val, 0x1e00, s->IntrMask); + + s->IntrMask = val; + + rtl8139_update_irq(s); +} + +static uint32_t +rtl8139_IntrMask_read(RTL8139State *s) +{ + uint32_t ret = s->IntrMask; + + rtl8139_log("IntrMask read(w) val=0x%04x\n", ret); + + return ret; +} + +static void +rtl8139_IntrStatus_write(RTL8139State *s, uint32_t val) +{ + rtl8139_log("IntrStatus write(w) val=0x%04x\n", val); + +#if 0 + + /* writing to ISR has no effect */ + + return; + +#else + uint16_t newStatus = s->IntrStatus & ~val; + + /* mask unwritable bits */ + newStatus = SET_MASKED(newStatus, 0x1e00, s->IntrStatus); + + /* writing 1 to interrupt status register bit clears it */ + s->IntrStatus = 0; + rtl8139_update_irq(s); + + s->IntrStatus = newStatus; + rtl8139_update_irq(s); + +#endif +} + +static uint32_t +rtl8139_IntrStatus_read(RTL8139State *s) +{ + uint32_t ret = s->IntrStatus; + + rtl8139_log("IntrStatus read(w) val=0x%04x\n", ret); + +#if 0 + + /* reading ISR clears all interrupts */ + s->IntrStatus = 0; + + rtl8139_update_irq(s); + +#endif + + return ret; +} + +static void +rtl8139_MultiIntr_write(RTL8139State *s, uint32_t val) +{ + rtl8139_log("MultiIntr write(w) val=0x%04x\n", val); + + /* mask unwritable bits */ + val = SET_MASKED(val, 0xf000, s->MultiIntr); + + s->MultiIntr = val; +} + +static uint32_t +rtl8139_MultiIntr_read(RTL8139State *s) +{ + uint32_t ret = s->MultiIntr; + + rtl8139_log("MultiIntr read(w) val=0x%04x\n", ret); + + return ret; +} + +static void +rtl8139_io_writeb(uint32_t addr, uint8_t val, void *priv) +{ + RTL8139State *s = priv; + + addr &= 0xFF; + switch (addr) { + case MAC0 ... MAC0 + 4: + s->phys[addr - MAC0] = val; + break; + case MAC0 + 5: + s->phys[addr - MAC0] = val; + break; + case MAC0 + 6 ... MAC0 + 7: + /* reserved */ + break; + case MAR0 ... MAR0 + 7: + s->mult[addr - MAR0] = val; + break; + case ChipCmd: + rtl8139_ChipCmd_write(s, val); + break; + case Cfg9346: + rtl8139_Cfg9346_write(s, val); + break; + case TxConfig: /* windows driver sometimes writes using byte-lenth call */ + rtl8139_TxConfig_writeb(s, val); + break; + case Config0: + rtl8139_Config0_write(s, val); + break; + case Config1: + rtl8139_Config1_write(s, val); + break; + case Config3: + rtl8139_Config3_write(s, val); + break; + case Config4: + rtl8139_Config4_write(s, val); + break; + case Config5: + rtl8139_Config5_write(s, val); + break; + case MediaStatus: + /* ignore */ + rtl8139_log("not implemented write(b) to MediaStatus val=0x%02x\n", val); + break; + + case HltClk: + rtl8139_log("HltClk write val=0x%08x\n", val); + if (val == 'R') { + s->clock_enabled = 1; + } else if (val == 'H') { + s->clock_enabled = 0; + } + break; + + case TxThresh: + rtl8139_log("C+ TxThresh write(b) val=0x%02x\n", val); + s->TxThresh = val; + break; + + case TxPoll: + rtl8139_log("C+ TxPoll write(b) val=0x%02x\n", val); + if (val & (1 << 7)) { + rtl8139_log("C+ TxPoll high priority transmission (not " + "implemented)\n"); +#if 0 + rtl8139_cplus_transmit(s); +#endif + } + if (val & (1 << 6)) { + rtl8139_log("C+ TxPoll normal priority transmission\n"); + rtl8139_cplus_transmit(s); + } + + break; + + default: + rtl8139_log("not implemented write(b) addr=0x%x val=0x%02x\n", addr, val); + break; + } +} + +static void +rtl8139_io_writew(uint32_t addr, uint16_t val, void *priv) +{ + RTL8139State *s = priv; + + addr &= 0xFF; + switch (addr) { + case IntrMask: + rtl8139_IntrMask_write(s, val); + break; + + case IntrStatus: + rtl8139_IntrStatus_write(s, val); + break; + + case MultiIntr: + rtl8139_MultiIntr_write(s, val); + break; + + case RxBufPtr: + rtl8139_RxBufPtr_write(s, val); + break; + + case BasicModeCtrl: + rtl8139_BasicModeCtrl_write(s, val); + break; + case BasicModeStatus: + rtl8139_BasicModeStatus_write(s, val); + break; + case NWayAdvert: + rtl8139_log("NWayAdvert write(w) val=0x%04x\n", val); + s->NWayAdvert = val; + break; + case NWayLPAR: + rtl8139_log("forbidden NWayLPAR write(w) val=0x%04x\n", val); + break; + case NWayExpansion: + rtl8139_log("NWayExpansion write(w) val=0x%04x\n", val); + s->NWayExpansion = val; + break; + + case CpCmd: + rtl8139_CpCmd_write(s, val); + break; + + case IntrMitigate: + rtl8139_IntrMitigate_write(s, val); + break; + + default: + rtl8139_log("ioport write(w) addr=0x%x val=0x%04x via write(b)\n", + addr, val); + + rtl8139_io_writeb(addr, val & 0xff, priv); + rtl8139_io_writeb(addr + 1, (val >> 8) & 0xff, priv); + break; + } +} + +/* TODO: Implement timer. */ + +static void +rtl8139_io_writel(uint32_t addr, uint32_t val, void *priv) +{ + RTL8139State *s = priv; + + addr &= 0xFF; + switch (addr) { + case RxMissed: + rtl8139_log("RxMissed clearing on write\n"); + s->RxMissed = 0; + break; + + case TxConfig: + rtl8139_TxConfig_write(s, val); + break; + + case RxConfig: + rtl8139_RxConfig_write(s, val); + break; + + case TxStatus0 ... TxStatus0 + 4 * 4 - 1: + rtl8139_TxStatus_write(s, addr - TxStatus0, val); + break; + + case TxAddr0 ... TxAddr0 + 4 * 4 - 1: + rtl8139_TxAddr_write(s, addr - TxAddr0, val); + break; + + case RxBuf: + rtl8139_RxBuf_write(s, val); + break; + + case RxRingAddrLO: + rtl8139_log("C+ RxRing low bits write val=0x%08x\n", val); + s->RxRingAddrLO = val; + break; + + case RxRingAddrHI: + rtl8139_log("C+ RxRing high bits write val=0x%08x\n", val); + s->RxRingAddrHI = val; + break; + + case Timer: + rtl8139_log("TCTR Timer reset on write\n"); + s->TCTR = 0; +#if 0 + s->TCTR_base = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + rtl8139_set_next_tctr_time(s); +#endif + break; + + case FlashReg: + rtl8139_log("FlashReg TimerInt write val=0x%08x\n", val); + if (s->TimerInt != val) + s->TimerInt = val; + break; + + default: + rtl8139_log("ioport write(l) addr=0x%x val=0x%08x via write(b)\n", + addr, val); + rtl8139_io_writeb(addr, val & 0xff, priv); + rtl8139_io_writeb(addr + 1, (val >> 8) & 0xff, priv); + rtl8139_io_writeb(addr + 2, (val >> 16) & 0xff, priv); + rtl8139_io_writeb(addr + 3, (val >> 24) & 0xff, priv); + break; + } +} + +static uint8_t +rtl8139_io_readb(uint32_t addr, void *priv) +{ + RTL8139State *s = priv; + uint8_t ret; + + addr &= 0xFF; + switch (addr) { + case MAC0 ... MAC0 + 5: + ret = s->phys[addr - MAC0]; + break; + case MAC0 + 6 ... MAC0 + 7: + ret = 0; + break; + case MAR0 ... MAR0 + 7: + ret = s->mult[addr - MAR0]; + break; + case TxStatus0 ... TxStatus0 + 4 * 4 - 1: + ret = rtl8139_TxStatus_TxAddr_read(s, s->TxStatus, TxStatus0, + addr, 1); + break; + case ChipCmd: + ret = rtl8139_ChipCmd_read(s); + break; + case Cfg9346: + ret = rtl8139_Cfg9346_read(s); + break; + case Config0: + ret = rtl8139_Config0_read(s); + break; + case Config1: + ret = rtl8139_Config1_read(s); + break; + case Config3: + ret = rtl8139_Config3_read(s); + break; + case Config4: + ret = rtl8139_Config4_read(s); + break; + case Config5: + ret = rtl8139_Config5_read(s); + break; + + case MediaStatus: + /* The LinkDown bit of MediaStatus is inverse with link status */ + ret = 0xd0 | (~s->BasicModeStatus & 0x04); + rtl8139_log("MediaStatus read 0x%x\n", ret); + break; + + case HltClk: + ret = s->clock_enabled; + rtl8139_log("HltClk read 0x%x\n", ret); + break; + + case PCIRevisionID: + ret = RTL8139_PCI_REVID; + rtl8139_log("PCI Revision ID read 0x%x\n", ret); + break; + + case TxThresh: + ret = s->TxThresh; + rtl8139_log("C+ TxThresh read(b) val=0x%02x\n", ret); + break; + + case 0x43: /* Part of TxConfig register. Windows driver tries to read it */ + ret = s->TxConfig >> 24; + rtl8139_log("RTL8139C TxConfig at 0x43 read(b) val=0x%02x\n", ret); + break; + + default: + rtl8139_log("not implemented read(b) addr=0x%x\n", addr); + ret = 0; + break; + } + + return ret; +} + +static uint16_t +rtl8139_io_readw(uint32_t addr, void *priv) +{ + RTL8139State *s = priv; + uint16_t ret; + + addr &= 0xFF; + switch (addr) { + case TxAddr0 ... TxAddr0 + 4 * 4 - 1: + ret = rtl8139_TxStatus_TxAddr_read(s, s->TxAddr, TxAddr0, addr, 2); + break; + case IntrMask: + ret = rtl8139_IntrMask_read(s); + break; + + case IntrStatus: + ret = rtl8139_IntrStatus_read(s); + break; + + case MultiIntr: + ret = rtl8139_MultiIntr_read(s); + break; + + case RxBufPtr: + ret = rtl8139_RxBufPtr_read(s); + break; + + case RxBufAddr: + ret = rtl8139_RxBufAddr_read(s); + break; + + case BasicModeCtrl: + ret = rtl8139_BasicModeCtrl_read(s); + break; + case BasicModeStatus: + ret = rtl8139_BasicModeStatus_read(s); + break; + case NWayAdvert: + ret = s->NWayAdvert; + rtl8139_log("NWayAdvert read(w) val=0x%04x\n", ret); + break; + case NWayLPAR: + ret = s->NWayLPAR; + rtl8139_log("NWayLPAR read(w) val=0x%04x\n", ret); + break; + case NWayExpansion: + ret = s->NWayExpansion; + rtl8139_log("NWayExpansion read(w) val=0x%04x\n", ret); + break; + + case CpCmd: + ret = rtl8139_CpCmd_read(s); + break; + + case IntrMitigate: + ret = rtl8139_IntrMitigate_read(s); + break; + + case TxSummary: + ret = rtl8139_TSAD_read(s); + break; + + case CSCR: + ret = rtl8139_CSCR_read(s); + break; + + default: + rtl8139_log("ioport read(w) addr=0x%x via read(b)\n", addr); + + ret = rtl8139_io_readb(addr, priv); + ret |= rtl8139_io_readb(addr + 1, priv) << 8; + + rtl8139_log("ioport read(w) addr=0x%x val=0x%04x\n", addr, ret); + break; + } + + return ret; +} + +static uint32_t +rtl8139_io_readl(uint32_t addr, void *priv) +{ + RTL8139State *s = priv; + uint32_t ret; + + addr &= 0xFF; + switch (addr) { + case RxMissed: + ret = s->RxMissed; + + rtl8139_log("RxMissed read val=0x%08x\n", ret); + break; + + case TxConfig: + ret = rtl8139_TxConfig_read(s); + break; + + case RxConfig: + ret = rtl8139_RxConfig_read(s); + break; + + case TxStatus0 ... TxStatus0 + 4 * 4 - 1: + ret = rtl8139_TxStatus_TxAddr_read(s, s->TxStatus, TxStatus0, + addr, 4); + break; + + case TxAddr0 ... TxAddr0 + 4 * 4 - 1: + ret = rtl8139_TxAddr_read(s, addr - TxAddr0); + break; + + case RxBuf: + ret = rtl8139_RxBuf_read(s); + break; + + case RxRingAddrLO: + ret = s->RxRingAddrLO; + rtl8139_log("C+ RxRing low bits read val=0x%08x\n", ret); + break; + + case RxRingAddrHI: + ret = s->RxRingAddrHI; + rtl8139_log("C+ RxRing high bits read val=0x%08x\n", ret); + break; + + case Timer: +#if 0 + ret = (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - s->TCTR_base) / + PCI_PERIOD; +#endif + ret = s->TCTR; + rtl8139_log("TCTR Timer read val=0x%08x\n", ret); + break; + + case FlashReg: + ret = s->TimerInt; + rtl8139_log("FlashReg TimerInt read val=0x%08x\n", ret); + break; + + default: + rtl8139_log("ioport read(l) addr=0x%x via read(b)\n", addr); + + ret = rtl8139_io_readb(addr, priv); + ret |= rtl8139_io_readb(addr + 1, priv) << 8; + ret |= rtl8139_io_readb(addr + 2, priv) << 16; + ret |= rtl8139_io_readb(addr + 3, priv) << 24; + + rtl8139_log("read(l) addr=0x%x val=%08x\n", addr, ret); + break; + } + + return ret; +} + +static uint32_t +rtl8139_io_readl_ioport(uint16_t addr, void *priv) +{ + return rtl8139_io_readl(addr, priv); +} + +static uint16_t +rtl8139_io_readw_ioport(uint16_t addr, void *priv) +{ + return rtl8139_io_readw(addr, priv); +} + +static uint8_t +rtl8139_io_readb_ioport(uint16_t addr, void *priv) +{ + return rtl8139_io_readb(addr, priv); +} + +static void +rtl8139_io_writel_ioport(uint16_t addr, uint32_t val, void *priv) +{ + return rtl8139_io_writel(addr, val, priv); +} + +static void +rtl8139_io_writew_ioport(uint16_t addr, uint16_t val, void *priv) +{ + return rtl8139_io_writew(addr, val, priv); +} + +static void +rtl8139_io_writeb_ioport(uint16_t addr, uint8_t val, void *priv) +{ + return rtl8139_io_writeb(addr, val, priv); +} + +static int +rtl8139_set_link_status(void *priv, uint32_t link_state) +{ + RTL8139State *s = (RTL8139State *) priv; + + if (link_state & NET_LINK_DOWN) { + s->BasicModeStatus &= ~0x04; + } else { + s->BasicModeStatus |= 0x04; + } + + s->IntrStatus |= RxUnderrun; + rtl8139_update_irq(s); + return 0; +} + +static void +rtl8139_timer(void *priv) +{ + RTL8139State *s = priv; + + timer_on_auto(&s->timer, 1000000.0 / ((double) cpu_pci_speed)); + + if (!s->clock_enabled) { + rtl8139_log(">>> timer: clock is not running\n"); + return; + } + + s->TCTR++; + + if (s->TCTR == s->TimerInt && s->TimerInt != 0) { + s->IntrStatus |= PCSTimeout; + rtl8139_update_irq(s); + } +} + +static uint8_t +rtl8139_pci_read(UNUSED(int func), int addr, void *priv) +{ + const RTL8139State *s = (RTL8139State *) priv; + + switch (addr) { + default: + return s->pci_conf[addr & 0xFF]; + case 0x00: + return 0xEC; + case 0x01: + return 0x10; + case 0x02: + return 0x39; + case 0x03: + return 0x81; + case 0x07: + return 0x02; + case 0x06: + return 0x80; + case 0x05: + return s->pci_conf[addr & 0xFF] & 1; + case 0x08: + return 0x20; + case 0x09: + return 0x0; + case 0x0a: + return 0x0; + case 0x0b: + return 0x2; + case 0x0d: + return s->pci_latency; + case 0x10: + return 1; + case 0x14: + return 0; + case 0x2c: + return 0xEC; + case 0x2d: + return 0x10; + case 0x2e: + return 0x39; + case 0x2f: + return 0x81; + case 0x34: + return 0xdc; + case 0x3d: + return PCI_INTA; + } +} + +static void +rtl8139_pci_write(int func, int addr, uint8_t val, void *priv) +{ + RTL8139State *s = (RTL8139State *) priv; + + switch (addr) { + case 0x04: + mem_mapping_disable(&s->bar_mem); + io_removehandler((s->pci_conf[0x11] << 8), 256, + rtl8139_io_readb_ioport, rtl8139_io_readw_ioport, rtl8139_io_readl_ioport, + rtl8139_io_writeb_ioport, rtl8139_io_writew_ioport, rtl8139_io_writel_ioport, + priv); + s->pci_conf[addr & 0xFF] = val; + if (val & PCI_COMMAND_IO) + io_sethandler((s->pci_conf[0x11] << 8), 256, + rtl8139_io_readb_ioport, rtl8139_io_readw_ioport, rtl8139_io_readl_ioport, + rtl8139_io_writeb_ioport, rtl8139_io_writew_ioport, rtl8139_io_writel_ioport, + priv); + if ((val & PCI_COMMAND_MEM) && s->bar_mem.size) + mem_mapping_enable(&s->bar_mem); + break; + case 0x05: + s->pci_conf[addr & 0xFF] = val & 1; + break; + case 0x0c: + s->pci_conf[addr & 0xFF] = val; + break; + case 0x0d: + s->pci_latency = val; + break; + case 0x10: + case 0x11: + io_removehandler((s->pci_conf[0x11] << 8), 256, + rtl8139_io_readb_ioport, rtl8139_io_readw_ioport, rtl8139_io_readl_ioport, + rtl8139_io_writeb_ioport, rtl8139_io_writew_ioport, rtl8139_io_writel_ioport, + priv); + s->pci_conf[addr & 0xFF] = val; + if (s->pci_conf[0x4] & PCI_COMMAND_IO) + io_sethandler((s->pci_conf[0x11] << 8), 256, + rtl8139_io_readb_ioport, rtl8139_io_readw_ioport, rtl8139_io_readl_ioport, + rtl8139_io_writeb_ioport, rtl8139_io_writew_ioport, rtl8139_io_writel_ioport, + priv); + break; + case 0x14: + case 0x15: + case 0x16: + case 0x17: + s->pci_conf[addr & 0xFF] = val; + if (s->pci_conf[0x4] & PCI_COMMAND_MEM) + mem_mapping_set_addr(&s->bar_mem, (s->pci_conf[0x15] << 8) | (s->pci_conf[0x16] << 16) | (s->pci_conf[0x17] << 24), 256); + break; + case 0x3c: + s->pci_conf[addr & 0xFF] = val; + break; + } +} + +static void * +nic_init(const device_t *info) +{ + RTL8139State *s = calloc(1, sizeof(RTL8139State)); + FILE *fp = NULL; + char eeprom_filename[1024] = { 0 }; + uint8_t *mac_bytes; + uint32_t mac; + + mem_mapping_add(&s->bar_mem, 0, 0, rtl8139_io_readb, rtl8139_io_readw, rtl8139_io_readl, rtl8139_io_writeb, rtl8139_io_writew, rtl8139_io_writel, NULL, MEM_MAPPING_EXTERNAL, s); + pci_add_card(PCI_ADD_NORMAL, rtl8139_pci_read, rtl8139_pci_write, s, &s->pci_slot); + s->inst = device_get_instance(); + + snprintf(eeprom_filename, sizeof(eeprom_filename), "eeprom_rtl8139c_plus_%d.nvr", s->inst); + + fp = nvr_fopen(eeprom_filename, "rb"); + if (fp) { + fread(s->eeprom.contents, 2, 64, fp); + fclose(fp); + fp = NULL; + } else { + /* prepare eeprom */ + s->eeprom.contents[0] = 0x8129; + + /* PCI vendor and device ID should be mirrored here */ + s->eeprom.contents[1] = 0x10EC; + s->eeprom.contents[2] = 0x8139; + + /* XXX: Get proper MAC addresses from real EEPROM dumps. OID taken from net_ne2000.c */ +#ifdef USE_REALTEK_OID + s->eeprom.contents[7] = 0xe000; + s->eeprom.contents[8] = 0x124c; +#else + s->eeprom.contents[7] = 0x1400; + s->eeprom.contents[8] = 0x122a; +#endif + s->eeprom.contents[9] = 0x1413; + } + + mac_bytes = (uint8_t *) &(s->eeprom.contents[7]); + + /* See if we have a local MAC address configured. */ + mac = device_get_config_mac("mac", -1); + + /* Set up our BIA. */ + if (mac & 0xff000000) { + /* Generate new local MAC. */ + mac_bytes[3] = random_generate(); + mac_bytes[4] = random_generate(); + mac_bytes[5] = random_generate(); + mac = (((int) mac_bytes[3]) << 16); + mac |= (((int) mac_bytes[4]) << 8); + mac |= ((int) mac_bytes[5]); + device_set_config_mac("mac", mac); + } else { + mac_bytes[3] = (mac >> 16) & 0xff; + mac_bytes[4] = (mac >> 8) & 0xff; + mac_bytes[5] = (mac & 0xff); + } + + s->nic = network_attach(s, (uint8_t *) &s->eeprom.contents[7], rtl8139_do_receive, rtl8139_set_link_status); + timer_add(&s->timer, rtl8139_timer, s, 0); + timer_on_auto(&s->timer, 1000000.0 / cpu_pci_speed); + + s->cplus_txbuffer = NULL; + s->cplus_txbuffer_len = 0; + s->cplus_txbuffer_offset = 0; + + return s; +} + +static void +nic_close(void *priv) +{ + const RTL8139State *s = (RTL8139State *) priv; + FILE *fp = NULL; + char eeprom_filename[1024] = { 0 }; + + snprintf(eeprom_filename, sizeof(eeprom_filename), "eeprom_rtl8139c_plus_%d.nvr", s->inst); + fp = nvr_fopen(eeprom_filename, "wb"); + if (fp) { + fwrite(s->eeprom.contents, 2, 64, fp); + fclose(fp); + fp = NULL; + } + free(priv); +} + +// clang-format off +static const device_config_t rtl8139c_config[] = { + { + .name = "mac", + .description = "MAC Address", + .type = CONFIG_MAC, + .default_string = "", + .default_int = -1 + }, + { .name = "", .description = "", .type = CONFIG_END } +}; +// clang-format on + +const device_t rtl8139c_plus_device = { + .name = "Realtek RTL8139C+", + .internal_name = "rtl8139c+", + .flags = DEVICE_PCI, + .local = 0, + .init = nic_init, + .close = nic_close, + .reset = rtl8139_reset, + { .available = NULL }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = rtl8139c_config +}; diff --git a/src/network/net_tulip.c b/src/network/net_tulip.c new file mode 100644 index 000000000..aaa60b4fc --- /dev/null +++ b/src/network/net_tulip.c @@ -0,0 +1,1529 @@ +/* + * 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. + * + * Emulation of DECchip "Tulip" 21143 NIC. + * + * Authors: Sven Schnelle, + * Cacodemon345, + * + * Copyright 2019-2023 Sven Schnelle. + * Copyright 2023 Cacodemon345. + */ +#include +#include +#include +#include +#include + +#include <86box/86box.h> +#include <86box/timer.h> +#include <86box/pci.h> +#include <86box/random.h> +#include <86box/io.h> +#include <86box/mem.h> +#include <86box/dma.h> +#include <86box/device.h> +#include <86box/thread.h> +#include <86box/network.h> +#include <86box/net_eeprom_nmc93cxx.h> +#include <86box/net_tulip.h> +#include <86box/bswap.h> +#include <86box/plat_unused.h> + +#define CSR(_x) ((_x) << 3) + +#define BIT(x) (1 << x) + +#define CSR0_SWR BIT(0) +#define CSR0_BAR BIT(1) +#define CSR0_DSL_SHIFT 2 +#define CSR0_DSL_MASK 0x1f +#define CSR0_BLE BIT(7) +#define CSR0_PBL_SHIFT 8 +#define CSR0_PBL_MASK 0x3f +#define CSR0_CAC_SHIFT 14 +#define CSR0_CAC_MASK 0x3 +#define CSR0_DAS 0x10000 +#define CSR0_TAP_SHIFT 17 +#define CSR0_TAP_MASK 0x7 +#define CSR0_DBO 0x100000 +#define CSR1_TPD 0x01 +#define CSR0_RLE BIT(23) +#define CSR0_WIE BIT(24) + +#define CSR2_RPD 0x01 + +#define CSR5_TI BIT(0) +#define CSR5_TPS BIT(1) +#define CSR5_TU BIT(2) +#define CSR5_TJT BIT(3) +#define CSR5_LNP_ANC BIT(4) +#define CSR5_UNF BIT(5) +#define CSR5_RI BIT(6) +#define CSR5_RU BIT(7) +#define CSR5_RPS BIT(8) +#define CSR5_RWT BIT(9) +#define CSR5_ETI BIT(10) +#define CSR5_GTE BIT(11) +#define CSR5_LNF BIT(12) +#define CSR5_FBE BIT(13) +#define CSR5_ERI BIT(14) +#define CSR5_AIS BIT(15) +#define CSR5_NIS BIT(16) +#define CSR5_RS_SHIFT 17 +#define CSR5_RS_MASK 7 +#define CSR5_TS_SHIFT 20 +#define CSR5_TS_MASK 7 + +#define CSR5_TS_STOPPED 0 +#define CSR5_TS_RUNNING_FETCH 1 +#define CSR5_TS_RUNNING_WAIT_EOT 2 +#define CSR5_TS_RUNNING_READ_BUF 3 +#define CSR5_TS_RUNNING_SETUP 5 +#define CSR5_TS_SUSPENDED 6 +#define CSR5_TS_RUNNING_CLOSE 7 + +#define CSR5_RS_STOPPED 0 +#define CSR5_RS_RUNNING_FETCH 1 +#define CSR5_RS_RUNNING_CHECK_EOR 2 +#define CSR5_RS_RUNNING_WAIT_RECEIVE 3 +#define CSR5_RS_SUSPENDED 4 +#define CSR5_RS_RUNNING_CLOSE 5 +#define CSR5_RS_RUNNING_FLUSH 6 +#define CSR5_RS_RUNNING_QUEUE 7 + +#define CSR5_EB_SHIFT 23 +#define CSR5_EB_MASK 7 + +#define CSR5_GPI BIT(26) +#define CSR5_LC BIT(27) + +#define CSR6_HP BIT(0) +#define CSR6_SR BIT(1) +#define CSR6_HO BIT(2) +#define CSR6_PB BIT(3) +#define CSR6_IF BIT(4) +#define CSR6_SB BIT(5) +#define CSR6_PR BIT(6) +#define CSR6_PM BIT(7) +#define CSR6_FKD BIT(8) +#define CSR6_FD BIT(9) + +#define CSR6_OM_SHIFT 10 +#define CSR6_OM_MASK 3 +#define CSR6_OM_NORMAL 0 +#define CSR6_OM_INT_LOOPBACK 1 +#define CSR6_OM_EXT_LOOPBACK 2 + +#define CSR6_FC BIT(12) +#define CSR6_ST BIT(13) + +#define CSR6_TR_SHIFT 14 +#define CSR6_TR_MASK 3 +#define CSR6_TR_72 0 +#define CSR6_TR_96 1 +#define CSR6_TR_128 2 +#define CSR6_TR_160 3 + +#define CSR6_CA BIT(17) +#define CSR6_RA BIT(30) +#define CSR6_SC BIT(31) + +#define CSR7_TIM BIT(0) +#define CSR7_TSM BIT(1) +#define CSR7_TUM BIT(2) +#define CSR7_TJM BIT(3) +#define CSR7_LPM BIT(4) +#define CSR7_UNM BIT(5) +#define CSR7_RIM BIT(6) +#define CSR7_RUM BIT(7) +#define CSR7_RSM BIT(8) +#define CSR7_RWM BIT(9) +#define CSR7_TMM BIT(11) +#define CSR7_LFM BIT(12) +#define CSR7_SEM BIT(13) +#define CSR7_ERM BIT(14) +#define CSR7_AIM BIT(15) +#define CSR7_NIM BIT(16) + +#define CSR8_MISSED_FRAME_OVL BIT(16) +#define CSR8_MISSED_FRAME_CNT_MASK 0xffff + +#define CSR9_DATA_MASK 0xff +#define CSR9_SR_CS BIT(0) +#define CSR9_SR_SK BIT(1) +#define CSR9_SR_DI BIT(2) +#define CSR9_SR_DO BIT(3) +#define CSR9_REG BIT(10) +#define CSR9_SR BIT(11) +#define CSR9_BR BIT(12) +#define CSR9_WR BIT(13) +#define CSR9_RD BIT(14) +#define CSR9_MOD BIT(15) +#define CSR9_MDC BIT(16) +#define CSR9_MDO BIT(17) +#define CSR9_MII BIT(18) +#define CSR9_MDI BIT(19) + +#define CSR11_CON BIT(16) +#define CSR11_TIMER_MASK 0xffff + +#define CSR12_MRA BIT(0) +#define CSR12_LS100 BIT(1) +#define CSR12_LS10 BIT(2) +#define CSR12_APS BIT(3) +#define CSR12_ARA BIT(8) +#define CSR12_TRA BIT(9) +#define CSR12_NSN BIT(10) +#define CSR12_TRF BIT(11) +#define CSR12_ANS_SHIFT 12 +#define CSR12_ANS_MASK 7 +#define CSR12_LPN BIT(15) +#define CSR12_LPC_SHIFT 16 +#define CSR12_LPC_MASK 0xffff + +#define CSR13_SRL BIT(0) +#define CSR13_CAC BIT(2) +#define CSR13_AUI BIT(3) +#define CSR13_SDM_SHIFT 4 +#define CSR13_SDM_MASK 0xfff + +#define CSR14_ECEN BIT(0) +#define CSR14_LBK BIT(1) +#define CSR14_DREN BIT(2) +#define CSR14_LSE BIT(3) +#define CSR14_CPEN_SHIFT 4 +#define CSR14_CPEN_MASK 3 +#define CSR14_MBO BIT(6) +#define CSR14_ANE BIT(7) +#define CSR14_RSQ BIT(8) +#define CSR14_CSQ BIT(9) +#define CSR14_CLD BIT(10) +#define CSR14_SQE BIT(11) +#define CSR14_LTE BIT(12) +#define CSR14_APE BIT(13) +#define CSR14_SPP BIT(14) +#define CSR14_TAS BIT(15) + +#define CSR15_JBD BIT(0) +#define CSR15_HUJ BIT(1) +#define CSR15_JCK BIT(2) +#define CSR15_ABM BIT(3) +#define CSR15_RWD BIT(4) +#define CSR15_RWR BIT(5) +#define CSR15_LE1 BIT(6) +#define CSR15_LV1 BIT(7) +#define CSR15_TSCK BIT(8) +#define CSR15_FUSQ BIT(9) +#define CSR15_FLF BIT(10) +#define CSR15_LSD BIT(11) +#define CSR15_DPST BIT(12) +#define CSR15_FRL BIT(13) +#define CSR15_LE2 BIT(14) +#define CSR15_LV2 BIT(15) + +#define RDES0_OF BIT(0) +#define RDES0_CE BIT(1) +#define RDES0_DB BIT(2) +#define RDES0_RJ BIT(4) +#define RDES0_FT BIT(5) +#define RDES0_CS BIT(6) +#define RDES0_TL BIT(7) +#define RDES0_LS BIT(8) +#define RDES0_FS BIT(9) +#define RDES0_MF BIT(10) +#define RDES0_RF BIT(11) +#define RDES0_DT_SHIFT 12 +#define RDES0_DT_MASK 3 +#define RDES0_DE BIT(14) +#define RDES0_ES BIT(15) +#define RDES0_FL_SHIFT 16 +#define RDES0_FL_MASK 0x3fff +#define RDES0_FF BIT(30) +#define RDES0_OWN BIT(31) + +#define RDES1_BUF1_SIZE_SHIFT 0 +#define RDES1_BUF1_SIZE_MASK 0x7ff + +#define RDES1_BUF2_SIZE_SHIFT 11 +#define RDES1_BUF2_SIZE_MASK 0x7ff +#define RDES1_RCH BIT(24) +#define RDES1_RER BIT(25) + +#define TDES0_DE BIT(0) +#define TDES0_UF BIT(1) +#define TDES0_LF BIT(2) +#define TDES0_CC_SHIFT 3 +#define TDES0_CC_MASK 0xf +#define TDES0_HF BIT(7) +#define TDES0_EC BIT(8) +#define TDES0_LC BIT(9) +#define TDES0_NC BIT(10) +#define TDES0_LO BIT(11) +#define TDES0_TO BIT(14) +#define TDES0_ES BIT(15) +#define TDES0_OWN BIT(31) + +#define TDES1_BUF1_SIZE_SHIFT 0 +#define TDES1_BUF1_SIZE_MASK 0x7ff + +#define TDES1_BUF2_SIZE_SHIFT 11 +#define TDES1_BUF2_SIZE_MASK 0x7ff + +#define TDES1_FT0 BIT(22) +#define TDES1_DPD BIT(23) +#define TDES1_TCH BIT(24) +#define TDES1_TER BIT(25) +#define TDES1_AC BIT(26) +#define TDES1_SET BIT(27) +#define TDES1_FT1 BIT(28) +#define TDES1_FS BIT(29) +#define TDES1_LS BIT(30) +#define TDES1_IC BIT(31) + +#define ETH_ALEN 6 + +struct tulip_descriptor { + uint32_t status; + uint32_t control; + uint32_t buf_addr1; + uint32_t buf_addr2; +}; + +struct TULIPState { + uint8_t pci_slot; + uint8_t irq_state; + const device_t *device_info; + uint16_t subsys_id; + uint16_t subsys_ven_id; + mem_mapping_t memory; + netcard_t *nic; + nmc93cxx_eeprom_t *eeprom; + uint32_t csr[16]; + uint8_t pci_conf[256]; + uint16_t mii_regs[32]; + + /* state for MII */ + uint32_t old_csr9; + uint32_t mii_word; + uint32_t mii_bitcnt; + + uint32_t current_rx_desc; + uint32_t current_tx_desc; + + uint8_t rx_frame[2048]; + uint8_t tx_frame[2048]; + uint16_t tx_frame_len; + uint16_t rx_frame_len; + uint16_t rx_frame_size; + + uint32_t rx_status; + uint8_t filter[16][6]; +}; + +typedef struct TULIPState TULIPState; + +static void +tulip_desc_read(TULIPState *s, uint32_t p, + struct tulip_descriptor *desc) +{ + desc->status = mem_readl_phys(p); + desc->control = mem_readl_phys(p + 4); + desc->buf_addr1 = mem_readl_phys(p + 8); + desc->buf_addr2 = mem_readl_phys(p + 12); + + if (s->csr[0] & CSR0_DBO) { + bswap32s(&desc->status); + bswap32s(&desc->control); + bswap32s(&desc->buf_addr1); + bswap32s(&desc->buf_addr2); + } +} + +static void +tulip_desc_write(TULIPState *s, uint32_t p, + struct tulip_descriptor *desc) +{ + if (s->csr[0] & CSR0_DBO) { + mem_writel_phys(p, bswap32(desc->status)); + mem_writel_phys(p + 4, bswap32(desc->control)); + mem_writel_phys(p + 8, bswap32(desc->buf_addr1)); + mem_writel_phys(p + 12, bswap32(desc->buf_addr2)); + } else { + mem_writel_phys(p, desc->status); + mem_writel_phys(p + 4, desc->control); + mem_writel_phys(p + 8, desc->buf_addr1); + mem_writel_phys(p + 12, desc->buf_addr2); + } +} + +static void +tulip_update_int(TULIPState *s) +{ + uint32_t ie = s->csr[5] & s->csr[7]; + bool assert = false; + + s->csr[5] &= ~(CSR5_AIS | CSR5_NIS); + + if (ie & (CSR5_TI | CSR5_TU | CSR5_RI | CSR5_GTE | CSR5_ERI)) { + s->csr[5] |= CSR5_NIS; + } + + if (ie & (CSR5_LC | CSR5_GPI | CSR5_FBE | CSR5_LNF | CSR5_ETI | CSR5_RWT | CSR5_RPS | CSR5_RU | CSR5_UNF | CSR5_LNP_ANC | CSR5_TJT | CSR5_TPS)) { + s->csr[5] |= CSR5_AIS; + } + + assert = s->csr[5] & s->csr[7] & (CSR5_AIS | CSR5_NIS); + if (!assert) + pci_clear_irq(s->pci_slot, PCI_INTA, &s->irq_state); + else + pci_set_irq(s->pci_slot, PCI_INTA, &s->irq_state); +} + +static bool +tulip_rx_stopped(TULIPState *s) +{ + return ((s->csr[5] >> CSR5_RS_SHIFT) & CSR5_RS_MASK) == CSR5_RS_STOPPED; +} + +static void +tulip_next_rx_descriptor(TULIPState *s, + struct tulip_descriptor *desc) +{ + if (desc->control & RDES1_RER) { + s->current_rx_desc = s->csr[3]; + } else if (desc->control & RDES1_RCH) { + s->current_rx_desc = desc->buf_addr2; + } else { + s->current_rx_desc += sizeof(struct tulip_descriptor) + (((s->csr[0] >> CSR0_DSL_SHIFT) & CSR0_DSL_MASK) << 2); + } + s->current_rx_desc &= ~3ULL; +} + +static void +tulip_copy_rx_bytes(TULIPState *s, struct tulip_descriptor *desc) +{ + int len1 = (desc->control >> RDES1_BUF1_SIZE_SHIFT) & RDES1_BUF1_SIZE_MASK; + int len2 = (desc->control >> RDES1_BUF2_SIZE_SHIFT) & RDES1_BUF2_SIZE_MASK; + int len; + + if (s->rx_frame_len && len1) { + if (s->rx_frame_len > len1) { + len = len1; + } else { + len = s->rx_frame_len; + } + + dma_bm_write(desc->buf_addr1, s->rx_frame + (s->rx_frame_size - s->rx_frame_len), len, 1); + s->rx_frame_len -= len; + } + + if (s->rx_frame_len && len2) { + if (s->rx_frame_len > len2) { + len = len2; + } else { + len = s->rx_frame_len; + } + + dma_bm_write(desc->buf_addr2, s->rx_frame + (s->rx_frame_size - s->rx_frame_len), len, 1); + s->rx_frame_len -= len; + } +} + +static bool +tulip_filter_address(TULIPState *s, const uint8_t *addr) +{ + static const char broadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + bool ret = false; + + for (uint8_t i = 0; i < 16 && ret == false; i++) { + if (!memcmp(&s->filter[i], addr, ETH_ALEN)) { + ret = true; + } + } + + if (!memcmp(addr, broadcast, ETH_ALEN)) { + return true; + } + + if (s->csr[6] & (CSR6_PR | CSR6_RA)) { + /* Promiscuous mode enabled */ + s->rx_status |= RDES0_FF; + return true; + } + + if ((s->csr[6] & CSR6_PM) && (addr[0] & 1)) { + /* Pass all Multicast enabled */ + s->rx_status |= RDES0_MF; + return true; + } + + if (s->csr[6] & CSR6_IF) { + ret ^= true; + } + return ret; +} + +static int +tulip_receive(void *priv, uint8_t *buf, int size) +{ + struct tulip_descriptor desc; + TULIPState *s = (TULIPState *) priv; + + if (size < 14 || size > sizeof(s->rx_frame) - 4 + || s->rx_frame_len || tulip_rx_stopped(s)) { + return 0; + } + + if (!tulip_filter_address(s, buf)) { + return 1; + } + + do { + tulip_desc_read(s, s->current_rx_desc, &desc); + + if (!(desc.status & RDES0_OWN)) { + s->csr[5] |= CSR5_RU; + tulip_update_int(s); + return s->rx_frame_size - s->rx_frame_len; + } + desc.status = 0; + + if (!s->rx_frame_len) { + s->rx_frame_size = size + 4; + s->rx_status = RDES0_LS | ((s->rx_frame_size & RDES0_FL_MASK) << RDES0_FL_SHIFT); + desc.status |= RDES0_FS; + memcpy(s->rx_frame, buf, size); + s->rx_frame_len = s->rx_frame_size; + } + + tulip_copy_rx_bytes(s, &desc); + + if (!s->rx_frame_len) { + desc.status |= s->rx_status; + s->csr[5] |= CSR5_RI; + tulip_update_int(s); + } + tulip_desc_write(s, s->current_rx_desc, &desc); + tulip_next_rx_descriptor(s, &desc); + } while (s->rx_frame_len); + return 1; +} + +static void +tulip_update_rs(TULIPState *s, int state) +{ + s->csr[5] &= ~(CSR5_RS_MASK << CSR5_RS_SHIFT); + s->csr[5] |= (state & CSR5_RS_MASK) << CSR5_RS_SHIFT; +} + +static const uint16_t tulip_mdi_default[] = { + /* MDI Registers 0 - 6, 7 */ + 0x3100, + 0xf02c, + 0x7810, + 0x0000, + 0x0501, + 0x4181, + 0x0000, + 0x0000, + /* MDI Registers 8 - 15 */ + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x3800, + 0x0000, + /* MDI Registers 16 - 31 */ + 0x0003, + 0x0600, + 0x0001, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, +}; + +/* Readonly mask for MDI (PHY) registers */ +extern uint16_t l80225_mii_readw(uint16_t* regs, uint16_t addr); +extern void l80225_mii_writew(uint16_t* regs, uint16_t addr, uint16_t val); + +static uint16_t +tulip_mii_read(TULIPState *s, int phy, int reg) +{ + uint16_t ret = 0; + if (phy == 1) { + ret = l80225_mii_readw(s->mii_regs, reg); + } + return ret; +} + +static void +tulip_mii_write(TULIPState *s, int phy, int reg, uint16_t data) +{ + if (phy != 1) { + return; + } + + return l80225_mii_writew(s->mii_regs, reg, data); +} + +static void +tulip_mii(TULIPState *s) +{ + uint32_t changed = s->old_csr9 ^ s->csr[9]; + uint16_t data; + int op; + int phy; + int reg; + + if (!(changed & CSR9_MDC)) { + return; + } + + if (!(s->csr[9] & CSR9_MDC)) { + return; + } + + s->mii_bitcnt++; + s->mii_word <<= 1; + + if (s->csr[9] & CSR9_MDO && (s->mii_bitcnt < 16 || !(s->csr[9] & CSR9_MII))) { + /* write op or address bits */ + s->mii_word |= 1; + } + + if (s->mii_bitcnt >= 16 && (s->csr[9] & CSR9_MII)) { + if (s->mii_word & 0x8000) { + s->csr[9] |= CSR9_MDI; + } else { + s->csr[9] &= ~CSR9_MDI; + } + } + + if (s->mii_word == 0xffffffff) { + s->mii_bitcnt = 0; + } else if (s->mii_bitcnt == 16) { + op = (s->mii_word >> 12) & 0x0f; + phy = (s->mii_word >> 7) & 0x1f; + reg = (s->mii_word >> 2) & 0x1f; + + if (op == 6) { + s->mii_word = tulip_mii_read(s, phy, reg); + } + } else if (s->mii_bitcnt == 32) { + op = (s->mii_word >> 28) & 0x0f; + phy = (s->mii_word >> 23) & 0x1f; + reg = (s->mii_word >> 18) & 0x1f; + data = s->mii_word & 0xffff; + + if (op == 5) { + tulip_mii_write(s, phy, reg, data); + } + } +} + +static uint32_t +tulip_csr9_read(TULIPState *s) +{ + if (s->csr[9] & CSR9_SR) { + if (nmc93cxx_eeprom_read(s->eeprom)) { + s->csr[9] |= CSR9_SR_DO; + } else { + s->csr[9] &= ~CSR9_SR_DO; + } + } + + tulip_mii(s); + return s->csr[9]; +} + +static void +tulip_update_ts(TULIPState *s, int state) +{ + s->csr[5] &= ~(CSR5_TS_MASK << CSR5_TS_SHIFT); + s->csr[5] |= (state & CSR5_TS_MASK) << CSR5_TS_SHIFT; +} + +static uint32_t +tulip_read(uint32_t addr, void *opaque) +{ + TULIPState *s = opaque; + uint32_t data = 0; + addr &= 127; + + switch (addr) { + case CSR(9): + data = tulip_csr9_read(s); + break; + + case CSR(12): + /* Fake autocompletion complete until we have PHY emulation */ + data = 5 << CSR12_ANS_SHIFT; + break; + + default: + if (!(addr & 7)) + data = s->csr[addr >> 3]; + break; + } + return data; +} + +static void +tulip_tx(TULIPState *s, struct tulip_descriptor *desc) +{ + if (s->tx_frame_len) { + if ((s->csr[6] >> CSR6_OM_SHIFT) & CSR6_OM_MASK) { + /* Internal or external Loopback */ + tulip_receive(s, s->tx_frame, s->tx_frame_len); + } else if (s->tx_frame_len <= sizeof(s->tx_frame)) { + network_tx(s->nic, s->tx_frame, s->tx_frame_len); + } + } + + if (desc->control & TDES1_IC) { + s->csr[5] |= CSR5_TI; + tulip_update_int(s); + } +} + +static int +tulip_copy_tx_buffers(TULIPState *s, struct tulip_descriptor *desc) +{ + int len1 = (desc->control >> TDES1_BUF1_SIZE_SHIFT) & TDES1_BUF1_SIZE_MASK; + int len2 = (desc->control >> TDES1_BUF2_SIZE_SHIFT) & TDES1_BUF2_SIZE_MASK; + + if (s->tx_frame_len + len1 > sizeof(s->tx_frame)) { + return -1; + } + if (len1) { + dma_bm_read(desc->buf_addr1, + s->tx_frame + s->tx_frame_len, len1, 1); + s->tx_frame_len += len1; + } + + if (s->tx_frame_len + len2 > sizeof(s->tx_frame)) { + return -1; + } + if (len2) { + dma_bm_read(desc->buf_addr2, + s->tx_frame + s->tx_frame_len, len2, 1); + s->tx_frame_len += len2; + } + desc->status = (len1 + len2) ? 0 : 0x7fffffff; + + return 0; +} + +static void +tulip_setup_filter_addr(TULIPState *s, uint8_t *buf, int n) +{ + int offset = n * 12; + + s->filter[n][0] = buf[offset]; + s->filter[n][1] = buf[offset + 1]; + + s->filter[n][2] = buf[offset + 4]; + s->filter[n][3] = buf[offset + 5]; + + s->filter[n][4] = buf[offset + 8]; + s->filter[n][5] = buf[offset + 9]; +} + +static void +tulip_setup_frame(TULIPState *s, + struct tulip_descriptor *desc) +{ + uint8_t buf[4096]; + int len = (desc->control >> TDES1_BUF1_SIZE_SHIFT) & TDES1_BUF1_SIZE_MASK; + + if (len == 192) { + dma_bm_read(desc->buf_addr1, buf, len, 1); + for (uint8_t i = 0; i < 16; i++) { + tulip_setup_filter_addr(s, buf, i); + } + } + + desc->status = 0x7fffffff; + + if (desc->control & TDES1_IC) { + s->csr[5] |= CSR5_TI; + tulip_update_int(s); + } +} + +static void +tulip_next_tx_descriptor(TULIPState *s, + struct tulip_descriptor *desc) +{ + if (desc->control & TDES1_TER) { + s->current_tx_desc = s->csr[4]; + } else if (desc->control & TDES1_TCH) { + s->current_tx_desc = desc->buf_addr2; + } else { + s->current_tx_desc += sizeof(struct tulip_descriptor) + (((s->csr[0] >> CSR0_DSL_SHIFT) & CSR0_DSL_MASK) << 2); + } + s->current_tx_desc &= ~3ULL; +} + +static uint32_t +tulip_ts(TULIPState *s) +{ + return (s->csr[5] >> CSR5_TS_SHIFT) & CSR5_TS_MASK; +} + +static void +tulip_xmit_list_update(TULIPState *s) +{ +#define TULIP_DESC_MAX 128 + struct tulip_descriptor desc; + + if (tulip_ts(s) != CSR5_TS_SUSPENDED) { + return; + } + + for (uint8_t i = 0; i < TULIP_DESC_MAX; i++) { + tulip_desc_read(s, s->current_tx_desc, &desc); + + if (!(desc.status & TDES0_OWN)) { + tulip_update_ts(s, CSR5_TS_SUSPENDED); + s->csr[5] |= CSR5_TU; + tulip_update_int(s); + return; + } + + if (desc.control & TDES1_SET) { + tulip_setup_frame(s, &desc); + } else { + if (desc.control & TDES1_FS) { + s->tx_frame_len = 0; + } + + if (!tulip_copy_tx_buffers(s, &desc)) { + if (desc.control & TDES1_LS) { + tulip_tx(s, &desc); + } + } + } + tulip_desc_write(s, s->current_tx_desc, &desc); + tulip_next_tx_descriptor(s, &desc); + } +} + +static void +tulip_csr9_write(TULIPState *s, UNUSED(uint32_t old_val), + uint32_t new_val) +{ + if (new_val & CSR9_SR) { + nmc93cxx_eeprom_write(s->eeprom, + !!(new_val & CSR9_SR_CS), + !!(new_val & CSR9_SR_SK), + !!(new_val & CSR9_SR_DI)); + } +} + +static void +tulip_reset(void *priv) +{ + TULIPState *s = (TULIPState *) priv; + const uint16_t *eeprom_data = nmc93cxx_eeprom_data(s->eeprom); + s->csr[0] = 0xfe000000; + s->csr[1] = 0xffffffff; + s->csr[2] = 0xffffffff; + s->csr[5] = 0xf0000000; + s->csr[6] = 0x32000040; + s->csr[7] = 0xf3fe0000; + s->csr[8] = 0xe0000000; + s->csr[9] = 0xfff483ff; + s->csr[11] = 0xfffe0000; + s->csr[12] = 0x000000c6; + s->csr[13] = 0xffff0000; + s->csr[14] = 0xffffffff; + s->csr[15] = 0x8ff00000; + s->subsys_id = eeprom_data[1]; + s->subsys_ven_id = eeprom_data[0]; +} + +static void +tulip_write(uint32_t addr, uint32_t data, void *opaque) +{ + TULIPState *s = opaque; + addr &= 127; + + switch (addr) { + case CSR(0): + s->csr[0] = data; + if (data & CSR0_SWR) { + tulip_reset(s); + tulip_update_int(s); + } + break; + + case CSR(1): + tulip_xmit_list_update(s); + break; + + case CSR(2): + break; + + case CSR(3): + s->csr[3] = data & ~3ULL; + s->current_rx_desc = s->csr[3]; + break; + + case CSR(4): + s->csr[4] = data & ~3ULL; + s->current_tx_desc = s->csr[4]; + tulip_xmit_list_update(s); + break; + + case CSR(5): + /* Status register, write clears bit */ + s->csr[5] &= ~(data & (CSR5_TI | CSR5_TPS | CSR5_TU | CSR5_TJT | CSR5_LNP_ANC | CSR5_UNF | CSR5_RI | CSR5_RU | CSR5_RPS | CSR5_RWT | CSR5_ETI | CSR5_GTE | CSR5_LNF | CSR5_FBE | CSR5_ERI | CSR5_AIS | CSR5_NIS | CSR5_GPI | CSR5_LC)); + tulip_update_int(s); + break; + + case CSR(6): + s->csr[6] = data; + if (s->csr[6] & CSR6_SR) { + tulip_update_rs(s, CSR5_RS_RUNNING_WAIT_RECEIVE); + } else { + tulip_update_rs(s, CSR5_RS_STOPPED); + } + + if (s->csr[6] & CSR6_ST) { + tulip_update_ts(s, CSR5_TS_SUSPENDED); + tulip_xmit_list_update(s); + } else { + tulip_update_ts(s, CSR5_TS_STOPPED); + s->csr[5] |= CSR5_TPS; + } + break; + + case CSR(7): + s->csr[7] = data; + tulip_update_int(s); + break; + + case CSR(8): + s->csr[9] = data; + break; + + case CSR(9): + tulip_csr9_write(s, s->csr[9], data); + /* don't clear MII read data */ + s->csr[9] &= CSR9_MDI; + s->csr[9] |= (data & ~CSR9_MDI); + tulip_mii(s); + s->old_csr9 = s->csr[9]; + break; + + case CSR(10): + s->csr[10] = data; + break; + + case CSR(11): + s->csr[11] = data; + break; + + case CSR(12): + /* SIA Status register, some bits are cleared by writing 1 */ + s->csr[12] &= ~(data & (CSR12_MRA | CSR12_TRA | CSR12_ARA)); + break; + + case CSR(13): + s->csr[13] = data; + break; + + case CSR(14): + s->csr[14] = data; + break; + + case CSR(15): + s->csr[15] = data; + break; + + default: + pclog("%s: write to CSR at unknown address " + "0x%u\n", + __func__, addr); + break; + } +} + +static void +tulip_write_io(uint16_t addr, uint32_t data, void *opaque) +{ + return tulip_write(addr, data, opaque); +} + +static uint32_t +tulip_read_io(uint16_t addr, void *opaque) +{ + return tulip_read(addr, opaque); +} + +static void +tulip_idblock_crc(uint16_t *srom) +{ + unsigned char bitval; + unsigned char crc; + const int len = 9; + crc = -1; + + for (int word = 0; word < len; word++) { + for (int8_t bit = 15; bit >= 0; bit--) { + if ((word == (len - 1)) && (bit == 7)) { + /* + * Insert the correct CRC result into input data stream + * in place. + */ + srom[len - 1] = (srom[len - 1] & 0xff00) | (unsigned short) crc; + break; + } + bitval = ((srom[word] >> bit) & 1) ^ ((crc >> 7) & 1); + crc = crc << 1; + if (bitval == 1) { + crc ^= 6; + crc |= 0x01; + } + } + } +} + +static uint16_t +tulip_srom_crc(uint8_t *eeprom, size_t len) +{ + unsigned long crc = 0xffffffff; + unsigned long flippedcrc = 0; + unsigned char currentbyte; + unsigned int msb; + unsigned int bit; + + for (size_t i = 0; i < len; i++) { + currentbyte = eeprom[i]; + for (bit = 0; bit < 8; bit++) { + msb = (crc >> 31) & 1; + crc <<= 1; + if (msb ^ (currentbyte & 1)) { + crc ^= 0x04c11db6; + crc |= 0x00000001; + } + currentbyte >>= 1; + } + } + + for (uint8_t i = 0; i < 32; i++) { + flippedcrc <<= 1; + bit = crc & 1; + crc >>= 1; + flippedcrc += bit; + } + return (flippedcrc ^ 0xffffffff) & 0xffff; +} + +static const uint8_t eeprom_default[128] = { + 0xf0, + 0x11, + 0x35, + 0x42, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x56, + 0x08, + 0x04, + 0x01, +#ifdef USE_DEC_OID + 0x00, + 0x80, + 0x48, +#else + 0x00, + 0x00, + 0xcb, +#endif + 0xb3, + 0x0e, + 0xa7, + 0x00, + 0x1e, + 0x00, + 0x00, + 0x00, + 0x08, + 0x01, + 0x8d, + 0x03, + 0x00, + 0x00, + 0x00, + 0x00, + 0x78, + 0xe0, + 0x01, + 0x00, + 0x50, + 0x00, + 0x18, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0xe8, + 0x6b, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x80, + 0x48, + 0xb3, + 0x0e, + 0xa7, + 0x40, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; + +/* MAC address at 14h, card-specific at 17h. */ +static const uint8_t eeprom_default_24110[128] = { + 0x46, + 0x26, + 0x00, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x56, + 0x08, + 0x04, + 0x01, + 0x00, /* Obtained from a Linux dump from the real Kingston KNE110TX: 00:C0:F0:16:2A:CB */ + 0xc0, + 0xf0, + 0x16, + 0x2a, + 0xcb, + 0x00, + 0x1e, + 0x00, + 0x00, + 0x00, + 0x08, + 0xff, + 0x01, + 0x8d, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x78, + 0xe0, + 0x01, + 0x00, + 0x50, + 0x00, + 0x18, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0xe8, + 0x6b, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x80, + 0x48, + 0xb3, + 0x0e, + 0xa7, + 0x40, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; + +static uint8_t +tulip_pci_read(UNUSED(int func), int addr, void *priv) +{ + const TULIPState *s = (TULIPState *) priv; + + switch (addr) { + default: + return s->pci_conf[addr & 0xFF]; + case 0x00: + return 0x11; + case 0x01: + return 0x10; + case 0x02: + return s->device_info->local ? 0x09 : 0x19; + case 0x03: + return 0x00; + case 0x07: + return 0x02; + case 0x06: + return 0x80; + case 0x5: + return s->pci_conf[addr & 0xFF] & 1; + case 0x8: + return 0x30; + case 0x9: + return 0x0; + case 0xA: + return 0x0; + case 0xB: + return 0x2; + case 0x10: + return (s->pci_conf[addr & 0xFF] & 0x80) | 1; + case 0x14: + return s->pci_conf[addr & 0xFF] & 0x80; + case 0x2C: + return s->subsys_ven_id & 0xFF; + case 0x2D: + return s->subsys_ven_id >> 8; + case 0x2E: + return s->subsys_id & 0xFF; + case 0x2F: + return s->subsys_id >> 8; + case 0x3D: + return PCI_INTA; + } +} + +static void +tulip_pci_write(UNUSED(int func), int addr, uint8_t val, void *priv) +{ + TULIPState *s = (TULIPState *) priv; + + switch (addr) { + case 0x4: + mem_mapping_disable(&s->memory); + io_removehandler((s->pci_conf[0x10] & 0x80) | (s->pci_conf[0x11] << 8), 128, + NULL, NULL, tulip_read_io, + NULL, NULL, tulip_write_io, + priv); + s->pci_conf[addr & 0xFF] = val; + if (val & PCI_COMMAND_IO) + io_sethandler((s->pci_conf[0x10] & 0x80) | (s->pci_conf[0x11] << 8), 128, + NULL, NULL, tulip_read_io, + NULL, NULL, tulip_write_io, + priv); + if ((val & PCI_COMMAND_MEM) && s->memory.size) + mem_mapping_enable(&s->memory); + break; + case 0x5: + s->pci_conf[addr & 0xFF] = val & 1; + break; + case 0x10: + case 0x11: + io_removehandler((s->pci_conf[0x10] & 0x80) | (s->pci_conf[0x11] << 8), 128, + NULL, NULL, tulip_read_io, + NULL, NULL, tulip_write_io, + priv); + s->pci_conf[addr & 0xFF] = val; + if (s->pci_conf[0x4] & PCI_COMMAND_IO) + io_sethandler((s->pci_conf[0x10] & 0x80) | (s->pci_conf[0x11] << 8), 128, + NULL, NULL, tulip_read_io, + NULL, NULL, tulip_write_io, + priv); + break; + case 0x14: + case 0x15: + case 0x16: + case 0x17: + s->pci_conf[addr & 0xFF] = val; + if (s->pci_conf[0x4] & PCI_COMMAND_MEM) + mem_mapping_set_addr(&s->memory, (s->pci_conf[0x14] & 0x80) | (s->pci_conf[0x15] << 8) | (s->pci_conf[0x16] << 16) | (s->pci_conf[0x17] << 24), 128); + break; + case 0x3C: + s->pci_conf[addr & 0xFF] = val; + break; + } +} + +static void * +nic_init(const device_t *info) +{ + uint8_t eeprom_default_local[128]; + nmc93cxx_eeprom_params_t params; + TULIPState *s = calloc(1, sizeof(TULIPState)); + char filename[1024] = { 0 }; + uint32_t mac; + + if (!s) + return NULL; + + s->device_info = info; + memcpy(eeprom_default_local, info->local ? eeprom_default_24110 : eeprom_default, + sizeof(eeprom_default)); + if (info->local == 2) { + /* Microsoft VPC DEC Tulip. */ + eeprom_default_local[0x14] = 0x00; + eeprom_default_local[0x15] = 0x03; + eeprom_default_local[0x16] = 0x0f; + } + + /* See if we have a local MAC address configured. */ + mac = device_get_config_mac("mac", -1); + + /* Set up our BIA. */ + if (mac & 0xff000000) { + /* Generate new local MAC. */ + eeprom_default_local[0x17] = random_generate(); + eeprom_default_local[0x18] = random_generate(); + eeprom_default_local[0x19] = random_generate(); + mac = (((int) eeprom_default_local[0x17]) << 16); + mac |= (((int) eeprom_default_local[0x18]) << 8); + mac |= ((int) eeprom_default_local[0x19]); + device_set_config_mac("mac", mac); + } else { + eeprom_default_local[0x17] = (mac >> 16) & 0xff; + eeprom_default_local[0x18] = (mac >> 8) & 0xff; + eeprom_default_local[0x19] = (mac & 0xff); + } + + tulip_idblock_crc((uint16_t *) eeprom_default_local); + ((uint16_t *) eeprom_default_local)[63] = (tulip_srom_crc((uint8_t *) eeprom_default_local, 126)); + + params.nwords = 64; + params.default_content = (uint16_t *) eeprom_default_local; + params.filename = filename; + snprintf(filename, sizeof(filename), "nmc93cxx_eeprom_%s_%d.nvr", info->internal_name, device_get_instance()); + s->eeprom = device_add_parameters(&nmc93cxx_device, ¶ms); + if (!s->eeprom) { + free(s); + return NULL; + } + memcpy(s->mii_regs, tulip_mdi_default, sizeof(tulip_mdi_default)); + s->nic = network_attach(s, (uint8_t *) &nmc93cxx_eeprom_data(s->eeprom)[10], tulip_receive, NULL); + pci_add_card(PCI_ADD_NORMAL, tulip_pci_read, tulip_pci_write, s, &s->pci_slot); + mem_mapping_add(&s->memory, 0, 0, NULL, NULL, tulip_read, NULL, NULL, tulip_write, NULL, MEM_MAPPING_EXTERNAL, s); + tulip_reset(s); + return s; +} + +static void +nic_close(void *priv) +{ + free(priv); +} + +// clang-format off +static const device_config_t dec_tulip_config[] = { + { + .name = "mac", + .description = "MAC Address", + .type = CONFIG_MAC, + .default_string = "", + .default_int = -1 + }, + { .name = "", .description = "", .type = CONFIG_END } +}; +// clang-format on + +const device_t dec_tulip_device = { + .name = "Compu-Shack FASTLine-II UTP 10/100 (DECchip 21143 \"Tulip\")", + .internal_name = "dec_21143_tulip", + .flags = DEVICE_PCI, + .local = 0, + .init = nic_init, + .close = nic_close, + .reset = tulip_reset, + { .available = NULL }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = dec_tulip_config +}; + +const device_t dec_tulip_21140_device = { + .name = "Kingston KNE100TX (DECchip 21140 \"Tulip FasterNet\")", + .internal_name = "dec_21140_tulip", + .flags = DEVICE_PCI, + .local = 1, + .init = nic_init, + .close = nic_close, + .reset = tulip_reset, + { .available = NULL }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = dec_tulip_config +}; + +const device_t dec_tulip_21140_vpc_device = { + .name = "Microsoft Virtual PC Network (DECchip 21140 \"Tulip FasterNet\")", + .internal_name = "dec_21140_tulip_vpc", + .flags = DEVICE_PCI, + .local = 2, + .init = nic_init, + .close = nic_close, + .reset = tulip_reset, + { .available = NULL }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = dec_tulip_config +}; diff --git a/src/network/network.c b/src/network/network.c index 47f009fba..38af502b2 100644 --- a/src/network/network.c +++ b/src/network/network.c @@ -73,6 +73,8 @@ #include <86box/net_pcnet.h> #include <86box/net_plip.h> #include <86box/net_wd8003.h> +#include <86box/net_tulip.h> +#include <86box/net_rtl8139.h> #ifdef _WIN32 # define WIN32_LEAN_AND_MEAN @@ -129,7 +131,11 @@ static const device_t *net_cards[] = { &wd8013epa_device, &pcnet_am79c973_device, &pcnet_am79c970a_device, + &dec_tulip_device, &rtl8029as_device, + &rtl8139c_plus_device, + &dec_tulip_21140_device, + &dec_tulip_21140_vpc_device, &pcnet_am79c960_vlb_device, NULL }; diff --git a/src/pit_fast.c b/src/pit_fast.c index 20633de80..1168cb3c5 100644 --- a/src/pit_fast.c +++ b/src/pit_fast.c @@ -404,6 +404,8 @@ pitf_write(uint16_t addr, uint8_t val, void *priv) pit_log("[%04X:%08X] pit_write(%04X, %02X, %08X)\n", CS, cpu_state.pc, addr, val, priv); + cycles -= ISA_CYCLES(8); + switch (addr & 3) { case 3: /* control */ t = val >> 6; @@ -541,6 +543,8 @@ pitf_read(uint16_t addr, void *priv) int t = (addr & 3); ctrf_t *ctr; + cycles -= ISA_CYCLES(8); + switch (addr & 3) { case 3: /* Control. */ /* This is 8254-only, 8253 returns 0x00. */ diff --git a/src/port_6x.c b/src/port_6x.c index 340d6df2b..971b92d28 100644 --- a/src/port_6x.c +++ b/src/port_6x.c @@ -37,6 +37,7 @@ #include <86box/video.h> #include <86box/port_6x.h> #include <86box/plat_unused.h> +#include <86box/random.h> #define PS2_REFRESH_TIME (16 * TIMER_USEC) @@ -45,6 +46,8 @@ #define PORT_6X_MIRROR 4 #define PORT_6X_SWA 8 +static int cycles_sub = 0; + static void port_6x_write(uint16_t port, uint8_t val, void *priv) { @@ -52,6 +55,8 @@ port_6x_write(uint16_t port, uint8_t val, void *priv) port &= 3; + cycles -= cycles_sub; + if ((port == 3) && (dev->flags & PORT_6X_MIRROR)) port = 1; @@ -80,6 +85,8 @@ port_61_read_simple(UNUSED(uint16_t port), UNUSED(void *priv)) { uint8_t ret = ppi.pb & 0x1f; + cycles -= cycles_sub; + if (ppispeakon) ret |= 0x20; @@ -92,6 +99,8 @@ port_61_read(UNUSED(uint16_t port), void *priv) const port_6x_t *dev = (port_6x_t *) priv; uint8_t ret = 0xff; + cycles -= cycles_sub; + if (dev->flags & PORT_6X_EXT_REF) { ret = ppi.pb & 0x0f; @@ -190,6 +199,8 @@ port_6x_init(const device_t *info) if (dev->flags & PORT_6X_SWA) io_sethandler(0x0062, 1, port_62_read, NULL, NULL, NULL, NULL, NULL, dev); + cycles_sub = is486 ? ISA_CYCLES(8) : 0; + return dev; } diff --git a/src/qt/languages/zh-CN.po b/src/qt/languages/zh-CN.po index fe099265b..3d045b74e 100644 --- a/src/qt/languages/zh-CN.po +++ b/src/qt/languages/zh-CN.po @@ -653,7 +653,7 @@ msgid "ZIP images" msgstr "ZIP 映像" msgid "86Box could not find any usable ROM images.\n\nPlease download a ROM set and extract it into the \"roms\" directory." -msgstr "86Box 找不到任何可用的 ROM 映像。\n\n请下载ROM 包并将其解压到 \"roms\" 文件夹中。" +msgstr "86Box 找不到任何可用的 ROM 映像。\n\n请下载 ROM 包并将其解压到 \"roms\" 文件夹中。" msgid "(empty)" msgstr "(空)" diff --git a/src/qt/qt_hardwarerenderer.cpp b/src/qt/qt_hardwarerenderer.cpp index ee2ec07df..47f0de718 100644 --- a/src/qt/qt_hardwarerenderer.cpp +++ b/src/qt/qt_hardwarerenderer.cpp @@ -136,6 +136,22 @@ HardwareRenderer::initializeGL() m_context->swapBuffers(this); } +void +HardwareRenderer::paintOverGL() +{ + /* Context switching is needed to make use of QPainter to draw status bar icons in fullscreen. + Especially since it seems to be impossible to use QPainter on externally-created OpenGL contexts. */ + if (video_fullscreen && status_icons_fullscreen) { + m_context->makeCurrent(nullptr); + makeCurrent(); + QPainter painter(this); + drawStatusBarIcons(&painter); + painter.end(); + doneCurrent(); + m_context->makeCurrent(this); + } +} + void HardwareRenderer::paintGL() { diff --git a/src/qt/qt_hardwarerenderer.hpp b/src/qt/qt_hardwarerenderer.hpp index da23c4b05..1918cda18 100644 --- a/src/qt/qt_hardwarerenderer.hpp +++ b/src/qt/qt_hardwarerenderer.hpp @@ -53,6 +53,7 @@ public: { onResize(size().width(), size().height()); } + void paintOverGL() override; std::vector> getBuffers() override; HardwareRenderer(QWidget *parent = nullptr, RenderType rtype = RenderType::OpenGL) : QOpenGLWindow(QOpenGLWindow::NoPartialUpdate, parent->windowHandle()) diff --git a/src/qt/qt_machinestatus.cpp b/src/qt/qt_machinestatus.cpp index bb3002df2..d7b115a64 100644 --- a/src/qt/qt_machinestatus.cpp +++ b/src/qt/qt_machinestatus.cpp @@ -429,11 +429,12 @@ MachineStatus::refresh(QStatusBar *sbar) bool has_xta = machine_has_flags(machine, MACHINE_XTA) > 0; bool has_esdi = machine_has_flags(machine, MACHINE_ESDI) > 0; - int c_mfm = hdd_count(HDD_BUS_MFM); - int c_esdi = hdd_count(HDD_BUS_ESDI); - int c_xta = hdd_count(HDD_BUS_XTA); - int c_ide = hdd_count(HDD_BUS_IDE); - int c_scsi = hdd_count(HDD_BUS_SCSI); + int c_mfm = hdd_count(HDD_BUS_MFM); + int c_esdi = hdd_count(HDD_BUS_ESDI); + int c_xta = hdd_count(HDD_BUS_XTA); + int c_ide = hdd_count(HDD_BUS_IDE); + int c_atapi = hdd_count(HDD_BUS_ATAPI); + int c_scsi = hdd_count(HDD_BUS_SCSI); sbar->removeWidget(d->cassette.label.get()); for (int i = 0; i < 2; ++i) { @@ -597,12 +598,21 @@ MachineStatus::refresh(QStatusBar *sbar) d->hdds[HDD_BUS_XTA].label->setToolTip(tr("Hard disk (%s)").replace("%s", "XTA")); sbar->addWidget(d->hdds[HDD_BUS_XTA].label.get()); } - if ((hasIDE() || hdc_name.left(5) == QStringLiteral("xtide") || hdc_name.left(3) == QStringLiteral("ide")) && c_ide > 0) { - d->hdds[HDD_BUS_IDE].label = std::make_unique(); - d->hdds[HDD_BUS_IDE].setActive(false); - d->hdds[HDD_BUS_IDE].refresh(); - d->hdds[HDD_BUS_IDE].label->setToolTip(tr("Hard disk (%s)").replace("%s", "IDE")); - sbar->addWidget(d->hdds[HDD_BUS_IDE].label.get()); + if (hasIDE() || hdc_name.left(5) == QStringLiteral("xtide") || hdc_name.left(3) == QStringLiteral("ide")) { + if (c_ide > 0) { + d->hdds[HDD_BUS_IDE].label = std::make_unique(); + d->hdds[HDD_BUS_IDE].setActive(false); + d->hdds[HDD_BUS_IDE].refresh(); + d->hdds[HDD_BUS_IDE].label->setToolTip(tr("Hard disk (%s)").replace("%s", "IDE")); + sbar->addWidget(d->hdds[HDD_BUS_IDE].label.get()); + } + if (c_atapi > 0) { + d->hdds[HDD_BUS_ATAPI].label = std::make_unique(); + d->hdds[HDD_BUS_ATAPI].setActive(false); + d->hdds[HDD_BUS_ATAPI].refresh(); + d->hdds[HDD_BUS_ATAPI].label->setToolTip(tr("Hard disk (%s)").replace("%s", "ATAPI")); + sbar->addWidget(d->hdds[HDD_BUS_ATAPI].label.get()); + } } if ((hasSCSI() || (scsi_card_current[0] != 0) || (scsi_card_current[1] != 0) || (scsi_card_current[2] != 0) || (scsi_card_current[3] != 0)) && c_scsi > 0) { d->hdds[HDD_BUS_SCSI].label = std::make_unique(); diff --git a/src/qt/qt_main.cpp b/src/qt/qt_main.cpp index 598025795..845ff705f 100644 --- a/src/qt/qt_main.cpp +++ b/src/qt/qt_main.cpp @@ -208,6 +208,18 @@ main(int argc, char *argv[]) return 0; } + /* Warn the user about unsupported configs */ + if (cpu_override) { + QMessageBox warningbox(QMessageBox::Icon::Warning, QObject::tr("You are loading an unsupported configuration"), + QObject::tr("CPU type filtering based on selected machine is disabled for this emulated machine.\n\nThis makes it possible to choose a CPU that is otherwise incompatible with the selected machine. However, you may run into incompatibilities with the machine BIOS or other software.\n\nEnabling this setting is not officially supported and any bug reports filed may be closed as invalid."), + QMessageBox::NoButton); + warningbox.addButton(QObject::tr("Continue"), QMessageBox::AcceptRole); + warningbox.addButton(QObject::tr("Exit"), QMessageBox::RejectRole); + warningbox.exec(); + if (warningbox.result() == QDialog::Accepted) + return 0; + } + #ifdef DISCORD discord_load(); #endif @@ -279,20 +291,6 @@ main(int argc, char *argv[]) socket.connectToServer(qgetenv("86BOX_MANAGER_SOCKET")); } - /* Warn the user about unsupported configs */ - if (cpu_override) { - QMessageBox warningbox(QMessageBox::Icon::Warning, QObject::tr("You are loading an unsupported configuration"), - QObject::tr("CPU type filtering based on selected machine is disabled for this emulated machine.\n\nThis makes it possible to choose a CPU that is otherwise incompatible with the selected machine. However, you may run into incompatibilities with the machine BIOS or other software.\n\nEnabling this setting is not officially supported and any bug reports filed may be closed as invalid."), - QMessageBox::NoButton, main_window); - warningbox.addButton(QObject::tr("Continue"), QMessageBox::AcceptRole); - warningbox.addButton(QObject::tr("Exit"), QMessageBox::RejectRole); - warningbox.exec(); - if (warningbox.result() == QDialog::Accepted) { - confirm_exit_cmdl = 0; /* skip the confirmation prompt without touching the config */ - emit main_window->close(); - } - } - // pc_reset_hard_init(); /* Set the PAUSE mode depending on the renderer. */ diff --git a/src/qt/qt_mainwindow.cpp b/src/qt/qt_mainwindow.cpp index 1d0b2451a..a372dbfb8 100644 --- a/src/qt/qt_mainwindow.cpp +++ b/src/qt/qt_mainwindow.cpp @@ -346,6 +346,7 @@ MainWindow::MainWindow(QWidget *parent) ui->actionUpdate_status_bar_icons->setChecked(update_icons); ui->actionEnable_Discord_integration->setChecked(enable_discord); ui->actionApply_fullscreen_stretch_mode_when_maximized->setChecked(video_fullscreen_scale_maximized); + ui->actionShow_status_icons_in_fullscreen->setChecked(status_icons_fullscreen); #ifndef DISCORD ui->actionEnable_Discord_integration->setVisible(false); @@ -614,6 +615,15 @@ MainWindow::MainWindow(QWidget *parent) if (!vnc_enabled) video_setblit(qt_blit); + if (start_in_fullscreen) { + connect(ui->stackedWidget, &RendererStack::blit, this, [this] () { + if (start_in_fullscreen) { + QTimer::singleShot(100, ui->actionFullscreen, &QAction::trigger); + start_in_fullscreen = 0; + } + }); + } + #ifdef MTR_ENABLED { ui->actionBegin_trace->setVisible(true); @@ -840,10 +850,6 @@ MainWindow::showEvent(QShowEvent *event) QApplication::processEvents(); this->adjustSize(); } - if (start_in_fullscreen) { - start_in_fullscreen = 0; - QTimer::singleShot(0, ui->actionFullscreen, &QAction::trigger); - } } void @@ -2027,3 +2033,11 @@ void MainWindow::on_actionACPI_Shutdown_triggered() { acpi_pwrbut_pressed = 1; } + +void MainWindow::on_actionShow_status_icons_in_fullscreen_triggered() +{ + status_icons_fullscreen = !status_icons_fullscreen; + ui->actionShow_status_icons_in_fullscreen->setChecked(status_icons_fullscreen); + config_save(); +} + diff --git a/src/qt/qt_mainwindow.hpp b/src/qt/qt_mainwindow.hpp index 0de3f8656..553f9602c 100644 --- a/src/qt/qt_mainwindow.hpp +++ b/src/qt/qt_mainwindow.hpp @@ -144,6 +144,7 @@ private slots: void on_actionCursor_Puck_triggered(); void on_actionACPI_Shutdown_triggered(); + void on_actionShow_status_icons_in_fullscreen_triggered(); private slots: void on_actionShow_non_primary_monitors_triggered(); diff --git a/src/qt/qt_mainwindow.ui b/src/qt/qt_mainwindow.ui index 0580b18e1..d682815bb 100644 --- a/src/qt/qt_mainwindow.ui +++ b/src/qt/qt_mainwindow.ui @@ -180,6 +180,7 @@ + @@ -881,6 +882,14 @@ Cursor/Puck + + + true + + + Show status icons in fullscreen + + true diff --git a/src/qt/qt_renderercommon.cpp b/src/qt/qt_renderercommon.cpp index cebda184c..983f14d26 100644 --- a/src/qt/qt_renderercommon.cpp +++ b/src/qt/qt_renderercommon.cpp @@ -17,11 +17,15 @@ #include "qt_renderercommon.hpp" #include "qt_mainwindow.hpp" +#include "qt_machinestatus.hpp" #include #include #include #include +#include +#include +#include #include @@ -29,6 +33,8 @@ extern "C" { #include <86box/86box.h> #include <86box/plat.h> #include <86box/video.h> + +int status_icons_fullscreen = 0; } RendererCommon::RendererCommon() = default; @@ -131,6 +137,52 @@ RendererCommon::onResize(int width, int height) monitors[r_monitor_index].mon_res_y = (double) destination.height(); } +void RendererCommon::drawStatusBarIcons(QPainter* painter) +{ + uint32_t x = 0; + auto prevcompositionMode = painter->compositionMode(); + painter->setCompositionMode(QPainter::CompositionMode::CompositionMode_SourceOver); + for (int i = 0; i < main_window->statusBar()->children().count(); i++) { + QLabel* label = qobject_cast(main_window->statusBar()->children()[i]); + if (label) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + const QPixmap pixmap = label->pixmap(); +#elif QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + const QPixmap pixmap = label->pixmap(Qt::ReturnByValue); +#else + const QPixmap pixmap = (label->pixmap() ? *label->pixmap() : QPixmap()); +#endif + if (!pixmap.isNull()) { + painter->setBrush(QColor::fromRgbF(0, 0, 0, 1.)); + painter->fillRect(x, painter->device()->height() - pixmap.height() - 5, + pixmap.width(), pixmap.height() + 5, QColor::fromRgbF(0, 0, 0, .5)); + painter->drawPixmap(x + main_window->statusBar()->layout()->spacing() / 2, + painter->device()->height() - pixmap.height() - 3, pixmap); + x += pixmap.width(); + if (i <= main_window->statusBar()->children().count() - 3) { + painter->fillRect(x, painter->device()->height() - pixmap.height() - 5, + main_window->statusBar()->layout()->spacing(), pixmap.height() + 5, + QColor::fromRgbF(0, 0, 0, .5)); + x += main_window->statusBar()->layout()->spacing(); + } else + painter->fillRect(x, painter->device()->height() - pixmap.height() - 4, 4, + pixmap.height() + 4, QColor::fromRgbF(0, 0, 0, .5)); + } + } + } + if (main_window->status->getMessage().isEmpty() == false) { + auto curStatusMsg = main_window->status->getMessage(); + auto textSize = painter->fontMetrics().size(Qt::TextSingleLine, QChar(' ') + curStatusMsg + QChar(' ')); + painter->setPen(QColor(0, 0, 0, 127)); + painter->fillRect(painter->device()->width() - textSize.width(), painter->device()->height() - textSize.height(), + textSize.width(), textSize.height(), QColor(0, 0, 0, 127)); + painter->setPen(QColor(255, 255, 255, 255)); + painter->drawText(QRectF(painter->device()->width() - textSize.width(), painter->device()->height() - textSize.height(), + textSize.width(), textSize.height()), Qt::TextSingleLine, QChar(' ') + curStatusMsg + QChar(' ')); + } + painter->setCompositionMode(prevcompositionMode); +} + bool RendererCommon::eventDelegate(QEvent *event, bool &result) { diff --git a/src/qt/qt_renderercommon.hpp b/src/qt/qt_renderercommon.hpp index 4385a0b73..897240d27 100644 --- a/src/qt/qt_renderercommon.hpp +++ b/src/qt/qt_renderercommon.hpp @@ -42,6 +42,7 @@ public: protected: bool eventDelegate(QEvent *event, bool &result); + void drawStatusBarIcons(QPainter* painter); QRect source { 0, 0, 0, 0 }; QRect destination; diff --git a/src/qt/qt_rendererstack.cpp b/src/qt/qt_rendererstack.cpp index 469dc04de..1b1ed45c3 100644 --- a/src/qt/qt_rendererstack.cpp +++ b/src/qt/qt_rendererstack.cpp @@ -144,7 +144,10 @@ int ignoreNextMouseEvent = 1; void RendererStack::mouseReleaseEvent(QMouseEvent *event) { - if (this->geometry().contains(event->pos()) && (event->button() == Qt::LeftButton) && !mouse_capture && (isMouseDown & 1) && (kbd_req_capture || (mouse_get_buttons() != 0)) && (mouse_input_mode == 0)) { + if (!dopause && this->geometry().contains(event->pos()) && + (event->button() == Qt::LeftButton) && !mouse_capture && + (isMouseDown & 1) && (kbd_req_capture || (mouse_get_buttons() != 0)) && + (mouse_input_mode == 0)) { plat_mouse_capture(1); this->setCursor(Qt::BlankCursor); if (!ignoreNextMouseEvent) diff --git a/src/qt/qt_sdl.c b/src/qt/qt_sdl.c index 166ea88fa..15af4d7b6 100644 --- a/src/qt/qt_sdl.c +++ b/src/qt/qt_sdl.c @@ -627,7 +627,7 @@ sdl_main() case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: { - if ((event.button.button == SDL_BUTTON_LEFT) + if (!dopause && (event.button.button == SDL_BUTTON_LEFT) && !(mouse_capture || video_fullscreen) && event.button.state == SDL_RELEASED && mouse_inside) { diff --git a/src/qt/qt_softwarerenderer.cpp b/src/qt/qt_softwarerenderer.cpp index a8c0229d3..ab9ed932d 100644 --- a/src/qt/qt_softwarerenderer.cpp +++ b/src/qt/qt_softwarerenderer.cpp @@ -24,6 +24,7 @@ extern "C" { #include <86box/86box.h> +#include <86box/plat.h> #include <86box/video.h> } @@ -113,6 +114,7 @@ SoftwareRenderer::onPaint(QPaintDevice *device) #endif painter.setCompositionMode(QPainter::CompositionMode_Plus); painter.drawImage(destination, *images[cur_image], source); + if (video_fullscreen && status_icons_fullscreen) drawStatusBarIcons(&painter); } std::vector> diff --git a/src/scsi/scsi_cdrom.c b/src/scsi/scsi_cdrom.c index 056c7fdfe..fb52ca898 100644 --- a/src/scsi/scsi_cdrom.c +++ b/src/scsi/scsi_cdrom.c @@ -1997,7 +1997,7 @@ begin: case CDROM_TYPE_NEC_211_100: case CDROM_TYPE_NEC_464_105: /*GPCMD_READ_DISC_INFORMATION_NEC*/ scsi_cdrom_set_phase(dev, SCSI_PHASE_DATA_IN); - scsi_cdrom_buf_alloc(dev, 22); /*NEC manual claims 4 bytes, but the Android source code (namely sr_vendor.c) actually states otherwise.*/ + scsi_cdrom_buf_alloc(dev, 22); /* NEC manual claims 4 bytes, but the Linux kernel (namely sr_vendor.c) actually states otherwise. */ if (!dev->drv->ops) { scsi_cdrom_not_ready(dev); diff --git a/src/scsi/scsi_disk.c b/src/scsi/scsi_disk.c index ee0c3fc00..93d29a672 100644 --- a/src/scsi/scsi_disk.c +++ b/src/scsi/scsi_disk.c @@ -128,7 +128,6 @@ static void scsi_disk_command_complete(scsi_disk_t *dev); static void scsi_disk_mode_sense_load(scsi_disk_t *dev); static void scsi_disk_init(scsi_disk_t *dev); -static void scsi_disk_reset(scsi_common_t *sc); #ifdef ENABLE_SCSI_DISK_LOG int scsi_disk_do_log = ENABLE_SCSI_DISK_LOG; @@ -787,7 +786,7 @@ scsi_disk_rezero(scsi_disk_t *dev) scsi_disk_seek(dev, 0); } -static void +void scsi_disk_reset(scsi_common_t *sc) { scsi_disk_t *dev = (scsi_disk_t *) sc; diff --git a/src/sio/CMakeLists.txt b/src/sio/CMakeLists.txt index eaff4c48a..d19ca7066 100644 --- a/src/sio/CMakeLists.txt +++ b/src/sio/CMakeLists.txt @@ -16,7 +16,7 @@ add_library(sio OBJECT sio_acc3221.c sio_ali5123.c sio_f82c710.c sio_82091aa.c sio_fdc37c6xx.c sio_fdc37c67x.c sio_fdc37c669.c sio_fdc37c93x.c sio_fdc37m60x.c sio_nsc366.c - sio_it8661f.c + sio_it86x1f.c sio_it8702.c sio_pc87306.c sio_pc87307.c sio_pc87309.c sio_pc87310.c sio_pc87311.c sio_pc87332.c sio_prime3b.c sio_prime3c.c diff --git a/src/sio/sio_it8661f.c b/src/sio/sio_it8661f.c deleted file mode 100644 index 5367ffedf..000000000 --- a/src/sio/sio_it8661f.c +++ /dev/null @@ -1,353 +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 ITE IT8661F chipset. - * - * Note: This Super I/O is partially incomplete and intended only for having the intended machine to function - * - * Authors: Tiseno100 - * - * Copyright 2021 Tiseno100 - * - */ -#include -#include -#include -#include -#include -#include -#define HAVE_STDARG_H -#include <86box/86box.h> -#include <86box/io.h> -#include <86box/timer.h> -#include <86box/device.h> -#include <86box/lpt.h> -#include <86box/serial.h> -#include <86box/fdd.h> -#include <86box/fdc.h> -#include <86box/fdd_common.h> -#include <86box/sio.h> -#include <86box/plat_unused.h> - -#define LDN dev->regs[7] - -typedef struct it8661f_t { - fdc_t *fdc_controller; - serial_t *uart[2]; - - uint8_t index; - uint8_t regs[256]; - uint8_t device_regs[6][256]; - int unlocked; - int enumerator; -} it8661f_t; - -static uint8_t mb_pnp_key[32] = { 0x6a, 0xb5, 0xda, 0xed, 0xf6, 0xfb, 0x7d, 0xbe, 0xdf, 0x6f, 0x37, 0x1b, 0x0d, 0x86, 0xc3, 0x61, 0xb0, 0x58, 0x2c, 0x16, 0x8b, 0x45, 0xa2, 0xd1, 0xe8, 0x74, 0x3a, 0x9d, 0xce, 0xe7, 0x73, 0x39 }; - -static void it8661f_reset(void *priv); - -#ifdef ENABLE_IT8661_LOG -int it8661_do_log = ENABLE_IT8661_LOG; - -void -it8661_log(const char *fmt, ...) -{ - va_list ap; - - if (it8661_do_log) { - va_start(ap, fmt); - pclog_ex(fmt, ap); - va_end(ap); - } -} -#else -# define it8661_log(fmt, ...) -#endif - -static void -it8661_fdc(uint16_t addr, uint8_t val, it8661f_t *dev) -{ - fdc_remove(dev->fdc_controller); - - if (((addr == 0x30) && (val & 1)) || (dev->device_regs[0][0x30] & 1)) { - switch (addr) { - case 0x30: - dev->device_regs[0][addr] = val & 1; - break; - - case 0x31: - dev->device_regs[0][addr] = val & 3; - if (val & 1) - dev->device_regs[0][addr] |= 0x55; - break; - - case 0x60: - case 0x61: - dev->device_regs[0][addr] = val & ((addr == 0x61) ? 0xff : 0xf8); - break; - - case 0x70: - dev->device_regs[0][addr] = val & 0x0f; - break; - - case 0x74: - dev->device_regs[0][addr] = val & 7; - break; - - case 0xf0: - dev->device_regs[0][addr] = val & 0x0f; - break; - - default: - break; - } - - fdc_set_base(dev->fdc_controller, (dev->device_regs[0][0x60] << 8) | (dev->device_regs[0][0x61])); - fdc_set_irq(dev->fdc_controller, dev->device_regs[0][0x70] & 0x0f); - fdc_set_dma_ch(dev->fdc_controller, dev->device_regs[0][0x74] & 7); - - if (dev->device_regs[0][0xf0] & 1) - fdc_writeprotect(dev->fdc_controller); - - it8661_log("ITE 8661-FDC: BASE %04x IRQ %02x\n", (dev->device_regs[0][0x60] << 8) | (dev->device_regs[0][0x61]), - dev->device_regs[0][0x70] & 0x0f); - } -} - -static void -it8661_serial(int uart, uint16_t addr, uint8_t val, it8661f_t *dev) -{ - serial_remove(dev->uart[uart]); - - if (((addr == 0x30) && (val & 1)) || (dev->device_regs[1 + uart][0x30] & 1)) { - switch (addr) { - case 0x30: - dev->device_regs[1 + uart][addr] = val & 1; - break; - - case 0x60: - case 0x61: - dev->device_regs[1 + uart][addr] = val & ((addr == 0x61) ? 0xff : 0xf8); - break; - - case 0x70: - dev->device_regs[1 + uart][addr] = val & 0x0f; - break; - - case 0x74: - dev->device_regs[1 + uart][addr] = val & 7; - break; - - case 0xf0: - dev->device_regs[1 + uart][addr] = val & 3; - break; - - default: - break; - } - - serial_setup(dev->uart[uart], (dev->device_regs[1 + uart][0x60] << 8) | (dev->device_regs[1 + uart][0x61]), dev->device_regs[1 + uart][0x70] & 0x0f); - - it8661_log("ITE 8661-UART%01x: BASE %04x IRQ %02x\n", 1 + (LDN % 1), - (dev->device_regs[1 + uart][0x60] << 8) | (dev->device_regs[1 + uart][0x61]), - dev->device_regs[1 + uart][0x70] & 0x0f); - } -} - -void -it8661_lpt(uint16_t addr, uint8_t val, it8661f_t *dev) -{ - lpt1_remove(); - - if (((addr == 0x30) && (val & 1)) || (dev->device_regs[3][0x30] & 1)) { - switch (addr) { - case 0x30: - dev->device_regs[3][addr] = val & 1; - break; - - case 0x60: - case 0x61: - dev->device_regs[3][addr] = val & ((addr == 0x61) ? 0xff : 0xf8); - break; - - case 0x70: - dev->device_regs[3][addr] = val & 0x0f; - break; - - case 0x74: - dev->device_regs[3][addr] = val & 7; - break; - - case 0xf0: - dev->device_regs[3][addr] = val & 3; - break; - - default: - break; - } - - lpt1_init((dev->device_regs[3][0x60] << 8) | (dev->device_regs[3][0x61])); - lpt1_irq(dev->device_regs[3][0x70] & 0x0f); - - it8661_log("ITE 8661-LPT: BASE %04x IRQ %02x\n", (dev->device_regs[3][0x60] << 8) | (dev->device_regs[3][0x61]), - dev->device_regs[3][0x70] & 0x0f); - } -} - -void -it8661_ldn(uint16_t addr, uint8_t val, it8661f_t *dev) -{ - switch (LDN) { - case 0: - it8661_fdc(addr, val, dev); - break; - case 1: - case 2: - it8661_serial((LDN & 2) - 1, addr, val, dev); - break; - case 3: - it8661_lpt(addr, val, dev); - break; - - default: - break; - } -} - -static void -it8661f_write(uint16_t addr, uint8_t val, void *priv) -{ - it8661f_t *dev = (it8661f_t *) priv; - - switch (addr) { - case FDC_SECONDARY_ADDR: - if (!dev->unlocked) { - (val == mb_pnp_key[dev->enumerator]) ? dev->enumerator++ : (dev->enumerator = 0); - if (dev->enumerator == 31) { - dev->unlocked = 1; - it8661_log("ITE8661F: Unlocked!\n"); - } - } else - dev->index = val; - break; - - case 0x371: - if (dev->unlocked) { - switch (dev->index) { - case 0x02: - dev->regs[dev->index] = val; - if (val & 1) - it8661f_reset(dev); - if (val & 2) - dev->unlocked = 0; - break; - case 0x07: - dev->regs[dev->index] = val; - break; - case 0x22: - dev->regs[dev->index] = val & 0x30; - break; - case 0x23: - dev->regs[dev->index] = val & 0x1f; - break; - default: - it8661_ldn(dev->index, val, dev); - break; - } - } - break; - - default: - break; - } - - return; -} - -static uint8_t -it8661f_read(uint16_t addr, void *priv) -{ - const it8661f_t *dev = (it8661f_t *) priv; - - it8661_log("IT8661F:\n", addr, dev->regs[dev->index]); - return (addr == 0xa79) ? dev->regs[dev->index] : 0xff; -} - -static void -it8661f_reset(void *priv) -{ - it8661f_t *dev = (it8661f_t *) priv; - dev->regs[0x20] = 0x86; - dev->regs[0x21] = 0x61; - - dev->device_regs[0][0x60] = 3; - dev->device_regs[0][0x61] = 0xf0; - dev->device_regs[0][0x70] = 6; - dev->device_regs[0][0x71] = 2; - dev->device_regs[0][0x74] = 2; - - dev->device_regs[1][0x60] = 3; - dev->device_regs[1][0x61] = 0xf8; - dev->device_regs[1][0x70] = 4; - dev->device_regs[1][0x71] = 2; - - dev->device_regs[2][0x60] = 2; - dev->device_regs[2][0x61] = 0xf8; - dev->device_regs[2][0x70] = 3; - dev->device_regs[2][0x71] = 2; - - dev->device_regs[3][0x60] = 3; - dev->device_regs[3][0x61] = 0x78; - dev->device_regs[3][0x70] = 7; - dev->device_regs[3][0x71] = 2; - dev->device_regs[3][0x74] = 3; - dev->device_regs[3][0xf0] = 3; -} - -static void -it8661f_close(void *priv) -{ - it8661f_t *dev = (it8661f_t *) priv; - - free(dev); -} - -static void * -it8661f_init(UNUSED(const device_t *info)) -{ - it8661f_t *dev = (it8661f_t *) malloc(sizeof(it8661f_t)); - memset(dev, 0, sizeof(it8661f_t)); - - dev->fdc_controller = device_add(&fdc_at_smc_device); - fdc_reset(dev->fdc_controller); - - dev->uart[0] = device_add_inst(&ns16550_device, 1); - dev->uart[1] = device_add_inst(&ns16550_device, 2); - - io_sethandler(FDC_SECONDARY_ADDR, 0x0002, it8661f_read, NULL, NULL, it8661f_write, NULL, NULL, dev); - - dev->enumerator = 0; - dev->unlocked = 0; - - it8661f_reset(dev); - return dev; -} - -const device_t it8661f_device = { - .name = "ITE IT8661F", - .internal_name = "it8661f", - .flags = 0, - .local = 0, - .init = it8661f_init, - .close = it8661f_close, - .reset = NULL, - { .available = NULL }, - .speed_changed = NULL, - .force_redraw = NULL, - .config = NULL -}; diff --git a/src/sio/sio_it86x1f.c b/src/sio/sio_it86x1f.c new file mode 100644 index 000000000..d53e78050 --- /dev/null +++ b/src/sio/sio_it86x1f.c @@ -0,0 +1,862 @@ +/* + * 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. + * + * Emulation of the ITE IT86x1F Super I/O chips. + * + * + * + * Authors: RichardG, + * + * Copyright 2023 RichardG. + */ +#include +#include +#include +#include +#include +#include +#define HAVE_STDARG_H +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/io.h> +#include <86box/timer.h> +#include <86box/pci.h> +#include <86box/lpt.h> +#include <86box/serial.h> +#include <86box/fdd.h> +#include <86box/fdc.h> +#include <86box/gameport.h> +#include <86box/sio.h> +#include <86box/isapnp.h> +#include <86box/plat_fallthrough.h> +#include <86box/plat_unused.h> + +enum { + ITE_IT8661F = 0x8661, + ITE_IT8671F = 0x8681 +}; + +#define CHIP_ID *((uint16_t *) &dev->global_regs[0]) + +static void it8671f_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv); +static void it8661f_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv); + +static const struct { + uint16_t chip_id; + uint16_t unlock_id; + uint8_t gpio_ldn; + /* Fake ROMs to delegate all the logical device register handling over to the ISAPnP subsystem. + The actual ROMs/IDs used by real chips when those are set to ISAPnP mode remain to be seen. */ + uint8_t *pnp_rom; + + const isapnp_device_config_t *pnp_defaults; + + void (*pnp_config_changed)(uint8_t ld, isapnp_device_config_t *config, void *priv); +} it86x1f_models[] = { + { + .chip_id = ITE_IT8661F, + .unlock_id = 0x8661, + .gpio_ldn = 0x05, + .pnp_rom = (uint8_t[]) { + 0x26, 0x85, 0x86, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, /* ITE8661, dummy checksum (filled in by isapnp_add_card) */ + 0x0a, 0x10, 0x10, /* PnP version 1.0, vendor version 1.0 */ + + 0x15, 0x41, 0xd0, 0x07, 0x00, 0x01, /* logical device PNP0700, can participate in boot */ + 0x23, 0xf8, 0x0f, 0x02, /* IRQ 3/4/5/6/7/8/9/10/11, low true edge sensitive */ + 0x2a, 0x0f, 0x0c, /* DMA 0/1/2/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */ + 0x47, 0x01, 0x00, 0x01, 0xf8, 0x0f, 0x08, 0x08, /* I/O 0x100-0xFF8, decodes 16-bit, 8-byte alignment, 8 addresses */ + + 0x15, 0x41, 0xd0, 0x05, 0x01, 0x01, /* logical device PNP0501, can participate in boot */ + 0x23, 0xf8, 0x0f, 0x02, /* IRQ 3/4/5/6/7/8/9/10/11, low true edge sensitive */ + 0x47, 0x01, 0x00, 0x01, 0xf8, 0x0f, 0x08, 0x08, /* I/O 0x100-0xFF8, decodes 16-bit, 8-byte alignment, 8 addresses */ + + 0x15, 0x41, 0xd0, 0x05, 0x01, 0x01, /* logical device PNP0501, can participate in boot */ + 0x23, 0xf8, 0x0f, 0x02, /* IRQ 3/4/5/6/7/8/9/10/11, low true edge sensitive */ + 0x47, 0x01, 0x00, 0x01, 0xf8, 0x0f, 0x08, 0x08, /* I/O 0x100-0xFF8, decodes 16-bit, 8-byte alignment, 8 addresses */ + + 0x15, 0x41, 0xd0, 0x04, 0x00, 0x01, /* logical device PNP0400, can participate in boot */ + 0x23, 0xf8, 0x0f, 0x02, /* IRQ 3/4/5/6/7/8/9/10/11, low true edge sensitive */ + 0x2a, 0x0f, 0x0c, /* DMA 0/1/2/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */ + 0x47, 0x01, 0x00, 0x01, 0xf8, 0x0f, 0x08, 0x08, /* I/O 0x100-0xFF8, decodes 16-bit, 8-byte alignment, 8 addresses */ + 0x47, 0x01, 0x00, 0x01, 0xfc, 0x0f, 0x04, 0x04, /* I/O 0x100-0xFFC, decodes 16-bit, 4-byte alignment, 4 addresses */ + + 0x15, 0x41, 0xd0, 0x05, 0x10, 0x01, /* logical device PNP0510, can participate in boot */ + 0x23, 0xf8, 0x0f, 0x02, /* IRQ 3/4/5/6/7/8/9/10/11, low true edge sensitive */ + 0x23, 0xf8, 0x0f, 0x02, /* IRQ 3/4/5/6/7/8/9/10/11, low true edge sensitive */ + 0x2a, 0x0f, 0x0c, /* DMA 0/1/2/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */ + 0x2a, 0x0f, 0x0c, /* DMA 0/1/2/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */ + 0x47, 0x01, 0x00, 0x01, 0xf8, 0x0f, 0x08, 0x08, /* I/O 0x100-0xFF8, decodes 16-bit, 8-byte alignment, 8 addresses */ + 0x47, 0x01, 0x00, 0x01, 0xf8, 0x0f, 0x08, 0x08, /* I/O 0x100-0xFF8, decodes 16-bit, 8-byte alignment, 8 addresses */ + + 0x79, 0x00 /* end tag, dummy checksum (filled in by isapnp_add_card) */ + }, + .pnp_defaults = (const isapnp_device_config_t[]) { + { + .activate = 0, + .io = { { .base = FDC_PRIMARY_ADDR }, }, + .irq = { { .irq = FDC_PRIMARY_IRQ }, }, + .dma = { { .dma = FDC_PRIMARY_DMA }, } + }, { + .activate = 0, + .io = { { .base = COM1_ADDR }, }, + .irq = { { .irq = COM1_IRQ }, } + }, { + .activate = 0, + .io = { { .base = COM2_ADDR }, }, + .irq = { { .irq = COM2_IRQ }, } + }, { + .activate = 0, + .io = { { .base = LPT1_ADDR }, { .base = 0x778 }, }, + .irq = { { .irq = LPT1_IRQ }, }, + .dma = { { .dma = 3 }, } + }, { + .activate = 0, + .io = { { .base = COM4_ADDR }, { .base = 0x300 }, }, + .irq = { { .irq = 10 }, { .irq = 11 }, }, + .dma = { { .dma = 1 }, { .dma = 0 }, } + }, { + .activate = -1 + } + }, + .pnp_config_changed = it8661f_pnp_config_changed + }, { + .chip_id = ITE_IT8671F, + .unlock_id = 0x8680, + .gpio_ldn = 0x07, + .pnp_rom = (uint8_t[]) { + 0x26, 0x85, 0x86, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, /* ITE8671, dummy checksum (filled in by isapnp_add_card) */ + 0x0a, 0x10, 0x10, /* PnP version 1.0, vendor version 1.0 */ + + 0x15, 0x41, 0xd0, 0x07, 0x00, 0x01, /* logical device PNP0700, can participate in boot */ + 0x23, 0xfa, 0x1f, 0x02, /* IRQ 1/3/4/5/6/7/8/9/10/11/12, low true edge sensitive */ + 0x2a, 0x0f, 0x0c, /* DMA 0/1/2/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */ + 0x47, 0x01, 0x00, 0x01, 0xf8, 0x0f, 0x08, 0x08, /* I/O 0x100-0xFF8, decodes 16-bit, 8-byte alignment, 8 addresses */ + + 0x15, 0x41, 0xd0, 0x05, 0x01, 0x01, /* logical device PNP0501, can participate in boot */ + 0x23, 0xfa, 0x1f, 0x02, /* IRQ 1/3/4/5/6/7/8/9/10/11/12, low true edge sensitive */ + 0x47, 0x01, 0x00, 0x01, 0xf8, 0x0f, 0x08, 0x08, /* I/O 0x100-0xFF8, decodes 16-bit, 8-byte alignment, 8 addresses */ + + 0x15, 0x41, 0xd0, 0x05, 0x10, 0x01, /* logical device PNP0510, can participate in boot */ + 0x23, 0xfa, 0x1f, 0x02, /* IRQ 1/3/4/5/6/7/8/9/10/11/12, low true edge sensitive */ + 0x23, 0xfa, 0x1f, 0x02, /* IRQ 1/3/4/5/6/7/8/9/10/11/12, low true edge sensitive */ + 0x2a, 0x0f, 0x0c, /* DMA 0/1/2/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */ + 0x2a, 0x0f, 0x0c, /* DMA 0/1/2/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */ + 0x47, 0x01, 0x00, 0x01, 0xf8, 0x0f, 0x08, 0x08, /* I/O 0x100-0xFF8, decodes 16-bit, 8-byte alignment, 8 addresses */ + 0x47, 0x01, 0x00, 0x01, 0xf8, 0x0f, 0x08, 0x08, /* I/O 0x100-0xFF8, decodes 16-bit, 8-byte alignment, 8 addresses */ + + 0x15, 0x41, 0xd0, 0x04, 0x00, 0x01, /* logical device PNP0400, can participate in boot */ + 0x23, 0xfa, 0x1f, 0x02, /* IRQ 1/3/4/5/6/7/8/9/10/11/12, low true edge sensitive */ + 0x2a, 0x0f, 0x0c, /* DMA 0/1/2/3, compatibility, no count by word, count by byte, is bus master, 8-bit only */ + 0x47, 0x01, 0x00, 0x01, 0xf8, 0x0f, 0x08, 0x08, /* I/O 0x100-0xFF8, decodes 16-bit, 8-byte alignment, 8 addresses */ + 0x47, 0x01, 0x00, 0x01, 0xfc, 0x0f, 0x04, 0x04, /* I/O 0x100-0xFFC, decodes 16-bit, 4-byte alignment, 4 addresses */ + + 0x15, 0x41, 0xd0, 0xff, 0xff, 0x00, /* logical device PNPFFFF (dummy to create APC gap in LDNs) */ + + 0x15, 0x41, 0xd0, 0x03, 0x03, 0x01, /* logical device PNP0303, can participate in boot */ + 0x23, 0xfa, 0x1f, 0x02, /* IRQ 1/3/4/5/6/7/8/9/10/11/12, low true edge sensitive */ + 0x47, 0x01, 0x00, 0x00, 0xff, 0x0f, 0x01, 0x01, /* I/O 0x0-0xFFF, decodes 16-bit, 1-byte alignment, 1 address */ + 0x47, 0x01, 0x00, 0x00, 0xff, 0x0f, 0x01, 0x01, /* I/O 0x0-0xFFF, decodes 16-bit, 1-byte alignment, 1 address */ + + 0x15, 0x41, 0xd0, 0x0f, 0x13, 0x01, /* logical device PNP0F13, can participate in boot */ + 0x23, 0xfa, 0x1f, 0x02, /* IRQ 1/3/4/5/6/7/8/9/10/11/12, low true edge sensitive */ + + 0x79, 0x00 /* end tag, dummy checksum (filled in by isapnp_add_card) */ + }, + .pnp_defaults = (const isapnp_device_config_t[]) { + { + .activate = 0, + .io = { { .base = FDC_PRIMARY_ADDR }, }, + .irq = { { .irq = FDC_PRIMARY_IRQ }, }, + .dma = { { .dma = FDC_PRIMARY_DMA }, } + }, { + .activate = 0, + .io = { { .base = COM1_ADDR }, }, + .irq = { { .irq = COM1_IRQ }, } + }, { + .activate = 0, + .io = { { .base = COM2_ADDR }, { .base = 0x300 }, }, + .irq = { { .irq = COM2_IRQ }, { .irq = 10 }, }, + .dma = { { .dma = 0 }, { .dma = 1 }, } + }, { + .activate = 0, + .io = { { .base = LPT1_ADDR }, { .base = 0x778 }, }, + .irq = { { .irq = LPT1_IRQ }, }, + .dma = { { .dma = 3 }, } + }, { + .activate = 0 + }, { + .activate = 1, + .io = { { .base = 0x60 }, { .base = 0x64 }, }, + .irq = { { .irq = 1 }, } + }, { + .activate = 0, + .irq = { { .irq = 12 }, } + }, { + .activate = -1 + } + }, + .pnp_config_changed = it8671f_pnp_config_changed + } +}; + +#ifdef ENABLE_IT86X1F_LOG +int it86x1f_do_log = ENABLE_IT86X1F_LOG; + +static void +it86x1f_log(const char *fmt, ...) +{ + va_list ap; + + if (it86x1f_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +# define it86x1f_log(fmt, ...) +#endif + +typedef struct it86x1f_t { + uint8_t instance; + uint8_t locked; + uint8_t cur_ldn; + uint8_t cur_reg; + void *pnp_card; + uint8_t global_regs[16]; /* [0x20:0x2f] */ + uint8_t ldn_regs[8][16]; /* [0xf0:0xff] */ + uint8_t gpio_regs[36]; /* [0x60:0x7f] then [0xe0:0xe3] */ + uint8_t gpio_ldn; + + uint16_t unlock_id; + uint16_t addr_port; + uint16_t data_port; + uint8_t unlock_val; + uint8_t unlock_pos : 2; + uint8_t key_pos : 5; + + fdc_t *fdc; + serial_t *uart[2]; + void *gameport; +} it86x1f_t; + +static void it86x1f_remap(it86x1f_t *dev, uint16_t addr_port, uint16_t data_port); + +static void +it8661f_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv) +{ + if (ld > 5) { + it86x1f_log("IT86x1F: Unknown logical device %d\n", ld); + return; + } + + it86x1f_t *dev = (it86x1f_t *) priv; + + switch (ld) { + case 0: + fdc_remove(dev->fdc); + + if (config->activate) { + it86x1f_log("IT86x1F: FDC enabled at port %04X IRQ %d DMA %d\n", config->io[0].base, config->irq[0].irq, (config->dma[0].dma == ISAPNP_DMA_DISABLED) ? -1 : config->dma[0].dma); + + if (config->io[0].base != ISAPNP_IO_DISABLED) + fdc_set_base(dev->fdc, config->io[0].base); + + fdc_set_irq(dev->fdc, config->irq[0].irq); + fdc_set_dma_ch(dev->fdc, (config->dma[0].dma == ISAPNP_DMA_DISABLED) ? -1 : config->dma[0].dma); + } else { + it86x1f_log("IT86x1F: FDC disabled\n"); + } + + break; + + case 1: + case 2: + serial_remove(dev->uart[ld - 1]); + + if (config->activate && (config->io[0].base != ISAPNP_IO_DISABLED)) { + it86x1f_log("IT86x1F: UART %d enabled at port %04X IRQ %d\n", ld - 1, config->io[0].base, config->irq[0].irq); + serial_setup(dev->uart[ld - 1], config->io[0].base, config->irq[0].irq); + } else { + it86x1f_log("IT86x1F: UART %d disabled\n", ld - 1); + } + + break; + + case 3: + lpt1_remove(); + + if (config->activate && (config->io[0].base != ISAPNP_IO_DISABLED)) { + it86x1f_log("IT86x1F: LPT enabled at port %04X IRQ %d\n", config->io[0].base, config->irq[0].irq); + lpt1_init(config->io[0].base); + } else { + it86x1f_log("IT86x1F: LPT disabled\n"); + } + + break; + + case 4: + if (config->activate && (config->io[0].base != ISAPNP_IO_DISABLED)) { + it86x1f_log("IT86x1F: IR enabled at ports %04X %04X IRQs %d %d DMAs %d %d\n", config->io[0].base, config->io[1].base, config->irq[0].irq, config->irq[1].irq, (config->dma[0].dma == ISAPNP_DMA_DISABLED) ? -1 : config->dma[0].dma, (config->dma[1].dma == ISAPNP_DMA_DISABLED) ? -1 : config->dma[1].dma); + } else { + it86x1f_log("IT86x1F: IR disabled\n"); + } + break; + + default: + break; + } +} + +static void +it8671f_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv) +{ + it86x1f_t *dev = (it86x1f_t *) priv; + + switch (ld) { + case 2: + it8661f_pnp_config_changed(4, config, dev); /* just for logging, should change if IR UART is implemented */ + fallthrough; + + case 0 ... 1: + case 3: + it8661f_pnp_config_changed(ld, config, dev); + break; + + case 5: + if (config->activate && (config->io[0].base != ISAPNP_IO_DISABLED) && (config->io[1].base != ISAPNP_IO_DISABLED)) { + it86x1f_log("IT86x1F: KBC enabled at ports %04X %04X IRQ %d\n", config->io[0].base, config->io[1].base, config->irq[0].irq); + } else { + it86x1f_log("IT86x1F: KBC disabled\n"); + } + break; + + case 6: + if (config->activate) { + it86x1f_log("IT86x1F: KBC mouse enabled at IRQ %d\n", config->irq[0].irq); + } else { + it86x1f_log("IT86x1F: KBC mouse disabled\n"); + } + break; + + default: + break; + } +} + +static uint8_t +it86x1f_pnp_read_vendor_reg(uint8_t ld, uint8_t reg, void *priv) +{ + it86x1f_t *dev = (it86x1f_t *) priv; + uint8_t ret = 0xff; + + switch (reg) { + case 0x20 ... 0x2f: + ret = dev->global_regs[reg & 0x0f]; + break; + + case 0x60 ... 0x7f: + if (ld != dev->gpio_ldn) + break; + + ret = dev->gpio_regs[reg & 0x1f]; + break; + + case 0xe0 ... 0xe3: + if (ld != dev->gpio_ldn) + break; + + ret = dev->gpio_regs[0x20 | (reg & 0x03)]; + break; + + case 0xf0 ... 0xff: + if (ld > dev->gpio_ldn) + break; + + ret = dev->ldn_regs[ld][reg & 0x0f]; + break; + + default: + break; + } + + it86x1f_log("IT86x1F: read_vendor_reg(%X, %02X) = %02X\n", ld, reg, ret); + + return ret; +} + +static void +it86x1f_pnp_write_vendor_reg(uint8_t ld, uint8_t reg, uint8_t val, void *priv) +{ + it86x1f_t *dev = (it86x1f_t *) priv; + uint8_t effective_ldn; + + it86x1f_log("IT86x1F: write_vendor_reg(%X, %02X, %02X)\n", ld, reg, val); + + switch (reg) { + case 0x22: + if (CHIP_ID == ITE_IT8661F) { + dev->global_regs[reg & 0x0f] = (val & 0x30) | (dev->global_regs[reg & 0x0f] & ~0x30); + uint8_t mcc = (val & 0x30) >> 4; + if (mcc != dev->instance) { + it86x1f_log("IT86x1F: Instance %d unmapping as ID %d was written\n", dev->instance, mcc); + it86x1f_remap(dev, 0, 0); + } + } + break; + + case 0x23: + val &= (1 << dev->gpio_ldn) - 1; + dev->global_regs[reg & 0x0f] = val; + if (val) + pclog("IT86x1F: Warning: ISAPnP mode enabled.\n"); + break; + + case 0x24: + dev->global_regs[reg & 0x0f] = val & ((CHIP_ID == ITE_IT8661F) ? 0x03 : 0x5f); + break; + + case 0x25: + val &= (CHIP_ID == ITE_IT8661F) ? 0x1f : 0xf0; + fallthrough; + + case 0x26: + if (ld == dev->gpio_ldn) + dev->global_regs[reg & 0x0f] = val; + break; + + case 0x2e ... 0x2f: + if ((CHIP_ID == ITE_IT8671F) && (ld == 0xf4)) + dev->global_regs[reg & 0x0f] = val; + break; + + case 0x60 ... 0x7f: + if (ld != dev->gpio_ldn) + break; + + dev->gpio_regs[reg & 0x1f] = val; + break; + + case 0xe0 ... 0xe3: + if (ld != dev->gpio_ldn) + break; + + dev->gpio_regs[0x20 | (reg & 0x0f)] = val; + break; + + case 0xf0 ... 0xff: + /* Translate GPIO LDN to 7 for the switch block. */ + if (ld == dev->gpio_ldn) + effective_ldn = 7; + else if (ld == 7) + effective_ldn = 8; /* dummy */ + else + effective_ldn = ld; + + switch ((effective_ldn << 8) | reg) { + case 0x0f0: + dev->ldn_regs[ld][reg & 0x0f] = val & 0x0f; + fdc_set_swwp(dev->fdc, !!(val & 0x01)); + fdc_set_swap(dev->fdc, !!(val & 0x04)); + break; + + case 0x1f0: + dev->ldn_regs[ld][reg & 0x0f] = val & 0x03; + break; + + case 0x2f0: + dev->ldn_regs[ld][reg & 0x0f] = val & ((CHIP_ID == ITE_IT8661F) ? 0x03 : 0xf3); + break; + + case 0x2f1: + if (CHIP_ID == ITE_IT8671F) + dev->ldn_regs[ld][reg & 0x0f] = val & 0xb7; + break; + + case 0x3f0: + dev->ldn_regs[ld][reg & 0x0f] = val & 0x07; + break; + + case 0x4f0: + if (CHIP_ID == ITE_IT8661F) + val &= 0x3f; + dev->ldn_regs[ld][reg & 0x0f] = val; + break; + + case 0x4f1: + if (CHIP_ID == ITE_IT8671F) + dev->ldn_regs[ld][reg & 0x0f] = val & 0x7f; + break; + + case 0x4f2: + case 0x4f6: + if (CHIP_ID == ITE_IT8671F) + dev->ldn_regs[ld][reg & 0x0f] = val; + break; + + case 0x4f7: + if (CHIP_ID == ITE_IT8671F) + dev->ldn_regs[ld][reg & 0x0f] = val & 0x7f; + break; + + case 0x4f8: + if (CHIP_ID == ITE_IT8671F) + dev->ldn_regs[ld][reg & 0x0f] = val & 0x07; + break; + + case 0x5f0: + dev->ldn_regs[ld][reg & 0x0f] = val & 0x1f; + break; + + case 0x6f0: + if (CHIP_ID == ITE_IT8671F) + dev->ldn_regs[ld][reg & 0x0f] = val & 0x03; + break; + + case 0x760: + case 0x762: + case 0x764: + case 0x766: + dev->gpio_regs[reg & 0x1f] = val & 0x0f; + break; + + case 0x772: + if (CHIP_ID != ITE_IT8671F) + break; + fallthrough; + + case 0x761: + case 0x763: + case 0x765: + case 0x767: + case 0x770: + dev->gpio_regs[reg & 0x1f] = val; + + case 0x771: + if (CHIP_ID == ITE_IT8671F) + dev->gpio_regs[reg & 0x1f] = val & 0xde; + break; + + case 0x7e0: + if (CHIP_ID == ITE_IT8671F) + dev->gpio_regs[0x20 | (reg & 0x03)] = val & 0xef; + break; + + case 0x7e1: + if (CHIP_ID == ITE_IT8671F) + dev->gpio_regs[0x20 | (reg & 0x03)] = val & 0x7f; + break; + + case 0x7e3: + if ((CHIP_ID == ITE_IT8671F) && (val & 0x80)) + *((uint16_t *) &dev->gpio_regs[0x22]) = 0x0000; + break; + + case 0x7fb: + if (CHIP_ID == ITE_IT8671F) + val &= 0x7f; + fallthrough; + + case 0x7f0 ... 0x7f5: + dev->ldn_regs[ld][reg & 0x0f] = val; + break; + + case 0x7f6: + dev->ldn_regs[ld][reg & 0x0f] = val & ((CHIP_ID == ITE_IT8661F) ? 0x3f : 0xcf); + break; + + case 0x7f7: + dev->ldn_regs[ld][reg & 0x0f] = val & ((CHIP_ID == ITE_IT8661F) ? 0x9f : 0xdf); + break; + + case 0x7f8 ... 0x7fa: + dev->ldn_regs[ld][reg & 0x0f] = val & ((CHIP_ID == ITE_IT8661F) ? 0x1f : 0x0f); + break; + + case 0x7fc: + if (CHIP_ID == ITE_IT8661F) + dev->ldn_regs[ld][reg & 0x0f] = val; + break; + + case 0x7ff: + if (CHIP_ID == ITE_IT8671F) + dev->ldn_regs[ld][reg & 0x0f] = val & 0x2f; + break; + + default: + break; + } + break; + + default: + break; + } +} + +static void +it86x1f_write_addr(uint16_t port, uint8_t val, void *priv) +{ + it86x1f_t *dev = (it86x1f_t *) priv; + + it86x1f_log("IT86x1F: write_addr(%04X, %02X)\n", port, val); + + if (dev->locked) { + if (val == isapnp_init_key[dev->key_pos]) { + if (++dev->key_pos == 0) { + it86x1f_log("IT86x1F: Unlocked\n"); + dev->locked = 0; + } + } else { + dev->key_pos = 0; + } + } else { + dev->cur_reg = val; + } +} + +static void +it86x1f_write_data(uint16_t port, uint8_t val, void *priv) +{ + it86x1f_t *dev = (it86x1f_t *) priv; + + it86x1f_log("IT86x1F: write_data(%04X, %02X)\n", port, val); + + if (dev->locked) + return; + + switch (dev->cur_reg) { + case 0x00 ... 0x01: + case 0x03 ... 0x06: + case 0x31: + case 0x71: + case 0x73: + break; /* ISAPnP-only */ + + case 0x07: + dev->cur_ldn = val; + break; + + case 0x02: + if (val & 0x02) { + it86x1f_log("IT86x1F: Locked => "); + dev->locked = 1; + it86x1f_remap(dev, 0, 0); + } + fallthrough; + + default: + isapnp_write_reg(dev->pnp_card, dev->cur_ldn, dev->cur_reg, val); + break; + } +} + +static uint8_t +it86x1f_read_addr(uint16_t port, void *priv) +{ + it86x1f_t *dev = (it86x1f_t *) priv; + uint8_t ret = dev->locked ? 0xff : dev->cur_reg; + + it86x1f_log("IT86x1F: read_addr(%04X) = %02X\n", port, ret); + + return ret; +} + +static uint8_t +it86x1f_read_data(uint16_t port, void *priv) +{ + it86x1f_t *dev = (it86x1f_t *) priv; + uint8_t ret = 0xff; + + switch (dev->cur_reg) { + case 0x00 ... 0x01: + case 0x03 ... 0x06: + case 0x31: + case 0x71: + case 0x73: + break; /* ISAPnP-only */ + + case 0x07: + ret = dev->cur_ldn; + break; + + default: + ret = isapnp_read_reg(dev->pnp_card, dev->cur_ldn, dev->cur_reg); + break; + } + + it86x1f_log("IT86x1F: read_data(%04X) = %02X\n", port, ret); + + return ret; +} + +static void +it86x1f_remap(it86x1f_t *dev, uint16_t addr_port, uint16_t data_port) +{ + if (dev->addr_port) + io_removehandler(dev->addr_port, 1, it86x1f_read_addr, NULL, NULL, it86x1f_write_addr, NULL, NULL, dev); + if (dev->data_port) + io_removehandler(dev->data_port, 1, it86x1f_read_data, NULL, NULL, it86x1f_write_data, NULL, NULL, dev); + + it86x1f_log("IT86x1F: remap(%04X, %04X)\n", addr_port, data_port); + dev->addr_port = addr_port; + dev->data_port = data_port; + + if (dev->addr_port) + io_sethandler(dev->addr_port, 1, it86x1f_read_addr, NULL, NULL, it86x1f_write_addr, NULL, NULL, dev); + if (dev->data_port) + io_sethandler(dev->data_port, 1, it86x1f_read_data, NULL, NULL, it86x1f_write_data, NULL, NULL, dev); +} + +static void +it86x1f_write_unlock(UNUSED(uint16_t port), uint8_t val, void *priv) +{ + it86x1f_t *dev = (it86x1f_t *) priv; + + it86x1f_log("IT86x1F: write_unlock(%04X, %02X)\n", port, val); + + if (!dev->locked) + dev->unlock_pos = 0; + + switch (dev->unlock_pos++) { + case 0: + if (val != (dev->unlock_id >> 8)) + dev->unlock_pos = 0; + break; + + case 1: + if (val != (dev->unlock_id & 0xff)) + dev->unlock_pos = 0; + break; + + case 2: + if ((val != 0x55) && (val != 0xaa)) + dev->unlock_pos = 0; + else + dev->unlock_val = val; + break; + + case 3: + switch ((dev->unlock_val << 8) | val) { + case 0x5555: + it86x1f_remap(dev, 0x3f0, 0x3f1); + break; + + case 0x55aa: + it86x1f_remap(dev, 0x3bd, 0x3bf); + break; + + case 0xaa55: + it86x1f_remap(dev, 0x370, 0x371); + break; + + default: + it86x1f_remap(dev, 0, 0); + break; + } + dev->unlock_pos = 0; + break; + } +} + +void +it86x1f_reset(it86x1f_t *dev) +{ + it86x1f_log("IT86x1F: reset()\n"); + + fdc_reset(dev->fdc); + + serial_remove(dev->uart[0]); + + serial_remove(dev->uart[1]); + + lpt1_remove(); + + isapnp_enable_card(dev->pnp_card, ISAPNP_CARD_DISABLE); + + dev->locked = 1; + + isapnp_reset_card(dev->pnp_card); +} + +static void +it86x1f_close(void *priv) +{ + it86x1f_t *dev = (it86x1f_t *) priv; + + it86x1f_log("IT86x1F: close()\n"); + + free(dev); +} + +static void * +it86x1f_init(UNUSED(const device_t *info)) +{ + it86x1f_t *dev = (it86x1f_t *) malloc(sizeof(it86x1f_t)); + memset(dev, 0, sizeof(it86x1f_t)); + + uint8_t i; + for (i = 0; i < (sizeof(it86x1f_models) / sizeof(it86x1f_models[0])); i++) { + if (it86x1f_models[i].chip_id == info->local) + break; + } + if (i >= (sizeof(it86x1f_models) / sizeof(it86x1f_models[0]))) { + fatal("IT86x1F: Unknown type %04X selected\n", info->local); + return NULL; + } + it86x1f_log("IT86x1F: init(%04X)\n", info->local); + + /* Let the resource data parser figure out the ROM size. */ + dev->pnp_card = isapnp_add_card(it86x1f_models[i].pnp_rom, -1, it86x1f_models[i].pnp_config_changed, NULL, it86x1f_pnp_read_vendor_reg, it86x1f_pnp_write_vendor_reg, dev); + for (uint8_t j = 0; it86x1f_models[i].pnp_defaults[j].activate != (uint8_t) -1; j++) + isapnp_set_device_defaults(dev->pnp_card, j, &it86x1f_models[i].pnp_defaults[j]); + + dev->fdc = device_add(&fdc_at_smc_device); + + dev->uart[0] = device_add_inst(&ns16550_device, 1); + dev->uart[1] = device_add_inst(&ns16550_device, 2); + + dev->gameport = gameport_add(&gameport_sio_device); + + dev->instance = device_get_instance(); + dev->gpio_ldn = it86x1f_models[i].gpio_ldn; + CHIP_ID = it86x1f_models[i].chip_id; + dev->unlock_id = it86x1f_models[i].unlock_id; + io_sethandler(0x279, 1, NULL, NULL, NULL, it86x1f_write_unlock, NULL, NULL, dev); + + it86x1f_reset(dev); + + return dev; +} + +const device_t it8661f_device = { + .name = "ITE IT8661F Super I/O", + .internal_name = "it8661f", + .flags = 0, + .local = ITE_IT8661F, + .init = it86x1f_init, + .close = it86x1f_close, + .reset = NULL, + { .available = NULL }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; + +const device_t it8671f_device = { + .name = "ITE IT8671F Super I/O", + .internal_name = "it8671f", + .flags = 0, + .local = ITE_IT8671F, + .init = it86x1f_init, + .close = it86x1f_close, + .reset = NULL, + { .available = NULL }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; diff --git a/src/sio/sio_pc87306.c b/src/sio/sio_pc87306.c index f5aa086d0..ab7f8597e 100644 --- a/src/sio/sio_pc87306.c +++ b/src/sio/sio_pc87306.c @@ -439,6 +439,7 @@ pc87306_reset_common(void *priv) fdc_reset(dev->fdc); pc87306_gpio_init(dev); nvr_lock_set(0x00, 256, 0, dev->nvr); + nvr_at_handler(0, 0x0070, dev->nvr); nvr_at_handler(1, 0x0070, dev->nvr); nvr_bank_set(0, 0, dev->nvr); nvr_wp_set(0, 0, dev->nvr); diff --git a/src/sio/sio_um8669f.c b/src/sio/sio_um8669f.c index 2643e76f7..61e9abd97 100644 --- a/src/sio/sio_um8669f.c +++ b/src/sio/sio_um8669f.c @@ -18,6 +18,35 @@ * Copyright 2016-2021 Miran Grca. * Copyright 2021 RichardG. */ + +/* + UMC UM8669F non-PnP register definitions + + C0: + [7] Infrared half duplex + [4:3] LPT mode: + 00 SPP + 01 EPP + 10 ECP + 11 ECP + EPP + + C1: + [7] Enable PnP access + [6:0] Always set regardless of PnP access enabled/disabled + + C2: + [6:5] Potentially pin muxing mode: (names from AMI "IR group" setup option) + 00 Reserved + 01 A (no IDE) + 10 B (no IDE) + 11 C + [4:3] Infrared mode: + 00 Reserved + 01 HPSIR + 10 ASKIR + 11 Disabled +*/ + #include #include #include @@ -35,8 +64,10 @@ #include <86box/fdd.h> #include <86box/fdc.h> #include <86box/gameport.h> -#include <86box/sio.h> +#include <86box/hdc.h> #include <86box/isapnp.h> +#include <86box/hdc_ide.h> +#include <86box/sio.h> #include <86box/plat_unused.h> /* Real chips don't have a PnP ROM and instead rely on the BIOS going in blind. @@ -63,7 +94,9 @@ static uint8_t um8669f_pnp_rom[] = { 0x22, 0xfa, 0x1f, /* IRQ 1/3/4/5/6/7/8/9/10/11/12 */ 0x47, 0x00, 0x00, 0x01, 0xf8, 0x03, 0x08, 0x08, /* I/O 0x100-0x3F8, decodes 10-bit, 8-byte alignment, 8 addresses */ - 0x15, 0x41, 0xd0, 0xff, 0xff, 0x00, /* logical device PNPFFFF (dummy to create a gap in LDNs) */ + 0x15, 0x41, 0xd0, 0x06, 0x00, 0x01, /* logical device PNP0600, can participate in boot */ + 0x22, 0xfa, 0x1f, /* IRQ 1/3/4/5/6/7/8/9/10/11/12 */ + 0x47, 0x00, 0x00, 0x01, 0xf8, 0x03, 0x08, 0x08, /* I/O 0x100-0x3F8, decodes 10-bit, 8-byte alignment, 8 addresses */ 0x15, 0x41, 0xd0, 0xb0, 0x2f, 0x01, /* logical device PNPB02F, can participate in boot */ 0x47, 0x00, 0x00, 0x01, 0xf8, 0x03, 0x08, 0x08, /* I/O 0x100-0x3F8, decodes 10-bit, 8-byte alignment, 8 addresses */ @@ -89,7 +122,9 @@ static const isapnp_device_config_t um8669f_pnp_defaults[] = { .io = { { .base = LPT1_ADDR }, }, .irq = { { .irq = LPT1_IRQ }, } }, { - .activate = 0 + .activate = 0, + .io = { { .base = 0x1f0 }, }, + .irq = { { .irq = 14 }, } }, { .activate = 0, .io = { { .base = 0x200 }, } @@ -116,13 +151,13 @@ um8669f_log(const char *fmt, ...) typedef struct um8669f_t { uint8_t locked; - uint8_t cur_reg_108; + uint8_t cur_reg; void *pnp_card; - - uint8_t regs_108[256]; + uint8_t regs[3]; fdc_t *fdc; serial_t *uart[2]; + uint8_t ide; void *gameport; } um8669f_t; @@ -179,6 +214,18 @@ um8669f_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *pri break; + case 4: + if (config->activate && (config->io[0].base != ISAPNP_IO_DISABLED)) + um8669f_log("UM8669F: IDE enabled at port %04X IRQ %d\n", config->io[0].base, config->irq[0].irq); + else + um8669f_log("UM8669F: IDE disabled\n"); + + if (dev->ide < IDE_BUS_MAX) { + config->io[1].base = config->io[0].base + 0x206; /* status port apparently fixed */ + ide_pnp_config_changed(0, config, (void *) (int) dev->ide); + } + break; + case 5: if (config->activate && (config->io[0].base != ISAPNP_IO_DISABLED)) { um8669f_log("UM8669F: Game port enabled at port %04X\n", config->io[0].base); @@ -209,11 +256,11 @@ um8669f_write(uint16_t port, uint8_t val, void *priv) if (val == 0x55) dev->locked = 1; else - dev->cur_reg_108 = val; - } else { - dev->regs_108[dev->cur_reg_108] = val; + dev->cur_reg = val; + } else if ((dev->cur_reg >= 0xc0) && (dev->cur_reg <= 0xc2)) { + dev->regs[dev->cur_reg & 3] = val; - if (dev->cur_reg_108 == 0xc1) { + if (dev->cur_reg == 0xc1) { um8669f_log("UM8669F: ISAPnP %sabled\n", (val & 0x80) ? "en" : "dis"); isapnp_enable_card(dev->pnp_card, (val & 0x80) ? ISAPNP_CARD_FORCE_CONFIG : ISAPNP_CARD_DISABLE); } @@ -229,9 +276,9 @@ um8669f_read(uint16_t port, void *priv) if (!dev->locked) { if (port == 0x108) - ret = dev->cur_reg_108; /* ??? */ - else - ret = dev->regs_108[dev->cur_reg_108]; + ret = dev->cur_reg; /* ??? */ + else if ((dev->cur_reg >= 0xc0) && (dev->cur_reg <= 0xc2)) + ret = dev->regs[dev->cur_reg & 3]; } um8669f_log("UM8669F: read(%04X) = %02X\n", port, ret); @@ -252,6 +299,9 @@ um8669f_reset(um8669f_t *dev) lpt1_remove(); + if (dev->ide < IDE_BUS_MAX) + ide_remove_handlers(dev->ide); + isapnp_enable_card(dev->pnp_card, ISAPNP_CARD_DISABLE); dev->locked = 1; @@ -270,9 +320,9 @@ um8669f_close(void *priv) } static void * -um8669f_init(UNUSED(const device_t *info)) +um8669f_init(const device_t *info) { - um8669f_log("UM8669F: init()\n"); + um8669f_log("UM8669F: init(%02X)\n", info->local); um8669f_t *dev = (um8669f_t *) malloc(sizeof(um8669f_t)); memset(dev, 0, sizeof(um8669f_t)); @@ -286,6 +336,10 @@ um8669f_init(UNUSED(const device_t *info)) dev->uart[0] = device_add_inst(&ns16550_device, 1); dev->uart[1] = device_add_inst(&ns16550_device, 2); + dev->ide = info->local; + if (dev->ide < IDE_BUS_MAX) + device_add(&ide_isa_device); + dev->gameport = gameport_add(&gameport_sio_device); io_sethandler(0x0108, 0x0002, @@ -300,6 +354,20 @@ const device_t um8669f_device = { .name = "UMC UM8669F Super I/O", .internal_name = "um8669f", .flags = 0, + .local = 0xff, + .init = um8669f_init, + .close = um8669f_close, + .reset = NULL, + { .available = NULL }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; + +const device_t um8669f_ide_device = { + .name = "UMC UM8669F Super I/O (With IDE)", + .internal_name = "um8669f_ide", + .flags = 0, .local = 0, .init = um8669f_init, .close = um8669f_close, @@ -309,3 +377,17 @@ const device_t um8669f_device = { .force_redraw = NULL, .config = NULL }; + +const device_t um8669f_ide_sec_device = { + .name = "UMC UM8669F Super I/O (With Secondary IDE)", + .internal_name = "um8669f_ide_sec", + .flags = 0, + .local = 1, + .init = um8669f_init, + .close = um8669f_close, + .reset = NULL, + { .available = NULL }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; diff --git a/src/sound/CMakeLists.txt b/src/sound/CMakeLists.txt index dbcf97dfb..bf4cf61be 100644 --- a/src/sound/CMakeLists.txt +++ b/src/sound/CMakeLists.txt @@ -18,7 +18,7 @@ add_library(snd OBJECT sound.c snd_opl.c snd_opl_nuked.c snd_opl_ymfm.cpp snd_re snd_lpt_dss.c snd_ps1.c snd_adlib.c snd_adlibgold.c snd_ad1848.c snd_audiopci.c snd_azt2316a.c snd_cms.c snd_cmi8x38.c snd_cs423x.c snd_gus.c snd_sb.c snd_sb_dsp.c snd_emu8k.c snd_mpu401.c snd_sn76489.c snd_ssi2001.c snd_wss.c snd_ym7128.c - snd_optimc.c) + snd_optimc.c midi_opl4.c midi_opl4_yrw801.c) if(OPENAL) if(VCPKG_TOOLCHAIN) diff --git a/src/sound/midi.c b/src/sound/midi.c index 41f82801a..c5dbd666f 100644 --- a/src/sound/midi.c +++ b/src/sound/midi.c @@ -100,6 +100,7 @@ static const MIDI_OUT_DEVICE devices[] = { #ifdef USE_RTMIDI { &rtmidi_output_device }, #endif + { &opl4_midi_device }, { NULL } // clang-format on }; diff --git a/src/sound/midi_fluidsynth.c b/src/sound/midi_fluidsynth.c index 08aefe16c..c1b9956d0 100644 --- a/src/sound/midi_fluidsynth.c +++ b/src/sound/midi_fluidsynth.c @@ -1,32 +1,31 @@ /* some code borrowed from scummvm */ -#ifdef USE_FLUIDSYNTH -# include -# include -# include -# include -# include -# ifdef __unix__ -# include -# endif -# define FLUIDSYNTH_NOT_A_DLL -# include +#include +#include +#include +#include +#include +#ifdef __unix__ +# include +#endif +#define FLUIDSYNTH_NOT_A_DLL +#include -# include <86box/86box.h> -# include <86box/config.h> -# include <86box/device.h> -# include <86box/midi.h> -# include <86box/thread.h> -# include <86box/sound.h> -# include <86box/plat_unused.h> +#include <86box/86box.h> +#include <86box/config.h> +#include <86box/device.h> +#include <86box/midi.h> +#include <86box/thread.h> +#include <86box/sound.h> +#include <86box/plat_unused.h> -# define RENDER_RATE 100 -# define BUFFER_SEGMENTS 10 +#define RENDER_RATE 100 +#define BUFFER_SEGMENTS 10 /* Check the FluidSynth version to determine wheteher to use the older reverb/chorus control functions that were deprecated in 2.2.0, or their newer replacements */ -# if (FLUIDSYNTH_VERSION_MAJOR < 2) || ((FLUIDSYNTH_VERSION_MAJOR == 2) && (FLUIDSYNTH_VERSION_MINOR < 2)) -# define USE_OLD_FLUIDSYNTH_API -# endif +#if (FLUIDSYNTH_VERSION_MAJOR < 2) || ((FLUIDSYNTH_VERSION_MAJOR == 2) && (FLUIDSYNTH_VERSION_MINOR < 2)) +# define USE_OLD_FLUIDSYNTH_API +#endif extern void givealbuffer_midi(void *buf, uint32_t size); extern void al_set_midi(int freq, int buf_size); @@ -167,19 +166,19 @@ fluidsynth_init(UNUSED(const device_t *info)) data->synth = new_fluid_synth(data->settings); const char *sound_font = device_get_config_string("sound_font"); -# ifdef __unix__ +#ifdef __unix__ if (!sound_font || sound_font[0] == 0) sound_font = (access("/usr/share/sounds/sf2/FluidR3_GM.sf2", F_OK) == 0 ? "/usr/share/sounds/sf2/FluidR3_GM.sf2" : (access("/usr/share/soundfonts/default.sf2", F_OK) == 0 ? "/usr/share/soundfonts/default.sf2" : "")); -# endif +#endif data->sound_font = fluid_synth_sfload(data->synth, sound_font, 1); if (device_get_config_int("chorus")) { -# ifndef USE_OLD_FLUIDSYNTH_API +#ifndef USE_OLD_FLUIDSYNTH_API fluid_synth_chorus_on(data->synth, -1, 1); -# else +#else fluid_synth_set_chorus_on(data->synth, 1); -# endif +#endif int chorus_voices = device_get_config_int("chorus_voices"); double chorus_level = device_get_config_int("chorus_level") / 100.0; @@ -192,48 +191,48 @@ fluidsynth_init(UNUSED(const device_t *info)) else chorus_waveform = FLUID_CHORUS_MOD_TRIANGLE; -# ifndef USE_OLD_FLUIDSYNTH_API +#ifndef USE_OLD_FLUIDSYNTH_API fluid_synth_set_chorus_group_nr(data->synth, -1, chorus_voices); fluid_synth_set_chorus_group_level(data->synth, -1, chorus_level); fluid_synth_set_chorus_group_speed(data->synth, -1, chorus_speed); fluid_synth_set_chorus_group_depth(data->synth, -1, chorus_depth); fluid_synth_set_chorus_group_type(data->synth, -1, chorus_waveform); -# else +#else fluid_synth_set_chorus(data->synth, chorus_voices, chorus_level, chorus_speed, chorus_depth, chorus_waveform); -# endif +#endif } else -# ifndef USE_OLD_FLUIDSYNTH_API +#ifndef USE_OLD_FLUIDSYNTH_API fluid_synth_chorus_on(data->synth, -1, 0); -# else +#else fluid_synth_set_chorus_on(data->synth, 0); -# endif +#endif if (device_get_config_int("reverb")) { -# ifndef USE_OLD_FLUIDSYNTH_API +#ifndef USE_OLD_FLUIDSYNTH_API fluid_synth_reverb_on(data->synth, -1, 1); -# else +#else fluid_synth_set_reverb_on(data->synth, 1); -# endif +#endif double reverb_room_size = device_get_config_int("reverb_room_size") / 100.0; double reverb_damping = device_get_config_int("reverb_damping") / 100.0; double reverb_width = device_get_config_int("reverb_width") / 10.0; double reverb_level = device_get_config_int("reverb_level") / 100.0; -# ifndef USE_OLD_FLUIDSYNTH_API +#ifndef USE_OLD_FLUIDSYNTH_API fluid_synth_set_reverb_group_roomsize(data->synth, -1, reverb_room_size); fluid_synth_set_reverb_group_damp(data->synth, -1, reverb_damping); fluid_synth_set_reverb_group_width(data->synth, -1, reverb_width); fluid_synth_set_reverb_group_level(data->synth, -1, reverb_level); -# else +#else fluid_synth_set_reverb(data->synth, reverb_room_size, reverb_damping, reverb_width, reverb_level); -# endif +#endif } else -# ifndef USE_OLD_FLUIDSYNTH_API +#ifndef USE_OLD_FLUIDSYNTH_API fluid_synth_reverb_on(data->synth, -1, 0); -# else +#else fluid_synth_set_reverb_on(data->synth, 0); -# endif +#endif int interpolation = device_get_config_int("interpolation"); int fs_interpolation = FLUID_INTERP_4THORDER; @@ -499,4 +498,3 @@ const device_t fluidsynth_device = { .config = fluidsynth_config }; -#endif /*USE_FLUIDSYNTH*/ diff --git a/src/sound/midi_opl4.c b/src/sound/midi_opl4.c new file mode 100644 index 000000000..9708db150 --- /dev/null +++ b/src/sound/midi_opl4.c @@ -0,0 +1,731 @@ +// Based off ROBOPLAY's OPL4 MID player code, with some fixes and modifications to make it work well. + +#include +#include +#include +#include +#include +#include +#include + +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> +#include <86box/midi.h> +#include <86box/plat.h> +#include <86box/thread.h> +#include <86box/rom.h> +#include <86box/sound.h> +#include <86box/ui.h> +#include <86box/snd_opl.h> +#include <86box/opl4_defines.h> + +#include "yrw801.h" + +#define NR_OF_MIDI_CHANNELS 16 +#define NR_OF_WAVE_CHANNELS 24 + +#define DRUM_CHANNEL 9 + +typedef struct +{ + uint8_t instrument; + uint8_t panpot; + uint8_t vibrato; + bool drum_channel; +} MIDI_CHANNEL_DATA; + +typedef struct +{ + bool is_active; + uint64_t activated; + + uint8_t number; + MIDI_CHANNEL_DATA *midi_channel; + uint8_t note; + uint8_t velocity; + const YRW801_WAVE_DATA *wave_data; + uint8_t level_direct; + uint8_t reg_f_number; + uint8_t reg_misc; + uint8_t reg_lfo_vibrato; +} VOICE_DATA; + +static const int16_t g_wave_pitch_map[0x600] = { + 0x000, 0x000, 0x001, 0x001, 0x002, 0x002, 0x003, 0x003, + 0x004, 0x004, 0x005, 0x005, 0x006, 0x006, 0x006, 0x007, + 0x007, 0x008, 0x008, 0x009, 0x009, 0x00a, 0x00a, 0x00b, + 0x00b, 0x00c, 0x00c, 0x00d, 0x00d, 0x00d, 0x00e, 0x00e, + 0x00f, 0x00f, 0x010, 0x010, 0x011, 0x011, 0x012, 0x012, + 0x013, 0x013, 0x014, 0x014, 0x015, 0x015, 0x015, 0x016, + 0x016, 0x017, 0x017, 0x018, 0x018, 0x019, 0x019, 0x01a, + 0x01a, 0x01b, 0x01b, 0x01c, 0x01c, 0x01d, 0x01d, 0x01e, + 0x01e, 0x01e, 0x01f, 0x01f, 0x020, 0x020, 0x021, 0x021, + 0x022, 0x022, 0x023, 0x023, 0x024, 0x024, 0x025, 0x025, + 0x026, 0x026, 0x027, 0x027, 0x028, 0x028, 0x029, 0x029, + 0x029, 0x02a, 0x02a, 0x02b, 0x02b, 0x02c, 0x02c, 0x02d, + 0x02d, 0x02e, 0x02e, 0x02f, 0x02f, 0x030, 0x030, 0x031, + 0x031, 0x032, 0x032, 0x033, 0x033, 0x034, 0x034, 0x035, + 0x035, 0x036, 0x036, 0x037, 0x037, 0x038, 0x038, 0x038, + 0x039, 0x039, 0x03a, 0x03a, 0x03b, 0x03b, 0x03c, 0x03c, + 0x03d, 0x03d, 0x03e, 0x03e, 0x03f, 0x03f, 0x040, 0x040, + 0x041, 0x041, 0x042, 0x042, 0x043, 0x043, 0x044, 0x044, + 0x045, 0x045, 0x046, 0x046, 0x047, 0x047, 0x048, 0x048, + 0x049, 0x049, 0x04a, 0x04a, 0x04b, 0x04b, 0x04c, 0x04c, + 0x04d, 0x04d, 0x04e, 0x04e, 0x04f, 0x04f, 0x050, 0x050, + 0x051, 0x051, 0x052, 0x052, 0x053, 0x053, 0x054, 0x054, + 0x055, 0x055, 0x056, 0x056, 0x057, 0x057, 0x058, 0x058, + 0x059, 0x059, 0x05a, 0x05a, 0x05b, 0x05b, 0x05c, 0x05c, + 0x05d, 0x05d, 0x05e, 0x05e, 0x05f, 0x05f, 0x060, 0x060, + 0x061, 0x061, 0x062, 0x062, 0x063, 0x063, 0x064, 0x064, + 0x065, 0x065, 0x066, 0x066, 0x067, 0x067, 0x068, 0x068, + 0x069, 0x069, 0x06a, 0x06a, 0x06b, 0x06b, 0x06c, 0x06c, + 0x06d, 0x06d, 0x06e, 0x06e, 0x06f, 0x06f, 0x070, 0x071, + 0x071, 0x072, 0x072, 0x073, 0x073, 0x074, 0x074, 0x075, + 0x075, 0x076, 0x076, 0x077, 0x077, 0x078, 0x078, 0x079, + 0x079, 0x07a, 0x07a, 0x07b, 0x07b, 0x07c, 0x07c, 0x07d, + 0x07d, 0x07e, 0x07e, 0x07f, 0x07f, 0x080, 0x081, 0x081, + 0x082, 0x082, 0x083, 0x083, 0x084, 0x084, 0x085, 0x085, + 0x086, 0x086, 0x087, 0x087, 0x088, 0x088, 0x089, 0x089, + 0x08a, 0x08a, 0x08b, 0x08b, 0x08c, 0x08d, 0x08d, 0x08e, + 0x08e, 0x08f, 0x08f, 0x090, 0x090, 0x091, 0x091, 0x092, + 0x092, 0x093, 0x093, 0x094, 0x094, 0x095, 0x096, 0x096, + 0x097, 0x097, 0x098, 0x098, 0x099, 0x099, 0x09a, 0x09a, + 0x09b, 0x09b, 0x09c, 0x09c, 0x09d, 0x09d, 0x09e, 0x09f, + 0x09f, 0x0a0, 0x0a0, 0x0a1, 0x0a1, 0x0a2, 0x0a2, 0x0a3, + 0x0a3, 0x0a4, 0x0a4, 0x0a5, 0x0a6, 0x0a6, 0x0a7, 0x0a7, + 0x0a8, 0x0a8, 0x0a9, 0x0a9, 0x0aa, 0x0aa, 0x0ab, 0x0ab, + 0x0ac, 0x0ad, 0x0ad, 0x0ae, 0x0ae, 0x0af, 0x0af, 0x0b0, + 0x0b0, 0x0b1, 0x0b1, 0x0b2, 0x0b2, 0x0b3, 0x0b4, 0x0b4, + 0x0b5, 0x0b5, 0x0b6, 0x0b6, 0x0b7, 0x0b7, 0x0b8, 0x0b8, + 0x0b9, 0x0ba, 0x0ba, 0x0bb, 0x0bb, 0x0bc, 0x0bc, 0x0bd, + 0x0bd, 0x0be, 0x0be, 0x0bf, 0x0c0, 0x0c0, 0x0c1, 0x0c1, + 0x0c2, 0x0c2, 0x0c3, 0x0c3, 0x0c4, 0x0c4, 0x0c5, 0x0c6, + 0x0c6, 0x0c7, 0x0c7, 0x0c8, 0x0c8, 0x0c9, 0x0c9, 0x0ca, + 0x0cb, 0x0cb, 0x0cc, 0x0cc, 0x0cd, 0x0cd, 0x0ce, 0x0ce, + 0x0cf, 0x0d0, 0x0d0, 0x0d1, 0x0d1, 0x0d2, 0x0d2, 0x0d3, + 0x0d3, 0x0d4, 0x0d5, 0x0d5, 0x0d6, 0x0d6, 0x0d7, 0x0d7, + 0x0d8, 0x0d8, 0x0d9, 0x0da, 0x0da, 0x0db, 0x0db, 0x0dc, + 0x0dc, 0x0dd, 0x0de, 0x0de, 0x0df, 0x0df, 0x0e0, 0x0e0, + 0x0e1, 0x0e1, 0x0e2, 0x0e3, 0x0e3, 0x0e4, 0x0e4, 0x0e5, + 0x0e5, 0x0e6, 0x0e7, 0x0e7, 0x0e8, 0x0e8, 0x0e9, 0x0e9, + 0x0ea, 0x0eb, 0x0eb, 0x0ec, 0x0ec, 0x0ed, 0x0ed, 0x0ee, + 0x0ef, 0x0ef, 0x0f0, 0x0f0, 0x0f1, 0x0f1, 0x0f2, 0x0f3, + 0x0f3, 0x0f4, 0x0f4, 0x0f5, 0x0f5, 0x0f6, 0x0f7, 0x0f7, + 0x0f8, 0x0f8, 0x0f9, 0x0f9, 0x0fa, 0x0fb, 0x0fb, 0x0fc, + 0x0fc, 0x0fd, 0x0fd, 0x0fe, 0x0ff, 0x0ff, 0x100, 0x100, + 0x101, 0x101, 0x102, 0x103, 0x103, 0x104, 0x104, 0x105, + 0x106, 0x106, 0x107, 0x107, 0x108, 0x108, 0x109, 0x10a, + 0x10a, 0x10b, 0x10b, 0x10c, 0x10c, 0x10d, 0x10e, 0x10e, + 0x10f, 0x10f, 0x110, 0x111, 0x111, 0x112, 0x112, 0x113, + 0x114, 0x114, 0x115, 0x115, 0x116, 0x116, 0x117, 0x118, + 0x118, 0x119, 0x119, 0x11a, 0x11b, 0x11b, 0x11c, 0x11c, + 0x11d, 0x11e, 0x11e, 0x11f, 0x11f, 0x120, 0x120, 0x121, + 0x122, 0x122, 0x123, 0x123, 0x124, 0x125, 0x125, 0x126, + 0x126, 0x127, 0x128, 0x128, 0x129, 0x129, 0x12a, 0x12b, + 0x12b, 0x12c, 0x12c, 0x12d, 0x12e, 0x12e, 0x12f, 0x12f, + 0x130, 0x131, 0x131, 0x132, 0x132, 0x133, 0x134, 0x134, + 0x135, 0x135, 0x136, 0x137, 0x137, 0x138, 0x138, 0x139, + 0x13a, 0x13a, 0x13b, 0x13b, 0x13c, 0x13d, 0x13d, 0x13e, + 0x13e, 0x13f, 0x140, 0x140, 0x141, 0x141, 0x142, 0x143, + 0x143, 0x144, 0x144, 0x145, 0x146, 0x146, 0x147, 0x148, + 0x148, 0x149, 0x149, 0x14a, 0x14b, 0x14b, 0x14c, 0x14c, + 0x14d, 0x14e, 0x14e, 0x14f, 0x14f, 0x150, 0x151, 0x151, + 0x152, 0x153, 0x153, 0x154, 0x154, 0x155, 0x156, 0x156, + 0x157, 0x157, 0x158, 0x159, 0x159, 0x15a, 0x15b, 0x15b, + 0x15c, 0x15c, 0x15d, 0x15e, 0x15e, 0x15f, 0x160, 0x160, + 0x161, 0x161, 0x162, 0x163, 0x163, 0x164, 0x165, 0x165, + 0x166, 0x166, 0x167, 0x168, 0x168, 0x169, 0x16a, 0x16a, + 0x16b, 0x16b, 0x16c, 0x16d, 0x16d, 0x16e, 0x16f, 0x16f, + 0x170, 0x170, 0x171, 0x172, 0x172, 0x173, 0x174, 0x174, + 0x175, 0x175, 0x176, 0x177, 0x177, 0x178, 0x179, 0x179, + 0x17a, 0x17a, 0x17b, 0x17c, 0x17c, 0x17d, 0x17e, 0x17e, + 0x17f, 0x180, 0x180, 0x181, 0x181, 0x182, 0x183, 0x183, + 0x184, 0x185, 0x185, 0x186, 0x187, 0x187, 0x188, 0x188, + 0x189, 0x18a, 0x18a, 0x18b, 0x18c, 0x18c, 0x18d, 0x18e, + 0x18e, 0x18f, 0x190, 0x190, 0x191, 0x191, 0x192, 0x193, + 0x193, 0x194, 0x195, 0x195, 0x196, 0x197, 0x197, 0x198, + 0x199, 0x199, 0x19a, 0x19a, 0x19b, 0x19c, 0x19c, 0x19d, + 0x19e, 0x19e, 0x19f, 0x1a0, 0x1a0, 0x1a1, 0x1a2, 0x1a2, + 0x1a3, 0x1a4, 0x1a4, 0x1a5, 0x1a6, 0x1a6, 0x1a7, 0x1a8, + 0x1a8, 0x1a9, 0x1a9, 0x1aa, 0x1ab, 0x1ab, 0x1ac, 0x1ad, + 0x1ad, 0x1ae, 0x1af, 0x1af, 0x1b0, 0x1b1, 0x1b1, 0x1b2, + 0x1b3, 0x1b3, 0x1b4, 0x1b5, 0x1b5, 0x1b6, 0x1b7, 0x1b7, + 0x1b8, 0x1b9, 0x1b9, 0x1ba, 0x1bb, 0x1bb, 0x1bc, 0x1bd, + 0x1bd, 0x1be, 0x1bf, 0x1bf, 0x1c0, 0x1c1, 0x1c1, 0x1c2, + 0x1c3, 0x1c3, 0x1c4, 0x1c5, 0x1c5, 0x1c6, 0x1c7, 0x1c7, + 0x1c8, 0x1c9, 0x1c9, 0x1ca, 0x1cb, 0x1cb, 0x1cc, 0x1cd, + 0x1cd, 0x1ce, 0x1cf, 0x1cf, 0x1d0, 0x1d1, 0x1d1, 0x1d2, + 0x1d3, 0x1d3, 0x1d4, 0x1d5, 0x1d5, 0x1d6, 0x1d7, 0x1d7, + 0x1d8, 0x1d9, 0x1d9, 0x1da, 0x1db, 0x1db, 0x1dc, 0x1dd, + 0x1dd, 0x1de, 0x1df, 0x1df, 0x1e0, 0x1e1, 0x1e1, 0x1e2, + 0x1e3, 0x1e4, 0x1e4, 0x1e5, 0x1e6, 0x1e6, 0x1e7, 0x1e8, + 0x1e8, 0x1e9, 0x1ea, 0x1ea, 0x1eb, 0x1ec, 0x1ec, 0x1ed, + 0x1ee, 0x1ee, 0x1ef, 0x1f0, 0x1f0, 0x1f1, 0x1f2, 0x1f3, + 0x1f3, 0x1f4, 0x1f5, 0x1f5, 0x1f6, 0x1f7, 0x1f7, 0x1f8, + 0x1f9, 0x1f9, 0x1fa, 0x1fb, 0x1fb, 0x1fc, 0x1fd, 0x1fe, + 0x1fe, 0x1ff, 0x200, 0x200, 0x201, 0x202, 0x202, 0x203, + 0x204, 0x205, 0x205, 0x206, 0x207, 0x207, 0x208, 0x209, + 0x209, 0x20a, 0x20b, 0x20b, 0x20c, 0x20d, 0x20e, 0x20e, + 0x20f, 0x210, 0x210, 0x211, 0x212, 0x212, 0x213, 0x214, + 0x215, 0x215, 0x216, 0x217, 0x217, 0x218, 0x219, 0x21a, + 0x21a, 0x21b, 0x21c, 0x21c, 0x21d, 0x21e, 0x21e, 0x21f, + 0x220, 0x221, 0x221, 0x222, 0x223, 0x223, 0x224, 0x225, + 0x226, 0x226, 0x227, 0x228, 0x228, 0x229, 0x22a, 0x22b, + 0x22b, 0x22c, 0x22d, 0x22d, 0x22e, 0x22f, 0x230, 0x230, + 0x231, 0x232, 0x232, 0x233, 0x234, 0x235, 0x235, 0x236, + 0x237, 0x237, 0x238, 0x239, 0x23a, 0x23a, 0x23b, 0x23c, + 0x23c, 0x23d, 0x23e, 0x23f, 0x23f, 0x240, 0x241, 0x241, + 0x242, 0x243, 0x244, 0x244, 0x245, 0x246, 0x247, 0x247, + 0x248, 0x249, 0x249, 0x24a, 0x24b, 0x24c, 0x24c, 0x24d, + 0x24e, 0x24f, 0x24f, 0x250, 0x251, 0x251, 0x252, 0x253, + 0x254, 0x254, 0x255, 0x256, 0x257, 0x257, 0x258, 0x259, + 0x259, 0x25a, 0x25b, 0x25c, 0x25c, 0x25d, 0x25e, 0x25f, + 0x25f, 0x260, 0x261, 0x262, 0x262, 0x263, 0x264, 0x265, + 0x265, 0x266, 0x267, 0x267, 0x268, 0x269, 0x26a, 0x26a, + 0x26b, 0x26c, 0x26d, 0x26d, 0x26e, 0x26f, 0x270, 0x270, + 0x271, 0x272, 0x273, 0x273, 0x274, 0x275, 0x276, 0x276, + 0x277, 0x278, 0x279, 0x279, 0x27a, 0x27b, 0x27c, 0x27c, + 0x27d, 0x27e, 0x27f, 0x27f, 0x280, 0x281, 0x282, 0x282, + 0x283, 0x284, 0x285, 0x285, 0x286, 0x287, 0x288, 0x288, + 0x289, 0x28a, 0x28b, 0x28b, 0x28c, 0x28d, 0x28e, 0x28e, + 0x28f, 0x290, 0x291, 0x291, 0x292, 0x293, 0x294, 0x294, + 0x295, 0x296, 0x297, 0x298, 0x298, 0x299, 0x29a, 0x29b, + 0x29b, 0x29c, 0x29d, 0x29e, 0x29e, 0x29f, 0x2a0, 0x2a1, + 0x2a1, 0x2a2, 0x2a3, 0x2a4, 0x2a5, 0x2a5, 0x2a6, 0x2a7, + 0x2a8, 0x2a8, 0x2a9, 0x2aa, 0x2ab, 0x2ab, 0x2ac, 0x2ad, + 0x2ae, 0x2af, 0x2af, 0x2b0, 0x2b1, 0x2b2, 0x2b2, 0x2b3, + 0x2b4, 0x2b5, 0x2b5, 0x2b6, 0x2b7, 0x2b8, 0x2b9, 0x2b9, + 0x2ba, 0x2bb, 0x2bc, 0x2bc, 0x2bd, 0x2be, 0x2bf, 0x2c0, + 0x2c0, 0x2c1, 0x2c2, 0x2c3, 0x2c4, 0x2c4, 0x2c5, 0x2c6, + 0x2c7, 0x2c7, 0x2c8, 0x2c9, 0x2ca, 0x2cb, 0x2cb, 0x2cc, + 0x2cd, 0x2ce, 0x2ce, 0x2cf, 0x2d0, 0x2d1, 0x2d2, 0x2d2, + 0x2d3, 0x2d4, 0x2d5, 0x2d6, 0x2d6, 0x2d7, 0x2d8, 0x2d9, + 0x2da, 0x2da, 0x2db, 0x2dc, 0x2dd, 0x2dd, 0x2de, 0x2df, + 0x2e0, 0x2e1, 0x2e1, 0x2e2, 0x2e3, 0x2e4, 0x2e5, 0x2e5, + 0x2e6, 0x2e7, 0x2e8, 0x2e9, 0x2e9, 0x2ea, 0x2eb, 0x2ec, + 0x2ed, 0x2ed, 0x2ee, 0x2ef, 0x2f0, 0x2f1, 0x2f1, 0x2f2, + 0x2f3, 0x2f4, 0x2f5, 0x2f5, 0x2f6, 0x2f7, 0x2f8, 0x2f9, + 0x2f9, 0x2fa, 0x2fb, 0x2fc, 0x2fd, 0x2fd, 0x2fe, 0x2ff, + 0x300, 0x301, 0x302, 0x302, 0x303, 0x304, 0x305, 0x306, + 0x306, 0x307, 0x308, 0x309, 0x30a, 0x30a, 0x30b, 0x30c, + 0x30d, 0x30e, 0x30f, 0x30f, 0x310, 0x311, 0x312, 0x313, + 0x313, 0x314, 0x315, 0x316, 0x317, 0x318, 0x318, 0x319, + 0x31a, 0x31b, 0x31c, 0x31c, 0x31d, 0x31e, 0x31f, 0x320, + 0x321, 0x321, 0x322, 0x323, 0x324, 0x325, 0x326, 0x326, + 0x327, 0x328, 0x329, 0x32a, 0x32a, 0x32b, 0x32c, 0x32d, + 0x32e, 0x32f, 0x32f, 0x330, 0x331, 0x332, 0x333, 0x334, + 0x334, 0x335, 0x336, 0x337, 0x338, 0x339, 0x339, 0x33a, + 0x33b, 0x33c, 0x33d, 0x33e, 0x33e, 0x33f, 0x340, 0x341, + 0x342, 0x343, 0x343, 0x344, 0x345, 0x346, 0x347, 0x348, + 0x349, 0x349, 0x34a, 0x34b, 0x34c, 0x34d, 0x34e, 0x34e, + 0x34f, 0x350, 0x351, 0x352, 0x353, 0x353, 0x354, 0x355, + 0x356, 0x357, 0x358, 0x359, 0x359, 0x35a, 0x35b, 0x35c, + 0x35d, 0x35e, 0x35f, 0x35f, 0x360, 0x361, 0x362, 0x363, + 0x364, 0x364, 0x365, 0x366, 0x367, 0x368, 0x369, 0x36a, + 0x36a, 0x36b, 0x36c, 0x36d, 0x36e, 0x36f, 0x370, 0x370, + 0x371, 0x372, 0x373, 0x374, 0x375, 0x376, 0x377, 0x377, + 0x378, 0x379, 0x37a, 0x37b, 0x37c, 0x37d, 0x37d, 0x37e, + 0x37f, 0x380, 0x381, 0x382, 0x383, 0x383, 0x384, 0x385, + 0x386, 0x387, 0x388, 0x389, 0x38a, 0x38a, 0x38b, 0x38c, + 0x38d, 0x38e, 0x38f, 0x390, 0x391, 0x391, 0x392, 0x393, + 0x394, 0x395, 0x396, 0x397, 0x398, 0x398, 0x399, 0x39a, + 0x39b, 0x39c, 0x39d, 0x39e, 0x39f, 0x39f, 0x3a0, 0x3a1, + 0x3a2, 0x3a3, 0x3a4, 0x3a5, 0x3a6, 0x3a7, 0x3a7, 0x3a8, + 0x3a9, 0x3aa, 0x3ab, 0x3ac, 0x3ad, 0x3ae, 0x3ae, 0x3af, + 0x3b0, 0x3b1, 0x3b2, 0x3b3, 0x3b4, 0x3b5, 0x3b6, 0x3b6, + 0x3b7, 0x3b8, 0x3b9, 0x3ba, 0x3bb, 0x3bc, 0x3bd, 0x3be, + 0x3bf, 0x3bf, 0x3c0, 0x3c1, 0x3c2, 0x3c3, 0x3c4, 0x3c5, + 0x3c6, 0x3c7, 0x3c7, 0x3c8, 0x3c9, 0x3ca, 0x3cb, 0x3cc, + 0x3cd, 0x3ce, 0x3cf, 0x3d0, 0x3d1, 0x3d1, 0x3d2, 0x3d3, + 0x3d4, 0x3d5, 0x3d6, 0x3d7, 0x3d8, 0x3d9, 0x3da, 0x3da, + 0x3db, 0x3dc, 0x3dd, 0x3de, 0x3df, 0x3e0, 0x3e1, 0x3e2, + 0x3e3, 0x3e4, 0x3e4, 0x3e5, 0x3e6, 0x3e7, 0x3e8, 0x3e9, + 0x3ea, 0x3eb, 0x3ec, 0x3ed, 0x3ee, 0x3ef, 0x3ef, 0x3f0, + 0x3f1, 0x3f2, 0x3f3, 0x3f4, 0x3f5, 0x3f6, 0x3f7, 0x3f8, + 0x3f9, 0x3fa, 0x3fa, 0x3fb, 0x3fc, 0x3fd, 0x3fe, 0x3ff +}; + +/* + * Attenuation according to GM recommendations, in -0.375 dB units. + * table[v] = 40 * log(v / 127) / -0.375 + */ +static const uint8_t g_volume_table[128] = { + 255, 224, 192, 173, 160, 150, 141, 134, + 128, 122, 117, 113, 109, 105, 102, 99, + 96, 93, 90, 88, 85, 83, 81, 79, + 77, 75, 73, 71, 70, 68, 67, 65, + 64, 62, 61, 59, 58, 57, 56, 54, + 53, 52, 51, 50, 49, 48, 47, 46, + 45, 44, 43, 42, 41, 40, 39, 39, + 38, 37, 36, 35, 34, 34, 33, 32, + 31, 31, 30, 29, 29, 28, 27, 27, + 26, 25, 25, 24, 24, 23, 22, 22, + 21, 21, 20, 19, 19, 18, 18, 17, + 17, 16, 16, 15, 15, 14, 14, 13, + 13, 12, 12, 11, 11, 10, 10, 9, + 9, 9, 8, 8, 7, 7, 6, 6, + 6, 5, 5, 4, 4, 4, 3, 3, + 2, 2, 2, 1, 1, 0, 0, 0 +}; + +#define BUFFER_SEGMENTS 10 +#define RENDER_RATE (48000 / 100) + +typedef struct opl4_midi { + fm_drv_t opl4; + MIDI_CHANNEL_DATA midi_channel_data[16]; + VOICE_DATA voice_data[24]; + int16_t buffer[(48000 / 100) * 2 * BUFFER_SEGMENTS]; + float buffer_float[(48000 / 100) * 2 * BUFFER_SEGMENTS]; + uint32_t midi_pos; + bool on; + atomic_bool gen_in_progress; + thread_t *thread; + event_t *wait_event; +} opl4_midi_t; + +static opl4_midi_t *opl4_midi_cur; + +static void +opl4_write_wave_register(const uint8_t reg, const uint8_t value, opl4_midi_t *opl4_midi) +{ + opl4_midi->opl4.write(0x380, reg, opl4_midi->opl4.priv); + opl4_midi->opl4.write(0x381, value, opl4_midi->opl4.priv); +} + +VOICE_DATA * +get_voice(const YRW801_WAVE_DATA *wave_data, opl4_midi_t *opl4_midi) +{ + VOICE_DATA *free_voice = &opl4_midi->voice_data[0]; + VOICE_DATA *oldest_voice = &opl4_midi->voice_data[0]; + for (uint8_t voice = 0; voice < 24; voice++) { + if (opl4_midi_cur->voice_data[voice].is_active) { + if (opl4_midi_cur->voice_data[voice].activated < oldest_voice->activated) + oldest_voice = &opl4_midi_cur->voice_data[voice]; + } else { + if (opl4_midi_cur->voice_data[voice].wave_data == wave_data) { + free_voice = &opl4_midi_cur->voice_data[voice]; + break; + } + + if (opl4_midi_cur->voice_data[voice].activated < free_voice->activated) { + free_voice = &opl4_midi_cur->voice_data[voice]; + break; + } + } + } + + /* If no free voice is found, look for one with the same instrument */ + if (free_voice->is_active) { + for (uint8_t voice = 0; voice < 24; voice++) { + if (opl4_midi_cur->voice_data[voice].is_active + && opl4_midi_cur->voice_data[voice].wave_data == wave_data) { + free_voice = &opl4_midi_cur->voice_data[voice]; + free_voice->is_active = 0; + free_voice->activated = 0; + + free_voice->reg_misc &= ~OPL4_KEY_ON_BIT; + opl4_write_wave_register(OPL4_REG_MISC + free_voice->number, free_voice->reg_misc, opl4_midi); + return free_voice; + } + } + } + + /* If still no free voice found, deactivate the 'oldest' */ + if (free_voice->is_active) { + free_voice = oldest_voice; + free_voice->is_active = 0; + free_voice->activated = 0; + + free_voice->reg_misc &= ~OPL4_KEY_ON_BIT; + opl4_write_wave_register(OPL4_REG_MISC + free_voice->number, free_voice->reg_misc, opl4_midi); + } + + return free_voice; +} + +static void +update_pan(VOICE_DATA *voice, opl4_midi_t *opl4_midi) +{ + int8_t pan = voice->wave_data->panpot; + + if (!voice->midi_channel->drum_channel) + pan += voice->midi_channel->panpot; + if (pan < -7) + pan = -7; + else if (pan > 7) + pan = 7; + + voice->reg_misc = (voice->reg_misc & ~OPL4_PAN_POT_MASK) | (pan & OPL4_PAN_POT_MASK); + opl4_write_wave_register(OPL4_REG_MISC + voice->number, voice->reg_misc, opl4_midi); +} + +void +update_pitch(VOICE_DATA *voice, uint16_t pitch_bend, opl4_midi_t *opl4_midi) +{ + int32_t pitch = voice->midi_channel->drum_channel ? 0 : (voice->note - 60) * 128; + pitch = pitch * (int) voice->wave_data->key_scaling / 100; + pitch = pitch + 7680; + pitch += voice->wave_data->pitch_offset; + pitch += pitch_bend * 256 / 0x2000; + if (pitch < 0) + pitch = 0; + else if (pitch > 0x5FFF) + pitch = 0x5FFF; + + int8_t octave = pitch / 0x600 - 8; + uint16_t fnumber = g_wave_pitch_map[pitch % 0x600]; + + opl4_write_wave_register(OPL4_REG_OCTAVE + voice->number, (octave << 4) | ((fnumber >> 7) & 0x07), opl4_midi); + voice->reg_f_number = (voice->reg_f_number & OPL4_TONE_NUMBER_BIT8) | ((fnumber << 1) & OPL4_F_NUMBER_LOW_MASK); + opl4_write_wave_register(OPL4_REG_F_NUMBER + voice->number, voice->reg_f_number, opl4_midi); +} + +void +update_volume(VOICE_DATA *voice, opl4_midi_t *opl4_midi) +{ + int16_t att = voice->wave_data->tone_attenuate; + + att += g_volume_table[voice->velocity]; + att = 0x7F - (0x7F - att) * (voice->wave_data->volume_factor) / 0xFE; + att -= 16; + if (att < 0) + att = 0; + else if (att > 0x7E) + att = 0x7E; + + opl4_write_wave_register(OPL4_REG_LEVEL + voice->number, (att << 1) | voice->level_direct, opl4_midi); + voice->level_direct = 0; +} + +void +note_off(uint8_t note, uint8_t velocity, MIDI_CHANNEL_DATA *midi_channel, opl4_midi_t *opl4_midi) +{ + /* Velocity not used */ + (void) velocity; + + while (opl4_midi->gen_in_progress) { } + for (uint8_t i = 0; i < 24; i++) { + VOICE_DATA *voice = &opl4_midi->voice_data[i]; + if (voice->is_active && voice->midi_channel == midi_channel && voice->note == note) { + voice->is_active = false; + voice->activated = 0; + + voice->reg_misc &= ~OPL4_KEY_ON_BIT; + opl4_write_wave_register(OPL4_REG_MISC + voice->number, voice->reg_misc, opl4_midi); + } + } +} + +void +update_vibrato_depth(VOICE_DATA *voice, opl4_midi_t *opl4_midi) +{ + uint16_t depth; + + depth = (7 - voice->wave_data->vibrato) * (voice->midi_channel->vibrato & 0x7F); + depth = (depth >> 7) + voice->wave_data->vibrato; + voice->reg_lfo_vibrato &= ~OPL4_VIBRATO_DEPTH_MASK; + voice->reg_lfo_vibrato |= depth & OPL4_VIBRATO_DEPTH_MASK; + opl4_write_wave_register(OPL4_REG_LFO_VIBRATO + voice->number, voice->reg_lfo_vibrato, opl4_midi); +} + +void +note_on(uint8_t note, uint8_t velocity, MIDI_CHANNEL_DATA *midi_channel, opl4_midi_t *opl4_midi) +{ + const YRW801_REGION_DATA_PTR *region_ptr = &snd_yrw801_regions[0]; + const YRW801_WAVE_DATA *wave_data[2]; + VOICE_DATA *voice[2]; + uint8_t i = 0, voices = 0; + + while (opl4_midi->gen_in_progress) { } + + if (midi_channel->drum_channel) + wave_data[voices++] = ®ion_ptr[0x80].regions[note - 0x1A].wave_data; + else { + /* Determine the number of voices and voice parameters */ + const YRW801_REGION_DATA *region = region_ptr[midi_channel->instrument & 0x7F].regions; + + while (i < region_ptr[midi_channel->instrument & 0x7F].count) { + if (note >= region[i].key_min && note <= region[i].key_max) { + wave_data[voices] = ®ion[i].wave_data; + if (++voices >= 2) + break; + } + i++; + } + } + + /* Allocate and initialize needed voices */ + for (i = 0; i < voices; i++) { + voice[i] = get_voice(wave_data[i], opl4_midi); + voice[i]->is_active = true; + voice[i]->activated = plat_get_ticks(); + + voice[i]->midi_channel = midi_channel; + voice[i]->note = note; + voice[i]->velocity = velocity & 0x7F; + } + + for (i = 0; i < voices; i++) { + voice[i]->reg_f_number = (wave_data[i]->tone >> 8) & OPL4_TONE_NUMBER_BIT8; + opl4_write_wave_register(OPL4_REG_F_NUMBER + voice[i]->number, voice[i]->reg_f_number, opl4_midi); + + bool new_wave = (voice[i]->wave_data != wave_data[i]); + /* Set tone number (triggers header loading) */ + if (new_wave) { + opl4_write_wave_register(OPL4_REG_TONE_NUMBER + voice[i]->number, wave_data[i]->tone & 0xFF, opl4_midi); + voice[i]->wave_data = wave_data[i]; + } + + voice[i]->reg_misc = OPL4_LFO_RESET_BIT; + update_pan(voice[i], opl4_midi); + update_pitch(voice[i], 0, opl4_midi); + voice[i]->level_direct = OPL4_LEVEL_DIRECT_BIT; + update_volume(voice[i], opl4_midi); + if (new_wave) { + /* Set remaining parameters */ + opl4_write_wave_register(OPL4_REG_ATTACK_DECAY1 + voice[i]->number, voice[i]->wave_data->reg_attack_decay1, opl4_midi); + opl4_write_wave_register(OPL4_REG_LEVEL_DECAY2 + voice[i]->number, voice[i]->wave_data->reg_level_decay2, opl4_midi); + opl4_write_wave_register(OPL4_REG_RELEASE_CORRECTION + voice[i]->number, voice[i]->wave_data->reg_release_correction, opl4_midi); + opl4_write_wave_register(OPL4_REG_TREMOLO + voice[i]->number, voice[i]->wave_data->reg_tremolo, opl4_midi); + + voice[i]->reg_lfo_vibrato = voice[i]->wave_data->reg_lfo_vibrato; + + if (!midi_channel->drum_channel) + update_vibrato_depth(voice[i], opl4_midi); + } + } + + /* Finally, switch on all voices */ + for (i = 0; i < voices; i++) { + voice[i]->reg_misc = (voice[i]->reg_misc & 0x1F) | OPL4_KEY_ON_BIT; + opl4_write_wave_register(OPL4_REG_MISC + voice[i]->number, voice[i]->reg_misc, opl4_midi); + } +} + +void +control_change(uint8_t midi_channel, uint8_t id, uint8_t value, opl4_midi_t *opl4_midi) +{ + int i = 0; + switch (id) { + case 10: + /* Change stereo panning */ + if (midi_channel != DRUM_CHANNEL) { + opl4_midi->midi_channel_data[midi_channel].panpot = (value - 0x40) >> 3; + for (i = 0; i < NR_OF_WAVE_CHANNELS; i++) { + if (opl4_midi->voice_data[i].is_active && opl4_midi->voice_data[i].midi_channel == &opl4_midi->midi_channel_data[midi_channel]) { + update_pan(&opl4_midi->voice_data[i], opl4_midi); + } + } + } + break; + } +} + +void +pitch_wheel(uint8_t midi_channel, uint16_t value, opl4_midi_t *opl4_midi) +{ + int i = 0; + + for (i = 0; i < 24; i++) { + if (opl4_midi->voice_data[i].is_active && opl4_midi->voice_data[i].midi_channel == &opl4_midi->midi_channel_data[midi_channel]) { + update_pitch(&opl4_midi->voice_data[i], value, opl4_midi); + } + } +} + +void +channel_pressure(uint8_t midi_channel, uint8_t pressure) +{ + (void) midi_channel; + (void) pressure; +} + +void +key_pressure(uint8_t midi_channel, uint8_t note, uint8_t pressure) +{ + (void) midi_channel; + (void) note; + (void) pressure; +} + +void +program_change(uint8_t midi_channel, uint8_t program, opl4_midi_t *opl4_midi) +{ + opl4_midi->midi_channel_data[midi_channel].instrument = program; +} + +static void +opl4_midi_thread(void *arg) +{ + opl4_midi_t *opl4_midi = opl4_midi_cur; + uint32_t i = 0; + uint32_t buf_size = RENDER_RATE * 2; + uint32_t buf_size_segments = buf_size * BUFFER_SEGMENTS; + uint32_t buf_pos = 0; + + int32_t buffer[RENDER_RATE * 2]; + + extern void givealbuffer_midi(void *buf, uint32_t size); + while (opl4_midi->on) { + thread_wait_event(opl4_midi->wait_event, -1); + thread_reset_event(opl4_midi->wait_event); + if (!opl4_midi->on) + break; + atomic_store(&opl4_midi->gen_in_progress, true); + opl4_midi->opl4.generate(opl4_midi->opl4.priv, buffer, RENDER_RATE); + atomic_store(&opl4_midi->gen_in_progress, false); + if (sound_is_float) { + for (i = 0; i < (buf_size / 2); i++) { + opl4_midi->buffer_float[(i + buf_pos) * 2] = buffer[i * 2] / 32768.0; + opl4_midi->buffer_float[((i + buf_pos) * 2) + 1] = buffer[(i * 2) + 1] / 32768.0; + } + buf_pos += buf_size / 2; + if (buf_pos >= (buf_size_segments / 2)) { + givealbuffer_midi(opl4_midi->buffer_float, buf_size_segments); + buf_pos = 0; + } + } else { + for (i = 0; i < (buf_size / 2); i++) { + opl4_midi->buffer[(i + buf_pos) * 2] = buffer[i * 2] & 0xFFFF; /* Outputs are clamped beforehand. */ + opl4_midi->buffer[((i + buf_pos) * 2) + 1] = buffer[(i * 2) + 1] & 0xFFFF; /* Outputs are clamped beforehand. */ + } + buf_pos += buf_size / 2; + if (buf_pos >= (buf_size_segments / 2)) { + givealbuffer_midi(opl4_midi->buffer, buf_size_segments); + buf_pos = 0; + } + } + } +} + +static void +opl4_midi_poll(void) +{ + opl4_midi_t *opl4_midi = opl4_midi_cur; + opl4_midi->midi_pos++; + if (opl4_midi->midi_pos == RENDER_RATE) { + opl4_midi->midi_pos = 0; + thread_set_event(opl4_midi->wait_event); + } +} + +void +opl4_midi_msg(uint8_t *val) +{ + opl4_midi_t *opl4_midi = opl4_midi_cur; + + uint32_t msg = *(uint32_t *) (val); + uint8_t data_byte_1 = msg & 0xFF; + uint8_t data_byte_2 = (msg >> 8) & 0xFF; + uint8_t data_byte_3 = (msg >> 16) & 0xFF; + + uint8_t midi_channel = data_byte_1 & 0x0F; + uint8_t midi_command = data_byte_1 >> 4; + + switch (midi_command) { + case 0x8: // Note OFF + note_off(data_byte_2 & 0x7F, data_byte_3, &opl4_midi->midi_channel_data[midi_channel], opl4_midi); + break; + case 0x9: // Note ON + note_on(data_byte_2 & 0x7F, data_byte_3, &opl4_midi->midi_channel_data[midi_channel], opl4_midi); + break; + case 0xA: // Key after-touch + break; + case 0xB: // Control change + control_change(midi_channel, data_byte_2, data_byte_3, opl4_midi); + break; + case 0xC: // Program change + program_change(midi_channel, data_byte_2 & 0x7F, opl4_midi); + break; + case 0xD: // Channel after-touch + break; + case 0xE: // Pitch wheel + pitch_wheel(midi_channel, ((data_byte_3 <<= 7) | data_byte_2), opl4_midi); + break; + } +} + +void +opl4_midi_sysex(uint8_t *data, unsigned int len) +{ +} + +void * +opl4_init(const device_t *info) +{ + midi_device_t *dev; + extern void al_set_midi(int freq, int buf_size); + + dev = malloc(sizeof(midi_device_t)); + memset(dev, 0, sizeof(midi_device_t)); + + dev->play_msg = opl4_midi_msg; + dev->play_sysex = opl4_midi_sysex; + dev->poll = opl4_midi_poll; + + al_set_midi(48000, 4800); + + opl4_midi_cur = calloc(1, sizeof(opl4_midi_t)); + + fm_driver_get(FM_YMF278B, &opl4_midi_cur->opl4); + + opl4_midi_cur->opl4.write(0x38A, 0x05, opl4_midi_cur->opl4.priv); + opl4_midi_cur->opl4.write(0x389, 0x3, opl4_midi_cur->opl4.priv); + midi_out_init(dev); + + opl4_midi_cur->on = true; + opl4_midi_cur->midi_channel_data[9].drum_channel = true; + atomic_init(&opl4_midi_cur->gen_in_progress, 0); + + for (uint8_t voice = 0; voice < NR_OF_WAVE_CHANNELS; voice++) { + opl4_midi_cur->voice_data[voice].number = voice; + opl4_midi_cur->voice_data[voice].is_active = false; + opl4_midi_cur->voice_data[voice].activated = 0; + opl4_midi_cur->voice_data[voice].midi_channel = NULL; + opl4_midi_cur->voice_data[voice].note = 0; + opl4_midi_cur->voice_data[voice].velocity = 0; + opl4_midi_cur->voice_data[voice].wave_data = NULL; + opl4_midi_cur->voice_data[voice].level_direct = 0; + opl4_midi_cur->voice_data[voice].reg_f_number = 0; + opl4_midi_cur->voice_data[voice].reg_misc = 0; + opl4_midi_cur->voice_data[voice].reg_lfo_vibrato = 0; + } + opl4_midi_cur->wait_event = thread_create_event(); + opl4_midi_cur->thread = thread_create(opl4_midi_thread, NULL); + return dev; +} + +void +opl4_close(void *p) +{ + if (!p) + return; + + opl4_midi_cur->on = false; + thread_set_event(opl4_midi_cur->wait_event); + thread_wait(opl4_midi_cur->thread); + free(opl4_midi_cur); + opl4_midi_cur = NULL; +} + +static int +opl4_midi_available(void) +{ + return rom_present("roms/sound/yamaha/yrw801.rom"); +} + +const device_t opl4_midi_device = { + .name = "OPL4-ML Daughterboard", + .internal_name = "opl4-ml", + .flags = 0, + .local = 0, + .init = opl4_init, + .close = opl4_close, + .reset = NULL, + { .available = opl4_midi_available }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; diff --git a/src/sound/midi_opl4_yrw801.c b/src/sound/midi_opl4_yrw801.c new file mode 100644 index 000000000..96822b9af --- /dev/null +++ b/src/sound/midi_opl4_yrw801.c @@ -0,0 +1,1042 @@ +/* Table taken from linux/sound/drivers/opl4/yrw801.c */ +/* Macros from Linux source code as well */ + +#include "yrw801.h" + +static const YRW801_REGION_DATA regions_00[] = { + /* Acoustic Grand Piano */ + { 0x14, 0x27, { 0x12c, 7474, 100, 0, 0, 0x00, 0xc8, 0x20, 0xf2, 0x13, 0x08, 0x0 }}, + { 0x28, 0x2d, { 0x12d, 6816, 100, 0, 0, 0x00, 0xc8, 0x20, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x2e, 0x33, { 0x12e, 5899, 100, 0, 0, 0x00, 0xc8, 0x20, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x34, 0x39, { 0x12f, 5290, 100, 0, 0, 0x00, 0xc8, 0x20, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x3a, 0x3f, { 0x130, 4260, 100, 0, 0, 0x0a, 0xc8, 0x20, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x40, 0x45, { 0x131, 3625, 100, 0, 0, 0x0a, 0xc8, 0x20, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x46, 0x4b, { 0x132, 3116, 100, 0, 0, 0x04, 0xc8, 0x20, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x4c, 0x52, { 0x133, 2081, 100, 0, 0, 0x03, 0xc8, 0x20, 0xf2, 0x14, 0x18, 0x0 }}, + { 0x53, 0x58, { 0x134, 1444, 100, 0, 0, 0x07, 0xc8, 0x20, 0xf3, 0x14, 0x18, 0x0 }}, + { 0x59, 0x6d, { 0x135, 1915, 100, 0, 0, 0x00, 0xc8, 0x20, 0xf4, 0x15, 0x08, 0x0 }} +}; +static const YRW801_REGION_DATA regions_01[] = { + /* Bright Acoustic Piano */ + { 0x14, 0x2d, { 0x12c, 7474, 100, 0, 0, 0x00, 0xc8, 0x20, 0xf2, 0x13, 0x08, 0x0 }}, + { 0x2e, 0x33, { 0x12d, 6816, 100, 0, 0, 0x00, 0xc8, 0x20, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x34, 0x39, { 0x12e, 5899, 100, 0, 0, 0x00, 0xc8, 0x20, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x3a, 0x3f, { 0x12f, 5290, 100, 0, 0, 0x00, 0xc8, 0x20, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x40, 0x45, { 0x130, 4260, 100, 0, 0, 0x0a, 0xc8, 0x20, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x46, 0x4b, { 0x131, 3625, 100, 0, 0, 0x0a, 0xc8, 0x20, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x4c, 0x52, { 0x132, 3116, 100, 0, 0, 0x04, 0xc8, 0x20, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x53, 0x58, { 0x133, 2081, 100, 0, 0, 0x07, 0xc8, 0x20, 0xf2, 0x14, 0x18, 0x0 }}, + { 0x59, 0x5e, { 0x134, 1444, 100, 0, 0, 0x0a, 0xc8, 0x20, 0xf3, 0x14, 0x18, 0x0 }}, + { 0x5f, 0x6d, { 0x135, 1915, 100, 0, 0, 0x00, 0xc8, 0x20, 0xf4, 0x15, 0x08, 0x0 }} +}; +static const YRW801_REGION_DATA regions_02[] = { + /* Electric Grand Piano */ + { 0x14, 0x2d, { 0x12c, 7476, 100, 1, 0, 0x00, 0xae, 0x20, 0xf2, 0x13, 0x07, 0x0 } }, + { 0x2e, 0x33, { 0x12d, 6818, 100, 1, 0, 0x00, 0xae, 0x20, 0xf2, 0x14, 0x07, 0x0 } }, + { 0x34, 0x39, { 0x12e, 5901, 100, 1, 0, 0x00, 0xae, 0x20, 0xf2, 0x14, 0x07, 0x0 } }, + { 0x3a, 0x3f, { 0x12f, 5292, 100, 1, 0, 0x00, 0xae, 0x20, 0xf2, 0x14, 0x07, 0x0 } }, + { 0x40, 0x45, { 0x130, 4262, 100, 1, 0, 0x00, 0xae, 0x20, 0xf2, 0x14, 0x07, 0x0 } }, + { 0x46, 0x4b, { 0x131, 3627, 100, 1, 0, 0x00, 0xae, 0x20, 0xf2, 0x14, 0x07, 0x0 } }, + { 0x4c, 0x52, { 0x132, 3118, 100, 1, 0, 0x00, 0xae, 0x20, 0xf2, 0x14, 0x07, 0x0 } }, + { 0x53, 0x58, { 0x133, 2083, 100, 1, 0, 0x00, 0xae, 0x20, 0xf2, 0x14, 0x17, 0x0 } }, + { 0x59, 0x5e, { 0x134, 1446, 100, 1, 0, 0x00, 0xae, 0x20, 0xf3, 0x14, 0x17, 0x0 } }, + { 0x5f, 0x6d, { 0x135, 1917, 100, 1, 0, 0x00, 0xae, 0x20, 0xf4, 0x15, 0x07, 0x0 } }, + { 0x00, 0x7f, { 0x06c, 6375, 100, -1, 0, 0x00, 0xc2, 0x28, 0xf4, 0x23, 0x18, 0x0 }} +}; +static const YRW801_REGION_DATA regions_03[] = { + /* Honky-Tonk Piano */ + { 0x14, 0x27, { 0x12c, 7474, 100, 0, 0, 0x00, 0xb4, 0x20, 0xf2, 0x13, 0x08, 0x0 }}, + { 0x28, 0x2d, { 0x12d, 6816, 100, 0, 0, 0x00, 0xb4, 0x20, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x2e, 0x33, { 0x12e, 5899, 100, 0, 0, 0x00, 0xb4, 0x20, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x34, 0x39, { 0x12f, 5290, 100, 0, 0, 0x00, 0xb4, 0x20, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x3a, 0x3f, { 0x130, 4260, 100, 0, 0, 0x0a, 0xb4, 0x20, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x40, 0x45, { 0x131, 3625, 100, 0, 0, 0x0a, 0xb4, 0x20, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x46, 0x4b, { 0x132, 3116, 100, 0, 0, 0x04, 0xb4, 0x20, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x4c, 0x52, { 0x133, 2081, 100, 0, 0, 0x03, 0xb4, 0x20, 0xf2, 0x14, 0x18, 0x0 }}, + { 0x53, 0x58, { 0x134, 1444, 100, 0, 0, 0x07, 0xb4, 0x20, 0xf3, 0x14, 0x18, 0x0 }}, + { 0x59, 0x6d, { 0x135, 1915, 100, 0, 0, 0x00, 0xb4, 0x20, 0xf4, 0x15, 0x08, 0x0 }}, + { 0x14, 0x27, { 0x12c, 7486, 100, 0, 0, 0x00, 0xb4, 0x20, 0xf2, 0x13, 0x08, 0x0 }}, + { 0x28, 0x2d, { 0x12d, 6803, 100, 0, 0, 0x00, 0xb4, 0x20, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x2e, 0x33, { 0x12e, 5912, 100, 0, 0, 0x00, 0xb4, 0x20, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x34, 0x39, { 0x12f, 5275, 100, 0, 0, 0x00, 0xb4, 0x20, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x3a, 0x3f, { 0x130, 4274, 100, 0, 0, 0x0a, 0xb4, 0x20, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x40, 0x45, { 0x131, 3611, 100, 0, 0, 0x0a, 0xb4, 0x20, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x46, 0x4b, { 0x132, 3129, 100, 0, 0, 0x04, 0xb4, 0x20, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x4c, 0x52, { 0x133, 2074, 100, 0, 0, 0x07, 0xb4, 0x20, 0xf2, 0x14, 0x18, 0x0 }}, + { 0x53, 0x58, { 0x134, 1457, 100, 0, 0, 0x01, 0xb4, 0x20, 0xf3, 0x14, 0x18, 0x0 }}, + { 0x59, 0x6d, { 0x135, 1903, 100, 0, 0, 0x00, 0xb4, 0x20, 0xf4, 0x15, 0x08, 0x0 }} +}; +static const YRW801_REGION_DATA regions_04[] = { + /* Electric Piano 1 */ + { 0x15, 0x6c, { 0x00b, 6570, 100, 0, 0, 0x00, 0x28, 0x38, 0xf0, 0x00, 0x0c, 0x0 }}, + { 0x00, 0x7f, { 0x06c, 6375, 100, 0, 2, 0x00, 0xb0, 0x22, 0xf4, 0x23, 0x19, 0x0 }} +}; +static const YRW801_REGION_DATA regions_05[] = { + /* Electric Piano 2 */ + { 0x14, 0x27, { 0x12c, 7476, 100, 0, 3, 0x00, 0xa2, 0x1b, 0xf2, 0x13, 0x08, 0x0 }}, + { 0x28, 0x2d, { 0x12d, 6818, 100, 0, 3, 0x00, 0xa2, 0x1b, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x2e, 0x33, { 0x12e, 5901, 100, 0, 3, 0x00, 0xa2, 0x1b, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x34, 0x39, { 0x12f, 5292, 100, 0, 3, 0x00, 0xa2, 0x1b, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x3a, 0x3f, { 0x130, 4262, 100, 0, 3, 0x0a, 0xa2, 0x1b, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x40, 0x45, { 0x131, 3627, 100, 0, 3, 0x0a, 0xa2, 0x1b, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x46, 0x4b, { 0x132, 3118, 100, 0, 3, 0x04, 0xa2, 0x1b, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x4c, 0x52, { 0x133, 2083, 100, 0, 3, 0x03, 0xa2, 0x1b, 0xf2, 0x14, 0x18, 0x0 }}, + { 0x53, 0x58, { 0x134, 1446, 100, 0, 3, 0x07, 0xa2, 0x1b, 0xf3, 0x14, 0x18, 0x0 }}, + { 0x59, 0x6d, { 0x135, 1917, 100, 0, 3, 0x00, 0xa2, 0x1b, 0xf4, 0x15, 0x08, 0x0 }}, + { 0x14, 0x2d, { 0x12c, 7472, 100, 0, 0, 0x00, 0xa2, 0x18, 0xf2, 0x13, 0x08, 0x0 }}, + { 0x2e, 0x33, { 0x12d, 6814, 100, 0, 0, 0x00, 0xa2, 0x18, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x34, 0x39, { 0x12e, 5897, 100, 0, 0, 0x00, 0xa2, 0x18, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x3a, 0x3f, { 0x12f, 5288, 100, 0, 0, 0x00, 0xa2, 0x18, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x40, 0x45, { 0x130, 4258, 100, 0, 0, 0x0a, 0xa2, 0x18, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x46, 0x4b, { 0x131, 3623, 100, 0, 0, 0x0a, 0xa2, 0x18, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x4c, 0x52, { 0x132, 3114, 100, 0, 0, 0x04, 0xa2, 0x18, 0xf2, 0x14, 0x08, 0x0 }}, + { 0x53, 0x58, { 0x133, 2079, 100, 0, 0, 0x07, 0xa2, 0x18, 0xf2, 0x14, 0x18, 0x0 }}, + { 0x59, 0x5e, { 0x134, 1442, 100, 0, 0, 0x0a, 0xa2, 0x18, 0xf3, 0x14, 0x18, 0x0 }}, + { 0x5f, 0x6d, { 0x135, 1913, 100, 0, 0, 0x00, 0xa2, 0x18, 0xf4, 0x15, 0x08, 0x0 }} +}; +static const YRW801_REGION_DATA regions_06[] = { + /* Harpsichord */ + { 0x15, 0x39, { 0x080, 5158, 100, 0, 0, 0x00, 0xb2, 0x20, 0xf5, 0x24, 0x19, 0x0 }}, + { 0x3a, 0x3f, { 0x081, 4408, 100, 0, 0, 0x00, 0xb2, 0x20, 0xf5, 0x25, 0x09, 0x0 }}, + { 0x40, 0x45, { 0x082, 3622, 100, 0, 0, 0x00, 0xb2, 0x20, 0xf5, 0x25, 0x09, 0x0 }}, + { 0x46, 0x4d, { 0x083, 2843, 100, 0, 0, 0x00, 0xb2, 0x20, 0xf5, 0x25, 0x19, 0x0 }}, + { 0x4e, 0x6c, { 0x084, 1307, 100, 0, 0, 0x00, 0xb2, 0x20, 0xf5, 0x25, 0x29, 0x0 }} +}; +static const YRW801_REGION_DATA regions_07[] = { + /* Clavinet */ + { 0x15, 0x51, { 0x027, 5009, 100, 0, 0, 0x00, 0xd2, 0x28, 0xf5, 0x13, 0x2b, 0x0 }}, + { 0x52, 0x6c, { 0x028, 3495, 100, 0, 0, 0x00, 0xd2, 0x28, 0xf5, 0x13, 0x3b, 0x0 }} +}; +static const YRW801_REGION_DATA regions_08[] = { + /* Celesta */ + { 0x15, 0x6c, { 0x02b, 3267, 100, 0, 0, 0x00, 0xdc, 0x20, 0xf4, 0x15, 0x07, 0x3 }} +}; +static const YRW801_REGION_DATA regions_09[] = { + /* Glockenspiel */ + { 0x15, 0x78, { 0x0f3, 285, 100, 0, 0, 0x00, 0xc2, 0x28, 0xf6, 0x25, 0x25, 0x0 }} +}; +static const YRW801_REGION_DATA regions_0a[] = { + /* Music Box */ + { 0x15, 0x6c, { 0x0f3, 3362, 100, 0, 0, 0x00, 0xb6, 0x20, 0xa6, 0x25, 0x25, 0x0 }}, + { 0x15, 0x6c, { 0x101, 4773, 100, 0, 0, 0x00, 0xaa, 0x20, 0xd4, 0x14, 0x16, 0x0 }} +}; +static const YRW801_REGION_DATA regions_0b[] = { + /* Vibraphone */ + { 0x15, 0x6c, { 0x101, 4778, 100, 0, 0, 0x00, 0xc0, 0x28, 0xf4, 0x14, 0x16, 0x4 }} +}; +static const YRW801_REGION_DATA regions_0c[] = { + /* Marimba */ + { 0x15, 0x3f, { 0x0f4, 4778, 100, 0, 0, 0x00, 0xc4, 0x38, 0xf7, 0x47, 0x08, 0x0 }}, + { 0x40, 0x4c, { 0x0f5, 3217, 100, 0, 0, 0x00, 0xc4, 0x38, 0xf7, 0x47, 0x08, 0x0 }}, + { 0x4d, 0x5a, { 0x0f5, 3217, 100, 0, 0, 0x00, 0xc4, 0x38, 0xf7, 0x48, 0x08, 0x0 }}, + { 0x5b, 0x7f, { 0x0f5, 3218, 100, 0, 0, 0x00, 0xc4, 0x38, 0xf7, 0x48, 0x18, 0x0 }} +}; +static const YRW801_REGION_DATA regions_0d[] = { + /* Xylophone */ + { 0x00, 0x7f, { 0x136, 1729, 100, 0, 0, 0x00, 0xd2, 0x38, 0xf0, 0x06, 0x36, 0x0 }} +}; +static const YRW801_REGION_DATA regions_0e[] = { + /* Tubular Bell */ + { 0x01, 0x7f, { 0x0ff, 3999, 100, 0, 1, 0x00, 0x90, 0x21, 0xf4, 0xa3, 0x25, 0x1 }} +}; +static const YRW801_REGION_DATA regions_0f[] = { + /* Dulcimer */ + { 0x00, 0x7f, { 0x03f, 4236, 100, 0, 1, 0x00, 0xbc, 0x29, 0xf5, 0x16, 0x07, 0x0 }}, + { 0x00, 0x7f, { 0x040, 4236, 100, 0, 2, 0x0e, 0x94, 0x2a, 0xf5, 0x16, 0x07, 0x0 }} +}; +static const YRW801_REGION_DATA regions_10[] = { + /* Drawbar Organ */ + { 0x01, 0x7f, { 0x08e, 4394, 100, 0, 2, 0x14, 0xc2, 0x3a, 0xf0, 0x00, 0x0a, 0x0 }} +}; +static const YRW801_REGION_DATA regions_11[] = { + /* Percussive Organ */ + { 0x15, 0x3b, { 0x08c, 6062, 100, 0, 3, 0x00, 0xbe, 0x3b, 0xf0, 0x00, 0x09, 0x0 }}, + { 0x3c, 0x6c, { 0x08d, 2984, 100, 0, 3, 0x00, 0xbe, 0x3b, 0xf0, 0x00, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_12[] = { + /* Rock Organ */ + { 0x15, 0x30, { 0x128, 6574, 100, 0, 1, 0x00, 0xcc, 0x39, 0xf0, 0x00, 0x0a, 0x0 }}, + { 0x31, 0x3c, { 0x129, 5040, 100, 0, 1, 0x00, 0xcc, 0x39, 0xf0, 0x00, 0x0a, 0x0 }}, + { 0x3d, 0x48, { 0x12a, 3498, 100, 0, 1, 0x00, 0xcc, 0x39, 0xf0, 0x00, 0x0a, 0x0 }}, + { 0x49, 0x54, { 0x12b, 1957, 100, 0, 1, 0x00, 0xcc, 0x39, 0xf0, 0x00, 0x0a, 0x0 }}, + { 0x55, 0x6c, { 0x127, 423, 100, 0, 1, 0x00, 0xcc, 0x39, 0xf0, 0x00, 0x0a, 0x0 } } +}; +static const YRW801_REGION_DATA regions_13[] = { + /* Church Organ */ + { 0x15, 0x29, { 0x087, 7466, 100, 0, 1, 0x00, 0xc4, 0x11, 0xf0, 0x00, 0x09, 0x0 }}, + { 0x2a, 0x30, { 0x088, 6456, 100, 0, 1, 0x00, 0xc4, 0x11, 0xf0, 0x00, 0x09, 0x0 }}, + { 0x31, 0x38, { 0x089, 5428, 100, 0, 1, 0x00, 0xc4, 0x11, 0xf0, 0x00, 0x09, 0x0 }}, + { 0x39, 0x41, { 0x08a, 4408, 100, 0, 1, 0x00, 0xc4, 0x11, 0xf0, 0x00, 0x09, 0x0 }}, + { 0x42, 0x6c, { 0x08b, 3406, 100, 0, 1, 0x00, 0xc4, 0x11, 0xf0, 0x00, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_14[] = { + /* Reed Organ */ + { 0x00, 0x53, { 0x0ac, 5570, 100, 0, 0, 0x06, 0xc0, 0x38, 0xf0, 0x00, 0x09, 0x1 }}, + { 0x54, 0x7f, { 0x0ad, 2497, 100, 0, 0, 0x00, 0xc0, 0x38, 0xf0, 0x00, 0x09, 0x1 }} +}; +static const YRW801_REGION_DATA regions_15[] = { + /* Accordion */ + { 0x15, 0x4c, { 0x006, 4261, 100, 0, 2, 0x00, 0xa4, 0x22, 0x90, 0x00, 0x09, 0x0 }}, + { 0x4d, 0x6c, { 0x007, 1530, 100, 0, 2, 0x00, 0xa4, 0x22, 0x90, 0x00, 0x09, 0x0 }}, + { 0x15, 0x6c, { 0x070, 4391, 100, 0, 3, 0x00, 0x8a, 0x23, 0xa0, 0x00, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_16[] = { + /* Harmonica */ + { 0x15, 0x6c, { 0x070, 4408, 100, 0, 0, 0x00, 0xae, 0x30, 0xa0, 0x00, 0x09, 0x2 }} +}; +static const YRW801_REGION_DATA regions_17[] = { + /* Tango Accordion */ + { 0x00, 0x53, { 0x0ac, 5573, 100, 0, 0, 0x00, 0xae, 0x38, 0xf0, 0x00, 0x09, 0x0 }}, + { 0x54, 0x7f, { 0x0ad, 2500, 100, 0, 0, 0x00, 0xae, 0x38, 0xf0, 0x00, 0x09, 0x0 }}, + { 0x15, 0x6c, { 0x041, 8479, 100, 0, 2, 0x00, 0x6a, 0x3a, 0x75, 0x20, 0x0a, 0x0 }} +}; +static const YRW801_REGION_DATA regions_18[] = { + /* Nylon Guitar */ + { 0x15, 0x2f, { 0x0b3, 6964, 100, 0, 0, 0x05, 0xca, 0x28, 0xf5, 0x34, 0x09, 0x0 }}, + { 0x30, 0x36, { 0x0b7, 5567, 100, 0, 0, 0x0c, 0xca, 0x28, 0xf5, 0x34, 0x09, 0x0 }}, + { 0x37, 0x3c, { 0x0b5, 4653, 100, 0, 0, 0x00, 0xca, 0x28, 0xf6, 0x34, 0x09, 0x0 }}, + { 0x3d, 0x43, { 0x0b4, 3892, 100, 0, 0, 0x00, 0xca, 0x28, 0xf6, 0x35, 0x09, 0x0 }}, + { 0x44, 0x60, { 0x0b6, 2723, 100, 0, 0, 0x00, 0xca, 0x28, 0xf6, 0x35, 0x19, 0x0 }} +}; +static const YRW801_REGION_DATA regions_19[] = { + /* Steel Guitar */ + { 0x15, 0x31, { 0x00c, 6937, 100, 0, 0, 0x00, 0xbc, 0x28, 0xf0, 0x04, 0x19, 0x0 }}, + { 0x32, 0x38, { 0x00d, 5410, 100, 0, 0, 0x00, 0xbc, 0x28, 0xf0, 0x05, 0x09, 0x0 }}, + { 0x39, 0x47, { 0x00e, 4379, 100, 0, 0, 0x00, 0xbc, 0x28, 0xf5, 0x94, 0x09, 0x0 }}, + { 0x48, 0x6c, { 0x00f, 2843, 100, 0, 0, 0x00, 0xbc, 0x28, 0xf6, 0x95, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_1a[] = { + /* Jazz Guitar */ + { 0x15, 0x31, { 0x05a, 6832, 100, 0, 0, 0x00, 0xca, 0x28, 0xf6, 0x34, 0x09, 0x0 }}, + { 0x32, 0x3f, { 0x05b, 4897, 100, 0, 0, 0x00, 0xca, 0x28, 0xf6, 0x34, 0x09, 0x0 }}, + { 0x40, 0x6c, { 0x05c, 3218, 100, 0, 0, 0x00, 0xca, 0x28, 0xf6, 0x34, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_1b[] = { + /* Clean Guitar */ + { 0x15, 0x2c, { 0x061, 7053, 100, 0, 1, 0x00, 0xb4, 0x29, 0xf5, 0x54, 0x0a, 0x0 }}, + { 0x2d, 0x31, { 0x060, 6434, 100, 0, 1, 0x00, 0xb4, 0x29, 0xf5, 0x54, 0x0a, 0x0 }}, + { 0x32, 0x38, { 0x063, 5764, 100, 0, 1, 0x00, 0xbe, 0x29, 0xf5, 0x55, 0x0a, 0x0 }}, + { 0x39, 0x3f, { 0x062, 4627, 100, 0, 1, 0x00, 0xb4, 0x29, 0xf5, 0x55, 0x0a, 0x0 }}, + { 0x40, 0x44, { 0x065, 3963, 100, 0, 1, 0x00, 0xb4, 0x29, 0xf5, 0x55, 0x1a, 0x0 }}, + { 0x45, 0x4b, { 0x064, 3313, 100, 0, 1, 0x00, 0xb4, 0x29, 0xf5, 0x55, 0x1a, 0x0 }}, + { 0x4c, 0x54, { 0x066, 2462, 100, 0, 1, 0x00, 0xb4, 0x29, 0xf5, 0x55, 0x2a, 0x0 }}, + { 0x55, 0x6c, { 0x067, 1307, 100, 0, 1, 0x00, 0xb4, 0x29, 0xf6, 0x56, 0x0a, 0x0 }} +}; +static const YRW801_REGION_DATA regions_1c[] = { + /* Muted Guitar */ + { 0x01, 0x7f, { 0x068, 4408, 100, 0, 0, 0x00, 0xcc, 0x28, 0xf6, 0x15, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_1d[] = { + /* Overdriven Guitar */ + { 0x00, 0x40, { 0x0a5, 6589, 100, 0, 1, 0x00, 0xc0, 0x29, 0xf2, 0x11, 0x09, 0x0 }}, + { 0x41, 0x7f, { 0x0a6, 5428, 100, 0, 1, 0x00, 0xc0, 0x29, 0xf2, 0x11, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_1e[] = { + /* Distortion Guitar */ + { 0x15, 0x2a, { 0x051, 6928, 100, 0, 1, 0x00, 0xbc, 0x21, 0xa2, 0x12, 0x0a, 0x0 }}, + { 0x2b, 0x2e, { 0x052, 6433, 100, 0, 1, 0x00, 0xbc, 0x21, 0xa2, 0x12, 0x0a, 0x0 }}, + { 0x2f, 0x32, { 0x053, 5944, 100, 0, 1, 0x00, 0xbc, 0x21, 0xa2, 0x12, 0x0a, 0x0 }}, + { 0x33, 0x36, { 0x054, 5391, 100, 0, 1, 0x00, 0xbc, 0x21, 0xa2, 0x12, 0x0a, 0x0 }}, + { 0x37, 0x3a, { 0x055, 4897, 100, 0, 1, 0x00, 0xbc, 0x21, 0xa2, 0x12, 0x0a, 0x0 }}, + { 0x3b, 0x3e, { 0x056, 4408, 100, 0, 1, 0x00, 0xbc, 0x21, 0xa2, 0x12, 0x0a, 0x0 }}, + { 0x3f, 0x42, { 0x057, 3892, 100, 0, 1, 0x00, 0xbc, 0x21, 0xa2, 0x12, 0x0a, 0x0 }}, + { 0x43, 0x46, { 0x058, 3361, 100, 0, 1, 0x00, 0xbc, 0x21, 0xa2, 0x12, 0x0a, 0x0 }}, + { 0x47, 0x6c, { 0x059, 2784, 100, 0, 1, 0x00, 0xbc, 0x21, 0xa2, 0x12, 0x0a, 0x0 }} +}; +static const YRW801_REGION_DATA regions_1f[] = { + /* Guitar Harmonics */ + { 0x15, 0x44, { 0x05e, 5499, 100, 0, 0, 0x00, 0xce, 0x28, 0xf4, 0x24, 0x09, 0x0 }}, + { 0x45, 0x49, { 0x05d, 4850, 100, 0, 0, 0x00, 0xe2, 0x28, 0xf4, 0x24, 0x09, 0x0 }}, + { 0x4a, 0x6c, { 0x05f, 4259, 100, 0, 0, 0x00, 0xce, 0x28, 0xf4, 0x24, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_20[] = { + /* Acoustic Bass */ + { 0x15, 0x30, { 0x004, 8053, 100, 0, 0, 0x00, 0xe2, 0x18, 0xf5, 0x15, 0x09, 0x0 }}, + { 0x31, 0x6c, { 0x005, 4754, 100, 0, 0, 0x00, 0xe2, 0x18, 0xf5, 0x15, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_21[] = { + /* Fingered Bass */ + { 0x01, 0x20, { 0x04a, 8762, 100, 0, 0, 0x00, 0xde, 0x18, 0xf6, 0x14, 0x09, 0x0 }}, + { 0x21, 0x25, { 0x04b, 8114, 100, 0, 0, 0x00, 0xde, 0x18, 0xf6, 0x14, 0x09, 0x0 }}, + { 0x26, 0x2a, { 0x04c, 7475, 100, 0, 0, 0x00, 0xde, 0x18, 0xf6, 0x14, 0x09, 0x0 }}, + { 0x2b, 0x7f, { 0x04d, 6841, 100, 0, 0, 0x00, 0xde, 0x18, 0xf6, 0x14, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_22[] = { + /* Picked Bass */ + { 0x15, 0x23, { 0x04f, 7954, 100, 0, 0, 0x00, 0xcc, 0x18, 0xf3, 0x90, 0x0a, 0x0 }}, + { 0x24, 0x2a, { 0x050, 7318, 100, 0, 0, 0x05, 0xcc, 0x18, 0xf3, 0x90, 0x1a, 0x0 }}, + { 0x2b, 0x2f, { 0x06b, 6654, 100, 0, 0, 0x00, 0xcc, 0x18, 0xf3, 0x90, 0x2a, 0x0 }}, + { 0x30, 0x47, { 0x069, 6031, 100, 0, 0, 0x00, 0xcc, 0x18, 0xf5, 0xb0, 0x0a, 0x0 }}, + { 0x48, 0x6c, { 0x06a, 5393, 100, 0, 0, 0x00, 0xcc, 0x18, 0xf5, 0xb0, 0x0a, 0x0 }} +}; +static const YRW801_REGION_DATA regions_23[] = { + /* Fretless Bass */ + { 0x01, 0x7f, { 0x04e, 5297, 100, 0, 0, 0x00, 0xd2, 0x10, 0xf3, 0x63, 0x19, 0x0 }} +}; +static const YRW801_REGION_DATA regions_24[] = { + /* Slap Bass 1 */ + { 0x15, 0x6c, { 0x0a3, 7606, 100, 0, 1, 0x00, 0xde, 0x19, 0xf5, 0x32, 0x1a, 0x0 }} +}; +static const YRW801_REGION_DATA regions_25[] = { + /* Slap Bass 2 */ + { 0x01, 0x7f, { 0x0a2, 6694, 100, 0, 0, 0x00, 0xda, 0x20, 0xb0, 0x02, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_26[] = { + /* Synth Bass 1 */ + { 0x15, 0x6c, { 0x0be, 7466, 100, 0, 1, 0x00, 0xb8, 0x39, 0xf4, 0x14, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_27[] = { + /* Synth Bass 2 */ + { 0x00, 0x7f, { 0x117, 8103, 100, 0, 1, 0x00, 0xca, 0x39, 0xf3, 0x50, 0x08, 0x0 }} +}; +static const YRW801_REGION_DATA regions_28[] = { + /* Violin */ + { 0x15, 0x3a, { 0x105, 5158, 100, 0, 3, 0x00, 0xcc, 0x3b, 0xf3, 0x20, 0x09, 0x0 }}, + { 0x3b, 0x3f, { 0x102, 4754, 100, 0, 3, 0x00, 0xcc, 0x3b, 0xf3, 0x20, 0x09, 0x0 }}, + { 0x40, 0x41, { 0x106, 4132, 100, 0, 3, 0x00, 0xcc, 0x3b, 0xf3, 0x20, 0x09, 0x0 }}, + { 0x42, 0x44, { 0x107, 4033, 100, 0, 3, 0x00, 0xcc, 0x3b, 0xf3, 0x20, 0x09, 0x0 }}, + { 0x45, 0x47, { 0x108, 3580, 100, 0, 3, 0x00, 0xcc, 0x3b, 0xf3, 0x20, 0x09, 0x0 }}, + { 0x48, 0x4a, { 0x10a, 2957, 100, 0, 3, 0x00, 0xcc, 0x3b, 0xf3, 0x20, 0x09, 0x0 }}, + { 0x4b, 0x4c, { 0x10b, 2724, 100, 0, 3, 0x00, 0xcc, 0x3b, 0xf3, 0x20, 0x09, 0x0 }}, + { 0x4d, 0x4e, { 0x10c, 2530, 100, 0, 3, 0x00, 0xcc, 0x3b, 0xf3, 0x20, 0x09, 0x0 }}, + { 0x4f, 0x51, { 0x10d, 2166, 100, 0, 3, 0x00, 0xcc, 0x3b, 0xf3, 0x20, 0x09, 0x0 }}, + { 0x52, 0x6c, { 0x109, 1825, 100, 0, 3, 0x00, 0xcc, 0x3b, 0xf3, 0x20, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_29[] = { + /* Viola */ + { 0x15, 0x32, { 0x103, 5780, 100, 0, 3, 0x00, 0xc4, 0x3b, 0xa3, 0x20, 0x09, 0x0 }}, + { 0x33, 0x35, { 0x104, 5534, 100, 0, 3, 0x00, 0xc4, 0x3b, 0xa3, 0x20, 0x09, 0x0 }}, + { 0x36, 0x38, { 0x105, 5158, 100, 0, 3, 0x00, 0xc4, 0x3b, 0xa3, 0x20, 0x09, 0x0 }}, + { 0x39, 0x3d, { 0x102, 4754, 100, 0, 3, 0x00, 0xca, 0x3b, 0xa3, 0x20, 0x09, 0x0 }}, + { 0x3e, 0x3f, { 0x106, 4132, 100, 0, 3, 0x00, 0xc4, 0x3b, 0xa3, 0x20, 0x09, 0x0 }}, + { 0x40, 0x42, { 0x107, 4033, 100, 0, 3, 0x00, 0xc4, 0x3b, 0xa3, 0x20, 0x09, 0x0 }}, + { 0x43, 0x45, { 0x108, 3580, 100, 0, 3, 0x00, 0xd0, 0x3b, 0xa3, 0x20, 0x09, 0x0 }}, + { 0x46, 0x48, { 0x10a, 2957, 100, 0, 3, 0x00, 0xca, 0x3b, 0xa3, 0x20, 0x09, 0x0 }}, + { 0x49, 0x4a, { 0x10b, 2724, 100, 0, 3, 0x00, 0xd0, 0x3b, 0xa3, 0x20, 0x09, 0x0 }}, + { 0x4b, 0x4c, { 0x10c, 2530, 100, 0, 3, 0x00, 0xca, 0x3b, 0xa3, 0x20, 0x09, 0x0 }}, + { 0x4d, 0x4f, { 0x10d, 2166, 100, 0, 3, 0x00, 0xd0, 0x3b, 0xa3, 0x20, 0x09, 0x0 }}, + { 0x50, 0x6c, { 0x109, 1825, 100, 0, 3, 0x00, 0xd0, 0x3b, 0xa3, 0x20, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_2a[] = { + /* Cello */ + { 0x15, 0x2d, { 0x112, 6545, 100, 0, 3, 0x00, 0xc0, 0x33, 0xa0, 0x00, 0x08, 0x0 }}, + { 0x2e, 0x37, { 0x113, 5764, 100, 0, 3, 0x00, 0xc0, 0x33, 0xa0, 0x00, 0x08, 0x0 }}, + { 0x38, 0x3e, { 0x115, 4378, 100, 0, 3, 0x00, 0xc0, 0x33, 0xa0, 0x00, 0x18, 0x0 }}, + { 0x3f, 0x44, { 0x116, 3998, 100, 0, 3, 0x00, 0xc0, 0x33, 0xa0, 0x00, 0x18, 0x0 }}, + { 0x45, 0x6c, { 0x114, 3218, 100, 0, 3, 0x00, 0xc0, 0x33, 0xa0, 0x00, 0x18, 0x0 }} +}; +static const YRW801_REGION_DATA regions_2b[] = { + /* Contrabass */ + { 0x15, 0x29, { 0x110, 7713, 100, 0, 1, 0x00, 0xc2, 0x19, 0x90, 0x00, 0x09, 0x0 }}, + { 0x2a, 0x6c, { 0x111, 6162, 100, 0, 1, 0x00, 0xc2, 0x19, 0x90, 0x00, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_2c[] = { + /* Tremolo Strings */ + { 0x15, 0x3b, { 0x0b0, 4810, 100, 0, 0, 0x0a, 0xde, 0x38, 0xf0, 0x00, 0x07, 0x6 }}, + { 0x3c, 0x41, { 0x035, 4035, 100, 0, 0, 0x05, 0xde, 0x38, 0xf0, 0x00, 0x07, 0x6 }}, + { 0x42, 0x47, { 0x033, 3129, 100, 0, 0, 0x05, 0xde, 0x38, 0xf0, 0x00, 0x07, 0x6 }}, + { 0x48, 0x52, { 0x034, 2625, 100, 0, 0, 0x05, 0xde, 0x38, 0xf0, 0x00, 0x07, 0x6 }}, + { 0x53, 0x6c, { 0x0af, 936, 100, 0, 0, 0x00, 0xde, 0x38, 0xf0, 0x00, 0x07, 0x6 } } +}; +static const YRW801_REGION_DATA regions_2d[] = { + /* Pizzicato Strings */ + { 0x15, 0x32, { 0x0b8, 6186, 100, 0, 0, 0x00, 0xbc, 0x28, 0xf0, 0x00, 0x05, 0x0 }}, + { 0x33, 0x3b, { 0x0b9, 5031, 100, 0, 0, 0x00, 0xbc, 0x28, 0xf0, 0x00, 0x05, 0x0 }}, + { 0x3c, 0x42, { 0x0bb, 4146, 100, 0, 0, 0x00, 0xbc, 0x28, 0xf0, 0x00, 0x05, 0x0 }}, + { 0x43, 0x48, { 0x0ba, 3245, 100, 0, 0, 0x00, 0xc2, 0x28, 0xf0, 0x00, 0x05, 0x0 }}, + { 0x49, 0x6c, { 0x0bc, 2352, 100, 0, 0, 0x00, 0xbc, 0x28, 0xf0, 0x00, 0x05, 0x0 }} +}; +static const YRW801_REGION_DATA regions_2e[] = { + /* Harp */ + { 0x15, 0x46, { 0x07e, 3740, 100, 0, 1, 0x00, 0xd2, 0x29, 0xf5, 0x25, 0x07, 0x0 }}, + { 0x47, 0x6c, { 0x07f, 2319, 100, 0, 1, 0x00, 0xd2, 0x29, 0xf5, 0x25, 0x07, 0x0 }} +}; +static const YRW801_REGION_DATA regions_2f[] = { + /* Timpani */ + { 0x15, 0x6c, { 0x100, 6570, 100, 0, 0, 0x00, 0xf8, 0x28, 0xf0, 0x05, 0x16, 0x0 }} +}; +static const YRW801_REGION_DATA regions_30[] = { + /* Strings */ + { 0x15, 0x3b, { 0x13c, 4806, 100, 0, 0, 0x00, 0xc8, 0x20, 0x80, 0x00, 0x07, 0x0 }}, + { 0x3c, 0x41, { 0x13e, 4035, 100, 0, 0, 0x00, 0xc8, 0x20, 0x80, 0x00, 0x07, 0x0 }}, + { 0x42, 0x47, { 0x13d, 3122, 100, 0, 0, 0x00, 0xc8, 0x20, 0x80, 0x00, 0x07, 0x0 }}, + { 0x48, 0x52, { 0x13f, 2629, 100, 0, 0, 0x00, 0xbe, 0x20, 0x80, 0x00, 0x07, 0x0 }}, + { 0x53, 0x6c, { 0x140, 950, 100, 0, 0, 0x00, 0xbe, 0x20, 0x80, 0x00, 0x07, 0x0 } } +}; +static const YRW801_REGION_DATA regions_31[] = { + /* Slow Strings */ + { 0x15, 0x3b, { 0x0b0, 4810, 100, 0, 1, 0x0a, 0xbe, 0x19, 0xf0, 0x00, 0x07, 0x0 }}, + { 0x3c, 0x41, { 0x035, 4035, 100, 0, 1, 0x05, 0xbe, 0x19, 0xf0, 0x00, 0x07, 0x0 }}, + { 0x42, 0x47, { 0x033, 3129, 100, 0, 1, 0x05, 0xbe, 0x19, 0xf0, 0x00, 0x07, 0x0 }}, + { 0x48, 0x52, { 0x034, 2625, 100, 0, 1, 0x05, 0xbe, 0x19, 0xf0, 0x00, 0x07, 0x0 }}, + { 0x53, 0x6c, { 0x0af, 936, 100, 0, 1, 0x00, 0xbe, 0x19, 0xf0, 0x00, 0x07, 0x0 } } +}; +static const YRW801_REGION_DATA regions_32[] = { + /* Synth Strings 1 */ + { 0x05, 0x71, { 0x002, 6045, 100, -2, 0, 0x00, 0xa6, 0x20, 0x93, 0x22, 0x06, 0x0 }}, + { 0x15, 0x6c, { 0x0ae, 3261, 100, 2, 0, 0x00, 0xc6, 0x20, 0x70, 0x01, 0x06, 0x0 } } +}; +static const YRW801_REGION_DATA regions_33[] = { + /* Synth Strings 2 */ + { 0x15, 0x6c, { 0x002, 4513, 100, 5, 1, 0x00, 0xb4, 0x19, 0x70, 0x00, 0x06, 0x0 } }, + { 0x15, 0x6c, { 0x002, 4501, 100, -5, 1, 0x00, 0xb4, 0x19, 0x70, 0x00, 0x06, 0x0 }} +}; +static const YRW801_REGION_DATA regions_34[] = { + /* Choir Aahs */ + { 0x15, 0x3a, { 0x018, 5010, 100, 0, 2, 0x00, 0xc2, 0x1a, 0x70, 0x00, 0x08, 0x0 }}, + { 0x3b, 0x40, { 0x019, 4370, 100, 0, 2, 0x00, 0xc2, 0x1a, 0x70, 0x00, 0x08, 0x0 }}, + { 0x41, 0x47, { 0x01a, 3478, 100, 0, 2, 0x00, 0xc2, 0x1a, 0x70, 0x00, 0x08, 0x0 }}, + { 0x48, 0x6c, { 0x01b, 2197, 100, 0, 2, 0x00, 0xc2, 0x1a, 0x70, 0x00, 0x08, 0x0 }} +}; +static const YRW801_REGION_DATA regions_35[] = { + /* Voice Oohs */ + { 0x15, 0x6c, { 0x029, 3596, 100, 0, 0, 0x00, 0xe6, 0x20, 0xf7, 0x20, 0x08, 0x0 }} +}; +static const YRW801_REGION_DATA regions_36[] = { + /* Synth Voice */ + { 0x15, 0x6c, { 0x02a, 3482, 100, 0, 1, 0x00, 0xc2, 0x19, 0x85, 0x21, 0x07, 0x0 }} +}; +static const YRW801_REGION_DATA regions_37[] = { + /* Orchestra Hit */ + { 0x15, 0x6c, { 0x049, 4394, 100, 0, 0, 0x00, 0xfe, 0x30, 0x80, 0x05, 0x05, 0x0 }} +}; +static const YRW801_REGION_DATA regions_38[] = { + /* Trumpet */ + { 0x15, 0x3c, { 0x0f6, 4706, 100, 0, 2, 0x00, 0xd6, 0x32, 0xf3, 0x20, 0x0a, 0x0 }}, + { 0x3d, 0x43, { 0x0f8, 3894, 100, 0, 2, 0x00, 0xd6, 0x32, 0xf3, 0x20, 0x0a, 0x0 }}, + { 0x44, 0x48, { 0x0f7, 3118, 100, 0, 2, 0x00, 0xd6, 0x32, 0xf3, 0x20, 0x0a, 0x0 }}, + { 0x49, 0x4e, { 0x0fa, 2322, 100, 0, 2, 0x00, 0xd6, 0x32, 0xf3, 0x20, 0x0a, 0x0 }}, + { 0x4f, 0x55, { 0x0f9, 1634, 100, 0, 2, 0x00, 0xd6, 0x32, 0xf3, 0x20, 0x0a, 0x0 }}, + { 0x56, 0x6c, { 0x0fb, 786, 100, 0, 2, 0x00, 0xd6, 0x32, 0xf3, 0x20, 0x0a, 0x0 } } +}; +static const YRW801_REGION_DATA regions_39[] = { + /* Trombone */ + { 0x15, 0x3a, { 0x0f0, 5053, 100, 0, 1, 0x00, 0xd6, 0x21, 0xf0, 0x00, 0x09, 0x0 }}, + { 0x3b, 0x3f, { 0x0f1, 4290, 100, 0, 1, 0x00, 0xd6, 0x21, 0xf0, 0x00, 0x09, 0x0 }}, + { 0x40, 0x6c, { 0x0f2, 3580, 100, 0, 1, 0x00, 0xd6, 0x21, 0xf0, 0x00, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_3a[] = { + /* Tuba */ + { 0x15, 0x2d, { 0x085, 7096, 100, 0, 1, 0x00, 0xde, 0x21, 0xf5, 0x10, 0x09, 0x0 }}, + { 0x2e, 0x6c, { 0x086, 6014, 100, 0, 1, 0x00, 0xde, 0x21, 0xf5, 0x10, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_3b[] = { + /* Muted Trumpet */ + { 0x15, 0x45, { 0x0b1, 4135, 100, 0, 0, 0x00, 0xcc, 0x28, 0xf3, 0x10, 0x0a, 0x1 }}, + { 0x46, 0x6c, { 0x0b2, 2599, 100, 0, 0, 0x00, 0xcc, 0x28, 0x83, 0x10, 0x0a, 0x1 }} +}; +static const YRW801_REGION_DATA regions_3c[] = { + /* French Horns */ + { 0x15, 0x49, { 0x07c, 3624, 100, 0, 2, 0x00, 0xd0, 0x1a, 0xf0, 0x00, 0x09, 0x0 }}, + { 0x4a, 0x6c, { 0x07d, 2664, 100, 0, 2, 0x00, 0xd0, 0x1a, 0xf0, 0x00, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_3d[] = { + /* Brass Section */ + { 0x15, 0x42, { 0x0fc, 4375, 100, 0, 0, 0x00, 0xd6, 0x28, 0xf0, 0x00, 0x0a, 0x0 }}, + { 0x43, 0x6c, { 0x0fd, 2854, 100, 0, 0, 0x00, 0xd6, 0x28, 0xf0, 0x00, 0x0a, 0x0 }} +}; +static const YRW801_REGION_DATA regions_3e[] = { + /* Synth Brass 1 */ + { 0x01, 0x27, { 0x0d3, 9094, 100, -1, 0, 0x00, 0xbe, 0x18, 0xa5, 0x11, 0x08, 0x0 }}, + { 0x28, 0x2d, { 0x0da, 8335, 100, -1, 0, 0x00, 0xbe, 0x18, 0xa5, 0x11, 0x08, 0x0 }}, + { 0x2e, 0x33, { 0x0d4, 7558, 100, -1, 0, 0x00, 0xbe, 0x18, 0xa5, 0x11, 0x08, 0x0 }}, + { 0x34, 0x39, { 0x0db, 6785, 100, -1, 0, 0x00, 0xbe, 0x18, 0xa5, 0x11, 0x08, 0x0 }}, + { 0x3a, 0x3f, { 0x0d5, 6042, 100, -1, 0, 0x00, 0xbe, 0x18, 0xa5, 0x11, 0x08, 0x0 }}, + { 0x40, 0x45, { 0x0dc, 5257, 100, -1, 0, 0x00, 0xbe, 0x18, 0xa5, 0x11, 0x08, 0x0 }}, + { 0x46, 0x4b, { 0x0d6, 4493, 100, -1, 0, 0x00, 0xbe, 0x18, 0xa5, 0x11, 0x08, 0x0 }}, + { 0x4c, 0x51, { 0x0dd, 3741, 100, -1, 0, 0x00, 0xbe, 0x18, 0xa5, 0x11, 0x08, 0x0 }}, + { 0x52, 0x57, { 0x0d7, 3012, 100, -1, 0, 0x00, 0xbe, 0x18, 0xa5, 0x11, 0x08, 0x0 }}, + { 0x58, 0x5d, { 0x0de, 2167, 100, -1, 0, 0x00, 0xbe, 0x18, 0xa5, 0x11, 0x08, 0x0 }}, + { 0x5e, 0x63, { 0x0d8, 1421, 100, -1, 0, 0x00, 0xbe, 0x18, 0xa5, 0x11, 0x08, 0x0 }}, + { 0x64, 0x7f, { 0x0d9, -115, 100, -1, 0, 0x00, 0xbe, 0x18, 0xa5, 0x11, 0x08, 0x0 }}, + { 0x01, 0x27, { 0x118, 9103, 100, 1, 1, 0x00, 0xbe, 0x19, 0x85, 0x23, 0x08, 0x0 } }, + { 0x28, 0x2d, { 0x119, 8340, 100, 1, 1, 0x00, 0xbe, 0x19, 0x85, 0x23, 0x08, 0x0 } }, + { 0x2e, 0x33, { 0x11a, 7565, 100, 1, 1, 0x00, 0xbe, 0x19, 0x85, 0x23, 0x08, 0x0 } }, + { 0x34, 0x39, { 0x11b, 6804, 100, 1, 1, 0x00, 0xbe, 0x19, 0x85, 0x23, 0x08, 0x0 } }, + { 0x3a, 0x3f, { 0x11c, 6042, 100, 1, 1, 0x00, 0xbe, 0x19, 0x85, 0x23, 0x08, 0x0 } }, + { 0x40, 0x45, { 0x11d, 5277, 100, 1, 1, 0x00, 0xbe, 0x19, 0x85, 0x23, 0x08, 0x0 } }, + { 0x46, 0x4b, { 0x11e, 4520, 100, 1, 1, 0x00, 0xbe, 0x19, 0x85, 0x23, 0x08, 0x0 } }, + { 0x4c, 0x51, { 0x11f, 3741, 100, 1, 1, 0x00, 0xbe, 0x19, 0x85, 0x23, 0x08, 0x0 } }, + { 0x52, 0x57, { 0x120, 3012, 100, 1, 1, 0x00, 0xbe, 0x19, 0x85, 0x23, 0x08, 0x0 } }, + { 0x58, 0x5d, { 0x121, 2166, 100, 1, 1, 0x00, 0xbe, 0x19, 0x85, 0x23, 0x08, 0x0 } }, + { 0x5e, 0x64, { 0x122, 1421, 100, 1, 1, 0x00, 0xbe, 0x19, 0x85, 0x23, 0x08, 0x0 } }, + { 0x65, 0x7f, { 0x123, -115, 100, 1, 1, 0x00, 0xbe, 0x19, 0x85, 0x23, 0x08, 0x0 } } +}; +static const YRW801_REGION_DATA regions_3f[] = { + /* Synth Brass 2 */ + { 0x01, 0x27, { 0x118, 9113, 100, 3, 6, 0x00, 0xae, 0x26, 0x85, 0x23, 0x08, 0x0 } }, + { 0x28, 0x2d, { 0x119, 8350, 100, 3, 6, 0x00, 0xae, 0x26, 0x85, 0x23, 0x08, 0x0 } }, + { 0x2e, 0x33, { 0x11a, 7575, 100, 3, 6, 0x00, 0xae, 0x26, 0x85, 0x23, 0x08, 0x0 } }, + { 0x34, 0x39, { 0x11b, 6814, 100, 3, 6, 0x00, 0xae, 0x26, 0x85, 0x23, 0x08, 0x0 } }, + { 0x3a, 0x3f, { 0x11c, 6052, 100, 3, 6, 0x00, 0xae, 0x26, 0x85, 0x23, 0x08, 0x0 } }, + { 0x40, 0x45, { 0x11d, 5287, 100, 3, 6, 0x00, 0xae, 0x26, 0x85, 0x23, 0x08, 0x0 } }, + { 0x46, 0x4b, { 0x11e, 4530, 100, 3, 6, 0x00, 0xae, 0x26, 0x85, 0x23, 0x08, 0x0 } }, + { 0x4c, 0x51, { 0x11f, 3751, 100, 3, 6, 0x00, 0xae, 0x26, 0x85, 0x23, 0x08, 0x0 } }, + { 0x52, 0x57, { 0x120, 3022, 100, 3, 6, 0x00, 0xae, 0x26, 0x85, 0x23, 0x08, 0x0 } }, + { 0x58, 0x5d, { 0x121, 2176, 100, 3, 6, 0x00, 0xae, 0x26, 0x85, 0x23, 0x08, 0x0 } }, + { 0x5e, 0x64, { 0x122, 1431, 100, 3, 6, 0x00, 0xae, 0x26, 0x85, 0x23, 0x08, 0x0 } }, + { 0x65, 0x7f, { 0x123, -105, 100, 3, 6, 0x00, 0xae, 0x26, 0x85, 0x23, 0x08, 0x0 } }, + { 0x00, 0x7f, { 0x124, 4034, 100, -3, 2, 0x00, 0xea, 0x22, 0x85, 0x23, 0x08, 0x0 }} +}; +static const YRW801_REGION_DATA regions_40[] = { + /* Soprano Sax */ + { 0x15, 0x3f, { 0x0e3, 4228, 100, 0, 1, 0x00, 0xc8, 0x21, 0xf5, 0x20, 0x0a, 0x0 }}, + { 0x40, 0x45, { 0x0e4, 3495, 100, 0, 1, 0x00, 0xc8, 0x21, 0xf5, 0x20, 0x0a, 0x0 }}, + { 0x46, 0x4b, { 0x0e5, 2660, 100, 0, 1, 0x00, 0xd6, 0x21, 0xf5, 0x20, 0x0a, 0x0 }}, + { 0x4c, 0x51, { 0x0e6, 2002, 100, 0, 1, 0x00, 0xd6, 0x21, 0xf5, 0x20, 0x0a, 0x0 }}, + { 0x52, 0x59, { 0x0e7, 1186, 100, 0, 1, 0x00, 0xd6, 0x21, 0xf5, 0x20, 0x0a, 0x0 }}, + { 0x59, 0x6c, { 0x0e8, 1730, 100, 0, 1, 0x00, 0xc8, 0x21, 0xf5, 0x20, 0x0a, 0x0 }} +}; +static const YRW801_REGION_DATA regions_41[] = { + /* Alto Sax */ + { 0x15, 0x32, { 0x092, 6204, 100, 0, 1, 0x00, 0xbe, 0x19, 0xf5, 0x20, 0x0b, 0x0 }}, + { 0x33, 0x35, { 0x096, 5812, 100, 0, 1, 0x00, 0xbe, 0x19, 0xf5, 0x20, 0x0b, 0x0 }}, + { 0x36, 0x3a, { 0x099, 5318, 100, 0, 1, 0x00, 0xbe, 0x19, 0xf5, 0x20, 0x0b, 0x0 }}, + { 0x3b, 0x3b, { 0x08f, 5076, 100, 0, 1, 0x00, 0xbe, 0x19, 0xf5, 0x20, 0x0b, 0x0 }}, + { 0x3c, 0x3e, { 0x093, 4706, 100, 0, 1, 0x00, 0xbe, 0x19, 0xf5, 0x20, 0x0b, 0x0 }}, + { 0x3f, 0x41, { 0x097, 4321, 100, 0, 1, 0x00, 0xbe, 0x19, 0xf5, 0x20, 0x0b, 0x0 }}, + { 0x42, 0x44, { 0x09a, 3893, 100, 0, 1, 0x00, 0xbe, 0x19, 0xf5, 0x20, 0x0b, 0x0 }}, + { 0x45, 0x47, { 0x090, 3497, 100, 0, 1, 0x00, 0xbe, 0x19, 0xf5, 0x20, 0x0b, 0x0 }}, + { 0x48, 0x4a, { 0x094, 3119, 100, 0, 1, 0x00, 0xbe, 0x19, 0xf5, 0x20, 0x0b, 0x0 }}, + { 0x4b, 0x4d, { 0x098, 2726, 100, 0, 1, 0x00, 0xbe, 0x19, 0xf5, 0x20, 0x0b, 0x0 }}, + { 0x4e, 0x50, { 0x09b, 2393, 100, 0, 1, 0x00, 0xbe, 0x19, 0xf5, 0x20, 0x0b, 0x0 }}, + { 0x51, 0x53, { 0x091, 2088, 100, 0, 1, 0x00, 0xbe, 0x19, 0xf5, 0x20, 0x0b, 0x0 }}, + { 0x54, 0x6c, { 0x095, 1732, 100, 0, 1, 0x00, 0xbe, 0x19, 0xf5, 0x20, 0x0b, 0x0 }} +}; +static const YRW801_REGION_DATA regions_42[] = { + /* Tenor Sax */ + { 0x24, 0x30, { 0x0e9, 6301, 100, 0, 1, 0x00, 0xbc, 0x19, 0xf4, 0x10, 0x0b, 0x0 }}, + { 0x31, 0x34, { 0x0ea, 5781, 100, 0, 1, 0x00, 0xbc, 0x19, 0xf4, 0x10, 0x0b, 0x0 }}, + { 0x35, 0x3a, { 0x0eb, 5053, 100, 0, 1, 0x00, 0xbc, 0x19, 0xf4, 0x10, 0x0b, 0x0 }}, + { 0x3b, 0x41, { 0x0ed, 4165, 100, 0, 1, 0x00, 0xbc, 0x19, 0xf4, 0x10, 0x0b, 0x0 }}, + { 0x42, 0x47, { 0x0ec, 3218, 100, 0, 1, 0x00, 0xbc, 0x19, 0xf4, 0x10, 0x0b, 0x0 }}, + { 0x48, 0x51, { 0x0ee, 2462, 100, 0, 1, 0x00, 0xbc, 0x19, 0xf4, 0x10, 0x0b, 0x0 }}, + { 0x52, 0x6c, { 0x0ef, 1421, 100, 0, 1, 0x00, 0xbc, 0x19, 0xf4, 0x10, 0x0b, 0x0 }} +}; +static const YRW801_REGION_DATA regions_43[] = { + /* Baritone Sax */ + { 0x15, 0x2d, { 0x0df, 6714, 100, 0, 1, 0x00, 0xce, 0x19, 0xf0, 0x00, 0x0a, 0x0 }}, + { 0x2e, 0x34, { 0x0e1, 5552, 100, 0, 1, 0x00, 0xce, 0x19, 0xf0, 0x00, 0x0a, 0x0 }}, + { 0x35, 0x39, { 0x0e2, 5178, 100, 0, 1, 0x00, 0xce, 0x19, 0xf0, 0x00, 0x0a, 0x0 }}, + { 0x3a, 0x6c, { 0x0e0, 4437, 100, 0, 1, 0x00, 0xce, 0x19, 0xf0, 0x00, 0x0a, 0x0 }} +}; +static const YRW801_REGION_DATA regions_44[] = { + /* Oboe */ + { 0x15, 0x3c, { 0x042, 4493, 100, 0, 1, 0x00, 0xe6, 0x39, 0xf4, 0x10, 0x0a, 0x0 }}, + { 0x3d, 0x43, { 0x044, 3702, 100, 0, 1, 0x00, 0xdc, 0x39, 0xf4, 0x10, 0x0a, 0x0 }}, + { 0x44, 0x49, { 0x043, 2956, 100, 0, 1, 0x00, 0xdc, 0x39, 0xf4, 0x10, 0x0a, 0x0 }}, + { 0x4a, 0x4f, { 0x046, 2166, 100, 0, 1, 0x00, 0xdc, 0x39, 0xf4, 0x10, 0x0a, 0x0 }}, + { 0x50, 0x55, { 0x045, 1420, 100, 0, 1, 0x00, 0xdc, 0x39, 0xf4, 0x10, 0x0a, 0x0 }}, + { 0x56, 0x6c, { 0x047, 630, 100, 0, 1, 0x00, 0xe6, 0x39, 0xf4, 0x10, 0x0a, 0x0 } } +}; +static const YRW801_REGION_DATA regions_45[] = { + /* English Horn */ + { 0x15, 0x38, { 0x03c, 5098, 100, 0, 1, 0x00, 0xc4, 0x31, 0xf0, 0x00, 0x09, 0x0 }}, + { 0x39, 0x3e, { 0x03b, 4291, 100, 0, 1, 0x00, 0xc4, 0x31, 0xf0, 0x00, 0x09, 0x0 }}, + { 0x3f, 0x6c, { 0x03d, 3540, 100, 0, 1, 0x00, 0xc4, 0x31, 0xf0, 0x00, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_46[] = { + /* Bassoon */ + { 0x15, 0x22, { 0x038, 7833, 100, 0, 1, 0x00, 0xc6, 0x31, 0xf0, 0x00, 0x0b, 0x0 }}, + { 0x23, 0x2e, { 0x03a, 7070, 100, 0, 1, 0x00, 0xc6, 0x31, 0xf0, 0x00, 0x0b, 0x0 }}, + { 0x2f, 0x6c, { 0x039, 6302, 100, 0, 1, 0x00, 0xc6, 0x31, 0xf0, 0x00, 0x0b, 0x0 }} +}; +static const YRW801_REGION_DATA regions_47[] = { + /* Clarinet */ + { 0x15, 0x3b, { 0x09e, 5900, 100, 0, 1, 0x00, 0xc8, 0x29, 0xf3, 0x20, 0x0a, 0x0 }}, + { 0x3c, 0x41, { 0x0a0, 5158, 100, 0, 1, 0x00, 0xc8, 0x29, 0xf3, 0x20, 0x0a, 0x0 }}, + { 0x42, 0x4a, { 0x09f, 4260, 100, 0, 1, 0x00, 0xc8, 0x29, 0xf3, 0x20, 0x0a, 0x0 }}, + { 0x4b, 0x6c, { 0x0a1, 2957, 100, 0, 1, 0x00, 0xc8, 0x29, 0xf3, 0x20, 0x0a, 0x0 }} +}; +static const YRW801_REGION_DATA regions_48[] = { + /* Piccolo */ + { 0x15, 0x40, { 0x071, 4803, 100, 0, 0, 0x00, 0xe6, 0x38, 0xf0, 0x00, 0x0a, 0x2 }}, + { 0x41, 0x4d, { 0x072, 3314, 100, 0, 0, 0x00, 0xe6, 0x38, 0xf0, 0x00, 0x0a, 0x2 }}, + { 0x4e, 0x53, { 0x073, 1731, 100, 0, 0, 0x00, 0xe6, 0x38, 0xf0, 0x00, 0x0a, 0x2 }}, + { 0x54, 0x5f, { 0x074, 2085, 100, 0, 0, 0x00, 0xe6, 0x38, 0xf0, 0x00, 0x0a, 0x2 }}, + { 0x60, 0x6c, { 0x075, 1421, 100, 0, 0, 0x00, 0xe6, 0x38, 0xf0, 0x00, 0x0a, 0x2 }} +}; +static const YRW801_REGION_DATA regions_49[] = { + /* Flute */ + { 0x15, 0x40, { 0x071, 4803, 100, 0, 0, 0x00, 0xdc, 0x38, 0xf0, 0x00, 0x0a, 0x2 }}, + { 0x41, 0x4d, { 0x072, 3314, 100, 0, 0, 0x00, 0xdc, 0x38, 0xf0, 0x00, 0x0a, 0x2 }}, + { 0x4e, 0x6c, { 0x073, 1731, 100, 0, 0, 0x00, 0xe6, 0x38, 0xf0, 0x00, 0x0a, 0x2 }} +}; +static const YRW801_REGION_DATA regions_4a[] = { + /* Recorder */ + { 0x15, 0x6f, { 0x0bd, 4897, 100, 0, 0, 0x00, 0xec, 0x30, 0x70, 0x00, 0x09, 0x1 }} +}; +static const YRW801_REGION_DATA regions_4b[] = { + /* Pan Flute */ + { 0x15, 0x6c, { 0x077, 2359, 100, 0, 0, 0x00, 0xde, 0x38, 0xf0, 0x00, 0x09, 0x3 }} +}; +static const YRW801_REGION_DATA regions_4c[] = { + /* Bottle Blow */ + { 0x15, 0x6c, { 0x077, 2359, 100, 0, 0, 0x00, 0xc8, 0x38, 0xf0, 0x00, 0x09, 0x1 }}, + { 0x01, 0x7f, { 0x125, 7372, 100, 0, 0, 0x1e, 0x80, 0x00, 0xf0, 0x00, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_4d[] = { + /* Shakuhachi */ + { 0x00, 0x7f, { 0x0ab, 4548, 100, 0, 0, 0x00, 0xd6, 0x30, 0xf0, 0x00, 0x0a, 0x3 }}, + { 0x15, 0x6c, { 0x076, 3716, 100, 0, 0, 0x00, 0xa2, 0x28, 0x70, 0x00, 0x09, 0x2 }} +}; +static const YRW801_REGION_DATA regions_4e[] = { + /* Whistle */ + { 0x00, 0x7f, { 0x0aa, 1731, 100, 0, 4, 0x00, 0xd2, 0x2c, 0x70, 0x00, 0x0a, 0x0 }} +}; +static const YRW801_REGION_DATA regions_4f[] = { + /* Ocarina */ + { 0x00, 0x7f, { 0x0aa, 1731, 100, 0, 1, 0x00, 0xce, 0x29, 0x90, 0x00, 0x0a, 0x1 }} +}; +static const YRW801_REGION_DATA regions_50[] = { + /* Square Lead */ + { 0x01, 0x2a, { 0x0cc, 9853, 100, 3, 0, 0x00, 0xac, 0x38, 0xc6, 0x21, 0x09, 0x0 } }, + { 0x2b, 0x36, { 0x0cd, 6785, 100, 3, 0, 0x00, 0xac, 0x38, 0xc6, 0x21, 0x09, 0x0 } }, + { 0x37, 0x42, { 0x0ca, 5248, 100, 3, 0, 0x00, 0xac, 0x38, 0xc6, 0x21, 0x09, 0x0 } }, + { 0x43, 0x4e, { 0x0cf, 3713, 100, 3, 0, 0x00, 0xac, 0x38, 0xc6, 0x21, 0x09, 0x0 } }, + { 0x4f, 0x5a, { 0x0ce, 2176, 100, 3, 0, 0x00, 0xac, 0x38, 0xc6, 0x21, 0x09, 0x0 } }, + { 0x5b, 0x7f, { 0x0cb, 640, 100, 3, 0, 0x00, 0xac, 0x38, 0xc6, 0x21, 0x09, 0x0 } }, + { 0x01, 0x2a, { 0x0cc, 9844, 100, -3, 0, 0x00, 0xac, 0x08, 0xc6, 0x21, 0x09, 0x0 }}, + { 0x2b, 0x36, { 0x0cd, 6776, 100, -3, 0, 0x00, 0xac, 0x08, 0xc6, 0x21, 0x09, 0x0 }}, + { 0x37, 0x42, { 0x0ca, 5239, 100, -3, 0, 0x00, 0xac, 0x08, 0xc6, 0x21, 0x09, 0x0 }}, + { 0x43, 0x4e, { 0x0cf, 3704, 100, -3, 0, 0x00, 0xac, 0x08, 0xc6, 0x21, 0x09, 0x0 }}, + { 0x4f, 0x5a, { 0x0ce, 2167, 100, -3, 0, 0x00, 0xac, 0x08, 0xc6, 0x21, 0x09, 0x0 }}, + { 0x5b, 0x7f, { 0x0cb, 631, 100, -3, 0, 0x00, 0xac, 0x08, 0xc6, 0x21, 0x09, 0x0 } } +}; +static const YRW801_REGION_DATA regions_51[] = { + /* Sawtooth Lead */ + { 0x01, 0x27, { 0x118, 9108, 100, 0, 0, 0x00, 0xc8, 0x30, 0xf2, 0x22, 0x0a, 0x0 }}, + { 0x28, 0x2d, { 0x119, 8345, 100, 0, 0, 0x00, 0xc8, 0x30, 0xf2, 0x22, 0x0a, 0x0 }}, + { 0x2e, 0x33, { 0x11a, 7570, 100, 0, 0, 0x00, 0xc8, 0x30, 0xf2, 0x22, 0x0a, 0x0 }}, + { 0x34, 0x39, { 0x11b, 6809, 100, 0, 0, 0x00, 0xc8, 0x30, 0xf2, 0x22, 0x0a, 0x0 }}, + { 0x3a, 0x3f, { 0x11c, 6047, 100, 0, 0, 0x00, 0xc8, 0x30, 0xf2, 0x22, 0x0a, 0x0 }}, + { 0x40, 0x45, { 0x11d, 5282, 100, 0, 0, 0x00, 0xc8, 0x30, 0xf2, 0x22, 0x0a, 0x0 }}, + { 0x46, 0x4b, { 0x11e, 4525, 100, 0, 0, 0x00, 0xc8, 0x30, 0xf2, 0x22, 0x0a, 0x0 }}, + { 0x4c, 0x51, { 0x11f, 3746, 100, 0, 0, 0x00, 0xc8, 0x30, 0xf2, 0x22, 0x0a, 0x0 }}, + { 0x52, 0x57, { 0x120, 3017, 100, 0, 0, 0x00, 0xc8, 0x30, 0xf2, 0x22, 0x0a, 0x0 }}, + { 0x58, 0x5d, { 0x121, 2171, 100, 0, 0, 0x00, 0xc8, 0x30, 0xf2, 0x22, 0x0a, 0x0 }}, + { 0x5e, 0x66, { 0x122, 1426, 100, 0, 0, 0x00, 0xc8, 0x30, 0xf2, 0x22, 0x0a, 0x0 }}, + { 0x67, 0x7f, { 0x123, -110, 100, 0, 0, 0x00, 0xc8, 0x30, 0xf2, 0x22, 0x0a, 0x0 }}, + { 0x01, 0x27, { 0x118, 9098, 100, 0, 0, 0x00, 0xc8, 0x30, 0xf2, 0x22, 0x0a, 0x0 }}, + { 0x28, 0x2d, { 0x119, 8335, 100, 0, 0, 0x00, 0xc8, 0x30, 0xf2, 0x22, 0x0a, 0x0 }}, + { 0x2e, 0x33, { 0x11a, 7560, 100, 0, 0, 0x00, 0xc8, 0x30, 0xf2, 0x22, 0x0a, 0x0 }}, + { 0x34, 0x39, { 0x11b, 6799, 100, 0, 0, 0x00, 0xc8, 0x30, 0xf2, 0x22, 0x0a, 0x0 }}, + { 0x3a, 0x3f, { 0x11c, 6037, 100, 0, 0, 0x00, 0xc8, 0x30, 0xf2, 0x22, 0x0a, 0x0 }}, + { 0x40, 0x45, { 0x11d, 5272, 100, 0, 0, 0x00, 0xc8, 0x30, 0xf2, 0x22, 0x0a, 0x0 }}, + { 0x46, 0x4b, { 0x11e, 4515, 100, 0, 0, 0x00, 0xc8, 0x30, 0xf2, 0x22, 0x0a, 0x0 }}, + { 0x4c, 0x51, { 0x11f, 3736, 100, 0, 0, 0x00, 0xc8, 0x30, 0xf2, 0x22, 0x0a, 0x0 }}, + { 0x52, 0x57, { 0x120, 3007, 100, 0, 0, 0x00, 0xc8, 0x30, 0xf2, 0x22, 0x0a, 0x0 }}, + { 0x58, 0x5d, { 0x121, 2161, 100, 0, 0, 0x00, 0xc8, 0x30, 0xf2, 0x22, 0x0a, 0x0 }}, + { 0x5e, 0x66, { 0x122, 1416, 100, 0, 0, 0x00, 0xc8, 0x30, 0xf2, 0x22, 0x0a, 0x0 }}, + { 0x67, 0x7f, { 0x123, -120, 100, 0, 0, 0x00, 0xc8, 0x30, 0xf2, 0x22, 0x0a, 0x0 }} +}; +static const YRW801_REGION_DATA regions_52[] = { + /* Calliope Lead */ + { 0x00, 0x7f, { 0x0aa, 1731, 100, 0, 0, 0x00, 0xc2, 0x28, 0x90, 0x00, 0x0a, 0x2 }}, + { 0x15, 0x6c, { 0x076, 3716, 100, 0, 0, 0x00, 0xb6, 0x28, 0xb0, 0x00, 0x09, 0x2 }} +}; +static const YRW801_REGION_DATA regions_53[] = { + /* Chiffer Lead */ + { 0x00, 0x7f, { 0x13a, 3665, 100, 0, 2, 0x00, 0xcc, 0x2a, 0xf0, 0x10, 0x09, 0x1 }}, + { 0x01, 0x7f, { 0x0fe, 3660, 100, 0, 0, 0x00, 0xbe, 0x28, 0xf3, 0x10, 0x17, 0x0 }} +}; +static const YRW801_REGION_DATA regions_54[] = { + /* Charang Lead */ + { 0x00, 0x40, { 0x0a5, 6594, 100, 0, 3, 0x00, 0xba, 0x33, 0xf2, 0x11, 0x09, 0x0 }}, + { 0x41, 0x7f, { 0x0a6, 5433, 100, 0, 3, 0x00, 0xba, 0x33, 0xf2, 0x11, 0x09, 0x0 }}, + { 0x01, 0x27, { 0x118, 9098, 100, 0, 2, 0x00, 0xa4, 0x2a, 0xf2, 0x22, 0x0e, 0x0 }}, + { 0x28, 0x2d, { 0x119, 8335, 100, 0, 2, 0x00, 0xa4, 0x2a, 0xf2, 0x22, 0x0e, 0x0 }}, + { 0x2e, 0x33, { 0x11a, 7560, 100, 0, 2, 0x00, 0xa4, 0x2a, 0xf2, 0x22, 0x0e, 0x0 }}, + { 0x34, 0x39, { 0x11b, 6799, 100, 0, 2, 0x00, 0xa4, 0x2a, 0xf2, 0x22, 0x0e, 0x0 }}, + { 0x3a, 0x3f, { 0x11c, 6037, 100, 0, 2, 0x00, 0xa4, 0x2a, 0xf2, 0x22, 0x0e, 0x0 }}, + { 0x40, 0x45, { 0x11d, 5272, 100, 0, 2, 0x00, 0xa4, 0x2a, 0xf2, 0x22, 0x0e, 0x0 }}, + { 0x46, 0x4b, { 0x11e, 4515, 100, 0, 2, 0x00, 0xa4, 0x2a, 0xf2, 0x22, 0x0e, 0x0 }}, + { 0x4c, 0x51, { 0x11f, 3736, 100, 0, 2, 0x00, 0xa4, 0x2a, 0xf2, 0x22, 0x0e, 0x0 }}, + { 0x52, 0x57, { 0x120, 3007, 100, 0, 2, 0x00, 0xa4, 0x2a, 0xf2, 0x22, 0x0e, 0x0 }}, + { 0x58, 0x5d, { 0x121, 2161, 100, 0, 2, 0x00, 0xa4, 0x2a, 0xf2, 0x22, 0x0e, 0x0 }}, + { 0x5e, 0x66, { 0x122, 1416, 100, 0, 2, 0x00, 0xa4, 0x2a, 0xf2, 0x22, 0x0e, 0x0 }}, + { 0x67, 0x7f, { 0x123, -120, 100, 0, 2, 0x00, 0xa4, 0x2a, 0xf2, 0x22, 0x0e, 0x0 }} +}; +static const YRW801_REGION_DATA regions_55[] = { + /* Voice Lead */ + { 0x00, 0x7f, { 0x0aa, 1739, 100, 0, 6, 0x00, 0x8c, 0x2e, 0x90, 0x00, 0x0a, 0x0 }}, + { 0x15, 0x6c, { 0x02a, 3474, 100, 0, 1, 0x00, 0xd8, 0x29, 0xf0, 0x05, 0x0a, 0x0 }} +}; +static const YRW801_REGION_DATA regions_56[] = { + /* 5ths Lead */ + { 0x01, 0x27, { 0x118, 8468, 100, 0, 2, 0x00, 0xd0, 0x32, 0xf5, 0x20, 0x08, 0x0 }}, + { 0x28, 0x2d, { 0x119, 7705, 100, 0, 2, 0x00, 0xd0, 0x32, 0xf5, 0x20, 0x08, 0x0 }}, + { 0x2e, 0x33, { 0x11a, 6930, 100, 0, 2, 0x00, 0xd0, 0x32, 0xf5, 0x20, 0x08, 0x0 }}, + { 0x34, 0x39, { 0x11b, 6169, 100, 0, 2, 0x00, 0xd0, 0x32, 0xf5, 0x20, 0x08, 0x0 }}, + { 0x3a, 0x3f, { 0x11c, 5407, 100, 0, 2, 0x00, 0xd0, 0x32, 0xf5, 0x20, 0x08, 0x0 }}, + { 0x40, 0x45, { 0x11d, 4642, 100, 0, 2, 0x00, 0xd0, 0x32, 0xf5, 0x20, 0x08, 0x0 }}, + { 0x46, 0x4b, { 0x11e, 3885, 100, 0, 2, 0x00, 0xd0, 0x32, 0xf5, 0x20, 0x08, 0x0 }}, + { 0x4c, 0x51, { 0x11f, 3106, 100, 0, 2, 0x00, 0xd0, 0x32, 0xf5, 0x20, 0x08, 0x0 }}, + { 0x52, 0x57, { 0x120, 2377, 100, 0, 2, 0x00, 0xd0, 0x32, 0xf5, 0x20, 0x08, 0x0 }}, + { 0x58, 0x5d, { 0x121, 1531, 100, 0, 2, 0x00, 0xd0, 0x32, 0xf5, 0x20, 0x08, 0x0 }}, + { 0x5e, 0x64, { 0x122, 786, 100, 0, 2, 0x00, 0xd0, 0x32, 0xf5, 0x20, 0x08, 0x0 } }, + { 0x65, 0x7f, { 0x123, -750, 100, 0, 2, 0x00, 0xd0, 0x32, 0xf5, 0x20, 0x08, 0x0 }}, + { 0x05, 0x71, { 0x002, 4503, 100, 0, 1, 0x00, 0xb8, 0x31, 0xb3, 0x20, 0x0b, 0x0 }} +}; +static const YRW801_REGION_DATA regions_57[] = { + /* Bass & Lead */ + { 0x00, 0x7f, { 0x117, 8109, 100, 0, 1, 0x00, 0xbc, 0x29, 0xf3, 0x50, 0x08, 0x0 }}, + { 0x01, 0x27, { 0x118, 9097, 100, 0, 2, 0x00, 0xbc, 0x2a, 0xf2, 0x20, 0x0a, 0x0 }}, + { 0x28, 0x2d, { 0x119, 8334, 100, 0, 2, 0x00, 0xbc, 0x2a, 0xf2, 0x20, 0x0a, 0x0 }}, + { 0x2e, 0x33, { 0x11a, 7559, 100, 0, 2, 0x00, 0xbc, 0x2a, 0xf2, 0x20, 0x0a, 0x0 }}, + { 0x34, 0x39, { 0x11b, 6798, 100, 0, 2, 0x00, 0xbc, 0x2a, 0xf2, 0x20, 0x0a, 0x0 }}, + { 0x3a, 0x3f, { 0x11c, 6036, 100, 0, 2, 0x00, 0xbc, 0x2a, 0xf2, 0x20, 0x0a, 0x0 }}, + { 0x40, 0x45, { 0x11d, 5271, 100, 0, 2, 0x00, 0xbc, 0x2a, 0xf2, 0x20, 0x0a, 0x0 }}, + { 0x46, 0x4b, { 0x11e, 4514, 100, 0, 2, 0x00, 0xbc, 0x2a, 0xf2, 0x20, 0x0a, 0x0 }}, + { 0x4c, 0x51, { 0x11f, 3735, 100, 0, 2, 0x00, 0xbc, 0x2a, 0xf2, 0x20, 0x0a, 0x0 }}, + { 0x52, 0x57, { 0x120, 3006, 100, 0, 2, 0x00, 0xbc, 0x2a, 0xf2, 0x20, 0x0a, 0x0 }}, + { 0x58, 0x5d, { 0x121, 2160, 100, 0, 2, 0x00, 0xbc, 0x2a, 0xf2, 0x20, 0x0a, 0x0 }}, + { 0x5e, 0x66, { 0x122, 1415, 100, 0, 2, 0x00, 0xbc, 0x2a, 0xf2, 0x20, 0x0a, 0x0 }}, + { 0x67, 0x7f, { 0x123, -121, 100, 0, 2, 0x00, 0xbc, 0x2a, 0xf2, 0x20, 0x0a, 0x0 }} +}; +static const YRW801_REGION_DATA regions_58[] = { + /* New Age Pad */ + { 0x15, 0x6c, { 0x002, 4501, 100, 0, 4, 0x00, 0xa4, 0x24, 0x80, 0x01, 0x05, 0x0 }}, + { 0x15, 0x6c, { 0x0f3, 4253, 100, 0, 3, 0x00, 0x8c, 0x23, 0xa2, 0x14, 0x06, 0x1 }} +}; +static const YRW801_REGION_DATA regions_59[] = { + /* Warm Pad */ + { 0x15, 0x6c, { 0x04e, 5306, 100, 2, 2, 0x00, 0x92, 0x2a, 0x34, 0x23, 0x05, 0x2 } }, + { 0x15, 0x6c, { 0x029, 3575, 100, -2, 2, 0x00, 0xbe, 0x22, 0x31, 0x23, 0x06, 0x0 }} +}; +static const YRW801_REGION_DATA regions_5a[] = { + /* Polysynth Pad */ + { 0x01, 0x27, { 0x118, 9111, 100, 0, 3, 0x00, 0xae, 0x23, 0xf2, 0x20, 0x07, 0x1 }}, + { 0x28, 0x2d, { 0x119, 8348, 100, 0, 3, 0x00, 0xae, 0x23, 0xf2, 0x20, 0x07, 0x1 }}, + { 0x2e, 0x33, { 0x11a, 7573, 100, 0, 3, 0x00, 0xae, 0x23, 0xf2, 0x20, 0x07, 0x1 }}, + { 0x34, 0x39, { 0x11b, 6812, 100, 0, 3, 0x00, 0xae, 0x23, 0xf2, 0x20, 0x07, 0x1 }}, + { 0x3a, 0x3f, { 0x11c, 6050, 100, 0, 3, 0x00, 0xae, 0x23, 0xf2, 0x20, 0x07, 0x1 }}, + { 0x40, 0x45, { 0x11d, 5285, 100, 0, 3, 0x00, 0xae, 0x23, 0xf2, 0x20, 0x07, 0x1 }}, + { 0x46, 0x4b, { 0x11e, 4528, 100, 0, 3, 0x00, 0xae, 0x23, 0xf2, 0x20, 0x07, 0x1 }}, + { 0x4c, 0x51, { 0x11f, 3749, 100, 0, 3, 0x00, 0xae, 0x23, 0xf2, 0x20, 0x07, 0x1 }}, + { 0x52, 0x57, { 0x120, 3020, 100, 0, 3, 0x00, 0xae, 0x23, 0xf2, 0x20, 0x07, 0x1 }}, + { 0x58, 0x5d, { 0x121, 2174, 100, 0, 3, 0x00, 0xae, 0x23, 0xf2, 0x20, 0x07, 0x1 }}, + { 0x5e, 0x66, { 0x122, 1429, 100, 0, 3, 0x00, 0xae, 0x23, 0xf2, 0x20, 0x07, 0x1 }}, + { 0x67, 0x7f, { 0x123, -107, 100, 0, 3, 0x00, 0xae, 0x23, 0xf2, 0x20, 0x07, 0x1 }}, + { 0x00, 0x7f, { 0x124, 4024, 100, 0, 2, 0x00, 0xae, 0x22, 0xe5, 0x20, 0x08, 0x0 }} +}; +static const YRW801_REGION_DATA regions_5b[] = { + /* Choir Pad */ + { 0x15, 0x3a, { 0x018, 5010, 100, 0, 5, 0x00, 0xb0, 0x25, 0x70, 0x00, 0x06, 0x0 }}, + { 0x3b, 0x40, { 0x019, 4370, 100, 0, 5, 0x00, 0xb0, 0x25, 0x70, 0x00, 0x06, 0x0 }}, + { 0x41, 0x47, { 0x01a, 3478, 100, 0, 5, 0x00, 0xb0, 0x25, 0x70, 0x00, 0x06, 0x0 }}, + { 0x48, 0x6c, { 0x01b, 2197, 100, 0, 5, 0x00, 0xb0, 0x25, 0x70, 0x00, 0x06, 0x0 }}, + { 0x15, 0x6c, { 0x02a, 3482, 100, 0, 4, 0x00, 0x98, 0x24, 0x65, 0x21, 0x06, 0x0 }} +}; +static const YRW801_REGION_DATA regions_5c[] = { + /* Bowed Pad */ + { 0x15, 0x6c, { 0x101, 4790, 100, -1, 1, 0x00, 0xbe, 0x19, 0x44, 0x14, 0x16, 0x0 }}, + { 0x00, 0x7f, { 0x0aa, 1720, 100, 1, 1, 0x00, 0x94, 0x19, 0x40, 0x00, 0x06, 0x0 } } +}; +static const YRW801_REGION_DATA regions_5d[] = { + /* Metallic Pad */ + { 0x15, 0x31, { 0x00c, 6943, 100, 0, 2, 0x00, 0xa0, 0x0a, 0x60, 0x03, 0x06, 0x0 }}, + { 0x32, 0x38, { 0x00d, 5416, 100, 0, 2, 0x00, 0xa0, 0x0a, 0x60, 0x03, 0x06, 0x0 }}, + { 0x39, 0x47, { 0x00e, 4385, 100, 0, 2, 0x00, 0xa0, 0x0a, 0x60, 0x03, 0x06, 0x0 }}, + { 0x48, 0x6c, { 0x00f, 2849, 100, 0, 2, 0x00, 0xa0, 0x0a, 0x60, 0x03, 0x06, 0x0 }}, + { 0x00, 0x7f, { 0x03f, 4224, 100, 0, 1, 0x00, 0x9c, 0x31, 0x65, 0x16, 0x07, 0x0 }} +}; +static const YRW801_REGION_DATA regions_5e[] = { + /* Halo Pad */ + { 0x00, 0x7f, { 0x124, 4038, 100, 0, 2, 0x00, 0xa6, 0x1a, 0x85, 0x23, 0x08, 0x0 }}, + { 0x15, 0x6c, { 0x02a, 3471, 100, 0, 3, 0x00, 0xc0, 0x1b, 0xc0, 0x05, 0x06, 0x0 }} +}; +static const YRW801_REGION_DATA regions_5f[] = { + /* Sweep Pad */ + { 0x01, 0x27, { 0x0d3, 9100, 100, 0, 1, 0x00, 0xce, 0x19, 0x13, 0x11, 0x06, 0x0 }}, + { 0x28, 0x2d, { 0x0da, 8341, 100, 0, 1, 0x00, 0xce, 0x19, 0x13, 0x11, 0x06, 0x0 }}, + { 0x2e, 0x33, { 0x0d4, 7564, 100, 0, 1, 0x00, 0xce, 0x19, 0x13, 0x11, 0x06, 0x0 }}, + { 0x34, 0x39, { 0x0db, 6791, 100, 0, 1, 0x00, 0xce, 0x19, 0x13, 0x11, 0x06, 0x0 }}, + { 0x3a, 0x3f, { 0x0d5, 6048, 100, 0, 1, 0x00, 0xce, 0x19, 0x13, 0x11, 0x06, 0x0 }}, + { 0x40, 0x45, { 0x0dc, 5263, 100, 0, 1, 0x00, 0xce, 0x19, 0x13, 0x11, 0x06, 0x0 }}, + { 0x46, 0x4b, { 0x0d6, 4499, 100, 0, 1, 0x00, 0xce, 0x19, 0x13, 0x11, 0x06, 0x0 }}, + { 0x4c, 0x51, { 0x0dd, 3747, 100, 0, 1, 0x00, 0xce, 0x19, 0x13, 0x11, 0x06, 0x0 }}, + { 0x52, 0x57, { 0x0d7, 3018, 100, 0, 1, 0x00, 0xce, 0x19, 0x13, 0x11, 0x06, 0x0 }}, + { 0x58, 0x5d, { 0x0de, 2173, 100, 0, 1, 0x00, 0xce, 0x19, 0x13, 0x11, 0x06, 0x0 }}, + { 0x5e, 0x63, { 0x0d8, 1427, 100, 0, 1, 0x00, 0xce, 0x19, 0x13, 0x11, 0x06, 0x0 }}, + { 0x64, 0x7f, { 0x0d9, -109, 100, 0, 1, 0x00, 0xce, 0x19, 0x13, 0x11, 0x06, 0x0 }}, + { 0x01, 0x27, { 0x0d3, 9088, 100, 0, 0, 0x00, 0xce, 0x18, 0x13, 0x11, 0x06, 0x0 }}, + { 0x28, 0x2d, { 0x0da, 8329, 100, 0, 0, 0x00, 0xce, 0x18, 0x13, 0x11, 0x06, 0x0 }}, + { 0x2e, 0x33, { 0x0d4, 7552, 100, 0, 0, 0x00, 0xce, 0x18, 0x13, 0x11, 0x06, 0x0 }}, + { 0x34, 0x39, { 0x0db, 6779, 100, 0, 0, 0x00, 0xce, 0x18, 0x13, 0x11, 0x06, 0x0 }}, + { 0x3a, 0x3f, { 0x0d5, 6036, 100, 0, 0, 0x00, 0xce, 0x18, 0x13, 0x11, 0x06, 0x0 }}, + { 0x40, 0x45, { 0x0dc, 5251, 100, 0, 0, 0x00, 0xce, 0x18, 0x13, 0x11, 0x06, 0x0 }}, + { 0x46, 0x4b, { 0x0d6, 4487, 100, 0, 0, 0x00, 0xce, 0x18, 0x13, 0x11, 0x06, 0x0 }}, + { 0x4c, 0x51, { 0x0dd, 3735, 100, 0, 0, 0x00, 0xce, 0x18, 0x13, 0x11, 0x06, 0x0 }}, + { 0x52, 0x57, { 0x0d7, 3006, 100, 0, 0, 0x00, 0xce, 0x18, 0x13, 0x11, 0x06, 0x0 }}, + { 0x58, 0x5d, { 0x0de, 2161, 100, 0, 0, 0x00, 0xce, 0x18, 0x13, 0x11, 0x06, 0x0 }}, + { 0x5e, 0x63, { 0x0d8, 1415, 100, 0, 0, 0x00, 0xce, 0x18, 0x13, 0x11, 0x06, 0x0 }}, + { 0x64, 0x7f, { 0x0d9, -121, 100, 0, 0, 0x00, 0xce, 0x18, 0x13, 0x11, 0x06, 0x0 }} +}; +static const YRW801_REGION_DATA regions_60[] = { + /* Ice Rain */ + { 0x01, 0x7f, { 0x04e, 9345, 100, 0, 2, 0x00, 0xcc, 0x22, 0xa3, 0x63, 0x17, 0x0 }}, + { 0x00, 0x7f, { 0x143, 5586, 20, 0, 2, 0x00, 0x6e, 0x2a, 0xf0, 0x05, 0x05, 0x0 } } +}; +static const YRW801_REGION_DATA regions_61[] = { + /* Soundtrack */ + { 0x15, 0x6c, { 0x002, 4501, 100, 0, 2, 0x00, 0xb6, 0x2a, 0x60, 0x01, 0x05, 0x0 }}, + { 0x15, 0x6c, { 0x0f3, 1160, 100, 0, 5, 0x00, 0xa8, 0x2d, 0x52, 0x14, 0x06, 0x2 }} +}; +static const YRW801_REGION_DATA regions_62[] = { + /* Crystal */ + { 0x15, 0x6c, { 0x0f3, 1826, 100, 0, 3, 0x00, 0xb8, 0x33, 0xf6, 0x25, 0x25, 0x0 }}, + { 0x15, 0x2c, { 0x06d, 7454, 100, 0, 3, 0x00, 0xac, 0x3b, 0x85, 0x24, 0x06, 0x0 }}, + { 0x2d, 0x36, { 0x06e, 5925, 100, 0, 3, 0x00, 0xac, 0x3b, 0x85, 0x24, 0x06, 0x0 }}, + { 0x37, 0x6c, { 0x06f, 4403, 100, 0, 3, 0x09, 0xac, 0x3b, 0x85, 0x24, 0x06, 0x0 }} +}; +static const YRW801_REGION_DATA regions_63[] = { + /* Atmosphere */ + { 0x05, 0x71, { 0x002, 4509, 100, 0, 2, 0x00, 0xc8, 0x32, 0x73, 0x22, 0x06, 0x1 }}, + { 0x15, 0x2f, { 0x0b3, 6964, 100, 0, 2, 0x05, 0xc2, 0x32, 0xf5, 0x34, 0x07, 0x2 }}, + { 0x30, 0x36, { 0x0b7, 5567, 100, 0, 2, 0x0c, 0xc2, 0x32, 0xf5, 0x34, 0x07, 0x2 }}, + { 0x37, 0x3c, { 0x0b5, 4653, 100, 0, 2, 0x00, 0xc2, 0x32, 0xf6, 0x34, 0x07, 0x2 }}, + { 0x3d, 0x43, { 0x0b4, 3892, 100, 0, 2, 0x00, 0xc2, 0x32, 0xf6, 0x35, 0x07, 0x2 }}, + { 0x44, 0x60, { 0x0b6, 2723, 100, 0, 2, 0x00, 0xc2, 0x32, 0xf6, 0x35, 0x17, 0x2 }} +}; +static const YRW801_REGION_DATA regions_64[] = { + /* Brightness */ + { 0x00, 0x7f, { 0x137, 5285, 100, 0, 2, 0x00, 0xbe, 0x2a, 0xa5, 0x18, 0x08, 0x0 }}, + { 0x15, 0x6c, { 0x02a, 3481, 100, 0, 1, 0x00, 0xc8, 0x29, 0x80, 0x05, 0x05, 0x0 }} +}; +static const YRW801_REGION_DATA regions_65[] = { + /* Goblins */ + { 0x15, 0x6c, { 0x002, 4501, 100, -1, 2, 0x00, 0xca, 0x2a, 0x40, 0x01, 0x05, 0x0 }}, + { 0x15, 0x6c, { 0x009, 9679, 20, 1, 4, 0x00, 0x3c, 0x0c, 0x22, 0x11, 0x06, 0x0 } } +}; +static const YRW801_REGION_DATA regions_66[] = { + /* Echoes */ + { 0x15, 0x6c, { 0x02a, 3487, 100, 0, 3, 0x00, 0xae, 0x2b, 0xf5, 0x21, 0x06, 0x0 }}, + { 0x00, 0x7f, { 0x124, 4027, 100, 0, 3, 0x00, 0xae, 0x2b, 0x85, 0x23, 0x07, 0x0 }} +}; +static const YRW801_REGION_DATA regions_67[] = { + /* Sci-Fi */ + { 0x15, 0x31, { 0x00c, 6940, 100, 0, 3, 0x00, 0xc8, 0x2b, 0x90, 0x05, 0x06, 0x3 }}, + { 0x32, 0x38, { 0x00d, 5413, 100, 0, 3, 0x00, 0xc8, 0x2b, 0x90, 0x05, 0x06, 0x3 }}, + { 0x39, 0x47, { 0x00e, 4382, 100, 0, 3, 0x00, 0xc8, 0x2b, 0x90, 0x05, 0x06, 0x3 }}, + { 0x48, 0x6c, { 0x00f, 2846, 100, 0, 3, 0x00, 0xc8, 0x2b, 0x90, 0x05, 0x06, 0x3 }}, + { 0x15, 0x6c, { 0x002, 4498, 100, 0, 2, 0x00, 0xd4, 0x22, 0x80, 0x01, 0x05, 0x0 }} +}; +static const YRW801_REGION_DATA regions_68[] = { + /* Sitar */ + { 0x00, 0x7f, { 0x10f, 4408, 100, 0, 2, 0x00, 0xc4, 0x32, 0xf4, 0x15, 0x16, 0x1 }} +}; +static const YRW801_REGION_DATA regions_69[] = { + /* Banjo */ + { 0x15, 0x34, { 0x013, 5685, 100, 0, 0, 0x00, 0xdc, 0x38, 0xf6, 0x15, 0x09, 0x0 }}, + { 0x35, 0x38, { 0x014, 5009, 100, 0, 0, 0x00, 0xdc, 0x38, 0xf6, 0x15, 0x09, 0x0 }}, + { 0x39, 0x3c, { 0x012, 4520, 100, 0, 0, 0x00, 0xdc, 0x38, 0xf6, 0x15, 0x09, 0x0 }}, + { 0x3d, 0x44, { 0x015, 3622, 100, 0, 0, 0x00, 0xdc, 0x38, 0xf6, 0x15, 0x09, 0x0 }}, + { 0x45, 0x4c, { 0x017, 2661, 100, 0, 0, 0x00, 0xdc, 0x38, 0xf6, 0x15, 0x09, 0x0 }}, + { 0x4d, 0x6d, { 0x016, 1632, 100, 0, 0, 0x00, 0xdc, 0x38, 0xf6, 0x15, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_6a[] = { + /* Shamisen */ + { 0x15, 0x6c, { 0x10e, 3273, 100, 0, 0, 0x00, 0xc0, 0x28, 0xf7, 0x76, 0x08, 0x0 }} +}; +static const YRW801_REGION_DATA regions_6b[] = { + /* Koto */ + { 0x00, 0x7f, { 0x0a9, 4033, 100, 0, 0, 0x00, 0xc6, 0x20, 0xf0, 0x06, 0x07, 0x0 }} +}; +static const YRW801_REGION_DATA regions_6c[] = { + /* Kalimba */ + { 0x00, 0x7f, { 0x137, 3749, 100, 0, 0, 0x00, 0xce, 0x38, 0xf5, 0x18, 0x08, 0x0 }} +}; +static const YRW801_REGION_DATA regions_6d[] = { + /* Bagpipe */ + { 0x15, 0x39, { 0x0a4, 7683, 100, 0, 4, 0x00, 0xc0, 0x1c, 0xf0, 0x00, 0x09, 0x0 }}, + { 0x15, 0x39, { 0x0a7, 7680, 100, 0, 1, 0x00, 0xaa, 0x19, 0xf0, 0x00, 0x09, 0x0 }}, + { 0x3a, 0x6c, { 0x0a8, 3697, 100, 0, 1, 0x00, 0xaa, 0x19, 0xf0, 0x00, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_6e[] = { + /* Fiddle */ + { 0x15, 0x3a, { 0x105, 5158, 100, 0, 1, 0x00, 0xca, 0x31, 0xf3, 0x20, 0x09, 0x0 }}, + { 0x3b, 0x3f, { 0x102, 4754, 100, 0, 1, 0x00, 0xca, 0x31, 0xf3, 0x20, 0x09, 0x0 }}, + { 0x40, 0x41, { 0x106, 4132, 100, 0, 1, 0x00, 0xca, 0x31, 0xf3, 0x20, 0x09, 0x0 }}, + { 0x42, 0x44, { 0x107, 4033, 100, 0, 1, 0x00, 0xca, 0x31, 0xf3, 0x20, 0x09, 0x0 }}, + { 0x45, 0x47, { 0x108, 3580, 100, 0, 1, 0x00, 0xca, 0x31, 0xf3, 0x20, 0x09, 0x0 }}, + { 0x48, 0x4a, { 0x10a, 2957, 100, 0, 1, 0x00, 0xca, 0x31, 0xf3, 0x20, 0x09, 0x0 }}, + { 0x4b, 0x4c, { 0x10b, 2724, 100, 0, 1, 0x00, 0xca, 0x31, 0xf3, 0x20, 0x09, 0x0 }}, + { 0x4d, 0x4e, { 0x10c, 2530, 100, 0, 1, 0x00, 0xca, 0x31, 0xf3, 0x20, 0x09, 0x0 }}, + { 0x4f, 0x51, { 0x10d, 2166, 100, 0, 1, 0x00, 0xca, 0x31, 0xf3, 0x20, 0x09, 0x0 }}, + { 0x52, 0x6c, { 0x109, 1825, 100, 0, 1, 0x00, 0xca, 0x31, 0xf3, 0x20, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_6f[] = { + /* Shanai */ + { 0x15, 0x6c, { 0x041, 6946, 100, 0, 1, 0x00, 0xc4, 0x31, 0x95, 0x20, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_70[] = { + /* Tinkle Bell */ + { 0x15, 0x73, { 0x0f3, 1821, 100, 0, 3, 0x00, 0xc8, 0x3b, 0xd6, 0x25, 0x25, 0x0 }}, + { 0x00, 0x7f, { 0x137, 5669, 100, 0, 3, 0x00, 0x66, 0x3b, 0xf5, 0x18, 0x08, 0x0 }} +}; +static const YRW801_REGION_DATA regions_71[] = { + /* Agogo */ + { 0x15, 0x74, { 0x00b, 2474, 100, 0, 0, 0x00, 0xd2, 0x38, 0xf0, 0x00, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_72[] = { + /* Steel Drums */ + { 0x01, 0x7f, { 0x0fe, 3670, 100, 0, 0, 0x00, 0xca, 0x38, 0xf3, 0x06, 0x17, 0x1 }}, + { 0x15, 0x6c, { 0x100, 9602, 100, 0, 0, 0x00, 0x54, 0x38, 0xb0, 0x05, 0x16, 0x1 }} +}; +static const YRW801_REGION_DATA regions_73[] = { + /* Woodblock */ + { 0x15, 0x6c, { 0x02c, 2963, 50, 0, 0, 0x07, 0xd4, 0x00, 0xf0, 0x00, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_74[] = { + /* Taiko Drum */ + { 0x13, 0x6c, { 0x03e, 1194, 50, 0, 0, 0x00, 0xaa, 0x38, 0xf0, 0x04, 0x04, 0x0 }} +}; +static const YRW801_REGION_DATA regions_75[] = { + /* Melodic Tom */ + { 0x15, 0x6c, { 0x0c7, 6418, 50, 0, 0, 0x00, 0xe4, 0x38, 0xf0, 0x05, 0x01, 0x0 }} +}; +static const YRW801_REGION_DATA regions_76[] = { + /* Synth Drum */ + { 0x15, 0x6c, { 0x026, 3898, 50, 0, 0, 0x00, 0xd0, 0x38, 0xf0, 0x04, 0x04, 0x0 }} +}; +static const YRW801_REGION_DATA regions_77[] = { + /* Reverse Cymbal */ + { 0x15, 0x6c, { 0x031, 4138, 50, 0, 0, 0x00, 0xfe, 0x38, 0x3a, 0xf0, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_78[] = { + /* Guitar Fret Noise */ + { 0x15, 0x6c, { 0x138, 5266, 100, 0, 0, 0x00, 0xa0, 0x38, 0xf0, 0x00, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_79[] = { + /* Breath Noise */ + { 0x01, 0x7f, { 0x125, 4269, 100, 0, 0, 0x1e, 0xd0, 0x38, 0xf0, 0x00, 0x09, 0x0 }} +}; +static const YRW801_REGION_DATA regions_7a[] = { + /* Seashore */ + { 0x15, 0x6c, { 0x008, 2965, 20, -2, 0, 0x00, 0xfe, 0x00, 0x20, 0x03, 0x04, 0x0 }}, + { 0x01, 0x7f, { 0x037, 4394, 20, 2, 0, 0x14, 0xfe, 0x00, 0x20, 0x04, 0x05, 0x0 } } +}; +static const YRW801_REGION_DATA regions_7b[] = { + /* Bird Tweet */ + { 0x15, 0x6c, { 0x009, 8078, 5, -4, 7, 0x00, 0xc2, 0x0f, 0x22, 0x12, 0x07, 0x0 }}, + { 0x15, 0x6c, { 0x009, 3583, 5, 4, 5, 0x00, 0xae, 0x15, 0x72, 0x12, 0x07, 0x0 } } +}; +static const YRW801_REGION_DATA regions_7c[] = { + /* Telephone Ring */ + { 0x15, 0x6c, { 0x003, 3602, 10, 0, 0, 0x00, 0xce, 0x00, 0xf0, 0x00, 0x0f, 0x0 }} +}; +static const YRW801_REGION_DATA regions_7d[] = { + /* Helicopter */ + { 0x0c, 0x7f, { 0x001, 2965, 10, -2, 0, 0x00, 0xe0, 0x08, 0x30, 0x01, 0x07, 0x0 }}, + { 0x01, 0x7f, { 0x037, 4394, 10, 2, 0, 0x44, 0x76, 0x00, 0x30, 0x01, 0x07, 0x0 } } +}; +static const YRW801_REGION_DATA regions_7e[] = { + /* Applause */ + { 0x15, 0x6c, { 0x036, 8273, 20, -6, 7, 0x00, 0xc4, 0x0f, 0x70, 0x01, 0x05, 0x0 }}, + { 0x15, 0x6c, { 0x036, 8115, 5, 6, 7, 0x00, 0xc6, 0x07, 0x70, 0x01, 0x05, 0x0 } } +}; +static const YRW801_REGION_DATA regions_7f[] = { + /* Gun Shot */ + { 0x15, 0x6c, { 0x139, 2858, 20, 0, 0, 0x00, 0xbe, 0x38, 0xf0, 0x03, 0x00, 0x0 }} +}; +static const YRW801_REGION_DATA regions_drums[] = { + { 0x18, 0x18, { 0x0cb, 6397, 100, 3, 0, 0x00, 0xf4, 0x38, 0xc9, 0x1c, 0x0c, 0x0 } }, + { 0x19, 0x19, { 0x0c4, 3714, 100, 0, 0, 0x00, 0xe0, 0x00, 0x97, 0x19, 0x09, 0x0 } }, + { 0x1a, 0x1a, { 0x0c4, 3519, 100, 0, 0, 0x00, 0xea, 0x00, 0x61, 0x01, 0x07, 0x0 } }, + { 0x1b, 0x1b, { 0x0c4, 3586, 100, 0, 0, 0x00, 0xea, 0x00, 0xf7, 0x19, 0x09, 0x0 } }, + { 0x1c, 0x1c, { 0x0c4, 3586, 100, 0, 0, 0x00, 0xea, 0x00, 0x81, 0x01, 0x07, 0x0 } }, + { 0x1e, 0x1e, { 0x0c3, 4783, 100, 0, 0, 0x00, 0xea, 0x00, 0xf0, 0x00, 0x09, 0x0 } }, + { 0x1f, 0x1f, { 0x0d1, 4042, 100, 0, 0, 0x00, 0xd6, 0x00, 0xf0, 0x05, 0x05, 0x0 } }, + { 0x20, 0x20, { 0x0d2, 5943, 100, 0, 0, 0x00, 0xcc, 0x00, 0xf0, 0x00, 0x09, 0x0 } }, + { 0x21, 0x21, { 0x011, 3842, 100, 0, 0, 0x00, 0xea, 0x00, 0xf0, 0x16, 0x06, 0x0 } }, + { 0x23, 0x23, { 0x011, 4098, 100, 0, 0, 0x00, 0xea, 0x00, 0xf0, 0x16, 0x06, 0x0 } }, + { 0x24, 0x24, { 0x011, 4370, 100, 0, 0, 0x00, 0xea, 0x00, 0xf0, 0x00, 0x06, 0x0 } }, + { 0x25, 0x25, { 0x0d2, 4404, 100, 0, 0, 0x00, 0xd6, 0x00, 0xf0, 0x00, 0x06, 0x0 } }, + { 0x26, 0x26, { 0x0d1, 4298, 100, 0, 0, 0x00, 0xd6, 0x00, 0xf0, 0x05, 0x05, 0x0 } }, + { 0x27, 0x27, { 0x00a, 4403, 100, -1, 0, 0x00, 0xd6, 0x00, 0xf0, 0x00, 0x09, 0x0 }}, + { 0x28, 0x28, { 0x0d1, 4554, 100, 0, 0, 0x00, 0xdc, 0x00, 0xf0, 0x07, 0x07, 0x0 } }, + { 0x29, 0x29, { 0x0c8, 4242, 100, -4, 0, 0x00, 0xd6, 0x00, 0xf6, 0x16, 0x06, 0x0 }}, + { 0x2a, 0x2a, { 0x079, 6160, 100, 2, 0, 0x00, 0xe0, 0x00, 0xf5, 0x19, 0x09, 0x0 } }, + { 0x2b, 0x2b, { 0x0c8, 4626, 100, -3, 0, 0x00, 0xd6, 0x00, 0xf6, 0x16, 0x06, 0x0 }}, + { 0x2c, 0x2c, { 0x07b, 6039, 100, 2, 0, 0x00, 0xd6, 0x00, 0xf0, 0x00, 0x09, 0x0 } }, + { 0x2d, 0x2d, { 0x0c8, 5394, 100, -2, 0, 0x00, 0xd6, 0x00, 0xf6, 0x16, 0x06, 0x0 }}, + { 0x2e, 0x2e, { 0x07a, 5690, 100, 2, 0, 0x00, 0xd6, 0x00, 0xf0, 0x00, 0x05, 0x0 } }, + { 0x2f, 0x2f, { 0x0c7, 5185, 100, 2, 0, 0x00, 0xe0, 0x00, 0xf6, 0x17, 0x07, 0x0 } }, + { 0x30, 0x30, { 0x0c7, 5650, 100, 3, 0, 0x00, 0xe0, 0x00, 0xf6, 0x17, 0x07, 0x0 } }, + { 0x31, 0x31, { 0x031, 4395, 100, 2, 0, 0x00, 0xea, 0x00, 0xf0, 0x05, 0x05, 0x0 } }, + { 0x32, 0x32, { 0x0c7, 6162, 100, 4, 0, 0x00, 0xe0, 0x00, 0xf6, 0x17, 0x07, 0x0 } }, + { 0x33, 0x33, { 0x02e, 4391, 100, -2, 0, 0x00, 0xea, 0x00, 0xf0, 0x05, 0x05, 0x0 }}, + { 0x34, 0x34, { 0x07a, 3009, 100, -2, 0, 0x00, 0xea, 0x00, 0xf2, 0x15, 0x05, 0x0 }}, + { 0x35, 0x35, { 0x021, 4522, 100, -3, 0, 0x00, 0xd6, 0x00, 0xf0, 0x05, 0x05, 0x0 }}, + { 0x36, 0x36, { 0x025, 5163, 100, 1, 0, 0x00, 0xe0, 0x00, 0xf0, 0x00, 0x09, 0x0 } }, + { 0x37, 0x37, { 0x031, 5287, 100, -1, 0, 0x00, 0xea, 0x00, 0xf5, 0x16, 0x06, 0x0 }}, + { 0x38, 0x38, { 0x01d, 4395, 100, 2, 0, 0x00, 0xe0, 0x00, 0xf0, 0x00, 0x09, 0x0 } }, + { 0x39, 0x39, { 0x031, 4647, 100, -2, 0, 0x00, 0xea, 0x00, 0xf4, 0x16, 0x06, 0x0 }}, + { 0x3a, 0x3a, { 0x09d, 4426, 100, -4, 0, 0x00, 0xe0, 0x00, 0xf4, 0x17, 0x07, 0x0 }}, + { 0x3b, 0x3b, { 0x02e, 4659, 100, -2, 0, 0x00, 0xea, 0x00, 0xf0, 0x06, 0x06, 0x0 }}, + { 0x3c, 0x3c, { 0x01c, 4769, 100, 4, 0, 0x00, 0xea, 0x00, 0xf0, 0x00, 0x09, 0x0 } }, + { 0x3d, 0x3d, { 0x01c, 4611, 100, 4, 0, 0x00, 0xea, 0x00, 0xf0, 0x00, 0x09, 0x0 } }, + { 0x3e, 0x3e, { 0x01e, 4402, 100, -3, 0, 0x00, 0xea, 0x00, 0xf0, 0x00, 0x09, 0x0 }}, + { 0x3f, 0x3f, { 0x01f, 4387, 100, -3, 0, 0x00, 0xea, 0x00, 0xf0, 0x00, 0x09, 0x0 }}, + { 0x40, 0x40, { 0x01f, 3983, 100, -2, 0, 0x00, 0xea, 0x00, 0xf0, 0x00, 0x09, 0x0 }}, + { 0x41, 0x41, { 0x09c, 4526, 100, 2, 0, 0x00, 0xea, 0x00, 0xf0, 0x00, 0x09, 0x0 } }, + { 0x42, 0x42, { 0x09c, 4016, 100, 2, 0, 0x00, 0xea, 0x00, 0xf0, 0x00, 0x09, 0x0 } }, + { 0x43, 0x43, { 0x00b, 4739, 100, -4, 0, 0x00, 0xea, 0x00, 0xf0, 0x00, 0x09, 0x0 }}, + { 0x44, 0x44, { 0x00b, 4179, 100, -4, 0, 0x00, 0xea, 0x00, 0xf0, 0x00, 0x09, 0x0 }}, + { 0x45, 0x45, { 0x02f, 4787, 100, -4, 0, 0x00, 0xd6, 0x00, 0xf0, 0x00, 0x09, 0x0 }}, + { 0x46, 0x46, { 0x030, 4665, 100, -4, 0, 0x00, 0xd6, 0x00, 0xf0, 0x00, 0x09, 0x0 }}, + { 0x47, 0x47, { 0x144, 4519, 100, 4, 0, 0x00, 0xea, 0x00, 0xf0, 0x00, 0x0b, 0x0 } }, + { 0x48, 0x48, { 0x144, 4111, 100, 4, 0, 0x00, 0xea, 0x00, 0xf0, 0x00, 0x0b, 0x0 } }, + { 0x49, 0x49, { 0x024, 6408, 100, 3, 0, 0x00, 0xe0, 0x00, 0xf0, 0x00, 0x09, 0x0 } }, + { 0x4a, 0x4a, { 0x024, 4144, 100, 3, 0, 0x00, 0xcc, 0x00, 0xf0, 0x00, 0x09, 0x0 } }, + { 0x4b, 0x4b, { 0x020, 4001, 100, 2, 0, 0x00, 0xe0, 0x00, 0xf0, 0x00, 0x09, 0x0 } }, + { 0x4c, 0x4c, { 0x02c, 4402, 100, 4, 0, 0x00, 0xea, 0x00, 0xf0, 0x00, 0x09, 0x0 } }, + { 0x4d, 0x4d, { 0x02c, 3612, 100, 4, 0, 0x00, 0xea, 0x00, 0xf0, 0x00, 0x09, 0x0 } }, + { 0x4e, 0x4e, { 0x022, 4129, 100, -2, 0, 0x00, 0xea, 0x00, 0xf0, 0x00, 0x09, 0x0 }}, + { 0x4f, 0x4f, { 0x023, 4147, 100, -2, 0, 0x00, 0xea, 0x00, 0xf0, 0x00, 0x09, 0x0 }}, + { 0x50, 0x50, { 0x032, 4412, 100, -4, 0, 0x00, 0xd6, 0x00, 0xf0, 0x08, 0x09, 0x0 }}, + { 0x51, 0x51, { 0x032, 4385, 100, -4, 0, 0x00, 0xd6, 0x00, 0xf0, 0x00, 0x09, 0x0 }}, + { 0x52, 0x52, { 0x02f, 5935, 100, -1, 0, 0x00, 0xd6, 0x00, 0xf0, 0x00, 0x09, 0x0 }} +}; + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +#define REGION(num) \ + { \ + ARRAY_SIZE(regions##num), regions##num \ + } +const YRW801_REGION_DATA_PTR snd_yrw801_regions[0x81] = { + REGION(_00), REGION(_01), REGION(_02), REGION(_03), + REGION(_04), REGION(_05), REGION(_06), REGION(_07), + REGION(_08), REGION(_09), REGION(_0a), REGION(_0b), + REGION(_0c), REGION(_0d), REGION(_0e), REGION(_0f), + REGION(_10), REGION(_11), REGION(_12), REGION(_13), + REGION(_14), REGION(_15), REGION(_16), REGION(_17), + REGION(_18), REGION(_19), REGION(_1a), REGION(_1b), + REGION(_1c), REGION(_1d), REGION(_1e), REGION(_1f), + REGION(_20), REGION(_21), REGION(_22), REGION(_23), + REGION(_24), REGION(_25), REGION(_26), REGION(_27), + REGION(_28), REGION(_29), REGION(_2a), REGION(_2b), + REGION(_2c), REGION(_2d), REGION(_2e), REGION(_2f), + REGION(_30), REGION(_31), REGION(_32), REGION(_33), + REGION(_34), REGION(_35), REGION(_36), REGION(_37), + REGION(_38), REGION(_39), REGION(_3a), REGION(_3b), + REGION(_3c), REGION(_3d), REGION(_3e), REGION(_3f), + REGION(_40), REGION(_41), REGION(_42), REGION(_43), + REGION(_44), REGION(_45), REGION(_46), REGION(_47), + REGION(_48), REGION(_49), REGION(_4a), REGION(_4b), + REGION(_4c), REGION(_4d), REGION(_4e), REGION(_4f), + REGION(_50), REGION(_51), REGION(_52), REGION(_53), + REGION(_54), REGION(_55), REGION(_56), REGION(_57), + REGION(_58), REGION(_59), REGION(_5a), REGION(_5b), + REGION(_5c), REGION(_5d), REGION(_5e), REGION(_5f), + REGION(_60), REGION(_61), REGION(_62), REGION(_63), + REGION(_64), REGION(_65), REGION(_66), REGION(_67), + REGION(_68), REGION(_69), REGION(_6a), REGION(_6b), + REGION(_6c), REGION(_6d), REGION(_6e), REGION(_6f), + REGION(_70), REGION(_71), REGION(_72), REGION(_73), + REGION(_74), REGION(_75), REGION(_76), REGION(_77), + REGION(_78), REGION(_79), REGION(_7a), REGION(_7b), + REGION(_7c), REGION(_7d), REGION(_7e), REGION(_7f), + REGION(_drums) +}; \ No newline at end of file diff --git a/src/sound/snd_opl_nuked.c b/src/sound/snd_opl_nuked.c index 6ed697bf3..e4131f1fe 100644 --- a/src/sound/snd_opl_nuked.c +++ b/src/sound/snd_opl_nuked.c @@ -1657,4 +1657,5 @@ const fm_drv_t nuked_opl_drv = { &nuked_drv_reset_buffer, &nuked_drv_set_do_cycles, NULL, + NULL, }; \ No newline at end of file diff --git a/src/sound/snd_opl_ymfm.cpp b/src/sound/snd_opl_ymfm.cpp index a8867e6ed..0f996f6bc 100644 --- a/src/sound/snd_opl_ymfm.cpp +++ b/src/sound/snd_opl_ymfm.cpp @@ -399,6 +399,13 @@ ymfm_drv_set_do_cycles(void *priv, int8_t do_cycles) drv->set_do_cycles(do_cycles); } +static void +ymfm_drv_generate(void *priv, int32_t *data, uint32_t num_samples) +{ + YMFMChipBase *drv = (YMFMChipBase *) priv; + drv->generate_resampled(data, num_samples); +} + const device_t ym3812_ymfm_device = { .name = "Yamaha YM3812 OPL2 (YMFM)", .internal_name = "ym3812_ymfm", @@ -462,6 +469,7 @@ const fm_drv_t ymfm_drv { &ymfm_drv_reset_buffer, &ymfm_drv_set_do_cycles, NULL, + ymfm_drv_generate, }; #ifdef __clang__ diff --git a/src/sound/snd_sb.c b/src/sound/snd_sb.c index bfd1ca401..5f1aeb261 100644 --- a/src/sound/snd_sb.c +++ b/src/sound/snd_sb.c @@ -945,7 +945,7 @@ sb_ct1745_mixer_write(uint16_t addr, uint8_t val, void *priv) break; case 0xff: - if (sb->dsp.sb_type >= SB16) { + if (sb->dsp.sb_type > SBAWE32) { /* Bit 5: High DMA channel enabled (0 = yes, 1 = no); Bit 2: ????; @@ -1173,7 +1173,7 @@ sb_ct1745_mixer_read(uint16_t addr, void *priv) - Register FF = FF: Volume playback normal. - Register FF = Not FF: Volume playback low unless bit 6 of 82h is set. */ - if (sb->dsp.sb_type >= SB16) + if (sb->dsp.sb_type > SBAWE32) ret = mixer->regs[mixer->index]; break; @@ -1599,8 +1599,14 @@ sb_16_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv) sb_dsp_setdma8(&sb->dsp, val); val = config->dma[1].dma; - if (val != ISAPNP_DMA_DISABLED) - sb_dsp_setdma16(&sb->dsp, val); + sb_dsp_setdma16_enabled(&sb->dsp, val != ISAPNP_DMA_DISABLED); + sb_dsp_setdma16_translate(&sb->dsp, val < ISAPNP_DMA_DISABLED); + if (val != ISAPNP_DMA_DISABLED) { + if (sb->dsp.sb_16_dma_supported) + sb_dsp_setdma16(&sb->dsp, val); + else + sb_dsp_setdma16_8(&sb->dsp, val); + } } break; @@ -1630,95 +1636,11 @@ static void sb_vibra16_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv) { sb_t *sb = (sb_t *) priv; - uint16_t addr = sb->dsp.sb_addr; - uint8_t val; switch (ld) { case 0: /* Audio */ - io_removehandler(addr, 0x0004, - sb->opl.read, NULL, NULL, - sb->opl.write, NULL, NULL, - sb->opl.priv); - io_removehandler(addr + 8, 0x0002, - sb->opl.read, NULL, NULL, - sb->opl.write, NULL, NULL, - sb->opl.priv); - io_removehandler(addr + 4, 0x0002, - sb_ct1745_mixer_read, NULL, NULL, - sb_ct1745_mixer_write, NULL, NULL, - sb); - - addr = sb->opl_pnp_addr; - if (addr) { - sb->opl_pnp_addr = 0; - io_removehandler(addr, 0x0004, - sb->opl.read, NULL, NULL, - sb->opl.write, NULL, NULL, - sb->opl.priv); - } - - sb_dsp_setaddr(&sb->dsp, 0); - sb_dsp_setirq(&sb->dsp, 0); - sb_dsp_setdma8(&sb->dsp, ISAPNP_DMA_DISABLED); - sb_dsp_setdma16(&sb->dsp, ISAPNP_DMA_DISABLED); - - mpu401_change_addr(sb->mpu, 0); - - if (config->activate) { - addr = config->io[0].base; - if (addr != ISAPNP_IO_DISABLED) { - io_sethandler(addr, 0x0004, - sb->opl.read, NULL, NULL, - sb->opl.write, NULL, NULL, - sb->opl.priv); - io_sethandler(addr + 8, 0x0002, - sb->opl.read, NULL, NULL, - sb->opl.write, NULL, NULL, - sb->opl.priv); - io_sethandler(addr + 4, 0x0002, - sb_ct1745_mixer_read, NULL, NULL, - sb_ct1745_mixer_write, NULL, NULL, - sb); - - sb_dsp_setaddr(&sb->dsp, addr); - } - - addr = config->io[1].base; - if (addr != ISAPNP_IO_DISABLED) - mpu401_change_addr(sb->mpu, addr); - - addr = config->io[2].base; - if (addr != ISAPNP_IO_DISABLED) { - sb->opl_pnp_addr = addr; - io_sethandler(addr, 0x0004, - sb->opl.read, NULL, NULL, - sb->opl.write, NULL, NULL, - sb->opl.priv); - } - - val = config->irq[0].irq; - if (val != ISAPNP_IRQ_DISABLED) - sb_dsp_setirq(&sb->dsp, val); - - val = config->dma[0].dma; - if (val != ISAPNP_DMA_DISABLED) - sb_dsp_setdma8(&sb->dsp, val); - - val = config->dma[1].dma; - sb_dsp_setdma16_enabled(&sb->dsp, val != ISAPNP_DMA_DISABLED); - sb_dsp_setdma16_translate(&sb->dsp, val < ISAPNP_DMA_DISABLED); - if (val != ISAPNP_DMA_DISABLED) { - if (sb->dsp.sb_16_dma_supported) - sb_dsp_setdma16(&sb->dsp, val); - else - sb_dsp_setdma16_8(&sb->dsp, val); - } - } - - break; - case 1: /* Game */ - gameport_remap(sb->gameport, (config->activate && (config->io[0].base != ISAPNP_IO_DISABLED)) ? config->io[0].base : 0); + sb_16_pnp_config_changed(ld * 3, config, sb); break; default: @@ -2179,6 +2101,7 @@ sb_16_init(UNUSED(const device_t *info)) sb_dsp_setdma8(&sb->dsp, device_get_config_int("dma")); sb_dsp_setdma16(&sb->dsp, device_get_config_int("dma16")); sb_dsp_setdma16_supported(&sb->dsp, 1); + sb_dsp_setdma16_enabled(&sb->dsp, 1); sb_ct1745_mixer_reset(sb); if (sb->opl_enabled) { @@ -2233,6 +2156,7 @@ sb_16_reply_mca_init(UNUSED(const device_t *info)) sb_dsp_init(&sb->dsp, SB16, SB_SUBTYPE_DEFAULT, sb); sb_dsp_setdma16_supported(&sb->dsp, 1); + sb_dsp_setdma16_enabled(&sb->dsp, 1); sb_ct1745_mixer_reset(sb); sb->mixer_enabled = 1; @@ -2304,7 +2228,7 @@ sb_16_pnp_init(UNUSED(const device_t *info)) sb_dsp_setdma16(&sb->dsp, ISAPNP_DMA_DISABLED); mpu401_change_addr(sb->mpu, 0); - ide_remove_handlers(2); + ide_remove_handlers(3); sb->gameport_addr = 0; gameport_remap(sb->gameport, 0); @@ -2415,6 +2339,7 @@ sb_16_compat_init(const device_t *info) sb_dsp_init(&sb->dsp, SB16, SB_SUBTYPE_DEFAULT, sb); sb_dsp_setdma16_supported(&sb->dsp, 1); + sb_dsp_setdma16_enabled(&sb->dsp, 1); sb_ct1745_mixer_reset(sb); sb->mixer_enabled = 1; @@ -2488,6 +2413,7 @@ sb_awe32_init(UNUSED(const device_t *info)) sb_dsp_setdma8(&sb->dsp, device_get_config_int("dma")); sb_dsp_setdma16(&sb->dsp, device_get_config_int("dma16")); sb_dsp_setdma16_supported(&sb->dsp, 1); + sb_dsp_setdma16_enabled(&sb->dsp, 1); sb_ct1745_mixer_reset(sb); if (sb->opl_enabled) { @@ -2631,7 +2557,8 @@ sb_awe32_pnp_init(const device_t *info) sb_dsp_setdma16(&sb->dsp, ISAPNP_DMA_DISABLED); mpu401_change_addr(sb->mpu, 0); - ide_remove_handlers(2); + if ((info->local != 2) && (info->local != 3) && (info->local != 4)) + ide_remove_handlers(3); emu8k_change_addr(&sb->emu8k, 0); diff --git a/src/sound/yrw801.h b/src/sound/yrw801.h new file mode 100644 index 000000000..0df3c0343 --- /dev/null +++ b/src/sound/yrw801.h @@ -0,0 +1,45 @@ +/* + * RoboPlay for MSX + * Copyright (C) 2022 by RoboSoft Inc. + * + * yrw801.h + */ + +/* Cacodemon345: Added pointer structs from Linux */ + +#pragma once + +#include + +typedef struct +{ + uint16_t tone; + int16_t pitch_offset; + uint8_t key_scaling; + int8_t panpot; + uint8_t vibrato; + uint8_t tone_attenuate; + uint8_t volume_factor; + uint8_t reg_lfo_vibrato; + uint8_t reg_attack_decay1; + uint8_t reg_level_decay2; + uint8_t reg_release_correction; + uint8_t reg_tremolo; +} YRW801_WAVE_DATA; + +typedef struct +{ + uint8_t key_min; + uint8_t key_max; + + YRW801_WAVE_DATA wave_data; +} YRW801_REGION_DATA; + +typedef struct +{ + int count; + + const YRW801_REGION_DATA* regions; +} YRW801_REGION_DATA_PTR; + +extern const YRW801_REGION_DATA_PTR snd_yrw801_regions[0x81]; \ No newline at end of file diff --git a/src/unix/assets/86Box.spec b/src/unix/assets/86Box.spec index 60e23d93c..a7e4786be 100644 --- a/src/unix/assets/86Box.spec +++ b/src/unix/assets/86Box.spec @@ -12,10 +12,10 @@ # After a successful build, you can install the RPMs as follows: # sudo dnf install RPMS/$(uname -m)/86Box-3* RPMS/noarch/86Box-roms* -%global romver 4.0.1 +%global romver 4.1 Name: 86Box -Version: 4.0.2 +Version: 4.1 Release: 1%{?dist} Summary: Classic PC emulator License: GPLv2+ @@ -121,5 +121,5 @@ popd %{_datadir}/%{name}/roms %changelog -* Mon Oct 16 2023 Robert de Rooy 4.0.2-1 +* Mon Oct 16 2023 Robert de Rooy 4.1-1 - Bump release diff --git a/src/unix/assets/net.86box.86Box.metainfo.xml b/src/unix/assets/net.86box.86Box.metainfo.xml index ce4e8e430..9e2c5dc88 100644 --- a/src/unix/assets/net.86box.86Box.metainfo.xml +++ b/src/unix/assets/net.86box.86Box.metainfo.xml @@ -10,7 +10,7 @@ net.86box.86Box.desktop - + diff --git a/src/unix/unix.c b/src/unix/unix.c index 7b3fd56cf..ecd17cadb 100644 --- a/src/unix/unix.c +++ b/src/unix/unix.c @@ -60,6 +60,7 @@ extern wchar_t sdl_win_title[512]; plat_joystick_t plat_joystick_state[MAX_PLAT_JOYSTICKS]; joystick_t joystick_state[MAX_JOYSTICKS]; int joysticks_present; +int status_icons_fullscreen = 0; /* unused. */ SDL_mutex *blitmtx; SDL_threadID eventthread; static int exit_event = 0; diff --git a/src/video/vid_8514a.c b/src/video/vid_8514a.c index a2cabfb0d..314fee307 100644 --- a/src/video/vid_8514a.c +++ b/src/video/vid_8514a.c @@ -964,8 +964,8 @@ ibm8514_accel_out(uint16_t port, uint32_t val, svga_t *svga, int len) if (!val) break; dev->accel.advfunc_cntl = val & 0x0f; - dev->on = val & 0x01; - vga_on = !dev->on; + dev->on[0] = val & 0x01; + vga_on = !dev->on[0]; ibm8514_log("IBM 8514/A: VGA ON = %i, val = %02x\n", vga_on, val); svga_recalctimings(svga); break; @@ -1200,17 +1200,16 @@ ibm8514_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat if (cpu_input) { if ((dev->accel.cmd & 2) || (pixcntl == 2)) { - if ((frgd_mix == 2) || (bkgd_mix == 2)) { + if ((frgd_mix == 2) || (bkgd_mix == 2)) count >>= 3; - } else if (pixcntl == 2) { - if (dev->accel.cmd & 2) { + else if (pixcntl == 2) { + if (dev->accel.cmd & 2) count >>= 1; - } else + else count >>= 3; } - } else { + } else count >>= 3; - } if (dev->bpp) { if ((dev->accel.cmd & 0x200) && (count == 2)) @@ -1299,7 +1298,8 @@ ibm8514_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat /*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)*/ - ibm8514_log("CMD8514: CMD=%d, full=%04x, pixcntl=%x, count=%d, frgdmix = %02x, bkgdmix = %02x, polygon=%x.\n", cmd, dev->accel.cmd, pixcntl, count, frgd_mix, bkgd_mix, dev->accel.multifunc[0x0a] & 6); + if (dev->accel.cmd == 0x53b1 && !cpu_dat) + ibm8514_log("CMD8514: CMD=%d, full=%04x, pixcntl=%x, count=%d, frgdmix = %02x, bkgdmix = %02x, polygon=%x, cpu=%08x, frgdmix=%02x, bkgdmix=%02x.\n", cmd, dev->accel.cmd, pixcntl, count, frgd_mix, bkgd_mix, dev->accel.multifunc[0x0a] & 6, cpu_dat, dev->accel.frgd_mix, dev->accel.bkgd_mix); switch (cmd) { case 0: /*NOP (Short Stroke Vectors)*/ @@ -4243,7 +4243,7 @@ ibm8514_recalctimings(svga_t *svga) { ibm8514_t *dev = (ibm8514_t *) svga->dev8514; - if (dev->on) { + if (dev->on[0]) { dev->h_disp = (dev->hdisp + 1) << 3; dev->pitch = (dev->accel.advfunc_cntl & 4) ? 1024 : 640; dev->h_total = (dev->htotal + 1); diff --git a/src/video/vid_ati68860_ramdac.c b/src/video/vid_ati68860_ramdac.c index 8b4c4924b..7cdd18019 100644 --- a/src/video/vid_ati68860_ramdac.c +++ b/src/video/vid_ati68860_ramdac.c @@ -73,16 +73,16 @@ ati68860_ramdac_out(uint16_t addr, uint8_t val, void *priv, svga_t *svga) switch (addr) { case 0: - svga_out((dev && dev->on) ? 0x2ec : 0x3c8, val, svga); + svga_out((dev && (dev->on[0] || dev->on[1])) ? 0x2ec : 0x3c8, val, svga); break; case 1: - svga_out((dev && dev->on) ? 0x2ed : 0x3c9, val, svga); + svga_out((dev && (dev->on[0] || dev->on[1])) ? 0x2ed : 0x3c9, val, svga); break; case 2: - svga_out((dev && dev->on) ? 0x2ea : 0x3c6, val, svga); + svga_out((dev && (dev->on[0] || dev->on[1])) ? 0x2ea : 0x3c6, val, svga); break; case 3: - svga_out((dev && dev->on) ? 0x2eb : 0x3c7, val, svga); + svga_out((dev && (dev->on[0] || dev->on[1])) ? 0x2eb : 0x3c7, val, svga); break; default: ramdac->regs[addr & 0xf] = val; @@ -178,16 +178,16 @@ ati68860_ramdac_in(uint16_t addr, void *priv, svga_t *svga) switch (addr) { case 0: - temp = svga_in((dev && dev->on) ? 0x2ec : 0x3c8, svga); + temp = svga_in((dev && (dev->on[0] || dev->on[1])) ? 0x2ec : 0x3c8, svga); break; case 1: - temp = svga_in((dev && dev->on) ? 0x2ed : 0x3c9, svga); + temp = svga_in((dev && (dev->on[0] || dev->on[1])) ? 0x2ed : 0x3c9, svga); break; case 2: - temp = svga_in((dev && dev->on) ? 0x2ea : 0x3c6, svga); + temp = svga_in((dev && (dev->on[0] || dev->on[1])) ? 0x2ea : 0x3c6, svga); break; case 3: - temp = svga_in((dev && dev->on) ? 0x2eb : 0x3c7, svga); + temp = svga_in((dev && (dev->on[0] || dev->on[1])) ? 0x2eb : 0x3c7, svga); break; case 4: case 8: diff --git a/src/video/vid_ati_mach8.c b/src/video/vid_ati_mach8.c index 8e7174666..90e41d9ba 100644 --- a/src/video/vid_ati_mach8.c +++ b/src/video/vid_ati_mach8.c @@ -100,8 +100,8 @@ typedef struct mach_t { uint8_t bank_w; uint8_t bank_r; uint16_t shadow_set; - int ext_on; - int ati_mode; + int ext_on[2]; + int ati_mode[2]; struct { uint8_t line_idx; @@ -1140,9 +1140,9 @@ mach_accel_start(int cmd_type, int cpu_input, int count, uint32_t mix_dat, uint3 mix = 1; break; case 1: - if (mach->accel.mono_pattern_enable) { + if (mach->accel.mono_pattern_enable) mix = mach->accel.mono_pattern[dev->accel.dy & 7][dev->accel.dx & 7]; - } else { + else { if ((dev->accel_bpp == 24) && (frgd_sel == 5) && (mach->accel.patt_len_reg & 0x4000)) mix = 1; else { @@ -1205,18 +1205,16 @@ mach_accel_start(int cmd_type, int cpu_input, int count, uint32_t mix_dat, uint3 src_dat = cpu_dat; else { READ(dev->accel.src + (dev->accel.cx), src_dat); - if (mono_src == 3) { + if (mono_src == 3) src_dat = (src_dat & rd_mask) == rd_mask; - } } break; case 5: if (mix) { - if (dev->bpp) { + if (dev->bpp) src_dat = mach->accel.color_pattern_word[mach->accel.color_pattern_idx]; - } else { + else src_dat = mach->accel.color_pattern[mach->accel.color_pattern_idx]; - } } else src_dat = 0; break; @@ -1318,9 +1316,9 @@ mach_accel_start(int cmd_type, int cpu_input, int count, uint32_t mix_dat, uint3 mach->accel.color_pattern_idx++; if (mach->accel.color_pattern_idx == 3) mach->accel.color_pattern_idx = 0; - } else { + } else mach->accel.color_pattern_idx = (mach->accel.color_pattern_idx + mach->accel.stepx) & mach->accel.patt_len; - } + } else if ((dev->accel_bpp == 24) && (mach->accel.patt_len_reg & 0x4000) && (frgd_sel == 5)) { mach->accel.color_pattern_idx++; if (mach->accel.color_pattern_idx == 3) @@ -1346,9 +1344,9 @@ mach_accel_start(int cmd_type, int cpu_input, int count, uint32_t mix_dat, uint3 if (dev->bpp) dev->accel.dest = (mach->accel.ge_offset << 1) + (dev->accel.dy * (dev->pitch)); - else { + else dev->accel.dest = (mach->accel.ge_offset << 2) + (dev->accel.dy * (dev->pitch)); - } + if ((mono_src == 1) && (dev->accel_bpp == 24) && (frgd_sel == 5)) mach->accel.color_pattern_idx = 0; else @@ -2347,7 +2345,7 @@ mach_out(uint16_t addr, uint8_t val, void *priv) mach->bank_r |= ((mach->regs[0xae] & 0x0c) << 2); mach->bank_w |= ((mach->regs[0xae] & 3) << 4); } - if (dev->on) + if (dev->on[0] || dev->on[1]) mach_log("Separate B2Bank = %02x, AEbank = %02x.\n", mach->regs[0xb2], mach->regs[0xae]); } else { /* Single bank mode */ mach->bank_w = ((mach->regs[0xb2] & 0x1e) >> 1); @@ -2355,7 +2353,7 @@ mach_out(uint16_t addr, uint8_t val, void *priv) mach->bank_w |= ((mach->regs[0xae] & 3) << 4); } mach->bank_r = mach->bank_w; - if (dev->on) + if (dev->on[0] || dev->on[1]) mach_log("Single B2Bank = %02x, AEbank = %02x.\n", mach->regs[0xb2], mach->regs[0xae]); } svga->read_bank = mach->bank_r << 16; @@ -2590,7 +2588,7 @@ mach_recalctimings(svga_t *svga) if (mach->regs[0xb0] & 0x20) svga->gdcreg[5] |= 0x40; - if (dev->on) { + if (dev->on[0] || dev->on[1]) { mach_log("8514/A ON.\n"); if (dev->local >= 2) { dev->h_disp = (dev->hdisp + 1) << 3; @@ -3686,19 +3684,16 @@ mach_accel_out(uint16_t port, uint8_t val, mach_t *mach) case 0x4ae8: case 0x4ae9: - if (!(port & 1)) { - if (dev->local < 2) - dev->ext_crt_pitch = 128; + if (dev->local < 2) + dev->ext_crt_pitch = 128; - dev->accel.advfunc_cntl = val & 0x0f; - } else { - dev->on = (dev->accel.advfunc_cntl & 0x01); - vga_on = !dev->on; - mach->ext_on = dev->on; - mach_log("ATI 8514/A: (0x4ae9) val = %04x, ext = %d.\n", dev->accel.advfunc_cntl & 0x01, mach->ext_on); - mach32_updatemapping(mach); - } - mach->ati_mode = 0; + WRITE8(port, dev->accel.advfunc_cntl, val); + dev->on[port & 1] = (dev->accel.advfunc_cntl & 0x01); + mach_log("%04x: ON=%d.\n", port, dev->on[port & 1]); + vga_on = !dev->on[port & 1]; + mach->ext_on[port & 1] = dev->on[port & 1]; + mach32_updatemapping(mach); + mach->ati_mode[port & 1] = 0; svga_recalctimings(svga); break; @@ -3806,29 +3801,22 @@ mach_accel_out(uint16_t port, uint8_t val, mach_t *mach) break; case 0x42ee: - mach->accel.test2[0] = val; - break; case 0x42ef: - mach->accel.test2[1] = val; + mach->accel.test2[port & 1] = val; break; case 0x46ee: - mach->accel.test3[0] = val; - break; case 0x46ef: - mach->accel.test3[1] = val; + mach->accel.test3[port & 1] = val; break; case 0x4aee: case 0x4aef: WRITE8(port, mach->accel.clock_sel, val); - if (port & 1) { - dev->on = mach->accel.clock_sel & 0x01; - mach->ext_on = dev->on; - vga_on = !dev->on; - mach_log("ATI 8514/A: (0x4aef) val = %04x, ext = %d.\n", mach->accel.clock_sel & 0x01, mach->ext_on); - } - mach->ati_mode = 1; + dev->on[port & 1] = mach->accel.clock_sel & 0x01; + mach->ext_on[port & 1] = dev->on[port & 1]; + vga_on = !dev->on[port & 1]; + mach->ati_mode[port & 1] = 1; svga_recalctimings(svga); break; @@ -3836,16 +3824,14 @@ mach_accel_out(uint16_t port, uint8_t val, mach_t *mach) case 0x52ef: mach_log("ATI 8514/A: (0x%04x) val = %04x.\n", port, val); WRITE8(port, mach->accel.scratch0, val); - if (port & 1) - mach->ext_on = 1; + mach->ext_on[port & 1] = 1; break; case 0x56ee: case 0x56ef: mach_log("ATI 8514/A: (0x%04x) val = %04x.\n", port, val); WRITE8(port, mach->accel.scratch1, val); - if (port & 1) - mach->ext_on = 1; + mach->ext_on[port & 1] = 1; break; case 0x5aee: @@ -3926,11 +3912,8 @@ mach_accel_out(uint16_t port, uint8_t val, mach_t *mach) break; } svga_set_ramdac_type(svga, !!(mach->accel.ext_ge_config & 0x4000)); - if (port & 1) { - mach->ati_mode = 1; - mach_log("ATI 8514/A: (0x%04x) val = %04x.\n", port, val); - mach32_updatemapping(mach); - } + mach->ati_mode[port & 1] = 1; + mach32_updatemapping(mach); } svga_recalctimings(svga); break; @@ -5115,10 +5098,12 @@ mach32_updatemapping(mach_t *mach) mach->ap_size = 4; mem_mapping_disable(&mach->mmio_linear_mapping); } - if (mach->ext_on && (dev->local >= 2) && mach->ati_mode) { + if ((mach->ext_on[0] || mach->ext_on[1]) && (dev->local >= 2) && (mach->ati_mode[0] || mach->ati_mode[1])) { + mach_log("ExtON.\n"); mem_mapping_set_handler(&svga->mapping, mach32_read, mach32_readw, mach32_readl, mach32_write, mach32_writew, mach32_writel); mem_mapping_set_p(&svga->mapping, mach); } else { + mach_log("ExtOFF.\n"); mem_mapping_set_handler(&svga->mapping, svga_read, svga_readw, svga_readl, svga_write, svga_writew, svga_writel); mem_mapping_set_p(&svga->mapping, svga); } @@ -5456,7 +5441,8 @@ mach_mca_reset(void *priv) mem_mapping_disable(&mach->bios_rom.mapping); mem_mapping_disable(&mach->bios_rom2.mapping); mach_log("MCA reset.\n"); - dev->on = 0; + dev->on[0] = 0; + dev->on[1] = 0; vga_on = 1; mach_mca_write(0x102, 0, mach); } @@ -5564,12 +5550,10 @@ mach32_pci_write(UNUSED(int func), int addr, uint8_t val, void *priv) case 0x12: mach->linear_base = (mach->linear_base & 0xff000000) | ((val & 0xc0) << 16); - mach->ati_mode = 1; mach32_updatemapping(mach); break; case 0x13: mach->linear_base = (mach->linear_base & 0xc00000) | (val << 24); - mach->ati_mode = 1; mach32_updatemapping(mach); break; @@ -5722,7 +5706,8 @@ mach8_init(const device_t *info) dev->bpp = 0; svga->getclock = ics2494_getclock; - dev->on = 0; + dev->on[0] = 0; + dev->on[1] = 0; dev->ext_pitch = 1024; dev->ext_crt_pitch = 0x80; dev->accel_bpp = 8; diff --git a/src/video/vid_svga.c b/src/video/vid_svga.c index 8ec645016..c48baeb6e 100644 --- a/src/video/vid_svga.c +++ b/src/video/vid_svga.c @@ -212,10 +212,12 @@ svga_out(uint16_t addr, uint8_t val, void *priv) svga_recalctimings(svga); break; case 0x3c3: - if (xga_active) + if (xga_active && xga) xga->on = (val & 0x01) ? 0 : 1; - if (ibm8514_active) - dev->on = (val & 0x01) ? 0 : 1; + if (ibm8514_active && dev) { + dev->on[0] = (val & 0x01) ? 0 : 1; + dev->on[1] = dev->on[0]; + } svga_log("3C3: XGA ON = %d.\n", xga->on); vga_on = val & 0x01; @@ -527,7 +529,7 @@ svga_set_ramdac_type(svga_t *svga, int type) svga->ramdac_type = type; for (int c = 0; c < 256; c++) { - if (ibm8514_active) { + if (ibm8514_active && dev) { if (svga->ramdac_type == RAMDAC_8BIT) dev->pallook[c] = makecol32(svga->vgapal[c].r, svga->vgapal[c].g, svga->vgapal[c].b); else @@ -535,7 +537,7 @@ svga_set_ramdac_type(svga_t *svga, int type) (svga->vgapal[c].g & 0x3f) * 4, (svga->vgapal[c].b & 0x3f) * 4); } - if (xga_active) { + if (xga_active && xga) { if (svga->ramdac_type == RAMDAC_8BIT) xga->pallook[c] = makecol32(svga->vgapal[c].r, svga->vgapal[c].g, svga->vgapal[c].b); else @@ -834,11 +836,11 @@ svga_poll(void *priv) int old_ma; if (!svga->override) { - if (ibm8514_active && dev->on) { + if (ibm8514_active && dev && (dev->on[0] || dev->on[1])) { ibm8514_poll(dev, svga); return; } - if (xga_active && xga->on) { + if (xga_active && xga && xga->on) { if ((xga->disp_cntl_2 & 7) >= 2) { xga_poll(xga, svga); return; @@ -1253,7 +1255,7 @@ svga_write_common(uint32_t addr, uint8_t val, uint8_t linear, void *priv) cycles -= svga->monitor->mon_video_timing_write_b; if (!linear) { - if (xga_active) { + if (xga_active && xga) { if (((xga->op_mode & 7) >= 4) && (xga->aperture_cntl >= 1)) { if (val == 0xa5) { /*Memory size test of XGA*/ xga->test = val; @@ -1474,7 +1476,7 @@ svga_read_common(uint32_t addr, uint8_t linear, void *priv) cycles -= svga->monitor->mon_video_timing_read_b; if (!linear) { - if (xga_active) { + if (xga_active && xga) { if (((xga->op_mode & 7) >= 4) && (xga->aperture_cntl >= 1)) { if (xga->test == 0xa5) { /*Memory size test of XGA*/ if (addr == 0xa0001) { diff --git a/src/video/vid_tgui9440.c b/src/video/vid_tgui9440.c index 0b5e99c4c..7bed43ac2 100644 --- a/src/video/vid_tgui9440.c +++ b/src/video/vid_tgui9440.c @@ -1462,7 +1462,7 @@ tgui_accel_command(int count, uint32_t cpu_dat, tgui_t *tgui) } } - /*See this: https://android.googlesource.com/kernel/tegra/+/android-tegra-flounder-3.10-lollipop-release/drivers/video/tridentfb.c for the pitch*/ + /* See Linux kernel drivers/video/tridentfb.c for the pitch */ tgui->accel.pitch = svga->rowoffset; switch (svga->bpp) { diff --git a/src/win/Makefile.mingw b/src/win/Makefile.mingw index 941684e92..148e2228c 100644 --- a/src/win/Makefile.mingw +++ b/src/win/Makefile.mingw @@ -637,7 +637,7 @@ DEVOBJ := bugger.o cartridge.o cassette.o hasp.o hwm.o hwm_lm75.o hwm_lm78.o hwm SIOOBJ := sio_acc3221.o sio_ali5123.o \ sio_f82c710.o sio_82091aa.o sio_fdc37c6xx.o \ sio_fdc37c67x.o sio_fdc37c669.o sio_fdc37c93x.o sio_fdc37m60x.o \ - sio_it8661f.o \ + sio_it86x1f.o \ sio_it8702.o \ sio_pc87306.o sio_pc87307.o sio_pc87309.o sio_pc87310.o sio_pc87311.o sio_pc87332.o \ sio_prime3b.o sio_prime3c.o \ @@ -692,7 +692,11 @@ NETOBJ := network.o \ net_3c503.o net_ne2000.o \ net_pcnet.o net_wd8003.o \ net_plip.o net_event.o \ - net_null.o + net_null.o \ + net_eeprom_nmc93cxx.o \ + net_tulip.o \ + net_rtl8139.o \ + net_l80225.o PRINTOBJ := png.o prt_cpmap.o \ prt_escp.o prt_text.o prt_ps.o @@ -708,6 +712,8 @@ SNDOBJ := sound.o \ wave8580__ST.o wave8580_P_T.o wave8580_PS_.o \ wave8580_PST.o wave.o \ midi.o \ + midi_opl4.o \ + midi_opl4_yrw801.o \ snd_speaker.o \ snd_pssj.o \ snd_ps1.o \ diff --git a/src/win/win_ui.c b/src/win/win_ui.c index 207158b29..73119140c 100644 --- a/src/win/win_ui.c +++ b/src/win/win_ui.c @@ -76,6 +76,8 @@ int hide_status_bar = 0; int hide_tool_bar = 0; int dpi = 96; +int status_icons_fullscreen = 0; /* unused. */ + extern char openfilestring[512]; extern WCHAR wopenfilestring[512]; diff --git a/vcpkg.json b/vcpkg.json index 9a21175ec..5fdfc175b 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,6 +1,6 @@ { "name": "86box", - "version-string": "4.0.2", + "version-string": "4.1", "homepage": "https://86box.net/", "documentation": "https://86box.readthedocs.io/", "license": "GPL-2.0-or-later",