1 /*
2 * apple2_disk.c
3 *
4 * Disk ][ card emulation
5 */
6
7 /* $Id: apple2_disk.c,v 1.11 2000/08/21 01:13:32 nyef Exp $ */
8
9 #include <stdlib.h>
10 #include <string.h>
11
12 #include "apple2_cards.h"
13 #include "types.h"
14 #include "tool.h"
15 #include "ui.h"
16 #include "menu.h"
17
18
19 /* manifest constants */
20
21 #define DISK_2_ROM_FILE "disk_ii.rom"
22
23 #define DSK_FILE_TRACK_LENGTH 0x1000
24 #define NIB_FILE_TRACK_LENGTH 0x1a00
25
26 #define BYTES_PER_NIB_SECTOR 0x1a0
27
28 #define TRACKS_PER_DISK 35 /* 0x23 */
29
30 #define DISK_SEEK_IN 0 /* higher tracks */
31 #define DISK_SEEK_OUT 1 /* lower tracks */
32
33
34 /* data structures */
35
36 struct a2_disk {
37 int is_inserted;
38 int is_readonly;
39
40 int last_phase;
41
42 int cur_track;
43
44 u8 *disk_data;
45
46 u8 *track_data;
47 int track_data_index;
48
49 struct ui_menu *menu;
50 };
51
52 struct a2_disk_card {
53 struct apple2_card api;
54
55 struct a2_disk disks[2];
56
57 int motor_on;
58 int drive_select;
59
60 u8 write_latch;
61
62 int qlines;
63 };
64
65
66 /* drive emulation */
67
a2_disk_seek(struct a2_disk * disk,int direction)68 void a2_disk_seek(struct a2_disk *disk, int direction)
69 {
70 int new_track;
71
72 new_track = disk->cur_track;
73
74 if (direction == DISK_SEEK_OUT) {
75 if (new_track == 0) { /* already at beginning */
76 return;
77 }
78 new_track--;
79 } else if (direction == DISK_SEEK_IN) {
80 new_track++;
81 if (new_track >= TRACKS_PER_DISK) { /* already at end */
82 return;
83 }
84 } else {
85 deb_printf("a2_disk: BUG: bogus direction parameter to seek.\n");
86 return;
87 }
88
89 disk->cur_track = new_track;
90 disk->track_data = disk->disk_data + (new_track * NIB_FILE_TRACK_LENGTH);
91 }
92
a2_line_toggle(struct a2_disk_card * card,u16 address)93 void a2_line_toggle(struct a2_disk_card *card, u16 address)
94 {
95 int addr;
96
97 addr = address & 0xf;
98
99 switch (addr) {
100 case 0x0: case 0x1: case 0x2: case 0x3:
101 case 0x4: case 0x5: case 0x6: case 0x7:
102 if (!(addr & 1)) {
103 return; /* stepper phase off, ingore it */
104 }
105 if (addr & 2) {
106 card->disks[card->drive_select].last_phase = addr;
107 return; /* odd stepper phase off, set phase in disk struct */
108 }
109 if ((addr & 4) == (card->disks[card->drive_select].last_phase & 4)) {
110 /* outward bound */
111 a2_disk_seek(&card->disks[card->drive_select], DISK_SEEK_OUT);
112 } else {
113 /* inward bound */
114 a2_disk_seek(&card->disks[card->drive_select], DISK_SEEK_IN);
115 }
116 return;
117
118 case 0x8:
119 card->motor_on = 0;
120 return;
121 case 0x9:
122 card->motor_on = 1;
123 return;
124
125 case 0xa:
126 card->drive_select = 0;
127 return;
128 case 0xb:
129 card->drive_select = 1;
130 return;
131
132 case 0xc: /* Q6L */
133 card->qlines &= ~1;
134 return;
135 case 0xd: /* Q6H */
136 card->qlines |= 1;
137 return;
138 case 0xe: /* Q7L */
139 card->qlines &= ~2;
140 return;
141 case 0xf: /* Q7H */
142 card->qlines |= 2;
143 return;
144 }
145 }
146
a2_disk_read(struct a2_disk_card * card,u16 address)147 u8 a2_disk_read(struct a2_disk_card *card, u16 address)
148 {
149 struct a2_disk *cur_disk;
150
151 a2_line_toggle(card, address);
152
153 cur_disk = &card->disks[card->drive_select];
154
155 switch (card->qlines & 3) {
156 case 0: /* read data */
157 if (card->motor_on && cur_disk->is_inserted) {
158 u8 retval;
159
160 retval = cur_disk->track_data[cur_disk->track_data_index];
161 cur_disk->track_data_index++;
162 cur_disk->track_data_index %= NIB_FILE_TRACK_LENGTH;
163
164 return retval;
165 }
166 break;
167 case 1: /* sense write protect */
168 return cur_disk->is_readonly << 7;
169 case 2: /* write byte */
170 if (card->motor_on && cur_disk->is_inserted && (!cur_disk->is_readonly)) {
171 cur_disk->track_data[cur_disk->track_data_index] = card->write_latch;
172 cur_disk->track_data_index++;
173 cur_disk->track_data_index %= NIB_FILE_TRACK_LENGTH;
174 }
175 break;
176 case 3: /* load write latch, SHOULD NOT HAPPEN */
177 deb_printf("a2_disk: load write latch during read.\n");
178 break;
179 }
180
181 return 0x00;
182 }
183
a2_disk_write(struct a2_disk_card * card,u16 address,u8 data)184 void a2_disk_write(struct a2_disk_card *card, u16 address, u8 data)
185 {
186 a2_line_toggle(card, address);
187
188 switch (card->qlines & 3) {
189 case 0: /* read data */
190 break;
191 case 1: /* sense write protect */
192 break;
193 case 2: /* write byte */
194 break;
195 case 3: /* load write latch */
196 card->write_latch = data;
197 break;
198 }
199 }
200
201
202 /* disk image conversion */
203
204 u8 a2_disk_6_2_write_table[64] = {
205 0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6,
206 0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3,
207 0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc,
208 0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3,
209 0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde,
210 0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec,
211 0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
212 0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
213 };
214
disk2_nibblize_sector_6_2(u8 * sector_data,u8 * output_buffer)215 int disk2_nibblize_sector_6_2(u8 *sector_data, u8 *output_buffer)
216 {
217 u8 aux_data_buffer[86]; /* 0x56 bytes */
218 const u8 bit_reverse_table[4] = {0, 2, 1, 3,};
219 int i;
220 int j;
221 u8 checksum;
222 u8 last_byte;
223
224 /* set up aux_data_buffer[] */
225 memset(aux_data_buffer, 0, 86);
226 #if 1
227 j = 85;
228 for (i = 0; i < 0x100; i++) {
229 aux_data_buffer[j] >>= 2;
230 aux_data_buffer[j] |= bit_reverse_table[sector_data[i] & 3] << 4;
231 if (!j--) {
232 j = 85;
233 }
234 }
235 #endif
236 aux_data_buffer[0] >>= 2;
237 aux_data_buffer[1] >>= 2;
238
239 /* output data to disk buffer */
240 checksum = 0;
241 j = 0;
242 for (i = 85; i >= 0; i--) {
243 last_byte = aux_data_buffer[i];
244 output_buffer[j++] = a2_disk_6_2_write_table[checksum ^ last_byte];
245 checksum = last_byte;
246 }
247 for (i = 0; i < 0x100; i++) {
248 last_byte = sector_data[i] >> 2;
249 output_buffer[j++] = a2_disk_6_2_write_table[checksum ^ last_byte];
250 checksum = last_byte;
251 }
252 output_buffer[j++] = a2_disk_6_2_write_table[checksum];
253
254 return j;
255 }
256
disk2_write_address_prologue(u8 * output_buffer)257 int disk2_write_address_prologue(u8 *output_buffer)
258 {
259 output_buffer[0] = 0xd5;
260 output_buffer[1] = 0xaa;
261 output_buffer[2] = 0x96;
262 return 3;
263 }
264
disk2_write_data_prologue(u8 * output_buffer)265 int disk2_write_data_prologue(u8 *output_buffer)
266 {
267 output_buffer[0] = 0xd5;
268 output_buffer[1] = 0xaa;
269 output_buffer[2] = 0xad;
270 return 3;
271 }
272
disk2_write_epilogue(u8 * output_buffer)273 int disk2_write_epilogue(u8 *output_buffer)
274 {
275 output_buffer[0] = 0xde;
276 output_buffer[1] = 0xaa;
277 output_buffer[2] = 0xeb;
278 return 3;
279 }
280
disk2_write_data_field(u8 * sector_data,u8 * output_buffer)281 int disk2_write_data_field(u8 *sector_data, u8 *output_buffer)
282 {
283 int length;
284
285 length = disk2_write_data_prologue(output_buffer);
286 length += disk2_nibblize_sector_6_2(sector_data, output_buffer + length);
287 length += disk2_write_epilogue(output_buffer + length);
288
289 return length;
290 }
291
disk2_write_address_field(u8 volume,u8 track,u8 sector,u8 * output_buffer)292 int disk2_write_address_field(u8 volume, u8 track, u8 sector, u8 *output_buffer)
293 {
294 int length;
295 u8 checksum;
296
297 length = disk2_write_address_prologue(output_buffer);
298
299 output_buffer[length++] = (volume >> 1) | 0xaa;
300 output_buffer[length++] = volume | 0xaa;
301
302 output_buffer[length++] = (track >> 1) | 0xaa;
303 output_buffer[length++] = track | 0xaa;
304
305 output_buffer[length++] = (sector >> 1) | 0xaa;
306 output_buffer[length++] = sector | 0xaa;
307
308 checksum = volume ^ track ^ sector;
309
310 output_buffer[length++] = (checksum >> 1) | 0xaa;
311 output_buffer[length++] = checksum | 0xaa;
312
313 length += disk2_write_epilogue(output_buffer + length);
314
315 return length;
316 }
317
disk2_write_sector(u8 volume,u8 track,u8 sector,u8 * sector_data,u8 * output_buffer)318 int disk2_write_sector(u8 volume, u8 track, u8 sector, u8 *sector_data, u8 *output_buffer)
319 {
320 int length;
321 int i;
322
323 for (i = 0; i < 0x25; i++) {
324 output_buffer[i] = 0xff;
325 }
326
327 length = i;
328
329 length += disk2_write_address_field(volume, track, sector, output_buffer + length);
330 for (i = 0; i < 0x10; i++) {
331 output_buffer[length++] = 0xff;
332 }
333
334 length += disk2_write_data_field(sector_data, output_buffer + length);
335
336 if (length != BYTES_PER_NIB_SECTOR) {
337 deb_printf("disk2_write_sector(): BUG: wrote 0x%x bytes, expected 0x%x bytes.\n", length, BYTES_PER_NIB_SECTOR);
338 }
339 return length;
340 }
341
disk2_convert_track(u8 volume,u8 track,u8 * input_buffer,u8 * output_buffer,u8 * translate_table)342 void disk2_convert_track(u8 volume, u8 track, u8 *input_buffer, u8 *output_buffer, u8 *translate_table)
343 {
344 int i;
345
346 for (i = 0; i < 16; i++) {
347 disk2_write_sector(volume, track, i,
348 input_buffer + (translate_table[i] * 0x100),
349 output_buffer + (i * BYTES_PER_NIB_SECTOR));
350 }
351 }
352
disk2_convert_disk(u8 * input_buffer,u8 * translate_table)353 u8 *disk2_convert_disk(u8 *input_buffer, u8 *translate_table)
354 {
355 u8 *retval;
356 int i;
357
358 retval = malloc(NIB_FILE_TRACK_LENGTH * TRACKS_PER_DISK);
359
360 if (!retval) {
361 return NULL;
362 }
363
364 for (i = 0; i < TRACKS_PER_DISK; i++) {
365 disk2_convert_track(254, i, input_buffer + (i * DSK_FILE_TRACK_LENGTH),
366 retval + (i * NIB_FILE_TRACK_LENGTH),
367 translate_table);
368 }
369
370 return retval;
371 }
372
373 u8 disk2_po_translate_table[0x10] = {
374 0x00, 0x08, 0x01, 0x09, 0x02, 0x0a, 0x03, 0x0b,
375 0x04, 0x0c, 0x05, 0x0d, 0x06, 0x0e, 0x07, 0x0f,
376 };
377
378 u8 disk2_do_translate_table[0x10] = {
379 0x00, 0x07, 0x0e, 0x06, 0x0d, 0x05, 0x0c, 0x04,
380 0x0b, 0x03, 0x0a, 0x02, 0x09, 0x01, 0x08, 0x0f,
381 };
382
a2_disk_load_disk(struct a2_disk_card * card)383 void a2_disk_load_disk(struct a2_disk_card *card)
384 {
385 rom_file romfile;
386
387 /* romfile = read_romimage("../roms/apple2/dc/Dos_3.3.dsk"); */
388 /* romfile = read_romimage("../roms/apple2/dc/DOS33.po"); */
389 /* romfile = read_romimage("../roms/apple2/wizardry/dc/Wizardry_I_boot.dsk"); */
390 romfile = read_romimage("../roms/apple2/sysroms/tarnover/dc/2400Game.do");
391 /* romfile = read_romimage("../roms/apple2/rockys_boots.dsk"); */
392 /* romfile = read_romimage("../roms/apple2/autodul1.dsk"); */
393 /* romfile = read_romimage("../roms/apple2/acs/AdvConstSet1of6.dsk"); */
394 /* romfile = read_romimage("../roms/apple2/ultima_1/U1Boot.dsk"); */
395 /* romfile = read_romimage("../roms/apple2/Conan1.dsk"); */
396 /* romfile = read_romimage("../roms/apple2/softporn.dsk"); */
397 /* romfile = read_romimage("../roms/apple2/ProDOS.do"); */
398 card->disks[0].disk_data = disk2_convert_disk(romfile->data, disk2_do_translate_table);
399 card->disks[0].track_data = card->disks[0].disk_data;
400 card->disks[0].is_inserted = 1;
401
402 romfile = read_romimage("../roms/apple2/sysroms/tarnover/dc/2400Plyr.do");
403 card->disks[1].disk_data = disk2_convert_disk(romfile->data, disk2_do_translate_table);
404 card->disks[1].track_data = card->disks[1].disk_data;
405 card->disks[1].is_inserted = 1;
406 card->disks[1].is_readonly = 0;
407 }
408
409 void disk2_remove_disk(struct a2_disk *disk);
410
disk2_open_callback(struct a2_disk * disk,char * filename)411 void disk2_open_callback(struct a2_disk *disk, char *filename)
412 {
413 rom_file romfile;
414
415 /* FIXME: is limited to DOS order only */
416 /* FIXME: leaks memory */
417 /* FIXME: is always readonly */
418 romfile = read_romimage(filename);
419 disk->disk_data = disk2_convert_disk(romfile->data, disk2_do_translate_table);
420 disk->track_data = disk[0].disk_data;
421 disk->cur_track = 0;
422 disk->is_inserted = 1;
423 disk->is_readonly = 1;
424
425 disk->menu[1].callback = (ui_menu_callback)disk2_remove_disk;
426 disk->menu[1].name = "Remove disk";
427 menu_rename_item(&disk->menu[1]);
428 }
429
430 #define DISK2_FILE_FILTER \
431 "All Supported Files\0*.dsk;*.do;*.po\0" \
432 "Disk images (*.DSK)\0*.dsk\0" \
433 "ProDOS-order images (*.PO)\0*.po" \
434 "DOS-order images (*.DO)\0*.do\0\0"
435
disk2_insert_disk(struct a2_disk * disk)436 void disk2_insert_disk(struct a2_disk *disk)
437 {
438 menu_file_open_box((ui_open_callback)disk2_open_callback,
439 disk, DISK2_FILE_FILTER);
440 }
441
disk2_remove_disk(struct a2_disk * disk)442 void disk2_remove_disk(struct a2_disk *disk)
443 {
444 /* FIXME: leaks memory */
445 /* FIXME: should sync modified images to disk */
446 disk->is_inserted = 0;
447
448 disk->menu[1].callback = (ui_menu_callback)disk2_insert_disk;
449 disk->menu[1].name = "Insert disk";
450 menu_rename_item(&disk->menu[1]);
451 }
452
453 /* initialization */
454
a2_disk_load_rom(struct a2_disk_card * card)455 void a2_disk_load_rom(struct a2_disk_card *card)
456 {
457 rom_file romfile;
458
459 romfile = read_romimage(DISK_2_ROM_FILE);
460
461 if (!romfile) {
462 /* rom not found */
463 deb_printf("unable to load \"" DISK_2_ROM_FILE "\", the disk rom file.\n");
464 return;
465 }
466
467 card->api.rom = romfile->data;
468 }
469
470 struct ui_menu disk2_menu[] = {
471 {"disk", MF_NONE}, /* menu title (maybe) */
472 {"Insert disk", MF_NONE, (ui_menu_callback)disk2_insert_disk},
473 {NULL, 0, NULL}, /* must be last entry */
474 };
475
disk2_init_menu(struct a2_disk * disk,int disk_num)476 void disk2_init_menu(struct a2_disk *disk, int disk_num)
477 {
478 struct ui_menu *menu;
479
480 menu = malloc(sizeof(disk2_menu));
481 memcpy(menu, disk2_menu, sizeof(disk2_menu));
482
483 menu[1].callback_data = disk;
484 disk->menu = menu;
485
486 apple2_set_child_menu(menu, disk_num);
487 }
488
apple2_disk_init(struct apple2_mainboard * apple2)489 struct apple2_card *apple2_disk_init(struct apple2_mainboard *apple2)
490 {
491 struct a2_disk_card *retval;
492
493 retval = malloc(sizeof(*retval));
494
495 if (!retval) {
496 return NULL;
497 }
498
499 a2_disk_load_rom(retval);
500
501 disk2_init_menu(&retval->disks[0], 0);
502 disk2_init_menu(&retval->disks[1], 1);
503
504 retval->api.read = (a2_card_read)a2_disk_read;
505 retval->api.write = (a2_card_write)a2_disk_write;
506
507 retval->motor_on = 0;
508 retval->drive_select = 0;
509 retval->qlines = 0;
510
511 retval->disks[0].is_inserted = 0;
512 retval->disks[0].is_readonly = 1;
513 retval->disks[0].cur_track = 0;
514
515 retval->disks[1].is_inserted = 0;
516 retval->disks[1].is_readonly = 1;
517 retval->disks[1].cur_track = 0;
518
519 return (struct apple2_card *)retval;
520 }
521
522 /*
523 * $Log: apple2_disk.c,v $
524 * Revision 1.11 2000/08/21 01:13:32 nyef
525 * added preliminary support for file open boxes
526 *
527 * Revision 1.10 2000/08/21 00:33:46 nyef
528 * fixed to initialize the per-disk menus (oops)
529 *
530 * Revision 1.9 2000/08/05 15:42:45 nyef
531 * really fixed warning in disk2_nibblize_sector_6_2()
532 *
533 * Revision 1.8 2000/07/01 15:43:32 nyef
534 * started adding preliminary per-disk menus
535 *
536 * Revision 1.7 2000/05/06 22:35:27 nyef
537 * fixed warning in disk2_nibblize_sector_6_2()
538 *
539 * Revision 1.6 2000/03/13 02:18:05 nyef
540 * fixed track seeks (I hope)
541 *
542 * Revision 1.5 2000/03/12 00:13:59 nyef
543 * fixed the DO skewing table
544 *
545 * Revision 1.4 2000/03/11 22:55:00 nyef
546 * fixed the PO skewing table
547 * added a DO skewing table
548 *
549 * Revision 1.3 2000/03/11 22:13:00 nyef
550 * added code to convert PO image files to NIB format (used internally)
551 *
552 * Revision 1.2 2000/03/11 16:32:00 nyef
553 * added preliminary track seeks, disk reading, and disk writing
554 *
555 * Revision 1.1 2000/03/11 02:29:58 nyef
556 * Initial revision
557 *
558 */
559