Annotation of hatari/src/psg.c, revision 1.1.1.18

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.18! root       65: /* 2015/10/15  [NP]    Better handling of the wait states when accessing YM2149 regs.  */
        !            66: /*                     Replace M68000_WaitState(1) by PSG_WaitState() which adds       */
        !            67: /*                     4 cycles every 4th access. Previous method worked because all   */
        !            68: /*                     cycles were rounded to 4, but it was not how real HW works and  */
        !            69: /*                     would not work in cycle exact mode where cycles are not rounded.*/
1.1.1.7   root       70: 
                     71: 
                     72: /* Emulating wait states when accessing $ff8800/01/02/03 with different 'move' variants        */
                     73: /* is a complex task. So far, adding 1 cycle wait state to each access and rounding the        */
1.1.1.18! root       74: /* final number to 4 gave some good results, but this is certainly not the way it's    */
1.1.1.7   root       75: /* working for real in the ST.                                                         */
1.1.1.18! root       76: /* Also in Hatari it only works when the cpu rounds all cycles to the next multiple    */
        !            77: /* of 4, but it will not work when running in cycle exact mode. This means we must     */
        !            78: /* add 4 cycles at a time, but not on every register access, see below.                */
        !            79: /*                                                                                     */
1.1.1.7   root       80: /* The following examples show some verified wait states for different accesses :      */
                     81: /*     lea     $ffff8800,a1                                                            */
                     82: /*     lea     $ffff8802,a2                                                            */
                     83: /*     lea     $ffff8801,a3                                                            */
                     84: /*                                                                                     */
                     85: /*     movep.w d1,(a1)         ; 20 16+4       (ventura loader)                        */
                     86: /*     movep.l d1,(a1)         ; 28 24+4       (ventura loader, ulm loader)            */
                     87: /*                                                                                     */
                     88: /*     movep.l d6,0(a5)        ; 28 24+4       (SNY I, TCB)                            */
                     89: /*     movep.w d5,0(a5)        ; 20 16+4       (SNY I, TCB)                            */
                     90: /*                                                                                     */
                     91: /*     move.b d1,(a1)          ; 12 8+4                                                */
                     92: /*     move.b d1,(a2)          ; 12 8+4                                                */
                     93: /*     move.b d1,(a3)          ; 12 8+4        (crickey ulm hidden)                    */
                     94: /*                                                                                     */
                     95: /*     move.w d1,(a1)          ; 12 8+4                                                */
                     96: /*     move.w d1,(a2)          ; 12 8+4                                                */
                     97: /*     move.l d1,(a1)          ; 16 12+4       (ulm loader)                            */
                     98: /*                                                                                     */
                     99: /*     movem.l d1,(a1)         ; 20 16+4                                               */
                    100: /*     movem.l d1-d2,(a1)      ; 28 24+4                                               */
                    101: /*     movem.l d1-d3,(a1)      ; 40 32+4+4                                             */
                    102: /*     movem.l d1-d4,(a1)      ; 48 40+4+4                                             */
                    103: /*     movem.l d1-d5,(a1)      ; 60 48+4+4+4                                           */
                    104: /*     movem.l d1-d6,(a1)      ; 68 56+4+4+4                                           */
                    105: /*     movem.l d1-d7,(a1)      ; 80 64+4+4+4+4                                         */
                    106: /*     movem.l d0-d7,(a1)      ; 88 72+4+4+4+4                                         */
                    107: /*                                                                                     */
1.1.1.10  root      108: /*     movep.w d0,(a3)                         (X-Out)                                 */
                    109: /*                                                                                     */
1.1.1.18! root      110: /*     clr.b (a1)              ; 20 12+8       (4 for read + 4 for write)              */
        !           111: /*     tas (a1)                ; 16            (no waitstate ?)                        */
        !           112: /*                                                                                     */
        !           113: /*                                                                                     */
