Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
716 | lvd | 1 | #include "std.h" |
2 | |||
3 | #include "emul.h" |
||
4 | #include "vars.h" |
||
5 | #include "tape.h" |
||
6 | #include "memory.h" |
||
7 | |||
8 | #include "util.h" |
||
9 | #include "png/zlib.h" |
||
10 | |||
11 | #define Z80FQ 3500000 |
||
12 | |||
13 | unsigned tape_pulse[0x100]; // Алфавит из импульсов разной длины (число символов алфавита не более 256, ссылки однобайтовые) |
||
14 | unsigned max_pulses = 0; |
||
15 | unsigned tape_err = 0; |
||
16 | |||
17 | unsigned char *tape_image = 0; // Массив ссылок на алфавит (ссылки однобайтовые) |
||
18 | unsigned tape_imagesize = 0; |
||
19 | |||
20 | TAPEINFO *tapeinfo; |
||
21 | unsigned tape_infosize; |
||
22 | |||
23 | unsigned appendable; |
||
24 | |||
25 | typedef int (__cdecl *inflateInit__ptr)(z_streamp strm, const char *version, int stream_size); |
||
26 | typedef int (__cdecl *inflate_ptr)(z_streamp strm, int flush); |
||
27 | typedef int (__cdecl *inflateEnd_ptr)(z_streamp strm); |
||
28 | |||
29 | static inflateInit__ptr inflateInit__p = 0; |
||
30 | static inflate_ptr inflate_p = 0; |
||
31 | static inflateEnd_ptr inflateEnd_p = 0; |
||
32 | |||
33 | static HMODULE ZlibDll = 0; |
||
34 | |||
35 | bool ZlibInit() |
||
36 | { |
||
37 | ZlibDll = LoadLibrary("zlib1.dll"); |
||
38 | if(!ZlibDll) |
||
39 | return false; |
||
40 | inflateInit__p = (inflateInit__ptr)GetProcAddress(ZlibDll, "inflateInit_"); |
||
41 | if(!inflateInit__p) |
||
42 | return false; |
||
43 | inflate_p = (inflate_ptr)GetProcAddress(ZlibDll,"inflate"); |
||
44 | if(!inflate_p) |
||
45 | return false; |
||
46 | inflateEnd_p = (inflateEnd_ptr)GetProcAddress(ZlibDll,"inflateEnd"); |
||
47 | if(!inflateEnd_p) |
||
48 | return false; |
||
49 | return true; |
||
50 | } |
||
51 | |||
52 | void ZlibDone() |
||
53 | { |
||
54 | if(ZlibDll) |
||
55 | FreeLibrary(ZlibDll); |
||
56 | } |
||
57 | |||
58 | // Ищет длину импульса в алфавите и возвращает индекс найденного элемента |
||
59 | // Если импульс с такой шириной не найден, он добавляется в конец и возвращается его индекс |
||
60 | // tape image contains indexes in tape_pulse[] |
||
61 | unsigned find_pulse(unsigned t) |
||
62 | { |
||
63 | if (max_pulses < _countof(tape_pulse)) |
||
64 | { |
||
65 | double e = 0.10 * t; // разброс 10% |
||
66 | for (unsigned i = 0; i < max_pulses; i++) |
||
67 | { |
||
68 | if ((t - e) < tape_pulse[i] && tape_pulse[i] <= (t + e)) |
||
69 | { |
||
70 | return i; |
||
71 | } |
||
72 | } |
||
73 | tape_pulse[max_pulses] = t; |
||
74 | return max_pulses++; |
||
75 | } |
||
76 | if (!tape_err) |
||
77 | { |
||
78 | errmsg("pulse table full"); |
||
79 | tape_err = 1; |
||
80 | } |
||
81 | unsigned nearest = 0; int delta = 0x7FFFFFFF; |
||
82 | for (unsigned i = 0; i < _countof(tape_pulse); i++) |
||
83 | { |
||
84 | if (delta > abs((int)t - (int)tape_pulse[i])) |
||
85 | { |
||
86 | nearest = i; |
||
87 | delta = abs((int)t - (int)tape_pulse[i]); |
||
88 | } |
||
89 | } |
||
90 | return nearest; |
||
91 | } |
||
92 | |||
93 | void find_tape_index() |
||
94 | { |
||
95 | for (unsigned i = 0; i < tape_infosize; i++) |
||
96 | if (comp.tape.play_pointer >= tape_image + tapeinfo[i].pos) |
||
97 | comp.tape.index = i; |
||
98 | temp.led.tape_started = -600*3500000; |
||
99 | } |
||
100 | |||
101 | void find_tape_sizes() |
||
102 | { |
||
103 | for (unsigned i = 0; i < tape_infosize; i++) { |
||
104 | unsigned end = (i == tape_infosize-1) ? tape_imagesize : tapeinfo[i+1].pos; |
||
105 | |||
106 | unsigned sz = 0; |
||
107 | for (unsigned j = tapeinfo[i].pos; j < end; j++) |
||
108 | sz += tape_pulse[tape_image[j]]; |
||
109 | tapeinfo[i].t_size = sz; |
||
110 | } |
||
111 | } |
||
112 | |||
113 | void stop_tape() |
||
114 | { |
||
115 | find_tape_index(); |
||
116 | const char *msg = "tape stopped"; |
||
117 | if (comp.tape.play_pointer == comp.tape.end_of_tape) |
||
118 | comp.tape.index = 0, msg = "end of tape"; |
||
119 | comp.tape.play_pointer = 0; |
||
120 | strcpy(statusline, msg); statcnt = 40; |
||
121 | comp.tape.edge_change = 0x7FFFFFFFFFFFFFFFULL; |
||
122 | comp.tape.tape_bit = -1; |
||
123 | } |
||
124 | |||
125 | void reset_tape() |
||
126 | { |
||
127 | comp.tape.index = 0; |
||
128 | comp.tape.play_pointer = 0; |
||
129 | comp.tape.edge_change = 0x7FFFFFFFFFFFFFFFULL; |
||
130 | comp.tape.tape_bit = -1; |
||
131 | } |
||
132 | |||
133 | void start_tape() |
||
134 | { |
||
135 | if (!tape_image) return; |
||
136 | strcpy(statusline, "tape started"); statcnt = 40; |
||
137 | comp.tape.play_pointer = tape_image + tapeinfo[comp.tape.index].pos; |
||
138 | comp.tape.end_of_tape = tape_image + tape_imagesize; |
||
139 | comp.tape.edge_change = comp.t_states + cpu.t; |
||
140 | temp.led.tape_started = -600*3500000; |
||
141 | comp.tape.tape_bit = -1; |
||
142 | } |
||
143 | |||
144 | void closetape() |
||
145 | { |
||
146 | if (tape_image) free(tape_image), tape_image = 0; |
||
147 | if (tapeinfo) free(tapeinfo), tapeinfo = 0; |
||
148 | comp.tape.play_pointer = 0; // stop tape |
||
149 | comp.tape.index = 0; // rewind tape |
||
150 | tape_err = max_pulses = tape_imagesize = tape_infosize = 0; |
||
151 | comp.tape.edge_change = 0x7FFFFFFFFFFFFFFFULL; |
||
152 | comp.tape.tape_bit = -1; |
||
153 | } |
||
154 | |||
155 | void reserve(unsigned datasize) |
||
156 | { |
||
157 | const int blocksize = 16384; |
||
158 | unsigned newsize = align_by(datasize+tape_imagesize+1, blocksize); |
||
159 | if (!tape_image) tape_image = (unsigned char*)malloc(newsize); |
||
160 | if (align_by(tape_imagesize, blocksize) < newsize) tape_image = (unsigned char*)realloc(tape_image, newsize); |
||
161 | } |
||
162 | |||
163 | void makeblock(const unsigned char *data, unsigned size, unsigned pilot_t, |
||
164 | unsigned s1_t, unsigned s2_t, unsigned zero_t, unsigned one_t, |
||
165 | unsigned pilot_len, unsigned pause, unsigned char last = 8) |
||
166 | { |
||
167 | reserve(size*16 + pilot_len + 3); |
||
168 | if (pilot_len != -1) { |
||
169 | unsigned t = find_pulse(pilot_t); |
||
170 | for (unsigned i = 0; i < pilot_len; i++) |
||
171 | tape_image[tape_imagesize++] = t; |
||
172 | tape_image[tape_imagesize++] = find_pulse(s1_t); |
||
173 | tape_image[tape_imagesize++] = find_pulse(s2_t); |
||
174 | } |
||
175 | unsigned t0 = find_pulse(zero_t), t1 = find_pulse(one_t); |
||
176 | for (; size>1; size--, data++) |
||
177 | for (unsigned char j = 0x80; j; j >>= 1) |
||
178 | tape_image[tape_imagesize++] = (*data & j) ? t1 : t0, |
||
179 | tape_image[tape_imagesize++] = (*data & j) ? t1 : t0; |
||
180 | for (unsigned char j = 0x80; j != (unsigned char)(0x80 >> last); j >>= 1) // last byte |
||
181 | tape_image[tape_imagesize++] = (*data & j) ? t1 : t0, |
||
182 | tape_image[tape_imagesize++] = (*data & j) ? t1 : t0; |
||
183 | if (pause) tape_image[tape_imagesize++] = find_pulse(pause*3500); |
||
184 | } |
||
185 | |||
186 | void desc(const unsigned char *data, unsigned size, char *dst) |
||
187 | { |
||
188 | unsigned char crc = 0; char prg[10]; |
||
189 | unsigned i; //Alone Coder 0.36.7 |
||
190 | for (/*unsigned*/ i = 0; i < size; i++) crc ^= data[i]; |
||
191 | if (!*data && size == 19 && (data[1] == 0 || data[1] == 3)) { |
||
192 | for (i = 0; i < 10; i++) prg[i] = (data[i+2] < ' ' || data[i+2] >= 0x80) ? '?' : data[i+2]; |
||
193 | for (i = 9; i && prg[i] == ' '; prg[i--] = 0); |
||
194 | sprintf(dst, "%s: \"%s\" %d,%d", data[1] ? "Bytes":"Program", prg, |
||
195 | *(unsigned short*)(data+14), *(unsigned short*)(data+12)); |
||
196 | } else if (*data == 0xFF) sprintf(dst, "data block, %d bytes", size-2); |
||
197 | else sprintf(dst, "#%02X block, %d bytes", *data, size-2); |
||
198 | sprintf(dst + strlen(dst), ", crc %s", crc ? "bad":"ok"); |
||
199 | } |
||
200 | |||
201 | static bool alloc_infocell() |
||
202 | { |
||
203 | TAPEINFO *tmp = (TAPEINFO*)realloc(tapeinfo, (tape_infosize+1)*sizeof(TAPEINFO)); |
||
204 | if(!tmp) |
||
205 | { |
||
206 | return false; |
||
207 | } |
||
208 | tapeinfo = tmp; |
||
209 | tapeinfo[tape_infosize].pos = tape_imagesize; |
||
210 | appendable = 0; |
||
211 | return true; |
||
212 | } |
||
213 | |||
214 | bool named_cell(const void *nm, unsigned sz = 0) |
||
215 | { |
||
216 | if(!alloc_infocell()) |
||
217 | return false; |
||
218 | |||
219 | const size_t n = _countof(tapeinfo[tape_infosize].desc)-1; |
||
220 | size_t len = min(n, sz ? sz : strlen((const char*)nm)); |
||
221 | strncpy(tapeinfo[tape_infosize].desc, (const char*)nm, len); |
||
222 | tapeinfo[tape_infosize].desc[len] = 0; |
||
223 | tape_infosize++; |
||
224 | return true; |
||
225 | } |
||
226 | |||
227 | int readTAP() |
||
228 | { |
||
229 | unsigned char *ptr = snbuf; closetape(); |
||
230 | while (ptr < snbuf+snapsize) { |
||
231 | unsigned size = *(unsigned short*)ptr; ptr += 2; |
||
232 | if (!size) break; |
||
233 | alloc_infocell(); |
||
234 | desc(ptr, size, tapeinfo[tape_infosize].desc); |
||
235 | tape_infosize++; |
||
236 | makeblock(ptr, size, 2168, 667, 735, 855, 1710, (*ptr < 4) ? 8064 : 3220, 1000); |
||
237 | ptr += size; |
||
238 | } |
||
239 | find_tape_sizes(); |
||
240 | return (ptr == snbuf+snapsize); |
||
241 | } |
||
242 | |||
243 | #pragma pack(push, 1) |
||
244 | struct TCswHdr |
||
245 | { |
||
246 | char Signature[22]; |
||
247 | u8 Term; |
||
248 | u8 VerMajor; |
||
249 | u8 VerMinor; |
||
250 | union |
||
251 | { |
||
252 | struct |
||
253 | { |
||
254 | u16 SampleRate; |
||
255 | u8 CompressionType; |
||
256 | u8 Flags; |
||
257 | u8 Reserved[3]; |
||
723 | lvd | 258 | u8 Data[1]; |
716 | lvd | 259 | } Ver1; |
260 | |||
261 | struct |
||
262 | { |
||
263 | u32 SampleRate; |
||
264 | u32 PulsesAfterDecompression; |
||
265 | u8 CompressionType; |
||
266 | u8 Flags; |
||
267 | u8 HeaderExtLen; |
||
268 | char EncAppDesc[16]; |
||
723 | lvd | 269 | u8 ExtHdr[1]; |
716 | lvd | 270 | } Ver2; |
271 | }; |
||
272 | }; |
||
273 | #pragma pack(pop) |
||
274 | |||
275 | int readCSW() |
||
276 | { |
||
277 | closetape(); |
||
278 | named_cell("CSW tape image"); |
||
279 | |||
280 | const TCswHdr *CswHdr = (TCswHdr *)snbuf; |
||
281 | u8 CompType; |
||
282 | u32 SampleRate; |
||
283 | u8 Flags; |
||
284 | u32 DataOffset; |
||
285 | u32 PulsesCount; |
||
286 | switch(CswHdr->VerMajor) |
||
287 | { |
||
288 | case 1: |
||
289 | CompType = CswHdr->Ver1.CompressionType; |
||
290 | SampleRate = CswHdr->Ver1.SampleRate; |
||
291 | Flags = CswHdr->Ver1.Flags; |
||
292 | DataOffset = offsetof(TCswHdr, Ver1.Data); |
||
293 | PulsesCount = snapsize - 0x18; // Непонятная константа |
||
294 | break; |
||
295 | |||
296 | case 2: |
||
297 | CompType = CswHdr->Ver2.CompressionType; |
||
298 | SampleRate = CswHdr->Ver2.SampleRate; |
||
299 | Flags = CswHdr->Ver2.Flags; |
||
300 | DataOffset = offsetof(TCswHdr, Ver2.ExtHdr) + CswHdr->Ver2.HeaderExtLen; |
||
301 | PulsesCount = CswHdr->Ver2.PulsesAfterDecompression; |
||
302 | break; |
||
303 | |||
304 | default: // unknown csw version |
||
305 | return 0; |
||
306 | } |
||
307 | |||
308 | u32 UncompressedSize = snapsize; |
||
309 | switch(CompType) |
||
310 | { |
||
311 | case 2: // Z-RLE |
||
312 | { |
||
313 | if(!temp.ZlibSupport) |
||
314 | return 0; |
||
315 | static const size_t out_sz = sizeof(snbuf); |
||
316 | void *out = malloc(out_sz); |
||
317 | if(!out) |
||
318 | return 0; |
||
319 | |||
320 | z_stream strm; |
||
321 | strm.zalloc = Z_NULL; |
||
322 | strm.zfree = Z_NULL; |
||
323 | strm.opaque = Z_NULL; |
||
324 | strm.avail_in = snapsize - DataOffset; |
||
325 | strm.next_in = snbuf + DataOffset; |
||
326 | int ret = inflateInit__p(&strm, ZLIB_VERSION, sizeof(strm)); |
||
327 | if(ret != Z_OK) |
||
328 | { |
||
329 | free(out); |
||
330 | return 0; |
||
331 | } |
||
332 | |||
333 | strm.avail_out = out_sz; |
||
334 | strm.next_out = (Byte *)out; |
||
335 | ret = inflate_p(&strm, Z_FINISH); |
||
336 | if(ret < 0) |
||
337 | { |
||
338 | free(out); |
||
339 | inflateEnd_p(&strm); |
||
340 | return 0; |
||
341 | } |
||
342 | UncompressedSize = out_sz - strm.avail_out; |
||
343 | memcpy(snbuf + DataOffset, out, UncompressedSize); |
||
344 | UncompressedSize += DataOffset; |
||
345 | free(out); |
||
346 | inflateEnd_p(&strm); |
||
347 | } |
||
348 | case 1: // RLE |
||
349 | break; |
||
350 | |||
351 | default: |
||
352 | return 0; // unknown compression type |
||
353 | } |
||
354 | |||
355 | unsigned rate = Z80FQ / SampleRate; // usually 3.5mhz / 44khz |
||
356 | if (!rate) |
||
357 | return 0; |
||
358 | |||
359 | reserve(PulsesCount); |
||
360 | |||
361 | if (!(Flags & 1)) |
||
362 | tape_image[tape_imagesize++] = find_pulse(1); |
||
363 | |||
364 | unsigned i = 0; |
||
365 | for (unsigned char *ptr = snbuf + DataOffset; ptr < snbuf + UncompressedSize; i++) |
||
366 | { |
||
367 | unsigned len = *ptr++ * rate; |
||
368 | if (!len) |
||
369 | { |
||
370 | len = *(unsigned*)ptr * rate; |
||
371 | ptr += 4; |
||
372 | } |
||
373 | tape_image[tape_imagesize++] = find_pulse(len); |
||
374 | } |
||
375 | |||
376 | tape_image[tape_imagesize++] = find_pulse(Z80FQ/10); |
||
377 | find_tape_sizes(); |
||
378 | return 1; |
||
379 | } |
||
380 | |||
381 | void create_appendable_block() |
||
382 | { |
||
383 | if (!tape_infosize || appendable) return; |
||
384 | named_cell("set of pulses"); appendable = 1; |
||
385 | } |
||
386 | |||
387 | void parse_hardware(const unsigned char *ptr) |
||
388 | { |
||
389 | unsigned n = *ptr++; |
||
390 | if (!n) return; |
||
391 | named_cell("- HARDWARE TYPE "); |
||
392 | static char ids[] = |
||
393 | "computer\0" |
||
394 | "ZX Spectrum 16k\0" |
||
395 | "ZX Spectrum 48k, Plus\0" |
||
396 | "ZX Spectrum 48k ISSUE 1\0" |
||
397 | "ZX Spectrum 128k (Sinclair)\0" |
||
398 | "ZX Spectrum 128k +2 (Grey case)\0" |
||
399 | "ZX Spectrum 128k +2A, +3\0" |
||
400 | "Timex Sinclair TC-2048\0" |
||
401 | "Timex Sinclair TS-2068\0" |
||
402 | "Pentagon 128\0" |
||
403 | "Sam Coupe\0" |
||
404 | "Didaktik M\0" |
||
405 | "Didaktik Gama\0" |
||
406 | "ZX-81 or TS-1000 with 1k RAM\0" |
||
407 | "ZX-81 or TS-1000 with 16k RAM or more\0" |
||
408 | "ZX Spectrum 128k, Spanish version\0" |
||
409 | "ZX Spectrum, Arabic version\0" |
||
410 | "TK 90-X\0" |
||
411 | "TK 95\0" |
||
412 | "Byte\0" |
||
413 | "Elwro\0" |
||
414 | "ZS Scorpion\0" |
||
415 | "Amstrad CPC 464\0" |
||
416 | "Amstrad CPC 664\0" |
||
417 | "Amstrad CPC 6128\0" |
||
418 | "Amstrad CPC 464+\0" |
||
419 | "Amstrad CPC 6128+\0" |
||
420 | "Jupiter ACE\0" |
||
421 | "Enterprise\0" |
||
422 | "Commodore 64\0" |
||
423 | "Commodore 128\0" |
||
424 | "\0" |
||
425 | "ext. storage\0" |
||
426 | "Microdrive\0" |
||
427 | "Opus Discovery\0" |
||
428 | "Disciple\0" |
||
429 | "Plus-D\0" |
||
430 | "Rotronics Wafadrive\0" |
||
431 | "TR-DOS (BetaDisk)\0" |
||
432 | "Byte Drive\0" |
||
433 | "Watsford\0" |
||
434 | "FIZ\0" |
||
435 | "Radofin\0" |
||
436 | "Didaktik disk drives\0" |
||
437 | "BS-DOS (MB-02)\0" |
||
438 | "ZX Spectrum +3 disk drive\0" |
||
439 | "JLO (Oliger) disk interface\0" |
||
440 | "FDD3000\0" |
||
441 | "Zebra disk drive\0" |
||
442 | "Ramex Millenia\0" |
||
443 | "Larken\0" |
||
444 | "\0" |
||
445 | "ROM/RAM type add-on\0" |
||
446 | "Sam Ram\0" |
||
447 | "Multiface\0" |
||
448 | "Multiface 128k\0" |
||
449 | "Multiface +3\0" |
||
450 | "MultiPrint\0" |
||
451 | "MB-02 ROM/RAM expansion\0" |
||
452 | "\0" |
||
453 | "sound device\0" |
||
454 | "Classic AY hardware\0" |
||
455 | "Fuller Box AY sound hardware\0" |
||
456 | "Currah microSpeech\0" |
||
457 | "SpecDrum\0" |
||
458 | "AY ACB stereo; Melodik\0" |
||
459 | "AY ABC stereo\0" |
||
460 | "\0" |
||
461 | "joystick\0" |
||
462 | "Kempston\0" |
||
463 | "Cursor, Protek, AGF\0" |
||
464 | "Sinclair 2\0" |
||
465 | "Sinclair 1\0" |
||
466 | "Fuller\0" |
||
467 | "\0" |
||
468 | "mice\0" |
||
469 | "AMX mouse\0" |
||
470 | "Kempston mouse\0" |
||
471 | "\0" |
||
472 | "other controller\0" |
||
473 | "Trickstick\0" |
||
474 | "ZX Light Gun\0" |
||
475 | "Zebra Graphics Tablet\0" |
||
476 | "\0" |
||
477 | "serial port\0" |
||
478 | "ZX Interface 1\0" |
||
479 | "ZX Spectrum 128k\0" |
||
480 | "\0" |
||
481 | "parallel port\0" |
||
482 | "Kempston S\0" |
||
483 | "Kempston E\0" |
||
484 | "ZX Spectrum 128k +2A, +3\0" |
||
485 | "Tasman\0" |
||
486 | "DK'Tronics\0" |
||
487 | "Hilderbay\0" |
||
488 | "INES Printerface\0" |
||
489 | "ZX LPrint Interface 3\0" |
||
490 | "MultiPrint\0" |
||
491 | "Opus Discovery\0" |
||
492 | "Standard 8255 chip with ports 31,63,95\0" |
||
493 | "\0" |
||
494 | "printer\0" |
||
495 | "ZX Printer, Alphacom 32 & compatibles\0" |
||
496 | "Generic Printer\0" |
||
497 | "EPSON Compatible\0" |
||
498 | "\0" |
||
499 | "modem\0" |
||
500 | "VTX 5000\0" |
||
501 | "T/S 2050 or Westridge 2050\0" |
||
502 | "\0" |
||
503 | "digitaiser\0" |
||
504 | "RD Digital Tracer\0" |
||
505 | "DK'Tronics Light Pen\0" |
||
506 | "British MicroGraph Pad\0" |
||
507 | "\0" |
||
508 | "network adapter\0" |
||
509 | "ZX Interface 1\0" |
||
510 | "\0" |
||
511 | "keyboard / keypad\0" |
||
512 | "Keypad for ZX Spectrum 128k\0" |
||
513 | "\0" |
||
514 | "AD/DA converter\0" |
||
515 | "Harley Systems ADC 8.2\0" |
||
516 | "Blackboard Electronics\0" |
||
517 | "\0" |
||
518 | "EPROM Programmer\0" |
||
519 | "Orme Electronics\0" |
||
520 | "\0" |
||
521 | "\0"; |
||
522 | for (unsigned i = 0; i < n; i++) { |
||
523 | unsigned char type_n = *ptr++; |
||
524 | unsigned char id_n = *ptr++; |
||
525 | unsigned char value_n = *ptr++; |
||
526 | const char *type = ids, *id, *value; |
||
527 | unsigned j; //Alone Coder 0.36.7 |
||
528 | for (/*unsigned*/ j = 0; j < type_n; j++) { |
||
529 | if (!*type) break; |
||
530 | while (*(short*)type) type++; |
||
531 | type += 2; |
||
532 | } |
||
533 | if (!*type) type = id = "??"; else { |
||
534 | id = type + strlen(type) + 1; |
||
535 | for (j = 0; j < id_n; j++) { |
||
536 | if (!*id) { id = "??"; break; } |
||
537 | id += strlen(id)+1; |
||
538 | } |
||
539 | } |
||
540 | switch (value_n) { |
||
541 | case 0: value = "compatible with"; break; |
||
542 | case 1: value = "uses"; break; |
||
543 | case 2: value = "compatible, but doesn't use"; break; |
||
544 | case 3: value = "incompatible with"; break; |
||
545 | default: value = "??"; |
||
546 | } |
||
547 | char bf[512]; sprintf(bf, "%s %s: %s", value, type, id); |
||
548 | named_cell(bf); |
||
549 | } |
||
550 | named_cell("-"); |
||
551 | } |
||
552 | |||
553 | int readTZX() |
||
554 | { |
||
555 | const unsigned char *ptr = snbuf; |
||
556 | closetape(); |
||
557 | unsigned size, pause, i, j, n, t, t0; |
||
558 | unsigned char pl, last; |
||
559 | const unsigned char *end; |
||
560 | char *p; |
||
561 | unsigned loop_n = 0, loop_p; |
||
562 | unsigned TzxVer = (ptr[8] << 8) | ptr[9]; |
||
563 | char nm[512]; |
||
564 | |||
565 | ptr += 10; // Пропуск заголовка |
||
566 | |||
567 | while (ptr < snbuf+snapsize) |
||
568 | { |
||
569 | switch (*ptr++) { |
||
570 | case 0x10: // normal block |
||
571 | alloc_infocell(); |
||
572 | size = *(unsigned short*)(ptr+2); |
||
573 | pause = *(unsigned short*)ptr; |
||
574 | ptr += 4; |
||
575 | desc(ptr, size, tapeinfo[tape_infosize].desc); |
||
576 | tape_infosize++; |
||
577 | makeblock(ptr, size, 2168, 667, 735, 855, 1710, |
||
578 | (*ptr < 4) ? 8064 : 3220, pause); |
||
579 | ptr += size; |
||
580 | break; |
||
581 | case 0x11: // turbo block |
||
582 | alloc_infocell(); |
||
583 | size = 0xFFFFFF & *(unsigned*)(ptr+0x0F); |
||
584 | desc(ptr + 0x12, size, tapeinfo[tape_infosize].desc); |
||
585 | tape_infosize++; |
||
586 | makeblock(ptr + 0x12, size, |
||
587 | *(unsigned short*)ptr, *(unsigned short*)(ptr+2), |
||
588 | *(unsigned short*)(ptr+4), *(unsigned short*)(ptr+6), |
||
589 | *(unsigned short*)(ptr+8), *(unsigned short*)(ptr+10), |
||
590 | *(unsigned short*)(ptr+13), ptr[12]); |
||
591 | // todo: test used bits - ptr+12 |
||
592 | ptr += size + 0x12; |
||
593 | break; |
||
594 | case 0x12: // pure tone |
||
595 | create_appendable_block(); |
||
596 | pl = find_pulse(*(unsigned short*)ptr); |
||
597 | n = *(unsigned short*)(ptr+2); |
||
598 | reserve(n); |
||
599 | for (i = 0; i < n; i++) tape_image[tape_imagesize++] = pl; |
||
600 | ptr += 4; |
||
601 | break; |
||
602 | case 0x13: // sequence of pulses of different lengths |
||
603 | create_appendable_block(); |
||
604 | n = *ptr++; |
||
605 | reserve(n); |
||
606 | for (i = 0; i < n; i++, ptr += 2) |
||
607 | tape_image[tape_imagesize++] = find_pulse(*(unsigned short*)ptr); |
||
608 | break; |
||
609 | case 0x14: // pure data block |
||
610 | create_appendable_block(); |
||
611 | size = 0xFFFFFF & *(unsigned*)(ptr+7); |
||
612 | makeblock(ptr + 0x0A, size, 0, 0, 0, *(unsigned short*)ptr, |
||
613 | *(unsigned short*)(ptr+2), -1, *(unsigned short*)(ptr+5), ptr[4]); |
||
614 | ptr += size + 0x0A; |
||
615 | break; |
||
616 | case 0x15: // direct recording |
||
617 | size = 0xFFFFFF & *(unsigned*)(ptr+5); |
||
618 | t0 = *(unsigned short*)ptr; |
||
619 | pause = *(unsigned short*)(ptr+2); |
||
620 | last = ptr[4]; |
||
621 | named_cell("direct recording"); |
||
622 | ptr += 8; |
||
623 | pl = 0; n = 0; |
||
624 | for (i = 0; i < size; i++) // count number of pulses |
||
625 | for (j = 0x80; j; j >>= 1) |
||
626 | if ((ptr[i] ^ pl) & j) n++, pl ^= -1; |
||
627 | t = 0; pl = 0; |
||
628 | reserve(n+2); |
||
629 | for (i = 1; i < size; i++, ptr++) // find pulses |
||
630 | for (j = 0x80; j; j >>= 1) { |
||
631 | t += t0; |
||
632 | if ((*ptr ^ pl) & j) { |
||
633 | tape_image[tape_imagesize++] = find_pulse(t); |
||
634 | pl ^= -1; t = 0; |
||
635 | } |
||
636 | } |
||
637 | // find pulses - last byte |
||
638 | for (j = 0x80; j != (unsigned char)(0x80 >> last); j >>= 1) { |
||
639 | t += t0; |
||
640 | if ((*ptr ^ pl) & j) { |
||
641 | tape_image[tape_imagesize++] = find_pulse(t); |
||
642 | pl ^= -1; t = 0; |
||
643 | } |
||
644 | } |
||
645 | ptr++; |
||
646 | tape_image[tape_imagesize++] = find_pulse(t); // last pulse ??? |
||
647 | if (pause) tape_image[tape_imagesize++] = find_pulse(pause*3500); |
||
648 | break; |
||
649 | case 0x20: // pause (silence) or 'stop the tape' command |
||
650 | pause = *(unsigned short*)ptr; |
||
651 | sprintf(nm, pause? "pause %d ms" : "stop the tape", pause); |
||
652 | named_cell(nm); |
||
653 | reserve(2); ptr += 2; |
||
654 | if (!pause) { // at least 1ms pulse as specified in TZX 1.13 |
||
655 | tape_image[tape_imagesize++] = find_pulse(3500); |
||
656 | pause = -1; |
||
657 | } else pause *= 3500; |
||
658 | tape_image[tape_imagesize++] = find_pulse(pause); |
||
659 | break; |
||
660 | case 0x21: // group start |
||
661 | n = *ptr++; |
||
662 | named_cell(ptr, n); ptr += n; |
||
663 | appendable = 1; |
||
664 | break; |
||
665 | case 0x22: // group end |
||
666 | break; |
||
667 | case 0x23: // jump to block |
||
668 | named_cell("* jump"); ptr += 2; |
||
669 | break; |
||
670 | case 0x24: // loop start |
||
671 | loop_n = *(unsigned short*)ptr; loop_p = tape_imagesize; ptr += 2; |
||
672 | break; |
||
673 | case 0x25: // loop end |
||
674 | if (!loop_n) break; |
||
675 | size = tape_imagesize - loop_p; |
||
676 | reserve((loop_n-1) * size); |
||
677 | for (i = 1; i < loop_n; i++) |
||
678 | memcpy(tape_image + loop_p + i*size, tape_image + loop_p, size); |
||
679 | tape_imagesize += (loop_n-1) * size; |
||
680 | loop_n = 0; |
||
681 | break; |
||
682 | case 0x26: // call |
||
683 | named_cell("* call"); ptr += 2 + 2 * *(unsigned short*)ptr; |
||
684 | break; |
||
685 | case 0x27: // ret |
||
686 | named_cell("* return"); |
||
687 | break; |
||
688 | case 0x28: // select block |
||
689 | { |
||
690 | int l = _countof(nm); |
||
691 | l -= sprintf(nm, "* choice: "); |
||
692 | n = ptr[2]; |
||
693 | p = (char*)ptr+3; |
||
694 | |||
695 | for (i = 0; i < n; i++) |
||
696 | { |
||
697 | size = *(unsigned char*)(p+2); |
||
698 | l -= size; |
||
699 | if(i) |
||
700 | l -= 4; |
||
701 | |||
702 | if(l >= 0) |
||
703 | { |
||
704 | if (i) |
||
705 | strcat(nm, " / "); |
||
706 | |||
707 | char *q = nm + strlen(nm); |
||
708 | memcpy(q, p+3, size); |
||
709 | q[size] = 0; |
||
710 | } |
||
711 | |||
712 | p += size+3; |
||
713 | } |
||
714 | named_cell(nm); |
||
715 | ptr += 2 + *(unsigned short*)ptr; |
||
716 | } |
||
717 | break; |
||
718 | case 0x2A: // stop if 48k |
||
719 | named_cell("* stop if 48K"); |
||
720 | ptr += 4 + *(unsigned*)ptr; |
||
721 | break; |
||
722 | case 0x30: // text description |
||
723 | n = *ptr++; |
||
724 | named_cell(ptr, n); ptr += n; |
||
725 | appendable = 1; |
||
726 | break; |
||
727 | case 0x31: // message block |
||
728 | named_cell("- MESSAGE BLOCK "); |
||
729 | end = ptr + 2 + ptr[1]; pl = *end; *(char *)end = 0; |
||
730 | for (p = (char*)ptr+2; p < (char*)end; p++) |
||
731 | if (*p == 0x0D) *p = 0; |
||
732 | for (p = (char*)ptr+2; p < (char*)end; p += strlen(p)+1) |
||
733 | named_cell(p); |
||
734 | *(char *)end = pl; ptr = end; |
||
735 | named_cell("-"); |
||
736 | break; |
||
737 | case 0x32: // archive info |
||
738 | named_cell("- ARCHIVE INFO "); |
||
739 | p = (char*)ptr + 3; |
||
740 | for (i = 0; i < ptr[2]; i++) { |
||
741 | const char *info; |
||
742 | switch (*p++) { |
||
743 | case 0: info = "Title"; break; |
||
744 | case 1: info = "Publisher"; break; |
||
745 | case 2: info = "Author"; break; |
||
746 | case 3: info = "Year"; break; |
||
747 | case 4: info = "Language"; break; |
||
748 | case 5: info = "Type"; break; |
||
749 | case 6: info = "Price"; break; |
||
750 | case 7: info = "Protection"; break; |
||
751 | case 8: info = "Origin"; break; |
||
752 | case -1:info = "Comment"; break; |
||
753 | default:info = "info"; break; |
||
754 | } |
||
755 | unsigned size = *(BYTE*)p+1; |
||
756 | char tmp = p[size]; p[size] = 0; |
||
757 | sprintf(nm, "%s: %s", info, p+1); |
||
758 | p[size] = tmp; p += size; |
||
759 | named_cell(nm); |
||
760 | } |
||
761 | named_cell("-"); |
||
762 | ptr += 2 + *(unsigned short*)ptr; |
||
763 | break; |
||
764 | case 0x33: // hardware type |
||
765 | parse_hardware(ptr); |
||
766 | ptr += 1 + 3 * *ptr; |
||
767 | break; |
||
768 | case 0x34: // emulation info |
||
769 | named_cell("* emulation info"); ptr += 8; |
||
770 | break; |
||
771 | case 0x35: // custom info |
||
772 | if (!memcmp(ptr, "POKEs ", 16)) { |
||
773 | named_cell("- POKEs block "); |
||
774 | named_cell(ptr+0x15, ptr[0x14]); |
||
775 | p = (char*)ptr + 0x15 + ptr[0x14]; |
||
776 | n = *(unsigned char*)p++; |
||
777 | for (i = 0; i < n; i++) { |
||
778 | named_cell(p+1, *(unsigned char*)p); |
||
779 | p += *p+1; |
||
780 | t = *(unsigned char*)p++; |
||
781 | strcpy(nm, "POKE "); |
||
782 | for (j = 0; j < t; j++) { |
||
783 | sprintf(nm+strlen(nm), "%d,", *(unsigned short*)(p+1)); |
||
784 | sprintf(nm+strlen(nm), *p & 0x10 ? "nn" : "%d", *(unsigned char*)(p+3)); |
||
785 | if (!(*p & 0x08)) sprintf(nm+strlen(nm), "(page %d)", *p & 7); |
||
786 | strcat(nm, "; "); p += 5; |
||
787 | } |
||
788 | named_cell(nm); |
||
789 | } |
||
790 | *(unsigned*)nm = '-'; |
||
791 | } else sprintf(nm, "* custom info: %s", ptr), nm[15+16] = 0; |
||
792 | named_cell(nm); |
||
793 | ptr += 0x14 + *(unsigned*)(ptr+0x10); |
||
794 | break; |
||
795 | case 0x40: // snapshot |
||
796 | named_cell("* snapshot"); ptr += 4 + (0xFFFFFF & *(unsigned*)(ptr + 1)); |
||
797 | break; |
||
798 | default: |
||
799 | if(TzxVer >= 0x10A) |
||
800 | { |
||
801 | // В начале каждого нестандартного блока идет его длина |
||
802 | sprintf(nm, "* unknown id: 0x%X", ptr[-1]); |
||
803 | named_cell(nm); |
||
804 | u32 Skip = *(u32 *)ptr; |
||
805 | ptr += Skip + sizeof(u32); |
||
806 | } |
||
807 | else |
||
808 | { |
||
809 | ptr += snapsize; |
||
810 | } |
||
811 | } |
||
812 | } |
||
813 | for (i = 0; i < tape_infosize; i++) { |
||
814 | if (*(short*)tapeinfo[i].desc == WORD2('*', ' ')) |
||
815 | { |
||
816 | if(strlen(tapeinfo[i].desc) < _countof(tapeinfo[i].desc) - sizeof(" [UNSUPPORTED]")) |
||
817 | strcat(tapeinfo[i].desc, " [UNSUPPORTED]"); |
||
818 | else |
||
819 | { |
||
820 | strcpy(tapeinfo[i].desc + _countof(tapeinfo[i].desc) - sizeof(" [UNSUPPORTED]"), " [UNSUPPORTED]"); |
||
821 | } |
||
822 | } |
||
823 | if (*tapeinfo[i].desc == '-') |
||
824 | while (strlen(tapeinfo[i].desc) < sizeof(tapeinfo[i].desc)-1) |
||
825 | strcat(tapeinfo[i].desc, "-"); |
||
826 | } |
||
827 | if (tape_imagesize && tape_pulse[tape_image[tape_imagesize-1]] < 350000) |
||
828 | reserve(1), tape_image[tape_imagesize++] = find_pulse(350000); // small pause [rqd for 3ddeathchase] |
||
829 | find_tape_sizes(); |
||
830 | return (ptr == snbuf+snapsize); |
||
831 | } |
||
832 | |||
833 | unsigned char tape_bit() // used in io.cpp & sound.cpp |
||
834 | { |
||
835 | __int64 cur = comp.t_states + cpu.t; |
||
836 | if (cur < comp.tape.edge_change) |
||
837 | return (unsigned char)comp.tape.tape_bit; |
||
838 | while (comp.tape.edge_change < cur) |
||
839 | { |
||
840 | if (!temp.sndblock) |
||
841 | { |
||
842 | unsigned t = (unsigned)(comp.tape.edge_change - comp.t_states - temp.cpu_t_at_frame_start); |
||
843 | if ((int)t >= 0) |
||
844 | { |
||
845 | unsigned tape_in = conf.sound.micin_vol & comp.tape.tape_bit; |
||
846 | // comp.tape.sound.update(t, tape_in, tape_in); //Alone Coder |
||
847 | comp.tape_sound.update(t, tape_in, tape_in); //Alone Coder |
||
848 | } |
||
849 | } |
||
850 | unsigned pulse; comp.tape.tape_bit ^= -1; |
||
851 | if (comp.tape.play_pointer == comp.tape.end_of_tape || |
||
852 | (pulse = tape_pulse[*comp.tape.play_pointer++]) == -1) |
||
853 | stop_tape(); |
||
854 | else |
||
855 | comp.tape.edge_change += pulse; |
||
856 | } |
||
857 | return (unsigned char)comp.tape.tape_bit; |
||
858 | } |
||
859 | |||
860 | void fast_tape() |
||
861 | { |
||
862 | unsigned char *ptr = am_r(cpu.pc); |
||
863 | unsigned p = *(unsigned*)ptr; |
||
864 | if (p == WORD4(0x3D,0x20,0xFD,0xA7)) |
||
865 | { // dec a:jr nz,$-1 |
||
866 | cpu.t += ((unsigned char)(cpu.a-1))*16; cpu.a = 1; |
||
867 | return; |
||
868 | } |
||
869 | if ((unsigned short)p == WORD2(0x10,0xFE)) |
||
870 | { // djnz $ |
||
871 | cpu.t += ((unsigned char)(cpu.b-1))*13; cpu.b = 1; |
||
872 | return; |
||
873 | } |
||
874 | if ((unsigned short)p == WORD2(0x3D,0xC2) && (cpu.pc & 0xFFFF)==(p>>16)) |
||
875 | { // dec a:jp nz,$-1 |
||
876 | cpu.t += ((unsigned char)(cpu.a-1))*14; cpu.a = 1; |
||
877 | return; |
||
878 | } |
||
879 | if ((p | WORD4(0,0,0,0xFF)) == WORD4(0x04,0xC8,0x3E,0xFF)) |
||
880 | { |
||
881 | if (*(unsigned*)(ptr+4) == WORD4(0xDB,0xFE,0x1F,0xD0) && |
||
882 | *(unsigned*)(ptr+8) == WORD4(0xA9,0xE6,0x20,0x28) && ptr[12] == 0xF3) |
||
883 | { // find edge (rom routine) |
||
884 | for (;;) |
||
885 | { |
||
886 | if (cpu.b == 0xFF) |
||
887 | return; |
||
888 | if ((tape_bit() ? 0x20 : 0) ^ (cpu.c & 0x20)) |
||
889 | return; |
||
890 | cpu.b++; cpu.t += 59; |
||
891 | } |
||
892 | } |
||
893 | if (*(unsigned*)(ptr+4) == WORD4(0xDB,0xFE,0xCB,0x1F) && |
||
894 | *(unsigned*)(ptr+8) == WORD4(0xA9,0xE6,0x20,0x28) && ptr[12] == 0xF3) |
||
895 | { // rra,ret nc => rr a (popeye2) |
||
896 | for (;;) |
||
897 | { |
||
898 | if (cpu.b == 0xFF) return; |
||
899 | if ((tape_bit() ^ cpu.c) & 0x20) return; |
||
900 | cpu.b++; cpu.t += 58; |
||
901 | } |
||
902 | } |
||
903 | if (*(unsigned*)(ptr+4) == WORD4(0xDB,0xFE,0x1F,0x00) && |
||
904 | *(unsigned*)(ptr+8) == WORD4(0xA9,0xE6,0x20,0x28) && ptr[12] == 0xF3) |
||
905 | { // ret nc nopped (some bleep loaders) |
||
906 | for (;;) |
||
907 | { |
||
908 | if (cpu.b == 0xFF) return; |
||
909 | if ((tape_bit() ^ cpu.c) & 0x20) return; |
||
910 | cpu.b++; cpu.t += 58; |
||
911 | } |
||
912 | } |
||
913 | if (*(unsigned*)(ptr+4) == WORD4(0xDB,0xFE,0xA9,0xE6) && |
||
914 | *(unsigned*)(ptr+8) == WORD4(0x40,0xD8,0x00,0x28) && ptr[12] == 0xF3) |
||
915 | { // no rra, no break check (rana rama) |
||
916 | for (;;) |
||
917 | { |
||
918 | if (cpu.b == 0xFF) return; |
||
919 | if ((tape_bit() ^ cpu.c) & 0x40) return; |
||
920 | cpu.b++; cpu.t += 59; |
||
921 | } |
||
922 | } |
||
923 | if (*(unsigned*)(ptr+4) == WORD4(0xDB,0xFE,0x1F,0xA9) && |
||
924 | *(unsigned*)(ptr+8) == WORD4(0xE6,0x20,0x28,0xF4)) |
||
925 | { // ret nc skipped: routine without BREAK checking (ZeroMusic & JSW) |
||
926 | for (;;) |
||
927 | { |
||
928 | if (cpu.b == 0xFF) return; |
||
929 | if ((tape_bit() ^ cpu.c) & 0x20) return; |
||
930 | cpu.b++; cpu.t += 54; |
||
931 | } |
||
932 | } |
||
933 | } |
||
934 | if ((p | WORD4(0,0,0,0xFF)) == WORD4(0x04,0x20,0x03,0xFF) && |
||
935 | ptr[6] == 0xDB && *(unsigned*)(ptr+8) == WORD4(0x1F,0xC8,0xA9,0xE6) && |
||
936 | (*(unsigned*)(ptr+0x0C) | WORD4(0,0,0,0xFF)) == WORD4(0x20,0x28,0xF1,0xFF)) |
||
937 | { // find edge from Donkey Kong |
||
938 | for (;;) { |
||
939 | if (cpu.b == 0xFF) return; |
||
940 | if ((tape_bit() ^ cpu.c) & 0x20) return; |
||
941 | cpu.b++; cpu.t += 59; |
||
942 | } |
||
943 | } |
||
944 | if ((p | WORD4(0,0xFF,0,0)) == WORD4(0x3E,0xFF,0xDB,0xFE) && |
||
945 | *(unsigned*)(ptr+4) == WORD4(0xA9,0xE6,0x40,0x20) && |
||
946 | (*(unsigned*)(ptr+8) | WORD4(0xFF,0,0,0)) == WORD4(0xFF,0x05,0x20,0xF4)) |
||
947 | { // lode runner |
||
948 | for (;;) |
||
949 | { |
||
950 | if (cpu.b == 1) return; |
||
951 | if ((tape_bit() ^ cpu.c) & 0x40) return; |
||
952 | cpu.t += 52; cpu.b--; |
||
953 | } |
||
954 | } |
||
955 | } |
||
956 | |||
957 | void tape_traps() |
||
958 | { |
||
959 | unsigned pulse; |
||
960 | do |
||
961 | { |
||
962 | if(comp.tape.play_pointer>=comp.tape.end_of_tape || |
||
963 | (pulse=tape_pulse[*comp.tape.play_pointer++])==-1) |
||
964 | { |
||
965 | stop_tape(); |
||
966 | return; |
||
967 | } |
||
968 | }while(pulse > 770); |
||
969 | comp.tape.play_pointer++; |
||
970 | |||
971 | // loading header |
||
972 | cpu.l=0; |
||
973 | cpu.h = 0; |
||
974 | for(unsigned bit=0x80;bit;bit>>=1) |
||
975 | { |
||
976 | if(comp.tape.play_pointer>=comp.tape.end_of_tape|| |
||
977 | (pulse=tape_pulse[*comp.tape.play_pointer++])==-1) |
||
978 | { |
||
979 | stop_tape(); |
||
980 | cpu.pc = 0x05DF; |
||
981 | return; |
||
982 | } |
||
983 | cpu.l |= (pulse>1240) ? bit : 0; |
||
984 | comp.tape.play_pointer++; |
||
985 | } |
||
986 | cpu.h ^= cpu.l; |
||
987 | |||
988 | // loading data |
||
989 | do |
||
990 | { |
||
991 | cpu.l = 0; |
||
992 | for(unsigned bit=0x80; bit; bit >>= 1) |
||
993 | { |
||
994 | if(comp.tape.play_pointer >= comp.tape.end_of_tape || |
||
995 | (pulse = tape_pulse[*comp.tape.play_pointer++])==-1) |
||
996 | { |
||
997 | stop_tape(); |
||
998 | cpu.pc = 0x05DF; |
||
999 | return; |
||
1000 | } |
||
1001 | cpu.l |= (pulse > 1240) ? bit : 0; |
||
1002 | comp.tape.play_pointer++; |
||
1003 | } |
||
1004 | cpu.h ^= cpu.l; |
||
1005 | cpu.DbgMemIf->wm(cpu.ix++, cpu.l); |
||
1006 | cpu.de--; |
||
1007 | }while(cpu.de & 0xFFFF); |
||
1008 | |||
1009 | // loading CRC |
||
1010 | cpu.l = 0; |
||
1011 | for(unsigned bit = 0x80; bit; bit >>= 1) |
||
1012 | { |
||
1013 | if(comp.tape.play_pointer>=comp.tape.end_of_tape|| |
||
1014 | (pulse=tape_pulse[*comp.tape.play_pointer++])==-1) |
||
1015 | { |
||
1016 | stop_tape(); |
||
1017 | cpu.pc = 0x05DF; |
||
1018 | return; |
||
1019 | } |
||
1020 | cpu.l |= (pulse > 1240) ? bit : 0; |
||
1021 | comp.tape.play_pointer++; |
||
1022 | } |
||
1023 | cpu.h ^= cpu.l; |
||
1024 | cpu.pc = 0x05DF; // ld a,h / cp 01 / ret |
||
1025 | cpu.bc = 0xB001; |
||
1026 | |||
1027 | comp.tape.play_pointer++; |
||
1028 | stop_tape(); |
||
1029 | |||
1030 | /*cpu.pc=0x0604; // the old one |
||
1031 | unsigned pulse; |
||
1032 | pulse = tape_pulse[*comp.tape.play_pointer++]; |
||
1033 | if(pulse == -1) stop_tape(); |
||
1034 | else{ |
||
1035 | comp.t_states+=pulse; |
||
1036 | comp.tape.edge_change = comp.t_states + cpu.t + 520; |
||
1037 | |||
1038 | cpu.b+=(pulse-520)/56; |
||
1039 | cpu.f|=CF; |
||
1040 | }*/ |
||
1041 | } |