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

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

unix.superglobalmegacorp.com

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