summaryrefslogblamecommitdiffstatshomepage
path: root/src/devices/video/tms3556.cpp
blob: 3088e02e6857f7888762f51812ac4cd28e8eab89 (plain) (tree)
1
2
                       
                                  















                                                                              

                   


                     

 



                                                                            

















                                             
 

 




                                                                            
                                                                                       


                      

                                              

                                          
 





                                                   
                                                                                        
 
                                    

                                                  










                                                                            
                                                       
 
                                                 






                                                   
                                                                   
 
                                                 










                                                                            
                                                                                                               
                                                        
                                                        
                                                       
                                                                                                                                      
                                    






                                             

                              

                                 



                                      
                                           












                                                   

                                


                                           
                                           

                                      








                                             
                                                  





                                                                                                  
                                                                                                              









                                                           
                                
 
                    


















                                                                 

                                    

         

                                           



                                                                           

                                         



                                                                       

                   





                                                   
                                         
 




















                                                                   
 

                                               






                                                   
                               
 




                                                                             






                                                   
                                        
 
                                                              
 
                         

                                      















                                                                                 
                        

                                                        
                 









                                                                            
         
 
 
                                                                            
                                                                            
                                                                            
 
                                   
 



                                                 
                    












                                                   
                                                  
 
              
 
                                         











                                                   
                                                        

                                           
                        









                                                             
                                            
















                                                                                                           
                                                














                                                
                                                        
                            
                                       




                                                                                                                       
                                       









                                                                                                                                            
                                      

                                                  
                                                                            








                                              
                                      




                                                                      
                                                                            










                                                             
                                             














                                                    
                                                          






                                         
                                            











                                                                    
                                                                                                               










                                      
                                             










                                                   
                                                 











                                                   
                                                   










                                                                              
                                                  

                      
                             





                                                                                      
                           




















                                                                                              
                         
                           


                                 
                                      

        
                                     

                                        


                                 
                                                                


                                                 
                                                                             





                                               
                              

                                            
                               

                                           
                                 

                                             
                                





                                            
         
                                                                       

                                                                                
         

















                                                   
                                                                      

















                                                        
                                

                                       
                                                                  


                                         
                                                             







                                                        
// license:BSD-3-Clause
// copyright-holders:Raphael Nabet
/*
    tms3556 emulation

    TODO:
    * implement remaining flags in control registers
    * test the whole thing
    * find the bloody tms3556 manual.  I mean the register and VRAM interfaces
      are mostly guesswork full of hacks, and I'd like to compare it with
      documentation.

    Raphael Nabet, 2004
*/

#include "emu.h"
#include "tms3556.h"

#include "screen.h"

//#define VERBOSE 1
#include "logmacro.h"



//**************************************************************************
//  MACROS / CONSTANTS
//**************************************************************************

#define VDP_POINTER m_control_regs[0]
#define VDP_COL     m_control_regs[1]
#define VDP_ROW     m_control_regs[2]
#define VDP_STAT    m_control_regs[3]
#define VDP_CM1     m_control_regs[4]
#define VDP_CM2     m_control_regs[5]
#define VDP_CM3     m_control_regs[6]
#define VDP_CM4     m_control_regs[7]
#define VDP_BAMT    m_address_regs[0]
#define VDP_BAMP    m_address_regs[1]
#define VDP_BAPA    m_address_regs[2]
#define VDP_BAGC0   m_address_regs[3]
#define VDP_BAGC1   m_address_regs[4]
#define VDP_BAGC2   m_address_regs[5]
#define VDP_BAGC3   m_address_regs[6]
#define VDP_BAMTF   m_address_regs[7]

ALLOW_SAVE_TYPE(tms3556_device::dma_mode_tt);



//**************************************************************************
//  GLOBAL VARIABLES
//**************************************************************************

// devices
DEFINE_DEVICE_TYPE(TMS3556, tms3556_device, "tms3556", "Texas Instruments TMS3556 VDP")


// default address map
void tms3556_device::tms3556(address_map &map)
{
	if (!has_configured_map(0))
		map(0x0000, 0xffff).ram();
}

//-------------------------------------------------
//  memory_space_config - return a description of
//  any address spaces owned by this device
//-------------------------------------------------

device_memory_interface::space_config_vector tms3556_device::memory_space_config() const
{
	return space_config_vector {
		std::make_pair(0, &m_space_config)
	};
}


//**************************************************************************
//  INLINE HELPERS
//**************************************************************************

//-------------------------------------------------
//  readbyte - read a byte at the given address
//-------------------------------------------------

