apple/dafb.cpp: New DAFB device for the Quadra 700/900/950, including video and "turbo SCSI". [R. Belmont]

This commit is contained in:
arbee 2023-07-05 11:04:58 -04:00
parent 445bf3e6f4
commit 578bef8bae
3 changed files with 1168 additions and 458 deletions

943
src/mame/apple/dafb.cpp Normal file
View File

@ -0,0 +1,943 @@
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/*
Apple DAFB and DAFB II video (343S0128-01 for DAFB, 343S0128-A for DAFB II)
Emulation by R. Belmont
Some inspiration from mv_sonora by Olivier Galibert and nubus_48gc by Vas Crabb
DAFB (officially Direct Access Frame Buffer, internally Dave's Awesome Frame Buffer) was the on-board video
for the Quadra 700, 900, and 950. Standalone DAFB and DAFB-II include what Apple calls "Turbo SCSI", which
is an interface for up to 2 5394/5396 chips that adds a configurable wait state to each access and can hold
off /DTACK on pseudo-DMA reads and writes.
Shipping configurations:
DAFB - original standalone chip, Quadra 700 and 900 (returns versions 0 and 1)
DAFB II - revised standalone chip with 15 bpp support added (uses AC843 CODEC instead of AC842) (returns version 2)
Used in Quadra 950.
MEMC - DAFB II without the Turbo SCSI logic, in the djMEMC and MEMCjr memory controllers. (returns version 3)
Used in LC 475, LC 575, Quadra 605, Quadra 610, Quadra 650, and Quadra 800.
This version uses a DP8533 timing generator instead of the DP8531.
The Turbo SCSI block moved into the IOSB and PrimeTime I/O ASICs for the machines where DAFB moved into the
memory controller. It was enhanced slightly to allow longword pseudo-DMA transfers.
----------------------------------------------------------------------------------------------------------------
Apple assigns 3 pins for monitor IDs. These allow 8 possible codes:
000 - color 2-Page Display (21")
001 - monochrome Full Page display (15")
010 - color 512x384 (12")
011 - monochrome 2 Page display (21")
100 - NTSC
101 - color Full Page display (15")
110 - High-Resolution Color (13" 640x480) or "type 6" extended codes
111 - No monitor connected or "type 7" extended codes
For extended codes, you drive one of the 3 pins at a time and read the 2
undriven pins. See http://support.apple.com/kb/TA21618?viewlocale=en_US
for details.
*/
#include "emu.h"
#include "dafb.h"
#define LOG_SWATCH (1U << 1)
#define LOG_CLOCKGEN (1U << 2)
#define LOG_MONSENSE (1U << 3)
#define LOG_AC842 (1U << 4)
#define LOG_TURBOSCSI (1U << 5)
#define VERBOSE (0)
#include "logmacro.h"
static constexpr int VRAM_SIZE = 0x200000 / sizeof(u32);
DEFINE_DEVICE_TYPE(DAFB, dafb_device, "macdafb", "Apple DAFB video")
//-------------------------------------------------
// ADDRESS_MAP
//-------------------------------------------------
void dafb_base::map(address_map &map)
{
map(0x00000000, 0x000000ff).rw(FUNC(dafb_base::dafb_r), FUNC(dafb_base::dafb_w));
map(0x00000100, 0x000001ff).rw(FUNC(dafb_base::swatch_r), FUNC(dafb_base::swatch_w));
map(0x00000200, 0x000002ff).rw(FUNC(dafb_base::ac842_r), FUNC(dafb_base::ac842_w));
map(0x00000300, 0x000003ff).rw(FUNC(dafb_base::clockgen_r), FUNC(dafb_base::clockgen_w));
}
dafb_base::dafb_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) :
device_t(mconfig, type, tag, owner, clock),
m_dafb_version(1),
m_screen(*this, "screen"),
m_palette(*this, "palette"),
m_monitor_config(*this, "monitor"),
m_irq(*this),
m_vram_offset(0), m_timing_control(0), m_mode(0), m_depth(0), m_monitor_id(0), m_pal_address(0),
m_pal_idx(0), m_ac842_pbctrl(0), m_base(0), m_stride(1024), m_test(0), m_swatch_mode(1),
m_cursor_line(0), m_anim_line(0), m_int_status(0), m_hres(0), m_vres(0), m_htotal(0), m_vtotal(0),
m_config(0), m_block_control(0), m_swatch_test(0)
{
std::fill(std::begin(m_horizontal_params), std::end(m_horizontal_params), 0);
std::fill(std::begin(m_vertical_params), std::end(m_vertical_params), 0);
m_scsi_read_cycles[0] = m_scsi_read_cycles[1] = 3;
m_scsi_write_cycles[0] = m_scsi_write_cycles[1] = 3,
m_scsi_dma_read_cycles[0] = m_scsi_dma_read_cycles[1] = 3;
m_scsi_dma_write_cycles[0] = m_scsi_dma_write_cycles[1] = 3;
}
dafb_device::dafb_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
dafb_base(mconfig, DAFB, tag, owner, clock),
m_maincpu(*this, finder_base::DUMMY_TAG)
{
m_drq[0] = m_drq[1] = 0;
m_ncr[0] = m_ncr[1] = nullptr;
}
void dafb_base::device_start()
{
m_vram = std::make_unique<u32[]>(VRAM_SIZE);
m_vbl_timer = timer_alloc(FUNC(dafb_base::vbl_tick), this);
m_cursor_timer = timer_alloc(FUNC(dafb_base::cursor_tick), this);
m_vbl_timer->adjust(attotime::never);
m_cursor_timer->adjust(attotime::never);
save_item(NAME(m_timing_control));
save_item(NAME(m_vram_offset));
save_item(NAME(m_mode));
save_item(NAME(m_depth));
save_item(NAME(m_monitor_id));
save_item(NAME(m_base));
save_item(NAME(m_stride));
save_item(NAME(m_swatch_mode));
save_item(NAME(m_pal_address));
save_item(NAME(m_pal_idx));
save_item(NAME(m_ac842_pbctrl));
save_item(NAME(m_cursor_line));
save_item(NAME(m_hres));
save_item(NAME(m_vres));
save_item(NAME(m_htotal));
save_item(NAME(m_vtotal));
save_item(NAME(m_pixel_clock));
save_item(NAME(m_horizontal_params));
save_item(NAME(m_vertical_params));
save_item(NAME(m_dp8531_regs));
save_item(NAME(m_test));
save_item(NAME(m_config));
save_item(NAME(m_block_control));
save_item(NAME(m_swatch_test));
save_item(NAME(m_int_status));
save_pointer(NAME(m_vram), VRAM_SIZE);
machine().save().register_postload(save_prepost_delegate(FUNC(dafb_base::recalc_mode), this));
}
void dafb_base::device_reset()
{
}
void dafb_base::device_add_mconfig(machine_config &config)
{
SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
// dot clock, htotal, hstart, hend, vtotal, vstart, vend
m_screen->set_raw(31334400, 896, 0, 640, 525, 0, 480);
m_screen->set_screen_update(FUNC(dafb_base::screen_update));
PALETTE(config, m_palette).set_entries(256);
}
static constexpr u8 ext(u8 bc, u8 ac, u8 ab)
{
return 0x40 | (bc << 4) | (ac << 2) | ab;
}
static INPUT_PORTS_START(monitor_config)
PORT_START("monitor")
PORT_CONFNAME(0x7f, 6, "Monitor type")
PORT_CONFSETTING(0x00, u8"Mac 21\" Color Display (1152\u00d7870)") // "RGB 2 Page" or "Kong"
PORT_CONFSETTING(0x01, u8"Mac Portrait Display (B&W 15\" 640\u00d7870)") // "Full Page" or "Portrait"
PORT_CONFSETTING(0x02, u8"Mac RGB Display (12\" 512\u00d7384)") // "Rubik" (modified IIgs AppleColor RGB)
PORT_CONFSETTING(0x03, u8"Mac Two-Page Display (B&W 21\" 1152\u00d7870)") // "2 Page"
PORT_CONFSETTING(0x06, u8"Mac Hi-Res Display (12-14\" 640\u00d7480)") // "High Res"
PORT_CONFSETTING(ext(0, 0, 0), "PAL Encoder (640\u00d7480, 768\u00d7576)")
PORT_CONFSETTING(ext(1, 1, 0), "NTSC Encoder (512\u00d7384, 640\u00d7480)")
PORT_CONFSETTING(ext(1, 1, 3), "640x480 VGA")
PORT_CONFSETTING(ext(2, 3, 1), "832x624 16\" RGB") // "Goldfish" or "16 inch RGB"
PORT_CONFSETTING(ext(3, 0, 0), "PAL (640\u00d7480, 768\u00d7576)")
INPUT_PORTS_END
ioport_constructor dafb_base::device_input_ports() const
{
return INPUT_PORTS_NAME(monitor_config);
}
u32 dafb_base::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
auto const vram8 = util::big_endian_cast<u8 const>(&m_vram[0]) + m_base;
const pen_t *pens = m_palette->pens();
if (m_swatch_mode & 1)
{
return 0;
}
// if convolution is enabled, the stride is fixed at 1024
const u32 stride = BIT(m_config, 3) ? 1024 : m_stride;
switch (m_mode)
{
case 0: // 1bpp
{
for (int y = 0; y < m_vres; y++)
{
u32 *scanline = &bitmap.pix(y);
for (int x = 0; x < m_hres/8; x++)
{
u8 const pixels = vram8[(y * stride) + x];
*scanline++ = pens[(pixels>>7)&1];
*scanline++ = pens[(pixels>>6)&1];
*scanline++ = pens[(pixels>>5)&1];
*scanline++ = pens[(pixels>>4)&1];
*scanline++ = pens[(pixels>>3)&1];
*scanline++ = pens[(pixels>>2)&1];
*scanline++ = pens[(pixels>>1)&1];
*scanline++ = pens[(pixels&1)];
}
}
}
break;
case 1: // 2bpp
{
for (int y = 0; y < m_vres; y++)
{
u32 *scanline = &bitmap.pix(y);
for (int x = 0; x < m_hres/4; x++)
{
u8 const pixels = vram8[(y * stride) + x];
*scanline++ = pens[((pixels>>6)&3)];
*scanline++ = pens[((pixels>>4)&3)];
*scanline++ = pens[((pixels>>2)&3)];
*scanline++ = pens[(pixels&3)];
}
}
}
break;
case 2: // 4bpp
{
for (int y = 0; y < m_vres; y++)
{
u32 *scanline = &bitmap.pix(y);
for (int x = 0; x < m_hres/2; x++)
{
u8 const pixels = vram8[(y * stride) + x];
*scanline++ = pens[(pixels>>4)];
*scanline++ = pens[(pixels&0xf)];
}
}
}
break;
case 3: // 8bpp
{
for (int y = 0; y < m_vres; y++)
{
u32 *scanline = &bitmap.pix(y);
for (int x = 0; x < m_hres; x++)
{
u8 const pixels = vram8[(y * stride) + x];
*scanline++ = pens[pixels];
}
}
}
break;
case 4: // 24 bpp
for (int y = 0; y < m_vres; y++)
{
u32 *scanline = &bitmap.pix(y);
u32 const *base = &m_vram[(y * (stride/4)) + (m_base/4)];
for (int x = 0; x < m_hres; x++)
{
*scanline++ = *base++;
}
}
break;
}
return 0;
}
u32 dafb_base::dafb_r(offs_t offset)
{
switch (offset<<2)
{
case 0x1c: // inverse of monitor sense
{
u8 mon = m_monitor_config->read();
u8 res;
LOGMASKED(LOG_MONSENSE, "mon = %02x, m_monitor_id = %02x\n", mon, m_monitor_id);
if (mon & 0x40)
{
res = 7;
if (m_monitor_id == 0x4)
{
res &= 4 | (BIT(mon, 5) << 1) | BIT(mon, 4);
}
if (m_monitor_id == 0x2)
{
res &= (BIT(mon, 3) << 2) | 2 | BIT(mon, 2);
}
if (m_monitor_id == 0x1)
{
res &= (BIT(mon, 1) << 2) | (BIT(mon, 0) << 1) | 1;
}
}
else
{
res = mon;
}
LOGMASKED(LOG_MONSENSE, "sense result = %x\n", res);
return res ^ 7; // return value is the inverse of the sense bits
}
break;
case 0x24: // SCSI 539x #1 status
return m_scsi_ctrl[0] | (m_drq[0] << 9);
case 0x28: // SCSI 539x #2 status
return m_scsi_ctrl[1] | (m_drq[1] << 9);
case 0x2c: // test / version (0 = original, 1 = NTSC and PAL fix, 2 = discrete DAFB II, 3 = MEMC/MEMCjr integrated DAFB cell)
return (m_test & 0x1ff) | (2<<9);
}
return 0;
}
void dafb_base::dafb_w(offs_t offset, u32 data)
{
switch (offset << 2)
{
case 0: // bits 20-9 of base
m_base &= 0x1e0;
m_base |= (data & 0xfff) << 9;
LOG("baseA: wrote %08x => %08x\n", data, m_base);
break;
case 4: // bits 8-5 of base
m_base &= ~0x1e0;
m_base |= (data & 0xf) << 5;
LOG("baseB wrote %08x => %08x\n", data, m_base);
break;
case 8:
m_stride = data<<2; // stride in DWORDs
LOG("Stride = %d\n", m_stride);
break;
case 0xc: // timing control
m_timing_control = data;
LOG("Timing control = %08x\n", data);
break;
case 0x10: // configuration
LOG("DAFB config = %08x\n", data);
m_config = data;
break;
case 0x14: // block write control
LOG("Block write control = %08x\n", data);
m_block_control = data;
break;
case 0x1c: // drive monitor sense lines. 0=drive to value in bit 0 of TEST, 1=tri-state
m_monitor_id = (data & 0x7) ^ 7;
LOGMASKED(LOG_MONSENSE, "%x to sense drive\n", data & 0xf);
break;
/*
SCSI bus 1 control:
bit 0 = SCSI register read is 6 clocks (if neither bit 0 or 1 are set, 3 clocks?)
bit 1 = SCSI register read is 4 clocks
bit 2 = SCSI register write is 3 clocks (else what?)
bit 3 = SCSI pseudo-DMA read is 3 clocks (else what?)
bit 4 = SCSI pseudo-DMA write is 5 clocks
bit 5 = SCSI pseudo-DMA write is 3 clocks
bit 6 = CS PW Check (?)
bit 7 = DRQ Check Read (PDMA reads wait if DRQ isn't set and bus error on timeout)
bit 8 = DRQ Check Write
bit 9 = DREQ status read
*/
case 0x24:
m_scsi_ctrl[0] = data;
if (BIT(data, 0))
{
m_scsi_read_cycles[0] = 6;
}
else if (BIT(data, 1))
{
m_scsi_read_cycles[0] = 4;
}
else
{
m_scsi_read_cycles[0] = 3;
}
if (BIT(data, 2))
{
m_scsi_write_cycles[0] = 3;
}
else
{
m_scsi_write_cycles[0] = 4;
}
if (BIT(data, 3))
{
m_scsi_dma_read_cycles[0] = 3;
}
else
{
m_scsi_dma_read_cycles[0] = 4;
}
if (BIT(data, 4))
{
m_scsi_dma_write_cycles[0] = 5;
}
else if (BIT(data, 5))
{
m_scsi_dma_write_cycles[0] = 3;
}
LOGMASKED(LOG_TURBOSCSI, "SCSI bus 1 timings: R %d W %d DMAR %d DMAW %d\n", m_scsi_read_cycles[0], m_scsi_write_cycles[0], m_scsi_dma_read_cycles[0], m_scsi_dma_write_cycles[0]);
break;
// SCSI bus 2 control, same definitions as above
case 0x28:
m_scsi_ctrl[1] = data;
if (BIT(data, 0))
{
m_scsi_read_cycles[1] = 6;
}
else if (BIT(data, 1))
{
m_scsi_read_cycles[1] = 4;
}
else
{
m_scsi_read_cycles[1] = 3;
}
if (BIT(data, 2))
{
m_scsi_write_cycles[1] = 3;
}
else
{
m_scsi_write_cycles[1] = 4;
}
if (BIT(data, 3))
{
m_scsi_dma_read_cycles[1] = 3;
}
else
{
m_scsi_dma_read_cycles[1] = 4;
}
if (BIT(data, 4))
{
m_scsi_dma_write_cycles[1] = 5;
}
else if (BIT(data, 5))
{
m_scsi_dma_write_cycles[1] = 3;
}
LOGMASKED(LOG_TURBOSCSI, "SCSI bus 2 timings: R %d W %d DMAR %d DMAW %d\n", m_scsi_read_cycles[1], m_scsi_write_cycles[1], m_scsi_dma_read_cycles[1], m_scsi_dma_write_cycles[1]);
break;
// TEST register. Bit 0 is supposedly the value to drive on the monitor sense pins, but that's not what
// the code does on the Q700.
case 0x2c:
LOG("%08x to TEST\n", data);
m_test = data;
break;
}
}
u32 dafb_base::swatch_r(offs_t offset)
{
switch (offset << 2)
{
case 0x8: // IRQ/VBL status
return m_int_status;
case 0xc: // clear cursor scanline int
m_int_status &= ~4;
recalc_ints();
break;
case 0x14: // clear VBL int
m_int_status &= ~1;
recalc_ints();
break;
case 0x20: // unused register, used by the driver to stash data
return m_swatch_test;
}
return 0;
}
void dafb_base::swatch_w(offs_t offset, u32 data)
{
switch (offset << 2)
{
case 0x0: // Swatch mode
m_swatch_mode = data;
break;
case 0x4:
if (data & 1) // VBL enable
{
m_vbl_timer->adjust(m_screen->time_until_pos(480, 0), 0);
}
else
{
m_vbl_timer->adjust(attotime::never);
m_int_status &= ~1;
recalc_ints();
}
if (data & 2) // aux scanline interrupt enable
{
fatalerror("DAFB: Aux scanline interrupt enable not supported!\n");
}
if (data & 4) // cursor scanline interrupt enable
{
m_cursor_timer->adjust(m_screen->time_until_pos(m_cursor_line, 0), 0);
}
else
{
m_cursor_timer->adjust(attotime::never);
m_int_status &= ~4;
recalc_ints();
}
break;
case 0xc: // clear cursor scanline int
m_int_status &= ~4;
recalc_ints();
break;
case 0x14: // clear VBL int
m_int_status &= ~1;
recalc_ints();
break;
case 0x18: // cursor IRQ line
m_cursor_line = data;
break;
case 0x1c: // animation IRQ line
m_anim_line = data;
break;
case 0x20:
m_swatch_test = data;
break;
case 0x24: // HSERR - location of horizontal serration pulse
case 0x28: // HLFLN - Half-line point where equalizing pulses or serrations fall
case 0x2c: // HEQ - Horizontal equalizing pulse
case 0x30: // HSP - Horizontal sync pulse
case 0x34: // HBWAY - Horizontal breezeway
case 0x38: // HBRST - Horizontal burst (where the NTSC colorburst would happen if this wasn't RGB)
case 0x3c: // HBP - Horizontal back porch
case 0x40: // HAL - Horizontal active line (start of active display area)
case 0x44: // HFP - Horizontal front porch (end of active display area)
case 0x48: // HPIX - Horizontal pixels - total # of pixel locations in a line minus 2
LOGMASKED(LOG_SWATCH, "%d to horiz param offset %02x\n", data, offset);
m_horizontal_params[offset - (0x24 / 4)] = data;
break;
case 0x4c: // VHLINE - Vertical half-lines, the total # of half-lines in a field (odd for interlaced, even for NI)
case 0x50: // VSYNC - Vertical sync
case 0x54: // VBPEQ - Vertical Back Porch Equalization
case 0x58: // VBP - Vertical Back Porch (start of active display area)
case 0x5c: // VAL - Vertical Active Lines (end of active display area)
case 0x60: // VFP - Vertical Front Porch
case 0x64: // VFPEQ - Vertical Front Porch Equalization
LOGMASKED(LOG_SWATCH, "%d to vertical param offset %02x\n", data, offset);
m_vertical_params[offset - (0x4c / 4)] = data;
break;
}
}
u32 dafb_base::ac842_r(offs_t offset)
{
switch (offset << 2)
{
case 0:
m_pal_idx = 0;
return m_pal_address;
case 0x10:
{
pen_t entry = m_palette->pen(m_pal_address);
m_pal_idx++;
switch (m_pal_idx - 1)
{
case 0:
return (entry >> 16) & 0xff;
case 1:
return (entry >> 8) & 0xff;
case 2:
return entry & 0xff;
}
}
break;
case 0x20:
return m_ac842_pbctrl;
}
return 0;
}
void dafb_base::ac842_w(offs_t offset, u32 data)
{
switch (offset << 2)
{
case 0:
m_pal_address = data & 0xff;
m_pal_idx = 0;
break;
case 0x10:
if ((m_monitor_config->read() == 1) || (m_monitor_config->read() == 3))
{
// monochrome monitors put info only on the blue channel
if (m_pal_idx == 2)
{
m_palette->set_pen_red_level(m_pal_address, data & 0xff);
m_palette->set_pen_green_level(m_pal_address, data & 0xff);
m_palette->set_pen_blue_level(m_pal_address, data & 0xff);
}
}
else
{
switch (m_pal_idx)
{
case 0:
m_palette->set_pen_red_level(m_pal_address, data & 0xff);
break;
case 1:
m_palette->set_pen_green_level(m_pal_address, data & 0xff);
break;
case 2:
m_palette->set_pen_blue_level(m_pal_address, data & 0xff);
break;
}
}
m_pal_idx++;
if (m_pal_idx == 3)
{
m_pal_idx = 0;
m_pal_address++;
}
break;
case 0x20:
m_ac842_pbctrl = data;
LOGMASKED(LOG_AC842, "%02x to AC842 pixel bus control, & 0x1c = %02x\n", data, data & 0x1c);
switch (data & 0x1c)
{
case 0x00:
m_mode = 0; // 1bpp
break;
case 0x08:
m_mode = 1; // 2bpp
break;
case 0x10:
m_mode = 2; // 4bpp
break;
case 0x18:
m_mode = 3; // 8bpp
break;
case 0x1c:
m_mode = 4; // 24bpp
break;
}
recalc_mode();
break;
}
}
void dafb_base::recalc_mode()
{
m_htotal = m_horizontal_params[HPIX];
m_vtotal = m_vertical_params[VFPEQ] >> 1;
if ((m_htotal > 0) && (m_vtotal > 0))
{
m_hres = m_horizontal_params[HFP] - m_horizontal_params[HAL];
m_vres = (m_vertical_params[VFP] >> 1) - (m_vertical_params[VAL] >> 1); // these are in half-line units for interlace
// Quadra 700 programs the wrong base for the 512x384 mode and is off-by-1 on the vertical res.
// Maybe that monitor wasn't really intended to be supported?
if (m_hres == 512)
{
m_base = 0x1000;
m_vres = 384;
}
const int clockdiv = 1 << ((m_ac842_pbctrl & 0x60) >> 5);
LOGMASKED(LOG_SWATCH, "RAW hres %d vres %d htotal %d vtotal %d (clockdiv %d conv %d)\n", m_hres, m_vres, m_htotal, m_vtotal, clockdiv, BIT(m_config, 3));
// If convolution is active, divide the horiz. res and stride by the clock divider.
// If it's not, multiply the horiz. res by the clockdiv.
if (BIT(m_config, 3))
{
m_hres /= clockdiv;
m_stride /= clockdiv;
// All modes with convolution enabled on the Q700 overstate the horizontal resolution by 23 for some reason.
// The documentation, including the spreadsheet of mode examples, doesn't show that.
// TODO: possibly working around a bug in early chip revisions? Check when DAFB-II machines are suported.
m_hres -= 23;
}
else
{
m_hres *= clockdiv;
}
// if we're interlaced, bump the vertical back to double
if (BIT(m_config, 2))
{
m_vres <<= 1;
m_vtotal <<= 1;
}
LOGMASKED(LOG_SWATCH, "hres %d vres %d htotal %d vtotal %d\n", m_hres, m_vres, m_htotal, m_vtotal);
if ((m_hres != 0) && (m_vres != 0))
{
rectangle visarea(0, m_hres - 1, 0, m_vres - 1);
m_screen->configure(m_htotal, m_vtotal, visarea, attotime::from_ticks(m_htotal * m_vtotal, m_pixel_clock).as_attoseconds());
}
}
}
u32 dafb_base::clockgen_r(offs_t offset)
{
return 0;
}
void dafb_base::clockgen_w(offs_t offset, u32 data)
{
m_dp8531_regs[offset>>2] = data & 0xf;
LOGMASKED(LOG_CLOCKGEN, "%s: Write %x to DP8531 at %d\n", tag(), data, offset);
if ((offset >> 2) == 15)
{
int r = m_dp8531_regs[6] << 8 | m_dp8531_regs[5] << 4 | m_dp8531_regs[4];
int p = (1 << m_dp8531_regs[9]);
int n_modulus = m_dp8531_regs[3]<<12 | m_dp8531_regs[2]<<8 | m_dp8531_regs[1]<<4 | m_dp8531_regs[0];
int a = (n_modulus & 0x1f) ^ 0x1f; // the inverse of the lowest 5 bits of n_modulus
int b = (n_modulus & 0xffe0) >> 5; // the top 11 bits of n_modulus
a = std::min(a, b);
b = std::max(b, 2);
// N = 32(B - A) + 31(1 + A)
int n = (32 * (b - a)) + (31 * (1 + a));
int vco = ((20'000'000/r) * n);
m_pixel_clock = vco / p;
LOGMASKED(LOG_CLOCKGEN, "VCO %d, PCLK %d\n", vco, m_pixel_clock);
}
}
u32 dafb_base::vram_r(offs_t offset)
{
return m_vram[offset];
}
void dafb_base::vram_w(offs_t offset, u32 data, u32 mem_mask)
{
COMBINE_DATA(&m_vram[offset]);
}
void dafb_base::recalc_ints()
{
if (m_int_status != 0)
{
m_irq(ASSERT_LINE);
}
else
{
m_irq(CLEAR_LINE);
}
}
TIMER_CALLBACK_MEMBER(dafb_base::vbl_tick)
{
m_int_status |= 1;
recalc_ints();
m_vbl_timer->adjust(m_screen->time_until_pos(480, 0), 0);
}
TIMER_CALLBACK_MEMBER(dafb_base::cursor_tick)
{
m_int_status |= 4;
recalc_ints();
m_cursor_timer->adjust(m_screen->time_until_pos(m_cursor_line, 0), 0);
}
// ************************************************************************
// dafb_device overrides/additions
// ************************************************************************
void dafb_device::device_start()
{
dafb_base::device_start();
m_maincpu->set_emmu_enable(true);
}
template <int bus>
u8 dafb_device::turboscsi_r(offs_t offset)
{
if (!machine().side_effects_disabled())
{
m_maincpu->adjust_icount(-m_scsi_read_cycles[bus]);
}
return m_ncr[bus]->read(offset>>4);
}
template u8 dafb_device::turboscsi_r<0>(offs_t offset);
template u8 dafb_device::turboscsi_r<1>(offs_t offset);
template <int bus>
void dafb_device::turboscsi_w(offs_t offset, u8 data)
{
m_maincpu->adjust_icount(-m_scsi_write_cycles[bus]);
m_ncr[bus]->write(offset>>4, data);
}
template void dafb_device::turboscsi_w<0>(offs_t offset, u8 data);
template void dafb_device::turboscsi_w<1>(offs_t offset, u8 data);
template <int bus>
u16 dafb_device::turboscsi_dma_r(offs_t offset, u16 mem_mask)
{
if (!machine().side_effects_disabled() && BIT(offset << 1, 18))
{
m_maincpu->adjust_icount(-m_scsi_dma_read_cycles[bus]);
}
if (BIT(m_scsi_ctrl[bus], 7))
{
if (!m_drq[bus])
{
// The real DAFB simply holds off /DTACK here, we simulate that
// by rewinding and repeating the instruction until DRQ is asserted.
m_maincpu->restart_this_instruction();
m_maincpu->spin_until_time(attotime::from_usec(50));
return 0xffff;
}
}
if (mem_mask == 0xffff)
{
return m_ncr[bus]->dma16_swap_r();
}
else if (ACCESSING_BITS_0_7)
{
return m_ncr[bus]->dma_r();
}
else
{
return m_ncr[bus]->dma_r()<<8;
}
}
template u16 dafb_device::turboscsi_dma_r<0>(offs_t offset, u16 mem_mask);
template u16 dafb_device::turboscsi_dma_r<1>(offs_t offset, u16 mem_mask);
template <int bus>
void dafb_device::turboscsi_dma_w(offs_t offset, u16 data, u16 mem_mask)
{
if (!machine().side_effects_disabled() && BIT(offset << 1, 18))
{
m_maincpu->adjust_icount(-m_scsi_dma_write_cycles[bus]);
}
LOGMASKED(LOG_TURBOSCSI, "dma_w %04x (mask %04x)\n", data & mem_mask, mem_mask);
if (BIT(m_scsi_ctrl[bus], 8))
{
if (!m_drq[bus])
{
m_maincpu->restart_this_instruction();
m_maincpu->spin_until_time(attotime::from_usec(50));
return;
}
if (mem_mask == 0xffff)
{
m_ncr[bus]->dma16_swap_w(data);
}
else if (ACCESSING_BITS_0_7)
{
m_ncr[bus]->dma_w(data & 0xff);
}
else
{
m_ncr[bus]->dma_w(data >> 8);
}
}
else // no DRQ safety check, just blindly push to the 53c9x
{
if (mem_mask == 0xffff)
{
m_ncr[bus]->dma16_swap_w(data);
}
else if (ACCESSING_BITS_0_7)
{
m_ncr[bus]->dma_w(data & 0xff);
}
else
{
m_ncr[bus]->dma_w(data >> 8);
}
}
}
template void dafb_device::turboscsi_dma_w<0>(offs_t offset, u16 data, u16 mem_mask);
template void dafb_device::turboscsi_dma_w<1>(offs_t offset, u16 data, u16 mem_mask);
template <int bus>
void dafb_device::turboscsi_drq_w(int state)
{
LOGMASKED(LOG_TURBOSCSI, "Bus %d DRQ %d (was %d)\n", bus + 1, state, m_drq[bus]);
m_drq[bus] = state;
}
template void dafb_device::turboscsi_drq_w<0>(int state);
template void dafb_device::turboscsi_drq_w<1>(int state);

