summaryrefslogtreecommitdiffstatshomepage
path: root/src/mess/video/zx.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mess/video/zx.c')
-rw-r--r--src/mess/video/zx.c217
1 files changed, 217 insertions, 0 deletions
diff --git a/src/mess/video/zx.c b/src/mess/video/zx.c
new file mode 100644
index 00000000000..8edb75ba4f2
--- /dev/null
+++ b/src/mess/video/zx.c
@@ -0,0 +1,217 @@
+/***************************************************************************
+ zx.c
+
+ video hardware
+ Juergen Buchmueller <pullmoll@t-online.de>, Dec 1999
+
+ The ZX has a very unorthodox video RAM system. To start a scanline,
+ the CPU must jump to video RAM at 0xC000, which is a mirror of the
+ RAM at 0x4000. The video chip (ULA?) pulls a switcharoo and changes
+ the video bytes as they are fetched by the CPU.
+
+ The video chip draws the scanline until a HALT instruction (0x76) is
+ reached, which indicates no further video RAM for this scanline. Any
+ other video byte is used to generate a tile and at the same time,
+ appears to the CPU as a NOP (0x00) instruction.
+
+****************************************************************************/
+
+#include "includes/zx.h"
+
+emu_timer *ula_nmi = NULL;
+//emu_timer *ula_irq = NULL;
+//int ula_nmi_active;
+int ula_frame_vsync = 0;
+//int ula_scancode_count = 0;
+int ula_scanline_count = 0;
+
+/*
+ * Toggle the video output between black and white.
+ * This happens whenever the ULA scanline IRQs are enabled/disabled.
+ * Normally this is done during the synchronized zx_ula_r() function,
+ * which outputs 8 pixels per code, but if the video sync is off
+ * (during tape IO or sound output) zx_ula_bkgnd() is used to
+ * simulate the display of a ZX80/ZX81.
+ */
+void zx_state::zx_ula_bkgnd(UINT8 color)
+{
+ screen_device *screen = machine().first_screen();
+ int width = screen->width();
+ int height = screen->height();
+ const rectangle &visarea = screen->visible_area();
+
+ if (m_ula_frame_vsync == 0 && color != m_old_c)
+ {
+ int y, new_x, new_y;
+ rectangle r;
+ bitmap_ind16 &bitmap = m_bitmap;
+
+ new_y = machine().primary_screen->vpos();
+ new_x = machine().primary_screen->hpos();
+/* logerror("zx_ula_bkgnd: %3d,%3d - %3d,%3d\n", state->m_old_x, state->m_old_y, new_x, new_y);*/
+ y = m_old_y;
+ for (;;)
+ {
+ if (y == new_y)
+ {
+ r.set(m_old_x, new_x, y, y);
+ bitmap.fill(color, r);
+ break;
+ }
+ else
+ {
+ r.set(m_old_x, visarea.max_x, y, y);
+ bitmap.fill(color, r);
+ m_old_x = 0;
+ }
+ if (++y == height)
+ y = 0;
+ }
+ m_old_x = (new_x + 1) % width;
+ m_old_y = new_y;
+ m_old_c = color;
+ }
+}
+
+/*
+ * PAL: 310 total lines,
+ * 0.. 55 vblank
+ * 56..247 192 visible lines
+ * 248..303 vblank
+ * 304... vsync
+ * NTSC: 262 total lines
+ * 0.. 31 vblank
+ * 32..223 192 visible lines
+ * 224..233 vblank
+ */
+static TIMER_CALLBACK(zx_ula_nmi)
+{
+ zx_state *state = machine.driver_data<zx_state>();
+ /*
+ * An NMI is issued on the ZX81 every 64us for the blanked
+ * scanlines at the top and bottom of the display.
+ */
+ screen_device *screen = machine.first_screen();
+ int height = screen->height();
+ const rectangle& r1 = screen->visible_area();
+ rectangle r;
+
+ bitmap_ind16 &bitmap = state->m_bitmap;
+ r.set(r1.min_x, r1.max_x, state->m_ula_scanline_count, state->m_ula_scanline_count);
+ bitmap.fill(1, r);
+// logerror("ULA %3d[%d] NMI, R:$%02X, $%04x\n", machine.primary_screen->vpos(), ula_scancode_count, (unsigned) cpu_get_reg(machine.device("maincpu"), Z80_R), (unsigned) cpu_get_reg(machine.device("maincpu"), Z80_PC));
+ cputag_set_input_line(machine, "maincpu", INPUT_LINE_NMI, PULSE_LINE);
+ if (++state->m_ula_scanline_count == height)
+ state->m_ula_scanline_count = 0;
+}
+
+static TIMER_CALLBACK(zx_ula_irq)
+{
+ zx_state *state = machine.driver_data<zx_state>();
+
+ /*
+ * An IRQ is issued on the ZX80/81 whenever the R registers
+ * bit 6 goes low. In MESS this IRQ timed from the first read
+ * from the copy of the DFILE in the upper 32K in zx_ula_r().
+ */
+ if (state->m_ula_irq_active)
+ {
+// logerror("ULA %3d[%d] IRQ, R:$%02X, $%04x\n", machine.primary_screen->vpos(), ula_scancode_count, (unsigned) cpu_get_reg(machine.device("maincpu"), Z80_R), (unsigned) cpu_get_reg(machine.device("maincpu"), Z80_PC));
+
+ state->m_ula_irq_active = 0;
+ cputag_set_input_line(machine, "maincpu", 0, HOLD_LINE);
+ }
+}
+
+void zx_ula_r(running_machine &machine, int offs, const char *region, const UINT8 param)
+{
+ zx_state *state = machine.driver_data<zx_state>();
+ screen_device *screen = machine.first_screen();
+ int offs0 = offs & 0x7fff;
+ UINT8 *rom = machine.root_device().memregion("maincpu")->base();
+ UINT8 chr = rom[offs0];
+
+ if ((!state->m_ula_irq_active) && (chr == 0x76))
+ {
+ bitmap_ind16 &bitmap = state->m_bitmap;
+ UINT16 y, *scanline;
+ UINT16 ireg = cpu_get_reg(machine.device("maincpu"), Z80_I) << 8;
+ UINT8 data, *chrgen, creg;
+
+ if (param)
+ creg = cpu_get_reg(machine.device("maincpu"), Z80_B);
+ else
+ creg = cpu_get_reg(machine.device("maincpu"), Z80_C);
+
+ chrgen = state->memregion(region)->base();
+
+ if ((++state->m_ula_scanline_count == screen->height()) || (creg == 32))
+ {
+ state->m_ula_scanline_count = 0;
+ state->m_offs1 = offs0;
+ }
+
+ state->m_ula_frame_vsync = 3;
+
+ state->m_charline_ptr = 0;
+
+ for (y = state->m_offs1+1; ((y < offs0) && (state->m_charline_ptr < ARRAY_LENGTH(state->m_charline))); y++)
+ {
+ state->m_charline[state->m_charline_ptr] = rom[y];
+ state->m_charline_ptr++;
+ }
+ for (y = state->m_charline_ptr; y < ARRAY_LENGTH(state->m_charline); y++)
+ state->m_charline[y] = 0;
+
+ machine.scheduler().timer_set(machine.device<cpu_device>("maincpu")->cycles_to_attotime(((32 - state->m_charline_ptr) << 2)), FUNC(zx_ula_irq));
+ state->m_ula_irq_active++;
+
+ scanline = &bitmap.pix16(state->m_ula_scanline_count);
+ y = 0;
+
+ for (state->m_charline_ptr = 0; state->m_charline_ptr < ARRAY_LENGTH(state->m_charline); state->m_charline_ptr++)
+ {
+ chr = state->m_charline[state->m_charline_ptr];
+ data = chrgen[ireg | ((chr & 0x3f) << 3) | ((8 - creg)&7) ];
+ if (chr & 0x80) data ^= 0xff;
+
+ scanline[y++] = (data >> 7) & 1;
+ scanline[y++] = (data >> 6) & 1;
+ scanline[y++] = (data >> 5) & 1;
+ scanline[y++] = (data >> 4) & 1;
+ scanline[y++] = (data >> 3) & 1;
+ scanline[y++] = (data >> 2) & 1;
+ scanline[y++] = (data >> 1) & 1;
+ scanline[y++] = (data >> 0) & 1;
+ state->m_charline[state->m_charline_ptr] = 0;
+ }
+
+ if (creg == 1) state->m_offs1 = offs0;
+ }
+}
+
+VIDEO_START( zx )
+{
+ zx_state *state = machine.driver_data<zx_state>();
+ state->m_ula_nmi = machine.scheduler().timer_alloc(FUNC(zx_ula_nmi));
+ state->m_ula_irq_active = 0;
+ machine.primary_screen->register_screen_bitmap(state->m_bitmap);
+}
+
+SCREEN_VBLANK( zx )
+{
+ // rising edge
+ if (vblank_on)
+ {
+ zx_state *state = screen.machine().driver_data<zx_state>();
+ /* decrement video synchronization counter */
+ if (state->m_ula_frame_vsync)
+ --state->m_ula_frame_vsync;
+ }
+}
+
+UINT32 zx_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
+ return 0;
+}