summaryrefslogblamecommitdiffstatshomepage
path: root/src/mess/video/zx.c
blob: fdd9189eb7d0063ebe1256d4ec6bbd523a8e40e2 (plain) (tree)



















                                                                             
 


















                                                                                       


























































                                                                                                      
                                           
 
          


                                                                  
                                                         



                                                     

                                                                              
                          

                                                                                                                                                                                         

                                             

 
                                           
 
          



                                                                     
                             
         
                                                                                                                                                                                             
 
                                     
                                                        


         
                                                                           
 
                                  
                                              

                               
                                                 
         
                                                
                                    
                                                               


                                          
                                                           
                    
                                                           
 
                                        
 
                                                                                   
                 

                                                 

                 
                                      
 
                                   
 
                                                                                                      
                 

                                                            
                 

                                                                           
 
                                                                                                      
                                   
 
                                                               

                      
                                                                                                     
                 
                                                         










                                                                                    
                                                       

                 
                                               


         
                            
 
                                               
                             
                                                   

 
                                                               

                      
                  
         
                                                             

                                            







                                                                                                      
/***************************************************************************
    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"


void zx_state::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
	switch (id)
	{
	case TIMER_TAPE_PULSE:
		zx_tape_pulse(ptr, param);
		break;
	case TIMER_ULA_NMI:
		zx_ula_nmi(ptr, param);
		break;
	case TIMER_ULA_IRQ:
		zx_ula_irq(ptr, param);
		break;
	default:
		assert_always(FALSE, "Unknown id in zx_state::device_timer");
	}
}


/*
 * 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
 */
TIMER_CALLBACK_MEMBER(zx_state::zx_ula_nmi)
{
	/*
	 * 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 = m_bitmap;
	r.set(r1.min_x, r1.max_x, m_ula_scanline_count, 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) m_maincpu->state_int(Z80_R), (unsigned) m_maincpu->state_int(Z80_PC));
	m_maincpu->set_input_line(INPUT_LINE_NMI, PULSE_LINE);
	if (++m_ula_scanline_count == height)
		m_ula_scanline_count = 0;
}

TIMER_CALLBACK_MEMBER(zx_state::zx_ula_irq)
{
	/*
	 * 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 (m_ula_irq_active)
	{
//      logerror("ULA %3d[%d] IRQ, R:$%02X, $%04x\n", machine().primary_screen->vpos(), ula_scancode_count, (unsigned) m_maincpu->state_int(Z80_R), (unsigned) m_maincpu->state_int(Z80_PC));

		m_ula_irq_active = 0;
		m_maincpu->set_input_line(0, HOLD_LINE);
	}
}

void zx_state::zx_ula_r(int offs, memory_region *region, const UINT8 param)
{
	int offs0 = offs & 0x7fff;
	UINT8 *rom = m_region_maincpu->base();
	UINT8 chr = rom[offs0];

	if ((!m_ula_irq_active) && (chr == 0x76))
	{
		bitmap_ind16 &bitmap = m_bitmap;
		UINT16 y, *scanline;
		UINT16 ireg = m_maincpu->state_int(Z80_I) << 8;
		UINT8 data, *chrgen, creg;

		if (param)
			creg = m_maincpu->state_int(Z80_B);
		else
			creg = m_maincpu->state_int(Z80_C);

		chrgen = region->base();

		if ((++m_ula_scanline_count == m_screen->height()) || (creg == 32))
		{
			m_ula_scanline_count = 0;
			m_offs1 = offs0;
		}

		m_ula_frame_vsync = 3;

		m_charline_ptr = 0;

		for (y = m_offs1+1; ((y < offs0) && (m_charline_ptr < ARRAY_LENGTH(m_charline))); y++)
		{
			m_charline[m_charline_ptr] = rom[y];
			m_charline_ptr++;
		}
		for (y = m_charline_ptr; y < ARRAY_LENGTH(m_charline); y++)
			m_charline[y] = 0;

		timer_set(m_maincpu->cycles_to_attotime(((32 - m_charline_ptr) << 2)), TIMER_ULA_IRQ);
		m_ula_irq_active++;

		scanline = &bitmap.pix16(m_ula_scanline_count);
		y = 0;

		for (m_charline_ptr = 0; m_charline_ptr < ARRAY_LENGTH(m_charline); m_charline_ptr++)
		{
			chr = m_charline[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;
			m_charline[m_charline_ptr] = 0;
		}

		if (creg == 1) m_offs1 = offs0;
	}
}

void zx_state::video_start()
{
	m_ula_nmi = timer_alloc(TIMER_ULA_NMI);
	m_ula_irq_active = 0;
	m_screen->register_screen_bitmap(m_bitmap);
}

void zx_state::screen_eof_zx(screen_device &screen, bool state)
{
	// rising edge
	if (state)
	{
		/* decrement video synchronization counter */
		if (m_ula_frame_vsync)
			--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;
}