--- trunk/src/devices/dev_sh4.c 2007/10/08 16:21:34 36 +++ trunk/src/devices/dev_sh4.c 2007/10/08 16:21:53 38 @@ -25,11 +25,17 @@ * SUCH DAMAGE. * * - * $Id: dev_sh4.c,v 1.32 2007/03/16 18:47:26 debug Exp $ + * $Id: dev_sh4.c,v 1.39 2007/04/13 16:12:39 debug Exp $ * * SH4 processor specific memory mapped registers (0xf0000000 - 0xffffffff). * - * TODO: Lots and lots of stuff. + * TODO: Among other things: + * + * x) Interrupt masks (msk register stuff). + * x) BSC (Bus state controller). + * x) DMA + * x) UBC + * x) ... */ #include @@ -54,6 +60,7 @@ #include "sh4_mmu.h" #include "sh4_rtcreg.h" #include "sh4_scifreg.h" +#include "sh4_scireg.h" #include "sh4_tmureg.h" @@ -61,6 +68,8 @@ #define SH4_TICK_SHIFT 14 #define N_SH4_TIMERS 3 +#define SCIF_TX_FIFO_SIZE 16 + /* General-purpose I/O stuff: */ #define SH4_PCTRA 0xff80002c #define SH4_PDTRA 0xff800030 @@ -80,10 +89,15 @@ uint16_t scif_scr; uint16_t scif_ssr; uint16_t scif_fcr; + uint16_t scif_lsr; int scif_delayed_tx; int scif_console_handle; + uint8_t scif_tx_fifo[SCIF_TX_FIFO_SIZE + 1]; + int scif_tx_fifo_cursize; struct interrupt scif_tx_irq; struct interrupt scif_rx_irq; + int scif_tx_irq_asserted; + int scif_rx_irq_asserted; /* Bus State Controller: */ uint32_t bsc_bcr1; @@ -101,6 +115,13 @@ uint32_t pctrb; /* Port Control Register B */ uint32_t pdtrb; /* Port Data Register B */ + /* SCI (serial interface): */ + int sci_bits_outputed; + int sci_bits_read; + uint8_t sci_scsptr; + uint8_t sci_curbyte; + uint8_t sci_cur_addr; + /* SD-RAM: */ uint16_t sdmr2; uint16_t sdmr3; @@ -122,7 +143,7 @@ }; -#define SH4_PSEUDO_TIMER_HZ 100.0 +#define SH4_PSEUDO_TIMER_HZ 110.0 /* @@ -186,18 +207,25 @@ static void scif_reassert_interrupts(struct sh4_data *d) { - if (d->scif_scr & SCSCR2_RIE) { - if (d->scif_ssr & SCSSR2_DR) - INTERRUPT_ASSERT(d->scif_rx_irq); - } else { + int old_tx_asserted = d->scif_tx_irq_asserted; + int old_rx_asserted = d->scif_rx_irq_asserted; + + d->scif_rx_irq_asserted = + d->scif_scr & SCSCR2_RIE && d->scif_ssr & SCSSR2_DR; + + if (d->scif_rx_irq_asserted && !old_rx_asserted) + INTERRUPT_ASSERT(d->scif_rx_irq); + else if (!d->scif_rx_irq_asserted && old_rx_asserted) INTERRUPT_DEASSERT(d->scif_rx_irq); - } - if (d->scif_scr & SCSCR2_TIE) { - if (d->scif_ssr & (SCSSR2_TDFE | SCSSR2_TEND)) - INTERRUPT_ASSERT(d->scif_tx_irq); - } else { + + d->scif_tx_irq_asserted = + d->scif_scr & SCSCR2_TIE && + d->scif_ssr & (SCSSR2_TDFE | SCSSR2_TEND); + + if (d->scif_tx_irq_asserted && !old_tx_asserted) + INTERRUPT_ASSERT(d->scif_tx_irq); + else if (!d->scif_tx_irq_asserted && old_tx_asserted) INTERRUPT_DEASSERT(d->scif_tx_irq); - } } @@ -206,15 +234,30 @@ struct sh4_data *d = (struct sh4_data *) extra; int i; - /* Serial controller interrupts: */ - /* TODO: Which bits go to which interrupt? */ + /* + * Serial controller interrupts: + * + * RX: Cause interrupt if any char is available. + * TX: Send entire TX FIFO contents, and interrupt. + */ if (console_charavail(d->scif_console_handle)) d->scif_ssr |= SCSSR2_DR; else - d->scif_ssr &= SCSSR2_DR; + d->scif_ssr &= ~SCSSR2_DR; + if (d->scif_delayed_tx) { - if (--d->scif_delayed_tx == 0) + if (--d->scif_delayed_tx == 0) { + /* Send TX FIFO contents: */ + for (i=0; iscif_tx_fifo_cursize; i++) + console_putchar(d->scif_console_handle, + d->scif_tx_fifo[i]); + + /* Clear FIFO: */ + d->scif_tx_fifo_cursize = 0; + + /* Done sending; cause a transmit end interrupt: */ d->scif_ssr |= SCSSR2_TDFE | SCSSR2_TEND; + } } scif_reassert_interrupts(d); @@ -228,6 +271,136 @@ } +/* + * sh_sci_cmd(): + * + * Handle a SCI command byte. + * + * Bit: Meaning: + * 7 Ignored (usually 1?) + * 6 0=Write, 1=Read + * 5 AD: Address transfer + * 4 DT: Data transfer + * 3..0 Data or address bits + */ +static void sh_sci_cmd(struct sh4_data *d, struct cpu *cpu) +{ + uint8_t cmd = d->sci_curbyte; + int writeflag = cmd & 0x40? 0 : 1; + int address_transfer; + + /* fatal("[ CMD BYTE %02x ]\n", cmd); */ + + if (!(cmd & 0x80)) { + fatal("SCI cmd bit 7 not set? TODO\n"); + exit(1); + } + + if ((cmd & 0x30) == 0x20) + address_transfer = 1; + else if ((cmd & 0x30) == 0x10) + address_transfer = 0; + else { + fatal("SCI: Neither data nor address transfer? TODO\n"); + exit(1); + } + + if (address_transfer) + d->sci_cur_addr = cmd & 0x0f; + + if (!writeflag) { + /* Read data from the current address: */ + uint8_t data_byte; + + cpu->memory_rw(cpu, cpu->mem, SCI_DEVICE_BASE + d->sci_cur_addr, + &data_byte, 1, MEM_READ, PHYSICAL); + + debug("[ SCI: read addr=%x data=%x ]\n", + d->sci_cur_addr, data_byte); + + d->sci_curbyte = data_byte; + + /* Set bit 7 right away: */ + d->sci_scsptr &= ~SCSPTR_SPB1DT; + if (data_byte & 0x80) + d->sci_scsptr |= SCSPTR_SPB1DT; + } + + if (writeflag && !address_transfer) { + /* Write the 4 data bits to the current address: */ + uint8_t data_byte = cmd & 0x0f; + + debug("[ SCI: write addr=%x data=%x ]\n", + d->sci_cur_addr, data_byte); + + cpu->memory_rw(cpu, cpu->mem, SCI_DEVICE_BASE + d->sci_cur_addr, + &data_byte, 1, MEM_WRITE, PHYSICAL); + } +} + + +/* + * sh_sci_access(): + * + * Reads or writes a bit via the SH4's serial interface. If writeflag is + * non-zero, input is used. If writeflag is zero, a bit is outputed as + * the return value from this function. + */ +static uint8_t sh_sci_access(struct sh4_data *d, struct cpu *cpu, + int writeflag, uint8_t input) +{ + if (writeflag) { + /* WRITE: */ + int clockpulse; + uint8_t old = d->sci_scsptr; + d->sci_scsptr = input; + + /* + * Clock pulse (SCSPTR_SPB0DT going from 0 to 1, + * when SCSPTR_SPB0IO was already set): + */ + clockpulse = old & SCSPTR_SPB0IO && + d->sci_scsptr & SCSPTR_SPB0DT && + !(old & SCSPTR_SPB0DT); + + if (!clockpulse) + return 0; + + /* Are we in output or input mode? */ + if (d->sci_scsptr & SCSPTR_SPB1IO) { + /* Output: */ + int bit = d->sci_scsptr & SCSPTR_SPB1DT? 1 : 0; + d->sci_curbyte <<= 1; + d->sci_curbyte |= bit; + d->sci_bits_outputed ++; + if (d->sci_bits_outputed == 8) { + /* 4 control bits and 4 address/data bits have + been written. */ + sh_sci_cmd(d, cpu); + d->sci_bits_outputed = 0; + } + } else { + /* Input: */ + int bit; + d->sci_bits_read ++; + d->sci_bits_read &= 7; + + bit = d->sci_curbyte & (0x80 >> d->sci_bits_read); + + d->sci_scsptr &= ~SCSPTR_SPB1DT; + if (bit) + d->sci_scsptr |= SCSPTR_SPB1DT; + } + + /* Return (value doesn't matter). */ + return 0; + } else { + /* READ: */ + return d->sci_scsptr; + } +} + + DEVICE_ACCESS(sh4_itlb_aa) { uint64_t idata = 0, odata = 0; @@ -368,9 +541,11 @@ continue; if (i < 0) { - cpu->cd.sh.itlb_lo[i] &= ~SH4_PTEL_V; + cpu->cd.sh.itlb_lo[i + + SH_N_ITLB_ENTRIES] &= ~SH4_PTEL_V; if (idata & SH4_UTLB_AA_V) - cpu->cd.sh.itlb_lo[i] |= + cpu->cd.sh.itlb_lo[ + i+SH_N_ITLB_ENTRIES] |= SH4_PTEL_V; } else { cpu->cd.sh.utlb_lo[i] &= @@ -739,8 +914,9 @@ exit(1); } + INTERRUPT_DEASSERT(d->timer_irq[timer_nr]); + if (d->tcr[timer_nr] & TCR_UNF && !(idata & TCR_UNF)) { - INTERRUPT_DEASSERT(d->timer_irq[timer_nr]); if (d->timer_interrupts_pending[timer_nr] > 0) d->timer_interrupts_pending[timer_nr]--; } @@ -947,6 +1123,15 @@ break; + /****************************/ + /* SCI: Serial Interface */ + + case SHREG_SCSPTR: + odata = sh_sci_access(d, cpu, + writeflag == MEM_WRITE? 1 : 0, idata); + break; + + /*********************************/ /* INTC: Interrupt Controller */ @@ -963,29 +1148,101 @@ case SH4_IPRA: if (writeflag == MEM_READ) odata = cpu->cd.sh.intc_ipra; - else + else { cpu->cd.sh.intc_ipra = idata; + sh_update_interrupt_priorities(cpu); + } break; case SH4_IPRB: if (writeflag == MEM_READ) odata = cpu->cd.sh.intc_iprb; - else + else { cpu->cd.sh.intc_iprb = idata; + sh_update_interrupt_priorities(cpu); + } break; case SH4_IPRC: if (writeflag == MEM_READ) odata = cpu->cd.sh.intc_iprc; - else + else { cpu->cd.sh.intc_iprc = idata; + sh_update_interrupt_priorities(cpu); + } break; case SH4_IPRD: if (writeflag == MEM_READ) odata = cpu->cd.sh.intc_iprd; - else + else { cpu->cd.sh.intc_iprd = idata; + sh_update_interrupt_priorities(cpu); + } + break; + + case SH4_INTPRI00: + if (writeflag == MEM_READ) + odata = cpu->cd.sh.intc_intpri00; + else { + cpu->cd.sh.intc_intpri00 = idata; + sh_update_interrupt_priorities(cpu); + } + break; + + case SH4_INTPRI00 + 4: + if (writeflag == MEM_READ) + odata = cpu->cd.sh.intc_intpri04; + else { + cpu->cd.sh.intc_intpri04 = idata; + sh_update_interrupt_priorities(cpu); + } + break; + + case SH4_INTPRI00 + 8: + if (writeflag == MEM_READ) + odata = cpu->cd.sh.intc_intpri08; + else { + cpu->cd.sh.intc_intpri08 = idata; + sh_update_interrupt_priorities(cpu); + } + break; + + case SH4_INTPRI00 + 0xc: + if (writeflag == MEM_READ) + odata = cpu->cd.sh.intc_intpri0c; + else { + cpu->cd.sh.intc_intpri0c = idata; + sh_update_interrupt_priorities(cpu); + } + break; + + case SH4_INTMSK00: + /* Note: Writes can only set bits, not clear them. */ + if (writeflag == MEM_READ) + odata = cpu->cd.sh.intc_intmsk00; + else + cpu->cd.sh.intc_intmsk00 |= idata; + break; + + case SH4_INTMSK00 + 4: + /* Note: Writes can only set bits, not clear them. */ + if (writeflag == MEM_READ) + odata = cpu->cd.sh.intc_intmsk04; + else + cpu->cd.sh.intc_intmsk04 |= idata; + break; + + case SH4_INTMSKCLR00: + /* Note: Writes can only clear bits, not set them. */ + if (writeflag == MEM_WRITE) + cpu->cd.sh.intc_intmsk00 &= ~idata; + break; + + case SH4_INTMSKCLR00 + 4: + /* Note: Writes can only clear bits, not set them. */ + if (writeflag == MEM_WRITE) + cpu->cd.sh.intc_intmsk04 &= ~idata; break; @@ -1019,8 +1276,15 @@ case SH4_SCIF_BASE + SCIF_FTDR: if (writeflag == MEM_WRITE) { - console_putchar(d->scif_console_handle, idata); - d->scif_delayed_tx = 1; + /* Add to TX fifo: */ + if (d->scif_tx_fifo_cursize >= sizeof( + d->scif_tx_fifo)) { + fatal("[ SCIF TX fifo overrun! ]\n"); + d->scif_tx_fifo_cursize = 0; + } + + d->scif_tx_fifo[d->scif_tx_fifo_cursize++] = idata; + d->scif_delayed_tx = 2; } break; @@ -1028,7 +1292,7 @@ if (writeflag == MEM_READ) { odata = d->scif_ssr; } else { - d->scif_ssr &= ~idata; + d->scif_ssr = idata; scif_reassert_interrupts(d); } break; @@ -1039,7 +1303,11 @@ if (x == 13) x = 10; odata = x < 0? 0 : x; - d->scif_ssr &= ~SCSSR2_DR; + if (console_charavail(d->scif_console_handle)) + d->scif_ssr |= SCSSR2_DR; + else + d->scif_ssr &= ~SCSSR2_DR; + scif_reassert_interrupts(d); } break; @@ -1051,8 +1319,14 @@ } break; + case SH4_SCIF_BASE + SCIF_LSR: + /* TODO: Implement all bits. */ + odata = 0; + break; + case SH4_SCIF_BASE + SCIF_FDR: - odata = console_charavail(d->scif_console_handle); + odata = (console_charavail(d->scif_console_handle)? 1 : 0) + + (d->scif_tx_fifo_cursize << 8); break; @@ -1200,7 +1474,10 @@ exit(1); } - /* Bus State Controller initial values: */ + /* + * Bus State Controller initial values, according to the + * SH7760 manual: + */ d->bsc_bcr2 = 0x3ffc; d->bsc_wcr1 = 0x77777777; d->bsc_wcr2 = 0xfffeefff;