128
src/mame/apple/dafb.h Normal file
View File

@ -0,0 +1,128 @@
// license:BSD-3-Clause
// copyright-holders:R. Belmont
#ifndef MAME_APPLE_DAFB_H
#define MAME_APPLE_DAFB_H
#pragma once
#include "cpu/m68000/m68040.h"
#include "machine/ncr53c90.h"
#include "emupal.h"
#include "screen.h"
class dafb_base : public device_t
{
public:
dafb_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock);
virtual ~dafb_base() = default;
void map(address_map &map);
u32 vram_r(offs_t offset);
void vram_w(offs_t offset, u32 data, u32 mem_mask);
auto dafb_irq() { return m_irq.bind(); }
protected:
virtual void device_start() override;
virtual void device_reset() override;
virtual void device_add_mconfig(machine_config &config) override;
virtual ioport_constructor device_input_ports() const override;
u16 m_scsi_ctrl[2];
int m_drq[2];
int m_scsi_read_cycles[2], m_scsi_write_cycles[2], m_scsi_dma_read_cycles[2], m_scsi_dma_write_cycles[2];
int m_dafb_version;
private:
required_device<screen_device> m_screen;
required_device<palette_device> m_palette;
required_ioport m_monitor_config;
devcb_write_line m_irq;
enum
{
HSERR = 0,
HLFLN,
HEQ,
HSP,
HBWAY,
HBRST,
HBP,
HAL,
HFP,
HPIX
};
enum
{
VHLINE = 0,
VSYNC,
VBPEQ,
VBP,
VAL,
VFP,
VFPEQ
};
std::unique_ptr<u32[]> m_vram;
emu_timer *m_vbl_timer, *m_cursor_timer;
u32 m_vram_offset;
u8 m_timing_control, m_mode, m_depth, m_monitor_id;
u8 m_pal_address, m_pal_idx, m_ac842_pbctrl;
u32 m_base, m_stride, m_test;
u32 m_horizontal_params[10], m_vertical_params[7];
u8 m_swatch_mode;
u16 m_cursor_line, m_anim_line;
s32 m_int_status;
u8 m_dp8531_regs[16];
u32 m_pixel_clock;
u32 m_hres, m_vres, m_htotal, m_vtotal, m_config, m_block_control, m_swatch_test;
u32 dafb_r(offs_t offset);
void dafb_w(offs_t offset, u32 data);
u32 swatch_r(offs_t offset);
void swatch_w(offs_t offset, u32 data);
u32 ac842_r(offs_t offset);
void ac842_w(offs_t offset, u32 data);
u32 clockgen_r(offs_t offset);
void clockgen_w(offs_t offset, u32 data);
void recalc_ints();
void recalc_mode();
u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
TIMER_CALLBACK_MEMBER(vbl_tick);
TIMER_CALLBACK_MEMBER(cursor_tick);
};
// Discrete DAFB: Quadra 700 & 900, includes "TurboSCSI"
class dafb_device: public dafb_base
{
public:
dafb_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0);
template <typename... T> void set_maincpu_tag(T &&... args) { m_maincpu.set_tag(std::forward<T>(args)...); }
void set_turboscsi1_device(ncr53c94_device *device) { m_ncr[0] = device; }
void set_turboscsi2_device(ncr53c94_device *device) { m_ncr[1] = device; }
template <int bus> void turboscsi_drq_w(int state);
template <int bus> u8 turboscsi_r(offs_t offset);
template <int bus> void turboscsi_w(offs_t offset, u8 data);
template <int bus> u16 turboscsi_dma_r(offs_t offset, u16 mem_mask);
template <int bus> void turboscsi_dma_w(offs_t offset, u16 data, u16 mem_mask);
protected:
virtual void device_start() override;
private:
required_device<m68000_musashi_device> m_maincpu;
ncr53c94_device *m_ncr[2];
};
DECLARE_DEVICE_TYPE(DAFB, dafb_device)
#endif /* MAME_APPLE_DAFB_H */

