// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************
video/apple2.cpp
***************************************************************************/
#include "emu.h"
#include "machine/ram.h"
#include "video/apple2.h"
#include "screen.h"
/***************************************************************************/
#define BLACK 0
#define DKRED 1
#define DKBLUE 2
#define PURPLE 3
#define DKGREEN 4
#define DKGRAY 5
#define BLUE 6
#define LTBLUE 7
#define BROWN 8
#define ORANGE 9
#define GRAY 10
#define PINK 11
#define GREEN 12
#define YELLOW 13
#define AQUA 14
#define WHITE 15
DEFINE_DEVICE_TYPE(APPLE2_VIDEO, a2_video_device, "a2video", "Apple II video")
//-------------------------------------------------
// a2_video_device - constructor
//-------------------------------------------------
a2_video_device::a2_video_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, APPLE2_VIDEO, tag, owner, clock)
, device_palette_interface(mconfig, *this)
, device_video_interface(mconfig, *this)
{
}
void a2_video_device::device_start()
{
static const uint8_t hires_artifact_color_table[] =
{
BLACK, PURPLE, GREEN, WHITE,
BLACK, BLUE, ORANGE, WHITE
};
static const uint8_t dhires_artifact_color_table[] =
{
BLACK, DKGREEN, BROWN, GREEN,
DKRED, DKGRAY, ORANGE, YELLOW,
DKBLUE, BLUE, GRAY, AQUA,
PURPLE, LTBLUE, PINK, WHITE
};
// generate hi-res artifact data
int i, j;
uint16_t c;
/* 2^3 dependent pixels * 2 color sets * 2 offsets */
m_hires_artifact_map = std::make_unique<uint16_t[]>(8 * 2 * 2);
/* build hires artifact map */
for (i = 0; i < 8; i++)
{
for (j = 0; j < 2; j++)
{
if (i & 0x02)
{
if ((i & 0x05) != 0)
c = 3;
else
c = j ? 2 : 1;
}
else
{
if ((i & 0x05) == 0x05)
c = j ? 1 : 2;
else
c = 0;
}
m_hires_artifact_map[ 0 + j*8 + i] = hires_artifact_color_table[(c + 0) % 8];
m_hires_artifact_map[16 + j*8 + i] = hires_artifact_color_table[(c + 4) % 8];
}
}
/* 2^4 dependent pixels */
m_dhires_artifact_map = std::make_unique<uint16_t[]>(16);
/* build double hires artifact map */
for (i = 0; i < 16; i++)
{
m_dhires_artifact_map[i] = dhires_artifact_color_table[i];
}
// initialise for device_palette_interface
init_palette();
save_item(NAME(m_page2));
save_item(NAME(m_flash));
save_item(NAME(m_mix));
save_item(NAME(m_graphics));
save_item(NAME(m_hires));
save_item(NAME(m_dhires));
save_item(NAME(m_80col));
save_item(NAME(m_altcharset));
save_item(NAME(m_an2));
save_item(NAME(m_80store));
save_item(NAME(m_monohgr));
save_item(NAME(m_GSfg));
save_item(NAME(m_GSbg));
save_item(NAME(m_GSborder));
save_item(NAME(m_newvideo));
save_item(NAME(m_monochrome));
save_item(NAME(m_shr_palette));
}
void a2_video_device::device_reset()
{
m_page2 = false;
m_graphics = false;
m_hires = false;
m_80col = false;
m_altcharset = false;
m_dhires = false;
m_flash = false;
m_mix = false;
m_sysconfig = 0;
m_an2 = false;
m_80store = false;
m_monohgr = false;
m_newvideo = 0x01;
}
WRITE_LINE_MEMBER(a2_video_device::txt_w)
{
if (m_graphics == state) // avoid flickering from II+ refresh polling
{
// select graphics or text mode
screen().update_now();
m_graphics = !state;
}
}
WRITE_LINE_MEMBER(a2_video_device::mix_w)
{
// select mixed mode or nomix
screen().update_now();
m_mix = state;
}
WRITE_LINE_MEMBER(a2_video_device::scr_w)
{
// select primary or secondary page
if (!m_80col)
screen().update_now();
m_page2 = state;
}
WRITE_LINE_MEMBER(a2_video_device::res_w)
{
// select lo-res or hi-res
screen().update_now();
m_hires = state;
}
WRITE_LINE_MEMBER(a2_video_device::dhires_w)
{
// select double hi-res
screen().update_now();
m_dhires = !state;
}
WRITE_LINE_MEMBER(a2_video_device::an2_w)
{
m_an2 = state;
}
void a2_video_device::plot_text_character(bitmap_ind16 &bitmap, int xpos, int ypos, int xscale, uint32_t code,
const uint8_t *textgfx_data, uint32_t textgfx_datalen, int fg, int bg)
{
int x, y, i;
const uint8_t *chardata;
uint16_t color;
if (!m_altcharset)
{
if ((code >= 0x40) && (code <= 0x7f))
{
code &= 0x3f;
if (m_flash)
{
i = fg;
fg = bg;
bg = i;
}
}
}
else
{
if ((code >= 0x60) && (code <= 0x7f))
{
code |= 0x80; // map to lowercase normal
i = fg; // and flip the color
fg = bg;
bg = i;
}
}
/* look up the character data */
chardata = &textgfx_data[(code * 8)];
for (y = 0; y < 8; y++)
{
for (x = 0; x < 7; x++)
{
color = (chardata[y] & (1 << x)) ? bg : fg;
for (i = 0; i < xscale; i++)
{
bitmap.pix16(ypos + y, xpos + (x * xscale) + i) = color;
}
}
}
}
void a2_video_device::plot_text_character_orig(bitmap_ind16 &bitmap, int xpos, int ypos, int xscale, uint32_t code,
const uint8_t *textgfx_data, uint32_t textgfx_datalen, int fg, int bg)
{
int x, y, i;
const uint8_t *chardata;
uint16_t color;
if ((code >= 0x40) && (code <= 0x7f))
{
if (m_flash)
{
i = fg;
fg = bg;
bg = i;
}
}
else if (code < 0x40) // inverse: flip FG and BG
{
i = fg;
fg = bg;
bg = i;
}
/* look up the character data */
chardata = &textgfx_data[(code * 8)];
for (y = 0; y < 8; y++)
{
for (x = 0; x < 7; x++)
{
color = (chardata[y] & (1 << (6-x))) ? fg : bg;
for (i = 0; i < xscale; i++)
{
bitmap.pix16(ypos + y, xpos + (x * xscale) + i) = color;
}
}
}
}
void a2_video_device::plot_text_character_jplus(bitmap_ind16 &bitmap, int xpos, int ypos, int xscale, uint32_t code,
const uint8_t *textgfx_data, uint32_t textgfx_datalen, int fg, int bg)
{
int x, y, i;
const uint8_t *chardata;
uint16_t color;
if ((code >= 0x40) && (code <= 0x7f))
{
code &= 0x3f;
if (m_flash)
{
i = fg;
fg = bg;
bg = i;
}
}
else if (code < 0x40) // inverse: flip FG and BG
{
i = fg;
fg = bg;
bg = i;
}
if (m_an2)
{
code |= 0x80;
}
/* look up the character data */
chardata = &textgfx_data[(code * 8)];
for (y = 0; y < 8; y++)
{
for (x = 0; x < 7; x++)
{
color = (chardata[y] & (1 << (6-x))) ? fg : bg;
for (i = 0; i < xscale; i++)
{
bitmap.pix16(ypos + y, xpos + (x * xscale) + i) = color;
}
}
}
}
void a2_video_device::plot_text_character_ultr(bitmap_ind16 &bitmap, int xpos, int ypos, int xscale, uint32_t code,
const uint8_t *textgfx_data, uint32_t textgfx_datalen, int fg, int bg)
{
int x, y, i;
const uint8_t *chardata;
uint16_t color;
if ((code >= 0x40) && (code <= 0x7f))
{
if (m_flash)
{
i = fg;
fg = bg;
bg = i;
}
}
/* look up the character data */
chardata = &textgfx_data[(code * 8)];
for (y = 0; y < 8; y++)
{
for (x = 1; x < 8; x++)
{
color = (chardata[y] & (1 << x)) ? fg : bg;
for (i = 0; i < xscale; i++)
{
bitmap.pix16(ypos + y, xpos + ((x-1) * xscale) + i) = color;
}
}
}
}
void a2_video_device::plot_text_characterGS(bitmap_ind16 &bitmap, int xpos, int ypos, int xscale, uint32_t code,
const uint8_t *textgfx_data, uint32_t textgfx_datalen, int fg, int bg)
{
int x, y, i;
const uint8_t *chardata;
uint16_t color;
if (!m_altcharset)
{
if ((code >= 0x40) && (code <= 0x7f))
{
code &= 0x3f;
if (m_flash)
{
i = fg;
fg = bg;
bg = i;
}
}
}
else
{
code |= 0x100;
}
/* look up the character data */
chardata = &textgfx_data[(code * 8)];
for (y = 0; y < 8; y++)
{
for (x = 0; x < 7; x++)
{
color = (chardata[y] & (1 << x)) ? bg : fg;
for (i = 0; i < xscale; i++)
{
bitmap.pix16(ypos + y, xpos + (x * xscale) + i) = color;
}
}
}
}
void a2_video_device::lores_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow)
{
int row, col, y, x;
uint8_t code;
uint32_t start_address = m_page2 ? 0x0800 : 0x0400;
uint32_t address;
int fg = 0;
switch (m_sysconfig & 0x03)
{
case 0: fg = WHITE; break;
case 1: fg = WHITE; break;
case 2: fg = GREEN; break;
case 3: fg = ORANGE; break;
}
/* perform adjustments */
beginrow = (std::max)(beginrow, cliprect.top());
endrow = (std::min)(endrow, cliprect.bottom());
if (!(m_sysconfig & 0x03))
{
for (row = beginrow; row <= endrow; row += 8)
{
for (col = 0; col < 40; col++)
{
/* calculate adderss */
address = start_address + ((((row/8) & 0x07) << 7) | (((row/8) & 0x18) * 5 + col));
/* perform the lookup */
code = m_ram_ptr[address];
/* and now draw */
for (y = 0; y < 4; y++)
{
if ((row + y) <= endrow)
{
for (x = 0; x < 14; x++)
{
bitmap.pix16(row + y, col * 14 + x) = (code >> 0) & 0x0F;
}
}
}
for (y = 4; y < 8; y++)
{
if ((row + y) <= endrow)
{
for (x = 0; x < 14; x++)
{
bitmap.pix16(row + y, col * 14 + x) = (code >> 4) & 0x0F;
}
}
}
}
}
}
else
{
for (row = beginrow; row <= endrow; row += 8)
{
for (col = 0; col < 40; col++)
{
uint8_t bits;
/* calculate adderss */
address = start_address + ((((row/8) & 0x07) << 7) | (((row/8) & 0x18) * 5 + col));
/* perform the lookup */
code = m_ram_ptr[address];
bits = (code >> 0) & 0x0F;
/* and now draw */
for (y = 0; y < 4; y++)
{
if ((row + y) <= endrow)
{
for (x = 0; x < 14; x++)
{
if (col & 1)
{
bitmap.pix16(row + y, col * 14 + x) = bits & (1 << ((x+2) % 4)) ? fg : 0;
}
else
{
bitmap.pix16(row + y, col * 14 + x) = bits & (1 << (x % 4)) ? fg : 0;
}
}
}
}
bits = (code >> 4) & 0x0F;
for (y = 4; y < 8; y++)
{
if ((row + y) <= endrow)
{
for (x = 0; x < 14; x++)
{
if (col & 1)
{
bitmap.pix16(row + y, col * 14 + x) = bits & (1 << ((x+2) % 4)) ? fg : 0;
}
else
{
bitmap.pix16(row + y, col * 14 + x) = bits & (1 << (x % 4)) ? fg : 0;
}
}
}
}
if (row == 64) printf("\n");
}
}
}
}
void a2_video_device::dlores_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow)
{
int row, col, y;
uint8_t code, auxcode;
uint32_t start_address = m_page2 ? 0x0800 : 0x0400;
uint32_t address;
static const int aux_colors[16] = { 0, 2, 4, 6, 8, 0xa, 0xc, 0xe, 1, 3, 5, 7, 9, 0xb, 0xd, 0xf };
int fg = 0;
switch (m_sysconfig & 0x03)
{
case 0: fg = WHITE; break;
case 1: fg = WHITE; break;
case 2: fg = GREEN; break;
case 3: fg = ORANGE; break;
}
/* perform adjustments */
beginrow = (std::max)(beginrow, cliprect.top() - (cliprect.top() % 8));
endrow = (std::min)(endrow, cliprect.bottom() - (cliprect.bottom() % 8) + 7);
if (!(m_sysconfig & 0x03))
{
for (row = beginrow; row <= endrow; row += 8)
{
for (col = 0; col < 40; col++)
{
/* calculate adderss */
address = start_address + ((((row/8) & 0x07) << 7) | (((row/8) & 0x18) * 5 + col));
/* perform the lookup */
code = m_ram_ptr[address];
auxcode = m_aux_ptr[address];
/* and now draw */
for (y = 0; y < 4; y++)
{
uint16_t *vram = &bitmap.pix16(row + y, (col * 14));
*vram++ = aux_colors[(auxcode >> 0) & 0x0F];
*vram++ = aux_colors[(auxcode >> 0) & 0x0F];
*vram++ = aux_colors[(auxcode >> 0) & 0x0F];
*vram++ = aux_colors[(auxcode >> 0) & 0x0F];
*vram++ = aux_colors[(auxcode >> 0) & 0x0F];
*vram++ = aux_colors[(auxcode >> 0) & 0x0F];
*vram++ = aux_colors[(auxcode >> 0) & 0x0F];
*vram++ = (code >> 0) & 0x0F;
*vram++ = (code >> 0) & 0x0F;
*vram++ = (code >> 0) & 0x0F;
*vram++ = (code >> 0) & 0x0F;
*vram++ = (code >> 0) & 0x0F;
*vram++ = (code >> 0) & 0x0F;
*vram++ = (code >> 0) & 0x0F;
}
for (y = 4; y < 8; y++)
{
uint16_t *vram = &bitmap.pix16(row + y, (col * 14));
*vram++ = aux_colors[(auxcode >> 4) & 0x0F];
*vram++ = aux_colors[(auxcode >> 4) & 0x0F];
*vram++ = aux_colors[(auxcode >> 4) & 0x0F];
*vram++ = aux_colors[(auxcode >> 4) & 0x0F];
*vram++ = aux_colors[(auxcode >> 4) & 0x0F];
*vram++ = aux_colors[(auxcode >> 4) & 0x0F];
*vram++ = aux_colors[(auxcode >> 4) & 0x0F];
*vram++ = (code >> 4) & 0x0F;
*vram++ = (code >> 4) & 0x0F;
*vram++ = (code >> 4) & 0x0F;
*vram++ = (code >> 4) & 0x0F;
*vram++ = (code >> 4) & 0x0F;
*vram++ = (code >> 4) & 0x0F;
*vram++ = (code >> 4) & 0x0F;
}
}
}
}
else
{
for (row = beginrow; row <= endrow; row += 8)
{
for (col = 0; col < 40; col++)
{
uint8_t bits, abits;
/* calculate adderss */
address = start_address + ((((row/8) & 0x07) << 7) | (((row/8) & 0x18) * 5 + col));
/* perform the lookup */
code = m_ram_ptr[address];
auxcode = m_aux_ptr[address];
bits = (code >> 0) & 0x0F;
abits = (auxcode >> 0) & 0x0F;
/* and now draw */
for (y = 0; y < 4; y++)
{
uint16_t *vram = &bitmap.pix16(row + y, (col * 14));
if (col & 1)
{
*vram++ = abits & (1 << 2) ? fg : 0;
*vram++ = abits & (1 << 3) ? fg : 0;
*vram++ = abits & (1 << 0) ? fg : 0;
*vram++ = abits & (1 << 1) ? fg : 0;
*vram++ = abits & (1 << 2) ? fg : 0;
*vram++ = abits & (1 << 3) ? fg : 0;
*vram++ = abits & (1 << 0) ? fg : 0;
*vram++ = bits & (1 << 1) ? fg : 0;
*vram++ = bits & (1 << 2) ? fg : 0;
*vram++ = bits & (1 << 3) ? fg : 0;
*vram++ = bits & (1 << 0) ? fg : 0;
*vram++ = bits & (1 << 1) ? fg : 0;
*vram++ = bits & (1 << 2) ? fg : 0;
*vram++ = bits & (1 << 3) ? fg : 0;
}
else
{
*vram++ = abits & (1 << 0) ? fg : 0;
*vram++ = abits & (1 << 1) ? fg : 0;
*vram++ = abits & (1 << 2) ? fg : 0;
*vram++ = abits & (1 << 3) ? fg : 0;
*vram++ = abits & (1 << 0) ? fg : 0;
*vram++ = abits & (1 << 1) ? fg : 0;
*vram++ = abits & (1 << 2) ? fg : 0;
*vram++ = bits & (1 << 0) ? fg : 0;
*vram++ = bits & (1 << 1) ? fg : 0;
*vram++ = bits & (1 << 2) ? fg : 0;
*vram++ = bits & (1 << 3) ? fg : 0;
*vram++ = bits & (1 << 0) ? fg : 0;
*vram++ = bits & (1 << 1) ? fg : 0;
*vram++ = bits & (1 << 2) ? fg : 0;
}
}
bits = (code >> 4) & 0x0F;
abits = (auxcode >> 4) & 0x0F;
for (y = 4; y < 8; y++)
{
uint16_t *vram = &bitmap.pix16(row + y, (col * 14));
if (col & 1)
{
*vram++ = abits & (1 << 2) ? fg : 0;
*vram++ = abits & (1 << 3) ? fg : 0;
*vram++ = abits & (1 << 0) ? fg : 0;
*vram++ = abits & (1 << 1) ? fg : 0;
*vram++ = abits & (1 << 2) ? fg : 0;
*vram++ = abits & (1 << 3) ? fg : 0;
*vram++ = abits & (1 << 0) ? fg : 0;
*vram++ = bits & (1 << 1) ? fg : 0;
*vram++ = bits & (1 << 2) ? fg : 0;
*vram++ = bits & (1 << 3) ? fg : 0;
*vram++ = bits & (1 << 0) ? fg : 0;
*vram++ = bits & (1 << 1) ? fg : 0;
*vram++ = bits & (1 << 2) ? fg : 0;
*vram++ = bits & (1 << 3) ? fg : 0;
}
else
{
*vram++ = abits & (1 << 0) ? fg : 0;
*vram++ = abits & (1 << 1) ? fg : 0;
*vram++ = abits & (1 << 2) ? fg : 0;
*vram++ = abits & (1 << 3) ? fg : 0;
*vram++ = abits & (1 << 0) ? fg : 0;
*vram++ = abits & (1 << 1) ? fg : 0;
*vram++ = abits & (1 << 2) ? fg : 0;
*vram++ = bits & (1 << 0) ? fg : 0;
*vram++ = bits & (1 << 1) ? fg : 0;
*vram++ = bits & (1 << 2) ? fg : 0;
*vram++ = bits & (1 << 3) ? fg : 0;
*vram++ = bits & (1 << 0) ? fg : 0;
*vram++ = bits & (1 << 1) ? fg : 0;
*vram++ = bits & (1 << 2) ? fg : 0;
}
}
}
}
}
}
void a2_video_device::text_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow)
{
int row, col;
uint32_t start_address;
uint32_t address;
uint8_t *aux_page = m_ram_ptr;
int fg = 0;
int bg = 0;
if (m_80col)
{
start_address = 0x400;
if (m_aux_ptr)
{
aux_page = m_aux_ptr;
}
}
else
{
start_address = m_page2 ? 0x800 : 0x400;
}
beginrow = (std::max)(beginrow, cliprect.top() - (cliprect.top() % 8));
endrow = (std::min)(endrow, cliprect.bottom() - (cliprect.bottom() % 8) + 7);
switch (m_sysconfig & 0x03)
{
case 0: fg = WHITE; break;
case 1: fg = WHITE; break;
case 2: fg = GREEN; break;
case 3: fg = ORANGE; break;
}
for (row = beginrow; row <= endrow; row += 8)
{
if (m_80col)
{
for (col = 0; col < 40; col++)
{
/* calculate address */
address = start_address + ((((row/8) & 0x07) << 7) | (((row/8) & 0x18) * 5 + col));
plot_text_character(bitmap, col * 14, row, 1, aux_page[address],
m_char_ptr, m_char_size, fg, bg);
plot_text_character(bitmap, col * 14 + 7, row, 1, m_ram_ptr[address],
m_char_ptr, m_char_size, fg, bg);
}
}
else
{
for (col = 0; col < 40; col++)
{
/* calculate address */
address = start_address + ((((row/8) & 0x07) << 7) | (((row/8) & 0x18) * 5 + col));
plot_text_character(bitmap, col * 14, row, 2, m_ram_ptr[address],
m_char_ptr, m_char_size, fg, bg);
}
}
}
}
void a2_video_device::text_update_orig(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow)
{
int row, col;
uint32_t start_address = m_page2 ? 0x800 : 0x400;
uint32_t address;
int fg = 0;
int bg = 0;
beginrow = (std::max)(beginrow, cliprect.top() - (cliprect.top() % 8));
endrow = (std::min)(endrow, cliprect.bottom() - (cliprect.bottom() % 8) + 7);
switch (m_sysconfig & 0x03)
{
case 0: fg = WHITE; break;
case 1: fg = WHITE; break;
case 2: fg = GREEN; break;
case 3: fg = ORANGE; break;
}
for (row = beginrow; row <= endrow; row += 8)
{
for (col = 0; col < 40; col++)
{
/* calculate address */
address = start_address + ((((row/8) & 0x07) << 7) | (((row/8) & 0x18) * 5 + col));
plot_text_character_orig(bitmap, col * 14, row, 2, m_ram_ptr[address],
m_char_ptr, m_char_size, fg, bg);
}
}
}
void a2_video_device::text_update_jplus(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow)
{
int row, col;
uint32_t start_address = m_page2 ? 0x800 : 0x400;
uint32_t address;
int fg = 0;
int bg = 0;
beginrow = (std::max)(beginrow, cliprect.top() - (cliprect.top() % 8));
endrow = (std::min)(endrow, cliprect.bottom() - (cliprect.bottom() % 8) + 7);
switch (m_sysconfig & 0x03)
{
case 0: fg = WHITE; break;
case 1: fg = WHITE; break;
case 2: fg = GREEN; break;
case 3: fg = ORANGE; break;
}
for (row = beginrow; row <= endrow; row += 8)
{
for (col = 0; col < 40; col++)
{
/* calculate address */
address = start_address + ((((row/8) & 0x07) << 7) | (((row/8) & 0x18) * 5 + col));
plot_text_character_jplus(bitmap, col * 14, row, 2, m_ram_ptr[address],
m_char_ptr, m_char_size, fg, bg);
}
}
}
void a2_video_device::text_update_ultr(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow)
{
int row, col;
uint32_t start_address = m_page2 ? 0x800 : 0x400;
uint32_t address;
int fg = 0;
int bg = 0;
beginrow = (std::max)(beginrow, cliprect.top() - (cliprect.top() % 8));
endrow = (std::min)(endrow, cliprect.bottom() - (cliprect.bottom() % 8) + 7);
switch (m_sysconfig & 0x03)
{
case 0: fg = WHITE; break;
case 1: fg = WHITE; break;
case 2: fg = GREEN; break;
case 3: fg = ORANGE; break;
}
for (row = beginrow; row <= endrow; row += 8)
{
for (col = 0; col < 40; col++)
{
/* calculate address */
address = start_address + ((((row/8) & 0x07) << 7) | (((row/8) & 0x18) * 5 + col));
plot_text_character_ultr(bitmap, col * 14, row, 2, m_ram_ptr[address],
m_char_ptr, m_char_size, fg, bg);
}
}
}
void a2_video_device::hgr_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow)
{
const uint8_t *vram;
int row, col, b;
int offset;
uint8_t vram_row[42];
uint16_t v;
uint16_t *p;
uint32_t w;
uint16_t *artifact_map_ptr;
int mon_type = m_sysconfig & 0x03;
int begincol = 0, endcol = 40;
/* sanity checks */
if (beginrow < cliprect.top())
beginrow = cliprect.top();
if (endrow > cliprect.bottom())
endrow = cliprect.bottom();
if (endrow < beginrow)
return;
// we generate 2 pixels per "column" so adjust
if (begincol < (cliprect.left()/14))
begincol = (cliprect.left()/14);
if (endcol > (cliprect.right()/14))
endcol = (cliprect.right()/14);
if (cliprect.right() > 39*14)
endcol = 40;
if (endcol < begincol)
return;
//printf("HGR draw: page %c, rows %d-%d cols %d-%d\n", m_page2 ? '2' : '1', beginrow, endrow, begincol, endcol);
vram = &m_ram_ptr[(m_page2 ? 0x4000 : 0x2000)];
vram_row[0] = 0;
vram_row[41] = 0;
for (row = beginrow; row <= endrow; row++)
{
for (col = begincol; col < endcol; col++)
{
offset = ((((row/8) & 0x07) << 7) | (((row/8) & 0x18) * 5 + col)) | ((row & 7) << 10);
vram_row[1+col] = vram[offset];
}
p = &bitmap.pix16(row);
for (col = 0; col < 40; col++)
{
w = (((uint32_t) vram_row[col+0] & 0x7f) << 0)
| (((uint32_t) vram_row[col+1] & 0x7f) << 7)
| (((uint32_t) vram_row[col+2] & 0x7f) << 14);
// verified on h/w: setting dhires w/o 80col emulates a rev. 0 Apple ][ with no orange/blue
if (m_dhires)
{
artifact_map_ptr = m_hires_artifact_map.get();
}
else
{
artifact_map_ptr = &m_hires_artifact_map[((vram_row[col + 1] & 0x80) >> 7) * 16];
}
// CEC mono HGR mode
if ((m_monohgr) && (mon_type == 0))
{
mon_type = 1;
}
switch (mon_type)
{
case 0:
for (b = 0; b < 7; b++)
{
v = artifact_map_ptr[((w >> (b + 7-1)) & 0x07) | (((b ^ col) & 0x01) << 3)];
*(p++) = v;
*(p++) = v;
}
break;
case 1:
w >>= 7;
if (vram_row[col+1] & 0x80)
{
p++;
}
for (b = 0; b < 7; b++)
{
v = (w & 1);
w >>= 1;
*(p++) = v ? WHITE : BLACK;
*(p++) = v ? WHITE : BLACK;
}
if (vram_row[col+1] & 0x80)
{
p--;
}
break;
case 2:
w >>= 7;
if (vram_row[col+1] & 0x80)
{
p++;
}
for (b = 0; b < 7; b++)
{
v = (w & 1);
w >>= 1;
*(p++) = v ? GREEN : BLACK;
*(p++) = v ? GREEN : BLACK;
}
if (vram_row[col+1] & 0x80)
{
p--;
}
break;
case 3:
w >>= 7;
if (vram_row[col+1] & 0x80)
{
p++;
}
for (b = 0; b < 7; b++)
{
v = (w & 1);
w >>= 1;
*(p++) = v ? ORANGE : BLACK;
*(p++) = v ? ORANGE : BLACK;
}
if (vram_row[col+1] & 0x80)
{
p--;
}
break;
}
}
}
}
// similar to regular A2 except page 2 is at $A000
void a2_video_device::hgr_update_tk2000(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow)
{
const uint8_t *vram;
int row, col, b;
int offset;
uint8_t vram_row[42];
uint16_t v;
uint16_t *p;
uint32_t w;
uint16_t *artifact_map_ptr;
int mon_type = m_sysconfig & 0x03;
/* sanity checks */
if (beginrow < cliprect.top())
beginrow = cliprect.top();
if (endrow > cliprect.bottom())
endrow = cliprect.bottom();
if (endrow < beginrow)
return;
vram = &m_ram_ptr[(m_page2 ? 0xa000 : 0x2000)];
vram_row[0] = 0;
vram_row[41] = 0;
for (row = beginrow; row <= endrow; row++)
{
for (col = 0; col < 40; col++)
{
offset = ((((row/8) & 0x07) << 7) | (((row/8) & 0x18) * 5 + col)) | ((row & 7) << 10);
vram_row[1+col] = vram[offset];
}
p = &bitmap.pix16(row);
for (col = 0; col < 40; col++)
{
w = (((uint32_t) vram_row[col+0] & 0x7f) << 0)
| (((uint32_t) vram_row[col+1] & 0x7f) << 7)
| (((uint32_t) vram_row[col+2] & 0x7f) << 14);
switch (mon_type)
{
case 0:
artifact_map_ptr = &m_hires_artifact_map[((vram_row[col+1] & 0x80) >> 7) * 16];
for (b = 0; b < 7; b++)
{
v = artifact_map_ptr[((w >> (b + 7-1)) & 0x07) | (((b ^ col) & 0x01) << 3)];
*(p++) = v;
*(p++) = v;
}
break;
case 1:
w >>= 7;
if (vram_row[col] & 0x80)
{
p--;
}
for (b = 0; b < 7; b++)
{
v = (w & 1);
w >>= 1;
*(p++) = v ? WHITE : BLACK;
*(p++) = v ? WHITE : BLACK;
}
if (vram_row[col] & 0x80)
{
p++;
}
break;
case 2:
w >>= 7;
if (vram_row[col] & 0x80)
{
p--;
}
for (b = 0; b < 7; b++)
{
v = (w & 1);
w >>= 1;
*(p++) = v ? GREEN : BLACK;
*(p++) = v ? GREEN : BLACK;
}
if (vram_row[col] & 0x80)
{
p++;
}
break;
case 3:
w >>= 7;
if (vram_row[col] & 0x80)
{
p--;
}
for (b = 0; b < 7; b++)
{
v = (w & 1);
w >>= 1;
*(p++) = v ? ORANGE : BLACK;
*(p++) = v ? ORANGE : BLACK;
}
if (vram_row[col] & 0x80)
{
p++;
}
break;
}
}
}
}
void a2_video_device::dhgr_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow)
{
const uint8_t *vram, *vaux;
int row, col, b;
int offset;
uint8_t vram_row[82];
uint16_t v;
uint16_t *p;
uint32_t w;
int page = m_page2 ? 0x4000 : 0x2000;
int mon_type = m_sysconfig & 0x03;
/* sanity checks */
if (beginrow < cliprect.top())
beginrow = cliprect.top();
if (endrow > cliprect.bottom())
endrow = cliprect.bottom();
if (endrow < beginrow)
return;
vram = &m_ram_ptr[page];
if (m_aux_ptr)
{
vaux = m_aux_ptr;
}
else
{
vaux = vram;
}
vaux += page;
vram_row[0] = 0;
vram_row[81] = 0;
for (row = beginrow; row <= endrow; row++)
{
for (col = 0; col < 40; col++)
{
offset = ((((row/8) & 0x07) << 7) | (((row/8) & 0x18) * 5 + col)) | ((row & 7) << 10);
vram_row[1+(col*2)+0] = vaux[offset];
vram_row[1+(col*2)+1] = vram[offset];
}
p = &bitmap.pix16(row);
for (col = 0; col < 80; col++)
{
w = (((uint32_t) vram_row[col+0] & 0x7f) << 0)
| (((uint32_t) vram_row[col+1] & 0x7f) << 7)
| (((uint32_t) vram_row[col+2] & 0x7f) << 14);
switch (mon_type)
{
case 0:
for (b = 0; b < 7; b++)
{
v = m_dhires_artifact_map[((((w >> (b + 7-1)) & 0x0F) * 0x11) >> (((2-(col*7+b))) & 0x03)) & 0x0F];
*(p++) = v;
}
break;
case 1:
w >>= 7;
for (b = 0; b < 7; b++)
{
v = (w & 1);
w >>= 1;
*(p++) = v ? WHITE : BLACK;
}
break;
case 2:
w >>= 7;
for (b = 0; b < 7; b++)
{
v = (w & 1);
w >>= 1;
*(p++) = v ? GREEN : BLACK;
}
break;
case 3:
w >>= 7;
for (b = 0; b < 7; b++)
{
v = (w & 1);
w >>= 1;
*(p++) = v ? ORANGE : BLACK;
}
break;
}
}
}
}
/*
Latest Apple II NTSC palette from "Apple II Video Display Theory"
https://docs.google.com/spreadsheets/d/1rKR6A_bVniSCtIP_rrv8QLWJdj4h6jEU1jJj0AebWwg/
*/
static const rgb_t apple2_palette[] =
{
rgb_t::black(),
rgb_t(0xa7, 0x0b, 0x40), /* Dark Red */
rgb_t(0x40, 0x1c, 0xf7), /* Dark Blue */
rgb_t(0xe6, 0x28, 0xff), /* Purple */
rgb_t(0x00, 0x74, 0x40), /* Dark Green */
rgb_t(0x80, 0x80, 0x80), /* Dark Gray */
rgb_t(0x19, 0x90, 0xff), /* Medium Blue */
rgb_t(0xbf, 0x9c, 0xff), /* Light Blue */
rgb_t(0x40, 0x63, 0x00), /* Brown */
rgb_t(0xe6, 0x6f, 0x00), /* Orange */
rgb_t(0x80, 0x80, 0x80), /* Light Grey */
rgb_t(0xff, 0x8b, 0xbf), /* Pink */
rgb_t(0x19, 0xd7, 0x00), /* Light Green */
rgb_t(0xbf, 0xe3, 0x08), /* Yellow */
rgb_t(0x58, 0xf4, 0xbf), /* Aquamarine */
rgb_t(0xff, 0xff, 0xff) /* White */
};
void a2_video_device::init_palette()
{
for (int i = 0; i < ARRAY_LENGTH(apple2_palette); i++)
set_pen_color(i, apple2_palette[i]);
}
uint32_t a2_video_device::palette_entries() const
{
return ARRAY_LENGTH(apple2_palette);
}
uint32_t a2_video_device::screen_update_GS(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
const uint8_t *vram;
uint32_t *scanline;
uint8_t scb, b;
int col, palette;
uint32_t last_pixel = 0, pixel;
int beamy;
uint16_t *a2pixel;
beamy = cliprect.top();
if (m_newvideo & 0x80)
{
// in top or bottom border?
if ((beamy < BORDER_TOP) || (beamy >= 200+BORDER_TOP))
{
// don't draw past the bottom border
if (beamy >= 231+BORDER_TOP)
{
return 0;
}
scanline = &bitmap.pix32(beamy);
for (col = 0; col < BORDER_LEFT+BORDER_RIGHT+640; col++)
{
scanline[col] = m_GSborder_colors[m_GSborder];
}
}
else // regular screen area
{
int shrline = beamy - BORDER_TOP;
scb = m_aux_ptr[0x9D00 + shrline];
palette = ((scb & 0x0f) << 4);
vram = &m_aux_ptr[0x2000 + (shrline * 160)];
scanline = &bitmap.pix32(beamy);
// draw left and right borders
for (col = 0; col < BORDER_LEFT; col++)
{
scanline[col] = m_GSborder_colors[m_GSborder];
scanline[col+BORDER_LEFT+640] = m_GSborder_colors[m_GSborder];
}
if (scb & 0x80) // 640 mode
{
for (col = 0; col < 160; col++)
{
b = vram[col];
scanline[col * 4 + 0 + BORDER_LEFT] = m_shr_palette[palette + 0 + ((b >> 6) & 0x03)];
scanline[col * 4 + 1 + BORDER_LEFT] = m_shr_palette[palette + 4 + ((b >> 4) & 0x03)];
scanline[col * 4 + 2 + BORDER_LEFT] = m_shr_palette[palette + 8 + ((b >> 2) & 0x03)];
scanline[col * 4 + 3 + BORDER_LEFT] = m_shr_palette[palette + 12 + ((b >> 0) & 0x03)];
}
}
else // 320 mode
{
for (col = 0; col < 160; col++)
{
b = vram[col];
pixel = (b >> 4) & 0x0f;
if ((scb & 0x20) && !pixel)
pixel = last_pixel;
else
last_pixel = pixel;
pixel += palette;
scanline[col * 4 + 0 + BORDER_LEFT] = m_shr_palette[pixel];
scanline[col * 4 + 1 + BORDER_LEFT] = m_shr_palette[pixel];
b = vram[col];
pixel = (b >> 0) & 0x0f;
if ((scb & 0x20) && !pixel)
pixel = last_pixel;
else
last_pixel = pixel;
pixel += palette;
scanline[col * 4 + 2 + BORDER_LEFT] = m_shr_palette[pixel];
scanline[col * 4 + 3 + BORDER_LEFT] = m_shr_palette[pixel];
}
}
}
}
else
{
/* call legacy Apple II video rendering at scanline 0 to draw into the off-screen buffer */
if (beamy == 0)
{
rectangle new_cliprect(0, 559, 0, 191);
screen_update_GS_8bit(screen, *m_8bit_graphics, new_cliprect);
}
if ((beamy < (BORDER_TOP+4)) || (beamy >= (192+4+BORDER_TOP)))
{
if (beamy >= (231+BORDER_TOP))
{
return 0;
}
scanline = &bitmap.pix32(beamy);
for (col = 0; col < BORDER_LEFT+BORDER_RIGHT+640; col++)
{
scanline[col] = m_GSborder_colors[m_GSborder];
}
}
else
{
scanline = &bitmap.pix32(beamy);
// draw left and right borders
for (col = 0; col < BORDER_LEFT + 40; col++)
{
scanline[col] = m_GSborder_colors[m_GSborder];
scanline[col+BORDER_LEFT+600] = m_GSborder_colors[m_GSborder];
}
a2pixel = &m_8bit_graphics->pix16(beamy-(BORDER_TOP+4));
for (int x = 0; x < 560; x++)
{
scanline[40 + BORDER_LEFT + x] = m_GSborder_colors[*a2pixel++];
}
}
}
return 0;
}
uint32_t a2_video_device::screen_update_GS_8bit(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
bool old_page2 = m_page2;
// don't display page2 if 80store is set (we just saved the previous value, don't worry)
if (m_80store)
{
m_page2 = false;
}
// always update the flash timer here so it's smooth regardless of mode switches
m_flash = ((machine().time() * 4).seconds() & 1) ? true : false;
if (m_graphics)
{
if (m_hires)
{
if (m_mix)
{
if ((m_dhires) && (m_80col))
{
dhgr_update(screen, bitmap, cliprect, 0, 159);
}
else
{
hgr_update(screen, bitmap, cliprect, 0, 159);
}
text_updateGS(screen, bitmap, cliprect, 160, 191);
}
else
{
if ((m_dhires) && (m_80col))
{
dhgr_update(screen, bitmap, cliprect, 0, 191);
}
else
{
hgr_update(screen, bitmap, cliprect, 0, 191);
}
}
}
else // lo-res
{
if (m_mix)
{
if ((m_dhires) && (m_80col))
{
dlores_update(screen, bitmap, cliprect, 0, 159);
}
else
{
lores_update(screen, bitmap, cliprect, 0, 159);
}
text_updateGS(screen, bitmap, cliprect, 160, 191);
}
else
{
if ((m_dhires) && (m_80col))
{
dlores_update(screen, bitmap, cliprect, 0, 191);
}
else
{
lores_update(screen, bitmap, cliprect, 0, 191);
}
}
}
}
else
{
text_updateGS(screen, bitmap, cliprect, 0, 191);
}
m_page2 = old_page2;
return 0;
}
void a2_video_device::text_updateGS(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow)
{
int row, col;
uint32_t start_address;
uint32_t address;
uint8_t *aux_page = m_ram_ptr;
if (m_80col)
{
start_address = 0x400;
if (m_aux_ptr)
{
aux_page = m_aux_ptr;
}
}
else
{
start_address = m_page2 ? 0x800 : 0x400;
}
beginrow = (std::max)(beginrow, cliprect.top() - (cliprect.top() % 8));
endrow = (std::min)(endrow, cliprect.bottom() - (cliprect.bottom() % 8) + 7);
for (row = beginrow; row <= endrow; row += 8)
{
if (m_80col)
{
for (col = 0; col < 40; col++)
{
/* calculate address */
address = start_address + ((((row/8) & 0x07) << 7) | (((row/8) & 0x18) * 5 + col));
plot_text_characterGS(bitmap, col * 14, row, 1, aux_page[address],
m_char_ptr, m_char_size, m_GSfg, m_GSbg);
plot_text_characterGS(bitmap, col * 14 + 7, row, 1, m_ram_ptr[address],
m_char_ptr, m_char_size, m_GSfg, m_GSbg);
}
}
else
{
for (col = 0; col < 40; col++)
{
/* calculate address */
address = start_address + ((((row/8) & 0x07) << 7) | (((row/8) & 0x18) * 5 + col));
plot_text_characterGS(bitmap, col * 14, row, 2, m_ram_ptr[address],
m_char_ptr, m_char_size, m_GSfg, m_GSbg);
}
}
}
}