diff options
Diffstat (limited to 'src/devices/video/upd3301.cpp')
-rw-r--r-- | src/devices/video/upd3301.cpp | 271 |
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); |