|
|
1.1 root 1: /* 1.1.1.4 root 2: Hatari - psg.c 1.1 root 3: 1.1.1.15! root 4: This file is distributed under the GNU General Public License, version 2 ! 5: or at your option any later version. Read the file gpl.txt for details. 1.1.1.3 root 6: 1.1.1.4 root 7: Programmable Sound Generator (YM-2149) - PSG 1.1.1.3 root 8: 1.1.1.4 root 9: Also used for the printer (centronics) port emulation (PSG Port B, Register 15) 1.1 root 10: */ 1.1.1.7 root 11: 12: 13: /* 2007/04/14 [NP] First approximation to get cycle accurate accesses to ff8800/02 */ 14: /* by cumulating wait state of 1 cycle and rounding the final */ 15: /* result to 4. */ 16: /* 2007/04/29 [NP] Functions PSG_Void_WriteByte and PSG_Void_ReadByte to handle */ 17: /* accesses to $ff8801/03. These adresses have no effect, but they */ 18: /* must give a 1 cycle wait state (e.g. move.l d0,ff8800). */ 19: /* 2007/09/29 [NP] Replace printf by calls to HATARI_TRACE. */ 20: /* 2007/10/23 [NP] In PSG_Void_WriteByte, add a wait state only if no wait state */ 21: /* were added so far (hack, but gives good result). */ 22: /* 2007/11/18 [NP] In PSG_DataRegister_WriteByte, set unused bit to 0, in case */ 23: /* the data reg is read later (fix Mindbomb Demo / BBC). */ 1.1.1.9 root 24: /* 2008/04/20 [NP] In PSG_DataRegister_WriteByte, set unused bit to 0 for register */ 25: /* 6 too (noise period). */ 26: /* 2008/07/27 [NP] Better separation between accesses to the YM hardware registers */ 27: /* and the sound rendering routines. Use Sound_WriteReg() to pass */ 28: /* all writes to the sound rendering functions. This allows to */ 1.1.1.15! root 29: /* have sound.c independent of psg.c (to ease replacement of */ 1.1.1.9 root 30: /* sound.c by another rendering method). */ 31: /* 2008/08/11 [NP] Set drive leds. */ 32: /* 2008/10/16 [NP] When writing to $ff8800, register select should not be masked */ 33: /* with 0xf, it's a real 8 bits register where all bits are */ 34: /* significant. This means only value <16 should be considered as */ 35: /* valid register selection. When reg select is >= 16, all writes */ 36: /* and reads in $ff8802 should be ignored. */ 37: /* (fix European Demo Intro, which sets addr reg to 0x10 when */ 38: /* sample playback is disabled). */ 1.1.1.10 root 39: /* 2008/12/21 [NP] After testing different cases on a real STF, rewrite registers */ 40: /* handling. As only pins BC1 and BDIR are used in an Atari to */ 41: /* address the YM2149, this means only 1 bit is necessary to access*/ 42: /* select/data registers. Other variations of the $ff88xx addresses*/ 43: /* will point to either $ff8800 or $ff8802. Only bit 1 of $ff88xx */ 44: /* is useful to know which register is accessed in the YM2149. */ 45: /* So, it's possible to access the YM2149 with $ff8801 and $ff8803 */ 46: /* but under conditions : the write to a shadow address (bit 0=1) */ 47: /* can't be made by an instruction that writes to the same address */ 48: /* with bit 0=0 at the same time (.W or .L access). */ 49: /* In that case, only the address with bit 0=0 is taken into */ 50: /* account. This means a write to $ff8801/03 will succeed only if */ 51: /* the access size is .B (byte) or the opcode is a movep (because */ 52: /* in that case we won't access the same register with 2 different */ 53: /* addresses) (fix the game X-Out, which uses movep.w to write to */ 54: /* $ff8801/03). */ 55: /* Refactorize some code for cleaner handling of these accesses. */ 56: /* Only reads to $ff8800 will return a data, reads to $ff8801/02/03*/ 57: /* always return 0xff (tested on STF). */ 58: /* When PSGRegisterSelect > 15, reads to $ff8800 also return 0xff. */ 59: /* 2009/01/24 [NP] Remove redundant test, as movep implies SIZE_BYTE access. */ 1.1.1.13 root 60: /* 2011/10/30 [NP] There's a special case when reading a register from $ff8800 : */ 61: /* if the register number was not changed since the last write, */ 62: /* then we must return the value that was written to $ff8802 */ 63: /* without masking the unused bit (fix the game Murders In Venice, */ 64: /* which expects to read $10 from reg 3). */ 1.1.1.7 root 65: 66: 67: /* Emulating wait states when accessing $ff8800/01/02/03 with different 'move' variants */ 68: /* is a complex task. So far, adding 1 cycle wait state to each access and rounding the */ 69: /* final number to 4 gives some good results, but this is certainly not the way it's */ 70: /* working for real in the ST. */ 71: /* The following examples show some verified wait states for different accesses : */ 72: /* lea $ffff8800,a1 */ 73: /* lea $ffff8802,a2 */ 74: /* lea $ffff8801,a3 */ 75: /* */ 76: /* movep.w d1,(a1) ; 20 16+4 (ventura loader) */ 77: /* movep.l d1,(a1) ; 28 24+4 (ventura loader, ulm loader) */ 78: /* */ 79: /* movep.l d6,0(a5) ; 28 24+4 (SNY I, TCB) */ 80: /* movep.w d5,0(a5) ; 20 16+4 (SNY I, TCB) */ 81: /* */ 82: /* move.b d1,(a1) ; 12 8+4 */ 83: /* move.b d1,(a2) ; 12 8+4 */ 84: /* move.b d1,(a3) ; 12 8+4 (crickey ulm hidden) */ 85: /* */ 86: /* move.w d1,(a1) ; 12 8+4 */ 87: /* move.w d1,(a2) ; 12 8+4 */ 88: /* move.l d1,(a1) ; 16 12+4 (ulm loader) */ 89: /* */ 90: /* movem.l d1,(a1) ; 20 16+4 */ 91: /* movem.l d1-d2,(a1) ; 28 24+4 */ 92: /* movem.l d1-d3,(a1) ; 40 32+4+4 */ 93: /* movem.l d1-d4,(a1) ; 48 40+4+4 */ 94: /* movem.l d1-d5,(a1) ; 60 48+4+4+4 */ 95: /* movem.l d1-d6,(a1) ; 68 56+4+4+4 */ 96: /* movem.l d1-d7,(a1) ; 80 64+4+4+4+4 */ 97: /* movem.l d0-d7,(a1) ; 88 72+4+4+4+4 */ 98: /* */ 1.1.1.10 root 99: /* movep.w d0,(a3) (X-Out) */ 100: /* */ 1.1.1.7 root 101: /* This gives the following "model" : */ 102: /* - each access to $ff8800 or $ff8802 add 1 cycle wait state */ 1.1.1.10 root 103: /* - accesses to $ff8801 or $ff8803 are considered "valid" only if we don't access */ 104: /* the corresponding "non shadow" addresses $ff8800/02 at the same time. */ 105: /* This means only .B size (move.b for example) or movep opcode will work. */ 106: /* If the access is valid, add 1 cycle wait state, else ignore the write and */ 107: /* don't add any cycle. */ 1.1.1.7 root 108: 109: 110: 1.1.1.11 root 111: const char PSG_fileid[] = "Hatari psg.c : " __DATE__ " " __TIME__; 1.1 root 112: 113: #include "main.h" 1.1.1.3 root 114: #include "configuration.h" 1.1.1.4 root 115: #include "ioMem.h" 1.1.1.5 root 116: #include "joy.h" 1.1.1.7 root 117: #include "log.h" 1.1.1.4 root 118: #include "m68000.h" 1.1 root 119: #include "memorySnapShot.h" 120: #include "sound.h" 1.1.1.4 root 121: #include "printer.h" /* because Printer I/O goes through PSG Register 15 */ 1.1.1.3 root 122: #include "psg.h" 1.1.1.7 root 123: #if ENABLE_DSP_EMU 124: #include "falcon/dsp.h" 125: #endif 1.1.1.12 root 126: #include "screen.h" 1.1.1.7 root 127: #include "video.h" 1.1.1.9 root 128: #include "statusbar.h" 129: #include "mfp.h" 130: 1.1 root 131: 1.1.1.13 root 132: Uint8 PSGRegisterSelect; /* Write to 0xff8800 sets the register number used in read/write accesses */ 133: Uint8 PSGRegisterReadData; /* Value returned when reading from 0xff8800 */ 1.1.1.14 root 134: Uint8 PSGRegisters[MAX_PSG_REGISTERS]; /* Registers in PSG, see PSG_REG_xxxx */ 1.1.1.4 root 135: 1.1.1.9 root 136: static unsigned int LastStrobe=0; /* Falling edge of Strobe used for printer */ 1.1 root 137: 138: 139: /*-----------------------------------------------------------------------*/ 1.1.1.7 root 140: /** 141: * Reset variables used in PSG 142: */ 1.1 root 143: void PSG_Reset(void) 144: { 1.1.1.13 root 145: int i; 146: 1.1.1.14 root 147: if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE)) 148: { 149: int FrameCycles, HblCounterVideo, LineCycles; 150: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles ); 151: LOG_TRACE_PRINT("ym reset video_cyc=%d %d@%d pc=%x instr_cycle %d\n", 152: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles); 153: } 154: 1.1.1.4 root 155: PSGRegisterSelect = 0; 1.1.1.13 root 156: PSGRegisterReadData = 0; 1.1.1.4 root 157: memset(PSGRegisters, 0, sizeof(PSGRegisters)); 1.1.1.14 root 158: PSGRegisters[PSG_REG_IO_PORTA] = 0xff; /* no drive selected + side 0 after a reset */ 159: 160: /* Update sound's emulation registers */ 161: for ( i=0 ; i < NUM_PSG_SOUND_REGISTERS; i++ ) 1.1.1.13 root 162: Sound_WriteReg ( i , 0 ); 163: 1.1.1.9 root 164: LastStrobe=0; 1.1 root 165: } 166: 1.1.1.2 root 167: 168: /*-----------------------------------------------------------------------*/ 1.1.1.7 root 169: /** 170: * Save/Restore snapshot of local variables ('MemorySnapShot_Store' handles type) 171: */ 1.1.1.9 root 172: void PSG_MemorySnapShot_Capture(bool bSave) 1.1 root 173: { 1.1.1.4 root 174: /* Save/Restore details */ 175: MemorySnapShot_Store(&PSGRegisterSelect, sizeof(PSGRegisterSelect)); 1.1.1.13 root 176: MemorySnapShot_Store(&PSGRegisterReadData, sizeof(PSGRegisterReadData)); 1.1.1.4 root 177: MemorySnapShot_Store(PSGRegisters, sizeof(PSGRegisters)); 1.1.1.9 root 178: MemorySnapShot_Store(&LastStrobe, sizeof(LastStrobe)); 1.1 root 179: } 180: 1.1.1.2 root 181: 182: /*-----------------------------------------------------------------------*/ 1.1.1.7 root 183: /** 1.1.1.10 root 184: * Write byte to the YM address register (usually 0xff8800). This is used 185: * as a selector for when we read/write the YM data register (0xff8802). 1.1.1.7 root 186: */ 1.1.1.10 root 187: void PSG_Set_SelectRegister(Uint8 val) 1.1 root 188: { 1.1.1.9 root 189: /* Store register used to read/write in $ff8802. This register */ 190: /* is 8 bits on the YM2149, this means it should not be masked */ 191: /* with 0xf. Instead, we keep the 8 bits, but we must ignore */ 192: /* read/write to ff8802 when PSGRegisterSelect >= 16 */ 1.1.1.10 root 193: PSGRegisterSelect = val; 1.1.1.7 root 194: 1.1.1.13 root 195: /* When address register is changed, a read from $ff8800 should */ 196: /* return the masked value of the register. We set the value here */ 197: /* to be returned in case PSG_Get_DataRegister is called */ 198: PSGRegisterReadData = PSGRegisters[PSGRegisterSelect]; 199: 1.1.1.11 root 200: if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE)) 201: { 202: int FrameCycles, HblCounterVideo, LineCycles; 203: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles ); 204: LOG_TRACE_PRINT("ym write reg=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n", 205: PSGRegisterSelect, FrameCycles, LineCycles, HblCounterVideo, 206: M68000_GetPC(), CurrentInstrCycles); 207: } 1.1 root 208: } 209: 1.1.1.2 root 210: 211: /*-----------------------------------------------------------------------*/ 1.1.1.7 root 212: /** 1.1.1.10 root 213: * Read byte from 0xff8800, return PSG data 1.1.1.7 root 214: */ 1.1.1.10 root 215: Uint8 PSG_Get_DataRegister(void) 1.1 root 216: { 1.1.1.9 root 217: /* Is a valid PSG register currently selected ? */ 1.1.1.14 root 218: if ( PSGRegisterSelect >= MAX_PSG_REGISTERS ) 1.1.1.10 root 219: return 0xff; /* not valid, return 0xff */ 1.1.1.9 root 220: 1.1.1.14 root 221: if (PSGRegisterSelect == PSG_REG_IO_PORTA) 1.1.1.5 root 222: { 223: /* Second parallel port joystick uses centronics strobe bit as fire button: */ 224: if (ConfigureParams.Joysticks.Joy[JOYID_PARPORT2].nJoystickMode != JOYSTICK_DISABLED) 225: { 226: if (Joy_GetStickData(JOYID_PARPORT2) & 0x80) 1.1.1.14 root 227: PSGRegisters[PSG_REG_IO_PORTA] &= ~32; 1.1.1.5 root 228: else 1.1.1.14 root 229: PSGRegisters[PSG_REG_IO_PORTA] |= 32; 1.1.1.5 root 230: } 231: } 1.1.1.14 root 232: else if (PSGRegisterSelect == PSG_REG_IO_PORTB) 1.1.1.5 root 233: { 234: /* PSG register 15 is parallel port data register - used by parallel port joysticks: */ 235: if (ConfigureParams.Joysticks.Joy[JOYID_PARPORT1].nJoystickMode != JOYSTICK_DISABLED) 236: { 1.1.1.14 root 237: PSGRegisters[PSG_REG_IO_PORTB] &= 0x0f; 238: PSGRegisters[PSG_REG_IO_PORTB] |= ~Joy_GetStickData(JOYID_PARPORT1) << 4; 1.1.1.5 root 239: } 240: if (ConfigureParams.Joysticks.Joy[JOYID_PARPORT2].nJoystickMode != JOYSTICK_DISABLED) 241: { 1.1.1.14 root 242: PSGRegisters[PSG_REG_IO_PORTB] &= 0xf0; 243: PSGRegisters[PSG_REG_IO_PORTB] |= ~Joy_GetStickData(JOYID_PARPORT2) & 0x0f; 1.1.1.5 root 244: } 245: } 246: 1.1.1.4 root 247: /* Read data last selected by register */ 1.1.1.13 root 248: return PSGRegisterReadData; 1.1 root 249: } 250: 1.1.1.2 root 251: 252: /*-----------------------------------------------------------------------*/ 1.1.1.7 root 253: /** 1.1.1.10 root 254: * Write byte to YM's register (0xff8802), store according to PSG select register (0xff8800) 1.1.1.7 root 255: */ 1.1.1.10 root 256: void PSG_Set_DataRegister(Uint8 val) 1.1.1.4 root 257: { 1.1.1.11 root 258: if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE)) 259: { 260: int FrameCycles, HblCounterVideo, LineCycles; 261: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles ); 262: LOG_TRACE_PRINT("ym write data reg=0x%x val=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n", 263: PSGRegisterSelect, val, FrameCycles, LineCycles, 264: HblCounterVideo, M68000_GetPC(), CurrentInstrCycles); 265: } 1.1.1.9 root 266: 267: /* Is a valid PSG register currently selected ? */ 1.1.1.14 root 268: if ( PSGRegisterSelect >= MAX_PSG_REGISTERS ) 1.1.1.9 root 269: return; /* not valid, ignore write and do nothing */ 270: 1.1.1.8 root 271: /* Create samples up until this point with current values */ 1.1.1.12 root 272: Sound_Update(false); 1.1.1.8 root 273: 1.1.1.13 root 274: /* When a read is made from $ff8800 without changing PSGRegisterSelect, we should return */ 275: /* the non masked value. */ 276: PSGRegisterReadData = val; /* store non masked value for PSG_Get_DataRegister */ 277: 1.1.1.10 root 278: /* Copy value to PSGRegisters[] */ 279: PSGRegisters[PSGRegisterSelect] = val; 1.1.1.4 root 280: 1.1.1.9 root 281: /* Clear unused bits for some regs */ 1.1.1.7 root 282: if ( ( PSGRegisterSelect == PSG_REG_CHANNEL_A_COARSE ) || ( PSGRegisterSelect == PSG_REG_CHANNEL_B_COARSE ) 283: || ( PSGRegisterSelect == PSG_REG_CHANNEL_C_COARSE ) || ( PSGRegisterSelect == PSG_REG_ENV_SHAPE ) ) 284: PSGRegisters[PSGRegisterSelect] &= 0x0f; /* only keep bits 0 - 3 */ 285: 286: else if ( ( PSGRegisterSelect == PSG_REG_CHANNEL_A_AMP ) || ( PSGRegisterSelect == PSG_REG_CHANNEL_B_AMP ) 1.1.1.9 root 287: || ( PSGRegisterSelect == PSG_REG_CHANNEL_C_AMP ) || ( PSGRegisterSelect == PSG_REG_NOISE_GENERATOR ) ) 1.1.1.7 root 288: PSGRegisters[PSGRegisterSelect] &= 0x1f; /* only keep bits 0 - 4 */ 289: 290: 1.1.1.9 root 291: if ( PSGRegisterSelect < NUM_PSG_SOUND_REGISTERS ) 1.1.1.4 root 292: { 1.1.1.9 root 293: /* Copy sound related registers 0..13 to the sound module's internal buffer */ 294: Sound_WriteReg ( PSGRegisterSelect , PSGRegisters[PSGRegisterSelect] ); 295: } 1.1.1.4 root 296: 1.1.1.9 root 297: else if ( PSGRegisterSelect == PSG_REG_IO_PORTA ) 298: { 299: /* 300: * FIXME: This is only a prelimary dirty hack! 301: * Port B (Printer port) - writing here needs to be dispatched to the printer 302: * STROBE (Port A bit5) does a short LOW and back to HIGH when the char is valid 303: * To print you need to write the character byte to IOB and you need to toggle STROBE 304: * (like EmuTOS does). 305: */ 1.1.1.4 root 306: /* Printer dispatching only when printing is activated */ 307: if (ConfigureParams.Printer.bEnablePrinting) 308: { 1.1.1.9 root 309: /* Bit 5 - Centronics strobe? If STROBE is low and the LastStrobe was high, 310: then print/transfer to the emulated Centronics port. 1.1.1.7 root 311: */ 1.1.1.9 root 312: if (LastStrobe && ( (PSGRegisters[PSG_REG_IO_PORTA]&(1<<5)) == 0 )) 1.1.1.4 root 313: { 314: /* Seems like we want to print something... */ 1.1.1.9 root 315: Printer_TransferByteTo(PSGRegisters[PSG_REG_IO_PORTB]); 316: /* Initiate a possible GPIP0 Printer BUSY interrupt */ 1.1.1.15! root 317: MFP_InputOnChannel ( MFP_INT_GPIP0 , 0 ); 1.1.1.9 root 318: /* Initiate a possible GPIP1 Falcon ACK interrupt */ 319: if (ConfigureParams.System.nMachineType == MACHINE_FALCON) 1.1.1.15! root 320: MFP_InputOnChannel ( MFP_INT_GPIP1 , 0 ); 1.1.1.4 root 321: } 322: } 1.1.1.9 root 323: LastStrobe = PSGRegisters[PSG_REG_IO_PORTA]&(1<<5); 324: 325: /* Bit 0-2 : side and drive select */ 326: if ( (PSGRegisters[PSG_REG_IO_PORTA]&(1<<1)) == 0 ) 327: { 328: /* floppy drive A is ON */ 1.1.1.15! root 329: Statusbar_SetFloppyLed(DRIVE_LED_A, LED_STATE_ON); 1.1.1.9 root 330: } 331: else 332: { 1.1.1.15! root 333: Statusbar_SetFloppyLed(DRIVE_LED_A, LED_STATE_OFF); 1.1.1.9 root 334: } 335: if ( (PSGRegisters[PSG_REG_IO_PORTA]&(1<<2)) == 0 ) 336: { 337: /* floppy drive B is ON */ 1.1.1.15! root 338: Statusbar_SetFloppyLed(DRIVE_LED_B, LED_STATE_ON); 1.1.1.9 root 339: } 340: else 341: { 1.1.1.15! root 342: Statusbar_SetFloppyLed(DRIVE_LED_B, LED_STATE_OFF); 1.1.1.9 root 343: } 344: 1.1.1.7 root 345: /* Bit 3 - Centronics as input */ 346: if(PSGRegisters[PSG_REG_IO_PORTA]&(1<<3)) 347: { 348: /* FIXME: might be needed if we want to emulate sound sampling hardware */ 349: } 350: 351: /* handle Falcon specific bits in PORTA of the PSG */ 352: if (ConfigureParams.System.nMachineType == MACHINE_FALCON) 353: { 354: /* Bit 4 - DSP reset? */ 355: if(PSGRegisters[PSG_REG_IO_PORTA]&(1<<4)) 356: { 357: Log_Printf(LOG_DEBUG, "Calling DSP_Reset?\n"); 358: #if ENABLE_DSP_EMU 359: if (ConfigureParams.System.nDSPType == DSP_TYPE_EMU) { 360: DSP_Reset(); 361: } 362: #endif 363: } 364: /* Bit 6 - Internal Speaker control */ 365: if(PSGRegisters[PSG_REG_IO_PORTA]&(1<<6)) 366: { 367: /*Log_Printf(LOG_DEBUG, "Falcon: Internal Speaker state\n");*/ 368: /* FIXME: add code to handle? (if we want to emulate the speaker at all? */ 369: } 370: /* Bit 7 - Reset IDE? */ 371: if(PSGRegisters[PSG_REG_IO_PORTA]&(1<<7)) 372: { 373: Log_Printf(LOG_DEBUG, "Falcon: Reset IDE subsystem\n"); 374: /* FIXME: add code to handle IDE reset */ 375: } 376: } 1.1.1.9 root 377: 1.1.1.4 root 378: } 1.1 root 379: } 380: 1.1.1.2 root 381: 382: /*-----------------------------------------------------------------------*/ 1.1.1.7 root 383: /** 1.1.1.10 root 384: * Read byte from 0xff8800. Return current content of data register 1.1.1.7 root 385: */ 1.1.1.10 root 386: void PSG_ff8800_ReadByte(void) 1.1 root 387: { 1.1.1.10 root 388: M68000_WaitState(1); /* [NP] FIXME not 100% accurate, but gives good results */ 389: 390: IoMem[IoAccessCurrentAddress] = PSG_Get_DataRegister(); 391: 1.1.1.11 root 392: if (LOG_TRACE_LEVEL(TRACE_PSG_READ)) 393: { 394: int FrameCycles, HblCounterVideo, LineCycles; 395: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles ); 396: LOG_TRACE_PRINT("ym read data %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n", 397: IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress], 398: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles); 399: } 1.1.1.10 root 400: } 401: 402: 403: /*-----------------------------------------------------------------------*/ 404: /** 405: * Read byte from 0xff8801/02/03. Return 0xff. 406: */ 407: void PSG_ff880x_ReadByte(void) 408: { 409: M68000_WaitState(1); /* [NP] FIXME not 100% accurate, but gives good results */ 1.1.1.4 root 410: 1.1.1.8 root 411: IoMem[IoAccessCurrentAddress] = 0xff; 1.1.1.10 root 412: 1.1.1.11 root 413: if (LOG_TRACE_LEVEL(TRACE_PSG_READ)) 414: { 415: int FrameCycles, HblCounterVideo, LineCycles; 416: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles ); 417: LOG_TRACE_PRINT("ym read void %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n", 418: IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress], 419: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles); 420: } 1.1 root 421: } 1.1.1.7 root 422: 423: 424: 425: /*-----------------------------------------------------------------------*/ 426: /** 1.1.1.10 root 427: * Write byte to 0xff8800. Set content of YM's address register. 1.1.1.7 root 428: */ 1.1.1.10 root 429: void PSG_ff8800_WriteByte(void) 1.1.1.7 root 430: { 1.1.1.10 root 431: // M68000_WaitState(4); 432: M68000_WaitState(1); /* [NP] FIXME not 100% accurate, but gives good results */ 1.1.1.7 root 433: 1.1.1.11 root 434: if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE)) 435: { 436: int FrameCycles, HblCounterVideo, LineCycles; 437: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles ); 438: LOG_TRACE_PRINT("ym write %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n", 439: IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress], 440: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles); 441: } 1.1.1.10 root 442: 443: PSG_Set_SelectRegister ( IoMem[IoAccessCurrentAddress] ); 444: } 445: 446: 447: /*-----------------------------------------------------------------------*/ 448: /** 449: * Write byte to 0xff8801. Set content of YM's address register under conditions. 450: * Address 0xff8801 is a shadow version of 0xff8800, so both addresses can't be written 451: * at the same time by the same instruction. This means only a .B access or 452: * a movep will have a valid effect, other accesses are ignored. 453: */ 454: void PSG_ff8801_WriteByte(void) 455: { 456: if ( nIoMemAccessSize == SIZE_BYTE ) /* byte access or movep */ 457: { 458: M68000_WaitState(1); /* [NP] FIXME not 100% accurate, but gives good results */ 459: 1.1.1.11 root 460: if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE)) 461: { 462: int FrameCycles, HblCounterVideo, LineCycles; 463: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles ); 464: LOG_TRACE_PRINT("ym write %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n", 465: IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress], 466: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles); 467: } 1.1.1.10 root 468: 469: PSG_Set_SelectRegister ( IoMem[IoAccessCurrentAddress] ); 470: } 471: 472: else 473: { /* do nothing, just a trace if needed */ 1.1.1.11 root 474: if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE)) 475: { 476: int FrameCycles, HblCounterVideo, LineCycles; 477: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles ); 478: LOG_TRACE_PRINT("ym write ignored %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n", 479: IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress], 480: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles); 481: } 1.1.1.10 root 482: } 1.1.1.7 root 483: } 484: 485: 1.1.1.10 root 486: /*-----------------------------------------------------------------------*/ 487: /** 488: * Write byte to 0xff8802. Set content of YM's data register. 489: */ 490: void PSG_ff8802_WriteByte(void) 491: { 492: // M68000_WaitState(4); 493: M68000_WaitState(1); /* [NP] FIXME not 100% accurate, but gives good results */ 494: 1.1.1.11 root 495: if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE)) 496: { 497: int FrameCycles, HblCounterVideo, LineCycles; 498: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles ); 499: LOG_TRACE_PRINT("ym write %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n", 500: IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress], 501: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles); 502: } 1.1.1.10 root 503: 504: PSG_Set_DataRegister ( IoMem[IoAccessCurrentAddress] ); 505: } 506: 1.1.1.7 root 507: 508: /*-----------------------------------------------------------------------*/ 509: /** 1.1.1.10 root 510: * Write byte to 0xff8803. Set content of YM's data register under conditions. 511: * Address 0xff8803 is a shadow version of 0xff8802, so both addresses can't be written 512: * at the same time by the same instruction. This means only a .B access or 513: * a movep will have a valid effect, other accesses are ignored. 1.1.1.7 root 514: */ 1.1.1.10 root 515: void PSG_ff8803_WriteByte(void) 1.1.1.7 root 516: { 1.1.1.10 root 517: if ( nIoMemAccessSize == SIZE_BYTE ) /* byte access or movep */ 518: { 519: M68000_WaitState(1); /* [NP] FIXME not 100% accurate, but gives good results */ 520: 1.1.1.11 root 521: if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE)) 522: { 523: int FrameCycles, HblCounterVideo, LineCycles; 524: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles ); 525: LOG_TRACE_PRINT("ym write %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n", 526: IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress], 527: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles); 528: } 1.1.1.10 root 529: 530: PSG_Set_DataRegister ( IoMem[IoAccessCurrentAddress] ); 531: } 532: 533: else 534: { /* do nothing, just a trace if needed */ 1.1.1.11 root 535: if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE)) 536: { 537: int FrameCycles, HblCounterVideo, LineCycles; 538: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles ); 539: LOG_TRACE_PRINT("ym write ignored %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n", 540: IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress], 541: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles); 542: } 1.1.1.10 root 543: } 1.1.1.7 root 544: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.