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