diff options
Diffstat (limited to 'src/mame/video/thomson.c')
-rw-r--r-- | src/mame/video/thomson.c | 1387 |
1 files changed, 1387 insertions, 0 deletions
diff --git a/src/mame/video/thomson.c b/src/mame/video/thomson.c new file mode 100644 index 00000000000..a258bb529da --- /dev/null +++ b/src/mame/video/thomson.c @@ -0,0 +1,1387 @@ +// license:BSD-3-Clause +// copyright-holders:Antoine Mine +/********************************************************************** + + Copyright (C) Antoine Mine' 2006 + + Thomson 8-bit computers + +**********************************************************************/ + +#include <math.h> +#include "includes/thomson.h" + + +#define VERBOSE 0 +#define LOG(x) do { if (VERBOSE) logerror x; } while (0) + + +/* One GPL is what is drawn in 1 us by the video system in the active window. + Most of the time, it corresponds to a 8-pixel wide horizontal span. + For some TO8/9/9+/MO6 modes, it can be 4-pixel or 16-pixel wide. + There are always 40 GPLs in an active row, and it is always defined by + two bytes in video memory (0x2000 bytes appart). +*/ + +#define THOM_GPL_PER_LINE 40 + + +/****************** dynamic screen size *****************/ + + +int thomson_state::thom_update_screen_size() +{ + const rectangle &visarea = m_screen->visible_area(); + UINT8 p = m_io_vconfig->read(); + int new_w, new_h, changed = 0; + + switch ( p & 3 ) + { + case 0: m_thom_bwidth = 56; m_thom_bheight = 47; break; /* as in original (?) */ + case 1: m_thom_bwidth = 16; m_thom_bheight = 16; break; /* small */ + default: m_thom_bwidth = 0; m_thom_bheight = 0; break; /* none */ + } + + switch ( p & 0xc ) + { + case 0: m_thom_hires = 0; break; /* low */ + case 4: m_thom_hires = 1; break; /* high */ + default: m_thom_hires = m_thom_hires_better; break; /* auto */ + } + + new_w = ( 320 + m_thom_bwidth * 2 ) * ( m_thom_hires + 1 ) - 1; + new_h = ( 200 + m_thom_bheight * 2 ) /** (m_thom_hires + 1 )*/ - 1; + if ( ( visarea.max_x != new_w ) || ( visarea.max_y != new_h ) ) + { + changed = 1; + m_screen->set_visible_area(0, new_w, 0, new_h ); + } + + return changed; +} + + + +/*********************** video timing ******************************/ + + +/* elapsed time from beginning of frame, in us */ +unsigned thomson_state::thom_video_elapsed() +{ + unsigned u; + attotime elapsed = m_thom_video_timer->elapsed(); + u = (elapsed * 1000000 ).seconds(); + if ( u >= 19968 ) + u = 19968; + return u; +} + + + +struct thom_vsignal thomson_state::thom_get_vsignal() +{ + struct thom_vsignal v; + int gpl = thom_video_elapsed() - 64 * THOM_BORDER_HEIGHT - 7; + if ( gpl < 0 ) + gpl += 19968; + + v.inil = ( gpl & 63 ) <= 40; + + v.init = gpl < (64 * THOM_ACTIVE_HEIGHT - 24); + + v.lt3 = ( gpl & 8 ) ? 1 : 0; + + v.line = gpl >> 6; + + v.count = v.line * 320 + ( gpl & 63 ) * 8; + + return v; +} + + + +/************************** lightpen *******************************/ + + + +void thomson_state::thom_get_lightpen_pos( int*x, int* y ) +{ + *x = m_io_lightpen_x->read(); + *y = m_io_lightpen_y->read(); + + if ( *x < 0 ) + *x = 0; + + if ( *y < 0 ) + *y = 0; + + if ( *x > 2 * m_thom_bwidth + 319 ) + *x = 2 * m_thom_bwidth + 319; + + if ( *y > 2 * m_thom_bheight + 199 ) + *y = 2 * m_thom_bheight + 199; +} + + + +struct thom_vsignal thomson_state::thom_get_lightpen_vsignal( int xdec, int ydec, int xdec2 ) +{ + struct thom_vsignal v; + int x, y; + int gpl; + + thom_get_lightpen_pos( &x, &y ); + x += xdec - m_thom_bwidth; + y += ydec - m_thom_bheight; + + gpl = (x >> 3) + y * 64; + if ( gpl < 0 ) + gpl += 19968; + + v.inil = (gpl & 63) <= 41; + + v.init = (gpl <= 64 * THOM_ACTIVE_HEIGHT - 24); + + v.lt3 = ( gpl & 8 ) ? 1 : 0; + + v.line = y; + + if ( v.inil && v.init ) + v.count = + ( gpl >> 6 ) * 320 + /* line */ + ( gpl & 63 ) * 8 + /* gpl inside line */ + ( (x + xdec2) & 7 ); /* pixel inside gpl */ + else + v.count = 0; + + return v; +} + + + +void thomson_state::thom_set_lightpen_callback( int nb ) +{ + LOG (( "%f thom_set_lightpen_callback called\n", machine().time().as_double())); + m_thom_lightpen_nb = nb; +} + +TIMER_CALLBACK_MEMBER( thomson_state::thom_lightpen_step ) +{ + int step = param; + + if ( m_thom_lightpen_cb ) + (this->*m_thom_lightpen_cb)( step ); + + if ( step < m_thom_lightpen_nb ) + m_thom_lightpen_timer->adjust(attotime::from_usec( 64 ), step + 1); +} + + + +/***************** scan-line oriented engine ***********************/ + +/* This code, common for all Thomson machines, emulates the TO8 + video hardware, with its 16-colors chosen among 4096, 9 video modes, + and 4 video pages. Moreover, it emulates changing the palette several times + per frame to simulate more than 16 colors per frame (and the same for mode + and page switchs) and cooper effects (distinguishing the left and right + border color of each row). + + TO7, TO7/70 and MO5 video hardware are much simpler (8 or 16 fixed colors, + one mode and one video page). Although the three are different, they can all + be emulated by the TO8 video hardware. + Thus, we use the same TO8-emulation code to deal with these simpler + hardware (although it is somewhat of an overkill). +*/ + + + +/* ---------------- state & state changes ---------------- */ + + +/* returns 1 if the mode is 640 pixel wide, 0 if it is 160 or 320 */ +int thomson_state::thom_mode_is_hires( int mode ) +{ + return ( mode == THOM_VMODE_80 ) || ( mode == THOM_VMODE_80_TO9 ); +} + + + +/* either the border index or its palette entry has changed */ +void thomson_state::thom_border_changed() +{ + unsigned l = thom_video_elapsed(); + unsigned y = l >> 6; + unsigned x = l & 63; + unsigned color = m_thom_pal[ m_thom_border_index ]; + + if ( y >= THOM_TOTAL_HEIGHT ) + { + /* end of page */ + m_thom_border_r[ THOM_TOTAL_HEIGHT ] = color; + } + else if ( ! x ) + { + /* start of line */ + m_thom_border_l[ y ] = color; + m_thom_border_r[ y ] = color; + } + else if ( x <= 19 ) + { + /* between left and right border */ + /* NOTE: this makes the lower right part of the color picker blink + in the TO8/TO9/TO9+, which actually happens on the real computer! + */ + m_thom_border_r[ y ] = color; + m_thom_border_l[ y + 1 ] = color; + } + else + { + /* end of line */ + m_thom_border_l[ y + 1 ] = color; + m_thom_border_r[ y + 1 ] = color; + } + m_thom_vstate_dirty = 1; +} + + + +/* the video mode or page has changed */ +void thomson_state::thom_gplinfo_changed() +{ + unsigned l = thom_video_elapsed() - THOM_BORDER_HEIGHT * 64 - 7; + unsigned y = l >> 6; + unsigned x = l & 63; + int modepage = ((int)m_thom_vmode << 8) | m_thom_vpage; + if ( y >= 200 || x>= 40 ) + m_thom_vmodepage[ 40 ] = modepage; + else + m_thom_vmodepage[ x ] = modepage; + m_thom_vmodepage_changed = 1; +} + + + +void thomson_state::thom_set_border_color( unsigned index ) +{ + assert( index < 16 ); + if ( index != m_thom_border_index ) + { + LOG (( "thom_set_border_color: %i at line %i col %i\n", index, thom_video_elapsed() / 64, thom_video_elapsed() % 64 )); + m_thom_border_index = index; + thom_border_changed(); + } +} + + + +void thomson_state::thom_set_palette( unsigned index, UINT16 color ) +{ + assert( index < 16 ); + + if ( color != 0x1000 ) + color &= 0xfff; + + if ( m_thom_pal[ index ] == color ) + return; + + LOG (( "thom_set_palette: %i to %03x at line %i col %i\n", index, color, thom_video_elapsed() / 64, thom_video_elapsed() % 64 )); + + m_thom_pal[ index ] = color; + if ( index == m_thom_border_index ) + thom_border_changed(); + m_thom_pal_changed = 1; + m_thom_vstate_dirty = 1; +} + + + +void thomson_state::thom_set_video_mode( unsigned mode ) +{ + assert( mode < THOM_VMODE_NB ); + + if ( mode != m_thom_vmode ) + { + LOG (( "thom_set_video_mode: %i at line %i, col %i\n", mode, thom_video_elapsed() / 64, thom_video_elapsed() % 64 )); + m_thom_vmode = mode; + thom_gplinfo_changed(); + m_thom_vstate_dirty = 1; + m_thom_hires_better |= thom_mode_is_hires( mode ); + } +} + + + +void thomson_state::thom_set_video_page( unsigned page ) +{ + assert( page < THOM_NB_PAGES ) + ; + if ( page != m_thom_vpage ) { + LOG (( "thom_set_video_page: %i at line %i col %i\n", page, thom_video_elapsed() / 64, thom_video_elapsed() % 64 )); + m_thom_vpage = page; + thom_gplinfo_changed(); + m_thom_vstate_dirty = 1; + } +} + + + +/* -------------- drawing --------------- */ + + + +typedef void ( thomson_state::*thom_scandraw ) ( UINT8* vram, UINT16* dst, UINT16* pal, int org, int len ); + + + +#define UPDATE( name, res ) \ + void thomson_state::name##_scandraw_##res ( \ + UINT8* vram, UINT16* dst, UINT16* pal, \ + int org, int len ) \ + { \ + unsigned gpl; \ + vram += org; \ + dst += org * res; \ + for ( gpl = 0; gpl < len; gpl++, dst += res, vram++ ) { \ + UINT8 rama = vram[ 0 ]; \ + UINT8 ramb = vram[ 0x2000 ]; + +#define END_UPDATE \ + } \ + } + +#define UPDATE_HI( name ) UPDATE( name, 16 ) +#define UPDATE_LOW( name ) UPDATE( name, 8 ) + + + +/* 320x200, 16-colors, constraints: 2 distinct colors per GPL (8 pixels) */ +/* this also works for TO7, assuming the 2 top bits of each color byte are 1 */ + +UPDATE_HI( to770 ) +{ + int i; + pen_t c[2]; + c[0] = pal[ ((ramb & 7) | ((ramb>>4) & 8)) ^ 8 ]; + c[1] = pal[ ((ramb >> 3) & 15) ^ 8 ]; + for ( i = 0; i < 16; i += 2, rama >>= 1 ) + dst[ 15 - i ] = dst[ 14 - i ] = c[ rama & 1 ]; +} +END_UPDATE + +UPDATE_LOW( to770 ) +{ + int i; + pen_t c[2]; + c[0] = pal[ ((ramb & 7) | ((ramb>>4) & 8)) ^ 8 ]; + c[1] = pal[ ((ramb >> 3) & 15) ^ 8 ]; + for ( i = 0; i < 8; i++, rama >>= 1 ) + dst[ 7 - i ] = c[ rama & 1 ]; +} +END_UPDATE + + + +/* as above, different (more logical but TO7-incompatible) color encoding */ + +UPDATE_HI( mo5 ) +{ + int i; + pen_t c[2]; + c[0] = pal[ ramb & 15 ]; + c[1] = pal[ ramb >> 4 ]; + for ( i = 0; i < 16; i += 2, rama >>= 1 ) + dst[ 15 - i ] = dst[ 14 - i ] = c[ rama & 1 ]; +} +END_UPDATE + +UPDATE_LOW( mo5 ) +{ + int i; + pen_t c[2]; + c[0] = pal[ ramb & 15 ]; + c[1] = pal[ ramb >> 4 ]; + for ( i = 0; i < 8; i++, rama >>= 1 ) + dst[ 7 - i ] = c[ rama & 1 ]; +} +END_UPDATE + + +/* as mo5, but with pastel bit switched */ + +UPDATE_HI( mo5alt ) +{ + int i; + pen_t c[2]; + c[0] = pal[ (ramb & 15) ^ 8 ]; + c[1] = pal[ (ramb >> 4) ^ 8 ]; + for ( i = 0; i < 16; i += 2, rama >>= 1 ) + dst[ 15 - i ] = dst[ 14 - i ] = c[ rama & 1 ]; +} +END_UPDATE + +UPDATE_LOW( mo5alt ) +{ + int i; + pen_t c[2]; + c[0] = pal[ (ramb & 15) ^ 8 ]; + c[1] = pal[ (ramb >> 4) ^ 8 ]; + for ( i = 0; i < 8; i++, rama >>= 1 ) + dst[ 7 - i ] = c[ rama & 1 ]; +} +END_UPDATE + + + +/* as to770, but with pastel color bit unswitched */ + +UPDATE_HI( to9 ) +{ + int i; + pen_t c[2]; + c[0] = pal[ (ramb & 7) | ((ramb>>4) & 8) ]; + c[1] = pal[ (ramb >> 3) & 15 ]; + for ( i = 0; i < 16; i += 2, rama >>= 1 ) + dst[ 15 - i ] = dst[ 14 - i ] = c[ rama & 1 ]; +} +END_UPDATE + +UPDATE_LOW( to9 ) +{ + int i; + pen_t c[2]; + c[0] = pal[ (ramb & 7) | ((ramb>>4) & 8) ]; + c[1] = pal[ (ramb >> 3) & 15 ]; + for ( i = 0; i < 8; i++, rama >>= 1 ) + dst[ 7 - i ] = c[ rama & 1 ]; +} +END_UPDATE + + + +/* 320x200, 4-colors, no constraint */ + +UPDATE_HI( bitmap4 ) +{ + int i; + pen_t c[2][2]; + c[0][0] = pal[ 0 ]; + c[0][1] = pal[ 1 ]; + c[1][0] = pal[ 2 ]; + c[1][1] = pal[ 3 ]; + for ( i = 0; i < 16; i += 2, rama >>= 1, ramb >>= 1 ) + dst[ 15 - i ] = dst[ 14 - i ] = c[ rama & 1 ] [ ramb & 1 ]; +} +END_UPDATE + +UPDATE_LOW( bitmap4 ) +{ + int i; + pen_t c[2][2]; + c[0][0] = pal[ 0 ]; + c[0][1] = pal[ 1 ]; + c[1][0] = pal[ 2 ]; + c[1][1] = pal[ 3 ]; + for ( i = 0; i < 8; i++, rama >>= 1, ramb >>= 1 ) + dst[ 7 - i ] = c[ rama & 1 ] [ ramb & 1 ]; +} +END_UPDATE + + + +/* as above, but using 2-bit pixels instead of 2 planes of 1-bit pixels */ + +UPDATE_HI( bitmap4alt ) +{ + int i; + pen_t c[4]; + c[0] = pal[ 0 ]; + c[1] = pal[ 1 ]; + c[2] = pal[ 2 ]; + c[3] = pal[ 3 ]; + for ( i = 0; i < 8; i += 2, ramb >>= 2 ) + dst[ 15 - i ] = dst[ 14 - i ] = c[ ramb & 3 ]; + for ( i = 0; i < 8; i += 2, rama >>= 2 ) + dst[ 7 - i ] = dst[ 6 - i ] = c[ rama & 3 ]; +} +END_UPDATE + +UPDATE_LOW( bitmap4alt ) +{ + int i; + pen_t c[4]; + c[0] = pal[ 0 ]; + c[1] = pal[ 1 ]; + c[2] = pal[ 2 ]; + c[3] = pal[ 3 ]; + for ( i = 0; i < 4; i++, ramb >>= 2 ) + dst[ 7 - i ] = c[ ramb & 3 ]; + for ( i = 0; i < 4; i++, rama >>= 2 ) + dst[ 3 - i ] = c[ rama & 3 ]; +} +END_UPDATE + + +/* 160x200, 4 colors, no constraint, using only one memory page (undocumented) */ + +UPDATE_HI( bitmap4althalf ) +{ + dst[ 0] = dst[ 1] = dst[ 2] = dst[ 3] = pal[ rama >> 6 ]; + dst[ 4] = dst[ 5] = dst[ 6] = dst[ 7] = pal[ (rama >> 4) & 3 ]; + dst[ 8] = dst[ 9] = dst[10] = dst[11] = pal[ (rama >> 2) & 3]; + dst[12] = dst[13] = dst[14] = dst[15] = pal[ rama & 3 ]; + (void)ramb; // ramb is not used +} +END_UPDATE + +UPDATE_LOW( bitmap4althalf ) +{ + dst[0] = dst[1] = pal[ rama >> 6 ]; + dst[2] = dst[3] = pal[ (rama >> 4) & 3 ]; + dst[4] = dst[5] = pal[ (rama >> 2) & 3]; + dst[6] = dst[7] = pal[ rama & 3 ]; + (void)ramb; // ramb is not used +} +END_UPDATE + + + +/* 160x200, 16-colors, no constraint */ + +UPDATE_HI( bitmap16 ) +{ + dst[ 0] = dst[ 1] = dst[ 2] = dst[ 3] = pal[ rama >> 4 ]; + dst[ 4] = dst[ 5] = dst[ 6] = dst[ 7] = pal[ rama & 15 ]; + dst[ 8] = dst[ 9] = dst[10] = dst[11] = pal[ ramb >> 4 ]; + dst[12] = dst[13] = dst[14] = dst[15] = pal[ ramb & 15 ]; +} +END_UPDATE + +UPDATE_LOW( bitmap16 ) +{ + dst[0] = dst[1] = pal[ rama >> 4 ]; + dst[2] = dst[3] = pal[ rama & 15 ]; + dst[4] = dst[5] = pal[ ramb >> 4 ]; + dst[6] = dst[7] = pal[ ramb & 15 ]; +} +END_UPDATE + + + +/* 640x200 (80 text column), 2-colors, no constraint */ + +UPDATE_HI( mode80 ) +{ + int i; + pen_t c[2]; + c[0] = pal[ 0 ]; + c[1] = pal[ 1 ]; + for ( i = 0; i < 8; i++, ramb >>= 1 ) + dst[ 15 - i ] = c[ ramb & 1 ]; + for ( i = 0; i < 8; i++, rama >>= 1 ) + dst[ 7 - i ] = c[ rama & 1 ]; +} +END_UPDATE + +UPDATE_LOW( mode80 ) +{ + /* 640-pixel mode but 320 pixels emulated => we merge pixels */ + int i; + pen_t c[4]; + c[0] = pal[ 0 ]; + c[1] = c[2] = c[3] = pal[ 1 ]; + for ( i = 0; i < 4; i++, ramb >>= 2 ) + dst[ 7 - i ] = c[ ramb & 3 ]; + for ( i = 0; i < 4; i++, rama >>= 2 ) + dst[ 3 - i ] = c[ rama & 3 ]; +} +END_UPDATE + + + +/* as above, but TO9 flavor */ + +UPDATE_HI( mode80_to9 ) +{ + int i; + pen_t c[2]; + c[0] = pal[ 0 ]; + c[1] = pal[ 6 ]; + for ( i = 0; i < 8; i++, ramb >>= 1 ) + dst[ 15 - i ] = c[ ramb & 1 ]; + for ( i = 0; i < 8; i++, rama >>= 1 ) + dst[ 7 - i ] = c[ rama & 1 ]; +} +END_UPDATE + +UPDATE_LOW( mode80_to9 ) +{ + int i; + pen_t c[4]; + c[0] = pal[ 0 ]; + c[1] = c[2] = c[3] = pal[ 6 ]; + for ( i = 0; i < 4; i++, ramb >>= 2 ) + dst[ 7 - i ] = c[ ramb & 3 ]; + for ( i = 0; i < 4; i++, rama >>= 2 ) + dst[ 3 - i ] = c[ rama & 3 ]; +} +END_UPDATE + + + +/* 320x200, 2-colors, two pages (untested) */ + +UPDATE_HI( page1 ) +{ + int i; + pen_t c[2]; + c[0] = pal[ 0 ]; + c[1] = pal[ 1 ]; + for ( i = 0; i < 16; i += 2, rama >>= 1 ) + dst[ 15 - i ] = dst[ 14 - i ] = c[ rama & 1 ]; + (void)ramb; +} +END_UPDATE + +UPDATE_LOW( page1 ) +{ + int i; + pen_t c[2]; + c[0] = pal[ 0 ]; + c[1] = pal[ 1 ]; + for ( i = 0; i < 8; i++, rama >>= 1 ) + dst[ 7 - i ] = c[ rama & 1 ]; + (void)ramb; +} +END_UPDATE + +UPDATE_HI( page2 ) +{ + int i; + pen_t c[2]; + c[0] = pal[ 0 ]; + c[1] = pal[ 2 ]; + for ( i = 0; i < 16; i += 2, ramb >>= 1 ) + dst[ 15 - i ] = dst[ 14 - i ] = c[ ramb & 1 ]; + (void)rama; +} +END_UPDATE + +UPDATE_LOW( page2 ) +{ + int i; + pen_t c[2]; + c[0] = pal[ 0 ]; + c[1] = pal[ 2 ]; + for ( i = 0; i < 8; i++, ramb >>= 1 ) + dst[ 7 - i ] = c[ ramb & 1 ]; + (void)rama; +} +END_UPDATE + + + +/* 320x200, 2-colors, two overlaid pages */ + +UPDATE_HI( overlay ) +{ + int i; + pen_t c[2][2]; + c[0][0] = pal[ 0 ]; + c[0][1] = c[1][1] = pal[ 1 ]; + c[1][0] = pal[ 2 ]; + for ( i = 0; i < 16; i += 2, rama >>= 1, ramb >>= 1 ) + dst[ 15 - i ] = dst[ 14 - i ] = c[ ramb & 1 ] [ rama & 1 ]; +} +END_UPDATE + +UPDATE_LOW( overlay ) +{ + int i; + pen_t c[2][2]; + c[0][0] = pal[ 0 ]; + c[0][1] = c[1][1] = pal[ 1 ]; + c[1][0] = pal[ 2 ]; + for ( i = 0; i < 8; i++, rama >>= 1, ramb >>= 1 ) + dst[ 7 - i ] = c[ ramb & 1 ] [ rama & 1 ]; +} +END_UPDATE + + +/* 160x200 undocumented variant of the above (2-colors, two overlaid pages) */ + +UPDATE_HI( overlayhalf ) +{ + int i; + pen_t c[2][2]; + c[0][0] = pal[ 0 ]; + c[0][1] = c[1][1] = pal[ 1 ]; + c[1][0] = pal[ 2 ]; + rama >>= 4; + ramb >>= 4; + for ( i = 0; i < 16; i += 4, rama >>= 1, ramb >>= 1 ) + dst[ 15 - i ] = dst[ 14 - i ] = dst[ 13 - i ] = dst[ 12 - i ] = + c[ ramb & 1 ] [ rama & 1 ]; +} +END_UPDATE + +UPDATE_LOW( overlayhalf ) +{ + int i; + pen_t c[2][2]; + c[0][0] = pal[ 0 ]; + c[0][1] = c[1][1] = pal[ 1 ]; + c[1][0] = pal[ 2 ]; + rama >>= 4; + ramb >>= 4; + for ( i = 0; i < 8; i += 2, rama >>= 1, ramb >>= 1 ) + dst[ 7 - i ] = dst[ 6 - i ] = c[ ramb & 1 ] [ rama & 1 ]; +} +END_UPDATE + + + +/* 160x200, 4-colors, four overlaid pages */ + +UPDATE_HI( overlay3 ) +{ + /* Note: "Manuel Technique" doc implies that the palette entries are 0,1,2,4,8; + we now use palette entries 0,1,2,3,4 instead, as this is what the TEO emulator uses and it has been confirmed correct + */ + static const int p[2][2][2][2] = { + { { { 0, 1 }, { 2, 1 } }, { { 3, 1 }, { 2, 1 } } }, + { { { 4, 1 }, { 2, 1 } }, { { 3, 1 }, { 2, 1 } } } + }; + int i; + for ( i = 0; i < 16; i += 4, rama >>= 1, ramb >>= 1 ) + dst[ 15 - i ] = dst[ 14 - i ] = dst[ 13 - i ] = dst[ 12 - i ] = + pal[ p[ ramb & 1 ] [ (ramb >> 4) & 1 ] + [ rama & 1 ] [ (rama >> 4) & 1 ] ]; +} +END_UPDATE + +UPDATE_LOW( overlay3 ) +{ + static const int p[2][2][2][2] = { + { { { 0, 1 }, { 2, 1 } }, { { 3, 1 }, { 2, 1 } } }, + { { { 4, 1 }, { 2, 1 } }, { { 3, 1 }, { 2, 1 } } } + }; + int i; + for ( i = 0; i < 8; i += 2, rama >>= 1, ramb >>= 1 ) + dst[ 7 - i ] = dst[ 6 - i ] = + pal[ p[ ramb & 1 ] [ (ramb >> 4) & 1 ] + [ rama & 1 ] [ (rama >> 4) & 1 ] ]; +} +END_UPDATE + + + +#define FUN(x) { &thomson_state::x##_scandraw_8, &thomson_state::x##_scandraw_16 } + + +static const thom_scandraw thom_scandraw_funcs[THOM_VMODE_NB][2] = +{ + FUN(to770), FUN(mo5), FUN(bitmap4), FUN(bitmap4alt), FUN(mode80), + FUN(bitmap16), FUN(page1), FUN(page2), FUN(overlay), FUN(overlay3), + FUN(to9), FUN(mode80_to9), + FUN(bitmap4althalf), FUN(mo5alt), FUN(overlayhalf), +}; + + + +/* called at the start of each scanline in the active area, just after + left border (-1<=y<199), and also after the last scanline (y=199) +*/ +TIMER_CALLBACK_MEMBER( thomson_state::thom_scanline_start ) +{ + int y = param; + + /* update active-area */ + if ( y >= 0 && (m_thom_vstate_dirty || m_thom_vstate_last_dirty || m_thom_vmem_dirty[y]) ) + { + int x = 0; + while ( x < 40 ) + { + int xx = x; + unsigned mode = m_thom_vmodepage[x] >> 8; + unsigned page = m_thom_vmodepage[x] & 0xff; + assert( mode < THOM_VMODE_NB ); + assert( page < 4 ); + if ( m_thom_vmodepage_changed ) + { + do + { + xx++; + } + while ( xx < 40 && m_thom_vmodepage[xx] == -1 ); + } + else + { + xx = 40; + } + (this->*thom_scandraw_funcs[ mode ][ m_thom_hires ]) + ( m_thom_vram + y * 40 + page * 0x4000, + m_thom_vbody + y * 320 * (m_thom_hires+1), + m_thom_last_pal, x, xx-x ); + x = xx; + } + m_thom_vmem_dirty[y] = 0; + } + + /* prepare for next scanline */ + if ( y == 199 ) + m_thom_scanline_timer->adjust(attotime::never); + else + { + if ( m_thom_vmodepage_changed ) + { + int x, m = 0; + for ( x = 0; x <= 40; x++ ) + { + if ( m_thom_vmodepage[x] !=-1 ) + { + m = m_thom_vmodepage[x]; + m_thom_vmodepage[x] = -1; + } + } + m_thom_vmodepage[0] = m; + m_thom_vmodepage_changed = 0; + } + + if ( m_thom_pal_changed ) + { + memcpy( m_thom_last_pal, m_thom_pal, 32 ); + m_thom_pal_changed = 0; + } + + m_thom_scanline_timer->adjust(attotime::from_usec(64), y + 1); + } +} + + + +/* -------------- misc --------------- */ + + +#define FLOP_STATE (m_thom_floppy_wcount ? 2 : m_thom_floppy_rcount ? 1 : 0) + + + +void thomson_state::thom_set_mode_point( int point ) +{ + assert( point >= 0 && point <= 1 ); + m_thom_mode_point = ( ! point ) * 0x2000; + m_vrambank->set_entry( ! point ); +} + + + +void thomson_state::thom_floppy_active( int write ) +{ + int fold = FLOP_STATE, fnew; + + /* stays up for a few frames */ + if ( write ) + m_thom_floppy_wcount = 25; + else + m_thom_floppy_rcount = 25; + + /* update icon */ + fnew = FLOP_STATE; + if ( fold != fnew ) + output_set_value( "floppy", fnew ); +} + + + +/* -------------- main update function --------------- */ + + + +UINT32 thomson_state::screen_update_thom(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) +{ + int y, ypos; + const int scale = m_thom_hires ? 2 : 1; + const int xbleft = m_thom_bwidth * scale; + const int xbright = ( m_thom_bwidth + THOM_ACTIVE_WIDTH ) * scale; + const int xright = ( m_thom_bwidth * 2 + THOM_ACTIVE_WIDTH ) * scale; + const int xwidth = THOM_ACTIVE_WIDTH * scale; + const int yup = THOM_BORDER_HEIGHT + THOM_ACTIVE_HEIGHT; + const int ybot = THOM_BORDER_HEIGHT + m_thom_bheight + 200; + UINT16* v = m_thom_vbody; + pen_t border = 0; + rectangle wrect(0, xright - 1, 0, 0); + rectangle lrect(0, xbleft - 1, 0, 0); + rectangle rrect(xbright, xright - 1, 0, 0); + + //LOG (( "%f thom: video update called\n", machine().time().as_double())); + + /* upper border */ + for ( y = 0; y < THOM_BORDER_HEIGHT - m_thom_bheight; y++ ) + { + if ( m_thom_border_l[ y ] != -1 ) + border = m_thom_border_l[ y ]; + } + ypos = 0; + while ( y < THOM_BORDER_HEIGHT ) + { + if ( m_thom_border_l[ y ] != -1 ) + border = m_thom_border_l[ y ]; + wrect.min_y = ypos; + do + { + y++; + ypos ++ /* += scale */; + } + while ( y < THOM_BORDER_HEIGHT && m_thom_border_l[ y ] == -1 ); + wrect.max_y = ypos - 1; + bitmap.fill(border, wrect ); + } + + /* left border */ + while ( y < yup ) + { + if ( m_thom_border_l[ y ] != -1 ) + border = m_thom_border_l[ y ]; + lrect.min_y = ypos; + do + { + y++; + ypos ++ /* += scale */; + } + while ( y < yup && m_thom_border_l[ y ] == -1 ); + lrect.max_y = ypos - 1; + bitmap.fill(border, lrect ); + } + + /* lower border */ + while (y < ybot ) + { + if ( m_thom_border_l[ y ] != -1 ) + border = m_thom_border_l[ y ]; + wrect.min_y = ypos; + do + { + y++; + ypos ++ /* += scale */; + } while ( y < ybot && m_thom_border_l[ y ] == -1 ); + wrect.max_y = ypos - 1; + bitmap.fill(border, wrect ); + } + + /* right border */ + for ( y = 0; y < THOM_BORDER_HEIGHT; y++ ) { + if ( m_thom_border_r[ y ] != -1 ) + border = m_thom_border_r[ y ]; + } + ypos = m_thom_bheight /* * scale */; + while ( y < yup ) + { + if ( m_thom_border_r[ y ] != -1 ) + border = m_thom_border_r[ y ]; + rrect.min_y = ypos; + do + { + y++; + ypos ++ /* += scale */; + } + while ( y < yup && m_thom_border_r[ y ] == -1 ); + rrect.max_y = ypos - 1; + bitmap.fill(border, rrect ); + } + + /* active area */ + ypos = m_thom_bheight /* * scale */; + for ( y = 0; y < 200; v += xwidth, y++ , ypos ++ /* += scale */ ) + { + draw_scanline16( bitmap, xbleft, ypos, xwidth, v, NULL ); +#if 0 + if ( thom_hires ) + draw_scanline16( bitmap, xbleft, ypos+1, xwidth, v, NULL ); +#endif + } + + return 0; +} + + + +/* -------------- frame start ------------------ */ + + +TIMER_CALLBACK_MEMBER( thomson_state::thom_set_init ) +{ + int init = param; + LOG (( "%f thom_set_init: %i, at line %i col %i\n", machine().time().as_double(), init, thom_video_elapsed() / 64, thom_video_elapsed() % 64 )); + + if ( m_thom_init_cb ) + (this->*m_thom_init_cb)( init ); + if ( ! init ) + m_thom_init_timer->adjust(attotime::from_usec( 64 * THOM_ACTIVE_HEIGHT - 24 ), 1-init); +} + +/* call this at the very beginning of each new frame */ +void thomson_state::thom_vblank( screen_device &screen, bool state ) +{ + // rising edge + if (state) + { + int fnew, fold = FLOP_STATE; + int i; + UINT16 b = 0; + struct thom_vsignal l = thom_get_lightpen_vsignal( 0, -1, 0 ); + + LOG (( "%f thom: video eof called\n", machine().time().as_double() )); + + /* floppy indicator count */ + if ( m_thom_floppy_wcount ) + m_thom_floppy_wcount--; + if ( m_thom_floppy_rcount ) + m_thom_floppy_rcount--; + fnew = FLOP_STATE; + if ( fnew != fold ) + output_set_value( "floppy", fnew ); + + /* prepare state for next frame */ + for ( i = 0; i <= THOM_TOTAL_HEIGHT; i++ ) + { + if ( m_thom_border_l[ i ] != -1 ) + b = m_thom_border_l[ i ]; + if ( m_thom_border_r[ i ] != -1 ) + b = m_thom_border_r[ i ]; + } + memset( m_thom_border_l, 0xff, sizeof( m_thom_border_l ) ); + memset( m_thom_border_r, 0xff, sizeof( m_thom_border_r ) ); + m_thom_border_l[ 0 ] = b; + m_thom_border_r[ 0 ] = b; + m_thom_vstate_last_dirty = m_thom_vstate_dirty; + m_thom_vstate_dirty = 0; + + /* schedule first init signal */ + m_thom_init_timer->adjust(attotime::from_usec( 64 * THOM_BORDER_HEIGHT + 7 )); + + /* schedule first lightpen signal */ + l.line &= ~1; /* hack (avoid lock in MO6 palette selection) */ + m_thom_lightpen_timer->adjust( + attotime::from_usec( 64 * ( THOM_BORDER_HEIGHT + l.line - 2 ) + 16 ), 0); + + /* schedule first active-area scanline call-back */ + m_thom_scanline_timer->adjust(attotime::from_usec( 64 * THOM_BORDER_HEIGHT + 7), -1); + + /* reset video frame time */ + m_thom_video_timer->adjust(attotime::zero); + + /* update screen size according to user options */ + if ( thom_update_screen_size() ) + m_thom_vstate_dirty = 1; + + /* hi-res automatic */ + m_thom_hires_better = thom_mode_is_hires( m_thom_vmode ); + } +} + + + +/* -------------- initialization --------------- */ + + +/* TO7, TO7/70 palette, hardcoded in ROM + without further information, we assume that the hardcoded values + are the same as those setup by the TO8/TO9+/MO6 when booting up + */ +static const UINT16 thom_pal_init[16] = +{ + 0x1000, /* 0: black */ 0x000f, /* 1: red */ + 0x00f0, /* 2: geen */ 0x00ff, /* 3: yellow */ + 0x0f00, /* 4: blue */ 0x0f0f, /* 5: purple */ + 0x0ff0, /* 6: cyan */ 0x0fff, /* 7: white */ + 0x0777, /* 8: gray */ 0x033a, /* 9: pink */ + 0x03a3, /* a: light green */ 0x03aa, /* b: light yellow */ + 0x0a33, /* c: light blue */ 0x0a3a, /* d: redish pink */ + 0x0ee7, /* e: light cyan */ 0x007b, /* f: orange */ +}; + +/* MO5 palette, hardcoded in a ROM + values are from "Manuel Technique du MO5", p.19 + */ +static const UINT16 mo5_pal_init[16] = +{ + 0x1000, /* 0: black */ 0x055f, /* 1: red */ + 0x00f0, /* 2: geen */ 0x00ff, /* 3: yellow */ + 0x0f55, /* 4: blue */ 0x0f0f, /* 5: purple */ + 0x0ff5, /* 6: cyan */ 0x0fff, /* 7: white */ + 0x0aaa, /* 8: gray */ 0x0aaf, /* 9: pink */ + 0x0afa, /* a: light green */ 0x0aff, /* b: light yellow */ + 0x0fa5, /* c: light blue */ 0x0faf, /* d: parama pink */ + 0x0ffa, /* e: light cyan */ 0x05af, /* f: orange */ +}; + + +VIDEO_START_MEMBER( thomson_state, thom ) +{ + LOG (( "thom: video start called\n" )); + + /* scan-line state */ + memset( m_thom_border_l, 0xff, sizeof( m_thom_border_l ) ); + memset( m_thom_border_r, 0xff, sizeof( m_thom_border_r ) ); + memset( m_thom_vbody, 0, sizeof( m_thom_vbody ) ); + memset( m_thom_vmodepage, 0xffff, sizeof( m_thom_vmodepage ) ); + memset( m_thom_vmem_dirty, 0, sizeof( m_thom_vmem_dirty ) ); + m_thom_border_l[ 0 ] = 0; + m_thom_border_r[ 0 ] = 0; + m_thom_vmodepage[ 0 ] = 0; + m_thom_vmodepage_changed = 0; + m_thom_vmode = 0; + m_thom_vpage = 0; + m_thom_border_index = 0; + m_thom_vstate_dirty = 1; + m_thom_vstate_last_dirty = 1; + save_pointer(NAME(m_thom_last_pal), sizeof(m_thom_last_pal)); + save_pointer(NAME(m_thom_pal), sizeof(m_thom_pal)); + save_pointer(NAME(m_thom_border_l), sizeof(m_thom_border_l)); + save_pointer(NAME(m_thom_border_r), sizeof(m_thom_border_r)); + save_pointer(NAME(m_thom_vbody), sizeof(m_thom_vbody)); + save_pointer(NAME(m_thom_vmodepage), sizeof(m_thom_vmodepage)); + save_pointer(NAME(m_thom_vmem_dirty), sizeof(m_thom_vmem_dirty)); + save_item(NAME(m_thom_pal_changed)); + save_item(NAME(m_thom_vmodepage_changed)); + save_item(NAME(m_thom_vmode)); + save_item(NAME(m_thom_vpage)); + save_item(NAME(m_thom_border_index)); + save_item(NAME(m_thom_vstate_dirty)); + save_item(NAME(m_thom_vstate_last_dirty)); + + m_thom_mode_point = 0; + save_item(NAME(m_thom_mode_point)); + m_vrambank->set_entry( 0 ); + + m_thom_floppy_rcount = 0; + m_thom_floppy_wcount = 0; + save_item(NAME(m_thom_floppy_wcount)); + save_item(NAME(m_thom_floppy_rcount)); + output_set_value( "floppy", 0 ); + + m_thom_video_timer = machine().scheduler().timer_alloc(FUNC_NULL); + + m_thom_scanline_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(thomson_state::thom_scanline_start),this)); + + m_thom_lightpen_nb = 0; + m_thom_lightpen_cb = NULL; + m_thom_lightpen_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(thomson_state::thom_lightpen_step),this)); + save_item(NAME(m_thom_lightpen_nb)); + + m_thom_init_cb = NULL; + m_thom_init_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(thomson_state::thom_set_init),this)); + + save_item(NAME(m_thom_bwidth)); + save_item(NAME(m_thom_bheight)); + save_item(NAME(m_thom_hires)); + save_item(NAME(m_thom_hires_better)); +} + + +/* sets the fixed palette (for MO5,TO7,TO7/70) and gamma correction */ +void thomson_state::thom_configure_palette(double gamma, const UINT16* pal, palette_device& palette) +{ + memcpy( m_thom_last_pal, pal, 32 ); + memcpy( m_thom_pal, pal, 32 ); + + for ( int i = 0; i < 4097; i++ ) + { + UINT8 r = 255. * pow( (i & 15) / 15., gamma ); + UINT8 g = 255. * pow( ((i>> 4) & 15) / 15., gamma ); + UINT8 b = 255. * pow( ((i >> 8) & 15) / 15., gamma ); + /* UINT8 alpha = i & 0x1000 ? 0 : 255; TODO: transparency */ + palette.set_pen_color(i, r, g, b ); + } +} + + +PALETTE_INIT_MEMBER(thomson_state, thom) +{ + LOG (( "thom: palette init called\n" )); + + /* TO8 and later use an EF9369 color palette chip + The spec shows a built-in gamma correction for gamma=2.8 + i.e., output is out = in ^ (1/2.8) + + For the TO7, the gamma correction is irrelevant. + + For the TO7/70, we use the same palette and gamma has the TO8, + which gives good results (but is not verified). + */ + thom_configure_palette(1.0 / 2.8, thom_pal_init, palette); +} + +PALETTE_INIT_MEMBER(thomson_state, mo5) +{ + LOG (( "thom: MO5 palette init called\n" )); + + /* The MO5 has a different fixed palette than the TO7/70. + We use a smaller gamma correction which gives intutively better + results (but is not verified). + */ + thom_configure_palette(1.0, mo5_pal_init, palette); +} + + + +/***************************** TO7 / T9000 *************************/ + + + +/* write to video memory through addresses 0x4000-0x5fff */ +WRITE8_MEMBER( thomson_state::to7_vram_w ) +{ + assert( offset < 0x2000 ); + /* force two topmost color bits to 1 */ + if ( m_thom_mode_point ) + data |= 0xc0; + if ( m_thom_vram[ offset + m_thom_mode_point ] == data ) + return; + m_thom_vram[ offset + m_thom_mode_point ] = data; + /* dirty whole scanline */ + m_thom_vmem_dirty[ offset / 40 ] = 1; +} + + + +/* bits 0-13 : latched gpl of lightpen position */ +/* bit 14: latched INIT */ +/* bit 15: latched INIL */ +unsigned thomson_state::to7_lightpen_gpl ( int decx, int decy ) +{ + int x,y; + thom_get_lightpen_pos( &x, &y ); + x -= m_thom_bwidth; + y -= m_thom_bheight; + if ( x < 0 || y < 0 || x >= 320 || y >= 200 ) + return 0; + x += decx; + y += decy; + return y*40 + x/8 + (x < 320 ? 0x4000 : 0) + 0x8000; +} + + + +/************************** TO7/70 / MO5 ****************************/ + + + +/* write to video memory through addresses 0x4000-0x5fff (TO) + or 0x0000-0x1fff (MO) */ +WRITE8_MEMBER( thomson_state::to770_vram_w ) +{ + assert( offset < 0x2000 ); + if ( m_thom_vram[ offset + m_thom_mode_point ] == data ) + return; + m_thom_vram[ offset + m_thom_mode_point ] = data; + /* dirty whole scanline */ + m_thom_vmem_dirty[ offset / 40 ] = 1; +} + + + + +/***************************** TO8 ******************************/ + + + +/* write to video memory through system space (always page 1) */ + +WRITE8_MEMBER( thomson_state::to8_sys_lo_w ) +{ + UINT8* dst = m_thom_vram + offset + 0x6000; + assert( offset < 0x2000 ); + if ( *dst == data ) + return; + *dst = data; + /* dirty whole scanline */ + m_thom_vmem_dirty[ offset / 40 ] = 1; +} + + + +WRITE8_MEMBER( thomson_state::to8_sys_hi_w ) +{ + UINT8* dst = m_thom_vram + offset + 0x4000; + assert( offset < 0x2000 ); + if ( *dst == data ) return; + *dst = data; + /* dirty whole scanline */ + m_thom_vmem_dirty[ offset / 40 ] = 1; +} + + + +/* write to video memory through data space */ + +WRITE8_MEMBER( thomson_state::to8_data_lo_w ) +{ + UINT8* dst = m_thom_vram + ( ( offset + 0x4000 * m_to8_data_vpage + 0x2000 ) & m_ram->mask() ); + assert( offset < 0x2000 ); + if ( *dst == data ) + return; + *dst = data; + /* dirty whole scanline */ + if ( m_to8_data_vpage >= 4 ) + return; + m_thom_vmem_dirty[ offset / 40 ] = 1; +} + + + +WRITE8_MEMBER( thomson_state::to8_data_hi_w ) +{ + UINT8* dst = m_thom_vram + ( ( offset + 0x4000 * m_to8_data_vpage ) & m_ram->mask() ); + assert( offset < 0x2000 ); + if ( *dst == data ) + return; + *dst = data; + /* dirty whole scanline */ + if ( m_to8_data_vpage >= 4 ) + return; + m_thom_vmem_dirty[ offset / 40 ] = 1; +} + + + +/* write to video memory page through cartridge addresses space */ +WRITE8_MEMBER( thomson_state::to8_vcart_w ) +{ + UINT8* dst = m_thom_vram + ( ( offset + 0x4000 * m_to8_cart_vpage ) & m_ram->mask() ); + assert( offset < 0x4000 ); + if ( *dst == data ) + return; + *dst = data; + /* dirty whole scanline */ + if ( m_to8_cart_vpage >= 4 ) + return; + m_thom_vmem_dirty[ (offset & 0x1fff) / 40 ] = 1; +} + +WRITE8_MEMBER( thomson_state::mo6_vcart_lo_w ) +{ + UINT8* dst = m_thom_vram + ( ( offset + 0x3000 + 0x4000 * m_to8_cart_vpage ) & m_ram->mask() ); + assert( offset < 0x1000 ); + if ( *dst == data ) + return; + *dst = data; + /* dirty whole scanline */ + if ( m_to8_cart_vpage >= 4 ) + return; + m_thom_vmem_dirty[ (offset & 0x1fff) / 40 ] = 1; +} + +WRITE8_MEMBER( thomson_state::mo6_vcart_hi_w ) +{ + UINT8* dst = m_thom_vram + ( ( offset + 0x4000 * m_to8_cart_vpage ) & m_ram->mask() ); + assert( offset < 0x3000 ); + if ( *dst == data ) + return; + *dst = data; + /* dirty whole scanline */ + if ( m_to8_cart_vpage >= 4 ) + return; + m_thom_vmem_dirty[ (offset & 0x1fff) / 40 ] = 1; +} |