|
|
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"
1.1.1.16 root 130: #include "fdc.h"
1.1.1.9 root 131:
1.1 root 132:
1.1.1.17! root 133: static Uint8 PSGRegisterSelect; /* Write to 0xff8800 sets the register number used in read/write accesses */
! 134: static Uint8 PSGRegisterReadData; /* Value returned when reading from 0xff8800 */
! 135: Uint8 PSGRegisters[MAX_PSG_REGISTERS]; /* Registers in PSG, see PSG_REG_xxxx */
1.1.1.4 root 136:
1.1.1.9 root 137: static unsigned int LastStrobe=0; /* Falling edge of Strobe used for printer */
1.1 root 138:
139:
140: /*-----------------------------------------------------------------------*/
1.1.1.7 root 141: /**
142: * Reset variables used in PSG
143: */
1.1 root 144: void PSG_Reset(void)
145: {
1.1.1.13 root 146: int i;
147:
1.1.1.14 root 148: if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE))
149: {
150: int FrameCycles, HblCounterVideo, LineCycles;
151: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
152: LOG_TRACE_PRINT("ym reset video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
153: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
154: }
155:
1.1.1.4 root 156: PSGRegisterSelect = 0;
1.1.1.13 root 157: PSGRegisterReadData = 0;
1.1.1.4 root 158: memset(PSGRegisters, 0, sizeof(PSGRegisters));
1.1.1.14 root 159: PSGRegisters[PSG_REG_IO_PORTA] = 0xff; /* no drive selected + side 0 after a reset */
160:
161: /* Update sound's emulation registers */
162: for ( i=0 ; i < NUM_PSG_SOUND_REGISTERS; i++ )
1.1.1.13 root 163: Sound_WriteReg ( i , 0 );
164:
1.1.1.9 root 165: LastStrobe=0;
1.1 root 166: }
167:
1.1.1.2 root 168:
169: /*-----------------------------------------------------------------------*/
1.1.1.7 root 170: /**
171: * Save/Restore snapshot of local variables ('MemorySnapShot_Store' handles type)
172: */
1.1.1.9 root 173: void PSG_MemorySnapShot_Capture(bool bSave)
1.1 root 174: {
1.1.1.4 root 175: /* Save/Restore details */
176: MemorySnapShot_Store(&PSGRegisterSelect, sizeof(PSGRegisterSelect));
1.1.1.13 root 177: MemorySnapShot_Store(&PSGRegisterReadData, sizeof(PSGRegisterReadData));
1.1.1.4 root 178: MemorySnapShot_Store(PSGRegisters, sizeof(PSGRegisters));
1.1.1.9 root 179: MemorySnapShot_Store(&LastStrobe, sizeof(LastStrobe));
1.1 root 180: }
181:
1.1.1.2 root 182:
183: /*-----------------------------------------------------------------------*/
1.1.1.7 root 184: /**
1.1.1.10 root 185: * Write byte to the YM address register (usually 0xff8800). This is used
186: * as a selector for when we read/write the YM data register (0xff8802).
1.1.1.7 root 187: */
1.1.1.10 root 188: void PSG_Set_SelectRegister(Uint8 val)
1.1 root 189: {
1.1.1.9 root 190: /* Store register used to read/write in $ff8802. This register */
191: /* is 8 bits on the YM2149, this means it should not be masked */
192: /* with 0xf. Instead, we keep the 8 bits, but we must ignore */
193: /* read/write to ff8802 when PSGRegisterSelect >= 16 */
1.1.1.10 root 194: PSGRegisterSelect = val;
1.1.1.7 root 195:
1.1.1.13 root 196: /* When address register is changed, a read from $ff8800 should */
197: /* return the masked value of the register. We set the value here */
198: /* to be returned in case PSG_Get_DataRegister is called */
199: PSGRegisterReadData = PSGRegisters[PSGRegisterSelect];
200:
1.1.1.11 root 201: if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE))
202: {
203: int FrameCycles, HblCounterVideo, LineCycles;
204: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
205: LOG_TRACE_PRINT("ym write reg=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
206: PSGRegisterSelect, FrameCycles, LineCycles, HblCounterVideo,
207: M68000_GetPC(), CurrentInstrCycles);
208: }
1.1 root 209: }
210:
1.1.1.2 root 211:
212: /*-----------------------------------------------------------------------*/
1.1.1.7 root 213: /**
1.1.1.10 root 214: * Read byte from 0xff8800, return PSG data
1.1.1.7 root 215: */
1.1.1.10 root 216: Uint8 PSG_Get_DataRegister(void)
1.1 root 217: {
1.1.1.9 root 218: /* Is a valid PSG register currently selected ? */
1.1.1.14 root 219: if ( PSGRegisterSelect >= MAX_PSG_REGISTERS )
1.1.1.10 root 220: return 0xff; /* not valid, return 0xff */
1.1.1.9 root 221:
1.1.1.14 root 222: if (PSGRegisterSelect == PSG_REG_IO_PORTA)
1.1.1.5 root 223: {
224: /* Second parallel port joystick uses centronics strobe bit as fire button: */
225: if (ConfigureParams.Joysticks.Joy[JOYID_PARPORT2].nJoystickMode != JOYSTICK_DISABLED)
226: {
227: if (Joy_GetStickData(JOYID_PARPORT2) & 0x80)
1.1.1.14 root 228: PSGRegisters[PSG_REG_IO_PORTA] &= ~32;
1.1.1.5 root 229: else
1.1.1.14 root 230: PSGRegisters[PSG_REG_IO_PORTA] |= 32;
1.1.1.5 root 231: }
232: }
1.1.1.14 root 233: else if (PSGRegisterSelect == PSG_REG_IO_PORTB)
1.1.1.5 root 234: {
235: /* PSG register 15 is parallel port data register - used by parallel port joysticks: */
236: if (ConfigureParams.Joysticks.Joy[JOYID_PARPORT1].nJoystickMode != JOYSTICK_DISABLED)
237: {
1.1.1.14 root 238: PSGRegisters[PSG_REG_IO_PORTB] &= 0x0f;
239: PSGRegisters[PSG_REG_IO_PORTB] |= ~Joy_GetStickData(JOYID_PARPORT1) << 4;
1.1.1.5 root 240: }
241: if (ConfigureParams.Joysticks.Joy[JOYID_PARPORT2].nJoystickMode != JOYSTICK_DISABLED)
242: {
1.1.1.14 root 243: PSGRegisters[PSG_REG_IO_PORTB] &= 0xf0;
244: PSGRegisters[PSG_REG_IO_PORTB] |= ~Joy_GetStickData(JOYID_PARPORT2) & 0x0f;
1.1.1.5 root 245: }
246: }
247:
1.1.1.4 root 248: /* Read data last selected by register */
1.1.1.13 root 249: return PSGRegisterReadData;
1.1 root 250: }
251:
1.1.1.2 root 252:
253: /*-----------------------------------------------------------------------*/
1.1.1.7 root 254: /**
1.1.1.10 root 255: * Write byte to YM's register (0xff8802), store according to PSG select register (0xff8800)
1.1.1.7 root 256: */
1.1.1.10 root 257: void PSG_Set_DataRegister(Uint8 val)
1.1.1.4 root 258: {
1.1.1.16 root 259: Uint8 val_old;
260:
1.1.1.11 root 261: if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE))
262: {
263: int FrameCycles, HblCounterVideo, LineCycles;
264: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
265: LOG_TRACE_PRINT("ym write data reg=0x%x val=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
266: PSGRegisterSelect, val, FrameCycles, LineCycles,
267: HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
268: }
1.1.1.9 root 269:
270: /* Is a valid PSG register currently selected ? */
1.1.1.14 root 271: if ( PSGRegisterSelect >= MAX_PSG_REGISTERS )
1.1.1.9 root 272: return; /* not valid, ignore write and do nothing */
273:
1.1.1.8 root 274: /* Create samples up until this point with current values */
1.1.1.12 root 275: Sound_Update(false);
1.1.1.8 root 276:
1.1.1.13 root 277: /* When a read is made from $ff8800 without changing PSGRegisterSelect, we should return */
278: /* the non masked value. */
279: PSGRegisterReadData = val; /* store non masked value for PSG_Get_DataRegister */
280:
1.1.1.16 root 281: /* Read previous content */
282: val_old = PSGRegisters[PSGRegisterSelect];
283:
1.1.1.10 root 284: /* Copy value to PSGRegisters[] */
285: PSGRegisters[PSGRegisterSelect] = val;
1.1.1.4 root 286:
1.1.1.9 root 287: /* Clear unused bits for some regs */
1.1.1.7 root 288: if ( ( PSGRegisterSelect == PSG_REG_CHANNEL_A_COARSE ) || ( PSGRegisterSelect == PSG_REG_CHANNEL_B_COARSE )
289: || ( PSGRegisterSelect == PSG_REG_CHANNEL_C_COARSE ) || ( PSGRegisterSelect == PSG_REG_ENV_SHAPE ) )
290: PSGRegisters[PSGRegisterSelect] &= 0x0f; /* only keep bits 0 - 3 */
291:
292: else if ( ( PSGRegisterSelect == PSG_REG_CHANNEL_A_AMP ) || ( PSGRegisterSelect == PSG_REG_CHANNEL_B_AMP )
1.1.1.9 root 293: || ( PSGRegisterSelect == PSG_REG_CHANNEL_C_AMP ) || ( PSGRegisterSelect == PSG_REG_NOISE_GENERATOR ) )
1.1.1.7 root 294: PSGRegisters[PSGRegisterSelect] &= 0x1f; /* only keep bits 0 - 4 */
295:
296:
1.1.1.9 root 297: if ( PSGRegisterSelect < NUM_PSG_SOUND_REGISTERS )
1.1.1.4 root 298: {
1.1.1.9 root 299: /* Copy sound related registers 0..13 to the sound module's internal buffer */
300: Sound_WriteReg ( PSGRegisterSelect , PSGRegisters[PSGRegisterSelect] );
301: }
1.1.1.4 root 302:
1.1.1.9 root 303: else if ( PSGRegisterSelect == PSG_REG_IO_PORTA )
304: {
305: /*
306: * FIXME: This is only a prelimary dirty hack!
307: * Port B (Printer port) - writing here needs to be dispatched to the printer
308: * STROBE (Port A bit5) does a short LOW and back to HIGH when the char is valid
309: * To print you need to write the character byte to IOB and you need to toggle STROBE
310: * (like EmuTOS does).
311: */
1.1.1.4 root 312: /* Printer dispatching only when printing is activated */
313: if (ConfigureParams.Printer.bEnablePrinting)
314: {
1.1.1.9 root 315: /* Bit 5 - Centronics strobe? If STROBE is low and the LastStrobe was high,
316: then print/transfer to the emulated Centronics port.
1.1.1.7 root 317: */
1.1.1.9 root 318: if (LastStrobe && ( (PSGRegisters[PSG_REG_IO_PORTA]&(1<<5)) == 0 ))
1.1.1.4 root 319: {
320: /* Seems like we want to print something... */
1.1.1.9 root 321: Printer_TransferByteTo(PSGRegisters[PSG_REG_IO_PORTB]);
322: /* Initiate a possible GPIP0 Printer BUSY interrupt */
1.1.1.15 root 323: MFP_InputOnChannel ( MFP_INT_GPIP0 , 0 );
1.1.1.9 root 324: /* Initiate a possible GPIP1 Falcon ACK interrupt */
325: if (ConfigureParams.System.nMachineType == MACHINE_FALCON)
1.1.1.15 root 326: MFP_InputOnChannel ( MFP_INT_GPIP1 , 0 );
1.1.1.4 root 327: }
328: }
1.1.1.9 root 329: LastStrobe = PSGRegisters[PSG_REG_IO_PORTA]&(1<<5);
330:
331: /* Bit 0-2 : side and drive select */
332: if ( (PSGRegisters[PSG_REG_IO_PORTA]&(1<<1)) == 0 )
333: {
334: /* floppy drive A is ON */
1.1.1.15 root 335: Statusbar_SetFloppyLed(DRIVE_LED_A, LED_STATE_ON);
1.1.1.9 root 336: }
337: else
338: {
1.1.1.15 root 339: Statusbar_SetFloppyLed(DRIVE_LED_A, LED_STATE_OFF);
1.1.1.9 root 340: }
341: if ( (PSGRegisters[PSG_REG_IO_PORTA]&(1<<2)) == 0 )
342: {
343: /* floppy drive B is ON */
1.1.1.15 root 344: Statusbar_SetFloppyLed(DRIVE_LED_B, LED_STATE_ON);
1.1.1.9 root 345: }
346: else
347: {
1.1.1.15 root 348: Statusbar_SetFloppyLed(DRIVE_LED_B, LED_STATE_OFF);
1.1.1.9 root 349: }
350:
1.1.1.16 root 351: /* Report a possible drive/side change */
352: FDC_SetDriveSide ( val_old & 7 , PSGRegisters[PSG_REG_IO_PORTA] & 7 );
353:
1.1.1.7 root 354: /* handle Falcon specific bits in PORTA of the PSG */
355: if (ConfigureParams.System.nMachineType == MACHINE_FALCON)
356: {
1.1.1.17! root 357: /* Bit 3 - centronics port SELIN line (pin 17) */
! 358: /*
! 359: if (PSGRegisters[PSG_REG_IO_PORTA] & (1 << 3))
! 360: {
! 361: // not emulated yet
! 362: }
! 363: */
! 364:
1.1.1.7 root 365: /* Bit 4 - DSP reset? */
366: if(PSGRegisters[PSG_REG_IO_PORTA]&(1<<4))
367: {
368: Log_Printf(LOG_DEBUG, "Calling DSP_Reset?\n");
369: #if ENABLE_DSP_EMU
370: if (ConfigureParams.System.nDSPType == DSP_TYPE_EMU) {
371: DSP_Reset();
372: }
373: #endif
374: }
375: /* Bit 6 - Internal Speaker control */
376: if(PSGRegisters[PSG_REG_IO_PORTA]&(1<<6))
377: {
378: /*Log_Printf(LOG_DEBUG, "Falcon: Internal Speaker state\n");*/
379: /* FIXME: add code to handle? (if we want to emulate the speaker at all? */
380: }
381: /* Bit 7 - Reset IDE? */
382: if(PSGRegisters[PSG_REG_IO_PORTA]&(1<<7))
383: {
384: Log_Printf(LOG_DEBUG, "Falcon: Reset IDE subsystem\n");
385: /* FIXME: add code to handle IDE reset */
386: }
387: }
1.1.1.16 root 388:
1.1.1.4 root 389: }
1.1 root 390: }
391:
1.1.1.2 root 392:
393: /*-----------------------------------------------------------------------*/
1.1.1.7 root 394: /**
1.1.1.10 root 395: * Read byte from 0xff8800. Return current content of data register
1.1.1.7 root 396: */
1.1.1.10 root 397: void PSG_ff8800_ReadByte(void)
1.1 root 398: {
1.1.1.10 root 399: M68000_WaitState(1); /* [NP] FIXME not 100% accurate, but gives good results */
400:
401: IoMem[IoAccessCurrentAddress] = PSG_Get_DataRegister();
402:
1.1.1.11 root 403: if (LOG_TRACE_LEVEL(TRACE_PSG_READ))
404: {
405: int FrameCycles, HblCounterVideo, LineCycles;
406: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
407: LOG_TRACE_PRINT("ym read data %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
408: IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress],
409: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
410: }
1.1.1.10 root 411: }
412:
413:
414: /*-----------------------------------------------------------------------*/
415: /**
416: * Read byte from 0xff8801/02/03. Return 0xff.
417: */
418: void PSG_ff880x_ReadByte(void)
419: {
420: M68000_WaitState(1); /* [NP] FIXME not 100% accurate, but gives good results */
1.1.1.4 root 421:
1.1.1.8 root 422: IoMem[IoAccessCurrentAddress] = 0xff;
1.1.1.10 root 423:
1.1.1.11 root 424: if (LOG_TRACE_LEVEL(TRACE_PSG_READ))
425: {
426: int FrameCycles, HblCounterVideo, LineCycles;
427: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
428: LOG_TRACE_PRINT("ym read void %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
429: IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress],
430: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
431: }
1.1 root 432: }
1.1.1.7 root 433:
434:
435:
436: /*-----------------------------------------------------------------------*/
437: /**
1.1.1.10 root 438: * Write byte to 0xff8800. Set content of YM's address register.
1.1.1.7 root 439: */
1.1.1.10 root 440: void PSG_ff8800_WriteByte(void)
1.1.1.7 root 441: {
1.1.1.10 root 442: // M68000_WaitState(4);
443: M68000_WaitState(1); /* [NP] FIXME not 100% accurate, but gives good results */
1.1.1.7 root 444:
1.1.1.11 root 445: if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE))
446: {
447: int FrameCycles, HblCounterVideo, LineCycles;
448: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
449: LOG_TRACE_PRINT("ym write %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
450: IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress],
451: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
452: }
1.1.1.10 root 453:
454: PSG_Set_SelectRegister ( IoMem[IoAccessCurrentAddress] );
455: }
456:
457:
458: /*-----------------------------------------------------------------------*/
459: /**
460: * Write byte to 0xff8801. Set content of YM's address register under conditions.
461: * Address 0xff8801 is a shadow version of 0xff8800, so both addresses can't be written
462: * at the same time by the same instruction. This means only a .B access or
463: * a movep will have a valid effect, other accesses are ignored.
464: */
465: void PSG_ff8801_WriteByte(void)
466: {
467: if ( nIoMemAccessSize == SIZE_BYTE ) /* byte access or movep */
468: {
469: M68000_WaitState(1); /* [NP] FIXME not 100% accurate, but gives good results */
470:
1.1.1.11 root 471: if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE))
472: {
473: int FrameCycles, HblCounterVideo, LineCycles;
474: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
475: LOG_TRACE_PRINT("ym write %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
476: IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress],
477: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
478: }
1.1.1.10 root 479:
480: PSG_Set_SelectRegister ( IoMem[IoAccessCurrentAddress] );
481: }
482:
483: else
484: { /* do nothing, just a trace if needed */
1.1.1.11 root 485: if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE))
486: {
487: int FrameCycles, HblCounterVideo, LineCycles;
488: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
489: LOG_TRACE_PRINT("ym write ignored %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
490: IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress],
491: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
492: }
1.1.1.10 root 493: }
1.1.1.7 root 494: }
495:
496:
1.1.1.10 root 497: /*-----------------------------------------------------------------------*/
498: /**
499: * Write byte to 0xff8802. Set content of YM's data register.
500: */
501: void PSG_ff8802_WriteByte(void)
502: {
503: // M68000_WaitState(4);
504: M68000_WaitState(1); /* [NP] FIXME not 100% accurate, but gives good results */
505:
1.1.1.11 root 506: if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE))
507: {
508: int FrameCycles, HblCounterVideo, LineCycles;
509: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
510: LOG_TRACE_PRINT("ym write %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
511: IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress],
512: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
513: }
1.1.1.10 root 514:
515: PSG_Set_DataRegister ( IoMem[IoAccessCurrentAddress] );
516: }
517:
1.1.1.7 root 518:
519: /*-----------------------------------------------------------------------*/
520: /**
1.1.1.10 root 521: * Write byte to 0xff8803. Set content of YM's data register under conditions.
522: * Address 0xff8803 is a shadow version of 0xff8802, so both addresses can't be written
523: * at the same time by the same instruction. This means only a .B access or
524: * a movep will have a valid effect, other accesses are ignored.
1.1.1.7 root 525: */
1.1.1.10 root 526: void PSG_ff8803_WriteByte(void)
1.1.1.7 root 527: {
1.1.1.10 root 528: if ( nIoMemAccessSize == SIZE_BYTE ) /* byte access or movep */
529: {
530: M68000_WaitState(1); /* [NP] FIXME not 100% accurate, but gives good results */
531:
1.1.1.11 root 532: if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE))
533: {
534: int FrameCycles, HblCounterVideo, LineCycles;
535: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
536: LOG_TRACE_PRINT("ym write %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
537: IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress],
538: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
539: }
1.1.1.10 root 540:
541: PSG_Set_DataRegister ( IoMem[IoAccessCurrentAddress] );
542: }
543:
544: else
545: { /* do nothing, just a trace if needed */
1.1.1.11 root 546: if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE))
547: {
548: int FrameCycles, HblCounterVideo, LineCycles;
549: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
550: LOG_TRACE_PRINT("ym write ignored %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
551: IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress],
552: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
553: }
1.1.1.10 root 554: }
1.1.1.7 root 555: }
1.1.1.16 root 556:
557:
558: /* ------------------------------------------------------------------
559: * YM-2149 register content dump (for debugger info command)
560: */
1.1.1.17! root 561: void PSG_Info(FILE *fp, Uint32 dummy)
1.1.1.16 root 562: {
563: int i;
564: for(i = 0; i < ARRAYSIZE(PSGRegisters); i++)
565: {
1.1.1.17! root 566: fprintf(fp, "Reg $%02X : $%02X\n", i, PSGRegisters[i]);
1.1.1.16 root 567: }
568: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.