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

1.1       root        1: /*
1.1.1.4   root        2:   Hatari - psg.c
1.1       root        3: 
1.1.1.4   root        4:   This file is distributed under the GNU Public License, version 2 or at
                      5:   your option any later version. Read the file gpl.txt for details.
1.1.1.3   root        6: 
1.1.1.4   root        7:   Programmable Sound Generator (YM-2149) - PSG
1.1.1.3   root        8: 
1.1.1.4   root        9:   Also used for the printer (centronics) port emulation (PSG Port B, Register 15)
1.1       root       10: */
1.1.1.7 ! root       11: 
        !            12: 
        !            13: /* 2007/04/14  [NP]    First approximation to get cycle accurate accesses to ff8800/02 */
        !            14: /*                     by cumulating wait state of 1 cycle and rounding the final      */
        !            15: /*                     result to 4.                                                    */
        !            16: /* 2007/04/29  [NP]    Functions PSG_Void_WriteByte and PSG_Void_ReadByte to handle    */
        !            17: /*                     accesses to $ff8801/03. These adresses have no effect, but they */
        !            18: /*                     must give a 1 cycle wait state (e.g. move.l d0,ff8800).         */
        !            19: /* 2007/09/29  [NP]    Replace printf by calls to HATARI_TRACE.                        */
        !            20: /* 2007/10/23  [NP]    In PSG_Void_WriteByte, add a wait state only if no wait state   */
        !            21: /*                     were added so far (hack, but gives good result).                */
        !            22: /* 2007/11/18  [NP]    In PSG_DataRegister_WriteByte, set unused bit to 0, in case     */
        !            23: /*                     the data reg is read later (fix Mindbomb Demo / BBC).           */
        !            24: 
        !            25: 
        !            26: /* Emulating wait states when accessing $ff8800/01/02/03 with different 'move' variants        */
        !            27: /* is a complex task. So far, adding 1 cycle wait state to each access and rounding the        */
        !            28: /* final number to 4 gives some good results, but this is certainly not the way it's   */
        !            29: /* working for real in the ST.                                                         */
        !            30: /* The following examples show some verified wait states for different accesses :      */
        !            31: /*     lea     $ffff8800,a1                                                            */
        !            32: /*     lea     $ffff8802,a2                                                            */
        !            33: /*     lea     $ffff8801,a3                                                            */
        !            34: /*                                                                                     */
        !            35: /*     movep.w d1,(a1)         ; 20 16+4       (ventura loader)                        */
        !            36: /*     movep.l d1,(a1)         ; 28 24+4       (ventura loader, ulm loader)            */
        !            37: /*                                                                                     */
        !            38: /*     movep.l d6,0(a5)        ; 28 24+4       (SNY I, TCB)                            */
        !            39: /*     movep.w d5,0(a5)        ; 20 16+4       (SNY I, TCB)                            */
        !            40: /*                                                                                     */
        !            41: /*     move.b d1,(a1)          ; 12 8+4                                                */
        !            42: /*     move.b d1,(a2)          ; 12 8+4                                                */
        !            43: /*     move.b d1,(a3)          ; 12 8+4        (crickey ulm hidden)                    */
        !            44: /*                                                                                     */
        !            45: /*     move.w d1,(a1)          ; 12 8+4                                                */
        !            46: /*     move.w d1,(a2)          ; 12 8+4                                                */
        !            47: /*     move.l d1,(a1)          ; 16 12+4       (ulm loader)                            */
        !            48: /*                                                                                     */
        !            49: /*     movem.l d1,(a1)         ; 20 16+4                                               */
        !            50: /*     movem.l d1-d2,(a1)      ; 28 24+4                                               */
        !            51: /*     movem.l d1-d3,(a1)      ; 40 32+4+4                                             */
        !            52: /*     movem.l d1-d4,(a1)      ; 48 40+4+4                                             */
        !            53: /*     movem.l d1-d5,(a1)      ; 60 48+4+4+4                                           */
        !            54: /*     movem.l d1-d6,(a1)      ; 68 56+4+4+4                                           */
        !            55: /*     movem.l d1-d7,(a1)      ; 80 64+4+4+4+4                                         */
        !            56: /*     movem.l d0-d7,(a1)      ; 88 72+4+4+4+4                                         */
        !            57: /*                                                                                     */
        !            58: /* This gives the following "model" :                                                  */
        !            59: /*     - each access to $ff8800 or $ff8802 add 1 cycle wait state                      */
        !            60: /*     - access to $ff8801 or $ff8803 give 0 wait state, except if this is the only    */
        !            61: /*       register accessed (move.b for example).                                       */
        !            62: 
        !            63: 
        !            64: 
        !            65: const char PSG_rcsid[] = "Hatari $Id: psg.c,v 1.18 2008/01/24 21:21:54 thothy Exp $";
