summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/video/upd3301.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/video/upd3301.cpp')
-rw-r--r--src/devices/video/upd3301.cpp271
1 files changed, 167 insertions, 104 deletions
diff --git a/src/devices/video/upd3301.cpp b/src/devices/video/upd3301.cpp
index ed57a55d760..f1252c0c3ae 100644
--- a/src/devices/video/upd3301.cpp
+++ b/src/devices/video/upd3301.cpp
@@ -10,20 +10,23 @@
TODO:
- - pinpoint how much of pc8001/pc8801 drawing functions should actually be inherited
- here;
- - N interrupt (special control character)
- - light pen
- - reset counters
+ - pinpoint and expose actual attribute pin meanings;
+ - verify if pc8001 / pc8801 attribute bit 3 extension is internal or
+ external to the chip;
+ - N interrupt (special control character);
+ - light pen;
+ - reset counters;
- proper DMA timing (now the whole screen is transferred at the end of the frame,
accurate timing requires CCLK timer which kills performance)
- - DMA burst mode;
+ - DMA burst mode (reportedly not working in pc8801 arch,
+ should return a blank screen for reasons);
+ - DMA underrun (sorcerml in pc8801?). Should throw a status U irq;
- cleanup: variable namings should be more verbose
- (i.e. not be a single letter like m_y, m_z, m_b ...);
- - sorcerml (pc8801) has buggy DMA burst mode, causing an underrun (hence a status U interrupt);
- - jettermi (pc8801) expects to colorize its underlying 400 b&w mode by masking with the
- text color attributes here;
- - xak2 (pc8801) throws text garbage on legacy renderer (verify);
+ (i.e. not be a single letter like m_y, m_z, m_b ...);
+ - escapepr (pc8801_flop): should draw a semigfx mask over gameplay window but all the attribute
+ areas are 0, with only a single 0x50 0x18 setup at the very end of the VRAM,
+ is it expecting to chain from one frame to another or it's simply failing to setup it properly
+ earlier?
*/
@@ -32,9 +35,27 @@
#include "screen.h"
-//#define VERBOSE 1
+#define LOG_WARN (1U << 1) // Illegal setup attempts
+#define LOG_CMD (1U << 2) // Command r/w
+#define LOG_CURSOR (1U << 3) // Cursor position (verbose)
+#define LOG_CRTC (1U << 4) // CRTC parameters
+#define LOG_INT (1U << 5) // INT, VRTC and DRQ lines
+#define LOG_HRTC (1U << 6) // HRTC (verbose)
+#define LOG_STRIPS (1U << 7) // attribute row strips (verbose)
+
+#define VERBOSE (LOG_WARN)
+//#define VERBOSE (LOG_WARN|LOG_CMD)
+//#define LOG_OUTPUT_FUNC osd_printf_info
+
#include "logmacro.h"
+#define LOGWARN(...) LOGMASKED(LOG_WARN, __VA_ARGS__)
+#define LOGCMD(...) LOGMASKED(LOG_CMD, __VA_ARGS__)
+#define LOGCURSOR(...) LOGMASKED(LOG_CURSOR, __VA_ARGS__)
+#define LOGCRTC(...) LOGMASKED(LOG_CRTC, __VA_ARGS__)
+#define LOGINT(...) LOGMASKED(LOG_INT, __VA_ARGS__)
+#define LOGHRTC(...) LOGMASKED(LOG_HRTC, __VA_ARGS__)
+#define LOGSTRIPS(...) LOGMASKED(LOG_STRIPS, __VA_ARGS__)
//**************************************************************************
@@ -125,19 +146,14 @@ upd3301_device::upd3301_device(const machine_config &mconfig, const char *tag, d
void upd3301_device::device_start()
{
screen().register_screen_bitmap(m_bitmap);
- // resolve callbacks
- m_write_drq.resolve_safe();
- m_write_int.resolve_safe();
- m_write_hrtc.resolve_safe();
- m_write_vrtc.resolve_safe();
- m_write_rvv.resolve();
+
+ // resolve delegates
m_display_cb.resolve();
m_attr_fetch_cb.resolve();
// allocate timers
- m_hrtc_timer = timer_alloc(TIMER_HRTC);
- m_vrtc_timer = timer_alloc(TIMER_VRTC);
- m_drq_timer = timer_alloc(TIMER_DRQ);
+ m_hrtc_timer = timer_alloc(FUNC(upd3301_device::hrtc_update), this);
+ m_vrtc_timer = timer_alloc(FUNC(upd3301_device::vrtc_update), this);
// state saving
save_item(NAME(m_y));
@@ -181,12 +197,15 @@ void upd3301_device::device_start()
void upd3301_device::device_reset()
{
+ set_display(0);
set_interrupt(0);
set_drq(0);
m_cm = 0;
m_b = 48;
m_reverse_display = false;
+ if (!m_write_rvv.isunset())
+ m_write_rvv(m_reverse_display);
recompute_parameters();
}
@@ -203,7 +222,7 @@ void upd3301_device::device_clock_changed()
//-------------------------------------------------
-// device_timer - handle timer events
+// timer events
//-------------------------------------------------
// this snipped was inside screen_update fn
@@ -229,45 +248,42 @@ inline void upd3301_device::reset_fifo_vrtc()
m_attr_frame = 0;
m_attr_blink = !m_attr_blink;
}
+ // Clear the buffer here. Needed in particular by PC-88, which usually disables
+ // DMA transfer in order to show Graphic layer only (i.e. bugattac).
+ m_bitmap.fill(0, screen().visible_area());
}
-void upd3301_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+TIMER_CALLBACK_MEMBER(upd3301_device::hrtc_update)
{
- switch (id)
- {
- case TIMER_HRTC:
- LOG("UPD3301 HRTC: %u\n", param);
+ LOGHRTC("HRTC: %u at beam %d x %d\n", param, screen().hpos(), screen().vpos());
- m_write_hrtc(param);
- m_hrtc = param;
+ m_write_hrtc(param);
+ m_hrtc = param;
- update_hrtc_timer(param);
- break;
+ update_hrtc_timer(param);
+}
- case TIMER_VRTC:
- LOG("UPD3301 VRTC: %u\n", param);
+TIMER_CALLBACK_MEMBER(upd3301_device::vrtc_update)
+{
+ LOGINT("VRTC: %u at y %d\n", param, screen().vpos());
- m_write_vrtc(param);
- m_vrtc = param;
+ m_write_vrtc(param);
+ m_vrtc = param;
- update_vrtc_timer(param);
- if(!(m_status & STATUS_VE))
- break;
-
- if(!param)
- reset_fifo_vrtc();
+ update_vrtc_timer(param);
+ if (!get_display_status())
+ return;
- if (param && !m_me)
- {
- m_status |= STATUS_E;
- set_interrupt(1);
- }
- else if(!param)
- set_drq(1);
- break;
+ if (!param)
+ {
+ reset_fifo_vrtc();
+ set_drq(1);
+ }
- case TIMER_DRQ:
- break;
+ if (param && !m_me)
+ {
+ m_status |= STATUS_E;
+ set_interrupt(1);
}
}
@@ -283,14 +299,14 @@ uint8_t upd3301_device::read(offs_t offset)
switch (offset & 0x01)
{
case 0: // data
- // TODO: light pen
if (!machine().side_effects_disabled())
- popmessage("light pen reading");
+ popmessage("light pen reading?");
break;
case 1: // status
data = m_status;
- m_status &= ~(STATUS_LP | STATUS_E |STATUS_N | STATUS_U);
+ if (!machine().side_effects_disabled())
+ m_status &= ~(STATUS_LP | STATUS_E |STATUS_N | STATUS_U);
break;
}
@@ -319,8 +335,8 @@ void upd3301_device::write(offs_t offset, uint8_t data)
m_h = (data & 0x7f) + 2;
if (m_h > 80)
popmessage("Illegal width set %d", m_h);
- LOG("UPD3301 DMA Mode: %s\n", m_dma_mode ? "character" : "burst");
- LOG("UPD3301 H: %u\n", m_h);
+ LOGCMD("DMA Mode: %s\n", m_dma_mode ? "character" : "burst");
+ LOGCMD("H: %u (characters per line)\n", m_h);
break;
case 1:
@@ -329,8 +345,8 @@ void upd3301_device::write(offs_t offset, uint8_t data)
// number of lines displayed -1
// (or in other words, tilemap y size)
m_l = (data & 0x3f) + 1;
- LOG("UPD3301 B: %u\n", m_b);
- LOG("UPD3301 L: %u\n", m_l);
+ LOGCMD("B: %u (cursor blink rate)\n", m_b);
+ LOGCMD("L: %u (number of lines displayed)\n", m_l);
break;
case 2:
@@ -349,9 +365,9 @@ void upd3301_device::write(offs_t offset, uint8_t data)
popmessage("cursor mode %02x", m_c);
// Number of lines per character -1
m_r = (data & 0x1f) + 1;
- LOG("UPD3301 S: %u\n", m_s);
- LOG("UPD3301 C: %u\n", m_c);
- LOG("UPD3301 R: %u\n", m_r);
+ LOGCMD("S: %u (skip line)\n", m_s);
+ LOGCMD("C: %u (cursor mode)\n", m_c);
+ LOGCMD("R: %u (number of lines per character)\n", m_r);
break;
case 3:
@@ -359,8 +375,8 @@ void upd3301_device::write(offs_t offset, uint8_t data)
m_v = (data >> 5) + 1;
// hblank width -2 (6 to 33)
m_z = (data & 0x1f) + 2;
- LOG("UPD3301 V: %u\n", m_v);
- LOG("UPD3301 Z: %u\n", m_z);
+ LOGCMD("V: %u (vblank lines)\n", m_v);
+ LOGCMD("Z: %u (hblank width)\n", m_z);
recompute_parameters();
break;
@@ -376,13 +392,17 @@ void upd3301_device::write(offs_t offset, uint8_t data)
//m_at0 = BIT(data, 6);
//m_sc = BIT(data, 5);
m_gfx_mode = (data & 0xe0) >> 5;
- if (m_gfx_mode & 0x5)
+ if (m_gfx_mode & 0x4)
popmessage("attr mode %02x", m_gfx_mode);
+
// Max number of attributes per line -1
// can't be higher than 20
- m_attr = std::min((data & 0x1f) + 1, 20);
- LOG("UPD3301 AT1: %u AT0: %u SC: %u\n", BIT(data, 7), BIT(data, 6), BIT(data, 5));
- LOG("UPD3301 ATTR: %u\n", m_attr);
+ // overriden to 0 in no attribute/no sc mode
+ m_attr = (m_gfx_mode == 1) ? 0 : std::min((data & 0x1f) + 1, 20);
+ LOGCMD("AT1: %u AT0: %u SC: %u (gfx mode = %02x)\n",
+ BIT(data, 7), BIT(data, 6), BIT(data, 5), m_gfx_mode
+ );
+ LOGCMD("ATTR: %u (num attributes per line)\n", m_attr);
m_mode = MODE_NONE;
break;
@@ -396,12 +416,12 @@ void upd3301_device::write(offs_t offset, uint8_t data)
{
case 0:
m_cx = data & 0x7f;
- LOG("UPD3301 CX: %u\n", m_cx);
+ LOGCURSOR("CX: %u\n", m_cx);
break;
case 1:
m_cy = data & 0x3f;
- LOG("UPD3301 CY: %u\n", m_cy);
+ LOGCURSOR("CY: %u\n", m_cy);
m_mode = MODE_NONE;
break;
@@ -411,7 +431,7 @@ void upd3301_device::write(offs_t offset, uint8_t data)
break;
default:
- LOG("UPD3301 Invalid Parameter Byte %02x!\n", data);
+ LOGWARN("Invalid Parameter Byte %02x!\n", data);
}
break;
@@ -422,25 +442,28 @@ void upd3301_device::write(offs_t offset, uint8_t data)
switch (data & 0xe0)
{
case COMMAND_RESET:
- LOG("UPD3301 Reset\n");
+ LOGCMD("Reset\n");
m_mode = MODE_RESET;
- // TODO: this also disables external display such as Graphic VRAM in PC-8801
+ // This should also disable external display such as Graphic layer in PC-8801
+ // sorcerml contradicts with it tho: it just issue a reset command without caring about
+ // re-enabling display.
set_display(0);
set_interrupt(0);
break;
case COMMAND_START_DISPLAY:
{
- LOG("UPD3301 Start Display\n");
+ LOGCMD("Start Display\n");
bool new_rvv = bool(BIT(data, 0));
+ LOGCMD("RVV: %u (reverse display)\n", new_rvv);
// misscmd (pc8001) enables this
if (m_reverse_display != new_rvv)
{
m_reverse_display = new_rvv;
- if (!m_write_rvv.isnull())
+ if (!m_write_rvv.isunset())
m_write_rvv(m_reverse_display);
else if (m_reverse_display == true)
- logerror("%s: reverse display enabled (warning)\n", machine().describe_context());
+ logerror("%s: RVV reverse display enabled (warning)\n", machine().describe_context());
}
set_display(1);
reset_counters();
@@ -448,38 +471,38 @@ void upd3301_device::write(offs_t offset, uint8_t data)
}
case COMMAND_SET_INTERRUPT_MASK:
- LOG("UPD3301 Set Interrupt Mask\n");
+ LOGCMD("Set Interrupt Mask\n");
// vblank irq mask
m_me = BIT(data, 0);
// special control character irq mask
m_mn = BIT(data, 1);
- // TODO: writing a negated bit 0 makes status bit 7 to be held high?
- LOG("UPD3301 ME: %u\n", m_me);
- LOG("UPD3301 MN: %u\n", m_mn);
+ // TODO: Apparently unmasking ME should be reflected in undocumented status bit 7
+ LOGCMD("ME: %u (vblank irq mask)\n", m_me);
+ LOGCMD("MN: %u (special control char irq mask)\n", m_mn);
break;
case COMMAND_READ_LIGHT_PEN:
- LOG("UPD3301 Read Light Pen\n");
+ LOGCMD("Read Light Pen\n");
// TODO: similar to cursor parameters except on read
// (plus an HR to bit 7 param [0])
m_mode = MODE_READ_LIGHT_PEN;
break;
case COMMAND_LOAD_CURSOR_POSITION:
- LOG("UPD3301 Load Cursor Position\n");
+ LOGCURSOR("Load Cursor Position\n");
m_mode = MODE_LOAD_CURSOR_POSITION;
// (1) show cursor (0) disable cursor
m_cm = BIT(data, 0);
- LOG("UPD3301 CM: %u\n", m_cm);
+ LOGCURSOR("CM: %u\n", m_cm);
break;
case COMMAND_RESET_INTERRUPT:
- LOG("UPD3301 Reset Interrupt\n");
+ LOGCMD("Reset Interrupt\n");
set_interrupt(0);
break;
case COMMAND_RESET_COUNTERS:
- LOG("UPD3301 Reset Counters\n");
+ LOGCMD("Reset Counters\n");
m_mode = MODE_RESET_COUNTERS;
reset_counters();
break;
@@ -495,6 +518,7 @@ void upd3301_device::write(offs_t offset, uint8_t data)
void upd3301_device::dack_w(uint8_t data)
{
+ // TODO: underrun condition
if (m_y >= (m_l * m_r))
{
return;
@@ -514,8 +538,6 @@ void upd3301_device::dack_w(uint8_t data)
if ((m_data_fifo_pos == m_h) && (m_attr_fifo_pos == (m_attr << 1)))
{
const u8 attr_max_size = 80;
- // first attribute start is always overwritten with a 0
- m_attr_fifo[m_input_fifo][0] = 0;
// last parameter always extends up to the end of the row
// (7narabe (pc8001) fills last row value with white when exausting available slots)
m_attr_fifo[m_input_fifo][40] = attr_max_size;
@@ -526,7 +548,7 @@ void upd3301_device::dack_w(uint8_t data)
draw_scanline();
- if (m_y == (m_l * m_r))
+ if (m_y >= (m_l * m_r))
{
// end DMA transfer
set_drq(0);
@@ -545,7 +567,7 @@ void upd3301_device::lpen_w(int state)
//-------------------------------------------------
-// hrtc_r -
+// hrtc_r - Horizontal ReTraCe
//-------------------------------------------------
int upd3301_device::hrtc_r()
@@ -555,7 +577,7 @@ int upd3301_device::hrtc_r()
//-------------------------------------------------
-// vrtc_r -
+// vrtc_r - Vertical ReTraCe
//-------------------------------------------------
int upd3301_device::vrtc_r()
@@ -571,20 +593,45 @@ int upd3301_device::vrtc_r()
UPD3301_FETCH_ATTRIBUTE( upd3301_device::default_attr_fetch )
{
const u8 attr_max_size = 80;
- std::array<u16, attr_max_size> attr_extend_info;
+ std::array<u16, attr_max_size> attr_extend_info = {0};
+
+ // elthlead (pc8801) uses b&w no attributes/no special control mode
+ if (m_gfx_mode == 1)
+ return attr_extend_info;
+
+ int row_offset = 0;
+
+ // if very first value is not a 0 then use next value as 0-[n] filler
+ // pc8801 examples:
+ // - N88 Basic (status on bottom)
+ // - jettermi
+ // - play6lim
+ // TODO: comsight uses an attr_row of 2 as first param when entering in code edit mode.
+ // Most likely a delay side effect with DMA that *shouldn't* pickup this branch.
+ if (attr_row[0] != 0)
+ {
+ // tdown (pc8801) unintentionally requires to clamp against max size while loading
+ // (fills TVRAM with floppy data)
+ u8 attr_end = std::min(attr_row[0], attr_max_size);
+ u8 attr_value = attr_row[1];
+ for (int i = 0; i < attr_end; i++)
+ attr_extend_info[i] = attr_value;
+ LOGSTRIPS("ex ----| start: 0 | end: %2u [%02x]\n", attr_end, attr_value);
+
+ row_offset = 2;
+ }
- // TODO: uPD3301 may actually fetch in LIFO order
- for (int ex = 0; ex < attr_fifo_size; ex+=2)
+ for (int ex = 0; ex < attr_fifo_size - row_offset; ex+=2)
{
u8 attr_start = std::min(attr_row[ex], attr_max_size);
- u8 attr_value = attr_row[ex+1];
- u8 attr_end = std::min(attr_row[ex+2], attr_max_size);
+ u8 attr_value = attr_row[ex + 1 + row_offset];
+ u8 attr_end = std::min(attr_row[ex + 2], attr_max_size);
// if the target is == 0 then just consider max size instead
// (starfire (pc8001) wants this otherwise will black screen on gameplay)
if (attr_end == 0)
attr_end = attr_max_size;
- //printf("%04x %d %d [%02x]\n", ex, attr_start, attr_end, attr_value);
+ LOGSTRIPS("ex %04x| start: %2u | end: %2u [%02x]%s\n", ex, attr_start, attr_end, attr_value, attr_start == attr_end ? " (ignored)" : "");
for (int i = attr_start; i < attr_end; i++)
attr_extend_info[i] = attr_value;
@@ -635,7 +682,10 @@ void upd3301_device::draw_scanline()
}
}
- m_y += m_r;
+ // sorcer (pc8801) enables the "skip line" then sets up DMA for 12 rows (start address 0xf9e8, do the math).
+ // Other than applying pseudo-interlace effect over the graphic layer this also seems to skip strips,
+ // sorcer wants the very last row (0xff88) to be used as a mask over bottom-most 16 lines.
+ m_y += m_r << m_s;
}
@@ -645,9 +695,10 @@ void upd3301_device::draw_scanline()
uint32_t upd3301_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
- bitmap.fill(rgb_t(0x00,0x00,0x00), cliprect);
+ // TODO: non-transparent modes
+ bitmap.fill(0, cliprect);
- if (!(m_status & STATUS_VE))
+ if (!get_display_status())
return 0;
copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
@@ -662,7 +713,7 @@ uint32_t upd3301_device::screen_update(screen_device &screen, bitmap_rgb32 &bitm
void upd3301_device::set_interrupt(int state)
{
- LOG("UPD3301 Interrupt: %u\n", state);
+ LOGINT("Interrupt: %u\n", state);
m_write_int(state);
@@ -679,7 +730,7 @@ void upd3301_device::set_interrupt(int state)
void upd3301_device::set_drq(int state)
{
- LOG("UPD3301 DRQ: %u\n", state);
+ LOGINT("DRQ: %u %d\n", state, screen().vpos());
m_write_drq(state);
}
@@ -689,6 +740,17 @@ void upd3301_device::set_drq(int state)
// set_display -
//-------------------------------------------------
+bool upd3301_device::get_display_status()
+{
+ return bool(m_status & STATUS_VE);
+}
+
+bool upd3301_device::is_gfx_color_mode()
+{
+ return get_display_status() && (m_gfx_mode == 2);
+}
+
+
void upd3301_device::set_display(int state)
{
if (state)
@@ -736,11 +798,12 @@ void upd3301_device::update_hrtc_timer(int state)
void upd3301_device::update_vrtc_timer(int state)
{
- int next_y = state ? (m_l * m_r) : 0;
+ const bool next_state = !state;
+ int next_y = next_state ? (m_l * m_r) : 0;
attotime duration = screen().time_until_pos(next_y, 0);
- m_vrtc_timer->adjust(duration, !state);
+ m_vrtc_timer->adjust(duration, next_state);
}
@@ -759,8 +822,8 @@ void upd3301_device::recompute_parameters()
visarea.set(0, (m_h * m_width) - 1, 0, (m_l * m_r) - 1);
- LOG("UPD3301 Screen: %u x %u @ %f Hz\n", horiz_pix_total, vert_pix_total, 1 / ATTOSECONDS_TO_DOUBLE(refresh));
- LOG("UPD3301 Visible Area: (%u, %u) - (%u, %u)\n", visarea.min_x, visarea.min_y, visarea.max_x, visarea.max_y);
+ LOGCRTC("Screen: %u x %u @ %f Hz\n", horiz_pix_total, vert_pix_total, 1 / ATTOSECONDS_TO_DOUBLE(refresh));
+ LOGCRTC("Visible Area: (%u, %u) - (%u, %u)\n", visarea.min_x, visarea.min_y, visarea.max_x, visarea.max_y);
screen().configure(horiz_pix_total, vert_pix_total, visarea, refresh);