/** * 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 function is called after each DSP instruction when debugging is enabled. */ void DebugDsp_Check(void) { if (bDspProfiling) { Profile_DspUpdate(); } if (LOG_TRACE_LEVEL((TRACE_DSP_DISASM|TRACE_DSP_SYMBOLS))) { DebugDsp_ShowAddressInfo(DSP_GetPC()); } if (nDspActiveCBs) { if (BreakCond_MatchDsp()) DebugUI(REASON_DSP_BREAKPOINT); } if (nDspSteps) { nDspSteps -= 1; if (nDspSteps == 0) DebugUI(REASON_DSP_STEPS); } if (bHistoryEnabled) { History_AddDsp(); } }
/** * Should be called before returning back emulation to tell the DSP core * to call us after each instruction if "real-time" debugging like * breakpoints has been set. */ void DebugDsp_SetDebugging(void) { bDspProfiling = Profile_DspStart(); nDspActiveCBs = BreakCond_BreakPointCount(true); if (nDspActiveCBs || nDspSteps || bDspProfiling || bHistoryEnabled || LOG_TRACE_LEVEL((TRACE_DSP_DISASM|TRACE_DSP_SYMBOLS))) DSP_SetDebugging(true); else DSP_SetDebugging(false); }
/** * 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); } }
/** * Should be called before returning back emulation to tell the CPU core * to call us after each instruction if "real-time" debugging like * breakpoints has been set. */ void DebugCpu_SetDebugging(void) { bCpuProfiling = Profile_CpuStart(); nCpuActiveCBs = BreakCond_CpuBreakPointCount(); if (nCpuActiveCBs || nCpuSteps || bCpuProfiling || History_TrackCpu() || LOG_TRACE_LEVEL((TRACE_CPU_DISASM|TRACE_CPU_SYMBOLS)) || ConOutDevice != CONOUT_DEVICE_NONE) { M68000_SetSpecial(SPCFLAG_DEBUGGER); nCpuInstructions = 0; } else M68000_UnsetSpecial(SPCFLAG_DEBUGGER); }
/** * 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] ); }
/** * 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; }
/** * 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 */ } } } }
/** * Check whether this is VDI/AES call and see if we need to re-direct * it to our own routines. Return true if VDI_Complete() function * needs to be called on OS call exit, otherwise return false. * * We enter here with Trap #2, so D0 tells which OS call it is (VDI/AES) * and D1 is pointer to VDI/AES vectors, i.e. Control, Intin, Ptsin etc... */ bool VDI_AES_Entry(void) { Uint16 call = Regs[REG_D0]; Uint32 TablePtr = Regs[REG_D1]; #if ENABLE_TRACING /* AES call? */ if (call == 0xC8) { if (!STMemory_ValidArea(TablePtr, 24)) { Log_Printf(LOG_WARN, "AES call failed due to invalid parameter block address 0x%x+%i\n", TablePtr, 24); return false; } /* store values for debugger "info aes" command */ AESControl = STMemory_ReadLong(TablePtr); AESGlobal = STMemory_ReadLong(TablePtr+4); AESIntin = STMemory_ReadLong(TablePtr+8); AESIntout = STMemory_ReadLong(TablePtr+12); AESAddrin = STMemory_ReadLong(TablePtr+16); AESAddrout = STMemory_ReadLong(TablePtr+20); AESOpCode = STMemory_ReadWord(AESControl); if (LOG_TRACE_LEVEL(TRACE_OS_AES)) { AES_OpcodeInfo(TraceFile, AESOpCode); } /* using same special opcode trick doesn't work for * both VDI & AES as AES functions can be called * recursively and VDI calls happen inside AES calls. */ return false; } #endif /* VDI call? */ if (call == 0x73) { if (!STMemory_ValidArea(TablePtr, 20)) { Log_Printf(LOG_WARN, "VDI call failed due to invalid parameter block address 0x%x+%i\n", TablePtr, 20); return false; } /* store values for extended VDI resolution handling * and debugger "info vdi" command */ VDIControl = STMemory_ReadLong(TablePtr); VDIIntin = STMemory_ReadLong(TablePtr+4); VDIPtsin = STMemory_ReadLong(TablePtr+8); VDIIntout = STMemory_ReadLong(TablePtr+12); VDIPtsout = STMemory_ReadLong(TablePtr+16); VDIOpCode = STMemory_ReadWord(VDIControl); #if ENABLE_TRACING { Uint16 subcode = STMemory_ReadWord(VDIControl+2*5); LOG_TRACE(TRACE_OS_VDI, "VDI call %3hd/%3hd (%s)\n", VDIOpCode, subcode, VDI_Opcode2Name(VDIOpCode, subcode)); } #endif /* Only workstation open needs to be handled at trap return */ return bUseVDIRes && VDI_isWorkstationOpen(VDIOpCode); } LOG_TRACE((TRACE_OS_VDI|TRACE_OS_AES), "Trap #2 with D0 = 0x%hX\n", call); return false; }