View File

@ -9,9 +9,14 @@
****************************************************************************/
//#define USE_ADBMODEM
#include "emu.h"
#ifdef USE_ADBMODEM
#include "adbmodem.h"
#endif
#include "dafb.h"
#include "macadb.h"
#include "macrtc.h"
#include "mactoolbox.h"
@ -31,8 +36,6 @@
#include "machine/z80scc.h"
#include "sound/asc.h"
#include "emupal.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"
@ -42,7 +45,6 @@
#define C15M (C32M/2)
#define C7M (C32M/4)
namespace {
class macquadra_state : public driver_device
@ -54,7 +56,9 @@ public:
m_via1(*this, "via1"),
m_via2(*this, "via2"),
m_macadb(*this, "macadb"),
#ifdef USE_ADBMODEM
m_adbmodem(*this, "adbmodem"),
#endif
m_ram(*this, RAM_TAG),
m_swim(*this, "fdc"),
m_floppy(*this, "fdc:%d", 0U),
@ -62,11 +66,9 @@ public:
m_scsibus1(*this, "scsi1"),
m_ncr1(*this, "scsi1:7:ncr53c96"),
m_sonic(*this, "sonic"),
m_screen(*this, "screen"),
m_palette(*this, "palette"),
m_dafb(*this, "dafb"),
m_easc(*this, "easc"),
m_scc(*this, "scc"),
m_vram(*this, "vram"),
m_cur_floppy(nullptr),
m_hdsel(0)
{
@ -81,7 +83,9 @@ private:
required_device<m68040_device> m_maincpu;
required_device<via6522_device> m_via1, m_via2;
required_device<macadb_device> m_macadb;
#ifdef USE_ADBMODEM
required_device<adbmodem_device> m_adbmodem;
#endif
required_device<ram_device> m_ram;
required_device<applefdintf_device> m_swim;
required_device_array<floppy_connector, 2> m_floppy;
@ -89,39 +93,17 @@ private:
required_device<nscsi_bus_device> m_scsibus1;
required_device<ncr53c96_device> m_ncr1;
required_device<dp83932c_device> m_sonic;
required_device<screen_device> m_screen;
required_device<palette_device> m_palette;
required_device<dafb_device> m_dafb;
required_device<asc_device> m_easc;
required_device<z80scc_device> m_scc;
required_shared_ptr<uint32_t> m_vram;
virtual void machine_start() override;
virtual void machine_reset() override;
virtual void video_start() override;
virtual void video_reset() override;
uint32_t screen_update_dafb(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
uint32_t dafb_r(offs_t offset, uint32_t mem_mask = ~0);
void dafb_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
uint32_t dafb_dac_r(offs_t offset, uint32_t mem_mask = ~0);
void dafb_dac_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
void dafb_recalc_ints();
TIMER_CALLBACK_MEMBER(dafb_vbl_tick);
TIMER_CALLBACK_MEMBER(dafb_cursor_tick);
u32 *m_ram_ptr = nullptr, *m_rom_ptr = nullptr;
u32 m_ram_mask = 0, m_ram_size = 0, m_rom_size = 0;
emu_timer *m_vbl_timer = nullptr, *m_cursor_timer = nullptr, *m_6015_timer = nullptr;
uint16_t m_cursor_line = 0;
uint16_t m_dafb_int_status = 0;
int m_dafb_scsi1_drq = 0, m_dafb_scsi2_drq = 0;
uint8_t m_dafb_mode = 0;
uint32_t m_dafb_base = 0, m_dafb_stride = 0;
uint32_t m_dafb_colors[3]{}, m_dafb_count = 0, m_dafb_clutoffs = 0, m_dafb_montype = 0, m_dafb_vbltime = 0;
uint32_t m_dafb_palette[256]{};
emu_timer *m_6015_timer = nullptr;
void nubus_irq_9_w(int state);
void nubus_irq_a_w(int state);
@ -129,32 +111,31 @@ private:
void nubus_irq_c_w(int state);
void nubus_irq_d_w(int state);
void nubus_irq_e_w(int state);
void nubus_slot_interrupt(uint8_t slot, uint32_t state);
void nubus_slot_interrupt(u8 slot, u32 state);
int m_via2_ca1_hack = 0, m_nubus_irq_state = 0;
void adb_irq_w(int state) { m_adb_irq_pending = state; }
int m_adb_irq_pending = 0;
void dafb_irq_w(int state) { nubus_slot_interrupt(0xf, state);}
void irq_539x_1_w(int state);
[[maybe_unused]] void irq_539x_2_w(int state);
void drq_539x_1_w(int state);
[[maybe_unused]] void drq_539x_2_w(int state);
floppy_image_device *m_cur_floppy = nullptr;
int m_hdsel = 0;
uint16_t mac_via_r(offs_t offset);
void mac_via_w(offs_t offset, uint16_t data, uint16_t mem_mask);
uint16_t mac_via2_r(offs_t offset);
void mac_via2_w(offs_t offset, uint16_t data, uint16_t mem_mask);
uint8_t mac_via_in_a();
uint8_t mac_via_in_b();
void mac_via_out_a(uint8_t data);
void mac_via_out_b(uint8_t data);
uint8_t mac_via2_in_a();
uint8_t mac_via2_in_b();
void mac_via2_out_a(uint8_t data);
void mac_via2_out_b(uint8_t data);
u16 mac_via_r(offs_t offset);
void mac_via_w(offs_t offset, u16 data, u16 mem_mask);
u16 mac_via2_r(offs_t offset);
void mac_via2_w(offs_t offset, u16 data, u16 mem_mask);
u8 mac_via_in_a();
u8 mac_via_in_b();
void mac_via_out_a(u8 data);
void mac_via_out_b(u8 data);
u8 mac_via2_in_a();
u8 mac_via2_in_b();
void mac_via2_out_a(u8 data);
void mac_via2_out_b(u8 data);
void mac_via_sync();
void field_interrupts();
void mac_via_irq(int state);
@ -162,21 +143,21 @@ private:
TIMER_CALLBACK_MEMBER(mac_6015_tick);
int m_via_interrupt = 0, m_via2_interrupt = 0, m_scc_interrupt = 0, m_last_taken_interrupt = 0;
uint32_t rom_switch_r(offs_t offset);
u32 rom_switch_r(offs_t offset);
bool m_overlay = 0;
uint16_t mac_scc_r(offs_t offset)
u16 mac_scc_r(offs_t offset)
{
mac_via_sync();
uint16_t result = m_scc->dc_ab_r(offset);
u16 result = m_scc->dc_ab_r(offset);
return (result << 8) | result;
}
void mac_scc_2_w(offs_t offset, uint16_t data) { mac_via_sync(); m_scc->dc_ab_w(offset, data >> 8); }
void mac_scc_2_w(offs_t offset, u16 data) { mac_via_sync(); m_scc->dc_ab_w(offset, data >> 8); }
void phases_w(uint8_t phases);
void devsel_w(uint8_t devsel);
void phases_w(u8 phases);
void devsel_w(u8 devsel);
uint16_t swim_r(offs_t offset, u16 mem_mask)
u16 swim_r(offs_t offset, u16 mem_mask)
{
if (!machine().side_effects_disabled())
{
@ -193,11 +174,6 @@ private:
else
m_swim->write((offset >> 8) & 0xf, data>>8);
}
uint8_t mac_5396_r(offs_t offset);
void mac_5396_w(offs_t offset, uint8_t data);
uint16_t mac_5396_dma_r(offs_t offset);
void mac_5396_dma_w(offs_t offset, uint16_t data);
};
void macquadra_state::field_interrupts()
@ -232,6 +208,9 @@ void macquadra_state::field_interrupts()
void macquadra_state::machine_start()
{
m_dafb->set_turboscsi1_device(m_ncr1);
m_dafb->set_turboscsi2_device(nullptr);
m_ram_ptr = (u32*)m_ram->pointer();
m_ram_size = m_ram->size()>>1;
m_ram_mask = m_ram_size - 1;
@ -243,19 +222,6 @@ void macquadra_state::machine_start()
m_6015_timer = timer_alloc(FUNC(macquadra_state::mac_6015_tick), this);
m_6015_timer->adjust(attotime::never);
save_item(NAME(m_cursor_line));
save_item(NAME(m_dafb_int_status));
save_item(NAME(m_dafb_scsi1_drq));
save_item(NAME(m_dafb_scsi2_drq));
save_item(NAME(m_dafb_mode));
save_item(NAME(m_dafb_base));
save_item(NAME(m_dafb_stride));
save_item(NAME(m_dafb_colors));
save_item(NAME(m_dafb_count));
save_item(NAME(m_dafb_clutoffs));
save_item(NAME(m_dafb_montype));
save_item(NAME(m_dafb_vbltime));
save_item(NAME(m_dafb_palette));
save_item(NAME(m_via2_ca1_hack));
save_item(NAME(m_nubus_irq_state));
save_item(NAME(m_adb_irq_pending));
@ -294,10 +260,10 @@ void macquadra_state::init_macqd700()
{
}
void macquadra_state::nubus_slot_interrupt(uint8_t slot, uint32_t state)
void macquadra_state::nubus_slot_interrupt(u8 slot, u32 state)
{
static const uint8_t masks[8] = { 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80 };
uint8_t mask = 0xff;
static const u8 masks[8] = { 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80 };
u8 mask = 0xff;
slot -= 9;
@ -334,312 +300,6 @@ void macquadra_state::nubus_irq_c_w(int state) { nubus_slot_interrupt(0xc, state
void macquadra_state::nubus_irq_d_w(int state) { nubus_slot_interrupt(0xd, state); }
void macquadra_state::nubus_irq_e_w(int state) { nubus_slot_interrupt(0xe, state); }
// DAFB: video for Quadra 700/900
void macquadra_state::dafb_recalc_ints()
{
if (m_dafb_int_status != 0)
{
nubus_slot_interrupt(0xf, ASSERT_LINE);
}
else
{
nubus_slot_interrupt(0xf, CLEAR_LINE);
}
}
TIMER_CALLBACK_MEMBER(macquadra_state::dafb_vbl_tick)
{
m_dafb_int_status |= 1;
dafb_recalc_ints();
m_vbl_timer->adjust(m_screen->time_until_pos(480, 0), 0);
}
TIMER_CALLBACK_MEMBER(macquadra_state::dafb_cursor_tick)
{
m_dafb_int_status |= 4;
dafb_recalc_ints();
m_cursor_timer->adjust(m_screen->time_until_pos(m_cursor_line, 0), 0);
}
void macquadra_state::video_start() // DAFB
{
m_vbl_timer = timer_alloc(FUNC(macquadra_state::dafb_vbl_tick), this);
m_cursor_timer = timer_alloc(FUNC(macquadra_state::dafb_cursor_tick), this);
m_vbl_timer->adjust(attotime::never);
m_cursor_timer->adjust(attotime::never);
}
void macquadra_state::video_reset() // DAFB
{
m_dafb_count = 0;
m_dafb_clutoffs = 0;
m_dafb_montype = 6;
m_dafb_vbltime = 0;
m_dafb_int_status = 0;
m_dafb_mode = 0;
m_dafb_base = 0x1000;
m_dafb_stride = 256*4;
memset(m_dafb_palette, 0, sizeof(m_dafb_palette));
}
uint32_t macquadra_state::dafb_r(offs_t offset, uint32_t mem_mask)
{
// if (offset != 0x108/4) printf("DAFB: Read @ %x (mask %x PC=%x)\n", offset*4, mem_mask, m_maincpu->pc());
switch (offset<<2)
{
case 0x1c: // inverse of monitor sense
return 7; // 21" color 2-page
case 0x24: // SCSI 539x #1 status
return m_dafb_scsi1_drq<<9;
case 0x28: // SCSI 539x #2 status
return m_dafb_scsi2_drq<<9;
case 0x108: // IRQ/VBL status
return m_dafb_int_status;
case 0x10c: // clear cursor scanline int
m_dafb_int_status &= ~4;
dafb_recalc_ints();
break;
case 0x114: // clear VBL int
m_dafb_int_status &= ~1;
dafb_recalc_ints();
break;
}
return 0;
}
void macquadra_state::dafb_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
// if (offset != 0x10c/4) printf("DAFB: Write %08x @ %x (mask %x PC=%x)\n", data, offset*4, mem_mask, m_maincpu->pc());
switch (offset<<2)
{
case 0: // bits 20-9 of base
m_dafb_base &= 0x1ff;
m_dafb_base |= (data & 0xffff) << 9;
// printf("DAFB baseH: %x\n", m_dafb_base);
break;
case 4: // bits 8-5 of base
m_dafb_base &= ~0x1ff;
m_dafb_base |= (data & 0xf) << 5;
// printf("DAFB baseL: %x\n", m_dafb_base);
break;
case 8:
m_dafb_stride = data<<2; // stride in DWORDs
// printf("DAFB stride: %x %x\n", m_dafb_stride, data);
break;
case 0x104:
if (data & 1) // VBL enable
{
m_vbl_timer->adjust(m_screen->time_until_pos(480, 0), 0);
}
else
{
m_vbl_timer->adjust(attotime::never);
m_dafb_int_status &= ~1;
dafb_recalc_ints();
}
if (data & 2) // aux scanline interrupt enable
{
fatalerror("DAFB: Aux scanline interrupt enable not supported!\n");
}
if (data & 4) // cursor scanline interrupt enable
{
m_cursor_timer->adjust(m_screen->time_until_pos(m_cursor_line, 0), 0);
}
else
{
m_cursor_timer->adjust(attotime::never);
m_dafb_int_status &= ~4;
dafb_recalc_ints();
}
break;
case 0x10c: // clear cursor scanline int
m_dafb_int_status &= ~4;
dafb_recalc_ints();
break;
case 0x114: // clear VBL int
m_dafb_int_status &= ~1;
dafb_recalc_ints();
break;
}
}
uint32_t macquadra_state::dafb_dac_r(offs_t offset, uint32_t mem_mask)
{
// printf("DAFB: Read DAC @ %x (mask %x PC=%x)\n", offset*4, mem_mask, m_maincpu->pc());
return 0;
}
void macquadra_state::dafb_dac_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
// if ((offset > 0) && (offset != 0x10/4)) printf("DAFB: Write %08x to DAC @ %x (mask %x PC=%x)\n", data, offset*4, mem_mask, m_maincpu->pc());
switch (offset<<2)
{
case 0:
m_dafb_clutoffs = data & 0xff;
m_dafb_count = 0;
break;
case 0x10:
m_dafb_colors[m_dafb_count++] = data&0xff;
if (m_dafb_count == 3)
{
m_palette->set_pen_color(m_dafb_clutoffs, rgb_t(m_dafb_colors[0], m_dafb_colors[1], m_dafb_colors[2]));
m_dafb_palette[m_dafb_clutoffs] = rgb_t(m_dafb_colors[0], m_dafb_colors[1], m_dafb_colors[2]);
m_dafb_clutoffs++;
m_dafb_count = 0;
}
break;
case 0x20:
switch (data & 0x9f)
{
case 0x80:
m_dafb_mode = 0; // 1bpp
break;
case 0x88:
m_dafb_mode = 1; // 2bpp
break;
case 0x90:
m_dafb_mode = 2; // 4bpp
break;
case 0x98:
m_dafb_mode = 3; // 8bpp
break;
case 0x9c:
m_dafb_mode = 4; // 24bpp
break;
}
break;
}
}
uint32_t macquadra_state::screen_update_dafb(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
auto const vram8 = util::big_endian_cast<uint8_t const>(m_vram.target()) + m_dafb_base;
switch (m_dafb_mode)
{
case 0: // 1bpp
{
for (int y = 0; y < 870; y++)
{
uint32_t *scanline = &bitmap.pix(y);
for (int x = 0; x < 1152/8; x++)
{
uint8_t const pixels = vram8[(y * m_dafb_stride) + x];
*scanline++ = m_dafb_palette[(pixels>>7)&1];
*scanline++ = m_dafb_palette[(pixels>>6)&1];
*scanline++ = m_dafb_palette[(pixels>>5)&1];
*scanline++ = m_dafb_palette[(pixels>>4)&1];
*scanline++ = m_dafb_palette[(pixels>>3)&1];
*scanline++ = m_dafb_palette[(pixels>>2)&1];
*scanline++ = m_dafb_palette[(pixels>>1)&1];
*scanline++ = m_dafb_palette[(pixels&1)];
}
}
}
break;
case 1: // 2bpp
{
for (int y = 0; y < 870; y++)
{
uint32_t *scanline = &bitmap.pix(y);
for (int x = 0; x < 1152/4; x++)
{
uint8_t const pixels = vram8[(y * m_dafb_stride) + x];
*scanline++ = m_dafb_palette[((pixels>>6)&3)];
*scanline++ = m_dafb_palette[((pixels>>4)&3)];
*scanline++ = m_dafb_palette[((pixels>>2)&3)];
*scanline++ = m_dafb_palette[(pixels&3)];
}
}
}
break;
case 2: // 4bpp
{
for (int y = 0; y < 870; y++)
{
uint32_t *scanline = &bitmap.pix(y);
for (int x = 0; x < 1152/2; x++)
{
uint8_t const pixels = vram8[(y * m_dafb_stride) + x];
*scanline++ = m_dafb_palette[(pixels>>4)];
*scanline++ = m_dafb_palette[(pixels&0xf)];
}
}
}
break;
case 3: // 8bpp
{
for (int y = 0; y < 870; y++)
{
uint32_t *scanline = &bitmap.pix(y);
for (int x = 0; x < 1152; x++)
{
uint8_t const pixels = vram8[(y * m_dafb_stride) + x];
*scanline++ = m_dafb_palette[pixels];
}
}
}
break;
case 4: // 24 bpp
for (int y = 0; y < 480; y++)
{
uint32_t *scanline = &bitmap.pix(y);
uint32_t const *base = &m_vram[(y * (m_dafb_stride/4)) + (m_dafb_base/4)];
for (int x = 0; x < 640; x++)
{
*scanline++ = *base++;
}
}
break;
}
return 0;
}
void macquadra_state::drq_539x_1_w(int state)
{
m_dafb_scsi1_drq = state;
}
void macquadra_state::drq_539x_2_w(int state)
{
m_dafb_scsi2_drq = state;
}
void macquadra_state::irq_539x_1_w(int state)
{
if (state) // make sure a CB1 transition occurs
@ -649,13 +309,9 @@ void macquadra_state::irq_539x_1_w(int state)
}
}
void macquadra_state::irq_539x_2_w(int state)
u16 macquadra_state::mac_via_r(offs_t offset)
{
}
uint16_t macquadra_state::mac_via_r(offs_t offset)
{
uint16_t data;
u16 data;
offset >>= 8;
offset &= 0x0f;
@ -668,7 +324,7 @@ uint16_t macquadra_state::mac_via_r(offs_t offset)
return (data & 0xff) | (data << 8);
}
void macquadra_state::mac_via_w(offs_t offset, uint16_t data, uint16_t mem_mask)
void macquadra_state::mac_via_w(offs_t offset, u16 data, u16 mem_mask)
{
offset >>= 8;
offset &= 0x0f;
@ -693,7 +349,7 @@ void macquadra_state::mac_via2_irq(int state)
field_interrupts();
}
uint16_t macquadra_state::mac_via2_r(offs_t offset)
u16 macquadra_state::mac_via2_r(offs_t offset)
{
int data;
@ -707,7 +363,7 @@ uint16_t macquadra_state::mac_via2_r(offs_t offset)
return (data & 0xff) | (data << 8);
}
void macquadra_state::mac_via2_w(offs_t offset, uint16_t data, uint16_t mem_mask)
void macquadra_state::mac_via2_w(offs_t offset, u16 data, u16 mem_mask)
{
offset >>= 8;
offset &= 0x0f;
@ -743,7 +399,7 @@ void macquadra_state::mac_via_sync()
m_maincpu->adjust_icount(-int(main_cycle - cycle));
}
uint32_t macquadra_state::rom_switch_r(offs_t offset)
u32 macquadra_state::rom_switch_r(offs_t offset)
{
// disable the overlay
if (m_overlay && !machine().side_effects_disabled())
@ -768,36 +424,6 @@ TIMER_CALLBACK_MEMBER(macquadra_state::mac_6015_tick)
m_macadb->adb_vblank();
}
uint8_t macquadra_state::mac_5396_r(offs_t offset)
{
return m_ncr1->read(offset>>4);
}
void macquadra_state::mac_5396_w(offs_t offset, uint8_t data)
{
m_ncr1->write(offset>>4, data);
}
uint16_t macquadra_state::mac_5396_dma_r(offs_t offset)
{
// HACK: Extra time is necessary to avoid underrunning the FIFO at 4 MB/sec transfer rates claimed to
// be typical for Apple HDDs of the period.
// Likely due to inaccurate 68040 bus timings or wait states; DAFB documentation is clear that there is
// no "magic latch" like the 5380 machines use.
if (!machine().side_effects_disabled() && BIT(offset << 1, 18))
m_maincpu->adjust_icount(-4);
return m_ncr1->dma16_swap_r();
}
void macquadra_state::mac_5396_dma_w(offs_t offset, uint16_t data)
{
if (!machine().side_effects_disabled() && BIT(offset << 1, 18))
m_maincpu->adjust_icount(-4);
m_ncr1->dma16_swap_w(data);
}
/***************************************************************************
ADDRESS MAPS
***************************************************************************/
@ -807,29 +433,32 @@ void macquadra_state::quadra700_map(address_map &map)
map(0x50000000, 0x50001fff).rw(FUNC(macquadra_state::mac_via_r), FUNC(macquadra_state::mac_via_w)).mirror(0x00fc0000);
map(0x50002000, 0x50003fff).rw(FUNC(macquadra_state::mac_via2_r), FUNC(macquadra_state::mac_via2_w)).mirror(0x00fc0000);
// 50008000 = Ethernet MAC ID PROM
// 5000a000 = Sonic (DP83932) ethernet
// 5000f000 = SCSI cf96, 5000f402 = SCSI #2 cf96
map(0x5000f000, 0x5000f0ff).rw(FUNC(macquadra_state::mac_5396_r), FUNC(macquadra_state::mac_5396_w)).mirror(0x00fc0000);
map(0x5000f100, 0x5000f101).rw(FUNC(macquadra_state::mac_5396_dma_r), FUNC(macquadra_state::mac_5396_dma_w)).select(0x00fc0000);
// 50008000 = Ethernet MAC ID PROM
map(0x5000a000, 0x5000b0ff).m(m_sonic, FUNC(dp83932c_device::map)).umask32(0x0000ffff).mirror(0x00fc0000);
// 5000e000 = Orwell controls
map(0x5000f000, 0x5000f0ff).rw(m_dafb, FUNC(dafb_device::turboscsi_r<0>), FUNC(dafb_device::turboscsi_w<0>)).mirror(0x00fc0000);
map(0x5000f100, 0x5000f101).rw(m_dafb, FUNC(dafb_device::turboscsi_dma_r<0>), FUNC(dafb_device::turboscsi_dma_w<0>)).select(0x00fc0000);
map(0x5000c000, 0x5000dfff).rw(FUNC(macquadra_state::mac_scc_r), FUNC(macquadra_state::mac_scc_2_w)).mirror(0x00fc0000);
map(0x50014000, 0x50015fff).rw(m_easc, FUNC(asc_device::read), FUNC(asc_device::write)).mirror(0x00fc0000);
map(0x5001e000, 0x5001ffff).rw(FUNC(macquadra_state::swim_r), FUNC(macquadra_state::swim_w)).mirror(0x00fc0000);
// f9800000 = VDAC / DAFB
map(0xf9000000, 0xf91fffff).ram().share("vram");
map(0xf9800000, 0xf98001ff).rw(FUNC(macquadra_state::dafb_r), FUNC(macquadra_state::dafb_w));
map(0xf9800200, 0xf980023f).rw(FUNC(macquadra_state::dafb_dac_r), FUNC(macquadra_state::dafb_dac_w));
map(0xf9000000, 0xf91fffff).rw(m_dafb, FUNC(dafb_device::vram_r), FUNC(dafb_device::vram_w));
map(0xf9800000, 0xf98003ff).m(m_dafb, FUNC(dafb_device::map));
}
uint8_t macquadra_state::mac_via_in_a()
u8 macquadra_state::mac_via_in_a()
{
return 0xc1;
}
uint8_t macquadra_state::mac_via_in_b()
u8 macquadra_state::mac_via_in_b()
{
int val = m_rtc->data_r();
#ifdef USE_ADBMODEM
u8 val = m_rtc->data_r();
#else
u8 val = m_macadb->get_adb_state()<<4;
val |= m_rtc->data_r();
#endif
if (!m_adb_irq_pending)
{
@ -841,7 +470,7 @@ uint8_t macquadra_state::mac_via_in_b()
return val;
}
void macquadra_state::mac_via_out_a(uint8_t data)
void macquadra_state::mac_via_out_a(u8 data)
{
int hdsel = BIT(data, 5);
if (hdsel != m_hdsel)
@ -854,43 +483,46 @@ void macquadra_state::mac_via_out_a(uint8_t data)
m_hdsel = hdsel;
}
void macquadra_state::mac_via_out_b(uint8_t data)
void macquadra_state::mac_via_out_b(u8 data)
{
// printf("%s VIA1 OUT B: %02x\n", machine().describe_context().c_str(), data);
#ifdef USE_ADBMODEM
m_adbmodem->set_via_state((data & 0x30) >> 4);
#else
m_macadb->mac_adb_newaction((data & 0x30) >> 4);
#endif
m_rtc->ce_w((data & 0x04)>>2);
m_rtc->data_w(data & 0x01);
m_rtc->clk_w((data >> 1) & 0x01);
}
uint8_t macquadra_state::mac_via2_in_a()
u8 macquadra_state::mac_via2_in_a()
{
return 0x80 | m_nubus_irq_state;
}
uint8_t macquadra_state::mac_via2_in_b()
u8 macquadra_state::mac_via2_in_b()
{
return 0xcf; // indicate no NuBus transaction error
}
void macquadra_state::mac_via2_out_a(uint8_t data)
void macquadra_state::mac_via2_out_a(u8 data)
{
}
void macquadra_state::mac_via2_out_b(uint8_t data)
void macquadra_state::mac_via2_out_b(u8 data)
{
// chain 60.15 Hz to VIA1
m_via1->write_ca1(data>>7);
}
void macquadra_state::phases_w(uint8_t phases)
void macquadra_state::phases_w(u8 phases)
{
if (m_cur_floppy)
m_cur_floppy->seek_phase_w(phases);
}
void macquadra_state::devsel_w(uint8_t devsel)
void macquadra_state::devsel_w(u8 devsel)
{
if (devsel == 1)
m_cur_floppy = m_floppy[0]->get_device();
@ -922,14 +554,9 @@ void macquadra_state::macqd700(machine_config &config)
m_maincpu->set_addrmap(AS_PROGRAM, &macquadra_state::quadra700_map);
m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");
SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
m_screen->set_refresh_hz(75.08);
m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(1260));
m_screen->set_size(1152, 870);
m_screen->set_visarea(0, 1152-1, 0, 870-1);
m_screen->set_screen_update(FUNC(macquadra_state::screen_update_dafb));
PALETTE(config, m_palette).set_entries(256);
DAFB(config, m_dafb, 50_MHz_XTAL / 2);
m_dafb->set_maincpu_tag("maincpu");
m_dafb->dafb_irq().set(FUNC(macquadra_state::dafb_irq_w));
RTC3430042(config, m_rtc, XTAL(32'768));
m_rtc->cko_cb().set(m_via1, FUNC(via6522_device::write_ca2));
@ -960,7 +587,7 @@ void macquadra_state::macqd700(machine_config &config)
adapter.set_busmd(ncr53c96_device::BUSMD_1);
adapter.irq_handler_cb().set(*this, FUNC(macquadra_state::irq_539x_1_w));
adapter.drq_handler_cb().set(*this, FUNC(macquadra_state::drq_539x_1_w));
adapter.drq_handler_cb().set(m_dafb, FUNC(dafb_device::turboscsi_drq_w<0>));
});
DP83932C(config, m_sonic, 40_MHz_XTAL / 2);
@ -983,6 +610,9 @@ void macquadra_state::macqd700(machine_config &config)
m_via1->writepa_handler().set(FUNC(macquadra_state::mac_via_out_a));
m_via1->writepb_handler().set(FUNC(macquadra_state::mac_via_out_b));
m_via1->irq_handler().set(FUNC(macquadra_state::mac_via_irq));
#ifndef USE_ADBMODEM
m_via1->cb2_handler().set(m_macadb, FUNC(macadb_device::adb_data_w));
#endif
R65NC22(config, m_via2, C7M/10);
m_via2->readpa_handler().set(FUNC(macquadra_state::mac_via2_in_a));
@ -991,16 +621,25 @@ void macquadra_state::macqd700(machine_config &config)
m_via2->writepb_handler().set(FUNC(macquadra_state::mac_via2_out_b));
m_via2->irq_handler().set(FUNC(macquadra_state::mac_via2_irq));
ADBMODEM(config, m_adbmodem, C7M);
m_adbmodem->via_clock_callback().set(m_via1, FUNC(via6522_device::write_cb1));
m_adbmodem->via_data_callback().set(m_via1, FUNC(via6522_device::write_cb2));
m_adbmodem->linechange_callback().set(m_macadb, FUNC(macadb_device::adb_linechange_w));
m_adbmodem->irq_callback().set(FUNC(macquadra_state::adb_irq_w));
m_via1->cb2_handler().set(m_adbmodem, FUNC(adbmodem_device::set_via_data));
config.set_maximum_quantum(attotime::from_hz(1000000));
#ifdef USE_ADBMODEM
ADBMODEM(config, m_adbmodem, C7M);
m_adbmodem->via_clock_callback().set(m_via1, FUNC(via6522_device::write_cb1));
m_adbmodem->via_data_callback().set(m_via1, FUNC(via6522_device::write_cb2));
m_adbmodem->linechange_callback().set(m_macadb, FUNC(macadb_device::adb_linechange_w));
m_adbmodem->irq_callback().set(FUNC(macquadra_state::adb_irq_w));
m_via1->cb2_handler().set(m_adbmodem, FUNC(adbmodem_device::set_via_data));
config.set_maximum_quantum(attotime::from_hz(1000000));
#endif
MACADB(config, m_macadb, C15M);
m_macadb->adb_data_callback().set(m_adbmodem, FUNC(adbmodem_device::set_adb_line));
#ifdef USE_ADBMODEM
m_macadb->adb_data_callback().set(m_adbmodem, FUNC(adbmodem_device::set_adb_line));
#else
m_macadb->set_mcu_mode(false);
m_macadb->via_clock_callback().set(m_via1, FUNC(via6522_device::write_cb1));
m_macadb->via_data_callback().set(m_via1, FUNC(via6522_device::write_cb2));
m_macadb->adb_irq_callback().set(FUNC(macquadra_state::adb_irq_w));
#endif
SPEAKER(config, "lspeaker").front_left();
SPEAKER(config, "rspeaker").front_right();