1.1.1.7   root      114: /* This gives the following "model" :                                                  */
1.1.1.18! root      115: /*     - each instruction accessing a valid YM2149 register gets an initial 4 cycle    */
        !           116: /*       wait state for the 1st access (whether it accesses just 1 reg (eg move.b)     */
        !           117: /*       or up to 4 regs (movep.l)).                                                   */
        !           118: /*     - susbequent accesses made by the same instruction don't add more wait state    */
        !           119: /*       (except if the instruction is a MOVEM).                                       */
        !           120: /*     - MOVEM can access more than 4 regs (up to 15) : in that case we add 4 extra    */
        !           121: /*       cycles each time we access a 4th register (eg : regs 4,8,12, ...)             */
1.1.1.10  root      122: /*     - accesses to $ff8801 or $ff8803 are considered "valid" only if we don't access */
1.1.1.18! root      123: /*       the corresponding "non shadow" addresses $ff8800/02 at the same time (ie with */
        !           124: /*       the same instruction).                                                        */
1.1.1.10  root      125: /*       This means only .B size (move.b for example) or movep opcode will work.       */
1.1.1.18! root      126: /*       If the access is valid, add 4 cycle wait state when necessary, else ignore    */
        !           127: /*       the write and don't add any cycle.                                            */
1.1.1.7   root      128: 
                    129: 
                    130: 
1.1.1.11  root      131: const char PSG_fileid[] = "Hatari psg.c : " __DATE__ " " __TIME__;
1.1       root      132: 
                    133: #include "main.h"
1.1.1.3   root      134: #include "configuration.h"
1.1.1.4   root      135: #include "ioMem.h"
1.1.1.5   root      136: #include "joy.h"
1.1.1.7   root      137: #include "log.h"
1.1.1.4   root      138: #include "m68000.h"
1.1       root      139: #include "memorySnapShot.h"
                    140: #include "sound.h"
1.1.1.4   root      141: #include "printer.h"            /* because Printer I/O goes through PSG Register 15 */
1.1.1.3   root      142: #include "psg.h"
1.1.1.7   root      143: #if ENABLE_DSP_EMU
                    144: #include "falcon/dsp.h"
                    145: #endif
1.1.1.12  root      146: #include "screen.h"
1.1.1.7   root      147: #include "video.h"
1.1.1.9   root      148: #include "statusbar.h"
                    149: #include "mfp.h"
1.1.1.16  root      150: #include "fdc.h"
1.1.1.9   root      151: 
1.1       root      152: 
1.1.1.17  root      153: static Uint8 PSGRegisterSelect;                /* Write to 0xff8800 sets the register number used in read/write accesses */
                    154: static Uint8 PSGRegisterReadData;      /* Value returned when reading from 0xff8800 */
                    155: Uint8 PSGRegisters[MAX_PSG_REGISTERS]; /* Registers in PSG, see PSG_REG_xxxx */
1.1.1.4   root      156: 
1.1.1.9   root      157: static unsigned int LastStrobe=0; /* Falling edge of Strobe used for printer */
1.1       root      158: 
                    159: 
                    160: /*-----------------------------------------------------------------------*/
1.1.1.7   root      161: /**
                    162:  * Reset variables used in PSG
                    163:  */
1.1       root      164: void PSG_Reset(void)
                    165: {
1.1.1.13  root      166:         int     i;
                    167: 
1.1.1.14  root      168:        if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE))
                    169:        {
                    170:                int FrameCycles, HblCounterVideo, LineCycles;
                    171:                Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
                    172:                LOG_TRACE_PRINT("ym reset video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
                    173:                                FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
                    174:        }
                    175: 
1.1.1.4   root      176:        PSGRegisterSelect = 0;
1.1.1.13  root      177:        PSGRegisterReadData = 0;
1.1.1.4   root      178:        memset(PSGRegisters, 0, sizeof(PSGRegisters));
1.1.1.14  root      179:        PSGRegisters[PSG_REG_IO_PORTA] = 0xff;                  /* no drive selected + side 0 after a reset */
                    180: 
                    181:        /* Update sound's emulation registers */
                    182:         for ( i=0 ; i < NUM_PSG_SOUND_REGISTERS; i++ )
1.1.1.13  root      183:                Sound_WriteReg ( i , 0 );
                    184: 
1.1.1.9   root      185:        LastStrobe=0;
1.1       root      186: }
                    187: 