inline uint8_t tms3556_device::readbyte(offs_t address)
{
	return space().read_byte(address&0xFFFF);
}


//-------------------------------------------------
//  writebyte - write a byte at the given address
//-------------------------------------------------

inline void tms3556_device::writebyte(offs_t address, uint8_t data)
{
	space().write_byte(address&0xFFFF, data);
}


//**************************************************************************
//  LIVE DEVICE
//**************************************************************************

//-------------------------------------------------
//  tms3556_device - constructor
//-------------------------------------------------

tms3556_device::tms3556_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, TMS3556, tag, owner, clock),
		device_memory_interface(mconfig, *this),
		device_video_interface(mconfig, *this),
		m_space_config("videoram", ENDIANNESS_LITTLE, 8, 17, 0, address_map_constructor(FUNC(tms3556_device::tms3556), this)),
		m_reg(0), m_reg2(0),
		m_row_col_written(0),
		m_bamp_written(0),
		m_colrow(0),
		m_vdp_acmpxy_mode(dma_write),
		m_vdp_acmpxy(0),
		m_vdp_acmp(0),
		m_init_read(0),
		m_scanline(0),
		m_blink(0),
		m_blink_count(0),
		m_bg_color(0)
{
	for (int i = 0; i < 8; i++)
	{
		m_control_regs[i] = 0;
		m_address_regs[i] = 0xFFFF;
	}
}


//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void tms3556_device::device_start()
{
	// register for state saving
	save_item(NAME(m_control_regs));
	save_item(NAME(m_address_regs));
	save_item(NAME(m_reg));
	save_item(NAME(m_reg2));
	save_item(NAME(m_row_col_written));
	save_item(NAME(m_bamp_written));
	save_item(NAME(m_colrow));
	save_item(NAME(m_vdp_acmpxy_mode));
	save_item(NAME(m_vdp_acmpxy));
	save_item(NAME(m_vdp_acmp));
	save_item(NAME(m_scanline));
	save_item(NAME(m_blink));
	save_item(NAME(m_blink_count));
	save_item(NAME(m_bg_color));
	save_item(NAME(m_name_offset));
	save_item(NAME(m_cg_flag));
	save_item(NAME(m_char_line_counter));
	save_item(NAME(m_dbl_h_phase));

	screen().register_screen_bitmap(m_bitmap);
}


/*static const char *const tms3556_mode_names[] = { "DISPLAY OFF", "TEXT", "GRAPHIC", "MIXED" };*/


uint32_t tms3556_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
	return 0;
}


//-------------------------------------------------
//  vram_r - VRAM read
//-------------------------------------------------

uint8_t tms3556_device::vram_r()
{
	uint8_t ret;
	if (!machine().side_effects_disabled()) {
		if (m_bamp_written) {
			m_bamp_written=false;
			m_vdp_acmpxy_mode=dma_write;
			if (m_init_read)
				m_vdp_acmp=VDP_BAMP;
			else
				m_vdp_acmp=(VDP_BAMP-1)&0xFFFF;
		}

		if (m_row_col_written) {
			m_row_col_written=0;
			m_vdp_acmpxy_mode=dma_read;
			if (m_init_read)
				m_vdp_acmpxy=m_colrow;
			else
				m_vdp_acmpxy=(m_colrow-1)&0xFFFF;
		}

		m_init_read = false;
		m_reg = 0;
	}

	if (m_vdp_acmpxy_mode==dma_read) {
		ret=readbyte(m_vdp_acmpxy);
		if (!machine().side_effects_disabled()) {
			m_vdp_acmpxy++;
			if (m_vdp_acmpxy==VDP_BAMTF) m_vdp_acmpxy=VDP_BAMP;
		}
	} else {
		ret=readbyte(m_vdp_acmp);
		if (!machine().side_effects_disabled()) {
			m_vdp_acmp++;
			if (m_vdp_acmp==VDP_BAMTF) m_vdp_acmp=VDP_BAMP;
		}
	}
	return ret;
}

//-------------------------------------------------
//  vram_w - VRAM write
//-------------------------------------------------

