/* * UAE - The Un*x Amiga Emulator * * X86 Bridge board emulation * A1060, A2088, A2088T, A2286, A2386 * * Copyright 2015 Toni Wilen * 8088 x86 and PC support chip emulation from Fake86. * 80286+ CPU and AT support chip emulation from DOSBox. * */ #define X86_DEBUG_BRIDGE 1 #define FLOPPY_IO_DEBUG 0 #define X86_DEBUG_BRIDGE_IO 0 #define X86_IO_PORT_DEBUG 0 #define DEBUG_DMA 0 #define DEBUG_PIT 0 #define DEBUG_INT 0 #include "sysconfig.h" #include "sysdeps.h" #include "options.h" #include "custom.h" #include "memory.h" #include "debug.h" #include "x86.h" #include "newcpu.h" #include "uae.h" #include "rommgr.h" #include "zfile.h" #include "disk.h" #include "driveclick.h" #include "scsi.h" #include "idecontrollers.h" #include "gfxboard.h" #include "dosbox/dosbox.h" #include "dosbox/mem.h" #include "dosbox/paging.h" #include "dosbox/setup.h" #include "dosbox/cpu.h" #include "dosbox/pic.h" #include "dosbox/timer.h" typedef Bits(CPU_Decoder)(void); extern CPU_Decoder *cpudecoder; extern Bit32s CPU_Cycles; #define TYPE_SIDECAR 0 #define TYPE_2088 1 #define TYPE_2088T 2 #define TYPE_2286 3 #define TYPE_2386 4 void x86_reset(void); int x86_execute(void); void exec86(uint32_t execloops); void reset86(int v20); void intcall86(uint8_t intnum); void x86_doirq(uint8_t irqnum); static frame_time_t last_cycles; #define DEFAULT_X86_INSTRUCTION_COUNT 120 static int x86_instruction_count; static bool x86_turbo_allowed; static bool x86_turbo_enabled; bool x86_turbo_on; bool x86_cpu_active; void CPU_Init(Section*, int, int); void CPU_ShutDown(Section*); void CPU_JMP(bool use32, Bitu selector, Bitu offset, Bitu oldeip); void PAGING_Init(Section * sec); void MEM_Init(Section * sec); void MEM_ShutDown(Section * sec); void CMOS_Init(Section* sec, int); void CMOS_Destroy(Section* sec); void PIC_Init(Section* sec); void PIC_Destroy(Section* sec); void TIMER_Destroy(Section*); void TIMER_Init(Section* sec); static Section_prop *dosbox_sec; Bitu x86_in_keyboard(Bitu port); void x86_out_keyboard(Bitu port, Bitu val); void cmos_selreg(Bitu port, Bitu val, Bitu iolen); void cmos_writereg(Bitu port, Bitu val, Bitu iolen); Bitu cmos_readreg(Bitu port, Bitu iolen); void x86_pic_write(Bitu port, Bitu val); Bitu x86_pic_read(Bitu port); void x86_timer_write(Bitu port, Bitu val); Bitu x86_timer_read(Bitu port); void PIC_ActivateIRQ(Bitu irq); void PIC_DeActivateIRQ(Bitu irq); void PIC_runIRQs(void); void KEYBOARD_AddBuffer(Bit8u data); void FPU_Init(Section*); Bit8u *x86_cmos_regs(Bit8u *regs); void MEM_SetVGAHandler(void); Bit32s ticksDone; Bit32u ticksScheduled; HostPt mono_start, mono_end, color_start, color_end; int x86_memsize; int x86_biosstart; int x86_fpu_enabled; int x86_cmos_bank; int x86_xrom_start[2]; int x86_xrom_end[2]; int x86_vga_mode; struct x86_bridge { uae_u8 acmemory[128]; addrbank *bank; int configured; int type; uaecptr baseaddress; int pc_maxram; uae_u8 *pc_ram; uae_u8 *pc_rom; uae_u8 *io_ports; uae_u8 *amiga_io; bool x86_reset; bool amiga_irq; bool amiga_forced_interrupts; bool pc_irq3a, pc_irq3b, pc_irq7; uae_u8 pc_jumpers; int pc_maxbaseram; int bios_size; int settings; int dosbox_cpu; int dosbox_cpu_arch; bool x86_reset_requested; struct zfile *cmosfile; uae_u8 cmosregs[3 * 0x40]; uae_u8 vlsi_regs[0x100]; int a2386_default_video; int cmossize; int scamp_idx1, scamp_idx2; uae_u8 rombank[1024 * 1024 / 4096]; float dosbox_vpos_tick; float dosbox_tick_vpos_cnt; struct romconfig *rc; }; static int x86_found; #define X86_BRIDGE_A1060 0 #define X86_BRIDGE_MAX (X86_BRIDGE_A1060 + 1) #define ACCESS_MODE_BYTE 0 #define ACCESS_MODE_WORD 1 #define ACCESS_MODE_GFX 2 #define ACCESS_MODE_IO 3 #define IO_AMIGA_INTERRUPT_STATUS 0x1ff1 #define IO_PC_INTERRUPT_STATUS 0x1ff3 #define IO_NEGATE_PC_RESET 0x1ff5 #define IO_MODE_REGISTER 0x1ff7 #define IO_INTERRUPT_MASK 0x1ff9 #define IO_PC_INTERRUPT_CONTROL 0x1ffb #define IO_CONTROL_REGISTER 0x1ffd #define IO_KEYBOARD_REGISTER_A1000 0x61f #define IO_KEYBOARD_REGISTER_A2000 0x1fff #define IO_A2386_CONFIG 0x1f9f #define ISVGA() (currprefs.rtgmem_type == GFXBOARD_VGA) static struct x86_bridge *bridges[X86_BRIDGE_MAX]; static struct x86_bridge *x86_bridge_alloc(void) { struct x86_bridge *xb = xcalloc(struct x86_bridge, 1); return xb; }; void x86_init_reset(void) { struct x86_bridge *xb = bridges[0]; xb->x86_reset_requested = true; write_log(_T("8042 CPU reset requested\n")); } static void reset_dosbox_cpu(void) { struct x86_bridge *xb = bridges[0]; CPU_ShutDown(dosbox_sec); CPU_Init(dosbox_sec, xb->dosbox_cpu, xb->dosbox_cpu_arch); CPU_JMP(false, 0xffff, 0, 0); } static void reset_x86_cpu(struct x86_bridge *xb) { write_log(_T("x86 CPU reset\n")); if (!xb->dosbox_cpu) { reset86(xb->type == ROMTYPE_A2088T); } else { reset_dosbox_cpu(); } // reset x86 hd controllers x86_ide_hd_put(-1, 0, 0); x86_xt_hd_bput(-1, 0); } uint8_t x86_get_jumpers(void) { struct x86_bridge *xb = bridges[0]; uint8_t v = 0; if (xb->type >= TYPE_2286) { if (xb->pc_maxbaseram > 512 * 1024) v |= 0x20; } if (xb->type == TYPE_2386) { // A2386 = software controlled if (xb->a2386_default_video) v |= 0x40; } else { // Others have default video jumper if (!(xb->settings & (1 << 14))) { // default video mde, 0 = cga, 1 = mda v |= 0x40; } } if (!(xb->settings & (1 << 15))) { // key lock state: 0 = on, 1 = off v |= 0x80; } return v; } static uae_u8 get_mode_register(struct x86_bridge *xb, uae_u8 v) { if (xb->type == TYPE_SIDECAR) { // PC/XT + keyboard v = 0x84; if (!(xb->settings & (1 << 12))) v |= 0x02; if (!(xb->settings & (1 << 8))) v |= 0x08; if (!(xb->settings & (1 << 9))) v |= 0x10; if ((xb->settings & (1 << 10))) v |= 0x20; if ((xb->settings & (1 << 11))) v |= 0x40; } else if (xb->type < TYPE_2286) { // PC/XT v |= 0x80; } else { // AT v &= ~0x80; } return v; } static uae_u8 x86_bridge_put_io(struct x86_bridge *xb, uaecptr addr, uae_u8 v) { #if X86_DEBUG_BRIDGE_IO write_log(_T("IO write %08x %02x\n"), addr, v); #endif switch (addr) { // read-only case IO_AMIGA_INTERRUPT_STATUS: v = xb->amiga_io[addr]; break; case IO_PC_INTERRUPT_STATUS: v = xb->amiga_io[addr]; #if X86_DEBUG_BRIDGE_IRQ write_log(_T("IO_PC_INTERRUPT_STATUS %02x\n"), v); #endif break; case IO_NEGATE_PC_RESET: v = xb->amiga_io[addr]; break; case IO_PC_INTERRUPT_CONTROL: if ((v & 1) || (v & (2 | 4 | 8)) != (2 | 4 | 8)) { #if X86_DEBUG_BRIDGE_IRQ write_log(_T("IO_PC_INTERRUPT_CONTROL %02x\n"), v); #endif if (xb->type < TYPE_2286 && (v & 1)) x86_doirq(1); if (!(v & 2) || !(v & 4)) x86_doirq(3); if (!(v & 8)) x86_doirq(7); } break; case IO_CONTROL_REGISTER: if (!(v & 0x4)) { xb->x86_reset = true; } if (!(v & 1)) v |= 2; else if (!(v & 2)) v |= 1; #if X86_DEBUG_BRIDGE_IO write_log(_T("IO_CONTROL_REGISTER %02x\n"), v); #endif break; case IO_KEYBOARD_REGISTER_A1000: if (xb->type == TYPE_SIDECAR) { #if X86_DEBUG_BRIDGE_IO write_log(_T("IO_KEYBOARD_REGISTER %02x\n"), v); #endif xb->io_ports[0x60] = v; } break; case IO_KEYBOARD_REGISTER_A2000: if (xb->type >= TYPE_2088) { #if X86_DEBUG_BRIDGE_IO write_log(_T("IO_KEYBOARD_REGISTER %02x\n"), v); #endif xb->io_ports[0x60] = v; if (xb->type >= TYPE_2286) { KEYBOARD_AddBuffer(v); } } break; case IO_MODE_REGISTER: v = get_mode_register(xb, v); #if X86_DEBUG_BRIDGE_IO write_log(_T("IO_MODE_REGISTER %02x\n"), v); #endif break; case IO_INTERRUPT_MASK: break; case IO_A2386_CONFIG: write_log(_T("A2386 CONFIG BYTE %02x\n"), v); if (v == 8 || v == 9) { xb->a2386_default_video = v & 1; write_log(_T("A2386 Default mode = %s\n"), xb->a2386_default_video ? _T("MDA") : _T("CGA")); } break; default: if (addr >= 0x400) write_log(_T("Unknown bridge IO write %08x = %02x\n"), addr, v); break; } xb->amiga_io[addr] = v; return v; } static uae_u8 x86_bridge_get_io(struct x86_bridge *xb, uaecptr addr) { uae_u8 v = xb->amiga_io[addr]; v = xb->amiga_io[addr]; switch(addr) { case IO_AMIGA_INTERRUPT_STATUS: xb->amiga_io[addr] = 0; #if X86_DEBUG_BRIDGE_IRQ if (v) write_log(_T("IO_AMIGA_INTERRUPT_STATUS %02x. CLEARED.\n"), v); #endif break; case IO_PC_INTERRUPT_STATUS: v |= 0xf0; #if X86_DEBUG_BRIDGE_IRQ write_log(_T("IO_PC_INTERRUPT_STATUS %02x\n"), v); #endif break; case IO_NEGATE_PC_RESET: { if (xb->x86_reset) { reset_x86_cpu(xb); xb->x86_reset = false; } // because janus.library has stupid CPU loop wait int vp = vpos; while (vp == vpos) { x_do_cycles(maxhpos * CYCLE_UNIT); } } break; case IO_MODE_REGISTER: v = get_mode_register(xb, v); #if X86_DEBUG_BRIDGE_IO write_log(_T("IO_MODE_REGISTER %02x\n"), v); #endif break; default: if (addr >= 0x400) write_log(_T("Unknown bridge IO read %08x\n"), addr); break; } return v; } static void set_interrupt(struct x86_bridge *xb, int bit) { if (xb->amiga_io[IO_AMIGA_INTERRUPT_STATUS] & (1 << bit)) return; #if X86_DEBUG_BRIDGE_IRQ write_log(_T("IO_AMIGA_INTERRUPT_STATUS set bit %d\n"), bit); #endif xb->amiga_io[IO_AMIGA_INTERRUPT_STATUS] |= 1 << bit; x86_bridge_rethink(); } /* 8237 and 8253 from fake86 with small modifications */ #define PIT_MODE_LATCHCOUNT 0 #define PIT_MODE_LOBYTE 1 #define PIT_MODE_HIBYTE 2 #define PIT_MODE_TOGGLE 3 struct i8253_s { uint16_t chandata[3]; uint8_t accessmode[3]; uint8_t bytetoggle[3]; uint32_t effectivedata[3]; float chanfreq[3]; uint8_t active[3]; uint16_t counter[3]; }; static struct i8253_s i8253; static uint16_t latched_val, latched; static uint64_t hostfreq = 313 * 50 * 227; static uint64_t tickgap, curtick, lasttick, lasti8253tick, i8253tickgap; static void inittiming() { curtick = 0; lasti8253tick = lasttick = curtick; i8253tickgap = hostfreq / 119318; } static void timing(int count) { struct x86_bridge *xb = bridges[0]; curtick += count; if (i8253.active[0]) { //timer interrupt channel on i8253 if (curtick >= (lasttick + tickgap)) { lasttick = curtick; x86_doirq(0); } } if (curtick >= (lasti8253tick + i8253tickgap)) { for (int i8253chan = 0; i8253chan<3; i8253chan++) { bool cantrun = (i8253chan == 2 && !(xb->io_ports[0x61] & 1)); if (i8253.active[i8253chan] && !cantrun) { if (i8253.counter[i8253chan] < 10) i8253.counter[i8253chan] = i8253.chandata[i8253chan]; i8253.counter[i8253chan] -= 10; } } lasti8253tick = curtick; } } static void out8253(uint16_t portnum, uint8_t value) { uint8_t curbyte; #if DEBUG_PIT write_log("out8253(0x%X = %02x);\n", portnum, value); #endif portnum &= 3; switch (portnum) { case 0: case 1: case 2: //channel data if ((i8253.accessmode[portnum] == PIT_MODE_LOBYTE) || ((i8253.accessmode[portnum] == PIT_MODE_TOGGLE) && (i8253.bytetoggle[portnum] == 0))) curbyte = 0; else if ((i8253.accessmode[portnum] == PIT_MODE_HIBYTE) || ((i8253.accessmode[portnum] == PIT_MODE_TOGGLE) && (i8253.bytetoggle[portnum] == 1))) curbyte = 1; if (curbyte == 0) { //low byte i8253.chandata[portnum] = (i8253.chandata[portnum] & 0xFF00) | value; } else { //high byte i8253.chandata[portnum] = (i8253.chandata[portnum] & 0x00FF) | ((uint16_t)value << 8); } if (i8253.chandata[portnum] == 0) i8253.effectivedata[portnum] = 65536; else i8253.effectivedata[portnum] = i8253.chandata[portnum]; i8253.counter[portnum] = i8253.chandata[portnum]; i8253.active[portnum] = 1; tickgap = (uint64_t)((float)hostfreq / (float)((float)1193182 / (float)i8253.effectivedata[0])); if (i8253.accessmode[portnum] == PIT_MODE_TOGGLE) i8253.bytetoggle[portnum] = (~i8253.bytetoggle[portnum]) & 1; i8253.chanfreq[portnum] = (float)((uint32_t)(((float) 1193182.0 / (float)i8253.effectivedata[portnum]) * (float) 1000.0)) / (float) 1000.0; //printf("[DEBUG] PIT channel %u counter changed to %u (%f Hz)\n", portnum, i8253.chandata[portnum], i8253.chanfreq[portnum]); break; case 3: //mode/command i8253.accessmode[value >> 6] = (value >> 4) & 3; if (i8253.accessmode[value >> 6] == PIT_MODE_LATCHCOUNT) { latched_val = i8253.counter[value >> 6]; latched = 2; } else if (i8253.accessmode[value >> 6] == PIT_MODE_TOGGLE) i8253.bytetoggle[value >> 6] = 0; break; } } static uint8_t in8253(uint16_t portnum) { uint8_t curbyte; uint8_t out = 0; #if DEBUG_PIT uint16_t portnum2 = portnum; #endif portnum &= 3; switch (portnum) { case 0: case 1: case 2: //channel data if (latched == 2) { latched = 1; out = latched_val & 0xff; } else if (latched == 1) { latched = 0; out = latched_val >> 8; } else { if ((i8253.accessmode[portnum] == 0) || (i8253.accessmode[portnum] == PIT_MODE_LOBYTE) || ((i8253.accessmode[portnum] == PIT_MODE_TOGGLE) && (i8253.bytetoggle[portnum] == 0))) curbyte = 0; else if ((i8253.accessmode[portnum] == PIT_MODE_HIBYTE) || ((i8253.accessmode[portnum] == PIT_MODE_TOGGLE) && (i8253.bytetoggle[portnum] == 1))) curbyte = 1; if ((i8253.accessmode[portnum] == 0) || (i8253.accessmode[portnum] == PIT_MODE_TOGGLE)) i8253.bytetoggle[portnum] = (~i8253.bytetoggle[portnum]) & 1; if (curbyte == 0) { //low byte out = ((uint8_t)i8253.counter[portnum]); } else { //high byte out = ((uint8_t)(i8253.counter[portnum] >> 8)); } } break; } #if DEBUG_PIT write_log("in8253(0x%X = %02x);\n", portnum2, out); #endif return out; } struct dmachan_s { uint32_t page; uint32_t addr; uint32_t reload; uint32_t count; uint8_t direction; uint8_t autoinit; uint8_t writemode; uint8_t modeselect; uint8_t masked; uint8_t verifymode; }; static struct dmachan_s dmachan[2 * 4]; static uae_u8 dmareg[2 * 16]; static uint8_t flipflop = 0; static int write8237(struct x86_bridge *xb, uint8_t channel, uint8_t data) { if (dmachan[channel].masked) return 0; if (dmachan[channel].autoinit && (dmachan[channel].count > dmachan[channel].reload)) dmachan[channel].count = 0; if (dmachan[channel].count > dmachan[channel].reload) return 0; //if (dmachan[channel].direction) ret = RAM[dmachan[channel].page + dmachan[channel].addr + dmachan[channel].count]; // else ret = RAM[dmachan[channel].page + dmachan[channel].addr - dmachan[channel].count]; if (!dmachan[channel].verifymode) { xb->pc_ram[dmachan[channel].page + dmachan[channel].addr] = data; if (dmachan[channel].direction == 0) { dmachan[channel].addr++; } else { dmachan[channel].addr--; } dmachan[channel].addr &= 0xffff; } dmachan[channel].count++; if (dmachan[channel].count > dmachan[channel].reload) return -1; return 1; } static uint8_t read8237(struct x86_bridge *xb, uint8_t channel, bool *end) { uint8_t ret = 128; *end = false; if (dmachan[channel].masked) { *end = true; return ret; } if (dmachan[channel].autoinit && (dmachan[channel].count > dmachan[channel].reload)) dmachan[channel].count = 0; if (dmachan[channel].count > dmachan[channel].reload) { *end = true; return ret; } //if (dmachan[channel].direction) ret = RAM[dmachan[channel].page + dmachan[channel].addr + dmachan[channel].count]; // else ret = RAM[dmachan[channel].page + dmachan[channel].addr - dmachan[channel].count]; if (!dmachan[channel].verifymode) { ret = xb->pc_ram[dmachan[channel].page + dmachan[channel].addr]; if (dmachan[channel].direction == 0) { dmachan[channel].addr++; } else { dmachan[channel].addr--; } dmachan[channel].addr &= 0xffff; } dmachan[channel].count++; if (dmachan[channel].count > dmachan[channel].reload) *end = true; return ret; } static void out8237(uint16_t addr, uint8_t value) { uint8_t channel; int chipnum = 0; int reg = -1; #if DEBUG_DMA write_log("out8237(0x%X, %X);\n", addr, value); #endif if (addr >= 0x80 && addr <= 0x8f) { dmareg[addr & 0xf] = value; reg = addr; } else if (addr >= 0xc0 && addr <= 0xdf) { reg = (addr - 0xc0) / 2; chipnum = 4; } else if (addr <= 0x0f) { reg = addr; chipnum = 0; } switch (reg) { case 0x0: case 0x2: //channel 1 address register case 0x4: case 0x6: channel = reg / 2 + chipnum; if (flipflop == 1) dmachan[channel].addr = (dmachan[channel].addr & 0x00FF) | ((uint32_t)value << 8); else dmachan[channel].addr = (dmachan[channel].addr & 0xFF00) | value; #if DEBUG_DMA if (flipflop == 1) write_log("[NOTICE] DMA channel %d address register = %04X\n", channel, dmachan[channel].addr); #endif flipflop = ~flipflop & 1; break; case 0x1: case 0x3: //channel 1 count register case 0x5: case 0x7: channel = reg / 2 + chipnum; if (flipflop == 1) dmachan[channel].reload = (dmachan[channel].reload & 0x00FF) | ((uint32_t)value << 8); else dmachan[channel].reload = (dmachan[channel].reload & 0xFF00) | value; if (flipflop == 1) { if (dmachan[channel].reload == 0) dmachan[channel].reload = 65536; dmachan[channel].count = 0; #if DEBUG_DMA write_log("[NOTICE] DMA channel %d reload register = %04X\n", channel, dmachan[channel].reload); #endif } flipflop = ~flipflop & 1; break; case 0xA: //write single mask register channel = (value & 3) + chipnum; dmachan[channel].masked = (value >> 2) & 1; #if DEBUG_DMA write_log("[NOTICE] DMA channel %u masking = %u\n", channel, dmachan[channel].masked); #endif break; case 0xB: //write mode register channel = (value & 3) + chipnum; dmachan[channel].direction = (value >> 5) & 1; dmachan[channel].autoinit = (value >> 4) & 1; dmachan[channel].modeselect = (value >> 6) & 3; dmachan[channel].writemode = (value >> 2) & 1; //not quite accurate dmachan[channel].verifymode = ((value >> 2) & 3) == 0; #if DEBUG_DMA write_log("[NOTICE] DMA channel %u write mode reg: direction = %u, autoinit = %u, write mode = %u, verify mode = %u, mode select = %u\n", channel, dmachan[channel].direction, dmachan[channel].autoinit, dmachan[channel].writemode, dmachan[channel].verifymode, dmachan[channel].modeselect); #endif break; case 0xC: //clear byte pointer flip-flop #if DEBUG_DMA write_log("[NOTICE] DMA cleared byte pointer flip-flop\n"); #endif flipflop = 0; break; case 0x89: // 6 case 0x8a: // 7 case 0x8b: // 5 case 0x81: // 2 case 0x82: // 3 case 0x83: // DMA channel 1 page register // Original PC design. It can't get any more stupid. if ((addr & 3) == 1) channel = 2; else if ((addr & 3) == 2) channel = 3; else channel = 1; if (addr >= 0x84) channel += 4; dmachan[channel].page = (uint32_t)value << 16; #if DEBUG_DMA write_log("[NOTICE] DMA channel %d page base = %05X\n", channel, dmachan[channel].page); #endif break; } } static uint8_t in8237(uint16_t addr) { struct x86_bridge *xb = bridges[0]; uint8_t channel; int reg = -1; int chipnum = addr >= 0xc0 ? 4 : 0; uint8_t out = 0; if (addr >= 0xc0 && addr <= 0xdf) { reg = (addr - 0xc0) / 2; chipnum = 4; } else if (addr <= 0x0f) { reg = addr; chipnum = 0; } switch (reg) { case 0x0: case 0x2: //channel 1 address register case 0x4: case 0x6: channel = reg / 2 + chipnum; flipflop = ~flipflop & 1; if (flipflop == 0) out = dmachan[channel].addr >> 8; else out = dmachan[channel].addr; break; case 0x1: case 0x3: //channel 1 count register case 0x5: case 0x7: channel = reg / 2 + chipnum; flipflop = ~flipflop & 1; if (flipflop == 0) out = dmachan[channel].reload >> 8; else out = dmachan[channel].reload; break; } if (addr >= 0x80 && addr <= 0x8f) out = dmareg[addr & 0xf]; #if DEBUG_DMA write_log("in8237(0x%X = %02x);\n", addr, out); #endif return out; } static int keyboardwaitack; void x86_ack_keyboard(void) { if (keyboardwaitack) set_interrupt(bridges[0], 4); keyboardwaitack = 0; } struct structpic { uint8_t imr; //mask register uint8_t irr; //request register uint8_t isr; //service register uint8_t icwstep; //used during initialization to keep track of which ICW we're at uint8_t icw[5]; uint8_t intoffset; //interrupt vector offset uint8_t priority; //which IRQ has highest priority uint8_t autoeoi; //automatic EOI mode uint8_t readmode; //remember what to return on read register from OCW3 uint8_t enabled; }; static struct structpic i8259[2]; static uint8_t in8259(uint16_t portnum) { struct x86_bridge *xb = bridges[0]; uint8_t out = 0; int chipnum = (portnum & 0x80) ? 1 : 0; if (chipnum && xb->type < TYPE_2286) return 0; switch (portnum & 1) { case 0: if (i8259[chipnum].readmode == 0) out = (i8259[chipnum].irr); else out = (i8259[chipnum].isr); break; case 1: //read mask register out = (i8259[chipnum].imr); break; } #if DEBUG_INT write_log("in8259(0x%X = %02x);\n", portnum, out); #endif return out; } extern uint32_t makeupticks; void out8259(uint16_t portnum, uint8_t value) { struct x86_bridge *xb = bridges[0]; uint8_t i; int chipnum = (portnum & 0x80) ? 1 : 0; #if DEBUG_INT write_log("out8259(0x%X = %02x);\n", portnum, value); #endif if (chipnum && xb->type < TYPE_2286) return; switch (portnum & 1) { case 0: if (value & 0x10) { //begin initialization sequence i8259[chipnum].icwstep = 1; i8259[chipnum].imr = 0; //clear interrupt mask register i8259[chipnum].icw[i8259[chipnum].icwstep++] = value; return; } if ((value & 0x98) == 8) { //it's an OCW3 if (value & 2) i8259[chipnum].readmode = value & 2; } if (value & 0x20) { //EOI command if (!chipnum) { x86_ack_keyboard(); keyboardwaitack = 0; } for (i = 0; i < 8; i++) if ((i8259[chipnum].isr >> i) & 1) { i8259[chipnum].isr ^= (1 << i); #if 0 if ((i == 0) && (makeupticks>0)) { makeupticks = 0; i8259.irr |= 1; } #endif return; } } break; case 1: if ((i8259[chipnum].icwstep == 3) && (i8259[chipnum].icw[1] & 2)) i8259[chipnum].icwstep = 4; //single mode, so don't read ICW3 if (i8259[chipnum].icwstep<5) { i8259[chipnum].icw[i8259[chipnum].icwstep++] = value; return; } //if we get to this point, this is just a new IMR value i8259[chipnum].imr = value; break; } } static uint8_t nextintr(void) { uint8_t i, tmpirr; tmpirr = i8259[0].irr & (~i8259[0].imr); //XOR request register with inverted mask register for (i = 0; i<8; i++) { if ((tmpirr >> i) & 1) { i8259[0].irr ^= (1 << i); i8259[0].isr |= (1 << i); return(i8259[0].icw[2] + i); } } return(0); //this won't be reached, but without it the compiler gives a warning } void x86_clearirq(uint8_t irqnum) { struct x86_bridge *xb = bridges[0]; if (xb->dosbox_cpu) { PIC_DeActivateIRQ(irqnum); } } void x86_doirq(uint8_t irqnum) { struct x86_bridge *xb = bridges[0]; if (xb->dosbox_cpu) { PIC_ActivateIRQ(irqnum); } else { i8259[0].irr |= (1 << irqnum); } if (irqnum == 1) keyboardwaitack = 1; } void x86_doirq_keyboard(void) { x86_doirq(1); } void check_x86_irq(void) { struct x86_bridge *xb = bridges[0]; if (xb->dosbox_cpu) { PIC_runIRQs(); } else { if (i8259[0].irr & (~i8259[0].imr)) { uint8_t irq = nextintr(); intcall86(irq); /* get next interrupt from the i8259, if any */ } } } struct pc_floppy { int seek_offset; int phys_cyl; int cyl; uae_u8 sector; uae_u8 head; }; static struct pc_floppy floppy_pc[4]; static uae_u8 floppy_dpc; static uae_s8 floppy_idx; static uae_u8 floppy_dir; static uae_u8 floppy_cmd_len; static uae_u8 floppy_cmd[16]; static uae_u8 floppy_result[16]; static uae_u8 floppy_status[4]; static uae_u8 floppy_num; static int floppy_seeking[4]; static uae_u8 floppy_seekcyl[4]; static int floppy_delay_hsync; static bool floppy_did_reset; static bool floppy_irq; static uae_s8 floppy_rate; #define PC_SEEK_DELAY 50 static void floppy_reset(void) { struct x86_bridge *xb = bridges[0]; write_log(_T("Floppy reset\n")); floppy_idx = 0; floppy_dir = 0; floppy_did_reset = true; if (xb->type == TYPE_2286) { // apparently A2286 BIOS AT driver assumes // floppy reset also resets IDE. // Perhaps this is forgotten feature from // Commodore PC model that uses same BIOS variant? x86_ide_hd_put(-1, 0, 0); } } static void floppy_hardreset(void) { floppy_rate = -1; floppy_reset(); } static void do_floppy_irq2(void) { write_log(_T("floppy%d irq (enable=%d)\n"), floppy_num, (floppy_dpc & 8) != 0); if (floppy_dpc & 8) { floppy_irq = true; x86_doirq(6); } } static void do_floppy_irq(void) { if (floppy_delay_hsync > 0) { do_floppy_irq2(); } floppy_delay_hsync = 0; } static void do_floppy_seek(int num, int error) { struct pc_floppy *pcf = &floppy_pc[floppy_num]; disk_reserved_reset_disk_change(num); if (!error) { struct floppy_reserved fr = { 0 }; bool valid_floppy = disk_reserved_getinfo(floppy_num, &fr); if (floppy_seekcyl[num] != pcf->phys_cyl) { if (floppy_seekcyl[num] > pcf->phys_cyl) pcf->phys_cyl++; else if (pcf->phys_cyl > 0) pcf->phys_cyl--; #ifdef DRIVESOUND if (valid_floppy) driveclick_click(fr.num, pcf->phys_cyl); #endif write_log(_T("Floppy%d seeking.. %d\n"), floppy_num, pcf->phys_cyl); if (pcf->phys_cyl - pcf->seek_offset <= 0) { pcf->phys_cyl = 0; if (pcf->seek_offset) { floppy_seekcyl[num] = 0; write_log(_T("Floppy%d early track zero\n"), floppy_num); pcf->seek_offset = 0; } } if (valid_floppy && fr.cyls < 80 && fr.drive_cyls >=80) pcf->cyl = pcf->phys_cyl / 2; else pcf->cyl = pcf->phys_cyl; floppy_seeking[num] = PC_SEEK_DELAY; disk_reserved_setinfo(floppy_num, pcf->cyl, pcf->head, 1); return; } if (valid_floppy && pcf->phys_cyl > fr.drive_cyls + 3) { pcf->seek_offset = pcf->phys_cyl - (fr.drive_cyls + 3); pcf->phys_cyl = fr.drive_cyls + 3; goto done; } if (valid_floppy && fr.cyls < 80 && fr.drive_cyls >= 80) pcf->cyl = pcf->phys_cyl / 2; else pcf->cyl = pcf->phys_cyl; if (floppy_seekcyl[num] != pcf->phys_cyl) return; } done: write_log(_T("Floppy%d seek done err=%d. pcyl=%d cyl=%d h=%d\n"), floppy_num, error, pcf->phys_cyl,pcf->cyl, pcf->head); floppy_status[0] = 0; floppy_status[0] |= 0x20; // seek end if (error) { floppy_status[0] |= 0x40; // abnormal termination floppy_status[0] |= 0x10; // equipment check } floppy_status[0] |= num; floppy_status[0] |= pcf->head ? 4 : 0; do_floppy_irq2(); } static int floppy_exists(void) { uae_u8 sel = floppy_dpc & 3; if (sel == 0) return 0; if (sel == 1) return 1; return -1; } static int floppy_selected(void) { uae_u8 motormask = (floppy_dpc >> 4) & 15; uae_u8 sel = floppy_dpc & 3; if (floppy_exists() < 0) return -1; if (motormask & (1 << sel)) return sel; return -1; } static bool floppy_valid_rate(struct floppy_reserved *fr) { struct x86_bridge *xb = bridges[0]; // A2386 BIOS sets 720k data rate for both 720k and 1.4M drives // probably because it thinks Amiga half-speed drive is connected? if (xb->type == TYPE_2386 && fr->rate == 0 && floppy_rate == 2) return true; return fr->rate == floppy_rate || floppy_rate < 0; } static void floppy_do_cmd(struct x86_bridge *xb) { uae_u8 cmd = floppy_cmd[0]; struct pc_floppy *pcf = &floppy_pc[floppy_num]; struct floppy_reserved fr = { 0 }; bool valid_floppy; valid_floppy = disk_reserved_getinfo(floppy_num, &fr); if (floppy_cmd_len) { write_log(_T("Command: ")); for (int i = 0; i < floppy_cmd_len; i++) { write_log(_T("%02x "), floppy_cmd[i]); } write_log(_T("\n")); } if (cmd == 8) { write_log(_T("Floppy%d Sense Interrupt Status\n"), floppy_num); floppy_cmd_len = 2; if (floppy_irq) { write_log(_T("Floppy interrupt reset\n")); } if (!floppy_irq) { floppy_status[0] = 0x80; floppy_cmd_len = 1; } else if (floppy_did_reset) { floppy_did_reset = false; // 0xc0 status after reset. floppy_status[0] = 0xc0; } x86_clearirq(6); floppy_irq = false; floppy_result[0] = floppy_status[0]; floppy_result[1] = pcf->phys_cyl; floppy_status[0] = 0; goto end; } memset(floppy_status, 0, sizeof floppy_status); memset(floppy_result, 0, sizeof floppy_result); if (floppy_exists() >= 0) { if (pcf->head) floppy_status[0] |= 4; floppy_status[3] |= pcf->phys_cyl == 0 ? 0x10 : 0x00; floppy_status[3] |= pcf->head ? 4 : 0; floppy_status[3] |= 8; // two side if (fr.wrprot) floppy_status[3] |= 0x40; // write protected floppy_status[3] |= 0x20; // ready } floppy_status[3] |= floppy_dpc & 3; floppy_status[0] |= floppy_dpc & 3; floppy_cmd_len = 0; switch (cmd & 31) { case 3: write_log(_T("Floppy%d Specify SRT=%d HUT=%d HLT=%d ND=%d\n"), floppy_num, floppy_cmd[1] >> 4, floppy_cmd[1] & 0x0f, floppy_cmd[2] >> 1, floppy_cmd[2] & 1); floppy_delay_hsync = -5; break; case 4: write_log(_T("Floppy%d Sense Drive Status\n"), floppy_num); floppy_delay_hsync = 5; floppy_result[0] = floppy_status[3]; floppy_cmd_len = 1; break; case 5: { write_log(_T("Floppy%d write MT=%d MF=%d C=%d:H=%d:R=%d:N=%d:EOT=%d:GPL=%d:DTL=%d\n"), (floppy_cmd[0] & 0x80) ? 1 : 0, (floppy_cmd[0] & 0x40) ? 1 : 0, floppy_cmd[2], floppy_cmd[3], floppy_cmd[4], floppy_cmd[5], floppy_cmd[6], floppy_cmd[7], floppy_cmd[8]); write_log(_T("DMA addr %08x len %04x\n"), dmachan[2].page | dmachan[2].addr, dmachan[2].reload); floppy_delay_hsync = 50; int eot = floppy_cmd[6]; bool mt = (floppy_cmd[0] & 0x80) != 0; int cyl = pcf->cyl; if (valid_floppy) { if (fr.img && pcf->cyl != floppy_cmd[2]) { floppy_status[0] |= 0x40; // abnormal termination floppy_status[2] |= 0x20; // wrong cylinder } else if (fr.img) { bool end = false; pcf->sector = floppy_cmd[4] - 1; pcf->head = (floppy_cmd[1] & 4) ? 1 : 0; while (!end) { int len = 128 << floppy_cmd[5]; uae_u8 buf[512] = { 0 }; for (int i = 0; i < 512 && i < len; i++) { buf[i] = read8237(xb, 2, &end); if (end) break; } zfile_fseek(fr.img, (pcf->cyl * fr.secs * fr.heads + pcf->head * fr.secs + pcf->sector) * 512, SEEK_SET); zfile_fwrite(buf, 1, 512, fr.img); pcf->sector++; if (!(floppy_cmd[0] & 0x80)) break; if (pcf->sector == eot) { pcf->sector = 0; if (mt) { if (pcf->head) pcf->cyl++; pcf->head ^= 1; } break; } if (pcf->sector >= fr.secs) { pcf->sector = 0; pcf->head ^= 1; } } floppy_result[3] = cyl; floppy_result[4] = pcf->head; floppy_result[5] = pcf->sector + 1; floppy_result[6] = floppy_cmd[5]; } else { floppy_status[0] |= 0x40; // abnormal termination floppy_status[0] |= 0x10; // equipment check } } floppy_cmd_len = 7; if (fr.wrprot) { floppy_status[0] |= 0x40; // abnormal termination floppy_status[1] |= 0x02; // not writable } floppy_result[0] = floppy_status[0]; floppy_result[1] = floppy_status[1]; floppy_result[2] = floppy_status[2]; floppy_delay_hsync = 10; disk_reserved_setinfo(floppy_num, pcf->cyl, pcf->head, 1); } break; case 6: { write_log(_T("Floppy%d read MT=%d MF=%d SK=%d C=%d:H=%d:R=%d:N=%d:EOT=%d:GPL=%d:DTL=%d\n"), floppy_num, (floppy_cmd[0] & 0x80) ? 1 : 0, (floppy_cmd[0] & 0x40) ? 1 : 0, (floppy_cmd[0] & 0x20) ? 1 : 0, floppy_cmd[2], floppy_cmd[3], floppy_cmd[4], floppy_cmd[5], floppy_cmd[6], floppy_cmd[7], floppy_cmd[8]); write_log(_T("DMA addr %08x len %04x\n"), dmachan[2].page | dmachan[2].addr, dmachan[2].reload); floppy_delay_hsync = 50; int eot = floppy_cmd[6]; bool mt = (floppy_cmd[0] & 0x80) != 0; int cyl = pcf->cyl; bool nodata = false; if (valid_floppy) { if (!floppy_valid_rate(&fr)) { nodata = true; } else if (pcf->head && fr.heads == 1) { nodata = true; } else if (fr.img && pcf->cyl != floppy_cmd[2]) { floppy_status[0] |= 0x40; // abnormal termination floppy_status[2] |= 0x20; // wrong cylinder } else if (fr.img) { bool end = false; pcf->sector = floppy_cmd[4] - 1; pcf->head = (floppy_cmd[1] & 4) ? 1 : 0; while (!end) { int len = 128 << floppy_cmd[5]; uae_u8 buf[512]; zfile_fseek(fr.img, (pcf->cyl * fr.secs * fr.heads + pcf->head * fr.secs + pcf->sector) * 512, SEEK_SET); zfile_fread(buf, 1, 512, fr.img); for (int i = 0; i < 512 && i < len; i++) { if (write8237(xb, 2, buf[i]) <= 0) end = true; } pcf->sector++; if (!(floppy_cmd[0] & 0x80)) break; if (pcf->sector == eot) { pcf->sector = 0; if (mt) { if (pcf->head) pcf->cyl++; pcf->head ^= 1; } break; } if (pcf->sector >= fr.secs) { pcf->sector = 0; pcf->head ^= 1; } } floppy_result[3] = cyl; floppy_result[4] = pcf->head; floppy_result[5] = pcf->sector + 1; floppy_result[6] = floppy_cmd[5]; } else { nodata = true; } } if (nodata) { floppy_status[0] |= 0x40; // abnormal termination floppy_status[1] |= 0x04; // no data } floppy_cmd_len = 7; floppy_result[0] = floppy_status[0]; floppy_result[1] = floppy_status[1]; floppy_result[2] = floppy_status[2]; floppy_delay_hsync = 10; disk_reserved_setinfo(floppy_num, pcf->cyl, pcf->head, 1); } break; case 7: write_log(_T("Floppy%d recalibrate\n"), floppy_num); if (valid_floppy) { floppy_seekcyl[floppy_num] = 0; floppy_seeking[floppy_num] = PC_SEEK_DELAY; } else { floppy_seeking[floppy_num] = -PC_SEEK_DELAY; } break; case 10: write_log(_T("Floppy read ID\n")); if (!valid_floppy || !fr.img || !floppy_valid_rate(&fr) || (pcf->head && fr.heads == 1)) { floppy_status[0] |= 0x40; // abnormal termination floppy_status[1] |= 0x04; // no data } floppy_cmd_len = 7; floppy_result[0] = floppy_status[0]; floppy_result[1] = floppy_status[1]; floppy_result[2] = floppy_status[2]; floppy_result[3] = pcf->cyl; floppy_result[4] = pcf->head; floppy_result[5] = pcf->sector + 1; floppy_result[6] = 2; if (valid_floppy && fr.img) { pcf->sector++; pcf->sector %= fr.secs; } floppy_delay_hsync = 10; disk_reserved_setinfo(floppy_num, pcf->cyl, pcf->head, 1); break; case 13: { write_log(_T("Floppy%d format MF=%d N=%d:SC=%d:GPL=%d:D=%d\n"), floppy_num, (floppy_cmd[0] & 0x40) ? 1 : 0, floppy_cmd[2], floppy_cmd[3], floppy_cmd[4], floppy_cmd[5]); write_log(_T("DMA addr %08x len %04x\n"), dmachan[2].page | dmachan[2].addr, dmachan[2].reload); int secs = floppy_cmd[3]; if (valid_floppy && fr.img) { // TODO: CHRN values totally ignored pcf->head = (floppy_cmd[1] & 4) ? 1 : 0; uae_u8 buf[512]; memset(buf, floppy_cmd[5], sizeof buf); for (int i = 0; i < secs && i < fr.secs; i++) { bool dmaend; uae_u8 cx = read8237(xb, 2, &dmaend); uae_u8 hx = read8237(xb, 2, &dmaend); uae_u8 rx = read8237(xb, 2, &dmaend); uae_u8 nx = read8237(xb, 2, &dmaend); pcf->sector = rx - 1; write_log(_T("Floppy%d %d/%d: C=%d H=%d R=%d N=%d\n"), floppy_num, i, fr.secs, cx, hx, rx, nx); zfile_fseek(fr.img, (pcf->cyl * fr.secs * fr.heads + pcf->head * fr.secs + pcf->sector) * 512, SEEK_SET); zfile_fwrite(buf, 1, 512, fr.img); } } else { floppy_status[0] |= 0x40; // abnormal termination floppy_status[0] |= 0x10; // equipment check } floppy_cmd_len = 7; if (fr.wrprot) { floppy_status[0] |= 0x40; // abnormal termination floppy_status[1] |= 0x02; // not writable } floppy_result[0] = floppy_status[0]; floppy_result[1] = floppy_status[1]; floppy_result[2] = floppy_status[2]; floppy_result[3] = pcf->cyl; floppy_result[4] = pcf->head; floppy_result[5] = pcf->sector + 1; floppy_result[6] = floppy_cmd[2]; floppy_delay_hsync = 10; disk_reserved_setinfo(floppy_num, pcf->cyl, pcf->head, 1); } break; case 15: { int newcyl = floppy_cmd[2]; write_log(_T("Floppy%d seek %d->%d (max %d)\n"), floppy_num, pcf->phys_cyl, newcyl, fr.cyls); floppy_seekcyl[floppy_num] = newcyl; floppy_seeking[floppy_num] = valid_floppy ? PC_SEEK_DELAY : -PC_SEEK_DELAY; pcf->head = (floppy_cmd[1] & 4) ? 1 : 0; } break; default: floppy_status[0] = 0x80; floppy_cmd_len = 1; break; } end: if (floppy_cmd_len) { floppy_idx = -1; floppy_dir = 1; write_log(_T("Status return: ")); for (int i = 0; i < floppy_cmd_len; i++) { write_log(_T("%02x "), floppy_result[i]); } write_log(_T("\n")); } else { floppy_idx = 0; floppy_dir = 0; } } static void outfloppy(struct x86_bridge *xb, int portnum, uae_u8 v) { if (!x86_turbo_allowed) { x86_turbo_allowed = true; } switch (portnum) { case 0x3f2: // dpc if ((v & 4) && !(floppy_dpc & 4)) { floppy_reset(); floppy_delay_hsync = 20; } #if FLOPPY_IO_DEBUG write_log(_T("DPC=%02x: Motormask %02x sel=%d dmaen=%d reset=%d\n"), v, (v >> 4) & 15, v & 3, (v & 8) ? 1 : 0, (v & 4) ? 0 : 1); #endif #ifdef DRIVESOUND for (int i = 0; i < 2; i++) { int mask = 0x10 << i; if ((floppy_dpc & mask) != (v & mask)) { struct floppy_reserved fr = { 0 }; bool valid_floppy = disk_reserved_getinfo(i, &fr); if (valid_floppy) driveclick_motor(fr.num, (v & mask) ? 1 : 0); } } #endif floppy_dpc = v; floppy_num = v & 3; for (int i = 0; i < 2; i++) { disk_reserved_setinfo(0, floppy_pc[i].cyl, floppy_pc[i].head, floppy_selected() == i); } break; case 0x3f5: // data reg floppy_cmd[floppy_idx] = v; if (floppy_idx == 0) { switch(v & 31) { case 3: // specify floppy_cmd_len = 3; break; case 4: // sense drive status floppy_cmd_len = 2; break; case 5: // write data floppy_cmd_len = 9; break; case 6: // read data floppy_cmd_len = 9; break; case 7: // recalibrate floppy_cmd_len = 2; break; case 8: // sense interrupt status floppy_cmd_len = 1; break; case 10: // read id floppy_cmd_len = 2; break; case 13: // format track floppy_cmd_len = 6; break; case 15: // seek floppy_cmd_len = 3; break; default: write_log(_T("Floppy unimplemented command %02x\n"), v); floppy_cmd_len = 1; break; } } floppy_idx++; if (floppy_idx >= floppy_cmd_len) { floppy_do_cmd(xb); } break; case 0x3f7: // configuration control if (xb->type >= TYPE_2286) { write_log(_T("FDC Control Register %02x\n"), v); floppy_rate = v & 3; } else { floppy_rate = -1; } break; default: write_log(_T("Unknown FDC %04x write %02x\n"), portnum, v); break; } #if FLOPPY_IO_DEBUG write_log(_T("out floppy port %04x %02x\n"), portnum, v); #endif } static uae_u8 infloppy(struct x86_bridge *xb, int portnum) { uae_u8 v = 0; switch (portnum) { case 0x3f4: // main status v = 0; if (!floppy_delay_hsync && (floppy_dpc & 4)) v |= 0x80; if (floppy_idx || floppy_delay_hsync) v |= 0x10; if ((v & 0x80) && floppy_dir) v |= 0x40; for (int i = 0; i < 4; i++) { if (floppy_seeking[i]) v |= 1 << i; } break; case 0x3f5: // data reg if (floppy_cmd_len && floppy_dir) { int idx = (-floppy_idx) - 1; if (idx < sizeof floppy_result) { v = floppy_result[idx]; idx++; floppy_idx--; if (idx >= floppy_cmd_len) { floppy_cmd_len = 0; floppy_dir = 0; floppy_idx = 0; } } } break; case 0x3f7: // digital input register if (xb->type >= TYPE_2286) { struct floppy_reserved fr = { 0 }; bool valid_floppy = disk_reserved_getinfo(floppy_num, &fr); v = 0x00; if (valid_floppy && fr.disk_changed) v = 0x80; } break; default: write_log(_T("Unknown FDC %04x read\n"), portnum); break; } #if FLOPPY_IO_DEBUG write_log(_T("in floppy port %04x %02x\n"), portnum, v); #endif return v; } void bridge_mono_hit(void) { struct x86_bridge *xb = bridges[0]; if (xb->amiga_io[IO_MODE_REGISTER] & 8) set_interrupt(xb, 0); } void bridge_color_hit(void) { struct x86_bridge *xb = bridges[0]; if (xb->amiga_io[IO_MODE_REGISTER] & 16) set_interrupt(xb, 1); } static void set_pc_address_access(struct x86_bridge *xb, uaecptr addr) { if (addr >= 0xb0000 && addr < 0xb2000) { // mono if (xb->amiga_io[IO_MODE_REGISTER] & 8) set_interrupt(xb, 0); } if (addr >= 0xb8000 && addr < 0xc0000) { // color if (xb->amiga_io[IO_MODE_REGISTER] & 16) set_interrupt(xb, 1); } } static void set_pc_io_access(struct x86_bridge *xb, uaecptr portnum, bool write) { uae_u8 mode_register = xb->amiga_io[IO_MODE_REGISTER]; if (write && portnum >= 0x3b0 && portnum < 0x3bf) { // mono crt if (mode_register & 8) set_interrupt(xb, 2); } else if (write && portnum >= 0x3d0 && portnum < 0x3df) { // color crt if (mode_register & 16) set_interrupt(xb, 3); } else if (portnum >= 0x37a && portnum < 0x37b) { // LPT1 set_interrupt(xb, 5); } else if (portnum >= 0x2f8 && portnum < 0x2f9) { // COM2 set_interrupt(xb, 6); } } static bool is_port_enabled(struct x86_bridge *xb, uint16_t portnum) { uae_u8 enables = xb->amiga_io[IO_MODE_REGISTER]; // COM2 if (portnum >= 0x2f8 && portnum < 0x300) { if (!(enables & 1)) return false; } // LPT1 if (portnum >= 0x378 && portnum < 0x37f) { if (!(enables & 2)) return false; } // Keyboard // ??? return true; } static uae_u8 vlsi_in(struct x86_bridge *xb, int portnum) { uae_u8 v = 0; switch(portnum) { case 0xed: v = xb->vlsi_regs[xb->scamp_idx2]; break; } //write_log(_T("VLSI_IN %02x = %02x\n"), portnum, v); return v; } static void vlsi_reg_out(struct x86_bridge *xb, int reg, uae_u8 v) { uae_u32 shadow_start = 0; uae_u32 shadow_size = 0; switch(reg) { case 0x1d: x86_cmos_bank = (v & 0x20) ? 1 : 0; break; case 0x0e: // ABAX shadow_size = 0x8000; shadow_start = 0xa0000; break; case 0x0f: // CAXS shadow_size = 0x4000; shadow_start = 0xc0000; break; case 0x10: // DAXS shadow_size = 0x4000; shadow_start = 0xd0000; break; case 0x11: // FEAXS shadow_size = 0x4000; shadow_start = 0xe0000; break; } if (shadow_size) { for (int i = 0; i < 4; i++) { int state = (v >> (i * 2)) & 3; write_log(_T("%06x - %06x : shadow status=%d\n"), shadow_start, shadow_start + shadow_size - 1, state); shadow_start += shadow_size; } } } static void vlsi_out(struct x86_bridge *xb, int portnum, uae_u8 v) { switch (portnum) { case 0xe8: xb->scamp_idx1 = v; break; case 0xec: xb->scamp_idx2 = v; break; case 0xed: xb->vlsi_regs[xb->scamp_idx2] = v; vlsi_reg_out(xb, xb->scamp_idx2, v); break; } //write_log(_T("VLSI_OUT %02x = %02x\n"), portnum, v); } void portout(uint16_t portnum, uint8_t v) { struct x86_bridge *xb = bridges[0]; uae_u8 *io = xb->io_ports; int aio = -1; uae_u8 enables = xb->amiga_io[IO_MODE_REGISTER]; bool mda_emu = (enables & 8) != 0; bool cga_emu = (enables & 16) != 0; if (portnum >= 0x400) return; if (!is_port_enabled(xb, portnum)) return; set_pc_io_access(xb, portnum, true); switch(portnum) { case 0x20: case 0x21: case 0xa0: case 0xa1: if (xb->dosbox_cpu) x86_pic_write(portnum, v); else out8259(portnum, v); break; case 0x40: case 0x41: case 0x42: case 0x43: if (xb->dosbox_cpu) x86_timer_write(portnum, v); else out8253(portnum, v); break; case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87: case 0x88: case 0x89: case 0x8a: case 0x8b: case 0x8c: case 0x8d: case 0x8e: case 0x8f: case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f: case 0xc0: case 0xc1: case 0xc2: case 0xc3: case 0xc4: case 0xc5: case 0xc6: case 0xc7: case 0xc8: case 0xc9: case 0xca: case 0xcb: case 0xcc: case 0xcd: case 0xce: case 0xcf: case 0xd0: case 0xd1: case 0xd2: case 0xd3: case 0xd4: case 0xd5: case 0xd6: case 0xd7: case 0xd8: case 0xd9: case 0xda: case 0xdb: case 0xdc: case 0xdd: case 0xde: case 0xdf: out8237(portnum, v); break; case 0x60: if (xb->type >= TYPE_2286) { x86_out_keyboard(0x60, v); } else { aio = 0x41f; } break; case 0x61: //write_log(_T("OUT Port B %02x\n"), v); if (xb->type >= TYPE_2286) { x86_out_keyboard(0x61, v); } else { aio = 0x5f; } break; case 0x62: //write_log(_T("AMIGA SYSINT. %02x\n"), v); set_interrupt(xb, 7); aio = 0x3f; if (xb->type > TYPE_SIDECAR) { // default video mode bits 4-5 come from jumpers. if (!(xb->io_ports[0x63] & 8)) { xb->pc_jumpers &= ~0xcf; xb->pc_jumpers |= v & 0xcf; } } break; case 0x63: write_log(_T("OUT CONFIG %02x\n"), v); if (xb->type > TYPE_SIDECAR) { if (xb->io_ports[0x63] & 8) { v |= 8; } if (xb->type == TYPE_2088T) { int speed = v >> 6; if (speed == 0) write_log(_T("A2088T: 4.77MHz\n")); if (speed == 1) write_log(_T("A2088T: 7.15MHz\n")); if (speed == 2) write_log(_T("A2088T: 9.54MHz\n")); } } break; case 0x64: if (xb->type >= TYPE_2286) { x86_out_keyboard(0x64, v); } break; case 0x70: if (xb->type >= TYPE_2286) cmos_selreg(portnum, v, 1); break; case 0x71: if (xb->type >= TYPE_2286) cmos_writereg(portnum, v, 1); break; case 0x2f8: if (xb->amiga_io[0x13f] & 0x80) aio = 0x7f; else aio = 0x7d; break; case 0x2f9: if (xb->amiga_io[0x13f] & 0x80) aio = 0x9f; else aio = 0xbd; break; case 0x2fb: aio = 0x11f; break; case 0x2fc: aio = 0x13f; break; case 0x2fa: case 0x2fd: case 0x2fe: case 0x2ff: aio = 0x1f; break; // vga case 0x3c2: x86_vga_mode = v & 1; case 0x3c0: case 0x3c1: case 0x3c3: case 0x3c4: case 0x3c5: case 0x3c6: case 0x3c7: case 0x3c8: case 0x3c9: case 0x3ca: case 0x3cb: case 0x3cc: case 0x3cd: case 0x3ce: case 0x3cf: case 0x3b9: if (ISVGA()) { vga_io_put(portnum, v); } break; // mono video case 0x3b0: case 0x3b2: case 0x3b4: case 0x3b6: if (mda_emu) { aio = 0x1ff; } else if (ISVGA()) { if (x86_vga_mode == 0) vga_io_put(portnum, v); } break; case 0x3b1: case 0x3b3: case 0x3b5: case 0x3b7: if (mda_emu) { aio = 0x2a1 + (xb->amiga_io[0x1ff] & 15) * 2; } else if (ISVGA()) { if (x86_vga_mode == 0) vga_io_put(portnum, v); } break; case 0x3b8: if (mda_emu) { aio = 0x2ff; } break; // color video case 0x3d0: case 0x3d2: case 0x3d4: case 0x3d6: if (cga_emu) { aio = 0x21f; } else if (ISVGA()) { if (x86_vga_mode == 1) vga_io_put(portnum, v); } break; case 0x3d1: case 0x3d3: case 0x3d5: case 0x3d7: if (cga_emu) { aio = 0x2c1 + (xb->amiga_io[0x21f] & 15) * 2; } else if (ISVGA()) { if (x86_vga_mode == 1) vga_io_put(portnum, v); } break; case 0x3d8: if (cga_emu) { aio = 0x23f; } break; case 0x3d9: if (cga_emu) { aio = 0x25f; } break; case 0x3dd: if (cga_emu) { aio = 0x29f; } break; case 0x3ba: if (cga_emu) { aio = 0x1f; } else if (ISVGA()) { if (x86_vga_mode == 0) vga_io_put(portnum, v); } break; case 0x3da: if (cga_emu) { aio = 0x1f; } else if (ISVGA()) { if (x86_vga_mode == 1) vga_io_put(portnum, v); } break; case 0x378: write_log(_T("BIOS DIAGNOSTICS CODE: %02x ~%02X\n"), v, v ^ 0xff); aio = 0x19f; // ?? break; case 0x379: if (xb->amiga_io[IO_MODE_REGISTER] & 2) { xb->amiga_forced_interrupts = (v & 40) ? false : true; } aio = 0x19f; // ?? break; case 0x37a: aio = 0x1df; break; case 0x3bb: case 0x3bc: case 0x3bd: case 0x3be: case 0x3bf: if (mda_emu) { aio = 0x1f; } break; case 0x3de: case 0x3df: if (cga_emu) { aio = 0x1f; } break; // A2386SX only case 0xe8: case 0xe9: case 0xea: case 0xeb: case 0xec: case 0xed: case 0xee: case 0xef: case 0xf0: case 0xf1: case 0xf4: case 0xf5: case 0xf8: case 0xf9: case 0xfa: case 0xfb: case 0xfc: case 0xfe: if (xb->type >= TYPE_2386) vlsi_out(xb, portnum, v); break; // floppy case 0x3f0: case 0x3f1: case 0x3f2: case 0x3f3: case 0x3f4: case 0x3f5: case 0x3f7: outfloppy(xb, portnum, v); break; // at ide 1 case 0x170: case 0x171: case 0x172: case 0x173: case 0x174: case 0x175: case 0x176: case 0x177: case 0x376: x86_ide_hd_put(portnum, v, 0); break; // at ide 0 case 0x1f0: case 0x1f1: case 0x1f2: case 0x1f3: case 0x1f4: case 0x1f5: case 0x1f6: case 0x1f7: case 0x3f6: x86_ide_hd_put(portnum, v, 0); break; // xt hd case 0x320: case 0x321: case 0x322: case 0x323: x86_xt_hd_bput(portnum, v); break; // universal xt bios case 0x300: case 0x301: case 0x302: case 0x303: case 0x304: case 0x305: case 0x306: case 0x307: case 0x308: case 0x309: case 0x30a: case 0x30b: case 0x30c: case 0x30d: case 0x30e: case 0x30f: x86_ide_hd_put(portnum, v, 0); break; case 0x101: case 0x102: // A2286 BIOS timer test fails if CPU is too fast // so we'll run normal speed until tests are done if (!x86_turbo_allowed) { x86_turbo_allowed = true; } break; default: write_log(_T("X86_OUT unknown %02x %02x\n"), portnum, v); break; } #if X86_IO_PORT_DEBUG write_log(_T("X86_OUT %08x %02X\n"), portnum, v); #endif if (aio >= 0) xb->amiga_io[aio] = v; xb->io_ports[portnum] = v; } void portout16(uint16_t portnum, uint16_t value) { switch (portnum) { case 0x170: case 0x1f0: case 0x300: x86_ide_hd_put(portnum, value, 1); break; default: portout(portnum, value); portout(portnum + 1, value >> 8); break; } } static void portout32(uint16_t portnum, uint32_t value) { switch (portnum) { case 0x170: case 0x1f0: case 0x300: x86_ide_hd_put(portnum, value, 1); x86_ide_hd_put(portnum, value >> 16, 1); break; default: write_log(_T("portout32 %08x %08x\n"), portnum, value); break; } } uint8_t portin(uint16_t portnum) { struct x86_bridge *xb = bridges[0]; int aio = -1; uae_u8 enables = xb->amiga_io[IO_MODE_REGISTER]; bool mda_emu = (enables & 8) != 0; bool cga_emu = (enables & 16) != 0; if (!is_port_enabled(xb, portnum)) return 0; if (portnum >= 0x400) return 0; set_pc_io_access(xb, portnum, false); uae_u8 v = xb->io_ports[portnum]; switch (portnum) { case 0x20: case 0x21: case 0xa0: case 0xa1: if (xb->dosbox_cpu) v = x86_pic_read(portnum); else v = in8259(portnum); break; case 0x40: case 0x41: case 0x42: case 0x43: if (xb->dosbox_cpu) v = x86_timer_read(portnum); else v = in8253(portnum); break; case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87: case 0x88: case 0x89: case 0x8a: case 0x8b: case 0x8c: case 0x8d: case 0x8e: case 0x8f: case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f: case 0xc0: case 0xc1: case 0xc2: case 0xc3: case 0xc4: case 0xc5: case 0xc6: case 0xc7: case 0xc8: case 0xc9: case 0xca: case 0xcb: case 0xcc: case 0xcd: case 0xce: case 0xcf: case 0xd0: case 0xd1: case 0xd2: case 0xd3: case 0xd4: case 0xd5: case 0xd6: case 0xd7: case 0xd8: case 0xd9: case 0xda: case 0xdb: case 0xdc: case 0xdd: case 0xde: case 0xdf: v = in8237(portnum); break; case 0x71: if (xb->type >= TYPE_2286) v = cmos_readreg(portnum, 1); break; case 0x2f8: if (xb->amiga_io[0x11f] & 0x80) aio = 0x7f; else aio = 0x9d; xb->pc_irq3b = false; break; case 0x2f9: if (xb->amiga_io[0x11f] & 0x80) aio = 0x9f; else aio = 0xdd; break; case 0x2fa: aio = 0xff; break; case 0x2fd: aio = 0x15f; break; case 0x2fe: aio = 0x17f; break; case 0x2fb: case 0x2fc: case 0x2ff: aio = 0x1f; break; case 0x378: aio = 0x19f; // ? break; case 0x379: xb->pc_irq7 = false; aio = 0x1bf; break; case 0x37a: aio = 0x19f; // ? break; // vga case 0x3c0: case 0x3c1: case 0x3c2: case 0x3c3: case 0x3c4: case 0x3c5: case 0x3c6: case 0x3c7: case 0x3c8: case 0x3c9: case 0x3ca: case 0x3cb: case 0x3cc: case 0x3cd: case 0x3ce: case 0x3cf: case 0x3b9: if (ISVGA()) { v = vga_io_get(portnum); } break; // mono video case 0x3b0: xb->pc_irq3a = false; aio = 0x1ff; break; case 0x3b2: case 0x3b4: case 0x3b6: if (mda_emu) { aio = 0x1ff; } else if (ISVGA()) { if (x86_vga_mode == 0) v = vga_io_get(portnum); } break; case 0x3b1: case 0x3b3: case 0x3b5: case 0x3b7: if (mda_emu) { aio = 0x2a1 + (xb->amiga_io[0x1ff] & 15) * 2; } else if (ISVGA()) { if (x86_vga_mode == 0) v = vga_io_get(portnum); } break; case 0x3b8: if (mda_emu) { aio = 0x2ff; } break; // color video case 0x3d0: case 0x3d2: case 0x3d4: case 0x3d6: if (cga_emu) { aio = 0x21f; } else if (ISVGA()) { if (x86_vga_mode == 1) v = vga_io_get(portnum); } break; case 0x3d1: case 0x3d3: case 0x3d5: case 0x3d7: if (cga_emu) { aio = 0x2c1 + (xb->amiga_io[0x21f] & 15) * 2; } else if (ISVGA()) { if (x86_vga_mode == 1) v = vga_io_get(portnum); } break; case 0x3d8: if (cga_emu) { aio = 0x23f; } break; case 0x3d9: if (cga_emu) { aio = 0x25f; } break; case 0x3ba: if (mda_emu) { v = 0; // not really correct but easy. if (vpos < 20) v |= 8 | 1; if (get_cycles() - last_cycles > maxhpos / 2) v |= 1; last_cycles = get_cycles(); } else if (ISVGA()) { if (x86_vga_mode == 0) v = vga_io_get(portnum); } break; case 0x3da: if (cga_emu) { v = 0; // not really correct but easy. if (vpos < 20) v |= 8 | 1; if (get_cycles() - last_cycles > maxhpos / 2) v |= 1; last_cycles = get_cycles(); } else if (ISVGA()) { if (x86_vga_mode == 1) v = vga_io_get(portnum); } break; case 0x3dd: if (cga_emu) { aio = 0x29f; } break; case 0x3bb: case 0x3bc: case 0x3bd: case 0x3be: case 0x3bf: if (mda_emu) { aio = 0x1f; } break; case 0x3de: case 0x3df: if (cga_emu) { aio = 0x1f; } break; // A2386SX only case 0xe8: case 0xe9: case 0xea: case 0xeb: case 0xec: case 0xed: case 0xee: case 0xef: case 0xf0: case 0xf1: case 0xf4: case 0xf5: case 0xf8: case 0xf9: case 0xfa: case 0xfb: case 0xfc: case 0xfe: if (xb->type >= TYPE_2386) v = vlsi_in(xb, portnum); break; // floppy case 0x3f0: case 0x3f1: case 0x3f2: case 0x3f3: case 0x3f4: case 0x3f5: case 0x3f7: v = infloppy(xb, portnum); break; case 0x60: if (xb->type >= TYPE_2286) { v = x86_in_keyboard(0x60); //write_log(_T("PC read keycode %02x\n"), v); } break; case 0x61: if (xb->type >= TYPE_2286) { // bios test hack v = x86_in_keyboard(0x61); } else { v = xb->amiga_io[0x5f]; } //write_log(_T("IN Port B %02x\n"), v); break; case 0x62: { v = xb->amiga_io[0x3f]; if (xb->type == TYPE_SIDECAR) { // Sidecar has jumpers. if (xb->amiga_io[0x5f] & 8) { // bit 0-1: display (11=mono 80x25,10=01=color 80x25,00=no video) // bit 2-3: number of drives (11=1..) v &= 0xf0; v |= (xb->pc_jumpers >> 4) & 0x0f; } else { v &= 0xf0; // bit 0: 0=loop on POST // bit 1: 0=8087 installed // bit 2-3: (11=640k,10=256k,01=512k,00=128k) RAM size v |= xb->pc_jumpers & 0xf; } } else { // A2088+ are software configurable (Except default video mode) if (!(xb->amiga_io[0x5f] & 4)) { v &= 0xf0; v |= (xb->pc_jumpers >> 4) & 0x0f; } else { v &= 0xf0; v |= xb->pc_jumpers & 0xf; } } v &= ~0x20; if (!(xb->amiga_io[0x5f] & 1) && i8253.active[2]) v |= 0x20; //write_log(_T("IN Port C %02x\n"), v); } break; case 0x63: //write_log(_T("IN Control %02x\n"), v); break; case 0x64: if (xb->type >= TYPE_2286) { v = x86_in_keyboard(0x64); } break; // at ide 1 case 0x170: case 0x171: case 0x172: case 0x173: case 0x174: case 0x175: case 0x176: case 0x177: case 0x376: v = x86_ide_hd_get(portnum, 0); break; // at ide 0 case 0x1f0: case 0x1f1: case 0x1f2: case 0x1f3: case 0x1f4: case 0x1f5: case 0x1f6: case 0x1f7: case 0x3f6: v = x86_ide_hd_get(portnum, 0); break; // xt hd case 0x320: case 0x321: case 0x322: case 0x323: v = x86_xt_hd_bget(portnum); break; // universal xt bios case 0x300: case 0x301: case 0x302: case 0x303: case 0x304: case 0x305: case 0x306: case 0x307: case 0x308: case 0x309: case 0x30a: case 0x30b: case 0x30c: case 0x30d: case 0x30e: case 0x30f: v = x86_ide_hd_get(portnum, 0); break; default: write_log(_T("X86_IN unknown %02x\n"), portnum); return 0; } if (aio >= 0) v = xb->amiga_io[aio]; #if X86_IO_PORT_DEBUG write_log(_T("X86_IN %08x %02X\n"), portnum, v); #endif return v; } uint16_t portin16(uint16_t portnum) { uae_u16 v = 0; switch (portnum) { case 0x170: case 0x1f0: case 0x300: v = x86_ide_hd_get(portnum, 1); break; default: write_log(_T("portin16 %08x\n"), portnum); break; } return v; } static uint32_t portin32(uint16_t portnum) { uint32_t v = 0; switch (portnum) { case 0x170: case 0x1f0: case 0x300: v = x86_ide_hd_get(portnum, 1) << 0; v |= x86_ide_hd_get(portnum, 1) << 16; break; default: write_log(_T("portin32 %08x\n"), portnum); break; } return v; } void write86(uint32_t addr32, uint8_t value) { struct x86_bridge *xb = bridges[0]; addr32 &= 0xFFFFF; if (addr32 >= xb->pc_maxbaseram && addr32 < 0xa0000) return; if (addr32 >= x86_xrom_start[0] && addr32 < x86_xrom_end[0]) return; if (addr32 >= x86_xrom_start[1] && addr32 < x86_xrom_end[1]) return; set_pc_address_access(xb, addr32); xb->pc_ram[addr32] = value; } void writew86(uint32_t addr32, uint16_t value) { struct x86_bridge *xb = bridges[0]; addr32 &= 0xFFFFF; if (addr32 >= xb->pc_maxbaseram && addr32 < 0xa0000) return; if (addr32 >= x86_xrom_start[0] && addr32 < x86_xrom_end[0]) return; if (addr32 >= x86_xrom_start[1] && addr32 < x86_xrom_end[1]) return; set_pc_address_access(xb, addr32); xb->pc_ram[addr32] = value & 0xff; xb->pc_ram[addr32 + 1] = value >> 8; } uint8_t read86(uint32_t addr32) { struct x86_bridge *xb = bridges[0]; addr32 &= 0xFFFFF; return xb->pc_ram[addr32]; } uint16_t readw86(uint32_t addr32) { uint16_t v; struct x86_bridge *xb = bridges[0]; addr32 &= 0xFFFFF; v = xb->pc_ram[addr32]; v |= xb->pc_ram[addr32 + 1] << 8; return v; } static uaecptr get_x86_address(struct x86_bridge *xb, uaecptr addr, int *mode) { addr -= xb->baseaddress; if (!xb->baseaddress || addr >= 0x80000) { *mode = -1; return 0; } *mode = addr >> 17; addr &= 0x1ffff; if (addr < 0x10000) { int opt = (xb->amiga_io[IO_MODE_REGISTER] >> 5) & 3; // disk buffer switch(opt) { case 1: default: return 0xa0000 + addr; case 2: return 0xd0000 + addr; case 3: return 0xe0000 + addr; } } if (addr < 0x18000) { // color video addr -= 0x10000; return 0xb8000 + addr; } if (addr < 0x1c000) { addr -= 0x18000; // parameter if (xb->type >= TYPE_2286) return 0xd0000 + addr; else return 0xf0000 + addr; } if (addr < 0x1e000) { addr -= 0x1c000; // mono video return 0xb0000 + addr; } // IO addr -= 0x1e000; return addr; } static struct x86_bridge *get_x86_bridge(uaecptr addr) { return bridges[0]; } static uae_u32 REGPARAM2 x86_bridge_wget(uaecptr addr) { #ifdef JIT special_mem |= S_READ; #endif uae_u16 v = 0; struct x86_bridge *xb = get_x86_bridge(addr); if (!xb) return v; int mode; uaecptr a = get_x86_address(xb, addr, &mode); if (mode == ACCESS_MODE_WORD) { v = (xb->pc_ram[a + 1] << 8) | (xb->pc_ram[a + 0] << 0); } else if (mode == ACCESS_MODE_GFX) { write_log(_T("ACCESS_MODE_GFX\n")); } else if (mode == ACCESS_MODE_IO) { v = x86_bridge_get_io(xb, a); v |= x86_bridge_get_io(xb, a + 1) << 8; } else if (mode >= 0) { v = (xb->pc_ram[a + 1] << 0) | (xb->pc_ram[a + 0] << 8); } #if X86_DEBUG_BRIDGE > 1 write_log(_T("x86_bridge_wget %08x (%08x,%d) PC=%08x\n"), addr - xb->baseaddress, a, mode, M68K_GETPC); #endif return v; } static uae_u32 REGPARAM2 x86_bridge_lget(uaecptr addr) { uae_u32 v; v = x86_bridge_wget(addr) << 16; v |= x86_bridge_wget(addr + 2); return v; } static uae_u32 REGPARAM2 x86_bridge_bget(uaecptr addr) { #ifdef JIT special_mem |= S_READ; #endif uae_u8 v = 0; struct x86_bridge *xb = get_x86_bridge(addr); if (!xb) return v; if (!xb->configured) { uaecptr offset = addr & 65535; if (offset >= sizeof xb->acmemory) return 0; return xb->acmemory[offset]; } int mode; uaecptr a = get_x86_address(xb, addr, &mode); if (mode == ACCESS_MODE_WORD) { v = xb->pc_ram[a ^ 1]; } else if (mode == ACCESS_MODE_IO) { v = x86_bridge_get_io(xb, a); } else if (mode >= 0) { v = xb->pc_ram[a]; } #if X86_DEBUG_BRIDGE > 1 write_log(_T("x86_bridge_bget %08x (%08x,%d) %02x PC=%08x\n"), addr - xb->baseaddress, a, mode, v, M68K_GETPC); #endif return v; } static void REGPARAM2 x86_bridge_wput(uaecptr addr, uae_u32 b) { #ifdef JIT special_mem |= S_WRITE; #endif struct x86_bridge *xb = get_x86_bridge(addr); if (!xb) return; int mode; uaecptr a = get_x86_address(xb, addr, &mode); #if X86_DEBUG_BRIDGE > 1 write_log(_T("pci_bridge_wput %08x (%08x,%d) %04x PC=%08x\n"), addr - xb->baseaddress, a, mode, b & 0xffff, M68K_GETPC); #endif if (a >= 0x100000 || mode < 0) return; if (xb->rombank[a / 4096]) return; if (mode == ACCESS_MODE_IO) { uae_u16 v = b; x86_bridge_put_io(xb, a, v & 0xff); x86_bridge_put_io(xb, a + 1, v >> 8); } else if (mode == ACCESS_MODE_WORD) { xb->pc_ram[a + 0] = b; xb->pc_ram[a + 1] = b >> 8; } else if (mode == ACCESS_MODE_GFX) { write_log(_T("ACCESS_MODE_GFX\n")); } else { xb->pc_ram[a + 1] = b; xb->pc_ram[a + 0] = b >> 8; } } static void REGPARAM2 x86_bridge_lput(uaecptr addr, uae_u32 b) { x86_bridge_wput(addr, b >> 16); x86_bridge_wput(addr + 2, b); } static void REGPARAM2 x86_bridge_bput(uaecptr addr, uae_u32 b) { #ifdef JIT special_mem |= S_WRITE; #endif struct x86_bridge *xb = get_x86_bridge(addr); if (!xb) return; if (!xb->configured) { uaecptr offset = addr & 65535; switch (offset) { case 0x48: map_banks_z2(xb->bank, b, expamem_z2_size >> 16); xb->baseaddress = b << 16; xb->configured = 1; expamem_next(xb->bank, NULL); break; case 0x4c: xb->configured = -1; expamem_shutup(xb->bank); break; } } int mode; uaecptr a = get_x86_address(xb, addr, &mode); if (a >= 0x100000 || mode < 0) return; if (xb->rombank[a / 4096]) return; #if X86_DEBUG_BRIDGE > 1 write_log(_T("x86_bridge_bput %08x (%08x,%d) %02x PC=%08x\n"), addr - xb->baseaddress, a, mode, b & 0xff, M68K_GETPC); #endif if (mode == ACCESS_MODE_IO) { x86_bridge_put_io(xb, a, b); } else if (mode == ACCESS_MODE_WORD) { xb->pc_ram[a ^ 1] = b; } else { xb->pc_ram[a] = b; } } addrbank x86_bridge_bank = { x86_bridge_lget, x86_bridge_wget, x86_bridge_bget, x86_bridge_lput, x86_bridge_wput, x86_bridge_bput, default_xlate, default_check, NULL, NULL, _T("X86 BRIDGE"), x86_bridge_lget, x86_bridge_wget, ABFLAG_IO | ABFLAG_SAFE }; void x86_bridge_rethink(void) { struct x86_bridge *xb = bridges[0]; if (!xb) return; if (!(xb->amiga_io[IO_CONTROL_REGISTER] & 1)) { uae_u8 intreq = xb->amiga_io[IO_AMIGA_INTERRUPT_STATUS]; uae_u8 intena = xb->amiga_io[IO_INTERRUPT_MASK]; uae_u8 status = intreq & ~intena; if (status) INTREQ_0(0x8000 | 0x0008); } } void x86_bridge_free(void) { x86_bridge_reset(); x86_found = 0; } void x86_bridge_reset(void) { x86_xrom_start[0] = x86_xrom_end[0] = 0; x86_xrom_start[1] = x86_xrom_end[1] = 0; for (int i = 0; i < X86_BRIDGE_MAX; i++) { struct x86_bridge *xb = bridges[i]; if (!xb) continue; if (xb->dosbox_cpu) { if (xb->cmosfile) { uae_u8 *regs = x86_cmos_regs(NULL); zfile_fseek(xb->cmosfile, 0, SEEK_SET); zfile_fwrite(regs, 1, xb->cmossize, xb->cmosfile); } CPU_ShutDown(dosbox_sec); CMOS_Destroy(dosbox_sec); TIMER_Destroy(dosbox_sec); PIC_Destroy(dosbox_sec); MEM_ShutDown(dosbox_sec); delete dosbox_sec; } xfree(xb->amiga_io); xfree(xb->io_ports); xfree(xb->pc_ram); xfree(xb->pc_rom); zfile_fclose(xb->cmosfile); bridges[i] = NULL; } } static void check_floppy_delay(void) { for (int i = 0; i < 4; i++) { if (floppy_seeking[i]) { bool neg = floppy_seeking[i] < 0; if (floppy_seeking[i] > 0) floppy_seeking[i]--; else if (neg) floppy_seeking[i]++; if (floppy_seeking[i] == 0) do_floppy_seek(i, neg); } } if (floppy_delay_hsync > 1 || floppy_delay_hsync < -1) { if (floppy_delay_hsync > 0) floppy_delay_hsync--; else floppy_delay_hsync++; if (floppy_delay_hsync == 1 || floppy_delay_hsync == -1) do_floppy_irq(); } } static void x86_cpu_execute(int cnt) { struct x86_bridge *xb = bridges[0]; if (!xb->x86_reset) { if (xb->dosbox_cpu) { if (xb->x86_reset_requested) { xb->x86_reset_requested = false; reset_x86_cpu(xb); } Bit32s old_cpu_cycles = CPU_Cycles; if (CPU_Cycles > cnt) CPU_Cycles = cnt; if (PIC_RunQueue()) { (*cpudecoder)(); } CPU_Cycles = old_cpu_cycles -= cnt; if (CPU_Cycles < 0) CPU_Cycles = 0; check_x86_irq(); } else { exec86(cnt); } } // BIOS has CPU loop delays in floppy driver... check_floppy_delay(); } void x86_bridge_execute_until(int until) { struct x86_bridge *xb = bridges[0]; if (!xb) return; if (!x86_turbo_allowed) return; for (;;) { x86_cpu_execute(until ? 10 : 1); if (until == 0) break; frame_time_t rpt = read_processor_time(); if ((int)until - (int)rpt <= 0) break; } } void x86_bridge_sync_change(void) { struct x86_bridge *xb = bridges[0]; if (!xb) return; xb->dosbox_vpos_tick = maxvpos * vblank_hz / 1000; if (xb->dosbox_vpos_tick >= xb->dosbox_vpos_tick) xb->dosbox_tick_vpos_cnt -= xb->dosbox_vpos_tick; } void x86_bridge_hsync(void) { struct x86_bridge *xb = bridges[0]; if (!xb) return; check_floppy_delay(); if (!xb->x86_reset) { if (xb->dosbox_cpu) { xb->dosbox_tick_vpos_cnt++; if (xb->dosbox_tick_vpos_cnt >= xb->dosbox_vpos_tick) { TIMER_AddTick(); xb->dosbox_tick_vpos_cnt -= xb->dosbox_vpos_tick; } x86_cpu_execute(x86_instruction_count); } else { for (int i = 0; i < 3; i++) { x86_cpu_execute(x86_instruction_count / 3); timing(maxhpos / 3); } } } if (currprefs.x86_speed_throttle != changed_prefs.x86_speed_throttle) { currprefs.x86_speed_throttle = changed_prefs.x86_speed_throttle; x86_instruction_count = DEFAULT_X86_INSTRUCTION_COUNT; if (currprefs.x86_speed_throttle < 0) { x86_turbo_enabled = true; } else { x86_turbo_enabled = false; x86_instruction_count = DEFAULT_X86_INSTRUCTION_COUNT + DEFAULT_X86_INSTRUCTION_COUNT * currprefs.x86_speed_throttle / 1000; } } if (x86_turbo_allowed && x86_turbo_enabled && !x86_turbo_on) { x86_turbo_on = true; } else if ((!x86_turbo_allowed || !x86_turbo_enabled) && x86_turbo_on) { x86_turbo_on = false; } } static void ew(uae_u8 *acmemory, int addr, uae_u8 value) { if (addr == 00 || addr == 02 || addr == 0x40 || addr == 0x42) { acmemory[addr] = (value & 0xf0); acmemory[addr + 2] = (value & 0x0f) << 4; } else { acmemory[addr] = ~(value & 0xf0); acmemory[addr + 2] = ~((value & 0x0f) << 4); } } static void setrombank(struct x86_bridge *xb, int start, int len) { while (len > 0) { xb->rombank[start / 4096] = 1; start += 4096; len -= 4096; } } static void bridge_reset(struct x86_bridge *xb) { xb->x86_reset = true; xb->configured = 0; xb->amiga_forced_interrupts = false; xb->amiga_irq = false; xb->pc_irq3a = xb->pc_irq3b = xb->pc_irq7 = false; x86_turbo_allowed = false; x86_cpu_active = false; x86_instruction_count = DEFAULT_X86_INSTRUCTION_COUNT; memset(xb->amiga_io, 0, 0x10000); memset(xb->io_ports, 0, 0x10000); memset(xb->pc_ram, 0, 0x100000 - xb->bios_size); memset(xb->rombank, 0, sizeof xb->rombank); for (int i = 0; i < 2; i++) { if (x86_xrom_end[i] - x86_xrom_start[i] > 0) { memcpy(xb->pc_ram + x86_xrom_start[i], xb->pc_rom + x86_xrom_start[i], x86_xrom_end[i] - x86_xrom_start[i]); setrombank(xb, x86_xrom_start[i], x86_xrom_end[i] - x86_xrom_start[i]); } } memcpy(xb->pc_ram + 0x100000 - xb->bios_size, xb->pc_rom + 0x100000 - xb->bios_size, xb->bios_size); setrombank(xb, 0x100000 - xb->bios_size, xb->bios_size); xb->amiga_io[IO_CONTROL_REGISTER] = 0xfe; xb->amiga_io[IO_PC_INTERRUPT_CONTROL] = 0xff; xb->amiga_io[IO_INTERRUPT_MASK] = 0xff; xb->amiga_io[IO_MODE_REGISTER] = 0x00; xb->amiga_io[IO_PC_INTERRUPT_STATUS] = 0xfe; x86_cmos_bank = 0; memset(xb->vlsi_regs, 0, sizeof xb->vlsi_regs); if (xb->type >= TYPE_2286) { int sel1 = (xb->settings >> 10) & 1; int sel2 = (xb->settings >> 11) & 1; // only A0000 or D0000 is valid for AT if ((!sel1 && !sel2) || (sel1 && sel2)) { sel1 = 0; sel2 = 1; } xb->amiga_io[IO_MODE_REGISTER] |= sel1 << 5; xb->amiga_io[IO_MODE_REGISTER] |= sel2 << 6; } if (xb->type == TYPE_2386) x86_turbo_allowed = true; x86_bridge_sync_change(); inittiming(); floppy_hardreset(); } int is_x86_cpu(struct uae_prefs *p) { struct x86_bridge *xb = bridges[0]; if (!xb) { if (x86_found > 0) return X86_STATE_STOP; else if (x86_found < 0) return X86_STATE_INACTIVE; if (is_device_rom(p, ROMTYPE_A1060, 0) < 0 && is_device_rom(p, ROMTYPE_A2088, 0) < 0 && is_device_rom(p, ROMTYPE_A2088T, 0) < 0 && is_device_rom(p, ROMTYPE_A2286, 0) < 0 && is_device_rom(p, ROMTYPE_A2386, 0) < 0) { if (p == &currprefs) x86_found = -1; return X86_STATE_INACTIVE; } else { if (p == &currprefs) x86_found = 1; } } if (!xb || xb->x86_reset) return X86_STATE_STOP; return X86_STATE_ACTIVE; } static void load_vga_bios(void) { struct x86_bridge *xb = bridges[0]; if (!xb || !ISVGA()) return; struct zfile *zf = read_device_rom(&currprefs, ROMTYPE_x86_VGA, 0, NULL); x86_xrom_start[1] = 0xc0000; x86_xrom_end[1] = x86_xrom_start[1]; if (zf) { x86_xrom_end[1] += zfile_fread(xb->pc_rom + x86_xrom_start[1], 1, 65536, zf); zfile_fclose(zf); x86_xrom_end[1] += 4095; x86_xrom_end[1] &= ~4095; memcpy(xb->pc_ram + x86_xrom_start[1], xb->pc_rom + x86_xrom_start[1], x86_xrom_end[1] - x86_xrom_start[1]); setrombank(xb, x86_xrom_start[1], x86_xrom_end[1] - x86_xrom_start[1]); } if (xb->dosbox_cpu) { MEM_ShutDown(dosbox_sec); MEM_Init(dosbox_sec); MEM_SetVGAHandler(); } } void x86_xt_ide_bios(struct zfile *z, struct romconfig *rc) { struct x86_bridge *xb = bridges[0]; uae_u32 addr = 0; if (!xb || !z) return; switch (rc->device_settings) { case 0: addr = 0xcc000; break; case 1: addr = 0xdc000; break; case 2: default: addr = 0xec000; break; } x86_xrom_start[0] = addr; x86_xrom_end[0] = x86_xrom_start[0] + 0x4000; zfile_fread(xb->pc_rom + x86_xrom_start[0], 1, 32768, z); memcpy(xb->pc_ram + x86_xrom_start[0], xb->pc_rom + x86_xrom_start[0], x86_xrom_end[0] - x86_xrom_start[0]); setrombank(xb, x86_xrom_start[0], x86_xrom_end[0] - x86_xrom_start[0]); if (xb->dosbox_cpu) { MEM_ShutDown(dosbox_sec); MEM_Init(dosbox_sec); if (ISVGA()) { MEM_SetVGAHandler(); } } } static const uae_u8 a1060_autoconfig[16] = { 0xc4, 0x01, 0x80, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const uae_u8 a2386_autoconfig[16] = { 0xc4, 0x67, 0x80, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; addrbank *x86_bridge_init(struct romconfig *rc, uae_u32 romtype, int type) { struct x86_bridge *xb = x86_bridge_alloc(); const uae_u8 *ac; if (!xb) return &expamem_null; bridges[0] = xb; xb->rc = rc; xb->type = type; xb->io_ports = xcalloc(uae_u8, 0x10000); xb->amiga_io = xcalloc(uae_u8, 0x10000); x86_xrom_start[0] = x86_xrom_end[0] = 0; x86_xrom_start[1] = x86_xrom_end[1] = 0; xb->settings = rc->device_settings; if (xb->type >= TYPE_2286) { xb->dosbox_cpu = ((xb->settings >> 19) & 3) + 1; xb->dosbox_cpu_arch = (xb->settings >> 23) & 7; xb->settings |= 0xff; ac = xb->type >= TYPE_2386 ? a2386_autoconfig : a1060_autoconfig; xb->pc_maxram = (1024 * 1024) << ((xb->settings >> 16) & 7); xb->bios_size = 65536; } else { xb->dosbox_cpu = (xb->settings >> 19) & 7; xb->dosbox_cpu_arch = (xb->settings >> 23) & 7; ac = a1060_autoconfig; xb->pc_maxram = 1 * 1024 * 1024; xb->bios_size = 32768; } xb->pc_ram = xcalloc(uae_u8, xb->pc_maxram + 1 * 1024 * 1024); xb->pc_rom = xcalloc(uae_u8, 0x100000); x86_memsize = xb->pc_maxram; mono_start = xb->pc_ram + 0xb0000; mono_end = xb->pc_ram + 0xb2000; color_start = xb->pc_ram + 0xb8000; color_end = xb->pc_ram + 0xc0000; x86_biosstart = 0x100000 - xb->bios_size; MemBase = xb->pc_ram; if (xb->dosbox_cpu) { ticksDone = 0; ticksScheduled = 0; x86_fpu_enabled = (xb->settings >> 22) & 1; dosbox_sec = new Section_prop("dummy"); MEM_Init(dosbox_sec); PAGING_Init(dosbox_sec); CMOS_Init(dosbox_sec, xb->type == TYPE_2386 ? 0x7f : 0x3f); PIC_Init(dosbox_sec); TIMER_Init(dosbox_sec); FPU_Init(dosbox_sec); if (xb->type >= TYPE_2286) { xb->cmossize = xb->type == TYPE_2386 ? 192 : 64; xb->cmosfile = zfile_fopen(currprefs.flashfile, _T("rb+"), ZFD_NORMAL); if (!xb->cmosfile) { xb->cmosfile = zfile_fopen(currprefs.flashfile, _T("wb")); } memset(xb->cmosregs, 0, sizeof xb->cmosregs); if (xb->cmosfile) { if (zfile_fread(xb->cmosregs, 1, xb->cmossize, xb->cmosfile) == xb->cmossize) { x86_cmos_regs(xb->cmosregs); } } } } if (ISVGA()) { if (xb->dosbox_cpu) { MEM_SetVGAHandler(); } load_vga_bios(); } xb->pc_jumpers = (xb->settings & 0xff) ^ ((0x80 | 0x40) | (0x20 | 0x10 | 0x01 | 0x02)); int ramsize = (xb->settings >> 2) & 3; switch(ramsize) { case 0: xb->pc_maxbaseram = 128 * 1024; break; case 1: xb->pc_maxbaseram = 256 * 1024; break; case 2: xb->pc_maxbaseram = 512 * 1024; break; case 3: xb->pc_maxbaseram = 640 * 1024; break; } bridge_reset(xb); // load bios if (!load_rom_rc(rc, romtype, xb->bios_size, 0, xb->pc_rom + 0x100000 - xb->bios_size, xb->bios_size, LOADROM_FILL)) { error_log(_T("Bridgeboard BIOS failed to load")); x86_bridge_free(); return &expamem_null; } memcpy(xb->pc_ram + 0x100000 - xb->bios_size, xb->pc_rom + 0x100000 - xb->bios_size, xb->bios_size); setrombank(xb, 0x100000 - xb->bios_size, xb->bios_size); xb->bank = &x86_bridge_bank; for (int i = 0; i < 16; i++) { ew(xb->acmemory, i * 4, ac[i]); } return xb->bank; } addrbank *a1060_init(struct romconfig *rc) { return x86_bridge_init(rc, ROMTYPE_A1060, TYPE_SIDECAR); } addrbank *a2088xt_init(struct romconfig *rc) { return x86_bridge_init(rc, ROMTYPE_A2088, TYPE_2088); } addrbank *a2088t_init(struct romconfig *rc) { return x86_bridge_init(rc, ROMTYPE_A2088T, TYPE_2088T); } addrbank *a2286_init(struct romconfig *rc) { return x86_bridge_init(rc, ROMTYPE_A2286, TYPE_2286); } addrbank *a2386_init(struct romconfig *rc) { return x86_bridge_init(rc, ROMTYPE_A2386, TYPE_2386); } /* dosbox cpu core support stuff */ void IO_WriteB(Bitu port, Bitu val) { portout(port, val); } void IO_WriteW(Bitu port, Bitu val) { portout16(port, val); } void IO_WriteD(Bitu port, Bitu val) { portout32(port, val); } Bitu IO_ReadB(Bitu port) { return portin(port); } Bitu IO_ReadW(Bitu port) { return portin16(port); } Bitu IO_ReadD(Bitu port) { return portin32(port); } Bits CPU_Core_Prefetch_Run(void) { return 0; } bool CommandLine::FindCommand(unsigned int which, std::string & value) { return false; } unsigned int CommandLine::GetCount(void) { return 0; } CommandLine::CommandLine(int argc, char const * const argv[]) { } CommandLine::CommandLine(char const * const name, char const * const cmdline) { } void Section::AddDestroyFunction(SectionFunction func, bool canchange) { } int Section_prop::Get_int(string const&_propname) const { return 0; } const char* Section_prop::Get_string(string const& _propname) const { return NULL; } Prop_multival* Section_prop::Get_multival(string const& _propname) const { return NULL; } void Section_prop::HandleInputline(string const& gegevens) { } void Section_prop::PrintData(FILE* outfile) const { } Section_prop::~Section_prop() { } string Section_prop::GetPropValue(string const& _property) const { return NO_SUCH_PROPERTY; } void DOSBOX_RunMachine(void) { } void GFX_SetTitle(Bit32s cycles, Bits frameskip, bool paused) { } void E_Exit(char *format, ...) { va_list parms; va_start(parms, format); char buffer[1000]; vsnprintf(buffer, sizeof(buffer), format, parms); write_log("DOSBOX E_Exit: %s\n", buffer); va_end(parms); } void GFX_ShowMsg(const char *format, ...) { va_list parms; va_start(parms, format); char buffer[1000]; vsnprintf(buffer, sizeof(buffer), format, parms); write_log("DOSBOX GFX_ShowMsg: %s\n", buffer); va_end(parms); }