diff options
Diffstat (limited to 'src/devices/video/upd3301.cpp')
-rw-r--r-- | src/devices/video/upd3301.cpp | 661 |
1 files changed, 427 insertions, 234 deletions
diff --git a/src/devices/video/upd3301.cpp b/src/devices/video/upd3301.cpp index 07b8a32c99b..f1252c0c3ae 100644 --- a/src/devices/video/upd3301.cpp +++ b/src/devices/video/upd3301.cpp @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Curt Coder +// copyright-holders:Curt Coder, Angelo Salese /********************************************************************** NEC uPD3301 Programmable CRT Controller emulation @@ -10,12 +10,23 @@ TODO: - - attributes - - N interrupt - - 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 (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 ...); + - 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? */ @@ -24,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__) //************************************************************************** @@ -77,32 +106,35 @@ DEFINE_DEVICE_TYPE(UPD3301, upd3301_device, "upd3301", "NEC uPD3301") // upd3301_device - constructor //------------------------------------------------- -upd3301_device::upd3301_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : - device_t(mconfig, UPD3301, tag, owner, clock), - device_video_interface(mconfig, *this), - m_write_int(*this), - m_write_drq(*this), - m_write_hrtc(*this), - m_write_vrtc(*this), - m_width(0), - m_status(0), - m_param_count(0), - m_data_fifo_pos(0), - m_attr_fifo_pos(0), - m_input_fifo(0), - m_me(0), - m_h(80), - m_l(20), - m_r(10), - m_v(6), - m_z(32), - m_attr_blink(0), - m_attr_frame(0), - m_cm(0), - m_cx(0), - m_cy(0), - m_cursor_blink(0), - m_cursor_frame(0) +upd3301_device::upd3301_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, UPD3301, tag, owner, clock) + , device_video_interface(mconfig, *this) + , m_write_int(*this) + , m_write_drq(*this) + , m_write_hrtc(*this) + , m_write_vrtc(*this) + , m_write_rvv(*this) + , m_display_cb(*this) + , m_attr_fetch_cb(*this) + , m_width(0) + , m_status(0) + , m_param_count(0) + , m_data_fifo_pos(0) + , m_attr_fifo_pos(0) + , m_input_fifo(0) + , m_me(0) + , m_h(80) + , m_l(20) + , m_r(10) + , m_v(6) + , m_z(32) + , m_attr_blink(0) + , m_attr_frame(0) + , m_cm(0) + , m_cx(0) + , m_cy(0) + , m_cursor_blink(0) + , m_cursor_frame(0) { } @@ -114,17 +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_display_cb.bind_relative_to(*owner()); - m_write_drq.resolve_safe(); - m_write_int.resolve_safe(); - m_write_hrtc.resolve_safe(); - m_write_vrtc.resolve_safe(); + + // 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)); @@ -147,9 +176,7 @@ void upd3301_device::device_start() save_item(NAME(m_r)); save_item(NAME(m_v)); save_item(NAME(m_z)); - save_item(NAME(m_at1)); - save_item(NAME(m_at0)); - save_item(NAME(m_sc)); + save_item(NAME(m_gfx_mode)); save_item(NAME(m_attr)); save_item(NAME(m_attr_blink)); save_item(NAME(m_attr_frame)); @@ -160,6 +187,7 @@ void upd3301_device::device_start() save_item(NAME(m_cursor_frame)); save_item(NAME(m_data_fifo)); save_item(NAME(m_attr_fifo)); + save_item(NAME(m_reverse_display)); } @@ -169,9 +197,16 @@ 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(); } @@ -187,43 +222,68 @@ void upd3301_device::device_clock_changed() //------------------------------------------------- -// device_timer - handle timer events +// timer events //------------------------------------------------- -void upd3301_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +// this snipped was inside screen_update fn +// bad idea: it causes all sort of desync glitches when emulation unthrottles +// TODO: verify if FIFO clear-out happens on vblank-in or -out +inline void upd3301_device::reset_fifo_vrtc() { - switch (id) + m_y = 0; + m_data_fifo_pos = 0; + m_attr_fifo_pos = 0; + + m_cursor_frame++; + + if (m_cursor_frame == m_b) { - case TIMER_HRTC: - LOG("UPD3301 HRTC: %u\n", param); + m_cursor_frame = 0; + m_cursor_blink = !m_cursor_blink; + } + + m_attr_frame++; + if (m_attr_frame == (m_b << 1)) + { + 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()); +} - m_write_hrtc(param); - m_hrtc = param; +TIMER_CALLBACK_MEMBER(upd3301_device::hrtc_update) +{ + LOGHRTC("HRTC: %u at beam %d x %d\n", param, screen().hpos(), screen().vpos()); - update_hrtc_timer(param); - break; + m_write_hrtc(param); + m_hrtc = param; - case TIMER_VRTC: - LOG("UPD3301 VRTC: %u\n", param); + update_hrtc_timer(param); +} - m_write_vrtc(param); - m_vrtc = param; +TIMER_CALLBACK_MEMBER(upd3301_device::vrtc_update) +{ + LOGINT("VRTC: %u at y %d\n", param, screen().vpos()); - update_vrtc_timer(param); - if(!(m_status & STATUS_VE)) - break; + m_write_vrtc(param); + m_vrtc = param; - if (param && !m_me) - { - m_status |= STATUS_E; - set_interrupt(1); - } - else if(!param) - set_drq(1); - break; + update_vrtc_timer(param); + if (!get_display_status()) + return; - case TIMER_DRQ: - break; + if (!param) + { + reset_fifo_vrtc(); + set_drq(1); + } + + if (param && !m_me) + { + m_status |= STATUS_E; + set_interrupt(1); } } @@ -232,19 +292,22 @@ void upd3301_device::device_timer(emu_timer &timer, device_timer_id id, int para // read - //------------------------------------------------- -READ8_MEMBER( upd3301_device::read ) +uint8_t upd3301_device::read(offs_t offset) { uint8_t data = 0; switch (offset & 0x01) { - case 0: // data - break; + case 0: // data + if (!machine().side_effects_disabled()) + popmessage("light pen reading?"); + break; - case 1: // status - data = m_status; - m_status &= ~(STATUS_LP | STATUS_E |STATUS_N | STATUS_U); - break; + case 1: // status + data = m_status; + if (!machine().side_effects_disabled()) + m_status &= ~(STATUS_LP | STATUS_E |STATUS_N | STATUS_U); + break; } return data; @@ -255,139 +318,196 @@ READ8_MEMBER( upd3301_device::read ) // write - //------------------------------------------------- -WRITE8_MEMBER( upd3301_device::write ) +void upd3301_device::write(offs_t offset, uint8_t data) { switch (offset & 0x01) { - case 0: // data - switch (m_mode) - { - case MODE_RESET: - switch (m_param_count) + case 0: // data + switch (m_mode) { - case 0: - m_dma_mode = BIT(data, 7); - m_h = (data & 0x7f) + 2; - LOG("UPD3301 DMA Mode: %s\n", m_dma_mode ? "character" : "burst"); - LOG("UPD3301 H: %u\n", m_h); + case MODE_RESET: + switch (m_param_count) + { + case 0: + m_dma_mode = BIT(data, 7); + // number of characters per line -2 + // TODO: doesn't seem to like anything beyond 80 + m_h = (data & 0x7f) + 2; + if (m_h > 80) + popmessage("Illegal width set %d", m_h); + LOGCMD("DMA Mode: %s\n", m_dma_mode ? "character" : "burst"); + LOGCMD("H: %u (characters per line)\n", m_h); + break; + + case 1: + // cursor/attribute blink rate + m_b = ((data >> 6) + 1) * 16; + // number of lines displayed -1 + // (or in other words, tilemap y size) + m_l = (data & 0x3f) + 1; + LOGCMD("B: %u (cursor blink rate)\n", m_b); + LOGCMD("L: %u (number of lines displayed)\n", m_l); + break; + + case 2: + // skip line (pseudo-interlace?) + m_s = BIT(data, 7); + if (m_s) + popmessage("skip line enable"); + // cursor mode + // (00) not blinking underline cursor + // (01) blinking underline cursor + // (10) not blinking solid cursor + // (11) blinking solid cursor + // NB: there must be at least 14 lines per char to make underline valid + m_c = (data >> 5) & 0x03; + if (m_c != 3) + popmessage("cursor mode %02x", m_c); + // Number of lines per character -1 + m_r = (data & 0x1f) + 1; + 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: + // vblank lines -1 (1 to 8) + m_v = (data >> 5) + 1; + // hblank width -2 (6 to 33) + m_z = (data & 0x1f) + 2; + LOGCMD("V: %u (vblank lines)\n", m_v); + LOGCMD("Z: %u (hblank width)\n", m_z); + recompute_parameters(); + break; + + case 4: + // AT|SC + // (00|0) transparent b&w with special control character + // (00|1) no attributes, no special control + // (01|0) transparent color + // (10|0) non-transparent b&w, special control + // (10|1) non-transparent b&w, no special control + // any other setting are invalid + //m_at1 = BIT(data, 7); + //m_at0 = BIT(data, 6); + //m_sc = BIT(data, 5); + m_gfx_mode = (data & 0xe0) >> 5; + 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 + // 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; + } + + m_param_count++; break; - case 1: - m_b = ((data >> 6) + 1) * 16; - m_l = (data & 0x3f) + 1; - LOG("UPD3301 B: %u\n", m_b); - LOG("UPD3301 L: %u\n", m_l); - break; + case MODE_LOAD_CURSOR_POSITION: + switch (m_param_count) + { + case 0: + m_cx = data & 0x7f; + LOGCURSOR("CX: %u\n", m_cx); + break; - case 2: - m_s = BIT(data, 7); - m_c = (data >> 4) & 0x03; - 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); - break; + case 1: + m_cy = data & 0x3f; + LOGCURSOR("CY: %u\n", m_cy); - case 3: - m_v = (data >> 5) + 1; - m_z = (data & 0x1f) + 2; - LOG("UPD3301 V: %u\n", m_v); - LOG("UPD3301 Z: %u\n", m_z); - recompute_parameters(); - break; + m_mode = MODE_NONE; + break; + } - case 4: - m_at1 = BIT(data, 7); - m_at0 = BIT(data, 6); - m_sc = BIT(data, 5); - m_attr = (data & 0x1f) + 1; - LOG("UPD3301 AT1: %u\n", m_at1); - LOG("UPD3301 AT0: %u\n", m_at0); - LOG("UPD3301 SC: %u\n", m_sc); - LOG("UPD3301 ATTR: %u\n", m_attr); - - m_mode = MODE_NONE; + m_param_count++; break; - } - m_param_count++; + default: + LOGWARN("Invalid Parameter Byte %02x!\n", data); + } break; - case MODE_LOAD_CURSOR_POSITION: - switch (m_param_count) + case 1: // command + m_mode = MODE_NONE; + m_param_count = 0; + + switch (data & 0xe0) { - case 0: - m_cx = data & 0x7f; - LOG("UPD3301 CX: %u\n", m_cx); + case COMMAND_RESET: + LOGCMD("Reset\n"); + m_mode = MODE_RESET; + // 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 1: - m_cy = data & 0x3f; - LOG("UPD3301 CY: %u\n", m_cy); - - m_mode = MODE_NONE; + case COMMAND_START_DISPLAY: + { + 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.isunset()) + m_write_rvv(m_reverse_display); + else if (m_reverse_display == true) + logerror("%s: RVV reverse display enabled (warning)\n", machine().describe_context()); + } + set_display(1); + reset_counters(); break; } - m_param_count++; - break; - - default: - LOG("UPD3301 Invalid Parameter Byte %02x!\n", data); - } - break; - - case 1: // command - m_mode = MODE_NONE; - m_param_count = 0; - - switch (data & 0xe0) - { - case COMMAND_RESET: - LOG("UPD3301 Reset\n"); - m_mode = MODE_RESET; - set_display(0); - set_interrupt(0); - break; - - case COMMAND_START_DISPLAY: - LOG("UPD3301 Start Display\n"); - set_display(1); - reset_counters(); - break; - - case COMMAND_SET_INTERRUPT_MASK: - LOG("UPD3301 Set Interrupt Mask\n"); - m_me = BIT(data, 0); - m_mn = BIT(data, 1); - LOG("UPD3301 ME: %u\n", m_me); - LOG("UPD3301 MN: %u\n", m_mn); - break; + case COMMAND_SET_INTERRUPT_MASK: + LOGCMD("Set Interrupt Mask\n"); + // vblank irq mask + m_me = BIT(data, 0); + // special control character irq mask + m_mn = BIT(data, 1); + // 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"); - m_mode = MODE_READ_LIGHT_PEN; - break; + case COMMAND_READ_LIGHT_PEN: + 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"); - m_mode = MODE_LOAD_CURSOR_POSITION; - m_cm = BIT(data, 0); - LOG("UPD3301 CM: %u\n", m_cm); - break; + case COMMAND_LOAD_CURSOR_POSITION: + LOGCURSOR("Load Cursor Position\n"); + m_mode = MODE_LOAD_CURSOR_POSITION; + // (1) show cursor (0) disable cursor + m_cm = BIT(data, 0); + LOGCURSOR("CM: %u\n", m_cm); + break; - case COMMAND_RESET_INTERRUPT: - LOG("UPD3301 Reset Interrupt\n"); - set_interrupt(0); - break; + case COMMAND_RESET_INTERRUPT: + LOGCMD("Reset Interrupt\n"); + set_interrupt(0); + break; - case COMMAND_RESET_COUNTERS: - LOG("UPD3301 Reset Counters\n"); - m_mode = MODE_RESET_COUNTERS; - reset_counters(); + case COMMAND_RESET_COUNTERS: + LOGCMD("Reset Counters\n"); + m_mode = MODE_RESET_COUNTERS; + reset_counters(); + break; + } break; - } - break; } } @@ -396,8 +516,9 @@ WRITE8_MEMBER( upd3301_device::write ) // dack_w - //------------------------------------------------- -WRITE8_MEMBER( upd3301_device::dack_w ) +void upd3301_device::dack_w(uint8_t data) { + // TODO: underrun condition if (m_y >= (m_l * m_r)) { return; @@ -405,17 +526,21 @@ WRITE8_MEMBER( upd3301_device::dack_w ) if (m_data_fifo_pos < m_h) { - m_data_fifo[m_data_fifo_pos][m_input_fifo] = data; + m_data_fifo[m_input_fifo][m_data_fifo_pos] = data; m_data_fifo_pos++; } else { - m_attr_fifo[m_attr_fifo_pos][m_input_fifo] = data; + m_attr_fifo[m_input_fifo][m_attr_fifo_pos] = data; m_attr_fifo_pos++; } if ((m_data_fifo_pos == m_h) && (m_attr_fifo_pos == (m_attr << 1))) { + const u8 attr_max_size = 80; + // 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; m_input_fifo = !m_input_fifo; m_data_fifo_pos = 0; @@ -423,7 +548,7 @@ WRITE8_MEMBER( upd3301_device::dack_w ) draw_scanline(); - if (m_y == (m_l * m_r)) + if (m_y >= (m_l * m_r)) { // end DMA transfer set_drq(0); @@ -436,26 +561,26 @@ WRITE8_MEMBER( upd3301_device::dack_w ) // lpen_w - //------------------------------------------------- -WRITE_LINE_MEMBER( upd3301_device::lpen_w ) +void upd3301_device::lpen_w(int state) { } //------------------------------------------------- -// hrtc_r - +// hrtc_r - Horizontal ReTraCe //------------------------------------------------- -READ_LINE_MEMBER( upd3301_device::hrtc_r ) +int upd3301_device::hrtc_r() { return m_hrtc; } //------------------------------------------------- -// vrtc_r - +// vrtc_r - Vertical ReTraCe //------------------------------------------------- -READ_LINE_MEMBER( upd3301_device::vrtc_r ) +int upd3301_device::vrtc_r() { return m_vrtc; } @@ -465,27 +590,102 @@ READ_LINE_MEMBER( upd3301_device::vrtc_r ) // draw_scanline - //------------------------------------------------- +UPD3301_FETCH_ATTRIBUTE( upd3301_device::default_attr_fetch ) +{ + const u8 attr_max_size = 80; + 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; + } + + 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 + 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; + + 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; + + if (attr_end == attr_max_size) + break; + } + + return attr_extend_info; +} + void upd3301_device::draw_scanline() { + // Olympia Boss never bothers in writing a correct attribute table for rows on resident OS, + // it just extends the full attribute RAM with a start: 0 end: 0xff value: 0. + // According to doc notes anything beyond width 80 is puked by the CRTC, therefore we clamp. + const u8 attr_max_size = 80; + const std::array<u8, 41> attr_fifo = m_attr_fifo[!m_input_fifo]; + + // expose attribute handling to our client + // PC-8801 schematics definitely shows extra TTL connections for handling its "8 to 16-bit" attribute conversion. + // It also practically needs to read the attribute mapping for various extra side-effects such as colorized 400 line 1bpp + // cfr. "その他 / other" section at http://mydocuments.g2.xrea.com/html/p8/vraminfo.html + std::array<u16, attr_max_size> extend_attr = m_attr_fetch_cb(attr_fifo, m_gfx_mode, m_y, m_attr << 1, m_h); + for (int lc = 0; lc < m_r; lc++) { + bool is_lowestline = lc == m_r - 1; for (int sx = 0; sx < m_h; sx++) { int y = m_y + lc; - uint8_t cc = m_data_fifo[sx][!m_input_fifo]; - int hlgt = 0; // TODO - int rvv = 0; // TODO - int vsp = 0; // TODO - int sl0 = 0; // TODO - int sl12 = 0; // TODO + uint8_t cc = m_data_fifo[!m_input_fifo][sx]; int csr = m_cm && m_cursor_blink && ((y / m_r) == m_cy) && (sx == m_cx); - int gpa = 0; // TODO - m_display_cb(m_bitmap, y, sx, cc, lc, hlgt, rvv, vsp, sl0, sl12, csr, gpa); + // datasheet mentions these but I find zero unambiguous information for PC-8001/PC-8801, i.e.: + // - "highlight" should be attribute blinking? + // - is "gpa" actually NEC-ese for attr bus? + // - is sl0 / sl12 NEC names for upper/lower line? +// int hlgt = 0; +// int rvv = 0; +// int vsp = 0; +// int sl0 = 0; +// int sl12 = 0; +// int gpa = 0; + +// m_display_cb(m_bitmap, y, sx, cc, lc, hlgt, rvv, vsp, sl0, sl12, csr, gpa); + m_display_cb(m_bitmap, y, sx, cc, lc, csr, m_attr_blink, extend_attr[sx], m_gfx_mode, is_lowestline); } } - 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; } @@ -495,33 +695,14 @@ void upd3301_device::draw_scanline() uint32_t upd3301_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) { - if (m_status & STATUS_VE) - { - m_y = 0; - m_data_fifo_pos = 0; - m_attr_fifo_pos = 0; + // TODO: non-transparent modes + bitmap.fill(0, cliprect); - m_cursor_frame++; + if (!get_display_status()) + return 0; - if (m_cursor_frame == m_b) - { - m_cursor_frame = 0; - m_cursor_blink = !m_cursor_blink; - } - - m_attr_frame++; + copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect); - if (m_attr_frame == (m_b << 1)) - { - m_attr_frame = 0; - m_attr_blink = !m_attr_blink; - } - copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect); - } - else - { - bitmap.fill(rgb_t(0x00,0x00,0x00), cliprect); - } return 0; } @@ -532,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); @@ -549,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); } @@ -559,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) @@ -606,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); } @@ -629,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); |