]> git.unchartedbackwaters.co.uk Git - francis/winuae.git/commitdiff
Partial NCR 77C22E+ and 77C32BLT emulation. Retina Z2 and Z3.
authorToni Wilen <twilen@winuae.net>
Tue, 26 Dec 2023 17:12:57 +0000 (19:12 +0200)
committerToni Wilen <twilen@winuae.net>
Tue, 26 Dec 2023 17:12:57 +0000 (19:12 +0200)
gfxboard.cpp
include/gfxboard.h
od-win32/winuae_msvc15/winuae_msvc.vcxproj
od-win32/winuae_msvc15/winuae_msvc.vcxproj.filters
pcem/pcemglue.cpp
pcem/vid_ncr.cpp [new file with mode: 0644]
pcem/vid_ncr.h [new file with mode: 0644]

index c3e57c19159e91c9374301bafa826256545fb27b..b5eb8a24b661f1c5a21de3c61c205abf95c746b4 100644 (file)
@@ -51,6 +51,7 @@ static bool memlogw = true;
 #include "pcem/vid_cl5429.h"
 #include "pcem/vid_s3.h"
 #include "pcem/vid_voodoo_banshee.h"
+#include "pcem/vid_ncr.h"
 #include "pci.h"
 #include "pci_hw.h"
 #include "pcem/pcemglue.h"
@@ -233,6 +234,20 @@ static const struct gfxboard boards[] =
                ROMTYPE_PICASSOIV,
                0, NULL, &gd5446_device
        },
