|
|
1.1 root 1: /*
1.1.1.4 root 2: Hatari - psg.c
1.1 root 3:
1.1.1.4 root 4: This file is distributed under the GNU Public License, version 2 or at
5: 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 */
29: /* have sound.c independant of psg.c (to ease replacement of */
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.7 root 60:
61:
62: /* Emulating wait states when accessing $ff8800/01/02/03 with different 'move' variants */
63: /* is a complex task. So far, adding 1 cycle wait state to each access and rounding the */
64: /* final number to 4 gives some good results, but this is certainly not the way it's */
65: /* working for real in the ST. */
66: /* The following examples show some verified wait states for different accesses : */
67: /* lea $ffff8800,a1 */
68: /* lea $ffff8802,a2 */
69: /* lea $ffff8801,a3 */
70: /* */
71: /* movep.w d1,(a1) ; 20 16+4 (ventura loader) */
72: /* movep.l d1,(a1) ; 28 24+4 (ventura loader, ulm loader) */
73: /* */
74: /* movep.l d6,0(a5) ; 28 24+4 (SNY I, TCB) */
75: /* movep.w d5,0(a5) ; 20 16+4 (SNY I, TCB) */
76: /* */
77: /* move.b d1,(a1) ; 12 8+4 */
78: /* move.b d1,(a2) ; 12 8+4 */
79: /* move.b d1,(a3) ; 12 8+4 (crickey ulm hidden) */
80: /* */
81: /* move.w d1,(a1) ; 12 8+4 */
82: /* move.w d1,(a2) ; 12 8+4 */
83: /* move.l d1,(a1) ; 16 12+4 (ulm loader) */
84: /* */
85: /* movem.l d1,(a1) ; 20 16+4 */
86: /* movem.l d1-d2,(a1) ; 28 24+4 */
87: /* movem.l d1-d3,(a1) ; 40 32+4+4 */
88: /* movem.l d1-d4,(a1) ; 48 40+4+4 */
89: /* movem.l d1-d5,(a1) ; 60 48+4+4+4 */
90: /* movem.l d1-d6,(a1) ; 68 56+4+4+4 */
91: /* movem.l d1-d7,(a1) ; 80 64+4+4+4+4 */
92: /* movem.l d0-d7,(a1) ; 88 72+4+4+4+4 */
93: /* */
1.1.1.10! root 94: /* movep.w d0,(a3) (X-Out) */
! 95: /* */
1.1.1.7 root 96: /* This gives the following "model" : */
97: /* - each access to $ff8800 or $ff8802 add 1 cycle wait state */
1.1.1.10! root 98: /* - accesses to $ff8801 or $ff8803 are considered "valid" only if we don't access */
! 99: /* the corresponding "non shadow" addresses $ff8800/02 at the same time. */
! 100: /* This means only .B size (move.b for example) or movep opcode will work. */
! 101: /* If the access is valid, add 1 cycle wait state, else ignore the write and */
! 102: /* don't add any cycle. */
1.1.1.7 root 103:
104:
105:
1.1.1.9 root 106: const char PSG_rcsid[] = "Hatari $Id: psg.c,v 1.34 2008/11/03 20:46:05 thothy Exp $";
1.1 root 107:
108: #include "main.h"
1.1.1.3 root 109: #include "configuration.h"
1.1.1.4 root 110: #include "ioMem.h"
1.1.1.5 root 111: #include "joy.h"
1.1.1.7 root 112: #include "log.h"
1.1.1.4 root 113: #include "m68000.h"
1.1 root 114: #include "memorySnapShot.h"
115: #include "sound.h"
1.1.1.4 root 116: #include "printer.h" /* because Printer I/O goes through PSG Register 15 */
1.1.1.3 root 117: #include "psg.h"
1.1.1.7 root 118: #if ENABLE_DSP_EMU
119: #include "falcon/dsp.h"
120: #endif
121: #include "video.h"
1.1.1.9 root 122: #include "statusbar.h"
123: #include "mfp.h"
124:
1.1 root 125:
1.1.1.4 root 126: Uint8 PSGRegisterSelect; /* 0xff8800 (read/write) */
127: Uint8 PSGRegisters[16]; /* Register in PSG, see PSG_REG_xxxx */
128:
1.1.1.9 root 129: static unsigned int LastStrobe=0; /* Falling edge of Strobe used for printer */
1.1 root 130:
131:
132: /*-----------------------------------------------------------------------*/
1.1.1.7 root 133: /**
134: * Reset variables used in PSG
135: */
1.1 root 136: void PSG_Reset(void)
137: {
1.1.1.4 root 138: PSGRegisterSelect = 0;
139: memset(PSGRegisters, 0, sizeof(PSGRegisters));
1.1.1.9 root 140: LastStrobe=0;
1.1 root 141: }
142:
1.1.1.2 root 143:
144: /*-----------------------------------------------------------------------*/
1.1.1.7 root 145: /**
146: * Save/Restore snapshot of local variables ('MemorySnapShot_Store' handles type)
147: */
1.1.1.9 root 148: void PSG_MemorySnapShot_Capture(bool bSave)
1.1 root 149: {
1.1.1.4 root 150: /* Save/Restore details */
151: MemorySnapShot_Store(&PSGRegisterSelect, sizeof(PSGRegisterSelect));
152: MemorySnapShot_Store(PSGRegisters, sizeof(PSGRegisters));
1.1.1.9 root 153: MemorySnapShot_Store(&LastStrobe, sizeof(LastStrobe));
1.1 root 154: }
155:
1.1.1.2 root 156:
157: /*-----------------------------------------------------------------------*/
1.1.1.7 root 158: /**
1.1.1.10! root 159: * Write byte to the YM address register (usually 0xff8800). This is used
! 160: * as a selector for when we read/write the YM data register (0xff8802).
1.1.1.7 root 161: */
1.1.1.10! root 162: void PSG_Set_SelectRegister(Uint8 val)
1.1 root 163: {
1.1.1.9 root 164: /* Store register used to read/write in $ff8802. This register */
165: /* is 8 bits on the YM2149, this means it should not be masked */
166: /* with 0xf. Instead, we keep the 8 bits, but we must ignore */
167: /* read/write to ff8802 when PSGRegisterSelect >= 16 */
1.1.1.10! root 168: PSGRegisterSelect = val;
1.1.1.7 root 169:
1.1.1.10! root 170: if ( HATARI_TRACE_LEVEL ( HATARI_TRACE_PSG_WRITE ) )
1.1.1.7 root 171: {
172: int nFrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO);;
173: int nLineCycles = nFrameCycles % nCyclesPerLine;
1.1.1.10! root 174: HATARI_TRACE_PRINT ( "ym write reg=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n" ,
1.1.1.7 root 175: PSGRegisterSelect, nFrameCycles, nLineCycles, nHBL, M68000_GetPC(), CurrentInstrCycles );
176: }
1.1 root 177: }
178:
1.1.1.2 root 179:
180: /*-----------------------------------------------------------------------*/
1.1.1.7 root 181: /**
1.1.1.10! root 182: * Read byte from 0xff8800, return PSG data
1.1.1.7 root 183: */
1.1.1.10! root 184: Uint8 PSG_Get_DataRegister(void)
1.1 root 185: {
1.1.1.6 root 186: M68000_WaitState(4);
187:
1.1.1.9 root 188: /* Is a valid PSG register currently selected ? */
189: if ( PSGRegisterSelect >= 16 )
1.1.1.10! root 190: return 0xff; /* not valid, return 0xff */
1.1.1.9 root 191:
1.1.1.5 root 192: if (PSGRegisterSelect == 14)
193: {
194: /* Second parallel port joystick uses centronics strobe bit as fire button: */
195: if (ConfigureParams.Joysticks.Joy[JOYID_PARPORT2].nJoystickMode != JOYSTICK_DISABLED)
196: {
197: if (Joy_GetStickData(JOYID_PARPORT2) & 0x80)
198: PSGRegisters[14] &= ~32;
199: else
200: PSGRegisters[14] |= 32;
201: }
202: }
203: else if (PSGRegisterSelect == 15)
204: {
205: /* PSG register 15 is parallel port data register - used by parallel port joysticks: */
206: if (ConfigureParams.Joysticks.Joy[JOYID_PARPORT1].nJoystickMode != JOYSTICK_DISABLED)
207: {
208: PSGRegisters[15] &= 0x0f;
209: PSGRegisters[15] |= ~Joy_GetStickData(JOYID_PARPORT1) << 4;
210: }
211: if (ConfigureParams.Joysticks.Joy[JOYID_PARPORT2].nJoystickMode != JOYSTICK_DISABLED)
212: {
213: PSGRegisters[15] &= 0xf0;
214: PSGRegisters[15] |= ~Joy_GetStickData(JOYID_PARPORT2) & 0x0f;
215: }
216: }
217:
1.1.1.4 root 218: /* Read data last selected by register */
1.1.1.10! root 219: return PSGRegisters[PSGRegisterSelect];
1.1 root 220: }
221:
1.1.1.2 root 222:
223: /*-----------------------------------------------------------------------*/
1.1.1.7 root 224: /**
1.1.1.10! root 225: * Write byte to YM's register (0xff8802), store according to PSG select register (0xff8800)
1.1.1.7 root 226: */
1.1.1.10! root 227: void PSG_Set_DataRegister(Uint8 val)
1.1.1.4 root 228: {
1.1.1.10! root 229: if ( HATARI_TRACE_LEVEL ( HATARI_TRACE_PSG_WRITE ) )
1.1.1.9 root 230: {
231: int nFrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO);;
232: int nLineCycles = nFrameCycles % nCyclesPerLine;
1.1.1.10! root 233: HATARI_TRACE_PRINT ( "ym write data reg=0x%x val=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n" ,
! 234: PSGRegisterSelect, val, nFrameCycles, nLineCycles, nHBL, M68000_GetPC(), CurrentInstrCycles );
1.1.1.9 root 235: }
236:
237: /* Is a valid PSG register currently selected ? */
238: if ( PSGRegisterSelect >= 16 )
239: return; /* not valid, ignore write and do nothing */
240:
1.1.1.8 root 241: /* Create samples up until this point with current values */
242: Sound_Update();
243:
1.1.1.10! root 244: /* Copy value to PSGRegisters[] */
! 245: PSGRegisters[PSGRegisterSelect] = val;
1.1.1.4 root 246:
1.1.1.9 root 247: /* Clear unused bits for some regs */
1.1.1.7 root 248: if ( ( PSGRegisterSelect == PSG_REG_CHANNEL_A_COARSE ) || ( PSGRegisterSelect == PSG_REG_CHANNEL_B_COARSE )
249: || ( PSGRegisterSelect == PSG_REG_CHANNEL_C_COARSE ) || ( PSGRegisterSelect == PSG_REG_ENV_SHAPE ) )
250: PSGRegisters[PSGRegisterSelect] &= 0x0f; /* only keep bits 0 - 3 */
251:
252: else if ( ( PSGRegisterSelect == PSG_REG_CHANNEL_A_AMP ) || ( PSGRegisterSelect == PSG_REG_CHANNEL_B_AMP )
1.1.1.9 root 253: || ( PSGRegisterSelect == PSG_REG_CHANNEL_C_AMP ) || ( PSGRegisterSelect == PSG_REG_NOISE_GENERATOR ) )
1.1.1.7 root 254: PSGRegisters[PSGRegisterSelect] &= 0x1f; /* only keep bits 0 - 4 */
255:
256:
257:
1.1.1.9 root 258: if ( PSGRegisterSelect < NUM_PSG_SOUND_REGISTERS )
1.1.1.4 root 259: {
1.1.1.9 root 260: /* Copy sound related registers 0..13 to the sound module's internal buffer */
261: Sound_WriteReg ( PSGRegisterSelect , PSGRegisters[PSGRegisterSelect] );
262: }
1.1.1.4 root 263:
1.1.1.9 root 264: else if ( PSGRegisterSelect == PSG_REG_IO_PORTA )
265: {
266: /*
267: * FIXME: This is only a prelimary dirty hack!
268: * Port B (Printer port) - writing here needs to be dispatched to the printer
269: * STROBE (Port A bit5) does a short LOW and back to HIGH when the char is valid
270: * To print you need to write the character byte to IOB and you need to toggle STROBE
271: * (like EmuTOS does).
272: */
1.1.1.4 root 273: /* Printer dispatching only when printing is activated */
274: if (ConfigureParams.Printer.bEnablePrinting)
275: {
1.1.1.9 root 276: /* Bit 5 - Centronics strobe? If STROBE is low and the LastStrobe was high,
277: then print/transfer to the emulated Centronics port.
1.1.1.7 root 278: */
1.1.1.9 root 279: if (LastStrobe && ( (PSGRegisters[PSG_REG_IO_PORTA]&(1<<5)) == 0 ))
1.1.1.4 root 280: {
281: /* Seems like we want to print something... */
1.1.1.9 root 282: Printer_TransferByteTo(PSGRegisters[PSG_REG_IO_PORTB]);
283: /* Initiate a possible GPIP0 Printer BUSY interrupt */
284: MFP_InputOnChannel(MFP_GPIP_0_BIT,MFP_IERB,&MFP_IPRB);
285: /* Initiate a possible GPIP1 Falcon ACK interrupt */
286: if (ConfigureParams.System.nMachineType == MACHINE_FALCON)
287: MFP_InputOnChannel(MFP_GPIP_1_BIT,MFP_IERB,&MFP_IPRB);
1.1.1.4 root 288: }
289: }
1.1.1.9 root 290: LastStrobe = PSGRegisters[PSG_REG_IO_PORTA]&(1<<5);
291:
292: /* Bit 0-2 : side and drive select */
293: if ( (PSGRegisters[PSG_REG_IO_PORTA]&(1<<1)) == 0 )
294: {
295: /* floppy drive A is ON */
296: Statusbar_SetFloppyLed(DRIVE_LED_A, TRUE);
297: }
298: else
299: {
300: Statusbar_SetFloppyLed(DRIVE_LED_A, FALSE);
301: }
302: if ( (PSGRegisters[PSG_REG_IO_PORTA]&(1<<2)) == 0 )
303: {
304: /* floppy drive B is ON */
305: Statusbar_SetFloppyLed(DRIVE_LED_B, TRUE);
306: }
307: else
308: {
309: Statusbar_SetFloppyLed(DRIVE_LED_B, FALSE);
310: }
311:
1.1.1.7 root 312: /* Bit 3 - Centronics as input */
313: if(PSGRegisters[PSG_REG_IO_PORTA]&(1<<3))
314: {
315: /* FIXME: might be needed if we want to emulate sound sampling hardware */
316: }
317:
318: /* handle Falcon specific bits in PORTA of the PSG */
319: if (ConfigureParams.System.nMachineType == MACHINE_FALCON)
320: {
321: /* Bit 4 - DSP reset? */
322: if(PSGRegisters[PSG_REG_IO_PORTA]&(1<<4))
323: {
324: Log_Printf(LOG_DEBUG, "Calling DSP_Reset?\n");
325: #if ENABLE_DSP_EMU
326: if (ConfigureParams.System.nDSPType == DSP_TYPE_EMU) {
327: DSP_Reset();
328: }
329: #endif
330: }
331: /* Bit 6 - Internal Speaker control */
332: if(PSGRegisters[PSG_REG_IO_PORTA]&(1<<6))
333: {
334: /*Log_Printf(LOG_DEBUG, "Falcon: Internal Speaker state\n");*/
335: /* FIXME: add code to handle? (if we want to emulate the speaker at all? */
336: }
337: /* Bit 7 - Reset IDE? */
338: if(PSGRegisters[PSG_REG_IO_PORTA]&(1<<7))
339: {
340: Log_Printf(LOG_DEBUG, "Falcon: Reset IDE subsystem\n");
341: /* FIXME: add code to handle IDE reset */
342: }
343: }
1.1.1.9 root 344:
1.1.1.4 root 345: }
1.1 root 346: }
347:
1.1.1.2 root 348:
349: /*-----------------------------------------------------------------------*/
1.1.1.7 root 350: /**
1.1.1.10! root 351: * Read byte from 0xff8800. Return current content of data register
1.1.1.7 root 352: */
1.1.1.10! root 353: void PSG_ff8800_ReadByte(void)
1.1 root 354: {
1.1.1.10! root 355: M68000_WaitState(1); /* [NP] FIXME not 100% accurate, but gives good results */
! 356:
! 357: IoMem[IoAccessCurrentAddress] = PSG_Get_DataRegister();
! 358:
! 359: if ( HATARI_TRACE_LEVEL ( HATARI_TRACE_PSG_READ ) )
! 360: {
! 361: int nFrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO);;
! 362: int nLineCycles = nFrameCycles % nCyclesPerLine;
! 363: HATARI_TRACE_PRINT ( "ym read data %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n" ,
! 364: IoAccessCurrentAddress , IoMem[IoAccessCurrentAddress] ,
! 365: nFrameCycles, nLineCycles, nHBL, M68000_GetPC(), CurrentInstrCycles );
! 366: }
! 367: }
! 368:
! 369:
! 370: /*-----------------------------------------------------------------------*/
! 371: /**
! 372: * Read byte from 0xff8801/02/03. Return 0xff.
! 373: */
! 374: void PSG_ff880x_ReadByte(void)
! 375: {
! 376: M68000_WaitState(1); /* [NP] FIXME not 100% accurate, but gives good results */
1.1.1.4 root 377:
1.1.1.8 root 378: IoMem[IoAccessCurrentAddress] = 0xff;
1.1.1.10! root 379:
! 380: if ( HATARI_TRACE_LEVEL ( HATARI_TRACE_PSG_READ ) )
! 381: {
! 382: int nFrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO);;
! 383: int nLineCycles = nFrameCycles % nCyclesPerLine;
! 384: HATARI_TRACE_PRINT ( "ym read void %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n" ,
! 385: IoAccessCurrentAddress , IoMem[IoAccessCurrentAddress] ,
! 386: nFrameCycles, nLineCycles, nHBL, M68000_GetPC(), CurrentInstrCycles );
! 387: }
1.1 root 388: }
1.1.1.7 root 389:
390:
391:
392: /*-----------------------------------------------------------------------*/
393: /**
1.1.1.10! root 394: * Write byte to 0xff8800. Set content of YM's address register.
1.1.1.7 root 395: */
1.1.1.10! root 396: void PSG_ff8800_WriteByte(void)
1.1.1.7 root 397: {
1.1.1.10! root 398: // M68000_WaitState(4);
! 399: M68000_WaitState(1); /* [NP] FIXME not 100% accurate, but gives good results */
1.1.1.7 root 400:
1.1.1.10! root 401: if ( HATARI_TRACE_LEVEL ( HATARI_TRACE_PSG_WRITE ) )
1.1.1.7 root 402: {
403: int nFrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO);;
404: int nLineCycles = nFrameCycles % nCyclesPerLine;
1.1.1.10! root 405: HATARI_TRACE_PRINT ( "ym write %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n" ,
! 406: IoAccessCurrentAddress , IoMem[IoAccessCurrentAddress] ,
1.1.1.7 root 407: nFrameCycles, nLineCycles, nHBL, M68000_GetPC(), CurrentInstrCycles );
408: }
1.1.1.10! root 409:
! 410: PSG_Set_SelectRegister ( IoMem[IoAccessCurrentAddress] );
! 411: }
! 412:
! 413:
! 414: /*-----------------------------------------------------------------------*/
! 415: /**
! 416: * Write byte to 0xff8801. Set content of YM's address register under conditions.
! 417: * Address 0xff8801 is a shadow version of 0xff8800, so both addresses can't be written
! 418: * at the same time by the same instruction. This means only a .B access or
! 419: * a movep will have a valid effect, other accesses are ignored.
! 420: */
! 421: void PSG_ff8801_WriteByte(void)
! 422: {
! 423: if ( nIoMemAccessSize == SIZE_BYTE ) /* byte access or movep */
! 424: {
! 425: M68000_WaitState(1); /* [NP] FIXME not 100% accurate, but gives good results */
! 426:
! 427: if ( HATARI_TRACE_LEVEL ( HATARI_TRACE_PSG_WRITE ) )
! 428: {
! 429: int nFrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO);;
! 430: int nLineCycles = nFrameCycles % nCyclesPerLine;
! 431: HATARI_TRACE_PRINT ( "ym write %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n" ,
! 432: IoAccessCurrentAddress , IoMem[IoAccessCurrentAddress] ,
! 433: nFrameCycles, nLineCycles, nHBL, M68000_GetPC(), CurrentInstrCycles );
! 434: }
! 435:
! 436: PSG_Set_SelectRegister ( IoMem[IoAccessCurrentAddress] );
! 437: }
! 438:
! 439: else
! 440: { /* do nothing, just a trace if needed */
! 441: if ( HATARI_TRACE_LEVEL ( HATARI_TRACE_PSG_WRITE ) )
! 442: {
! 443: int nFrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO);;
! 444: int nLineCycles = nFrameCycles % nCyclesPerLine;
! 445: HATARI_TRACE_PRINT ( "ym write ignored %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n" ,
! 446: IoAccessCurrentAddress , IoMem[IoAccessCurrentAddress] ,
! 447: nFrameCycles, nLineCycles, nHBL, M68000_GetPC(), CurrentInstrCycles );
! 448: }
! 449: }
1.1.1.7 root 450: }
451:
452:
1.1.1.10! root 453: /*-----------------------------------------------------------------------*/
! 454: /**
! 455: * Write byte to 0xff8802. Set content of YM's data register.
! 456: */
! 457: void PSG_ff8802_WriteByte(void)
! 458: {
! 459: // M68000_WaitState(4);
! 460: M68000_WaitState(1); /* [NP] FIXME not 100% accurate, but gives good results */
! 461:
! 462: if ( HATARI_TRACE_LEVEL ( HATARI_TRACE_PSG_WRITE ) )
! 463: {
! 464: int nFrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO);;
! 465: int nLineCycles = nFrameCycles % nCyclesPerLine;
! 466: HATARI_TRACE_PRINT ( "ym write %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n" ,
! 467: IoAccessCurrentAddress , IoMem[IoAccessCurrentAddress] ,
! 468: nFrameCycles, nLineCycles, nHBL, M68000_GetPC(), CurrentInstrCycles );
! 469: }
! 470:
! 471: PSG_Set_DataRegister ( IoMem[IoAccessCurrentAddress] );
! 472: }
! 473:
1.1.1.7 root 474:
475: /*-----------------------------------------------------------------------*/
476: /**
1.1.1.10! root 477: * Write byte to 0xff8803. Set content of YM's data register under conditions.
! 478: * Address 0xff8803 is a shadow version of 0xff8802, so both addresses can't be written
! 479: * at the same time by the same instruction. This means only a .B access or
! 480: * a movep will have a valid effect, other accesses are ignored.
1.1.1.7 root 481: */
1.1.1.10! root 482: void PSG_ff8803_WriteByte(void)
1.1.1.7 root 483: {
1.1.1.10! root 484: if ( nIoMemAccessSize == SIZE_BYTE ) /* byte access or movep */
! 485: {
! 486: M68000_WaitState(1); /* [NP] FIXME not 100% accurate, but gives good results */
! 487:
! 488: if ( HATARI_TRACE_LEVEL ( HATARI_TRACE_PSG_WRITE ) )
! 489: {
! 490: int nFrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO);;
! 491: int nLineCycles = nFrameCycles % nCyclesPerLine;
! 492: HATARI_TRACE_PRINT ( "ym write %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n" ,
! 493: IoAccessCurrentAddress , IoMem[IoAccessCurrentAddress] ,
! 494: nFrameCycles, nLineCycles, nHBL, M68000_GetPC(), CurrentInstrCycles );
! 495: }
! 496:
! 497: PSG_Set_DataRegister ( IoMem[IoAccessCurrentAddress] );
! 498: }
! 499:
! 500: else
! 501: { /* do nothing, just a trace if needed */
! 502: if ( HATARI_TRACE_LEVEL ( HATARI_TRACE_PSG_WRITE ) )
! 503: {
! 504: int nFrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO);;
! 505: int nLineCycles = nFrameCycles % nCyclesPerLine;
! 506: HATARI_TRACE_PRINT ( "ym write ignored %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n" ,
! 507: IoAccessCurrentAddress , IoMem[IoAccessCurrentAddress] ,
! 508: nFrameCycles, nLineCycles, nHBL, M68000_GetPC(), CurrentInstrCycles );
! 509: }
! 510: }
1.1.1.7 root 511: }
512:
1.1.1.10! root 513:
! 514:
! 515:
! 516:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.