5 // (C) 2019 Underground Software
7 // This is done by emulating the Apple 2 High-Speed SCSI card.
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).
16 #include "harddrive.h"
20 #include "firmware/a2hs-scsi.h"
24 #include "v65c02.h" // For dumpDis...
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 char buffer[2048];
33 static uint8_t reg[16];
35 // Stuff that will have to GTFO of here
36 static uint8_t * hdData = NULL;//[(0x10000 * 512) + 0x40];
40 $2 clears bit 1 and puts it back
41 $C clears bit 0 & 1 and puts it back
42 $F sets bit 7 and puts it back
43 reads $4, if 0 or <= 4 after anding with $BE, CLC & RTS
44 else, put $81 into $C88F, else SEC & RTS (obv. failure mode)
45 $3 is cleared before going to $CF2F
46 which sets, clears, then sets again bit 7 of $E
55 6: Physical DMA switch on card
60 3: Enable RAM bank in bits 0-2 (or make writable maybe?)
64 #1 sets DMA on/off (switch pos UP = OPEN = off)
65 #2-4 sets the computer's SCSI ID number (preset at factory to 7)
67 Looks like bits 5-7 of register $E is device ID
69 From Apple II SCSI Card Tech. Ref.:
71 $0 R Current SCSI data register
72 $0 W Output data register
73 $1 R/W Initiator command register
74 $2 R/W Mode Select register
75 $3 R/W Target command register
77 $4 W Select enable register
78 $5 R Bus and Status register
79 $6 R Input data register
80 $7 R Reset parity/interrupts
84 $A W Memory Bank Select register
87 $E R Read DRQ status bit through D7 bit
89 N.B.: The A2 HS SCSI card wires the A0-A2 lines backwards. So it maps like so: (No, it must be a mistake on the schematic as the code doesn't line up with that interpretation)
101 So the path of execution is:
103 $CC00 is written to, is that the bank select writable flag (@ reg. $E)?
104 $CD00 hide bank select?
105 $CD01 restore bank select?
106 $C808 gets slot # (+$20)
109 $C80B gets set with $98 to signal we've been there already
110 $5D gets flags (6 = running on GS, 5 = bit 6 of reg. $C is set)
111 [could it be that bit 6 of $C is physical DMA enable switch?]
112 $5E is the slot # (+$20)
113 $C80C gets the contents of $5D
114 $C893 gets set with $80 (signal we're in I set mode)
115 $C896 is set with GS Speed Register (0 on non-GS models)
116 $C807 gets set with the SP
117 execution then jumps to...
119 $C809 gets $40 (& $BF32 as well!)
122 - Bank 3:0 (Look for bootable drive)
125 $C80D gets 0 (# of drives found?)
127 $C8DA gets: Device ID from $E is massaged and changed into a single bit
130 Stores $80 (RST) in reg. $1, burns some cycles, stores 0 in reg. $1
131 [Looks like ASSERT /RST]
132 burn cycles, but burn most if $C8DA == 4
133 Clears 32 bytes @ $C92F
134 $C817 gets $40 |_________________
135 $C818 gets 0 | Failure countdown
136 $C8DB gets SCSI ID # from loop (bit field)
137 $4F gets cleared (error flag)
138 Calls $CF5F (send command to device?)
139 So the buffer (@ $C923) looks like so before the call:
140 * 00 00 00 00 00 00 .. .. .. .. .. .. C3 C9 00
141 ^$60/1 points here ^$56/7 points here ($62 = $58 = 0)
142 * Puts $C9C3 into $C92F/30, zeroes $C931
147 Clears bit 1 from reg. $2, bits 0 & 1 from $C
148 [Looks like it clears the DMA MODE bit]
149 * Clears $4F, $C806, $C88F, $C890, $C8EE-F0
150 Sets bit 7 of reg. $F
152 Gets reg. $4, checks for 0, returns success if so
153 [R is SCSI Bus Status]
154 Masks bits 1-5 & 7, checks for 2 or 4, returns success if so
155 [bit 2 = /I/O, bit 1 = /SEL]
156 Else, $81 -> $C88F, returns failure (set bit 7 of $C806, sets C)
158 Returns since $C893 has $80 in it
159 Calls $CC24 (Arbitrate phase)
161 [Target Command, set Data Out]
162 Toggles bit 7 of reg. $E (ON-off-ON)
163 Puts host ID(?) in reg. $0
164 [W: Output Data - sends data on SCSI bus]
166 Puts 0 in reg. $2, then sets bit 0 of reg. $2
167 [bit 0 is ARBITRATE, requires SCSI device ID in $0]
168 Gets reg. $C, checks bit 4
169 If clear, then toggle reg. $E (ON-off-ON) & count down to failure
170 Check bit 6 of reg. $1, loop back if not set
171 [Initiator Command. bit 6 AIP, if set bus free detected]
172 Check bit 5 of reg. $1, loop back if not clear
173 [Initiator Command, bit 5 LA, if set, bus was lost]
174 Check reg. $0 to see if it's same as what's in $C8DA
175 [R: Current SCSI Data]
176 If not, see if it's >= to EORed value & loop back if it is
177 Checks bit 5 of reg. $1, loop back if not clear
178 [Initiator Command, bit 5 LA, if set, bus was lost]
179 Sets bits 1 & 2 of reg. $1, clear bits 5 & 6 of same
180 [Initiator Command: 1 = ASSERT /ATN, 2 = ASSERT /SEL, clear AIP, LA]
181 Clear C and return if success, set $C88F to $80 & set C if failure
182 Calls $CC7A if succesful:
184 [Select Enable: disable interrupts]
185 Stores $C8DA ORed with $C8DB into reg. $0
186 [W: Output Data - writing ?]
187 Set bits 0 & 6 in reg. $1, clear 5 & 6 in reg. $1
188 [W: 0- ASSERT DATA BUS, 6- TEST MODE; 5- unused(?), 6- TEST MODE off]
189 Clear bit 0 in reg. $2
191 Puts contents of $C8DC +set bit 7 into $C821
192 Clears bit 3 in reg. $1
193 [W: 3- ASSERT /BSY (0 disconnects from bus)]
195 Toggle bit 7 of reg. $E (ON-off-ON)
196 Wait for bit 6 of reg. $4 to come on, if not, set C (signal failure)
198 Clears bit 2 in reg. $1
199 [W: ASSERT /SEL (0 de-asserts)]
200 Clears bits 1, 5, 6 in reg. $1
201 [W: 1- /ATN, 5- unused(?), 6- TEST MODE]
202 Clears bit 0 in reg. $1
203 [W: ASSERT DATA BUS (0 de-asserts)]
204 Signals success (C = 0) or failure (C = 1, $C88F = $81)
206 Returns since $C893 has $80 in it still
208 Checks if bit 4 of reg. $C is set, if not, toggle bit 7 of reg. $E
209 Checks $4, if either of bits 1 & 6 are set, if not, signal failure
210 If only bit 2 or 2 & 6 is set, loop back to beginning of call
211 Clears bit 1 of reg. $2, then restores it to what it was
213 Checks for bit 5 of reg. $4, if not set, loop back to begin
215 Moves $C81F into $C820
216 Restores reg. $4 from Y, masks off bits 2-4 and puts it in $C81F
217 [R: 4- /MSG, 3- /C/D, 2- /I/O]
218 Puts prev. value r. shifted 1 into $C82B
219 Uses that as index into jump table
220 R. shifts again by 1 and stuffs into reg. $3
221 [W: Target Command- writes /MSG, /C/D, /I/O]
223 Using Y as index, push value pair @ $CFB4 onto stack & return to call
224 Calls a routine from 0-7...
225 0-1 goes to $6E6C or copies $56-8 into $C81C-E, calls bank 18:0
228 Calls bank 20:0 or 1 (0 for read, 1 for write--PIO mode)
229 2 calls bank 17:0 (/C/D)
230 3 calls bank 17:3 (/C/D + /I/O)
231 4-5 signals failure & returns (bit 4: /MSG, no /C/D = failure)
232 6 calls bank 17:2 (/MSG + /C/D)
233 [During init, it comes here...]
234 Gets $C821, compares it to 1, if so, signal failure & return
237 7 calls bank 17:1 (/MSG + /C/D + /I/O)
238 If bit 7 of $C806 is clear, loop back to begin
240 Does some error checking on $C88F and $C8EC
242 Clears bit 1 from reg. $2, bits 0-1 from reg. $C
244 If it's 0 or $8E, or reg. $4 is 0, skip over next
247 Zeroes out regs. $1, $2, $3, $C
249 [Returns to $CC6D in bank 3:0]
250 Calls $CC9F (Function 1 - INQUIRY + more
253 [12 00 00 00 1E 00 .. .. .. .. .. .. C3 C9 00 .. 1E]
254 Calls bank 16:0 (Do INQUIRY)
255 if $C9C3 (1st byte of INQUIRY data) == $10
263 else (depending on 1st byte),
264 (5=CDROM, 6=DA Tape drive, 7=HD, 8=Scanner, 9=Printer, 3=nonspecific)
265 $C8CB <- 07 06 09 FF FF 05 08
266 $C8B9 <- C0 C0 A0 00 00 C0 A0
267 $C8CC <- F8 F8 78 FF FF B4 70
268 Sets bit 5, clears bit 6 in $C8CC
269 if bits 4-5, 7 are set, set bit 0 of $C8CF
270 Copies 16 bytes of returned data from $C9C3 + $17 to $C8BB
273 Calls bank 21:1 (lock CD-ROM?)
274 Does PREVENT ALLOW MEDIUM REMOVAL if $C8CB == 5
276 Calls $CDDD (MODE SENSE/MODE SELECT)
277 Calls $CEA8 (READ CAPACITY)
278 Calls 16:0 with command READ CAPACITY, data returned @ $C9C3 (8 bytes)
282 if $C8A9 =! 0, set bit 0 of $C8CF
284 if $C8A8 != 0, set bit 0 of $C8CF
285 $C8AF <- $C9C7, save flags
286 $C8AE <- $C9C8, save flags
287 $C8AD <- $C9C9, save in Y
289 Zeroes error flag ($4F)
291 Calls $CFC7 (bank 4:0 direct)
293 Calls $CD65 (READ--reads 512 bytes from LBA set from $C8D2 + 1)
294 Calls $CDDA, sets carry if 1st two bytes are not 'PM'
299 [Returns to $CCDB in bank 3:0]
300 Loops back if bit 7 of $C892 is clear
301 Calls $CD05 (bank 21:2 direct--unlock CD-ROM?)
303 Loops back if $C8DC != 8
305 [Returns to $CC10 in bank 15:0]
306 Checks if call was successful (if not jumps to bank 11:1)
307 execution jumps to...
309 Puts 1 in $43 (unit #), $44
310 Zeroes out $46-49 (block # [2], ??? [2])
311 Puts $08 in $41, zeroes out $40, $42 (command)
320 Selection: In this state, the initiator selects a target unit and get the target to carry out a given function, such as reading or writing data. The initiator outpus the OR value of its SCSI-ID and the SCSI-ID of the target onto the data bus (for example, if the initiator is 2 and the target is 5 then the OR-ed ID on the bus will be 00100100). The target then determines that its ID is on the data bus and sets the /BSY line active. If this does not happen within a given time, then the initiator deactivates the /SEL signal, and the bus will be free. The target determines that it is selected when the /SEL signal and its SCSI ID bit are active and the /BSY and /I/O signals are false. It then asserts the signal within a selection abort time (200µs).
322 static bool DATA_BUS = false;
323 static bool DMA_MODE = false;
324 static bool BSY = false;
325 static bool ATN = false;
326 static bool SEL = false;
327 static bool ACK = false;
328 static bool RST = false;
329 static bool MSG = false;
330 static bool C_D = false;
331 static bool I_O = false;
332 static bool REQ = false;
333 static bool DEV_BSY = false;
334 static bool DRQ = false;
335 static bool DACK = false;
336 static uint8_t devMode = 8;
337 static uint8_t cmdLength;
338 static uint8_t cmd[256];
339 static uint32_t bytesToSend;
340 static uint8_t * buf;
341 static uint32_t bufPtr;
342 static uint8_t response;
344 static void RunDevice(void)
346 //WriteLog(" >>> RUNNING HD...\n");
347 // Let's see where it's really going...
348 /* if (mainCPU.pc == 0xCE7E)
351 static uint8_t readCapacity[8] = { 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00 };
352 static uint8_t inquireData[30] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 'S', 'E', 'A', 'G', 'A', 'T', 'E', ' ', 'P', 'h', 'o', 'n', 'y', '1' };
353 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 };
356 DVM_DATA_OUT = 0, DVM_DATA_IN = 1, DVM_COMMAND = 2, DVM_STATUS = 3,
357 DVM_MESSAGE_OUT = 6, DVM_MESSAGE_IN = 7, DVM_BUS_FREE, DVM_ARBITRATE,
363 //WriteLog(" >>> DEVICE RESET...\n");
364 devMode = DVM_BUS_FREE;
372 if (SEL)//(BSY && SEL)
373 devMode = DVM_ARBITRATE;
377 ////WriteLog(" >>> ARBITRATE PHASE (BSY=%i SEL=%i DATA_BUS=%i [%02X])\n", BSY, SEL, DATA_BUS, reg[0]);
378 if (!BSY && SEL && DATA_BUS && (reg[0] & 0x40))
379 devMode = DVM_SELECT, DEV_BSY = true;
380 else if (!BSY && !SEL)
381 devMode = DVM_BUS_FREE;
385 //WriteLog(" >>> SELECT PHASE\n");
386 // Preset response code to "Good"
391 MSG = true, C_D = true, I_O = false;
392 devMode = DVM_MESSAGE_OUT;
397 // If no ATN is asserted, go to COMMAND I guess?
399 // errrr, no. this does not work. Or does it???
400 MSG = false, C_D = true, I_O = false;
401 devMode = DVM_COMMAND;
407 //WriteLog(" >>> DATA OUT PHASE (bts=%u)\n", bytesToSend);
417 else if (DRQ && DACK)
420 buf[bufPtr] = reg[0];
427 if (bytesToSend == 0)
430 MSG = false, C_D = true, I_O = true;
431 devMode = DVM_STATUS;
439 //WriteLog(" >>> DATA IN PHASE (bts=%u)\n", bytesToSend);
447 // We just send zeroes for now...
451 reg[6] = buf[bufPtr];
455 else if (DRQ && DACK)
462 if (bytesToSend == 0)
465 MSG = false, C_D = true, I_O = true;
466 devMode = DVM_STATUS;
474 //WriteLog(" >>> COMMAND PHASE\n");
479 cmd[cmdLength++] = reg[0];
480 // WriteLog("HD: Write to target value $%02X\n", reg[0]);
484 // Handle "Test Unit Ready" command
485 if ((cmd[0] == 0) && (cmdLength == 6))
487 WriteLog("HD: Received command TEST UNIT READY\n");
490 MSG = false, C_D = true, I_O = true;
491 devMode = DVM_STATUS;
493 // Handle "Request Sense" command
494 else if ((cmd[0] == 0x03) && (cmdLength == 6))
496 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]);
499 MSG = false, C_D = false, I_O = true;
500 devMode = DVM_DATA_IN;
501 bytesToSend = cmd[4];
503 // Return error for LUNs other than 0
504 if ((cmd[1] & 0xE0) != 0)
510 // Handle "Read" (6) command
511 else if ((cmd[0] == 0x08) && (cmdLength == 6))
513 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]);
516 MSG = false, C_D = false, I_O = true;
517 devMode = DVM_DATA_IN;
518 bytesToSend = cmd[4] * 512; // amount is set in blocks
520 // Handle "Inquire" command
521 else if ((cmd[0] == 0x12) && (cmdLength == 6))
523 WriteLog("HD: Received command INQUIRE [%02X %02X %02X %02X %02X %02X]\n", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5]);
526 MSG = false, C_D = false, I_O = true;
527 devMode = DVM_DATA_IN;
528 bytesToSend = cmd[4];
532 // Reject all but LUN 0
533 if ((cmd[1] & 0xE0) != 0)
535 response = 0x02; // Check condition code
536 // MSG = false, C_D = false, I_O = false;
538 // devMode = DVM_BUS_FREE;
541 // Handle "Mode Select" command
542 else if ((cmd[0] == 0x15) && (cmdLength == 6))
544 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]);
547 MSG = false, C_D = false, I_O = false;
548 devMode = DVM_DATA_OUT;
549 bytesToSend = cmd[4];
551 // Handle "Mode Sense" command
552 else if ((cmd[0] == 0x1A) && (cmdLength == 6))
554 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]);
557 MSG = false, C_D = false, I_O = true;
558 devMode = DVM_DATA_IN;
559 bytesToSend = cmd[4];
561 // Handle "Read Capacity" command
562 else if ((cmd[0] == 0x25) && (cmdLength == 10))
564 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]);
567 MSG = false, C_D = false, I_O = true;
568 devMode = DVM_DATA_IN;
569 bytesToSend = 8;//cmd[4];
573 // Handle "Read" (10) command
574 else if ((cmd[0] == 0x28) && (cmdLength == 10))
576 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]);
579 MSG = false, C_D = false, I_O = true;
580 devMode = DVM_DATA_IN;
581 bytesToSend = ((cmd[7] << 8) | cmd[8]) * 512; // amount is set in blocks
582 uint32_t lba = (cmd[2] << 24) | (cmd[3] << 16) | (cmd[4] << 8) | cmd[5];
583 buf = (hdData != NULL ? &hdData[(lba * 512) + 0x40] : NULL);
586 // Handle "Write" (10) command
587 else if ((cmd[0] == 0x2A) && (cmdLength == 10))
589 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]);
592 MSG = false, C_D = false, I_O = false;
593 devMode = DVM_DATA_OUT;
594 bytesToSend = ((cmd[7] << 8) | cmd[8]) * 512; // amount is set in blocks
595 uint32_t lba = (cmd[2] << 24) | (cmd[3] << 16) | (cmd[4] << 8) | cmd[5];
596 buf = (hdData != NULL ? &hdData[(lba * 512) + 0x40] : NULL);
599 else if ((cmdLength == 6) && ((cmd[0] & 0xE0) == 0))
601 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]);
603 else if ((cmdLength == 10) && (((cmd[0] & 0xE0) == 0x20) || ((cmd[0] & 0xE0) == 0x40)))
605 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]);
610 //WriteLog(" >>> STATUS PHASE\n");
613 // Return A-OK for everything for now...
621 MSG = true, C_D = true, I_O = true;
622 devMode = DVM_MESSAGE_IN;
626 case DVM_MESSAGE_OUT:
627 //WriteLog(" >>> MESSAGE OUT PHASE\n");
630 uint8_t msg = reg[0];
631 // WriteLog("HD: Write to target value $%02X\n", msg);
634 MSG = false, C_D = true, I_O = false;
635 devMode = DVM_COMMAND;
641 //WriteLog(" >>> MESSAGE IN PHASE\n");
644 // Return A-OK for everything for now...
652 MSG = false, C_D = false, I_O = false;
654 devMode = DVM_BUS_FREE;
662 static uint8_t SlotIOR(uint16_t address)
664 // This should prolly go somewhere else...
667 char SCSIName[16][256] = {
668 "(RO) Current SCSI Data",
672 "(RO) Current SCSI Bus Status",
673 "(RO) Bus and Status",
675 "(RO) Reset Parity/Interrupt",
686 uint8_t response = reg[address & 0x0F];
688 switch (address & 0x0F)
691 // (RO) Current SCSI Data register
694 // Initiator Command register. Bits, from hi to lo:
695 // ASS. /RST, AIP, LA, ASS. /ACK, A./BSY, A./SEL, A./ATN, DATA BUS
697 // Simulate ARBITRATE signal
703 // Mode register (chip control)
706 // Target Command register (SCSI bus info xfer phase)
709 // (RO) Current SCSI Bus Status register: Bits from hi to lo:
710 // /RST, /BSY, /REQ, /MSG, /C/D, /I/O, /SEL, /DBP
711 /*if (((mainCPU.pc != 0xCD7C) && (mainCPU.pc != 0xCD5F)) || (romBank != 16))
712 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]);//*/
714 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);
718 // (RO) Bus and Status register
719 response = (ACK ? 0x01 : 0) | (ATN ? 0x02 : 0) | (DRQ ? 0x40 : 0);
720 uint8_t tgtMode = (MSG ? 0x04 : 0) | (C_D ? 0x02 : 0) | (I_O ? 0x01 : 0);
722 if ((reg[3] & 0x07) == tgtMode)
728 // (RO) Input Data register (read from from SCSI bus)
734 // (RO) Reset Parity/Interrupt
735 // Resets PARITY ERR (bit 6), IRQ (bit 5), BUSY ERROR (bit 3) in
736 // register 5 (Bus & Status)
739 response = 0x10 | (dmaSwitch ? 0x40 : 0);
742 response = romBank | (deviceID << 5);
746 /* if (((mainCPU.pc != 0xCD7C) && (mainCPU.pc != 0xCD5F)) || (romBank != 16))
747 WriteLog("HD Slot I/O read %s ($%02X <- $%X, PC=%04X:%u)\n", SCSIName[address & 0x0F], response, address & 0x0F, mainCPU.pc, romBank);//*/
753 static void SlotIOW(uint16_t address, uint8_t byte)
755 char SCSIName[16][256] = {
760 "(WO) Select Enable",
761 "(WO) Start DMA Send",
762 "(WO) Start DMA Target Receive",
763 "(WO) Start DMA Initiator Receive",
774 switch (address & 0x0F)
777 // (WO) Output Data register (data sent over SCSI bus)
783 // Initiator Command register. Bits, from hi to lo:
784 // ASS. /RST, AIP, LA, ASS. /ACK, A./BSY, A./SEL, A./ATN, DATA BUS
785 DATA_BUS = (byte & 0x01 ? true : false);
786 ATN = (byte & 0x02 ? true : false);
787 SEL = (byte & 0x04 ? true : false);
788 BSY = (byte & 0x08 ? true : false);
789 ACK = (byte & 0x10 ? true : false);
790 RST = (byte & 0x80 ? true : false);
793 // Mode register (chip control)
795 // Dma ReQuest is reset here (as well as by hitting a pin)
796 DMA_MODE = (byte & 0x02 ? true : false);
803 // Target Command register (SCSI bus info xfer phase)
806 // (WO) Select Enable register
809 // (WO) Start DMA Send (initiates DMA send)
813 // (WO) Start DMA Target Receive (initiate DMA receive--tgt mode)
817 // (WO) Start DMA Initiator Receive (initiate DMA receive--ini mode)
821 // Lo byte of DMA address?
824 // Hi byte of DMA address?
827 // 2's complement of lo byte of transfer amount?
830 // 2's complement of hi byte of transfer amount?
833 // Control/status register?
839 // Bottom 5 bits of $E set the ROM bank
840 romBank = byte & 0x1F;
843 // Bottom 3 bits of $F set the RAM bank
844 ramBank = byte & 0x07;
848 /* WriteLog("HD Slot I/O write %s ($%02X -> $%X, PC=%04X:%u)\n", SCSIName[address & 0x0F], byte, address & 0x0F, mainCPU.pc, romBank);//*/
849 reg[address & 0x0F] = byte;
851 /* if ((address & 0x0F) == 0x0E)
853 if (mainCPU.pc == 0xC78B)
855 uint16_t sp = mainCPU.sp;
856 uint16_t pc = ram[0x100 + sp + 1] | (ram[0x100 + sp + 2] << 8);
857 WriteLog(" *** Returning to bank %u, $%04X\n", romBank, pc + 1);
859 else if (mainCPU.pc == 0xC768)
861 WriteLog(" *** Calling to bank %u:%u\n", mainCPU.a, (mainCPU.y & 0xE0) >> 5);
864 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]);
867 // This should prolly go somewhere else...
872 static uint8_t SlotROM(uint16_t address)
874 return a2hsScsiROM[address];
878 static uint8_t SlotIOExtraR(uint16_t address)
881 return staticRAM[(ramBank * 0x400) + address];
883 return a2hsScsiROM[(romBank * 0x400) + address - 0x400];
887 static void SlotIOExtraW(uint16_t address, uint8_t byte)
890 staticRAM[(ramBank * 0x400) + address] = byte;
893 WriteLog("HD: Unhandled HD 1K ROM write ($%02X) @ $C%03X...\n", byte, address + 0x800);
895 /* if ((mainCPU.pc == 0xCDDD) && (romBank == 11))
901 void InstallHardDrive(uint8_t slot)
903 SlotData hd = { SlotIOR, SlotIOW, SlotROM, 0, SlotIOExtraR, SlotIOExtraW };
904 InstallSlotHandler(slot, &hd);
906 // If this fails to read the file, the pointer is set to NULL
908 hdData = ReadFile(settings.hdPath, &size);
911 WriteLog("HD: Read Hard Drive image file, %u bytes ($%X)\n", size - 0x40, size - 0x40);
913 WriteLog("HD: Could not read Hard Drive image file!\n");