1.1       root       66: 
                     67: #include "main.h"
1.1.1.3   root       68: #include "configuration.h"
1.1.1.4   root       69: #include "ioMem.h"
1.1.1.5   root       70: #include "joy.h"
1.1.1.7 ! root       71: #include "log.h"
1.1.1.4   root       72: #include "m68000.h"
1.1       root       73: #include "memorySnapShot.h"
                     74: #include "sound.h"
1.1.1.4   root       75: #include "printer.h"            /* because Printer I/O goes through PSG Register 15 */
1.1.1.3   root       76: #include "psg.h"
1.1.1.7 ! root       77: #if ENABLE_DSP_EMU
        !            78: #include "falcon/dsp.h"
        !            79: #endif
        !            80: #include "video.h"
        !            81: #include "trace.h"
1.1       root       82: 
1.1.1.4   root       83: Uint8 PSGRegisterSelect;        /* 0xff8800 (read/write) */
                     84: Uint8 PSGRegisters[16];         /* Register in PSG, see PSG_REG_xxxx */
                     85: 
                     86: static BOOL bLastWriteToIOB;    /* boolean flag: did the last write to the PSG go to IOB? */
1.1       root       87: 
                     88: 
                     89: /*-----------------------------------------------------------------------*/
1.1.1.7 ! root       90: /**
        !            91:  * Reset variables used in PSG
        !            92:  */
1.1       root       93: void PSG_Reset(void)
                     94: {
1.1.1.4   root       95:        PSGRegisterSelect = 0;
                     96:        memset(PSGRegisters, 0, sizeof(PSGRegisters));
                     97:        bLastWriteToIOB = FALSE;
1.1       root       98: }
                     99: 
1.1.1.2   root      100: 
                    101: /*-----------------------------------------------------------------------*/
1.1.1.7 ! root      102: /**
        !           103:  * Save/Restore snapshot of local variables ('MemorySnapShot_Store' handles type)
        !           104:  */
1.1       root      105: void PSG_MemorySnapShot_Capture(BOOL bSave)
                    106: {
1.1.1.4   root      107:        /* Save/Restore details */
                    108:        MemorySnapShot_Store(&PSGRegisterSelect, sizeof(PSGRegisterSelect));
                    109:        MemorySnapShot_Store(PSGRegisters, sizeof(PSGRegisters));
                    110:        MemorySnapShot_Store(&bLastWriteToIOB, sizeof(bLastWriteToIOB));
1.1       root      111: }
                    112: 
1.1.1.2   root      113: 
                    114: /*-----------------------------------------------------------------------*/
1.1.1.7 ! root      115: /**
        !           116:  * Write byte to 0xff88000, this is used as a selector for when we read/write
        !           117:  * to address 0xff8802
        !           118:  */
