Annotation of hatari/src/acia.c, revision 1.1

1.1     ! root        1: /*
        !             2:   Hatari - acia.c
        !             3: 
        !             4:   Copyright (C) 2012 by Nicolas Pomarède
        !             5: 
        !             6:   This file is distributed under the GNU Public License, version 2 or at
        !             7:   your option any later version. Read the file gpl.txt for details.
        !             8: 
        !             9:   MC6850 ACIA emulation.
        !            10: */
        !            11: 
        !            12: const char ACIA_fileid[] = "Hatari acia.c : " __DATE__ " " __TIME__;
        !            13: 
        !            14: 
        !            15: /* 2012/09/28  [NP]    Start of the full rewrite of the MC6850 ACIA emulation, using the official      */
        !            16: /*                     datasheets for maximum accuracy, as well as bit level serial transfers (start,  */
        !            17: /*                     stop and parity bits).                                                          */
        !            18: /* 2012/12/21  [NP]    Add accurate cycles delays when accessing an ACIA register, taking E Clock      */
        !            19: /*                     into account.                                                                   */
        !            20: /* 2013/04/24  [NP]    Remove INTERRUPT_ACIA_MFP used to add a 4 cycle delay when IRQ is set, as this  */
        !            21: /*                     delay is now correctly handled directly in the MFP since 2013/03/01.            */
        !            22: 
        !            23: 
        !            24: 
        !            25: 
        !            26: /*
        !            27:   6850 ACIA (Asynchronous Communications Inferface Apdater)
        !            28: 
        !            29:   References :
        !            30:    - MC6850 datasheet by Motorola (DS9493R4, 1985)
        !            31:    - A6850 datasheet by Altera (A-DS-A6850-01, 1996) (nearly identical component)
        !            32: 
        !            33:   Others references :
        !            34:    - MAME's 6850acia.c for RTS, CTS and DCD behaviour
        !            35: 
        !            36: 
        !            37:   Pins :
        !            38:     Vss
        !            39:     RX DATA Receive Data
        !            40:     RX CLK Receive Clock
        !            41:     TX CLK Transmitter Clock
        !            42:     RTS Request To Send
        !            43:     TX DATA Transmitter Data
        !            44:     IRQ Interrupt Request
        !            45:     CS 0,1,2 Chip Select
        !            46:     RS Register Select
        !            47:     Vcc Voltage
        !            48:     R/W Read/Write
        !            49:     E Enable
        !            50:     D0-D7 Data
        !            51:     DCD Data Carrier Detect
        !            52:     CTS Clear To Send
        !            53: 
        !            54:   Registers :
        !            55:     0xfffc00 Keyboard ACIA Control (write)/Status(read)
        !            56:     0xfffc02 Keyboard ACIA Data
        !            57:     0xfffc04 MIDI ACIA Control (write)/Status(read)
        !            58:     0xfffc06 MIDI ACIA Data
        !            59: 
        !            60:   Control Register (0xfffc00 write) :
        !            61:     Bits 0,1 - These bits determine by which factor the transmitter and receiver
        !            62:       clock will be divided. These bits also are joined with a master reset
        !            63:       function. The 6850 has no separate reset line, so it must be
        !            64:       accomplished though software.
        !            65:         0 0    RXCLK/TXCLK without division
        !            66:         0 1    RXCLK/TXCLK by 16 (MIDI)
        !            67:         1 0    RXCLK/TXCLK by 64 (Keyboard)
        !            68:         1 1    Master RESET
        !            69:     Bits 2,3,4 - These so-called Word Select bits tell whether 7 or 8 data-bits are
        !            70:       involved; whether 1 or 2 stop-bits are transferred; and the type of parity
        !            71:     Bits 5,6 - These Transmitter Control bits set the RTS output pin, and allow or prevent
        !            72:       an interrupt through the ACIA when the send register is emptied. Also, BREAK signals
        !            73:       can be sent over the serial output by this line. A BREAK signal is nothing more than
        !            74:       a long seqence of null bits
        !            75:         0 0    RTS low, transmitter IRQ disabled
        !            76:         0 1    RTS low, transmitter IRQ enabled
        !            77:         1 0    RTS high, transmitter IRQ disabled
        !            78:         1 1    RTS low, transmitter IRQ disabled, BREAK sent
        !            79:     Bit 7 - The Receiver Interrupt Enable bit determines whether the receiver interrupt
        !            80:       will be on. An interrupt can be caused by the DCD line chaning from low to high, or
        !            81:       by the receiver data buffer filling. Besides that, an interrupt can occur from an
        !            82:       OVERRUN (a received character isn't properly read from the processor).
        !            83:         0 Interrupt disabled
        !            84:         1 Interrupt enabled
        !            85: 
        !            86:   Status Register (0xfffc00 read) :
        !            87:     Bit 0 - When this bit is high, the RX data register is full. The byte must be read
        !            88:       before a new character is received (otherwise an OVERRUN happens)
        !            89:     Bit 1 - This bit reflects the status of the TX data buffer. An empty register
        !            90:       set the bit.
        !            91:     Bit 2 - A low-high change in pin DCD sets bit 2. If the receiver interrupt is allowable, the IRQ
        !            92:       is cancelled. The bit is cleared when the status register and the receiver register are
        !            93:       read. This also cancels the IRQ. Bit 2 register remains highis the signal on the DCD pin
        !            94:       is still high; Bit 2 register low if DCD becomes low.
        !            95:     Bit 3 - This line shows the status of CTS. This signal cannot be altered by a mater reset,
        !            96:       or by ACIA programming.
        !            97:     Bit 4 - Shows 'Frame Errors'. Frame errors are when no stop-bit is recognized in receiver
        !            98:       switching. It can be set with every new character.
        !            99:     Bit 5 - This bit display the previously mentioned OVERRUN condition. Bit 5 is reset when the
        !           100:       RX buffer is read.
        !           101:     Bit 6 - This bit recognizes whether the parity of a received character is correct. The bit is
        !           102:       set on an error.
        !           103:     Bit 7 - This signals the state of the IRQ pins; this bit make it possible to switch several
        !           104:       IRQ lines on one interrupt input. In cases where an interrupt is program-generated, bit 7
        !           105:       can tell which IC cut off the interrupt.
        !           106: 
        !           107:   ST ACIA :
        !           108:     CTS,DCD and RTS are not connected
        !           109:     The keyboard ACIA addresses are 0xfffc000 and 0xfffc02.
        !           110:     The MIDI ACIA addresses are 0xfffc004 and 0xfffc06.
        !           111:     Default keyboard parameters are : 8-bit word, 1 stopbit, no parity, 7812.5 baud; 500KHz/64 (keyboard clock div)
        !           112:     Default MIDI parameters are as above but : 31250 baud; 500KHz/16 (MIDI clock div)
        !           113: 
        !           114: 
        !           115:   CPU cycles in the ST :
        !           116:     When accessing an ACIA register, an additional delay will be added to the usual number of
        !           117:     cycles for this CPU instruction. This delay is made of 2 parts (for a 68000 at 8 MHz) :
        !           118:        - a fixed delay of 6 cycles.
        !           119:        - a variable delay of 0 to 8 cycles to synchronise with the E Clock.
        !           120: 
        !           121:     Examples for some common instructions measured on a real 520 STF
        !           122:     (with a0=$fffffc00 and 'n' the delay for E Clock) :
        !           123:        move.b  (a0),d2 : 14 cycles = 8 + 6 + n
        !           124:        move.w  (a0),d2 : 14 cycles = 8 + 6 + n
        !           125:        move.l  (a0),d2 : 24 cycles = 12 + 6 + 6 + n
        !           126:        movep.w (a0),d2 : 28 cycles = 16 + 6 + 6 + n
        !           127:        movep.l (a0),d2 : 48 cycles = 24 + 6 + 6 + 6 + 6 + n
        !           128:     (on ST, those values might be rounded to the next multiple of 4 cycles)
        !           129: 
        !           130:     When the ACIA's IRQ signal goes low, the resulting bit in the MFP is visible to the CPU only 4 cycles later.
        !           131:     From the hardware point of view, the ACIA's irq signal is immediately propagated to the MFP,
        !           132:     but the MFP will then add a 4 cycle delay before generating a 68000 interrupt.
        !           133: 
        !           134: */
        !           135: 
        !           136: /*-----------------------------------------------------------------------*/
        !           137: 
        !           138: 
        !           139: #include "main.h"
        !           140: #include "log.h"
        !           141: #include "memorySnapShot.h"
        !           142: #include "configuration.h"
        !           143: #include "acia.h"
        !           144: #include "m68000.h"
        !           145: #include "cycInt.h"
        !           146: #include "ioMem.h"
        !           147: #include "clocks_timings.h"
        !           148: #include "mfp.h"
        !           149: #include "screen.h"
        !           150: #include "video.h"
        !           151: 
        !           152: 
        !           153: #define        ACIA_SR_BIT_RDRF                        0x01            /* Receive Data Register Full */
        !           154: #define        ACIA_SR_BIT_TDRE                        0x02            /* Transmit Data Register Empty */
        !           155: #define        ACIA_SR_BIT_DCD                         0x04            /* Data Carrier Detect */
        !           156: #define        ACIA_SR_BIT_CTS                         0x08            /* Clear To Send */
        !           157: #define        ACIA_SR_BIT_FE                          0x10            /* Framing Error */
        !           158: #define        ACIA_SR_BIT_OVRN                        0x20            /* Receiver Overrun */
        !           159: #define        ACIA_SR_BIT_PE                          0x40            /* Parity Error */
        !           160: #define        ACIA_SR_BIT_IRQ                         0x80            /* IRQ */
        !           161: 
        !           162: #define        ACIA_CR_COUNTER_DIVIDE( CR )            ( CR & 0x03 )           /* CR1 + CR0 : 0x03 causes a master reset */
        !           163: #define        ACIA_CR_WORD_SELECT( CR )               ( ( CR >> 2 ) & 0x07 )  /* CR4 + CR3 + CR2 : size, parity, stop bits */
        !           164: #define        ACIA_CR_TRANSMITTER_CONTROL( CR )       ( ( CR >> 5 ) & 0x03 )  /* CR6 + CR5 : RTS + IRQ on send */
        !           165: #define        ACIA_CR_RECEIVE_INTERRUPT_ENABLE( CR )  ( ( CR >> 7 ) & 0x01 )  /* CR7 : Reveive interrupt enable */
        !           166: 
        !           167: 
        !           168: 
        !           169: int    ACIA_Counter_Divide[ 3 ] = { 1 , 16 , 64 };             /* Used to divide txclock/rxclock to get the correct baud rate */
        !           170: 
        !           171: 
        !           172: /* Data size, parity and stop bits used for the transfer depending on CR_WORD_SELECT */
        !           173: enum
        !           174: {
        !           175:        ACIA_PARITY_NONE ,
        !           176:        ACIA_PARITY_EVEN ,
        !           177:        ACIA_PARITY_ODD
        !           178: };
        !           179: 
        !           180: 
        !           181: static struct {
        !           182:        int     DataBits;                                               /* 7 or 8 */
        !           183:        int     Parity;                                                 /* EVEN or ODD or NONE */
        !           184:        int     StopBits;                                               /* 1 or 2 */
        !           185: } ACIA_Serial_Params [ 8 ] = {
        !           186:   { 7 , ACIA_PARITY_EVEN , 2 },
        !           187:   { 7 , ACIA_PARITY_ODD ,  2 },
        !           188:   { 7 , ACIA_PARITY_EVEN , 1 },
        !           189:   { 7 , ACIA_PARITY_ODD ,  1 },
        !           190:   { 8 , ACIA_PARITY_NONE , 2 },
        !           191:   { 8 , ACIA_PARITY_NONE , 1 },
        !           192:   { 8 , ACIA_PARITY_EVEN , 1 },
        !           193:   { 8 , ACIA_PARITY_ODD ,  1 }
        !           194: };
        !           195: 
        !           196: 
        !           197: 
        !           198: /* Possible states when handling TX/RX interrupts */
        !           199: enum
        !           200: {
        !           201:        ACIA_STATE_IDLE = 0,
        !           202:        ACIA_STATE_DATA_BIT,
        !           203:        ACIA_STATE_PARITY_BIT,
        !           204:        ACIA_STATE_STOP_BIT
        !           205: };
        !           206: 
        !           207: 
        !           208: ACIA_STRUCT            ACIA_Array[ ACIA_MAX_NB ];
        !           209: ACIA_STRUCT            *pACIA_IKBD;
        !           210: ACIA_STRUCT            *pACIA_MIDI;
        !           211: 
        !           212: 
        !           213: 
        !           214: 
        !           215: /*--------------------------------------------------------------*/
        !           216: /* Local functions prototypes                                  */
        !           217: /*--------------------------------------------------------------*/
        !           218: 
        !           219: static void            ACIA_Init_Pointers ( ACIA_STRUCT *pAllACIA );
        !           220: 
        !           221: static void            ACIA_Set_Line_IRQ_MFP ( int bit );
        !           222: static Uint8           ACIA_Get_Line_CTS_Dummy ( void );
        !           223: static Uint8           ACIA_Get_Line_DCD_Dummy ( void );
        !           224: static void            ACIA_Set_Line_RTS_Dummy ( int bit );
        !           225: 
        !           226: static void            ACIA_Set_Timers_IKBD ( void *pACIA );
        !           227: static void            ACIA_Start_InterruptHandler_IKBD ( ACIA_STRUCT *pACIA , int InternalCycleOffset );
        !           228: 
        !           229: static Uint8           ACIA_MasterReset ( ACIA_STRUCT *pACIA , Uint8 CR );
        !           230: 
        !           231: static void            ACIA_UpdateIRQ ( ACIA_STRUCT *pACIA );
        !           232: 
        !           233: static Uint8           ACIA_Read_SR ( ACIA_STRUCT *pACIA );
        !           234: static void            ACIA_Write_CR ( ACIA_STRUCT *pACIA , Uint8 CR );
        !           235: static Uint8           ACIA_Read_RDR ( ACIA_STRUCT *pACIA );
        !           236: static void            ACIA_Write_TDR ( ACIA_STRUCT *pACIA , Uint8 TDR );
        !           237: 
        !           238: static void            ACIA_Prepare_TX ( ACIA_STRUCT *pACIA );
        !           239: static void            ACIA_Prepare_RX ( ACIA_STRUCT *pACIA );
        !           240: static void            ACIA_Clock_TX ( ACIA_STRUCT *pACIA );
        !           241: static void            ACIA_Clock_RX ( ACIA_STRUCT *pACIA );
        !           242: 
        !           243: 
        !           244: 
        !           245: 
        !           246: 
        !           247: /*-----------------------------------------------------------------------*/
        !           248: /**
        !           249:  * Init the 2 ACIAs in an Atari ST.
        !           250:  * Both ACIAs have a 500 MHZ TX/RX clock.
        !           251:  * This is called only once, when the emulator starts.
        !           252:  * NOTE : when testing EmuTos on real hardware, it seems the tx/rx is working
        !           253:  * after a cold reset (ST switched on), even if Clock_Divider was not initialized yet.
        !           254:  * The default behaviour is not described in the ACIA's ref doc, but bits
        !           255:  * seem to be transmitted (maybe with errors ?). So we default
        !           256:  * to 9600 bauds to avoid a lock if a program uses tx/rx after a reset.
        !           257:  */
        !           258: void   ACIA_Init ( ACIA_STRUCT *pAllACIA , Uint32 TX_Clock , Uint32 RX_Clock )
        !           259: {
        !           260:        int     i;
        !           261: 
        !           262: 
        !           263:        LOG_TRACE ( TRACE_ACIA, "acia init tx_clock=%d rx_clock=%d\n" , TX_Clock , RX_Clock );
        !           264: 
        !           265:        for ( i=0 ; i<ACIA_MAX_NB ; i++ )
        !           266:        {
        !           267:                memset ( (void *)&(pAllACIA[ i ]) , 0 , sizeof ( ACIA_STRUCT) );
        !           268: 
        !           269:                pAllACIA[ i ].TX_Clock = TX_Clock;
        !           270:                pAllACIA[ i ].RX_Clock = RX_Clock;
        !           271:                pAllACIA[ i ].Clock_Divider = 0;                /* Divider not initialized yet */
        !           272:                pAllACIA[ i ].FirstMasterReset = 1;
        !           273:        }
        !           274: 
        !           275:        /* Set the default common callback functions + other pointers */
        !           276:        ACIA_Init_Pointers ( pAllACIA );
        !           277: }
        !           278: 
        !           279: 
        !           280: 
        !           281: /*-----------------------------------------------------------------------*/
        !           282: /**
        !           283:  * Init some functions/memory pointers for each ACIA.
        !           284:  * This is called at Init and when restoring a memory snapshot.
        !           285:  */
        !           286: static void    ACIA_Init_Pointers ( ACIA_STRUCT *pAllACIA )
        !           287: {
        !           288:        int     i;
        !           289: 
        !           290: 
        !           291:        for ( i=0 ; i<ACIA_MAX_NB ; i++ )
        !           292:        {
        !           293:                /* Set the default common callback functions */
        !           294:                pAllACIA[ i ].Set_Line_IRQ = ACIA_Set_Line_IRQ_MFP;
        !           295:                pAllACIA[ i ].Get_Line_CTS = ACIA_Get_Line_CTS_Dummy;
        !           296:                pAllACIA[ i ].Get_Line_DCD = ACIA_Get_Line_DCD_Dummy;
        !           297:                pAllACIA[ i ].Set_Line_RTS = ACIA_Set_Line_RTS_Dummy;
        !           298:        }
        !           299: 
        !           300:        strcpy ( pAllACIA[ 0 ].ACIA_Name , "ikbd" );
        !           301:        strcpy ( pAllACIA[ 1 ].ACIA_Name , "midi" );
        !           302: 
        !           303:        pACIA_IKBD = &(pAllACIA[ 0 ]);
        !           304:        pACIA_MIDI = &(pAllACIA[ 1 ]);
        !           305: 
        !           306:        pACIA_IKBD->Set_Timers = ACIA_Set_Timers_IKBD;
        !           307: //     pACIA_MIDI->Set_Timers = ACIA_Set_Timers_MIDI;                  /* Not used for now */
        !           308: }
        !           309: 
        !           310: 
        !           311: 
        !           312: 
        !           313: /*-----------------------------------------------------------------------*/
        !           314: /**
        !           315:  * There's no real hardware reset on the ACIA, but as the Reset_ST()
        !           316:  * functions turns off all internal interrupts, we must restart the ACIA's
        !           317:  * interrupt after a reset.
        !           318:  */
        !           319: void   ACIA_Reset ( ACIA_STRUCT *pAllACIA )
        !           320: {
        !           321:        int     i;
        !           322: 
        !           323: 
        !           324:        LOG_TRACE ( TRACE_ACIA, "acia reset\n" );
        !           325: 
        !           326:        for ( i=0 ; i<ACIA_MAX_NB ; i++ )
        !           327:        {
        !           328:                if ( pAllACIA[ i ].Clock_Divider > 0 )                          /* Divider already initialized */
        !           329:                        pAllACIA[ i ].Set_Timers ( &(pAllACIA[ i ]) );          /* Restart the timer */
        !           330:        }
        !           331: }
        !           332: 
        !           333: 
        !           334: 
        !           335: /*-----------------------------------------------------------------------*/
        !           336: /**
        !           337:  * Save/Restore snapshot of local variables ('MemorySnapShot_Store' handles type)
        !           338:  */
        !           339: void   ACIA_MemorySnapShot_Capture ( bool bSave )
        !           340: {
        !           341:        MemorySnapShot_Store(&ACIA_Array, sizeof(ACIA_Array));
        !           342: 
        !           343:        if ( !bSave )                                           /* If restoring */
        !           344:                ACIA_Init_Pointers ( ACIA_Array );              /* Restore pointers */
        !           345: }
        !           346: 
        !           347: 
        !           348: 
        !           349: 
        !           350: /*-----------------------------------------------------------------------*/
        !           351: /**
        !           352:  * Set or reset the ACIA's IRQ signal.
        !           353:  * IRQ signal is inverted (0/low sets irq, 1/high resets irq)
        !           354:  * In the ST, the 2 ACIA's IRQ pins are connected to the same MFP pin,
        !           355:  * so they share the same IRQ bit in the MFP.
        !           356:  */
        !           357: static void    ACIA_Set_Line_IRQ_MFP ( int bit )
        !           358: {
        !           359:        LOG_TRACE ( TRACE_ACIA, "acia set irq line val=%d VBL=%d HBL=%d\n" , bit , nVBLs , nHBL );
        !           360: 
        !           361:        if ( bit == 0 )
        !           362:        {
        !           363:                /* There's a small delay on a real ST between the point in time
        !           364:                * the irq bit is set and the MFP interrupt is triggered - for example
        !           365:                * the "V8 music system" demo depends on this behaviour.
        !           366:                * This 4 cycle delay is handled in mfp.c */
        !           367: 
        !           368:                MFP_GPIP &= ~0x10;                              /* set IRQ signal for GPIP P4 */
        !           369:                MFP_InputOnChannel ( MFP_INT_ACIA , 0 );
        !           370:        }
        !           371:        else
        !           372:        {
        !           373:                /* GPIP I4 - General Purpose Pin Keyboard/MIDI interrupt */
        !           374:                MFP_GPIP |= 0x10;                               /* IRQ bit was reset */
        !           375:        }
        !           376: }
        !           377: 
        !           378: 
        !           379: 
        !           380: 
        !           381: /*-----------------------------------------------------------------------*/
        !           382: /**
        !           383:  * Read the Clear To Send (CTS) pin
        !           384:  * When CTS is high, TDRE should always be set to 0
        !           385:  * Note : this is not connected on an ST, so we always return 0.
        !           386:  */
        !           387: static Uint8   ACIA_Get_Line_CTS_Dummy ( void )
        !           388: {
        !           389:        Uint8           bit;
        !           390: 
        !           391:        bit = 0;
        !           392:        LOG_TRACE ( TRACE_ACIA, "acia get cts=%d VBL=%d HBL=%d\n" , bit , nVBLs , nHBL );
        !           393:        return bit;
        !           394: }
        !           395: 
        !           396: /*-----------------------------------------------------------------------*/
        !           397: /**
        !           398:  * Read the Data Carrier Detect (DCD) pin
        !           399:  * Note : this is not connected on an ST, so we always return 0.
        !           400:  */
        !           401: static Uint8   ACIA_Get_Line_DCD_Dummy ( void )
        !           402: {
        !           403:        Uint8           bit;
        !           404: 
        !           405:        bit = 0;
        !           406:        LOG_TRACE ( TRACE_ACIA, "acia get dcd=%d VBL=%d HBL=%d\n" , bit , nVBLs , nHBL );
        !           407:        return bit;
        !           408: }
        !           409: 
        !           410: /*-----------------------------------------------------------------------*/
        !           411: /**
        !           412:  * Set the Request To Send (RTS) pin.
        !           413:  * Note : this is not connected on an ST, so we ignore it.
        !           414:  */
        !           415: static void    ACIA_Set_Line_RTS_Dummy ( int bit )
        !           416: {
        !           417:        LOG_TRACE ( TRACE_ACIA, "acia set rts val=%d VBL=%d HBL=%d\n" , bit , nVBLs , nHBL );
        !           418: }
        !           419: 
        !           420: 
        !           421: 
        !           422: 
        !           423: /*-----------------------------------------------------------------------*/
        !           424: /**
        !           425:  * Set the required timers to handle RX / TX, depending on the CR_DIVIDE
        !           426:  * value.
        !           427:  * When CR is changed with a new CR_DIVIDE value, we restart the timers.
        !           428:  */
        !           429: static void    ACIA_Set_Timers_IKBD ( void *pACIA )
        !           430: {
        !           431:        ACIA_Start_InterruptHandler_IKBD ( (ACIA_STRUCT *)pACIA , 0 );
        !           432: }
        !           433: 
        !           434: 
        !           435: 
        !           436: 
        !           437: /*-----------------------------------------------------------------------*/
        !           438: /**
        !           439:  * Set a timer to handle the RX / TX bits at the expected baud rate.
        !           440:  * NOTE : on ST, TX_Clock and RX_Clock are the same, so the timer's freq will be
        !           441:  * TX_Clock / Divider and we only need one timer interrupt to handle both RX and TX.
        !           442:  * This freq should be converted to CPU_CYCLE : 1 ACIA cycle = 16 CPU cycles
        !           443:  * (with cpu running at 8 MHz)
        !           444:  * InternalCycleOffset allows to compensate for a != 0 value in PendingInterruptCount
        !           445:  * to keep a constant baud rate.
        !           446:  * TODO : we use a fixed 8 MHz clock and nCpuFreqShift to convert cycles for our
        !           447:  * internal timers in cycInt.c. This should be replaced some days by using
        !           448:  * MachineClocks.CPU_Freq and not using nCpuFreqShift anymore.
        !           449:  */
        !           450: static void    ACIA_Start_InterruptHandler_IKBD ( ACIA_STRUCT *pACIA , int InternalCycleOffset )
        !           451: {
        !           452:        int             Cycles;
        !           453: 
        !           454: 
        !           455: //     Cycles = MachineClocks.CPU_Freq / pACIA->TX_Clock;              /* Convert ACIA cycles in CPU cycles */
        !           456:        Cycles = 8021247 / pACIA->TX_Clock;                             /* Convert ACIA cycles in CPU cycles, for a 8 MHz STF reference */
        !           457:        Cycles *= pACIA->Clock_Divider;
        !           458:        Cycles <<= nCpuFreqShift;                                       /* Compensate for x2 or x4 cpu speed */
        !           459: 
        !           460:        LOG_TRACE ( TRACE_ACIA, "acia %s start timer divider=%d cpu_cycles=%d VBL=%d HBL=%d\n" , pACIA->ACIA_Name ,
        !           461:                pACIA->Clock_Divider , Cycles , nVBLs , nHBL );
        !           462: 
        !           463:        CycInt_AddRelativeInterruptWithOffset ( Cycles, INT_CPU_CYCLE, INTERRUPT_ACIA_IKBD , InternalCycleOffset );
        !           464: }
        !           465: 
        !           466: 
        !           467: 
        !           468: 
        !           469: /*-----------------------------------------------------------------------*/
        !           470: /**
        !           471:  * Interrupt called each time a new bit must be sent / received with the IKBD.
        !           472:  * This interrupt will be called at freq ( 500 MHz / ACIA_CR_COUNTER_DIVIDE )
        !           473:  * On ST, RX_Clock = TX_Clock = 500 MHz.
        !           474:  * We continuously restart the interrupt, taking into account PendingCyclesOver.
        !           475:  */
        !           476: void   ACIA_InterruptHandler_IKBD ( void )
        !           477: {
        !           478:        int     PendingCyclesOver;
        !           479: 
        !           480: 
        !           481:        /* Number of internal cycles we went over for this timer ( <= 0 ) */
        !           482:        /* Used to restart the next timer and keep a constant baud rate */
        !           483:        PendingCyclesOver = -PendingInterruptCount;                     /* >= 0 */
        !           484: 
        !           485:        LOG_TRACE ( TRACE_ACIA, "acia ikbd interrupt handler pending_cyc=%d VBL=%d HBL=%d\n" , PendingCyclesOver , nVBLs , nHBL );
        !           486: 
        !           487:        /* Remove this interrupt from list and re-order */
        !           488:        CycInt_AcknowledgeInterrupt();
        !           489: 
        !           490:        ACIA_Clock_TX ( pACIA_IKBD );
        !           491:        ACIA_Clock_RX ( pACIA_IKBD );
        !           492: 
        !           493:        ACIA_Start_InterruptHandler_IKBD ( pACIA_IKBD , -PendingCyclesOver );   /* Compensate for a != 0 value of PendingCyclesOver */
        !           494: }
        !           495: 
        !           496: 
        !           497: 
        !           498: 
        !           499: /*-----------------------------------------------------------------------*/
        !           500: /**
        !           501:  * Interrupt called each time a new bit must be sent / received with the MIDI.
        !           502:  * This interrupt will be called at freq ( 500 MHz / ACIA_CR_COUNTER_DIVIDE )
        !           503:  * On ST, RX_Clock = TX_Clock = 500 MHz.
        !           504:  */
        !           505: void   ACIA_InterruptHandler_MIDI ( void )
        !           506: {
        !           507:        ACIA_Clock_TX ( pACIA_MIDI );
        !           508:        ACIA_Clock_RX ( pACIA_MIDI );
        !           509: }
        !           510: 
        !           511: 
        !           512: 
        !           513: 
        !           514: /*-----------------------------------------------------------------------*/
        !           515: /**
        !           516:  * - For each access to an ACIA register, a 6 cycles delay is added to the
        !           517:  *   normal 68000 timing for the current CPU instruction. If the instruction
        !           518:  *   accesses several registers at once, the delays are cumulated.
        !           519:  * - An additional delay will also be added to ensure the 68000 clock and
        !           520:  *   the E clock are synchronised ; this delay can add between 0 and 8 cycles
        !           521:  *   to reach the next multiple of 10 cycles. This delay is added only once
        !           522:  *   per CPU instruction.
        !           523:  * These delays are measured for an 8 MHz 68000 CPU.
        !           524:  */
        !           525: void   ACIA_AddWaitCycles ( void )
        !           526: {
        !           527:        int     cycles;
        !           528: 
        !           529:        /* Add a default of 6 cycles for each access */
        !           530:        cycles = 6;
        !           531: 
        !           532:        /* Wait for E clock only if this is the first ACIA access for this instruction */
        !           533:        /* (NOTE : in UAE, movep behaves like several bytes access with different IoAccessBaseAddress, */
        !           534:        /* so only the first movep's access should wait for E Clock) */
        !           535:        if ( ( ( MovepByteNbr == 0 ) && ( IoAccessBaseAddress == IoAccessCurrentAddress ) )
        !           536:          || ( MovepByteNbr == 1 ) )                                    /* First access of a movep */
        !           537:                cycles += M68000_WaitEClock ();
        !           538:        
        !           539:        M68000_WaitState ( cycles );
        !           540: }
        !           541: 
        !           542: 
        !           543: 
        !           544: /*-----------------------------------------------------------------------*/
        !           545: /**
        !           546:  * Return SR for the IKBD's ACIA (0xfffc00)
        !           547:  */
        !           548: void   ACIA_IKBD_Read_SR ( void )
        !           549: {
        !           550:        ACIA_AddWaitCycles ();                                          /* Additional cycles when accessing the ACIA */
        !           551: 
        !           552:        IoMem[0xfffc00] = ACIA_Read_SR ( pACIA_IKBD );
        !           553: 
        !           554:        if (LOG_TRACE_LEVEL(TRACE_ACIA))
        !           555:        {
        !           556:                int FrameCycles, HblCounterVideo, LineCycles;
        !           557:                Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
        !           558:                LOG_TRACE_PRINT("acia %s read fffc00 sr=0x%02x video_cyc=%d %d@%d pc=%x instr_cycle %d\n", pACIA_IKBD->ACIA_Name ,
        !           559:                                IoMem[0xfffc00], FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
        !           560:        }
        !           561: }
        !           562: 
        !           563: 
        !           564: 
        !           565: 
        !           566: /*-----------------------------------------------------------------------*/
        !           567: /**
        !           568:  * Return RDR for the IKBD's ACIA (0xfffc02) : receive a byte from the IKBD
        !           569:  */
        !           570: void   ACIA_IKBD_Read_RDR ( void )
        !           571: {
        !           572:        ACIA_AddWaitCycles ();                                          /* Additional cycles when accessing the ACIA */
        !           573: 
        !           574:        IoMem[0xfffc02] = ACIA_Read_RDR ( pACIA_IKBD );
        !           575: 
        !           576:        if (LOG_TRACE_LEVEL(TRACE_IKBD_ACIA))
        !           577:        {
        !           578:                int FrameCycles, HblCounterVideo, LineCycles;
        !           579:                Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
        !           580:                LOG_TRACE_PRINT("acia %s read fffc02 rdr=0x%02x video_cyc=%d %d@%d pc=%x instr_cycle %d\n", pACIA_IKBD->ACIA_Name ,
        !           581:                                IoMem[0xfffc02], FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
        !           582:        }
        !           583: }
        !           584: 
        !           585: 
        !           586: 
        !           587: 
        !           588: /*-----------------------------------------------------------------------*/
        !           589: /**
        !           590:  * Write to CR in the IKBD's ACIA (0xfffc00)
        !           591:  */
        !           592: void   ACIA_IKBD_Write_CR ( void )
        !           593: {
        !           594:        int FrameCycles, HblCounterVideo, LineCycles;
        !           595: 
        !           596:        ACIA_AddWaitCycles ();                                          /* Additional cycles when accessing the ACIA */
        !           597: 
        !           598:        Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
        !           599:        LOG_TRACE(TRACE_IKBD_ACIA, "acia %s write fffc00 cr=0x%02x video_cyc=%d %d@%d pc=%x instr_cycle %d\n", pACIA_IKBD->ACIA_Name ,
        !           600:                                IoMem[0xfffc00], FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
        !           601: 
        !           602:        ACIA_Write_CR ( pACIA_IKBD , IoMem[0xfffc00] );
        !           603: }
        !           604: 
        !           605: 
        !           606: 
        !           607: 
        !           608: /*-----------------------------------------------------------------------*/
        !           609: /**
        !           610:  * Write to TDR in the IKBD's ACIA (0xfffc02) : send a byte to the IKBD
        !           611:  */
        !           612: void   ACIA_IKBD_Write_TDR ( void )
        !           613: {
        !           614:        int FrameCycles, HblCounterVideo, LineCycles;
        !           615: 
        !           616:        ACIA_AddWaitCycles ();                                          /* Additional cycles when accessing the ACIA */
        !           617: 
        !           618:        Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
        !           619:        LOG_TRACE(TRACE_IKBD_ACIA, "acia %s write fffc02 tdr=0x%02x video_cyc=%d %d@%d pc=%x instr_cycle %d\n", pACIA_IKBD->ACIA_Name ,
        !           620:                                IoMem[0xfffc02], FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
        !           621: 
        !           622:        ACIA_Write_TDR ( pACIA_IKBD , IoMem[0xfffc02] );
        !           623: }
        !           624: 
        !           625: 
        !           626: 
        !           627: 
        !           628: /*----------------------------------------------------------------------*/
        !           629: /* The part below is the real core of the 6850's emulation.            */
        !           630: /*                                                                     */
        !           631: /* This core is not correlated to any specific machine. All the specific*/
        !           632: /* code between the 6850 and the rest of Hatari is called through some */
        !           633: /* callback functions (see above).                                     */
        !           634: /*----------------------------------------------------------------------*/
        !           635: 
        !           636: 
        !           637: 
        !           638: /*-----------------------------------------------------------------------*/
        !           639: /**
        !           640:  * Reset an ACIA.
        !           641:  * There's no RESET pin on the MC6850, so the only way to reset the ACIA
        !           642:  * is to set bit 0 an 1 to 0x03 in the CR to force a master reset.
        !           643:  * This will clear SR (except CTS and DCD) and halt/initialize both the
        !           644:  * receiver and transmitter.
        !           645:  * This also returns the new state of the RTS bit, that must be updated
        !           646:  * in ACIA_Write_CR.
        !           647:  */
        !           648: static Uint8   ACIA_MasterReset ( ACIA_STRUCT *pACIA , Uint8 CR )
        !           649: {
        !           650:        Uint8           dcd_bit;
        !           651:        Uint8           cts_bit;
        !           652:        Uint8           rts_bit;
        !           653: 
        !           654: 
        !           655:        LOG_TRACE ( TRACE_ACIA, "acia %s master reset VBL=%d HBL=%d\n" , pACIA->ACIA_Name , nVBLs , nHBL );
        !           656:        
        !           657:        dcd_bit = pACIA->Get_Line_DCD ();
        !           658:        cts_bit = pACIA->Get_Line_CTS ();
        !           659: 
        !           660:        pACIA->SR = ACIA_SR_BIT_TDRE | ( dcd_bit << 2 ) | ( cts_bit << 3 );
        !           661: 
        !           662:        pACIA->TX_State = ACIA_STATE_IDLE;
        !           663:        pACIA->TSR = 0;
        !           664:        pACIA->TX_Size = 0;
        !           665:        pACIA->TX_SendBrk = 0;
        !           666: 
        !           667:        pACIA->RX_State = ACIA_STATE_IDLE;
        !           668:        pACIA->RSR = 0;
        !           669:        pACIA->RX_Size = 0;
        !           670:        pACIA->RX_Overrun = 0;
        !           671: 
        !           672:        /* On Master Reset, IRQ line is high */
        !           673:        /* If it's the 1st reset, RTS should be high, else RTS depends on CR bit 5 and 6 */
        !           674:        pACIA->Set_Line_IRQ ( 1 );                                      /* IRQ line goes high */
        !           675:        if ( pACIA->FirstMasterReset == 1 )
        !           676:        {
        !           677:                pACIA->FirstMasterReset = 0;
        !           678:                rts_bit = 1;                                            /* RTS line goes high */
        !           679:        }
        !           680:        else
        !           681:                rts_bit = ( ACIA_CR_TRANSMITTER_CONTROL ( CR ) == 0x02 ) ? 1 : 0;
        !           682: 
        !           683:        return rts_bit;
        !           684: }
        !           685: 
        !           686: 
        !           687: 
        !           688: 
        !           689: /*-----------------------------------------------------------------------*/
        !           690: /**
        !           691:  * Check if the IRQ must be changed in SR.
        !           692:  * When there's a change, we must change the IRQ line too.
        !           693:  */
        !           694: static void    ACIA_UpdateIRQ ( ACIA_STRUCT *pACIA )
        !           695: {
        !           696:        Uint8           irq_bit_new;
        !           697: 
        !           698:        irq_bit_new = 0;
        !           699: 
        !           700:        if ( ACIA_CR_RECEIVE_INTERRUPT_ENABLE ( pACIA->CR )             /* Check for RX causes of interrupt */
        !           701:          && ( ( pACIA->SR & ( ACIA_SR_BIT_RDRF | ACIA_SR_BIT_DCD ) )
        !           702:            || ( pACIA->RX_Overrun ) ) )
        !           703:          irq_bit_new = ACIA_SR_BIT_IRQ;
        !           704: 
        !           705:        if ( pACIA->TX_EnableInt                                        /* Check for TX causes of interrupt */
        !           706:          && ( pACIA->SR & ACIA_SR_BIT_TDRE )
        !           707:          && ( ( pACIA->SR & ACIA_SR_BIT_CTS ) == 0 ) )
        !           708:          irq_bit_new = ACIA_SR_BIT_IRQ;
        !           709:        
        !           710:        /* Update SR and IRQ line if a change happened */
        !           711:        if ( ( pACIA->SR & ACIA_SR_BIT_IRQ ) != irq_bit_new )
        !           712:        {
        !           713:                LOG_TRACE ( TRACE_ACIA, "acia %s update irq irq_new=%d VBL=%d HBL=%d\n" , pACIA->ACIA_Name , irq_bit_new?1:0 , nVBLs , nHBL );
        !           714: 
        !           715:                if ( irq_bit_new )
        !           716:                {
        !           717:                        pACIA->SR |= ACIA_SR_BIT_IRQ;                   /* Set IRQ bit */
        !           718:                        pACIA->Set_Line_IRQ ( 0 );                      /* IRQ line goes low */
        !           719:                }
        !           720:                else
        !           721:                {
        !           722:                        pACIA->SR &= ~ACIA_SR_BIT_IRQ;                  /* Clear IRQ bit */
        !           723:                        pACIA->Set_Line_IRQ ( 1 );                      /* IRQ line goes high */
        !           724:                }
        !           725:        }
        !           726: }
        !           727: 
        !           728: 
        !           729: 
        !           730: /*-----------------------------------------------------------------------*/
        !           731: /**
        !           732:  * Read SR.
        !           733:  * Also update CTS ; when CTS is high, TDRE should always be masked to 0.
        !           734:  */
        !           735: static Uint8   ACIA_Read_SR ( ACIA_STRUCT *pACIA )
        !           736: {
        !           737:        Uint8   SR;
        !           738: 
        !           739: 
        !           740:        if ( pACIA->Get_Line_CTS() == 1 )
        !           741:                pACIA->SR |= ACIA_SR_BIT_CTS;
        !           742:        else
        !           743:                pACIA->SR &= ~ACIA_SR_BIT_CTS;
        !           744: 
        !           745:        SR = pACIA->SR;
        !           746:        pACIA->SR_Read = 1;                                             /* Used in ACIA_Read_RDR to clear Overrun and DCD IRQ */
        !           747: 
        !           748:        if ( SR & ACIA_SR_BIT_CTS )
        !           749:                SR &= ~ACIA_SR_BIT_TDRE;                                /* Inhibit TDRE when CTS is set */
        !           750: 
        !           751:        LOG_TRACE ( TRACE_ACIA, "acia %s read sr data=0x%02x VBL=%d HBL=%d\n" , pACIA->ACIA_Name , SR , nVBLs , nHBL );
        !           752: 
        !           753:        return SR;
        !           754: }
        !           755: 
        !           756: 
        !           757: 
        !           758: 
        !           759: /*-----------------------------------------------------------------------*/
        !           760: /**
        !           761:  * Write to CR.
        !           762:  */
        !           763: static void    ACIA_Write_CR ( ACIA_STRUCT *pACIA , Uint8 CR )
        !           764: {
        !           765:        int     Divide;
        !           766:        int     Force_rts_bit;
        !           767:        Uint8   rts_bit=0;
        !           768: 
        !           769:        LOG_TRACE ( TRACE_ACIA, "acia %s write cr data=0x%02x VBL=%d HBL=%d\n" , pACIA->ACIA_Name , CR , nVBLs , nHBL );
        !           770: 
        !           771:        /* Bit 0 and 1 : Counter Divide */
        !           772:        Divide = ACIA_CR_COUNTER_DIVIDE ( CR );
        !           773:        if ( Divide == 0x03 )
        !           774:        {
        !           775:                Force_rts_bit = ACIA_MasterReset ( pACIA , CR );        /* Special behaviour for RTS after a master reset */
        !           776:        }
        !           777:        else
        !           778:        {
        !           779:                if ( ACIA_CR_COUNTER_DIVIDE ( CR ) != ACIA_CR_COUNTER_DIVIDE ( pACIA->CR ) )
        !           780:                {
        !           781:                        pACIA->Clock_Divider = ACIA_Counter_Divide[ Divide ];
        !           782:                        pACIA->Set_Timers ( pACIA );                    /* Set a timer at the baud rate computed from Clock_Divider */
        !           783:                }
        !           784:                Force_rts_bit = -1;                                     /* Don't force RTS bit, use bit 5/6 in CR */
        !           785:        }
        !           786: 
        !           787:        /* Bits 2, 3 and 4 : word select */
        !           788:        /* Don't do anything here, see ACIA_Prepare_TX and ACIA_Prepare_RX */
        !           789: 
        !           790:        /* Bits 5 and 6 : transmitter control */
        !           791:        pACIA->TX_EnableInt = 0;
        !           792:        pACIA->TX_SendBrk = 0;
        !           793:        switch ( ACIA_CR_TRANSMITTER_CONTROL ( CR ) )
        !           794:        {
        !           795:          case 0x00 :
        !           796:                rts_bit = 0;
        !           797:                break;
        !           798:          case 0x01 :
        !           799:                rts_bit = 0;
        !           800:                pACIA->TX_EnableInt = 1;
        !           801:                break;
        !           802:          case 0x02 :
        !           803:                rts_bit = 1;
        !           804:                break;
        !           805:          case 0x03 :
        !           806:                rts_bit = 0;
        !           807:                pACIA->TX_SendBrk = 1;                                  /* We will send break bit until CR is changed */
        !           808:                break;
        !           809:        }
        !           810: 
        !           811:        if ( Force_rts_bit >= 0 )
        !           812:                rts_bit = Force_rts_bit;                                /* Use the value from ACIA_MasterReset */
        !           813:        pACIA->Set_Line_RTS ( rts_bit );
        !           814: 
        !           815:        /* Bits 7 : receive interrupt enable, see ACIA_UpdateIRQ */
        !           816: 
        !           817:        pACIA->CR = CR;
        !           818: 
        !           819:        ACIA_UpdateIRQ ( pACIA );
        !           820: }
        !           821: 
        !           822: 
        !           823: 
        !           824: 
        !           825: /*-----------------------------------------------------------------------*/
        !           826: /**
        !           827:  * Read RDR. This will clear RDRF and PE.
        !           828:  * - OVRN / DCD bits are cleared if SR was read before reading RDR.
        !           829:  * - OVRN bit is set only when reading RDR, not when the actual overrun happened
        !           830:  *   during ACIA_Clock_RX.
        !           831:  * - IRQ bit should be updated depending on the new values of BIT_RDRF,
        !           832:  *   BIT_DCD and BIT_OVRN.
        !           833:  */
        !           834: static Uint8   ACIA_Read_RDR ( ACIA_STRUCT *pACIA )
        !           835: {
        !           836:        pACIA->SR &= ~( ACIA_SR_BIT_RDRF | ACIA_SR_BIT_PE );
        !           837: 
        !           838:        /* If we read RDR after reading SR, we clear OVRN / DCD bits */
        !           839:        if ( pACIA->SR_Read == 1 )
        !           840:        {
        !           841:                pACIA->SR_Read = 0;
        !           842:                pACIA->SR &= ~( ACIA_SR_BIT_DCD | ACIA_SR_BIT_OVRN );
        !           843:                if ( pACIA->Get_Line_DCD () == 1 )
        !           844:                        pACIA->SR |= ACIA_SR_BIT_DCD;
        !           845:        }
        !           846: 
        !           847:        if ( pACIA->RX_Overrun )
        !           848:        {  
        !           849:                pACIA->SR |= ACIA_SR_BIT_OVRN;
        !           850:                pACIA->RX_Overrun = 0;
        !           851:        }
        !           852: 
        !           853:        ACIA_UpdateIRQ ( pACIA );
        !           854: 
        !           855:        LOG_TRACE ( TRACE_ACIA, "acia %s read rdr data=0x%02x new sr=0x%02x overrun=%s VBL=%d HBL=%d\n" , pACIA->ACIA_Name , pACIA->RDR ,
        !           856:                pACIA->SR , ( pACIA->SR & ACIA_SR_BIT_OVRN ) ? "yes" : "no" , nVBLs , nHBL );
        !           857: 
        !           858:        return pACIA->RDR;
        !           859: }
        !           860: 
        !           861: 
        !           862: 
        !           863: 
        !           864: /*-----------------------------------------------------------------------*/
        !           865: /**
        !           866:  * Write to TDR.
        !           867:  * If the TX process is idle, we should not prepare a new transfer
        !           868:  * immediately, to ensure that BIT_TDRE remains clear until the next bit
        !           869:  * is sent (BIT_TDRE will be set again in ACIA_Clock_TX).
        !           870:  */
        !           871: static void    ACIA_Write_TDR ( ACIA_STRUCT *pACIA , Uint8 TDR )
        !           872: {
        !           873:        LOG_TRACE ( TRACE_ACIA, "acia %s write tdr data=0x%02x overwrite=%s tx_state=%d VBL=%d HBL=%d\n" , pACIA->ACIA_Name , TDR ,
        !           874:                ( pACIA->SR & ACIA_SR_BIT_TDRE ) ? "no" : "yes" , pACIA->TX_State , nVBLs , nHBL );
        !           875: 
        !           876:        pACIA->TDR = TDR;
        !           877:        pACIA->SR &= ~ACIA_SR_BIT_TDRE;                                 /* TDR is not empty anymore */
        !           878: 
        !           879:        ACIA_UpdateIRQ ( pACIA );
        !           880: }
        !           881: 
        !           882: 
        !           883: 
        !           884: 
        !           885: /*-----------------------------------------------------------------------*/
        !           886: /**
        !           887:  * Prepare a new transfer. Copy TDR to TSR and initialize parity, data size
        !           888:  * and stop bits.
        !           889:  * Transfer will then start at the next call of ACIA_Clock_TX
        !           890:  */
        !           891: static void    ACIA_Prepare_TX ( ACIA_STRUCT *pACIA )
        !           892: {
        !           893:        pACIA->TSR = pACIA->TDR;
        !           894:        pACIA->TX_Parity = 0;
        !           895:        pACIA->TX_Size = ACIA_Serial_Params[ ACIA_CR_WORD_SELECT ( pACIA->CR ) ].DataBits;
        !           896:        pACIA->TX_StopBits = ACIA_Serial_Params[ ACIA_CR_WORD_SELECT ( pACIA->CR ) ].StopBits;
        !           897: 
        !           898:        pACIA->SR |= ACIA_SR_BIT_TDRE;                                  /* TDR was copied to TSR. TDR is now empty */
        !           899: 
        !           900:        LOG_TRACE ( TRACE_ACIA, "acia %s prepare tx tsr=0x%02x size=%d stop=%d VBL=%d HBL=%d\n" , pACIA->ACIA_Name , pACIA->TSR ,
        !           901:                pACIA->TX_Size , pACIA->TX_StopBits , nVBLs , nHBL );
        !           902: }
        !           903: 
        !           904: 
        !           905: 
        !           906: 
        !           907: /*-----------------------------------------------------------------------*/
        !           908: /**
        !           909:  * Prepare a new reception. Initialize parity, data size and stop bits.
        !           910:  */
        !           911: static void    ACIA_Prepare_RX ( ACIA_STRUCT *pACIA )
        !           912: {
        !           913:        pACIA->RSR = 0;
        !           914:        pACIA->RX_Parity = 0;
        !           915:        pACIA->RX_Size = ACIA_Serial_Params[ ACIA_CR_WORD_SELECT ( pACIA->CR ) ].DataBits;
        !           916:        pACIA->RX_StopBits = ACIA_Serial_Params[ ACIA_CR_WORD_SELECT ( pACIA->CR ) ].StopBits;
        !           917: 
        !           918:        LOG_TRACE ( TRACE_ACIA, "acia %s prepare rx size=%d stop=%d VBL=%d HBL=%d\n" , pACIA->ACIA_Name ,
        !           919:                pACIA->RX_Size , pACIA->RX_StopBits , nVBLs , nHBL );
        !           920: }
        !           921: 
        !           922: 
        !           923: 
        !           924: 
        !           925: /*-----------------------------------------------------------------------*/
        !           926: /**
        !           927:  * Write a new bit on the TX line each time the TX clock expires.
        !           928:  * This will send TDR over the serial line, using TSR, with additional
        !           929:  * parity and start/stop bits.
        !           930:  * We send bit 0 of TSR, then TSR is shifted to the right.
        !           931:  */
        !           932: static void    ACIA_Clock_TX ( ACIA_STRUCT *pACIA )
        !           933: {
        !           934:        int     StateNext;
        !           935:        Uint8   tx_bit;
        !           936: 
        !           937: 
        !           938:        LOG_TRACE ( TRACE_ACIA, "acia %s clock_tx tx_state=%d VBL=%d HBL=%d\n" , pACIA->ACIA_Name , pACIA->TX_State , nVBLs , nHBL );
        !           939: 
        !           940:        StateNext = -1;
        !           941:        switch ( pACIA->TX_State )
        !           942:        {
        !           943:          case ACIA_STATE_IDLE :
        !           944:                if ( pACIA->TX_SendBrk )
        !           945:                {
        !           946:                        pACIA->Set_Line_TX ( 0 );                       /* Send 1 break bit */
        !           947:                        break;
        !           948:                }
        !           949: 
        !           950:                /* If TDR is not empty when we are in idle state, */
        !           951:                /* this means we have a new byte to send */
        !           952:                if ( ( pACIA->SR & ACIA_SR_BIT_TDRE ) == 0 )
        !           953:                        ACIA_Prepare_TX ( pACIA );
        !           954: 
        !           955:                if ( pACIA->TX_Size == 0 )                              /* TSR is empty */
        !           956:                        pACIA->Set_Line_TX ( 1 );                       /* Send stop bits when idle */
        !           957: 
        !           958:                else                                                    /* TSR has some new bits to transfer */
        !           959:                {
        !           960:                        pACIA->Set_Line_TX ( 0 );                       /* Send 1 start bit */
        !           961:                        StateNext = ACIA_STATE_DATA_BIT;
        !           962:                }
        !           963:                break;
        !           964: 
        !           965:          case ACIA_STATE_DATA_BIT :
        !           966:                tx_bit = pACIA->TSR & 1;                                /* New bit to send */
        !           967:                pACIA->Set_Line_TX ( tx_bit );
        !           968:                pACIA->TX_Parity ^= tx_bit;
        !           969:                pACIA->TSR >>= 1;
        !           970:                pACIA->TX_Size--;
        !           971: 
        !           972:                if ( pACIA->TX_Size == 0 )
        !           973:                {
        !           974:                        if ( ACIA_Serial_Params[ ACIA_CR_WORD_SELECT ( pACIA->CR ) ].Parity != ACIA_PARITY_NONE )
        !           975:                                StateNext = ACIA_STATE_PARITY_BIT;
        !           976:                        else
        !           977:                                StateNext = ACIA_STATE_STOP_BIT;        /* No parity */
        !           978:                }
        !           979:                break;
        !           980: 
        !           981:          case ACIA_STATE_PARITY_BIT :
        !           982:                if ( ACIA_Serial_Params[ ACIA_CR_WORD_SELECT ( pACIA->CR ) ].Parity == ACIA_PARITY_EVEN )
        !           983:                        pACIA->Set_Line_TX ( pACIA->TX_Parity );
        !           984:                else
        !           985:                        pACIA->Set_Line_TX ( ( ~pACIA->TX_Parity ) & 1 );
        !           986: 
        !           987:                StateNext = ACIA_STATE_STOP_BIT;
        !           988:                break;
        !           989: 
        !           990:          case ACIA_STATE_STOP_BIT :
        !           991:                pACIA->Set_Line_TX ( 1 );                               /* Send 1 stop bit */
        !           992:                pACIA->TX_StopBits--;
        !           993: 
        !           994:                if ( pACIA->TX_StopBits == 0 )                          /* All stop bits were sent : transfer is complete */
        !           995:                {
        !           996:                        StateNext = ACIA_STATE_IDLE;                    /* Go to idle state to see if a new TDR need to be sent */
        !           997:                }
        !           998:                break;
        !           999:        }
        !          1000: 
        !          1001:        ACIA_UpdateIRQ ( pACIA );
        !          1002: 
        !          1003:        if ( StateNext >= 0 )
        !          1004:                pACIA->TX_State = StateNext;                            /* Go to a new state */
        !          1005: }
        !          1006: 
        !          1007: 
        !          1008: 
        !          1009: 
        !          1010: /*-----------------------------------------------------------------------*/
        !          1011: /**
        !          1012:  * Handle a new bit on the RX line each time the RX clock expires.
        !          1013:  * This will fill RDR with bits received from the serial line, using RSR.
        !          1014:  * Incoming bits are stored in bit 7 of RSR, then RSR is shifted to the right.
        !          1015:  */
        !          1016: static void    ACIA_Clock_RX ( ACIA_STRUCT *pACIA )
        !          1017: {
        !          1018:        int     StateNext;
        !          1019:        Uint8   rx_bit;
        !          1020: 
        !          1021: 
        !          1022:        rx_bit = pACIA->Get_Line_RX();
        !          1023: 
        !          1024:        LOG_TRACE ( TRACE_ACIA, "acia %s clock_rx rx_state=%d bit=%d VBL=%d HBL=%d\n" , pACIA->ACIA_Name , pACIA->RX_State , rx_bit , nVBLs , nHBL );
        !          1025: 
        !          1026:        StateNext = -1;
        !          1027:        switch ( pACIA->RX_State )
        !          1028:        {
        !          1029:          case ACIA_STATE_IDLE :
        !          1030:                if ( rx_bit == 0 )                                      /* Receive 1 start bit */
        !          1031:                {
        !          1032:                        ACIA_Prepare_RX ( pACIA );
        !          1033:                        StateNext = ACIA_STATE_DATA_BIT;
        !          1034:                }
        !          1035:                break;                                                  /* If no start bit, we stay in idle state */
        !          1036: 
        !          1037:          case ACIA_STATE_DATA_BIT :
        !          1038:                if ( rx_bit )
        !          1039:                        pACIA->RSR |= 0x80;
        !          1040:                pACIA->RX_Parity ^= rx_bit;
        !          1041:                pACIA->RX_Size--;
        !          1042: 
        !          1043:                if ( pACIA->RX_Size > 0 )                               /* All bits were not received yet */
        !          1044:                {
        !          1045:                        pACIA->RSR >>= 1;
        !          1046:                }
        !          1047:                else
        !          1048:                {
        !          1049: // [NP] : MC6850 doc is not very clear "the overrun condition begins at the midpoint of the last bit
        !          1050: // of the second character received [...]". Is it the last bit of the data word, or the stop bit ?
        !          1051: // It makes more sense to check for overrun after the stop bit, when RSR should be copied to RDR,
        !          1052: // because RDR could be read between the last data bit and the stop bit, so RX_Overrun and
        !          1053: // ACIA_SR_BIT_OVRN would need to be cancelled.
        !          1054: //                     if ( pACIA->SR & ACIA_SR_BIT_RDRF )
        !          1055: //                     {
        !          1056: //                             LOG_TRACE ( TRACE_ACIA, "acia %s clock_rx overrun rsr=0x%02x VBL=%d HBL=%d\n" ,
        !          1057: //                                     pACIA->ACIA_Name , pACIA->RSR , nVBLs , nHBL );
        !          1058: //                             pACIA->RX_Overrun = 1;                  /* Bit in SR will be set when reading RDR */
        !          1059: //                     }
        !          1060:                        if ( ACIA_Serial_Params[ ACIA_CR_WORD_SELECT ( pACIA->CR ) ].Parity != ACIA_PARITY_NONE )
        !          1061:                                StateNext = ACIA_STATE_PARITY_BIT;
        !          1062:                        else
        !          1063:                                StateNext = ACIA_STATE_STOP_BIT;        /* No parity */
        !          1064:                }
        !          1065:                break;
        !          1066: 
        !          1067:          case ACIA_STATE_PARITY_BIT :
        !          1068:                if ( ( ACIA_Serial_Params[ ACIA_CR_WORD_SELECT ( pACIA->CR ) ].Parity == ACIA_PARITY_EVEN )
        !          1069:                    && ( pACIA->RX_Parity != rx_bit ) )
        !          1070:                        pACIA->SR |= ACIA_SR_BIT_PE;
        !          1071: 
        !          1072:                else if ( pACIA->RX_Parity == rx_bit )                  /* Odd parity */
        !          1073:                        pACIA->SR |= ACIA_SR_BIT_PE;
        !          1074: 
        !          1075:                if ( pACIA->SR & ACIA_SR_BIT_PE )
        !          1076:                        LOG_TRACE ( TRACE_ACIA, "acia %s clock_rx parity error VBL=%d HBL=%d\n" , pACIA->ACIA_Name , nVBLs , nHBL );
        !          1077: 
        !          1078:                StateNext = ACIA_STATE_STOP_BIT;
        !          1079:                break;
        !          1080: 
        !          1081:          case ACIA_STATE_STOP_BIT :
        !          1082:                if ( rx_bit == 1 )                                      /* Wait for 1 or 2 "1" stop bits */
        !          1083:                {
        !          1084:                        pACIA->RX_StopBits--;
        !          1085:                        if ( pACIA->RX_StopBits == 0 )                  /* All stop bits were received : reception is complete */
        !          1086:                        {
        !          1087:                                pACIA->SR &= ~ACIA_SR_BIT_FE;
        !          1088:                                
        !          1089:                                if ( ( pACIA->SR & ACIA_SR_BIT_RDRF ) == 0 )
        !          1090:                                {
        !          1091:                                        pACIA->RDR = pACIA->RSR;
        !          1092:                                        pACIA->SR |= ACIA_SR_BIT_RDRF;
        !          1093:                                        LOG_TRACE ( TRACE_ACIA, "acia %s clock_rx received rdr=0x%02x VBL=%d HBL=%d\n" ,
        !          1094:                                                pACIA->ACIA_Name , pACIA->RDR , nVBLs , nHBL );
        !          1095:                                }
        !          1096:                                else
        !          1097:                                {
        !          1098:                                        LOG_TRACE ( TRACE_ACIA, "acia %s clock_rx overrun rsr=0x%02x unread rdr=0x%02x VBL=%d HBL=%d\n" ,
        !          1099:                                                pACIA->ACIA_Name , pACIA->RSR , pACIA->RDR , nVBLs , nHBL );
        !          1100:                                        pACIA->RX_Overrun = 1;          /* Bit in SR will be set when reading RDR */
        !          1101:                                }
        !          1102:                                StateNext = ACIA_STATE_IDLE;            /* Go to idle state and wait for start bit */
        !          1103:                        }
        !          1104:                }
        !          1105:                else                                                    /* Not a valid stop bit */
        !          1106:                {
        !          1107:                        LOG_TRACE ( TRACE_ACIA, "acia %s clock_rx framing error VBL=%d HBL=%d\n" , pACIA->ACIA_Name , nVBLs , nHBL );
        !          1108: 
        !          1109:                        /* According to the A6850 doc, RSR is copied to RDR in case of a framing error */
        !          1110:                        /* (Should be the same for the MC6850 ?) */
        !          1111:                        pACIA->SR |= ACIA_SR_BIT_FE;
        !          1112:                        pACIA->RDR = pACIA->RSR;
        !          1113:                        StateNext = ACIA_STATE_IDLE;                    /* Go to idle state and wait for start bit */
        !          1114:                }
        !          1115:                break;
        !          1116:        }
        !          1117: 
        !          1118:        ACIA_UpdateIRQ ( pACIA );
        !          1119: 
        !          1120:        if ( StateNext >= 0 )
        !          1121:                pACIA->RX_State = StateNext;                            /* Go to a new state */
        !          1122: }
        !          1123: 
        !          1124: 
        !          1125: 

unix.superglobalmegacorp.com

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