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