/***************************************************************************** * pce * *****************************************************************************/ /***************************************************************************** * File name: src/chipset/e6850.c * * Created: 2013-05-31 by Hampa Hug * * Copyright: (C) 2013-2015 Hampa Hug * *****************************************************************************/ /***************************************************************************** * This program is free software. You can redistribute it and / or modify it * * under the terms of the GNU General Public License version 2 as published * * by the Free Software Foundation. * * * * This program is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY, without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * * Public License for more details. * *****************************************************************************/ #include #include #include "e6850.h" #ifndef DEBUG_ACIA #define DEBUG_ACIA 0 #endif #define E6850_SR_RDRF 0x01 #define E6850_SR_TDRE 0x02 #define E6850_SR_DCD 0x04 #define E6850_SR_CTS 0x08 #define E6850_SR_FE 0x10 #define E6850_SR_OVRN 0x20 #define E6850_SR_PE 0x40 #define E6850_SR_IRQ 0x80 void e6850_init (e6850_t *acia) { acia->send_ext = NULL; acia->send_fct = NULL; acia->recv_ext = NULL; acia->recv_fct = NULL; acia->irq_val = 0; acia->irq_ext = NULL; acia->irq_fct = NULL; } void e6850_free (e6850_t *acia) { } void e6850_set_irq_fct (e6850_t *acia, void *ext, void *fct) { acia->irq_ext = ext; acia->irq_fct = fct; } void e6850_set_send_fct (e6850_t *acia, void *ext, void *fct) { acia->send_ext = ext; acia->send_fct = fct; } void e6850_set_recv_fct (e6850_t *acia, void *ext, void *fct) { acia->recv_ext = ext; acia->recv_fct = fct; } static void e6850_set_irq (e6850_t *acia, int val) { val = (val != 0); if (acia->irq_val != val) { acia->irq_val = val; #if DEBUG_ACIA >= 2 fprintf (stderr, "ACIA: irq = %d\n", val); #endif if (acia->irq_fct != NULL) { acia->irq_fct (acia->irq_ext, acia->irq_val); } } } static void e6850_check_int (e6850_t *acia) { acia->sr &= ~E6850_SR_IRQ; if ((acia->cr & 0x80) && (acia->sr & E6850_SR_RDRF)) { acia->sr |= E6850_SR_IRQ; } else if (((acia->cr & 0x60) == 0x20) && (acia->sr & E6850_SR_TDRE)) { acia->sr |= E6850_SR_IRQ; } if ((acia->cr & 3) == 3) { /* master reset */ acia->sr &= ~E6850_SR_IRQ; } e6850_set_irq (acia, acia->sr & E6850_SR_IRQ); } static void e6850_send_tdr (e6850_t *acia) { acia->tsr = acia->tdr; acia->sr |= E6850_SR_TDRE; acia->send_timer = (unsigned long) acia->char_bits << acia->clock_div; e6850_check_int (acia); } static void e6850_send_tsr (e6850_t *acia) { if (acia->send_fct != NULL) { acia->send_fct (acia->send_ext, acia->tsr); } if ((acia->sr & E6850_SR_TDRE) == 0) { e6850_send_tdr (acia); } } static void e6850_receive_rsr (e6850_t *acia) { unsigned char val; if (acia->recv_fct == NULL) { return; } if (acia->recv_fct (acia->recv_ext, &val)) { return; } acia->rsr = val; acia->recv_timer = (unsigned long) acia->char_bits << acia->clock_div; } static void e6850_receive_rdr (e6850_t *acia) { acia->rdr = acia->rsr; acia->sr |= E6850_SR_RDRF; e6850_receive_rsr (acia); e6850_check_int (acia); } unsigned char e6850_get_status (e6850_t *acia) { return (acia->sr); } unsigned char e6850_get_data (e6850_t *acia) { e6850_set_irq (acia, 0); acia->sr &= ~E6850_SR_RDRF; e6850_check_int (acia); #if DEBUG_ACIA >= 2 fprintf (stderr, "ACIA: read data %02X\n", acia->rdr); #endif return (acia->rdr); } void e6850_set_control (e6850_t *acia, unsigned char val) { unsigned char ws; if (acia->cr == val) { return; } #if DEBUG_ACIA >= 1 fprintf (stderr, "ACIA: set cr %02X\n", val); #endif acia->cr = val; switch (val & 3) { case 0: acia->clock_div = 0; break; case 1: acia->clock_div = 4; break; case 2: acia->clock_div = 6; break; default: acia->clock_div = 0; acia->recv_timer = 0; #if DEBUG_ACIA >= 1 fprintf (stderr, "ACIA: master reset\n"); #endif return; } ws = (val >> 2) & 7; acia->data_bits = (ws & 2) ? 8 : 7; acia->stop_bits = (ws & 1) ? 1 : 2; if (ws == 4) { acia->stop_bits = 2; } acia->char_bits = acia->data_bits + acia->stop_bits; if ((ws != 4) && (ws != 5)) { acia->char_bits += 1; } e6850_check_int (acia); } void e6850_set_data (e6850_t *acia, unsigned char val) { e6850_set_irq (acia, 0); acia->tdr = val; acia->sr &= ~E6850_SR_TDRE; e6850_check_int (acia); if (acia->send_timer == 0) { e6850_send_tdr (acia); } e6850_check_int (acia); } unsigned char e6850_get_uint8 (e6850_t *acia, unsigned long addr) { switch (addr) { case 0: return (e6850_get_status (acia)); case 1: return (e6850_get_data (acia)); } return (0); } unsigned short e6850_get_uint16 (e6850_t *acia, unsigned long addr) { return (0); } unsigned long e6850_get_uint32 (e6850_t *acia, unsigned long addr) { return (0); } void e6850_set_uint8 (e6850_t *acia, unsigned long addr, unsigned char val) { switch (addr) { case 0: e6850_set_control (acia, val); break; case 1: e6850_set_data (acia, val); break; } } void e6850_set_uint16 (e6850_t *acia, unsigned long addr, unsigned short val) { } void e6850_set_uint32 (e6850_t *acia, unsigned long addr, unsigned long val) { } int e6850_receive_ready (const e6850_t *acia) { return ((acia->sr & E6850_SR_RDRF) == 0); } void e6850_receive (e6850_t *acia, unsigned char val) { acia->rsr = val; acia->recv_timer = (unsigned long) acia->char_bits << acia->clock_div; } void e6850_reset (e6850_t *acia) { acia->cr = 0; acia->sr = E6850_SR_TDRE; acia->rdr = 0; acia->tdr = 0; acia->rsr = 0; acia->tsr = 0; acia->clock_div = 0; acia->data_bits = 0; acia->stop_bits = 0; acia->char_bits = 0; acia->recv_timer = 0; acia->send_timer = 0; e6850_check_int (acia); } void e6850_clock (e6850_t *acia, unsigned cnt) { if (acia->recv_timer > 0) { if (cnt < acia->recv_timer) { acia->recv_timer -= cnt; } else { acia->recv_timer = 0; e6850_receive_rdr (acia); } } if (acia->send_timer > 0) { if (cnt < acia->send_timer) { acia->send_timer -= cnt; } else { acia->send_timer = 0; e6850_send_tsr (acia); } } }