1.1.1.4   root      119: void PSG_SelectRegister_WriteByte(void)
1.1       root      120: {
1.1.1.7 ! root      121: //     M68000_WaitState(4);
        !           122:        M68000_WaitState(1);                            /* [NP] FIXME not 100% accurate, but gives good results */
1.1.1.4   root      123: 
1.1.1.6   root      124:        PSGRegisterSelect = IoMem[0xff8800] & 0x0f;     /* Store register to select (value in bits 0-3) */
1.1.1.7 ! root      125: 
        !           126:        if ( HATARI_TRACE_LEVEL ( HATARI_TRACE_PSG_WRITE_REG ) )
        !           127:          {
        !           128:            int nFrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO);;
        !           129:            int nLineCycles = nFrameCycles % nCyclesPerLine;
        !           130:            HATARI_TRACE_PRINT ( "write ym sel reg=%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n" ,
        !           131:                PSGRegisterSelect, nFrameCycles, nLineCycles, nHBL, M68000_GetPC(), CurrentInstrCycles );
        !           132:          }
1.1       root      133: }
                    134: 
1.1.1.2   root      135: 
                    136: /*-----------------------------------------------------------------------*/
1.1.1.7 ! root      137: /**
        !           138:  * Read byte from 0xff8800, returns PSG data
        !           139:  */
1.1.1.4   root      140: void PSG_SelectRegister_ReadByte(void)
1.1       root      141: {
1.1.1.6   root      142:        M68000_WaitState(4);
                    143: 
1.1.1.5   root      144:        if (PSGRegisterSelect == 14)
                    145:        {
                    146:                /* Second parallel port joystick uses centronics strobe bit as fire button: */
                    147:                if (ConfigureParams.Joysticks.Joy[JOYID_PARPORT2].nJoystickMode != JOYSTICK_DISABLED)
                    148:                {
                    149:                        if (Joy_GetStickData(JOYID_PARPORT2) & 0x80)
                    150:                                PSGRegisters[14] &= ~32;
                    151:                        else
                    152:                                PSGRegisters[14] |= 32;
                    153:                }
                    154:        }
                    155:        else if (PSGRegisterSelect == 15)
                    156:        {
                    157:                /* PSG register 15 is parallel port data register - used by parallel port joysticks: */
                    158:                if (ConfigureParams.Joysticks.Joy[JOYID_PARPORT1].nJoystickMode != JOYSTICK_DISABLED)
                    159:                {
                    160:                        PSGRegisters[15] &= 0x0f;
                    161:                        PSGRegisters[15] |= ~Joy_GetStickData(JOYID_PARPORT1) << 4;
                    162:                }
                    163:                if (ConfigureParams.Joysticks.Joy[JOYID_PARPORT2].nJoystickMode != JOYSTICK_DISABLED)
                    164:                {
                    165:                        PSGRegisters[15] &= 0xf0;
                    166:                        PSGRegisters[15] |= ~Joy_GetStickData(JOYID_PARPORT2) & 0x0f;
                    167:                }
                    168:        }
                    169: 
1.1.1.4   root      170:        /* Read data last selected by register */
                    171:        IoMem[0xff8800] = PSGRegisters[PSGRegisterSelect];
1.1       root      172: }
                    173: 
1.1.1.2   root      174: 
                    175: /*-----------------------------------------------------------------------*/
1.1.1.7 ! root      176: /**
        !           177:  * Write byte to 0xff8802, stores according to PSG select register (write 0xff8800)
        !           178:  */
