summaryrefslogtreecommitdiffstatshomepage
path: root/src/mess/video/hp48.c
blob: 7694ea1660855d321d4cde96c37217e3c28dc7d5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/**********************************************************************

  Copyright (C) Antoine Mine' 2008

   Hewlett Packard HP48 S/SX & G/GX/G+

**********************************************************************/

#include "emu.h"
#include "includes/hp48.h"

/***************************************************************************
    DEBUGGING
***************************************************************************/


#define VERBOSE       0

#define LOG(x)  do { if (VERBOSE) logerror x; } while (0)



/***************************************************************************
    GLOBAL VARIABLES & CONSTANTS
***************************************************************************/

/* base colors */
static const int hp48_bg_color[3] = { 136, 147, 109 };  /* yellow */
static const int hp48_fg_color[3] = {   0,   0,  64 };  /* dark blue */

/* color mixing */
#define mix(c1,c2,x) (c1)*(1-(x))+(c2)*(x)
#define mix2(i,x) mix(hp48_bg_color[i],hp48_fg_color[i],x)




/***************************************************************************
    FUNCTIONS
***************************************************************************/

PALETTE_INIT_MEMBER(hp48_state, hp48)
{
	int i;
	for ( i = 0; i < 255; i++ )
	{
		float c = i/255.;
		m_palette->set_pen_color( i, rgb_t( 0, mix2(0,c), mix2(1,c), mix2(2,c) ) );
	}
}



/* The screen is organised as follows:



            announciators

        ----------------------      ^       ^
       |                      |     |       |
       |                      |     |       |
       |                      |     |       |
       |                      |  M lines    |
       |      main screen     |     |       |
       |                      |     |       |
       |                      |     |    64 lines
       |                      |     |       |
       |                      |     |       |
       |----------------------|     -       |
       |                      |     |       |
       |        menu          |  m lines    |
       |                      |     |       |
        ----------------------      v       v

       <----- 131 columns ---->



        The LCD is 131x64 pixels and has two components:
    - a main screen
    - a menu screen

    The main height (M) and the menu height (m) can be changed with the constraints:
    - m+M=64
    - M>=2

    Pixels are 1-bit, packed in memory.

    The start address of both screens can be changed independently
    (this allows smooth, pixel-precise vertical scrooling of both parts).
    They must be even addresses (in nibbles).

    The stride (offset in nibbles between two scanlines) can be changed for the main screen.
    It must be even.
    It is fixed to 34 for the menu screen.

    The bit offset for the first column of the main screen can be changed
    (this allows smooth, pixel-precise horizontal scrooling).
    It is always 0 for the menu screen.

    Above the LCD, there are 6 annonciators that can be independently turned
    on and off (and independetly from the LCD).
    They are not handled here, but through output_set_value.
 */


/*
        In theory, the LCD is monorchrome, with a global adjustable contrast (32 levels).
    However, by switching between screens at each refresh (64 Hz), one can achieve the
    illusion of grayscale, with moderate flickering.
    This technique was very widespread.
    We emulate it by simply averaging between the last few (HP48_NB_SCREENS) frames.

    HP48_NB_SCREENS should be a multiple of the period of screen flips to avoid
    flickering in the emulation.
 */


#define draw_pixel                          \
	state->m_screens[ state->m_cur_screen ][ y ][ xp + 8 ] = (data & 1) ? fg : 0; \
	xp++;                               \
	data >>= 1

#define draw_quart                  \
	UINT8 data = space.read_byte( addr );   \
	draw_pixel; draw_pixel; draw_pixel; draw_pixel;


UINT32 hp48_state::screen_update_hp48(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	hp48_state *state = machine().driver_data<hp48_state>();
	address_space &space = m_maincpu->space(AS_PROGRAM);
	int x, y, xp, i, addr;
	int display       = HP48_IO_4(0) >> 3;           /* 1=on, 0=off */
	int left_margin   = HP48_IO_4(0) & 7;            /* 0..7 pixels for main bitmap */
	int contrast      = HP48_IO_8(1) & 0x1f;         /* 0..31 */
	int refresh       = HP48_IO_4(3) >> 3;           /* vertical refresh */
	int bitmap_start  = HP48_IO_20(0x20) & ~1;       /* main bitmap address */
	int right_margin  = HP48_IO_12(0x25) & ~1;       /* -2048..2046 nibbles for main bitmap */
	int last_line     = HP48_IO_8(0x28) & 0x3f;      /* 2..63 lines of main bitmap before menu */
	int menu_start    = HP48_IO_20(0x30) & ~1;       /* menu bitmap address */
	int fg = contrast + 2;

	LOG(( "%f hp48 video_update called: ", machine().time().as_double()));

	if ( !display || refresh )
	{
		LOG(( "display off\n" ));
		bitmap.fill(0 );
		return 0;
	}

	/* correcting factors */
	if ( right_margin & 0x800 ) right_margin -= 0x1000;
	if ( last_line <= 1 ) last_line = 0x3f;

	LOG(( "on=%i lmargin=%i rmargin=%i contrast=%i start=%05x lline=%i menu=%05x\n",
			display, left_margin, right_margin, contrast, bitmap_start, last_line, menu_start ));

	/* draw main bitmap */
	addr = bitmap_start;
	for ( y = 0; y <= last_line; y++ )
	{
		xp = -left_margin;
		for ( x = 0; x < 34; x++, addr++ )
		{
			draw_quart;
		}
		addr += (right_margin + (left_margin / 4) + 1) & ~1;
	}

	/* draw menu bitmap */
	addr = menu_start;
	for ( ; y <= 0x3f; y++ )
	{
		xp = 0;
		for ( x = 0; x < 34; x++, addr++ )
		{
			draw_quart;
		}
	}

	/* draw averaged frames */
	for ( y = 0; y < 64; y++ )
	{
		for ( x = 0; x < 131; x++ )
		{
			int acc = 0;
			for ( i = 0; i < HP48_NB_SCREENS; i++ )
			{
				acc += m_screens[ i ][ y ][ x+8 ];
			}
			acc = (acc * 255) / (33 * HP48_NB_SCREENS);
			bitmap.pix16(y, x ) = acc;
		}
	}

	m_cur_screen = (m_cur_screen + 1) % HP48_NB_SCREENS;

	return 0;
}