1.1.1.2   root      188: 
                    189: /*-----------------------------------------------------------------------*/
1.1.1.7   root      190: /**
                    191:  * Save/Restore snapshot of local variables ('MemorySnapShot_Store' handles type)
                    192:  */
1.1.1.9   root      193: void PSG_MemorySnapShot_Capture(bool bSave)
1.1       root      194: {
1.1.1.4   root      195:        /* Save/Restore details */
                    196:        MemorySnapShot_Store(&PSGRegisterSelect, sizeof(PSGRegisterSelect));
1.1.1.13  root      197:        MemorySnapShot_Store(&PSGRegisterReadData, sizeof(PSGRegisterReadData));
1.1.1.4   root      198:        MemorySnapShot_Store(PSGRegisters, sizeof(PSGRegisters));
1.1.1.9   root      199:        MemorySnapShot_Store(&LastStrobe, sizeof(LastStrobe));
1.1       root      200: }
                    201: 
1.1.1.2   root      202: 
                    203: /*-----------------------------------------------------------------------*/
1.1.1.7   root      204: /**
1.1.1.10  root      205:  * Write byte to the YM address register (usually 0xff8800). This is used
                    206:  * as a selector for when we read/write the YM data register (0xff8802).
1.1.1.7   root      207:  */
1.1.1.10  root      208: void PSG_Set_SelectRegister(Uint8 val)
1.1       root      209: {
1.1.1.9   root      210:        /* Store register used to read/write in $ff8802. This register */
                    211:        /* is 8 bits on the YM2149, this means it should not be masked */
                    212:        /* with 0xf. Instead, we keep the 8 bits, but we must ignore */
                    213:        /* read/write to ff8802 when PSGRegisterSelect >= 16 */
1.1.1.10  root      214:        PSGRegisterSelect = val;
1.1.1.7   root      215: 
1.1.1.13  root      216:        /* When address register is changed, a read from $ff8800 should */
                    217:        /* return the masked value of the register. We set the value here */
                    218:        /* to be returned in case PSG_Get_DataRegister is called */
                    219:        PSGRegisterReadData = PSGRegisters[PSGRegisterSelect];
                    220: 
1.1.1.11  root      221:        if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE))
                    222:        {
                    223:                int FrameCycles, HblCounterVideo, LineCycles;
                    224:                Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
                    225:                LOG_TRACE_PRINT("ym write reg=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
                    226:                                PSGRegisterSelect, FrameCycles, LineCycles, HblCounterVideo,
                    227:                                M68000_GetPC(), CurrentInstrCycles);
                    228:        }
1.1       root      229: }
                    230: 
1.1.1.2   root      231: 
                    232: /*-----------------------------------------------------------------------*/