1.1.1.4   root      179: void PSG_DataRegister_WriteByte(void)
                    180: {
1.1.1.7 ! root      181: //     M68000_WaitState(4);
        !           182:        M68000_WaitState(1);                            /* [NP] FIXME not 100% accurate, but gives good results */
1.1.1.6   root      183: 
1.1.1.7 ! root      184:        Sound_Update();                                 /* Create samples up until this point with current values */
1.1.1.4   root      185:        PSGRegisters[PSGRegisterSelect] = IoMem[0xff8802];        /* Write value to PSGRegisters[] */
                    186: 
1.1.1.7 ! root      187:        /* [NP] Clear unused bits for some regs */
        !           188:        if ( ( PSGRegisterSelect == PSG_REG_CHANNEL_A_COARSE ) || ( PSGRegisterSelect == PSG_REG_CHANNEL_B_COARSE )
        !           189:                || ( PSGRegisterSelect == PSG_REG_CHANNEL_C_COARSE ) || ( PSGRegisterSelect == PSG_REG_ENV_SHAPE ) )
        !           190:          PSGRegisters[PSGRegisterSelect] &= 0x0f;      /* only keep bits 0 - 3 */
        !           191: 
        !           192:        else if ( ( PSGRegisterSelect == PSG_REG_CHANNEL_A_AMP ) || ( PSGRegisterSelect == PSG_REG_CHANNEL_B_AMP )
        !           193:                || ( PSGRegisterSelect == PSG_REG_CHANNEL_C_AMP ) )
        !           194:          PSGRegisters[PSGRegisterSelect] &= 0x1f;      /* only keep bits 0 - 4 */
        !           195: 
        !           196: 
        !           197:        if ( HATARI_TRACE_LEVEL ( HATARI_TRACE_PSG_WRITE_DATA ) )
        !           198:          {
        !           199:            int nFrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO);;
        !           200:            int nLineCycles = nFrameCycles % nCyclesPerLine;
        !           201:            HATARI_TRACE_PRINT ( "write ym data reg=%x val=%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n" ,
        !           202:                PSGRegisterSelect, IoMem[0xff8802], nFrameCycles, nLineCycles, nHBL, M68000_GetPC(), CurrentInstrCycles );
        !           203:          }
        !           204: 
        !           205: 
1.1.1.4   root      206:        switch (PSGRegisterSelect)
                    207:        {
                    208: 
                    209:         /* Check registers 8,9 and 10 which are 'amplitude' for each channel and
                    210:          * store if wrote to (to check for sample playback) */
                    211:         case PSG_REG_CHANNEL_A_AMP:
                    212:                bWriteChannelAAmp = TRUE;
                    213:                break;
                    214:         case PSG_REG_CHANNEL_B_AMP:
                    215:                bWriteChannelBAmp = TRUE;
                    216:                break;
                    217:         case PSG_REG_CHANNEL_C_AMP:
                    218:                bWriteChannelCAmp = TRUE;
                    219:                break;
                    220: 
                    221:         case PSG_REG_ENV_SHAPE:            /* Whenever 'write' to register 13, cause envelope to reset */
                    222:                bEnvelopeFreqFlag = TRUE;
                    223:                bWriteEnvelopeFreq = TRUE;
                    224:                break;
                    225: 
                    226:         /*
                    227:          * FIXME: This is only a prelimary dirty hack!
                    228:          * Port B (Printer port) - writing here needs to be dispatched to the printer
                    229:          * STROBE (Port A bit5) does a short LOW and back to HIGH when the char is valid
                    230:          * To print you need to write the character byte to IOB and you need to toggle STROBE
                    231:          * (like EmuTOS does)....therefor we print when STROBE gets low and last write access to
                    232:          * the PSG was to IOB
                    233:          */
                    234:         case PSG_REG_IO_PORTA:
                    235:                /* Printer dispatching only when printing is activated */
                    236:                if (ConfigureParams.Printer.bEnablePrinting)
                    237:                {
1.1.1.7 ! root      238:                        /* Bit 5 - Centronics strobe? If STROBE is low and the last write did go to IOB then
        !           239:                         * there is data in PORTB to print/transfer to the emulated Centronics port
        !           240:                         */
        !           241:                        if ((((PSGRegisters[PSG_REG_IO_PORTA]&(1<<5))==0) && bLastWriteToIOB))
1.1.1.4   root      242:                        {
                    243:                                /* Seems like we want to print something... */
                    244:                                Printer_TransferByteTo((unsigned char) PSGRegisters[PSG_REG_IO_PORTB]);
                    245:                        }
                    246:                }
1.1.1.7 ! root      247:                /* Bit 3 - Centronics as input */
        !           248:                if(PSGRegisters[PSG_REG_IO_PORTA]&(1<<3))
        !           249:                {
        !           250:                        /* FIXME: might be needed if we want to emulate sound sampling hardware */
        !           251:                }
        !           252:                
        !           253:                /* handle Falcon specific bits in PORTA of the PSG */
        !           254:                if (ConfigureParams.System.nMachineType == MACHINE_FALCON)
        !           255:                {
        !           256:                        /* Bit 4 - DSP reset? */
        !           257:                        if(PSGRegisters[PSG_REG_IO_PORTA]&(1<<4))
        !           258:                        {
        !           259:                                Log_Printf(LOG_DEBUG, "Calling DSP_Reset?\n");
        !           260: #if ENABLE_DSP_EMU
        !           261:                                if (ConfigureParams.System.nDSPType == DSP_TYPE_EMU) {
        !           262:                                        DSP_Reset();
        !           263:                                }
        !           264: #endif
        !           265:                        }
        !           266:                        /* Bit 6 - Internal Speaker control */
        !           267:                        if(PSGRegisters[PSG_REG_IO_PORTA]&(1<<6))
        !           268:                        {
        !           269:                                /*Log_Printf(LOG_DEBUG, "Falcon: Internal Speaker state\n");*/
        !           270:                                /* FIXME: add code to handle? (if we want to emulate the speaker at all? */
        !           271:                        }
        !           272:                        /* Bit 7 - Reset IDE? */
        !           273:                        if(PSGRegisters[PSG_REG_IO_PORTA]&(1<<7))
        !           274:                        {
        !           275:                                Log_Printf(LOG_DEBUG, "Falcon: Reset IDE subsystem\n");
        !           276:                                /* FIXME: add code to handle IDE reset */
        !           277:                        }
        !           278:                }
1.1.1.4   root      279:                break;
                    280:        }
                    281: 
                    282:        /* Remember if we wrote to IO Port B */
                    283:        bLastWriteToIOB = (PSGRegisterSelect == PSG_REG_IO_PORTB);
