/** * Write byte to 0xff8803. Set content of YM's data register under conditions. * Address 0xff8803 is a shadow version of 0xff8802, so both addresses can't be written * at the same time by the same instruction. This means only a .B access or * a movep will have a valid effect, other accesses are ignored. */ void PSG_ff8803_WriteByte(void) { if ( nIoMemAccessSize == SIZE_BYTE ) /* byte access or movep */ { M68000_WaitState(1); /* [NP] FIXME not 100% accurate, but gives good results */ if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE)) { int FrameCycles, HblCounterVideo, LineCycles; Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles ); LOG_TRACE_PRINT("ym write %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress], FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles); } PSG_Set_DataRegister ( IoMem[IoAccessCurrentAddress] ); } else { /* do nothing, just a trace if needed */ if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE)) { int FrameCycles, HblCounterVideo, LineCycles; Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles ); LOG_TRACE_PRINT("ym write ignored %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress], FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles); } } }
/** * This is called after completion of each VDI call */ unsigned long OpCode_VDI(uae_u32 opcode) { Uint32 pc = M68000_GetPC(); /* this is valid only after VDI trap, called from cartridge code */ if (VDI_OldPC && pc >= 0xfa0000 && pc < 0xfc0000) { VDI_Complete(); /* Set PC back to where originated from to continue instruction decoding */ m68k_setpc(VDI_OldPC); VDI_OldPC = 0; } else { /* illegal instruction */ op_illg(opcode); } get_word_prefetch (0); regs.ir = regs.irc; get_word_prefetch(2); return 4 * CYCLE_UNIT / 2; }
/** * Handle word read access from IO memory. */ uae_u32 IoMem_wget(uaecptr addr) { Uint32 idx; Uint16 val; /* Check if access is made by a new instruction or by the same instruction doing multiple word accesses */ if ( IoAccessInstrPrevClock == CyclesGlobalClockCounter ) IoAccessInstrCount++; /* Same instruction, increase access count */ else { IoAccessInstrPrevClock = CyclesGlobalClockCounter; if ( ( table68k[ M68000_CurrentOpcode ].size == 1 ) && ( OpcodeFamily != i_MVMEL ) && ( OpcodeFamily != i_MVMLE ) ) IoAccessInstrCount = 0; /* Instruction size is word and not a movem : no multiple accesses */ else IoAccessInstrCount = 1; /* 1st access of a long or movem.w */ } addr &= 0x00ffffff; /* Use a 24 bit address */ if (addr < 0xff8000 || !regs.s) { /* invalid memory addressing --> bus error */ M68000_BusError(addr, BUS_ERROR_READ, BUS_ERROR_SIZE_WORD, BUS_ERROR_ACCESS_DATA); return -1; } if (addr > 0xfffffe) { fprintf(stderr, "Illegal IO memory access: IoMem_wget($%x)\n", addr); return -1; } IoAccessBaseAddress = addr; /* Store for exception frame */ nIoMemAccessSize = SIZE_WORD; nBusErrorAccesses = 0; idx = addr - 0xff8000; IoAccessCurrentAddress = addr; pInterceptReadTable[idx](); /* Call 1st handler */ if (pInterceptReadTable[idx+1] != pInterceptReadTable[idx]) { IoAccessCurrentAddress = addr + 1; pInterceptReadTable[idx+1](); /* Call 2nd handler */ } /* Check if we completely read from a bus-error region */ if (nBusErrorAccesses == 2) { M68000_BusError(addr, BUS_ERROR_READ, BUS_ERROR_SIZE_WORD, BUS_ERROR_ACCESS_DATA); return -1; } val = IoMem_ReadWord(addr); LOG_TRACE(TRACE_IOMEM_RD, "IO read.w $%06x = $%04x pc=%x\n", addr, val, M68000_GetPC()); return val; }
/** * Default information on entering the debugger */ static void DebugInfo_Default(Uint32 dummy) { int hbl, fcycles, lcycles; Video_GetPosition(&fcycles, &hbl, &lcycles); fprintf(stderr, "\nCPU=$%x, VBL=%d, FrameCycles=%d, HBL=%d, LineCycles=%d, DSP=", M68000_GetPC(), 0, fcycles, hbl, lcycles); fprintf(stderr, "N/A\n"); }
/** * Dissassemble - arg = starting address, or PC. */ int DebugCpu_DisAsm(int nArgc, char *psArgs[]) { Uint32 disasm_upper = 0; int insts, max_insts; uaecptr nextpc; FILE* mydebugOutput=debugOutput; if (nArgc > 1) { switch (Eval_Range(psArgs[1], &disasm_addr, &disasm_upper, false)) { case -1: /* invalid value(s) */ return DEBUGGER_CMDDONE; case 0: /* single value */ break; case 1: /* range */ break; } if (nArgc > 2) { mydebugOutput=fopen(psArgs[2],"w"); if (mydebugOutput==NULL) { fprintf(debugOutput,"Cannot open %s abort\n",psArgs[2]); return DEBUGGER_CMDDONE; } } } else { /* continue */ if(!disasm_addr) disasm_addr = M68000_GetPC(); } /* limit is topmost address or instruction count */ if (disasm_upper) { max_insts = INT_MAX; } else { // max_insts = ConfigureParams.Debugger.nDisasmLines; max_insts = 5; disasm_upper = 0xFFFFFFFF; } /* output a range */ for (insts = 0; insts < max_insts && disasm_addr < disasm_upper; insts++) { DebugCpu_ShowAddressInfo(disasm_addr); Disasm(debugOutput, (uaecptr)disasm_addr, &nextpc, 1, DISASM_ENGINE_EXT); disasm_addr = nextpc; } fflush(mydebugOutput); if (mydebugOutput!=debugOutput) {fclose(mydebugOutput);} return DEBUGGER_CMDCONT; }
/** * Handle long-word write access to IO memory. */ void IoMem_lput(uaecptr addr, uae_u32 val) { Uint32 idx; int n; /* Check if access is made by a new instruction or by the same instruction doing multiple long accesses */ if ( IoAccessInstrPrevClock == CyclesGlobalClockCounter ) IoAccessInstrCount++; /* Same instruction, increase access count */ else { IoAccessInstrPrevClock = CyclesGlobalClockCounter; if ( ( OpcodeFamily != i_MVMEL ) && ( OpcodeFamily != i_MVMLE ) ) IoAccessInstrCount = 0; /* Instruction is not a movem : no multiple accesses */ else IoAccessInstrCount = 1; /* 1st access of a movem.l */ } addr &= 0x00ffffff; /* Use a 24 bit address */ LOG_TRACE(TRACE_IOMEM_WR, "IO write.l $%06x = $%08x pc=%x\n", addr, val, M68000_GetPC()); if (addr < 0xff8000 || !regs.s) { /* invalid memory addressing --> bus error */ M68000_BusError(addr, BUS_ERROR_WRITE, BUS_ERROR_SIZE_LONG, BUS_ERROR_ACCESS_DATA); return; } if (addr > 0xfffffc) { fprintf(stderr, "Illegal IO memory access: IoMem_lput($%x)\n", addr); return; } IoAccessBaseAddress = addr; /* Store for exception frame, just in case */ nIoMemAccessSize = SIZE_LONG; nBusErrorAccesses = 0; IoMem_WriteLong(addr, val); idx = addr - 0xff8000; IoAccessCurrentAddress = addr; pInterceptWriteTable[idx](); /* Call first handler */ for (n = 1; n < nIoMemAccessSize; n++) { if (pInterceptWriteTable[idx+n] != pInterceptWriteTable[idx+n-1]) { IoAccessCurrentAddress = addr + n; pInterceptWriteTable[idx+n](); /* Call n-th handler */ } } /* Check if we wrote to a bus-error region */ if (nBusErrorAccesses == 4) { M68000_BusError(addr, BUS_ERROR_WRITE, BUS_ERROR_SIZE_LONG, BUS_ERROR_ACCESS_DATA); } }
/** * Initialize CPU profiling when necessary. Return true if profiling. */ bool Profile_CpuStart(void) { int size; Profile_FreeCallinfo(&(cpu_callinfo)); if (cpu_profile.sort_arr) { /* remove previous results */ free(cpu_profile.sort_arr); free(cpu_profile.data); cpu_profile.sort_arr = NULL; cpu_profile.data = NULL; printf("Freed previous CPU profile buffers.\n"); } if (!cpu_profile.enabled) { return false; } /* zero everything */ memset(&cpu_profile, 0, sizeof(cpu_profile)); /* Shouldn't change within same debug session */ size = (STRamEnd + 0x20000 + TosSize) / 2; /* Add one entry for catching invalid PC values */ cpu_profile.data = calloc(size + 1, sizeof(*cpu_profile.data)); if (!cpu_profile.data) { perror("ERROR, new CPU profile buffer alloc failed"); return false; } printf("Allocated CPU profile buffer (%d MB).\n", (int)sizeof(*cpu_profile.data)*size/(1024*1024)); cpu_profile.size = size; Profile_AllocCallinfo(&(cpu_callinfo), Symbols_CpuCount(), "CPU"); /* special hack for EmuTOS */ etos_switcher = PC_UNDEFINED; if (cpu_callinfo.sites && bIsEmuTOS && (!Symbols_GetCpuAddress(SYMTYPE_TEXT, "_switchto", &etos_switcher) || etos_switcher < TosAddress)) { etos_switcher = PC_UNDEFINED; } cpu_profile.prev_cycles = Cycles_GetCounter(CYCLES_COUNTER_CPU); cpu_profile.prev_family = OpcodeFamily; cpu_profile.prev_pc = M68000_GetPC() & 0xffffff; cpu_profile.loop_start = PC_UNDEFINED; cpu_profile.loop_end = PC_UNDEFINED; cpu_profile.loop_count = 0; Profile_LoopReset(); cpu_profile.disasm_addr = 0; cpu_profile.processed = false; cpu_profile.enabled = true; return cpu_profile.enabled; }
/** * Dissassemble - arg = starting address, or PC. */ int DebugCpu_DisAsm(int nArgc, char *psArgs[]) { Uint32 disasm_upper = 0; int insts, max_insts; uaecptr nextpc; if (nArgc > 1) { switch (Eval_Range(psArgs[1], &disasm_addr, &disasm_upper, false)) { case -1: /* invalid value(s) */ return DEBUGGER_CMDDONE; case 0: /* single value */ break; case 1: /* range */ break; } } else { /* continue */ if(!disasm_addr) disasm_addr = M68000_GetPC(); } /* limit is topmost address or instruction count */ if (disasm_upper) { max_insts = INT_MAX; } else { disasm_upper = 0xFFFFFFFF; max_insts = ConfigureParams.Debugger.nDisasmLines; } /* output a range */ for (insts = 0; insts < max_insts && disasm_addr < disasm_upper; insts++) { DebugCpu_ShowAddressInfo(disasm_addr); Disasm(debugOutput, (uaecptr)disasm_addr, &nextpc, 1); disasm_addr = nextpc; } fflush(debugOutput); return DEBUGGER_CMDCONT; }
/** * XBIOS Scrdmp * Call 20 */ static bool XBios_Scrdmp(Uint32 Params) { LOG_TRACE(TRACE_OS_XBIOS, "XBIOS 0x14 Scrdmp() at PC 0x%X\n" , M68000_GetPC()); if (!bXBiosCommands) return false; ScreenSnapShot_SaveScreen(); /* Correct return code? */ Regs[REG_D0] = 0; return true; }
/** * Read byte from 0xff8801/02/03. Return 0xff. */ void PSG_ff880x_ReadByte(void) { M68000_WaitState(1); /* [NP] FIXME not 100% accurate, but gives good results */ IoMem[IoAccessCurrentAddress] = 0xff; if (LOG_TRACE_LEVEL(TRACE_PSG_READ)) { int FrameCycles, HblCounterVideo, LineCycles; Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles ); LOG_TRACE_PRINT("ym read void %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress], FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles); } }
/** * Command: Step CPU, but proceed through subroutines * Does this by temporary conditional breakpoint */ static int DebugCpu_Next(int nArgc, char *psArgv[]) { char command[40]; if (nArgc > 1) { int optype; if(strcmp(psArgv[1], "branch") == 0) optype = CALL_BRANCH; else if(strcmp(psArgv[1], "exception") == 0) optype = CALL_EXCEPTION; else if(strcmp(psArgv[1], "exreturn") == 0) optype = CALL_EXCRETURN; else if(strcmp(psArgv[1], "subcall") == 0) optype = CALL_SUBROUTINE; else if (strcmp(psArgv[1], "subreturn") == 0) optype = CALL_SUBRETURN; else if (strcmp(psArgv[1], "return") == 0) optype = CALL_SUBRETURN | CALL_EXCRETURN; else { fprintf(stderr, "Unrecognized opcode type given!\n"); return DEBUGGER_CMDDONE; } sprintf(command, "CpuOpcodeType & $%x > 0 :once :quiet\n", optype); } else { Uint32 optype, nextpc; optype = DebugCpu_OpcodeType(); /* can this instruction be stepped normally? */ if (optype != CALL_SUBROUTINE && optype != CALL_EXCEPTION) { nCpuSteps = 1; return DEBUGGER_END; } nextpc = Disasm_GetNextPC(M68000_GetPC()); sprintf(command, "pc=$%x :once :quiet\n", nextpc); } /* use breakpoint, not steps */ if (BreakCond_Command(command, false)) { nCpuSteps = 0; return DEBUGGER_END; } return DEBUGGER_CMDDONE; }
/** * XBIOS Devconnect * Call 139 */ static bool XBios_Devconnect(Uint32 Params) { Uint16 src,dst,clk,prescale,protocol; /* Read details from stack */ src = STMemory_ReadWord(Params); dst = STMemory_ReadWord(Params+SIZE_WORD); clk = STMemory_ReadWord(Params+SIZE_WORD+SIZE_WORD); prescale = STMemory_ReadWord(Params+SIZE_WORD+SIZE_WORD+SIZE_WORD); protocol = STMemory_ReadWord(Params+SIZE_WORD+SIZE_WORD+SIZE_WORD+SIZE_WORD); LOG_TRACE(TRACE_OS_XBIOS, "XBIOS 0x8B Devconnect(%hd, 0x%hx, %hd, %hd, %hd) at PC 0x%X\n", src, dst, clk, prescale, protocol , M68000_GetPC() ); return false; }
/** * Write byte to 0xff8802. Set content of YM's data register. */ void PSG_ff8802_WriteByte(void) { // M68000_WaitState(4); M68000_WaitState(1); /* [NP] FIXME not 100% accurate, but gives good results */ if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE)) { int FrameCycles, HblCounterVideo, LineCycles; Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles ); LOG_TRACE_PRINT("ym write %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n", IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress], FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles); } PSG_Set_DataRegister ( IoMem[IoAccessCurrentAddress] ); }
/** * Handle byte read access from IO memory. */ uae_u32 IoMem_bget(uaecptr addr) { Uint8 val; /* Check if access is made by a new instruction or by the same instruction doing multiple byte accesses */ if ( IoAccessInstrPrevClock == CyclesGlobalClockCounter ) IoAccessInstrCount++; /* Same instruction, increase access count */ else { IoAccessInstrPrevClock = CyclesGlobalClockCounter; if ( table68k[ M68000_CurrentOpcode ].size == 0 ) IoAccessInstrCount = 0; /* Instruction size is byte : no multiple accesses */ else IoAccessInstrCount = 1; /* 1st access */ } addr &= 0x00ffffff; /* Use a 24 bit address */ if (addr < 0xff8000 || !regs.s) { /* invalid memory addressing --> bus error */ M68000_BusError(addr, BUS_ERROR_READ, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA); return -1; } IoAccessBaseAddress = addr; /* Store access location */ nIoMemAccessSize = SIZE_BYTE; nBusErrorAccesses = 0; IoAccessCurrentAddress = addr; pInterceptReadTable[addr-0xff8000](); /* Call handler */ /* Check if we read from a bus-error region */ if (nBusErrorAccesses == 1) { M68000_BusError(addr, BUS_ERROR_READ, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA); return -1; } val = IoMem[addr]; LOG_TRACE(TRACE_IOMEM_RD, "IO read.b $%06x = $%02x pc=%x\n", addr, val, M68000_GetPC()); return val; }
/** * XBIOS RsConf * Call 15 */ static bool XBios_Rsconf(Uint32 Params) { Sint16 Baud,Ctrl,Ucr; #if ENABLE_TRACING Sint16 Rsr,Tsr,Scr; #endif Baud = STMemory_ReadWord(Params); Ctrl = STMemory_ReadWord(Params+SIZE_WORD); Ucr = STMemory_ReadWord(Params+SIZE_WORD+SIZE_WORD); #if ENABLE_TRACING Rsr = STMemory_ReadWord(Params+SIZE_WORD+SIZE_WORD+SIZE_WORD); Tsr = STMemory_ReadWord(Params+SIZE_WORD+SIZE_WORD+SIZE_WORD+SIZE_WORD); Scr = STMemory_ReadWord(Params+SIZE_WORD+SIZE_WORD+SIZE_WORD+SIZE_WORD+SIZE_WORD); LOG_TRACE(TRACE_OS_XBIOS, "XBIOS 0x0F Rsconf(%d, %d, %d, %d, %d, %d) at PC 0x%X\n", Baud, Ctrl, Ucr, Rsr, Tsr, Scr, M68000_GetPC()); #endif if (!bXBiosCommands) return false; if (!ConfigureParams.RS232.bEnableRS232) return false; /* Set baud rate and other configuration, if RS232 emaulation is enabled */ if (Baud >= 0 && Baud < ARRAYSIZE(BaudRates)) { /* Convert ST baud rate index to value */ int BaudRate = BaudRates[Baud]; /* And set new baud rate: */ RS232_SetBaudRate(BaudRate); } if (Ucr != -1) { RS232_HandleUCR(Ucr); } if (Ctrl != -1) { RS232_SetFlowControl(Ctrl); } return true; }
/** * XBIOS Floppy Write * Call 9 */ static bool XBios_Flopwr(Uint32 Params) { Uint32 pBuffer; Uint16 Dev,Sector,Side,Track,Count; /* Read details from stack */ pBuffer = STMemory_ReadLong(Params); Dev = STMemory_ReadWord(Params+SIZE_LONG+SIZE_LONG); /* skip reserved long */ Sector = STMemory_ReadWord(Params+SIZE_LONG+SIZE_LONG+SIZE_WORD); Track = STMemory_ReadWord(Params+SIZE_LONG+SIZE_LONG+SIZE_WORD+SIZE_WORD); Side = STMemory_ReadWord(Params+SIZE_LONG+SIZE_LONG+SIZE_WORD+SIZE_WORD+SIZE_WORD); Count = STMemory_ReadWord(Params+SIZE_LONG+SIZE_LONG+SIZE_WORD+SIZE_WORD+SIZE_WORD+SIZE_WORD); LOG_TRACE(TRACE_OS_XBIOS, "XBIOS 0x09 Flopwr(0x%x, %d, %d, %d, %d, %d) at PC 0x%X for: %s\n", pBuffer, Dev, Sector, Track, Side, Count, M68000_GetPC(), EmulationDrives[Dev].sFileName); return false; }
/** * Handle byte write access to IO memory. */ void IoMem_bput(uaecptr addr, uae_u32 val) { /* Check if access is made by a new instruction or by the same instruction doing multiple byte accesses */ if ( IoAccessInstrPrevClock == CyclesGlobalClockCounter ) IoAccessInstrCount++; /* Same instruction, increase access count */ else { IoAccessInstrPrevClock = CyclesGlobalClockCounter; if ( table68k[ M68000_CurrentOpcode ].size == 0 ) IoAccessInstrCount = 0; /* Instruction size is byte : no multiple accesses */ else IoAccessInstrCount = 1; /* 1st access */ } addr &= 0x00ffffff; /* Use a 24 bit address */ LOG_TRACE(TRACE_IOMEM_WR, "IO write.b $%06x = $%02x pc=%x\n", addr, val&0xff, M68000_GetPC()); if (addr < 0xff8000 || !regs.s) { /* invalid memory addressing --> bus error */ M68000_BusError(addr, BUS_ERROR_WRITE, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA); return; } IoAccessBaseAddress = addr; /* Store for exception frame, just in case */ nIoMemAccessSize = SIZE_BYTE; nBusErrorAccesses = 0; IoMem[addr] = val; IoAccessCurrentAddress = addr; pInterceptWriteTable[addr-0xff8000](); /* Call handler */ /* Check if we wrote to a bus-error region */ if (nBusErrorAccesses == 1) { M68000_BusError(addr, BUS_ERROR_WRITE, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA); } }
/** * Write byte to the YM address register (usually 0xff8800). This is used * as a selector for when we read/write the YM data register (0xff8802). */ void PSG_Set_SelectRegister(Uint8 val) { /* Store register used to read/write in $ff8802. This register */ /* is 8 bits on the YM2149, this means it should not be masked */ /* with 0xf. Instead, we keep the 8 bits, but we must ignore */ /* read/write to ff8802 when PSGRegisterSelect >= 16 */ PSGRegisterSelect = val; /* When address register is changed, a read from $ff8800 should */ /* return the masked value of the register. We set the value here */ /* to be returned in case PSG_Get_DataRegister is called */ PSGRegisterReadData = PSGRegisters[PSGRegisterSelect]; if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE)) { int FrameCycles, HblCounterVideo, LineCycles; Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles ); LOG_TRACE_PRINT("ym write reg=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n", PSGRegisterSelect, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles); } }
/** * This function is called after each CPU instruction when debugging is enabled. */ void DebugCpu_Check(void) { if (bCpuProfiling) { Profile_CpuUpdate(); } if (LOG_TRACE_LEVEL(TRACE_CPU_DISASM)) { DebugCpu_ShowAddressInfo(M68000_GetPC()); } if (nCpuActiveCBs) { if (BreakCond_MatchCpu()) DebugUI(); } if (nCpuSteps) { nCpuSteps -= 1; if (nCpuSteps == 0) DebugUI(); } }
/** * This function is called after each CPU instruction when debugging is enabled. */ void DebugCpu_Check(void) { nCpuInstructions++; if (bCpuProfiling) { Profile_CpuUpdate(); } if (LOG_TRACE_LEVEL((TRACE_CPU_DISASM|TRACE_CPU_SYMBOLS))) { DebugCpu_ShowAddressInfo(M68000_GetPC()); } if (nCpuActiveCBs) { if (BreakCond_MatchCpu()) { DebugUI(REASON_CPU_BREAKPOINT); /* make sure we don't decrease step count * below, before even even getting out of here */ if (nCpuSteps) nCpuSteps++; } } if (nCpuSteps) { nCpuSteps--; if (nCpuSteps == 0) DebugUI(REASON_CPU_STEPS); } if (History_TrackCpu()) { History_AddCpu(); } if (ConOutDevice != CONOUT_DEVICE_NONE) { Console_Check(); } }
/** * Reset variables used in PSG */ void PSG_Reset(void) { int i; if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE)) { int FrameCycles, HblCounterVideo, LineCycles; Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles ); LOG_TRACE_PRINT("ym reset video_cyc=%d %d@%d pc=%x instr_cycle %d\n", FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles); } PSGRegisterSelect = 0; PSGRegisterReadData = 0; memset(PSGRegisters, 0, sizeof(PSGRegisters)); PSGRegisters[PSG_REG_IO_PORTA] = 0xff; /* no drive selected + side 0 after a reset */ /* Update sound's emulation registers */ for ( i=0 ; i < NUM_PSG_SOUND_REGISTERS; i++ ) Sound_WriteReg ( i , 0 ); LastStrobe=0; }
/* helper to get instruction type */ Uint32 DebugCpu_OpcodeType(void) { /* cannot use OpcodeFamily like profiler does, * as that's for previous instructions */ Uint16 opcode = STMemory_ReadWord(M68000_GetPC()); if (opcode == 0x4e74 || /* RTD */ opcode == 0x4e75 || /* RTS */ opcode == 0x4e77) /* RTR */ return CALL_SUBRETURN; if (opcode == 0x4e73) /* RTE */ return CALL_EXCRETURN; /* NOTE: BSR needs to be matched before BRA/BCC! */ if ((opcode & 0xff00) == 0x6100 || /* BSR */ (opcode & 0xffc0) == 0x4e80) /* JSR */ return CALL_SUBROUTINE; /* TODO: ftrapcc, chk2? */ if (opcode == 0x4e72 || /* STOP */ opcode == 0x4afc || /* ILLEGAL */ opcode == 0x4e76 || /* TRAPV */ (opcode & 0xfff0) == 0x4e40 || /* TRAP */ (opcode & 0xf1c0) == 0x4180 || /* CHK */ (opcode & 0xfff8) == 0x4848) /* BKPT */ return CALL_EXCEPTION; /* TODO: fbcc, fdbcc */ if ((opcode & 0xf000) == 0x6000 || /* BRA / BCC */ (opcode & 0xffc0) == 0x4ec0 || /* JMP */ (opcode & 0xf0f8) == 0x50c8) /* DBCC */ return CALL_BRANCH; return CALL_UNKNOWN; }
/** * Write byte to YM's register (0xff8802), store according to PSG select register (0xff8800) */ void PSG_Set_DataRegister(Uint8 val) { if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE)) { int FrameCycles, HblCounterVideo, LineCycles; Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles ); LOG_TRACE_PRINT("ym write data reg=0x%x val=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n", PSGRegisterSelect, val, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles); } /* Is a valid PSG register currently selected ? */ if ( PSGRegisterSelect >= MAX_PSG_REGISTERS ) return; /* not valid, ignore write and do nothing */ /* Create samples up until this point with current values */ Sound_Update(false); /* When a read is made from $ff8800 without changing PSGRegisterSelect, we should return */ /* the non masked value. */ PSGRegisterReadData = val; /* store non masked value for PSG_Get_DataRegister */ /* Copy value to PSGRegisters[] */ PSGRegisters[PSGRegisterSelect] = val; /* Clear unused bits for some regs */ if ( ( PSGRegisterSelect == PSG_REG_CHANNEL_A_COARSE ) || ( PSGRegisterSelect == PSG_REG_CHANNEL_B_COARSE ) || ( PSGRegisterSelect == PSG_REG_CHANNEL_C_COARSE ) || ( PSGRegisterSelect == PSG_REG_ENV_SHAPE ) ) PSGRegisters[PSGRegisterSelect] &= 0x0f; /* only keep bits 0 - 3 */ else if ( ( PSGRegisterSelect == PSG_REG_CHANNEL_A_AMP ) || ( PSGRegisterSelect == PSG_REG_CHANNEL_B_AMP ) || ( PSGRegisterSelect == PSG_REG_CHANNEL_C_AMP ) || ( PSGRegisterSelect == PSG_REG_NOISE_GENERATOR ) ) PSGRegisters[PSGRegisterSelect] &= 0x1f; /* only keep bits 0 - 4 */ if ( PSGRegisterSelect < NUM_PSG_SOUND_REGISTERS ) { /* Copy sound related registers 0..13 to the sound module's internal buffer */ Sound_WriteReg ( PSGRegisterSelect , PSGRegisters[PSGRegisterSelect] ); } else if ( PSGRegisterSelect == PSG_REG_IO_PORTA ) { /* * FIXME: This is only a prelimary dirty hack! * Port B (Printer port) - writing here needs to be dispatched to the printer * STROBE (Port A bit5) does a short LOW and back to HIGH when the char is valid * To print you need to write the character byte to IOB and you need to toggle STROBE * (like EmuTOS does). */ /* Printer dispatching only when printing is activated */ if (ConfigureParams.Printer.bEnablePrinting) { /* Bit 5 - Centronics strobe? If STROBE is low and the LastStrobe was high, then print/transfer to the emulated Centronics port. */ if (LastStrobe && ( (PSGRegisters[PSG_REG_IO_PORTA]&(1<<5)) == 0 )) { /* Seems like we want to print something... */ Printer_TransferByteTo(PSGRegisters[PSG_REG_IO_PORTB]); /* Initiate a possible GPIP0 Printer BUSY interrupt */ MFP_InputOnChannel ( MFP_INT_GPIP0 , 0 ); /* Initiate a possible GPIP1 Falcon ACK interrupt */ if (ConfigureParams.System.nMachineType == MACHINE_FALCON) MFP_InputOnChannel ( MFP_INT_GPIP1 , 0 ); } } LastStrobe = PSGRegisters[PSG_REG_IO_PORTA]&(1<<5); /* Bit 0-2 : side and drive select */ if ( (PSGRegisters[PSG_REG_IO_PORTA]&(1<<1)) == 0 ) { /* floppy drive A is ON */ Statusbar_SetFloppyLed(DRIVE_LED_A, LED_STATE_ON); } else { Statusbar_SetFloppyLed(DRIVE_LED_A, LED_STATE_OFF); } if ( (PSGRegisters[PSG_REG_IO_PORTA]&(1<<2)) == 0 ) { /* floppy drive B is ON */ Statusbar_SetFloppyLed(DRIVE_LED_B, LED_STATE_ON); } else { Statusbar_SetFloppyLed(DRIVE_LED_B, LED_STATE_OFF); } /* Bit 3 - Centronics as input */ if(PSGRegisters[PSG_REG_IO_PORTA]&(1<<3)) { /* FIXME: might be needed if we want to emulate sound sampling hardware */ } /* handle Falcon specific bits in PORTA of the PSG */ if (ConfigureParams.System.nMachineType == MACHINE_FALCON) { /* Bit 4 - DSP reset? */ if(PSGRegisters[PSG_REG_IO_PORTA]&(1<<4)) { Log_Printf(LOG_DEBUG, "Calling DSP_Reset?\n"); #if ENABLE_DSP_EMU if (ConfigureParams.System.nDSPType == DSP_TYPE_EMU) { DSP_Reset(); } #endif } /* Bit 6 - Internal Speaker control */ if(PSGRegisters[PSG_REG_IO_PORTA]&(1<<6)) { /*Log_Printf(LOG_DEBUG, "Falcon: Internal Speaker state\n");*/ /* FIXME: add code to handle? (if we want to emulate the speaker at all? */ } /* Bit 7 - Reset IDE? */ if(PSGRegisters[PSG_REG_IO_PORTA]&(1<<7)) { Log_Printf(LOG_DEBUG, "Falcon: Reset IDE subsystem\n"); /* FIXME: add code to handle IDE reset */ } } } }
/** * Update CPU cycle and count statistics for PC address. * * This gets called after instruction has executed and PC * has advanced to next instruction. */ void Profile_CpuUpdate(void) { counters_t *counters = &(cpu_profile.all); Uint32 pc, prev_pc, idx, cycles, misses; cpu_profile_item_t *prev; prev_pc = cpu_profile.prev_pc; /* PC may have extra bits, they need to be masked away as * emulation itself does that too when PC value is used */ cpu_profile.prev_pc = pc = M68000_GetPC() & 0xffffff; if (unlikely(profile_loop.fp)) { if (pc < prev_pc) { if (pc == cpu_profile.loop_start && prev_pc == cpu_profile.loop_end) { cpu_profile.loop_count++; } else { cpu_profile.loop_start = pc; cpu_profile.loop_end = prev_pc; cpu_profile.loop_count = 1; } } else { if (pc > cpu_profile.loop_end) { log_last_loop(); cpu_profile.loop_end = 0xffffffff; cpu_profile.loop_count = 0; } } } idx = address2index(prev_pc); assert(idx <= cpu_profile.size); prev = cpu_profile.data + idx; if (likely(prev->count < MAX_CPU_PROFILE_VALUE)) { prev->count++; } #if USE_CYCLES_COUNTER /* Confusingly, with DSP enabled, cycle counter is for this instruction, * without DSP enabled, it's a monotonically increasing counter. */ if (bDspEnabled) { cycles = Cycles_GetCounter(CYCLES_COUNTER_CPU); } else { Uint32 newcycles = Cycles_GetCounter(CYCLES_COUNTER_CPU); cycles = newcycles - cpu_profile.prev_cycles; cpu_profile.prev_cycles = newcycles; } #else cycles = CurrentInstrCycles + nWaitStateCycles; #endif /* cycles are based on 8Mhz clock, change them to correct one */ cycles <<= nCpuFreqShift; if (likely(prev->cycles < MAX_CPU_PROFILE_VALUE - cycles)) { prev->cycles += cycles; } else { prev->cycles = MAX_CPU_PROFILE_VALUE; } #if ENABLE_WINUAE_CPU misses = CpuInstruction.iCacheMisses; assert(misses < MAX_MISS); cpu_profile.miss_counts[misses]++; if (likely(prev->misses < MAX_CPU_PROFILE_VALUE - misses)) { prev->misses += misses; } else { prev->misses = MAX_CPU_PROFILE_VALUE; } #else misses = 0; #endif if (cpu_callinfo.sites) { collect_calls(prev_pc, counters); } /* counters are increased after caller info is processed, * otherwise cost for the instruction calling the callee * doesn't get accounted to caller (but callee). */ counters->misses += misses; counters->cycles += cycles; counters->count++; #if DEBUG if (unlikely(OpcodeFamily == 0)) { Uint32 nextpc; fputs("WARNING: instruction opcode family is zero (=i_ILLG) for instruction:\n", stderr); Disasm(stderr, prev_pc, &nextpc, 1); } /* catch too large (and negative) cycles for other than STOP instruction */ if (unlikely(cycles > 512 && OpcodeFamily != i_STOP)) { Uint32 nextpc; fprintf(stderr, "WARNING: cycles %d > 512:\n", cycles); Disasm(stderr, prev_pc, &nextpc, 1); } if (unlikely(cycles == 0)) { Uint32 nextpc; fputs("WARNING: Zero cycles for an opcode:\n", stderr); Disasm(stderr, prev_pc, &nextpc, 1); } #endif }
/** * XBIOS remote control interface for Hatari * Call 255 */ static bool XBios_HatariControl(Uint32 Params) { const char *pText; pText = (const char *)STRAM_ADDR(STMemory_ReadLong(Params)); LOG_TRACE(TRACE_OS_XBIOS, "XBIOS 0x%02X HatariControl(%s) at PC 0x%X\n", HATARI_CONTROL_OPCODE, pText, M68000_GetPC()); if (!bXBiosCommands) return false; Control_ProcessBuffer(pText); Regs[REG_D0] = 0; return true; }
/** * Check if we need to re-direct XBios call to our own routines */ bool XBios(void) { Uint32 Params; Uint16 XBiosCall; /* Find call */ Params = Regs[REG_A7]; XBiosCall = STMemory_ReadWord(Params); Params += SIZE_WORD; switch (XBiosCall) { /* commands with special handling */ case 8: return XBios_Floprd(Params); case 9: return XBios_Flopwr(Params); case 15: return XBios_Rsconf(Params); case 20: return XBios_Scrdmp(Params); case 139: return XBios_Devconnect(Params); case HATARI_CONTROL_OPCODE: return XBios_HatariControl(Params); case 2: /* Physbase */ case 3: /* Logbase */ case 4: /* Getrez */ case 17: /* Random */ case 23: /* Gettime */ case 24: /* Bioskeys */ case 34: /* Kbdvbase */ case 37: /* Vsync */ case 39: /* Puntaes */ case 81: /* EgetShift */ case 89: /* VgetMonitor */ case 103: /* Dsp_GetWordSize */ case 104: /* Dsp_Lock */ case 105: /* Dsp_Unlock */ case 113: /* Dsp_RequestUniqueAbility */ case 114: /* Dsp_GetProgAbility */ case 115: /* Dsp_FlushSubroutines */ case 121: /* Dsp_Hf2 */ case 122: /* Dsp_Hf3 */ case 125: /* Dsp_Hstat */ case 128: /* Locksnd */ case 129: /* Unlocksnd */ /* commands with no args */ LOG_TRACE(TRACE_OS_XBIOS, "XBIOS 0x%02hX %s() at PC 0x%X\n", XBiosCall, XBios_Call2Name(XBiosCall), M68000_GetPC()); return false; case 1: /* Ssbrk */ case 14: /* Iorec */ case 26: /* Jdisint */ case 27: /* Jenabint */ case 29: /* Offgibit */ case 30: /* Ongibit */ case 33: /* Setprt */ case 44: /* Bconmap */ case 64: /* Blitmode */ case 80: /* EsetShift */ case 82: /* EsetBank */ case 86: /* EsetGray */ case 87: /* EsetSmear */ case 88: /* VsetMode */ case 90: /* VsetSync */ case 91: /* VgetSize */ case 102: /* Dsp_RemoveInterrupts */ case 112: /* Dsp_TriggerHC */ case 117: /* Dsp_InqSubrAbility */ case 118: /* Dsp_RunSubroutine */ case 119: /* Dsp_Hf0 */ case 120: /* Dsp_Hf1 */ case 132: /* Setmode */ case 134: /* Setmontracks */ case 136: /* Buffoper */ case 140: /* Sndstatus */ /* ones taking single word */ LOG_TRACE(TRACE_OS_XBIOS, "XBIOS 0x%02hX %s(0x%hX) at PC 0x%X\n", XBiosCall, XBios_Call2Name(XBiosCall), STMemory_ReadWord(Params), M68000_GetPC()); return false; case 6: /* Setpalette */ case 22: /* Settime */ case 32: /* Dosound */ case 36: /* Ptrblt */ case 38: /* Supexec */ case 48: /* Metainit */ case 141: /* Buffptr */ /* ones taking long or pointer */ LOG_TRACE(TRACE_OS_XBIOS, "XBIOS 0x%02hX %s(0x%X) at PC 0x%X\n", XBiosCall, XBios_Call2Name(XBiosCall), STMemory_ReadLong(Params), M68000_GetPC()); return false; case 7: /* Setcolor */ case 21: /* Cursconf */ case 28: /* Giaccess */ case 35: /* Kbrate */ case 41: /* Floprate */ case 83: /* EsetColor */ case 130: /* Soundcmd */ case 133: /* Settracks */ case 137: /* Dsptristate */ case 135: /* Setinterrupt */ case 138: /* Gpio */ /* ones taking two words */ LOG_TRACE(TRACE_OS_XBIOS, "XBIOS 0x%02hX %s(0x%hX, 0x%hX) at PC 0x%X\n", XBiosCall, XBios_Call2Name(XBiosCall), STMemory_ReadWord(Params), STMemory_ReadWord(Params+SIZE_WORD), M68000_GetPC()); return false; case 12: /* Midiws */ case 13: /* Mfpint */ case 25: /* Ikbdws */ /* ones taking word length/index and pointer */ LOG_TRACE(TRACE_OS_XBIOS, "XBIOS 0x%02hX %s(%hd, 0x%X) at PC 0x %X\n", XBiosCall, XBios_Call2Name(XBiosCall), STMemory_ReadWord(Params), STMemory_ReadLong(Params+SIZE_WORD), M68000_GetPC()); return false; case 11: /* Dbmsg */ case 84: /* EsetPalette */ case 85: /* EgetPalette */ case 93: /* VsetRGB */ case 94: /* VgetRGB */ /* ones taking word, word and long/pointer */ LOG_TRACE(TRACE_OS_XBIOS, "XBIOS 0x%02hX %s(0x%hX, 0x%hX, 0x%X) at PC 0x%X\n", XBiosCall, XBios_Call2Name(XBiosCall), STMemory_ReadWord(Params), STMemory_ReadWord(Params+SIZE_WORD), STMemory_ReadLong(Params+SIZE_WORD+SIZE_WORD), M68000_GetPC()); return false; case 106: /* Dsp_Available */ case 107: /* Dsp_Reserve */ case 111: /* Dsp_LodToBinary */ case 126: /* Dsp_SetVectors */ /* ones taking two longs/pointers */ LOG_TRACE(TRACE_OS_XBIOS, "XBIOS 0x%02hX %s(0x%X, 0x%X) at PC 0x%X\n", XBiosCall, XBios_Call2Name(XBiosCall), STMemory_ReadLong(Params), STMemory_ReadLong(Params+SIZE_LONG), M68000_GetPC()); return false; case 5: /* Setscreen */ if (STMemory_ReadWord(Params+SIZE_LONG+SIZE_LONG) == 3) { /* actually VSetscreen with extra parameter */ LOG_TRACE(TRACE_OS_XBIOS, "XBIOS 0x%02hX VsetScreen(0x%X, 0x%X, 3, 0x%hX) at PC 0x%X\n", XBiosCall, STMemory_ReadLong(Params), STMemory_ReadLong(Params+SIZE_LONG), STMemory_ReadWord(Params+SIZE_LONG+SIZE_LONG+SIZE_WORD), M68000_GetPC()); return false; } case 109: /* Dsp_ExecProg */ case 110: /* Dsp_ExecBoot */ case 116: /* Dsp_LoadSubroutine */ case 150: /* VsetMask */ /* ones taking two longs/pointers and a word */ LOG_TRACE(TRACE_OS_XBIOS, "XBIOS 0x%02hX %s(0x%X, 0x%X, 0x%hX) at PC 0x%X\n", XBiosCall, XBios_Call2Name(XBiosCall), STMemory_ReadLong(Params), STMemory_ReadLong(Params+SIZE_LONG), STMemory_ReadWord(Params+SIZE_LONG+SIZE_LONG), M68000_GetPC()); return false; default: /* rest of XBios calls */ LOG_TRACE(TRACE_OS_XBIOS, "XBIOS 0x%02hX (%s)\n", XBiosCall, XBios_Call2Name(XBiosCall)); return false; } }
/** * Parse unsigned register/symbol/number value and set it to "number" * and the number base used for parsing to "base". * Return how many characters were parsed or zero for error. */ static int getValue(const char *str, Uint32 *number, int *base, bool bForDsp) { char name[64]; const char *end; Uint32 mask, *addr; int len; for (end = str; *end == '_' || isalnum(*end); end++); len = end-str; if (len >= (int)sizeof(name)) { fprintf(stderr, "ERROR: symbol name at '%s' too long (%d chars)\n", str, len); return 0; } memcpy(name, str, len); name[len] = '\0'; *base = 0; /* no base (e.g. variable) */ /* internal Hatari variable? */ if (BreakCond_GetHatariVariable(name, number)) { return len; } if (bForDsp) { int regsize = DSP_GetRegisterAddress(name, &addr, &mask); /* DSP register or symbol? */ switch (regsize) { case 16: *number = (*((Uint16*)addr) & mask); return len; case 32: *number = (*addr & mask); return len; default: if (Symbols_GetDspAddress(SYMTYPE_ALL, name, number)) { return len; } } } else { /* a special case CPU register? */ if (strcasecmp(name, "PC") == 0) { *number = M68000_GetPC(); return len; } if (strcasecmp(name, "SR") == 0) { *number = M68000_GetSR(); return len; } /* a normal CPU register or symbol? */ if (DebugCpu_GetRegisterAddress(name, &addr)) { *number = *addr; return len; } if (Symbols_GetCpuAddress(SYMTYPE_ALL, name, number)) { return len; } } /* none of above, assume it's a number */ return getNumber(str, number, base); }
/** * Should be called when debugger is re-entered to reset * relevant CPU debugging variables. */ void DebugCpu_InitSession(void) { disasm_addr = M68000_GetPC(); Profile_CpuStop(); }