1 /*
2  * cmdhd.c - Whole CMDHD emulation
3  *
4  * Written by
5  * Roberto Muscedere (rmusced@uwindsor.ca)
6  *
7  * Based on old code by
8  *  Kajtar Zsolt <soci@c64.rulez.org>
9  *  Andreas Boose <viceteam@t-online.de>
10  *  Andre Fachat <fachat@physik.tu-chemnitz.de>
11  *  Daniel Sladic <sladic@eecg.toronto.edu>
12  *  Ettore Perazzoli <ettore@comm2000.it>
13  *
14  * This file is part of VICE, the Versatile Commodore Emulator.
15  * See README for copyright notice.
16  *
17  *  This program is free software; you can redistribute it and/or modify
18  *  it under the terms of the GNU General Public License as published by
19  *  the Free Software Foundation; either version 2 of the License, or
20  *  (at your option) any later version.
21  *
22  *  This program is distributed in the hope that it will be useful,
23  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
24  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  *  GNU General Public License for more details.
26  *
27  *  You should have received a copy of the GNU General Public License
28  *  along with this program; if not, write to the Free Software
29  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
30  *  02111-1307  USA.
31  *
32  */
33 
34 #include "vice.h"
35 
36 #include <stdio.h>
37 #include <stdint.h>
38 
39 #include "diskimage.h"
40 #include "debug.h"
41 #include "drive.h"
42 #include "drivesync.h"
43 #include "drivetypes.h"
44 #include "iecbus.h"
45 #include "iecdrive.h"
46 #include "interrupt.h"
47 #include "lib.h"
48 #include "types.h"
49 #include "cmdhd.h"
50 #include "util.h"
51 #include "diskimage/fsimage.h"
52 #include "rtc/rtc-72421.h"
53 
54 #define LOG LOG_DEFAULT
55 #define ERR LOG_ERR
56 
57 /* #define CMDLOG */
58 /* #define CMDIO */
59 /* #define CMDBUS */
60 
61 #ifdef CMDLOG
62 #define CLOG(_x_) log_message _x_
63 #else
64 #define CLOG(_x_)
65 #endif
66 
67 #ifdef CMDIO
68 #define IDBG(_x_) log_message _x_
69 #else
70 #define IDBG(_x_)
71 #endif
72 
73 #ifdef CMDBUS
74 #define BLOG(_x_) log_message _x_
75 #else
76 #define BLOG(_x_)
77 #endif
78 
79 #define CRIT(_x_) log_message _x_
80 
81 #define iecbus (viap->v_iecbus)
82 
83 cmdbus_t cmdbus;
84 
85 typedef struct drivevia_context_s {
86     unsigned int number;
87     struct drive_s *drive;
88     struct iecbus_s *v_iecbus;
89 } drivevia_context_t;
90 
91 /* Although SCSI can be a multi-master bus, CMD HD's assume they are the
92 masters so we won't implement all the possible varitions, just a "target"
93 device that will respond to all (0-6) IDs and 0-7 LUNs for each */
94 /* Latest boot rom (2.8) and even earlier have a bug when deleting partitions
95 across multiple drives */
96 /* Although we can support up to 55 (tested), deleting partitions corrupts
97 the data so we will not allow this, at least for now */
98 /* SCSI states are setup to reflect the register values coming out of U13 to
99 simplify the interface */
100 /* All signals asserted high here, opposite in physical implementation
101    (open-collector/drain etc)
102 
103   SEL  BSY  IO  MSG  CD  U9-PB2-0  PHASE
104   0    0    X   X    X   X         BUSFREE
105   0    1    X   X    X   X         ARBITRATION
106   1    0    X   X    X   X         SELECTION (phase 1)
107   1    1    X   X    X   X         SELECTION (phase 2)
108   0    1    0   0    0   0         DATA-OUT
109   0    1    0   0    1   1         COMMAND
110   0    1    1   0    0   4         DATA-IN
111   0    1    1   0    1   5         STATUS
112   0    1    0   1    1   3         MESSAGE-OUT
113   0    1    1   1    1   7         MESSAGE-IN
114 
115 */
116 
117 #define CMD_STATE_DATAOUT    0x00
118 #define CMD_STATE_COMMAND    0x01
119 #define CMD_STATE_DATAIN     0x04
120 #define CMD_STATE_STATUS     0x05
121 #define CMD_STATE_MESSAGEOUT 0x03
122 #define CMD_STATE_MESSAGEIN  0x07
123 
124 # undef DEBUG_IEC_DRV_WRITE
125 # undef DEBUG_IEC_DRV_READ
126 
127 #ifdef DEBUG
128 
129 # define DEBUG_IEC_DRV_WRITE(_data) my_debug_iec_drv_write(_data)
130 # define DEBUG_IEC_DRV_READ(_data) my_debug_iec_drv_read(_data)
131 
132 #else
133 
134 # define DEBUG_IEC_DRV_WRITE(_data)
135 # define DEBUG_IEC_DRV_READ(_data)
136 
137 #endif
138 
139 #ifdef DEBUG
140 
141 #include "log.h"
142 
my_debug_iec_drv_write(unsigned int data)143 void my_debug_iec_drv_write(unsigned int data)
144 {
145     if (debug.iec) {
146         uint8_t value = data;
147         static uint8_t oldvalue = 0;
148 
149         if (value != oldvalue) {
150             oldvalue = value;
151 
152             log_debug("$1800 store: %s %s %s",
153                       value & 0x02 ? "DATA OUT" : "        ",
154                       value & 0x08 ? "CLK OUT" : "       ",
155                       value & 0x10 ? "ATNA   " : "       "
156                       );
157         }
158     }
159 }
160 
my_debug_iec_drv_read(unsigned int data)161 void my_debug_iec_drv_read(unsigned int data)
162 {
163     if (debug.iec) {
164         uint8_t value = data;
165         static uint8_t oldvalue = { 0 };
166         const char * data_correct = "";
167 
168         if (value != oldvalue) {
169             unsigned int atn = value & 0x80 ? 1 : 0;
170             unsigned int atna = value & 0x10 ? 1 : 0;
171             unsigned int ddata = value & 0x01 ? 1 : 0;
172 
173             oldvalue = value;
174 
175             if (atn && atna) {
176                 if (!ddata) {
177                     data_correct = " ***** ERROR: ATN, ATNA & DATA! *****";
178                 }
179             }
180 
181             log_debug("$1800 read:  %s %s %s %s %s %s%s",
182                       value & 0x02 ? "DATA OUT" : "        ",
183                       value & 0x08 ? "CLK OUT" : "       ",
184                       value & 0x10 ? "ATNA   " : "       ",
185 
186                       value & 0x01 ? "DATA IN" : "       ",
187                       value & 0x04 ? "CLK IN" : "       ",
188                       value & 0x80 ? "ATN" : "   ",
189                       data_correct
190                       );
191         }
192     }
193 }
194 #endif
195 
196 /* copy some functions here so we don't make them external */
drive_read_rom(diskunit_context_t * drv,uint16_t address)197 static uint8_t drive_read_rom(diskunit_context_t *drv, uint16_t address)
198 {
199     return drv->rom[address & 0x7fff];
200 }
201 
drive_read_ram(diskunit_context_t * drv,uint16_t address)202 static uint8_t drive_read_ram(diskunit_context_t *drv, uint16_t address)
203 {
204     return drv->drive_ram[address];
205 }
206 
drive_store_ram(diskunit_context_t * drv,uint16_t address,uint8_t value)207 static void drive_store_ram(diskunit_context_t *drv, uint16_t address, uint8_t value)
208 {
209     drv->drive_ram[address] = value;
210 }
211 
reset_alarm_handler(CLOCK offset,void * data)212 static void reset_alarm_handler(CLOCK offset, void *data)
213 {
214     cmdhd_context_t *hd = (cmdhd_context_t *)data;
215 
216     CLOG((LOG, "CMDHD: alarm triggered at %u; releasing buttons",
217         *(hd->mycontext->clk_ptr)));
218     /* stop pressing WP, SWAP8, and SWAP9 buttons */
219     hd->i8255a_i[1] |= (0x08 | 0x04 | 0x02);
220     /* update in drive context too */
221     hd->mycontext->button = 0;
222 
223     alarm_unset(hd->reset_alarm);
224 }
225 
226 /* returns 0 if block has CMCHD signature at end */
cmdhd_has_sig(unsigned char * buf)227 static int cmdhd_has_sig(unsigned char *buf)
228 {
229     unsigned char hdmagic[16]={0x43, 0x4d, 0x44, 0x20, 0x48, 0x44, 0x20, 0x20,
230         0x8d, 0x03, 0x88, 0x8e, 0x02, 0x88, 0xea, 0x60};
231     return memcmp(&(buf[0xf0]), hdmagic, 16);
232 }
233 
cmdhd_scsiread(struct scsi_context_s * scsi)234 static void cmdhd_scsiread(struct scsi_context_s *scsi)
235 {
236     cmdhd_context_t *hd = (cmdhd_context_t*)(scsi->p);
237     drive_t *dc = (drive_t *)(hd->mycontext->drives[0]);
238     int unit = hd->mycontext->mynumber + 8;
239     int track;
240 
241     /* update track info on status bar; never 100 or above */
242     track = (scsi->address * 200) / ((hd->imagesize >> 9) + 1);
243     if (track >= 200) {
244         track = 199;
245     }
246     dc->current_half_track = track;
247 
248     /* correct device number on the fly since it is stored on HD not by switch or EEPROM */
249     if (hd->baselba != UINT32_MAX && scsi->address == hd->baselba + 2) {
250         /* make sure it has the cmd signature first */
251         if (!cmdhd_has_sig(&(scsi->data_buf[256]))) {
252             /* if it isn't what was defined by vice, set it to it */
253             if ( (scsi->data_buf[0x1e1] != unit) ||
254                 (scsi->data_buf[0x1e4] != unit) ) {
255                 /* tell the user it is happening */
256                 CLOG((LOG, "CMDHD: drive number is now %d; was %d in config block",
257                     unit, (int)scsi->data_buf[0x1e1]));
258                 scsi->data_buf[0x1e1] = unit;
259                 scsi->data_buf[0x1e4] = unit;
260             }
261         } else {
262             /* block we had on record no long has signature, invalidate it */
263             hd->baselba = UINT32_MAX;
264         }
265     }
266 }
267 
cmdhd_scsiwrite(struct scsi_context_s * scsi)268 static void cmdhd_scsiwrite(struct scsi_context_s *scsi)
269 {
270     cmdhd_context_t *hd = (cmdhd_context_t*)(scsi->p);
271     drive_t *dc = (drive_t *)(hd->mycontext->drives[0]);
272     uint32_t temp;
273 
274     /* keep track of the maximum lba written to */
275     temp=(scsi->address + 1) << 9;
276     if (temp > hd->imagesize) {
277         hd->imagesize = temp;
278     }
279 
280     /* update track info on status bar */
281     dc->current_half_track = (scsi->address * 200) / ((hd->imagesize >> 9) + 1);
282 }
283 
284 /* We don't actually format the disk, we just remove the 16 byte CMD signature */
cmdhd_scsiformat(struct scsi_context_s * scsi)285 static void cmdhd_scsiformat(struct scsi_context_s *scsi)
286 {
287     cmdhd_context_t *hd = (cmdhd_context_t*)(scsi->p);
288     int i;
289 
290     /* figure out where to start looking */
291     if (hd->baselba != UINT32_MAX) {
292         /* use what we already have if we found it before */
293         if (hd->baselba < (hd->imagesize >> 9)) {
294             scsi->address = hd->baselba + 2;
295         } else {
296         /* other wise, start from scratch */
297             hd->baselba = UINT32_MAX;
298             scsi->address = 2;
299         }
300     } else {
301         scsi->address = 2;
302     }
303     /* start searching, ever 128 LBAs starting from 2 or known base */
304     while (scsi->address < (hd->imagesize >> 9)) {
305         /* stop if we hit the end of the file */
306         if (scsi_image_read(scsi) < 0) {
307             break;
308         }
309         /* check for the CMD sig */
310         if (!cmdhd_has_sig(&(scsi->data_buf[256]))) {
311             hd->baselba = scsi->address - 2;
312             /* we found it, zero it out */
313             for (i = 0; i < 16; i++) {
314                 scsi->data_buf[0x1f0 + i] = 0;
315             }
316             /* write it back */
317             scsi_image_write(scsi);
318             break;
319         }
320         /* otherwise, keep looking */
321         scsi->address += 128;
322     }
323 }
324 
325 /* Set all the inputs high, like the pull-up resistors do */
cmdbus_init(void)326 void cmdbus_init(void)
327 {
328     int i;
329 
330     cmdbus.cpu_data = 0xff;
331     cmdbus.cpu_bus = 0xff;
332     cmdbus.data = 0xff;
333     cmdbus.bus = 0xff;
334     for (i = 0; i < NUM_DISK_UNITS; i++) {
335         cmdbus.drv_data[i] = 0xff;
336         cmdbus.drv_bus[i] = 0xff;
337     }
338 }
339 
340 /* Calculate the data/bus values */
cmdbus_update(void)341 void cmdbus_update(void)
342 {
343     int i;
344 
345     /* only allow devices to impact the bus if bit 0 is set on bus part */
346     if (cmdbus.cpu_bus & 1) {
347         cmdbus.bus = cmdbus.cpu_bus;
348         cmdbus.data = cmdbus.cpu_data;
349     } else {
350         cmdbus.bus = 0xff;
351         cmdbus.data = 0xff;
352     }
353     for (i = 0; i < NUM_DISK_UNITS; i++) {
354         /* only allow devices to impact the bus if bit 0 is set on bus part */
355         if (cmdbus.drv_bus[i] & 1) {
356             cmdbus.bus &= cmdbus.drv_bus[i];
357             cmdbus.data &= cmdbus.drv_data[i];
358         }
359     }
360 
361     BLOG(("CMDBUS PREADY=%s PCLK=%s PATN=%s",cmdbus.bus&0x80?"LOW ":"HIGH",
362         cmdbus.bus&0x40?"LOW ":"HIGH", cmdbus.bus&0x20?"LOW ":"HIGH"));
363 }
364 
365 /* U11 or i8255a interfacing */
366 /* Port A is for CMD Parallel bus, input/output, data only */
set_pa(struct _i8255a_state * ctx,uint8_t byte,int8_t reg)367 static void set_pa(struct _i8255a_state *ctx, uint8_t byte, int8_t reg)
368 {
369     cmdhd_context_t *hd = (cmdhd_context_t*)(ctx->p);
370 
371     cmdbus.drv_data[hd->mycontext->mynumber] = byte;
372     cmdbus_update();
373 }
374 
get_pa(struct _i8255a_state * ctx,int8_t reg)375 static uint8_t get_pa(struct _i8255a_state *ctx, int8_t reg)
376 {
377     uint8_t data = 0xff;
378 
379     if (reg == 0) {
380         /* if reg is 0, it is an actual read from the register */
381         data = cmdbus.data;
382     } else {
383         /* otherwise it is a bus change; physically it is pulled up */
384         data = 0xff;
385     }
386 
387     return data;
388 }
389 
390 /* Port B is for CMD Parallel bus the buttons (input only):
391    PB7 is PATN
392    PB6 is PCLK
393    PB5 is not referenced
394    PB4 is not referenced
395    PB3 is WP (active low)
396    PB2 is SWAP9 (active low)
397    PB1 is SWAP8 (active low)
398    PB0 is PREADY
399 */
set_pb(struct _i8255a_state * ctx,uint8_t byte,int8_t reg)400 static void set_pb(struct _i8255a_state *ctx, uint8_t byte, int8_t reg)
401 {
402     cmdhd_context_t *hd = (cmdhd_context_t*)(ctx->p);
403 
404     hd->i8255a_o[1] = byte;
405 }
406 
get_pb(struct _i8255a_state * ctx,int8_t reg)407 static uint8_t get_pb(struct _i8255a_state *ctx, int8_t reg)
408 {
409     cmdhd_context_t *hd = (cmdhd_context_t*)(ctx->p);
410     uint8_t data = 0xff;
411 
412     data = ((~cmdbus.bus << 2) & 0x80) | /* PATN */
413            (~cmdbus.bus & 0x40) |        /* PCLK */
414            ((~cmdbus.bus >> 7) & 0x01) | /* PREADY */
415            (hd->i8255a_i[1] & 0x3e);     /* Everything else */
416 
417     return data;
418 }
419 
420 /* Called to deal with how PATN changes PREADY */
421 /* "new" and "old", 0 or 1, are PATN, not /PATN */
422 /* CMDHD's have a circuit which drives /PREADY */
423 /* It has a FF where PC7 controls /CLR and PATN is the CLK */
424 /* The output is NANDed with PATN which drives /PREADY */
425 /* /PREADY is also connected to a buffer (OC) driven by PC7 */
cmdhd_patn_changed(unsigned int unit,int new,int old)426 static int cmdhd_patn_changed(unsigned int unit, int new, int old)
427 {
428     cmdhd_context_t *hd;
429     int t;
430 
431     /* standard unit range check */
432     if (unit < 8 || unit > 8 + NUM_DISK_UNITS) {
433         return -1;
434     }
435 
436     /* check context */
437     if (!diskunit_context[unit - 8]) {
438         return -1;
439     }
440 
441     if (diskunit_context[unit - 8]->type != DRIVE_TYPE_CMDHD) {
442         return -1;
443     }
444 
445     /* get context */
446     hd = diskunit_context[unit - 8]->cmdhd;
447 
448     /* leave if no HD contxt provided */
449     if (!hd) {
450         return -1;
451     }
452 
453     /* The drive type is a CMDHD by this point */
454 
455     if ((hd->i8255a_o[2] & 0x80) == 0) {
456         /* when PC7 is 0, PREADYFF becomes 0 */
457         hd->preadyff = 0;
458     } else if (old == 0 && new == 1) {
459         /* on rising edge of PATN, PREADYFF = 1 */
460         hd->preadyff = 1;
461     }
462     /* /PREADY = !(NEWATN & PREADYFF) */
463     t = !(new & hd->preadyff);
464     /* The OC buffer */
465     if ((hd->i8255a_o[2] & 0x80) == 0) {
466         t = 0;
467     }
468 
469     cmdbus.drv_bus[unit - 8] = (cmdbus.drv_bus[unit - 8] & 0x7f) | (t << 7);
470 
471     return 0;
472 }
473 
474 /* Update ALL TDE units when PATN changed */
475 /* called from ramlink */
cmdbus_patn_changed(int new,int old)476 void cmdbus_patn_changed(int new, int old)
477 {
478     int unit;
479 
480     for (unit = 8; unit < 8 + NUM_DISK_UNITS; unit++ ) {
481         cmdhd_patn_changed(unit, new, old);
482     }
483 }
484 
485 /* Port C is for CMD Parallel bus, SCSI, and memory control (output only):
486    PC7 is for used for driving PREADY
487    PC6 is /PCLK
488    PC5 is /PEXT
489    PC4 is SCSI BSY
490    PC3 is SCSI RST
491    PC2 is SCSI ATN
492    PC1 is RAM mapping
493    PC0 is ROM control
494 */
set_pc(struct _i8255a_state * ctx,uint8_t byte,int8_t reg)495 static void set_pc(struct _i8255a_state *ctx, uint8_t byte, int8_t reg)
496 {
497     cmdhd_context_t *hd = (cmdhd_context_t*)(ctx->p);
498     scsi_context_t *scsi = (scsi_context_t*)(hd->scsi);
499 /*    drivecpu_context_t *cpu = hd->mycontext->cpu; */
500     int t;
501     int mynumber = hd->mycontext->mynumber;
502 
503     hd->i8255a_o[2] = byte;
504     scsi->atn = ((hd->i8255a_o[2] & 4)!=0);
505     scsi->rst = ((hd->i8255a_o[2] & 8)!=0);
506     scsi->bsyi = ((hd->i8255a_o[2] & 16)!=0);
507     scsi_process_noack(scsi);
508 
509     /* get the PATN state (from /PATN) */
510     t = (cmdbus.bus) & 0x20 ? 0 : 1;
511 
512     /* adjust /PREADY */
513     cmdhd_patn_changed(mynumber + 8, t, t);
514 
515     /* update bus */
516     cmdbus.drv_bus[mynumber] =
517         (cmdbus.drv_bus[mynumber] & 0xa0)  | /* old /PREADY and /PATN */
518         (hd->i8255a_o[2] & 0x40)           | /* /PCLK */
519         ((hd->i8255a_o[2] & 0x20)>>1)      | /* /PEXT */
520         0x0f;                                /* everything else */
521     cmdbus_update();
522 }
523 
get_pc(struct _i8255a_state * ctx,int8_t reg)524 static uint8_t get_pc(struct _i8255a_state *ctx, int8_t reg)
525 {
526     cmdhd_context_t *hd = (cmdhd_context_t*)(ctx->p);
527     uint8_t data = 0xff;
528 
529     data = hd->i8255a_i[2];
530 
531     return data;
532 }
533 
updateleds(diskunit_context_t * ctxptr)534 static void updateleds(diskunit_context_t *ctxptr)
535 {
536     ctxptr->drives[0]->led_status = (ctxptr->cmdhd->LEDs & 0x02) ? 1 : 0;
537     ctxptr->drives[0]->led_status |= (ctxptr->cmdhd->LEDs & 0x01) ? 2 : 0;
538 }
539 
cmdhd_store(diskunit_context_t * ctxptr,uint16_t addr,uint8_t data)540 void cmdhd_store(diskunit_context_t *ctxptr, uint16_t addr, uint8_t data)
541 {
542 #ifdef CMDIO
543     static uint8_t oldd;
544     static uint16_t olda;
545     static CLOCK oldc = 0;
546     drivecpu_context_t *cpu = ctxptr->cpu;
547 #define storedebug() \
548 if (olda != addr || olds != data) { \
549     IDBG((LOG, "CMDHD: IO write %02x to %04x PC=%04x CYCLE=%u", data, addr, \
550         cpu->cpu_R65C02_regs.pc, *(ctxptr->clk_ptr)-oldc); \
551     old=data; \
552     olda=addr; \
553     oldc=*(ctxptr->clk_ptr); \
554 }
555 #else
556 #define storedebug()
557 #endif
558     /* Decode bits 15-12 */
559     switch ( (addr >> 12) & 15 ) {
560     case 0x4:
561     case 0x5:
562     case 0x6:
563     case 0x7:
564         /* Since the ROM wants to read/write from the RAM from 0xC000-0xFFFF,
565             when 0x8802.1 = 0, RAM from 0xC000-0xFFFF maps to 0x4000-0x7FFF */
566         if (ctxptr->cmdhd->i8255a_o[2]&2) {
567             drive_store_ram(ctxptr, (addr & 0x3fff) | 0x4000, data );
568         } else {
569             drive_store_ram(ctxptr, (addr & 0x3fff) | 0xC000, data );
570         }
571         break;
572     case 0x8:
573         /* Decode bits 11-8 */
574         /* Since the kernel is loaded into RAM from the HD on startup, if
575             0x8F00.5=0 then the memory (not IO)  above 0x8000 is protected from
576             being written to */
577         switch ((addr >> 8) & 15) {
578         case 0x0: /* 0x80xx U10 */
579         case 0x1: /* 0x81xx U10 */
580             storedebug()
581             viacore_store(ctxptr->cmdhd->via10, addr & 15, data);
582             break;
583         case 0x4: /* 0x84xx U9 */
584         case 0x5: /* 0x85xx U9 */
585             storedebug()
586             viacore_store(ctxptr->cmdhd->via9, addr & 15, data);
587             break;
588         case 0x8: /* 0x88xx U11 */
589         case 0x9: /* 0x89xx U11 */
590             storedebug()
591             i8255a_store(ctxptr->cmdhd->i8255a, addr & 3, data);
592             break;
593         case 0xc: /* 0x8cxx */
594         case 0xd: /* 0x8dxx */
595             storedebug()
596             rtc72421_write(ctxptr->cmdhd->rtc, addr & 15, data);
597             break;
598         case 0xf: /* 0x8fxx U20 */
599             /* Although page 0x8F is RAM, all writes go to U20 */
600             /* OS often reads from 0x8f00 to get past values to OR/AND them */
601             storedebug()
602             ctxptr->cmdhd->LEDs = data;
603             drive_store_ram(ctxptr, (addr & 255) | 0x8f00, data);
604             updateleds(ctxptr);
605             break;
606         case 0xe: /* 0x8exx unprotected RAM */
607             drive_store_ram(ctxptr, (addr & 255) | 0x8e00, data);
608             break;
609         default:
610             /* Everything else writes to RAM if switch is on */
611             if (ctxptr->cmdhd->LEDs&32) {
612                 drive_store_ram(ctxptr, addr, data);
613             }
614             break;
615         }
616         break;
617     case 0x9:
618     case 0xa:
619     case 0xb:
620     case 0xc:
621     case 0xd:
622     case 0xe:
623     case 0xf:
624         if (ctxptr->cmdhd->LEDs&32) {
625             drive_store_ram(ctxptr, addr, data);
626         }
627         break;
628     }
629     return;
630 }
631 
cmdhd_read(diskunit_context_t * ctxptr,uint16_t addr)632 uint8_t cmdhd_read(diskunit_context_t *ctxptr, uint16_t addr)
633 {
634 #ifdef CMDIO
635     static CLOCK oldc = 0;
636     drivecpu_context_t *cpu = ctxptr->cpu;
637 #define readdebug() \
638     IDBG((LOG, "CMDHD: IO read %02x from %04x PC=%04x CYCLE=%u", data, addr, \
639         cpu->cpu_R65C02_regs.pc, *(ctxptr->clk_ptr)-oldc); \
640     oldc=*(ctxptr->clk_ptr);
641 #else
642 #define readdebug()
643 #endif
644     uint8_t data;
645 
646     /* Decode bits 15-12 */
647     switch ( (addr >> 12) & 15 ) {
648     case 0x4:
649     case 0x5:
650     case 0x6:
651     case 0x7:
652         /* Since the ROM wants to read/write from the RAM from 0xC000-0xFFFF,
653             when 0x8802.1 = 0, RAM from 0xC000-0xFFFF maps to 0x4000-0x7FFF */
654         if (ctxptr->cmdhd->i8255a_o[2]&2) {
655             return drive_read_ram(ctxptr, (addr & 0x3fff) | 0x4000 );
656         } else {
657             return drive_read_ram(ctxptr, (addr & 0x3fff) | 0xC000 );
658         }
659         break;
660     case 0x8:
661         /* Decode bits 11-8 */
662         switch ((addr >> 8) & 15) {
663         case 0x0: /* 0x80xx U10 */
664         case 0x1: /* 0x81xx U10 */
665             data = viacore_read(ctxptr->cmdhd->via10, addr & 15);
666             readdebug()
667             return data;
668             break;
669         case 0x4: /* 0x84xx U9 */
670         case 0x5: /* 0x85xx U9 */
671             data = viacore_read(ctxptr->cmdhd->via9, addr & 15);
672             readdebug()
673             return data;
674             break;
675         case 0x8: /* 0x88xx U11 */
676         case 0x9: /* 0x89xx U11 */
677             data = i8255a_read(ctxptr->cmdhd->i8255a, addr & 3);
678             readdebug()
679             return data;
680             break;
681         case 0xc: /* 0x8cxx */
682         case 0xd: /* 0x8dxx */
683             data = rtc72421_read(ctxptr->cmdhd->rtc, addr & 15);
684             readdebug()
685             return data;
686             break;
687         default:
688             return drive_read_ram(ctxptr, addr);
689             break;
690         }
691         break;
692     case 0x9:
693     case 0xa:
694     case 0xb:
695         return drive_read_ram(ctxptr, addr);
696         break;
697     case 0xc:
698     case 0xd:
699     case 0xe:
700     case 0xf:
701         /* ROM is enabled when 0x8802.0 is 1, else RAM */
702         if (ctxptr->cmdhd->i8255a_o[2]&1) {
703             return drive_read_rom(ctxptr, addr & 0x3fff );
704         } else {
705             return drive_read_ram(ctxptr, addr);
706         }
707         break;
708     }
709     return 0;
710 }
711 
cmdhd_peek(diskunit_context_t * ctxptr,uint16_t addr)712 uint8_t cmdhd_peek(diskunit_context_t *ctxptr, uint16_t addr)
713 {
714     return 0;
715 }
716 
cmdhd_dump(diskunit_context_t * ctxptr,uint16_t addr)717 int cmdhd_dump(diskunit_context_t *ctxptr, uint16_t addr)
718 {
719     return 0;
720 }
721 
set_ca2(via_context_t * via_context,int state)722 static void set_ca2(via_context_t *via_context, int state)
723 {
724 }
725 
set_cb2(via_context_t * via_context,int state)726 static void set_cb2(via_context_t *via_context, int state)
727 {
728 }
729 
set_int(via_context_t * via_context,unsigned int int_num,int value,CLOCK rclk)730 static void set_int(via_context_t *via_context, unsigned int int_num,
731                     int value, CLOCK rclk)
732 {
733     cmdhd_context_t *hd = (cmdhd_context_t *)via_context->context;
734     diskunit_context_t *dc = (diskunit_context_t *)(hd->mycontext);
735 
736     interrupt_set_irq(dc->cpu->int_status, int_num, value, rclk);
737 }
738 
restore_int(via_context_t * via_context,unsigned int int_num,int value)739 static void restore_int(via_context_t *via_context, unsigned int int_num,
740                         int value)
741 {
742     cmdhd_context_t *hd = (cmdhd_context_t *)via_context->context;
743     diskunit_context_t *dc = (diskunit_context_t *)(hd->mycontext);
744 
745     interrupt_restore_irq(dc->cpu->int_status, int_num, value);
746 }
747 
undump_pra(via_context_t * via_context,uint8_t byte)748 static void undump_pra(via_context_t *via_context, uint8_t byte)
749 {
750 }
751 
undump_prb10(via_context_t * via_context,uint8_t byte)752 static void undump_prb10(via_context_t *via_context, uint8_t byte)
753 {
754     drivevia_context_t *viap = (drivevia_context_t *)(via_context->prv);
755 
756     if (iecbus != NULL) {
757         uint8_t *drive_bus, *drive_data;
758         unsigned int unit;
759 
760         drive_bus = &(iecbus->drv_bus[viap->number + 8]);
761         drive_data = &(iecbus->drv_data[viap->number + 8]);
762 
763         *drive_data = ~byte;
764         *drive_bus = ((((*drive_data) << 3) & 0x40)
765                       | (((*drive_data) << 6)
766                          & (((*drive_data) | iecbus->cpu_bus) << 3) & 0x80));
767 
768         iecbus->cpu_port = iecbus->cpu_bus;
769         for (unit = 4; unit < 8 + NUM_DISK_UNITS; unit++) {
770             iecbus->cpu_port &= iecbus->drv_bus[unit];
771         }
772 
773         iecbus->drv_port = (((iecbus->cpu_port >> 4) & 0x4)
774                             | (iecbus->cpu_port >> 7)
775                             | ((iecbus->cpu_bus << 3) & 0x80));
776     } else {
777         iec_drive_write((uint8_t)(~byte), viap->number);
778     }
779 }
780 
store_pra9(via_context_t * via_context,uint8_t byte,uint8_t oldpa,uint16_t addr)781 static void store_pra9(via_context_t *via_context, uint8_t byte, uint8_t oldpa,
782                       uint16_t addr)
783 {
784     cmdhd_context_t *hd = (cmdhd_context_t *)via_context->context;
785     scsi_context_t *scsi = (scsi_context_t*)(hd->scsi);
786     scsi_set_bus(scsi, byte);
787 
788     if (scsi->state!=SCSI_STATE_BUSFREE && ((addr & 0xf) == VIA_PRA) ) {
789         scsi_process_ack(scsi);
790     } else {
791         scsi_process_noack(scsi);
792     }
793 }
794 
read_pra9(via_context_t * via_context,uint16_t addr)795 static uint8_t read_pra9(via_context_t *via_context, uint16_t addr)
796 {
797     cmdhd_context_t *hd = (cmdhd_context_t *)via_context->context;
798     scsi_context_t *scsi = (scsi_context_t*)(hd->scsi);
799     uint8_t byte;
800 
801     byte = scsi_get_bus(scsi);
802     if (scsi->state!=SCSI_STATE_BUSFREE && ((addr & 0xf) == VIA_PRA) ) {
803         scsi_process_ack(scsi);
804     } else {
805         scsi_process_noack(scsi);
806     }
807     return byte;
808 }
809 
store_prb9(via_context_t * via_context,uint8_t byte,uint8_t p_oldpb,uint16_t addr)810 static void store_prb9(via_context_t *via_context, uint8_t byte, uint8_t p_oldpb,
811                       uint16_t addr)
812 {
813     cmdhd_context_t *hd = (cmdhd_context_t *)via_context->context;
814     scsi_context_t *scsi = (scsi_context_t*)(hd->scsi);
815 
816     scsi->sel = (byte & 0x10) ? 1 : 0;
817     hd->scsi_dir = (byte & 0x08) ? 1 : 0;
818     scsi_process_noack(scsi);
819     IDBG((LOG, "CMDHD: sprb9: SEL=%d BSY=%d DATA=%02x RST=%d",
820         scsi->sel, scsi->bsyo, byte, scsi->rst));
821 }
822 
read_prb9(via_context_t * via_context)823 static uint8_t read_prb9(via_context_t *via_context)
824 {
825     cmdhd_context_t *hd = (cmdhd_context_t *)via_context->context;
826     scsi_context_t *scsi = (scsi_context_t*)(hd->scsi);
827     uint8_t temp, state;
828 
829     scsi_process_noack(scsi);
830     IDBG((LOG, "CMDHD: rprb9: SEL=%d BSY=%d REQ=%d", scsi->sel, scsi->bsyo, scsi->req));
831     if ( (via_context->via[VIA_PCR] & 0xf0) == 0xf0 ) {
832         temp=(scsi->sel) & (scsi->bsyo);
833     } else {
834         temp=(!scsi->sel) & (scsi->bsyo);
835     }
836 
837     /* mask scsi state to cmd specific pld value */
838     switch (scsi->state)
839     {
840         case SCSI_STATE_DATAOUT:
841         state = CMD_STATE_DATAOUT;
842         break;
843         case SCSI_STATE_DATAIN:
844         state = CMD_STATE_DATAIN;
845         break;
846         case SCSI_STATE_COMMAND:
847         state = CMD_STATE_COMMAND;
848         break;
849         case SCSI_STATE_STATUS:
850         state = CMD_STATE_STATUS;
851         break;
852         case SCSI_STATE_MESSAGEOUT:
853         state = CMD_STATE_MESSAGEOUT;
854         break;
855         case SCSI_STATE_MESSAGEIN:
856         state = CMD_STATE_MESSAGEIN;
857         break;
858         default:
859         state = CMD_STATE_STATUS;
860     }
861 
862     return (scsi->req << 7) | (scsi->ack << 6) | (temp << 5) | (scsi->sel << 4) |
863         (hd->scsi_dir << 3) | (state & 7);
864 }
865 
read_prb10(via_context_t * via_context)866 static uint8_t read_prb10(via_context_t *via_context)
867 {
868     uint8_t byte;
869     drivevia_context_t *viap;
870 
871     viap = (drivevia_context_t *)(via_context->prv);
872 
873     if (iecbus != NULL) {
874         byte = (((via_context->via[VIA_PRB] & 0x1a)
875                  | iecbus->drv_port) ^ 0x85);
876     } else {
877         byte = (((via_context->via[VIA_PRB] & 0x1a)
878                  | iec_drive_read(viap->number)) ^ 0x85);
879     }
880 
881     DEBUG_IEC_DRV_READ(byte);
882 
883     DEBUG_IEC_BUS_READ(byte);
884 
885     return byte;
886 }
887 
store_prb10(via_context_t * via_context,uint8_t byte,uint8_t oldpb,uint16_t addr)888 static void store_prb10(via_context_t *via_context, uint8_t byte, uint8_t oldpb,
889                       uint16_t addr)
890 {
891     drivevia_context_t *viap;
892 
893     viap = (drivevia_context_t *)(via_context->prv);
894 
895     if (byte != oldpb) {
896         DEBUG_IEC_DRV_WRITE(byte);
897 
898         if (iecbus != NULL) {
899             uint8_t *drive_data, *drive_bus;
900             unsigned int unit;
901 
902             drive_bus = &(iecbus->drv_bus[viap->number + 8]);
903             drive_data = &(iecbus->drv_data[viap->number + 8]);
904 
905             *drive_data = ~byte;
906             *drive_bus = ((((*drive_data) << 3) & 0x40)
907                           | (((*drive_data) << 6)
908                              & (((*drive_data) | iecbus->cpu_bus) << 3) & 0x80));
909 
910             iecbus->cpu_port = iecbus->cpu_bus;
911             for (unit = 4; unit < 8 + NUM_DISK_UNITS; unit++) {
912                 iecbus->cpu_port &= iecbus->drv_bus[unit];
913             }
914 
915             iecbus->drv_port = (((iecbus->cpu_port >> 4) & 0x4)
916                                 | (iecbus->cpu_port >> 7)
917                                 | ((iecbus->cpu_bus << 3) & 0x80));
918 
919             DEBUG_IEC_BUS_WRITE(iecbus->drv_port);
920         } else {
921             iec_drive_write((uint8_t)(~byte), viap->number);
922             DEBUG_IEC_BUS_WRITE(~byte);
923         }
924 
925         iec_fast_drive_direction(byte & 0x20, viap->number);
926     }
927 }
928 
undump_prb(via_context_t * via_context,uint8_t byte)929 static void undump_prb(via_context_t *via_context, uint8_t byte)
930 {
931 }
932 
store_pra10(via_context_t * via_context,uint8_t byte,uint8_t p_oldpb,uint16_t addr)933 static void store_pra10(via_context_t *via_context, uint8_t byte, uint8_t p_oldpb,
934                       uint16_t addr)
935 {
936 }
937 
undump_pcr(via_context_t * via_context,uint8_t byte)938 static void undump_pcr(via_context_t *via_context, uint8_t byte)
939 {
940 }
941 
store_pcr(via_context_t * via_context,uint8_t byte,uint16_t addr)942 static uint8_t store_pcr(via_context_t *via_context, uint8_t byte, uint16_t addr)
943 {
944     return byte;
945 }
946 
undump_acr(via_context_t * via_context,uint8_t byte)947 static void undump_acr(via_context_t *via_context, uint8_t byte)
948 {
949 }
950 
store_acr(via_context_t * via_context,uint8_t byte)951 static void store_acr(via_context_t *via_context, uint8_t byte)
952 {
953 }
954 
store_sr(via_context_t * via_context,uint8_t byte)955 static void store_sr(via_context_t *via_context, uint8_t byte)
956 {
957 }
958 
store_sr10(via_context_t * via_context,uint8_t byte)959 static void store_sr10(via_context_t *via_context, uint8_t byte)
960 {
961     drivevia_context_t *viap;
962 
963     viap = (drivevia_context_t *)(via_context->prv);
964 
965     iec_fast_drive_write(byte, viap->number);
966 }
967 
store_t2l(via_context_t * via_context,uint8_t byte)968 static void store_t2l(via_context_t *via_context, uint8_t byte)
969 {
970 }
971 
reset(via_context_t * via_context)972 static void reset(via_context_t *via_context)
973 {
974 }
975 
read_pra10(via_context_t * via_context,uint16_t addr)976 static uint8_t read_pra10(via_context_t *via_context, uint16_t addr)
977 {
978     return 255;
979 }
980 
cmdhd_setup_context(diskunit_context_t * ctxptr)981 void cmdhd_setup_context(diskunit_context_t *ctxptr)
982 {
983     drivevia_context_t *via10p;
984     via_context_t *via10, *via9;
985     scsi_context_t *scsi;
986     i8255a_state *i8255a;
987     char *name = NULL;
988 
989     CLOG((LOG, "CMDHD: setup_context"));
990 
991     ctxptr->drives[0]->side = 0;
992 
993     ctxptr->cmdhd = lib_calloc(1, sizeof(cmdhd_context_t));
994     ctxptr->cmdhd->myname = lib_msprintf("CMDHD%d", ctxptr->mynumber);
995     ctxptr->cmdhd->mycontext = ctxptr;
996 
997     ctxptr->cmdhd->image = NULL;
998 
999     /* Clear struct as snapshot code may write uninitialized values.  */
1000     ctxptr->cmdhd->via10 = lib_calloc(1, sizeof(via_context_t));
1001     via10 = ctxptr->cmdhd->via10;
1002 
1003     via10->prv = lib_malloc(sizeof(drivevia_context_t));
1004     via10p = (drivevia_context_t *)(via10->prv);
1005     via10p->number = ctxptr->mynumber;
1006 
1007     via10->context = (void *)ctxptr->cmdhd;
1008 
1009     via10->rmw_flag = &(ctxptr->cpu->rmw_flag);
1010     via10->clk_ptr = ctxptr->clk_ptr;
1011 
1012     via10->myname = lib_msprintf("CMDHD%dVIA10", ctxptr->mynumber);
1013     via10->my_module_name = lib_msprintf("CMDHD%dVIA10", ctxptr->mynumber);
1014 
1015     viacore_setup_context(via10);
1016 
1017     via10->my_module_name_alt1 = lib_msprintf("CMDHDVIA10-%d", ctxptr->mynumber);
1018     via10->my_module_name_alt2 = lib_msprintf("CMDHDVIA10");
1019 
1020     via10->irq_line = IK_IRQ;
1021 
1022     via10p->drive = ctxptr->drives[0];
1023     via10p->v_iecbus = iecbus_drive_port();
1024 
1025     via10->undump_pra = undump_pra;
1026     via10->undump_prb = undump_prb10;
1027     via10->undump_pcr = undump_pcr;
1028     via10->undump_acr = undump_acr;
1029     via10->store_pra = store_pra10;
1030     via10->store_prb = store_prb10;
1031     via10->store_pcr = store_pcr;
1032     via10->store_acr = store_acr;
1033     via10->store_sr = store_sr10;
1034     via10->store_t2l = store_t2l;
1035     via10->read_pra = read_pra10;
1036     via10->read_prb = read_prb10;
1037     via10->set_int = set_int;
1038     via10->restore_int = restore_int;
1039     via10->set_ca2 = set_ca2;
1040     via10->set_cb2 = set_cb2;
1041     via10->reset = reset;
1042 
1043     ctxptr->cmdhd->via9 = lib_calloc(1, sizeof(via_context_t));
1044     via9 = ctxptr->cmdhd->via9;
1045 
1046     via9->context = (void *)ctxptr->cmdhd;
1047 
1048     via9->rmw_flag = &(ctxptr->cpu->rmw_flag);
1049     via9->clk_ptr = ctxptr->clk_ptr;
1050 
1051     via9->myname = lib_msprintf("CMDHD%dVIA9", ctxptr->mynumber);
1052     via9->my_module_name = lib_msprintf("CMDHD%dVIA9", ctxptr->mynumber);
1053 
1054     viacore_setup_context(via9);
1055 
1056     via9->my_module_name_alt1 = lib_msprintf("CMDHDVIA9-%d", ctxptr->mynumber);
1057     via9->my_module_name_alt2 = lib_msprintf("CMDHDVIA9");
1058 
1059     via9->irq_line = IK_IRQ;
1060 
1061     via9->undump_pra = undump_pra;
1062     via9->undump_prb = undump_prb;
1063     via9->undump_pcr = undump_pcr;
1064     via9->undump_acr = undump_acr;
1065     via9->store_pra = store_pra9;
1066     via9->store_prb = store_prb9;
1067     via9->store_pcr = store_pcr;
1068     via9->store_acr = store_acr;
1069     via9->store_sr = store_sr;
1070     via9->store_t2l = store_t2l;
1071     via9->read_pra = read_pra9;
1072     via9->read_prb = read_prb9;
1073     via9->set_int = set_int;
1074     via9->restore_int = restore_int;
1075     via9->set_ca2 = set_ca2;
1076     via9->set_cb2 = set_cb2;
1077     via9->reset = reset;
1078 
1079     ctxptr->cmdhd->scsi = lib_calloc(1, sizeof(scsi_context_t));
1080     scsi = ctxptr->cmdhd->scsi;
1081     scsi->p = ctxptr->cmdhd;
1082     scsi->myname = lib_msprintf("CMDHD%dSCSI", ctxptr->mynumber);
1083 
1084     ctxptr->cmdhd->i8255a = lib_calloc(1, sizeof(i8255a_state));
1085     i8255a = ctxptr->cmdhd->i8255a;
1086     i8255a->p = ctxptr->cmdhd;
1087     i8255a->set_pa = set_pa;
1088     i8255a->set_pb = set_pb;
1089     i8255a->set_pc = set_pc;
1090     i8255a->get_pa = get_pa;
1091     i8255a->get_pb = get_pb;
1092     i8255a->get_pc = get_pc;
1093 
1094     name = lib_msprintf("CMDHD%dRTC", ctxptr->mynumber);
1095     ctxptr->cmdhd->rtc = rtc72421_init(name);
1096     lib_free(name);
1097 
1098     ctxptr->cmdhd->rtc->stop = 0;
1099 
1100     name = lib_msprintf("%sEXEC", ctxptr->cmdhd->myname);
1101     ctxptr->cmdhd->reset_alarm = alarm_new(ctxptr->cpu->alarm_context, name,
1102         reset_alarm_handler, ctxptr->cmdhd);
1103     lib_free(name);
1104 }
1105 
cmdhd_init(diskunit_context_t * ctxptr)1106 void cmdhd_init(diskunit_context_t *ctxptr)
1107 {
1108     scsi_context_t *scsi = (scsi_context_t*)(ctxptr->cmdhd->scsi);
1109 
1110     CLOG((LOG, "CMDHD: init"));
1111 
1112     /* init via cores */
1113     viacore_init(ctxptr->cmdhd->via9, ctxptr->cpu->alarm_context,
1114                  ctxptr->cpu->int_status, ctxptr->cpu->clk_guard);
1115     viacore_init(ctxptr->cmdhd->via10, ctxptr->cpu->alarm_context,
1116                  ctxptr->cpu->int_status, ctxptr->cpu->clk_guard);
1117 
1118     /* reset scsi system */
1119     scsi_reset(scsi);
1120     /* change any defaults */
1121     scsi->max_imagesize = 16777214U << 8;
1122     /* CMDHD can handle 56 drives, but the latest boot rom has a bug which
1123         corrupts data when deleting partitions that span across disk
1124         boundaries. So we only allow it to use one disk. This is fine as
1125         one disk is enough to max out the whole system. */
1126     scsi->max_ids = 1;
1127     /* Setup SCSI user functions */
1128     scsi->user_format = cmdhd_scsiformat;
1129     scsi->user_read = cmdhd_scsiread;
1130     scsi->user_write = cmdhd_scsiwrite;
1131 
1132     ctxptr->cmdhd->preadyff = 1;
1133 }
1134 
cmdhd_shutdown(cmdhd_context_t * hd)1135 void cmdhd_shutdown(cmdhd_context_t *hd)
1136 {
1137     CLOG((LOG, "CMDHD: shutdown"));
1138 
1139     /* leave if no contxt provided */
1140     if (!hd) {
1141         return;
1142     }
1143 
1144     rtc72421_destroy(hd->rtc, hd->mycontext->rtc_save);
1145     /* Don't know if we need this, so why is it even available? */
1146 /*    alarm_destroy(hd->reset_alarm); */
1147     viacore_shutdown(hd->via9);
1148     viacore_shutdown(hd->via10);
1149     lib_free(hd->scsi->myname);
1150     lib_free(hd->scsi);
1151     lib_free(hd->i8255a);
1152     lib_free(hd->myname);
1153     lib_free(hd);
1154 }
1155 
1156 /* Function to find the baselba of the cmd partition. We need this so we
1157 can modify the device number of the drive as it is stored on disk. */
cmdhd_findbaselba(cmdhd_context_t * hd)1158 static void cmdhd_findbaselba(cmdhd_context_t *hd)
1159 {
1160     uint32_t  i;
1161     disk_addr_t dadr;
1162     unsigned char buf[256];
1163 
1164     CLOG((LOG, "CMDHD: findbaselba"));
1165 
1166     /* leave if no contxt provided */
1167     if (!hd) {
1168         return;
1169     }
1170 
1171     /* reset value to invalid */
1172     hd->baselba = UINT32_MAX;
1173 
1174     /* leave if no image provided */
1175     if (!hd->image) {
1176         return;
1177     }
1178 
1179     /* look for configuration block */
1180     i = 2;
1181     /* start at LBA 2 as in 512-byte blocks */
1182     while (i < (hd->imagesize >> 9)) {
1183         /* translate to T/S for DHD images, ie. 65536 for each */
1184         dadr.track = (i / 32768) + 1;
1185         dadr.sector = (i % 32768) * 2 + 1;
1186         /* read the sector */
1187         if (disk_image_read_sector(hd->image, buf, &dadr) < 0) {
1188             /* hit the end of file */
1189             break;
1190         }
1191         /* otherwise check the cmd sig */
1192         if (!cmdhd_has_sig(buf)) {
1193             /* if it has it, update the offset */
1194             hd->baselba = i - 2;
1195             /* quit */
1196             break;
1197         }
1198         /* try next 128 sectors */
1199         i += 128;
1200     }
1201 
1202     CLOG((LOG, "CMDHD: findbaselba=%u", hd->baselba));
1203 }
1204 
cmdhd_reset(cmdhd_context_t * hd)1205 void cmdhd_reset(cmdhd_context_t *hd)
1206 {
1207     CLOCK c;
1208     size_t i;
1209     int unit;
1210 
1211     CLOG((LOG, "CMDHD: reset"));
1212 
1213     /* leave if no contxt provided */
1214     if (!hd) {
1215         return;
1216     }
1217 
1218     /* reset vias */
1219     viacore_reset(hd->via9);
1220     viacore_reset(hd->via10);
1221 
1222     /* setup default inputs to U11 (pullups/downs) */
1223     hd->i8255a_i[0] = 0xff;
1224     hd->i8255a_i[1] = 0x7f;
1225     hd->i8255a_i[2] = 0xe3;
1226     hd->scsi_dir = 0;
1227 
1228     /* check RAM for CMC signature */
1229     /* The HD ROM does a series of hardware checks on reset if it doesn't
1230         find a signature in memory. Otherwise it skips to the boot loader.
1231         If the user holds down a button on reset, we need to set the alarm
1232         to remove the button press based on this. So on a cold reset, wait
1233         about 8M cycles, where as if it is a soft reset, wait 500K cycles. */
1234     c = *(hd->mycontext->clk_ptr) +
1235         cmdhd_has_sig(&(hd->mycontext->drive_ram[0x9000])) ? 8000000 : 500000;
1236     CLOG((LOG, "CMDHD: alarm set for %u from %u", c, *(hd->mycontext->clk_ptr)));
1237     alarm_set(hd->reset_alarm, c);
1238 
1239     /* look for base lba as it may have changed on reset */
1240     cmdhd_findbaselba(hd);
1241 
1242     /* check if write protect button is pressed */
1243     if (hd->mycontext->button&1) {
1244         hd->i8255a_i[1]&=0xf7;
1245         CLOG((LOG, "CMDHD: WP pressed down"));
1246     }
1247     /* check if swap8 button is pressed */
1248     if (hd->mycontext->button&2) {
1249         hd->i8255a_i[1]&=0xfd;
1250         CLOG((LOG, "CMDHD: SWAP8 pressed down"));
1251     }
1252     /* check if swap9 button is pressed */
1253     if (hd->mycontext->button&4) {
1254         hd->i8255a_i[1]&=0xfb;
1255         CLOG((LOG, "CMDHD: SWAP9 pressed down"));
1256     }
1257 
1258     /* count the number of connected drives */
1259     unit = 0;
1260     for (i = 0; i < 7; i++) {
1261         if (hd->scsi->file[i]) {
1262             unit++;
1263         }
1264     }
1265 
1266     /* if the image size is too small, put the drive in installation mode */
1267     /* but if there is more than one drive connect, go to normal mode */
1268     if (hd->imagesize < 73728) {
1269         if (unit == 1) {
1270             hd->i8255a_i[1]&=0xf9;
1271             CRIT((ERR, "CMDHD: image size too small, starting up in installation mode"));
1272         } else {
1273             /* remove scsi ID 0 */
1274             hd->scsi->file[0] = NULL;
1275         }
1276     }
1277 
1278     /* make sure the cmdbus isn't held down */
1279     unit = hd->mycontext->mynumber + 8;
1280     cmdbus.drv_data[unit - 8] = 0xff;
1281     cmdbus.drv_bus[unit - 8] = 0xff;
1282 
1283     /* propogate inputs to output */
1284     i8255a_reset(hd->i8255a);
1285 }
1286 
cmdhd_attach_image(disk_image_t * image,unsigned int unit)1287 int cmdhd_attach_image(disk_image_t *image, unsigned int unit)
1288 {
1289     cmdhd_context_t *hd;
1290     char *basename, *testname;
1291     size_t i;
1292     FILE *test;
1293     size_t filelength;
1294 
1295     CLOG((LOG, "CMDHD: attach_image"));
1296 
1297     /* standard unit range check */
1298     if (unit < 8 || unit > 8 + NUM_DISK_UNITS) {
1299         return -1;
1300     }
1301 
1302     /* make sure this is a DHD image */
1303     switch (image->type) {
1304         case DISK_IMAGE_TYPE_DHD:
1305             disk_image_attach_log(image, LOG, unit, 0);
1306             break;
1307         default:
1308             return -1;
1309     }
1310 
1311     /* get context */
1312     hd = diskunit_context[unit - 8]->cmdhd;
1313 
1314     /* leave if no contxt provided */
1315     if (!hd) {
1316         return -1;
1317     }
1318 
1319     /* record passed values */
1320     hd->image = image;
1321     hd->imagesize = disk_image_size(image);
1322 
1323     /* leave if there is a problem getting the image size */
1324     if (hd->imagesize == UINT32_MAX) {
1325         return -1;
1326     }
1327 
1328     /* copy file FD to the scsi module */
1329     hd->scsi->file[0] = image->media.fsimage->fd;
1330 
1331     /* find the base lba */
1332     cmdhd_findbaselba(hd);
1333 
1334     /* look to see if there are more files with the same base
1335        name, but different extensions: s10, s20, s30,
1336        s<ID><LUN>, no LUN support yet */
1337     hd->scsi->max_ids = 1;
1338     /* copy the file name */
1339     basename = lib_strdup(image->media.fsimage->name);
1340 
1341     /* make sure it ends in some form of DHD */
1342     i = strlen(basename);
1343     if (i > 0 &&
1344         (basename[i - 1] == 'd' || basename[i - 1] == 'D') &&
1345         (basename[i - 2] == 'h' || basename[i - 2] == 'H') &&
1346         (basename[i - 3] == 'd' || basename[i - 3] == 'D')) {
1347         /* strip off the last 2 letters */
1348         basename[i - 2] = 0;
1349         /* change the first D to an S */
1350         basename[i - 3] = (basename[i - 3] & ~31) | 'S';
1351         /* cycle through all of them S10-S60 */
1352         for (i = 1; i < 7; i++) {
1353             /* generate the name */
1354             testname = lib_msprintf("%s%1u0", basename, i);
1355             /* open the file */
1356             test = fopen(testname, "rb+");
1357             if (test) {
1358                 /* if it is there, check the length */
1359                 filelength = util_file_length(test);
1360                 /* must be multiple of 512 */
1361                 if ((filelength % 512) == 0) {
1362                     /* set the FILE pointer */
1363                     hd->scsi->file[i] = test;
1364                     /* update the max ID */
1365                     hd->scsi->max_ids = i + 1;
1366                 } else {
1367                     /* otherwise make sure it is zero */
1368                     hd->scsi->file[i] = NULL;
1369                     fclose(test);
1370                 }
1371             }
1372             /* release any memory */
1373             lib_free(testname);
1374         }
1375     }
1376 
1377     /* release any more memory */
1378     lib_free(basename);
1379 
1380     return 0;
1381 }
1382 
cmdhd_detach_image(disk_image_t * image,unsigned int unit)1383 int cmdhd_detach_image(disk_image_t *image, unsigned int unit)
1384 {
1385     cmdhd_context_t *hd;
1386     int32_t i;
1387 
1388     CLOG((LOG, "CMDHD: detach_image"));
1389 
1390     /* standard unit range check */
1391     if (image == NULL || unit < 8 || unit > 8 + NUM_DISK_UNITS) {
1392         return -1;
1393     }
1394 
1395     /* make sure this is a DHD image */
1396     switch (image->type) {
1397         case DISK_IMAGE_TYPE_DHD:
1398             disk_image_detach_log(image, LOG, unit, 0);
1399             break;
1400         default:
1401             return -1;
1402     }
1403 
1404     /* get context */
1405     hd = diskunit_context[unit - 8]->cmdhd;
1406 
1407     /* leave if no contxt provided */
1408     if (!hd) {
1409         return -1;
1410     }
1411 
1412     /* remove all image settings in this context */
1413     hd->image = NULL;
1414     hd->imagesize = 0;
1415     hd->baselba = UINT32_MAX;
1416     hd->scsi->file[0] = NULL;
1417 
1418     /* close all additional SCSI ID files */
1419     for (i = 1; i < 7; i++) {
1420         /* if it isn't NULL, it must be a file */
1421         if (hd->scsi->file[i]) {
1422             /* close it and set to NULL */
1423             fclose(hd->scsi->file[i]);
1424             hd->scsi->file[i] = NULL;
1425         }
1426     }
1427 
1428     /* make sure the cmdbus isn't held down */
1429     cmdbus.drv_data[unit - 8] = 0xff;
1430     cmdbus.drv_bus[unit - 8] = 0xff;
1431 
1432     return 0;
1433 }
1434 
1435 #define CMDHD_SNAP_MAJOR 1
1436 #define CMDHD_SNAP_MINOR 0
1437 
cmdhd_snapshot_write_module(cmdhd_context_t * drv,struct snapshot_s * s)1438 int cmdhd_snapshot_write_module(cmdhd_context_t *drv, struct snapshot_s *s)
1439 {
1440     snapshot_module_t *m;
1441 
1442     CLOG((LOG, "CMDHD: snapshot_write_module"));
1443 
1444     m = snapshot_module_create(s, drv->myname, CMDHD_SNAP_MAJOR, CMDHD_SNAP_MINOR);
1445 
1446     if (m == NULL) {
1447         return -1;
1448     }
1449 
1450     if (0
1451         || SMW_B(m, drv->LEDs) < 0
1452         || SMW_BA(m, drv->i8255a_i, 3) < 0
1453         || SMW_BA(m, drv->i8255a_o, 3) < 0
1454         || SMW_B(m, drv->scsi_dir) < 0
1455         || SMW_B(m, drv->preadyff) < 0 ) {
1456         snapshot_module_close(m);
1457         return -1;
1458     }
1459 
1460     if (i8255a_snapshot_write_data(drv->i8255a, m) < 0) {
1461         snapshot_module_close(m);
1462         return -1;
1463     }
1464 
1465     if (snapshot_module_close(m) < 0) {
1466         return -1;
1467     }
1468 
1469     if (viacore_snapshot_write_module(drv->via9, s) < 0) {
1470         return -1;
1471     }
1472 
1473     if (viacore_snapshot_write_module(drv->via10, s) < 0) {
1474         return -1;
1475     }
1476 
1477     if (scsi_snapshot_write_module(drv->scsi, s) < 0) {
1478         return -1;
1479     }
1480 
1481     if (rtc72421_write_snapshot(drv->rtc, s) < 0) {
1482         return -1;
1483     }
1484 
1485     return 0;
1486 }
1487 
cmdhd_snapshot_read_module(cmdhd_context_t * drv,struct snapshot_s * s)1488 int cmdhd_snapshot_read_module(cmdhd_context_t *drv, struct snapshot_s *s)
1489 {
1490     uint8_t vmajor, vminor;
1491     snapshot_module_t *m;
1492 
1493     CLOG((LOG, "CMDHD: snapshot_read_module"));
1494 
1495     m = snapshot_module_open(s, drv->myname, &vmajor, &vminor);
1496     if (m == NULL) {
1497         return -1;
1498     }
1499 
1500     /* Do not accept higher versions than current */
1501     if (snapshot_version_is_bigger(vmajor, vminor, CMDHD_SNAP_MAJOR, CMDHD_SNAP_MAJOR)) {
1502         snapshot_set_error(SNAPSHOT_MODULE_HIGHER_VERSION);
1503         snapshot_module_close(m);
1504         return -1;
1505     }
1506 
1507     if (0
1508         || SMR_B(m, &drv->LEDs) < 0
1509         || SMR_BA(m, drv->i8255a_i, 3) < 0
1510         || SMR_BA(m, drv->i8255a_o, 3) < 0
1511         || SMR_B(m, &drv->scsi_dir) < 0
1512         || SMR_B(m, &drv->preadyff) < 0 ) {
1513         snapshot_module_close(m);
1514         return -1;
1515     }
1516 
1517     if (i8255a_snapshot_read_data(drv->i8255a, m) < 0) {
1518         snapshot_module_close(m);
1519         return -1;
1520     }
1521 
1522     if (snapshot_module_close(m) < 0) {
1523         return -1;
1524     }
1525 
1526     alarm_unset(drv->reset_alarm);
1527 
1528     if (viacore_snapshot_read_module(drv->via9, s) < 0) {
1529         return -1;
1530     }
1531 
1532     if (viacore_snapshot_read_module(drv->via10, s) < 0) {
1533         return -1;
1534     }
1535 
1536     if (scsi_snapshot_read_module(drv->scsi, s) < 0) {
1537         return -1;
1538     }
1539 
1540     if (rtc72421_read_snapshot(drv->rtc, s) < 0) {
1541         return -1;
1542     }
1543 
1544     return 0;
1545 }
1546 
1547