1.1.1.7   root      233: /**
1.1.1.10  root      234:  * Read byte from 0xff8800, return PSG data
1.1.1.7   root      235:  */
1.1.1.10  root      236: Uint8 PSG_Get_DataRegister(void)
1.1       root      237: {
1.1.1.9   root      238:        /* Is a valid PSG register currently selected ? */
1.1.1.14  root      239:        if ( PSGRegisterSelect >= MAX_PSG_REGISTERS )
1.1.1.10  root      240:                return 0xff;                            /* not valid, return 0xff */
1.1.1.9   root      241: 
1.1.1.14  root      242:        if (PSGRegisterSelect == PSG_REG_IO_PORTA)
1.1.1.5   root      243:        {
                    244:                /* Second parallel port joystick uses centronics strobe bit as fire button: */
                    245:                if (ConfigureParams.Joysticks.Joy[JOYID_PARPORT2].nJoystickMode != JOYSTICK_DISABLED)
                    246:                {
                    247:                        if (Joy_GetStickData(JOYID_PARPORT2) & 0x80)
1.1.1.14  root      248:                                PSGRegisters[PSG_REG_IO_PORTA] &= ~32;
1.1.1.5   root      249:                        else
1.1.1.14  root      250:                                PSGRegisters[PSG_REG_IO_PORTA] |= 32;
1.1.1.5   root      251:                }
                    252:        }
1.1.1.14  root      253:        else if (PSGRegisterSelect == PSG_REG_IO_PORTB)
1.1.1.5   root      254:        {
                    255:                /* PSG register 15 is parallel port data register - used by parallel port joysticks: */
                    256:                if (ConfigureParams.Joysticks.Joy[JOYID_PARPORT1].nJoystickMode != JOYSTICK_DISABLED)
                    257:                {
1.1.1.14  root      258:                        PSGRegisters[PSG_REG_IO_PORTB] &= 0x0f;
                    259:                        PSGRegisters[PSG_REG_IO_PORTB] |= ~Joy_GetStickData(JOYID_PARPORT1) << 4;
1.1.1.5   root      260:                }
                    261:                if (ConfigureParams.Joysticks.Joy[JOYID_PARPORT2].nJoystickMode != JOYSTICK_DISABLED)
                    262:                {
1.1.1.14  root      263:                        PSGRegisters[PSG_REG_IO_PORTB] &= 0xf0;
                    264:                        PSGRegisters[PSG_REG_IO_PORTB] |= ~Joy_GetStickData(JOYID_PARPORT2) & 0x0f;
1.1.1.5   root      265:                }
                    266:        }
                    267: 
1.1.1.4   root      268:        /* Read data last selected by register */
1.1.1.13  root      269:        return PSGRegisterReadData;
1.1       root      270: }
                    271: 
1.1.1.2   root      272: 
                    273: /*-----------------------------------------------------------------------*/