void tms3556_device::vram_w(uint8_t data)
{
	if (m_bamp_written) {
		m_bamp_written=false;
		m_vdp_acmpxy_mode=dma_read;
		m_vdp_acmp=VDP_BAMP;
	}

	if (m_row_col_written) {
		m_row_col_written=0;
		m_vdp_acmpxy_mode=dma_write;
		m_vdp_acmpxy=m_colrow;
	}

	if (m_vdp_acmpxy_mode==dma_write) {
		writebyte(m_vdp_acmpxy,data);
		m_vdp_acmpxy++;
		if (m_vdp_acmpxy==VDP_BAMTF) m_vdp_acmpxy=VDP_BAMP;
	} else {
		writebyte(m_vdp_acmp,data);
		m_vdp_acmp++;
		if (m_vdp_acmp==VDP_BAMTF) m_vdp_acmp=VDP_BAMP;
	}

	if (!machine().side_effects_disabled())
		m_reg = 0;
}


//-------------------------------------------------
//  reg_r - read from register port
//-------------------------------------------------

uint8_t tms3556_device::reg_r()
{
	uint8_t reply = 0; // FIXME : will send internal status (VBL, HBL...)
	if (!machine().side_effects_disabled()) {
		LOG("TMS3556 Register %X Read: %02X\n", m_reg, reply);
		m_reg = m_reg2;
	}
	return reply;
}

//-------------------------------------------------
//  reg_w - write to register port
//-------------------------------------------------

void tms3556_device::reg_w(uint8_t data)
{
	LOG("TMS3556 Register %X Write: %02X\n", m_reg, data);

	if (m_reg == 0) {
		m_reg=data&0x0F;
		m_reg2=(data&0xF0)>>4;
	} else if (m_reg<8) {
		m_control_regs[m_reg]=data;
		// leve un flag si le dernier registre ecrit est row ou col
		if ((m_reg==2) || (m_reg==1)) {
			m_colrow=(m_control_regs[2]<<8)|m_control_regs[1];
			m_row_col_written=true;
		}

		if (!machine().side_effects_disabled())
			m_reg = m_reg2;
	} else {
		m_address_regs[m_reg-8]=(m_control_regs[2]<<8)|m_control_regs[1];
		// cas speciaux de decalage pour les generateurs
		if ((m_reg>=0xB) && (m_reg<=0xE)) {
			m_address_regs[m_reg-8]+=2;
			m_address_regs[m_reg-8]&=0xFFFF;
		} else {
			m_address_regs[m_reg-8]+=1;
			m_address_regs[m_reg-8]&=0xFFFF;
		}
		if (m_reg==9) {
			m_row_col_written=false;
			m_bamp_written=true;
		} else {
			m_row_col_written=0;
			m_bamp_written=false;
		}
		//logerror("VDP16[%d] = x%x",m_reg,m_address_regs[m_reg-8]);
		if (!machine().side_effects_disabled())
			m_reg = m_reg2;
	}
}

//--------------------------------------------------------------------------
//  initptr_r - set VDP in read mode (not exactly on the VDP but on the TAL)
//--------------------------------------------------------------------------

uint8_t tms3556_device::initptr_r()
{
	if (!machine().side_effects_disabled()) {
		m_init_read = true;
		m_reg = 0;
	}
	return 0xff;
}


//-------------------------------------------------
//  redraw code
//-------------------------------------------------


//-------------------------------------------------
//  draw_line_empty - draw an empty line (used for
//  top and bottom borders, and screen off mode)
//-------------------------------------------------

void tms3556_device::draw_line_empty(uint16_t *ln)
{
	int i;

	for (i = 0; i < TOTAL_WIDTH; i++)
#if TMS3556_DOUBLE_WIDTH
		*ln++ = m_bg_color;
#endif
		*ln++ = m_bg_color;
}


//-------------------------------------------------
//  draw_line_text_common - draw a line of text
//  (called by draw_line_text and draw_line_mixed)
//-------------------------------------------------