+       {
+               GFXBOARD_ID_RETINA_Z2,
+               _T("Retina [Zorro II]"), _T("Macro System"), _T("Retina_Z2"),
+               18260, 6, 0, 0,
+               0x00000000, 0x00100000, 0x00400000, 0x00020000, 0, 2, 2, false,
+               0, 0, NULL, &ncr_retina_z2_device
+       },
+       {
+               GFXBOARD_ID_RETINA_Z3,
+               _T("Retina [Zorro III]"), _T("Macro System"), _T("Retina_Z3"),
+               18260, 16, 0, 0,
+               0x00000000, 0x00100000, 0x00400000, 0x00400000, 0, 3, 2, false,
+               0, 0, NULL, &ncr_retina_z3_device
+       },
        {
                GFXBOARD_ID_HARLEQUIN,
                _T("Harlequin [Zorro II]"), _T("ACS"), _T("Harlequin_PAL"),
@@ -335,6 +350,7 @@ struct rtggfxboard
        int vga_width, vga_height, vga_width_mult, vga_height_mult;
        bool vga_refresh_active;
        int device_settings;
+       uae_u8 extradata[16];
 
        uae_u32 vgaioregionptr, vgavramregionptr, vgabank0regionptr, vgabank1regionptr;
 
@@ -871,7 +887,7 @@ static void init_board (struct rtggfxboard *gb)
        gb->vram_offset_enabled = false;
        gb->gfxmem_bank->reserved_size = vramsize;
        gb->gfxmem_bank->start = gb->gfxboardmem_start;
-       if (gb->board->manufacturer) {
+       if (gb->board->manufacturer && gb->board->banksize >= 0x40000) {
                gb->gfxmem_bank->flags |= ABFLAG_ALLOCINDIRECT | ABFLAG_PPCIOSPACE;
                gb->gfxmem_bank->label = _T("*");
                mapped_malloc(gb->gfxmem_bank);
@@ -2730,6 +2746,25 @@ static void REGPARAM2 gfxboard_wput_mem_autoconfig (uaecptr addr, uae_u32 b)
                                gb->pcem_vram_mask = 0x3fffff;
                                gb->pcem_io_mask = 0x3fff;
 
+                       } else if (boardnum == GFXBOARD_ID_RETINA_Z3) {
+
+                               uae_u32 offset = 0xc00000;
+                               for (;;) {
+                                       map_banks_z3(&gb->gfxboard_bank_vram_pcem, (start + offset) >> 16, gb->gfxboard_bank_vram_pcem.allocated_size >> 16);
+                                       offset += gb->gfxboard_bank_vram_pcem.allocated_size;
+                                       if (offset >= 0x1000000)
+                                               break;
+                               }
+                               map_banks_z3(&gb->gfxboard_bank_mmio_wbs_pcem, (start + 0xb00000) >> 16, 1);
+                               map_banks_z3(&gb->gfxboard_bank_io_swap_pcem, (start + 0x000000) >> 16, 1);
+                               gb->pcem_vram_offset = 0x800000;
+                               gb->pcem_vram_mask = 0x3fffff;
+                               gb->pcem_io_mask = 0x3fff;
+                               gb->pcem_mmio_offset = 0x00300000;
+                               gb->pcem_mmio_mask = 0xff;
+                               gb->configured_regs = gb->gfxmem_bank->start >> 16;
+                               gb->gfxboard_intena = 1;
+
                        }
 
                } else {
@@ -2871,6 +2906,22 @@ static void REGPARAM2 gfxboard_bput_mem_autoconfig (uaecptr addr, uae_u32 b)
                                        gb->pcem_vram_mask = 0x1fffff;
                                        gb->pcem_io_mask = 0x3fff;
 
+                               } else if (boardnum == GFXBOARD_ID_RETINA_Z2) {
+
+                                       gb->configured_mem = b;
+                                       gb->configured_regs = b;
+
+                                       ab = &gb->gfxboard_bank_special_pcem;
+                                       map_banks_z2(ab, b, gb->board->banksize >> 16);
+
+                                       init_board(gb);
+
+                                       gb->mem_start[0] = b << 16;
+                                       gb->mem_end[0] = gb->mem_start[0] + gb->board->banksize;
+                                       gb->pcem_vram_offset = 0x800000;
+                                       gb->pcem_vram_mask = 0x3fffff;
+                                       gb->pcem_io_mask = 0x3fff;
+
                                } else {
 
                                        // Picasso II, Picasso II+
@@ -4320,33 +4371,43 @@ bool gfxboard_init_memory (struct autoconfig_info *aci)
        
        z2_flags = 0x05; // 1M
        z3_flags = 0;
+       type = 0;
        bank = gb->board->banksize;
-       bank /= 0x00100000;
-       if (bank >= 16) {
-               ext_size = true;
-               bank /= 16;
-               while (bank > 1) {
-                       z3_flags++;
-                       bank >>= 1;
+       if (bank <= 0x40000) {
+               z2_flags = 0x00;
+               while (bank >= 0x10000) {
+                       z2_flags++;
+                       bank /= 2;
                }
+               type = z2_flags | 0xc0;
        } else {
-               while (bank > 1) {
-                       z2_flags++;
-                       bank >>= 1;
+               bank /= 0x00100000;
+               if (bank >= 16) {
+                       ext_size = true;
+                       bank /= 16;
+                       while (bank > 1) {
+                               z3_flags++;
+                               bank >>= 1;
+                       }
+               } else {
+                       while (bank > 1) {
+                               z2_flags++;
+                               bank >>= 1;
+                       }
+                       z2_flags &= 7;
                }
-               z2_flags &= 7;
-       }
-       if (gb->board->configtype == 3) {
-               type = 0x80;
-               flags |= 0x10;
-               if (ext_size) {
-                       flags |= 0x20;
-                       type |= z3_flags;
+               if (gb->board->configtype == 3) {
+                       type = 0x80;
+                       flags |= 0x10;
+                       if (ext_size) {
+                               flags |= 0x20;
+                               type |= z3_flags;
+                       } else {
+                               type |= z2_flags;
+                       }
                } else {
-                       type |= z2_flags;
+                       type = z2_flags | 0xc0;
                }
-       } else {
-               type = z2_flags | 0xc0;
        }
        type |= gb->board->model_registers && !gb->board->model_extra ? 0x08 : 0x00;
        flags |= gb->board->er_flags;
@@ -4571,7 +4632,7 @@ bool gfxboard_init_registersx(struct autoconfig_info *aci, int regnum)
        }
 
        memset (gb->automemory, 0xff, GFXBOARD_AUTOCONFIG_SIZE);
-       if (gb->rbc->rtgmem_type == GFXBOARD_ID_PIXEL64) {
+       if (gb->rbc->rtgmem_type == GFXBOARD_ID_PIXEL64 || gb->rbc->rtgmem_type == GFXBOARD_ID_RETINA_Z2) {
                ew(gb, 0x00, 0xc0 | 0x02); // 128 Z2
                size = BOARD_REGISTERS_SIZE * 2;
        } else {
@@ -5451,7 +5512,61 @@ static void special_pcem_put(uaecptr addr, uae_u32 v, int size)
                write_log(_T("PCEM SPECIAL PUT %08x %08x %d PC=%08x\n"), addr, v, size, M68K_GETPC);
 #endif
 
-       if (boardnum == GFXBOARD_ID_PIXEL64) {
+       if (boardnum == GFXBOARD_ID_RETINA_Z2) {
+       
+               addr &= 0x1ffff;
+               if (addr & 0x10000) {
+                       // VRAM banks
+                       uaecptr mem = addr & 0xffff;
+                       if (size == 2) {
+                               v = do_byteswap_32(v);
+                       } else if (size == 1) {
+                               v = do_byteswap_16(v);
+                       }
+                       put_mem_pcem(mem + 0xa0000, v, size);
+               } else if (addr & 0x8000) {
+                       // RAMDAC
+                       int dac = addr & 15;
+                       if (dac == 1) {
+                               // palette index
+                               gb->extradata[0] = v;
+                               gb->extradata[1] = 0;
+                       } else if (dac == 3) {
+                               // palette data
+                               gb->extradata[2 + gb->extradata[1]] = v;
+                               gb->extradata[1]++;
+                               if (gb->extradata[1] == 3) {
+                                       put_io_pcem(0x3c8, gb->extradata[0], 0);
+                                       put_io_pcem(0x3c9, gb->extradata[2], 0);
+                                       put_io_pcem(0x3c9, gb->extradata[3], 0);
+                                       put_io_pcem(0x3c9, gb->extradata[4], 0);
+                                       gb->extradata[0]++;
+                                       gb->extradata[0] &= 255;
+                                       gb->extradata[1] = 0;
+                               }
+                       } else if (dac == 13) {
+                               // 16/24 bit mode
+                               if ((v & (0x80 | 0x40 | 0x20)) == (0x80 | 0x40 | 0x20)) {
+                                       put_io_pcem(0x3c4, 0x3f, 0);
+                                       put_io_pcem(0x3c5, 24, 0);
+                               } else if (v & 0x80) {
+                                       put_io_pcem(0x3c4, 0x3f, 0);
+                                       put_io_pcem(0x3c5, 16, 0);
+                               } else {
+                                       put_io_pcem(0x3c4, 0x3f, 0);
+                                       put_io_pcem(0x3c5, 8, 0);
+                               }
+                       }
+               } else {
+                       // IO
+                       int io = addr & 0x3fff;
+                       if (!(addr & 0x4000)) {
+                               io++;
+                       }
+                       put_io_pcem(io, v, 0);
+               }
+
+       } else if (boardnum == GFXBOARD_ID_PIXEL64) {
 
                addr &= 0xffff;
                if (size) {
@@ -5743,7 +5858,30 @@ static uae_u32 special_pcem_get(uaecptr addr, int size)
        write_log(_T("PCEM SPECIAL GET %08x %d PC=%08x\n"), addr, size, M68K_GETPC);
 #endif
 
-       if (boardnum == GFXBOARD_ID_PIXEL64) {
+       if (boardnum == GFXBOARD_ID_RETINA_Z2) {
+
+               addr &= 0x1ffff;
+               if (addr & 0x10000) {
+                       // VRAM banks
+                       uaecptr mem = addr & 0xffff;
+                       v = get_mem_pcem(mem + 0xa0000, size);
+                       if (size == 2) {
+                               v = do_byteswap_32(v);
+                       } else if (size == 1) {
+                               v = do_byteswap_16(v);
+                       }
+               } else if (addr & 0x8000) {
+                       // RAMDAC
+               } else {
+                       // IO
+                       int io = addr & 0x3fff;
+                       if (!(addr & 0x4000)) {
+                               io++;
+                       }
+                       v = get_io_pcem(io, 0);
+               }
+
+       } else if (boardnum == GFXBOARD_ID_PIXEL64) {
 
                if (size) {
                        v = get_io_pcem(addr + 0, 0) << 8;
index 81a93103b5be982d4772b76de5450157532700a5..0f8c42afd67cd0da3bc694701943311134125bd2 100644 (file)
@@ -81,7 +81,9 @@ int pcem_getvramsize(void);
 #define GFXBOARD_ID_VOODOO3_PCI 18
 #define GFXBOARD_ID_S3VIRGE_PCI 19
 #define GFXBOARD_ID_PIXEL64 20
-#define GFXBOARD_ID_VOODOO5_PCI 21
+#define GFXBOARD_ID_RETINA_Z2 21
+#define GFXBOARD_ID_RETINA_Z3 22
+#define GFXBOARD_ID_VOODOO5_PCI 23
 
 struct gfxboard_mode
 {
index 5a7ae28fbe755b328ba277ab19dd03820c308902..4d4fcd2aa807cbe2d17d4dbb744677626ca11233 100644 (file)
     <ClCompile Include="..\..\pcem\sound_speaker.cpp" />
     <ClCompile Include="..\..\pcem\timer.cpp" />
     <ClCompile Include="..\..\pcem\vid_cl5429.cpp" />
+    <ClCompile Include="..\..\pcem\vid_ncr.cpp" />
     <ClCompile Include="..\..\pcem\vid_s3.cpp" />
     <ClCompile Include="..\..\pcem\vid_s3_virge.cpp" />
     <ClCompile Include="..\..\pcem\vid_sdac_ramdac.cpp" />
index 3cc050909d4c5b6a271c3f975510c6c40f5d74d4..5e358ac85cfdd78aed063f927a298c14e30d5d30 100644 (file)
     <ClCompile Include="..\..\dsp3210\dsp_glue.cpp">
       <Filter>dsp3210</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\pcem\vid_ncr.cpp">
+      <Filter>pcem</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <None Include="..\resources\35floppy.ico">
index 6e7c7af6c23ff8763f8cb7426618428be89e0623..2b07faaf9ef07d95bd82ea778d8c09a40525dded 100644 (file)
@@ -799,7 +799,7 @@ static mem_mapping_t *getmm(uaecptr *addrp)
 void put_mem_pcem(uaecptr addr, uae_u32 v, int size)
 {
 #if 0
-       write_log("%08x %08x %d\n", addr, v, size);
+       write_log("put_mem_pcem %08x %08x %d\n", addr, v, size);
 #endif
        mem_mapping_t *m = getmm(&addr);
        if (m) {
@@ -825,6 +825,9 @@ uae_u32 get_mem_pcem(uaecptr addr, int size)
                        v = m->read_l(addr, m->p);
                }
        }
+#if 0
+       write_log("get_mem_pcem %08x %08x %d\n", addr, v, size);
+#endif
        return v;
 }
 
diff --git a/pcem/vid_ncr.cpp b/pcem/vid_ncr.cpp
new file mode 100644 (file)
index 0000000..41996a1
--- /dev/null
@@ -0,0 +1,1185 @@
+
+/* NCR 77C22E+ and 77C32BLT emulation by Toni Wilen 2023 */
+
+#include <stdlib.h>
+#include "ibm.h"
+#include "device.h"
+#include "io.h"
+#include "pci.h"
+#include "mem.h"
+#include "rom.h"
+#include "thread.h"
+#include "video.h"
+#include "vid_ncr.h"
+#include "vid_svga.h"
+#include "vid_svga_render.h"
+#include "vid_sdac_ramdac.h"
+
+extern void activate_debugger();
+
+enum
+{
+    NCR_TYPE_22EP = 2,
+    NCR_TYPE_32BLT
+};
+
+typedef struct ncr_t
+{
+        mem_mapping_t linear_mapping;
+        mem_mapping_t mmio_mapping;
+        
+        rom_t bios_rom;
+        
+        svga_t svga;
+        sdac_ramdac_t ramdac;
+
+        uint8_t ma_ext;
+        int width;
+        int bpp;
+        int ramdacbpp;
+
+        int chip;
+        
+        uint8_t id, id_ext;
+
+        uint32_t linear_base, linear_size;
+        uint32_t mmio_base, mmio_size;
+
+        int card;
+        uint32_t bank[4];
+        uint32_t vram_mask;
+        
+        uint8_t subsys_cntl, subsys_stat;
+        int vblank_irq;
+        
+        uint32_t hwc_fg_col, hwc_bg_col;
+        int hwc_col_stack_pos;
+
+        int blitter_busy;
+        uint64_t blitter_time;
+        uint64_t status_time;
+
+        uint8_t hwcursor_pal[2];
+
+        uint8_t blt_status, blt_rop, blt_hprot, blt_vprot;
+        uint16_t blt_width, blt_height, blt_control;
+        uint32_t blt_src, blt_dst, blt_pat;
+        uint64_t blt_fifo_data;
+        uint32_t blt_srcbak, blt_dstbak, blt_patbak, blt_patbak2;
+        uint32_t blt_c0, blt_c1;
+        uint16_t blt_w, blt_h;
+        uint8_t blt_stage;
+        int8_t blt_fifo_read, blt_fifo_write;
+        uint8_t blt_srcdata, blt_patdata, blt_dstdata;
+        int8_t blt_expand_offset, blt_expand_bit, blt_first_expand;
+        uint8_t blt_patdone, blt_start;
+        uint8_t blt_fifo_size;
+        uint8_t blt_patx, blt_paty;
+        uint8_t blt_src_size;
+        uint8_t blt_bpp;
+
+} ncr_t;
+
+void ncr_updatemapping(ncr_t*);
+void ncr_updatebanking(ncr_t*);
+
+static int ncr_vga_vsync_enabled(ncr_t *ncr)
+{
+    if (!(ncr->svga.crtc[0x11] & 0x20) && ncr->vblank_irq > 0)
+        return 1;
+    return 0;
+}
+
+static void ncr_update_irqs(ncr_t *ncr)
+{
+    if (ncr->vblank_irq > 0 && ncr_vga_vsync_enabled(ncr))
+        pci_set_irq(NULL, PCI_INTA);
+    else
+        pci_clear_irq(NULL, PCI_INTA);
+}
+
+static void ncr_vblank_start(svga_t *svga)
+{
+    ncr_t *ncr = (ncr_t *)svga->p;
+    if (ncr->vblank_irq >= 0) {
+        ncr->vblank_irq = 1;
+        ncr_update_irqs(ncr);
+    }
+}
+
+
+void ncr_hwcursor_draw(svga_t *svga, int displine)
+{
+    ncr_t *ncr = (ncr_t*)svga->p;
+    int x;
+    uint8_t dat[2];
+    uint32_t c[2];
+    int xx;
+    int offset = svga->hwcursor_latch.x - svga->hwcursor_latch.xoff;
+    int line_offset = 0;
+
+    offset <<= svga->horizontal_linedbl;
+
+    if (svga->interlace && svga->hwcursor_oddeven)
+        svga->hwcursor_latch.addr += line_offset;
+
+    if (svga->bpp <= 8) {
+        c[0] = svga->pallook[ncr->hwcursor_pal[0]];
+        c[1] = svga->pallook[ncr->hwcursor_pal[1]];
+    } else {
+        for (int i = 0; i < 2; i++) {
+            uint8_t r = (ncr->hwcursor_pal[i] & (0x80 | 0x40 | 0x20));
+            uint8_t b = (ncr->hwcursor_pal[i] & (0x10 | 0x08)) << 3;
+            uint8_t g = (ncr->hwcursor_pal[i] & (4 | 2 | 1) << 5);
+            r |= (r >> 3) | (r >> 6);
+            b |= (b >> 2) | (b >> 4) | (b >> 6);
+            g |= (g >> 3) | (g >> 6);
+            c[i] = (r << 16) | (g << 8) | b;
+            c[i] <<= 8;
+        }
+    }
+
+    svga->hwcursor_latch.addr += (svga->hwcursor.xsize / 8);
+    for (x = 0; x < svga->hwcursor.xsize; x += 8)
+    {
+            svga->hwcursor_latch.addr--;
+            uint32_t addroffset = svga->hwcursor_latch.addr & svga->vram_display_mask;
+            dat[0] = svga->vram[addroffset];
+            addroffset = (svga->hwcursor_latch.addr + (svga->hwcursor.xsize / 8)) & svga->vram_display_mask;
+            dat[1] = svga->vram[addroffset];
+            for (xx = 0; xx < 8; xx++)
+            {
+                if (offset >= svga->hwcursor_latch.x)
+                {
+                    if (!(dat[0] & 0x80))
+                        ((uint32_t *)buffer32->line[displine])[offset + 32] = c[(dat[1] & 0x80) ? 0 : 1];
+                    else if (dat[1] & 0x80)
+                        ((uint32_t *)buffer32->line[displine])[offset + 32] ^= 0xffffff;
+                }
+
+                offset++;
+                dat[0] <<= 1;
+                dat[1] <<= 1;
+            }
+    }
+    svga->hwcursor_latch.addr += (svga->hwcursor.xsize / 8) * 2;
+
+    if (svga->interlace && !svga->hwcursor_oddeven)
+        svga->hwcursor_latch.addr += line_offset;
+}
+
+
+void ncr_out(uint16_t addr, uint8_t val, void *p)
+{
+        ncr_t *ncr = (ncr_t *)p;
+        svga_t *svga = &ncr->svga;
+        uint8_t old;
+
+        if (((addr & 0xfff0) == 0x3d0 || (addr & 0xfff0) == 0x3b0) && !(svga->miscout & 1)) 
+                addr ^= 0x60;
+        
+        switch (addr)
+        {
+                case 0x3c4:
+                svga->seqaddr = val & 0x3f;
+                break;
+                case 0x3c5:
+                {
+                    old = svga->seqregs[svga->seqaddr];
+                    svga->seqregs[svga->seqaddr] = val;
+                    if (old != val && svga->seqaddr >= 0x0a) {
+                        svga->fullchange = changeframecount;
+                    }
+                    switch (svga->seqaddr)
+                    {
+                        case 0x0a:
+                            ncr->hwcursor_pal[0] = val;
+                            break;
+                        case 0x0b:
+                            ncr->hwcursor_pal[1] = val;
+                            break;
+                        case 0x0c:
+                            svga->hwcursor.ena = val & 1;
+                            svga->hwcursor.ysize = 16 << ((val >> 1) & 3);
+                            svga->hwcursor.xsize = (val & 0x80) && ncr->chip == NCR_TYPE_32BLT ? 64 : 32;
+                            break;
+                        case 0x0d:
+                        case 0x0e:
+                        case 0x0f:
+                        case 0x10:
+                            svga->hwcursor.x = (svga->seqregs[0x0d] << 8) | svga->seqregs[0x0e];
+                            svga->hwcursor.y = (svga->seqregs[0x0f] << 8) | svga->seqregs[0x10];
+                            return;
+                        case 0x11:
+                            svga->hwcursor.xoff = val & 31;
+                            return;
+                        case 0x12:
+                            svga->hwcursor.yoff = val & 127;
+                            return;
+                        case 0x13:
+                        case 0x14:
+                        case 0x15:
+                        case 0x16:
+                        {
+                            int offset = 0;
+                            if (ncr->chip == NCR_TYPE_32BLT) {
+                                if (svga->seqregs[0x1e] & 0x10) {
+                                    offset = (svga->seqregs[0x15] << 8) | svga->seqregs[0x16];
+                                }
+                                svga->hwcursor.addr = (svga->seqregs[0x13] << 8) | svga->seqregs[0x14];
+                                svga->hwcursor.addr += offset << 6;
+                                svga->hwcursor.addr &= ~0x3ff;
+                            } else {
+                                svga->hwcursor.addr = (svga->seqregs[0x15] << 16) | (svga->seqregs[0x16] << 8) | svga->seqregs[0x14];
+                                svga->hwcursor.addr >>= 2;
+                            }
+                        }
+                        break;
+                        case 0x18:
+                        case 0x19:
+                        case 0x1a:
+                        case 0x1b:
+                        case 0x1c:
+                        case 0x1d:
+                            ncr_updatebanking(ncr);
+                        break;
+                        case 0x1e:
+                            ncr_updatemapping(ncr);
+                        break;
+                        case 0x21:
+                        if (val & 1) {
+                            svga->bpp = 8;
+                            svga->bpp = 8 << ((val >> 4) & 3);
+                            if (svga->bpp > 24) {
+                                svga->bpp = 24;
+                            }
+                        } else {
+                            svga->bpp = 4;
+                        }
+                        break;
+                        case 0x30:
+                        case 0x31:
+                        case 0x32:
+                        case 0x33:
+                            ncr_updatemapping(ncr);
+                        break;
+                        case 0x3f: // ram dac fake
+                        ncr->ramdacbpp = val;
+                        break;
+                    }
+                    if (old != val)
+                    {
+                        svga->fullchange = changeframecount;
+                        svga_recalctimings(svga);
+                    }
+                }
+                break;
+
+                case 0x3d4:
+                svga->crtcreg = val & svga->crtcreg_mask;;
+                return;
+                case 0x3d5:
+                {
+                    old = svga->crtc[svga->crtcreg];
+                    svga->crtc[svga->crtcreg] = val;
+                    switch (svga->crtcreg)
+                    {
+                            case 0x11:
+                            if (!(val & 0x10)) {
+                                if (ncr->vblank_irq > 0)
+                                    ncr->vblank_irq = -1;
+                            } else if (ncr->vblank_irq < 0) {
+                                ncr->vblank_irq = 0;
+                            }
+                            ncr_update_irqs(ncr);
+                            if ((val & ~0x30) == (old & ~0x30))
+                                old = val;
+                            break;
+
+                            case 0x31:
+                            ncr->ma_ext = val & 15;
+                            break;
+
+                    }
+                    if (old != val)
+                    {
+                            if (svga->crtcreg < 0xe || svga->crtcreg > 0x10)
+                            {
+                                    svga->fullchange = changeframecount;
+                                    svga_recalctimings(svga);
+                            }
+                    }
+                }
+                break;
+        }
+        svga_out(addr, val, svga);
+}
+
+uint8_t ncr_in(uint16_t addr, void *p)
+{
+        ncr_t *ncr = (ncr_t *)p;
+        svga_t *svga = &ncr->svga;
+        uint8_t ret;
+
+        if (((addr & 0xfff0) == 0x3d0 || (addr & 0xfff0) == 0x3b0) && !(svga->miscout & 1)) 
+                addr ^= 0x60;
+
+        switch (addr)
+        {
+                case 0x3c2:
+                ret = svga_in(addr, svga);
+                ret |= ncr->vblank_irq > 0 ? 0x80 : 0x00;
+                return ret;
+                case 0x3c5:
+                switch(svga->seqaddr)
+                {
+                    case 0x08:
+                    ret = ncr->chip << 4;
+                    if (ncr->chip == NCR_TYPE_22EP) {
+                        ret |= 8;
+                    }
+                    return ret;
+                }
+                if (svga->seqaddr >= 0x08 && svga->seqaddr < 0x40)
+                        return svga->seqregs[svga->seqaddr];
+                break;
+
+                case 0x3d4:
+                return svga->crtcreg;
+                case 0x3d5:
+                switch (svga->crtcreg)
+                {
+                }
+                return svga->crtc[svga->crtcreg];
+        }
+        return svga_in(addr, svga);
+}
+
+static const int fontwidths[] =
+{
+    4, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 8, 8, 8, 8, 8
+};
+
+void ncr_recalctimings(svga_t *svga)
+{
+        ncr_t *ncr = (ncr_t*)svga->p;
+
+        svga->hdisp = svga->crtc[1] - ((svga->crtc[5] & 0x60) >> 5);
+        if (svga->crtc[0x30] & 0x02)
+            svga->hdisp += 0x100;
+        if (svga->crtc[0x32] & 0x02)
+            svga->hdisp += 0x200;
+        svga->hdisp++;
+        if (svga->seqregs[0x1f] & 0x10) {
+            svga->hdisp *= fontwidths[svga->seqregs[0x1f] & 15];
+        } else {
+            svga->hdisp *= (svga->seqregs[1] & 8) ? 16 : 8;
+        }
+
+        if (svga->crtc[0x30] & 0x01)
+            svga->htotal += 0x100;
+        if (svga->crtc[0x32] & 0x01)
+            svga->htotal += 0x200;
+
+        if (svga->crtc[0x33] & 0x02)
+            svga->dispend += 0x400;
+        if (svga->crtc[0x33] & 0x04)
+            svga->vblankstart += 0x400;
+
+        if (svga->crtc[0x33] & 0x01)
+            svga->vtotal += 0x400;
+
+        if (svga->crtc[0x33] & 0x10)
+            svga->split += 0x400;
+
+        if (svga->crtc[0x31] & 0x10)
+            svga->rowoffset += 0x100;
+
+        if (!svga->rowoffset)
+            svga->rowoffset = 256;
+
+        svga->interlace = svga->crtc[0x30] & 0x10;
+
+        if (svga->crtc[0x30] & 0x40)
+            svga->clock /= 2;
+
+        svga->ma_latch |= (ncr->ma_ext << 16);
+
+        if (svga->seqregs[0x21] & 0x02)
+        {
+            svga->render = svga_render_4bpp_highres;
+            svga->hdisp /= 2;
+        }
+
+        if (svga->seqregs[0x21] & 0x01)
+        {
+            if (ncr->chip < NCR_TYPE_32BLT) {
+                svga->bpp = ncr->ramdacbpp;
+            }
+        }
+        switch (svga->bpp)
+        {
+            case 4:
+                svga->render = svga_render_4bpp_highres;
+                break;
+            case 8:
+                svga->render = svga_render_8bpp_highres;
+                break;
+            case 16:
+                svga->render = svga_render_16bpp_highres;
+                svga->hdisp /= 2;
+                break;
+            case 24:
+                svga->render = svga_render_24bpp_highres;
+                svga->hdisp /= 3;
+                break;
+        }
+
+        svga->horizontal_linedbl = svga->dispend * 9 / 10 >= svga->hdisp;
+}
+
+
+static void ncr_write(uint32_t addr, uint8_t val, void *p)
+{
+    ncr_t *ncr = (ncr_t *)p;
+    svga_t *svga = &ncr->svga;
+
+    addr = (addr & 0xffff) + ncr->bank[(addr >> 15) & 3];
+
+    addr &= svga->decode_mask;
+    if (addr >= svga->vram_max)
+        return;
+    addr &= svga->vram_mask;
+    svga->changedvram[addr >> 12] = changeframecount;
+    *(uint8_t *)&svga->vram[addr] = val;
+}
+static void ncr_writew(uint32_t addr, uint16_t val, void *p)
+{
+    ncr_t *ncr = (ncr_t *)p;
+    svga_t *svga = &ncr->svga;
+
+    addr = (addr & 0xffff) + ncr->bank[(addr >> 15) & 3];
+
+    addr &= svga->decode_mask;
+    if (addr >= svga->vram_max)
+        return;
+    addr &= svga->vram_mask;
+    svga->changedvram[addr >> 12] = changeframecount;
+    *(uint16_t *)&svga->vram[addr] = val;
+}
+static void ncr_writel(uint32_t addr, uint32_t val, void *p)
+{
+    ncr_t *ncr = (ncr_t *)p;
+    svga_t *svga = &ncr->svga;
+
+    addr = (addr & 0xffff) + ncr->bank[(addr >> 15) & 3];
+
+    addr &= svga->decode_mask;
+    if (addr >= svga->vram_max)
+        return;
+    addr &= svga->vram_mask;
+    svga->changedvram[addr >> 12] = changeframecount;
+    *(uint32_t *)&svga->vram[addr] = val;
+}
+
+static uint8_t ncr_read(uint32_t addr, void *p)
+{
+    ncr_t *ncr = (ncr_t *)p;
+    svga_t *svga = &ncr->svga;
+
+    addr = (addr & 0xffff) + ncr->bank[(addr >> 15) & 3];
+
+    addr &= svga->decode_mask;
+    if (addr >= svga->vram_max)
+        return 0xff;
+
+    return *(uint8_t *)&svga->vram[addr & svga->vram_mask];
+}
+static uint16_t ncr_readw(uint32_t addr, void *p)
+{
+    ncr_t *ncr = (ncr_t *)p;
+    svga_t *svga = &ncr->svga;
+
+    addr = (addr & 0xffff) + ncr->bank[(addr >> 15) & 3];
+
+    addr &= svga->decode_mask;
+    if (addr >= svga->vram_max)
+        return 0xffff;
+    return *(uint16_t *)&svga->vram[addr & svga->vram_mask];
+}
+static uint32_t ncr_readl(uint32_t addr, void *p)
+{
+    ncr_t *ncr = (ncr_t *)p;
+    svga_t *svga = &ncr->svga;
+
+    addr = (addr & 0xffff) + ncr->bank[(addr >> 15) & 3];
+
+    addr &= svga->decode_mask;
+    if (addr >= svga->vram_max)
+        return 0xffff;
+
+    return *(uint32_t *)&svga->vram[addr & svga->vram_mask];
+}
+
+static uint8_t blitter_rop(uint8_t rop, uint8_t src, uint8_t pat, uint8_t dst)
+{
+    uint8_t out = 0;
+    for (int c = 0; c < 8; c++) {
+        uint8_t d = (dst & (1 << c)) ? 1 : 0;
+        if (src & (1 << c))
+            d |= 2;
+        if (pat & (1 << c))
+            d |= 4;
+        if (rop & (1 << d))
+            out |= (1 << c);
+    }
+    return out;
+}
+
+static uint8_t blitter_read(ncr_t *ncr, uint32_t addr)
+{
+    uint32_t offset = ((addr >> 3) + ncr->blt_bpp) & ncr->vram_mask;
+    return ncr->svga.vram[offset];
+}
+
+static void blitter_write(ncr_t *ncr, uint32_t addr, uint8_t v)
+{
+    uint32_t offset = ((addr >> 3) + ncr->blt_bpp) & ncr->vram_mask;
+    ncr->svga.vram[offset] = v;
+    ncr->svga.changedvram[offset >> 12] = changeframecount;
+}
+
+static bool blitter_proc(ncr_t *ncr)
+{
+    bool colorfill = (ncr->blt_control & (1 << 9)) != 0;
+    bool colorexpand = (ncr->blt_control & (1 << 11)) != 0;
+    int32_t bpp = ncr->svga.bpp;
+    bool next = false;
+
+    if (ncr->blt_stage == 0) {
+        uint8_t src = 0;
+        if (!(ncr->blt_control & (1 << 15))) {
+            // waiting for fifo write?
+            if (ncr->blt_fifo_size < 8) {
+                ncr->blt_fifo_write = 1;
+                return false;
+            }
+            if (ncr->blt_first_expand && colorexpand && ncr->blt_expand_offset) {
+                ncr->blt_fifo_size -= ncr->blt_expand_offset;
+                ncr->blt_fifo_data >>= ncr->blt_expand_offset;
+                ncr->blt_first_expand = 0;
+                if (ncr->blt_fifo_size < 8) {
+                    ncr->blt_fifo_write = 1;
+                    return false;
+                }
+            }
+            src = ncr->blt_fifo_data & 0xff;
+            ncr->blt_fifo_data >>= 8;
+            ncr->blt_fifo_size -= 8;
+            ncr->blt_fifo_write = 0;
+        } else {
+            src = blitter_read(ncr, ncr->blt_src);
+        }
+
+        ncr->blt_srcdata = src;
+        ncr->blt_stage++;
+    }
+
+    if (ncr->blt_stage == 1) {
+
+        uint8_t skip = 0;
+        uint8_t srcdata = ncr->blt_srcdata;
+        if (colorexpand) {
+            int8_t offset = ncr->blt_expand_bit;
+            if (offset < 8) {
+                if (srcdata & (1 << offset)) {
+                    srcdata = ncr->blt_c0 >> (ncr->blt_bpp * 8);
+                } else if (ncr->blt_control & (1 << 5)) {
+                    /// transparent
+                    skip = 1;
+                } else {
+                    srcdata = ncr->blt_c1 >> (ncr->blt_bpp * 8);
+                }
+            }
+        }
+        if (!skip) {
+            ncr->blt_patdata = blitter_read(ncr, ncr->blt_pat);
+            ncr->blt_dstdata = blitter_read(ncr, ncr->blt_dst);
+            uint8_t out = blitter_rop(ncr->blt_rop, srcdata, ncr->blt_patdata, ncr->blt_dstdata);
+            blitter_write(ncr, ncr->blt_dst, out);
+        }
+
+        ncr->blt_bpp++;
+        if (ncr->blt_bpp == bpp / 8) {
+            ncr->blt_bpp = 0;
+            int16_t dir = (ncr->blt_control & (1 << 7)) ? 1 : -1;
+            int16_t dx = (bpp / 8) * dir;
+
+            if (colorexpand) {
+                ncr->blt_expand_bit--;
+                if (ncr->blt_expand_bit < 0) {
+                    ncr->blt_expand_bit = 7;
+                    ncr->blt_src += (1 << 3) * dir;
+                    ncr->blt_stage = 0;
+                } else {
+                    ncr->blt_stage = 1;
+                }
+            } else {
+                ncr->blt_src += dx << 3;
+                ncr->blt_stage = 0;
+            }
+            ncr->blt_dst += dx << 3;
+
+            int16_t patsize = (ncr->blt_control & (1 << 4)) ? 16 : 8;
+            ncr->blt_patx++;
+            ncr->blt_pat += dx << 3;
+            if (ncr->blt_patx == patsize) {
+                ncr->blt_patx = 0;
+                ncr->blt_pat = ncr->blt_patbak;
+            }
+            next = true;
+
+        } else {
+            if (colorexpand) {
+                ncr->blt_stage = 1;
+            } else {
+                ncr->blt_stage = 0;
+            }
+        }
+
+    }
+    return next;
+}
+
+static void blitter_done(ncr_t *ncr)
+{
+    ncr->blt_status = 1;
+    ncr->blt_start &= ~1;
+}
+
+static void blitter_run(ncr_t *ncr)
+{
+    for (;;) {
+        if (ncr->blt_status & 1) {
+            return;
+        }
+        if (ncr->blt_fifo_read > 0 || ncr->blt_fifo_write > 0) {
+            break;
+        }
+        if (blitter_proc(ncr)) {
+            ncr->blt_w++;
+            if (ncr->blt_w >= ncr->blt_width) {
+                ncr->blt_w = 0;
+                ncr->blt_first_expand = 1;
+                int bpp = ncr->svga.bpp / 8;
+                int dir = (ncr->blt_control & (1 << 6)) ? 1 : -1;
+                int dy = dir * ncr->svga.rowoffset * 8;
+
+                ncr->blt_fifo_size = 0;
+
+                if (!(ncr->blt_control & (1 << 12))) {
+                    ncr->blt_dstbak += dy << 3;
+                    ncr->blt_dst = ncr->blt_dstbak;
+                }
+
+                if (!(ncr->blt_control & (1 << 13))) {
+                    ncr->blt_srcbak += dy << 3;
+                    ncr->blt_src = ncr->blt_srcbak;
+                }
+
+                if (!(ncr->blt_control & (1 << 9))) {
+                    int patsize = (ncr->blt_control & (1 << 4)) ? 16 : 8;
+                    int patdx = (ncr->svga.bpp / 4) << 3;
+                    ncr->blt_paty++;
+                    ncr->blt_patbak += dir * patdx;
+                    if (ncr->blt_paty == patsize) {
+                        ncr->blt_paty = 0;
+                        ncr->blt_patbak = ncr->blt_patbak2;
+                    }
+                    ncr->blt_pat = ncr->blt_patbak;
+                }
+
+                ncr->blt_h++;
+                if (ncr->blt_h >= ncr->blt_height) {
+                    blitter_done(ncr);
+                    break;
+                }
+             }
+        }
+    }
+}
+
+static void blitter_write_fifo(ncr_t *ncr, uint32_t v, int size)
+{
+    ncr->blt_fifo_write = -1;
+    ncr->blt_fifo_data <<= ncr->blt_fifo_size;
+    ncr->blt_fifo_data |= ((uint64_t)v) << ncr->blt_fifo_size;
+    ncr->blt_fifo_size += 32;
+    blitter_run(ncr);
+}
+
+
+static void blitter_start(ncr_t *ncr)
+{
+#if 1
+    pclog("C=%04x ROP=%02x W=%d H=%d S=%08x/%d P=%08x/%d D=%08x/%d F=%d E=%d DM=%d SM=%d DL=%d SL=%d TR=%d MW=%d\n",
+        ncr->blt_control, ncr->blt_rop, ncr->blt_width, ncr->blt_height,
+        ncr->blt_src >> 3, ncr->blt_src & 7,
+        ncr->blt_pat >> 3, ncr->blt_pat & 7,
+        ncr->blt_dst >> 3, ncr->blt_dst & 7,
+        (ncr->blt_control & (1 << 9)) != 0,
+        (ncr->blt_control & (1 << 11)) != 0,
+        (ncr->blt_control & (1 << 12)) != 0,
+        (ncr->blt_control & (1 << 13)) != 0,
+        (ncr->blt_control & (1 << 14)) != 0,
+        (ncr->blt_control & (1 << 15)) != 0,
+        (ncr->blt_control & (1 << 5)) != 0,
+        (ncr->blt_control & (1 << 3)) != 0);
+#endif
+    ncr->blt_status = 0;
+    if (ncr->blt_start & 2) {
+        pclog("text blits not implemenented");
+        blitter_done(ncr);
+        return;
+    }
+    ncr->blt_expand_offset = ncr->blt_src & 31;
+    ncr->blt_expand_bit = 7;
+    ncr->blt_first_expand = 1;
+    ncr->blt_srcbak = ncr->blt_src;
+    ncr->blt_dstbak = ncr->blt_dst;
+    ncr->blt_patbak = ncr->blt_pat;
+    ncr->blt_patbak2 = ncr->blt_pat;
+    ncr->blt_w = 0;
+    ncr->blt_h = 0;
+    ncr->blt_fifo_read = 0;
+    ncr->blt_fifo_write = 0;
+    ncr->blt_fifo_size = 0;
+    ncr->blt_fifo_data = 0;
+    ncr->blt_stage = 0;
+    ncr->blt_patx = 0;
+    ncr->blt_paty = 0;
+    ncr->blt_patdone = 0;
+    blitter_run(ncr);
+    //activate_debugger();
+}
+
+static void ncr_mmio_write(uint32_t addr, uint8_t val, void *p)
+{
+    ncr_t *ncr = (ncr_t*)p;
+    int reg = addr & 0xff;
+    switch (reg)
+    {
+        case 0x09: // mode control
+            ncr->svga.seqregs[0x01e] = val;
+            ncr_updatemapping(ncr);
+            break;
+        case 0x30: // blitter start
+            ncr->blt_start = val;
+            if (val & 1) {
+                blitter_start(ncr);
+            }
+            break;
+        case 0x38: // pattern x rotation
+            ncr->blt_hprot = val;
+            break;
+        case 0x39: // pattern y rotation
+            ncr->blt_vprot = val;
+            break;
+        case 0x03a: // rop
+            ncr->blt_rop = val;
+            break;
+    }
+}
+
+static void ncr_mmio_write_w(uint32_t addr, uint16_t val, void *p)
+{
+    ncr_t *ncr = (ncr_t*)p;
+    int reg = (addr & 0xff) & ~1;
+    switch(reg)
+    {
+        case 0x00: // primary host offset
+            break;
+        case 0x04: // secondary host offset
+            break;
+        case 0x0c: // cursor x
+            ncr->svga.hwcursor.x = val & ((1 << 10) - 1);
+            break;
+        case 0x0e: // cursor y
+            ncr->svga.hwcursor.y = val & ((1 << 11) - 1);
+            break;
+        case 0x34: // blitter control
+            ncr->blt_control = val;
+            break;
+        case 0x3c: // blitter bitmap width
+            ncr->blt_width = val;
+            break;
+        case 0x3e: // blitter bitmap height
+            ncr->blt_height = val;
+            break;
+        case 0x40: // blitter destination
+            ncr->blt_dst &= 0xffff0000;
+            ncr->blt_dst |= val << 0;
+            break;
+        case 0x42:
+            ncr->blt_dst &= 0x0000ffff;
+            ncr->blt_dst |= val << 16;
+            break;
+        case 0x44: // blitter source
+            ncr->blt_src &= 0xffff0000;
+            ncr->blt_src |= val << 0;
+            break;
+        case 0x46:
+            ncr->blt_src &= 0x0000ffff;
+            ncr->blt_src |= val << 16;
+            break;
+        case 0x48: // blitter pattern
+            ncr->blt_pat &= 0xffff0000;
+            ncr->blt_pat |= val << 0;
+            break;
+        case 0x4a:
+            ncr->blt_pat &= 0x0000ffff;
+            ncr->blt_pat |= val << 16;
+            break;
+        case 0x4c: // foreground
+            ncr->blt_c0 &= 0xffff0000;
+            ncr->blt_c0 |= val << 0;
+            break;
+        case 0x4e:
+            ncr->blt_c0 &= 0x0000ffff;
+            ncr->blt_c0 |= val << 16;
+            break;
+        case 0x50: // foreground
+            ncr->blt_c1 &= 0xffff0000;
+            ncr->blt_c1 |= val << 0;
+            break;
+        case 0x52:
+            ncr->blt_c1 &= 0x0000ffff;
+            ncr->blt_c1 |= val << 16;
+            break;
+        default:
+            ncr_mmio_write(addr + 0, (uint8_t)val, p);
+            ncr_mmio_write(addr + 1, val >> 8, p);
+            break;
+    }
+
+}
+static void ncr_mmio_write_l(uint32_t addr, uint32_t val, void *p)
+{
+    ncr_t *ncr = (ncr_t*)p;
+    int reg = (addr & 0xff) & ~3;
+    ncr_mmio_write_w(addr + 0, val, p);
+    ncr_mmio_write_w(addr + 2, val >> 16, p);
+
+}
+
+static uint8_t ncr_mmio_read(uint32_t addr, void *p)
+{
+    ncr_t *ncr = (ncr_t *)p;
+    int reg = addr & 0xff;
+    uint8_t val = 0;
+    switch(reg)
+    {
+        case 0x09: // mode control
+            val = ncr->svga.seqregs[0x1e];
+            break;
+        case 0x30:
+            val = ncr->blt_start;
+            break;
+        case 0x32: // blt status
+            val = ncr->blt_status;
+            break;
+        default:
+            pclog("blitter read reg %02x\n", reg);
+            break;
+            
+
+    }
+
+    return val;
+}
+
+static uint16_t ncr_mmio_readw(uint32_t addr, void *p)
+{
+    uint16_t v;
+    v = ncr_mmio_read(addr + 0, p);
+    v |= ncr_mmio_read(addr + 1, p) << 8;
+    return v;
+}
+
+static uint32_t ncr_mmio_readl(uint32_t addr, void *p)
+{
+    uint32_t v;
+    v = ncr_mmio_read(addr + 0, p);
+    v |= ncr_mmio_read(addr + 2, p) << 16;
+    return v;
+}
+
+static void ncr_write_linear(uint32_t addr, uint8_t val, void *p)
+{
+    svga_t *svga = (svga_t *)p;
+    ncr_t *ncr = (ncr_t *)svga->p;
+    svga_write_linear(addr, val, p);
+}
+static void ncr_writew_linear(uint32_t addr, uint16_t val, void *p)
+{
+    svga_t *svga = (svga_t *)p;
+    ncr_t *ncr = (ncr_t *)svga->p;
+    svga_writew_linear(addr, val, p);
+}
+static void ncr_writel_linear(uint32_t addr, uint32_t val, void *p)
+{
+    svga_t *svga = (svga_t *)p;
+    ncr_t *ncr = (ncr_t *)svga->p;
+    if (ncr->blt_fifo_write > 0) {
+        blitter_write_fifo(ncr, val, 2);
+        return;
+    }
+    svga_writel_linear(addr, val, p);
+}
+
+
+void ncr_updatebanking(ncr_t *ncr)
+{
+    svga_t *svga = &ncr->svga;
+
+    svga->banked_mask = 0xffff;
+    ncr->bank[0] = (svga->seqregs[0x18] << 8) | svga->seqregs[0x19];
+    if (svga->seqregs[0x1e] & 0x4) {
+        ncr->bank[1] = (svga->seqregs[0x1c] << 8) | svga->seqregs[0x1d];
+    } else {
+        ncr->bank[1] = ncr->bank[0] + 0x8000;
+    }
+    if (svga->seqregs[0x1e] & 0x10) {
+        ncr->bank[0] <<= 6;
+        ncr->bank[1] <<= 6;
+    }
+    int mode = svga->seqregs[0x1e] >> 5;
+    if (mode != 2 && mode != 3 && mode != 6) {
+        pclog("unsupport banking mode %d\n", mode);
+    }
+    // Primary at A0000h-AFFFFh, Secondary at B0000h-BFFFFh. Both Read / Write.
+    if (mode == 2) {
+        ncr->bank[2] = ncr->bank[1];
+        ncr->bank[3] = ncr->bank[1];
+        ncr->bank[1] = ncr->bank[0];
+    } else {
+        ncr->bank[2] = ncr->bank[0];
+        ncr->bank[3] = ncr->bank[1];
+    }
+    // Read and Write to Secondary only
+    if (mode == 3) {
+        ncr->bank[0] = ncr->bank[1];
+        ncr->bank[2] = ncr->bank[3];
+    }
+}
+
+void ncr_updatemapping(ncr_t *ncr)
+{
+        svga_t *svga = &ncr->svga;
+        
+        ncr_updatebanking(ncr);
+
+        mem_mapping_disablex(&svga->mapping);
+        mem_mapping_disablex(&ncr->mmio_mapping);
+        mem_mapping_disablex(&ncr->linear_mapping);
+
+        if (ncr->chip == NCR_TYPE_32BLT) {
+            ncr->linear_size = 0x100000 << (svga->seqregs[0x1a] & 3);
+            if (ncr->linear_size > 0x400000)
+                ncr->linear_size = 0x400000;
+            ncr->linear_base = ((svga->seqregs[0x1a] >> 4) << 20) | (svga->seqregs[0x1b] << 24);
+            ncr->linear_base &= ~(ncr->linear_size - 1);
+            mem_mapping_set_addrx(&ncr->linear_mapping, ncr->linear_base, ncr->linear_size);
+
+            if (svga->seqregs[0x30] & 1) {
+                ncr->mmio_base = (svga->seqregs[0x31] << 8) | (svga->seqregs[0x32] << 16) | (svga->seqregs[0x33] << 24);
+                ncr->mmio_size = 0x100;
+                mem_mapping_set_addrx(&ncr->mmio_mapping, ncr->mmio_base, ncr->mmio_size);
+                mem_mapping_enablex(&ncr->mmio_mapping);
+            } else {
+                mem_mapping_disablex(&ncr->mmio_mapping);
+            }
+
+        } else {
+
+            mem_mapping_set_addrx(&svga->mapping, 0xa0000, 0x10000);
+            mem_mapping_enablex(&svga->mapping);
+#if 0
+            pclog("%08x %08x (%04x %04x %02x)\n", ncr->bank[0], ncr->bank[1],
+                (svga->seqregs[0x18] << 8) | svga->seqregs[0x19], (svga->seqregs[0x1c] << 8) | svga->seqregs[0x1d],
+                svga->seqregs[0x1e]);
+#endif
+        }
+}
+
+static inline uint32_t dword_remap(uint32_t in_addr)
+{
+    return in_addr;
+}
+
+static void ncr_io_remove(ncr_t *ncr)
+{
+        io_removehandlerx(0x03c0, 0x0020, ncr_in, NULL, NULL, ncr_out, NULL, NULL, ncr);
+}
+
+static void ncr_io_set(ncr_t *ncr)
+{
+        ncr_io_remove(ncr);
+
+        io_sethandlerx(0x03c0, 0x0020, ncr_in, NULL, NULL, ncr_out, NULL, NULL, ncr);
+}
+
+static void *ncr_init(char *bios_fn, int chip)
+{
+        ncr_t *ncr = (ncr_t*)calloc(sizeof(ncr_t), 1);
+        svga_t *svga = &ncr->svga;
+        int vram;
+        uint32_t vram_size;
+        
+        memset(ncr, 0, sizeof(ncr_t));
+        
+        vram = device_get_config_int("memory");
+        if (vram)
+                vram_size = vram << 20;
+        else
+                vram_size = 512 << 10;
+        ncr->vram_mask = vram_size - 1;
+
+        rom_init(&ncr->bios_rom, bios_fn, 0xc0000, 0x8000, 0x7fff, 0, MEM_MAPPING_EXTERNAL);
+
+        svga_init(&ncr->svga, ncr, vram_size,
+            ncr_recalctimings,
+            ncr_in, ncr_out,
+            ncr_hwcursor_draw,
+            NULL);
+
+        mem_mapping_addx(&ncr->linear_mapping, 0,       0,       svga_read_linear, svga_readw_linear, svga_readl_linear, ncr_write_linear, ncr_writew_linear, ncr_writel_linear, NULL, MEM_MAPPING_EXTERNAL, &ncr->svga);
+        mem_mapping_set_handlerx(&ncr->svga.mapping, ncr_read, ncr_readw, ncr_readl, ncr_write, ncr_writew, ncr_writel);
+        mem_mapping_addx(&ncr->mmio_mapping, 0xa0000, 0x10000, ncr_mmio_read, ncr_mmio_readw, ncr_mmio_readl, ncr_mmio_write, ncr_mmio_write_w, ncr_mmio_write_l, NULL, MEM_MAPPING_EXTERNAL, ncr);
+        mem_mapping_set_px(&ncr->svga.mapping, ncr);
+        mem_mapping_disablex(&ncr->mmio_mapping);
+
+        svga->decode_mask = (4 << 20) - 1;
+        switch (vram)
+        {
+                case 0: /*512kb*/
+                svga->vram_mask = (1 << 19) - 1;
+                svga->vram_max = 4 << 20;
+                break;
+                case 1: /*1MB*/
+                svga->vram_mask = (1 << 20) - 1;
+                svga->vram_max = 4 << 20;
+                break;
+                case 2: default: /*2MB*/
+                svga->vram_mask = (2 << 20) - 1;
+                svga->vram_max = 4 << 20;
+                break;
+                case 4: /*4MB*/
+                svga->vram_mask = (4 << 20) - 1;
+                svga->vram_max = 4 << 20;
+                break;
+        }
+                
+        svga->vblank_start = ncr_vblank_start;
+        ncr->vblank_irq = -1;
+
+        ncr_io_set(ncr);
+
+        ncr->chip = chip;
+        ncr->blt_status = 1;
+
+        ncr_updatemapping(ncr);
+
+        return ncr;
+}
+
+void *ncr_retina_z2_init()
+{
+    ncr_t *ncr = (ncr_t *)ncr_init("ncr.bin", NCR_TYPE_22EP);
+
+    //ncr->getclock = ncr_getclock;
+    //ncr->getclock_p = ncr;
+    ncr->svga.fb_only = 0;
+    svga_set_ramdac_type(&ncr->svga, RAMDAC_8BIT);
+
+    return ncr;
+}
+
+void *ncr_retina_z3_init()
+{
+    ncr_t *ncr = (ncr_t *)ncr_init("ncr.bin", NCR_TYPE_32BLT);
+
+    //ncr->getclock = ncr_getclock;
+    //ncr->getclock_p = ncr;
+    ncr->svga.fb_only = -1;
+
+    return ncr;
+}
+
+void ncr_close(void *p)
+{
+        ncr_t *ncr = (ncr_t*)p;
+
+        svga_close(&ncr->svga);
+        
+        free(ncr);
+}
+
+void ncr_speed_changed(void *p)
+{
+        ncr_t *ncr = (ncr_t *)p;
+        
+        svga_recalctimings(&ncr->svga);
+}
+
+void ncr_force_redraw(void *p)
+{
+        ncr_t *ncr = (ncr_t *)p;
+
+        ncr->svga.fullchange = changeframecount;
+}
+
+void ncr_add_status_info(char *s, int max_len, void *p)
+{
+        ncr_t *ncr = (ncr_t *)p;
+        char temps[256];
+        uint64_t new_time = timer_read();
+        uint64_t status_diff = new_time - ncr->status_time;
+        ncr->status_time = new_time;
+
+        if (!status_diff)
+                status_diff = 1;
+        
+        svga_add_status_info(s, max_len, &ncr->svga);
+        sprintf(temps, "%f%% CPU\n%f%% CPU (real)\n\n", ((double)ncr->blitter_time * 100.0) / timer_freq, ((double)ncr->blitter_time * 100.0) / status_diff);
+        strncat(s, temps, max_len);
+
+        ncr->blitter_time = 0;
+}
+
+device_t ncr_retina_z2_device =
+{
+    "NCR 77C22E+",
+    0,
+    ncr_retina_z2_init,
+    ncr_close,
+    NULL,
+    ncr_speed_changed,
+    ncr_force_redraw,
+    ncr_add_status_info,
+    NULL
+};
+
+device_t ncr_retina_z3_device =
+{
+    "NCR 77C32BLT",
+    0,
+    ncr_retina_z3_init,
+    ncr_close,
+    NULL,
+    ncr_speed_changed,
+    ncr_force_redraw,
+    ncr_add_status_info,
+    NULL
+};
diff --git a/pcem/vid_ncr.h b/pcem/vid_ncr.h
new file mode 100644 (file)
index 0000000..13450ce
--- /dev/null
@@ -0,0 +1,2 @@
+extern device_t ncr_retina_z2_device;
+extern device_t ncr_retina_z3_device;