1.1.1.7   root      274: /**
1.1.1.10  root      275:  * Write byte to YM's register (0xff8802), store according to PSG select register (0xff8800)
1.1.1.7   root      276:  */
1.1.1.10  root      277: void PSG_Set_DataRegister(Uint8 val)
1.1.1.4   root      278: {
1.1.1.16  root      279:        Uint8   val_old;
                    280: 
1.1.1.11  root      281:        if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE))
                    282:        {
                    283:                int FrameCycles, HblCounterVideo, LineCycles;
                    284:                Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
                    285:                LOG_TRACE_PRINT("ym write data reg=0x%x val=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
                    286:                                PSGRegisterSelect, val, FrameCycles, LineCycles,
                    287:                                HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
                    288:        }
1.1.1.9   root      289: 
                    290:        /* Is a valid PSG register currently selected ? */
1.1.1.14  root      291:        if ( PSGRegisterSelect >= MAX_PSG_REGISTERS )
1.1.1.9   root      292:                return;                                 /* not valid, ignore write and do nothing */
                    293: 
1.1.1.8   root      294:        /* Create samples up until this point with current values */
1.1.1.12  root      295:        Sound_Update(false);
1.1.1.8   root      296: 
1.1.1.13  root      297:        /* When a read is made from $ff8800 without changing PSGRegisterSelect, we should return */
                    298:        /* the non masked value. */
                    299:        PSGRegisterReadData = val;                      /* store non masked value for PSG_Get_DataRegister */
                    300: 
1.1.1.16  root      301:        /* Read previous content */
                    302:        val_old = PSGRegisters[PSGRegisterSelect];
                    303: 
1.1.1.10  root      304:        /* Copy value to PSGRegisters[] */
                    305:        PSGRegisters[PSGRegisterSelect] = val;
1.1.1.4   root      306: 
1.1.1.9   root      307:        /* Clear unused bits for some regs */
1.1.1.7   root      308:        if ( ( PSGRegisterSelect == PSG_REG_CHANNEL_A_COARSE ) || ( PSGRegisterSelect == PSG_REG_CHANNEL_B_COARSE )
                    309:                || ( PSGRegisterSelect == PSG_REG_CHANNEL_C_COARSE ) || ( PSGRegisterSelect == PSG_REG_ENV_SHAPE ) )
                    310:          PSGRegisters[PSGRegisterSelect] &= 0x0f;      /* only keep bits 0 - 3 */
                    311: 
                    312:        else if ( ( PSGRegisterSelect == PSG_REG_CHANNEL_A_AMP ) || ( PSGRegisterSelect == PSG_REG_CHANNEL_B_AMP )
1.1.1.9   root      313:                || ( PSGRegisterSelect == PSG_REG_CHANNEL_C_AMP ) || ( PSGRegisterSelect == PSG_REG_NOISE_GENERATOR ) )
1.1.1.7   root      314:          PSGRegisters[PSGRegisterSelect] &= 0x1f;      /* only keep bits 0 - 4 */
                    315: 
                    316: 
1.1.1.9   root      317:        if ( PSGRegisterSelect < NUM_PSG_SOUND_REGISTERS )
1.1.1.4   root      318:        {
1.1.1.9   root      319:                /* Copy sound related registers 0..13 to the sound module's internal buffer */
                    320:                Sound_WriteReg ( PSGRegisterSelect , PSGRegisters[PSGRegisterSelect] );
                    321:        }
1.1.1.4   root      322: 
1.1.1.9   root      323:        else if ( PSGRegisterSelect == PSG_REG_IO_PORTA )
                    324:        {
                    325:        /*
                    326:         * FIXME: This is only a prelimary dirty hack!
                    327:         * Port B (Printer port) - writing here needs to be dispatched to the printer
                    328:         * STROBE (Port A bit5) does a short LOW and back to HIGH when the char is valid
                    329:         * To print you need to write the character byte to IOB and you need to toggle STROBE
                    330:         * (like EmuTOS does).
                    331:         */
1.1.1.4   root      332:                /* Printer dispatching only when printing is activated */
                    333:                if (ConfigureParams.Printer.bEnablePrinting)
                    334:                {
1.1.1.9   root      335:                        /* Bit 5 - Centronics strobe? If STROBE is low and the LastStrobe was high,
                    336:                                        then print/transfer to the emulated Centronics port.
1.1.1.7   root      337:                         */
1.1.1.9   root      338:                        if (LastStrobe && ( (PSGRegisters[PSG_REG_IO_PORTA]&(1<<5)) == 0 ))
1.1.1.4   root      339:                        {
                    340:                                /* Seems like we want to print something... */
1.1.1.9   root      341:                                Printer_TransferByteTo(PSGRegisters[PSG_REG_IO_PORTB]);
                    342:                                /* Initiate a possible GPIP0 Printer BUSY interrupt */
1.1.1.15  root      343:                                MFP_InputOnChannel ( MFP_INT_GPIP0 , 0 );
1.1.1.9   root      344:                                /* Initiate a possible GPIP1 Falcon ACK interrupt */
1.1.1.18! root      345:                                if (Config_IsMachineFalcon())
1.1.1.15  root      346:                                        MFP_InputOnChannel ( MFP_INT_GPIP1 , 0 );
1.1.1.4   root      347:                        }
                    348:                }
1.1.1.9   root      349:                LastStrobe = PSGRegisters[PSG_REG_IO_PORTA]&(1<<5);
                    350: 
                    351:                /* Bit 0-2 : side and drive select */
                    352:                if ( (PSGRegisters[PSG_REG_IO_PORTA]&(1<<1)) == 0 )
                    353:                {
                    354:                        /* floppy drive A is ON */
1.1.1.15  root      355:                        Statusbar_SetFloppyLed(DRIVE_LED_A, LED_STATE_ON);
1.1.1.9   root      356:                }
                    357:                else
                    358:                {
1.1.1.15  root      359:                        Statusbar_SetFloppyLed(DRIVE_LED_A, LED_STATE_OFF);
1.1.1.9   root      360:                }
                    361:                if ( (PSGRegisters[PSG_REG_IO_PORTA]&(1<<2)) == 0 )
                    362:                {
                    363:                        /* floppy drive B is ON */
1.1.1.15  root      364:                        Statusbar_SetFloppyLed(DRIVE_LED_B, LED_STATE_ON);
1.1.1.9   root      365:                }
                    366:                else
                    367:                {
1.1.1.15  root      368:                        Statusbar_SetFloppyLed(DRIVE_LED_B, LED_STATE_OFF);
1.1.1.9   root      369:                }
                    370: 
1.1.1.16  root      371:                /* Report a possible drive/side change */
                    372:                FDC_SetDriveSide ( val_old & 7 , PSGRegisters[PSG_REG_IO_PORTA] & 7 );
                    373: 
1.1.1.7   root      374:                /* handle Falcon specific bits in PORTA of the PSG */
1.1.1.18! root      375:                if (Config_IsMachineFalcon())
1.1.1.7   root      376:                {
1.1.1.17  root      377:                        /* Bit 3 - centronics port SELIN line (pin 17) */
                    378:                        /*
                    379:                        if (PSGRegisters[PSG_REG_IO_PORTA] & (1 << 3))
                    380:                        {
                    381:                                // not emulated yet
                    382:                        }
                    383:                        */
                    384: 
1.1.1.7   root      385:                        /* Bit 4 - DSP reset? */
                    386:                        if(PSGRegisters[PSG_REG_IO_PORTA]&(1<<4))
                    387:                        {
                    388:                                Log_Printf(LOG_DEBUG, "Calling DSP_Reset?\n");
                    389: #if ENABLE_DSP_EMU
                    390:                                if (ConfigureParams.System.nDSPType == DSP_TYPE_EMU) {
                    391:                                        DSP_Reset();
                    392:                                }
                    393: #endif
                    394:                        }
                    395:                        /* Bit 6 - Internal Speaker control */
                    396:                        if(PSGRegisters[PSG_REG_IO_PORTA]&(1<<6))
                    397:                        {
                    398:                                /*Log_Printf(LOG_DEBUG, "Falcon: Internal Speaker state\n");*/
                    399:                                /* FIXME: add code to handle? (if we want to emulate the speaker at all? */
                    400:                        }
                    401:                        /* Bit 7 - Reset IDE? */
                    402:                        if(PSGRegisters[PSG_REG_IO_PORTA]&(1<<7))
                    403:                        {
                    404:                                Log_Printf(LOG_DEBUG, "Falcon: Reset IDE subsystem\n");
                    405:                                /* FIXME: add code to handle IDE reset */
                    406:                        }
                    407:                }
1.1.1.16  root      408: 
1.1.1.4   root      409:        }
1.1       root      410: }
                    411: 
