From 1c67127d1d13e55bb709935a4850b92f8f7b77cf Mon Sep 17 00:00:00 2001 From: Akamaki <97360908+akmed772@users.noreply.github.com> Date: Mon, 16 Feb 2026 00:57:23 +0900 Subject: [PATCH 01/25] ps55da2: avoid resizing when video output is disabled --- src/video/vid_ps55da2.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/video/vid_ps55da2.c b/src/video/vid_ps55da2.c index 3df73f92c..a1d7fe25f 100644 --- a/src/video/vid_ps55da2.c +++ b/src/video/vid_ps55da2.c @@ -2352,12 +2352,8 @@ da2_recalctimings(da2_t *da2) da2->render = da2_render_blank; /* determine display mode */ // if (da2->attr_palette_enable && (da2->attrc[0x1f] & 0x08)) - /* if output disabled or VGA passthrough */ - if (da2->ioctl[LS_MODE] & 0x02 || !(da2->attrc[LV_COMPATIBILITY] & 0x08)) { - da2->render = da2_render_blank; - // return; /* 16 color graphics mode */ - } else if (!(da2->ioctl[LS_MODE] & 0x01)) { + if (!(da2->ioctl[LS_MODE] & 0x01)) { da2->hdisp *= 16; da2->char_width = 13; if (da2->crtc[LC_VIEWPORT_PRIORITY] & 0x80) { @@ -2383,6 +2379,9 @@ da2_recalctimings(da2_t *da2) da2->hdisp *= 13; da2->char_width = 13; } + /* if output disabled or VGA passthrough */ + if (da2->ioctl[LS_MODE] & 0x02 || !(da2->attrc[LV_COMPATIBILITY] & 0x08)) + da2->render = da2_render_blank; if (da2->vblankstart < da2->dispend) da2->dispend = da2->vblankstart; From c3db24162131f7dba9b76b3015f1ddb0a55ccd45 Mon Sep 17 00:00:00 2001 From: Akamaki <97360908+akmed772@users.noreply.github.com> Date: Thu, 19 Feb 2026 11:00:09 +0900 Subject: [PATCH 02/25] IBM 5550: Added font 16 emulation --- src/include/86box/machine.h | 4 +- src/machine/m_xt_ibm5550.c | 392 ++++++++++++++++++++++++++++++------ src/machine/machine_table.c | 2 +- 3 files changed, 331 insertions(+), 67 deletions(-) diff --git a/src/include/86box/machine.h b/src/include/86box/machine.h index af39e17e4..8a631f40a 100644 --- a/src/include/86box/machine.h +++ b/src/include/86box/machine.h @@ -1521,7 +1521,9 @@ extern int machine_xt_lxt3_init(const machine_t *); extern int machine_xt_compaq_deskpro_init(const machine_t *); /* m_xt_ibm5550.c */ - +#ifdef EMU_DEVICE_H +extern const device_t ibm5550_vid_device; +#endif extern int machine_xt_ibm5550_init(const machine_t *); /* m_xt_t1000.c */ diff --git a/src/machine/m_xt_ibm5550.c b/src/machine/m_xt_ibm5550.c index fed353344..089de761f 100644 --- a/src/machine/m_xt_ibm5550.c +++ b/src/machine/m_xt_ibm5550.c @@ -74,6 +74,8 @@ #define EPOCH_MASK_VRAM 0x3ffff /* 0xFFFFF */ // #define EPOCH_MASK_VRAMPLANE 0x1ffff /* 0x1FFFF */ #define EPOCH_PIXELCLOCK 40000000.0 /* 40 MHz interlaced */ +#define EPOCH_CONFIG_MONO16 0 /* Model 5551-Axx (Font 16, monochrome) */ +#define EPOCH_CONFIG_MONO24 1 /* Model 5551-Bxx (Font 24, monochrome) */ #define LC_INDEX 0x3D0 #define LC_DATA 0x3D1 @@ -156,11 +158,11 @@ enum epoch_nvr_ADDR { }; #ifndef RELEASE_BUILD -// #define ENABLE_EPOCH_LOG 1 +#define ENABLE_EPOCH_LOG 1 #endif #ifdef ENABLE_EPOCH_LOG -// #define ENABLE_EPOCH_DEBUGIO 1 +#define ENABLE_EPOCH_DEBUGIO 1 // #define ENABLE_EPOCH_DEBUGKBD 1 int epoch_do_log = ENABLE_EPOCH_LOG; @@ -236,6 +238,7 @@ typedef struct epoch_t { int cursorvisible, cursoron, blink; int scrollcache; int char_width; + int font24; int firstline, lastline; int firstline_draw, lastline_draw; @@ -307,6 +310,30 @@ The IBM 5550 has different IRQ assignments like the 6580 Displaywriter System. | FC000h | ROM | */ +#ifdef ENABLE_EPOCH_LOG +// #include +// static int dumpno = 0x61; +// static void +// epoch_dumpvram(void *priv) +// { +// FILE *fp; +// epoch_t *epoch = (epoch_t *) priv; +// char str1[64] = "epoch_vramvm_"; +// char str2[3] = {0x30, 0x30, 0}; +// if (!isalnum(dumpno)) +// return; +// str2[0] = dumpno; +// dumpno++; +// str2[1] = (epoch->crtmode & 0xf) + 0x30; +// strcat(str1,str2); +// fp = fopen(str1, "wb"); +// if (fp != NULL) { +// fwrite(epoch->vram, EPOCH_SIZE_VRAM, 1, fp); +// fclose(fp); +// } +// } +#endif + static void epoch_out(uint16_t addr, uint16_t val, void *priv) { @@ -370,7 +397,11 @@ epoch_out(uint16_t addr, uint16_t val, void *priv) // mem_mapping_enable(&epoch->paritymap); break; case 0x3D8: + /* Bit 3: Font access in read, Bit 1: graphic mode, Bit 0: Font access in write */ epoch->crtmode = val; +#ifdef ENABLE_EPOCH_LOG + // epoch_dumpvram(epoch); +#endif epoch_recalctimings(epoch); // epoch->attrff ^= 1; break; @@ -613,22 +644,43 @@ epoch_inw(uint16_t addr, void *priv) return temp; } -/* Get character line pattern from jfont rom or gaiji volatile memory */ +/* Get font pattern in a line from video memory */ static uint32_t getfont_ps55dbcs(int32_t code, int32_t line, void *priv) { - epoch_t *epoch = (epoch_t *) priv; + epoch_t *epoch = (epoch_t *) priv; uint32_t font = 0; - if (code < 1536) { - code *= 0x80; - font = epoch->vram[code + line * 4]; - font <<= 8; - font |= epoch->vram[code + line * 4 + 1]; - font <<= 8; - font |= epoch->vram[code + line * 4 + 2]; - font <<= 8; - font |= epoch->vram[code + line * 4 + 3]; - } else + if (code < 1536) { + code *= 0x80; + code += line * 4; + if (epoch->font24) { /* Font 24 (2 x 13 x 29) */ + font = epoch->vram[code]; + font <<= 8; + code++; + font |= epoch->vram[code]; + font <<= 8; + code++; + font |= epoch->vram[code]; + font <<= 8; + code++; + font |= epoch->vram[code]; + } else { /* Font 16 (2 x 9 x 21) */ + int bitnum = code & 0x7; + font = epoch->vram[code]; + font <<= 8; + font |= (epoch->vram[(code >> 3) + 0x20000 ] << (7 - bitnum)) & 0xff; /* get 9th bit */ + // font &= 0xffffff80; + // font |= epoch->vram[code + line * 4 + 1]; + font <<= 8; + code++; + font |= epoch->vram[code]; + font <<= 8; + bitnum = code & 0x7; + font |= (epoch->vram[(code >> 3) + 0x20000 ] << (7 - bitnum)) & 0xff; /* get 9th bit */ + font &= 0xff80ff80; + // font |= epoch->vram[code + line * 4 + 3]; + } + } else font = EPOCH_INVALIDACCESS32; return font; } @@ -676,12 +728,13 @@ epoch_render_text(epoch_t *epoch) uint8_t chr, attr; int fg, bg; uint32_t chr_dbcs; + int underscore_y = (epoch->font24) ? 28 : 20; int chr_wide = 0; // int colormode = ((epoch->attrc[LV_PAS_STATUS_CNTRL] & 0x80) == 0x80); int colormode = 0; // epoch_log("displ: %x, first: %x, epochma: %x, epochsc: %x\n", // epoch->displine , epoch->firstline_draw, epoch->memaddr, epoch->scanline); - for (x = 0; x < epoch->hdisp; x += 13) { + for (x = 0; x < epoch->hdisp; x += epoch->char_width) { chr = epoch->cram[(epoch->memaddr) & EPOCH_MASK_CRAM]; attr = epoch->cram[(epoch->memaddr + 1) & EPOCH_MASK_CRAM]; // if(chr!=0x20) epoch_log("chr: %x, attr: %x ", chr, attr); @@ -713,7 +766,7 @@ epoch_render_text(epoch_t *epoch) // if(chr!=0x20) epoch_log("chr: %x, %x, %x, %x, %x ", chr, attr, fg, epoch->egapal[fg], epoch->pallook[epoch->egapal[fg]]); } /* Draw character */ - for (uint32_t n = 0; n < 13; n++) + for (uint32_t n = 0; n < epoch->char_width; n++) p[n] = epoch->pallook[epoch->egapal[bg]]; /* draw blank */ /* SBCS or DBCS left half */ if (chr_wide == 0) { @@ -729,23 +782,37 @@ epoch_render_text(epoch_t *epoch) /* Get the font pattern */ uint32_t font = getfont_ps55dbcs(chr_dbcs, epoch->scanline, epoch); /* Draw 13 dots */ - for (uint32_t n = 0; n < 13; n++) { + for (uint32_t n = 0; n < epoch->char_width; n++) { p[n] = epoch->pallook[epoch->egapal[(font & 0x80000000) ? fg : bg]]; font <<= 1; } } else { /* the char code is SBCS (ANK) */ uint32_t fontbase; + uint16_t font; if (attr & 0x02) /* second map of SBCS font */ fontbase = EPOCH_VRAM_SBEX; else fontbase = EPOCH_VRAM_SBCS; - uint16_t font = epoch->vram[fontbase + chr * 0x80 + epoch->scanline * 4]; /* w13xh29 font */ - font <<= 8; - font |= epoch->vram[fontbase + chr * 0x80 + epoch->scanline * 4 + 1]; /* w13xh29 font */ + if (epoch->font24) { + font = epoch->vram[fontbase + chr * 0x80 + epoch->scanline * 4 + 2]; /* w13xh29 font */ + font <<= 8; + font |= epoch->vram[fontbase + chr * 0x80 + epoch->scanline * 4 + 3]; + } else { + int bitnum; + fontbase = (fontbase & 0x1ffff) + chr * 0x80 + epoch->scanline * 4; + bitnum = fontbase & 0x7; + font = epoch->vram[fontbase + 2]; /* w9xh21 font */ + font <<= 8; + // font |= epoch->vram[fontbase + chr * 0x80 + epoch->scanline * 4 + 3]; + fontbase >>= 3; + fontbase += 0x20000; /* real: C0000h */ + font |= epoch->vram[fontbase] << (8 + 7 - bitnum); + // if(chr!=0x20) epoch_log("faddr: %x, scline: %x, chr: %x, font: %x ", fontbase + chr * 0x80 + epoch->scanline * 4, epoch->scanline, chr, font); + } // if(chr!=0x20) epoch_log("memaddr: %x, scanline: %x, chr: %x, font: %x ", epoch->memaddr, epoch->scanline, chr, font); /* Draw 13 dots */ - for (uint32_t n = 0; n < 13; n++) { + for (uint32_t n = 0; n < epoch->char_width; n++) { p[n] = epoch->pallook[epoch->egapal[(font & 0x8000) ? fg : bg]]; font <<= 1; } @@ -755,15 +822,15 @@ epoch_render_text(epoch_t *epoch) else { uint32_t font = getfont_ps55dbcs(chr_dbcs, epoch->scanline, epoch); /* Draw 13 dots */ - for (uint32_t n = 0; n < 13; n++) { + for (uint32_t n = 0; n < epoch->char_width; n++) { p[n] = epoch->pallook[epoch->egapal[(font & 0x8000) ? fg : bg]]; font <<= 1; } chr_wide = 0; } /* Line 28 (Underscore) Note: Draw this first to display blink + vertical + underline correctly. */ - if (epoch->scanline == 28 && attr & 0x40 && !colormode) { /* Underscore only in monochrome mode */ - for (uint32_t n = 0; n < 13; n++) + if (epoch->scanline == underscore_y && attr & 0x40 && !colormode) { /* Underscore only in monochrome mode */ + for (uint32_t n = 0; n < epoch->char_width; n++) p[n] = epoch->pallook[epoch->egapal[fg]]; /* under line (white) */ } /* Column 1 (Vertical Line) */ @@ -771,14 +838,14 @@ epoch_render_text(epoch_t *epoch) p[0] = epoch->pallook[epoch->egapal[2]]; /* vertical line (white) */ } if (epoch->scanline == 0 && attr & 0x20) { /* HGrid */ - for (uint32_t n = 0; n < 13; n++) + for (uint32_t n = 0; n < epoch->char_width; n++) p[n] = epoch->pallook[epoch->egapal[2]]; /* horizontal line (white) */ } /* Drawing text cursor */ drawcursor = ((epoch->memaddr == epoch->cursoraddr) && epoch->cursorvisible && epoch->cursoron); if (drawcursor && epoch->scanline >= epoch->crtc[LC_CURSOR_ROW_START] && epoch->scanline <= epoch->crtc[LC_CURSOR_ROW_END]) { // int cursorwidth = (epoch->crtc[LC_COMPATIBILITY] & 0x20 ? 26 : 13); - int cursorwidth = 13; + int cursorwidth = epoch->char_width; int cursorcolor = 2; /* Choose color 2 if mode 8 */ fg = ((attr & 0x08) ? 3 : 2); bg = 0; @@ -793,7 +860,7 @@ epoch_render_text(epoch_t *epoch) p[n] = (p[n] == epoch->pallook[epoch->egapal[bg]]) ? epoch->pallook[epoch->egapal[cursorcolor]] : p[n]; } epoch->memaddr += 2; - p += 13; + p += epoch->char_width; } } } @@ -905,8 +972,13 @@ epoch_recalctimings(epoch_t *epoch) /* determine display mode */ if (epoch->crtmode & 0x02) { - epoch->hdisp *= 16; - epoch->char_width = 16; + if (epoch->font24) { + epoch->hdisp *= 16; + epoch->char_width = 16; + } else { + epoch->hdisp *= 11; + epoch->char_width = 11; + } /* PS/55 8-color */ epoch_log("Set videomode to PS/55 4 bpp graphics.\n"); epoch->render = epoch_render_color_4bpp; @@ -916,8 +988,13 @@ epoch_recalctimings(epoch_t *epoch) epoch_log("Set videomode to PS/55 Mode 8/E text.\n"); epoch->render = epoch_render_text; epoch->vram_display_mask = EPOCH_MASK_CRAM; - epoch->hdisp *= 13; - epoch->char_width = 13; + if (epoch->font24) { + epoch->hdisp *= 13; + epoch->char_width = 13; + } else { + epoch->hdisp *= 9; + epoch->char_width = 9; + } } if (epoch->crtmode & 0x08) epoch->render = epoch_render_blank; @@ -1114,55 +1191,211 @@ epoch_vram_write(uint32_t addr, uint8_t val, void *priv) { epoch_t *epoch = (epoch_t *) priv; // if ((addr & ~0xfff) != 0xE0000) return; - addr -= 0xA0000; + // epoch_log("epoch_vw: %x %x\n", addr, val); addr &= EPOCH_MASK_VRAM; epoch->vram[addr] = val; epoch->fullchange = changeframecount; // if(val == 0x66) // epoch_log("66 %04X:%04X %04X:%04X>%04X:%04X\n", cs >> 4, cpu_state.pc, DS, SI,ES,DI); } -static void -epoch_vram_writeb(uint32_t addr, uint8_t val, void *priv) -{ - epoch_t *epoch = (epoch_t *) priv; - // epoch_log("%04X:%04X epoch_vram_writeb: %x, val %x\n", cs >> 4, cpu_state.pc, addr, val); - cycles -= video_timing_write_b; - epoch_vram_write(addr, val, epoch); -} -static void -epoch_vram_writew(uint32_t addr, uint16_t val, void *priv) -{ - // epoch_log("%04X:%04X epoch_vram_writew: %x, val %x\n", cs >> 4, cpu_state.pc, addr, val); - epoch_t *epoch = (epoch_t *) priv; - cycles -= video_timing_write_w; - epoch_vram_write(addr, val & 0xff, epoch); - epoch_vram_write(addr + 1, val >> 8, epoch); -} - static uint8_t epoch_vram_read(uint32_t addr, void *priv) { epoch_t *epoch = (epoch_t *) priv; // if ((addr & ~epoch_MASK_CRAM) != 0xE0000) // return epoch_INVALIDACCESS8; - addr -= 0xA0000; addr &= EPOCH_MASK_VRAM; return epoch->vram[addr]; } + +static void +epoch_vram_writeb(uint32_t addr, uint8_t val, void *priv) +{ + epoch_t *epoch = (epoch_t *) priv; +// epoch_log("%04X:%04X epoch_vwb: %x, val %x\n", cs >> 4, cpu_state.pc, addr, val); + cycles -= video_timing_write_b; + // return; +// epoch_vram_write(addr, val, epoch); +} +static void +epoch_vram_writew(uint32_t addr, uint16_t val, void *priv) +{ + epoch_t *epoch = (epoch_t *) priv; + // uint8_t convert[8] = {0, 2, 8, 8, 1, 3, 8, 8};/* only even chars */ + //uint8_t convert[8] = {8, 8, 0, 2, 8, 8, 1, 3};/* only even chars */ + // uint8_t convert[8] = {0, 1, 2, 3, 4, 5, 6, 7};/* all but alt */ + // uint8_t convert[8] = {0, 1, 2, 3, 8, 8, 8, 8};/* even alt */ + // uint8_t convert[8] = {8, 8, 8, 8, 0, 1, 2, 3};/* even alt */ + // uint8_t convert[8] = {0, 4, 1, 5, 2, 6, 3, 7};/* all but pos+-1 */ + // uint8_t convert[8] = {8, 8, 4, 6, 8, 8, 5, 7};/* only odd (0) */ + // uint8_t convert[8] = {8, 8, 0, 2, 8, 8, 1, 3};/* only even */ + // uint8_t convert[8] = {4, 6, 8, 8, 5, 7, 8, 8};/* only odd (0) */ + // uint8_t convert[8] = {8, 4, 6, 8, 8, 5, 7, 8};/* none */ + // epoch_log("%04X:%04X epoch_vww: %x, val %x DS %x SI %x ES %x DI %x %x\n", cs >> 4, cpu_state.pc, addr, val,DS,SI,ES,DI, epoch->crtc[LC_INTERLACE_AND_SKEW]); + epoch_log("%04X:%04X epoch_vww: %x, val %x cm %x\n", cs >> 4, cpu_state.pc, addr, val, epoch->crtmode); + cycles -= video_timing_write_w; + addr -= 0xA0000; + // addr &= 0xfffffffe; + // if (0) { + if (!(epoch->crtmode & 0x02) && !(epoch->font24)) { + uint32_t toaddr, bitnum; + // if (addr < 0xd0000) { + // int index = (addr - 0xA0000) >> 7; /* 128 bytes per char */ + // addr &= 0x007f; + // addr += 0xA0000 + 84 * index; + // epoch_log("%x %x\n", addr, index); + // } else if (addr >= 0xD8000) { + // int index = (addr - 0xD8000) >> 7; /* 128 bytes per char */ + // addr &= 0x007f; + // if (addr & 2) return; + // addr >>= 1; + // addr += 0xC0000 + 42 * index; + // epoch_log("%x %x\n", addr, index); + // } else + // return; + + // if (addr < 0xd0000) { + // addr -= 0xA0000; + // if (addr & 2) + // return; + // addr >>= 1; + // addr += 0xA0000; + // } else if (addr >= 0xD8000) { + // addr -= 0xD8000; + // if (addr & 2) + // return; + // addr >>= 1; + // addr += 0xD8000; + // } else + // return; + + // addr >>= 1; + // if(addr & 0x02) + // addr--; + // addr ^= 0x06; + //addr &= 0xffffd;/* 1101 */ + // addr >>= 1; + + // if (convert[(addr + 0) & 7] > 7) + // return; + // epoch_vram_write((addr & 0xffff8) + convert[(addr + 0) & 7], val & 0xff, epoch); + // epoch_vram_write((addr & 0xffff8) + convert[(addr + 1) & 7], val >> 8, epoch); + + /* rw one word with 9 bits */ + /* virtual: 20000h (0010b, 0011b) -> real: 00001h (0000b, 0001b) */ + toaddr = addr; + if (addr & 2) + toaddr--; + if (addr & 0x20000) + toaddr+=2; + toaddr &= 0xdffff; + + //addr >>= 1; + + epoch_vram_write(toaddr, val & 0xff, epoch); + // epoch_log("%x %x ", toaddr, val); + /* get 9th bit */ + bitnum = addr & 0x7; + val >>= 15; + val <<= bitnum; + addr >>= 3; + addr += 0x20000; /* real: C0000h */ + val |= epoch_vram_read(addr, epoch) & ~(1 << bitnum);/* mask to update one bit */ + epoch_vram_write(addr, val, epoch); + // epoch_log("%x %x\n", addr, val); + } else {/* is graphic mode */ + epoch_vram_write(addr, val & 0xff, epoch); + epoch_vram_write(addr + 1, val >> 8, epoch); + } + // epoch_log("%x %x\n", addr, addr + 1); +} + static uint8_t epoch_vram_readb(uint32_t addr, void *priv) { epoch_t *epoch = (epoch_t *) priv; cycles -= video_timing_read_b; - return epoch_vram_read(addr, epoch); + // return 0xff; + // epoch_log("%04X:%04X epoch_vrb: %x, val %x\n", cs >> 4, cpu_state.pc, addr, epoch_vram_read(addr, epoch)); + // return epoch_vram_read(addr, epoch); } static uint16_t epoch_vram_readw(uint32_t addr, void *priv) { epoch_t *epoch = (epoch_t *) priv; + // uint8_t convert[8] = {0, 2, 4, 6, 1, 3, 5, 7};/* all but pos+-1 */ + // uint8_t convert[8] = {0, 4, 8, 8, 8, 8, 8, 8};/* all but pos+-1 */ cycles -= video_timing_read_w; - return epoch_vram_read(addr, epoch) | (epoch_vram_read(addr + 1, epoch) << 8); + addr -= 0xA0000; + epoch_log("%04X:%04X epoch_vrw: %x cm %x\n", + cs >> 4, cpu_state.pc, addr, epoch->crtmode); + // addr &= 0xfffffffe; + //read 0->0,1 2->2,3 4->4,5 6->6,7 + //read 0->0,2 2->1,3 4->4,6 6->5,7 + // if (0) { + if (!(epoch->crtmode & 0x02) && !(epoch->font24)) { + uint16_t ret; + uint32_t bitnum; + uint32_t toaddr; + // if (addr < 0xd0000) { + // int index = (addr - 0xA0000) >> 7; /* 128 bytes per char */ + // addr &= 0x007f; + // addr += 0xA0000 + 84 * index; + // epoch_log("%x %x %x\n", addr, index, epoch_vram_read(addr, epoch) | (epoch_vram_read(addr + 1, epoch) << 8)); + // } else if (addr >= 0xD8000) { + // int index = (addr - 0xD8000) >> 7; /* 128 bytes per char */ + // addr &= 0x007f; + // if (addr & 2) return EPOCH_INVALIDACCESS16; + // addr >>= 1; + // addr += 0xC0000 + 42 * index; + // epoch_log("%x %x %x\n", addr, index, epoch_vram_read(addr, epoch) | (epoch_vram_read(addr + 1, epoch) << 8)); + // } else + // return EPOCH_INVALIDACCESS16; + + // if (addr < 0xd0000) { + // addr -= 0xA0000; + // if (addr & 2) + // return EPOCH_INVALIDACCESS16; + // addr >>= 1; + // addr += 0xA0000; + // } else if (addr >= 0xD8000) { + // addr -= 0xD8000; + // if (addr & 2) + // return EPOCH_INVALIDACCESS16; + // addr >>= 1; + // addr += 0xD8000; + // } else + // return EPOCH_INVALIDACCESS16; + + // if(addr & 0x02) + // addr--; + // // addr ^= 0x06; + // addr >>= 1; + + // if (convert[(addr + 0) & 7] > 7) + // return 0; + // return epoch_vram_read((addr & 0xffff8) + convert[addr & 7], epoch) | (epoch_vram_read((addr & 0xffff8) + convert[(addr + 1) & 7], epoch) << 8); + + /* rw one word with 9 bits */ + /* virtual: 20000h (0010b, 0011b) -> real: 00001h (0000b, 0001b) */ + toaddr = addr; /* 1101 */ + if (addr & 2) + toaddr--; + if (addr & 0x20000) + toaddr+=2; + toaddr &= 0xdffff; + ret = epoch_vram_read(toaddr, epoch); + /* get 9th bit */ + bitnum = addr & 0x7; + addr >>= 3; + addr += 0x20000; /* real: C0000h */ + ret |= (epoch_vram_read(addr, epoch) << (8 + 7 - bitnum)) & 0x8000; + return ret; + // return epoch_vram_read(addr, epoch) | (epoch_vram_read(addr + 1, epoch) << 8); + } else {/* is graphic mode */ + return epoch_vram_read(addr, epoch) | (epoch_vram_read(addr + 1, epoch) << 8); + } } @@ -1327,8 +1560,10 @@ x1xx xxxx: No floppy drive 1xxx xxxx: No (bootable?) hard drive */ case 0xA2: - ret = 0xA8;/* Mono 24 */ - // ret = 0xA2;/* Mono 16 */ + if (epoch->font24) + ret = 0xA8; /* Mono 24 */ + else + ret = 0xA2; /* Mono 16 */ break; /* I/O A3h R: @@ -1502,7 +1737,7 @@ static void kbd_epoch_adddata(uint16_t val) { key_queue[key_queue_end] = val; - epoch_kbdlog("epochkbd: %02X added to key queue at %i\n", + epoch_log("epochkbd: %02X added to key queue at %i\n", val, key_queue_end); key_queue_end = (key_queue_end + 1) & 0x0f; } @@ -1703,6 +1938,7 @@ kbd_close(void *priv) /* Stop the timer. */ timer_disable(&kbd->send_delay_timer); + mouse_close(); /* Disable scanning. */ keyboard_scan = 0; @@ -1967,10 +2203,12 @@ epoch_reset(void *priv) epoch->parityenabled = 1; epoch->lowmemorydisabled = 1; epoch->crtioenabled = 0; + mem_mapping_disable(&epoch->cmap); + mem_mapping_disable(&epoch->vmap); // epoch->attrc[LV_CURSOR_COLOR] = 0x0f; /* cursor color */ epoch->crtc[LC_HORIZONTAL_TOTAL] = 103; /* Horizontal Total */ epoch->crtc[LC_VERTICAL_TOTAL] = 26; /* Vertical Total (These two must be set before the timer starts.) */ - epoch->crtmode = 0x08; + epoch->crtmode = 0; epoch->vram_display_mask = EPOCH_MASK_CRAM; // epoch->plane_mask = 1; epoch->oddeven = 0; @@ -2080,8 +2318,6 @@ epoch_init(UNUSED(const device_t *info)) epoch->epochconst = (uint64_t) ((cpuclock / EPOCH_PIXELCLOCK) * (double) (1ull << 32)); - epoch_reset(epoch); - mem_mapping_add(&epoch->cmap, 0xE0000, 0x1000, epoch_cram_readb, epoch_cram_readw, NULL, epoch_cram_writeb, epoch_cram_writew, NULL, NULL, MEM_MAPPING_EXTERNAL, epoch); mem_mapping_add(&epoch->vmap, 0xA0000, 0x40000, epoch_vram_readb, epoch_vram_readw, NULL, @@ -2089,8 +2325,6 @@ epoch_init(UNUSED(const device_t *info)) // mem_mapping_add(&epoch->fontcard.map, 0xF0000, 0xC000, epoch_font_readb, NULL, NULL, // epoch_font_writeb, NULL, NULL, NULL, MEM_MAPPING_EXTERNAL, epoch); - mem_mapping_disable(&epoch->cmap); - mem_mapping_disable(&epoch->vmap); // mem_mapping_disable(&epoch->fontcard.map); mem_mapping_add(&epoch->paritymap, 0, 0xA0000, epoch_parity_readb, epoch_parity_readw, NULL, epoch_parity_writeb, epoch_parity_writew, NULL, NULL, MEM_MAPPING_CACHE, epoch); @@ -2106,10 +2340,14 @@ epoch_init(UNUSED(const device_t *info)) // io_sethandler(0x160, 0x0010, // epoch_misc_in, NULL, NULL, epoch_misc_out, NULL, NULL, epoch); + epoch_reset(epoch); + timer_add(&epoch->timer, epoch_poll, epoch, 1); epoch_nvr_init(epoch, 17); + epoch->font24 = device_get_config_int("model"); + return epoch; } @@ -2138,6 +2376,7 @@ epoch_close(void *priv) // } fp = fopen("epoch_daregs.txt", "w"); if (fp != NULL) { + fprintf(fp, "3d8(crtmode) %02X\n", epoch->crtmode); // for (uint8_t i = 0; i < 0x10; i++) // fprintf(fp, "3e1(ioctl) %02X: %4X %d\n", i, epoch->ioctl[i], epoch->ioctl[i]); // for (uint8_t i = 0; i < 0x20; i++) @@ -2186,8 +2425,31 @@ epoch_force_redraw(void *priv) epoch->fullchange = changeframecount; } -static const device_t epoch_device = { - .name = "IBM 5550 (Epoch) Video Controller", +static const device_config_t epoch_config[] = { + // clang-format off + { + .name = "model", + .description = "Model", + .type = CONFIG_SELECTION, + .default_int = EPOCH_CONFIG_MONO24, + .selection = { + { + .description = "A (Font 16)", + .value = EPOCH_CONFIG_MONO16 + }, + { + .description = "B (Font 24)", + .value = EPOCH_CONFIG_MONO24 + }, + { .description = "" } + } + }, + { .name = "", .description = "", .type = CONFIG_END } + // clang-format on +}; + +const device_t ibm5550_vid_device = { + .name = "IBM 5550 Video Adapter", .internal_name = "ibm5550vid", .flags = DEVICE_ISA, .local = 0, @@ -2197,7 +2459,7 @@ static const device_t epoch_device = { .available = NULL, .speed_changed = epoch_speed_changed, .force_redraw = epoch_force_redraw, - .config = NULL + .config = epoch_config }; static void @@ -2258,7 +2520,7 @@ machine_xt_ibm5550_init(const machine_t *model) pit_ibm5550_init(); nmi_mask = 0; - device_add(&epoch_device); + device_add(&ibm5550_vid_device); device_add(&lpt_port_device); serial_t *uart = device_add(&ns8250_device); diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index 85b09676e..2e76f5a24 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -2728,7 +2728,7 @@ const machine_t machines[] = { .kbd_device = NULL, .fdc_device = NULL, .sio_device = NULL, - .vid_device = NULL, + .vid_device = &ibm5550_vid_device, .snd_device = NULL, .net_device = NULL }, From 0a4b44184affb8abf55017e933b2079b57577e2f Mon Sep 17 00:00:00 2001 From: Akamaki <97360908+akmed772@users.noreply.github.com> Date: Thu, 19 Feb 2026 11:04:37 +0900 Subject: [PATCH 03/25] IBM 5550: comment out unused code --- src/machine/m_xt_ibm5550.c | 54 ++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/src/machine/m_xt_ibm5550.c b/src/machine/m_xt_ibm5550.c index 089de761f..ff369dede 100644 --- a/src/machine/m_xt_ibm5550.c +++ b/src/machine/m_xt_ibm5550.c @@ -79,8 +79,6 @@ #define LC_INDEX 0x3D0 #define LC_DATA 0x3D1 -#define LS_ENABLE 0x3D2 -#define LS_DISABLE 0x3D3 #define LC_HORIZONTAL_TOTAL 0x00 /* -1 */ #define LC_HORIZONTAL_DISPLAYED 0x01 #define LC_H_SYNC_POSITION 0x02 @@ -99,6 +97,10 @@ #define LC_CURSOR_LOC_LOWJ 0x0F #define LC_LIGHT_PEN_HIGH 0x10 #define LC_LIGHT_PEN_LOW 0x11 +#define LS_ENABLE 0x3D2 +#define LS_DISABLE 0x3D3 +#define LS_MODE 0x3D8 +#define LS_MONSENSE 0x3DA // #define LV_PORT 0x3E8 // #define LV_PALETTE_0 0x00 // #define LV_MODE_CONTROL 0x10 @@ -396,8 +398,8 @@ epoch_out(uint16_t addr, uint16_t val, void *priv) mem_mapping_disable(&epoch->vmap); // mem_mapping_enable(&epoch->paritymap); break; - case 0x3D8: - /* Bit 3: Font access in read, Bit 1: graphic mode, Bit 0: Font access in write */ + case LS_MODE: + /* Bit 3: Video output enable, Bit 1: Graphic mode (switch 16 / 9 bit word in Font 16 system) */ epoch->crtmode = val; #ifdef ENABLE_EPOCH_LOG // epoch_dumpvram(epoch); @@ -515,7 +517,7 @@ epoch_in(uint16_t addr, void *priv) // // epoch_iolog("epoch In %04X(%02X) %04X %04X:%04X\n", addr, epoch->attraddr, temp, cs >> 4, cpu_state.pc); // epoch->attrff = 0; /* reset flipflop (VGA does not reset flipflop) */ // break; - case 0x3DA: + case LS_MONSENSE: temp = 0xff; if (!(epoch->crtmode & 0x08)) {/* The video out is active */ if(epoch->cgastat & 8) @@ -525,7 +527,7 @@ epoch_in(uint16_t addr, void *priv) // temp &= 0xfe; /* color */ break; } - if (addr != 0x3DA) + if (addr != LS_MONSENSE) epoch_iolog("%04X:%04X epoch In %04X %04X\n", cs >> 4, cpu_state.pc, addr, temp); return temp; } @@ -1208,15 +1210,15 @@ epoch_vram_read(uint32_t addr, void *priv) return epoch->vram[addr]; } -static void -epoch_vram_writeb(uint32_t addr, uint8_t val, void *priv) -{ - epoch_t *epoch = (epoch_t *) priv; -// epoch_log("%04X:%04X epoch_vwb: %x, val %x\n", cs >> 4, cpu_state.pc, addr, val); - cycles -= video_timing_write_b; - // return; -// epoch_vram_write(addr, val, epoch); -} +// static void +// epoch_vram_writeb(uint32_t addr, uint8_t val, void *priv) +// { +// epoch_t *epoch = (epoch_t *) priv; +// // epoch_log("%04X:%04X epoch_vwb: %x, val %x\n", cs >> 4, cpu_state.pc, addr, val); +// cycles -= video_timing_write_b; +// // return; +// // epoch_vram_write(addr, val, epoch); +// } static void epoch_vram_writew(uint32_t addr, uint16_t val, void *priv) { @@ -1310,15 +1312,15 @@ epoch_vram_writew(uint32_t addr, uint16_t val, void *priv) // epoch_log("%x %x\n", addr, addr + 1); } -static uint8_t -epoch_vram_readb(uint32_t addr, void *priv) -{ - epoch_t *epoch = (epoch_t *) priv; - cycles -= video_timing_read_b; - // return 0xff; - // epoch_log("%04X:%04X epoch_vrb: %x, val %x\n", cs >> 4, cpu_state.pc, addr, epoch_vram_read(addr, epoch)); - // return epoch_vram_read(addr, epoch); -} +// static uint8_t +// epoch_vram_readb(uint32_t addr, void *priv) +// { +// epoch_t *epoch = (epoch_t *) priv; +// cycles -= video_timing_read_b; +// // return 0xff; +// // epoch_log("%04X:%04X epoch_vrb: %x, val %x\n", cs >> 4, cpu_state.pc, addr, epoch_vram_read(addr, epoch)); +// // return epoch_vram_read(addr, epoch); +// } static uint16_t epoch_vram_readw(uint32_t addr, void *priv) @@ -2320,8 +2322,8 @@ epoch_init(UNUSED(const device_t *info)) mem_mapping_add(&epoch->cmap, 0xE0000, 0x1000, epoch_cram_readb, epoch_cram_readw, NULL, epoch_cram_writeb, epoch_cram_writew, NULL, NULL, MEM_MAPPING_EXTERNAL, epoch); - mem_mapping_add(&epoch->vmap, 0xA0000, 0x40000, epoch_vram_readb, epoch_vram_readw, NULL, - epoch_vram_writeb, epoch_vram_writew, NULL, NULL, MEM_MAPPING_EXTERNAL, epoch); + mem_mapping_add(&epoch->vmap, 0xA0000, 0x40000, NULL, epoch_vram_readw, NULL, + NULL, epoch_vram_writew, NULL, NULL, MEM_MAPPING_EXTERNAL, epoch); // mem_mapping_add(&epoch->fontcard.map, 0xF0000, 0xC000, epoch_font_readb, NULL, NULL, // epoch_font_writeb, NULL, NULL, NULL, MEM_MAPPING_EXTERNAL, epoch); From 5e6e81353ecf5844b10164709f298e2df602f79c Mon Sep 17 00:00:00 2001 From: win2kgamer <47463859+win2kgamer@users.noreply.github.com> Date: Wed, 18 Feb 2026 22:25:38 -0600 Subject: [PATCH 04/25] VL82C48x: Implement ROMSET register ROMMOV/ROMCS# functionality --- src/chipset/vl82c480.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/chipset/vl82c480.c b/src/chipset/vl82c480.c index cf3682754..e5e904567 100644 --- a/src/chipset/vl82c480.c +++ b/src/chipset/vl82c480.c @@ -77,6 +77,14 @@ vl82c480_recalc_shadow(vl82c480_t *dev) } } + /* Implement ROMCS# disable portion of ROMMOV behavior */ + if ((dev->regs[0x11] == 0x00) && ((dev->regs[0x0c] & 0x20) || (dev->regs[0x0c] & 0x10))) + mem_set_mem_state(0xe0000, 0x10000, MEM_READ_EXTERNAL | MEM_WRITE_EXTERNAL); + if (!(dev->regs[0x0f] & 0x0f) && !(dev->regs[0x0c] & 0x20)) { + mem_set_mem_state(0xc0000, 0x8000, MEM_READ_EXTERNAL | MEM_WRITE_EXTERNAL); + if (!(dev->regs[0x0f] & 0xf0) && !((dev->regs[0x0c] & 0x30) == 0x30)) { + mem_set_mem_state(0xc8000, 0x8000, MEM_READ_EXTERNAL | MEM_WRITE_EXTERNAL); + flushmmucache(); } @@ -146,6 +154,10 @@ vl82c480_write(uint16_t addr, uint8_t val, void *priv) case 0x07: dev->regs[dev->idx] = (dev->regs[dev->idx] & 0x40) | (val & 0xbf); break; + case 0x0c: + dev->regs[dev->idx] = val; + vl82c480_recalc_shadow(dev); + break; case 0x0d ... 0x12: dev->regs[dev->idx] = val; vl82c480_recalc_shadow(dev); From 58323156c1b29537ed76265d43aa715d18be2290 Mon Sep 17 00:00:00 2001 From: win2kgamer <47463859+win2kgamer@users.noreply.github.com> Date: Wed, 18 Feb 2026 22:35:39 -0600 Subject: [PATCH 05/25] VL82C48x: Add logging and fix a compile-breaking mistake --- src/chipset/vl82c480.c | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/src/chipset/vl82c480.c b/src/chipset/vl82c480.c index e5e904567..fccf8f1ab 100644 --- a/src/chipset/vl82c480.c +++ b/src/chipset/vl82c480.c @@ -12,11 +12,17 @@ * * Copyright 2020 Miran Grca. */ + +#ifdef ENABLE_VL82C48X_LOG +#include +#endif + #include #include #include #include #include +#define HAVE_STDARG_H #include <86box/86box.h> #include "cpu.h" #include <86box/timer.h> @@ -27,11 +33,31 @@ #include <86box/nmi.h> #include <86box/port_92.h> #include <86box/chipset.h> +#include <86box/log.h> + +#ifdef ENABLE_VL82C48X_LOG +int vl82c48x_do_log = ENABLE_VL82C48X_LOG; + +static void +vl82c48x_log(void *priv, const char *fmt, ...) +{ + if (vl82c48x_do_log) { + va_list ap; + va_start(ap, fmt); + log_out(priv, fmt, ap); + va_end(ap); + } +} +#else +# define vl82c48x_log(fmt, ...) +#endif typedef struct vl82c480_t { uint8_t idx; uint8_t regs[256]; uint32_t banks[4]; + + void * log; // New logging system } vl82c480_t; static int @@ -80,9 +106,9 @@ vl82c480_recalc_shadow(vl82c480_t *dev) /* Implement ROMCS# disable portion of ROMMOV behavior */ if ((dev->regs[0x11] == 0x00) && ((dev->regs[0x0c] & 0x20) || (dev->regs[0x0c] & 0x10))) mem_set_mem_state(0xe0000, 0x10000, MEM_READ_EXTERNAL | MEM_WRITE_EXTERNAL); - if (!(dev->regs[0x0f] & 0x0f) && !(dev->regs[0x0c] & 0x20)) { + if (!(dev->regs[0x0f] & 0x0f) && !(dev->regs[0x0c] & 0x20)) mem_set_mem_state(0xc0000, 0x8000, MEM_READ_EXTERNAL | MEM_WRITE_EXTERNAL); - if (!(dev->regs[0x0f] & 0xf0) && !((dev->regs[0x0c] & 0x30) == 0x30)) { + if (!(dev->regs[0x0f] & 0xf0) && !((dev->regs[0x0c] & 0x30) == 0x30)) mem_set_mem_state(0xc8000, 0x8000, MEM_READ_EXTERNAL | MEM_WRITE_EXTERNAL); flushmmucache(); @@ -124,6 +150,8 @@ vl82c480_write(uint16_t addr, uint8_t val, void *priv) { vl82c480_t *dev = (vl82c480_t *) priv; + vl82c48x_log(dev->log, "[%04X:%08X] VL82c48x: [W] %04X = %02X\n", CS, cpu_state.pc, addr, val); + switch (addr) { case 0xec: dev->idx = val; @@ -207,6 +235,8 @@ vl82c480_read(uint16_t addr, void *priv) break; } + vl82c48x_log(dev->log, "[%04X:%08X] VL82c48x: [R] %04X = %02X\n", CS, cpu_state.pc, addr, ret); + return ret; } @@ -215,6 +245,11 @@ vl82c480_close(void *priv) { vl82c480_t *dev = (vl82c480_t *) priv; + if (dev->log != NULL) { + log_close(dev->log); + dev->log = NULL; + } + free(dev); } @@ -229,6 +264,8 @@ vl82c480_init(const device_t *info) uint8_t min_j = (machines[machine].init == machine_at_monsoon_init) ? 2 : 2; uint8_t max_j = (machines[machine].init == machine_at_monsoon_init) ? 7 : 7; + dev->log = log_open("VL82c48x"); + dev->regs[0x00] = info->local; dev->regs[0x01] = 0xff; dev->regs[0x02] = 0x8a; From 4bab8b1fc5a66f53ad09c23d5646a76585bf0d67 Mon Sep 17 00:00:00 2001 From: win2kgamer <47463859+win2kgamer@users.noreply.github.com> Date: Wed, 18 Feb 2026 23:35:51 -0600 Subject: [PATCH 06/25] Mirror the ROMs for the HP Vectra 486VL and Siemens-Nixdorf D824 and remove the now unneeded Cirrus VBIOS hacks for them --- src/machine/m_at_socket1.c | 26 ++++++++++++++++++++++++-- src/video/vid_cl54xx.c | 11 ++--------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/machine/m_at_socket1.c b/src/machine/m_at_socket1.c index 563227f2e..7bb69d368 100644 --- a/src/machine/m_at_socket1.c +++ b/src/machine/m_at_socket1.c @@ -294,11 +294,13 @@ machine_at_vect486vl_init(const machine_t *model) // has HDC problems int ret; ret = bios_load_linear("roms/machines/vect486vl/aa0500.ami", - 0x000e0000, 131072, 0); + 0x000c0000, 262144, 0); if (bios_only || !ret) return ret; + memcpy(&rom[0x00020000], rom, 131072); + if (gfxcard[0] == VID_INTERNAL) device_add(machine_get_vid_device(machine)); @@ -311,6 +313,15 @@ machine_at_vect486vl_init(const machine_t *model) // has HDC problems device_add(&ide_isa_device); device_add_params(&fdc37c6xx_device, (void *) (FDC37C651 | FDC37C6XX_IDE_PRI)); + video_reset(gfxcard[0]); + + if (gfxcard[0] != VID_INTERNAL) { + for (uint16_t i = 0; i < 32768; i++) + rom[i] = mem_readb_phys(0x000c0000 + i); + } + mem_mapping_set_addr(&bios_mapping, 0x0c0000, 0x40000); + mem_mapping_set_exec(&bios_mapping, rom); + return ret; } @@ -321,11 +332,13 @@ machine_at_d824_init(const machine_t *model) int ret; ret = bios_load_linear("roms/machines/d824/fts-biosupdated824noflashbiosepromv320-320334-160.bin", - 0x000e0000, 131072, 0); + 0x000c0000, 262144, 0); if (bios_only || !ret) return ret; + memcpy(&rom[0x00020000], rom, 131072); + if (gfxcard[0] == VID_INTERNAL) device_add(machine_get_vid_device(machine)); @@ -342,6 +355,15 @@ machine_at_d824_init(const machine_t *model) device_add(&ide_isa_device); device_add_params(&fdc37c6xx_device, (void *) FDC37C651); + video_reset(gfxcard[0]); + + if (gfxcard[0] != VID_INTERNAL) { + for (uint16_t i = 0; i < 32768; i++) + rom[i] = mem_readb_phys(0x000c0000 + i); + } + mem_mapping_set_addr(&bios_mapping, 0x0c0000, 0x40000); + mem_mapping_set_exec(&bios_mapping, rom); + return ret; } diff --git a/src/video/vid_cl54xx.c b/src/video/vid_cl54xx.c index ae2da7e15..edf49fe88 100644 --- a/src/video/vid_cl54xx.c +++ b/src/video/vid_cl54xx.c @@ -54,8 +54,6 @@ #define BIOS_GD5428_ISA_PATH "roms/video/cirruslogic/5428.bin" #define BIOS_GD5428_MCA_PATH "roms/video/cirruslogic/SVGA141.ROM" #define BIOS_GD5428_ONBOARD_ACER_PATH "roms/machines/acera1g/4alo001.bin" -#define BIOS_GD5428_ONBOARD_HP_PATH "roms/machines/vect486vl/aa0500.ami" -#define BIOS_GD5428_ONBOARD_SNI_PATH "roms/machines/d824/fts-biosupdated824noflashbiosepromv320-320334-160.bin" #define BIOS_GD5428_PATH "roms/video/cirruslogic/vlbusjapan.BIN" #define BIOS_GD5428_BOCA_ISA_PATH_1 "roms/video/cirruslogic/boca_gd5428_1.30b_1.bin" #define BIOS_GD5428_BOCA_ISA_PATH_2 "roms/video/cirruslogic/boca_gd5428_1.30b_2.bin" @@ -4317,10 +4315,7 @@ gd54xx_init(const device_t *info) case CIRRUS_ID_CLGD5426: if (info->local & 0x200) - if (machines[machine].init == machine_at_vect486vl_init) - romfn = BIOS_GD5428_ISA_PATH; - else - romfn = NULL; + romfn = NULL; else { if (info->local & 0x100) romfn = BIOS_GD5426_DIAMOND_A1_ISA_PATH; @@ -4337,9 +4332,7 @@ gd54xx_init(const device_t *info) case CIRRUS_ID_CLGD5428: if (info->local & 0x200) { - if (machines[machine].init == machine_at_d824_init) - romfn = BIOS_GD5428_ISA_PATH; - else if (machines[machine].init == machine_at_acera1g_init) + if (machines[machine].init == machine_at_acera1g_init) romfn = BIOS_GD5428_ONBOARD_ACER_PATH; else romfn = NULL; From 4284f155747e944e902bc7be34ebfe6e7fab1617 Mon Sep 17 00:00:00 2001 From: Akamaki <97360908+akmed772@users.noreply.github.com> Date: Fri, 20 Feb 2026 09:03:45 +0900 Subject: [PATCH 07/25] Keyboard: Fix a bug in IBM 5576-001 emulation Fix a bug that the make code is sent when the 5576-001 emulation is enabled and the key is released. --- src/device/keyboard.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/device/keyboard.c b/src/device/keyboard.c index e77a5fc1d..ea8bcc1e7 100644 --- a/src/device/keyboard.c +++ b/src/device/keyboard.c @@ -216,22 +216,24 @@ key_process(uint16_t scan, int down) */ if (key5576mode) { int i = 0; - if (!down) { - /* Do and exit the 5576-001 emulation when a key is pressed other than trigger keys. */ - if (scan != 0x1d && scan != 0x2a && scan != 0x138) - { + if (down) { + while (scconv55_8a[i].sc != 0) { + if (scconv55_8a[i].sc == scan) { + while (scconv55_8a[i].mk[c] != 0) + keyboard_send(scconv55_8a[i].mk[c++]); + } + i++; + } + } + /* Do and exit the 5576-001 emulation when a key is pressed other than trigger keys. */ + if (scan != 0x1d && scan != 0x2a && scan != 0x138) { + if (!down) { key5576mode = 0; kbc_at_log("5576-001 key emulation disabled.\n"); } - } - while (scconv55_8a[i].sc != 0) - { - if (scconv55_8a[i].sc == scan) { - while (scconv55_8a[i].mk[c] != 0) - keyboard_send(scconv55_8a[i].mk[c++]); - return; - } - i++; + /* If the key is found in the table, the scancode has been sent. + Or else, do nothing. */ + return; } } From c1661b5a90121cb37952c29db6901c29c747fa8a Mon Sep 17 00:00:00 2001 From: Akamaki <97360908+akmed772@users.noreply.github.com> Date: Fri, 20 Feb 2026 09:06:33 +0900 Subject: [PATCH 08/25] IBM 5550: Fix the screen res in graphic mode --- src/machine/m_xt_ibm5550.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/machine/m_xt_ibm5550.c b/src/machine/m_xt_ibm5550.c index ff369dede..35edec8f3 100644 --- a/src/machine/m_xt_ibm5550.c +++ b/src/machine/m_xt_ibm5550.c @@ -305,7 +305,7 @@ The IBM 5550 has different IRQ assignments like the 6580 Displaywriter System. | 40000h | 128 KB Expansion RAM Card | | 60000h | 128 KB Expansion RAM Card | | 80000h | 128 KB Expansion RAM Card | -| A0000h | 256 KB Video RAM | +| A0000h | Video RAM (Font 16: 144 KB, Font 24: 256 KB) | | E0000h | 4 KB Code/Attribute Buffer | | E8000h | ? KB Hard Disk Control Local Memory (not implemented) | | F0000h | Kanji Font Card (not implemented) | @@ -917,13 +917,15 @@ epoch_render_color_4bpp(epoch_t *epoch) /* INT 10h video modes supported in DOS K3.44. Mode Type Colors Text Base Address PELs Render - 2 A/N 3 80 x 25 E0000h 1040 x 725 text - 8 A/N/K 3 80 x 25 E0000h 1040 x 725 text + 2 A/N 3 80 x 25 E0000h 1040 x 725* text + 8 A/N/K 3 80 x 25 E0000h 1040 x 725* text + 9 APA 2 80 x 25 A0000h 720 x 512 color_4bpp Ah APA 2 78 x 25 A0000h 1024 x 768 color_4bpp Bh APA 16 40 x 25 A0000h 360 x 512 n/a Ch APA 16 80 x 25 A0000h 720 x 512 n/a Dh APA 16 78 x 25 A0000h 1024 x 768 n/a - Eh A/N/K 16 80 x 25 E0000h 1040 x 725 n/a + Eh A/N/K 16 80 x 25 E0000h 1040 x 725* n/a + (* 720 x 525 in the Font 16 system.) */ static void epoch_recalctimings(epoch_t *epoch) @@ -978,8 +980,8 @@ epoch_recalctimings(epoch_t *epoch) epoch->hdisp *= 16; epoch->char_width = 16; } else { - epoch->hdisp *= 11; - epoch->char_width = 11; + epoch->hdisp *= 12; + epoch->char_width = 12; } /* PS/55 8-color */ epoch_log("Set videomode to PS/55 4 bpp graphics.\n"); From 45c300a566b43af67a0da631bfc435d580532a46 Mon Sep 17 00:00:00 2001 From: Akamaki <97360908+akmed772@users.noreply.github.com> Date: Fri, 20 Feb 2026 09:10:16 +0900 Subject: [PATCH 09/25] IBM 5550: Change method to store 9th bit into memory --- src/machine/m_xt_ibm5550.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/machine/m_xt_ibm5550.c b/src/machine/m_xt_ibm5550.c index 35edec8f3..f13ccd6be 100644 --- a/src/machine/m_xt_ibm5550.c +++ b/src/machine/m_xt_ibm5550.c @@ -1236,7 +1236,7 @@ epoch_vram_writew(uint32_t addr, uint16_t val, void *priv) // uint8_t convert[8] = {4, 6, 8, 8, 5, 7, 8, 8};/* only odd (0) */ // uint8_t convert[8] = {8, 4, 6, 8, 8, 5, 7, 8};/* none */ // epoch_log("%04X:%04X epoch_vww: %x, val %x DS %x SI %x ES %x DI %x %x\n", cs >> 4, cpu_state.pc, addr, val,DS,SI,ES,DI, epoch->crtc[LC_INTERLACE_AND_SKEW]); - epoch_log("%04X:%04X epoch_vww: %x, val %x cm %x\n", cs >> 4, cpu_state.pc, addr, val, epoch->crtmode); + // epoch_log("%04X:%04X epoch_vww: %x, val %x cm %x\n", cs >> 4, cpu_state.pc, addr, val, epoch->crtmode); cycles -= video_timing_write_w; addr -= 0xA0000; // addr &= 0xfffffffe; @@ -1291,21 +1291,21 @@ epoch_vram_writew(uint32_t addr, uint16_t val, void *priv) if (addr & 2) toaddr--; if (addr & 0x20000) - toaddr+=2; - toaddr &= 0xdffff; + toaddr += 2; + toaddr &= 0x1ffff; //addr >>= 1; epoch_vram_write(toaddr, val & 0xff, epoch); // epoch_log("%x %x ", toaddr, val); /* get 9th bit */ - bitnum = addr & 0x7; + bitnum = toaddr & 0x7; + toaddr >>= 3; + toaddr += 0x20000; /* real: C0000h */ val >>= 15; val <<= bitnum; - addr >>= 3; - addr += 0x20000; /* real: C0000h */ - val |= epoch_vram_read(addr, epoch) & ~(1 << bitnum);/* mask to update one bit */ - epoch_vram_write(addr, val, epoch); + val |= epoch_vram_read(toaddr, epoch) & ~(1 << bitnum);/* mask to update one bit */ + epoch_vram_write(toaddr, val, epoch); // epoch_log("%x %x\n", addr, val); } else {/* is graphic mode */ epoch_vram_write(addr, val & 0xff, epoch); @@ -1332,8 +1332,7 @@ epoch_vram_readw(uint32_t addr, void *priv) // uint8_t convert[8] = {0, 4, 8, 8, 8, 8, 8, 8};/* all but pos+-1 */ cycles -= video_timing_read_w; addr -= 0xA0000; - epoch_log("%04X:%04X epoch_vrw: %x cm %x\n", - cs >> 4, cpu_state.pc, addr, epoch->crtmode); + // epoch_log("%04X:%04X epoch_vrw: %x cm %x\n", cs >> 4, cpu_state.pc, addr, epoch->crtmode); // addr &= 0xfffffffe; //read 0->0,1 2->2,3 4->4,5 6->6,7 //read 0->0,2 2->1,3 4->4,6 6->5,7 @@ -1387,14 +1386,14 @@ epoch_vram_readw(uint32_t addr, void *priv) if (addr & 2) toaddr--; if (addr & 0x20000) - toaddr+=2; - toaddr &= 0xdffff; + toaddr += 2; + toaddr &= 0x1ffff; ret = epoch_vram_read(toaddr, epoch); /* get 9th bit */ - bitnum = addr & 0x7; - addr >>= 3; - addr += 0x20000; /* real: C0000h */ - ret |= (epoch_vram_read(addr, epoch) << (8 + 7 - bitnum)) & 0x8000; + bitnum = toaddr & 0x7; + toaddr >>= 3; + toaddr += 0x20000; /* real: C0000h */ + ret |= (epoch_vram_read(toaddr, epoch) << (8 + 7 - bitnum)) & 0x8000; return ret; // return epoch_vram_read(addr, epoch) | (epoch_vram_read(addr + 1, epoch) << 8); } else {/* is graphic mode */ @@ -1806,7 +1805,7 @@ kbd_write(uint16_t port, uint8_t val, void *priv) timer_process(); speaker_update(); - + // if(!speaker_enable && (val & 2)) epoch_log("Buz!\n"); speaker_gated = val & 2; speaker_enable = val & 2; From 25d208cb8ef8d7b1443d05a7ed3c3213c27811be Mon Sep 17 00:00:00 2001 From: skiretic Date: Thu, 19 Feb 2026 20:03:03 -0500 Subject: [PATCH 10/25] Fix macOS toolbar label disappearing on mouse capture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BSD swprintf fails (returns -1) when the format string or a %ls argument contains non-ASCII wide characters under the C locale. On macOS, QKeySequence::NativeText produces Unicode key symbols (⌘ U+2318, ⌫ U+232B) in the release-mouse string, causing swprintf to zero out mouse_msg[1]. This made the title bar label collapse to empty during mouse capture. Avoid swprintf for non-ASCII content on macOS: store message suffixes with wcsncpy and build the title string via wcsncat at the update site. --- src/86box.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/86box.c b/src/86box.c index cf992df4c..0f72ddef1 100644 --- a/src/86box.c +++ b/src/86box.c @@ -1870,6 +1870,27 @@ update_mouse_msg(void) (mouse_get_buttons() > 2) ? plat_get_string(STRING_MOUSE_RELEASE) : plat_get_string(STRING_MOUSE_RELEASE_MMB)); swprintf(mouse_msg[2], sizeof_w(mouse_msg[2]), L"%ls v%ls - %%i%%%% - %ls - %ls/%ls", EMU_NAME_W, EMU_VERSION_FULL_W, wmachine, wcpufamily, wcpu); +#else +#ifdef __APPLE__ + /* + * On macOS, BSD swprintf fails (returns -1) when the format string + * or a %ls argument contains non-ASCII wide characters (e.g. the + * native key symbols ⌘ U+2318, ⌫ U+232B) and the C locale is + * active. Store just the message suffixes here; the title update + * path in pc_render_monitor_dispatch() builds the full string + * without swprintf. + */ + wcsncpy(mouse_msg[0], plat_get_string(STRING_MOUSE_CAPTURE), sizeof_w(mouse_msg[0]) - 1); + mouse_msg[0][sizeof_w(mouse_msg[0]) - 1] = L'\0'; + + { + wchar_t *rel = (mouse_get_buttons() > 2) ? plat_get_string(STRING_MOUSE_RELEASE) + : plat_get_string(STRING_MOUSE_RELEASE_MMB); + wcsncpy(mouse_msg[1], rel, sizeof_w(mouse_msg[1]) - 1); + mouse_msg[1][sizeof_w(mouse_msg[1]) - 1] = L'\0'; + } + + mouse_msg[2][0] = L'\0'; #else swprintf(mouse_msg[0], sizeof_w(mouse_msg[0]), L"%%i%%%% - %ls", plat_get_string(STRING_MOUSE_CAPTURE)); @@ -1877,6 +1898,7 @@ update_mouse_msg(void) (mouse_get_buttons() > 2) ? plat_get_string(STRING_MOUSE_RELEASE) : plat_get_string(STRING_MOUSE_RELEASE_MMB)); wcsncpy(mouse_msg[2], L"%i%%", sizeof_w(mouse_msg[2])); #endif +#endif } void @@ -2009,11 +2031,20 @@ pc_run(void) else fps = ((fps + 20) / 50) * 50; #endif - swprintf(temp, sizeof_w(temp), mouse_msg[mouse_msg_idx], fps / (force_10ms ? 1 : 10)); #ifdef __APPLE__ + /* + * mouse_msg[] stores suffixes only on macOS (see update_mouse_msg). + * Build the title without passing non-ASCII chars through swprintf. + */ + swprintf(temp, sizeof_w(temp), L"%i%%", fps / (force_10ms ? 1 : 10)); + if (mouse_msg[mouse_msg_idx][0]) { + wcsncat(temp, L" - ", sizeof_w(temp) - wcslen(temp) - 1); + wcsncat(temp, mouse_msg[mouse_msg_idx], sizeof_w(temp) - wcslen(temp) - 1); + } /* Needed due to modifying the UI on the non-main thread is a big no-no. */ dispatch_async_f(dispatch_get_main_queue(), wcsdup((const wchar_t *) temp), _ui_window_title); #else + swprintf(temp, sizeof_w(temp), mouse_msg[mouse_msg_idx], fps / (force_10ms ? 1 : 10)); ui_window_title(temp); #endif title_update = 0; From 617be3b135e6a89a4f6c973aba14f16b745d1a03 Mon Sep 17 00:00:00 2001 From: Akamaki <97360908+akmed772@users.noreply.github.com> Date: Fri, 20 Feb 2026 10:51:00 +0900 Subject: [PATCH 11/25] IBM 5550: Fix a bug in Font 16 drawing --- src/machine/m_xt_ibm5550.c | 78 +++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 34 deletions(-) diff --git a/src/machine/m_xt_ibm5550.c b/src/machine/m_xt_ibm5550.c index f13ccd6be..2edb547a4 100644 --- a/src/machine/m_xt_ibm5550.c +++ b/src/machine/m_xt_ibm5550.c @@ -646,6 +646,18 @@ epoch_inw(uint16_t addr, void *priv) return temp; } +static uint32_t +getaddr_9bitword(int32_t addr) +{ + int32_t bit9addr = addr; + if (bit9addr & 2) + bit9addr--; + if (bit9addr & 0x20000) + bit9addr += 2; + bit9addr &= 0x1ffff; + return bit9addr; +} + /* Get font pattern in a line from video memory */ static uint32_t getfont_ps55dbcs(int32_t code, int32_t line, void *priv) @@ -667,19 +679,22 @@ getfont_ps55dbcs(int32_t code, int32_t line, void *priv) code++; font |= epoch->vram[code]; } else { /* Font 16 (2 x 9 x 21) */ - int bitnum = code & 0x7; + int32_t bit9addr = getaddr_9bitword(code); + int bitnum = bit9addr & 7; + bit9addr >>= 3; + bit9addr += 0x20000; /* real: C0000h */ font = epoch->vram[code]; font <<= 8; - font |= (epoch->vram[(code >> 3) + 0x20000 ] << (7 - bitnum)) & 0xff; /* get 9th bit */ - // font &= 0xffffff80; + font |= (epoch->vram[bit9addr] << (7 - bitnum)) & 0x80; /* get 9th bit */ + // font &= 0xff80; // font |= epoch->vram[code + line * 4 + 1]; font <<= 8; code++; font |= epoch->vram[code]; font <<= 8; bitnum = code & 0x7; - font |= (epoch->vram[(code >> 3) + 0x20000 ] << (7 - bitnum)) & 0xff; /* get 9th bit */ - font &= 0xff80ff80; + font |= (epoch->vram[bit9addr] << (7 - bitnum)) & 0x80; /* get 9th bit */ + // font &= 0xff80ff80; // font |= epoch->vram[code + line * 4 + 3]; } } else @@ -801,15 +816,17 @@ epoch_render_text(epoch_t *epoch) font <<= 8; font |= epoch->vram[fontbase + chr * 0x80 + epoch->scanline * 4 + 3]; } else { - int bitnum; - fontbase = (fontbase & 0x1ffff) + chr * 0x80 + epoch->scanline * 4; - bitnum = fontbase & 0x7; + uint32_t bitnum, bit9addr; + fontbase += chr * 0x80 + epoch->scanline * 4; + bit9addr = getaddr_9bitword(fontbase); + bitnum = bit9addr & 7; + bit9addr >>= 3; + bit9addr += 0x20000; /* real: C0000h */ + + fontbase &= 0x1ffff; font = epoch->vram[fontbase + 2]; /* w9xh21 font */ font <<= 8; - // font |= epoch->vram[fontbase + chr * 0x80 + epoch->scanline * 4 + 3]; - fontbase >>= 3; - fontbase += 0x20000; /* real: C0000h */ - font |= epoch->vram[fontbase] << (8 + 7 - bitnum); + font |= (epoch->vram[bit9addr] << (7 - bitnum)) & 0x80; // if(chr!=0x20) epoch_log("faddr: %x, scline: %x, chr: %x, font: %x ", fontbase + chr * 0x80 + epoch->scanline * 4, epoch->scanline, chr, font); } // if(chr!=0x20) epoch_log("memaddr: %x, scanline: %x, chr: %x, font: %x ", epoch->memaddr, epoch->scanline, chr, font); @@ -1287,31 +1304,27 @@ epoch_vram_writew(uint32_t addr, uint16_t val, void *priv) /* rw one word with 9 bits */ /* virtual: 20000h (0010b, 0011b) -> real: 00001h (0000b, 0001b) */ - toaddr = addr; - if (addr & 2) - toaddr--; - if (addr & 0x20000) - toaddr += 2; - toaddr &= 0x1ffff; - - //addr >>= 1; + toaddr = getaddr_9bitword(addr); + bitnum = toaddr & 7; epoch_vram_write(toaddr, val & 0xff, epoch); - // epoch_log("%x %x ", toaddr, val); + // epoch_log("%x %x\n", toaddr, val); /* get 9th bit */ - bitnum = toaddr & 0x7; toaddr >>= 3; toaddr += 0x20000; /* real: C0000h */ val >>= 15; + // epoch_log("%x %x ", toaddr, val); val <<= bitnum; - val |= epoch_vram_read(toaddr, epoch) & ~(1 << bitnum);/* mask to update one bit */ + // epoch_log("%x ", val); + val |= (epoch_vram_read(toaddr, epoch) & (~(1 << bitnum)));/* mask to update one bit */ epoch_vram_write(toaddr, val, epoch); + // epoch_log("%x %x\n", toaddr, val); // epoch_log("%x %x\n", addr, val); } else {/* is graphic mode */ epoch_vram_write(addr, val & 0xff, epoch); epoch_vram_write(addr + 1, val >> 8, epoch); } - // epoch_log("%x %x\n", addr, addr + 1); + // epoch_log("%x %x\n", addr, val); } // static uint8_t @@ -1382,15 +1395,10 @@ epoch_vram_readw(uint32_t addr, void *priv) /* rw one word with 9 bits */ /* virtual: 20000h (0010b, 0011b) -> real: 00001h (0000b, 0001b) */ - toaddr = addr; /* 1101 */ - if (addr & 2) - toaddr--; - if (addr & 0x20000) - toaddr += 2; - toaddr &= 0x1ffff; + toaddr = getaddr_9bitword(addr); + bitnum = toaddr & 7; ret = epoch_vram_read(toaddr, epoch); /* get 9th bit */ - bitnum = toaddr & 0x7; toaddr >>= 3; toaddr += 0x20000; /* real: C0000h */ ret |= (epoch_vram_read(toaddr, epoch) << (8 + 7 - bitnum)) & 0x8000; @@ -2148,11 +2156,11 @@ epoch_nvr_reset(nvr_t *nvr) } static void -epoch_nvr_init(epoch_t *epoch, int size) +epoch_nvr_init(epoch_t *epoch) { nvr_t* nvr = &epoch->nvr; /* This is machine specific. */ - nvr->size = size; + nvr->size = 17; nvr->irq = -1; /* Set up any local handlers here. */ nvr->reset = epoch_nvr_reset; @@ -2315,6 +2323,8 @@ epoch_init(UNUSED(const device_t *info)) changeframecount = 3; epoch->vram = calloc(1, 256* 1024); + // for(int i=0;i<256*1024;i++) /* for debug */ + // epoch->vram[i] = 0xff; epoch->cram = calloc(1, 4 * 1024); // epoch->fontcard.rom = calloc(1, EPOCH_FONTROM_SIZE); // epoch_video_load_font("roms/machines/ibm5550/GEN1FONT.BIN", epoch); @@ -2347,7 +2357,7 @@ epoch_init(UNUSED(const device_t *info)) timer_add(&epoch->timer, epoch_poll, epoch, 1); - epoch_nvr_init(epoch, 17); + epoch_nvr_init(epoch); epoch->font24 = device_get_config_int("model"); From 87500155883a3c3c6e9ada358371a65faefdbf95 Mon Sep 17 00:00:00 2001 From: WNT50 <173389620+WNT50@users.noreply.github.com> Date: Fri, 20 Feb 2026 13:16:08 +0800 Subject: [PATCH 12/25] IBM PS/1 XTA controller fixes --- src/disk/hdc_xta_ps1.c | 43 ++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/disk/hdc_xta_ps1.c b/src/disk/hdc_xta_ps1.c index 38f49fa67..c37bf3186 100644 --- a/src/disk/hdc_xta_ps1.c +++ b/src/disk/hdc_xta_ps1.c @@ -381,6 +381,7 @@ typedef struct hdc_t { pc_timer_t timer; int8_t state; /* controller state */ int8_t reset; /* reset state counter */ + int8_t ready; /* ready state counter */ /* Data transfer. */ int16_t buf_idx; /* buffer index and pointer */ @@ -723,6 +724,15 @@ hdc_callback(void *priv) uint8_t cmd = ccb->cmd & 0x0f; #endif + /* If we are returning from a RESET, handle this first. */ + if (dev->reset) { + ps1_hdc_log("XTA reset.\n"); + dev->status &= ~ASR_BUSY; + dev->reset = 0; + do_finish(dev); + return; + } + /* Clear the SSB error bits. */ dev->ssb.track_0 = 0; dev->ssb.cylinder_err = 0; @@ -754,6 +764,12 @@ hdc_callback(void *priv) return; } + if (!(dev->ready | no_data)) { + /* Delay a bit, transfer not ready. */ + timer_advance_u64(&dev->timer, HDC_TIME); + return; + } + switch (dev->state) { case STATE_IDLE: /* Seek to cylinder if requested. */ @@ -944,6 +960,12 @@ do_send: return; } + if (!(dev->ready | no_data)) { + /* Delay a bit, transfer not ready. */ + timer_advance_u64(&dev->timer, HDC_TIME); + return; + } + switch (dev->state) { case STATE_IDLE: /* Seek to cylinder if requested. */ @@ -1228,24 +1250,21 @@ hdc_write(uint16_t port, uint8_t val, void *priv) if (val & ACR_INT_EN) set_intr(dev, 0); /* clear IRQ */ - if (dev->reset != 0) { - if (++dev->reset == 3) { - dev->reset = 0; - - set_intr(dev, 1); - } - break; + if (val & ACR_RESET) { + dev->reset = 1; + dev->status |= ASR_BUSY; + /* Schedule command execution. */ + timer_set_delay_u64(&dev->timer, HDC_TIME); } - if (val & ACR_RESET) - dev->reset = 1; break; case 4: /* ATTN */ dev->status &= ~ASR_INT_REQ; - if (val & ATT_DATA) { - /* Dunno. Start PIO/DMA now? */ - } + if (val & ATT_DATA) + dev->ready = 1; + else + dev->ready = 0; if (val & ATT_SSB) { if (dev->attn & ATT_CCB) { From 99a0a1ef04b4208251100a90bce4f5753e9e54d1 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Fri, 20 Feb 2026 15:03:51 +0600 Subject: [PATCH 13/25] MPU-401: Don't clear queues on reset Backported from DOSBox SVN revision r4492 --- src/sound/snd_mpu401.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sound/snd_mpu401.c b/src/sound/snd_mpu401.c index 87c191cec..b1ff6ab8b 100644 --- a/src/sound/snd_mpu401.c +++ b/src/sound/snd_mpu401.c @@ -316,7 +316,6 @@ MPU401_Reset(mpu_t *mpu) mpu->ch_toref[i] = 4; /* Dummy reftable. */ } - MPU401_ClrQueue(mpu); mpu->state.data_onoff = -1; mpu->state.req_mask = 0; From c03a4e91c945fe1745d958e7d44e6074c276644a Mon Sep 17 00:00:00 2001 From: Akamaki <97360908+akmed772@users.noreply.github.com> Date: Fri, 20 Feb 2026 23:36:08 +0900 Subject: [PATCH 14/25] IBM 5550: Add cursor blinking Added cursor blinking controlled by CRTC (HD46505). --- src/machine/m_xt_ibm5550.c | 20 +++++++++++++++++--- src/video/vid_ps55da2.c | 2 +- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/machine/m_xt_ibm5550.c b/src/machine/m_xt_ibm5550.c index 2edb547a4..ccc55578b 100644 --- a/src/machine/m_xt_ibm5550.c +++ b/src/machine/m_xt_ibm5550.c @@ -862,7 +862,7 @@ epoch_render_text(epoch_t *epoch) } /* Drawing text cursor */ drawcursor = ((epoch->memaddr == epoch->cursoraddr) && epoch->cursorvisible && epoch->cursoron); - if (drawcursor && epoch->scanline >= epoch->crtc[LC_CURSOR_ROW_START] && epoch->scanline <= epoch->crtc[LC_CURSOR_ROW_END]) { + if (drawcursor) { // int cursorwidth = (epoch->crtc[LC_COMPATIBILITY] & 0x20 ? 26 : 13); int cursorwidth = epoch->char_width; int cursorcolor = 2; /* Choose color 2 if mode 8 */ @@ -1140,11 +1140,25 @@ epoch_poll(void *priv) // if (epoch->attrc[LV_CURSOR_CONTROL] & 0x01) /* cursor blinking */ // { // epoch->cursoron = (epoch->blink | 1) & epoch->blinkconf; - epoch->cursoron = 1; + // epoch->cursoron = 1; // } else { // epoch->cursoron = 0; // } - if (!(epoch->blink & (0x10 - 1))) /* force redrawing for cursor and blink attribute */ + switch (epoch->crtc[LC_CURSOR_ROW_START] & 0x60) { + case 0x20: + epoch->cursoron = 0; + break; + case 0x60: + epoch->cursoron = epoch->blink & 0x10; + break; + case 0x40: + epoch->cursoron = epoch->blink & 0x08; + break; + default: + epoch->cursoron = 1; + break; + } + if (!(epoch->blink & (0x08 - 2))) /* force redrawing for cursor and blink attribute */ epoch->fullchange = changeframecount; } epoch->blink++; diff --git a/src/video/vid_ps55da2.c b/src/video/vid_ps55da2.c index a1d7fe25f..9dffcdf97 100644 --- a/src/video/vid_ps55da2.c +++ b/src/video/vid_ps55da2.c @@ -2055,7 +2055,7 @@ da2_render_text(da2_t *da2) } /* Drawing text cursor */ drawcursor = ((da2->memaddr == da2->cursoraddr) && da2->cursorvisible && da2->cursoron); - if (drawcursor && da2->scanline >= da2->crtc[LC_CURSOR_ROW_START] && da2->scanline <= da2->crtc[LC_CURSOR_ROW_END]) { + if (drawcursor) { int cursorwidth = (da2->crtc[LC_COMPATIBILITY] & 0x20 ? 26 : 13); int cursorcolor = (colormode) ? IRGBtoBGRI(da2->attrc[LV_CURSOR_COLOR]) : 2; /* Choose color 2 if mode 8 */ fg = (colormode) ? getPS55ForeColor(attr, da2) : ((attr & 0x08) ? 3 : 2); From 894e4bb0cf08456d3221b47b67744dfce04c7ed9 Mon Sep 17 00:00:00 2001 From: Akamaki <97360908+akmed772@users.noreply.github.com> Date: Sat, 21 Feb 2026 02:25:07 +0900 Subject: [PATCH 15/25] IBM 5550: Set pixel clock for Font 16 --- src/machine/m_xt_ibm5550.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/machine/m_xt_ibm5550.c b/src/machine/m_xt_ibm5550.c index ccc55578b..aefbf0193 100644 --- a/src/machine/m_xt_ibm5550.c +++ b/src/machine/m_xt_ibm5550.c @@ -73,7 +73,8 @@ #define EPOCH_MASK_CRAM 0xfff /* 0xFFF */ #define EPOCH_MASK_VRAM 0x3ffff /* 0xFFFFF */ // #define EPOCH_MASK_VRAMPLANE 0x1ffff /* 0x1FFFF */ -#define EPOCH_PIXELCLOCK 40000000.0 /* 40 MHz interlaced */ +#define EPOCH_PIXELCLOCK24 40000000.0 /* 40 MHz interlaced */ +#define EPOCH_PIXELCLOCK16 20000000.0 /* 20 MHz interlaced (not confirmed) */ #define EPOCH_CONFIG_MONO16 0 /* Model 5551-Axx (Font 16, monochrome) */ #define EPOCH_CONFIG_MONO24 1 /* Model 5551-Bxx (Font 24, monochrome) */ @@ -241,6 +242,7 @@ typedef struct epoch_t { int scrollcache; int char_width; int font24; + double pixelclock; int firstline, lastline; int firstline_draw, lastline_draw; @@ -1088,7 +1090,7 @@ epoch_poll(void *priv) video_wait_for_buffer(); } - if((epoch->displine ^ !epoch->oddeven) & 1) + if ((epoch->displine ^ !epoch->oddeven) & 1) epoch->render(epoch); if (epoch->lastline < epoch->displine) @@ -1199,8 +1201,8 @@ epoch_poll(void *priv) epoch->vslines = 0; - epoch->memaddr - = epoch->memaddr_backup = epoch->memaddr_latch << 1; + epoch->memaddr + = epoch->memaddr_backup = epoch->memaddr_latch << 1; epoch->cursoraddr = ((epoch->crtc[LC_CURSOR_LOC_HIGH] << 8) | epoch->crtc[LC_CURSOR_LOC_LOWJ]) + epoch->ca_adj; epoch->cursoraddr <<= 1; @@ -1431,7 +1433,7 @@ epoch_cram_write(uint32_t addr, uint8_t val, void *priv) // if ((addr & ~0xfff) != 0xE0000) return; addr &= EPOCH_MASK_CRAM; epoch->cram[addr] = val; - epoch->fullchange = changeframecount;; + epoch->fullchange = changeframecount; // epoch_log("cw %04X:%04X %04X %02X\n", cs >> 4, cpu_state.pc, addr, val); } static void @@ -2328,6 +2330,8 @@ static void * epoch_init(UNUSED(const device_t *info)) { epoch_t *epoch = calloc(1, sizeof(epoch_t)); + epoch->font24 = device_get_config_int("model"); + video_inform(VIDEO_FLAG_TYPE_NONE, &timing_epoch_vid); video_update_timing(); @@ -2335,6 +2339,10 @@ epoch_init(UNUSED(const device_t *info)) epoch->dispofftime = 1000ull << 32; // epoch->changedvram = calloc(1, (EPOCH_MASK_VRAMPLANE + 1) >> 9); /* XX000h */ changeframecount = 3; + if (epoch->font24) + epoch->pixelclock = EPOCH_PIXELCLOCK24; + else + epoch->pixelclock = EPOCH_PIXELCLOCK16; epoch->vram = calloc(1, 256* 1024); // for(int i=0;i<256*1024;i++) /* for debug */ @@ -2343,7 +2351,7 @@ epoch_init(UNUSED(const device_t *info)) // epoch->fontcard.rom = calloc(1, EPOCH_FONTROM_SIZE); // epoch_video_load_font("roms/machines/ibm5550/GEN1FONT.BIN", epoch); - epoch->epochconst = (uint64_t) ((cpuclock / EPOCH_PIXELCLOCK) * (double) (1ull << 32)); + epoch->epochconst = (uint64_t) ((cpuclock / epoch->pixelclock) * (double) (1ull << 32)); mem_mapping_add(&epoch->cmap, 0xE0000, 0x1000, epoch_cram_readb, epoch_cram_readw, NULL, epoch_cram_writeb, epoch_cram_writew, NULL, NULL, MEM_MAPPING_EXTERNAL, epoch); @@ -2373,8 +2381,6 @@ epoch_init(UNUSED(const device_t *info)) epoch_nvr_init(epoch); - epoch->font24 = device_get_config_int("model"); - return epoch; } @@ -2441,7 +2447,7 @@ static void epoch_speed_changed(void *priv) { epoch_t *epoch = (epoch_t *) priv; - epoch->epochconst = (uint64_t) ((cpuclock / EPOCH_PIXELCLOCK) * (double) (1ull << 32)); + epoch->epochconst = (uint64_t) ((cpuclock / epoch->pixelclock) * (double) (1ull << 32)); epoch_recalctimings(epoch); } From 3a199fb8cb83d4540d985e677d0431208f398ec8 Mon Sep 17 00:00:00 2001 From: Akamaki <97360908+akmed772@users.noreply.github.com> Date: Sat, 21 Feb 2026 02:26:54 +0900 Subject: [PATCH 16/25] PS55DA2: Change to interlaced screen update --- src/video/vid_ps55da2.c | 62 ++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/src/video/vid_ps55da2.c b/src/video/vid_ps55da2.c index 9dffcdf97..0bd5035b0 100644 --- a/src/video/vid_ps55da2.c +++ b/src/video/vid_ps55da2.c @@ -64,7 +64,7 @@ #define DA2_MASK_GAIJIRAM 0x3ffff /* 0x3FFFF */ #define DA2_MASK_VRAM 0xfffff /* 0xFFFFF */ #define DA2_MASK_VRAMPLANE 0x1ffff /* 0x1FFFF */ -#define DA2_PIXELCLOCK 29000000.0 /* 58 MHz interlaced */ +#define DA2_PIXELCLOCK 58000000.0 /* 58 MHz interlaced */ #define DA2_BLT_MEMSIZE 0x10 #define DA2_BLT_REGSIZE 0x40 #define DA2_DEBUG_BLTLOG_SIZE (DA2_BLT_REGSIZE + 1) @@ -368,6 +368,7 @@ typedef struct da2_t { int firstline, lastline; int firstline_draw, lastline_draw; int displine; + int oddeven; /* Attribute Buffer E0000-E0FFFh (4 KB) */ uint8_t *cram; @@ -441,6 +442,9 @@ typedef struct da2_t { int old_pos2; } da2_t; +static video_timings_t timing_da2_mca = +{ .type = VIDEO_MCA, .write_b = 4, .write_w = 4, .write_l = 10, .read_b = 4, .read_w = 4, .read_l = 10 }; + static void da2_recalctimings(da2_t *da2); static void da2_mmio_gc_writeW(uint32_t addr, uint16_t val, void *p); static void da2_bitblt_exec(void *p); @@ -515,7 +519,7 @@ da2_WritePlaneDataWithBitmask(uint32_t destaddr, const uint16_t mask, pixel32 *s uint32_t writepx[8]; destaddr &= 0xfffffffe; /* align to word address to work bit shift correctly */ // da2_log("DA2_WPDWB addr %x mask %x rop %x shift %d\n", destaddr, mask, da2->bitblt.raster_op, da2->bitblt.bitshift_destr); - da2->changedvram[(DA2_MASK_VRAMPLANE & destaddr) >> 9] = changeframecount; + da2->changedvram[(DA2_MASK_VRAMPLANE & destaddr) >> 9] = 3; destaddr <<= 3; /* read destination data with big endian order */ for (uint8_t i = 0; i < 8; i++) @@ -1245,7 +1249,7 @@ da2_out(uint16_t addr, uint16_t val, void *priv) case 0x3C9: /* Data */ // da2_iolog("DA2 Out addr %03X idx %d:%d val %02X %04X:%04X esdi %04X:%04X\n", addr, da2->dac_write, da2->dac_pos, val, cs >> 4, cpu_state.pc, ES, DI); da2->dac_status = 0; - da2->fullchange = changeframecount; + da2->fullchange = 3; switch (da2->dac_pos) { case 0: da2->dac_r = val; @@ -1283,7 +1287,7 @@ da2_out(uint16_t addr, uint16_t val, void *priv) if (da2->ioctladdr == LS_RESET && val & 0x01) /* Reset register */ da2_reset_ioctl(da2); else if (da2->ioctladdr == LS_MODE && ((oldval ^ val) & 0x03)) { /* Mode register */ - da2->fullchange = changeframecount; + da2->fullchange = 3; da2_recalctimings(da2); da2_updatevidselector(da2); } @@ -1372,7 +1376,7 @@ da2_out(uint16_t addr, uint16_t val, void *priv) case LC_START_V_DISPLAY_ENAB: case LC_VIEWPORT_SELECT: case LC_VIEWPORT_PRIORITY: - da2->fullchange = changeframecount; + da2->fullchange = 3; da2_recalctimings(da2); break; default: @@ -1391,13 +1395,13 @@ da2_out(uint16_t addr, uint16_t val, void *priv) // da2_iolog("set attraddr: %X\n", da2->attraddr); } else { if ((da2->attraddr == LV_PANNING) && (da2->attrc[LV_PANNING] != val)) - da2->fullchange = changeframecount; + da2->fullchange = 3; if (da2->attrc[da2->attraddr & 0x3f] != val) da2_iolog("attr changed %x: %x -> %x\n", da2->attraddr & 0x3f, da2->attrc[da2->attraddr & 0x3f], val); da2->attrc[da2->attraddr & 0x3f] = val; // da2_iolog("set attrc %x: %x\n", da2->attraddr & 31, val); if (da2->attraddr < 16) - da2->fullchange = changeframecount; + da2->fullchange = 3; if (da2->attraddr == LV_MODE_CONTROL || da2->attraddr < 0x10) { for (uint8_t c = 0; c < 16; c++) { // if (da2->attrc[LV_MODE_CONTROL] & 0x80) da2->egapal[c] = (da2->attrc[c] & 0xf) | ((da2->attrc[0x14] & 0xf) << 4); @@ -1411,7 +1415,7 @@ da2_out(uint16_t addr, uint16_t val, void *priv) switch (da2->attraddr) { case LV_COLOR_PLANE_ENAB: if ((val & 0xff) != da2->plane_mask) - da2->fullchange = changeframecount; + da2->fullchange = 3; da2->plane_mask = val & 0xff; break; case LV_CURSOR_CONTROL: @@ -2743,7 +2747,7 @@ da2_mmio_write(uint32_t addr, uint8_t val, void *priv) //} #endif cycles -= video_timing_write_b; - da2->changedvram[addr >> 9] = changeframecount;/* 0x1FFFF -> 0x1F */ + da2->changedvram[addr >> 9] = 3;/* 0x1FFFF -> 0x1F */ addr <<= 3; for (uint8_t i = 0; i < 8; i++) @@ -2804,7 +2808,7 @@ da2_mmio_write(uint32_t addr, uint8_t val, void *priv) } else { /* mode 3h text */ cycles -= video_timing_write_b; da2_vram_w(addr, val, da2); - da2->fullchange = 2; + da2->fullchange = 3; } } static uint16_t @@ -2843,7 +2847,7 @@ da2_mmio_gc_writeW(uint32_t addr, uint16_t val, void *priv) // da2_log("da2_gcW m%d a%x d%x\n", da2->writemode, addr, val); // da2_log("da2_gcW %05X %02X %04X:%04X esdi %04X:%04X dssi %04X:%04X\n", addr, val, cs >> 4, cpu_state.pc, ES, DI, DS, SI); - da2->changedvram[addr >> 9] = changeframecount; + da2->changedvram[addr >> 9] = 3; addr <<= 3; for (uint8_t i = 0; i < 8; i++) @@ -2942,7 +2946,7 @@ da2_code_write(uint32_t addr, uint8_t val, void *priv) // if ((addr & ~0xfff) != 0xE0000) return; addr &= DA2_MASK_CRAM; da2->cram[addr] = val; - da2->fullchange = 2; + da2->fullchange = 3; } static void da2_code_writeb(uint32_t addr, uint8_t val, void *priv) @@ -2987,7 +2991,7 @@ da2_code_readw(uint32_t addr, void *priv) } static void -da2_doblit(int y1, int y2, int wx, int wy, da2_t *da2) +da2_doblit(int wx, int wy, da2_t *da2) { if (wx != xsize || wy != ysize) { xsize = wx; @@ -3026,14 +3030,14 @@ da2_poll(void *priv) video_wait_for_buffer(); } - if (!da2->override) + if (!da2->override && ((da2->displine ^ !da2->oddeven) & 1)) da2->render(da2); if (da2->lastline < da2->displine) da2->lastline = da2->displine; } - - // da2_log("%03i %06X %06X\n", da2->displine, da2->memaddr,da2->vram_display_mask); + // if(da2->fullchange) + // pclog("%03i %05X %d %d\n", da2->displine, da2->memaddr, ((da2->displine ^ !da2->oddeven) & 1), da2->fullchange); da2->displine++; if ((da2->cgastat & 8) && ((da2->displine & 0xf) == (da2->crtc[LC_VERTICAL_SYNC_END] & 0xf)) && da2->vslines) { // da2_log("Vsync off at line %i\n",displine); @@ -3085,14 +3089,13 @@ da2_poll(void *priv) // if (da2->crtc[10] & 0x20) da2->cursoron = 0; // else da2->cursoron = da2->blink & 16; if (da2->ioctl[LS_MODE] & 1) { /* in text mode */ - if (da2->attrc[LV_CURSOR_CONTROL] & 0x01) /* cursor blinking */ - { + if (da2->attrc[LV_CURSOR_CONTROL] & 0x01) {/* cursor blinking */ da2->cursoron = (da2->blink | 1) & da2->blinkconf; } else { da2->cursoron = 0; } - if (!(da2->blink & (0x10 - 1))) /* force redrawing for cursor and blink attribute */ - da2->fullchange = 2; + if (!(da2->blink & (0x08 - 1))) /* force redrawing for cursor and blink attribute */ + da2->fullchange = 3; } da2->blink++; @@ -3103,6 +3106,7 @@ da2_poll(void *priv) // memset(changedvram,0,2048); del if (da2->fullchange) { da2->fullchange--; + // pclog("fc %d %d\n",da2->fullchange,da2->oddeven); } } if (da2->vc == da2->vsyncstart) { @@ -3112,10 +3116,15 @@ da2_poll(void *priv) da2->cgastat |= 8; x = da2->hdisp; + if (!da2->oddeven) + da2->lastline++; + if (da2->oddeven) + da2->firstline--; + wx = x; wy = da2->lastline - da2->firstline; - da2_doblit(da2->firstline_draw, da2->lastline_draw + 1, wx, wy, da2); + da2_doblit(wx, wy, da2); da2->firstline = 2000; da2->lastline = 0; @@ -3123,7 +3132,8 @@ da2_poll(void *priv) da2->firstline_draw = 2000; da2->lastline_draw = 0; - changeframecount = 2; + da2->oddeven ^= 1; + da2->vslines = 0; da2->memaddr @@ -3137,7 +3147,7 @@ da2_poll(void *priv) // da2_log("VC vtotal\n"); // printf("Frame over at line %i %i %i %i\n",displine,vc,da2_vsyncstart,da2_dispend); da2->vc = 0; - da2->scanline = da2->crtc[LC_PRESET_ROW_SCANJ] & 0x1f; + da2->scanline = da2->crtc[LC_PRESET_ROW_SCANJ] & 0x1f; da2->dispon = 1; da2->displine = 0; da2->scrollcache = da2->attrc[LV_PANNING] & 7; @@ -3229,7 +3239,8 @@ da2_reset(void *priv) da2->attrc[LV_CURSOR_COLOR] = 0x0f; /* cursor color */ da2->crtc[LC_HORIZONTAL_TOTAL] = 63; /* Horizontal Total */ da2->crtc[LC_VERTICAL_TOTALJ] = 255; /* Vertical Total (These two must be set before the timer starts.) */ - da2->memaddr_latch = 0; + da2->memaddr_latch = 0; + da2->oddeven = 0; da2->attrc[LV_CURSOR_CONTROL] = 0x13; /* cursor options */ da2->attr_palette_enable = 0; /* disable attribute generator */ @@ -3278,6 +3289,7 @@ da2_init(UNUSED(const device_t *info)) mca_add(da2_mca_read, da2_mca_write, da2_mca_feedb, da2_mca_reset, da2); da2->da2const = (uint64_t) ((cpuclock / DA2_PIXELCLOCK) * (double) (1ull << 32)); + video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_da2_mca); memset(da2->bitblt.payload, 0x00, DA2_BLT_MEMSIZE); memset(da2->bitblt.reg, 0xfe, DA2_BLT_REGSIZE * sizeof(uint32_t)); /* clear memory */ #ifdef ENABLE_DA2_DEBUGBLT @@ -3432,7 +3444,7 @@ static void da2_force_redraw(void *priv) { da2_t *da2 = (da2_t *) priv; - da2->fullchange = changeframecount; + da2->fullchange = 3; } static const device_config_t da2_configuration[] = { From dad8edd32e89094d85a8ca7533dcae0aa222b72f Mon Sep 17 00:00:00 2001 From: Akamaki <97360908+akmed772@users.noreply.github.com> Date: Sat, 21 Feb 2026 02:40:26 +0900 Subject: [PATCH 17/25] cleanup (m_xt_ibm5550.c) --- src/machine/m_xt_ibm5550.c | 169 ++++--------------------------------- 1 file changed, 17 insertions(+), 152 deletions(-) diff --git a/src/machine/m_xt_ibm5550.c b/src/machine/m_xt_ibm5550.c index aefbf0193..00f203f1a 100644 --- a/src/machine/m_xt_ibm5550.c +++ b/src/machine/m_xt_ibm5550.c @@ -18,7 +18,7 @@ * These first-gen models have 1-3 DSQD diskette drives. * You need select "Type: 5.25" 720k" in the Settings dialog - Floppy & CD-ROM drives. * - * Currently, this module only support the model B configuration without hard disk. + * Currently, this module supports model A and B configurations without hard disk. * * Authors: Akamaki. * @@ -133,12 +133,12 @@ // #define LV_OUTPUT 0x3E // #define LV_COMPATIBILITY 0x3F -#define TIMER_CTR_0 0 //DMA -#define TIMER_CTR_1 1 //8253 timer -#define TIMER_CTR_2 2 //Speaker +#define TIMER_CTR_0 0 /* DMA */ +#define TIMER_CTR_1 1 /* PIT */ +#define TIMER_CTR_2 2 /* Speaker */ -#define EPOCH_IRQ3_BIT (1 << 3) //Keyboard -#define EPOCH_IRQ6_BIT (1 << 6) //Timer +#define EPOCH_IRQ3_BIT (1 << 3) /* Keyboard */ +#define EPOCH_IRQ6_BIT (1 << 6) /* PIT */ enum epoch_nvr_ADDR { epoch_nvr_SECOND1, @@ -161,7 +161,7 @@ enum epoch_nvr_ADDR { }; #ifndef RELEASE_BUILD -#define ENABLE_EPOCH_LOG 1 +//#define ENABLE_EPOCH_LOG 1 #endif #ifdef ENABLE_EPOCH_LOG @@ -381,7 +381,7 @@ epoch_out(uint16_t addr, uint16_t val, void *priv) case LC_MAXIMUM_SCAN_LINE: case LC_START_ADDRESS_HIGH: case LC_START_ADDRESS_LOW: - epoch->fullchange = changeframecount; + epoch->fullchange = 3; epoch_recalctimings(epoch); break; default: @@ -648,6 +648,7 @@ epoch_inw(uint16_t addr, void *priv) return temp; } +/* Return a memory address for 9-bit word access */ static uint32_t getaddr_9bitword(int32_t addr) { @@ -1139,13 +1140,6 @@ epoch_poll(void *priv) if (epoch->vc == epoch->dispend) { epoch->dispon = 0; if (!(epoch->crtmode & 0x02)) { /* in text mode */ - // if (epoch->attrc[LV_CURSOR_CONTROL] & 0x01) /* cursor blinking */ - // { - // epoch->cursoron = (epoch->blink | 1) & epoch->blinkconf; - // epoch->cursoron = 1; - // } else { - // epoch->cursoron = 0; - // } switch (epoch->crtc[LC_CURSOR_ROW_START] & 0x60) { case 0x20: epoch->cursoron = 0; @@ -1161,7 +1155,7 @@ epoch_poll(void *priv) break; } if (!(epoch->blink & (0x08 - 2))) /* force redrawing for cursor and blink attribute */ - epoch->fullchange = changeframecount; + epoch->fullchange = 3; } epoch->blink++; @@ -1227,112 +1221,44 @@ static void epoch_vram_write(uint32_t addr, uint8_t val, void *priv) { epoch_t *epoch = (epoch_t *) priv; - // if ((addr & ~0xfff) != 0xE0000) return; // epoch_log("epoch_vw: %x %x\n", addr, val); addr &= EPOCH_MASK_VRAM; epoch->vram[addr] = val; - epoch->fullchange = changeframecount; - // if(val == 0x66) - // epoch_log("66 %04X:%04X %04X:%04X>%04X:%04X\n", cs >> 4, cpu_state.pc, DS, SI,ES,DI); + epoch->fullchange = 3; } static uint8_t epoch_vram_read(uint32_t addr, void *priv) { epoch_t *epoch = (epoch_t *) priv; - // if ((addr & ~epoch_MASK_CRAM) != 0xE0000) - // return epoch_INVALIDACCESS8; addr &= EPOCH_MASK_VRAM; return epoch->vram[addr]; } -// static void -// epoch_vram_writeb(uint32_t addr, uint8_t val, void *priv) -// { -// epoch_t *epoch = (epoch_t *) priv; -// // epoch_log("%04X:%04X epoch_vwb: %x, val %x\n", cs >> 4, cpu_state.pc, addr, val); -// cycles -= video_timing_write_b; -// // return; -// // epoch_vram_write(addr, val, epoch); -// } static void epoch_vram_writew(uint32_t addr, uint16_t val, void *priv) { epoch_t *epoch = (epoch_t *) priv; - // uint8_t convert[8] = {0, 2, 8, 8, 1, 3, 8, 8};/* only even chars */ - //uint8_t convert[8] = {8, 8, 0, 2, 8, 8, 1, 3};/* only even chars */ - // uint8_t convert[8] = {0, 1, 2, 3, 4, 5, 6, 7};/* all but alt */ - // uint8_t convert[8] = {0, 1, 2, 3, 8, 8, 8, 8};/* even alt */ - // uint8_t convert[8] = {8, 8, 8, 8, 0, 1, 2, 3};/* even alt */ - // uint8_t convert[8] = {0, 4, 1, 5, 2, 6, 3, 7};/* all but pos+-1 */ - // uint8_t convert[8] = {8, 8, 4, 6, 8, 8, 5, 7};/* only odd (0) */ - // uint8_t convert[8] = {8, 8, 0, 2, 8, 8, 1, 3};/* only even */ - // uint8_t convert[8] = {4, 6, 8, 8, 5, 7, 8, 8};/* only odd (0) */ - // uint8_t convert[8] = {8, 4, 6, 8, 8, 5, 7, 8};/* none */ // epoch_log("%04X:%04X epoch_vww: %x, val %x DS %x SI %x ES %x DI %x %x\n", cs >> 4, cpu_state.pc, addr, val,DS,SI,ES,DI, epoch->crtc[LC_INTERLACE_AND_SKEW]); // epoch_log("%04X:%04X epoch_vww: %x, val %x cm %x\n", cs >> 4, cpu_state.pc, addr, val, epoch->crtmode); cycles -= video_timing_write_w; addr -= 0xA0000; - // addr &= 0xfffffffe; - // if (0) { if (!(epoch->crtmode & 0x02) && !(epoch->font24)) { uint32_t toaddr, bitnum; - // if (addr < 0xd0000) { - // int index = (addr - 0xA0000) >> 7; /* 128 bytes per char */ - // addr &= 0x007f; - // addr += 0xA0000 + 84 * index; - // epoch_log("%x %x\n", addr, index); - // } else if (addr >= 0xD8000) { - // int index = (addr - 0xD8000) >> 7; /* 128 bytes per char */ - // addr &= 0x007f; - // if (addr & 2) return; - // addr >>= 1; - // addr += 0xC0000 + 42 * index; - // epoch_log("%x %x\n", addr, index); - // } else - // return; - - // if (addr < 0xd0000) { - // addr -= 0xA0000; - // if (addr & 2) - // return; - // addr >>= 1; - // addr += 0xA0000; - // } else if (addr >= 0xD8000) { - // addr -= 0xD8000; - // if (addr & 2) - // return; - // addr >>= 1; - // addr += 0xD8000; - // } else - // return; - - // addr >>= 1; - // if(addr & 0x02) - // addr--; - // addr ^= 0x06; - //addr &= 0xffffd;/* 1101 */ - // addr >>= 1; - - // if (convert[(addr + 0) & 7] > 7) - // return; - // epoch_vram_write((addr & 0xffff8) + convert[(addr + 0) & 7], val & 0xff, epoch); - // epoch_vram_write((addr & 0xffff8) + convert[(addr + 1) & 7], val >> 8, epoch); /* rw one word with 9 bits */ /* virtual: 20000h (0010b, 0011b) -> real: 00001h (0000b, 0001b) */ - toaddr = getaddr_9bitword(addr); bitnum = toaddr & 7; epoch_vram_write(toaddr, val & 0xff, epoch); - // epoch_log("%x %x\n", toaddr, val); + /* get 9th bit */ toaddr >>= 3; toaddr += 0x20000; /* real: C0000h */ val >>= 15; - // epoch_log("%x %x ", toaddr, val); val <<= bitnum; - // epoch_log("%x ", val); - val |= (epoch_vram_read(toaddr, epoch) & (~(1 << bitnum)));/* mask to update one bit */ + + /* mask to update one bit */ + val |= (epoch_vram_read(toaddr, epoch) & (~(1 << bitnum))); epoch_vram_write(toaddr, val, epoch); // epoch_log("%x %x\n", toaddr, val); // epoch_log("%x %x\n", addr, val); @@ -1343,72 +1269,17 @@ epoch_vram_writew(uint32_t addr, uint16_t val, void *priv) // epoch_log("%x %x\n", addr, val); } -// static uint8_t -// epoch_vram_readb(uint32_t addr, void *priv) -// { -// epoch_t *epoch = (epoch_t *) priv; -// cycles -= video_timing_read_b; -// // return 0xff; -// // epoch_log("%04X:%04X epoch_vrb: %x, val %x\n", cs >> 4, cpu_state.pc, addr, epoch_vram_read(addr, epoch)); -// // return epoch_vram_read(addr, epoch); -// } - static uint16_t epoch_vram_readw(uint32_t addr, void *priv) { epoch_t *epoch = (epoch_t *) priv; - // uint8_t convert[8] = {0, 2, 4, 6, 1, 3, 5, 7};/* all but pos+-1 */ - // uint8_t convert[8] = {0, 4, 8, 8, 8, 8, 8, 8};/* all but pos+-1 */ cycles -= video_timing_read_w; addr -= 0xA0000; // epoch_log("%04X:%04X epoch_vrw: %x cm %x\n", cs >> 4, cpu_state.pc, addr, epoch->crtmode); - // addr &= 0xfffffffe; - //read 0->0,1 2->2,3 4->4,5 6->6,7 - //read 0->0,2 2->1,3 4->4,6 6->5,7 - // if (0) { if (!(epoch->crtmode & 0x02) && !(epoch->font24)) { uint16_t ret; uint32_t bitnum; uint32_t toaddr; - // if (addr < 0xd0000) { - // int index = (addr - 0xA0000) >> 7; /* 128 bytes per char */ - // addr &= 0x007f; - // addr += 0xA0000 + 84 * index; - // epoch_log("%x %x %x\n", addr, index, epoch_vram_read(addr, epoch) | (epoch_vram_read(addr + 1, epoch) << 8)); - // } else if (addr >= 0xD8000) { - // int index = (addr - 0xD8000) >> 7; /* 128 bytes per char */ - // addr &= 0x007f; - // if (addr & 2) return EPOCH_INVALIDACCESS16; - // addr >>= 1; - // addr += 0xC0000 + 42 * index; - // epoch_log("%x %x %x\n", addr, index, epoch_vram_read(addr, epoch) | (epoch_vram_read(addr + 1, epoch) << 8)); - // } else - // return EPOCH_INVALIDACCESS16; - - // if (addr < 0xd0000) { - // addr -= 0xA0000; - // if (addr & 2) - // return EPOCH_INVALIDACCESS16; - // addr >>= 1; - // addr += 0xA0000; - // } else if (addr >= 0xD8000) { - // addr -= 0xD8000; - // if (addr & 2) - // return EPOCH_INVALIDACCESS16; - // addr >>= 1; - // addr += 0xD8000; - // } else - // return EPOCH_INVALIDACCESS16; - - // if(addr & 0x02) - // addr--; - // // addr ^= 0x06; - // addr >>= 1; - - // if (convert[(addr + 0) & 7] > 7) - // return 0; - // return epoch_vram_read((addr & 0xffff8) + convert[addr & 7], epoch) | (epoch_vram_read((addr & 0xffff8) + convert[(addr + 1) & 7], epoch) << 8); - /* rw one word with 9 bits */ /* virtual: 20000h (0010b, 0011b) -> real: 00001h (0000b, 0001b) */ toaddr = getaddr_9bitword(addr); @@ -1430,10 +1301,9 @@ static void epoch_cram_write(uint32_t addr, uint8_t val, void *priv) { epoch_t *epoch = (epoch_t *) priv; - // if ((addr & ~0xfff) != 0xE0000) return; addr &= EPOCH_MASK_CRAM; epoch->cram[addr] = val; - epoch->fullchange = changeframecount; + epoch->fullchange = 3; // epoch_log("cw %04X:%04X %04X %02X\n", cs >> 4, cpu_state.pc, addr, val); } static void @@ -1458,8 +1328,6 @@ static uint8_t epoch_cram_read(uint32_t addr, void *priv) { epoch_t *epoch = (epoch_t *) priv; - // if ((addr & ~epoch_MASK_CRAM) != 0xE0000) - // return epoch_INVALIDACCESS8; addr &= EPOCH_MASK_CRAM; return epoch->cram[addr]; } @@ -1495,7 +1363,6 @@ epoch_parity_readb(uint32_t addr, void *priv) return EPOCH_INVALIDACCESS8; } } - // return EPOCH_INVALIDACCESS8; return mem_read_ram(addr, priv); } @@ -1533,8 +1400,6 @@ epoch_parity_writeb(uint32_t addr, uint8_t val, void *priv) return; } } - // if (val == 0xcb) - // epoch_log("CB %04X:%04X %04X:%04X>%04X:%04X\n", cs >> 4, cpu_state.pc, DS, SI, ES, DI); mem_write_ram(addr, val, priv); } static void @@ -2338,7 +2203,7 @@ epoch_init(UNUSED(const device_t *info)) epoch->dispontime = 1000ull << 32; epoch->dispofftime = 1000ull << 32; // epoch->changedvram = calloc(1, (EPOCH_MASK_VRAMPLANE + 1) >> 9); /* XX000h */ - changeframecount = 3; + if (epoch->font24) epoch->pixelclock = EPOCH_PIXELCLOCK24; else From 3fd344d957e16a19cf2c968f3230104c6cfa42e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miran=20Gr=C4=8Da?= Date: Sat, 21 Feb 2026 04:24:20 +0100 Subject: [PATCH 18/25] MPU-401: The IRQ must still be cleared on reset. --- src/sound/snd_mpu401.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sound/snd_mpu401.c b/src/sound/snd_mpu401.c index b1ff6ab8b..2562f16cc 100644 --- a/src/sound/snd_mpu401.c +++ b/src/sound/snd_mpu401.c @@ -316,6 +316,9 @@ MPU401_Reset(mpu_t *mpu) mpu->ch_toref[i] = 4; /* Dummy reftable. */ } + mpu->state.irq_pending = 0; + MPU401_UpdateIRQ(mpu, 0); + mpu->state.data_onoff = -1; mpu->state.req_mask = 0; From 74824bbac4f0455621f4be0cbb7113390875115d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miran=20Gr=C4=8Da?= Date: Sun, 22 Feb 2026 01:34:07 +0100 Subject: [PATCH 19/25] Voodoo 64-bit recompiler: Fix shift in FOG_Z per report by skiretic. --- src/include/86box/vid_voodoo_codegen_x86-64.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/86box/vid_voodoo_codegen_x86-64.h b/src/include/86box/vid_voodoo_codegen_x86-64.h index b7028c7d7..a9701860c 100644 --- a/src/include/86box/vid_voodoo_codegen_x86-64.h +++ b/src/include/86box/vid_voodoo_codegen_x86-64.h @@ -2327,7 +2327,7 @@ voodoo_generate(uint8_t *code_block, voodoo_t *voodoo, voodoo_params_t *params, addlong(offsetof(voodoo_state_t, z)); addbyte(0xc1); /*SHR EAX, 12*/ addbyte(0xe8); - addbyte(12); + addbyte(20); addbyte(0x25); /*AND EAX, 0xff*/ addlong(0xff); #if 0 From f23e88df4d6e9bb50fa34fddcd5b1209a6aa3f84 Mon Sep 17 00:00:00 2001 From: Jasmine Iwanek Date: Sun, 22 Feb 2026 01:44:51 -0500 Subject: [PATCH 20/25] Initial corrections to PAS16 DSP info --- src/include/86box/snd_sb_dsp.h | 1 + src/sound/snd_pas16.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/include/86box/snd_sb_dsp.h b/src/include/86box/snd_sb_dsp.h index 200912c42..b73508211 100644 --- a/src/include/86box/snd_sb_dsp.h +++ b/src/include/86box/snd_sb_dsp.h @@ -11,6 +11,7 @@ #define SB_SUBTYPE_CLONE_AZTPR16_0X09 3 /* Aztech Sound Galaxy Pro 16 Extra */ #define SB_SUBTYPE_ESS_ES688 4 /* ESS Technology ES688 */ #define SB_SUBTYPE_ESS_ES1688 5 /* ESS Technology ES1688 */ +#define SB_SUBTYPE_MVD201 6 /* Mediavision MVD201, found on the thunderboard and PAS16 */ /* ESS-related */ #define IS_ESS(dsp) ((dsp)->sb_subtype >= SB_SUBTYPE_ESS_ES688) /* Check for future ESS cards here */ diff --git a/src/sound/snd_pas16.c b/src/sound/snd_pas16.c index 9b82d580d..d3addf41a 100644 --- a/src/sound/snd_pas16.c +++ b/src/sound/snd_pas16.c @@ -2327,7 +2327,7 @@ pas16_init(const device_t *info) pas16->has_scsi = (!pas16->type) || (pas16->type == 0x0f); fm_driver_get(FM_YMF262, &pas16->opl); sb_dsp_set_real_opl(&pas16->dsp, 1); - sb_dsp_init(&pas16->dsp, SB_DSP_201, SB_SUBTYPE_DEFAULT, pas16); + sb_dsp_init(&pas16->dsp, SB_DSP_200, SB_SUBTYPE_MVD201, pas16); pas16->mpu = (mpu_t *) calloc(1, sizeof(mpu_t)); mpu401_init(pas16->mpu, 0, 0, M_UART, device_get_config_int("receive_input401")); sb_dsp_set_mpu(&pas16->dsp, pas16->mpu); From 97afe701a835c553b98b2747947b8cecc9ebe722 Mon Sep 17 00:00:00 2001 From: Jasmine Iwanek Date: Sun, 22 Feb 2026 03:57:31 -0500 Subject: [PATCH 21/25] Some cleanups to SB DSP, add two known missing DSP revisions --- src/include/86box/snd_sb.h | 6 ++++-- src/sound/snd_ad1816.c | 2 +- src/sound/snd_azt2316a.c | 2 +- src/sound/snd_cmi8x38.c | 2 +- src/sound/snd_optimc.c | 2 +- src/sound/snd_sb.c | 12 ++++++------ src/sound/snd_sb_dsp.c | 36 +++++++++++++++++++----------------- src/sound/snd_ymf701.c | 2 +- src/sound/snd_ymf71x.c | 2 +- 9 files changed, 35 insertions(+), 31 deletions(-) diff --git a/src/include/86box/snd_sb.h b/src/include/86box/snd_sb.h index 51822f210..394b1b249 100644 --- a/src/include/86box/snd_sb.h +++ b/src/include/86box/snd_sb.h @@ -28,12 +28,14 @@ enum { SADLIB = 1, /* No DSP */ - SB_DSP_105, /* DSP v1.05, Original CT1320 (Also known as CT1310) */ + SB_DSP_103, /* DSP v1.03, "killer card" prototype (Also known as CT1310) */ + SB_DSP_105, /* DSP v1.05, Original CT1320 */ SB_DSP_200, /* DSP v2.00 */ SB_DSP_201, /* DSP v2.01 - needed for high-speed DMA, Seen on CT1350B with CT1336 */ SB_DSP_202, /* DSP v2.02 - Seen on CT1350B with CT1336A */ SBPRO_DSP_300, /* DSP v3.00 */ - SBPRO2_DSP_302, /* DSP v3.02 + OPL3 */ + SBPRO_DSP_301, /* DSP v3.01 */ + SBPRO_DSP_302, /* DSP v3.02 */ SB16_DSP_404, /* DSP v4.05 + OPL3 */ SB16_DSP_405, /* DSP v4.05 + OPL3 */ SB16_DSP_406, /* DSP v4.06 + OPL3 */ diff --git a/src/sound/snd_ad1816.c b/src/sound/snd_ad1816.c index 4e929794d..d47f70a04 100644 --- a/src/sound/snd_ad1816.c +++ b/src/sound/snd_ad1816.c @@ -819,7 +819,7 @@ ad1816_init(const device_t *info) ad1816->sb->opl_enabled = 1; sb_dsp_set_real_opl(&ad1816->sb->dsp, FM_YMF262); - sb_dsp_init(&ad1816->sb->dsp, SBPRO2_DSP_302, SB_SUBTYPE_DEFAULT, ad1816); + sb_dsp_init(&ad1816->sb->dsp, SBPRO_DSP_302, SB_SUBTYPE_DEFAULT, ad1816); sb_dsp_setaddr(&ad1816->sb->dsp, ad1816->cur_sb_addr); sb_dsp_setirq(&ad1816->sb->dsp, ad1816->cur_irq); sb_dsp_setirq(&ad1816->sb->dsp, ad1816->cur_dma); diff --git a/src/sound/snd_azt2316a.c b/src/sound/snd_azt2316a.c index 08fca0f87..b7f981d5c 100644 --- a/src/sound/snd_azt2316a.c +++ b/src/sound/snd_azt2316a.c @@ -1835,7 +1835,7 @@ azt_init(const device_t *info) fm_driver_get(FM_YMF262, &azt2316a->sb->opl); sb_dsp_set_real_opl(&azt2316a->sb->dsp, 1); - sb_dsp_init(&azt2316a->sb->dsp, SBPRO2_DSP_302, azt2316a->type, azt2316a); + sb_dsp_init(&azt2316a->sb->dsp, SBPRO_DSP_302, azt2316a->type, azt2316a); sb_dsp_setaddr(&azt2316a->sb->dsp, azt2316a->cur_addr); sb_dsp_setirq(&azt2316a->sb->dsp, azt2316a->cur_irq); sb_dsp_setdma8(&azt2316a->sb->dsp, azt2316a->cur_dma); diff --git a/src/sound/snd_cmi8x38.c b/src/sound/snd_cmi8x38.c index d4490ac23..826f44bb0 100644 --- a/src/sound/snd_cmi8x38.c +++ b/src/sound/snd_cmi8x38.c @@ -877,7 +877,7 @@ cmi8x38_write(uint16_t addr, uint8_t val, void *priv) dev->sb->dsp.sbleftright_default = !!(val & 0x02); /* Enable or disable SB16 mode. */ - dev->sb->dsp.sb_type = (val & 0x01) ? SBPRO2_DSP_302 : SB16_DSP_405; + dev->sb->dsp.sb_type = (val & 0x01) ? SBPRO_DSP_302 : SB16_DSP_405; break; case 0x22: diff --git a/src/sound/snd_optimc.c b/src/sound/snd_optimc.c index 5ea5d773c..0bad013ff 100644 --- a/src/sound/snd_optimc.c +++ b/src/sound/snd_optimc.c @@ -1095,7 +1095,7 @@ optimc_init(const device_t *info) optimc->fm_type = (info->local & OPTIMC_OPL4) ? FM_YMF278B : FM_YMF262; sb_dsp_set_real_opl(&optimc->sb->dsp, optimc->fm_type != FM_YMF278B); - sb_dsp_init(&optimc->sb->dsp, SBPRO2_DSP_302, SB_SUBTYPE_DEFAULT, optimc); + sb_dsp_init(&optimc->sb->dsp, SBPRO_DSP_302, SB_SUBTYPE_DEFAULT, optimc); sb_dsp_setaddr(&optimc->sb->dsp, optimc->cur_addr); sb_dsp_setirq(&optimc->sb->dsp, optimc->cur_irq); sb_dsp_setdma8(&optimc->sb->dsp, optimc->cur_dma); diff --git a/src/sound/snd_sb.c b/src/sound/snd_sb.c index c2193869d..9a9a9978a 100644 --- a/src/sound/snd_sb.c +++ b/src/sound/snd_sb.c @@ -3170,7 +3170,7 @@ sb_pro_v2_init(UNUSED(const device_t *info)) fm_driver_get(FM_YMF262, &sb->opl); sb_dsp_set_real_opl(&sb->dsp, 1); - sb_dsp_init(&sb->dsp, SBPRO2_DSP_302, SB_SUBTYPE_DEFAULT, sb); + sb_dsp_init(&sb->dsp, SBPRO_DSP_302, SB_SUBTYPE_DEFAULT, sb); sb_dsp_setaddr(&sb->dsp, addr); sb_dsp_setirq(&sb->dsp, device_get_config_int("irq")); sb_dsp_setdma8(&sb->dsp, device_get_config_int("dma")); @@ -3226,7 +3226,7 @@ sb_pro_mcv_init(UNUSED(const device_t *info)) fm_driver_get(FM_YMF262, &sb->opl); sb_dsp_set_real_opl(&sb->dsp, 1); - sb_dsp_init(&sb->dsp, SBPRO2_DSP_302, SB_SUBTYPE_DEFAULT, sb); + sb_dsp_init(&sb->dsp, SBPRO_DSP_302, SB_SUBTYPE_DEFAULT, sb); sb_ct1345_mixer_reset(sb); sb->mixer_enabled = 1; @@ -3258,7 +3258,7 @@ sb_pro_compat_init(UNUSED(const device_t *info)) fm_driver_get(FM_YMF262, &sb->opl); sb_dsp_set_real_opl(&sb->dsp, 1); - sb_dsp_init(&sb->dsp, SBPRO2_DSP_302, SB_SUBTYPE_DEFAULT, sb); + sb_dsp_init(&sb->dsp, SBPRO_DSP_302, SB_SUBTYPE_DEFAULT, sb); sb_ct1345_mixer_reset(sb); sb->mixer_enabled = 1; @@ -3938,7 +3938,7 @@ ess_x688_init(UNUSED(const device_t *info)) fm_driver_get(info->local ? FM_ESFM : FM_YMF262, &ess->opl); sb_dsp_set_real_opl(&ess->dsp, 1); - sb_dsp_init(&ess->dsp, SBPRO2_DSP_302, info->local ? SB_SUBTYPE_ESS_ES1688 : SB_SUBTYPE_ESS_ES688, ess); + sb_dsp_init(&ess->dsp, SBPRO_DSP_302, info->local ? SB_SUBTYPE_ESS_ES1688 : SB_SUBTYPE_ESS_ES688, ess); sb_dsp_setaddr(&ess->dsp, addr); sb_dsp_setirq(&ess->dsp, device_get_config_int("irq")); sb_dsp_setdma8(&ess->dsp, device_get_config_int("dma")); @@ -4050,7 +4050,7 @@ ess_x688_pnp_init(UNUSED(const device_t *info)) fm_driver_get(info->local ? FM_ESFM : FM_YMF262, &ess->opl); sb_dsp_set_real_opl(&ess->dsp, 1); - sb_dsp_init(&ess->dsp, SBPRO2_DSP_302, info->local ? SB_SUBTYPE_ESS_ES1688 : SB_SUBTYPE_ESS_ES688, ess); + sb_dsp_init(&ess->dsp, SBPRO_DSP_302, info->local ? SB_SUBTYPE_ESS_ES1688 : SB_SUBTYPE_ESS_ES688, ess); sb_dsp_setdma16_supported(&ess->dsp, 0); ess_mixer_reset(ess); @@ -4136,7 +4136,7 @@ ess_x688_mca_init(UNUSED(const device_t *info)) fm_driver_get(info->local ? FM_ESFM : FM_YMF262, &ess->opl); sb_dsp_set_real_opl(&ess->dsp, 1); - sb_dsp_init(&ess->dsp, SBPRO2_DSP_302, info->local ? SB_SUBTYPE_ESS_ES1688 : SB_SUBTYPE_ESS_ES688, ess); + sb_dsp_init(&ess->dsp, SBPRO_DSP_302, info->local ? SB_SUBTYPE_ESS_ES1688 : SB_SUBTYPE_ESS_ES688, ess); sb_dsp_setdma16_supported(&ess->dsp, 0); ess_mixer_reset(ess); diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 8adb46c5d..275c90576 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -85,20 +85,22 @@ char sb202_copyright[] = "COPYRIGHT(C) CREATIVE TECHNOLOGY PTE. LTD. (1991) char sb16_copyright[] = "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992."; uint16_t sb_dsp_versions[] = { 0, /* Pad */ - 0, /* SADLIB - No DSP */ + 0, /* SADLIB - No DSP */ + 0x103, /* SB_DSP_103 - SB "killer card" prototype, DSP v1.03 */ 0x105, /* SB_DSP_105 - SB1/1.5, DSP v1.05 */ 0x200, /* SB_DSP_200 - SB1.5/2, DSP v2.00 */ 0x201, /* SB_DSP_201 - SB1.5/2, DSP v2.01 - needed for high-speed DMA */ 0x202, /* SB_DSP_202 - SB2, DSP v2.02 */ - 0x300, /* SB_PRO_DSP_300 - SB Pro, DSP v3.00 */ - 0x302, /* SBPRO2_DSP_302 - SB Pro 2, DSP v3.02 + OPL3 */ - 0x404, /* SB16_DSP_404 - DSP v4.04 + OPL3 */ - 0x405, /* SB16_405 - DSP v4.05 + OPL3 */ - 0x406, /* SB16_406 - DSP v4.06 + OPL3 */ - 0x40b, /* SB16_411 - DSP v4.11 + OPL3 */ - 0x40c, /* SBAWE32 - DSP v4.12 + OPL3 */ - 0x40d, /* SBAWE32PNP - DSP v4.13 + OPL3 */ - 0x410 /* SBAWE64 - DSP v4.16 + OPL3 */ + 0x300, /* SBPRO_DSP_300 - SB Pro, DSP v3.00 */ + 0x301, /* SBPRO_DSP_301 - SB Pro/Pro 2, DSP v3.01 */ + 0x302, /* SBPRO_DSP_302 - SB Pro/Pro 2, DSP v3.02 */ + 0x404, /* SB16_DSP_404 - DSP v4.04 + OPL3 */ + 0x405, /* SB16_DSP_405 - DSP v4.05 + OPL3 */ + 0x406, /* SB16_DSP_406 - DSP v4.06 + OPL3 */ + 0x40b, /* SB16_DSP_411 - DSP v4.11 + OPL3 */ + 0x40c, /* SBAWE32_DSP_412 - DSP v4.12 + OPL3 */ + 0x40d, /* SBAWE32_DSP_413 - DSP v4.13 + OPL3 */ + 0x410 /* SBAWE64_DSP_416 - DSP v4.16 + OPL3 */ }; /*These tables were 'borrowed' from DOSBox*/ @@ -1614,7 +1616,7 @@ sb_exec_command(sb_dsp_t *dsp) break; case 0xA0: /* Set input mode to mono */ case 0xA8: /* Set input mode to stereo */ - if ((dsp->sb_type < SBPRO_DSP_300) || (dsp->sb_type > SBPRO2_DSP_302)) + if ((dsp->sb_type < SBPRO_DSP_300) || (dsp->sb_type > SBPRO_DSP_302)) break; /* TODO: Implement. 3.xx-only command. */ break; @@ -1859,12 +1861,12 @@ sb_exec_command(sb_dsp_t *dsp) * 059h Fetches the samples and then immediately plays them back. SB??? * 078h Auto-init DMA ADPCM SB2??? * 07Ah 2.6-bit ADPCM SB??? - * 0E3h DSP Copyright SBPro2??? (SBPRO2_DSP_302) - * 0F0h Sine Generator SB (SB_DSP_105, DSP20x) - * 0F1h DSP Auxiliary Status (Obsolete) SB-Pro2 (DSP20x, SBPRO2_DSP_302) - * 0F2h IRQ Request, 8-bit SB (SB_DSP_105, DSP20x) + * 0E3h DSP Copyright SBPro2??? (SBPRO_DSP_302) + * 0F0h Sine Generator SB (SB_DSP_105, SB_DSP_20x) + * 0F1h DSP Auxiliary Status (Obsolete) SB-Pro2 (SB_DSP_20x, SBPRO_DSP_302) + * 0F2h IRQ Request, 8-bit SB (SB_DSP_105, SB_DSP_20x) * 0F3h IRQ Request, 16-bit SB16 - * 0F4h Perform ROM checksum SB (SB_DSP_105, DSP20x) + * 0F4h Perform ROM checksum SB (SB_DSP_105, SB_DSP_20x) * 0FBh DSP Status SB16 * 0FCh DSP Auxiliary Status SB16 * 0FDh DSP Command Status SB16 @@ -2236,7 +2238,7 @@ sb_dsp_init(sb_dsp_t *dsp, int type, int subtype, void *parent) a set frequency command is sent. */ recalc_sb16_filter(0, 3200 * 2); } - if (IS_ESS(dsp) || (dsp->sb_type >= SBPRO2_DSP_302)) { + if (IS_ESS(dsp) || (dsp->sb_type >= SBPRO_DSP_302)) { /* OPL3 or dual OPL2 is stereo. */ if (dsp->sb_has_real_opl) recalc_opl_filter(FREQ_49716 * 2); diff --git a/src/sound/snd_ymf701.c b/src/sound/snd_ymf701.c index c6b1467c8..c599eed9e 100644 --- a/src/sound/snd_ymf701.c +++ b/src/sound/snd_ymf701.c @@ -419,7 +419,7 @@ ymf701_init(const device_t *info) ymf701->sb->opl_enabled = 1; sb_dsp_set_real_opl(&ymf701->sb->dsp, 1); - sb_dsp_init(&ymf701->sb->dsp, SBPRO2_DSP_302, SB_SUBTYPE_DEFAULT, ymf701); + sb_dsp_init(&ymf701->sb->dsp, SBPRO_DSP_302, SB_SUBTYPE_DEFAULT, ymf701); sb_dsp_setaddr(&ymf701->sb->dsp, ymf701->cur_sb_addr); sb_dsp_setirq(&ymf701->sb->dsp, ymf701->cur_sb_irq); sb_dsp_setdma8(&ymf701->sb->dsp, ymf701->cur_sb_dma); diff --git a/src/sound/snd_ymf71x.c b/src/sound/snd_ymf71x.c index 70ccf6872..4886e69b8 100644 --- a/src/sound/snd_ymf71x.c +++ b/src/sound/snd_ymf71x.c @@ -712,7 +712,7 @@ ymf71x_init(const device_t *info) ymf71x->sb->opl_enabled = 1; sb_dsp_set_real_opl(&ymf71x->sb->dsp, 1); - sb_dsp_init(&ymf71x->sb->dsp, SBPRO2_DSP_302, SB_SUBTYPE_DEFAULT, ymf71x); + sb_dsp_init(&ymf71x->sb->dsp, SBPRO_DSP_302, SB_SUBTYPE_DEFAULT, ymf71x); sb_ct1345_mixer_reset(ymf71x->sb); ymf71x->sb->opl_mixer = ymf71x; From 2bb0ad3165f9f60edd0123a67f5d0c9bb1ebe80c Mon Sep 17 00:00:00 2001 From: Jasmine Iwanek Date: Sun, 22 Feb 2026 03:59:17 -0500 Subject: [PATCH 22/25] Correct ESS x688 DSP revisions --- src/sound/snd_sb.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sound/snd_sb.c b/src/sound/snd_sb.c index 9a9a9978a..de9b71f9b 100644 --- a/src/sound/snd_sb.c +++ b/src/sound/snd_sb.c @@ -3938,7 +3938,7 @@ ess_x688_init(UNUSED(const device_t *info)) fm_driver_get(info->local ? FM_ESFM : FM_YMF262, &ess->opl); sb_dsp_set_real_opl(&ess->dsp, 1); - sb_dsp_init(&ess->dsp, SBPRO_DSP_302, info->local ? SB_SUBTYPE_ESS_ES1688 : SB_SUBTYPE_ESS_ES688, ess); + sb_dsp_init(&ess->dsp, SBPRO_DSP_301, info->local ? SB_SUBTYPE_ESS_ES1688 : SB_SUBTYPE_ESS_ES688, ess); sb_dsp_setaddr(&ess->dsp, addr); sb_dsp_setirq(&ess->dsp, device_get_config_int("irq")); sb_dsp_setdma8(&ess->dsp, device_get_config_int("dma")); @@ -4050,7 +4050,7 @@ ess_x688_pnp_init(UNUSED(const device_t *info)) fm_driver_get(info->local ? FM_ESFM : FM_YMF262, &ess->opl); sb_dsp_set_real_opl(&ess->dsp, 1); - sb_dsp_init(&ess->dsp, SBPRO_DSP_302, info->local ? SB_SUBTYPE_ESS_ES1688 : SB_SUBTYPE_ESS_ES688, ess); + sb_dsp_init(&ess->dsp, SBPRO_DSP_301, info->local ? SB_SUBTYPE_ESS_ES1688 : SB_SUBTYPE_ESS_ES688, ess); sb_dsp_setdma16_supported(&ess->dsp, 0); ess_mixer_reset(ess); @@ -4136,7 +4136,7 @@ ess_x688_mca_init(UNUSED(const device_t *info)) fm_driver_get(info->local ? FM_ESFM : FM_YMF262, &ess->opl); sb_dsp_set_real_opl(&ess->dsp, 1); - sb_dsp_init(&ess->dsp, SBPRO_DSP_302, info->local ? SB_SUBTYPE_ESS_ES1688 : SB_SUBTYPE_ESS_ES688, ess); + sb_dsp_init(&ess->dsp, SBPRO_DSP_301, info->local ? SB_SUBTYPE_ESS_ES1688 : SB_SUBTYPE_ESS_ES688, ess); sb_dsp_setdma16_supported(&ess->dsp, 0); ess_mixer_reset(ess); From 597adb5897aa713d63d65f090c3c0c28e9ba0d37 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Sun, 22 Feb 2026 15:56:07 +0600 Subject: [PATCH 23/25] Add functionality to emulate QLogic ISP SCSI HBA devices Tested with Windows 2000 and NetBSD 10.1. Target Mode is not implemented, because I had no OS using it. --- src/include/86box/pci.h | 37 + src/include/86box/scsi_qlogic.h | 19 + src/scsi/CMakeLists.txt | 1 + src/scsi/scsi.c | 6 + src/scsi/scsi_ql1xxx.c | 4581 +++++++++++++++++++++++++++++++ 5 files changed, 4644 insertions(+) create mode 100644 src/include/86box/scsi_qlogic.h create mode 100644 src/scsi/scsi_ql1xxx.c diff --git a/src/include/86box/pci.h b/src/include/86box/pci.h index f5f5aee04..d07dff7eb 100644 --- a/src/include/86box/pci.h +++ b/src/include/86box/pci.h @@ -31,6 +31,43 @@ #define PCI_REG_LATENCY_TIMER 0x0d #define PCI_REG_HEADER_TYPE 0x0e #define PCI_REG_BIST 0x0f +#define PCI_REG_BAR0_BYTE0 0x10 +#define PCI_REG_BAR0_BYTE1 0x11 +#define PCI_REG_BAR0_BYTE2 0x12 +#define PCI_REG_BAR0_BYTE3 0x13 +#define PCI_REG_BAR1_BYTE0 0x14 +#define PCI_REG_BAR1_BYTE1 0x15 +#define PCI_REG_BAR1_BYTE2 0x16 +#define PCI_REG_BAR1_BYTE3 0x17 +#define PCI_REG_BAR2_BYTE0 0x18 +#define PCI_REG_BAR2_BYTE1 0x19 +#define PCI_REG_BAR2_BYTE2 0x1a +#define PCI_REG_BAR2_BYTE3 0x1b +#define PCI_REG_BAR3_BYTE0 0x1c +#define PCI_REG_BAR3_BYTE1 0x1d +#define PCI_REG_BAR3_BYTE2 0x1e +#define PCI_REG_BAR3_BYTE3 0x1e +#define PCI_REG_BAR4_BYTE0 0x20 +#define PCI_REG_BAR4_BYTE1 0x21 +#define PCI_REG_BAR4_BYTE2 0x22 +#define PCI_REG_BAR4_BYTE3 0x23 +#define PCI_REG_BAR5_BYTE0 0x24 +#define PCI_REG_BAR5_BYTE1 0x25 +#define PCI_REG_BAR5_BYTE2 0x26 +#define PCI_REG_BAR5_BYTE3 0x27 +#define PCI_REG_SUBVEN_ID_L 0x2c +#define PCI_REG_SUBVEN_ID_H 0x2d +#define PCI_REG_SUBSYS_ID_L 0x2e +#define PCI_REG_SUBSYS_ID_H 0x2f +#define PCI_REG_ROM_BAR_BYTE0 0x30 +#define PCI_REG_ROM_BAR_BYTE1 0x31 +#define PCI_REG_ROM_BAR_BYTE2 0x32 +#define PCI_REG_ROM_BAR_BYTE3 0x33 +#define PCI_REG_CAPS_PTR 0x34 +#define PCI_REG_INT_LINE 0x3c +#define PCI_REG_INT_PIN 0x3d +#define PCI_REG_MIN_GRANT 0x3e +#define PCI_REG_MAX_LAT 0x3f #define PCI_COMMAND_L_IO 0x01 #define PCI_COMMAND_L_MEM 0x02 diff --git a/src/include/86box/scsi_qlogic.h b/src/include/86box/scsi_qlogic.h new file mode 100644 index 000000000..f3c0a695e --- /dev/null +++ b/src/include/86box/scsi_qlogic.h @@ -0,0 +1,19 @@ +/* + * 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. + * + * Emulation of QLogic QLA1x40/QLA1x80/QLA1x160 SCSI HBA. + * + * Authors: Dmitry Borisov, + * + * Copyright 2026 Dmitry Borisov + */ +#pragma once + +extern const device_t qla1040b_device; +extern const device_t qla1080_device; +extern const device_t qla1240_device; +extern const device_t qla1280_device; +extern const device_t qla12160a_device; diff --git a/src/scsi/CMakeLists.txt b/src/scsi/CMakeLists.txt index c710f758d..63bf9c225 100644 --- a/src/scsi/CMakeLists.txt +++ b/src/scsi/CMakeLists.txt @@ -28,5 +28,6 @@ add_library(scsi OBJECT scsi_t128.c scsi_ncr53c8xx.c scsi_pcscsi.c + scsi_ql1xxx.c scsi_spock.c ) diff --git a/src/scsi/scsi.c b/src/scsi/scsi.c index 36e1d0638..89ea9fc29 100644 --- a/src/scsi/scsi.c +++ b/src/scsi/scsi.c @@ -38,6 +38,7 @@ #include <86box/scsi_ncr5380.h> #include <86box/scsi_ncr53c8xx.h> #include <86box/scsi_pcscsi.h> +#include <86box/scsi_qlogic.h> #include <86box/scsi_spock.h> int scsi_card_current[SCSI_CARD_MAX] = { 0, 0, 0, 0 }; @@ -91,6 +92,11 @@ static SCSI_CARD scsi_cards[] = { { &ncr53c860_pci_device, }, { &ncr53c875_pci_device, }, { &dc390_pci_device, }, + { &qla1040b_device, }, + { &qla1080_device, }, + { &qla1240_device, }, + { &qla1280_device, }, + { &qla12160a_device, }, { NULL, }, // clang-format on }; diff --git a/src/scsi/scsi_ql1xxx.c b/src/scsi/scsi_ql1xxx.c new file mode 100644 index 000000000..def034163 --- /dev/null +++ b/src/scsi/scsi_ql1xxx.c @@ -0,0 +1,4581 @@ +/* + * 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. + * + * QLogic QLA1x40/QLA1x80/QLA1x160 SCSI HBA emulation. + * + * Register values are derived from the Matthew Jacob's + * multiplatform driver for ISP chipsets. + * + * Authors: Dmitry Borisov, + * + * Copyright 2026 Dmitry Borisov + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define HAVE_STDARG_H +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/io.h> +#include <86box/mem.h> +#include <86box/dma.h> +#include <86box/rom.h> +#include <86box/timer.h> +#include <86box/nvr.h> +#include <86box/pci.h> +#include <86box/scsi.h> +#include <86box/scsi_device.h> +#include <86box/scsi_qlogic.h> +#include <86box/nmc93cxx.h> +#include <86box/fifo8.h> +#include <86box/plat_unused.h> +#include <86box/plat_fallthrough.h> + +#include "cpu.h" + +#define ARRAY_SIZE(x) sizeof(x)/sizeof(x[0]) + +/* + * Device configuration + */ +#define QL_CFG_BIOS_ENABLE "bios" +#define QL_CFG_BIOS_REVISION "bios_rev" + +/* + * Device info->local definitions + */ +#define QL_DEV_CHIP_TYPE_MASK 0x000000FF +#define QL_DEV_CHIP_REV_MASK 0x00000F00 +#define QL_DEV_FLASH_TYPE_MASK 0x0000F000 +#define QL_DEV_CHIP_REV_SHIFT 8 +#define QL_DEV_FLASH_TYPE_SHIFT 12 + +#define QL_ISP1040 0x00000000 +#define QL_ISP1080 0x00000001 +#define QL_ISP1240 0x00000002 +#define QL_ISP1280 0x00000003 +#define QL_ISP12160 0x00000004 +#define QL_REV_ISP1080 0x00000100 +#define QL_REV_ISP1020 0x00000000 +#define QL_REV_ISP1020A 0x00000100 +#define QL_REV_ISP1040 0x00000200 +#define QL_REV_ISP1040A 0x00000300 +#define QL_REV_ISP1040B 0x00000400 +#define QL_REV_ISP1040C 0x00000500 +#define QL_FLASH_AM29F010 0x00000000 +#define QL_FLASH_AM29LV010B 0x00000100 + +/* ISP firmware version extracted from the BIOS */ +#define ISP_FW_VER(Major, Minor, Micro) (((Major) << 16) | ((Minor) << 8) | (Micro)) + +/* Address of the IOCB handler in firmware for the 1040 ISP chips */ +#define QL_IOCB_FW_BASE 0x0700 + +/* Maximum SCSI devices supported by the chip */ +#define QL_MAX_TID 16 + +#define QL_PCI_PM_BASE 0x44 +#define QL_PCI_IO_BAR_SIZE 0x100 +#define QL_PCI_MMIO_BAR_SIZE 0x1000 +#define QL_PCI_ROM_BAR_64K_SIZE 0x10000 +#define QL_PCI_ROM_BAR_128K_SIZE 0x20000 + +#define QL_IO_DECODE_MASK (QL_PCI_IO_BAR_SIZE - 1) + +/* The hardware register layout consists of a set of 2-byte aligned registers */ +#define REG_TO_IDX(x) ((x) / 2) +#define IDX_TO_REG(x) ((x) * 2) + +/* + * Flash command definitions + */ +#define FLASH_CMD_CHIP_ERASE_CONFIRM 0x10 +#define FLASH_CMD_BLOCK_ERASE_CONFIRM 0x30 +#define FLASH_CMD_SETUP_ERASE 0x80 +#define FLASH_CMD_AUTO_SELECT 0x90 +#define FLASH_CMD_PROGRAM 0xA0 +#define FLASH_CMD_READ_ARRAY 0xF0 + +/* + * Flash status register definitions + */ +#define FLASH_STATUS_ERASE_TIMEOUT_EXPIRED 0x08 // DQ3 +#define FLASH_STATUS_ERROR 0x20 // DQ5 +#define FLASH_STATUS_TOGGLE 0x40 // DQ6 +#define FLASH_STATUS_DATA_POLLING 0x80 // DQ7 + +/* + * Flash block brotection status definitions + */ +#define FLASH_BLOCK_STATUS_NOT_PROTECTED 0x00 +#define FLASH_BLOCK_STATUS_PROTECTED 0x01 + +/* All boards use Am29F010 or Am29LV010B, which are 128KB in size */ +#define AM29_FLASH_SIZE 0x20000 +#define AM29_MAX_BLOCKS 8 + +#define AM29_MANUFACTURER_ID 0x01 // AMD +#define AM29F010_MODEL_ID 0x20 +#define AM29LV010B_MODEL_ID 0x6E + +#define AM29_BLOCK_ERASE_ACCEPT_TIMEOUT_US 50.0 // 50 us + +/* A0-A1 */ +#define AM29_AUTOSEL_ADDR_MASK 0x3 + +/* Read the manufacturer ID. A0 = 0, A1 = 0 */ +#define AM29_AUTOSEL_ADDR_MANUFACTURER_ID 0x0 + +/* Read the model ID. A0 = 1, A1 = 0 */ +#define AM29_AUTOSEL_ADDR_MODEL_ID 0x1 + +/* + * Hardware registers + */ +#define QL_REG_ID_LOW REG_TO_IDX(0x00) +#define QL_REG_ID_HIGH REG_TO_IDX(0x02) +#define QL_REG_CFG0 REG_TO_IDX(0x04) +#define QL_REG_CFG1 REG_TO_IDX(0x06) +#define QL_REG_INT_CTRL REG_TO_IDX(0x08) +#define QL_REG_INT_STATUS REG_TO_IDX(0x0A) +#define QL_REG_SEMAPHORE REG_TO_IDX(0x0C) +#define QL_REG_NVRAM REG_TO_IDX(0x0E) +#define QL_REG_FLASH_BIOS_DATA REG_TO_IDX(0x10) +#define QL_REG_FLASH_BIOS_ADDRESS REG_TO_IDX(0x12) +#define QL_REG_MAILBOX0 REG_TO_IDX(0x70) +#define QL_REG_MAILBOX1 REG_TO_IDX(0x72) +#define QL_REG_MAILBOX2 REG_TO_IDX(0x74) +#define QL_REG_MAILBOX3 REG_TO_IDX(0x76) +#define QL_REG_MAILBOX4 REG_TO_IDX(0x78) +#define QL_REG_MAILBOX5 REG_TO_IDX(0x7A) +#define QL_REG_MAILBOX6 REG_TO_IDX(0x7C) +#define QL_REG_MAILBOX7 REG_TO_IDX(0x7E) + +/* RISC bank registers (at offset 0x80) */ +#define QL_REG_RISC_PSR REG_TO_IDX(0x20) +#define QL_REG_RISC_PCR REG_TO_IDX(0x24) +#define QL_REG_RISC_PC REG_TO_IDX(0x2C) +#define QL_REG_RISC_MTR REG_TO_IDX(0x2E) +#define QL_REG_HOST_CMD REG_TO_IDX(0x40) +#define QL_REG_GPIO_DATA REG_TO_IDX(0x4C) +#define QL_REG_GPIO_ENABLE REG_TO_IDX(0x4E) + +/* SXP bank registers (at offset 0x80) */ +#define QL_REG_SXP_PINS_DIFF REG_TO_IDX(0x76) + +/* DMA bank registers (at offset 0x20 for the 1040, for other chips at offset 0x80) */ +#define QL_REG_CDMA_CFG REG_TO_IDX(0x00) +#define QL_REG_CDMA_CTRL REG_TO_IDX(0x02) +#define QL_REG_CDMA_STATUS REG_TO_IDX(0x04) +#define QL_REG_CDMA_FIFO_STATUS REG_TO_IDX(0x06) +#define QL_REG_CDMA_COUNT REG_TO_IDX(0x08) +#define QL_REG_CDMA_ADDR0 REG_TO_IDX(0x0C) +#define QL_REG_CDMA_ADDR1 REG_TO_IDX(0x0E) +#define QL_REG_CDMA_ADDR2 REG_TO_IDX(0x10) +#define QL_REG_CDMA_ADDR3 REG_TO_IDX(0x12) +#define QL_REG_DDMA_CFG REG_TO_IDX(0x20) +#define QL_REG_DDMA_CTRL REG_TO_IDX(0x22) +#define QL_REG_DDMA_STATUS REG_TO_IDX(0x24) +#define QL_REG_DDMA_FIFO_STATUS REG_TO_IDX(0x26) +#define QL_REG_DDMA_XFER_COUNT_LOW REG_TO_IDX(0x28) +#define QL_REG_DDMA_XFER_COUNT_HIGH REG_TO_IDX(0x2A) +#define QL_REG_DDMA_ADDR0 REG_TO_IDX(0x2C) +#define QL_REG_DDMA_ADDR1 REG_TO_IDX(0x2E) +#define QL_REG_DDMA_ADDR2 REG_TO_IDX(0x30) +#define QL_REG_DDMA_ADDR3 REG_TO_IDX(0x32) +#define QL_REG_DDMA_DATA_PORT REG_TO_IDX(0x42) + +/* QL_REG_SXP_PINS_DIFF */ +#define SXP_PINS_SE_MODE 0x0400 +#define SXP_PINS_HVD_MODE 0x0800 +#define SXP_PINS_LVD_MODE 0x1000 + +/* QL_REG_CFG0 */ +#define BIU_CONF0_HW_MASK 0x000F /* Hardware revision mask */ + +#define BIU_CONF0_REV_1020 0x0000 +#define BIU_CONF0_REV_1020A 0x0001 +#define BIU_CONF0_REV_1040 0x0002 +#define BIU_CONF0_REV_1040A 0x0003 +#define BIU_CONF0_REV_1040B 0x0004 +#define BIU_CONF0_REV_1040C 0x0005 + +#define BIU_CONF0_REV_1080 0x0001 + +/* QL_REG_CFG1 */ +#define BIU_BURST_ENABLE 0x0004 +#define BIU_PCI_CONF1_SXP 0x0008 /* SXP bank select (1040 only) */ +#define BIU_PCI_CONF1_FIFO_16 0x0010 +#define BIU_PCI_CONF1_FIFO_32 0x0020 +#define BIU_PCI_CONF1_FIFO_64 0x0030 +#define BIU_PCI_CONF1_FIFO_128 0x0040 + +#define BIU_PCI1080_CONF1_SXP0 0x0100 /* SXP bank #1 select */ +#define BIU_PCI1080_CONF1_SXP1 0x0200 /* SXP bank #2 select */ +#define BIU_PCI1080_CONF1_DMA 0x0300 /* DMA bank select */ +#define BIU_PCI1080_REG_BANK_MASK 0x0700 /* Bank mask */ + +/* QL_REG_INT_CTRL */ +#define QL_IFACE_SOFT_RESET 0x0001 +#define QL_IFACE_FLASH_ENABLE 0x0100 + +/* QL_REG_INT_STATUS and QL_REG_INT_CTRL */ +#define QL_INTR_REQ 0x0002 +#define QL_RISC_INTR_REQ 0x0004 + +/* QL_REG_SEMAPHORE */ +#define QL_SEMAPHORE_LOCK 0x0001 +#define QL_SEMAPHORE_STATUS 0x0002 + +/* QL_REG_NVRAM */ +#define QL_EEPROM_SK 0x0001 +#define QL_EEPROM_CS 0x0002 +#define QL_EEPROM_DI 0x0004 +#define QL_EEPROM_DO 0x0008 + +/* QL_REG_HOST_CMD read */ +#define QL_HC_FLAG_RISC_EXT 0x0010 +#define QL_HC_FLAG_RISC_PAUSE 0x0020 +#define QL_HC_FLAG_RISC_RESET 0x0040 +#define QL_HC_FLAG_HOST_INTR 0x0080 + +/* QL_REG_HOST_CMD write */ +#define QL_HC_RESET_RISC 0x1000 +#define QL_HC_PAUSE_RISC 0x2000 +#define QL_HC_RELEASE_RISC 0x3000 +#define QL_HC_SET_HOST_INTR 0x5000 +#define QL_HC_CLEAR_HOST_INTR 0x6000 +#define QL_HC_CLEAR_RISC_INTR 0x7000 +#define QL_HC_DISABLE_BIOS 0x9000 + +/* + * All IOCB Queue entries are this size + */ +#define QENTRY_LEN 64 + +/* + * Mailbox commands + */ +#define QL_CMD_NOP 0x0000 +#define QL_CMD_LOAD_RAM 0x0001 +#define QL_CMD_EXEC_FIRMWARE 0x0002 +#define QL_CMD_DUMP_RAM 0x0003 +#define QL_CMD_WRITE_RAM_WORD 0x0004 +#define QL_CMD_READ_RAM_WORD 0x0005 +#define QL_CMD_REGISTER_TEST 0x0006 +#define QL_CMD_VERIFY_CHECKSUM 0x0007 +#define QL_CMD_ABOUT_FIRMWARE 0x0008 +#define QL_CMD_LOAD_RAM_A64 0x0009 +#define QL_CMD_DUMP_RAM_A64 0x000A +#define QL_CMD_INIT_REQ_QUEUE 0x0010 +#define QL_CMD_INIT_RSP_QUEUE 0x0011 +#define QL_CMD_EXECUTE_IOCB 0x0012 +#define QL_CMD_ABORT_COMMAND 0x0015 +#define QL_CMD_ABORT_DEVICE 0x0016 +#define QL_CMD_ABORT_TARGET 0x0017 +#define QL_CMD_BUS_RESET 0x0018 +#define QL_CMD_START_QUEUE 0x001A +#define QL_CMD_GET_FIRMWARE_STATUS 0x001F +#define QL_CMD_GET_RETRY_COUNT 0x0022 +#define QL_CMD_GET_ACT_NEG_STATE 0x0025 +#define QL_CMD_GET_TARGET_PARAMETERS 0x0028 +#define QL_CMD_SET_INITIATOR_ID 0x0030 +#define QL_CMD_SET_SELECTION_TIMEOUT 0x0031 +#define QL_CMD_SET_RETRY_COUNT 0x0032 +#define QL_CMD_SET_TAG_AGE_LIMIT 0x0033 +#define QL_CMD_SET_CLOCK_RATE 0x0034 +#define QL_CMD_SET_ACTIVE_NEGATION 0x0035 +#define QL_CMD_SET_ASYNC_DATA_SETUP 0x0036 +#define QL_CMD_SET_PCI_CONTROL 0x0037 +#define QL_CMD_SET_TARGET_PARAMETERS 0x0038 +#define QL_CMD_SET_DEVICE_QUEUE 0x0039 +#define QL_CMD_RETURN_BIOS_BLOCK_ADDR 0x0040 +#define QL_CMD_WRITE_FOUR_RAM_WORDS 0x0041 +#define QL_CMD_EXEC_BIOS_IOCB 0x0042 +#define QL_CMD_SET_SYSTEM_PARAMETER 0x0045 +#define QL_CMD_SET_FIRMWARE_FEATURES 0x004A +#define QL_CMD_INIT_REQ_QUEUE_A64 0x0052 +#define QL_CMD_INIT_RSP_QUEUE_A64 0x0053 +#define QL_CMD_EXECUTE_IOCB_A64 0x0054 +#define QL_CMD_GET_TRANSFER_MODE 0x0059 +#define QL_CMD_SET_DATA_OVERRUN_RECOVERY 0x005A + +/* + * Mailbox command complete status codes + */ +#define QL_MBOX_STATUS_COMPLETE 0x4000 +#define QL_MBOX_STATUS_INVALID 0x4001 +#define QL_MBOX_STATUS_HOST_IFACE_ERROR 0x4002 +#define QL_MBOX_STATUS_TEST_FAILED 0x4003 +#define QL_MBOX_STATUS_CMD_ERROR 0x4005 +#define QL_MBOX_STATUS_CMD_PARAM_ERROR 0x4006 +#define QL_MBOX_STATUS_PENDING 0xFFFF // Invalid, for internal use only + +/* + * Mailbox asynchronous event status codes + */ +#define QL_ASYNC_STATUS_BUS_RESET 0x8001 +#define QL_ASYNC_STATUS_SYSTEM_ERROR 0x8002 +#define QL_ASYNC_STATUS_REQ_XFER_ERROR 0x8003 +#define QL_ASYNC_STATUS_RSP_XFER_ERROR 0x8004 +#define QL_ASYNC_STATUS_SCSI_CMD_COMPLETE 0x8020 + +/* + * Mailbox I/O interface registers + */ +#define QL_MBOX_STATUS 0 +#define QL_MBOX_HNDL_LOW 1 +#define QL_MBOX_HNDL_HIGH 2 +#define QL_MBOX_RQST 4 +#define QL_MBOX_RESP 5 +#define QL_MBOX_REGS_MAX 8 + +/* + * Request and response ring helpers + */ +#define QL_RQST_CONS(dev) ((dev)->reg_mbox_out[QL_MBOX_RQST]) // consumer (HW) +#define QL_RQST_PROD(dev) ((dev)->reg_mbox_in[QL_MBOX_RQST]) // producer (SW) +#define QL_RESP_CONS(dev) ((dev)->reg_mbox_in[QL_MBOX_RESP]) // consumer (SW) +#define QL_RESP_PROD(dev) ((dev)->reg_mbox_out[QL_MBOX_RESP]) // producer (HW) + +/* + * Firmware features flags (QL_CMD_SET_FIRMWARE_FEATURES) + */ +#define FW_FEATURE_FAST_POST 0x1 +#define FW_FEATURE_LVD_NOTIFY 0x2 +#define FW_FEATURE_RIO_32BIT 0x4 +#define FW_FEATURE_RIO_16BIT 0x8 + +/* + * Request header flags definitions + */ +#define RQSFLAG_CONTINUATION 0x01 +#define RQSFLAG_FULL 0x02 +#define RQSFLAG_BADHEADER 0x04 +#define RQSFLAG_BADPACKET 0x08 +#define RQSFLAG_BADCOUNT 0x10 +#define RQSFLAG_BADORDER 0x20 +#define RQSFLAG_MASK 0x3F + +/* + * Request header entry_type definitions + */ +#define RQSTYPE_REQUEST 0x01 +#define RQSTYPE_DATASEG 0x02 +#define RQSTYPE_RESPONSE 0x03 +#define RQSTYPE_MARKER 0x04 +#define RQSTYPE_CMDONLY 0x05 +#define RQSTYPE_ATIO 0x06 // Target Mode +#define RQSTYPE_CTIO 0x07 // Target Mode +#define RQSTYPE_REQUEST_A64 0x09 +#define RQSTYPE_A64_CONT 0x0A +#define RQSTYPE_ENABLE_LUN 0x0B // Target Mode +#define RQSTYPE_MODIFY_LUN 0x0C // Target Mode +#define RQSTYPE_NOTIFY 0x0D // Target Mode +#define RQSTYPE_NOTIFY_ACK 0x0E // Target Mode +#define RQSTYPE_CTIO_A64 0x0F // Target Mode + +/* + * Request flags values + */ +#define REQFLAG_NODISCON 0x0001 +#define REQFLAG_HTAG 0x0002 +#define REQFLAG_OTAG 0x0004 +#define REQFLAG_STAG 0x0008 +#define REQFLAG_TARGET_RTN 0x0010 + +#define REQFLAG_NODATA 0x0000 +#define REQFLAG_DATA_IN 0x0020 +#define REQFLAG_DATA_OUT 0x0040 +#define REQFLAG_DATA_BIDIRECTIONAL 0x0060 + +#define REQFLAG_DISARQ 0x0100 +#define REQFLAG_FRC_ASYNC 0x0200 +#define REQFLAG_FRC_SYNC 0x0400 +#define REQFLAG_FRC_WIDE 0x0800 +#define REQFLAG_NOPARITY 0x1000 +#define REQFLAG_STOPQ 0x2000 +#define REQFLAG_XTRASNS 0x4000 +#define REQFLAG_PRIORITY 0x8000 + +/* + * Request completion status cdes + */ +#define RQCS_COMPLETE 0x0000 +#define RQCS_INCOMPLETE 0x0001 +#define RQCS_DMA_ERROR 0x0002 +#define RQCS_TRANSPORT_ERROR 0x0003 +#define RQCS_RESET_OCCURRED 0x0004 +#define RQCS_ABORTED 0x0005 +#define RQCS_TIMEOUT 0x0006 +#define RQCS_DATA_OVERRUN 0x0007 +#define RQCS_COMMAND_OVERRUN 0x0008 +#define RQCS_STATUS_OVERRUN 0x0009 +#define RQCS_BAD_MESSAGE 0x000A +#define RQCS_NO_MESSAGE_OUT 0x000B +#define RQCS_EXT_ID_FAILED 0x000C +#define RQCS_IDE_MSG_FAILED 0x000D +#define RQCS_ABORT_MSG_FAILED 0x000E +#define RQCS_REJECT_MSG_FAILED 0x000F +#define RQCS_NOP_MSG_FAILED 0x0010 +#define RQCS_PARITY_ERROR_MSG_FAILED 0x0011 +#define RQCS_DEVICE_RESET_MSG_FAILED 0x0012 +#define RQCS_ID_MSG_FAILED 0x0013 +#define RQCS_UNEXP_BUS_FREE 0x0014 +#define RQCS_DATA_UNDERRUN 0x0015 +#define RQCS_XACT_ERR1 0x0018 +#define RQCS_XACT_ERR2 0x0019 +#define RQCS_XACT_ERR3 0x001A +#define RQCS_BAD_ENTRY 0x001B +#define RQCS_PHASE_SKIPPED 0x001D +#define RQCS_ARQS_FAILED 0x001E +#define RQCS_WIDE_FAILED 0x001F +#define RQCS_QUEUE_FULL 0x001C +#define RQCS_SYNCXFER_FAILED 0x0020 +#define RQCS_LVD_BUSERR 0x0021 + +/* + * Request State Flags + */ +#define RQSF_GOT_BUS 0x0100 +#define RQSF_GOT_TARGET 0x0200 +#define RQSF_SENT_CDB 0x0400 +#define RQSF_XFRD_DATA 0x0800 +#define RQSF_GOT_STATUS 0x1000 +#define RQSF_GOT_SENSE 0x2000 +#define RQSF_XFER_COMPLETE 0x4000 + +/* + * Request Status Flags + */ +#define RQSTF_DISCONNECT 0x0001 +#define RQSTF_SYNCHRONOUS 0x0002 +#define RQSTF_PARITY_ERROR 0x0004 +#define RQSTF_BUS_RESET 0x0008 +#define RQSTF_DEVICE_RESET 0x0010 +#define RQSTF_ABORTED 0x0020 +#define RQSTF_TIMEOUT 0x0040 +#define RQSTF_NEGOTIATION 0x0080 + +/* + * Device Flags (QL_CMD_SET_TARGET_PARAMETERS, QL_CMD_GET_TARGET_PARAMETERS) + */ +#define DPARM_PPR 0x0020 +#define DPARM_ASYNC 0x0040 +#define DPARM_NARROW 0x0080 +#define DPARM_RENEG 0x0100 +#define DPARM_QFRZ 0x0200 +#define DPARM_ARQ 0x0400 +#define DPARM_TQING 0x0800 +#define DPARM_SYNC 0x1000 +#define DPARM_WIDE 0x2000 +#define DPARM_PARITY 0x4000 +#define DPARM_DISC 0x8000 + +/* + * Generic mailbox command completion time. + * On QLA1080 real hardware the loading of firmware image (15675 words) + * via PIO takes approximately 1878 ms. + */ +#define QL_MBOX_GENERIC_TIME_US 110 // 110 us + +/* + * Command completion time when the SCSI device does not exists, + * measured on QLA1080 real hardware. + */ +#define QL_CMD_SELECTION_TIMEOUT_TIME_US 370000 // 370 ms + +#define SXP_FLAG_ENGINE_ACTIVE 0x00000001 +#define SXP_FLAG_PICK_UP_MBOX 0x00000002 +#define SXP_FLAG_FAST_POSTING 0x00000004 +#define SXP_FLAG_MBOX_IOCB 0x00000008 +#define SXP_FLAG_BIOS_IOCB 0x00000010 +#define SXP_FLAG_WRITE_RESP_IOCB 0x00000020 +#define SXP_FLAG_INC_RESP_RING 0x00000040 +#define SXP_FLAG_ABORTED_CMD 0x00000080 + +#define QL_BIT(b) (1 << (b)) + +#define ql_dma_write(address, buffer, size) dma_bm_write(address, (uint8_t *)(buffer), size, 4); +#define ql_dma_write8(address, buffer) dma_bm_write(address, (uint8_t *)(buffer), 1, 4); +#define ql_dma_write16(address, buffer) dma_bm_write(address, (uint8_t *)(buffer), 2, 4); +#define ql_dma_write32(address, buffer) dma_bm_write(address, (uint8_t *)(buffer), 4, 4); + +#define ql_dma_read(address, buffer, size) dma_bm_read(address, (uint8_t *)(buffer), size, 4); +#define ql_dma_read8(address, buffer) dma_bm_read(address, (uint8_t *)(buffer), 1, 4); +#define ql_dma_read16(address, buffer) dma_bm_read(address, (uint8_t *)(buffer), 2, 4); +#define ql_dma_read32(address, buffer) dma_bm_read(address, (uint8_t *)(buffer), 4, 4); + +typedef enum FlashMode { + M_READ_ARRAY, + M_AUTO_SELECT, + M_PROGRAM, + M_BLOCK_ERASE, + M_CHIP_ERASE, + M_MAX, +} FlashMode; + +typedef enum FlashBusCycleState { + CYCLE_INVALID, + CYCLE_CHECK_55, + CYCLE_CHECK_AA, + CYCLE_CHECK_FIRST_CMD, + CYCLE_CHECK_SECOND_CMD, + CYCLE_ENTER_PROGRAM, +} FlashBusCycleState; + +typedef struct flash_block_t { + /* Block index */ + uint32_t number; + /* Starting address */ + uint32_t start_addr; + /* Ending address */ + uint32_t end_addr; + /* Block protection status */ + uint8_t protection_status; +} flash_block_t; + +typedef struct flash_t { + /* Data access cycles */ + int access_cycles; + /* BIOS image (memory array data) */ + uint8_t *array_data; + /* Operation mode */ + FlashMode mode; + /* Command bus cycle */ + uint8_t bus_cycle; + /* Command decoder state */ + uint8_t cmd_cycle; + /* Write operation status register */ + uint8_t status_reg; + /* Manufacturer ID */ + uint8_t manufacturer_id; + /* Model ID */ + uint8_t model_id; + /* Block select address mask for erase operations */ + uint32_t block_select_addr_mask; + /* Address mask for command decode */ + uint32_t cmd_cycle_addr_mask; + /* Physical address of the AAAA coded cycle */ + uint32_t addr_AAAA_phys; + /* Physical address of the 5555 coded cycle */ + uint32_t addr_5555_phys; + /* Bitmap of pending blocks for erase operation */ + uint32_t blocks_to_erase_bitmap; + /* Byte programming time */ + double program_time_us; + /* Block erase time */ + double block_erase_time_us; + /* Chip erase time */ + double chip_erase_time_us; + /* Erase timer */ + pc_timer_t erase_accept_timeout_timer; + /* Command completion timer */ + pc_timer_t cmd_complete_timer; + /* Blocks (sectors) */ + flash_block_t block[AM29_MAX_BLOCKS]; + /* Name of BIOS image file */ + char filename[1024]; +} flash_t; + +typedef struct isp_hdr_t { + uint8_t entry_type; + uint8_t entry_count; + uint8_t seqno; + uint8_t flags; +} isp_hdr_t; + +typedef struct isp_data_seg_t { + uint32_t address; + uint32_t length; +} isp_data_seg_t; + +/* RQSTYPE_RESPONSE HW structure */ +typedef struct isp_req_status_t { + isp_hdr_t hdr; + uint32_t handle; + uint16_t scsi_status; + uint16_t comp_status; + uint16_t state_flags; + uint16_t status_flags; + uint16_t time; + uint16_t sense_length; + uint32_t residual_length; + uint8_t response[8]; + uint8_t sense_data[32]; +} isp_req_status_t; + +typedef enum SxpState { + SXP_STATE_IDLE, + SXP_STATE_BAD_PACKET, + SXP_STATE_SELECT_DEVICE, + SXP_STATE_SELECTION_TIMEOUT, + SXP_STATE_SEND_CDB, + SXP_STATE_COMPLETE_COMMAND, + SXP_STATE_SCHEDULE_MBOX_INTERRUPT, + SXP_STATE_ACQUIRE_MBOX_SEMAPHORE, + SXP_STATE_SCHEDULE_RISC_INTERRUPT, + SXP_STATE_SET_RISC_INTERRUPT, + SXP_STATE_SCHEDULE_TIMER_SILENCE, + SXP_STATE_MBOX_WAIT_TIMER, + SXP_STATE_WAIT_TIMER, +} SxpState; + +/* Common structure for request entries (RQSTYPE_REQUEST, RQSTYPE_REQUEST_A64, RQSTYPE_MARKER) */ +typedef struct ql_sxp_req_t { + isp_hdr_t hdr; + uint32_t handle; + uint8_t lun; + uint8_t bus_target; + uint16_t cdb_length; + uint16_t flags; + uint16_t reserved; + uint16_t timeout; + uint16_t seg_count; + uint8_t cdb[12]; +} ql_sxp_req_t; + +typedef struct ql_fw_target_params { + uint16_t flags; + uint16_t sync_period; +} ql_fw_target_params; + +typedef struct ql_t { + /* Hardware registers */ + uint16_t reg_cdma_cfg; + uint16_t reg_cdma_ctrl; + uint16_t reg_ddma_cfg; + uint16_t reg_ddma_ctrl; + uint16_t reg_risc_psr; + uint16_t reg_risc_pcr; + uint16_t reg_risc_pc; + uint16_t reg_risc_mtr; + uint16_t reg_id_low; + uint16_t reg_id_high; + uint16_t reg_cfg0; + uint16_t reg_cfg1; + uint16_t reg_scsi_diff_pins; + uint16_t reg_gpio_data; + uint16_t reg_gpio_enable; + uint16_t reg_flash_bios_addr; + uint16_t reg_nvram; + uint16_t reg_host_cmd_flags; + uint16_t reg_intr_ctrl; + uint16_t reg_intr_status; + uint16_t reg_semaphore; + uint16_t reg_mbox_in[QL_MBOX_REGS_MAX]; + uint16_t reg_mbox_out[QL_MBOX_REGS_MAX]; + /* SCSI executive processor state */ + SxpState sxp_state; + /* SXP flags */ + uint32_t sxp_flags; + /* Physical address of the request ring */ + uint32_t rqst_ring_base; + /* Request ring size */ + uint16_t rqst_ring_size; + /* Response ring size */ + uint16_t resp_ring_size; + /* Physical address of the response ring */ + uint32_t resp_ring_base; + /* Physical address of the current request IOCB */ + uint32_t pkt_address; + /* Current request IOCB */ + ql_sxp_req_t pkt; + /* Current response IOCB */ + isp_req_status_t pkt_resp; + /* Holds the request queue indexes to abort */ + Fifo8 abort_iocbs_fifo; + /* Current Path ID */ + uint8_t curr_path_id; + /* Current Target ID */ + uint8_t curr_target_id; + /* Number of SCSI buses on this HBA */ + uint8_t max_bus_count; + /* SCSI bus emulation internal index */ + uint8_t scsi_bus; + /* ISP chip type */ + uint8_t isp_type; + /* ISP chip revision */ + uint8_t isp_rev; + /* IRQ emulation internal context */ + uint8_t irq_state; + /* PCI slot emulation internal context */ + uint8_t pci_slot; + /* Bit map of mailbox registers to return to the host */ + uint32_t mbox_out_mask; + /* Pending mailbox response */ + uint16_t mbox_data[QL_MBOX_REGS_MAX]; + /* Transfer speed in bytes per second */ + double xfer_rate_bps; + /* Command timer period in microsecounds */ + double timer_period; + /* Command timer */ + pc_timer_t cmd_timer; + /* Firmware device parameters */ + ql_fw_target_params fw_tid_params[2][QL_MAX_TID]; + /* Firmware retry count and delay settings */ + uint16_t fw_retry_params[4]; + /* Firmware version */ + uint32_t fw_version; + /* Size of the SCSI payload data */ + uint32_t scsi_data_size; + /* Current offset in the SCSI payload data */ + uint32_t scsi_data_offset; + /* Temporary buffer to hold payload data of the SCSI command */ + uint8_t *scsi_data_buffer; + /* PCI MMIO BAR mapping */ + mem_mapping_t mmio_bar_mapping; + /* PCI ROM BAR mapping */ + mem_mapping_t rom_bar_mapping; + /* PCI configuration space */ + uint8_t pci_cfg[256]; + /* Size of the area to map the expansion ROM */ + uint32_t pci_rom_area_size; + /* Writable bits of the ROM BAR */ + uint32_t rom_bar_mask; + /* Supports PCI capabilities */ + bool has_pci_caps; + /* NVRAM device */ + nmc93cxx_eeprom_t *eeprom_device; + /* Flash BIOS device */ + flash_t flash_device; + /* RISC CPU memory */ + uint16_t cpu_mem[0x10000]; +} ql_t; + +extern double cpuclock; + +static bool +ql_sxp_fetch_request(ql_sxp_req_t* pkt, uint32_t address); + +#ifdef ENABLE_QL_LOG +int ql_do_log = ENABLE_QL_LOG; + +static bool ql_fw_load_in_progress = false; +static bool ql_nvram_read_in_progress = false; + +static void +ql_log(const char *fmt, ...) +{ + va_list ap; + + if (ql_fw_load_in_progress || ql_nvram_read_in_progress) { + return; + } + + if (ql_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} + +static void +ql_debug_check_for_nvram_access(uint32_t addr) +{ + /* Suppress log messages during NVRAM access */ + if (!ql_nvram_read_in_progress) { + if (addr == QL_REG_NVRAM) { + ql_log("QL: Disable NVRAM log\n"); + ql_nvram_read_in_progress = true; + } + } else { + if (addr != QL_REG_NVRAM) { + ql_nvram_read_in_progress = false; + ql_log("QL: Enable NVRAM log\n"); + } + } +} + +#define MAKE_CASE(x) case x: return #x; +static char * +ql_debug_cmd_to_name(uint16_t opcode) +{ + switch (opcode) { + MAKE_CASE(QL_CMD_NOP ); + MAKE_CASE(QL_CMD_LOAD_RAM ); + MAKE_CASE(QL_CMD_EXEC_FIRMWARE ); + MAKE_CASE(QL_CMD_DUMP_RAM ); + MAKE_CASE(QL_CMD_WRITE_RAM_WORD ); + MAKE_CASE(QL_CMD_READ_RAM_WORD ); + MAKE_CASE(QL_CMD_REGISTER_TEST ); + MAKE_CASE(QL_CMD_VERIFY_CHECKSUM ); + MAKE_CASE(QL_CMD_ABOUT_FIRMWARE ); + MAKE_CASE(QL_CMD_LOAD_RAM_A64 ); + MAKE_CASE(QL_CMD_DUMP_RAM_A64 ); + MAKE_CASE(QL_CMD_INIT_REQ_QUEUE ); + MAKE_CASE(QL_CMD_INIT_RSP_QUEUE ); + MAKE_CASE(QL_CMD_EXECUTE_IOCB ); + MAKE_CASE(QL_CMD_ABORT_COMMAND ); + MAKE_CASE(QL_CMD_ABORT_DEVICE ); + MAKE_CASE(QL_CMD_ABORT_TARGET ); + MAKE_CASE(QL_CMD_BUS_RESET ); + MAKE_CASE(QL_CMD_START_QUEUE ); + MAKE_CASE(QL_CMD_GET_FIRMWARE_STATUS ); + MAKE_CASE(QL_CMD_GET_RETRY_COUNT ); + MAKE_CASE(QL_CMD_GET_ACT_NEG_STATE ); + MAKE_CASE(QL_CMD_GET_TARGET_PARAMETERS ); + MAKE_CASE(QL_CMD_SET_INITIATOR_ID ); + MAKE_CASE(QL_CMD_SET_SELECTION_TIMEOUT ); + MAKE_CASE(QL_CMD_SET_RETRY_COUNT ); + MAKE_CASE(QL_CMD_SET_TAG_AGE_LIMIT ); + MAKE_CASE(QL_CMD_SET_CLOCK_RATE ); + MAKE_CASE(QL_CMD_SET_ACTIVE_NEGATION ); + MAKE_CASE(QL_CMD_SET_ASYNC_DATA_SETUP ); + MAKE_CASE(QL_CMD_SET_PCI_CONTROL ); + MAKE_CASE(QL_CMD_SET_TARGET_PARAMETERS ); + MAKE_CASE(QL_CMD_SET_DEVICE_QUEUE ); + MAKE_CASE(QL_CMD_RETURN_BIOS_BLOCK_ADDR ); + MAKE_CASE(QL_CMD_WRITE_FOUR_RAM_WORDS ); + MAKE_CASE(QL_CMD_EXEC_BIOS_IOCB ); + MAKE_CASE(QL_CMD_SET_SYSTEM_PARAMETER ); + MAKE_CASE(QL_CMD_SET_FIRMWARE_FEATURES ); + MAKE_CASE(QL_CMD_INIT_REQ_QUEUE_A64 ); + MAKE_CASE(QL_CMD_INIT_RSP_QUEUE_A64 ); + MAKE_CASE(QL_CMD_EXECUTE_IOCB_A64 ); + MAKE_CASE(QL_CMD_GET_TRANSFER_MODE ); + MAKE_CASE(QL_CMD_SET_DATA_OVERRUN_RECOVERY); + default: + return ""; + } +} + +static char * +debug_sxp_state_to_name(SxpState state) +{ + switch (state) { + MAKE_CASE(SXP_STATE_IDLE); + MAKE_CASE(SXP_STATE_BAD_PACKET); + MAKE_CASE(SXP_STATE_SELECT_DEVICE); + MAKE_CASE(SXP_STATE_SELECTION_TIMEOUT); + MAKE_CASE(SXP_STATE_SEND_CDB); + MAKE_CASE(SXP_STATE_COMPLETE_COMMAND); + MAKE_CASE(SXP_STATE_SCHEDULE_MBOX_INTERRUPT); + MAKE_CASE(SXP_STATE_SCHEDULE_RISC_INTERRUPT); + MAKE_CASE(SXP_STATE_SET_RISC_INTERRUPT); + MAKE_CASE(SXP_STATE_ACQUIRE_MBOX_SEMAPHORE); + MAKE_CASE(SXP_STATE_SCHEDULE_TIMER_SILENCE); + MAKE_CASE(SXP_STATE_WAIT_TIMER); + MAKE_CASE(SXP_STATE_MBOX_WAIT_TIMER); + default: + return ""; + } +} +#undef MAKE_CASE +#else +# define ql_log(fmt, ...) +#endif + +static flash_block_t* +am29_address_to_block(flash_t *dev, uint32_t addr) +{ + for (uint32_t i = 0; i < AM29_MAX_BLOCKS; i++) { + flash_block_t *block = &dev->block[i]; + + if ((addr >= block->start_addr) && (addr <= block->end_addr)) + return block; + } + + /* Should not happen */ + assert(false); + return NULL; +} + +static void +am29_reset_cmd_sequence(flash_t *dev) +{ + dev->bus_cycle = 0; + dev->cmd_cycle = 0; +} + +static void +am29_set_mode(flash_t *dev, FlashMode mode) +{ + if (mode != dev->mode) { + ql_log("FLASH: Set mode %u\n", mode); + } + dev->mode = mode; + + am29_reset_cmd_sequence(dev); +} + +static void +am29_process_reset_or_complete_cmd(flash_t *dev) +{ + am29_set_mode(dev, M_READ_ARRAY); + + dev->status_reg = 0; + dev->blocks_to_erase_bitmap = 0; + + /* Terminate the block erase timeout */ + timer_stop(&dev->erase_accept_timeout_timer); +} + +static void +am29_reset(flash_t *dev) +{ + am29_process_reset_or_complete_cmd(dev); +} + +static void +am29_cmd_complete_timer_callback(void *priv) +{ + flash_t *dev = priv; + + ql_log("FLASH: Command completed with status %02X\n", dev->status_reg); + + /* The memory will return to the Read Mode, unless an error has occurred */ + if (dev->status_reg & FLASH_STATUS_ERROR) { + return; + } + am29_process_reset_or_complete_cmd(dev); +} + +static void +am29_erase_blocks(flash_t *dev) +{ + for (uint32_t i = 0; i < AM29_MAX_BLOCKS; i++) { + flash_block_t *block = &dev->block[i]; + void *block_start; + size_t block_length; + + if (!(dev->blocks_to_erase_bitmap & (1 << i))) { + continue; + } + + /* Protected block: The data remains unchanged, no error is given */ + if (block->protection_status == FLASH_BLOCK_STATUS_PROTECTED) { + continue; + } + + ql_log("FLASH: Erase block #%lu %lX-%lX\n", block->number, block->start_addr, block->end_addr); + + block_start = &dev->array_data[block->start_addr]; + block_length = (block->end_addr - block->start_addr) + 1; + + memset(block_start, 0xFF, block_length); + } +} + +static void +am29_erase_begin_timer_callback(void *priv) +{ + flash_t *dev = priv; + + ql_log("FLASH: Begin erase operation\n"); + + /* Finally, erase the blocks (fill with 0xFF) */ + am29_erase_blocks(dev); + + if (dev->mode == M_CHIP_ERASE) { + timer_on_auto(&dev->cmd_complete_timer, dev->chip_erase_time_us); + } else { + dev->status_reg |= FLASH_STATUS_ERASE_TIMEOUT_EXPIRED; + + timer_on_auto(&dev->cmd_complete_timer, dev->block_erase_time_us); + } +} + +static uint8_t +am29_status_register_read(flash_t *dev) +{ + switch (dev->mode) { + case M_PROGRAM: + case M_BLOCK_ERASE: + case M_CHIP_ERASE: { + dev->status_reg ^= FLASH_STATUS_TOGGLE; + break; + } + + default: + break; + } + + return dev->status_reg; +} + +static void +am29_accept_cmd(flash_t *dev, uint32_t addr, uint8_t val) +{ + flash_block_t *block; + + /* Single cycle command (write to any address inside the device) */ + if ((val == FLASH_CMD_READ_ARRAY) && (dev->mode != M_PROGRAM)) { + am29_process_reset_or_complete_cmd(dev); + return; + } + + switch (dev->mode) { + case M_PROGRAM: { + block = am29_address_to_block(dev, addr); + + /* Write to a protected block: The data remains unchanged, no error is given */ + if (block->protection_status == FLASH_BLOCK_STATUS_PROTECTED) { + ql_log("FLASH: Program failure - the block #lu is protected\n", block->number); + goto ProgramDone; + } + + /* The program command cannot change a bit set at '0' back to '1' */ + if (~dev->array_data[addr] & val) { + ql_log("FLASH: Program error - the address %lX " + "was not previously erased %04X <> %04X\n", + addr, + dev->array_data[addr], + val); + + dev->status_reg |= FLASH_STATUS_ERROR; + goto ProgramDone; + } + + /* Data# polling */ + if (!(val & FLASH_STATUS_DATA_POLLING)) { + dev->status_reg |= FLASH_STATUS_DATA_POLLING; + } + + /* Finally, program the value */ + ql_log("FLASH: Program %08lX to %04X\n", addr, val); + dev->array_data[addr] = val; + +ProgramDone: + timer_on_auto(&dev->cmd_complete_timer, dev->program_time_us); + break; + } + + case M_BLOCK_ERASE: { + /* We shouldn't get here if the operation has already started */ + assert(!(dev->status_reg & FLASH_STATUS_ERASE_TIMEOUT_EXPIRED)); + + addr &= dev->block_select_addr_mask; + block = am29_address_to_block(dev, addr); + + ql_log("FLASH: Queued block #%lu %lX-%lX for erase\n", + block->number, + block->start_addr, + block->end_addr); + + /* Add block to the list */ + dev->blocks_to_erase_bitmap |= 1 << block->number; + + /* Wait for a next block to erase (restart the timeout period) */ + timer_stop(&dev->erase_accept_timeout_timer); + timer_on_auto(&dev->erase_accept_timeout_timer, AM29_BLOCK_ERASE_ACCEPT_TIMEOUT_US); + break; + } + + case M_CHIP_ERASE: { + /* Add all blocks to the list */ + dev->blocks_to_erase_bitmap = 0xFFFFFFFF; + + /* Immediately start the erase operation */ + am29_erase_begin_timer_callback(dev); + break; + } + + default: + break; + } +} + +static void +am29_interpret_cmd_sequence(flash_t *dev, uint32_t addr, uint8_t val) +{ + // clang-format off + static const uint8_t cmd_seq_next_state[6][2] = { + // Phase 0 Phase 1 + { CYCLE_CHECK_AA, CYCLE_INVALID }, // Cycle 1 + { CYCLE_CHECK_55, CYCLE_INVALID }, // Cycle 2 + { CYCLE_CHECK_FIRST_CMD, CYCLE_INVALID }, // Cycle 3 + { CYCLE_CHECK_AA, CYCLE_ENTER_PROGRAM }, // Cycle 4 + { CYCLE_CHECK_55, CYCLE_INVALID }, // Cycle 5 + { CYCLE_CHECK_SECOND_CMD, CYCLE_INVALID }, // Cycle 6 + }; + // clang-format on + + addr &= dev->cmd_cycle_addr_mask; + + switch (cmd_seq_next_state[dev->bus_cycle][dev->cmd_cycle]) { + case CYCLE_CHECK_AA: { + if ((val == 0xAA) && (addr == dev->addr_5555_phys)) { + dev->bus_cycle++; + return; + } + break; + } + + case CYCLE_CHECK_55: { + if ((val == 0x55) && (addr == dev->addr_AAAA_phys)) { + dev->bus_cycle++; + return; + } + break; + } + + case CYCLE_CHECK_FIRST_CMD: { + if (addr != dev->addr_5555_phys) { + break; + } + + switch (val) { + case FLASH_CMD_AUTO_SELECT: + am29_set_mode(dev, M_AUTO_SELECT); + return; + + case FLASH_CMD_PROGRAM: + dev->bus_cycle++; + dev->cmd_cycle++; + return; + + case FLASH_CMD_SETUP_ERASE: + dev->bus_cycle++; + return; + + default: + break; + } + break; + } + + case CYCLE_ENTER_PROGRAM: { + am29_set_mode(dev, M_PROGRAM); + return; + } + + case CYCLE_CHECK_SECOND_CMD: { + switch (val) { + case FLASH_CMD_BLOCK_ERASE_CONFIRM: { + am29_set_mode(dev, M_BLOCK_ERASE); + return; + } + + case FLASH_CMD_CHIP_ERASE_CONFIRM: { + if (addr == dev->addr_5555_phys) { + am29_set_mode(dev, M_CHIP_ERASE); + return; + } + break; + } + + default: + break; + } + break; + } + + default: + break; + } + + am29_reset_cmd_sequence(dev); +} + +static void +am29_mmio_write8(uint32_t addr, uint8_t val, flash_t *dev) +{ + cycles -= dev->access_cycles; + + addr &= (AM29_FLASH_SIZE - 1); + + ql_log("FLASH: [%08lX] <-- %02X\n", addr, val); + + switch (dev->mode) { + /* Ignore all commands while the chip is being programmed or erased */ + case M_CHIP_ERASE: + case M_PROGRAM: { + /* A Read/Reset command can be issued to reset the error condition */ + if ((dev->status_reg & FLASH_STATUS_ERROR) && (val == FLASH_CMD_READ_ARRAY)) + break; + return; + } + + /* Ignore all commands during the Block Erase except the Read/Reset command */ + case M_BLOCK_ERASE: { + /* The command has not started yet, we keep accepting blocks to the erase list */ + if (!(dev->status_reg & FLASH_STATUS_ERASE_TIMEOUT_EXPIRED)) + break; + + if (val == FLASH_CMD_READ_ARRAY) + break; + return; + } + + default: + break; + } + + /* Receive the command sequence */ + if ((dev->mode == M_READ_ARRAY) || (dev->mode == M_AUTO_SELECT)) { + am29_interpret_cmd_sequence(dev, addr, val); + } + + /* Begin the operation */ + am29_accept_cmd(dev, addr, val); +} + +static uint8_t +am29_mmio_read8(uint32_t addr, flash_t *dev) +{ + uint8_t ret; + + cycles -= dev->access_cycles; + + addr &= (AM29_FLASH_SIZE - 1); + + switch (dev->mode) { + case M_READ_ARRAY: { + /* Read array data */ + ret = dev->array_data[addr]; + break; + } + + case M_AUTO_SELECT: { + switch (addr & AM29_AUTOSEL_ADDR_MASK) { + case AM29_AUTOSEL_ADDR_MANUFACTURER_ID: + ret = dev->manufacturer_id; + break; + + case AM29_AUTOSEL_ADDR_MODEL_ID: + ret = dev->model_id; + break; + + default: { + flash_block_t *block = am29_address_to_block(dev, addr); + + /* Read the block protection status */ + ret = block->protection_status; + break; + } + } + break; + } + + default: { + /* Return the status register during Program and Erase operations */ + ret = am29_status_register_read(dev); + break; + } + } + + if (dev->mode != M_READ_ARRAY) { + ql_log("FLASH: [%08lX] --> %02X\n", addr, ret); + } + return ret; +} + +static uint8_t +ql_rom_bar_mmio_read8(uint32_t addr, void* priv) +{ + ql_t *dev = priv; + + return am29_mmio_read8(addr, &dev->flash_device); +} + +static void +ql_update_irq(ql_t *dev) +{ + uint16_t isr = dev->reg_intr_status & dev->reg_intr_ctrl; + + /* NOTE: Unmasking QL_INTR_REQ does nothing on QLA1080 real hardware */ + isr &= QL_RISC_INTR_REQ; + + if (isr != 0) + pci_set_irq(dev->pci_slot, PCI_INTA, &dev->irq_state); + else + pci_clear_irq(dev->pci_slot, PCI_INTA, &dev->irq_state); +} + +static void +ql_reset_asic(ql_t *dev) +{ + ql_log("QL: Reset ASIC\n"); + + dev->reg_id_low = 0x1077; + dev->reg_id_high = 0x1240; + dev->reg_cfg0 = dev->isp_rev; + dev->reg_cfg1 = BIU_BURST_ENABLE | BIU_PCI_CONF1_FIFO_128; + dev->reg_intr_ctrl = 0; + dev->reg_intr_status = 0; + dev->reg_semaphore = 0; + dev->reg_nvram = 0; + dev->reg_flash_bios_addr = 0; + dev->reg_mbox_out[0] = 0; + dev->reg_mbox_out[1] = 0x4953; // 'IS' + dev->reg_mbox_out[2] = 0x5020; // 'P ' + dev->reg_mbox_out[3] = 0x2020; // ' ' + dev->reg_mbox_out[4] = 1; + dev->reg_mbox_out[5] = 7; + dev->reg_mbox_out[6] = 0; + dev->reg_mbox_out[7] = 1; + dev->reg_host_cmd_flags = 0; + dev->reg_gpio_data = 0x000F; + dev->reg_gpio_enable = 0; + dev->reg_scsi_diff_pins = SXP_PINS_SE_MODE; + dev->reg_cdma_cfg = 0x0001; + dev->reg_cdma_ctrl = 0; + dev->reg_ddma_cfg = 0x0008; + dev->reg_ddma_ctrl = 0; + dev->reg_risc_psr = 0x2000; + dev->reg_risc_pcr = 0; + dev->reg_risc_pc = 0; + dev->reg_risc_mtr = 0x0001; + + memset(dev->cpu_mem, 0, sizeof(dev->cpu_mem)); + /* FastUtil BIOS checks area 0xFF03-0xFFF3 if the word at 0xFF03 is invalid */ + dev->cpu_mem[0xFF03] = 0xFBFF; + + timer_stop(&dev->cmd_timer); + dev->rqst_ring_base = 0; + dev->resp_ring_base = 0; + dev->sxp_flags = 0; + dev->sxp_state = SXP_STATE_IDLE; + + ql_update_irq(dev); +} + + +static bool +ql_sxp_abort_commands(ql_t *dev, uint8_t path_id, uint8_t target_id, uint8_t lun, uint32_t handle, bool is_handle_valid) +{ + bool success = false; + uint16_t cons; + + if (!(dev->sxp_flags & SXP_FLAG_ENGINE_ACTIVE)) { + return false; + } + + /* Iterate over the request IOCBs looking for a match */ + for (cons = QL_RQST_CONS(dev); cons != QL_RQST_PROD(dev); cons = (cons + 1) % dev->rqst_ring_size) { + uint32_t pkt_address = dev->rqst_ring_base + cons * QENTRY_LEN; + ql_sxp_req_t pkt; + uint8_t curr_path_id, curr_target_id; + + if (!ql_sxp_fetch_request(&pkt, pkt_address)) { + continue; + } + + curr_path_id = (pkt.bus_target & 0x80) >> 7; + curr_target_id = pkt.bus_target & ~0x80; + + /* Exact match? */ + if ((curr_path_id != path_id) || (curr_target_id != target_id)) { + continue; + } + if ((lun != 0xFF) && (pkt.lun != lun)) { + continue; + } + if (is_handle_valid && (pkt.handle != handle)) { + continue; + } + + /* Save the queue index, it will be checked before the next command is processed */ + fifo8_push(&dev->abort_iocbs_fifo, cons & 0xFF); + fifo8_push(&dev->abort_iocbs_fifo, (cons >> 8) & 0xFF); + + success = true; + } + + return success; +} + +static void +ql_sxp_initialize_queues(ql_t *dev) +{ + if ((dev->rqst_ring_base != 0) && (dev->resp_ring_base != 0)) { + /* Each queue index consists of 2 bytes (size of the mailbox register) */ + uint32_t fifo_size = dev->rqst_ring_size * sizeof(uint16_t); + + fifo8_destroy(&dev->abort_iocbs_fifo); + fifo8_create(&dev->abort_iocbs_fifo, fifo_size); + + dev->sxp_flags |= SXP_FLAG_ENGINE_ACTIVE; + } else { + dev->sxp_flags &= ~SXP_FLAG_ENGINE_ACTIVE; + } +} + +static uint16_t +ql_handle_cmd_nop(ql_t *dev) +{ + /* Nothing to do */ + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_write_ram_word(ql_t *dev) +{ +#ifdef ENABLE_QL_LOG + /* Suppress log messages during loading of firmware */ + if (!ql_fw_load_in_progress) { + if (dev->reg_mbox_in[1] == 0x1003) { + ql_log("QL: Disable log before FW load\n"); + ql_fw_load_in_progress = true; + } + } else { + if ((dev->reg_mbox_in[1] - 0x1000) == (dev->cpu_mem[0x1003] - 2)) { + ql_fw_load_in_progress = false; + ql_log("QL: Enable log after FW load\n"); + } + } +#endif + + dev->cpu_mem[dev->reg_mbox_in[1]] = dev->reg_mbox_in[2]; + + dev->mbox_out_mask |= QL_BIT(2); + dev->mbox_data[2] = dev->reg_mbox_in[2]; + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_write_four_ram_words(ql_t *dev) +{ + uint16_t address = dev->reg_mbox_in[1]; + + if (dev->isp_type != QL_ISP1040) { + return QL_MBOX_STATUS_INVALID; + } + + if (address >= (ARRAY_SIZE(dev->cpu_mem) - 4)) { + return QL_MBOX_STATUS_CMD_PARAM_ERROR; + } + + dev->cpu_mem[address + 0] = dev->reg_mbox_in[2]; + dev->cpu_mem[address + 1] = dev->reg_mbox_in[3]; + dev->cpu_mem[address + 2] = dev->reg_mbox_in[4]; + dev->cpu_mem[address + 3] = dev->reg_mbox_in[5]; + + if (address == (QL_IOCB_FW_BASE + 4)) { + /* Save the SCSI device address for later use */ + dev->cpu_mem[QL_IOCB_FW_BASE + 48] = dev->cpu_mem[address]; + + /* Return some status data */ + dev->cpu_mem[address] = 0x0300; + } + + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_read_ram_word(ql_t *dev) +{ + dev->mbox_out_mask |= QL_BIT(2) | QL_BIT(3); + dev->mbox_data[2] = dev->cpu_mem[dev->reg_mbox_in[1]]; + dev->mbox_data[3] = 0; + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_load_ram_block(ql_t *dev, UNUSED(bool is_64bit_addr)) +{ + uint32_t offset = dev->reg_mbox_in[1]; + uint32_t block_size_words = dev->reg_mbox_in[4]; + uint8_t *dest_address = (uint8_t *)&dev->cpu_mem[offset]; + uint32_t src_address = ((uint32_t)dev->reg_mbox_in[2] << 16) | dev->reg_mbox_in[3]; + + if ((offset + block_size_words) > ARRAY_SIZE(dev->cpu_mem)) { + return QL_MBOX_STATUS_CMD_PARAM_ERROR; + } + + ql_dma_read(src_address, dest_address, block_size_words * sizeof(uint16_t)); + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_dump_ram_block(ql_t *dev, UNUSED(bool is_64bit_addr)) +{ + uint32_t offset = dev->reg_mbox_in[1]; + uint32_t block_size_words = dev->reg_mbox_in[4]; + uint8_t *src_address = (uint8_t *)&dev->cpu_mem[offset]; + uint32_t dest_address = ((uint32_t)dev->reg_mbox_in[2] << 16) | dev->reg_mbox_in[3]; + + if ((offset + block_size_words) > ARRAY_SIZE(dev->cpu_mem)) { + return QL_MBOX_STATUS_CMD_PARAM_ERROR; + } + + ql_dma_write(dest_address, src_address, block_size_words * sizeof(uint16_t)); + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_register_test(ql_t *dev) +{ + for (uint32_t i = 1; i < ARRAY_SIZE(dev->reg_mbox_in); i++) { + dev->mbox_out_mask |= QL_BIT(i); + dev->mbox_data[i] = dev->reg_mbox_in[i]; + } + + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_verify_checksum(ql_t *dev) +{ + uint32_t start_addr = dev->reg_mbox_in[1]; + uint16_t fw_size_words; + uint32_t end_addr; + uint16_t crc; + + if (start_addr >= (ARRAY_SIZE(dev->cpu_mem) - 3)) { + return QL_MBOX_STATUS_CMD_PARAM_ERROR; + } + + fw_size_words = dev->cpu_mem[start_addr + 3]; + + end_addr = start_addr + fw_size_words; + if (end_addr > ARRAY_SIZE(dev->cpu_mem)) { + return QL_MBOX_STATUS_CMD_PARAM_ERROR; + } + + crc = 0; + for (uint32_t i = start_addr; i < end_addr; i++) { + crc += dev->cpu_mem[i]; + } + dev->mbox_out_mask |= QL_BIT(1); + dev->mbox_data[1] = crc; + + if (crc != 0) { + ql_log("QL: Firmware crc 0x%X\n", crc); + return QL_MBOX_STATUS_TEST_FAILED; + } + + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_exec_firmware(ql_t *dev) +{ + dev->mbox_out_mask |= QL_BIT(1) | QL_BIT(2) | QL_BIT(3) | QL_BIT(4) | QL_BIT(5); + dev->mbox_data[1] = 0x4953; // 'IS' + dev->mbox_data[2] = 0x5020; // 'P ' + dev->mbox_data[3] = 0x2020; // ' ' + dev->mbox_data[4] = 8; + dev->mbox_data[5] = 0x04FE; + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_about_firmware(ql_t *dev) +{ + dev->mbox_out_mask |= QL_BIT(1) | QL_BIT(2) | QL_BIT(3); + dev->mbox_data[1] = (dev->fw_version >> 16) & 0xFF; // Major revision + dev->mbox_data[2] = (dev->fw_version >> 8) & 0xFF; // Minor revision + dev->mbox_data[3] = (dev->fw_version >> 0) & 0xFF; // Micro revision + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_init_request_queue(ql_t *dev, UNUSED(bool is_64bit_addr)) +{ + uint32_t address = ((uint32_t)dev->reg_mbox_in[2] << 16) | dev->reg_mbox_in[3]; + uint16_t queue_length = dev->reg_mbox_in[1]; + + ql_log("QL: REQ queue address 0x%X, length %u\n", address, queue_length); + + if ((address == 0) || (queue_length == 0)) { + return QL_MBOX_STATUS_CMD_PARAM_ERROR; + } + + dev->rqst_ring_base = address; + dev->rqst_ring_size = queue_length; + ql_sxp_initialize_queues(dev); + + dev->mbox_out_mask |= 1 << QL_MBOX_RQST; + dev->mbox_data[QL_MBOX_RQST] = dev->reg_mbox_in[QL_MBOX_RQST]; + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_init_response_queue(ql_t *dev, UNUSED(bool is_64bit_addr)) +{ + uint32_t address = ((uint32_t)dev->reg_mbox_in[2] << 16) | dev->reg_mbox_in[3]; + uint16_t queue_length = dev->reg_mbox_in[1]; + + ql_log("QL: RSP queue address 0x%X, length %u\n", address, queue_length); + + if ((address == 0) || (queue_length == 0)) { + return QL_MBOX_STATUS_CMD_PARAM_ERROR; + } + + dev->resp_ring_base = address; + dev->resp_ring_size = queue_length; + ql_sxp_initialize_queues(dev); + + dev->mbox_out_mask |= 1 << QL_MBOX_RESP; + dev->mbox_data[QL_MBOX_RESP] = dev->reg_mbox_in[QL_MBOX_RESP]; + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_abort_command(ql_t *dev) +{ + uint8_t path_id = dev->reg_mbox_in[1] >> 15; + uint8_t target_id = (dev->reg_mbox_in[1] >> 8) & ~0x80; + uint8_t lun = dev->reg_mbox_in[1] & 0xFF; + uint32_t handle; + bool success; + + ql_log("QL: [%u:%u] Abort command\n", path_id, target_id); + + if (path_id >= dev->max_bus_count) { + return QL_MBOX_STATUS_CMD_PARAM_ERROR; + } + + if (target_id >= QL_MAX_TID) { + return QL_MBOX_STATUS_CMD_PARAM_ERROR; + } + + /* Abort an active command on the device (PATH:TID:LUN) that match the handle */ + handle = dev->reg_mbox_in[QL_MBOX_HNDL_LOW]; + handle |= (uint32_t)dev->reg_mbox_in[QL_MBOX_HNDL_HIGH] << 16; + success = ql_sxp_abort_commands(dev, path_id, target_id, lun, handle, true); + + dev->mbox_out_mask |= QL_BIT(1) | QL_BIT(2) | QL_BIT(3); + dev->mbox_data[1] = dev->reg_mbox_in[1]; + dev->mbox_data[2] = dev->reg_mbox_in[2]; + dev->mbox_data[3] = dev->reg_mbox_in[3]; + + if (!success) { + return QL_MBOX_STATUS_CMD_ERROR; + } + + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_abort_device(ql_t *dev) +{ + uint8_t path_id = dev->reg_mbox_in[1] >> 15; + uint8_t target_id = (dev->reg_mbox_in[1] >> 8) & ~0x80; + uint8_t lun = dev->reg_mbox_in[1] & 0xFF; + + ql_log("QL: [%u:%u:%u] Abort device\n", path_id, target_id, lun); + + if (path_id >= dev->max_bus_count) { + return QL_MBOX_STATUS_CMD_PARAM_ERROR; + } + + if (target_id >= QL_MAX_TID) { + return QL_MBOX_STATUS_CMD_PARAM_ERROR; + } + + /* Abort all active commands on the device (PATH:TID:LUN) */ + (void) ql_sxp_abort_commands(dev, path_id, target_id, lun, 0, false); + + dev->mbox_out_mask |= QL_BIT(1) | QL_BIT(2) | QL_BIT(3); + dev->mbox_data[1] = dev->reg_mbox_in[1]; + dev->mbox_data[2] = dev->reg_mbox_in[2]; + dev->mbox_data[3] = dev->reg_mbox_in[3]; + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_abort_target(ql_t *dev) +{ + uint8_t path_id = dev->reg_mbox_in[1] >> 15; + uint8_t target_id = (dev->reg_mbox_in[1] >> 8) & ~0x80; + + ql_log("QL: [%u:%u] Abort target\n", path_id, target_id); + + if (path_id >= dev->max_bus_count) { + return QL_MBOX_STATUS_CMD_PARAM_ERROR; + } + + if (target_id >= QL_MAX_TID) { + return QL_MBOX_STATUS_CMD_PARAM_ERROR; + } + + /* Abort all active commands on the target (PATH:TID) */ + (void) ql_sxp_abort_commands(dev, path_id, target_id, 0xFF, 0, false); + + dev->mbox_out_mask |= QL_BIT(1) | QL_BIT(2) | QL_BIT(3); + dev->mbox_data[1] = dev->reg_mbox_in[1]; + dev->mbox_data[2] = dev->reg_mbox_in[2]; + dev->mbox_data[3] = dev->reg_mbox_in[3]; + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_bus_reset(ql_t *dev) +{ + uint8_t path_id; + + if (dev->isp_type == QL_ISP1040) { + path_id = 0; + } else { + path_id = dev->reg_mbox_in[2]; + } + + ql_log("QL: Reset bus %u, delay %u\n", path_id, dev->reg_mbox_in[1]); + + if (path_id >= dev->max_bus_count) { + return QL_MBOX_STATUS_CMD_PARAM_ERROR; + } + + /* 86Box supports one SCSI bus per controller for now */ + if (path_id == 0) { + for (uint32_t i = 0; i < MIN(QL_MAX_TID, SCSI_ID_MAX); i++) { + scsi_device_reset(&scsi_devices[dev->scsi_bus][i]); + } + } + + dev->mbox_out_mask |= QL_BIT(1) | QL_BIT(2) | QL_BIT(3); + dev->mbox_data[1] = dev->reg_mbox_in[1]; + dev->mbox_data[2] = dev->reg_mbox_in[2]; + dev->mbox_data[3] = 0; + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_get_firmware_status(ql_t *dev) +{ + dev->mbox_out_mask |= QL_BIT(1) | QL_BIT(2) | QL_BIT(3); + dev->mbox_data[1] = 0; + dev->mbox_data[2] = 527; // Max queue depth + dev->mbox_data[3] = 0; + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_set_initialor_id(ql_t *dev) +{ + ql_log("QL: Initialor ID 0x%X\n", dev->reg_mbox_in[1]); + + dev->mbox_out_mask |= QL_BIT(1) | QL_BIT(2) | QL_BIT(3); + dev->mbox_data[1] = dev->reg_mbox_in[1]; + dev->mbox_data[2] = 0; + dev->mbox_data[3] = 0; + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_set_selection_timeout(ql_t *dev) +{ + ql_log("QL: Selection timeout #1 %u\n", dev->reg_mbox_in[1]); + ql_log("QL: Selection timeout #2 %u\n", dev->reg_mbox_in[2]); + + dev->mbox_out_mask |= QL_BIT(1) | QL_BIT(2) | QL_BIT(3); + dev->mbox_data[1] = dev->reg_mbox_in[1]; + dev->mbox_data[2] = dev->reg_mbox_in[2]; + dev->mbox_data[3] = 0; + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_set_retry_count(ql_t *dev) +{ + ql_log("QL: Retry count #1 %u\n", dev->reg_mbox_in[1]); + ql_log("QL: Retry count #2 %u\n", dev->reg_mbox_in[2]); + ql_log("QL: Retry delay #1 %u\n", dev->reg_mbox_in[6]); + ql_log("QL: Retry delay #2 %u\n", dev->reg_mbox_in[7]); + + dev->fw_retry_params[0] = dev->reg_mbox_in[1]; + dev->fw_retry_params[1] = dev->reg_mbox_in[2]; + dev->fw_retry_params[2] = dev->reg_mbox_in[6]; + dev->fw_retry_params[3] = dev->reg_mbox_in[7]; + + dev->mbox_out_mask |= QL_BIT(1) | QL_BIT(2) | QL_BIT(3); + dev->mbox_data[1] = 8; + dev->mbox_data[2] = 5; + dev->mbox_data[3] = 0; + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_get_retry_count(ql_t *dev) +{ + dev->mbox_out_mask |= QL_BIT(1) | QL_BIT(2) | QL_BIT(3); + dev->mbox_data[1] = dev->fw_retry_params[0]; + dev->mbox_data[2] = dev->fw_retry_params[1]; + dev->mbox_data[3] = 0; + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_get_act_neg_state(ql_t *dev) +{ + dev->mbox_out_mask |= QL_BIT(1) | QL_BIT(2) | QL_BIT(3); + dev->mbox_data[1] = 0; + dev->mbox_data[2] = 0; + dev->mbox_data[3] = 0; + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_set_tag_age_limit(ql_t *dev) +{ + ql_log("QL: Tag age limit #1 %u\n", dev->reg_mbox_in[1]); + ql_log("QL: Tag age limit #2 %u\n", dev->reg_mbox_in[2]); + + dev->mbox_out_mask |= QL_BIT(1) | QL_BIT(2) | QL_BIT(3); + dev->mbox_data[1] = dev->reg_mbox_in[1]; + dev->mbox_data[2] = 0; + dev->mbox_data[3] = 0; + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_set_clock_rate(ql_t *dev) +{ + ql_log("QL: Clock rate %u\n", dev->reg_mbox_in[1]); + + dev->mbox_out_mask |= QL_BIT(1) | QL_BIT(2) | QL_BIT(3); + dev->mbox_data[1] = dev->reg_mbox_in[1]; + dev->mbox_data[2] = 0; + dev->mbox_data[3] = 0; + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_set_active_negation(ql_t *dev) +{ + ql_log("QL: Acq active negation 0x%X\n", dev->reg_mbox_in[1]); + ql_log("QL: Data line active negation 0x%X\n", dev->reg_mbox_in[2]); + + dev->mbox_out_mask |= QL_BIT(1) | QL_BIT(2) | QL_BIT(3); + dev->mbox_data[1] = dev->reg_mbox_in[1]; + dev->mbox_data[2] = 0; + dev->mbox_data[3] = 0; + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_set_async_data_setup(ql_t *dev) +{ + ql_log("QL: Async data setup %u %u\n", dev->reg_mbox_in[1], dev->reg_mbox_in[2]); + + dev->mbox_out_mask |= QL_BIT(1) | QL_BIT(2) | QL_BIT(3); + dev->mbox_data[1] = dev->reg_mbox_in[1]; + dev->mbox_data[2] = 0; + dev->mbox_data[3] = 0; + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_set_pci_control(ql_t *dev) +{ + ql_log("QL: PCI control %x %x\n", dev->reg_mbox_in[1], dev->reg_mbox_in[2]); + + dev->mbox_out_mask |= QL_BIT(1) | QL_BIT(2) | QL_BIT(3); + dev->mbox_data[1] = dev->reg_mbox_in[1]; + dev->mbox_data[2] = dev->reg_mbox_in[2]; + dev->mbox_data[3] = 0; + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_set_target_parameters(ql_t *dev) +{ + uint8_t path_id = dev->reg_mbox_in[1] >> 15; + uint8_t target_id = (dev->reg_mbox_in[1] >> 8) & ~0x80; + + ql_log("QL: [%u:%u] Set params 0x%X 0x%X\n", path_id, target_id, dev->reg_mbox_in[2], dev->reg_mbox_in[3]); + + if (path_id >= dev->max_bus_count) { + return QL_MBOX_STATUS_CMD_PARAM_ERROR; + } + + if (target_id >= QL_MAX_TID) { + return QL_MBOX_STATUS_CMD_PARAM_ERROR; + } + + dev->fw_tid_params[path_id][target_id].flags = dev->reg_mbox_in[2]; + dev->fw_tid_params[path_id][target_id].sync_period = dev->reg_mbox_in[3]; + + dev->mbox_out_mask |= QL_BIT(1) | QL_BIT(2) | QL_BIT(3); + dev->mbox_data[1] = dev->reg_mbox_in[1]; + dev->mbox_data[2] = dev->reg_mbox_in[2]; + dev->mbox_data[3] = dev->reg_mbox_in[3]; + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_get_target_parameters(ql_t *dev) +{ + uint8_t path_id = dev->reg_mbox_in[1] >> 15; + uint8_t target_id = (dev->reg_mbox_in[1] >> 8) & ~0x80; + + ql_log("QL: [%u:%u] Return params\n", path_id, target_id); + + if (path_id >= dev->max_bus_count) { + return QL_MBOX_STATUS_CMD_PARAM_ERROR; + } + + if (target_id >= QL_MAX_TID) { + return QL_MBOX_STATUS_CMD_PARAM_ERROR; + } + + dev->mbox_out_mask |= QL_BIT(1) | QL_BIT(2) | QL_BIT(3) | QL_BIT(6); + dev->mbox_data[1] = dev->reg_mbox_in[1]; + dev->mbox_data[2] = dev->fw_tid_params[path_id][target_id].flags; + dev->mbox_data[3] = dev->fw_tid_params[path_id][target_id].sync_period; + dev->mbox_data[6] = 0; + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_set_device_queue(ql_t *dev) +{ + ql_log("QL: Queue params 0x%X %u %u\n", dev->reg_mbox_in[1], dev->reg_mbox_in[2], dev->reg_mbox_in[3]); + + dev->mbox_out_mask |= QL_BIT(1) | QL_BIT(2) | QL_BIT(3); + dev->mbox_data[1] = dev->reg_mbox_in[1]; + dev->mbox_data[2] = dev->reg_mbox_in[2]; + dev->mbox_data[3] = 0x0064; + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_return_bios_block_addr(ql_t *dev) +{ + if (dev->isp_type != QL_ISP1040) { + return QL_MBOX_STATUS_INVALID; + } + + dev->mbox_out_mask |= QL_BIT(1); + dev->mbox_data[1] = QL_IOCB_FW_BASE; + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_set_system_parameter(ql_t *dev) +{ + ql_log("QL: System parameter 0x%X\n", dev->reg_mbox_in[1]); + + dev->mbox_out_mask |= QL_BIT(1) | QL_BIT(2) | QL_BIT(3); + dev->mbox_data[1] = 2; + dev->mbox_data[2] = 0; + dev->mbox_data[3] = 0; + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_set_firmware_features(ql_t *dev) +{ + ql_log("QL: FW features 0x%X\n", dev->reg_mbox_in[1]); + + if (dev->reg_mbox_in[1] & FW_FEATURE_FAST_POST) { + dev->sxp_flags |= SXP_FLAG_FAST_POSTING; + } else { + dev->sxp_flags &= ~SXP_FLAG_FAST_POSTING; + } + + dev->mbox_out_mask |= QL_BIT(1) | QL_BIT(2) | QL_BIT(3); + dev->mbox_data[1] = 0; + dev->mbox_data[2] = 0; + dev->mbox_data[3] = 0; + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_get_transfer_mode(ql_t *dev) +{ + uint8_t path_id = dev->reg_mbox_in[1] >> 15; + uint8_t target_id = (dev->reg_mbox_in[1] >> 8) & ~0x80; + + ql_log("QL: [%u:%u] Return xfer mode\n", path_id, target_id); + + if (path_id >= dev->max_bus_count) { + return QL_MBOX_STATUS_CMD_PARAM_ERROR; + } + + if (target_id >= QL_MAX_TID) { + return QL_MBOX_STATUS_CMD_PARAM_ERROR; + } + + dev->mbox_out_mask |= QL_BIT(1) | QL_BIT(2) | QL_BIT(3) | QL_BIT(6); + dev->mbox_data[1] = dev->reg_mbox_in[1]; + dev->mbox_data[2] = 0; + dev->mbox_data[3] = 0x000C; + dev->mbox_data[6] = 0xFD10; + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_cmd_set_data_overrun_recovery(ql_t *dev) +{ + ql_log("QL: Data overrun recovery 0x%X\n", dev->reg_mbox_in[1]); + + dev->mbox_out_mask |= QL_BIT(1) | QL_BIT(2) | QL_BIT(3); + dev->mbox_data[1] = 0; + dev->mbox_data[2] = 0; + dev->mbox_data[3] = 0; + return QL_MBOX_STATUS_COMPLETE; +} + +static uint16_t +ql_handle_exec_bios_iocb(ql_t *dev) +{ + if (dev->isp_type != QL_ISP1040) { + return QL_MBOX_STATUS_INVALID; + } + + dev->sxp_flags |= SXP_FLAG_BIOS_IOCB; + return QL_MBOX_STATUS_PENDING; +} + +static uint16_t +ql_handle_exec_mbox_iocb(ql_t *dev, bool is_64bit_addr) +{ + if (is_64bit_addr && (dev->reg_mbox_in[1] != QENTRY_LEN)) { + return QL_MBOX_STATUS_CMD_ERROR; + } + + dev->sxp_flags |= SXP_FLAG_MBOX_IOCB; + return QL_MBOX_STATUS_PENDING; +} + +static uint16_t +ql_process_mailbox(ql_t *dev) +{ + uint16_t status; + + ql_log("QL: Command %02X %s\n", dev->reg_mbox_in[0], ql_debug_cmd_to_name(dev->reg_mbox_in[0])); + + switch (dev->reg_mbox_in[0]) { + case QL_CMD_NOP: + status = ql_handle_cmd_nop(dev); + break; + case QL_CMD_LOAD_RAM: + status = ql_handle_cmd_load_ram_block(dev, false); + break; + case QL_CMD_EXEC_FIRMWARE: + status = ql_handle_cmd_exec_firmware(dev); + break; + case QL_CMD_DUMP_RAM: + status = ql_handle_cmd_dump_ram_block(dev, false); + break; + case QL_CMD_WRITE_RAM_WORD: + status = ql_handle_cmd_write_ram_word(dev); + break; + case QL_CMD_READ_RAM_WORD: + status = ql_handle_cmd_read_ram_word(dev); + break; + case QL_CMD_REGISTER_TEST: + status = ql_handle_cmd_register_test(dev); + break; + case QL_CMD_VERIFY_CHECKSUM: + status = ql_handle_cmd_verify_checksum(dev); + break; + case QL_CMD_ABOUT_FIRMWARE: + status = ql_handle_cmd_about_firmware(dev); + break; + case QL_CMD_LOAD_RAM_A64: + status = ql_handle_cmd_load_ram_block(dev, true); + break; + case QL_CMD_DUMP_RAM_A64: + status = ql_handle_cmd_dump_ram_block(dev, true); + break; + case QL_CMD_INIT_REQ_QUEUE: + status = ql_handle_cmd_init_request_queue(dev, false); + break; + case QL_CMD_INIT_RSP_QUEUE: + status = ql_handle_cmd_init_response_queue(dev, false); + break; + case QL_CMD_EXECUTE_IOCB: + status = ql_handle_exec_mbox_iocb(dev, false); + break; + case QL_CMD_ABORT_COMMAND: + status = ql_handle_cmd_abort_command(dev); + break; + case QL_CMD_ABORT_DEVICE: + status = ql_handle_cmd_abort_device(dev); + break; + case QL_CMD_ABORT_TARGET: + status = ql_handle_cmd_abort_target(dev); + break; + case QL_CMD_BUS_RESET: + status = ql_handle_cmd_bus_reset(dev); + break; + case QL_CMD_START_QUEUE: + status = ql_handle_cmd_nop(dev); + break; + case QL_CMD_GET_RETRY_COUNT: + status = ql_handle_cmd_get_retry_count(dev); + break; + case QL_CMD_GET_ACT_NEG_STATE: + status = ql_handle_cmd_get_act_neg_state(dev); + break; + case QL_CMD_GET_TARGET_PARAMETERS: + status = ql_handle_cmd_get_target_parameters(dev); + break; + case QL_CMD_GET_FIRMWARE_STATUS: + status = ql_handle_cmd_get_firmware_status(dev); + break; + case QL_CMD_SET_INITIATOR_ID: + status = ql_handle_cmd_set_initialor_id(dev); + break; + case QL_CMD_SET_SELECTION_TIMEOUT: + status = ql_handle_cmd_set_selection_timeout(dev); + break; + case QL_CMD_SET_RETRY_COUNT: + status = ql_handle_cmd_set_retry_count(dev); + break; + case QL_CMD_SET_TAG_AGE_LIMIT: + status = ql_handle_cmd_set_tag_age_limit(dev); + break; + case QL_CMD_SET_CLOCK_RATE: + status = ql_handle_cmd_set_clock_rate(dev); + break; + case QL_CMD_SET_ACTIVE_NEGATION: + status = ql_handle_cmd_set_active_negation(dev); + break; + case QL_CMD_SET_ASYNC_DATA_SETUP: + status = ql_handle_cmd_set_async_data_setup(dev); + break; + case QL_CMD_SET_PCI_CONTROL: + status = ql_handle_cmd_set_pci_control(dev); + break; + case QL_CMD_SET_TARGET_PARAMETERS: + status = ql_handle_cmd_set_target_parameters(dev); + break; + case QL_CMD_SET_DEVICE_QUEUE: + status = ql_handle_cmd_set_device_queue(dev); + break; + case QL_CMD_RETURN_BIOS_BLOCK_ADDR: + status = ql_handle_cmd_return_bios_block_addr(dev); + break; + case QL_CMD_WRITE_FOUR_RAM_WORDS: + status = ql_handle_cmd_write_four_ram_words(dev); + break; + case QL_CMD_EXEC_BIOS_IOCB: + status = ql_handle_exec_bios_iocb(dev); + break; + case QL_CMD_SET_SYSTEM_PARAMETER: + status = ql_handle_cmd_set_system_parameter(dev); + break; + case QL_CMD_SET_FIRMWARE_FEATURES: + status = ql_handle_cmd_set_firmware_features(dev); + break; + case QL_CMD_INIT_REQ_QUEUE_A64: + status = ql_handle_cmd_init_request_queue(dev, true); + break; + case QL_CMD_INIT_RSP_QUEUE_A64: + status = ql_handle_cmd_init_response_queue(dev, true); + break; + case QL_CMD_EXECUTE_IOCB_A64: + status = ql_handle_exec_mbox_iocb(dev, true); + break; + case QL_CMD_GET_TRANSFER_MODE: + status = ql_handle_cmd_get_transfer_mode(dev); + break; + case QL_CMD_SET_DATA_OVERRUN_RECOVERY: + status = ql_handle_cmd_set_data_overrun_recovery(dev); + break; + + default: + ql_log("Unhandled or invalid command %02X\n", dev->reg_mbox_in[0]); + status = QL_MBOX_STATUS_INVALID; + break; + } + + return status; +} + +/* RQSTYPE_REQUEST */ +static void +ql_pkt_get_sgl_req(uint32_t address, uint32_t idx, isp_data_seg_t *data_seg) +{ + uint32_t offset = address + 32 + idx * 8; + + ql_dma_read32(offset + 0, &data_seg->address); + ql_dma_read32(offset + 4, &data_seg->length); +} + +/* RQSTYPE_REQUEST_A64 */ +static void +ql_pkt_get_sgl_req64(uint32_t address, uint32_t idx, isp_data_seg_t *data_seg) +{ + uint32_t offset = address + 36 + idx * 12; + + /* The high part of the address is ignored for 86Box */ + ql_dma_read32(offset + 0, &data_seg->address); + ql_dma_read32(offset + 8, &data_seg->length); +} + +/* RQSTYPE_DATASEG */ +static void +ql_pkt_get_sgl_cont(uint32_t address, uint32_t idx, isp_data_seg_t *data_seg) +{ + uint32_t offset = address + 8 + idx * 8; + + ql_dma_read32(offset + 0, &data_seg->address); + ql_dma_read32(offset + 4, &data_seg->length); +} + +/* RQSTYPE_A64_CONT */ +static void +ql_pkt_get_sgl_cont64(uint32_t address, uint32_t idx, isp_data_seg_t *data_seg) +{ + uint32_t offset = address + 4 + idx * 12; + + /* The high part of the address is ignored for 86Box */ + ql_dma_read32(offset + 0, &data_seg->address); + ql_dma_read32(offset + 8, &data_seg->length); +} + +static void +ql_pkt_get_entry_type(uint32_t address, uint8_t *entry_type) +{ + ql_dma_read8(address + 0, entry_type); +} + +static void +ql_pkt_put_header(uint32_t address, isp_hdr_t *hdr) +{ + ql_dma_write8(address + 0, &hdr->entry_type); + ql_dma_write8(address + 1, &hdr->entry_count); + ql_dma_write8(address + 2, &hdr->seqno); + ql_dma_write8(address + 3, &hdr->flags); +} + +static void +ql_pkt_put_request_status(uint32_t address, isp_req_status_t *resp) +{ + ql_pkt_put_header(address, &resp->hdr); + + ql_dma_write32(address + 4, &resp->handle); + ql_dma_write16(address + 8, &resp->scsi_status); + ql_dma_write16(address + 10, &resp->comp_status); + ql_dma_write16(address + 12, &resp->state_flags); + ql_dma_write16(address + 14, &resp->status_flags); + ql_dma_write16(address + 16, &resp->time); + ql_dma_write16(address + 18, &resp->sense_length); + ql_dma_write32(address + 20, &resp->residual_length); + ql_dma_write(address + 24, &resp->response[0], ARRAY_SIZE(resp->response)); + ql_dma_write(address + 32, &resp->sense_data[0], ARRAY_SIZE(resp->sense_data)); + + ql_log("QL: RESP HDR type 0x%X cnt %u seq %u fl 0x%X\n", + resp->hdr.entry_type, + resp->hdr.entry_count, + resp->hdr.seqno, + resp->hdr.flags); + ql_log("QL: RESP h 0x%X scsi 0x%X comp 0x%X state 0x%X statfl 0x%X time %u sens %u resid %u\n", + resp->handle, + resp->scsi_status, + resp->comp_status, + resp->state_flags, + resp->status_flags, + resp->time, + resp->sense_length, + resp->residual_length); +} + +static bool +ql_sxp_fetch_request(ql_sxp_req_t* pkt, uint32_t address) +{ + /* Fetch the header */ + ql_dma_read8(address + 0, &pkt->hdr.entry_type); + ql_dma_read8(address + 1, &pkt->hdr.entry_count); + ql_dma_read8(address + 2, &pkt->hdr.seqno); + ql_dma_read8(address + 3, &pkt->hdr.flags); + + switch (pkt->hdr.entry_type) { + case RQSTYPE_REQUEST: + case RQSTYPE_REQUEST_A64: { + ql_dma_read32(address + 4, &pkt->handle); + ql_dma_read8(address + 8, &pkt->lun); + ql_dma_read8(address + 9, &pkt->bus_target); + ql_dma_read16(address + 10, &pkt->cdb_length); + ql_dma_read16(address + 12, &pkt->flags); + ql_dma_read16(address + 14, &pkt->reserved); + ql_dma_read16(address + 16, &pkt->timeout); + ql_dma_read16(address + 18, &pkt->seg_count); + ql_dma_read(address + 20, &pkt->cdb[0], ARRAY_SIZE(pkt->cdb)); + return true; + } + + case RQSTYPE_MARKER: { + ql_dma_read32(address + 4, &pkt->handle); + ql_dma_read8(address + 8, &pkt->lun); + ql_dma_read8(address + 9, &pkt->bus_target); + pkt->timeout = 0; + return true; + } + + default: + break; + } + + ql_log("QL: Unknown/invalid request type %u\n", pkt->hdr.entry_type); + return false; +} + +static void +ql_sxp_begin_response_entry(ql_sxp_req_t *pkt, isp_req_status_t *resp) +{ + memset(resp, 0, sizeof(*resp)); + resp->hdr.entry_type = RQSTYPE_RESPONSE; + resp->hdr.entry_count = pkt->hdr.entry_count; + resp->hdr.seqno = pkt->hdr.seqno; + resp->hdr.flags = pkt->hdr.flags; + resp->handle = pkt->handle; + resp->time = pkt->timeout; +} + +static double +ql_sxp_handle_state_send_cdb_bios(ql_t *dev, scsi_device_t *sd) +{ + ql_sxp_req_t *pkt = &dev->pkt; + isp_req_status_t *resp = &dev->pkt_resp; + double media_period = 10.0; + uint64_t bytes_xfered = 0; + + if (dev->scsi_data_buffer) { + free(dev->scsi_data_buffer); + dev->scsi_data_buffer = NULL; + } + + if ((sd->phase != SCSI_PHASE_STATUS) && (sd->buffer_length > 0)) { + uint32_t dev_buffer_length = sd->buffer_length; + uint8_t *dev_buffer = sd->sc->temp_buffer; + uint32_t host_buffer_size; + double p; + + p = scsi_device_get_callback(sd); + if (p <= 0.0) + bytes_xfered += sd->buffer_length; + else + media_period = p; + + host_buffer_size = dev->cpu_mem[QL_IOCB_FW_BASE + 2]; + host_buffer_size |= (uint32_t)dev->cpu_mem[QL_IOCB_FW_BASE + 3] << 16; + + if (dev->cpu_mem[QL_IOCB_FW_BASE + 12] == 3) { + /* PIO */ + dev->scsi_data_offset = 0; + dev->scsi_data_size = dev_buffer_length; + dev->scsi_data_buffer = malloc(dev_buffer_length); + memcpy(dev->scsi_data_buffer, dev_buffer, dev_buffer_length); + } else { + uint32_t address, block_size; + + address = dev->cpu_mem[QL_IOCB_FW_BASE + 0]; + address |= (uint32_t)dev->cpu_mem[QL_IOCB_FW_BASE + 1] << 16; + + block_size = MIN(host_buffer_size, dev_buffer_length); + + /* DMA */ + if (sd->phase == SCSI_PHASE_DATA_IN) { + ql_log("QL: DMA to 0x%lx %lu bytes\n", address, block_size); + ql_dma_write(address, dev_buffer, block_size); + } else if (sd->phase == SCSI_PHASE_DATA_OUT) { + ql_log("QL: DMA from 0x%lx %lu bytes\n", address, block_size); + ql_dma_read(address, dev_buffer, block_size); + } + } + scsi_device_command_phase1(sd); + + if (dev_buffer_length < host_buffer_size) { + /* + * Data underrun + * + * NOTE: It seems that there is no way to return residual_length to the software. + * It has been observed that the BIOS does a retry in PIO mode + * when a DMA underrun error occurs. + */ + resp->comp_status = RQCS_DATA_UNDERRUN; + } else if (dev_buffer_length > host_buffer_size) { + /* Data overrun */ + resp->comp_status = RQCS_DATA_OVERRUN; + } else { + /* Normal completion */ + resp->comp_status = RQCS_COMPLETE; + } + resp->scsi_status = sd->status; + } else { + resp->scsi_status = sd->status; + resp->comp_status = RQCS_COMPLETE; + } + + return media_period + (1000000.0 / dev->xfer_rate_bps) * (double)bytes_xfered; +} + +static double +ql_sxp_handle_state_send_cdb_sgl(ql_t *dev, scsi_device_t *sd) +{ + ql_sxp_req_t *pkt = &dev->pkt; + isp_req_status_t *resp = &dev->pkt_resp; + double media_period = 10.0; + uint64_t bytes_xfered = QENTRY_LEN; + + /* Read/write command */ + if ((sd->phase != SCSI_PHASE_STATUS) && (sd->buffer_length > 0)) { + uint32_t dev_buffer_length = sd->buffer_length; + uint8_t *dev_buffer = sd->sc->temp_buffer; + uint8_t entry_type = pkt->hdr.entry_type; + uint32_t pkt_address = dev->pkt_address; + uint16_t pkt_entry_idx = QL_RQST_CONS(dev); + uint32_t host_buffer_size = 0; + uint32_t bytes_moved = 0; + uint32_t max_seg_count; + double p; + + p = scsi_device_get_callback(sd); + if (p <= 0.0) + bytes_xfered += sd->buffer_length; + else + media_period = p; + + if (entry_type == RQSTYPE_REQUEST) { + max_seg_count = 4; + } else { + assert(entry_type == RQSTYPE_REQUEST_A64); + max_seg_count = 2; + } + + /* Process the S/G list */ + for (uint32_t i = 0, seg_idx = 0; i < pkt->seg_count; i++, seg_idx++) { + isp_data_seg_t data_seg; + uint32_t block_size; + + /* Fetch a continuation segment entry */ + if (seg_idx == max_seg_count) { + seg_idx = 0; + + pkt_entry_idx = (pkt_entry_idx + 1) % dev->rqst_ring_size; + pkt_address = dev->rqst_ring_base + pkt_entry_idx * QENTRY_LEN; + + ql_pkt_get_entry_type(pkt_address, &entry_type); + if (entry_type == RQSTYPE_DATASEG) { + max_seg_count = 7; + } else if (entry_type == RQSTYPE_A64_CONT) { + max_seg_count = 5; + } else { + /* Bad entry in the request ring */ + ql_log("QL: Expected continuation segment but got %u\n", entry_type); + break; + } + } + + /* Read the S/G fragment */ + switch (entry_type) { + case RQSTYPE_REQUEST: + ql_pkt_get_sgl_req(pkt_address, seg_idx, &data_seg); + break; + case RQSTYPE_REQUEST_A64: + ql_pkt_get_sgl_req64(pkt_address, seg_idx, &data_seg); + break; + case RQSTYPE_DATASEG: + ql_pkt_get_sgl_cont(pkt_address, seg_idx, &data_seg); + break; + case RQSTYPE_A64_CONT: + ql_pkt_get_sgl_cont64(pkt_address, seg_idx, &data_seg); + break; + + default: + /* Should not happen */ + assert(false); + break; + } + host_buffer_size += data_seg.length; + block_size = MIN(data_seg.length, dev_buffer_length - bytes_moved); + bytes_xfered += sizeof(data_seg); + + if (sd->phase == SCSI_PHASE_DATA_IN) { + ql_log("QL: DMA to 0x%lx %lu bytes\n", data_seg.address, block_size); + ql_dma_write(data_seg.address, &dev_buffer[bytes_moved], block_size); + } else if (sd->phase == SCSI_PHASE_DATA_OUT) { + ql_log("QL: DMA from 0x%lx %lu bytes\n", data_seg.address, block_size); + ql_dma_read(data_seg.address, &dev_buffer[bytes_moved], block_size); + } + bytes_moved += block_size; + } + scsi_device_command_phase1(sd); + + if (dev_buffer_length < host_buffer_size) { + /* Data underrun */ + resp->residual_length = host_buffer_size - dev_buffer_length; + resp->comp_status = RQCS_DATA_UNDERRUN; + } else if (dev_buffer_length > host_buffer_size) { + /* Data overrun */ + resp->comp_status = RQCS_DATA_OVERRUN; + } else { + /* Normal completion */ + resp->comp_status = RQCS_COMPLETE; + } + resp->scsi_status = sd->status; + resp->state_flags |= RQSF_GOT_STATUS | RQSF_XFER_COMPLETE; + } else { + resp->scsi_status = sd->status; + resp->comp_status = RQCS_COMPLETE; + resp->state_flags |= RQSF_GOT_STATUS; + } + + /* Auto request sense */ + if ((resp->scsi_status == SCSI_STATUS_CHECK_CONDITION) && !(pkt->flags & REQFLAG_DISARQ)) { + if (dev->fw_tid_params[dev->curr_path_id][dev->curr_target_id].flags & DPARM_ARQ) { + scsi_device_request_sense(sd, resp->sense_data, sizeof(resp->sense_data)); + + resp->state_flags |= RQSF_GOT_SENSE; + resp->sense_length = sizeof(resp->sense_data); + + bytes_xfered += resp->sense_length; + } + } + + return media_period + (1000000.0 / dev->xfer_rate_bps) * (double)bytes_xfered; +} + +static bool +ql_sxp_state_machine(ql_t *dev) +{ + switch (dev->sxp_state) { + case SXP_STATE_IDLE: { + ql_sxp_req_t *pkt = &dev->pkt; + + /* Do we have to pick mailbox registers up? */ + if (dev->sxp_flags & SXP_FLAG_PICK_UP_MBOX) { + dev->sxp_flags &= ~SXP_FLAG_PICK_UP_MBOX; + + dev->mbox_out_mask = 1 << QL_MBOX_STATUS; + dev->mbox_data[QL_MBOX_STATUS] = ql_process_mailbox(dev); + + /* Check for mailbox commands that do not need to go through the command processor */ + if (dev->mbox_data[QL_MBOX_STATUS] != QL_MBOX_STATUS_PENDING) { + dev->timer_period = QL_MBOX_GENERIC_TIME_US; + dev->sxp_state = SXP_STATE_SCHEDULE_MBOX_INTERRUPT; + break; + } + + assert(dev->sxp_flags & (SXP_FLAG_BIOS_IOCB | SXP_FLAG_MBOX_IOCB)); + + if (dev->sxp_flags & SXP_FLAG_BIOS_IOCB) { + uint8_t lun = dev->cpu_mem[QL_IOCB_FW_BASE + 48] & 0x0F; + uint8_t target_id = (dev->cpu_mem[QL_IOCB_FW_BASE + 48] & 0xF0) >> 4 ; + uint8_t *cdb_bytes = (uint8_t *)&dev->cpu_mem[QL_IOCB_FW_BASE + 5]; + + /* Translate the BIOS IOCB request into a common structure for easier processing */ + memset(pkt, 0, sizeof(*pkt)); + pkt->hdr.entry_type = RQSTYPE_REQUEST; + pkt->hdr.entry_count = 1; + pkt->lun = lun; + pkt->bus_target = target_id; + pkt->cdb_length = ARRAY_SIZE(pkt->cdb); + memcpy(pkt->cdb, cdb_bytes, ARRAY_SIZE(pkt->cdb)); + + dev->sxp_state = SXP_STATE_SELECT_DEVICE; + break; + } + } + + if (dev->sxp_flags & SXP_FLAG_MBOX_IOCB) { + dev->pkt_address = ((uint32_t)dev->reg_mbox_in[2] << 16) | dev->reg_mbox_in[3]; + } else { + /* Skip processing command queues if they are not initialized */ + if (!(dev->sxp_flags & SXP_FLAG_ENGINE_ACTIVE)) { + return false; + } + + /* No available entries the request queue, try again later */ + if (QL_RQST_CONS(dev) == QL_RQST_PROD(dev)) { + return false; + } + + /* No room in the response queue, try again later */ + if (((QL_RESP_PROD(dev) + 1) % dev->resp_ring_size) == QL_RESP_CONS(dev)) { + return false; + } + + /* Check is this entry has been aborted */ + if (!fifo8_is_empty(&dev->abort_iocbs_fifo)) { + const uint8_t* idx_buf; + uint16_t rqst_idx; + + idx_buf = fifo8_peek_bufptr(&dev->abort_iocbs_fifo, sizeof(rqst_idx), NULL); + rqst_idx = ((uint16_t)idx_buf[1] << 8) | idx_buf[0]; + + if (rqst_idx == QL_RQST_CONS(dev)) { + fifo8_drop(&dev->abort_iocbs_fifo, sizeof(rqst_idx)); + + dev->sxp_flags |= SXP_FLAG_ABORTED_CMD; + + dev->timer_period = QL_MBOX_GENERIC_TIME_US; + dev->sxp_state = SXP_STATE_COMPLETE_COMMAND; + break; + } + } + + dev->pkt_address = dev->rqst_ring_base + QL_RQST_CONS(dev) * QENTRY_LEN; + } + + if (!ql_sxp_fetch_request(pkt, dev->pkt_address)) { + /* Skip this packet */ + pkt->hdr.entry_count = 1; + + dev->sxp_state = SXP_STATE_BAD_PACKET; + break; + } + + dev->sxp_state = SXP_STATE_SELECT_DEVICE; + break; + } + case SXP_STATE_SELECT_DEVICE: { + ql_sxp_req_t *pkt = &dev->pkt; + + ql_log("QL: RQST HDR type 0x%X cnt %u seq %u fl 0x%X\n", + pkt->hdr.entry_type, + pkt->hdr.entry_count, + pkt->hdr.seqno, + pkt->hdr.flags); + ql_log("QL: RQST h 0x%X lun %u bus_tid 0x%X cdb_len %u fl 0x%X time %u seq_cnt %u\n", + pkt->handle, + pkt->lun, + pkt->bus_target, + pkt->cdb_length, + pkt->flags, + pkt->timeout, + pkt->seg_count); + + /* Extract the SCSI address */ + dev->curr_path_id = (pkt->bus_target & 0x80) >> 7; + dev->curr_target_id = pkt->bus_target & ~0x80; + + if (dev->curr_path_id >= dev->max_bus_count) { + dev->sxp_state = SXP_STATE_SELECTION_TIMEOUT; + break; + } + + /* 86Box supports one SCSI bus per controller for now */ + if (dev->curr_path_id != 0) { + dev->sxp_state = SXP_STATE_SELECTION_TIMEOUT; + break; + } + + if (dev->curr_target_id >= MIN(QL_MAX_TID, SCSI_ID_MAX)) { + dev->sxp_state = SXP_STATE_SELECTION_TIMEOUT; + break; + } + + if (!scsi_device_present(&scsi_devices[dev->scsi_bus][dev->curr_target_id])) { + dev->sxp_state = SXP_STATE_SELECTION_TIMEOUT; + break; + } + + ql_log("QL: Selected target %u:%u\n", dev->curr_path_id, dev->curr_target_id); + dev->sxp_state = SXP_STATE_SEND_CDB; + break; + } + case SXP_STATE_SEND_CDB: { + scsi_device_t *sd = &scsi_devices[dev->scsi_bus][dev->curr_target_id]; + ql_sxp_req_t *pkt = &dev->pkt; + isp_req_status_t *resp = &dev->pkt_resp; + + ql_sxp_begin_response_entry(pkt, resp); + resp->state_flags = RQSF_GOT_BUS | RQSF_GOT_TARGET | RQSF_SENT_CDB; + + if (pkt->hdr.entry_type == RQSTYPE_MARKER) { + resp->scsi_status = SCSI_STATUS_OK; + resp->comp_status = RQCS_COMMAND_OVERRUN; + resp->status_flags = RQSTF_ABORTED; + + dev->timer_period = QL_MBOX_GENERIC_TIME_US; + dev->sxp_state = SXP_STATE_COMPLETE_COMMAND; + break; + } + + scsi_device_identify(sd, pkt->lun); + + for (uint32_t i = 0; i < ARRAY_SIZE(pkt->cdb); i++) { + ql_log("QL: SCSI CDB[%2lu]=%02X\n", i, pkt->cdb[i]); + } + + sd->buffer_length = -1; + scsi_device_command_phase0(sd, pkt->cdb); + + if (dev->sxp_flags & SXP_FLAG_BIOS_IOCB) { + dev->timer_period = ql_sxp_handle_state_send_cdb_bios(dev, sd); + } else { + dev->timer_period = ql_sxp_handle_state_send_cdb_sgl(dev, sd); + } + scsi_device_identify(sd, SCSI_LUN_USE_CDB); + + dev->sxp_state = SXP_STATE_COMPLETE_COMMAND; + break; + } + case SXP_STATE_SELECTION_TIMEOUT: { + isp_req_status_t *resp = &dev->pkt_resp; + + ql_sxp_begin_response_entry(&dev->pkt, resp); + resp->scsi_status = SCSI_STATUS_OK; + resp->comp_status = RQCS_INCOMPLETE; + resp->state_flags = RQSF_GOT_BUS; + resp->status_flags = RQSTF_TIMEOUT; + + dev->timer_period = QL_CMD_SELECTION_TIMEOUT_TIME_US; + dev->sxp_state = SXP_STATE_COMPLETE_COMMAND; + break; + } + case SXP_STATE_BAD_PACKET: { + isp_req_status_t *resp = &dev->pkt_resp; + + memset(resp, 0, sizeof(*resp)); + resp->hdr.entry_type = 0; + resp->hdr.flags = RQSFLAG_BADHEADER; + resp->comp_status = RQCS_INCOMPLETE; + + dev->timer_period = QL_MBOX_GENERIC_TIME_US; + dev->sxp_state = SXP_STATE_COMPLETE_COMMAND; + break; + } + case SXP_STATE_COMPLETE_COMMAND: { + isp_req_status_t *resp = &dev->pkt_resp; + ql_sxp_req_t *pkt = &dev->pkt; + + /* There are three possible ways of sending SCSI commands to ISP */ + if (dev->sxp_flags & SXP_FLAG_BIOS_IOCB) { + dev->mbox_out_mask = (1 << QL_MBOX_STATUS) | QL_BIT(1) | QL_BIT(2); + dev->mbox_data[QL_MBOX_STATUS] = QL_MBOX_STATUS_COMPLETE; + dev->mbox_data[1] = resp->comp_status; + dev->mbox_data[2] = resp->scsi_status; + + /* Return the internal queue index */ + dev->cpu_mem[QL_IOCB_FW_BASE + 13] = 0; + + dev->sxp_state = SXP_STATE_SCHEDULE_MBOX_INTERRUPT; + } else if (dev->sxp_flags & SXP_FLAG_MBOX_IOCB) { + dev->mbox_out_mask = (1 << QL_MBOX_STATUS); + dev->mbox_data[QL_MBOX_STATUS] = QL_MBOX_STATUS_COMPLETE; + + /* Mailbox IOCB commands always write a response queue entry */ + dev->sxp_flags |= SXP_FLAG_WRITE_RESP_IOCB; + dev->sxp_state = SXP_STATE_SCHEDULE_MBOX_INTERRUPT; + } else { + uint16_t num_consumed = MAX(pkt->hdr.entry_count, 1); + QL_RQST_CONS(dev) = (QL_RQST_CONS(dev) + num_consumed) % dev->rqst_ring_size; + + if ((dev->sxp_flags & SXP_FLAG_ABORTED_CMD) || (pkt->hdr.entry_type == RQSTYPE_MARKER)) { + /* + * Marker entries never raise an interrupt, even if the SCSI device does not exist. + * Aborted commands also never raise an interrupt. + */ + dev->sxp_state = SXP_STATE_SCHEDULE_TIMER_SILENCE; + } else if ((dev->sxp_flags & SXP_FLAG_FAST_POSTING) && + (resp->scsi_status == SCSI_STATUS_OK) && + (resp->comp_status == RQCS_COMPLETE)) { + /* Fast posting avoids having to deal with response queue for successful commands */ + dev->mbox_out_mask = (1 << QL_MBOX_STATUS) | (1 << QL_MBOX_HNDL_LOW) | 1 << (QL_MBOX_HNDL_HIGH); + dev->mbox_data[QL_MBOX_STATUS] = QL_ASYNC_STATUS_SCSI_CMD_COMPLETE; + dev->mbox_data[QL_MBOX_HNDL_LOW] = pkt->handle & 0xFFFF; + dev->mbox_data[QL_MBOX_HNDL_HIGH] = (pkt->handle >> 16) & 0xFFFF; + + dev->sxp_state = SXP_STATE_SCHEDULE_MBOX_INTERRUPT; + } else { + /* Normal command completion, write a response queue entry */ + dev->pkt_address = dev->resp_ring_base + QL_RESP_PROD(dev) * QENTRY_LEN; + dev->sxp_flags |= SXP_FLAG_WRITE_RESP_IOCB | SXP_FLAG_INC_RESP_RING; + dev->sxp_state = SXP_STATE_SCHEDULE_RISC_INTERRUPT; + } + } + break; + } + case SXP_STATE_SCHEDULE_RISC_INTERRUPT: { + dev->sxp_state = SXP_STATE_SET_RISC_INTERRUPT; + timer_on_auto(&dev->cmd_timer, dev->timer_period); + return false; + } + case SXP_STATE_SCHEDULE_MBOX_INTERRUPT: { + dev->sxp_state = SXP_STATE_MBOX_WAIT_TIMER; + timer_on_auto(&dev->cmd_timer, dev->timer_period); + return false; + } + case SXP_STATE_MBOX_WAIT_TIMER: { + dev->sxp_state = SXP_STATE_ACQUIRE_MBOX_SEMAPHORE; + break; + } + case SXP_STATE_ACQUIRE_MBOX_SEMAPHORE: { + /* Wait for the mailbox semaphore to be released */ + if (dev->reg_semaphore & QL_SEMAPHORE_LOCK) { + return false; + } + + /* Return mailbox registers to the host */ + for (uint32_t i = 0; i < ARRAY_SIZE(dev->reg_mbox_out); i++) { + if (dev->mbox_out_mask & (1 << i)) { + dev->reg_mbox_out[i] = dev->mbox_data[i]; + } + } + dev->reg_semaphore = QL_SEMAPHORE_LOCK; + fallthrough; + } + case SXP_STATE_SET_RISC_INTERRUPT: { + if (dev->sxp_flags & SXP_FLAG_WRITE_RESP_IOCB) { + ql_pkt_put_request_status(dev->pkt_address, &dev->pkt_resp); + + if (dev->sxp_flags & SXP_FLAG_INC_RESP_RING) { + QL_RESP_PROD(dev) = (QL_RESP_PROD(dev) + 1) % dev->resp_ring_size; + } + } + dev->sxp_flags &= ~(SXP_FLAG_WRITE_RESP_IOCB | SXP_FLAG_INC_RESP_RING + | SXP_FLAG_BIOS_IOCB | SXP_FLAG_MBOX_IOCB | SXP_FLAG_ABORTED_CMD); + + dev->reg_intr_status |= QL_INTR_REQ | QL_RISC_INTR_REQ; + ql_update_irq(dev); + + dev->sxp_state = SXP_STATE_IDLE; + break; + } + case SXP_STATE_SCHEDULE_TIMER_SILENCE: { + dev->sxp_state = SXP_STATE_WAIT_TIMER; + timer_on_auto(&dev->cmd_timer, dev->timer_period); + return false; + } + case SXP_STATE_WAIT_TIMER: { + dev->sxp_state = SXP_STATE_IDLE; + break; + } + + default: { + /* Should not happen */ + assert(false); + break; + } + } + + return true; +} + +static void +ql_sxp_run_state_machine(ql_t *dev) +{ + ql_log("QL: Enter with %s\n", debug_sxp_state_to_name(dev->sxp_state)); + + while (true) { + SxpState old_state = dev->sxp_state; + + if (!ql_sxp_state_machine(dev)) { + break; + } + + if (dev->sxp_state != old_state) { + ql_log("QL: State %s --> %s\n", debug_sxp_state_to_name(old_state), debug_sxp_state_to_name(dev->sxp_state)); + } + } + + ql_log("QL: Exit with %s\n", debug_sxp_state_to_name(dev->sxp_state)); +} + +static void +ql_sxp_kick_engine(ql_t *dev) +{ + if (dev->sxp_state == SXP_STATE_IDLE) { + ql_log("QL: SCSI kick\n"); + ql_sxp_run_state_machine(dev); + } +} + +static void +ql_sxp_timer_callback(void *priv) +{ + ql_t *dev = priv; + + ql_log("QL: Timer called\n"); + + assert(dev->sxp_state != SXP_STATE_IDLE); + + ql_sxp_run_state_machine(dev); +} + +static void +ql_sxp_write_semaphore(ql_t *dev, uint16_t val) +{ + if (val == 0) { + val = QL_SEMAPHORE_STATUS; + } else { + val &= QL_SEMAPHORE_STATUS | QL_SEMAPHORE_LOCK; + } + dev->reg_semaphore = val; + + if (dev->sxp_state == SXP_STATE_ACQUIRE_MBOX_SEMAPHORE) { + ql_sxp_run_state_machine(dev); + } +} + +static void +ql_sxp_write_mailbox(ql_t *dev, uint32_t idx, uint16_t val) +{ + dev->reg_mbox_in[idx] = val; + + /* Kick the command processor */ + if ((idx == QL_MBOX_RQST) || (idx == QL_MBOX_RESP)) { + ql_sxp_kick_engine(dev); + } +} + +static void +ql_write_host_command(ql_t *dev, uint16_t val) +{ + switch (val) { + case QL_HC_RESET_RISC: + dev->reg_host_cmd_flags = QL_HC_FLAG_RISC_RESET; + break; + case QL_HC_PAUSE_RISC: + dev->reg_host_cmd_flags |= QL_HC_FLAG_RISC_PAUSE; + break; + case QL_HC_RELEASE_RISC: + dev->reg_host_cmd_flags &= ~(QL_HC_FLAG_RISC_PAUSE | QL_HC_FLAG_RISC_RESET); + break; + case QL_HC_SET_HOST_INTR: + dev->reg_host_cmd_flags |= QL_HC_FLAG_HOST_INTR; + dev->sxp_flags |= SXP_FLAG_PICK_UP_MBOX; + ql_sxp_kick_engine(dev); + break; + case QL_HC_CLEAR_HOST_INTR: + /* This command does nothing */ + break; + case QL_HC_CLEAR_RISC_INTR: + dev->reg_host_cmd_flags &= ~QL_HC_FLAG_HOST_INTR; + dev->reg_intr_status = 0; + ql_update_irq(dev); + break; + case QL_HC_DISABLE_BIOS: + dev->reg_host_cmd_flags = 0; + break; + + default: { + ql_log("QL: Unknown host command %04X\n", val); + break; + } + } +} + +static void +ql_write_interface_control(ql_t *dev, uint16_t val) +{ + if (val & QL_IFACE_SOFT_RESET) { + ql_reset_asic(dev); + return; + } + + dev->reg_intr_ctrl = val & 0x01FF; + ql_update_irq(dev); +} + +static uint32_t +ql_get_flash_bios_addr(ql_t *dev) +{ + uint32_t address = dev->reg_flash_bios_addr; + + /* Select between two consecutive flash memory banks of 64KB each */ + if (dev->reg_nvram & QL_EEPROM_CS) { + address |= 0x10000; + } + return address; +} + +static uint16_t +ql_read_flash_bios_data(ql_t *dev) +{ + uint8_t byte; + + if (!(dev->reg_intr_ctrl & QL_IFACE_FLASH_ENABLE)) { + return 0xFFFF; + } + + byte = am29_mmio_read8(ql_get_flash_bios_addr(dev), &dev->flash_device); + return (byte << 8) | byte; +} + +static void +ql_write_flash_bios_data(ql_t *dev, uint16_t value) +{ + if (!(dev->reg_intr_ctrl & QL_IFACE_FLASH_ENABLE)) { + return; + } + + am29_mmio_write8(ql_get_flash_bios_addr(dev), (uint8_t)value, &dev->flash_device); +} + +static void +ql_write_bank_sxp(ql_t *dev, uint32_t addr, uint32_t bus_number, uint16_t val) +{ + if (bus_number >= dev->max_bus_count) { + return; + } + + ql_log("QL: [W16] Unhandled SPX bank write %lX <-- %X\n", IDX_TO_REG(addr), val); +} + +static void +ql_write_bank_dma(ql_t *dev, uint32_t addr, uint16_t val) +{ + switch (addr) { + case QL_REG_CDMA_CFG: + dev->reg_cdma_cfg = val; + break; + case QL_REG_CDMA_CTRL: + dev->reg_cdma_ctrl = val; + break; + case QL_REG_DDMA_CFG: + dev->reg_ddma_cfg = val; + break; + case QL_REG_DDMA_CTRL: + dev->reg_ddma_ctrl = val; + break; + + default: + ql_log("QL: [W16] Unhandled DMA bank write %lX <-- %X\n", IDX_TO_REG(addr), val); + break; + } +} + +static uint16_t +ql_write_bank_risc(ql_t *dev, uint32_t addr, uint16_t val) +{ + switch (addr) { + case QL_REG_RISC_PSR: + dev->reg_risc_psr = val; + break; + case QL_REG_RISC_PCR: + dev->reg_risc_pcr = val; + break; + case QL_REG_RISC_MTR: + dev->reg_risc_mtr = val; + break; + case QL_REG_HOST_CMD: + ql_write_host_command(dev, val); + break; + case QL_REG_GPIO_DATA: + dev->reg_gpio_data = val; + break; + case QL_REG_GPIO_ENABLE: + dev->reg_gpio_enable = val; + break; + + default: + ql_log("QL: [W16] Unhandled RISC bank write %lX <-- %X\n", IDX_TO_REG(addr), val); + break; + } +} + +static void +ql_mmio_write16(uint32_t addr, uint16_t val, void* priv) +{ + ql_t *dev = priv; + + /* The hw does not handle unaligned access */ + if (addr & 0x1) { + return; + } + + addr &= QL_IO_DECODE_MASK; + addr = REG_TO_IDX(addr); + +#ifdef ENABLE_QL_LOG + ql_debug_check_for_nvram_access(addr); +#endif + + ql_log("QL: [W16] [%02lX] <-- %4X\n", IDX_TO_REG(addr), val); + + switch (addr) { + case QL_REG_CFG1: + dev->reg_cfg1 = val & 0x03FF; + break; + case QL_REG_INT_CTRL: + ql_write_interface_control(dev, val); + break; + case QL_REG_SEMAPHORE: + ql_sxp_write_semaphore(dev, val); + break; + case QL_REG_NVRAM: + dev->reg_nvram = val & (QL_EEPROM_SK | QL_EEPROM_CS | QL_EEPROM_DI); + nmc93cxx_eeprom_write(dev->eeprom_device, + !!(val & QL_EEPROM_CS), + !!(val & QL_EEPROM_SK), + !!(val & QL_EEPROM_DI)); + break; + case QL_REG_FLASH_BIOS_DATA: + ql_write_flash_bios_data(dev, val); + break; + case QL_REG_FLASH_BIOS_ADDRESS: + dev->reg_flash_bios_addr = val; + break; + case QL_REG_MAILBOX0: + case QL_REG_MAILBOX1: + case QL_REG_MAILBOX2: + case QL_REG_MAILBOX3: + case QL_REG_MAILBOX4: + case QL_REG_MAILBOX5: + case QL_REG_MAILBOX6: + case QL_REG_MAILBOX7: + ql_sxp_write_mailbox(dev, addr - QL_REG_MAILBOX0, val); + break; + + default: { + if (dev->isp_type == QL_ISP1040) { + if (addr >= REG_TO_IDX(0x20) && addr < REG_TO_IDX(0x80)) { + /* DMA bank */ + addr -= REG_TO_IDX(0x20); + ql_write_bank_dma(dev, addr, val); + break; + } else if (addr >= REG_TO_IDX(0x80) && addr < REG_TO_IDX(0xFF)) { + /* RISC or SXP bank */ + addr -= REG_TO_IDX(0x80); + if (dev->reg_cfg1 & BIU_PCI_CONF1_SXP) { + ql_write_bank_sxp(dev, addr, 0, val); + } else { + ql_write_bank_risc(dev, addr, val); + } + break; + } + } else { + if (addr >= REG_TO_IDX(0x80) && addr < REG_TO_IDX(0xFF)) { + addr -= REG_TO_IDX(0x80); + + switch (dev->reg_cfg1 & BIU_PCI1080_REG_BANK_MASK) { + case BIU_PCI1080_CONF1_SXP0: + ql_write_bank_sxp(dev, addr, 0, val); + break; + case BIU_PCI1080_CONF1_SXP1: + ql_write_bank_sxp(dev, addr, 1, val); + break; + case BIU_PCI1080_CONF1_DMA: + ql_write_bank_dma(dev, addr, val); + break; + default: + ql_write_bank_risc(dev, addr, val); + break; + } + break; + } + } + + ql_log("QL: [W16] Unhandled write %lX <-- %X\n", IDX_TO_REG(addr), val); + break; + } + } +} + +static uint16_t +ql_read_bank_sxp(ql_t *dev, uint32_t addr, uint32_t bus_number) +{ + uint16_t ret; + + if (bus_number >= dev->max_bus_count) { + return 0; + } + + switch (addr) { + case QL_REG_SXP_PINS_DIFF: + ret = dev->reg_scsi_diff_pins; + break; + + default: + ql_log("QL: [R16] Unhandled SXP bank read %lX\n", IDX_TO_REG(addr)); + ret = 0; + break; + } + + return ret; +} + +static uint16_t +ql_read_bank_dma(ql_t *dev, uint32_t addr) +{ + uint16_t ret; + + switch (addr) { + case QL_REG_CDMA_CFG: + ret = dev->reg_cdma_cfg; + break; + case QL_REG_CDMA_CTRL: + ret = dev->reg_cdma_ctrl; + break; + case QL_REG_DDMA_CFG: + ret = dev->reg_ddma_cfg; + break; + case QL_REG_DDMA_CTRL: + ret = dev->reg_ddma_ctrl; + break; + + case QL_REG_DDMA_DATA_PORT: { + if (!dev->scsi_data_buffer) { + ret = 0; + break; + } + + /* Return a word via PIO interface */ + ret = 0; + for (uint32_t i = 0; i < sizeof(uint16_t); i++) { + uint8_t byte = dev->scsi_data_buffer[dev->scsi_data_offset++]; + + ql_log("QL: Return byte 0x%02X at offset %lu\n", byte, dev->scsi_data_offset - 1); + + ret |= (uint16_t)byte << (i * 8); + + /* Last byte, destroy the buffer */ + if (dev->scsi_data_offset >= dev->scsi_data_size) { + ql_log("QL: All data has been transmitted\n"); + + free(dev->scsi_data_buffer); + dev->scsi_data_buffer = NULL; + break; + } + } + break; + } + + default: + ql_log("QL: [R16] Unhandled DMA bank read %lX\n", IDX_TO_REG(addr)); + ret = 0; + break; + } + + return ret; +} + +static uint16_t +ql_read_bank_risc(ql_t *dev, uint32_t addr) +{ + uint16_t ret; + + switch (addr) { + case QL_REG_RISC_PSR: + ret = dev->reg_risc_psr; + break; + case QL_REG_RISC_PCR: + ret = dev->reg_risc_pcr; + break; + case QL_REG_RISC_PC: + ret = dev->reg_risc_pc; + break; + case QL_REG_RISC_MTR: + ret = dev->reg_risc_mtr; + break; + case QL_REG_HOST_CMD: + ret = dev->reg_host_cmd_flags; + break; + case QL_REG_GPIO_DATA: + ret = dev->reg_gpio_data; + break; + case QL_REG_GPIO_ENABLE: + ret = dev->reg_gpio_enable; + break; + + default: + ql_log("QL: [R16] Unhandled RISC bank read %lX\n", IDX_TO_REG(addr)); + ret = 0; + break; + } + + return ret; +} + +static uint16_t +ql_mmio_read16(uint32_t addr, void* priv) +{ + ql_t *dev = priv; + uint16_t ret; + + /* The hw does not handle unaligned access */ + if (addr & 0x1) { + return 0xFFFF; + } + + addr &= QL_IO_DECODE_MASK; + addr = REG_TO_IDX(addr); + + switch (addr) { + case QL_REG_ID_LOW: + ret = dev->reg_id_low; + break; + case QL_REG_ID_HIGH: + ret = dev->reg_id_high; + break; + case QL_REG_CFG0: + ret = dev->reg_cfg0; + break; + case QL_REG_CFG1: + ret = dev->reg_cfg1; + break; + case QL_REG_INT_CTRL: + ret = dev->reg_intr_ctrl; + break; + case QL_REG_INT_STATUS: + ret = dev->reg_intr_status; + break; + case QL_REG_SEMAPHORE: + ret = dev->reg_semaphore; + break; + case QL_REG_NVRAM: + ret = dev->reg_nvram; + if (nmc93cxx_eeprom_read(dev->eeprom_device)) { + ret |= QL_EEPROM_DO; + } + break; + case QL_REG_FLASH_BIOS_DATA: + ret = ql_read_flash_bios_data(dev); + break; + case QL_REG_FLASH_BIOS_ADDRESS: + ret = dev->reg_flash_bios_addr; + break; + case QL_REG_MAILBOX0: + case QL_REG_MAILBOX1: + case QL_REG_MAILBOX2: + case QL_REG_MAILBOX3: + case QL_REG_MAILBOX4: + case QL_REG_MAILBOX5: + case QL_REG_MAILBOX6: + case QL_REG_MAILBOX7: + ret = dev->reg_mbox_out[addr - QL_REG_MAILBOX0]; + break; + + default: { + uint32_t bank_addr; + + if (dev->isp_type == QL_ISP1040) { + if (addr >= REG_TO_IDX(0x20) && addr < REG_TO_IDX(0x80)) { + /* DMA bank */ + bank_addr = addr - REG_TO_IDX(0x20); + ret = ql_read_bank_dma(dev, bank_addr); + break; + } else if (addr >= REG_TO_IDX(0x80) && addr < REG_TO_IDX(0xFF)) { + /* RISC or SXP bank */ + bank_addr = addr - REG_TO_IDX(0x80); + if (dev->reg_cfg1 & BIU_PCI_CONF1_SXP) { + ret = ql_read_bank_sxp(dev, bank_addr, 0); + } else { + ret = ql_read_bank_risc(dev, bank_addr); + } + break; + } + } else { + if (addr >= REG_TO_IDX(0x80) && addr < REG_TO_IDX(0xFF)) { + bank_addr = addr - REG_TO_IDX(0x80); + + switch (dev->reg_cfg1 & BIU_PCI1080_REG_BANK_MASK) { + case BIU_PCI1080_CONF1_SXP0: + ret = ql_read_bank_sxp(dev, bank_addr, 0); + break; + case BIU_PCI1080_CONF1_SXP1: + ret = ql_read_bank_sxp(dev, bank_addr, 1); + break; + case BIU_PCI1080_CONF1_DMA: + ret = ql_read_bank_dma(dev, bank_addr); + break; + default: + ret = ql_read_bank_risc(dev, bank_addr); + break; + } + break; + } + } + + ql_log("QL: [R16] Unhandled read %lX\n", IDX_TO_REG(addr)); + ret = 0; + break; + } + } + +#ifdef ENABLE_QL_LOG + ql_debug_check_for_nvram_access(addr); +#endif + + ql_log("QL: [R16] [%02lX] --> %4X\n", IDX_TO_REG(addr), ret); + return ret; +} + +static uint8_t +ql_mmio_read8(uint32_t addr, void* priv) +{ + /* The hw only supports 16-bit R/W access */ + return 0xFF; +} + +static uint32_t +ql_mmio_read32(uint32_t addr, void* priv) +{ + /* The hw only supports 16-bit R/W access */ + return 0xFFFFFFFF; +} + +static void +ql_mmio_write8(uint32_t addr, uint8_t val, void* priv) +{ + /* The hw only supports 16-bit R/W access */ +} + +static void +ql_mmio_write32(uint32_t addr, uint32_t val, void* priv) +{ + /* The hw only supports 16-bit R/W access */ +} + +static void +ql_ioport_write32(uint16_t addr, uint32_t val, void *priv) +{ + ql_mmio_write32(addr, val, priv); +} + +static void +ql_ioport_write16(uint16_t addr, uint16_t val, void *priv) +{ + ql_mmio_write16(addr, val, priv); +} + +static void +ql_ioport_write8(uint16_t addr, uint8_t val, void *priv) +{ + ql_mmio_write8(addr, val, priv); +} + +static uint16_t +ql_ioport_read16(uint16_t addr, void* priv) +{ + return ql_mmio_read16(addr, priv); +} + +static uint32_t +ql_ioport_read32(uint16_t addr, void* priv) +{ + return ql_mmio_read32(addr, priv); +} + +static uint8_t +ql_ioport_read8(uint16_t addr, void* priv) +{ + return ql_mmio_read8(addr, priv); +} + +static void +ql_pci_remap_rom_mapping(ql_t *dev, bool do_enable) +{ + uint32_t rom_addr; + + if (do_enable) { + rom_addr = dev->pci_cfg[PCI_REG_ROM_BAR_BYTE0]; + rom_addr |= dev->pci_cfg[PCI_REG_ROM_BAR_BYTE1] << 8; + rom_addr |= dev->pci_cfg[PCI_REG_ROM_BAR_BYTE2] << 16; + rom_addr |= dev->pci_cfg[PCI_REG_ROM_BAR_BYTE3] << 24; + rom_addr &= ~0x01; + + ql_log("QL: ROM Base %08lX\n", rom_addr); + mem_mapping_set_addr(&dev->rom_bar_mapping, rom_addr, dev->pci_rom_area_size); + } else { + mem_mapping_disable(&dev->rom_bar_mapping); + } +} + +static void +ql_pci_remap_mmio_mapping(ql_t *dev, bool do_enable) +{ + uint32_t mmio_base; + + if (do_enable) { + mmio_base = dev->pci_cfg[PCI_REG_BAR1_BYTE0]; + mmio_base |= dev->pci_cfg[PCI_REG_BAR1_BYTE1] << 8; + mmio_base |= dev->pci_cfg[PCI_REG_BAR1_BYTE2] << 16; + mmio_base |= dev->pci_cfg[PCI_REG_BAR1_BYTE3] << 24; + mmio_base &= ~0x0F; + + ql_log("QL: MMIO I/O Base %08lX\n", mmio_base); + mem_mapping_set_addr(&dev->mmio_bar_mapping, mmio_base, QL_PCI_MMIO_BAR_SIZE); + } else { + mem_mapping_disable(&dev->mmio_bar_mapping); + } +} + +static void +ql_pci_remap_ioport_mapping(ql_t *dev, bool do_enable) +{ + uint32_t ioport_base; + + ioport_base = dev->pci_cfg[PCI_REG_BAR0_BYTE0]; + ioport_base |= dev->pci_cfg[PCI_REG_BAR0_BYTE1] << 8; + ioport_base |= dev->pci_cfg[PCI_REG_BAR0_BYTE2] << 16; + ioport_base |= dev->pci_cfg[PCI_REG_BAR0_BYTE3] << 24; + ioport_base &= ~0x03; + + if (do_enable) { + ql_log("QL: I/O Base %08lX\n", ioport_base); + io_sethandler(ioport_base, + QL_PCI_IO_BAR_SIZE, + ql_ioport_read8, + ql_ioport_read16, + ql_ioport_read32, + ql_ioport_write8, + ql_ioport_write16, + ql_ioport_write32, + dev); + } else { + io_removehandler(ioport_base, + QL_PCI_IO_BAR_SIZE, + ql_ioport_read8, + ql_ioport_read16, + ql_ioport_read32, + ql_ioport_write8, + ql_ioport_write16, + ql_ioport_write32, + dev); + } +} + +static void +ql_pci_write(UNUSED(int func), int addr, UNUSED(int len), uint8_t val, void *priv) +{ + ql_t *dev = priv; + uint8_t write_bits_mask; + + ql_log("QL: PCI [%2X] <-- %X\n", addr, val); + + assert(addr < 256); + + switch (addr) { + case PCI_REG_COMMAND_L: + write_bits_mask = 0x57; + break; + case PCI_REG_COMMAND_H: + write_bits_mask = 0x01; + break; + + case PCI_REG_CACHELINE_SIZE: + write_bits_mask = 0xFF; + break; + + case PCI_REG_LATENCY_TIMER: + write_bits_mask = 0xF8; + break; + + /* BAR[0] length 0x100 */ + case PCI_REG_BAR0_BYTE0: + write_bits_mask = 0; + break; + case PCI_REG_BAR0_BYTE1: + case PCI_REG_BAR0_BYTE2: + case PCI_REG_BAR0_BYTE3: + write_bits_mask = 0xFF; + break; + + /* BAR[1] length 0x1000 */ + case PCI_REG_BAR1_BYTE0: + write_bits_mask = 0; + break; + case PCI_REG_BAR1_BYTE1: + write_bits_mask = 0xF0; + break; + case PCI_REG_BAR1_BYTE2: + write_bits_mask = 0xFF; + break; + case PCI_REG_BAR1_BYTE3: + write_bits_mask = 0xFF; + break; + + /* ROM BAR */ + case PCI_REG_ROM_BAR_BYTE0: + write_bits_mask = (uint8_t)(dev->rom_bar_mask >> 0); + break; + case PCI_REG_ROM_BAR_BYTE1: + write_bits_mask = (uint8_t)(dev->rom_bar_mask >> 8); + break; + case PCI_REG_ROM_BAR_BYTE2: + write_bits_mask = (uint8_t)(dev->rom_bar_mask >> 16); + break; + case PCI_REG_ROM_BAR_BYTE3: + write_bits_mask = (uint8_t)(dev->rom_bar_mask >> 24); + break; + + case PCI_REG_INT_LINE: + write_bits_mask = 0xFF; + break; + + case 0x40: + write_bits_mask = 0xFF; + break; + case 0x41: + write_bits_mask = 0x03; + break; + + /* PMCSR */ + case QL_PCI_PM_BASE + 4: + write_bits_mask = dev->has_pci_caps ? 0x03 : 0; + break; + case QL_PCI_PM_BASE + 5: + write_bits_mask = dev->has_pci_caps ? 0x1E : 0; + break; + + default: + write_bits_mask = 0; + break; + } + + val &= write_bits_mask; + + /* Disable old BAR mapping and handle command change */ + switch (addr) { + case PCI_REG_COMMAND_L: + if ((val ^ dev->pci_cfg[addr]) & PCI_COMMAND_IO) { + ql_pci_remap_ioport_mapping(dev, !!(val & PCI_COMMAND_IO)); + } + if ((val ^ dev->pci_cfg[addr]) & PCI_COMMAND_MEM) { + ql_pci_remap_mmio_mapping(dev, !!(val & PCI_COMMAND_MEM)); + + if ((val & PCI_COMMAND_MEM) && (dev->pci_cfg[PCI_REG_ROM_BAR_BYTE0] & 0x01)) { + ql_pci_remap_rom_mapping(dev, true); + } else { + ql_pci_remap_rom_mapping(dev, false); + } + } + break; + + case PCI_REG_BAR0_BYTE0: + case PCI_REG_BAR0_BYTE1: + case PCI_REG_BAR0_BYTE2: + case PCI_REG_BAR0_BYTE3: + ql_pci_remap_ioport_mapping(dev, false); + break; + + case PCI_REG_BAR1_BYTE0: + case PCI_REG_BAR1_BYTE1: + case PCI_REG_BAR1_BYTE2: + case PCI_REG_BAR1_BYTE3: + ql_pci_remap_mmio_mapping(dev, false); + break; + + case PCI_REG_ROM_BAR_BYTE0: + case PCI_REG_ROM_BAR_BYTE1: + case PCI_REG_ROM_BAR_BYTE2: + case PCI_REG_ROM_BAR_BYTE3: + ql_pci_remap_rom_mapping(dev, false); + break; + + default: + break; + } + + /* Update PCI register value */ + val |= dev->pci_cfg[addr] & ~write_bits_mask; + dev->pci_cfg[addr] = val; + + /* Enable new BAR mapping */ + switch (addr) { + case PCI_REG_BAR0_BYTE0: + case PCI_REG_BAR0_BYTE1: + case PCI_REG_BAR0_BYTE2: + case PCI_REG_BAR0_BYTE3: + if (dev->pci_cfg[PCI_REG_COMMAND_L] & PCI_COMMAND_IO) { + ql_pci_remap_ioport_mapping(dev, true); + } + break; + + case PCI_REG_BAR1_BYTE0: + case PCI_REG_BAR1_BYTE1: + case PCI_REG_BAR1_BYTE2: + case PCI_REG_BAR1_BYTE3: + if (dev->pci_cfg[PCI_REG_COMMAND_L] & PCI_COMMAND_MEM) { + ql_pci_remap_mmio_mapping(dev, true); + } + break; + + case PCI_REG_ROM_BAR_BYTE0: + case PCI_REG_ROM_BAR_BYTE1: + case PCI_REG_ROM_BAR_BYTE2: + case PCI_REG_ROM_BAR_BYTE3: + if (dev->pci_cfg[PCI_REG_COMMAND_L] & PCI_COMMAND_MEM) { + if (dev->pci_cfg[PCI_REG_ROM_BAR_BYTE0] & 0x01) { + ql_pci_remap_rom_mapping(dev, true); + } + } + break; + + default: + break; + } +} + +static uint8_t +ql_pci_read(UNUSED(int func), int addr, UNUSED(int len), void *priv) +{ + ql_t *dev = priv; + uint8_t ret; + + assert(addr < 256); + + ret = dev->pci_cfg[addr]; + + ql_log("QL: PCI [%2X] --> %X\n", addr, ret); + return ret; +} + +static void ql_init_scsi(ql_t *dev) { + switch (dev->isp_type) { + case QL_ISP1040: + /* Ultra SCSI, 40 MB/s */ + dev->xfer_rate_bps = 40 * 1000000.0; + dev->max_bus_count = 1; + break; + case QL_ISP1080: + /* Ultra2 SCSI, 80 MB/s */ + dev->xfer_rate_bps = 80 * 1000000.0; + dev->max_bus_count = 1; + break; + case QL_ISP1240: + /* Ultra SCSI, 40 MB/s */ + dev->xfer_rate_bps = 40 * 1000000.0; + dev->max_bus_count = 2; + break; + case QL_ISP1280: + /* Ultra2 SCSI, 80 MB/s */ + dev->xfer_rate_bps = 80 * 1000000.0; + dev->max_bus_count = 2; + break; + case QL_ISP12160: + /* Ultra3 SCSI, 160 MB/s */ + dev->xfer_rate_bps = 160 * 1000000.0; + dev->max_bus_count = 2; + break; + + default: + /* Should not happen */ + assert(false); + break; + } + + /* 86Box supports one SCSI bus per controller for now */ + dev->scsi_bus = scsi_get_bus(); + + scsi_bus_set_speed(dev->scsi_bus, dev->xfer_rate_bps); + + timer_add(&dev->cmd_timer, ql_sxp_timer_callback, dev, 0); +} + +static uint8_t +ql_get_eeprom_checksum(const uint8_t* buffer, size_t size) +{ + size_t i; + uint8_t crc = 0; + + for (i = 0; i < size - 1; i++) { + crc += buffer[i]; + } + + return -crc; +} + +static void +ql_create_eeprom_image_1040(uint8_t* nvr) +{ + /* ID header */ + nvr[0x00] = 'I'; + nvr[0x01] = 'S'; + nvr[0x02] = 'P'; + nvr[0x03] = ' '; + /* NVRAM version */ + nvr[0x04] = 7; + + /* ISP config */ + nvr[0x05] = 0x7A; + + /* Bus reset delay */ + nvr[0x06] = 5; + /* Bus retry count */ + nvr[0x07] = 0; + /* Bus retry delay */ + nvr[0x08] = 0; + + /* Bus config */ + nvr[0x09] = 0xF9; + /* Tag age limit */ + nvr[0x0A] = 8; + /* Bus flags */ + nvr[0x0B] = 0x0B; + /* Bus selection timeout */ + nvr[0x0C] = 250; + nvr[0x0D] = 0; + /* Bus max queue depth */ + nvr[0x0E] = 0x00; + nvr[0x0F] = 0x01; + + /* Board type */ + nvr[0x10] = 0x17; + + /* System Vendor */ + nvr[0x14] = 0x77; + nvr[0x15] = 0x10; + /* System ID */ + nvr[0x12] = 0x01; + nvr[0x13] = 0x00; + + /* ISP paramrter */ + nvr[0x16] = 0x03; + nvr[0x17] = 0x00; + + /* FW features */ + nvr[0x18] = 0x01; + + /* Target settings */ + for (uint32_t target_id = 0; target_id < QL_MAX_TID; target_id++) { + const uint32_t tid_offset = 28 + target_id * 6; + + /* Config */ + nvr[tid_offset + 0] = 0xFD; + /* Execution throttle */ + nvr[tid_offset + 1] = 16; + /* Sync period */ + nvr[tid_offset + 2] = 12; + /* Flags */ + nvr[tid_offset + 3] = 0x18; + } + + /* System ID offset in words */ + nvr[0x7E] = 0x09; +} + +static void +ql_create_eeprom_image_1080(ql_t *dev, uint8_t* nvr) +{ + /* ID header */ + nvr[0x00] = 'I'; + nvr[0x01] = 'S'; + nvr[0x02] = 'P'; + nvr[0x03] = ' '; + /* NVRAM version */ + nvr[0x04] = 1; + + /* ISP config */ + nvr[0x10] = 0x44; + /* Bus termination */ + nvr[0x11] = 0x0C; + /* FW features */ + nvr[0x14] = 0x21; + + /* Bus settings */ + for (uint32_t path_id = 0; path_id < dev->max_bus_count; path_id++) { + const uint32_t bus_offset = path_id * 112; + + /* Bus config 1 */ + nvr[bus_offset + 0x18] = 0x67; + /* Bus reset delay */ + nvr[bus_offset + 0x19] = 5; + /* Bus retry count */ + nvr[bus_offset + 0x1A] = 0; + /* Bus retry delay */ + nvr[bus_offset + 0x1B] = 0; + /* Bus config 2 */ + nvr[bus_offset + 0x1C] = 0x39; + /* Bus selection timeout */ + nvr[bus_offset + 0x1E] = 250; + nvr[bus_offset + 0x1F] = 0; + /* Bus max queue depth */ + nvr[bus_offset + 0x20] = 0x00; + nvr[bus_offset + 0x21] = 0x01; + + /* Target settings */ + for (uint32_t target_id = 0; target_id < QL_MAX_TID; target_id++) { + const uint32_t tid_offset = bus_offset + 40 + target_id * 6; + + /* Config */ + nvr[tid_offset + 0] = 0xFD; + /* Execution throttle */ + nvr[tid_offset + 1] = 16; + /* Sync period */ + nvr[tid_offset + 2] = 10; + /* Flags */ + if (dev->isp_type == QL_ISP12160) { + nvr[tid_offset + 3] = 0x30; + } else { + nvr[tid_offset + 3] = 0x18; + } + } + } + + /* System Vendor */ + nvr[0xFA] = 0x77; + nvr[0xFB] = 0x10; + + /* System ID */ + switch (dev->isp_type) { + case QL_ISP1280: + nvr[0xFC] = 0x06; + nvr[0xFD] = 0x00; + break; + case QL_ISP12160: + nvr[0xFC] = 0x07; + nvr[0xFD] = 0x00; + break; + + default: + nvr[0xFC] = 0x01; + nvr[0xFD] = 0x00; + break; + } + + /* System Vendor offset in words */ + if (dev->isp_type != QL_ISP1080) { + nvr[0xFE] = 0xFA; + } +} + +static void +ql_register_eeprom_device(const device_t *info, ql_t *dev) +{ + int inst = device_get_instance(); + nmc93cxx_eeprom_params_t params; + char filename[1024] = { 0 }; + nmc93cxx_eeprom_type nvram_type; + size_t nvram_size; + + if (dev->isp_type == QL_ISP1040) { + nvram_type = NMC_93C46_x16_64; + nvram_size = 2 * 64; + } else { + nvram_type = NMC_93C56_x16_128; + nvram_size = 2 * 128; + } + + uint8_t* nvr = calloc(1, nvram_size); + + if (dev->isp_type == QL_ISP1040) { + ql_create_eeprom_image_1040(nvr); + } else { + ql_create_eeprom_image_1080(dev, nvr); + } + + /* Checksum */ + nvr[nvram_size - 1] = ql_get_eeprom_checksum(nvr, nvram_size); + + snprintf(filename, sizeof(filename), "nmc93cxx_eeprom_%s_%d.nvr", info->internal_name, inst); + params.type = nvram_type; + params.default_content = nvr; + params.filename = filename; + dev->eeprom_device = device_add_inst_params(&nmc93cxx_device, inst, ¶ms); + + free(nvr); +} + +static void +am29_create_flash_image(const device_t *info, flash_t *dev) +{ + FILE *fp; + size_t bytes_written; + const char *bios_path; + + dev->array_data = calloc(1, AM29_FLASH_SIZE); + + snprintf(dev->filename, + sizeof(dev->filename), + "am29f400_option_rom_%s_%d.bin", + info->internal_name, + device_get_instance()); + + /* Load the flash image, if it is already present in the system */ + fp = nvr_fopen(dev->filename, "rb"); + if (fp) { + bytes_written = fread(dev->array_data, 1, AM29_FLASH_SIZE, fp); + } else { + bios_path = device_get_bios_file(info, device_get_config_bios(QL_CFG_BIOS_REVISION), 0); + + /* Clone the ROM data to create a new image */ + fp = rom_fopen(bios_path, "rb"); + if (fp) { + bytes_written = fread(dev->array_data, 1, AM29_FLASH_SIZE, fp); + } else { + bytes_written = 0; + ql_log("Unable to load the AM29 Flash ROM file\n"); + } + } + if (fp) + fclose(fp); + + /* Fill the rest with 0xFF (make the memory content erased) */ + if (bytes_written < AM29_FLASH_SIZE) { + ql_log("Less than %lu bytes read from the AM29 Flash ROM file\n", (uint32_t)bytes_written); + + memset(dev->array_data + bytes_written, 0xFF, AM29_FLASH_SIZE - bytes_written); + } +} + +static void +am29_update_flash_image(flash_t *dev) +{ + FILE *fp = nvr_fopen(dev->filename, "wb"); + + /* Replace the original flash image with new version */ + if (fp) { + fwrite(dev->array_data, AM29_FLASH_SIZE, 1, fp); + fclose(fp); + } + + free(dev->array_data); +} + +static void +am29_init(const device_t *info, flash_t *dev) +{ + uint32_t flash_type = (info->local & QL_DEV_FLASH_TYPE_MASK) >> QL_DEV_FLASH_TYPE_SHIFT; + double access_time_us; + + dev->manufacturer_id = AM29_MANUFACTURER_ID; + dev->block_select_addr_mask = 0x1C000; // A14-A16 + + if (flash_type == QL_FLASH_AM29F010) { + dev->model_id = AM29F010_MODEL_ID; + dev->cmd_cycle_addr_mask = 0x1FFFF; // A0-A16 + dev->addr_5555_phys = 0x5555; + dev->addr_AAAA_phys = 0x2AAA; + dev->program_time_us = 14.0; + dev->block_erase_time_us = 200000.0; // 0.2 sec + dev->chip_erase_time_us = 1000000.0; // 1 sec + access_time_us = 0.045; + } else { + dev->model_id = AM29LV010B_MODEL_ID; + dev->cmd_cycle_addr_mask = 0x7FF; // A0-A10 + dev->addr_5555_phys = 0x555; + dev->addr_AAAA_phys = 0x2AA; + dev->program_time_us = 9.0; + dev->block_erase_time_us = 200000.0; // 0.2 sec + dev->chip_erase_time_us = 1000000.0; // 1 sec + access_time_us = 0.070; + } + dev->access_cycles = (cpuclock / (1000000.0 / access_time_us)); + + dev->block[0].start_addr = 0x00000; + dev->block[0].end_addr = 0x03FFF; + + dev->block[1].start_addr = 0x04000; + dev->block[1].end_addr = 0x07FFF; + + dev->block[2].start_addr = 0x08000; + dev->block[2].end_addr = 0x0BFFF; + + dev->block[3].start_addr = 0x0C000; + dev->block[3].end_addr = 0x0FFFF; + + dev->block[4].start_addr = 0x10000; + dev->block[4].end_addr = 0x13FFF; + + dev->block[5].start_addr = 0x14000; + dev->block[5].end_addr = 0x17FFF; + + dev->block[6].start_addr = 0x18000; + dev->block[6].end_addr = 0x1BFFF; + + dev->block[7].start_addr = 0x1C000; + dev->block[7].end_addr = 0x1FFFF; + + am29_create_flash_image(info, dev); + am29_set_mode(dev, M_READ_ARRAY); + + timer_add(&dev->erase_accept_timeout_timer, am29_erase_begin_timer_callback, dev, 0); + timer_add(&dev->cmd_complete_timer, am29_cmd_complete_timer_callback, dev, 0); + + /* Assign block numbers */ + for (uint32_t i = 0; i < AM29_MAX_BLOCKS; i++) { + flash_block_t *block = &dev->block[i]; + + /* We maintain a bitmap of blocks to erase */ + assert(i < 32); + assert(sizeof(dev->blocks_to_erase_bitmap) == sizeof(uint32_t)); + + block->number = i; + } +} + +static void +ql_init_pci_config(ql_t *dev) +{ + const uint8_t *eeprom_data = (const uint8_t *)nmc93cxx_eeprom_data(dev->eeprom_device); + + memset(dev->pci_cfg, 0, sizeof(dev->pci_cfg)); + + dev->pci_cfg[PCI_REG_STATUS_L] = PCI_STATUS_L_FAST_B2B | PCI_STATUS_L_CAPAB; + dev->pci_cfg[PCI_REG_STATUS_H] = PCI_DEVSEL_MEDIUM; + + /* QLA1xxx */ + switch (dev->isp_type) { + case QL_ISP1040: + dev->pci_cfg[PCI_REG_DEVICE_ID_L] = 0x20; + dev->pci_cfg[PCI_REG_DEVICE_ID_H] = 0x10; + dev->pci_cfg[PCI_REG_REVISION] = 0x05; + break; + case QL_ISP1080: + dev->pci_cfg[PCI_REG_DEVICE_ID_L] = 0x80; + dev->pci_cfg[PCI_REG_DEVICE_ID_H] = 0x10; + dev->pci_cfg[PCI_REG_REVISION] = 0x01; + break; + case QL_ISP1240: + dev->pci_cfg[PCI_REG_DEVICE_ID_L] = 0x40; + dev->pci_cfg[PCI_REG_DEVICE_ID_H] = 0x12; + dev->pci_cfg[PCI_REG_REVISION] = 0x01; + break; + case QL_ISP1280: + dev->pci_cfg[PCI_REG_DEVICE_ID_L] = 0x80; + dev->pci_cfg[PCI_REG_DEVICE_ID_H] = 0x12; + dev->pci_cfg[PCI_REG_REVISION] = 0x01; + break; + case QL_ISP12160: + dev->pci_cfg[PCI_REG_DEVICE_ID_L] = 0x16; + dev->pci_cfg[PCI_REG_DEVICE_ID_H] = 0x12; + dev->pci_cfg[PCI_REG_REVISION] = 0x06; + dev->pci_cfg[PCI_REG_STATUS_L] |= PCI_STATUS_L_66MHZ; + break; + + default: + /* Should not happen */ + assert(false); + break; + } + + /* Actual system ID comes from NVRAM. The ISP1040 system ID words are in swapped order */ + if (dev->isp_type == QL_ISP1040) { + dev->pci_cfg[PCI_REG_SUBVEN_ID_L] = eeprom_data[0x14]; + dev->pci_cfg[PCI_REG_SUBVEN_ID_H] = eeprom_data[0x15]; + dev->pci_cfg[PCI_REG_SUBSYS_ID_L] = eeprom_data[0x12]; + dev->pci_cfg[PCI_REG_SUBSYS_ID_H] = eeprom_data[0x13]; + } else { + dev->pci_cfg[PCI_REG_SUBVEN_ID_L] = eeprom_data[0xFA]; + dev->pci_cfg[PCI_REG_SUBVEN_ID_H] = eeprom_data[0xFB]; + dev->pci_cfg[PCI_REG_SUBSYS_ID_L] = eeprom_data[0xFC]; + dev->pci_cfg[PCI_REG_SUBSYS_ID_H] = eeprom_data[0xFD]; + } + + /* QLogic */ + dev->pci_cfg[PCI_REG_VENDOR_ID_L] = 0x77; + dev->pci_cfg[PCI_REG_VENDOR_ID_H] = 0x10; + + /* SCSI Controller */ + dev->pci_cfg[PCI_REG_CLASS] = 0x01; + + dev->pci_cfg[PCI_REG_CACHELINE_SIZE] = 64; + dev->pci_cfg[PCI_REG_LATENCY_TIMER] = 248; + dev->pci_cfg[PCI_REG_INT_PIN] = PCI_INTA; + + /* BAR[0] I/O ports */ + dev->pci_cfg[PCI_REG_BAR0_BYTE0] = 0x01; + + /* BAR[1] Memory */ + dev->pci_cfg[PCI_REG_BAR1_BYTE0] = 0; + + dev->pci_cfg[0x40] = 0x44; + + if (dev->has_pci_caps) { + dev->pci_cfg[PCI_REG_STATUS_L] |= PCI_STATUS_L_CAPAB; + + dev->pci_cfg[PCI_REG_CAPS_PTR] = QL_PCI_PM_BASE; + + /* Power management capabilities */ + dev->pci_cfg[QL_PCI_PM_BASE + 0] = 0x01; // POWER MANAGEMENT + dev->pci_cfg[QL_PCI_PM_BASE + 1] = 0x00; // Last entry + /* PMC */ + dev->pci_cfg[QL_PCI_PM_BASE + 2] = 0x01; // Version 1.0 + dev->pci_cfg[QL_PCI_PM_BASE + 3] = 0x00; + /* PMCSR */ + dev->pci_cfg[QL_PCI_PM_BASE + 4] = 0x00; + dev->pci_cfg[QL_PCI_PM_BASE + 5] = 0x00; + /* PMCSR_BSE */ + dev->pci_cfg[QL_PCI_PM_BASE + 6] = 0x00; + /* Data */ + dev->pci_cfg[QL_PCI_PM_BASE + 7] = 0x00; + } + + /* This area for some reason holds the VenID/DevID pair */ + for (uint32_t reg = 0x4C; reg < sizeof(dev->pci_cfg); reg += 4) { + dev->pci_cfg[reg + 0] = dev->pci_cfg[PCI_REG_VENDOR_ID_L]; + dev->pci_cfg[reg + 1] = dev->pci_cfg[PCI_REG_VENDOR_ID_H]; + dev->pci_cfg[reg + 2] = dev->pci_cfg[PCI_REG_DEVICE_ID_L]; + dev->pci_cfg[reg + 3] = dev->pci_cfg[PCI_REG_DEVICE_ID_H]; + } +} + +static void +ql_reset(void *priv) +{ + ql_t *dev = priv; + + /* Clear all BAR memory mappings and I/O handlers */ + ql_pci_remap_ioport_mapping(dev, false); + ql_pci_remap_mmio_mapping(dev, false); + ql_pci_remap_rom_mapping(dev, false); + + /* Reset PCI configuration registers */ + ql_init_pci_config(dev); + + ql_reset_asic(dev); + am29_reset(&dev->flash_device); +} + +static void * +ql_init(const device_t *info) +{ + ql_t *dev = calloc(1, sizeof(ql_t)); + + dev->isp_type = info->local & QL_DEV_CHIP_TYPE_MASK; + dev->isp_rev = (info->local & QL_DEV_CHIP_REV_MASK) >> QL_DEV_CHIP_REV_SHIFT; + + dev->fw_version = device_get_bios_local(device_context_get_device(), device_get_config_bios(QL_CFG_BIOS_REVISION)); + dev->has_pci_caps = (dev->isp_type != QL_ISP1040); + + /* + * Determine size of the area to map the expansion ROM. + * NOTE: On most ISP chips this area is smaller than the FLASH size. + * The QLogic boot code reads the required data directly from the FLASH at run-time. + */ + if (dev->isp_type == QL_ISP12160) { + dev->pci_rom_area_size = QL_PCI_ROM_BAR_128K_SIZE; + } else { + dev->pci_rom_area_size = QL_PCI_ROM_BAR_64K_SIZE; + } + + /* Determine writable bits of the ROM BAR */ + if (!device_get_config_int(QL_CFG_BIOS_ENABLE)) { + dev->rom_bar_mask = 0; + } else { + uint32_t length = dev->pci_rom_area_size; + uint32_t ln2size = 0; + + while (length != 1) { + ln2size++; + length >>= 1; + } + + dev->rom_bar_mask = ~((1 << ln2size) - 1); + dev->rom_bar_mask |= 1; // Expansion ROM enable bit + } + + ql_init_scsi(dev); + am29_init(info, &dev->flash_device); + ql_register_eeprom_device(info, dev); + ql_init_pci_config(dev); + ql_reset_asic(dev); + + mem_mapping_add(&dev->rom_bar_mapping, + 0, + 0, + ql_rom_bar_mmio_read8, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + MEM_MAPPING_EXTERNAL, + dev); + mem_mapping_disable(&dev->rom_bar_mapping); + + mem_mapping_add(&dev->mmio_bar_mapping, + 0, + 0, + ql_mmio_read8, + ql_mmio_read16, + ql_mmio_read32, + ql_mmio_write8, + ql_mmio_write16, + ql_mmio_write32, + NULL, + MEM_MAPPING_EXTERNAL, + dev); + mem_mapping_disable(&dev->mmio_bar_mapping); + + pci_add_card(PCI_CARD_NORMAL, ql_pci_read, ql_pci_write, dev, &dev->pci_slot); + return dev; +} + +static void +ql_close(void *priv) +{ + ql_t *dev = priv; + + am29_update_flash_image(&dev->flash_device); + + fifo8_destroy(&dev->abort_iocbs_fifo); + + if (dev->scsi_data_buffer) { + free(dev->scsi_data_buffer); + } + free(dev); +} + +// clang-format off +static const device_config_t qla1040b_config[] = { + { + .name = QL_CFG_BIOS_REVISION, + .description = "BIOS Revision", + .type = CONFIG_BIOS, + .default_string = "v6_26", + .default_int = 0, + .file_filter = NULL, + .spinner = { 0 }, + .bios = { + { + .name = "Version 6.20", + .internal_name = "v6_20", + .bios_type = BIOS_NORMAL, + .files_no = 1, + .local = ISP_FW_VER(4, 53, 0), + .size = 0x10000, + .files = { "roms/scsi/qlogic/qla1040_v6_20.bin", "" } + }, + { + .name = "Version 6.26", + .internal_name = "v6_26", + .bios_type = BIOS_NORMAL, + .files_no = 1, + .local = ISP_FW_VER(4, 55, 0), + .size = 0x10000, + .files = { "roms/scsi/qlogic/qla1040_v6_26.bin", "" } + }, + { .files_no = 0 } + }, + }, + { + .name = QL_CFG_BIOS_ENABLE, + .description = "Enable BIOS", + .type = CONFIG_BINARY, + .default_string = NULL, + .default_int = 1, + .file_filter = NULL, + .spinner = { 0 }, + .selection = { { 0 } }, + .bios = { { 0 } } + }, + { .name = "", .description = "", .type = CONFIG_END } +}; + +static const device_config_t qla1080_config[] = { + { + .name = QL_CFG_BIOS_REVISION, + .description = "BIOS Revision", + .type = CONFIG_BIOS, + .default_string = "v1_19", + .default_int = 0, + .file_filter = NULL, + .spinner = { 0 }, + .bios = { + { + .name = "Version 1.11", + .internal_name = "v1_11", + .bios_type = BIOS_NORMAL, + .files_no = 1, + .local = ISP_FW_VER(2, 13, 0), + .size = 0x10000, + .files = { "roms/scsi/qlogic/qla1080_v1_11.bin", "" } + }, + { + .name = "Version 1.16", + .internal_name = "v1_16", + .bios_type = BIOS_NORMAL, + .files_no = 1, + .local = ISP_FW_VER(8, 3, 0), + .size = 0x20000, + .files = { "roms/scsi/qlogic/qla1080_v1_16.bin", "" } + }, + { + .name = "Version 1.19", + .internal_name = "v1_19", + .bios_type = BIOS_NORMAL, + .files_no = 1, + .local = ISP_FW_VER(8, 9, 0), + .size = 0x20000, + .files = { "roms/scsi/qlogic/qla1080_v1_19.bin", "" } + }, + { .files_no = 0 } + }, + }, + { + .name = QL_CFG_BIOS_ENABLE, + .description = "Enable BIOS", + .type = CONFIG_BINARY, + .default_string = NULL, + .default_int = 1, + .file_filter = NULL, + .spinner = { 0 }, + .selection = { { 0 } }, + .bios = { { 0 } } + }, + { .name = "", .description = "", .type = CONFIG_END } +}; + +static const device_config_t qla1240_config[] = { + { + .name = QL_CFG_BIOS_REVISION, + .description = "BIOS Revision", + .type = CONFIG_BIOS, + .default_string = "v1_26", + .default_int = 0, + .file_filter = NULL, + .spinner = { 0 }, + .bios = { + { + .name = "Version 1.26", + .internal_name = "v1_26", + .bios_type = BIOS_NORMAL, + .files_no = 1, + .local = ISP_FW_VER(2, 13, 0), + .size = 0x10000, + .files = { "roms/scsi/qlogic/qla1240_v1_26.bin", "" } + }, + { .files_no = 0 } + }, + }, + { + .name = QL_CFG_BIOS_ENABLE, + .description = "Enable BIOS", + .type = CONFIG_BINARY, + .default_string = NULL, + .default_int = 1, + .file_filter = NULL, + .spinner = { 0 }, + .selection = { { 0 } }, + .bios = { { 0 } } + }, + { .name = "", .description = "", .type = CONFIG_END } +}; + +static const device_config_t qla1280_config[] = { + { + .name = QL_CFG_BIOS_REVISION, + .description = "BIOS Revision", + .type = CONFIG_BIOS, + .default_string = "v1_30", + .default_int = 0, + .file_filter = NULL, + .spinner = { 0 }, + .bios = { + { + .name = "Version 1.30", + .internal_name = "v1_30", + .bios_type = BIOS_NORMAL, + .files_no = 1, + .local = ISP_FW_VER(8, 15, 0), + .size = 0x20000, + .files = { "roms/scsi/qlogic/qla1280_v1_30.bin", "" } + }, + { .files_no = 0 } + }, + }, + { + .name = QL_CFG_BIOS_ENABLE, + .description = "Enable BIOS", + .type = CONFIG_BINARY, + .default_string = NULL, + .default_int = 1, + .file_filter = NULL, + .spinner = { 0 }, + .selection = { { 0 } }, + .bios = { { 0 } } + }, + { .name = "", .description = "", .type = CONFIG_END } +}; + +static const device_config_t qla12160a_config[] = { + { + .name = QL_CFG_BIOS_REVISION, + .description = "BIOS Revision", + .type = CONFIG_BIOS, + .default_string = "v1_37", + .default_int = 0, + .file_filter = NULL, + .spinner = { 0 }, + .bios = { + { + .name = "Version 1.34", + .internal_name = "v1_34", + .bios_type = BIOS_NORMAL, + .files_no = 1, + .local = ISP_FW_VER(10, 4, 0), + .size = 0x20000, + .files = { "roms/scsi/qlogic/qla12160_v1_34.bin", "" } + }, + { + .name = "Version 1.37", + .internal_name = "v1_37", + .bios_type = BIOS_NORMAL, + .files_no = 1, + .local = ISP_FW_VER(10, 4, 0), + .size = 0x20000, + .files = { "roms/scsi/qlogic/qla12160_v1_37.bin", "" } + }, + { .files_no = 0 } + }, + }, + { + .name = QL_CFG_BIOS_ENABLE, + .description = "Enable BIOS", + .type = CONFIG_BINARY, + .default_string = NULL, + .default_int = 1, + .file_filter = NULL, + .spinner = { 0 }, + .selection = { { 0 } }, + .bios = { { 0 } } + }, + { .name = "", .description = "", .type = CONFIG_END } +}; +// clang-format on + +const device_t qla1040b_device = { + .name = "QLogic QLA1040B", + .internal_name = "qla1040b", + .flags = DEVICE_PCI, + .local = QL_ISP1040 | QL_REV_ISP1040B | QL_FLASH_AM29F010, + .init = ql_init, + .close = ql_close, + .reset = ql_reset, + .available = NULL, + .speed_changed = NULL, + .force_redraw = NULL, + .config = qla1040b_config, +}; + +const device_t qla1080_device = { + .name = "QLogic QLA1080", + .internal_name = "qla1080", + .flags = DEVICE_PCI, + .local = QL_ISP1080 | QL_REV_ISP1080 | QL_FLASH_AM29F010, + .init = ql_init, + .close = ql_close, + .reset = ql_reset, + .available = NULL, + .speed_changed = NULL, + .force_redraw = NULL, + .config = qla1080_config, +}; + +const device_t qla1240_device = { + .name = "QLogic QLA1240", + .internal_name = "qla1240", + .flags = DEVICE_PCI, + .local = QL_ISP1240 | QL_REV_ISP1080 | QL_FLASH_AM29LV010B, + .init = ql_init, + .close = ql_close, + .reset = ql_reset, + .available = NULL, + .speed_changed = NULL, + .force_redraw = NULL, + .config = qla1240_config, +}; + +const device_t qla1280_device = { + .name = "QLogic QLA1280", + .internal_name = "qla1280", + .flags = DEVICE_PCI, + .local = QL_ISP1280 | QL_REV_ISP1080 | QL_FLASH_AM29LV010B, + .init = ql_init, + .close = ql_close, + .reset = ql_reset, + .available = NULL, + .speed_changed = NULL, + .force_redraw = NULL, + .config = qla1280_config, +}; + +const device_t qla12160a_device = { + .name = "QLogic QLA12160A", + .internal_name = "qla12160a", + .flags = DEVICE_PCI, + .local = QL_ISP12160 | QL_REV_ISP1080 | QL_FLASH_AM29LV010B, + .init = ql_init, + .close = ql_close, + .reset = ql_reset, + .available = NULL, + .speed_changed = NULL, + .force_redraw = NULL, + .config = qla12160a_config, +}; From f0a3c3707362de3fb911d29371df0189875fea11 Mon Sep 17 00:00:00 2001 From: TC1995 Date: Sun, 22 Feb 2026 18:45:40 +0100 Subject: [PATCH 24/25] 8514/A: Correct interlaced refresh rates And when the accel command is bitblt (6), the patterns and'ed to 3 are from the destination x register. --- src/include/86box/vid_8514a.h | 2 + src/video/vid_8514a.c | 220 +++++++++++++++++++++++----------- src/video/vid_ati_mach8.c | 8 +- 3 files changed, 156 insertions(+), 74 deletions(-) diff --git a/src/include/86box/vid_8514a.h b/src/include/86box/vid_8514a.h index d0cd25547..f0c9fdf24 100644 --- a/src/include/86box/vid_8514a.h +++ b/src/include/86box/vid_8514a.h @@ -65,6 +65,7 @@ typedef union { typedef struct ibm8514_t { rom_t bios_rom; + rom_t bios_rom2; uint8_t *rom1; uint8_t *rom2; hwcursor8514_t hwcursor; @@ -187,6 +188,7 @@ typedef struct ibm8514_t { uint32_t dst_ge_offset; uint16_t src_pitch; uint16_t dst_pitch; + uint16_t read_pixel; int64_t cur_x_24bpp; int64_t cur_y_24bpp; int64_t dest_x_24bpp; diff --git a/src/video/vid_8514a.c b/src/video/vid_8514a.c index d44d9e9d8..e152e98a5 100644 --- a/src/video/vid_8514a.c +++ b/src/video/vid_8514a.c @@ -1121,83 +1121,159 @@ ibm8514_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat if (pixcntl == 1) { mix_dat = 0; - if (and3 == 3) { - if (dev->accel.multifunc[8] & 0x02) - mix_dat |= 0x08; - if (dev->accel.multifunc[8] & 0x04) - mix_dat |= 0x10; - if (dev->accel.multifunc[8] & 0x08) - mix_dat |= 0x20; - if (dev->accel.multifunc[8] & 0x10) - mix_dat |= 0x40; - if (dev->accel.multifunc[9] & 0x02) - mix_dat |= 0x80; - if (dev->accel.multifunc[9] & 0x04) - mix_dat |= 0x01; - if (dev->accel.multifunc[9] & 0x08) - mix_dat |= 0x02; - if (dev->accel.multifunc[9] & 0x10) - mix_dat |= 0x04; - } - if (and3 == 2) { - if (dev->accel.multifunc[8] & 0x02) - mix_dat |= 0x04; - if (dev->accel.multifunc[8] & 0x04) - mix_dat |= 0x08; - if (dev->accel.multifunc[8] & 0x08) - mix_dat |= 0x10; - if (dev->accel.multifunc[8] & 0x10) - mix_dat |= 0x20; - if (dev->accel.multifunc[9] & 0x02) - mix_dat |= 0x40; - if (dev->accel.multifunc[9] & 0x04) - mix_dat |= 0x80; - if (dev->accel.multifunc[9] & 0x08) - mix_dat |= 0x01; - if (dev->accel.multifunc[9] & 0x10) - mix_dat |= 0x02; - } - if (and3 == 1) { - if (dev->accel.multifunc[8] & 0x02) - mix_dat |= 0x02; - if (dev->accel.multifunc[8] & 0x04) - mix_dat |= 0x04; - if (dev->accel.multifunc[8] & 0x08) - mix_dat |= 0x08; - if (dev->accel.multifunc[8] & 0x10) - mix_dat |= 0x10; - if (dev->accel.multifunc[9] & 0x02) - mix_dat |= 0x20; - if (dev->accel.multifunc[9] & 0x04) - mix_dat |= 0x40; - if (dev->accel.multifunc[9] & 0x08) - mix_dat |= 0x80; - if (dev->accel.multifunc[9] & 0x10) - mix_dat |= 0x01; - } - if (and3 == 0) { - if (dev->accel.multifunc[8] & 0x02) - mix_dat |= 0x01; - if (dev->accel.multifunc[8] & 0x04) - mix_dat |= 0x02; - if (dev->accel.multifunc[8] & 0x08) - mix_dat |= 0x04; - if (dev->accel.multifunc[8] & 0x10) - mix_dat |= 0x08; - if (dev->accel.multifunc[9] & 0x02) - mix_dat |= 0x10; - if (dev->accel.multifunc[9] & 0x04) - mix_dat |= 0x20; - if (dev->accel.multifunc[9] & 0x08) - mix_dat |= 0x40; - if (dev->accel.multifunc[9] & 0x10) - mix_dat |= 0x80; + if (cmd == 6) { + if (and3_blt == 3) { + if (dev->accel.multifunc[8] & 0x02) + mix_dat |= 0x08; + if (dev->accel.multifunc[8] & 0x04) + mix_dat |= 0x10; + if (dev->accel.multifunc[8] & 0x08) + mix_dat |= 0x20; + if (dev->accel.multifunc[8] & 0x10) + mix_dat |= 0x40; + if (dev->accel.multifunc[9] & 0x02) + mix_dat |= 0x80; + if (dev->accel.multifunc[9] & 0x04) + mix_dat |= 0x01; + if (dev->accel.multifunc[9] & 0x08) + mix_dat |= 0x02; + if (dev->accel.multifunc[9] & 0x10) + mix_dat |= 0x04; + } + if (and3_blt == 2) { + if (dev->accel.multifunc[8] & 0x02) + mix_dat |= 0x04; + if (dev->accel.multifunc[8] & 0x04) + mix_dat |= 0x08; + if (dev->accel.multifunc[8] & 0x08) + mix_dat |= 0x10; + if (dev->accel.multifunc[8] & 0x10) + mix_dat |= 0x20; + if (dev->accel.multifunc[9] & 0x02) + mix_dat |= 0x40; + if (dev->accel.multifunc[9] & 0x04) + mix_dat |= 0x80; + if (dev->accel.multifunc[9] & 0x08) + mix_dat |= 0x01; + if (dev->accel.multifunc[9] & 0x10) + mix_dat |= 0x02; + } + if (and3_blt == 1) { + if (dev->accel.multifunc[8] & 0x02) + mix_dat |= 0x02; + if (dev->accel.multifunc[8] & 0x04) + mix_dat |= 0x04; + if (dev->accel.multifunc[8] & 0x08) + mix_dat |= 0x08; + if (dev->accel.multifunc[8] & 0x10) + mix_dat |= 0x10; + if (dev->accel.multifunc[9] & 0x02) + mix_dat |= 0x20; + if (dev->accel.multifunc[9] & 0x04) + mix_dat |= 0x40; + if (dev->accel.multifunc[9] & 0x08) + mix_dat |= 0x80; + if (dev->accel.multifunc[9] & 0x10) + mix_dat |= 0x01; + } + if (and3_blt == 0) { + if (dev->accel.multifunc[8] & 0x02) + mix_dat |= 0x01; + if (dev->accel.multifunc[8] & 0x04) + mix_dat |= 0x02; + if (dev->accel.multifunc[8] & 0x08) + mix_dat |= 0x04; + if (dev->accel.multifunc[8] & 0x10) + mix_dat |= 0x08; + if (dev->accel.multifunc[9] & 0x02) + mix_dat |= 0x10; + if (dev->accel.multifunc[9] & 0x04) + mix_dat |= 0x20; + if (dev->accel.multifunc[9] & 0x08) + mix_dat |= 0x40; + if (dev->accel.multifunc[9] & 0x10) + mix_dat |= 0x80; + } + } else { + if (and3 == 3) { + if (dev->accel.multifunc[8] & 0x02) + mix_dat |= 0x08; + if (dev->accel.multifunc[8] & 0x04) + mix_dat |= 0x10; + if (dev->accel.multifunc[8] & 0x08) + mix_dat |= 0x20; + if (dev->accel.multifunc[8] & 0x10) + mix_dat |= 0x40; + if (dev->accel.multifunc[9] & 0x02) + mix_dat |= 0x80; + if (dev->accel.multifunc[9] & 0x04) + mix_dat |= 0x01; + if (dev->accel.multifunc[9] & 0x08) + mix_dat |= 0x02; + if (dev->accel.multifunc[9] & 0x10) + mix_dat |= 0x04; + } + if (and3 == 2) { + if (dev->accel.multifunc[8] & 0x02) + mix_dat |= 0x04; + if (dev->accel.multifunc[8] & 0x04) + mix_dat |= 0x08; + if (dev->accel.multifunc[8] & 0x08) + mix_dat |= 0x10; + if (dev->accel.multifunc[8] & 0x10) + mix_dat |= 0x20; + if (dev->accel.multifunc[9] & 0x02) + mix_dat |= 0x40; + if (dev->accel.multifunc[9] & 0x04) + mix_dat |= 0x80; + if (dev->accel.multifunc[9] & 0x08) + mix_dat |= 0x01; + if (dev->accel.multifunc[9] & 0x10) + mix_dat |= 0x02; + } + if (and3 == 1) { + if (dev->accel.multifunc[8] & 0x02) + mix_dat |= 0x02; + if (dev->accel.multifunc[8] & 0x04) + mix_dat |= 0x04; + if (dev->accel.multifunc[8] & 0x08) + mix_dat |= 0x08; + if (dev->accel.multifunc[8] & 0x10) + mix_dat |= 0x10; + if (dev->accel.multifunc[9] & 0x02) + mix_dat |= 0x20; + if (dev->accel.multifunc[9] & 0x04) + mix_dat |= 0x40; + if (dev->accel.multifunc[9] & 0x08) + mix_dat |= 0x80; + if (dev->accel.multifunc[9] & 0x10) + mix_dat |= 0x01; + } + if (and3 == 0) { + if (dev->accel.multifunc[8] & 0x02) + mix_dat |= 0x01; + if (dev->accel.multifunc[8] & 0x04) + mix_dat |= 0x02; + if (dev->accel.multifunc[8] & 0x08) + mix_dat |= 0x04; + if (dev->accel.multifunc[8] & 0x10) + mix_dat |= 0x08; + if (dev->accel.multifunc[9] & 0x02) + mix_dat |= 0x10; + if (dev->accel.multifunc[9] & 0x04) + mix_dat |= 0x20; + if (dev->accel.multifunc[9] & 0x08) + mix_dat |= 0x40; + if (dev->accel.multifunc[9] & 0x10) + mix_dat |= 0x80; + } } } old_mix_dat = mix_dat; - ibm8514_log("CMD=%d, full=%04x, pixcntl=%d, filling=%02x, ssvdraw=%02x.\n", cmd, dev->accel.cmd, pixcntl, dev->accel.multifunc[0x0a] & 0x06, dev->accel.ssv_draw); + if (!(dev->accel.cmd & 0x01)) + ibm8514_log("CMD=%d, full=%04x, pixcntl=%d, filling=%02x, ssvdraw=%02x.\n", cmd, dev->accel.cmd, pixcntl, dev->accel.multifunc[0x0a] & 0x06, dev->accel.ssv_draw); /*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 diff --git a/src/video/vid_ati_mach8.c b/src/video/vid_ati_mach8.c index ab38e26f6..58a54fb48 100644 --- a/src/video/vid_ati_mach8.c +++ b/src/video/vid_ati_mach8.c @@ -3021,8 +3021,10 @@ ati8514_recalctimings(svga_t *svga) if (dev->ven_clock & 0x40) svga->clock_8514 *= 2.0; - if (dev->interlace) + if (dev->interlace) { dev->dispend >>= 1; + svga->clock_8514 /= 2.0; + } mach_log("cntl=%d, hv(%d,%d), pitch=%d, rowoffset=%d, gextconfig=%03x, shadow=%x interlace=%d.\n", dev->accel.advfunc_cntl & 0x04, dev->h_disp, dev->dispend, dev->pitch, dev->rowoffset, @@ -3183,8 +3185,10 @@ mach_recalctimings(svga_t *svga) mach_log("8514/A modes=%d, clocksel=%02x, clkselmode=%02x, divide reg ibm=%02x, divide reg vga=%02x, vgainterlace=%x, interlace=%x, htotal=%02x.\n", _8514_modes, mach->accel.clock_sel & 0xfe, mach->accel.clock_sel_mode & 0xfe, mach->accel.clock_sel & 0x40, mach->regs[0xb8] & 0x40, svga->interlace, dev->interlace, dev->htotal); - if (dev->interlace) + if (dev->interlace) { dev->dispend >>= 1; + svga->clock_8514 /= 2.0; + } if (ATI_MACH32) { switch ((mach->shadow_set >> 8) & 0x03) { From 81ba889f9e03d91a91778fd8db31ad6e423c1af7 Mon Sep 17 00:00:00 2001 From: TC1995 Date: Sun, 22 Feb 2026 21:05:39 +0100 Subject: [PATCH 25/25] Sort out the IRQ/INT enabling/disabling stuff of the 5380/53c80 chip. Allowing proper operation of the IRQ in the 53c400 and, at the same time, recognizing the RTASPI10.SYS driver of the Rancho RT1000B. Code based on the 53c400 manual and MAME. TODO: Verify the Trantor T128 scheme. --- src/include/86box/scsi_ncr5380.h | 1 + src/scsi/scsi_ncr5380.c | 22 ++++--- src/scsi/scsi_ncr53c400.c | 103 +++++++++++++++++++------------ src/scsi/scsi_t128.c | 10 ++- 4 files changed, 86 insertions(+), 50 deletions(-) diff --git a/src/include/86box/scsi_ncr5380.h b/src/include/86box/scsi_ncr5380.h index 6453340ae..151a9873c 100644 --- a/src/include/86box/scsi_ncr5380.h +++ b/src/include/86box/scsi_ncr5380.h @@ -84,6 +84,7 @@ typedef struct ncr_t { int (*dma_send_ext)(void *priv, void *ext_priv); int (*dma_initiator_receive_ext)(void *priv, void *ext_priv); void (*timer)(void *ext_priv, double period); + int (*irq_ena)(void *priv, void *ext_priv, int state); scsi_bus_t scsibus; } ncr_t; diff --git a/src/scsi/scsi_ncr5380.c b/src/scsi/scsi_ncr5380.c index 102e637c4..2a29b070d 100644 --- a/src/scsi/scsi_ncr5380.c +++ b/src/scsi/scsi_ncr5380.c @@ -62,13 +62,9 @@ void ncr5380_irq(ncr_t *ncr, int set_irq) { if (set_irq) { - ncr->irq_state = 1; - ncr->isr |= STATUS_INT; if (ncr->irq != -1) picint(1 << ncr->irq); } else { - ncr->irq_state = 0; - ncr->isr &= ~STATUS_INT; if (ncr->irq != 1) picintc(1 << ncr->irq); } @@ -104,7 +100,12 @@ ncr5380_reset(ncr_t *ncr) scsi_bus->data = 0; scsi_bus->command_issued = 0; - ncr5380_irq(ncr, 0); + if (ncr->irq_ena) + ncr->irq_ena(ncr, ncr->priv, 0); + else + ncr5380_irq(ncr, 0); + + ncr->isr &= ~STATUS_INT; } uint32_t @@ -164,7 +165,7 @@ ncr5380_write(uint16_t port, uint8_t val, ncr_t *ncr) if ((val & 0x80) && !(ncr->icr & 0x80)) { ncr5380_log("Resetting the 5380\n"); ncr5380_reset(ncr); - ncr5380_irq(ncr, 1); + ncr->isr |= STATUS_INT; } ncr->icr = val; ncr5380_log("ICR WaitData=%d, ClearReq=%d.\n", scsi_bus->wait_data, scsi_bus->clear_req); @@ -301,7 +302,7 @@ ncr5380_read(uint16_t port, ncr_t *ncr) if (bus & BUS_MSG) bus_state |= TCR_MSG; if ((ncr->tcr & 7) != bus_state) { - ncr5380_irq(ncr, 1); + ncr->isr |= STATUS_INT; ncr5380_log("IRQ issued\n"); } } @@ -321,7 +322,12 @@ ncr5380_read(uint16_t port, ncr_t *ncr) case 7: /* reset Parity/Interrupt */ ncr->isr &= ~(STATUS_BUSY_ERROR | 0x20); - ncr5380_irq(ncr, 0); + if (ncr->irq_ena) + ncr->irq_ena(ncr, ncr->priv, 0); + else + ncr5380_irq(ncr, 0); + + ncr->isr &= ~STATUS_INT; ncr5380_log("Reset Interrupt\n"); break; diff --git a/src/scsi/scsi_ncr53c400.c b/src/scsi/scsi_ncr53c400.c index 31a2de8b3..c8520d082 100644 --- a/src/scsi/scsi_ncr53c400.c +++ b/src/scsi/scsi_ncr53c400.c @@ -72,6 +72,7 @@ typedef struct ncr53c400_t { int8_t type; uint8_t block_count; uint8_t status_ctrl; + uint8_t irq_config; int block_count_loaded; @@ -103,6 +104,23 @@ ncr53c400_log(const char *fmt, ...) # define ncr53c400_log(fmt, ...) #endif +static int +ncr53c400_irq_enable(void *priv, void *ext_priv, int state) +{ + ncr53c400_t *ncr400 = (ncr53c400_t *) ext_priv; + ncr_t *ncr = (ncr_t *) priv; + + if (ncr->irq_state != state) { + ncr->irq_state = state; + ncr400->status_ctrl &= ~0x01; + ncr400->status_ctrl |= (state << 0); + ncr53c400_log("Status Control bit 4=%02x.\n", ncr400->status_ctrl); + if (ncr400->status_ctrl & 0x10) + ncr5380_irq(ncr, state); + } + return 1; +} + static void ncr53c400_timer_on_auto(void *ext_priv, double period) { @@ -263,7 +281,8 @@ ncr53c400_read(uint32_t addr, void *priv) ncr->isr |= STATUS_END_OF_DMA; if (ncr->mode & MODE_ENA_EOP_INT) { ncr53c400_log("NCR read irq\n"); - ncr5380_irq(ncr, 1); + ncr53c400_irq_enable(ncr, ncr400, 1); + ncr->isr |= STATUS_INT; } } else if (!timer_is_enabled(&ncr400->timer)) { ncr53c400_log("Timer re-enabled.\n"); @@ -277,15 +296,16 @@ ncr53c400_read(uint32_t addr, void *priv) case 0x3980: switch (addr) { case 0x3980: /* status */ + if (ncr400->reset) { + ncr400->reset = 0; + ncr53c400_irq_enable(ncr, ncr400, 1); + } + ret = ncr400->status_ctrl; ncr53c400_log("NCR status ctrl read=%02x.\n", ncr400->status_ctrl & STATUS_BUFFER_NOT_READY); if (!ncr400->busy) ret |= STATUS_5380_ACCESSIBLE; - if (ncr400->reset) { - ncr400->reset = 0; - ret |= 0x01; - } ncr53c400_log("NCR 53c400 status=%02x.\n", ret); break; @@ -295,10 +315,7 @@ ncr53c400_read(uint32_t addr, void *priv) break; case 0x3982: /* switch register read */ - if (ncr->irq != -1) { - ret = 0xf8; - ret += ncr->irq; - } + ret = ((ncr400->irq_config >> 5) & 7) | 0xf8; ncr53c400_log("Switches read=%02x.\n", ret); break; @@ -519,7 +536,8 @@ ncr53c400_callback(void *priv) ncr->isr |= STATUS_END_OF_DMA; if (ncr->mode & MODE_ENA_EOP_INT) { ncr53c400_log("NCR 53c400 write irq\n"); - ncr5380_irq(ncr, 1); + ncr53c400_irq_enable(ncr, ncr400, 1); + ncr->isr |= STATUS_INT; } } break; @@ -573,7 +591,8 @@ ncr53c400_callback(void *priv) ncr->isr |= STATUS_END_OF_DMA; if (ncr->mode & MODE_ENA_EOP_INT) { ncr53c400_log("NCR read irq\n"); - ncr5380_irq(ncr, 1); + ncr53c400_irq_enable(ncr, ncr400, 1); + ncr->isr |= STATUS_INT; } } else timer_on_auto(&ncr400->timer, 1.0); @@ -675,7 +694,8 @@ ncr53c400_init(const device_t *info) switch (ncr400->type) { case ROM_LCS6821N: /* Longshine LCS6821N */ ncr400->rom_addr = device_get_config_hex20("bios_addr"); - ncr->irq = device_get_config_int("irq"); + ncr400->irq_config = device_get_config_hex16("irq"); + ncr->irq = (ncr400->irq_config >> 5) & 7; rom_init(&ncr400->bios_rom, LCS6821N_ROM, ncr400->rom_addr, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL); @@ -689,7 +709,8 @@ ncr53c400_init(const device_t *info) case ROM_LS2000: /* Corel LS2000 */ ncr400->rom_addr = device_get_config_hex20("bios_addr"); - ncr->irq = device_get_config_int("irq"); + ncr400->irq_config = device_get_config_hex16("irq"); + ncr->irq = (ncr400->irq_config >> 5) & 7; rom_init(&ncr400->bios_rom, COREL_LS2000_ROM, ncr400->rom_addr, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL); @@ -702,7 +723,9 @@ ncr53c400_init(const device_t *info) case ROM_RT1000B: /* Rancho RT1000B/MC */ ncr400->rom_addr = device_get_config_hex20("bios_addr"); - ncr->irq = device_get_config_int("irq"); + ncr400->irq_config = device_get_config_hex16("irq"); + ncr->irq = (ncr400->irq_config >> 5) & 7; + if (info->flags & DEVICE_MCA) { rom_init(&ncr400->bios_rom, RT1000B_820R_ROM, 0xd8000, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL); @@ -729,7 +752,8 @@ ncr53c400_init(const device_t *info) case ROM_T130B: /* Trantor T130B */ ncr400->rom_addr = device_get_config_hex20("bios_addr"); ncr400->base = device_get_config_hex16("base"); - ncr->irq = device_get_config_int("irq"); + ncr400->irq_config = device_get_config_hex16("irq"); + ncr->irq = (ncr400->irq_config >> 5) & 7; if (ncr400->rom_addr > 0x00000) { rom_init(&ncr400->bios_rom, T130B_ROM, @@ -754,6 +778,7 @@ ncr53c400_init(const device_t *info) ncr->dma_send_ext = NULL; ncr->dma_initiator_receive_ext = NULL; ncr->timer = ncr53c400_timer_on_auto; + ncr->irq_ena = ncr53c400_irq_enable; scsi_bus->bus_device = ncr->bus; scsi_bus->timer = ncr->timer; scsi_bus->priv = ncr->priv; @@ -839,16 +864,16 @@ static const device_config_t ncr53c400_mmio_config[] = { { .name = "irq", .description = "IRQ", - .type = CONFIG_SELECTION, + .type = CONFIG_HEX16, .default_string = NULL, - .default_int = 5, + .default_int = 0xa0, .file_filter = NULL, .spinner = { 0 }, .selection = { - { .description = "None", .value = -1 }, - { .description = "IRQ 3", .value = 3 }, - { .description = "IRQ 5", .value = 5 }, - { .description = "IRQ 7", .value = 7 }, + { .description = "IRQ 3", .value = 0x60 }, + { .description = "IRQ 4", .value = 0x80 }, + { .description = "IRQ 5", .value = 0xa0 }, + { .description = "IRQ 7", .value = 0xe0 }, { .description = "" } }, .bios = { { 0 } } @@ -910,16 +935,16 @@ static const device_config_t rt1000b_config[] = { { .name = "irq", .description = "IRQ", - .type = CONFIG_SELECTION, + .type = CONFIG_HEX16, .default_string = NULL, - .default_int = 5, + .default_int = 0xa0, .file_filter = NULL, .spinner = { 0 }, .selection = { - { .description = "None", .value = -1 }, - { .description = "IRQ 3", .value = 3 }, - { .description = "IRQ 5", .value = 5 }, - { .description = "IRQ 7", .value = 7 }, + { .description = "IRQ 3", .value = 0x60 }, + { .description = "IRQ 4", .value = 0x80 }, + { .description = "IRQ 5", .value = 0xa0 }, + { .description = "IRQ 7", .value = 0xe0 }, { .description = "" } }, .bios = { { 0 } } @@ -931,16 +956,16 @@ static const device_config_t rt1000b_mc_config[] = { { .name = "irq", .description = "IRQ", - .type = CONFIG_SELECTION, + .type = CONFIG_HEX16, .default_string = NULL, - .default_int = 5, + .default_int = 0xa0, .file_filter = NULL, .spinner = { 0 }, .selection = { - { .description = "None", .value = -1 }, - { .description = "IRQ 3", .value = 3 }, - { .description = "IRQ 5", .value = 5 }, - { .description = "IRQ 7", .value = 7 }, + { .description = "IRQ 3", .value = 0x60 }, + { .description = "IRQ 4", .value = 0x80 }, + { .description = "IRQ 5", .value = 0xa0 }, + { .description = "IRQ 7", .value = 0xe0 }, { .description = "" } }, .bios = { { 0 } } @@ -987,16 +1012,16 @@ static const device_config_t t130b_config[] = { { .name = "irq", .description = "IRQ", - .type = CONFIG_SELECTION, + .type = CONFIG_HEX16, .default_string = NULL, - .default_int = 5, + .default_int = 0xa0, .file_filter = NULL, .spinner = { 0 }, .selection = { - { .description = "None", .value = -1 }, - { .description = "IRQ 3", .value = 3 }, - { .description = "IRQ 5", .value = 5 }, - { .description = "IRQ 7", .value = 7 }, + { .description = "IRQ 3", .value = 0x60 }, + { .description = "IRQ 4", .value = 0x80 }, + { .description = "IRQ 5", .value = 0xa0 }, + { .description = "IRQ 7", .value = 0xe0 }, { .description = "" } }, .bios = { { 0 } } diff --git a/src/scsi/scsi_t128.c b/src/scsi/scsi_t128.c index 5b893dd70..2c2451cc7 100644 --- a/src/scsi/scsi_t128.c +++ b/src/scsi/scsi_t128.c @@ -27,7 +27,6 @@ #define HAVE_STDARG_H #include <86box/86box.h> #include <86box/io.h> -#include "cpu.h" #include <86box/timer.h> #include <86box/dma.h> #include <86box/pic.h> @@ -41,6 +40,7 @@ #include <86box/scsi_device.h> #include <86box/scsi_ncr5380.h> #include <86box/scsi_t128.h> +#include "cpu.h" #define T128_ROM "roms/scsi/ncr5380/trantor_t128_bios_v1.12.bin" @@ -75,7 +75,7 @@ t128_write(uint32_t addr, uint8_t val, void *priv) if ((addr >= 0x1800) && (addr < 0x1880)) t128->ext_ram[addr & 0x7f] = val; else if ((addr >= 0x1c00) && (addr < 0x1c20)) { - t128_log("T128 ctrl write=%02x, mode=%02x.\n", val, ncr->mode & MODE_DMA); + t128_log("T128 ctrl write=%02x, mode=%02x.\n", val & 0x10, ncr->mode & MODE_DMA); t128->ctrl = val; } else if ((addr >= 0x1d00) && (addr < 0x1e00)) ncr5380_write((addr - 0x1d00) >> 5, val, ncr); @@ -141,6 +141,7 @@ t128_read(uint32_t addr, void *priv) if (ncr->mode & MODE_ENA_EOP_INT) { t128_log("T128 read irq\n"); ncr5380_irq(ncr, 1); + ncr->isr |= STATUS_INT; } scsi_bus->bus_out |= BUS_CD; scsi_bus->tx_mode = PIO_TX_BUS; @@ -298,6 +299,7 @@ t128_callback(void *priv) if (ncr->mode & MODE_ENA_EOP_INT) { t128_log("T128 write irq\n"); ncr5380_irq(ncr, 1); + ncr->isr |= STATUS_INT; } scsi_bus->tx_mode = PIO_TX_BUS; timer_stop(&t128->timer); @@ -497,6 +499,7 @@ t128_init(const device_t *info) ncr->dma_send_ext = t128_dma_send_ext; ncr->dma_initiator_receive_ext = t128_dma_initiator_receive_ext; ncr->timer = t128_timer_on_auto; + ncr->irq_ena = NULL; scsi_bus->bus_device = ncr->bus; scsi_bus->timer = ncr->timer; scsi_bus->priv = ncr->priv; @@ -566,12 +569,13 @@ static const device_config_t t128_config[] = { .description = "IRQ", .type = CONFIG_SELECTION, .default_string = NULL, - .default_int = 5, + .default_int = -1, .file_filter = NULL, .spinner = { 0 }, .selection = { { .description = "None", .value = -1 }, { .description = "IRQ 3", .value = 3 }, + { .description = "IRQ 4", .value = 4 }, { .description = "IRQ 5", .value = 5 }, { .description = "IRQ 7", .value = 7 }, { .description = "IRQ 10", .value = 10 },