1 // license:BSD-3-Clause
2 // copyright-holders:Olivier Galibert
3 /***************************************************************************
4 
5     MSM6222B
6 
7     A somewhat hd44780-compatible LCD controller.
8 
9     The -01 variant has a fixed cgrom, the other variants are mask-programmed.
10 
11 ***************************************************************************/
12 
13 #include "emu.h"
14 #include "msm6222b.h"
15 
16 DEFINE_DEVICE_TYPE(MSM6222B,    msm6222b_device,    "msm6222b",   "Oki MSM6222B-xx LCD Controller")
17 DEFINE_DEVICE_TYPE(MSM6222B_01, msm6222b_01_device, "msm6222b01", "Oki MSM6222B-01 LCD Controller")
18 
ROM_START(msm6222b_01)19 ROM_START( msm6222b_01 )
20 	ROM_REGION( 0x1000, "cgrom", 0 )
21 	ROM_LOAD( "msm6222b-01.bin", 0x0000, 0x1000, CRC(8ffa8521) SHA1(e108b520e6d20459a7bbd5958bbfa1d551a690bd) )
22 ROM_END
23 
24 msm6222b_device::msm6222b_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
25 	device_t(mconfig, type, tag, owner, clock),
26 	m_cgrom(*this, finder_base::DUMMY_TAG),
27 	cursor_direction(false), cursor_blinking(false), two_line(false), shift_on_write(false), double_height(false), cursor_on(false), display_on(false), adc(0), shift(0)
28 {
29 }
30 
msm6222b_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)31 msm6222b_device::msm6222b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
32 	msm6222b_device(mconfig, MSM6222B, tag, owner, clock)
33 {
34 	m_cgrom.set_tag(*this, DEVICE_SELF);
35 }
36 
msm6222b_01_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)37 msm6222b_01_device::msm6222b_01_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
38 	msm6222b_device(mconfig, MSM6222B_01, tag, owner, clock)
39 {
40 	// load the fixed cgrom
41 	m_cgrom.set_tag(*this, "cgrom");
42 }
43 
device_rom_region() const44 const tiny_rom_entry *msm6222b_01_device::device_rom_region() const
45 {
46 	return ROM_NAME(msm6222b_01);
47 }
48 
device_start()49 void msm6222b_device::device_start()
50 {
51 	memset(cgram, 0, sizeof(cgram));
52 	memset(ddram, 0x20, sizeof(ddram));
53 
54 	cursor_direction = true;
55 	cursor_blinking = false;
56 	display_on = false;
57 	two_line = false;
58 	cursor_on = false;
59 	shift_on_write = false;
60 	double_height = false;
61 	adc = 0x00;
62 	shift = 0;
63 }
64 
control_w(uint8_t data)65 void msm6222b_device::control_w(uint8_t data)
66 {
67 	int cmd;
68 	for(cmd = 7; cmd >= 0 && !(data & (1<<cmd)); cmd--) {};
69 	switch(cmd) {
70 	case 0:
71 		memset(ddram, 0x20, sizeof(ddram));
72 		adc = 0x00;
73 		break;
74 
75 	case 1:
76 		adc = 0x00;
77 		shift = 0x00;
78 		break;
79 	case 2:
80 		shift_on_write = data & 1;
81 		cursor_direction = data & 2;
82 		break;
83 
84 	case 3:
85 		display_on = data & 4;
86 		cursor_on = data & 2;
87 		cursor_blinking = data & 1;
88 		break;
89 
90 	case 4:
91 		if(data & 8)
92 			shift_step(data & 4);
93 		else
94 			cursor_step(data & 4);
95 		break;
96 
97 	case 5:
98 		two_line = data & 8;
99 		double_height = (data & 0xc) == 4;
100 		// Bit 4 is 4bits/8bits data access
101 		break;
102 
103 	case 6:
104 		adc = data & 0x3f;
105 		break;
106 
107 	case 7:
108 		adc = data; // Bit 7 is set
109 		break;
110 	}
111 }
112 
control_r()113 uint8_t msm6222b_device::control_r()
114 {
115 	return adc & 0x7f;
116 }
117 
data_w(uint8_t data)118 void msm6222b_device::data_w(uint8_t data)
119 {
120 	if(adc & 0x80) {
121 		int adr = adc & 0x7f;
122 		if(two_line) {
123 			if((adr >= 40 && adr < 64) || adr >= 64+40)
124 				adr = -1;
125 			if(adr >= 64)
126 				adr += 40-64;
127 		} else {
128 			if(adr >= 80)
129 				adr = -1;
130 		}
131 		if(adr != -1) {
132 			ddram[adr] = data;
133 			if(shift_on_write)
134 				shift_step(cursor_direction);
135 			else
136 				cursor_step(cursor_direction);
137 		}
138 	} else {
139 		if(adc < 8*8) {
140 			cgram[adc] = data;
141 			cursor_step(cursor_direction);
142 		}
143 	}
144 }
145 
cursor_step(bool direction)146 void msm6222b_device::cursor_step(bool direction)
147 {
148 	if(direction) {
149 		if(adc & 0x80) {
150 			if(two_line && adc == (0x80|39))
151 				adc = 0x80|64;
152 			else if(two_line && adc == (0x80|(64+39)))
153 				adc = 0x80;
154 			else if((!two_line) && adc == (0x80|79))
155 				adc = 0x80;
156 			else
157 				adc++;
158 		} else {
159 			if(adc == 8*8-1)
160 				adc = 0x00;
161 			else
162 				adc++;
163 		}
164 	} else {
165 		if(adc & 0x80) {
166 			if(adc == 0x80)
167 				adc = two_line ? 0x80|(64+39) : 0x80|79;
168 			else if(two_line && adc == (0x80|64))
169 				adc = 0x80|39;
170 			else
171 				adc--;
172 		} else {
173 			if(adc == 0x00)
174 				adc = 8*8-1;
175 			else
176 				adc--;
177 		}
178 	}
179 }
180 
shift_step(bool direction)181 void msm6222b_device::shift_step(bool direction)
182 {
183 	if(direction) {
184 		if(shift == 79)
185 			shift = 0;
186 		else
187 			shift++;
188 	} else {
189 		if(shift == 0)
190 			shift = 79;
191 		else
192 			shift--;
193 	}
194 }
195 
blink_on() const196 bool msm6222b_device::blink_on() const
197 {
198 	if(!cursor_blinking)
199 		return false;
200 	uint64_t clocks = machine().time().as_ticks(250000);
201 	if(double_height)
202 		return clocks % 281600 >= 140800;
203 	else
204 		return clocks % 204800 >= 102400;
205 }
206 
render()207 const uint8_t *msm6222b_device::render()
208 {
209 	memset(render_buf, 0, 80*16);
210 	if(!display_on)
211 		return render_buf;
212 
213 	int char_height = double_height ? 11 : 8;
214 
215 	for(int i=0; i<80; i++) {
216 		uint8_t c = ddram[(i+shift) % 80];
217 		if(c < 16)
218 			memcpy(render_buf + 16*i, double_height ? cgram + 8*(c & 6) : cgram + 8*(c & 7), char_height);
219 		else if (m_cgrom.found())
220 			memcpy(render_buf + 16*i, &m_cgrom[16*c], char_height);
221 	}
222 
223 	if(cursor_on) {
224 		int cpos = adc & 0x7f;
225 		if(two_line) {
226 			if((cpos >= 40 && cpos < 64) || cpos >= 64+40)
227 				cpos = -1;
228 			else if(cpos >= 64)
229 				cpos += 40-64;
230 		} else {
231 			if(cpos >= 80)
232 				cpos = -1;
233 		}
234 		if(cpos != -1) {
235 			cpos = (cpos + shift) % 80;
236 			render_buf[cpos*16 + (double_height ? 10 : 7)] |= 0x1f;
237 			if(blink_on())
238 				for(int i=0; i<char_height; i++)
239 					render_buf[cpos*16 + i] ^= 0x1f;
240 		}
241 	}
242 
243 	return render_buf;
244 }
245