1.1.1.2   root      412: 
                    413: /*-----------------------------------------------------------------------*/
1.1.1.7   root      414: /**
1.1.1.18! root      415:  * Handle wait state when accessing YM2149 registers
        !           416:  * - each instruction accessing YM2149 gets an initial 4 cycle wait state
        !           417:  *   for the 1st access (whether it accesses just 1 reg (eg move.b) or up to 4 regs (movep.l))
        !           418:  * - special case for movem which can access more than 4 regs (up to 15) :
        !           419:  *   we add 4 extra cycles each time we access a 4th reg (eg : regs 4,8,12, ...)
        !           420:  *
        !           421:  * See top of this file for several examples measured on real STF
        !           422:  */
        !           423: static void PSG_WaitState(void)
        !           424: {
        !           425: #if 0
        !           426:        M68000_WaitState(1);                            /* [NP] FIXME not 100% accurate, but gives good results */
        !           427: #else
        !           428:        static Uint64   PSG_InstrPrevClock;
        !           429:        static int      NbrAccesses;
        !           430: 
        !           431:        if ( PSG_InstrPrevClock != CyclesGlobalClockCounter )   /* New instruction accessing YM2149 : add 4 cycles */
        !           432:        {
        !           433:                M68000_WaitState ( 4 );
        !           434:                PSG_InstrPrevClock = CyclesGlobalClockCounter;
        !           435:                NbrAccesses = 0;
        !           436:        }
        !           437: 
        !           438:        else                                                    /* Same instruction doing several accesses : only movem can add more cycles */
        !           439:        {
        !           440:                if ( ( OpcodeFamily == i_MVMEL ) || ( OpcodeFamily == i_MVMLE ) )
        !           441:                {
        !           442:                        NbrAccesses += 1;
        !           443:                        if ( NbrAccesses % 4 == 0 )             /* Add 4 extra cycles every 4th access */
        !           444:                                M68000_WaitState ( 4 );
        !           445:                }
        !           446:        }
        !           447: 
        !           448: #endif
        !           449: }
        !           450: 
        !           451: 
        !           452: /*-----------------------------------------------------------------------*/
        !           453: /**
1.1.1.10  root      454:  * Read byte from 0xff8800. Return current content of data register
1.1.1.7   root      455:  */
1.1.1.10  root      456: void PSG_ff8800_ReadByte(void)
1.1       root      457: {
1.1.1.18! root      458:        PSG_WaitState();
1.1.1.10  root      459: 
                    460:        IoMem[IoAccessCurrentAddress] = PSG_Get_DataRegister();
                    461: 
1.1.1.11  root      462:        if (LOG_TRACE_LEVEL(TRACE_PSG_READ))
                    463:        {
                    464:                int FrameCycles, HblCounterVideo, LineCycles;
                    465:                Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
                    466:                LOG_TRACE_PRINT("ym read data %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
                    467:                                IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress],
                    468:                                FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
                    469:        }
1.1.1.10  root      470: }
                    471: 
                    472: 
                    473: /*-----------------------------------------------------------------------*/
                    474: /**
                    475:  * Read byte from 0xff8801/02/03. Return 0xff.
                    476:  */
                    477: void PSG_ff880x_ReadByte(void)
                    478: {
1.1.1.18! root      479:        PSG_WaitState();
1.1.1.4   root      480: 
1.1.1.8   root      481:        IoMem[IoAccessCurrentAddress] = 0xff;
1.1.1.10  root      482: 
1.1.1.11  root      483:        if (LOG_TRACE_LEVEL(TRACE_PSG_READ))
                    484:        {
                    485:                int FrameCycles, HblCounterVideo, LineCycles;
                    486:                Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
                    487:                LOG_TRACE_PRINT("ym read void %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:        }
1.1       root      491: }
1.1.1.7   root      492: 
                    493: 
                    494: 
                    495: /*-----------------------------------------------------------------------*/
                    496: /**
1.1.1.10  root      497:  * Write byte to 0xff8800. Set content of YM's address register.
1.1.1.7   root      498:  */
1.1.1.10  root      499: void PSG_ff8800_WriteByte(void)
1.1.1.7   root      500: {
1.1.1.18! root      501:        PSG_WaitState();
1.1.1.7   root      502: 
1.1.1.11  root      503:        if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE))
                    504:        {
                    505:                int FrameCycles, HblCounterVideo, LineCycles;
                    506:                Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
                    507:                LOG_TRACE_PRINT("ym write %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
                    508:                                IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress],
                    509:                                FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
                    510:        }
1.1.1.10  root      511: 
                    512:        PSG_Set_SelectRegister ( IoMem[IoAccessCurrentAddress] );
                    513: }
                    514: 
                    515: 
                    516: /*-----------------------------------------------------------------------*/
                    517: /**
                    518:  * Write byte to 0xff8801. Set content of YM's address register under conditions.
                    519:  * Address 0xff8801 is a shadow version of 0xff8800, so both addresses can't be written
                    520:  * at the same time by the same instruction. This means only a .B access or
                    521:  * a movep will have a valid effect, other accesses are ignored.
                    522:  */
                    523: void PSG_ff8801_WriteByte(void)
                    524: {
                    525:        if ( nIoMemAccessSize == SIZE_BYTE )            /* byte access or movep */
                    526:        {       
1.1.1.18! root      527:                PSG_WaitState();
1.1.1.10  root      528:        
1.1.1.11  root      529:                if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE))
                    530:                {
                    531:                        int FrameCycles, HblCounterVideo, LineCycles;
                    532:                        Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
                    533:                        LOG_TRACE_PRINT("ym write %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
                    534:                                        IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress],
                    535:                                        FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
                    536:                }
