diff --git a/.gitignore b/.gitignore index 6ff8ef8..ad2bfdc 100644 --- a/.gitignore +++ b/.gitignore @@ -13,9 +13,14 @@ reference/ src/gui/foooked/ ROMs/bin2c ROMs/from-applewin/ -res/ -docs/ -web/source/ +ROMs/misc/ ROMs/asimov/ +res/ +docs/*.pdf +docs/*.jpg +docs/redbook/ +docs/scsi/ +docs/misc/ +web/source/ misc/ changes-since-last-commit.txt diff --git a/src/harddrive.cpp b/src/harddrive.cpp index 731f417..ca90a28 100644 --- a/src/harddrive.cpp +++ b/src/harddrive.cpp @@ -29,296 +29,17 @@ static uint8_t ramBank = 0; static uint8_t deviceID = 7; static bool dmaSwitch = false; static uint8_t staticRAM[0x2000] = { 0 }; -//static char buffer[2048]; static uint8_t reg[16]; // Stuff that will have to GTFO of here -static uint8_t * hdData = NULL;//[(0x10000 * 512) + 0x40]; +static uint8_t * hdData = NULL; +enum { + DVM_DATA_OUT = 0, DVM_DATA_IN = 1, DVM_COMMAND = 2, DVM_STATUS = 3, + DVM_MESSAGE_OUT = 6, DVM_MESSAGE_IN = 7, DVM_BUS_FREE = 8, + DVM_ARBITRATE = 16, DVM_SELECT = 32 +}; -/* -$2 clears bit 1 and puts it back -$C clears bit 0 & 1 and puts it back -$F sets bit 7 and puts it back -reads $4, if 0 or <= 4 after anding with $BE, CLC & RTS - else, put $81 into $C88F, else SEC & RTS (obv. failure mode) -$3 is cleared before going to $CF2F - which sets, clears, then sets again bit 7 of $E - -$C bits: - 0: - 1: - 2: - 3: - 4: - 5: - 6: Physical DMA switch on card - 7: - -$F bits: - 0-2: RAM bank # (?) - 3: Enable RAM bank in bits 0-2 (or make writable maybe?) - 4-7: ??? - -Switches on the card: -#1 sets DMA on/off (switch pos UP = OPEN = off) -#2-4 sets the computer's SCSI ID number (preset at factory to 7) - -Looks like bits 5-7 of register $E is device ID - -From Apple II SCSI Card Tech. Ref.: - -$0 R Current SCSI data register -$0 W Output data register -$1 R/W Initiator command register -$2 R/W Mode Select register -$3 R/W Target command register -$4 R SCSI bus status -$4 W Select enable register -$5 R Bus and Status register -$6 R Input data register -$7 R Reset parity/interrupts - -$8 R/W PDMA/DACK -$9 R SCSI device ID -$A W Memory Bank Select register -$B W Reset 5380 IC -$D W PDMA mode enable -$E R Read DRQ status bit through D7 bit - -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) - -ZP locations: -$42 Command number -$43 Unit number -$44-45 Buffer pointer -$46-47 Block number - -0123456789ABCDEF -@ABCDEFGHIJKLMNO -PQRSTUVWXYZ _ - -So the path of execution is: - - $CC00 is written to, is that the bank select writable flag (@ reg. $E)? - $CD00 hide bank select? - $CD01 restore bank select? - $C808 gets slot # (+$20) - $C809 gets 0 - - Bank 11:0 - $C80B gets set with $98 to signal we've been there already - $5D gets flags (6 = running on GS, 5 = bit 6 of reg. $C is set) - [could it be that bit 6 of $C is physical DMA enable switch?] - $5E is the slot # (+$20) - $C80C gets the contents of $5D - $C893 gets set with $80 (signal we're in I set mode) - $C896 is set with GS Speed Register (0 on non-GS models) - $C807 gets set with the SP - execution then jumps to... - - Bank 15:0 - $C809 gets $40 (& $BF32 as well!) - $C80A gets 0 - Calls bank 3:0 - - Bank 3:0 (Look for bootable drive) - $C883 gets 0 - $C815 gets 0 - $C80D gets 0 (# of drives found?) - $C80F gets 0 - $C8DA gets: Device ID from $E is massaged and changed into a single bit - Calls bank 21:3 - - Bank 21:3 - Stores $80 (RST) in reg. $1, burns some cycles, stores 0 in reg. $1 - [Looks like ASSERT /RST] - burn cycles, but burn most if $C8DA == 4 - Clears 32 bytes @ $C92F - $C817 gets $40 |_________________ - $C818 gets 0 | Failure countdown - $C8DB gets SCSI ID # from loop (bit field) - $4F gets cleared (error flag) - Calls $CF5F (send command to device?) - So the buffer (@ $C923) looks like so before the call: -* 00 00 00 00 00 00 .. .. .. .. .. .. C3 C9 00 - ^$60/1 points here ^$56/7 points here ($62 = $58 = 0) -* Puts $C9C3 into $C92F/30, zeroes $C931 - Then calls bank 16:0 - - Bank 16:0 - Stores to $CD00 - Calls $CDD0 - Clears bit 1 from reg. $2, bits 0 & 1 from $C - [Looks like it clears the DMA MODE bit] -* Clears $4F, $C806, $C88F, $C890, $C8EE-F0 - Sets bit 7 of reg. $F - Calls $CECE - Gets reg. $4, checks for 0, returns success if so - [R is SCSI Bus Status] - Masks bits 1-5 & 7, checks for 2 or 4, returns success if so - [bit 2 = /I/O, bit 1 = /SEL] - Else, $81 -> $C88F, returns failure (set bit 7 of $C806, sets C) - Calls $CF42 - Returns since $C893 has $80 in it - Calls $CC24 (Arbitrate phase) - Zeroes reg. $3 - [Target Command, set Data Out] - Toggles bit 7 of reg. $E (ON-off-ON) - Puts host ID(?) in reg. $0 - [W: Output Data - sends data on SCSI bus] - Loop: - Puts 0 in reg. $2, then sets bit 0 of reg. $2 - [bit 0 is ARBITRATE, requires SCSI device ID in $0] - Gets reg. $C, checks bit 4 - If clear, then toggle reg. $E (ON-off-ON) & count down to failure - Check bit 6 of reg. $1, loop back if not set - [Initiator Command. bit 6 AIP, if set bus free detected] - Check bit 5 of reg. $1, loop back if not clear - [Initiator Command, bit 5 LA, if set, bus was lost] - Check reg. $0 to see if it's same as what's in $C8DA - [R: Current SCSI Data] - If not, see if it's >= to EORed value & loop back if it is - Checks bit 5 of reg. $1, loop back if not clear - [Initiator Command, bit 5 LA, if set, bus was lost] - Sets bits 1 & 2 of reg. $1, clear bits 5 & 6 of same - [Initiator Command: 1 = ASSERT /ATN, 2 = ASSERT /SEL, clear AIP, LA] - Clear C and return if success, set $C88F to $80 & set C if failure - Calls $CC7A if succesful: - Zeroes out reg. $4 - [Select Enable: disable interrupts] - Stores $C8DA ORed with $C8DB into reg. $0 - [W: Output Data - writing ?] - Set bits 0 & 6 in reg. $1, clear 5 & 6 in reg. $1 - [W: 0- ASSERT DATA BUS, 6- TEST MODE; 5- unused(?), 6- TEST MODE off] - Clear bit 0 in reg. $2 - [W: Clear ARBITRATE] - Puts contents of $C8DC +set bit 7 into $C821 - Clears bit 3 in reg. $1 - [W: 3- ASSERT /BSY (0 disconnects from bus)] - Calls $CD51 - Toggle bit 7 of reg. $E (ON-off-ON) - Wait for bit 6 of reg. $4 to come on, if not, set C (signal failure) - [R: bit 6- /BSY] - Clears bit 2 in reg. $1 - [W: ASSERT /SEL (0 de-asserts)] - Clears bits 1, 5, 6 in reg. $1 - [W: 1- /ATN, 5- unused(?), 6- TEST MODE] - Clears bit 0 in reg. $1 - [W: ASSERT DATA BUS (0 de-asserts)] - Signals success (C = 0) or failure (C = 1, $C88F = $81) - Calls $CF58 - Returns since $C893 has $80 in it still - Calls $CCE4 - Checks if bit 4 of reg. $C is set, if not, toggle bit 7 of reg. $E - Checks $4, if either of bits 1 & 6 are set, if not, signal failure - If only bit 2 or 2 & 6 is set, loop back to beginning of call - Clears bit 1 of reg. $2, then restores it to what it was - [W: 1- DMA MODE] - Checks for bit 5 of reg. $4, if not set, loop back to begin - [R: 5- /REQ] - Moves $C81F into $C820 - Restores reg. $4 from Y, masks off bits 2-4 and puts it in $C81F - [R: 4- /MSG, 3- /C/D, 2- /I/O] - Puts prev. value r. shifted 1 into $C82B - Uses that as index into jump table - R. shifts again by 1 and stuffs into reg. $3 - [W: Target Command- writes /MSG, /C/D, /I/O] - Calls $CD48 - Using Y as index, push value pair @ $CFB4 onto stack & return to call - Calls a routine from 0-7... - 0-1 goes to $6E6C or copies $56-8 into $C81C-E, calls bank 18:0 - - Bank 18:0 - ... - Calls bank 20:0 or 1 (0 for read, 1 for write--PIO mode) - 2 calls bank 17:0 (/C/D) - 3 calls bank 17:3 (/C/D + /I/O) - 4-5 signals failure & returns (bit 4: /MSG, no /C/D = failure) - 6 calls bank 17:2 (/MSG + /C/D) - [During init, it comes here...] - Gets $C821, compares it to 1, if so, signal failure & return - Calls $CE79 - a - 7 calls bank 17:1 (/MSG + /C/D + /I/O) - If bit 7 of $C806 is clear, loop back to begin - Calls $CDA0 - Does some error checking on $C88F and $C8EC - Jumps to $CE18 - Clears bit 1 from reg. $2, bits 0-1 from reg. $C - Moves $C88F into $4F - If it's 0 or $8E, or reg. $4 is 0, skip over next - Calls $CE6C - Moves $C88F into $4F - Zeroes out regs. $1, $2, $3, $C - Stores to $CD01 - [Returns to $CC6D in bank 3:0] - Calls $CC9F (Function 1 - INQUIRY + more - Zeroes $C8CF, $C892 - Calls $CD0E - [12 00 00 00 1E 00 .. .. .. .. .. .. C3 C9 00 .. 1E] - Calls bank 16:0 (Do INQUIRY) - if $C9C3 (1st byte of INQUIRY data) == $10 - $C892 <- $80 - $C8CB <- $06 - $C8B9 <- $F8 - $C8CC <- $C0 - else if == 2 or 6, - $C892 <- $40 - $C8CF |= $0C - else (depending on 1st byte), - (5=CDROM, 6=DA Tape drive, 7=HD, 8=Scanner, 9=Printer, 3=nonspecific) - $C8CB <- 07 06 09 FF FF 05 08 - $C8B9 <- C0 C0 A0 00 00 C0 A0 - $C8CC <- F8 F8 78 FF FF B4 70 - Sets bit 5, clears bit 6 in $C8CC - if bits 4-5, 7 are set, set bit 0 of $C8CF - Copies 16 bytes of returned data from $C9C3 + $17 to $C8BB - $C8CE <- $30 - $C8CD <- $00 - Calls bank 21:1 (lock CD-ROM?) - Does PREVENT ALLOW MEDIUM REMOVAL if $C8CB == 5 - $C927 <- $01 - Calls $CDDD (MODE SENSE/MODE SELECT) - Calls $CEA8 (READ CAPACITY) - Calls 16:0 with command READ CAPACITY, data returned @ $C9C3 (8 bytes) - $C8AB <- $C9C6 - $C8AA <- $C9C5 - $C8A9 <- $C9C4 - if $C8A9 =! 0, set bit 0 of $C8CF - $C8A8 <- $C9C3 - if $C8A8 != 0, set bit 0 of $C8CF - $C8AF <- $C9C7, save flags - $C8AE <- $C9C8, save flags - $C8AD <- $C9C9, save in Y - $C8AC <- $C9CA - Zeroes error flag ($4F) - $C8CF |= 0x0C - Calls $CFC7 (bank 4:0 direct) - - Bank 4:0 - Calls $CD65 (READ--reads 512 bytes from LBA set from $C8D2 + 1) - Calls $CDDA, sets carry if 1st two bytes are not 'PM' - Calls $CD39 - Calls $CD1A - $C8D0 <- $C80F - Calls $CDF1 - [Returns to $CCDB in bank 3:0] - Loops back if bit 7 of $C892 is clear - Calls $CD05 (bank 21:2 direct--unlock CD-ROM?) - Adds 1 to $C8DC - Loops back if $C8DC != 8 - ... - [Returns to $CC10 in bank 15:0] - Checks if call was successful (if not jumps to bank 11:1) - execution jumps to... - - Bank 11:2 ($CD9A) - Puts 1 in $43 (unit #), $44 - Zeroes out $46-49 (block # [2], ??? [2]) - Puts $08 in $41, zeroes out $40, $42 (command) - Calls bank 9:0 - - Bank 9:0 - ... - Calls bank 16:0 - -SCSI Phases ------------ - -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). -*/ static bool DATA_BUS = false; static bool DMA_MODE = false; static bool BSY = false; @@ -333,7 +54,7 @@ static bool REQ = false; static bool DEV_BSY = false; static bool DRQ = false; static bool DACK = false; -static uint8_t devMode = 8; +static uint8_t devMode = DVM_BUS_FREE; static uint8_t cmdLength; static uint8_t cmd[256]; static uint32_t bytesToSend; @@ -341,26 +62,30 @@ static uint8_t * buf; static uint32_t bufPtr; static uint8_t response; + +static inline void SetNextState(uint8_t state) +{ + devMode = state; + MSG = (state & 0x04 ? true : false); + C_D = (state & 0x02 ? true : false); + I_O = (state & 0x01 ? true : false); + cmdLength = 0; +} + + static void RunDevice(void) { -//WriteLog(" >>> RUNNING HD...\n"); // Let's see where it's really going... /* if (mainCPU.pc == 0xCE7E) dumpDis = true;//*/ + // These are SCSI messages sent in response to certain commands static uint8_t readCapacity[8] = { 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00 }; - 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' }; + 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', ' ' }; 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 }; - enum { - DVM_DATA_OUT = 0, DVM_DATA_IN = 1, DVM_COMMAND = 2, DVM_STATUS = 3, - DVM_MESSAGE_OUT = 6, DVM_MESSAGE_IN = 7, DVM_BUS_FREE, DVM_ARBITRATE, - DVM_SELECT - }; - if (RST) { -//WriteLog(" >>> DEVICE RESET...\n"); devMode = DVM_BUS_FREE; DEV_BSY = false; return; @@ -369,40 +94,32 @@ static void RunDevice(void) switch (devMode) { case DVM_BUS_FREE: - if (SEL)//(BSY && SEL) - devMode = DVM_ARBITRATE; - - break; + // We never initiate, so this we don't worry about whether or not the + // bus is free. case DVM_ARBITRATE: -////WriteLog(" >>> ARBITRATE PHASE (BSY=%i SEL=%i DATA_BUS=%i [%02X])\n", BSY, SEL, DATA_BUS, reg[0]); - if (!BSY && SEL && DATA_BUS && (reg[0] & 0x40)) - devMode = DVM_SELECT, DEV_BSY = true; - else if (!BSY && !SEL) - devMode = DVM_BUS_FREE; - + // Likewise, we don't arbitrate either. break; + case DVM_SELECT: -//WriteLog(" >>> SELECT PHASE\n"); - // Preset response code to "Good" - response = 0x00; + // If we're in Selection phase, see if our ID is on the bus, and, if so, + // go on to the next phase (since the Target drives the phase dance). + if ((reg[0] & 0x40) && DATA_BUS) + { + DEV_BSY = true; - if (ATN) - { - MSG = true, C_D = true, I_O = false; - devMode = DVM_MESSAGE_OUT; - REQ = true; - } - else - { - // If no ATN is asserted, go to COMMAND I guess? - // Let's try it -// errrr, no. this does not work. Or does it??? - MSG = false, C_D = true, I_O = false; - devMode = DVM_COMMAND; - cmdLength = 0; + // Preset response code to "Good" + response = 0x00; + + if (ATN) + SetNextState(DVM_MESSAGE_OUT); + else + // If no ATN is asserted, go to COMMAND? Dunno, the firmware + // doesn't ever go there; it *always* starts with MESSAGE OUT. + SetNextState(DVM_COMMAND); } break; + case DVM_DATA_OUT: //WriteLog(" >>> DATA OUT PHASE (bts=%u)\n", bytesToSend); if (!ACK) @@ -419,22 +136,21 @@ static void RunDevice(void) if (buf) buf[bufPtr] = reg[0]; - DRQ = false; - DACK = false; + DRQ = DACK = false; bytesToSend--; bufPtr++; if (bytesToSend == 0) { REQ = false; - MSG = false, C_D = true, I_O = true; - devMode = DVM_STATUS; + SetNextState(DVM_STATUS); buf = NULL; } } } break; + case DVM_DATA_IN: //WriteLog(" >>> DATA IN PHASE (bts=%u)\n", bytesToSend); if (!ACK) @@ -444,214 +160,198 @@ static void RunDevice(void) { if (!DACK) { - // We just send zeroes for now... - if (buf == NULL) - reg[6] = 0; - else - reg[6] = buf[bufPtr]; - + // If there's no buffer set up, send zeroes... + reg[6] = (buf == NULL ? 0 : buf[bufPtr]); DRQ = true; } else if (DRQ && DACK) { - DRQ = false; - DACK = false; + DRQ = DACK = false; bytesToSend--; bufPtr++; if (bytesToSend == 0) { REQ = false; - MSG = false, C_D = true, I_O = true; - devMode = DVM_STATUS; + SetNextState(DVM_STATUS); buf = NULL; } } } break; + case DVM_COMMAND: -//WriteLog(" >>> COMMAND PHASE\n"); + { if (!ACK) REQ = true; else if (REQ && ACK) { cmd[cmdLength++] = reg[0]; -// WriteLog("HD: Write to target value $%02X\n", reg[0]); REQ = false; } - // Handle "Test Unit Ready" command - if ((cmd[0] == 0) && (cmdLength == 6)) - { - WriteLog("HD: Received command TEST UNIT READY\n"); - REQ = false; - // Drive next phase - MSG = false, C_D = true, I_O = true; - devMode = DVM_STATUS; - } - // Handle "Request Sense" command - else if ((cmd[0] == 0x03) && (cmdLength == 6)) - { - 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]); - REQ = false; - // Drive next phase - MSG = false, C_D = false, I_O = true; - devMode = DVM_DATA_IN; - bytesToSend = cmd[4]; + uint8_t cmdType = (cmd[0] & 0xE0) >> 5; - // Return error for LUNs other than 0 - if ((cmd[1] & 0xE0) != 0) + if ((cmdType == 0) && (cmdLength == 6)) + { + // Handle "Test Unit Ready" command + if (cmd[0] == 0) { - buf = badSense; + WriteLog("HD: Received command TEST UNIT READY\n"); + SetNextState(DVM_STATUS); + } + // Handle "Request Sense" command + else if (cmd[0] == 0x03) + { + 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]); + SetNextState(DVM_DATA_IN); + bytesToSend = cmd[4]; + + // Return error for LUNs other than 0 + if ((cmd[1] & 0xE0) != 0) + { + buf = badSense; + bufPtr = 0; + } + } + // Handle "Read" (6) command + else if (cmd[0] == 0x08) + { + 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]); + SetNextState(DVM_DATA_IN); + bytesToSend = cmd[4] * 512; // amount is set in blocks + uint32_t lba = ((cmd[1] & 0x1F) << 16) | (cmd[2] << 8) | cmd[3]; + buf = (hdData != NULL ? &hdData[(lba * 512) + 0x40] : NULL); bufPtr = 0; } - } - // Handle "Read" (6) command - else if ((cmd[0] == 0x08) && (cmdLength == 6)) - { - 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]); - REQ = false; - // Drive next phase - MSG = false, C_D = false, I_O = true; - devMode = DVM_DATA_IN; - bytesToSend = cmd[4] * 512; // amount is set in blocks - } - // Handle "Inquire" command - else if ((cmd[0] == 0x12) && (cmdLength == 6)) - { - WriteLog("HD: Received command INQUIRE [%02X %02X %02X %02X %02X %02X]\n", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5]); - REQ = false; - // Drive next phase - MSG = false, C_D = false, I_O = true; - devMode = DVM_DATA_IN; - bytesToSend = cmd[4]; - buf = inquireData; - bufPtr = 0; - - // Reject all but LUN 0 - if ((cmd[1] & 0xE0) != 0) + // Handle "Inquire" command + else if (cmd[0] == 0x12) { + WriteLog("HD: Received command INQUIRE [%02X %02X %02X %02X %02X %02X]\n", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5]); + SetNextState(DVM_DATA_IN); + bytesToSend = cmd[4]; + buf = inquireData; + bufPtr = 0; + + // Reject all but LUN 0 + if ((cmd[1] & 0xE0) != 0) + response = 0x02; // "Check Condition" code + } + // Handle "Mode Select" command + else if (cmd[0] == 0x15) + { + 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]); + SetNextState(DVM_DATA_OUT); + bytesToSend = cmd[4]; + } + // Handle "Mode Sense" command + else if (cmd[0] == 0x1A) + { + 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]); + SetNextState(DVM_DATA_IN); + bytesToSend = cmd[4]; + } + else + { + 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]); + // Return failure... + SetNextState(DVM_STATUS); response = 0x02; // Check condition code -// MSG = false, C_D = false, I_O = false; -// DEV_BSY = false; -// devMode = DVM_BUS_FREE; } } - // Handle "Mode Select" command - else if ((cmd[0] == 0x15) && (cmdLength == 6)) + else if (((cmdType == 1) || (cmdType == 2)) && (cmdLength == 10)) { - 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]); - REQ = false; - // Drive next phase - MSG = false, C_D = false, I_O = false; - devMode = DVM_DATA_OUT; - bytesToSend = cmd[4]; + // Handle "Read Capacity" command + if (cmd[0] == 0x25) + { + 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]); + SetNextState(DVM_DATA_IN); + bytesToSend = 8;//it's always 8...//cmd[4]; + // N.B.: We need to hook this up to the actual emulated HD size... + buf = readCapacity; + bufPtr = 0; + } + // Handle "Read" (10) command + else if (cmd[0] == 0x28) + { + 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]); + // Drive next phase + SetNextState(DVM_DATA_IN); + bytesToSend = ((cmd[7] << 8) | cmd[8]) * 512; // amount is set in blocks + uint32_t lba = (cmd[2] << 24) | (cmd[3] << 16) | (cmd[4] << 8) | cmd[5]; + buf = (hdData != NULL ? &hdData[(lba * 512) + 0x40] : NULL); + bufPtr = 0; + } + // Handle "Write" (10) command + else if (cmd[0] == 0x2A) + { + 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]); + // Drive next phase + SetNextState(DVM_DATA_OUT); + bytesToSend = ((cmd[7] << 8) | cmd[8]) * 512; // amount is set in blocks + uint32_t lba = (cmd[2] << 24) | (cmd[3] << 16) | (cmd[4] << 8) | cmd[5]; + buf = (hdData != NULL ? &hdData[(lba * 512) + 0x40] : NULL); + bufPtr = 0; + } + else + { + 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]); + // Return failure... + SetNextState(DVM_STATUS); + response = 0x02; // "Check Condition" code + } } - // Handle "Mode Sense" command - else if ((cmd[0] == 0x1A) && (cmdLength == 6)) + else if ((cmdType == 5) && (cmdLength == 12)) { - 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]); - REQ = false; - // Drive next phase - MSG = false, C_D = false, I_O = true; - devMode = DVM_DATA_IN; - bytesToSend = cmd[4]; - } - // Handle "Read Capacity" command - else if ((cmd[0] == 0x25) && (cmdLength == 10)) - { - 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]); - REQ = false; - // Drive next phase - MSG = false, C_D = false, I_O = true; - devMode = DVM_DATA_IN; - bytesToSend = 8;//cmd[4]; - buf = readCapacity; - bufPtr = 0; - } - // Handle "Read" (10) command - else if ((cmd[0] == 0x28) && (cmdLength == 10)) - { - 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]); - REQ = false; - // Drive next phase - MSG = false, C_D = false, I_O = true; - devMode = DVM_DATA_IN; - bytesToSend = ((cmd[7] << 8) | cmd[8]) * 512; // amount is set in blocks - uint32_t lba = (cmd[2] << 24) | (cmd[3] << 16) | (cmd[4] << 8) | cmd[5]; - buf = (hdData != NULL ? &hdData[(lba * 512) + 0x40] : NULL); - bufPtr = 0; - } - // Handle "Write" (10) command - else if ((cmd[0] == 0x2A) && (cmdLength == 10)) - { - 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]); - REQ = false; - // Drive next phase - MSG = false, C_D = false, I_O = false; - devMode = DVM_DATA_OUT; - bytesToSend = ((cmd[7] << 8) | cmd[8]) * 512; // amount is set in blocks - uint32_t lba = (cmd[2] << 24) | (cmd[3] << 16) | (cmd[4] << 8) | cmd[5]; - buf = (hdData != NULL ? &hdData[(lba * 512) + 0x40] : NULL); - bufPtr = 0; - } - else if ((cmdLength == 6) && ((cmd[0] & 0xE0) == 0)) - { - 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]); - } - else if ((cmdLength == 10) && (((cmd[0] & 0xE0) == 0x20) || ((cmd[0] & 0xE0) == 0x40))) - { - 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]); + 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]); + // Return failure... + SetNextState(DVM_STATUS); + response = 0x02; // "Check Condition" code } break; + } + case DVM_STATUS: -//WriteLog(" >>> STATUS PHASE\n"); if (!ACK) { // Return A-OK for everything for now... - reg[0] = 0; + reg[0] = 0; // N.B.: This is necessary for some reason... REQ = true; } else if (REQ && ACK) { REQ = false; - // Drive next phase - MSG = true, C_D = true, I_O = true; - devMode = DVM_MESSAGE_IN; + SetNextState(DVM_MESSAGE_IN); } break; + case DVM_MESSAGE_OUT: -//WriteLog(" >>> MESSAGE OUT PHASE\n"); + if (!ACK) + REQ = true; if (REQ && ACK) { - uint8_t msg = reg[0]; -// WriteLog("HD: Write to target value $%02X\n", msg); +// WriteLog("HD: Write to target value $%02X\n", reg[0]); REQ = false; - // Drive next phase - MSG = false, C_D = true, I_O = false; - devMode = DVM_COMMAND; - cmdLength = 0; + SetNextState(DVM_COMMAND); } break; + case DVM_MESSAGE_IN: -//WriteLog(" >>> MESSAGE IN PHASE\n"); if (!ACK) { - // Return A-OK for everything for now... + // Return appropriate response reg[0] = response; REQ = true; } else if (REQ && ACK) { REQ = false; - // Drive next phase - MSG = false, C_D = false, I_O = false; DEV_BSY = false; - devMode = DVM_BUS_FREE; + SetNextState(DVM_BUS_FREE); } break; @@ -664,25 +364,6 @@ static uint8_t SlotIOR(uint16_t address) // This should prolly go somewhere else... RunDevice(); - char SCSIName[16][256] = { - "(RO) Current SCSI Data", - "Initiator Command", - "Mode", - "Target Command", - "(RO) Current SCSI Bus Status", - "(RO) Bus and Status", - "(RO) Input Data", - "(RO) Reset Parity/Interrupt", - "DMA Address LO", - "DMA Address HI", - "DMA Count LO", - "DMA Count HI", - "$C", - "$D", - "Bank/SCSI ID", - "$F" - }; - uint8_t response = reg[address & 0x0F]; switch (address & 0x0F) @@ -743,24 +424,16 @@ static uint8_t SlotIOR(uint16_t address) break; } -/* if (((mainCPU.pc != 0xCD7C) && (mainCPU.pc != 0xCD5F)) || (romBank != 16)) - WriteLog("HD Slot I/O read %s ($%02X <- $%X, PC=%04X:%u)\n", SCSIName[address & 0x0F], response, address & 0x0F, mainCPU.pc, romBank);//*/ - - return response; -} - - -static void SlotIOW(uint16_t address, uint8_t byte) -{ +#if 0 char SCSIName[16][256] = { - "(WO) Output Data", + "(RO) Current SCSI Data", "Initiator Command", "Mode", "Target Command", - "(WO) Select Enable", - "(WO) Start DMA Send", - "(WO) Start DMA Target Receive", - "(WO) Start DMA Initiator Receive", + "(RO) Current SCSI Bus Status", + "(RO) Bus and Status", + "(RO) Input Data", + "(RO) Reset Parity/Interrupt", "DMA Address LO", "DMA Address HI", "DMA Count LO", @@ -771,6 +444,16 @@ static void SlotIOW(uint16_t address, uint8_t byte) "$F" }; + if (((mainCPU.pc != 0xCD7C) && (mainCPU.pc != 0xCD5F)) || (romBank != 16)) + WriteLog("HD Slot I/O read %s ($%02X <- $%X, PC=%04X:%u)\n", SCSIName[address & 0x0F], response, address & 0x0F, mainCPU.pc, romBank); +#endif + + return response; +} + + +static void SlotIOW(uint16_t address, uint8_t byte) +{ switch (address & 0x0F) { case 0x00: @@ -788,9 +471,18 @@ static void SlotIOW(uint16_t address, uint8_t byte) BSY = (byte & 0x08 ? true : false); ACK = (byte & 0x10 ? true : false); RST = (byte & 0x80 ? true : false); + + if (!(SEL || BSY || DEV_BSY)) + devMode = DVM_BUS_FREE; + + if (SEL && (devMode == DVM_ARBITRATE)) + devMode = DVM_SELECT; + break; case 0x02: // Mode register (chip control) + if ((byte & 0x01) && (devMode == DVM_BUS_FREE)) + devMode = DVM_ARBITRATE; // Dma ReQuest is reset here (as well as by hitting a pin) DMA_MODE = (byte & 0x02 ? true : false); @@ -845,10 +537,33 @@ static void SlotIOW(uint16_t address, uint8_t byte) break; } -/* WriteLog("HD Slot I/O write %s ($%02X -> $%X, PC=%04X:%u)\n", SCSIName[address & 0x0F], byte, address & 0x0F, mainCPU.pc, romBank);//*/ reg[address & 0x0F] = byte; -/* if ((address & 0x0F) == 0x0E) +#if 0 + char SCSIName[16][256] = { + "(WO) Output Data", + "Initiator Command", + "Mode", + "Target Command", + "(WO) Select Enable", + "(WO) Start DMA Send", + "(WO) Start DMA Target Receive", + "(WO) Start DMA Initiator Receive", + "DMA Address LO", + "DMA Address HI", + "DMA Count LO", + "DMA Count HI", + "$C", + "$D", + "Bank/SCSI ID", + "$F" + }; + char SCSIPhase[11][256] = { "DATA OUT", "DATA IN", "COMMAND", "STATUS", "ERR4", "ERR5", "MESSAGE OUT", "MESSAGE IN", "BUS FREE", "ARBITRATE", "SELECT" }; + + + 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]); + + if ((address & 0x0F) == 0x0E) { if (mainCPU.pc == 0xC78B) { @@ -862,7 +577,8 @@ static void SlotIOW(uint16_t address, uint8_t byte) } 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]); - }//*/ + } +#endif // This should prolly go somewhere else... RunDevice();