pc_vga: add xga blits

This commit is contained in:
cracyc 2021-10-17 16:47:13 -05:00
parent 0ba5d664f9
commit 1a5fe419c2
2 changed files with 319 additions and 2 deletions

View File

@ -5881,7 +5881,7 @@ void xga_copro_device::start_command()
logerror("line draw write\n");
break;
case 8:
logerror("pxblt\n");
do_pxblt();
break;
case 9:
logerror("inverting pxblt\n");
@ -5892,9 +5892,309 @@ void xga_copro_device::start_command()
}
}
void do_pxblt()
// Can maps be not byte aligned? XGA manual says they must be 32bit aligned in at least some cases.
u32 xga_copro_device::read_map_pixel(int x, int y, int map)
{
offs_t addr = m_pelmap_base[map];
int width = m_pelmap_width[map] + 1;
int height = m_pelmap_height[map] + 1;
int endian = m_pelmap_format[map] & 8;
int wbytes, bits;
u8 byte;
if((x > width) || (y > height) || (x < 0) || (y < 0))
return 0;
switch(m_pelmap_format[map] & 7)
{
case 0:
wbytes = m_pelmap_width[map] / 8;
addr += y * wbytes;
addr += x / 8;
byte = m_mem_read_cb(addr);
bits = (x % 8) - (endian ? 8 : 0);
return (byte >> bits) & 1;
case 1:
wbytes = m_pelmap_width[map] / 4;
addr += y * wbytes;
addr += x / 4;
byte = m_mem_read_cb(addr);
bits = (x % 4) - (endian ? 4 : 0);
return (byte >> (bits * 2)) & 3;
case 2:
wbytes = m_pelmap_width[map] / 2;
addr += y * wbytes;
addr += x / 2;
byte = m_mem_read_cb(addr);
bits = (x % 2) - (endian ? 2 : 0);
return (byte >> (bits * 4)) & 0xf;
case 3:
wbytes = m_pelmap_width[map];
addr += y * wbytes;
addr += x;
return m_mem_read_cb(addr);
case 4:
wbytes = m_pelmap_width[map] * 2;
addr += y * wbytes;
addr += x * 2;
if(endian)
return m_mem_read_cb(addr + 1) | (m_mem_read_cb(addr) << 8);
return m_mem_read_cb(addr) | (m_mem_read_cb(addr + 1) << 8);
case 5:
wbytes = m_pelmap_width[map] * 4;
addr += y * wbytes;
addr += x * 4;
if(endian)
return m_mem_read_cb(addr + 3) | (m_mem_read_cb(addr + 2) << 8) |
(m_mem_read_cb(addr + 1) << 16) | (m_mem_read_cb(addr) << 24);
return m_mem_read_cb(addr) | (m_mem_read_cb(addr + 1) << 8) |
(m_mem_read_cb(addr + 2) << 16) | (m_mem_read_cb(addr + 3) << 24);
}
logerror("invalid pixel map mode %d %d\n", map, m_pelmap_format[map] & 7);
return 0;
}
void xga_copro_device::write_map_pixel(int x, int y, int map, u32 pixel)
{
offs_t addr = m_pelmap_base[map];
int width = m_pelmap_width[map] + 1;
int height = m_pelmap_height[map] + 1;
int endian = m_pelmap_format[map] & 8;
int wbytes;
u8 byte, mask;
if((x > width) || (y > height) || (x < 0) || (y < 0))
return;
switch(m_pelmap_format[map] & 7)
{
case 0:
wbytes = m_pelmap_width[map] / 8;
addr += y * wbytes;
addr += x / 8;
byte = m_mem_read_cb(addr);
mask = 1 << ((x % 8) - (endian ? 8 : 0));
byte = (byte & ~mask) | ((pixel ? 0xff : 0) & mask);
m_mem_write_cb(addr, byte);
break;
case 1:
wbytes = m_pelmap_width[map] / 4;
addr += y * wbytes;
addr += x / 4;
byte = m_mem_read_cb(addr);
mask = 3 << (((x % 4) - (endian ? 4 : 0)) * 2);
byte = (byte & ~mask) | ((pixel ? 0xff : 0) & mask);
m_mem_write_cb(addr, byte);
break;
case 2:
wbytes = m_pelmap_width[map] / 2;
addr += y * wbytes;
addr += x / 2;
byte = m_mem_read_cb(addr);
mask = 0xf << (((x % 2) - (endian ? 2 : 0)) * 4);
byte = (byte & ~mask) | ((pixel ? 0xff : 0) & mask);
m_mem_write_cb(addr, byte);
break;
case 3:
wbytes = m_pelmap_width[map];
addr += y * wbytes;
addr += x;
m_mem_write_cb(addr, (u8)pixel);
break;
case 4:
wbytes = m_pelmap_width[map] * 2;
addr += y * wbytes;
addr += x * 2;
if(endian)
{
m_mem_write_cb(addr + 1, pixel & 0xff);
m_mem_write_cb(addr, pixel >> 8);
}
else
{
m_mem_write_cb(addr, pixel & 0xff);
m_mem_write_cb(addr + 1, pixel >> 8);
}
break;
case 5:
wbytes = m_pelmap_width[map] * 4;
addr += y * wbytes;
addr += x * 4;
if(endian)
{
m_mem_write_cb(addr + 3, pixel & 0xff);
m_mem_write_cb(addr + 2, pixel >> 8);
m_mem_write_cb(addr + 1, pixel >> 16);
m_mem_write_cb(addr, pixel >> 24);
}
else
{
m_mem_write_cb(addr, pixel & 0xff);
m_mem_write_cb(addr + 1, pixel >> 8);
m_mem_write_cb(addr + 2, pixel >> 16);
m_mem_write_cb(addr + 3, pixel >> 24);
}
break;
default:
logerror("invalid pixel map mode %d %d\n", map, m_pelmap_format[map] & 7);
break;
}
}
u32 xga_copro_device::rop(u32 s, u32 d, u8 op)
{
if(m_var == TYPE::OTI111)
{
switch(op)
{
default:
case 0:
return 0;
case 1:
return ~s & ~d;
case 2:
return ~s & d;
case 3:
return ~s;
case 4:
return s & ~d;
case 5:
return ~s;
case 6:
return s ^ d;
case 7:
return ~s | ~d;
case 8:
return s & d;
case 9:
return s ^ ~d;
case 10:
return d;
case 11:
return ~s | d;
case 12:
return s;
case 13:
return s | ~d;
case 14:
return s | d;
case 15:
return -1;
}
}
switch(op)
{
default:
case 0:
return 0;
case 1:
return s & d;
case 2:
return s & ~d;
case 3:
return s;
case 4:
return ~s & d;
case 5:
return d;
case 6:
return s ^ d;
case 7:
return s | d;
case 8:
return ~s & ~d;
case 9:
return s ^ ~d;
case 10:
return ~d;
case 11:
return s | ~d;
case 12:
return ~s;
case 13:
return ~s | d;
case 14:
return ~s | ~d;
case 15:
return -1;
case 16:
return std::max(s, d);
case 17:
return std::min(s, d);
case 18:
return 0; // saturate add
case 19:
return 0; // saturate sub d - s
case 20:
return 0; // saturate sub s - d
case 21:
return 0; // avg
}
}
void xga_copro_device::do_pxblt()
{
u8 dir = (m_pelop >> 25) & 2;
int xstart, xend, xdir, ystart, yend, ydir;
u8 srcmap = ((m_pelop >> 20) & 0xf) - 1;
u8 dstmap = ((m_pelop >> 16) & 0xf) - 1;
u8 patmap = ((m_pelop >> 12) & 0xf) - 1;
logerror("pxblt src %d pat %d dst %d dim1 %d dim2 %d\n", srcmap, dstmap, patmap, m_opdim1, m_opdim2);
if((srcmap > 3) || (dstmap > 3) || ((patmap > 3) && (patmap != 7) && (patmap != 8)))
{
logerror("invalid pelmap\n");
return;
}
if(dir & 1)
{
ystart = m_opdim2 + 1;
yend = 0;
ydir = -1;
}
else
{
ystart = 0;
yend = m_opdim2 + 1;
ydir = 1;
}
if(dir & 2)
{
xstart = m_opdim1 + 1;
xend = 0;
xdir = -1;
}
else
{
xstart = 0;
xend = m_opdim1 + 1;
xdir = 1;
}
for(int y = ystart; y != yend; y += ydir)
{
u16 patxaddr = m_patxaddr;
u16 srcxaddr = m_srcxaddr;
u16 dstxaddr = m_dstxaddr;
for(int x = xstart; x != xend; x += xdir)
{
u32 src, dst, pat;
if(patmap <= 3)
{
pat = read_map_pixel(patxaddr, m_patyaddr, patmap + 1);
patxaddr += xdir;
}
else
pat = 1; //TODO: generate from source mode
if(pat)
src = (((m_pelop >> 28) & 3) == 2) ? read_map_pixel(srcxaddr, m_srcyaddr, srcmap + 1) : m_fcolor;
else
src = (((m_pelop >> 30) & 3) == 2) ? read_map_pixel(srcxaddr, m_srcyaddr, srcmap + 1) : m_bcolor;
srcxaddr += xdir;
dst = read_map_pixel(dstxaddr, m_dstyaddr, dstmap + 1);
dst = (dst & ~m_pelbmask) | (rop(src, dst, pat ? m_fmix : m_bmix) & m_pelbmask);
write_map_pixel(dstxaddr, m_dstyaddr, dstmap + 1, dst); // TODO: color compare
dstxaddr += xdir;
}
m_patyaddr += ydir;
m_srcyaddr += ydir;
m_dstyaddr += ydir;
}
}
u8 xga_copro_device::xga_read(offs_t offset)
@ -5942,8 +6242,12 @@ u8 xga_copro_device::xga_read(offs_t offset)
case 0x2f:
return m_dir >> 24;
case 0x48:
if(m_var == TYPE::OTI111)
return m_fmix << 4 | m_bmix;
return m_fmix;
case 0x49:
if(m_var == TYPE::OTI111)
return 0;
return m_bmix;
case 0x4a:
return m_destccc;
@ -6104,9 +6408,17 @@ void xga_copro_device::xga_write(offs_t offset, u8 data)
m_dir = (m_dir & ~0xff000000) | (data << 24);
break;
case 0x48:
if(m_var == TYPE::OTI111)
{
m_fmix = data >> 4;
m_bmix = data & 0xf;
break;
}
m_fmix = data;
break;
case 0x49:
if(m_var == TYPE::OTI111)
break;
m_bmix = data;
break;
case 0x4a:
@ -6250,6 +6562,8 @@ void xga_copro_device::xga_write(offs_t offset, u8 data)
void xga_copro_device::device_start()
{
m_mem_read_cb.resolve_safe(0);
m_mem_write_cb.resolve_safe();
}
void xga_copro_device::device_reset()

View File

@ -716,6 +716,9 @@ protected:
private:
void start_command();
void do_pxblt();
u32 read_map_pixel(int x, int y, int map);
void write_map_pixel(int x, int y, int map, u32 pixel);
u32 rop(u32 src, u32 dst, u8 rop);
u8 m_pelmap;
u32 m_pelmap_base[4];