void tms3556_device::draw_line_text_common(uint16_t *ln)
{
	int pattern, x, xx, i, name_offset;
	uint16_t fg, bg;
	offs_t nametbl_base;
	offs_t patterntbl_base[4];
	int name_hi, name_lo;
	int pattern_ix;
	int alphanumeric_mode, dbl_w, dbl_h, dbl_w_phase = 0;

	nametbl_base = m_address_regs[2];
	for (i = 0; i < 4; i++)
		patterntbl_base[i] = m_address_regs[i + 3];

	for (xx = 0; xx < LEFT_BORDER; xx++)
#if TMS3556_DOUBLE_WIDTH
		*ln++ = m_bg_color;
#endif
		*ln++ = m_bg_color;

	name_offset = m_name_offset;

	for (x = 0; x < 40; x++)
	{
		name_hi = readbyte(nametbl_base + name_offset);
		name_lo = readbyte(nametbl_base + name_offset + 1);
		pattern_ix = ((name_hi >> 2) & 2) | ((name_hi >> 4) & 1);
		alphanumeric_mode = (pattern_ix < 2) || ((pattern_ix == 3) && !(m_control_regs[7] & 0x08));
		fg = (name_hi >> 5) & 0x7;
		if (alphanumeric_mode)
		{
			if (name_hi & 4)
			{   /* inverted color */
				bg = fg;
				fg = m_bg_color;
			}
			else
				bg = m_bg_color;
			dbl_w = name_hi & 0x2;
			dbl_h = name_hi & 0x1;
		}
		else
		{
			bg = name_hi & 0x7;
			dbl_w = 0;
			dbl_h = 0;
		}
		if ((name_lo & 0x80) && m_blink)
			fg = bg;    /* blink off time */
		if (! dbl_h)
		{   /* single height */
			pattern = readbyte(patterntbl_base[pattern_ix] + (name_lo & 0x7f) + 128 * m_char_line_counter);
			if (m_char_line_counter == 0)
				m_dbl_h_phase[x] = 0;
		}
		else
		{   /* double height */
			if (! m_dbl_h_phase[x])
				/* first phase: pattern from upper half */
				pattern = readbyte(patterntbl_base[pattern_ix] + (name_lo & 0x7f) + 128 * (5 + (m_char_line_counter >> 1)));
			else
				/* second phase: pattern from lower half */
				pattern = readbyte(patterntbl_base[pattern_ix] + (name_lo & 0x7f) + 128 * (m_char_line_counter >> 1));
			if (m_char_line_counter == 0)
				m_dbl_h_phase[x] = !m_dbl_h_phase[x];
		}
		if (!dbl_w)
		{   /* single width */
			for (xx = 0; xx < 8; xx++)
			{
				uint16_t color = (pattern & 0x80) ? fg : bg;
#if TMS3556_DOUBLE_WIDTH
				*ln++ = color;
#endif
				*ln++ = color;
				pattern <<= 1;
			}
			dbl_w_phase = 0;
		}
		else
		{   /* double width */
			if (dbl_w_phase)
				/* second phase: display right half */
				pattern <<= 4;
			for (xx = 0; xx < 4; xx++)
			{
				uint16_t color = (pattern & 0x80) ? fg : bg;
#if TMS3556_DOUBLE_WIDTH
				*ln++ = color; *ln++ = color;
#endif
				*ln++ = color; *ln++ = color;
				pattern <<= 1;
			}
			dbl_w_phase = !dbl_w_phase;
		}
		name_offset += 2;
	}

	for (xx = 0; xx < RIGHT_BORDER; xx++)
#if TMS3556_DOUBLE_WIDTH
		*ln++ = m_bg_color;
#endif
		*ln++ = m_bg_color;

	if (m_char_line_counter == 0)
		m_name_offset = name_offset;
}


//-------------------------------------------------
//  draw_line_bitmap_common - draw a line of bitmap
//  (called by draw_line_bitmap and draw_line_mixed)
//-------------------------------------------------

void tms3556_device::draw_line_bitmap_common(uint16_t *ln)
{
	int x, xx;
	offs_t nametbl_base;
	int name_b, name_g, name_r;

	nametbl_base = m_address_regs[2];

	for (xx = 0; xx < LEFT_BORDER; xx++)
#if TMS3556_DOUBLE_WIDTH
		*ln++ = m_bg_color;
#endif
		*ln++ = m_bg_color;

	for (x = 0; x < 40; x++)
	{
		name_b = readbyte(nametbl_base + m_name_offset);
		name_g = readbyte(nametbl_base + m_name_offset + 1);
		name_r = readbyte(nametbl_base + m_name_offset + 2);
		for (xx = 0; xx < 8; xx++)
		{
			uint16_t color = ((name_b >> 5) & 0x4) | ((name_g >> 6) & 0x2) | ((name_r >> 7) & 0x1);
#if TMS3556_DOUBLE_WIDTH
			*ln++ = color;
#endif
			*ln++ = color;
			name_b <<= 1;
			name_g <<= 1;
			name_r <<= 1;
		}
		m_name_offset += 3;
	}

	for (xx = 0; xx < RIGHT_BORDER; xx++)
#if TMS3556_DOUBLE_WIDTH
		*ln++ = m_bg_color;
#endif
		*ln++ = m_bg_color;
}


//-------------------------------------------------
//  draw_line_text - draw a line in text mode
//-------------------------------------------------

void tms3556_device::draw_line_text(uint16_t *ln)
{
	if (m_char_line_counter == 0)
		m_char_line_counter = 10;
	m_char_line_counter--;
	draw_line_text_common(ln);
}