1.1       root      284: }
                    285: 
1.1.1.2   root      286: 
                    287: /*-----------------------------------------------------------------------*/
1.1.1.7 ! root      288: /**
        !           289:  * Read byte from 0xff8802, returns 0xff
        !           290:  */
1.1.1.4   root      291: void PSG_DataRegister_ReadByte(void)
1.1       root      292: {
1.1.1.6   root      293:        M68000_WaitState(4);
1.1.1.4   root      294: 
1.1.1.6   root      295:        IoMem[0xff8802] = 0xff;
1.1       root      296: }
1.1.1.7 ! root      297: 
        !           298: 
        !           299: 
        !           300: /*-----------------------------------------------------------------------*/
        !           301: /**
        !           302:  * Write byte to 0xff8801/03. Do nothing, but add some wait states if needed.
        !           303:  */
        !           304: void PSG_Void_WriteByte(void)
        !           305: {
        !           306:        /* [NP] FIXME If no wait states were added so far, it's possible we're accessing */
        !           307:        /* 8801/8803 through a .B instruction, so we need to add a wait state */
        !           308:        /* Else, the wait states will be added when writing to 8800/8802 */
        !           309:        /* This works so far, but this model is certainly not 100% accurate */
        !           310:        if ( nWaitStateCycles == 0 )
        !           311:          M68000_WaitState(1);
        !           312: 
        !           313:        if ( HATARI_TRACE_LEVEL ( HATARI_TRACE_PSG_WRITE_DATA ) )
        !           314:          {
        !           315:            int nFrameCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO);;
        !           316:            int nLineCycles = nFrameCycles % nCyclesPerLine;
        !           317:            HATARI_TRACE_PRINT ( "write ym 8801/03 video_cyc=%d %d@%d pc=%x instr_cycle %d\n" ,
        !           318:                nFrameCycles, nLineCycles, nHBL, M68000_GetPC(), CurrentInstrCycles );
        !           319:          }
        !           320: }
        !           321: 
        !           322: 
        !           323: 
        !           324: /*-----------------------------------------------------------------------*/
        !           325: /**
        !           326:  * Read byte from 0xff8801/03. Do nothing, but add some wait states.
        !           327:  */
        !           328: void PSG_Void_ReadByte(void)
        !           329: {
        !           330:        M68000_WaitState(1);
        !           331: }
        !           332: 

unix.superglobalmegacorp.com

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