]> Shamusworld >> Repos - apple2/blob - src/harddrive.cpp
0f06a500eafe85375a3d2ff055df1803c0881c47
[apple2] / src / harddrive.cpp
1 //
2 // Hard drive support
3 //
4 // by James Hammons
5 // (C) 2019 Underground Software
6 //
7 // This is done by emulating the Apple 2 High-Speed SCSI card.
8 //
9 // How it works:
10 //
11 // First 1K is the driver ROM, repeated four times.  After that, there are 31 1K
12 // chunks that are addressed in the $CC00-$CFFF address range; $C800-$CBFF is a
13 // 1K RAM space (8K static RAM, bank switched).
14 //
15
16 #include "harddrive.h"
17 #include "apple2.h"
18 #include "dis65c02.h"
19 #include "fileio.h"
20 #include "firmware/a2hs-scsi.h"
21 #include "log.h"
22 #include "mmu.h"
23 #include "settings.h"
24 #include "v65c02.h"             // For dumpDis...
25
26
27 static uint8_t romBank = 0;
28 static uint8_t ramBank = 0;
29 static uint8_t deviceID = 7;
30 static bool dmaSwitch = false;
31 static uint8_t staticRAM[0x2000] = { 0 };
32 static uint8_t reg[16];
33
34 // Stuff that will have to GTFO of here
35 static uint8_t * hdData = NULL;
36
37 enum {
38         DVM_DATA_OUT = 0, DVM_DATA_IN = 1, DVM_COMMAND = 2, DVM_STATUS = 3,
39         DVM_MESSAGE_OUT = 6, DVM_MESSAGE_IN = 7, DVM_BUS_FREE = 8,
40         DVM_ARBITRATE = 16, DVM_SELECT = 32
41 };
42
43 static bool DATA_BUS = false;
44 static bool DMA_MODE = false;
45 static bool BSY = false;
46 static bool ATN = false;
47 static bool SEL = false;
48 static bool ACK = false;
49 static bool RST = false;
50 static bool MSG = false;
51 static bool C_D = false;
52 static bool I_O = false;
53 static bool REQ = false;
54 static bool DEV_BSY = false;
55 static bool DRQ = false;
56 static bool DACK = false;
57 static uint8_t devMode = DVM_BUS_FREE;
58 static uint8_t cmdLength;
59 static uint8_t cmd[256];
60 static uint32_t bytesToSend;
61 static uint8_t * buf;
62 static uint32_t bufPtr;
63 static uint8_t response;
64
65
66 static inline void SetNextState(uint8_t state)
67 {
68         devMode = state;
69         MSG = (state & 0x04 ? true : false);
70         C_D = (state & 0x02 ? true : false);
71         I_O = (state & 0x01 ? true : false);
72         cmdLength = 0;
73 }
74
75
76 static void RunDevice(void)
77 {
78         // Let's see where it's really going...
79 /*      if (mainCPU.pc == 0xCE7E)
80                 dumpDis = true;//*/
81
82         // These are SCSI messages sent in response to certain commands
83         static uint8_t readCapacity[8] = { 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00 };
84         static uint8_t inquireData[30] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 'S', 'E', 'A', 'G', 'A', 'T', 'E', ' ', '3', '1', '3', '3', '7', ' ' };
85         static uint8_t badSense[20] = { 0x70, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
86
87         if (RST)
88         {
89                 devMode = DVM_BUS_FREE;
90                 DEV_BSY = false;
91                 return;
92         }
93
94         switch (devMode)
95         {
96         case DVM_BUS_FREE:
97                 // We never initiate, so this we don't worry about whether or not the
98                 // bus is free.
99         case DVM_ARBITRATE:
100                 // Likewise, we don't arbitrate either.
101                 break;
102
103         case DVM_SELECT:
104                 // If we're in Selection phase, see if our ID is on the bus, and, if so,
105                 // go on to the next phase (since the Target drives the phase dance).
106                 if ((reg[0] & 0x40) && DATA_BUS)
107                 {
108                         DEV_BSY = true;
109
110                         // Preset response code to "Good"
111                         response = 0x00;
112
113                         if (ATN)
114                                 SetNextState(DVM_MESSAGE_OUT);
115                         else
116                                 // If no ATN is asserted, go to COMMAND?  Dunno, the firmware
117                                 // doesn't ever go there; it *always* starts with MESSAGE OUT.
118                                 SetNextState(DVM_COMMAND);
119                 }
120
121                 break;
122
123         case DVM_DATA_OUT:
124 //WriteLog("   >>> DATA OUT PHASE (bts=%u)\n", bytesToSend);
125                 if (!ACK)
126                         REQ = true;
127
128                 if (DMA_MODE)
129                 {
130                         if (!DACK)
131                         {
132                                 DRQ = true;
133                         }
134                         else if (DRQ && DACK)
135                         {
136                                 if (buf)
137                                         buf[bufPtr] = reg[0];
138
139                                 DRQ = DACK = false;
140                                 bytesToSend--;
141                                 bufPtr++;
142
143                                 if (bytesToSend == 0)
144                                 {
145                                         REQ = false;
146                                         SetNextState(DVM_STATUS);
147                                         buf = NULL;
148                                 }
149                         }
150                 }
151
152                 break;
153
154         case DVM_DATA_IN:
155 //WriteLog("   >>> DATA IN PHASE (bts=%u)\n", bytesToSend);
156                 if (!ACK)
157                         REQ = true;
158
159                 if (DMA_MODE)
160                 {
161                         if (!DACK)
162                         {
163                                 // If there's no buffer set up, send zeroes...
164                                 reg[6] = (buf == NULL ? 0 : buf[bufPtr]);
165                                 DRQ = true;
166                         }
167                         else if (DRQ && DACK)
168                         {
169                                 DRQ = DACK = false;
170                                 bytesToSend--;
171                                 bufPtr++;
172
173                                 if (bytesToSend == 0)
174                                 {
175                                         REQ = false;
176                                         SetNextState(DVM_STATUS);
177                                         buf = NULL;
178                                 }
179                         }
180                 }
181
182                 break;
183
184         case DVM_COMMAND:
185         {
186                 if (!ACK)
187                         REQ = true;
188                 else if (REQ && ACK)
189                 {
190                         cmd[cmdLength++] = reg[0];
191                         REQ = false;
192                 }
193
194                 uint8_t cmdType = (cmd[0] & 0xE0) >> 5;
195
196                 if ((cmdType == 0) && (cmdLength == 6))
197                 {
198                         // Handle "Test Unit Ready" command
199                         if (cmd[0] == 0)
200                         {
201                                 WriteLog("HD: Received command TEST UNIT READY\n");
202                                 SetNextState(DVM_STATUS);
203                         }
204                         // Handle "Request Sense" command
205                         else if (cmd[0] == 0x03)
206                         {
207                                 WriteLog("HD: Received command REQUEST SENSE [%02X %02X %02X %02X %02X %02X]\n", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5]);
208                                 SetNextState(DVM_DATA_IN);
209                                 bytesToSend = cmd[4];
210
211                                 // Return error for LUNs other than 0
212                                 if ((cmd[1] & 0xE0) != 0)
213                                 {
214                                         buf = badSense;
215                                         bufPtr = 0;
216                                 }
217                         }
218                         // Handle "Read" (6) command
219                         else if (cmd[0] == 0x08)
220                         {
221                                 WriteLog("HD: Received command READ(6) [%02X %02X %02X %02X %02X %02X]\n", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5]);
222                                 SetNextState(DVM_DATA_IN);
223                                 bytesToSend = cmd[4] * 512; // amount is set in blocks
224                                 uint32_t lba = ((cmd[1] & 0x1F) << 16) | (cmd[2] << 8) | cmd[3];
225                                 buf = (hdData != NULL ? &hdData[(lba * 512) + 0x40] : NULL);
226                                 bufPtr = 0;
227                         }
228                         // Handle "Inquire" command
229                         else if (cmd[0] == 0x12)
230                         {
231                                 WriteLog("HD: Received command INQUIRE [%02X %02X %02X %02X %02X %02X]\n", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5]);
232                                 SetNextState(DVM_DATA_IN);
233                                 bytesToSend = cmd[4];
234                                 buf = inquireData;
235                                 bufPtr = 0;
236
237                                 // Reject all but LUN 0
238                                 if ((cmd[1] & 0xE0) != 0)
239                                         response = 0x02; // "Check Condition" code
240                         }
241                         // Handle "Mode Select" command
242                         else if (cmd[0] == 0x15)
243                         {
244                                 WriteLog("HD: Received command MODE SELECT [%02X %02X %02X %02X %02X %02X]\n", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5]);
245                                 SetNextState(DVM_DATA_OUT);
246                                 bytesToSend = cmd[4];
247                         }
248                         // Handle "Mode Sense" command
249                         else if (cmd[0] == 0x1A)
250                         {
251                                 WriteLog("HD: Received command MODE SENSE [%02X %02X %02X %02X %02X %02X]\n", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5]);
252                                 SetNextState(DVM_DATA_IN);
253                                 bytesToSend = cmd[4];
254                         }
255                         else
256                         {
257                                 WriteLog("HD: Received unhandled 6 command [%02X %02X %02X %02X %02X %02X]\n", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5]);
258                                 // Return failure...
259                                 SetNextState(DVM_STATUS);
260                                 response = 0x02; // Check condition code
261                         }
262                 }
263                 else if (((cmdType == 1) || (cmdType == 2)) && (cmdLength == 10))
264                 {
265                         // Handle "Read Capacity" command
266                         if (cmd[0] == 0x25)
267                         {
268                                 WriteLog("HD: Received command READ CAPACITY [%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X]\n", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6], cmd[7], cmd[8], cmd[9]);
269                                 SetNextState(DVM_DATA_IN);
270                                 bytesToSend = 8;//it's always 8...//cmd[4];
271                                 // N.B.: We need to hook this up to the actual emulated HD size...
272                                 buf = readCapacity;
273                                 bufPtr = 0;
274                         }
275                         // Handle "Read" (10) command
276                         else if (cmd[0] == 0x28)
277                         {
278                                 WriteLog("HD: Received command READ(10) [%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X]\n", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6], cmd[7], cmd[8], cmd[9]);
279                                 // Drive next phase
280                                 SetNextState(DVM_DATA_IN);
281                                 bytesToSend = ((cmd[7] << 8) | cmd[8]) * 512; // amount is set in blocks
282                                 uint32_t lba = (cmd[2] << 24) | (cmd[3] << 16) | (cmd[4] << 8) | cmd[5];
283                                 buf = (hdData != NULL ? &hdData[(lba * 512) + 0x40] : NULL);
284                                 bufPtr = 0;
285                         }
286                         // Handle "Write" (10) command
287                         else if (cmd[0] == 0x2A)
288                         {
289                                 WriteLog("HD: Received command WRITE(10) [%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X]\n", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6], cmd[7], cmd[8], cmd[9]);
290                                 // Drive next phase
291                                 SetNextState(DVM_DATA_OUT);
292                                 bytesToSend = ((cmd[7] << 8) | cmd[8]) * 512; // amount is set in blocks
293                                 uint32_t lba = (cmd[2] << 24) | (cmd[3] << 16) | (cmd[4] << 8) | cmd[5];
294                                 buf = (hdData != NULL ? &hdData[(lba * 512) + 0x40] : NULL);
295                                 bufPtr = 0;
296                         }
297                         else
298                         {
299                                 WriteLog("HD: Received unhandled 10 command [%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X]\n", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6], cmd[7], cmd[8], cmd[9]);
300                                 // Return failure...
301                                 SetNextState(DVM_STATUS);
302                                 response = 0x02; // "Check Condition" code
303                         }
304                 }
305                 else if ((cmdType == 5) && (cmdLength == 12))
306                 {
307                         WriteLog("HD: Received unhandled 12 command [%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X]\n", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6], cmd[7], cmd[8], cmd[9], cmd[10], cmd[11]);
308                         // Return failure...
309                         SetNextState(DVM_STATUS);
310                         response = 0x02; // "Check Condition" code
311                 }
312
313                 break;
314         }
315
316         case DVM_STATUS:
317                 if (!ACK)
318                 {
319                         // Return A-OK for everything for now...
320                         reg[0] = 0; // N.B.: This is necessary for some reason...
321                         REQ = true;
322                 }
323                 else if (REQ && ACK)
324                 {
325                         REQ = false;
326                         SetNextState(DVM_MESSAGE_IN);
327                 }
328
329                 break;
330
331         case DVM_MESSAGE_OUT:
332                 if (!ACK)
333                         REQ = true;
334                 if (REQ && ACK)
335                 {
336 //                      WriteLog("HD: Write to target value $%02X\n", reg[0]);
337                         REQ = false;
338                         SetNextState(DVM_COMMAND);
339                 }
340
341                 break;
342
343         case DVM_MESSAGE_IN:
344                 if (!ACK)
345                 {
346                         // Return appropriate response
347                         reg[0] = response;
348                         REQ = true;
349                 }
350                 else if (REQ && ACK)
351                 {
352                         REQ = false;
353                         DEV_BSY = false;
354                         SetNextState(DVM_BUS_FREE);
355                 }
356
357                 break;
358         }
359 }
360
361
362 static uint8_t SlotIOR(uint16_t address)
363 {
364         // This should prolly go somewhere else...
365         RunDevice();
366
367         uint8_t response = reg[address & 0x0F];
368
369         switch (address & 0x0F)
370         {
371                 case 0x00:
372                         // (RO) Current SCSI Data register
373                         break;
374                 case 0x01:
375                         // Initiator Command register.  Bits, from hi to lo:
376                         // ASS. /RST, AIP, LA, ASS. /ACK, A./BSY, A./SEL, A./ATN, DATA BUS
377
378                         // Simulate ARBITRATE signal
379                         if (reg[2] & 0x01)
380                                 response |= 0x40;
381
382                         break;
383                 case 0x02:
384                         // Mode register (chip control)
385                         break;
386                 case 0x03:
387                         // Target Command register (SCSI bus info xfer phase)
388                         break;
389                 case 0x04:
390                         // (RO) Current SCSI Bus Status register:  Bits from hi to lo:
391                         // /RST, /BSY, /REQ, /MSG, /C/D, /I/O, /SEL, /DBP
392 /*if (((mainCPU.pc != 0xCD7C) && (mainCPU.pc != 0xCD5F)) || (romBank != 16))
393         WriteLog("  [%02X %02X %02X %02X %02X %02X %02X %02X] [$C81F=$%02X $C80D=$%02X $C80A=$%02X $C887=$%02X $C806=$%02X $C88F=$%02X $C8EC=$%02X $4F=$%02X]\n", reg[0], reg[1], reg[2], reg[3], reg[4], reg[5], reg[6], reg[7], staticRAM[0x1F], staticRAM[0x0D], staticRAM[0x0A], staticRAM[0x87], staticRAM[0x06], staticRAM[0x8F], staticRAM[0xEC], ram[0x4F]);//*/
394
395                         response = (RST ? 0x80 : 0) | (BSY | DEV_BSY ? 0x40 : 0) | (REQ ? 0x20 : 0) | (MSG ? 0x10 : 0) | (C_D ? 0x08 : 0) | (I_O ? 0x04 : 0) | (SEL ? 0x02 : 0);
396                         break;
397                 case 0x05:
398                 {
399                         // (RO) Bus and Status register
400                         response = (ACK ? 0x01 : 0) | (ATN ? 0x02 : 0) | (DRQ ? 0x40 : 0);
401                         uint8_t tgtMode = (MSG ? 0x04 : 0) | (C_D ? 0x02 : 0) | (I_O ? 0x01 : 0);
402
403                         if ((reg[3] & 0x07) == tgtMode)
404                                 response |= 0x08;
405
406                         break;
407                 }
408                 case 0x06:
409                         // (RO) Input Data register (read from from SCSI bus)
410                         if (DRQ)
411                                 DACK = true;
412
413                         break;
414                 case 0x07:
415                         // (RO) Reset Parity/Interrupt
416                         // Resets PARITY ERR (bit 6), IRQ (bit 5), BUSY ERROR (bit 3) in
417                         // register 5 (Bus & Status)
418                         break;
419                 case 0x0C:
420                         response = 0x10 | (dmaSwitch ? 0x40 : 0);
421                         break;
422                 case 0x0E:
423                         response = romBank | (deviceID << 5);
424                         break;
425         }
426
427 #if 0
428         char SCSIName[16][256] = {
429                 "(RO) Current SCSI Data",
430                 "Initiator Command",
431                 "Mode",
432                 "Target Command",
433                 "(RO) Current SCSI Bus Status",
434                 "(RO) Bus and Status",
435                 "(RO) Input Data",
436                 "(RO) Reset Parity/Interrupt",
437                 "DMA Address LO",
438                 "DMA Address HI",
439                 "DMA Count LO",
440                 "DMA Count HI",
441                 "$C",
442                 "$D",
443                 "Bank/SCSI ID",
444                 "$F"
445         };
446
447         if (((mainCPU.pc != 0xCD7C) && (mainCPU.pc != 0xCD5F)) || (romBank != 16))
448                 WriteLog("HD Slot I/O read %s ($%02X <- $%X, PC=%04X:%u)\n", SCSIName[address & 0x0F], response, address & 0x0F, mainCPU.pc, romBank);
449 #endif
450
451         return response;
452 }
453
454
455 static void SlotIOW(uint16_t address, uint8_t byte)
456 {
457         switch (address & 0x0F)
458         {
459                 case 0x00:
460                         // (WO) Output Data register (data sent over SCSI bus)
461                         if (DRQ)
462                                 DACK = true;
463
464                         break;
465                 case 0x01:
466                         // Initiator Command register.  Bits, from hi to lo:
467                         // ASS. /RST, AIP, LA, ASS. /ACK, A./BSY, A./SEL, A./ATN, DATA BUS
468                         DATA_BUS = (byte & 0x01 ? true : false);
469                         ATN = (byte & 0x02 ? true : false);
470                         SEL = (byte & 0x04 ? true : false);
471                         BSY = (byte & 0x08 ? true : false);
472                         ACK = (byte & 0x10 ? true : false);
473                         RST = (byte & 0x80 ? true : false);
474
475                         if (!(SEL || BSY || DEV_BSY))
476                                 devMode = DVM_BUS_FREE;
477
478                         if (SEL && (devMode == DVM_ARBITRATE))
479                                 devMode = DVM_SELECT;
480
481                         break;
482                 case 0x02:
483                         // Mode register (chip control)
484                         if ((byte & 0x01) && (devMode == DVM_BUS_FREE))
485                                 devMode = DVM_ARBITRATE;
486
487                         // Dma ReQuest is reset here (as well as by hitting a pin)
488                         DMA_MODE = (byte & 0x02 ? true : false);
489
490                         if (!DMA_MODE)
491                                 DRQ = DACK = false;
492
493                         break;
494                 case 0x03:
495                         // Target Command register (SCSI bus info xfer phase)
496                         break;
497                 case 0x04:
498                         // (WO) Select Enable register
499                         break;
500                 case 0x05:
501                         // (WO) Start DMA Send (initiates DMA send)
502                         DRQ = true;
503                         break;
504                 case 0x06:
505                         // (WO) Start DMA Target Receive (initiate DMA receive--tgt mode)
506                         DRQ = true;
507                         break;
508                 case 0x07:
509                         // (WO) Start DMA Initiator Receive (initiate DMA receive--ini mode)
510                         DRQ = true;
511                         break;
512                 case 0x08:
513                         // Lo byte of DMA address?
514                         break;
515                 case 0x09:
516                         // Hi byte of DMA address?
517                         break;
518                 case 0x0A:
519                         // 2's complement of lo byte of transfer amount?
520                         break;
521                 case 0x0B:
522                         // 2's complement of hi byte of transfer amount?
523                         break;
524                 case 0x0C:
525                         // Control/status register?
526                         break;
527                 case 0x0D:
528                         // ???
529                         break;
530                 case 0x0E:
531                         // Bottom 5 bits of $E set the ROM bank
532                         romBank = byte & 0x1F;
533                         break;
534                 case 0x0F:
535                         // Bottom 3 bits of $F set the RAM bank
536                         ramBank = byte & 0x07;
537                         break;
538         }
539
540         reg[address & 0x0F] = byte;
541
542 #if 0
543         char SCSIName[16][256] = {
544                 "(WO) Output Data",
545                 "Initiator Command",
546                 "Mode",
547                 "Target Command",
548                 "(WO) Select Enable",
549                 "(WO) Start DMA Send",
550                 "(WO) Start DMA Target Receive",
551                 "(WO) Start DMA Initiator Receive",
552                 "DMA Address LO",
553                 "DMA Address HI",
554                 "DMA Count LO",
555                 "DMA Count HI",
556                 "$C",
557                 "$D",
558                 "Bank/SCSI ID",
559                 "$F"
560         };
561         char SCSIPhase[11][256] = { "DATA OUT", "DATA IN", "COMMAND", "STATUS", "ERR4", "ERR5", "MESSAGE OUT", "MESSAGE IN", "BUS FREE", "ARBITRATE", "SELECT" };
562
563
564         WriteLog("HD Slot I/O write %s ($%02X -> $%X, PC=%04X:%u) [%s]\n", SCSIName[address & 0x0F], byte, address & 0x0F, mainCPU.pc, romBank, SCSIPhase[devMode]);
565
566         if ((address & 0x0F) == 0x0E)
567         {
568                 if (mainCPU.pc == 0xC78B)
569                 {
570                         uint16_t sp = mainCPU.sp;
571                         uint16_t pc = ram[0x100 + sp + 1] | (ram[0x100 + sp + 2] << 8);
572                         WriteLog("   *** Returning to bank %u, $%04X\n", romBank, pc + 1);
573                 }
574                 else if (mainCPU.pc == 0xC768)
575                 {
576                         WriteLog("   *** Calling to bank %u:%u\n", mainCPU.a, (mainCPU.y & 0xE0) >> 5);
577                 }
578
579                 WriteLog("  [%02X %02X %02X %02X %02X %02X %02X %02X] [$C81F=$%02X $C80D=$%02X $C80A=$%02X $C887=$%02X $C806=$%02X $C88F=$%02X $C8EC=$%02X $4F=$%02X]\n", reg[0], reg[1], reg[2], reg[3], reg[4], reg[5], reg[6], reg[7], staticRAM[0x1F], staticRAM[0x0D], staticRAM[0x0A], staticRAM[0x87], staticRAM[0x06], staticRAM[0x8F], staticRAM[0xEC], ram[0x4F]);
580         }
581 #endif
582
583         // This should prolly go somewhere else...
584         RunDevice();
585 }
586
587
588 static uint8_t SlotROM(uint16_t address)
589 {
590         return a2hsScsiROM[address];
591 }
592
593
594 static uint8_t SlotIOExtraR(uint16_t address)
595 {
596         if (address < 0x400)
597                 return staticRAM[(ramBank * 0x400) + address];
598         else
599                 return a2hsScsiROM[(romBank * 0x400) + address - 0x400];
600 }
601
602
603 static void SlotIOExtraW(uint16_t address, uint8_t byte)
604 {
605         if (address < 0x400)
606                 staticRAM[(ramBank * 0x400) + address] = byte;
607         else
608 //      {
609                 WriteLog("HD: Unhandled HD 1K ROM write ($%02X) @ $C%03X...\n", byte, address + 0x800);
610
611 /*              if ((mainCPU.pc == 0xCDDD) && (romBank == 11))
612                         dumpDis = true;//*/
613 //      }
614 }
615
616
617 void InstallHardDrive(uint8_t slot)
618 {
619         SlotData hd = { SlotIOR, SlotIOW, SlotROM, 0, SlotIOExtraR, SlotIOExtraW };
620         InstallSlotHandler(slot, &hd);
621         char fnBuf[MAX_PATH + 1];
622
623         // If this fails to read the file, the pointer is set to NULL
624         uint32_t size = 0;
625         sprintf(fnBuf, "%s%s", settings.disksPath, settings.hd[0]);
626         hdData = ReadFile(fnBuf, &size);
627
628         if (hdData)
629                 WriteLog("HD: Read Hard Drive image file, %u bytes ($%X)\n", size - 0x40, size - 0x40);
630         else
631                 WriteLog("HD: Could not read Hard Drive image file!\n");
632 }
633