diff options
Diffstat (limited to 'src/mess/machine/upd71071.c')
-rw-r--r-- | src/mess/machine/upd71071.c | 444 |
1 files changed, 444 insertions, 0 deletions
diff --git a/src/mess/machine/upd71071.c b/src/mess/machine/upd71071.c new file mode 100644 index 00000000000..e5a3b1ccab0 --- /dev/null +++ b/src/mess/machine/upd71071.c @@ -0,0 +1,444 @@ + +/* + + NEC uPD71071 DMA Controller + Used on the Fujitsu FM-Towns + + Register description: + + 0x00: Initialise (Write-only) + - bit 0: Reset + - bit 1: 16-bit data bus + + 0x01: Channel Register + On read: + - bits 0-3: Selected channel + - bit 4: Only base registers may be read or written + On write: + - bits 0-1: Select channel for programming count, address, and mode registers + - bit 2: Only base registers can be read or written to + + 0x02: + 0x03: Count Register (16-bit) + DMA Transfer counter + + 0x04: + 0x05: + 0x06: + 0x07: Address Register (32-bit) + Self-explanatory, I hope. :) + NOTE: Datasheet clearly shows this as 24-bit, with register 7 unused. + But the FM-Towns definitely uses reg 7 as bits 24-31. + + 0x08: + 0x09: Device Control register (16-bit) + bit 0: Enable memory-to-memory (MTM) transfers + bit 1: Enable fixed address for channel 0 only (MTM only) + bit 2: Disable DMA operation (stops HLDRQ signal to the CPU) + bit 3: Use compressed timing + bit 4: Rotational Priority + bit 5: Extended Writing + bit 6: DMARQ active level (1=active low) + bit 7: DMAAK active level (1=active high) + bit 8: Bus mode (0=bus release, 1=bus hold) + bit 9: Wait Enable during Verify + + 0x0a: Mode Control register + bit 0: Transfer size (1=16-bit, 0=8-bit, 16-bit data bus size only) + bit 2-3: Transfer direction (ignored for MTM transfers) + 00 = Verify + 01 = I/O to memory + 10 = memory to I/O + 11 = invalid + bit 4: Enable auto-initialise + bit 5: Address direction (0=increment, 1=decrement, affects only current Address reg) + bit 6-7: Transfer mode (ignored for MTM transfers) + 00 = Demand + 01 = Single + 10 = Block + 11 = Cascade + + 0x0b: Status register + bit 0-3: Terminal count (per channel) + bit 4-7: DMA request present (external hardware DMA only) + + 0x0c: + 0x0d: Temporary register (16-bit, read-only) + Stores the last data transferred in an MTM transfer + + 0x0e: Request register + bit 0-3: Software DMA request (1=set) + bit 0 only in MTM transfers + + 0x0f: Mask register + bit 0-3: DMARQ mask + bits 1 and 0 only in MTM transfers + + +*/ + +#include "emu.h" +#include "machine/upd71071.h" + +struct upd71071_reg +{ + UINT8 initialise; + UINT8 channel; + UINT16 count_current[4]; + UINT16 count_base[4]; + UINT32 address_current[4]; + UINT32 address_base[4]; + UINT16 device_control; + UINT8 mode_control[4]; + UINT8 status; + UINT8 temp_l; + UINT8 temp_h; + UINT8 request; + UINT8 mask; +}; + +typedef struct _upd71071_t upd71071_t; +struct _upd71071_t +{ + struct upd71071_reg reg; + int selected_channel; + int buswidth; + int dmarq[4]; + emu_timer* timer[4]; + int in_progress[4]; + int transfer_size[4]; + int base; + const upd71071_intf* intf; +}; + +INLINE upd71071_t *get_safe_token(device_t *device) +{ + assert(device != NULL); + assert(device->type() == UPD71071); + + return (upd71071_t*)downcast<legacy_device_base *>(device)->token(); +} + +static TIMER_CALLBACK(dma_transfer_timer) +{ + // single byte or word transfer + device_t* device = (device_t*)ptr; + upd71071_t* dmac = get_safe_token(device); + address_space* space = device->machine().device(dmac->intf->cputag)->memory().space(AS_PROGRAM); + int channel = param; + UINT16 data = 0; // data to transfer + + switch(dmac->reg.mode_control[channel] & 0x0c) + { + case 0x00: // Verify + break; + case 0x04: // I/O -> memory + if(dmac->intf->dma_read[channel]) + data = dmac->intf->dma_read[channel](device->machine()); + space->write_byte(dmac->reg.address_current[channel],data & 0xff); + if(dmac->reg.mode_control[channel] & 0x20) // Address direction + dmac->reg.address_current[channel]--; + else + dmac->reg.address_current[channel]++; + if(dmac->reg.count_current[channel] == 0) + { + if(dmac->reg.mode_control[channel] & 0x10) // auto-initialise + { + dmac->reg.address_current[channel] = dmac->reg.address_base[channel]; + dmac->reg.count_current[channel] = dmac->reg.count_base[channel]; + } + // TODO: send terminal count + } + else + dmac->reg.count_current[channel]--; + break; + case 0x08: // memory -> I/O + data = space->read_byte(dmac->reg.address_current[channel]); + if(dmac->intf->dma_read[channel]) + dmac->intf->dma_write[channel](device->machine(),data); + if(dmac->reg.mode_control[channel] & 0x20) // Address direction + dmac->reg.address_current[channel]--; + else + dmac->reg.address_current[channel]++; + if(dmac->reg.count_current[channel] == 0) + { + if(dmac->reg.mode_control[channel] & 0x10) // auto-initialise + { + dmac->reg.address_current[channel] = dmac->reg.address_base[channel]; + dmac->reg.count_current[channel] = dmac->reg.count_base[channel]; + } + // TODO: send terminal count + } + else + dmac->reg.count_current[channel]--; + break; + case 0x0c: // Invalid + break; + } +} + +static void upd71071_soft_reset(device_t* device) +{ + upd71071_t* dmac = get_safe_token(device); + int x; + + // Does not change base/current address, count, or buswidth + dmac->selected_channel = 0; + dmac->base = 0; + for(x=0;x<4;x++) + dmac->reg.mode_control[x] = 0; + dmac->reg.device_control = 0; + dmac->reg.temp_h = 0; + dmac->reg.temp_l = 0; + dmac->reg.mask = 0x0f; // mask all channels + dmac->reg.status &= ~0x0f; // clears bits 0-3 only + dmac->reg.request = 0; +} + +int upd71071_dmarq(device_t* device, int state,int channel) +{ + upd71071_t* dmac = get_safe_token(device); + + if(state != 0) + { + if(dmac->reg.device_control & 0x0004) + return 2; + + if(dmac->reg.mask & (1 << channel)) // is channel masked? + return 1; + + dmac->dmarq[channel] = 1; // DMARQ line is set + dmac->reg.status |= (0x10 << channel); + + // start transfer + switch(dmac->reg.mode_control[channel] & 0xc0) + { + case 0x00: // Demand + // TODO + break; + case 0x40: // Single + dmac->timer[channel]->adjust(attotime::from_hz(dmac->intf->clock),channel); + break; + case 0x80: // Block + // TODO + break; + case 0xc0: // Cascade + // TODO + break; + + } + } + else + { + dmac->dmarq[channel] = 0; // clear DMARQ line + dmac->reg.status &= ~(0x10 << channel); + dmac->reg.status |= (0x01 << channel); // END or TC + } + return 0; +} + +static DEVICE_START(upd71071) +{ + upd71071_t* dmac = get_safe_token(device); + int x; + + dmac->intf = (const upd71071_intf*)device->static_config(); + for(x=0;x<4;x++) + { + dmac->timer[x] = device->machine().scheduler().timer_alloc(FUNC(dma_transfer_timer), (void*)device); + } + dmac->selected_channel = 0; +} + +static READ8_DEVICE_HANDLER(upd71071_read) +{ + upd71071_t* dmac = get_safe_token(device); + UINT8 ret = 0; + + logerror("DMA: read from register %02x\n",offset); + switch(offset) + { + case 0x01: // Channel + ret = (1 << dmac->selected_channel); + if(dmac->base != 0) + ret |= 0x10; + break; + case 0x02: // Count (low) + if(dmac->base != 0) + ret = dmac->reg.count_base[dmac->selected_channel] & 0xff; + else + ret = dmac->reg.count_current[dmac->selected_channel] & 0xff; + break; + case 0x03: // Count (high) + if(dmac->base != 0) + ret = (dmac->reg.count_base[dmac->selected_channel] >> 8) & 0xff; + else + ret = (dmac->reg.count_current[dmac->selected_channel] >> 8) & 0xff; + break; + case 0x04: // Address (low) + if(dmac->base != 0) + ret = dmac->reg.address_base[dmac->selected_channel] & 0xff; + else + ret = dmac->reg.address_current[dmac->selected_channel] & 0xff; + break; + case 0x05: // Address (mid) + if(dmac->base != 0) + ret = (dmac->reg.address_base[dmac->selected_channel] >> 8) & 0xff; + else + ret = (dmac->reg.address_current[dmac->selected_channel] >> 8) & 0xff; + break; + case 0x06: // Address (high) + if(dmac->base != 0) + ret = (dmac->reg.address_base[dmac->selected_channel] >> 16) & 0xff; + else + ret = (dmac->reg.address_current[dmac->selected_channel] >> 16) & 0xff; + break; + case 0x07: // Address (highest) + if(dmac->base != 0) + ret = (dmac->reg.address_base[dmac->selected_channel] >> 24) & 0xff; + else + ret = (dmac->reg.address_current[dmac->selected_channel] >> 24) & 0xff; + break; + case 0x08: // Device control (low) + ret = dmac->reg.device_control & 0xff; + break; + case 0x09: // Device control (high) + ret = (dmac->reg.device_control >> 8) & 0xff; + break; + case 0x0a: // Mode control + ret = dmac->reg.mode_control[dmac->selected_channel]; + break; + case 0x0b: // Status + ret = dmac->reg.status; + dmac->reg.status &= ~0x0f; // resets END/TC? + break; + case 0x0c: // Temporary (low) + ret = dmac->reg.temp_h; + break; + case 0x0d: // Temporary (high) + ret = dmac->reg.temp_l; + break; + case 0x0e: // Request + ret = dmac->reg.request; + break; + case 0x0f: // Mask + ret = dmac->reg.mask; + break; + } + return ret; +} + +static WRITE8_DEVICE_HANDLER(upd71071_write) +{ + upd71071_t* dmac = get_safe_token(device); + + switch(offset) + { + case 0x00: // Initialise + // TODO: reset (bit 0) + dmac->buswidth = data & 0x02; + if(data & 0x01) + upd71071_soft_reset(device); + logerror("DMA: Initialise [%02x]\n",data); + break; + case 0x01: // Channel + dmac->selected_channel = data & 0x03; + dmac->base = data & 0x04; + logerror("DMA: Channel selected [%02x]\n",data); + break; + case 0x02: // Count (low) + dmac->reg.count_base[dmac->selected_channel] = + (dmac->reg.count_base[dmac->selected_channel] & 0xff00) | data; + if(dmac->base == 0) + dmac->reg.count_current[dmac->selected_channel] = + (dmac->reg.count_current[dmac->selected_channel] & 0xff00) | data; + logerror("DMA: Channel %i Counter set [%04x]\n",dmac->selected_channel,dmac->reg.count_base[dmac->selected_channel]); + break; + case 0x03: // Count (high) + dmac->reg.count_base[dmac->selected_channel] = + (dmac->reg.count_base[dmac->selected_channel] & 0x00ff) | (data << 8); + if(dmac->base == 0) + dmac->reg.count_current[dmac->selected_channel] = + (dmac->reg.count_current[dmac->selected_channel] & 0x00ff) | (data << 8); + logerror("DMA: Channel %i Counter set [%04x]\n",dmac->selected_channel,dmac->reg.count_base[dmac->selected_channel]); + break; + case 0x04: // Address (low) + dmac->reg.address_base[dmac->selected_channel] = + (dmac->reg.address_base[dmac->selected_channel] & 0xffffff00) | data; + if(dmac->base == 0) + dmac->reg.address_current[dmac->selected_channel] = + (dmac->reg.address_current[dmac->selected_channel] & 0xffffff00) | data; + logerror("DMA: Channel %i Address set [%08x]\n",dmac->selected_channel,dmac->reg.address_base[dmac->selected_channel]); + break; + case 0x05: // Address (mid) + dmac->reg.address_base[dmac->selected_channel] = + (dmac->reg.address_base[dmac->selected_channel] & 0xffff00ff) | (data << 8); + if(dmac->base == 0) + dmac->reg.address_current[dmac->selected_channel] = + (dmac->reg.address_current[dmac->selected_channel] & 0xffff00ff) | (data << 8); + logerror("DMA: Channel %i Address set [%08x]\n",dmac->selected_channel,dmac->reg.address_base[dmac->selected_channel]); + break; + case 0x06: // Address (high) + dmac->reg.address_base[dmac->selected_channel] = + (dmac->reg.address_base[dmac->selected_channel] & 0xff00ffff) | (data << 16); + if(dmac->base == 0) + dmac->reg.address_current[dmac->selected_channel] = + (dmac->reg.address_current[dmac->selected_channel] & 0xff00ffff) | (data << 16); + logerror("DMA: Channel %i Address set [%08x]\n",dmac->selected_channel,dmac->reg.address_base[dmac->selected_channel]); + break; + case 0x07: // Address (highest) + dmac->reg.address_base[dmac->selected_channel] = + (dmac->reg.address_base[dmac->selected_channel] & 0x00ffffff) | (data << 24); + if(dmac->base == 0) + dmac->reg.address_current[dmac->selected_channel] = + (dmac->reg.address_current[dmac->selected_channel] & 0x00ffffff) | (data << 24); + logerror("DMA: Channel %i Address set [%08x]\n",dmac->selected_channel,dmac->reg.address_base[dmac->selected_channel]); + break; + case 0x08: // Device control (low) + dmac->reg.device_control = (dmac->reg.device_control & 0xff00) | data; + logerror("DMA: Device control set [%04x]\n",dmac->reg.device_control); + break; + case 0x09: // Device control (high) + dmac->reg.device_control = (dmac->reg.device_control & 0x00ff) | (data << 8); + logerror("DMA: Device control set [%04x]\n",dmac->reg.device_control); + break; + case 0x0a: // Mode control + dmac->reg.mode_control[dmac->selected_channel] = data; + logerror("DMA: Channel %i Mode control set [%02x]\n",dmac->selected_channel,dmac->reg.mode_control[dmac->selected_channel]); + break; + case 0x0e: // Request + dmac->reg.request = data; + logerror("DMA: Request set [%02x]\n",data); + break; + case 0x0f: // Mask + dmac->reg.mask = data; + logerror("DMA: Mask set [%02x]\n",data); + break; + } +} + +DEVICE_GET_INFO(upd71071) +{ + switch (state) + { + /* --- the following bits of info are returned as 64-bit signed integers --- */ + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(upd71071_t); break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(upd71071); break; + case DEVINFO_FCT_STOP: /* Nothing */ break; + case DEVINFO_FCT_RESET: /* Nothing */ break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case DEVINFO_STR_NAME: strcpy(info->s, "NEC uPD71071"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "DMA Controller"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright the MESS Team"); break; + } +} + +READ8_DEVICE_HANDLER(upd71071_r) { return upd71071_read(device,offset); } +WRITE8_DEVICE_HANDLER(upd71071_w) { upd71071_write(device,offset,data); } + +DEFINE_LEGACY_DEVICE(UPD71071, upd71071); |