--- previous/src/dma.c 2018/04/24 19:27:32 1.1 +++ previous/src/dma.c 2018/04/24 19:31:14 1.1.1.4 @@ -1,62 +1,44 @@ /* NeXT DMA Emulation * Contains informations from QEMU-NeXT - * NeXT DMA consists of 12 channel processors with 128 bytes internal buffer for each channel - * 12 channels: SCSI, Sound in, Sound out, Optical disk, Printer, SCC, DSP, + * NeXT Integrated Channel Processor (ISP) consists of 12 channel processors + * with 128 bytes internal buffer for each channel. + * 12 channels: + * SCSI, Sound in, Sound out, Optical disk, Printer, SCC, DSP, * Ethernet transmit, Ethernet receive, Video, Memory to register, Register to memory */ #include "ioMem.h" #include "ioMemTables.h" #include "m68000.h" +#include "scsi.h" #include "esp.h" +#include "mo.h" +#include "scc.h" #include "sysReg.h" #include "dma.h" #include "configuration.h" #include "ethernet.h" +#include "floppy.h" +#include "printer.h" +#include "snd.h" +#include "dsp.h" +#include "mmu_common.h" +#include "kms.h" +#include "audio.h" - -#define LOG_DMA_LEVEL LOG_WARN +#define LOG_DMA_LEVEL LOG_DEBUG #define IO_SEG_MASK 0x1FFFF -/* read CSR bits */ -#define DMA_ENABLE 0x01000000 /* enable dma transfer */ -#define DMA_SUPDATE 0x02000000 /* single update */ -#define DMA_COMPLETE 0x08000000 /* current dma has completed */ -#define DMA_BUSEXC 0x10000000 /* bus exception occurred */ -/* write CSR bits */ -#define DMA_SETENABLE 0x00010000 /* set enable */ -#define DMA_SETSUPDATE 0x00020000 /* set single update */ -#define DMA_M2DEV 0x00000000 /* dma from mem to dev */ -#define DMA_DEV2M 0x00040000 /* dma from dev to mem */ -#define DMA_CLRCOMPLETE 0x00080000 /* clear complete conditional */ -#define DMA_RESET 0x00100000 /* clr cmplt, sup, enable */ -#define DMA_INITBUF 0x00200000 /* initialize DMA buffers */ - -/* Read and write CSR bits for 68030 based NeXT Computer. - * We convert these to 68040 values before using in functions. - * read CSR bits * - #define DMA_ENABLE 0x01 - #define DMA_SUPDATE 0x02 - #define DMA_COMPLETE 0x08 - #define DMA_BUSEXC 0x10 - * write CSR bits * - #define DMA_SETENABLE 0x01 - #define DMA_SETSUPDATE 0x02 - #define DMA_M2DEV 0x00 - #define DMA_DEV2M 0x04 - #define DMA_CLRCOMPLETE 0x08 - #define DMA_RESET 0x10 - #define DMA_INITBUF 0x20 - */ +int get_channel(Uint32 address); +int get_interrupt_type(int channel); +void dma_interrupt(int channel); +void dma_initialize_buffer(int channel, Uint8 offset); - -/* DMA registers */ - -typedef struct { - Uint32 csr; +struct { + Uint8 csr; Uint32 saved_next; Uint32 saved_limit; Uint32 saved_start; @@ -65,32 +47,93 @@ typedef struct { Uint32 limit; Uint32 start; Uint32 stop; - Uint32 init; - Uint32 size; -} DMA_CONTROL; + + Uint8 direction; +} dma[12]; + + +/* DMA internal buffers */ +#define DMA_BURST_SIZE 16 + +int espdma_buf_size = 0; +int espdma_buf_limit = 0; +Uint8 espdma_buf[DMA_BURST_SIZE]; +int modma_buf_size = 0; +int modma_buf_limit = 0; +Uint8 modma_buf[DMA_BURST_SIZE]; -DMA_CONTROL dma[16]; +/* Read and write CSR bits for 68030 based NeXT Computer. */ + +/* read CSR bits */ +#define DMA_ENABLE 0x01 /* enable dma transfer */ +#define DMA_SUPDATE 0x02 /* single update */ +#define DMA_COMPLETE 0x08 /* current dma has completed */ +#define DMA_BUSEXC 0x10 /* bus exception occurred */ +/* write CSR bits */ +#define DMA_SETENABLE 0x01 /* set enable */ +#define DMA_SETSUPDATE 0x02 /* set single update */ +#define DMA_M2DEV 0x00 /* dma from mem to dev */ +#define DMA_DEV2M 0x04 /* dma from dev to mem */ +#define DMA_CLRCOMPLETE 0x08 /* clear complete conditional */ +#define DMA_RESET 0x10 /* clr cmplt, sup, enable */ +#define DMA_INITBUF 0x20 /* initialize DMA buffers */ + +/* CSR masks */ +#define DMA_CMD_MASK (DMA_SETENABLE|DMA_SETSUPDATE|DMA_CLRCOMPLETE|DMA_RESET|DMA_INITBUF) +#define DMA_STAT_MASK (DMA_ENABLE|DMA_SUPDATE|DMA_COMPLETE|DMA_BUSEXC) + + +/* Read and write CSR bits for 68040 based Machines. + * We convert these to 68030 values before using in functions. + * read CSR bits * + #define DMA_ENABLE 0x01000000 + #define DMA_SUPDATE 0x02000000 + #define DMA_COMPLETE 0x08000000 + #define DMA_BUSEXC 0x10000000 + * write CSR bits * + #define DMA_SETENABLE 0x00010000 + #define DMA_SETSUPDATE 0x00020000 + #define DMA_M2DEV 0x00000000 + #define DMA_DEV2M 0x00040000 + #define DMA_CLRCOMPLETE 0x00080000 + #define DMA_RESET 0x00100000 + #define DMA_INITBUF 0x00200000 + */ + + + +static inline Uint32 dma_getlong(Uint8 *buf, Uint32 pos) { + return (buf[pos] << 24) | (buf[pos+1] << 16) | (buf[pos+2] << 8) | buf[pos+3]; +} + +static inline void dma_putlong(Uint32 val, Uint8 *buf, Uint32 pos) { + buf[pos] = val >> 24; + buf[pos+1] = val >> 16; + buf[pos+2] = val >> 8; + buf[pos+3] = val; +} int get_channel(Uint32 address) { int channel = address&IO_SEG_MASK; + switch (channel) { - case 0x010: printf("channel SCSI:\n"); return CHANNEL_SCSI; break; - case 0x040: printf("channel Sound Out:\n"); return CHANNEL_SOUNDOUT; break; - case 0x050: printf("channel MO Disk:\n"); return CHANNEL_DISK; break; - case 0x080: printf("channel Sound in:\n"); return CHANNEL_SOUNDIN; break; - case 0x090: printf("channel Printer:\n"); return CHANNEL_PRINTER; break; - case 0x0c0: printf("channel SCC:\n"); return CHANNEL_SCC; break; - case 0x0d0: printf("channel DSP:\n"); return CHANNEL_DSP; break; - case 0x110: printf("channel Ethernet Tx:\n"); return CHANNEL_EN_TX; break; - case 0x150: printf("channel Ethernet Rx:\n"); return CHANNEL_EN_RX; break; - case 0x180: printf("channel Video:\n"); return CHANNEL_VIDEO; break; - case 0x1d0: printf("channel M2R:\n"); return CHANNEL_M2R; break; - case 0x1c0: printf("channel R2M:\n"); return CHANNEL_R2M; break; + case 0x010: Log_Printf(LOG_DMA_LEVEL,"channel SCSI:"); return CHANNEL_SCSI; break; + case 0x040: Log_Printf(LOG_DMA_LEVEL,"channel Sound Out:"); return CHANNEL_SOUNDOUT; break; + case 0x050: Log_Printf(LOG_DMA_LEVEL,"channel MO Disk:"); return CHANNEL_DISK; break; + case 0x080: Log_Printf(LOG_DMA_LEVEL,"channel Sound in:"); return CHANNEL_SOUNDIN; break; + case 0x090: Log_Printf(LOG_DMA_LEVEL,"channel Printer:"); return CHANNEL_PRINTER; break; + case 0x0c0: Log_Printf(LOG_DMA_LEVEL,"channel SCC:"); return CHANNEL_SCC; break; + case 0x0d0: Log_Printf(LOG_DMA_LEVEL,"channel DSP:"); return CHANNEL_DSP; break; + case 0x110: Log_Printf(LOG_DMA_LEVEL,"channel Ethernet Tx:"); return CHANNEL_EN_TX; break; + case 0x150: Log_Printf(LOG_DMA_LEVEL,"channel Ethernet Rx:"); return CHANNEL_EN_RX; break; + case 0x180: Log_Printf(LOG_DMA_LEVEL,"channel Video:"); return CHANNEL_VIDEO; break; + case 0x1d0: Log_Printf(LOG_DMA_LEVEL,"channel M2R:"); return CHANNEL_M2R; break; + case 0x1c0: Log_Printf(LOG_DMA_LEVEL,"channel R2M:"); return CHANNEL_R2M; break; default: - Log_Printf(LOG_DMA_LEVEL, "Unknown DMA channel!\n"); + Log_Printf(LOG_WARN, "Unknown DMA channel!\n"); return -1; break; } @@ -107,12 +150,12 @@ int get_interrupt_type(int channel) { case CHANNEL_DSP: return INT_DSP_DMA; break; case CHANNEL_EN_TX: return INT_EN_TX_DMA; break; case CHANNEL_EN_RX: return INT_EN_RX_DMA; break; - case CHANNEL_VIDEO: return 0; break; // no interrupt? CHECK THIS + case CHANNEL_VIDEO: return INT_VIDEO; break; case CHANNEL_M2R: return INT_M2R_DMA; break; case CHANNEL_R2M: return INT_R2M_DMA; break; default: - Log_Printf(LOG_DMA_LEVEL, "Unknown DMA interrupt!\n"); + Log_Printf(LOG_WARN, "Unknown DMA interrupt!\n"); return 0; break; } @@ -120,63 +163,76 @@ int get_interrupt_type(int channel) { void DMA_CSR_Read(void) { // 0x02000010, length of register is byte on 68030 based NeXT Computer int channel = get_channel(IoAccessCurrentAddress); - if(ConfigureParams.System.nMachineType == NEXT_CUBE030) { // for 68030 based NeXT Computer - IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = dma[channel].csr >> 24; - Log_Printf(LOG_DMA_LEVEL,"DMA CSR read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, dma[channel].csr >> 24, m68k_getpc()); - } else { - IoMem_WriteLong(IoAccessCurrentAddress & IO_SEG_MASK, dma[channel].csr); - Log_Printf(LOG_DMA_LEVEL,"DMA CSR read at $%08x val=$%08x PC=$%08x\n", IoAccessCurrentAddress, dma[channel].csr, m68k_getpc()); - } + + IoMem[IoAccessCurrentAddress & IO_SEG_MASK] = dma[channel].csr; + IoMem[(IoAccessCurrentAddress+1) & IO_SEG_MASK] = IoMem[(IoAccessCurrentAddress+2) & IO_SEG_MASK] = IoMem[(IoAccessCurrentAddress+3) & IO_SEG_MASK] = 0x00; // just to be sure + Log_Printf(LOG_DMA_LEVEL,"DMA CSR read at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, dma[channel].csr, m68k_getpc()); } void DMA_CSR_Write(void) { int channel = get_channel(IoAccessCurrentAddress); int interrupt = get_interrupt_type(channel); - Uint32 writecsr; - if(ConfigureParams.System.nMachineType == NEXT_CUBE030) { // for 68030 based NeXT Computer - writecsr = IoMem[IoAccessCurrentAddress & IO_SEG_MASK] << 16; - Log_Printf(LOG_DMA_LEVEL,"DMA CSR write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, writecsr >> 16, m68k_getpc()); - } else { - writecsr = IoMem_ReadLong(IoAccessCurrentAddress & IO_SEG_MASK); - Log_Printf(LOG_DMA_LEVEL,"DMA CSR write at $%08x val=$%08x PC=$%08x\n", IoAccessCurrentAddress, writecsr, m68k_getpc()); - } - - if(writecsr & DMA_DEV2M) { - if(ConfigureParams.System.nMachineType == NEXT_CUBE030) { - dma[channel].csr |= (0x04 << 24); // use 8 bit DMA_DEV2M value for 68030 based NeXT Computer - } else { - dma[channel].csr |= DMA_DEV2M; - } + Uint8 writecsr = IoMem[IoAccessCurrentAddress & IO_SEG_MASK]|IoMem[(IoAccessCurrentAddress+1) & IO_SEG_MASK]|IoMem[(IoAccessCurrentAddress+2) & IO_SEG_MASK]|IoMem[(IoAccessCurrentAddress+3) & IO_SEG_MASK]; + + Log_Printf(LOG_DMA_LEVEL,"DMA CSR write at $%08x val=$%02x PC=$%08x\n", IoAccessCurrentAddress, writecsr, m68k_getpc()); + + /* For debugging */ + if(writecsr&DMA_DEV2M) Log_Printf(LOG_DMA_LEVEL,"DMA from dev to mem"); - } else { + else Log_Printf(LOG_DMA_LEVEL,"DMA from mem to dev"); + + switch (writecsr&DMA_CMD_MASK) { + case DMA_RESET: + Log_Printf(LOG_DMA_LEVEL,"DMA reset"); break; + case DMA_INITBUF: + Log_Printf(LOG_DMA_LEVEL,"DMA initialize buffers"); break; + case (DMA_RESET | DMA_INITBUF): + case (DMA_RESET | DMA_INITBUF | DMA_CLRCOMPLETE): + Log_Printf(LOG_DMA_LEVEL,"DMA reset and initialize buffers"); break; + case DMA_CLRCOMPLETE: + Log_Printf(LOG_DMA_LEVEL,"DMA end chaining"); break; + case (DMA_SETSUPDATE | DMA_CLRCOMPLETE): + Log_Printf(LOG_DMA_LEVEL,"DMA continue chaining"); break; + case DMA_SETENABLE: + Log_Printf(LOG_DMA_LEVEL,"DMA start single transfer"); break; + case (DMA_SETENABLE | DMA_SETSUPDATE): + case (DMA_SETENABLE | DMA_SETSUPDATE | DMA_CLRCOMPLETE): + Log_Printf(LOG_DMA_LEVEL,"DMA start chaining"); break; + case 0: + Log_Printf(LOG_DMA_LEVEL,"DMA no command"); break; + default: + Log_Printf(LOG_DMA_LEVEL,"DMA: unknown command!"); break; } - if(writecsr & DMA_SETENABLE) { - dma[channel].csr |= DMA_ENABLE; - Log_Printf(LOG_DMA_LEVEL,"DMA enable transfer"); - if ((channel == CHANNEL_EN_TX) && !(writecsr&DMA_DEV2M)) { - Ethernet_Transmit(); // Ethernet Transmit - } + + /* Handle CSR bits */ + dma[channel].direction = writecsr&DMA_DEV2M; + + if (writecsr&DMA_RESET) { + dma[channel].csr &= ~(DMA_COMPLETE | DMA_SUPDATE | DMA_ENABLE); } - if(writecsr & DMA_SETSUPDATE) { - dma[channel].csr |= DMA_SUPDATE; - Log_Printf(LOG_DMA_LEVEL,"DMA set single update"); + if (writecsr&DMA_INITBUF) { + dma_initialize_buffer(channel, 0); } - if(writecsr & DMA_CLRCOMPLETE) { - dma[channel].csr &= ~DMA_COMPLETE; - Log_Printf(LOG_DMA_LEVEL,"DMA clear complete conditional"); - - set_interrupt(interrupt, RELEASE_INT); // also somewhat experimental... + if (writecsr&DMA_SETSUPDATE) { + dma[channel].csr |= DMA_SUPDATE; } - if(writecsr & DMA_RESET) { - dma[channel].csr &= ~(DMA_COMPLETE | DMA_SUPDATE | DMA_ENABLE | DMA_DEV2M); - Log_Printf(LOG_WARN,"DMA reset"); + if (writecsr&DMA_SETENABLE) { + dma[channel].csr |= DMA_ENABLE; - set_interrupt(interrupt, RELEASE_INT); // also somewhat experimental... + /* Enable Memory to Memory DMA, if read and write channels are enabled */ + if (channel == CHANNEL_R2M || channel == CHANNEL_M2R) { + if (dma[channel].next==dma[channel].limit) { + dma[channel].csr &= ~DMA_ENABLE; + } + dma_m2m(); + } } - if(writecsr & DMA_INITBUF) { // needs to be filled - Log_Printf(LOG_DMA_LEVEL,"DMA initialize buffers"); + if (writecsr&DMA_CLRCOMPLETE) { + dma[channel].csr &= ~DMA_COMPLETE; } + + set_interrupt(interrupt, RELEASE_INT); // experimental } void DMA_Saved_Next_Read(void) { // 0x02004000 @@ -277,159 +333,861 @@ void DMA_Stop_Write(void) { void DMA_Init_Read(void) { // 0x02004210 int channel = get_channel(IoAccessCurrentAddress-0x4200); - IoMem_WriteLong(IoAccessCurrentAddress & IO_SEG_MASK, dma[channel].init); - Log_Printf(LOG_DMA_LEVEL,"DMA Init read at $%08x val=$%08x PC=$%08x\n", IoAccessCurrentAddress, dma[channel].init, m68k_getpc()); + IoMem_WriteLong(IoAccessCurrentAddress & IO_SEG_MASK, dma[channel].next); + Log_Printf(LOG_DMA_LEVEL,"DMA Init read at $%08x val=$%08x PC=$%08x\n", IoAccessCurrentAddress, dma[channel].next, m68k_getpc()); } void DMA_Init_Write(void) { int channel = get_channel(IoAccessCurrentAddress-0x4200); - dma[channel].init = IoMem_ReadLong(IoAccessCurrentAddress & IO_SEG_MASK); - Log_Printf(LOG_DMA_LEVEL,"DMA Init write at $%08x val=$%08x PC=$%08x\n", IoAccessCurrentAddress, dma[channel].init, m68k_getpc()); + dma[channel].next = IoMem_ReadLong(IoAccessCurrentAddress & IO_SEG_MASK); + dma_initialize_buffer(channel, dma[channel].next&0xF); + Log_Printf(LOG_DMA_LEVEL,"DMA Init write at $%08x val=$%08x PC=$%08x\n", IoAccessCurrentAddress, dma[channel].next, m68k_getpc()); } -void DMA_Size_Read(void) { // 0x02004214 - int channel = get_channel(IoAccessCurrentAddress-0x4204); - IoMem_WriteLong(IoAccessCurrentAddress & IO_SEG_MASK, dma[channel].size); - Log_Printf(LOG_DMA_LEVEL,"DMA Size read at $%08x val=$%08x PC=$%08x\n", IoAccessCurrentAddress, dma[channel].size, m68k_getpc()); -} +/* Initialize DMA internal buffer */ -void DMA_Size_Write(void) { - int channel = get_channel(IoAccessCurrentAddress-0x4204); - dma[channel].size = IoMem_ReadLong(IoAccessCurrentAddress & IO_SEG_MASK); - Log_Printf(LOG_DMA_LEVEL,"DMA Size write at $%08x val=$%08x PC=$%08x\n", IoAccessCurrentAddress, dma[channel].size, m68k_getpc()); +void dma_initialize_buffer(int channel, Uint8 offset) { + if (offset>0) { + Log_Printf(LOG_WARN, "DMA Initializing buffer with offset %i", offset); + } + switch (channel) { + case CHANNEL_SCSI: + esp_dma.status = 0x00; /* just a guess */ + espdma_buf_size = 0; + espdma_buf_limit = offset; + break; + case CHANNEL_DISK: + modma_buf_size = 0; + modma_buf_limit = offset; + break; + default: + break; + } } +/* DMA interrupt functions */ +void dma_interrupt(int channel) { + int interrupt = get_interrupt_type(channel); -/* DMA Functions */ + /* If we have reached limit, generate an interrupt and set the flags */ + if (dma[channel].next==dma[channel].limit) { + + dma[channel].csr |= DMA_COMPLETE; + + if (dma[channel].csr & DMA_SUPDATE) { /* if we are in chaining mode */ + dma[channel].next = dma[channel].start; + dma[channel].limit = dma[channel].stop; + /* Set bits in CSR */ + dma[channel].csr &= ~DMA_SUPDATE; /* 1st done */ + } else { + dma[channel].csr &= ~DMA_ENABLE; /* all done */ + } + set_interrupt(interrupt, SET_INT); + } else if (dma[channel].csr&DMA_BUSEXC) { + set_interrupt(interrupt, SET_INT); + } +} -/*void copy_to_scsidma_buffer(Uint8 device_outbuf[], int outbuf_size) { - memcpy(dma_buffer, device_outbuf, outbuf_size); -}*/ -/*void dma_clear_memory(Uint32 datalength) { - Uint32 start_addr; - Uint32 end_addr; +/* DMA Read and Write Memory Functions */ + +/* Channel SCSI (shared with floppy drive) */ +void dma_esp_write_memory(void) { + Log_Printf(LOG_DMA_LEVEL, "[DMA] Channel SCSI: Write to memory at $%08x, %i bytes (ESP counter %i)", + dma[CHANNEL_SCSI].next,dma[CHANNEL_SCSI].limit-dma[CHANNEL_SCSI].next,esp_counter); - if(dma_init == 0) - start_addr = dma_next; - else - start_addr = dma_init; + if (!(dma[CHANNEL_SCSI].csr&DMA_ENABLE)) { + Log_Printf(LOG_WARN, "[DMA] Channel SCSI: Error! DMA not enabled!"); + return; + } + if ((dma[CHANNEL_SCSI].limit%DMA_BURST_SIZE) || (dma[CHANNEL_SCSI].next%4)) { + Log_Printf(LOG_WARN, "[DMA] Channel SCSI: Error! Bad alignment! (Next: $%08X, Limit: $%08X)", + dma[CHANNEL_SCSI].next, dma[CHANNEL_SCSI].limit); + abort(); + } + + TRY(prb) { + if (espdma_buf_size>0) { + Log_Printf(LOG_WARN, "[DMA] Channel SCSI: Starting with %i residual bytes in DMA buffer.", espdma_buf_size); + } + + while (dma[CHANNEL_SCSI].next<=dma[CHANNEL_SCSI].limit) { + /* Fill DMA channel FIFO (only if limit < FIFO size) */ + if (espdma_buf_limit0) { + espdma_buf[espdma_buf_limit]=flp_buffer.data[flp_buffer.limit-flp_buffer.size]; + flp_buffer.size--; + espdma_buf_limit++; + espdma_buf_size++; + } + } else { + while (espdma_buf_limit0 && SCSIbus.phase==PHASE_DI) { + espdma_buf[espdma_buf_limit]=SCSIdisk_Send_Data(); + esp_counter--; + espdma_buf_limit++; + espdma_buf_size++; + } + } + } + + if (espdma_buf_limit0) { + NEXTMemory_WriteLong(dma[CHANNEL_SCSI].next, dma_getlong(espdma_buf, DMA_BURST_SIZE-espdma_buf_size)); + dma[CHANNEL_SCSI].next+=4; + espdma_buf_size-=4; + } + if (espdma_buf_size>0) { /* Not complete, stop */ + Log_Printf(LOG_DMA_LEVEL, "[DMA] Channel SCSI: Channel limit reached. Stopping with %i residual bytes.", + espdma_buf_size); + break; + } + espdma_buf_limit = espdma_buf_size; /* Should be 0 */ + } + } + } CATCH(prb) { + Log_Printf(LOG_WARN, "[DMA] Channel SCSI: Bus error while writing to %08x",dma[CHANNEL_SCSI].next); + dma[CHANNEL_SCSI].csr &= ~DMA_ENABLE; + dma[CHANNEL_SCSI].csr |= (DMA_COMPLETE|DMA_BUSEXC); + } ENDTRY - end_addr = start_addr + datalength; + dma_interrupt(CHANNEL_SCSI); +} + +void dma_esp_flush_buffer(void) { + if (!(dma[CHANNEL_SCSI].csr&DMA_ENABLE)) { + Log_Printf(LOG_DMA_LEVEL, "[DMA] Channel SCSI: Not flushing buffer. DMA not enabled."); + return; + } + if (dma[CHANNEL_SCSI].direction!=DMA_DEV2M) { + Log_Printf(LOG_DMA_LEVEL, "[DMA] Channel SCSI: Not flushing buffer. Bad direction!"); + return; + } + + TRY(prb) { + if (dma[CHANNEL_SCSI].next0) { + /* Write one long word to memory */ + NEXTMemory_WriteLong(dma[CHANNEL_SCSI].next, dma_getlong(espdma_buf, espdma_buf_limit-espdma_buf_size)); + espdma_buf_size-=4; + } + dma[CHANNEL_SCSI].next+=4; + } else { + Log_Printf(LOG_WARN, "[DMA] Channel SCSI: Not flushing buffer. DMA done."); + } + } CATCH(prb) { + Log_Printf(LOG_WARN, "[DMA] Channel SCSI: Bus error while flushing to %08x",dma[CHANNEL_SCSI].next); + dma[CHANNEL_SCSI].csr &= ~DMA_ENABLE; + dma[CHANNEL_SCSI].csr |= (DMA_COMPLETE|DMA_BUSEXC); + } ENDTRY - NEXTMemory_Clear(start_addr, end_addr); -}*/ + dma_interrupt(CHANNEL_SCSI); +} -void dma_memory_read(Uint8 *buf, Uint32 *size, int channel) { - Uint32 base_addr; - Uint8 align = 16; - Uint32 size_count = 0; - Uint32 read_addr; - int interrupt = get_interrupt_type(channel); +void dma_esp_read_memory(void) { + Log_Printf(LOG_DMA_LEVEL, "[DMA] Channel SCSI: Read from memory at $%08x, %i bytes (ESP counter %i)", + dma[CHANNEL_SCSI].next,dma[CHANNEL_SCSI].limit-dma[CHANNEL_SCSI].next,esp_counter); - if ((channel == CHANNEL_EN_TX) && !ConfigureParams.System.bTurbo) - *size = (dma[channel].limit&0x0FFFFFFF) - (dma[channel].init&0x0FFFFFFF); - else - *size = (dma[channel].limit&0x0FFFFFFF) - (dma[channel].next&0x0FFFFFFF); + if (!(dma[CHANNEL_SCSI].csr&DMA_ENABLE)) { + Log_Printf(LOG_WARN, "[DMA] Channel SCSI: Error! DMA not enabled!"); + return; + } + if ((dma[CHANNEL_SCSI].limit%DMA_BURST_SIZE) || (dma[CHANNEL_SCSI].next%4)) { + Log_Printf(LOG_WARN, "[DMA] Channel SCSI: Error! Bad alignment! (Next: $%08X, Limit: $%08X)", + dma[CHANNEL_SCSI].next, dma[CHANNEL_SCSI].limit); + abort(); + } - if(channel == CHANNEL_EN_RX || channel == CHANNEL_EN_TX) - align = 32; + TRY(prb) { + if (espdma_buf_size>0) { + Log_Printf(LOG_WARN, "[DMA] Channel SCSI: Starting with %i residual bytes in DMA buffer.", espdma_buf_size); + } + + while (dma[CHANNEL_SCSI].next0 && flp_buffer.size0 && esp_counter>0 && SCSIbus.phase==PHASE_DO) { + SCSIdisk_Receive_Data(espdma_buf[espdma_buf_limit-espdma_buf_size]); + esp_counter--; + espdma_buf_size--; + } + } + if (espdma_buf_size>0) { /* Not complete, stop */ + Log_Printf(LOG_DMA_LEVEL, "[DMA] Channel SCSI: No more data request. Stopping with %i residual bytes.", + espdma_buf_size); + break; + } + espdma_buf_limit = espdma_buf_size; /* Should be 0 */ + } + } + } CATCH(prb) { + Log_Printf(LOG_WARN, "[DMA] Channel SCSI: Bus error while reading from %08x",dma[CHANNEL_SCSI].next); + dma[CHANNEL_SCSI].csr &= ~DMA_ENABLE; + dma[CHANNEL_SCSI].csr |= (DMA_COMPLETE|DMA_BUSEXC); + } ENDTRY -// if((*size % align) != 0) { -// *size -= *size % align; -// *size += align; -// } + if ((floppy_select && flp_buffer.size0) { + Log_Printf(LOG_WARN, "[DMA] Channel MO: Starting with %i residual bytes in DMA buffer.", modma_buf_size); + } + + while (dma[CHANNEL_DISK].next<=dma[CHANNEL_DISK].limit) { + /* Fill DMA channel FIFO (only if limit < FIFO size) */ + if (modma_buf_limit0) { + modma_buf[modma_buf_limit]=ecc_buffer[eccout].data[ecc_buffer[eccout].limit-ecc_buffer[eccout].size]; + ecc_buffer[eccout].size--; + modma_buf_limit++; + modma_buf_size++; + } + } + + if (modma_buf_limit0) { + NEXTMemory_WriteLong(dma[CHANNEL_DISK].next, dma_getlong(modma_buf, DMA_BURST_SIZE-modma_buf_size)); + dma[CHANNEL_DISK].next+=4; + modma_buf_size-=4; + } + if (modma_buf_size>0) { /* Not complete, stop */ + Log_Printf(LOG_DMA_LEVEL, "[DMA] Channel MO: Channel limit reached. Stopping with %i residual bytes.", + modma_buf_size); + break; + } + modma_buf_limit = modma_buf_size; /* Should be 0 */ + } + } + } CATCH(prb) { + Log_Printf(LOG_WARN, "[DMA] Channel MO: Bus error while writing to %08x",dma[CHANNEL_DISK].next); + dma[CHANNEL_DISK].csr &= ~DMA_ENABLE; + dma[CHANNEL_DISK].csr |= (DMA_COMPLETE|DMA_BUSEXC); + } ENDTRY + + dma_interrupt(CHANNEL_DISK); +} - dma[channel].csr |= DMA_COMPLETE | DMA_SUPDATE; +void dma_mo_read_memory(void) { + Log_Printf(LOG_DMA_LEVEL, "[DMA] Channel MO: Read from memory at $%08x, %i bytes", + dma[CHANNEL_DISK].next,dma[CHANNEL_DISK].limit-dma[CHANNEL_DISK].next); - set_interrupt(interrupt, SET_INT); + if (!(dma[CHANNEL_DISK].csr&DMA_ENABLE)) { + Log_Printf(LOG_WARN, "[DMA] Channel MO: Error! DMA not enabled!"); + return; + } + if ((dma[CHANNEL_DISK].limit%DMA_BURST_SIZE) || (dma[CHANNEL_DISK].next%4)) { + Log_Printf(LOG_WARN, "[DMA] Channel MO: Error! Bad alignment! (Next: $%08X, Limit: $%08X)", + dma[CHANNEL_DISK].next, dma[CHANNEL_DISK].limit); + abort(); + } + + TRY(prb) { + if (modma_buf_size>0) { + Log_Printf(LOG_WARN, "[DMA] Channel MO: Starting with %i residual bytes in DMA buffer.", modma_buf_size); + } + + while (dma[CHANNEL_DISK].next0 && ecc_buffer[eccin].size0) { /* Not complete, stop */ + Log_Printf(LOG_DMA_LEVEL, "[DMA] Channel MO: No more data request. Stopping with %i residual bytes.", + modma_buf_size); + break; + } + modma_buf_limit = modma_buf_size; /* Should be 0 */ + } + } + } CATCH(prb) { + Log_Printf(LOG_WARN, "[DMA] Channel MO: Bus error while reading from %08x",dma[CHANNEL_DISK].next); + dma[CHANNEL_DISK].csr &= ~DMA_ENABLE; + dma[CHANNEL_DISK].csr |= (DMA_COMPLETE|DMA_BUSEXC); + } ENDTRY + + if (ecc_buffer[eccin].size (dma[channel].limit - dma[channel].init)) { - tail_addr = dma[channel].start; - dma_tail = size - (dma[channel].limit - dma[channel].init); - size = (dma[channel].limit - dma[channel].init); - Log_Printf(LOG_WARN, "[DMA] Residual bytes: %i", dma_tail); + if ((dma[CHANNEL_PRINTER].limit%4) || (dma[CHANNEL_PRINTER].next%4)) { + Log_Printf(LOG_WARN, "[DMA] Channel Printer: Error! Bad alignment! (Next: $%08X, Limit: $%08X)", + dma[CHANNEL_PRINTER].next, dma[CHANNEL_PRINTER].limit); + abort(); } + + TRY(prb) { + while (dma[CHANNEL_PRINTER].next0) { + NEXTMemory_WriteByte(dma[CHANNEL_EN_RX].next, enet_rx_buffer.data[enet_rx_buffer.limit-enet_rx_buffer.size]); + enet_rx_buffer.size--; + dma[CHANNEL_EN_RX].next++; + } + } CATCH(prb) { + Log_Printf(LOG_WARN, "[DMA] Channel Ethernet Receive: Bus error while writing to %08x",dma[CHANNEL_EN_RX].next); + dma[CHANNEL_EN_RX].csr &= ~DMA_ENABLE; + dma[CHANNEL_EN_RX].csr |= (DMA_COMPLETE|DMA_BUSEXC); + } ENDTRY - if (dma_tail) { - Log_Printf(LOG_WARN, "[DMA] Write residual bytes at $%08x, %i bytes",tail_addr,dma_tail); - for (size_count = 0; size_count < dma_tail; size_count++) { - write_addr = tail_addr + size_count; - NEXTMemory_WriteByte(write_addr, buf[size+size_count]); + if (enet_rx_buffer.size==0) { + if (eop) { /* TODO: check if this is correct */ + Log_Printf(LOG_WARN, "[DMA] Channel Ethernet Receive: Last buffer of chain done."); + dma[CHANNEL_EN_RX].next|=EN_BOP; } + dma[CHANNEL_EN_RX].saved_limit = dma[CHANNEL_EN_RX].next; } + dma_enet_interrupt(CHANNEL_EN_RX); +} + +bool dma_enet_read_memory(void) { + if (dma[CHANNEL_EN_TX].csr&DMA_ENABLE) { + Log_Printf(LOG_DMA_LEVEL, "[DMA] Channel Ethernet Transmit: Read from memory at $%08x, %i bytes", + dma[CHANNEL_EN_TX].next,ENADDR(dma[CHANNEL_EN_TX].limit)-dma[CHANNEL_EN_TX].next); + + TRY(prb) { + while (dma[CHANNEL_EN_TX].next 0) { + NEXTMemory_WriteByte(dma[CHANNEL_R2M].next, m2m_buffer[DMA_BURST_SIZE-m2m_buffer_size]); + m2m_buffer_size--; + dma[CHANNEL_R2M].next++; + } + } CATCH(prb) { + Log_Printf(LOG_WARN, "[DMA] Channel M2M: Bus error while writing to %08x",dma[CHANNEL_R2M].next); + dma[CHANNEL_R2M].csr &= ~DMA_ENABLE; + dma[CHANNEL_R2M].csr |= (DMA_COMPLETE|DMA_BUSEXC); + } ENDTRY + } - if(!(dma[channel].csr & DMA_SUPDATE)||(channel==CHANNEL_EN_RX)) { // Ethernet: this needs to be checked! - dma[channel].next = dma[channel].start; - dma[channel].limit = dma[channel].stop; + dma_interrupt(CHANNEL_R2M); +} + + +/* Channel DSP */ +#define LOG_DMA_DSP_LEVEL LOG_DEBUG + +void dma_dsp_write_memory(Uint8 val) { + Log_Printf(LOG_DMA_DSP_LEVEL, "[DMA] Channel DSP: Write to memory at $%08x, %i bytes", + dma[CHANNEL_DSP].next,dma[CHANNEL_DSP].limit-dma[CHANNEL_DSP].next); + + if (!(dma[CHANNEL_DSP].csr&DMA_ENABLE)) { + Log_Printf(LOG_WARN, "[DMA] Channel DSP: Error! DMA not enabled!"); + return; + } + + TRY(prb) { + if (dma[CHANNEL_DSP].next>16)&DMA_DEV2M; + + if (writecsr&TDMA_RESET) { + dma[channel].csr &= ~(DMA_COMPLETE | DMA_SUPDATE | DMA_ENABLE); + } + if (writecsr&TDMA_BUFRESET) { + dma_initialize_buffer(channel, 0); + } + if (writecsr&TDMA_SETSUPDATE) { + dma[channel].csr |= DMA_SUPDATE; + } + if (writecsr&TDMA_SETENABLE) { + dma[channel].csr |= DMA_ENABLE; + } + if (writecsr&TDMA_CLRCOMPLETE) { + dma[channel].csr &= ~DMA_COMPLETE; + } + + set_interrupt(interrupt, RELEASE_INT); +} + +void TDMA_Saved_Next_Read(void) { // 0x02004050 + IoMem_WriteLong(IoAccessCurrentAddress & IO_SEG_MASK, saved_next_turbo); + Log_Printf(LOG_DMA_LEVEL,"TDMA SNext read at $%08x val=$%08x PC=$%08x\n", IoAccessCurrentAddress, saved_next_turbo, m68k_getpc()); +} + +/* Flush DMA buffer */ +/* FIXME: Implement function for all buffered channels */ +void tdma_flush_buffer(int channel) { + int i; + + if (!(dma[CHANNEL_SCSI].csr&DMA_ENABLE)) { + Log_Printf(LOG_DMA_LEVEL, "[DMA] Channel SCSI: Not flushing buffer. DMA not enabled."); + return; + } + if (dma[CHANNEL_SCSI].direction!=DMA_DEV2M) { + Log_Printf(LOG_DMA_LEVEL, "[DMA] Channel SCSI: Not flushing buffer. Bad direction!"); + return; + } + + TRY(prb) { + Log_Printf(LOG_DMA_LEVEL, "[DMA] Channel SCSI: Flush buffer to memory at $%08x, %i bytes", + dma[CHANNEL_SCSI].next,espdma_buf_size); + + for (i = 0; i < DMA_BURST_SIZE; i+=4) { + if (dma[CHANNEL_SCSI].next