1 // license:GPL-2.0+
2 // copyright-holders:Juergen Buchmueller
3 /***************************************************************************
4 vtech2.c
5
6 machine driver
7 Juergen Buchmueller <pullmoll@t-online.de> MESS driver, Jan 2000
8 Davide Moretti <dave@rimini.com> ROM dump and hardware description
9
10 TODO:
11 RS232 support.
12 Check if the FDC is really the same as in the
13 Laser 210/310 (aka VZ200/300) series.
14
15 ****************************************************************************/
16
17 #include "emu.h"
18 #include "includes/vtech2.h"
19
20 /* public */
21
22 /* static */
23
24 #define TRKSIZE_VZ 0x9a0 /* arbitrary (actually from analyzing format) */
25
26 static const uint8_t laser_fdc_wrprot[2] = {0x80, 0x80};
27
init_laser()28 void vtech2_state::init_laser()
29 {
30 init_waitstates();
31
32 uint8_t *gfx = memregion("gfx2")->base();
33 int i;
34
35 m_laser_track_x2[0] = m_laser_track_x2[1] = 80;
36 m_laser_fdc_bits = 8;
37 m_laser_drive = -1;
38 m_cart_size = 0;
39
40 for (i = 0; i < 256; i++)
41 gfx[i] = i;
42
43 m_laser_latch = -1;
44
45 // check ROM expansion
46 std::string region_tag;
47 m_cart_rom = memregion(region_tag.assign(m_cart->tag()).append(GENERIC_ROM_REGION_TAG).c_str());
48 }
49
50
DEVICE_IMAGE_LOAD_MEMBER(vtech2_state::cart_load)51 DEVICE_IMAGE_LOAD_MEMBER( vtech2_state::cart_load )
52 {
53 m_cart_size = m_cart->common_get_size("rom");
54
55 if (m_cart_size > 0x10000)
56 {
57 image.seterror(IMAGE_ERROR_UNSPECIFIED, "Cartridge bigger than 64k");
58 m_cart_size = 0;
59 return image_init_result::FAIL;
60 }
61
62 m_cart->rom_alloc(m_cart_size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
63 m_cart->common_load_rom(m_cart->get_rom_base(), m_cart_size, "rom");
64
65 return image_init_result::PASS;
66 }
67
machine_reset()68 void vtech2_state::machine_reset()
69 {
70 m_language = m_io_keyboard[5]->read() & 0x30;
71 }
72
machine_start()73 void vtech2_state::machine_start()
74 {
75 save_item(NAME(m_laser_frame_message));
76 save_item(NAME(m_laser_frame_time));
77 save_item(NAME(m_laser_latch));
78 save_item(NAME(m_laser_track_x2));
79 save_item(NAME(m_laser_fdc_status));
80 save_item(NAME(m_laser_fdc_data));
81 save_item(NAME(m_laser_data));
82 save_item(NAME(m_laser_fdc_edge));
83 save_item(NAME(m_laser_fdc_bits));
84 save_item(NAME(m_laser_drive));
85 save_item(NAME(m_laser_fdc_start));
86 save_item(NAME(m_laser_fdc_write));
87 save_item(NAME(m_laser_fdc_offs));
88 save_item(NAME(m_laser_fdc_latch));
89 save_item(NAME(m_level_old));
90 save_item(NAME(m_cassette_bit));
91 save_item(NAME(m_laser_bg_mode));
92 save_item(NAME(m_laser_two_color));
93 save_item(NAME(m_language));
94 save_item(NAME(m_cart_size));
95 }
96
cart_r(offs_t offset)97 uint8_t vtech2_state::cart_r(offs_t offset)
98 {
99 if (offset >= m_cart_size)
100 return 0xff;
101 return m_cart->read_rom(offset);
102 }
103
104 // The ULA inserts one waitstate for every read and write in each space (MT 07094, MT 07141)
init_waitstates()105 void vtech2_state::init_waitstates()
106 {
107 address_space &mem = m_maincpu->space(AS_PROGRAM);
108 address_space &io = m_maincpu->space(AS_IO);
109
110 mem.install_read_tap(0x0000, 0xffff, "mem_wait_r", [this](offs_t offset, u8 &data, u8 mem_mask)
111 {
112 if (!machine().side_effects_disabled())
113 m_maincpu->adjust_icount(-1);
114 return data;
115 });
116 mem.install_write_tap(0x0000, 0xffff, "mem_wait_w", [this](offs_t offset, u8 &data, u8 mem_mask)
117 {
118 if (!machine().side_effects_disabled())
119 m_maincpu->adjust_icount(-1);
120 return data;
121 });
122 io.install_read_tap(0x00, 0xff, "io_wait_r", [this](offs_t offset, u8 &data, u8 mem_mask)
123 {
124 if (!machine().side_effects_disabled())
125 m_maincpu->adjust_icount(-1);
126 return data;
127 });
128 io.install_write_tap(0x00, 0xff, "io_wait_w", [this](offs_t offset, u8 &data, u8 mem_mask)
129 {
130 if (!machine().side_effects_disabled())
131 m_maincpu->adjust_icount(-1);
132 return data;
133 });
134 }
135
136
137 /*************************************************
138 * memory mapped I/O read
139 * bit function
140 * 7 not assigned
141 * 6 column 6
142 * 5 column 5
143 * 4 column 4
144 * 3 column 3
145 * 2 column 2
146 * 1 column 1
147 * 0 column 0
148 ************************************************/
mmio_r(offs_t offset)149 uint8_t vtech2_state::mmio_r(offs_t offset)
150 {
151 u8 data = 0x7f;
152
153 offset = ~offset & 0x7ff;
154 if (BIT(offset, 10))
155 data &= m_io_keyboard[BIT(offset,8,2) + 8]->read(); // ROW A-D
156 else
157 for (u8 i = 0; i < 8; i++)
158 if (BIT(offset, i))
159 data &= m_io_keyboard[i]->read(); // ROW 0-7
160
161 /* BIT 7 - tape input */
162 data |= (m_cassette->input() > +0.02) ? 0x80 : 0;
163
164 return data;
165 }
166
167 /*************************************************
168 * memory mapped I/O write
169 * bit function
170 * 7-6 not assigned
171 * 5 speaker B ???
172 * 4 ???
173 * 3 mode: 1 graphics, 0 text
174 * 2 cassette out (MSB)
175 * 1 cassette out (LSB)
176 * 0 speaker A
177 ************************************************/
mmio_w(uint8_t data)178 void vtech2_state::mmio_w(uint8_t data)
179 {
180 m_speaker->level_w(data & 1);
181 m_laser_latch = data;
182 m_cassette->output( BIT(data, 2) ? -1.0 : +1.0);
183 }
184
185
laser_get_track()186 void vtech2_state::laser_get_track()
187 {
188 sprintf(m_laser_frame_message, "#%d get track %02d", m_laser_drive, m_laser_track_x2[m_laser_drive]/2);
189 m_laser_frame_time = 30;
190 /* drive selected or and image file ok? */
191 if( m_laser_drive >= 0 && m_laser_file[m_laser_drive].found() )
192 {
193 device_image_interface &image = *m_laser_file[m_laser_drive];
194 int size = TRKSIZE_VZ;
195 int offs = TRKSIZE_VZ * m_laser_track_x2[m_laser_drive]/2;
196 image.fseek(offs, SEEK_SET);
197 size = image.fread(m_laser_fdc_data, size);
198 logerror("get track @$%05x $%04x bytes\n", offs, size);
199 }
200 m_laser_fdc_offs = 0;
201 m_laser_fdc_write = 0;
202 }
203
laser_put_track()204 void vtech2_state::laser_put_track()
205 {
206 /* drive selected and image file ok? */
207 if( m_laser_drive >= 0 && m_laser_file[m_laser_drive].found() )
208 {
209 device_image_interface &image = *m_laser_file[m_laser_drive];
210 int offs = TRKSIZE_VZ * m_laser_track_x2[m_laser_drive]/2;
211 image.fseek(offs + m_laser_fdc_start, SEEK_SET);
212 int size = image.fwrite(&m_laser_fdc_data[m_laser_fdc_start], m_laser_fdc_write);
213 logerror("put track @$%05X+$%X $%04X/$%04X bytes\n", offs, m_laser_fdc_start, size, m_laser_fdc_write);
214 }
215 }
216
217 #define PHI0(n) (((n)>>0)&1)
218 #define PHI1(n) (((n)>>1)&1)
219 #define PHI2(n) (((n)>>2)&1)
220 #define PHI3(n) (((n)>>3)&1)
221
laser_fdc_r(offs_t offset)222 uint8_t vtech2_state::laser_fdc_r(offs_t offset)
223 {
224 int data = 0xff;
225 switch( offset )
226 {
227 case 1: /* data (read-only) */
228 if( m_laser_fdc_bits > 0 )
229 {
230 if( m_laser_fdc_status & 0x80 )
231 m_laser_fdc_bits--;
232 data = (m_laser_data >> m_laser_fdc_bits) & 0xff;
233 #if 0
234 logerror("laser_fdc_r bits %d%d%d%d%d%d%d%d\n",
235 (data>>7)&1,(data>>6)&1,(data>>5)&1,(data>>4)&1,
236 (data>>3)&1,(data>>2)&1,(data>>1)&1,(data>>0)&1 );
237 #endif
238 }
239 if( m_laser_fdc_bits == 0 )
240 {
241 m_laser_data = m_laser_fdc_data[m_laser_fdc_offs];
242 logerror("laser_fdc_r %d : data ($%04X) $%02X\n", offset, m_laser_fdc_offs, m_laser_data);
243 if( m_laser_fdc_status & 0x80 )
244 {
245 m_laser_fdc_bits = 8;
246 m_laser_fdc_offs = (m_laser_fdc_offs + 1) % TRKSIZE_FM;
247 }
248 m_laser_fdc_status &= ~0x80;
249 }
250 break;
251 case 2: /* polling (read-only) */
252 /* fake */
253 if( m_laser_drive >= 0 )
254 m_laser_fdc_status |= 0x80;
255 data = m_laser_fdc_status;
256 break;
257 case 3: /* write protect status (read-only) */
258 if( m_laser_drive >= 0 )
259 data = laser_fdc_wrprot[m_laser_drive];
260 logerror("laser_fdc_r %d : write_protect $%02X\n", offset, data);
261 break;
262 }
263 return data;
264 }
265
laser_fdc_w(offs_t offset,uint8_t data)266 void vtech2_state::laser_fdc_w(offs_t offset, uint8_t data)
267 {
268 int drive;
269
270 switch( offset )
271 {
272 case 0: /* latch (write-only) */
273 drive = (data & 0x10) ? 0 : (data & 0x80) ? 1 : -1;
274 if( drive != m_laser_drive )
275 {
276 m_laser_drive = drive;
277 if( m_laser_drive >= 0 )
278 laser_get_track();
279 }
280 if( m_laser_drive >= 0 )
281 {
282 if( (PHI0(data) && !(PHI1(data) || PHI2(data) || PHI3(data)) && PHI1(m_laser_fdc_latch)) ||
283 (PHI1(data) && !(PHI0(data) || PHI2(data) || PHI3(data)) && PHI2(m_laser_fdc_latch)) ||
284 (PHI2(data) && !(PHI0(data) || PHI1(data) || PHI3(data)) && PHI3(m_laser_fdc_latch)) ||
285 (PHI3(data) && !(PHI0(data) || PHI1(data) || PHI2(data)) && PHI0(m_laser_fdc_latch)) )
286 {
287 if( m_laser_track_x2[m_laser_drive] > 0 )
288 m_laser_track_x2[m_laser_drive]--;
289 logerror("laser_fdc_w(%d) $%02X drive %d: stepout track #%2d.%d\n", offset, data, m_laser_drive, m_laser_track_x2[m_laser_drive]/2,5*(m_laser_track_x2[m_laser_drive]&1));
290 if( (m_laser_track_x2[m_laser_drive] & 1) == 0 )
291 laser_get_track();
292 }
293 else
294 if( (PHI0(data) && !(PHI1(data) || PHI2(data) || PHI3(data)) && PHI3(m_laser_fdc_latch)) ||
295 (PHI1(data) && !(PHI0(data) || PHI2(data) || PHI3(data)) && PHI0(m_laser_fdc_latch)) ||
296 (PHI2(data) && !(PHI0(data) || PHI1(data) || PHI3(data)) && PHI1(m_laser_fdc_latch)) ||
297 (PHI3(data) && !(PHI0(data) || PHI1(data) || PHI2(data)) && PHI2(m_laser_fdc_latch)) )
298 {
299 if( m_laser_track_x2[m_laser_drive] < 2*40 )
300 m_laser_track_x2[m_laser_drive]++;
301 logerror("laser_fdc_w(%d) $%02X drive %d: stepin track #%2d.%d\n", offset, data, m_laser_drive, m_laser_track_x2[m_laser_drive]/2,5*(m_laser_track_x2[m_laser_drive]&1));
302 if( (m_laser_track_x2[m_laser_drive] & 1) == 0 )
303 laser_get_track();
304 }
305 if( (data & 0x40) == 0 )
306 {
307 m_laser_data <<= 1;
308 if( (m_laser_fdc_latch ^ data) & 0x20 )
309 m_laser_data |= 1;
310 if( (m_laser_fdc_edge ^= 1) == 0 )
311 {
312 if( --m_laser_fdc_bits == 0 )
313 {
314 uint8_t value = 0;
315 m_laser_data &= 0xffff;
316 if( m_laser_data & 0x4000 ) value |= 0x80;
317 if( m_laser_data & 0x1000 ) value |= 0x40;
318 if( m_laser_data & 0x0400 ) value |= 0x20;
319 if( m_laser_data & 0x0100 ) value |= 0x10;
320 if( m_laser_data & 0x0040 ) value |= 0x08;
321 if( m_laser_data & 0x0010 ) value |= 0x04;
322 if( m_laser_data & 0x0004 ) value |= 0x02;
323 if( m_laser_data & 0x0001 ) value |= 0x01;
324 logerror("laser_fdc_w(%d) data($%04X) $%02X <- $%02X ($%04X)\n", offset, m_laser_fdc_offs, m_laser_fdc_data[m_laser_fdc_offs], value, m_laser_data);
325 m_laser_fdc_data[m_laser_fdc_offs] = value;
326 m_laser_fdc_offs = (m_laser_fdc_offs + 1) % TRKSIZE_FM;
327 m_laser_fdc_write++;
328 m_laser_fdc_bits = 8;
329 }
330 }
331 }
332 /* change of write signal? */
333 if( (m_laser_fdc_latch ^ data) & 0x40 )
334 {
335 /* falling edge? */
336 if ( m_laser_fdc_latch & 0x40 )
337 {
338 sprintf(m_laser_frame_message, "#%d put track %02d", m_laser_drive, m_laser_track_x2[m_laser_drive]/2);
339 m_laser_frame_time = 30;
340 m_laser_fdc_start = m_laser_fdc_offs;
341 m_laser_fdc_edge = 0;
342 }
343 else
344 {
345 /* data written to track before? */
346 if( m_laser_fdc_write )
347 laser_put_track();
348 }
349 m_laser_fdc_bits = 8;
350 m_laser_fdc_write = 0;
351 }
352 }
353 m_laser_fdc_latch = data;
354 break;
355 }
356 }
357