1 // license:BSD-3-Clause
2 // copyright-holders:Tim Schuerewegen
3 /*
4 
5     Hitachi HD66421 LCD Controller/Driver
6 
7     (c) 2001-2007 Tim Schuerewegen
8 
9 */
10 
11 #include "emu.h"
12 #include "hd66421.h"
13 
14 
15 //**************************************************************************
16 //  MACROS / CONSTANTS
17 //**************************************************************************
18 
19 //#define HD66421_BRIGHTNESS_DOES_NOT_WORK
20 
21 #define LOG_LEVEL  1
22 #define _logerror(level,x)  do { if (LOG_LEVEL > level) logerror x; } while (0)
23 
24 #define HD66421_RAM_SIZE  (hd66421_device::WIDTH * hd66421_device::HEIGHT / 4) // 2-bits per pixel
25 
26 // R0 - control register 1
27 #define LCD_R0_RMW      0x80 // read-modify-write mode
28 #define LCD_R0_DISP     0x40 // display on/off
29 #define LCD_R0_STBY     0x20 // standby (internal operation and power circuit halt)
30 #define LCD_R0_PWR      0x10
31 #define LCD_R0_AMP      0x08
32 #define LCD_R0_REV      0x04 // reverse
33 #define LCD_R0_HOLT     0x02
34 #define LCD_R0_ADC      0x01
35 
36 // R1 - control register 2
37 #define LCD_R1_BIS1     0x80 // bias ratio (bit 1)
38 #define LCD_R1_BIS0     0x40 // bias ratio (bit 0)
39 #define LCD_R1_WLS      0x20
40 #define LCD_R1_GRAY     0x10 // grayscale palette 4/32
41 #define LCD_R1_DTY1     0x08 // display duty cycle (bit 1)
42 #define LCD_R1_DTY0     0x04 // display duty cycle (bit 0)
43 #define LCD_R1_INC      0x02
44 #define LCD_R1_BLK      0x01 // blink function
45 
46 // register 0 to 16
47 #define LCD_REG_CONTROL_1   0x00 // control register 1
48 #define LCD_REG_CONTROL_2   0x01 // control register 2
49 #define LCD_REG_ADDR_X      0x02 // x address register
50 #define LCD_REG_ADDR_Y      0x03 // y address register
51 #define LCD_REG_RAM         0x04 // display ram access register
52 #define LCD_REG_START_Y     0x05 // display start line register
53 #define LCD_REG_BLINK_START 0x06 // blink start line register
54 #define LCD_REG_BLINK_END   0x07 // blink end line register
55 #define LCD_REG_BLINK_1     0x08 // blink register 1
56 #define LCD_REG_BLINK_2     0x09 // blink register 2
57 #define LCD_REG_BLINK_3     0x0A // blink register 3
58 #define LCD_REG_PARTIAL     0x0B // partial display block register
59 #define LCD_REG_COLOR_1     0x0C // gray scale palette 1 (0,0)
60 #define LCD_REG_COLOR_2     0x0D // gray scale palette 2 (0,1)
61 #define LCD_REG_COLOR_3     0x0E // gray scale palette 3 (1,0)
62 #define LCD_REG_COLOR_4     0x0F // gray scale palette 4 (1,1)
63 #define LCD_REG_CONTRAST    0x10 // contrast control register
64 #define LCD_REG_PLANE       0x11 // plane selection register
65 
66 //**************************************************************************
67 //  GLOBAL VARIABLES
68 //**************************************************************************
69 
70 // devices
71 DEFINE_DEVICE_TYPE(HD66421, hd66421_device, "hd66421", "Hitachi HD66421 LCD Controller")
72 
73 
74 // default address map
hd66421(address_map & map)75 void hd66421_device::hd66421(address_map &map)
76 {
77 	if (!has_configured_map(0))
78 		map(0x0000, HD66421_RAM_SIZE).ram();
79 }
80 
81 //-------------------------------------------------
82 //  memory_space_config - return a description of
83 //  any address spaces owned by this device
84 //-------------------------------------------------
85 
memory_space_config() const86 device_memory_interface::space_config_vector hd66421_device::memory_space_config() const
87 {
88 	return space_config_vector {
89 		std::make_pair(0, &m_space_config)
90 	};
91 }
92 
93 
94 //**************************************************************************
95 //  INLINE HELPERS
96 //**************************************************************************
97 
98 //-------------------------------------------------
99 //  readbyte - read a byte at the given address
100 //-------------------------------------------------
101 
readbyte(offs_t address)102 inline uint8_t hd66421_device::readbyte(offs_t address)
103 {
104 	return space().read_byte(address);
105 }
106 
107 
108 //-------------------------------------------------
109 //  writebyte - write a byte at the given address
110 //-------------------------------------------------
111 
writebyte(offs_t address,uint8_t data)112 inline void hd66421_device::writebyte(offs_t address, uint8_t data)
113 {
114 	space().write_byte(address, data);
115 }
116 
117 
118 //**************************************************************************
119 //  LIVE DEVICE
120 //**************************************************************************
121 
122 //-------------------------------------------------
123 //  hd66421_device - constructor
124 //-------------------------------------------------
125 
hd66421_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)126 hd66421_device::hd66421_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
127 	: device_t(mconfig, HD66421, tag, owner, clock)
128 	, device_memory_interface(mconfig, *this)
129 	, m_space_config("videoram", ENDIANNESS_LITTLE, 8, 17, 0, address_map_constructor(FUNC(hd66421_device::hd66421), this))
130 	, m_cmd(0)
131 	, m_x(0)
132 	, m_y(0)
133 	, m_palette(*this, "palette")
134 {
135 	for (auto &elem : m_reg)
136 		elem = 0;
137 }
138 
139 
140 //-------------------------------------------------
141 //  device_start - device-specific startup
142 //-------------------------------------------------
143 
device_start()144 void hd66421_device::device_start()
145 {
146 	// register for state saving
147 	save_item(NAME(m_cmd));
148 	save_item(NAME(m_reg));
149 	save_item(NAME(m_x));
150 	save_item(NAME(m_y));
151 }
152 
reg_idx_r()153 uint8_t hd66421_device::reg_idx_r()
154 {
155 	_logerror( 2, ("reg_idx_r\n"));
156 	return m_cmd;
157 }
158 
reg_idx_w(uint8_t data)159 void hd66421_device::reg_idx_w(uint8_t data)
160 {
161 	_logerror( 2, ("reg_idx_w (%02X)\n", data));
162 	m_cmd = data;
163 }
164 
reg_dat_r()165 uint8_t hd66421_device::reg_dat_r()
166 {
167 	_logerror( 2, ("reg_dat_r\n"));
168 	return m_reg[m_cmd];
169 }
170 
reg_dat_w(uint8_t data)171 void hd66421_device::reg_dat_w(uint8_t data)
172 {
173 	_logerror( 2, ("reg_dat_w (%02X)\n", data));
174 	m_reg[m_cmd] = data;
175 
176 	switch (m_cmd)
177 	{
178 		case LCD_REG_ADDR_X :
179 			m_x = data;
180 			break;
181 
182 		case LCD_REG_ADDR_Y :
183 			m_y = data;
184 			break;
185 
186 		case LCD_REG_RAM :
187 		{
188 			uint8_t r1;
189 			writebyte(m_y * (WIDTH / 4) + m_x, data);
190 			r1 = m_reg[LCD_REG_CONTROL_2];
191 			if (r1 & 0x02)
192 				m_x++;
193 			else
194 				m_y++;
195 
196 			if (m_x >= (WIDTH / 4))
197 			{
198 				m_x = 0;
199 				m_y++;
200 			}
201 
202 			if (m_y >= HEIGHT)
203 				m_y = 0;
204 		}
205 		break;
206 	}
207 }
208 
plot_pixel(bitmap_ind16 & bitmap,int x,int y,uint32_t color)209 void hd66421_device::plot_pixel(bitmap_ind16 &bitmap, int x, int y, uint32_t color)
210 {
211 	bitmap.pix(y, x) = (uint16_t)color;
212 }
213 
update_screen(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)214 uint32_t hd66421_device::update_screen(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
215 {
216 	pen_t pen[4];
217 
218 	_logerror( 1, ("video_update_hd66421\n"));
219 
220 	// update palette
221 	for (int i = 0; i < 4; i++)
222 	{
223 		double bright;
224 		int temp;
225 		temp = 31 - (m_reg[LCD_REG_COLOR_1 + i] - m_reg[LCD_REG_CONTRAST] + 0x03);
226 		if (temp <  0) temp =  0;
227 		if (temp > 31) temp = 31;
228 		bright = 1.0 * temp / 31;
229 		pen[i] = i;
230 #ifdef HD66421_BRIGHTNESS_DOES_NOT_WORK
231 		m_palette->set_pen_color(pen[i], 255 * bright, 255 * bright, 255 * bright);
232 #else
233 		m_palette->set_pen_contrast(pen[i], bright);
234 #endif
235 	}
236 
237 	// draw bitmap (bottom to top)
238 	if (m_reg[0] & LCD_R0_DISP)
239 	{
240 		int x, y;
241 		x = 0;
242 		y = HEIGHT - 1;
243 
244 		for (int i = 0; i < HD66421_RAM_SIZE; i++)
245 		{
246 			plot_pixel(bitmap, x++, y, pen[(readbyte(i) >> 6) & 3]);
247 			plot_pixel(bitmap, x++, y, pen[(readbyte(i) >> 4) & 3]);
248 			plot_pixel(bitmap, x++, y, pen[(readbyte(i) >> 2) & 3]);
249 			plot_pixel(bitmap, x++, y, pen[(readbyte(i) >> 0) & 3]);
250 			if (x >= WIDTH)
251 			{
252 				x = 0;
253 				y = y - 1;
254 			}
255 		}
256 	}
257 	else
258 	{
259 		rectangle rect(0, WIDTH - 1, 0, HEIGHT - 1);
260 		bitmap.fill(m_palette->white_pen(), rect);
261 	}
262 
263 	return 0;
264 }
265 
hd66421_palette(palette_device & palette) const266 void hd66421_device::hd66421_palette(palette_device &palette) const
267 {
268 	// init palette
269 	for (int i = 0; i < 4; i++)
270 	{
271 		palette.set_pen_color(i, rgb_t::white());
272 #ifndef HD66421_BRIGHTNESS_DOES_NOT_WORK
273 		palette.set_pen_contrast(i, double(i) / (4 - 1));
274 #endif
275 	}
276 }
277 
278 
279 //-------------------------------------------------
280 //  device_add_mconfig - add device configuration
281 //-------------------------------------------------
282 
device_add_mconfig(machine_config & config)283 void hd66421_device::device_add_mconfig(machine_config &config)
284 {
285 	PALETTE(config, m_palette, FUNC(hd66421_device::hd66421_palette), 4);
286 }
287