1.1.1.10  root      537:        
                    538:                PSG_Set_SelectRegister ( IoMem[IoAccessCurrentAddress] );
                    539:        }
                    540: 
                    541:        else
                    542:        {                                               /* do nothing, just a trace if needed */
1.1.1.11  root      543:                if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE))
                    544:                {
                    545:                        int FrameCycles, HblCounterVideo, LineCycles;
                    546:                        Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
                    547:                        LOG_TRACE_PRINT("ym write ignored %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
                    548:                                        IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress],
                    549:                                        FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
                    550:                }
1.1.1.10  root      551:        }
1.1.1.7   root      552: }
                    553: 
                    554: 
1.1.1.10  root      555: /*-----------------------------------------------------------------------*/
                    556: /**
                    557:  * Write byte to 0xff8802. Set content of YM's data register.
                    558:  */
                    559: void PSG_ff8802_WriteByte(void)
                    560: {
1.1.1.18! root      561:        PSG_WaitState();
1.1.1.10  root      562: 
1.1.1.11  root      563:        if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE))
                    564:        {
                    565:                int FrameCycles, HblCounterVideo, LineCycles;
                    566:                Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
                    567:                LOG_TRACE_PRINT("ym write %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
                    568:                                IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress],
                    569:                                FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
                    570:        }
