Source to src/dma.c
/* NeXT DMA Emulation
* Contains informations from QEMU-NeXT
* 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_DEBUG
#define IO_SEG_MASK 0x1FFFF
int get_channel(Uint32 address);
int get_interrupt_type(int channel);
void dma_interrupt(int channel);
void dma_initialize_buffer(int channel, Uint8 offset);
struct {
Uint8 csr;
Uint32 saved_next;
Uint32 saved_limit;
Uint32 saved_start;
Uint32 saved_stop;
Uint32 next;
Uint32 limit;
Uint32 start;
Uint32 stop;
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];
/* 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: 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_WARN, "Unknown DMA channel!\n");
return -1;
break;
}
}
int get_interrupt_type(int channel) {
switch (channel) {
case CHANNEL_SCSI: return INT_SCSI_DMA; break;
case CHANNEL_SOUNDOUT: return INT_SND_OUT_DMA; break;
case CHANNEL_DISK: return INT_DISK_DMA; break;
case CHANNEL_SOUNDIN: return INT_SND_IN_DMA; break;
case CHANNEL_PRINTER: return INT_PRINTER_DMA; break;
case CHANNEL_SCC: return INT_SCC_DMA; break;
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 INT_VIDEO; break;
case CHANNEL_M2R: return INT_M2R_DMA; break;
case CHANNEL_R2M: return INT_R2M_DMA; break;
default:
Log_Printf(LOG_WARN, "Unknown DMA interrupt!\n");
return 0;
break;
}
}
void DMA_CSR_Read(void) { // 0x02000010, length of register is byte on 68030 based NeXT Computer
int channel = get_channel(IoAccessCurrentAddress);
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);
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
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;
}
/* 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_INITBUF) {
dma_initialize_buffer(channel, 0);
}
if (writecsr&DMA_SETSUPDATE) {
dma[channel].csr |= DMA_SUPDATE;
}
if (writecsr&DMA_SETENABLE) {
dma[channel].csr |= DMA_ENABLE;
/* 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_CLRCOMPLETE) {
dma[channel].csr &= ~DMA_COMPLETE;
}
set_interrupt(interrupt, RELEASE_INT); // experimental
}
void DMA_Saved_Next_Read(void) { // 0x02004000
int channel = get_channel(IoAccessCurrentAddress-0x3FF0);
IoMem_WriteLong(IoAccessCurrentAddress & IO_SEG_MASK, dma[channel].saved_next);
Log_Printf(LOG_DMA_LEVEL,"DMA SNext read at $%08x val=$%08x PC=$%08x\n", IoAccessCurrentAddress, dma[channel].saved_next, m68k_getpc());
}
void DMA_Saved_Next_Write(void) {
int channel = get_channel(IoAccessCurrentAddress-0x3FF0);
dma[channel].saved_next = IoMem_ReadLong(IoAccessCurrentAddress & IO_SEG_MASK);
Log_Printf(LOG_DMA_LEVEL,"DMA SNext write at $%08x val=$%08x PC=$%08x\n", IoAccessCurrentAddress, dma[channel].saved_next, m68k_getpc());
}
void DMA_Saved_Limit_Read(void) { // 0x02004004
int channel = get_channel(IoAccessCurrentAddress-0x3FF4);
IoMem_WriteLong(IoAccessCurrentAddress & IO_SEG_MASK, dma[channel].saved_limit);
Log_Printf(LOG_DMA_LEVEL,"DMA SLimit read at $%08x val=$%08x PC=$%08x\n", IoAccessCurrentAddress, dma[channel].saved_limit, m68k_getpc());
}
void DMA_Saved_Limit_Write(void) {
int channel = get_channel(IoAccessCurrentAddress-0x3FF4);
dma[channel].saved_limit = IoMem_ReadLong(IoAccessCurrentAddress & IO_SEG_MASK);
Log_Printf(LOG_DMA_LEVEL,"DMA SLimit write at $%08x val=$%08x PC=$%08x\n", IoAccessCurrentAddress, dma[channel].saved_limit, m68k_getpc());
}
void DMA_Saved_Start_Read(void) { // 0x02004008
int channel = get_channel(IoAccessCurrentAddress-0x3FF8);
IoMem_WriteLong(IoAccessCurrentAddress & IO_SEG_MASK, dma[channel].saved_start);
Log_Printf(LOG_DMA_LEVEL,"DMA SStart read at $%08x val=$%08x PC=$%08x\n", IoAccessCurrentAddress, dma[channel].saved_start, m68k_getpc());
}
void DMA_Saved_Start_Write(void) {
int channel = get_channel(IoAccessCurrentAddress-0x3FF8);
dma[channel].saved_start = IoMem_ReadLong(IoAccessCurrentAddress & IO_SEG_MASK);
Log_Printf(LOG_DMA_LEVEL,"DMA SStart write at $%08x val=$%08x PC=$%08x\n", IoAccessCurrentAddress, dma[channel].saved_start, m68k_getpc());
}
void DMA_Saved_Stop_Read(void) { // 0x0200400c
int channel = get_channel(IoAccessCurrentAddress-0x3FFC);
IoMem_WriteLong(IoAccessCurrentAddress & IO_SEG_MASK, dma[channel].saved_stop);
Log_Printf(LOG_DMA_LEVEL,"DMA SStop read at $%08x val=$%08x PC=$%08x\n", IoAccessCurrentAddress, dma[channel].saved_stop, m68k_getpc());
}
void DMA_Saved_Stop_Write(void) {
int channel = get_channel(IoAccessCurrentAddress-0x3FFC);
dma[channel].saved_stop = IoMem_ReadLong(IoAccessCurrentAddress & IO_SEG_MASK);
Log_Printf(LOG_DMA_LEVEL,"DMA SStop write at $%08x val=$%08x PC=$%08x\n", IoAccessCurrentAddress, dma[channel].saved_stop, m68k_getpc());
}
void DMA_Next_Read(void) { // 0x02004010
int channel = get_channel(IoAccessCurrentAddress-0x4000);
IoMem_WriteLong(IoAccessCurrentAddress & IO_SEG_MASK, dma[channel].next);
Log_Printf(LOG_DMA_LEVEL,"DMA Next read at $%08x val=$%08x PC=$%08x\n", IoAccessCurrentAddress, dma[channel].next, m68k_getpc());
}
void DMA_Next_Write(void) {
int channel = get_channel(IoAccessCurrentAddress-0x4000);
dma[channel].next = IoMem_ReadLong(IoAccessCurrentAddress & IO_SEG_MASK);
Log_Printf(LOG_DMA_LEVEL,"DMA Next write at $%08x val=$%08x PC=$%08x\n", IoAccessCurrentAddress, dma[channel].next, m68k_getpc());
}
void DMA_Limit_Read(void) { // 0x02004014
int channel = get_channel(IoAccessCurrentAddress-0x4004);
IoMem_WriteLong(IoAccessCurrentAddress & IO_SEG_MASK, dma[channel].limit);
Log_Printf(LOG_DMA_LEVEL,"DMA Limit read at $%08x val=$%08x PC=$%08x\n", IoAccessCurrentAddress, dma[channel].limit, m68k_getpc());
}
void DMA_Limit_Write(void) {
int channel = get_channel(IoAccessCurrentAddress-0x4004);
dma[channel].limit = IoMem_ReadLong(IoAccessCurrentAddress & IO_SEG_MASK);
Log_Printf(LOG_DMA_LEVEL,"DMA Limit write at $%08x val=$%08x PC=$%08x\n", IoAccessCurrentAddress, dma[channel].limit, m68k_getpc());
}
void DMA_Start_Read(void) { // 0x02004018
int channel = get_channel(IoAccessCurrentAddress-0x4008);
IoMem_WriteLong(IoAccessCurrentAddress & IO_SEG_MASK, dma[channel].start);
Log_Printf(LOG_DMA_LEVEL,"DMA Start read at $%08x val=$%08x PC=$%08x\n", IoAccessCurrentAddress, dma[channel].start, m68k_getpc());
}
void DMA_Start_Write(void) {
int channel = get_channel(IoAccessCurrentAddress-0x4008);
dma[channel].start = IoMem_ReadLong(IoAccessCurrentAddress & IO_SEG_MASK);
Log_Printf(LOG_DMA_LEVEL,"DMA Start write at $%08x val=$%08x PC=$%08x\n", IoAccessCurrentAddress, dma[channel].start, m68k_getpc());
}
void DMA_Stop_Read(void) { // 0x0200401c
int channel = get_channel(IoAccessCurrentAddress-0x400C);
IoMem_WriteLong(IoAccessCurrentAddress & IO_SEG_MASK, dma[channel].stop);
Log_Printf(LOG_DMA_LEVEL,"DMA Stop read at $%08x val=$%08x PC=$%08x\n", IoAccessCurrentAddress, dma[channel].stop, m68k_getpc());
}
void DMA_Stop_Write(void) {
int channel = get_channel(IoAccessCurrentAddress-0x400C);
dma[channel].stop = IoMem_ReadLong(IoAccessCurrentAddress & IO_SEG_MASK);
Log_Printf(LOG_DMA_LEVEL,"DMA Stop write at $%08x val=$%08x PC=$%08x\n", IoAccessCurrentAddress, dma[channel].stop, m68k_getpc());
}
void DMA_Init_Read(void) { // 0x02004210
int channel = get_channel(IoAccessCurrentAddress-0x4200);
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].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());
}
/* Initialize DMA internal buffer */
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);
/* 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);
}
}
/* 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[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_limit<DMA_BURST_SIZE) {
if (floppy_select) {
while (espdma_buf_limit<DMA_BURST_SIZE && flp_buffer.size>0) {
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_limit<DMA_BURST_SIZE && esp_counter>0 && SCSIbus.phase==PHASE_DI) {
espdma_buf[espdma_buf_limit]=SCSIdisk_Send_Data();
esp_counter--;
espdma_buf_limit++;
espdma_buf_size++;
}
}
}
if (espdma_buf_limit<DMA_BURST_SIZE) { /* Not complete, stop */
Log_Printf(LOG_DMA_LEVEL, "[DMA] Channel SCSI: No more data. Stopping with %i residual bytes.",
espdma_buf_size);
break;
} else { /* Empty DMA channel FIFO (only if limit reached FIFO size) */
ESP_DMA_set_status();
while (dma[CHANNEL_SCSI].next<dma[CHANNEL_SCSI].limit && espdma_buf_size>0) {
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
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].next<dma[CHANNEL_SCSI].limit) {
Log_Printf(LOG_DMA_LEVEL, "[DMA] Channel SCSI: Flush buffer to memory at $%08x, 4 bytes",dma[CHANNEL_SCSI].next);
if (espdma_buf_size>0) {
/* 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
dma_interrupt(CHANNEL_SCSI);
}
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 (!(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) {
/* Read data from memory to DMA channel FIFO (only if limit < FIFO size) */
if (espdma_buf_limit<DMA_BURST_SIZE) {
while (dma[CHANNEL_SCSI].next<dma[CHANNEL_SCSI].limit && espdma_buf_limit<DMA_BURST_SIZE) {
dma_putlong(NEXTMemory_ReadLong(dma[CHANNEL_SCSI].next), espdma_buf, espdma_buf_limit);
dma[CHANNEL_SCSI].next+=4;
espdma_buf_limit+=4;
espdma_buf_size+=4;
}
}
if (espdma_buf_limit<DMA_BURST_SIZE) { /* Not complete, stop */
Log_Printf(LOG_DMA_LEVEL, "[DMA] Channel SCSI: Channel limit reached. Stopping with %i residual bytes.",
espdma_buf_size);
break;
} else { /* Empty DMA channel FIFO (only if limit reached FIFO size) */
ESP_DMA_set_status();
if (floppy_select) {
while (espdma_buf_size>0 && flp_buffer.size<flp_buffer.limit) {
flp_buffer.data[flp_buffer.size]=espdma_buf[espdma_buf_limit-espdma_buf_size];
flp_buffer.size++;
espdma_buf_size--;
}
} else {
while (espdma_buf_size>0 && 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 ((floppy_select && flp_buffer.size<flp_buffer.limit) || SCSIbus.phase==PHASE_DO) {
Log_Printf(LOG_DMA_LEVEL, "[DMA] Channel SCSI: Warning! Data not yet written to disk.");
if (espdma_buf_size!=0) {
Log_Printf(LOG_WARN, "[DMA] Channel SCSI: WARNING: Loss of data in DMA buffer possible!");
}
}
dma_interrupt(CHANNEL_SCSI);
}
/* Channel MO */
void dma_mo_write_memory(void) {
Log_Printf(LOG_DMA_LEVEL, "[DMA] Channel MO: Write to memory at $%08x, %i bytes",
dma[CHANNEL_DISK].next,dma[CHANNEL_DISK].limit-dma[CHANNEL_DISK].next);
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].next<=dma[CHANNEL_DISK].limit) {
/* Fill DMA channel FIFO (only if limit < FIFO size) */
if (modma_buf_limit<DMA_BURST_SIZE) {
while (modma_buf_limit<DMA_BURST_SIZE && ecc_buffer[eccout].size>0) {
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_limit<DMA_BURST_SIZE) { /* Not complete, stop */
Log_Printf(LOG_DMA_LEVEL, "[DMA] Channel MO: No more data. Stopping with %i residual bytes.",
modma_buf_size);
break;
} else { /* Empty DMA channel FIFO (only if limit reached FIFO size) */
while (dma[CHANNEL_DISK].next<dma[CHANNEL_DISK].limit && modma_buf_size>0) {
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);
}
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);
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].next<dma[CHANNEL_DISK].limit) {
/* Read data from memory to DMA channel FIFO (only if limit < FIFO size) */
if (modma_buf_limit<DMA_BURST_SIZE) {
while (dma[CHANNEL_DISK].next<dma[CHANNEL_DISK].limit && modma_buf_limit<DMA_BURST_SIZE) {
dma_putlong(NEXTMemory_ReadLong(dma[CHANNEL_DISK].next), modma_buf, modma_buf_limit);
dma[CHANNEL_DISK].next+=4;
modma_buf_limit+=4;
modma_buf_size+=4;
}
}
if (modma_buf_limit<DMA_BURST_SIZE) { /* Not complete, stop */
Log_Printf(LOG_DMA_LEVEL, "[DMA] Channel MO: Channel limit reached. Stopping with %i residual bytes.",
modma_buf_size);
break;
} else { /* Empty DMA channel FIFO (only if limit reached FIFO size) */
while (modma_buf_size>0 && ecc_buffer[eccin].size<ecc_buffer[eccin].limit) {
ecc_buffer[eccin].data[ecc_buffer[eccin].size]=modma_buf[modma_buf_limit-modma_buf_size];
ecc_buffer[eccin].size++;
modma_buf_size--;
}
if (modma_buf_size>0) { /* 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<ecc_buffer[eccin].limit) {
Log_Printf(LOG_DMA_LEVEL, "[DMA] Channel MO: Warning! Data not yet written to disk.");
if (modma_buf_size!=0) {
Log_Printf(LOG_WARN, "[DMA] Channel MO: WARNING: Loss of data in DMA buffer possible!");
}
}
dma_interrupt(CHANNEL_DISK);
}
Uint8* dma_sndout_read_memory(int* len) {
int i;
Uint8* result = NULL;
*len = 0;
if (dma[CHANNEL_SOUNDOUT].csr&DMA_ENABLE) {
Log_Printf(LOG_DMA_LEVEL, "[DMA] Channel Sound Out: Read from memory at $%08x, %i bytes",
dma[CHANNEL_SOUNDOUT].next,dma[CHANNEL_SOUNDOUT].limit-dma[CHANNEL_SOUNDOUT].next);
if ((dma[CHANNEL_SOUNDOUT].limit&3) || (dma[CHANNEL_SOUNDOUT].next&3)) {
Log_Printf(LOG_WARN, "[DMA] Channel Sound Out: Error! Bad alignment! (Next: $%08X, Limit: $%08X)",
dma[CHANNEL_SOUNDOUT].next, dma[CHANNEL_SOUNDOUT].limit);
dma[CHANNEL_SOUNDOUT].next &= ~3;
dma[CHANNEL_SOUNDOUT].limit &= ~3;
}
TRY(prb) {
*len = dma[CHANNEL_SOUNDOUT].limit - dma[CHANNEL_SOUNDOUT].next;
result = malloc(*len * 2);
for(i = 0; dma[CHANNEL_SOUNDOUT].next<dma[CHANNEL_SOUNDOUT].limit; dma[CHANNEL_SOUNDOUT].next++, i++)
result[i] = NEXTMemory_ReadByte(dma[CHANNEL_SOUNDOUT].next);
} CATCH(prb) {
Log_Printf(LOG_WARN, "[DMA] Channel Sound Out: Bus error reading from %08x",dma[CHANNEL_SOUNDOUT].next);
dma[CHANNEL_SOUNDOUT].csr &= ~DMA_ENABLE;
dma[CHANNEL_SOUNDOUT].csr |= (DMA_COMPLETE|DMA_BUSEXC);
} ENDTRY
}
return result;
}
void dma_sndout_intr() {
if (dma[CHANNEL_SOUNDOUT].csr&DMA_ENABLE) {
dma_interrupt(CHANNEL_SOUNDOUT);
}
}
int dma_sndin_write_memory() {
int value = 0;
if (dma[CHANNEL_SOUNDIN].csr&DMA_ENABLE) {
Audio_Input_Lock();
Log_Printf(LOG_DMA_LEVEL, "[DMA] Channel Sound In: Write to memory at $%08x, %i bytes",
dma[CHANNEL_SOUNDIN].next,dma[CHANNEL_SOUNDIN].limit-dma[CHANNEL_SOUNDIN].next);
TRY(prb) {
while (dma[CHANNEL_SOUNDIN].next<dma[CHANNEL_SOUNDIN].limit) {
value = Audio_Input_Read();
if (value < 0) {
break;
}
NEXTMemory_WriteByte(dma[CHANNEL_SOUNDIN].next, value);
dma[CHANNEL_SOUNDIN].next++;
}
} CATCH(prb) {
Log_Printf(LOG_WARN, "[DMA] Channel Sound In: Bus error reading from %08x",dma[CHANNEL_SOUNDIN].next);
dma[CHANNEL_SOUNDIN].csr &= ~DMA_ENABLE;
dma[CHANNEL_SOUNDIN].csr |= (DMA_COMPLETE|DMA_BUSEXC);
} ENDTRY
Audio_Input_Unlock();
dma[CHANNEL_SOUNDIN].saved_limit = dma[CHANNEL_SOUNDIN].next;
dma_interrupt(CHANNEL_SOUNDIN);
return (dma[CHANNEL_SOUNDIN].next==dma[CHANNEL_SOUNDIN].limit);
}
return 1;
}
/* Channel Printer */
void dma_printer_read_memory(void) {
if (dma[CHANNEL_PRINTER].csr&DMA_ENABLE) {
Log_Printf(LOG_DMA_LEVEL, "[DMA] Channel Printer: Read from memory at $%08x, %i bytes",
dma[CHANNEL_PRINTER].next,dma[CHANNEL_PRINTER].limit-dma[CHANNEL_PRINTER].next);
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].next<dma[CHANNEL_PRINTER].limit && lp_buffer.size<lp_buffer.limit) {
lp_buffer.data[lp_buffer.size]=NEXTMemory_ReadByte(dma[CHANNEL_PRINTER].next);
lp_buffer.size++;
dma[CHANNEL_PRINTER].next++;
}
} CATCH(prb) {
Log_Printf(LOG_WARN, "[DMA] Channel Printer: Bus error reading from %08x",dma[CHANNEL_PRINTER].next);
dma[CHANNEL_PRINTER].csr &= ~DMA_ENABLE;
dma[CHANNEL_PRINTER].csr |= (DMA_COMPLETE|DMA_BUSEXC);
} ENDTRY
dma_interrupt(CHANNEL_PRINTER);
}
}
/* Channel Ethernet (this channel does not use DMA buffering) */
#define EN_EOP 0x80000000 /* end of packet */
#define EN_BOP 0x40000000 /* beginning of packet */
#define ENADDR(x) ((x)&~(EN_EOP|EN_BOP))
Uint32 saved_next_turbo = 0;
static void dma_enet_interrupt(int channel) {
int interrupt = get_interrupt_type(channel);
dma[channel].csr |= DMA_COMPLETE;
if (dma[channel].csr & DMA_SUPDATE) { /* if we are in chaining mode */
/* Update pointers */
saved_next_turbo = dma[channel].next;
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);
}
void dma_enet_write_memory(bool eop) {
Log_Printf(LOG_DMA_LEVEL, "[DMA] Channel Ethernet Receive: Write to memory at $%08x, %i bytes",
dma[CHANNEL_EN_RX].next,dma[CHANNEL_EN_RX].limit-dma[CHANNEL_EN_RX].next);
if (!(dma[CHANNEL_EN_RX].csr&DMA_ENABLE)) {
Log_Printf(LOG_WARN, "[DMA] Channel Ethernet Receive: Error! DMA not enabled!");
return;
}
if ((dma[CHANNEL_EN_RX].limit%DMA_BURST_SIZE) || (dma[CHANNEL_EN_RX].next%DMA_BURST_SIZE)) {
Log_Printf(LOG_WARN, "[DMA] Channel Ethernet Receive: Error! Bad alignment! (Next: $%08X, Limit: $%08X)",
dma[CHANNEL_EN_RX].next, dma[CHANNEL_EN_RX].limit);
abort();
}
TRY(prb) {
while (dma[CHANNEL_EN_RX].next<dma[CHANNEL_EN_RX].limit && enet_rx_buffer.size>0) {
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 (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<ENADDR(dma[CHANNEL_EN_TX].limit) && enet_tx_buffer.size<enet_tx_buffer.limit) {
enet_tx_buffer.data[enet_tx_buffer.size]=NEXTMemory_ReadByte(dma[CHANNEL_EN_TX].next);
enet_tx_buffer.size++;
dma[CHANNEL_EN_TX].next++;
}
} CATCH(prb) {
Log_Printf(LOG_WARN, "[DMA] Channel Ethernet Transmit: Bus error while writing to %08x",dma[CHANNEL_EN_TX].next);
dma[CHANNEL_EN_TX].csr &= ~DMA_ENABLE;
dma[CHANNEL_EN_TX].csr |= (DMA_COMPLETE|DMA_BUSEXC);
} ENDTRY
if (dma[CHANNEL_EN_TX].limit&EN_EOP) {
Log_Printf(LOG_DMA_LEVEL, "[DMA] Channel Ethernet Transmit: Packet done.");
dma_enet_interrupt(CHANNEL_EN_TX);
return true;
}
dma_enet_interrupt(CHANNEL_EN_TX);
}
return false;
}
/* Memory to Memory */
Uint32 m2m_buffer[DMA_BURST_SIZE];
int m2m_buffer_size;
void M2MDMA_IO_Handler(void) {
CycInt_AcknowledgeInterrupt();
if (dma[CHANNEL_R2M].csr&DMA_ENABLE) {
dma_m2m_write_memory();
CycInt_AddRelativeInterruptCycles(4, INTERRUPT_M2M_IO);
}
}
void dma_m2m(void) {
if ((dma[CHANNEL_M2R].csr&DMA_ENABLE) && (dma[CHANNEL_R2M].csr&DMA_ENABLE)) {
if (((dma[CHANNEL_R2M].limit-dma[CHANNEL_R2M].next)%DMA_BURST_SIZE) ||
((dma[CHANNEL_M2R].limit-dma[CHANNEL_M2R].next)%DMA_BURST_SIZE)) {
Log_Printf(LOG_WARN, "[DMA] Channel M2M: Error! Memory not burst size aligned!");
return;
}
Log_Printf(LOG_DMA_LEVEL, "[DMA] Channel M2M: Copying %i bytes from $%08X to %i bytes at $%08X.",
dma[CHANNEL_M2R].limit-dma[CHANNEL_M2R].next,dma[CHANNEL_M2R].next,
dma[CHANNEL_R2M].limit-dma[CHANNEL_R2M].next,dma[CHANNEL_R2M].next);
CycInt_AddRelativeInterruptCycles(4, INTERRUPT_M2M_IO);
}
}
void dma_m2m_write_memory(void) {
if (dma[CHANNEL_R2M].next<dma[CHANNEL_R2M].limit) {
if (dma[CHANNEL_M2R].next<dma[CHANNEL_M2R].limit) {
/* (Re)fill the buffer, if there is still data to read */
m2m_buffer_size = 0;
TRY(prb) {
while (m2m_buffer_size < DMA_BURST_SIZE) {
m2m_buffer[m2m_buffer_size]=NEXTMemory_ReadByte(dma[CHANNEL_M2R].next);
m2m_buffer_size++;
dma[CHANNEL_M2R].next++;
}
} CATCH(prb) {
Log_Printf(LOG_WARN, "[DMA] Channel M2M: Bus error while reading from %08x",dma[CHANNEL_M2R].next);
dma[CHANNEL_M2R].csr &= ~DMA_ENABLE;
dma[CHANNEL_M2R].csr |= (DMA_COMPLETE|DMA_BUSEXC);
} ENDTRY
dma_interrupt(CHANNEL_M2R);
} else {
/* Re-use data in buffer */
m2m_buffer_size = DMA_BURST_SIZE;
}
TRY(prb) {
/* Write the contents of the buffer to memory */
while (m2m_buffer_size > 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
}
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<dma[CHANNEL_DSP].limit) {
NEXTMemory_WriteByte(dma[CHANNEL_DSP].next, val);
dma[CHANNEL_DSP].next++;
}
} CATCH(prb) {
Log_Printf(LOG_WARN, "[DMA] Channel DSP: Bus error while writing to %08x",dma[CHANNEL_DSP].next);
dma[CHANNEL_DSP].csr &= ~DMA_ENABLE;
dma[CHANNEL_DSP].csr |= (DMA_COMPLETE|DMA_BUSEXC);
} ENDTRY
if (dma[CHANNEL_DSP].next==dma[CHANNEL_DSP].limit) {
DSP_SetIRQB();
dma_interrupt(CHANNEL_DSP);
}
}
Uint8 dma_dsp_read_memory(void) {
Uint8 val = 0;
Log_Printf(LOG_DMA_DSP_LEVEL, "[DMA] Channel DSP: Read from 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 val;
}
TRY(prb) {
if (dma[CHANNEL_DSP].next<dma[CHANNEL_DSP].limit) {
val = NEXTMemory_ReadByte(dma[CHANNEL_DSP].next);
dma[CHANNEL_DSP].next++;
}
} CATCH(prb) {
Log_Printf(LOG_WARN, "[DMA] Channel DSP: Bus error while writing to %08x",dma[CHANNEL_DSP].next);
dma[CHANNEL_DSP].csr &= ~DMA_ENABLE;
dma[CHANNEL_DSP].csr |= (DMA_COMPLETE|DMA_BUSEXC);
} ENDTRY
if (dma[CHANNEL_DSP].next==dma[CHANNEL_DSP].limit) {
DSP_SetIRQB();
dma_interrupt(CHANNEL_DSP);
}
return val;
}
bool dma_dsp_ready(void) {
if (!(dma[CHANNEL_DSP].csr&DMA_ENABLE) ||
!(dma[CHANNEL_DSP].next<dma[CHANNEL_DSP].limit)) {
Log_Printf(LOG_DEBUG, "[DMA] Channel DSP: Not ready!");
return false;
} else {
return true;
}
}
/* ---------------------- DMA Scratchpad ---------------------- */
/* This is used to interrupt at vertical screen retrace.
* TODO: find out how the interrupt is generated in real
* hardware using the Limit register of the DMA chip.
* (0xEA * 1024 = visible videomem size)
*/
/* Interrupt Handler (called from Video_InterruptHandler in video.c) */
void dma_video_interrupt(void) {
if (dma[CHANNEL_VIDEO].limit==0xEA) {
set_interrupt(INT_VIDEO, SET_INT); /* interrupt is released by writing to CSR */
} else if (dma[CHANNEL_VIDEO].limit && dma[CHANNEL_VIDEO].limit!=0xEA) {
abort();
}
}
/* FIXME: This is just for passing power-on test. Add real SCC channel later. */
void dma_scc_read_memory(void) {
Log_Printf(LOG_DMA_LEVEL, "[DMA] Channel SCC: Read from memory at $%08x, %i bytes",
dma[CHANNEL_SCC].next,dma[CHANNEL_SCC].limit-dma[CHANNEL_SCC].next);
while (dma[CHANNEL_SCC].next<dma[CHANNEL_SCC].limit) {
scc_buf[0]=NEXTMemory_ReadByte(dma[CHANNEL_SCC].next);
dma[CHANNEL_SCC].next++;
}
dma_interrupt(CHANNEL_SCC);
}
/* DMA CSR on Turbo systems */
/* CSR read bits */
#define TDMA_BYTECOUNT_MASK 0x00000007
#define TDMA_WRITEPTR_MASK 0x00000018
#define TDMA_READPTR_MASK 0x00000060
#define TDMA_DIRTY_MASK 0x00000180
#define TDMA_BUFSEL 0x00000200
#define TDMA_ENABLE 0x01000000
#define TDMA_SUPDATE 0x02000000
#define TDMA_COMPLETE 0x08000000
#define TDMA_BUSEXC 0x10000000
/* CSR write bits */
#define TDMA_SETENABLE 0x00010000
#define TDMA_SETSUPDATE 0x00020000
#define TDMA_DEV2M 0x00040000
#define TDMA_CLRCOMPLETE 0x00080000
#define TDMA_RESET 0x00100000
#define TDMA_SETCOMPLETE 0x00200000
#define TDMA_FLUSH 0x00400000
#define TDMA_BUFRESET 0x00800000
/* CSR masks */
#define TDMA_CMD_MASK 0x00FB0000
void TDMA_CSR_Read(void) { // 0x02000010, length of register is byte on 68030 based NeXT Computer
int channel = get_channel(IoAccessCurrentAddress);
IoMem_WriteLong(IoAccessCurrentAddress & IO_SEG_MASK, dma[channel].csr<<24);
Log_Printf(LOG_DMA_LEVEL,"DMA CSR read at $%08x val=$%08x PC=$%08x\n", IoAccessCurrentAddress, dma[channel].csr<<24, m68k_getpc());
}
void TDMA_CSR_Write(void) {
int channel = get_channel(IoAccessCurrentAddress);
int interrupt = get_interrupt_type(channel);
Uint32 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());
/* For debugging */
if(writecsr&TDMA_DEV2M)
Log_Printf(LOG_DMA_LEVEL,"DMA from dev to mem");
else
Log_Printf(LOG_DMA_LEVEL,"DMA from mem to dev");
switch (writecsr&TDMA_CMD_MASK) {
case TDMA_RESET:
Log_Printf(LOG_DMA_LEVEL,"DMA reset"); break;
case TDMA_BUFRESET:
Log_Printf(LOG_DMA_LEVEL,"DMA initialize buffers"); break;
case (TDMA_RESET | TDMA_BUFRESET):
case (TDMA_RESET | TDMA_BUFRESET | TDMA_CLRCOMPLETE):
Log_Printf(LOG_DMA_LEVEL,"DMA reset and initialize buffers"); break;
case TDMA_CLRCOMPLETE:
Log_Printf(LOG_DMA_LEVEL,"DMA end chaining"); break;
case (TDMA_SETSUPDATE | TDMA_CLRCOMPLETE):
Log_Printf(LOG_DMA_LEVEL,"DMA continue chaining"); break;
case TDMA_SETENABLE:
Log_Printf(LOG_DMA_LEVEL,"DMA start single transfer"); break;
case (TDMA_SETENABLE | TDMA_SETSUPDATE):
case (TDMA_SETENABLE | TDMA_SETSUPDATE | TDMA_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_WARN,"DMA: unknown command!"); break;
}
/* Handle CSR bits */
dma[channel].direction = (writecsr>>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<dma[CHANNEL_SCSI].limit) {
if (espdma_buf_size) {
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;
}
}
} 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
dma_interrupt(CHANNEL_SCSI);
}