1 /*
2   Hatari - psg.c
3 
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.
6 
7   Programmable Sound Generator (YM-2149) - PSG
8 
9   Also used for the printer (centronics) port emulation (PSG Port B, Register 15)
10 */
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).		*/
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 independent 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).					*/
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.	*/
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).				*/
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 /*											*/
99 /*	movep.w	d0,(a3)				(X-Out)					*/
100 /*											*/
101 /* This gives the following "model" :							*/
102 /*	- each access to $ff8800 or $ff8802 add 1 cycle wait state			*/
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.								*/
108 
109 
110 
111 const char PSG_fileid[] = "Hatari psg.c : " __DATE__ " " __TIME__;
112 
113 #include "main.h"
114 #include "configuration.h"
115 #include "ioMem.h"
116 #include "joy.h"
117 #include "log.h"
118 #include "m68000.h"
119 #include "memorySnapShot.h"
120 #include "sound.h"
121 #include "printer.h"            /* because Printer I/O goes through PSG Register 15 */
122 #include "psg.h"
123 #if ENABLE_DSP_EMU
124 #include "falcon/dsp.h"
125 #endif
126 #include "screen.h"
127 #include "video.h"
128 #include "statusbar.h"
129 #include "mfp.h"
130 #include "fdc.h"
131 
132 
133 Uint8 PSGRegisterSelect;        /* Write to 0xff8800 sets the register number used in read/write accesses */
134 Uint8 PSGRegisterReadData;	/* Value returned when reading from 0xff8800 */
135 Uint8 PSGRegisters[MAX_PSG_REGISTERS]; /* Registers in PSG, see PSG_REG_xxxx */
136 
137 static unsigned int LastStrobe=0; /* Falling edge of Strobe used for printer */
138 
139 
140 /*-----------------------------------------------------------------------*/
141 /**
142  * Reset variables used in PSG
143  */
PSG_Reset(void)144 void PSG_Reset(void)
145 {
146         int     i;
147 
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 
156 	PSGRegisterSelect = 0;
157 	PSGRegisterReadData = 0;
158 	memset(PSGRegisters, 0, sizeof(PSGRegisters));
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++ )
163 		Sound_WriteReg ( i , 0 );
164 
165 	LastStrobe=0;
166 }
167 
168 
169 /*-----------------------------------------------------------------------*/
170 /**
171  * Save/Restore snapshot of local variables ('MemorySnapShot_Store' handles type)
172  */
PSG_MemorySnapShot_Capture(bool bSave)173 void PSG_MemorySnapShot_Capture(bool bSave)
174 {
175 	/* Save/Restore details */
176 	MemorySnapShot_Store(&PSGRegisterSelect, sizeof(PSGRegisterSelect));
177 	MemorySnapShot_Store(&PSGRegisterReadData, sizeof(PSGRegisterReadData));
178 	MemorySnapShot_Store(PSGRegisters, sizeof(PSGRegisters));
179 	MemorySnapShot_Store(&LastStrobe, sizeof(LastStrobe));
180 }
181 
182 
183 /*-----------------------------------------------------------------------*/
184 /**
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).
187  */
PSG_Set_SelectRegister(Uint8 val)188 void PSG_Set_SelectRegister(Uint8 val)
189 {
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 */
194 	PSGRegisterSelect = val;
195 
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 
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 	}
209 }
210 
211 
212 /*-----------------------------------------------------------------------*/
213 /**
214  * Read byte from 0xff8800, return PSG data
215  */
PSG_Get_DataRegister(void)216 Uint8 PSG_Get_DataRegister(void)
217 {
218 	/* Is a valid PSG register currently selected ? */
219 	if ( PSGRegisterSelect >= MAX_PSG_REGISTERS )
220 		return 0xff;				/* not valid, return 0xff */
221 
222 	if (PSGRegisterSelect == PSG_REG_IO_PORTA)
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)
228 				PSGRegisters[PSG_REG_IO_PORTA] &= ~32;
229 			else
230 				PSGRegisters[PSG_REG_IO_PORTA] |= 32;
231 		}
232 	}
233 	else if (PSGRegisterSelect == PSG_REG_IO_PORTB)
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 		{
238 			PSGRegisters[PSG_REG_IO_PORTB] &= 0x0f;
239 			PSGRegisters[PSG_REG_IO_PORTB] |= ~Joy_GetStickData(JOYID_PARPORT1) << 4;
240 		}
241 		if (ConfigureParams.Joysticks.Joy[JOYID_PARPORT2].nJoystickMode != JOYSTICK_DISABLED)
242 		{
243 			PSGRegisters[PSG_REG_IO_PORTB] &= 0xf0;
244 			PSGRegisters[PSG_REG_IO_PORTB] |= ~Joy_GetStickData(JOYID_PARPORT2) & 0x0f;
245 		}
246 	}
247 
248 	/* Read data last selected by register */
249 	return PSGRegisterReadData;
250 }
251 
252 
253 /*-----------------------------------------------------------------------*/
254 /**
255  * Write byte to YM's register (0xff8802), store according to PSG select register (0xff8800)
256  */
PSG_Set_DataRegister(Uint8 val)257 void PSG_Set_DataRegister(Uint8 val)
258 {
259 	Uint8	val_old;
260 
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 	}
269 
270 	/* Is a valid PSG register currently selected ? */
271 	if ( PSGRegisterSelect >= MAX_PSG_REGISTERS )
272 		return;					/* not valid, ignore write and do nothing */
273 
274 	/* Create samples up until this point with current values */
275 	Sound_Update(false);
276 
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 
281 	/* Read previous content */
282 	val_old = PSGRegisters[PSGRegisterSelect];
283 
284 	/* Copy value to PSGRegisters[] */
285 	PSGRegisters[PSGRegisterSelect] = val;
286 
287 	/* Clear unused bits for some regs */
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 )
293 		|| ( PSGRegisterSelect == PSG_REG_CHANNEL_C_AMP ) || ( PSGRegisterSelect == PSG_REG_NOISE_GENERATOR ) )
294 	  PSGRegisters[PSGRegisterSelect] &= 0x1f;	/* only keep bits 0 - 4 */
295 
296 
297 	if ( PSGRegisterSelect < NUM_PSG_SOUND_REGISTERS )
298 	{
299 		/* Copy sound related registers 0..13 to the sound module's internal buffer */
300 		Sound_WriteReg ( PSGRegisterSelect , PSGRegisters[PSGRegisterSelect] );
301 	}
302 
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 	 */
312 		/* Printer dispatching only when printing is activated */
313 		if (ConfigureParams.Printer.bEnablePrinting)
314 		{
315 			/* Bit 5 - Centronics strobe? If STROBE is low and the LastStrobe was high,
316 					then print/transfer to the emulated Centronics port.
317 			 */
318 			if (LastStrobe && ( (PSGRegisters[PSG_REG_IO_PORTA]&(1<<5)) == 0 ))
319 			{
320 				/* Seems like we want to print something... */
321 				Printer_TransferByteTo(PSGRegisters[PSG_REG_IO_PORTB]);
322 				/* Initiate a possible GPIP0 Printer BUSY interrupt */
323 				MFP_InputOnChannel ( MFP_INT_GPIP0 , 0 );
324 				/* Initiate a possible GPIP1 Falcon ACK interrupt */
325 				if (ConfigureParams.System.nMachineType == MACHINE_FALCON)
326 					MFP_InputOnChannel ( MFP_INT_GPIP1 , 0 );
327 			}
328 		}
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 */
335 			Statusbar_SetFloppyLed(DRIVE_LED_A, LED_STATE_ON);
336 		}
337 		else
338 		{
339 			Statusbar_SetFloppyLed(DRIVE_LED_A, LED_STATE_OFF);
340 		}
341 		if ( (PSGRegisters[PSG_REG_IO_PORTA]&(1<<2)) == 0 )
342 		{
343 			/* floppy drive B is ON */
344 			Statusbar_SetFloppyLed(DRIVE_LED_B, LED_STATE_ON);
345 		}
346 		else
347 		{
348 			Statusbar_SetFloppyLed(DRIVE_LED_B, LED_STATE_OFF);
349 		}
350 
351 		/* Report a possible drive/side change */
352 		FDC_SetDriveSide ( val_old & 7 , PSGRegisters[PSG_REG_IO_PORTA] & 7 );
353 
354 		/* Bit 3 - Centronics as input */
355 		if(PSGRegisters[PSG_REG_IO_PORTA]&(1<<3))
356 		{
357 			/* FIXME: might be needed if we want to emulate sound sampling hardware */
358 		}
359 
360 		/* handle Falcon specific bits in PORTA of the PSG */
361 		if (ConfigureParams.System.nMachineType == MACHINE_FALCON)
362 		{
363 			/* Bit 4 - DSP reset? */
364 			if(PSGRegisters[PSG_REG_IO_PORTA]&(1<<4))
365 			{
366 				Log_Printf(LOG_DEBUG, "Calling DSP_Reset?\n");
367 #if ENABLE_DSP_EMU
368 				if (ConfigureParams.System.nDSPType == DSP_TYPE_EMU) {
369 					DSP_Reset();
370 				}
371 #endif
372 			}
373 			/* Bit 6 - Internal Speaker control */
374 			if(PSGRegisters[PSG_REG_IO_PORTA]&(1<<6))
375 			{
376 				/*Log_Printf(LOG_DEBUG, "Falcon: Internal Speaker state\n");*/
377 				/* FIXME: add code to handle? (if we want to emulate the speaker at all? */
378 			}
379 			/* Bit 7 - Reset IDE? */
380 			if(PSGRegisters[PSG_REG_IO_PORTA]&(1<<7))
381 			{
382 				Log_Printf(LOG_DEBUG, "Falcon: Reset IDE subsystem\n");
383 				/* FIXME: add code to handle IDE reset */
384 			}
385 		}
386 
387 	}
388 }
389 
390 
391 /*-----------------------------------------------------------------------*/
392 /**
393  * Read byte from 0xff8800. Return current content of data register
394  */
PSG_ff8800_ReadByte(void)395 void PSG_ff8800_ReadByte(void)
396 {
397 	M68000_WaitState(1);				/* [NP] FIXME not 100% accurate, but gives good results */
398 
399 	IoMem[IoAccessCurrentAddress] = PSG_Get_DataRegister();
400 
401 	if (LOG_TRACE_LEVEL(TRACE_PSG_READ))
402 	{
403 		int FrameCycles, HblCounterVideo, LineCycles;
404 		Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
405 		LOG_TRACE_PRINT("ym read data %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
406 		                IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress],
407 		                FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
408 	}
409 }
410 
411 
412 /*-----------------------------------------------------------------------*/
413 /**
414  * Read byte from 0xff8801/02/03. Return 0xff.
415  */
PSG_ff880x_ReadByte(void)416 void PSG_ff880x_ReadByte(void)
417 {
418 	M68000_WaitState(1);				/* [NP] FIXME not 100% accurate, but gives good results */
419 
420 	IoMem[IoAccessCurrentAddress] = 0xff;
421 
422 	if (LOG_TRACE_LEVEL(TRACE_PSG_READ))
423 	{
424 		int FrameCycles, HblCounterVideo, LineCycles;
425 		Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
426 		LOG_TRACE_PRINT("ym read void %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
427 		                IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress],
428 		                FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
429 	}
430 }
431 
432 
433 
434 /*-----------------------------------------------------------------------*/
435 /**
436  * Write byte to 0xff8800. Set content of YM's address register.
437  */
PSG_ff8800_WriteByte(void)438 void PSG_ff8800_WriteByte(void)
439 {
440 //	M68000_WaitState(4);
441 	M68000_WaitState(1);				/* [NP] FIXME not 100% accurate, but gives good results */
442 
443 	if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE))
444 	{
445 		int FrameCycles, HblCounterVideo, LineCycles;
446 		Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
447 		LOG_TRACE_PRINT("ym write %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
448 		                IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress],
449 		                FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
450 	}
451 
452 	PSG_Set_SelectRegister ( IoMem[IoAccessCurrentAddress] );
453 }
454 
455 
456 /*-----------------------------------------------------------------------*/
457 /**
458  * Write byte to 0xff8801. Set content of YM's address register under conditions.
459  * Address 0xff8801 is a shadow version of 0xff8800, so both addresses can't be written
460  * at the same time by the same instruction. This means only a .B access or
461  * a movep will have a valid effect, other accesses are ignored.
462  */
PSG_ff8801_WriteByte(void)463 void PSG_ff8801_WriteByte(void)
464 {
465 	if ( nIoMemAccessSize == SIZE_BYTE )		/* byte access or movep */
466 	{
467 		M68000_WaitState(1);			/* [NP] FIXME not 100% accurate, but gives good results */
468 
469 		if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE))
470 		{
471 			int FrameCycles, HblCounterVideo, LineCycles;
472 			Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
473 			LOG_TRACE_PRINT("ym write %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
474 					IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress],
475 					FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
476 		}
477 
478 		PSG_Set_SelectRegister ( IoMem[IoAccessCurrentAddress] );
479 	}
480 
481 	else
482 	{						/* do nothing, just a trace if needed */
483 		if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE))
484 		{
485 			int FrameCycles, HblCounterVideo, LineCycles;
486 			Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
487 			LOG_TRACE_PRINT("ym write ignored %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
488 					IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress],
489 					FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
490 		}
491 	}
492 }
493 
494 
495 /*-----------------------------------------------------------------------*/
496 /**
497  * Write byte to 0xff8802. Set content of YM's data register.
498  */
PSG_ff8802_WriteByte(void)499 void PSG_ff8802_WriteByte(void)
500 {
501 //	M68000_WaitState(4);
502 	M68000_WaitState(1);				/* [NP] FIXME not 100% accurate, but gives good results */
503 
504 	if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE))
505 	{
506 		int FrameCycles, HblCounterVideo, LineCycles;
507 		Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
508 		LOG_TRACE_PRINT("ym write %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
509 				IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress],
510 				FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
511 	}
512 
513 	PSG_Set_DataRegister ( IoMem[IoAccessCurrentAddress] );
514 }
515 
516 
517 /*-----------------------------------------------------------------------*/
518 /**
519  * Write byte to 0xff8803. Set content of YM's data register under conditions.
520  * Address 0xff8803 is a shadow version of 0xff8802, so both addresses can't be written
521  * at the same time by the same instruction. This means only a .B access or
522  * a movep will have a valid effect, other accesses are ignored.
523  */
PSG_ff8803_WriteByte(void)524 void PSG_ff8803_WriteByte(void)
525 {
526 	if ( nIoMemAccessSize == SIZE_BYTE )		/* byte access or movep */
527 	{
528 		M68000_WaitState(1);			/* [NP] FIXME not 100% accurate, but gives good results */
529 
530 		if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE))
531 		{
532 			int FrameCycles, HblCounterVideo, LineCycles;
533 			Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
534 			LOG_TRACE_PRINT("ym write %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
535 					IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress],
536 					FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
537 		}
538 
539 		PSG_Set_DataRegister ( IoMem[IoAccessCurrentAddress] );
540 	}
541 
542 	else
543 	{						/* do nothing, just a trace if needed */
544 		if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE))
545 		{
546 			int FrameCycles, HblCounterVideo, LineCycles;
547 			Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
548 			LOG_TRACE_PRINT("ym write ignored %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
549 					IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress],
550 					FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
551 		}
552 	}
553 }
554 
555 
556 /* ------------------------------------------------------------------
557  * YM-2149 register content dump (for debugger info command)
558  */
PSG_Info(Uint32 dummy)559 void PSG_Info(Uint32 dummy)
560 {
561 	int i;
562 	for(i = 0; i < ARRAYSIZE(PSGRegisters); i++)
563 	{
564 		fprintf(stderr, "Reg $%02X : $%02X\n", i, PSGRegisters[i]);
565 	}
566 }
567