//-------------------------------------------------
//  draw_line_bitmap - draw a line in bitmap mode
//-------------------------------------------------

void tms3556_device::draw_line_bitmap(uint16_t *ln)
{
	draw_line_bitmap_common(ln);
	m_bg_color = (readbyte(m_address_regs[2] + m_name_offset) >> 5) & 0x7;
	m_name_offset += 2;
}


//-------------------------------------------------
//  draw_line_mixed - draw a line in mixed mode
//-------------------------------------------------

void tms3556_device::draw_line_mixed(uint16_t *ln)
{
	if (m_cg_flag)
	{   /* bitmap line */
		draw_line_bitmap_common(ln);
		m_bg_color = (readbyte(m_address_regs[2] + m_name_offset) >> 5) & 0x7;
		m_cg_flag = (readbyte(m_address_regs[2] + m_name_offset) >> 4) & 0x1;
		m_name_offset += 2;
	}
	else
	{   /* text line */
		if (m_char_line_counter == 0)
			m_char_line_counter = 10;
		m_char_line_counter--;
		draw_line_text_common(ln);
		if (m_char_line_counter == 0)
		{
			m_bg_color = (readbyte(m_address_regs[2] + m_name_offset) >> 5) & 0x7;
			m_cg_flag = (readbyte(m_address_regs[2] + m_name_offset) >> 4) & 0x1;
			m_name_offset += 2;
		}
	}
}


//-------------------------------------------------
//  draw_line - draw a line. If non-interlaced mode,
//  duplicate the line.
//-------------------------------------------------

void tms3556_device::draw_line(bitmap_ind16 &bmp, int line)
{
	int double_lines;
	uint16_t *ln, *ln2;

//  if (m_control_regs[4] & 0x??)
//  {   // interlaced mode
//      ln = &bmp->pix(line, m_field);
//  }
//  else
	{   /* non-interlaced mode */
		ln = &bmp.pix(line);
		ln2 = &bmp.pix(line, 1);
		double_lines = 1;
	}

	if ((line < TOP_BORDER) || (line >= (TOP_BORDER + 250)))
	{
		/* draw top and bottom borders */
		draw_line_empty(ln);
		m_cg_flag=0; // FIXME : forme text mode for 1st line in mixed
	}
	else
	{
		/* draw useful area */
		switch (m_control_regs[6] >> 6)
		{
		case MODE_OFF:
			draw_line_empty(ln);
			break;
		case MODE_TEXT:
			draw_line_text(ln);
			break;
		case MODE_BITMAP:
			draw_line_bitmap(ln);
			break;
		case MODE_MIXED:
			draw_line_mixed(ln);
			break;
		}
	}

	if (double_lines)
	{
		// TODO: this overlaps in exeltel - use memmove for now
		//memcpy(ln2, ln, TOTAL_WIDTH * (TMS3556_DOUBLE_WIDTH ? 2 : 1));
		memmove(ln2, ln, TOTAL_WIDTH * (TMS3556_DOUBLE_WIDTH ? 2 : 1));
	}
}


//-------------------------------------------------
//  interrupt_start_vblank - Do vblank-time tasks
//-------------------------------------------------

void tms3556_device::interrupt_start_vblank(void)
{
	/* at every frame, vdp switches fields */
	//m_field = !m_field;

	/* color blinking */
	if (m_blink_count)
		m_blink_count--;
	if (!m_blink_count)
	{
		m_blink = !m_blink;
		m_blink_count = 60; /*no idea what the real value is*/
	}
	/* reset background color */
	m_bg_color = (m_control_regs[7] >> 5) & 0x7;
	/* reset name offset */
	m_name_offset = 0;
	/* reset character line counter */
	m_char_line_counter = 0;
	/* reset c/g flag */
	m_cg_flag = 0;
	/* reset double height phase flags */
	memset(m_dbl_h_phase, 0, sizeof(m_dbl_h_phase));
}


//-------------------------------------------------
//  interrupt - scanline handler
//-------------------------------------------------

void tms3556_device::interrupt()
{
	/* check for start of vblank */
	if (m_scanline == 310)  /*no idea what the real value is*/
		interrupt_start_vblank();

	/* render the current line */
	if ((m_scanline >= 0) && (m_scanline < TOTAL_HEIGHT))
	{
		//if (!video_skip_this_frame())
			draw_line(m_bitmap, m_scanline);
	}

	if (++m_scanline == 313)
		m_scanline = 0;
}