1.1.1.10  root      571: 
                    572:        PSG_Set_DataRegister ( IoMem[IoAccessCurrentAddress] );
                    573: }
                    574: 
1.1.1.7   root      575: 
                    576: /*-----------------------------------------------------------------------*/
                    577: /**
1.1.1.10  root      578:  * Write byte to 0xff8803. Set content of YM's data register under conditions.
                    579:  * Address 0xff8803 is a shadow version of 0xff8802, so both addresses can't be written
                    580:  * at the same time by the same instruction. This means only a .B access or
                    581:  * a movep will have a valid effect, other accesses are ignored.
1.1.1.7   root      582:  */
1.1.1.10  root      583: void PSG_ff8803_WriteByte(void)
1.1.1.7   root      584: {
1.1.1.10  root      585:        if ( nIoMemAccessSize == SIZE_BYTE )            /* byte access or movep */
                    586:        {       
1.1.1.18! root      587:                PSG_WaitState();
1.1.1.10  root      588:        
1.1.1.11  root      589:                if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE))
                    590:                {
                    591:                        int FrameCycles, HblCounterVideo, LineCycles;
                    592:                        Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
                    593:                        LOG_TRACE_PRINT("ym write %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
                    594:                                        IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress],
                    595:                                        FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
                    596:                }
1.1.1.10  root      597:                
                    598:                PSG_Set_DataRegister ( IoMem[IoAccessCurrentAddress] );
                    599:        }
                    600: 
                    601:        else
                    602:        {                                               /* do nothing, just a trace if needed */
1.1.1.11  root      603:                if (LOG_TRACE_LEVEL(TRACE_PSG_WRITE))
                    604:                {
                    605:                        int FrameCycles, HblCounterVideo, LineCycles;
                    606:                        Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
                    607:                        LOG_TRACE_PRINT("ym write ignored %x=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
                    608:                                        IoAccessCurrentAddress, IoMem[IoAccessCurrentAddress],
                    609:                                        FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
                    610:                }
1.1.1.10  root      611:        }
1.1.1.7   root      612: }
1.1.1.16  root      613: 
                    614: 
                    615: /* ------------------------------------------------------------------
                    616:  * YM-2149 register content dump (for debugger info command)
                    617:  */
1.1.1.17  root      618: void PSG_Info(FILE *fp, Uint32 dummy)
1.1.1.16  root      619: {
                    620:        int i;
1.1.1.18! root      621:        for(i = 0; i < ARRAY_SIZE(PSGRegisters); i++)
1.1.1.16  root      622:        {
1.1.1.17  root      623:                fprintf(fp, "Reg $%02X : $%02X\n", i, PSGRegisters[i]);
1.1.1.16  root      624:        }
                    625: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.