// license:BSD-3-Clause // copyright-holders:Brett Wyer, Raphael Nabet /***************************************************************************** * * includes/corvushd.h * * Implementation of the Corvus Systems Flat Cable Hard Disk controller * * Corvus Model 6 (5 MB): IMI 5006H hard disk (-chs 144,4,20 -ss 512) * Corvus Model 11 (10 MB): IMI 5012H hard disk (-chs 358,3,20 -ss 512) * Corvus Model 20 (20 MB): IMI 5018H hard disk (-chs 388,5,20 -ss 512) * ****************************************************************************/ #ifndef CORVUSHD_H_ #define CORVUSHD_H_ #include "emu.h" #include "imagedev/harddriv.h" #include // // Controller Commands // // Read/Write Commands #define READ_SECTOR_256 0x02 #define WRITE_SECTOR_256 0x03 #define READ_CHUNK_128 0x12 #define READ_CHUNK_256 0x22 #define READ_CHUNK_512 0x32 #define WRITE_CHUNK_128 0x13 #define WRITE_CHUNK_256 0x23 #define WRITE_CHUNK_512 0x33 // Semaphore Commands #define SEMAPHORE_LOCK_CODE 0x0b #define SEMAPHORE_LOCK_MOD 0x01 #define SEMAPHORE_UNLOCK_CODE 0x0b #define SEMAPHORE_UNLOCK_MOD 0x11 #define SEMAPHORE_INIT_CODE 0x1a #define SEMAPHORE_INIT_MOD 0x10 #define SEMAPHORE_STATUS_CODE 0x1a #define SEMAPHORE_STATUS_MOD 0x41 // Pipe Commands #define PIPE_READ_CODE 0x1a #define PIPE_READ_MOD 0x20 #define PIPE_WRITE_CODE 0x1a #define PIPE_WRITE_MOD 0x21 #define PIPE_CLOSE_CODE 0x1a #define PIPE_CLOSE_MOD 0x40 #define PIPE_STATUS_CODE 0x1a #define PIPE_STATUS_MOD 0x41 #define PIPE_OPEN_WRITE_CODE 0x1b #define PIPE_OPEN_WRITE_MOD 0x80 #define PIPE_AREA_INIT_CODE 0x1b #define PIPE_AREA_INIT_MOD 0xa0 #define PIPE_OPEN_READ_CODE 0x1b #define PIPE_OPEN_READ_MOD 0xc0 // Active User Table Commands #define ADDACTIVE_CODE 0x34 #define ADDACTIVE_MOD 0x03 #define DELACTIVEUSR_REVBH_CODE 0x34 #define DELACTIVEUSR_REVBH_MOD 0x00 #define DELACTIVENUM_OMNI_CODE 0x34 #define DELACTIVENUM_OMNI_MOD 0x00 #define DELACTIVEUSR_OMNI_CODE 0x34 #define DELACTIVEUSR_OMNI_MOD 0x31 #define FINDACTIVE_CODE 0x34 #define FINDACTIVE_MOD 0x05 #define READTEMPBLOCK 0xc4 #define WRITETEMPBLOCK 0xb4 // Miscellaneous Commands #define BOOT 0x14 #define READ_BOOT_BLOCK 0x44 #define GET_DRIVE_PARAMETERS 0x10 #define PARK_HEADS_REVH 0x11 #define PARK_HEADS_OMNI 0x80 #define ECHO 0xf4 // Put drive in Prep Mode #define PREP_MODE_SELECT 0x11 // Prep Mode Commands #define PREP_RESET_DRIVE 0x00 #define PREP_FORMAT_DRIVE 0x01 #define PREP_FILL_DRIVE_OMNI 0x81 #define PREP_VERIFY 0x07 #define PREP_READ_FIRMWARE 0x32 #define PREP_WRITE_FIRMWARE 0x33 // // Controller Status Codes // // Disk status codes #define STAT_SUCCESS 0x00 #define STAT_HEADER_FAULT 0x00 #define STAT_SEEK_TIMEOUT 0x01 #define STAT_SEEK_FAULT 0x02 #define STAT_SEEK_ERROR 0x03 #define STAT_HEADER_CRC_ERROR 0x04 #define STAT_REZERO_FAULT 0x05 #define STAT_REZERO_TIMEOUT 0x06 #define STAT_DRIVE_NOT_ONLINE 0x07 #define STAT_WRITE_FAULT 0x08 #define STAT_NOT_USED 0x09 #define STAT_READ_DATA_FAULT 0x0a #define STAT_DATA_CRC_ERROR 0x0b #define STAT_SECTOR_LOCATE_ERR 0x0c #define STAT_WRITE_PROTECTED 0x0d #define STAT_ILL_SECTOR_ADDRESS 0x0e #define STAT_ILL_CMD_OP_CODE 0x0f #define STAT_DRIVE_NOT_ACK 0x10 #define STAT_ACK_STUCK_ACTIVE 0x11 #define STAT_TIMEOUT 0x12 #define STAT_FAULT 0x13 #define STAT_CRC 0x14 #define STAT_SEEK 0x15 #define STAT_VERIFICATION 0x16 #define STAT_SPEED_ERROR 0x17 #define STAT_ILL_ADDRESS 0x18 #define STAT_RW_FAULT_ERROR 0x19 #define STAT_SERVO_ERROR 0x1a #define STAT_GUARD_BAND 0x1b #define STAT_PLO_ERROR 0x1c #define STAT_RW_UNSAFE 0x1d // Disk status modifiers (added to status code) #define STAT_RECOVERABLE_ERR 0x20 #define STAT_VERIFY_ERR 0x40 #define STAT_FATAL_ERR 0x80 // Semaphore status codes #define SEM_PRIOR_STATE_NOT_SET 0x00 #define SEM_PRIOR_STATE_SET 0x80 #define SEM_TABLE_FULL 0xfd #define SEM_DISK_ERROR 0xfe // Pipe Status codes #define PIPE_REQ_SUCCESSFUL 0x00 #define PIPE_EMPTY_PIPE_READ 0x08 #define PIPE_NOT_OPEN 0x09 #define PIPE_WRITE_TO_FULL_PIPE 0x0a #define PIPE_OPEN_OPEN_PIPE 0x0b #define PIPE_PIPE_NOT_EXIST 0x0c #define PIPE_NO_ROOM_FOR_NEW 0x0d #define PIPE_ILLEGAL_COMMAND 0x0e #define PIPE_AREA_NOT_INIT 0x0f // Pipe State codes #define PIPE_OPEN_WRITE_EMPTY 0x01 #define PIPE_OPEN_READ_EMPTY 0x02 #define PIPE_NOT_OPEN_FULL 0x80 #define PIPE_OPEN_WRITE_FULL 0x81 #define PIPE_OPEN_READ_FULL 0x82 // Status Register Bits #define CONTROLLER_BUSY 0x80 // Set = Busy, Clear = Ready #define CONTROLLER_DIRECTION 0x40 // Set = Controller->Host, Clear = Host->Controller #define MAX_COMMAND_SIZE 4096 // The maximum size of a command packet (the controller only has 5K of RAM...) class corvus_hdc_t : public device_t { public: // construction/destruction corvus_hdc_t(const machine_config &mconfig, std::string tag, device_t *owner, UINT32 clock); DECLARE_READ8_MEMBER( read ); DECLARE_WRITE8_MEMBER( write ); DECLARE_READ8_MEMBER( status_r ); protected: // device-level overrides virtual void device_start() override; virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; private: enum { TIMER_TIMEOUT, TIMER_COMMAND }; // Sector addressing scheme for Rev B/H drives used in various commands (Called a DADR in the docs) struct dadr_t { UINT8 address_msn_and_drive;// Most significant nibble: Most signficant nibble of sector address, Least significant nibble: Drive # UINT8 address_lsb; // Least significant byte of sector address UINT8 address_mid; // Middle byte of sector address }; UINT8 m_status; // Controller status byte (DIRECTION + BUSY/READY) // Prep mode bool m_prep_mode; // Whether the controller is in Prep Mode or not UINT8 m_prep_drv; // If in prep mode, Corvus drive id (1..15) being prepped // Physical drive info UINT8 m_sectors_per_track; // Number of sectors per track for this drive UINT8 m_tracks_per_cylinder;// Number of tracks per cylinder (heads) UINT16 m_cylinders_per_drive;// Number of cylinders per drive // Command Processing UINT16 m_offset; // Current offset into raw_data buffer bool m_awaiting_modifier; // We've received a two-byte command and we're waiting for the mod UINT16 m_recv_bytes; // Number of bytes expected to be received from Host UINT16 m_xmit_bytes; // Number of bytes expected to be transmitted to host // Timing-related values UINT16 m_last_cylinder; // Last cylinder accessed - for calculating seek times UINT32 m_delay; // Delay in microseconds for callback emu_timer *m_timeout_timer; // Four-second timer for timeouts emu_timer *m_cmd_timer; bool m_invalid_command_flag; // I hate this, but it saves a lot more tests // // Union below represents both an input and output buffer and interpretations of it // union { // // Raw Buffer // UINT8 raw_data[MAX_COMMAND_SIZE]; // // Basic interpretation of code and modifier // struct { UINT8 code; // First byte of data is the code (command) UINT8 modifier; // Second byte of data is the modifier } command; // // Basic response code // struct { UINT8 status; // Status code returned by the command executed } single_byte_response; // // Read sector command // struct { UINT8 code; // Command code dadr_t dadr; // Encoded drive and sector to read } read_sector_command; // // 128-byte Read Sector response // struct { UINT8 status; // Status code returned by command executed UINT8 data[128]; // Data returned from read } read_128_response; // // 256-byte Read Sector response // struct { UINT8 status; // Status code returned by command executed UINT8 data[256]; // Data returned from read } read_256_response; // // 512-byte Read Sector response // struct { UINT8 status; // Status code returned by command executed UINT8 data[512]; // Data returned by read } read_512_response; // // Write 128-byte sector command // struct { UINT8 code; // Command code dadr_t dadr; // Encoded drive and sector to write UINT8 data[128]; // Data to be written } write_128_command; // // Write 256-byte sector command // struct { UINT8 code; // Command code dadr_t dadr; // Encoded drive and sector to write UINT8 data[256]; // Data to be written } write_256_command; // // Write 512-byte sector command // struct { UINT8 code; // Command Code dadr_t dadr; // Encoded drive and sector to write UINT8 data[512]; // Data to be written } write_512_command; // // Semaphore Lock command // struct { UINT8 code; // Command code UINT8 modifier; // Command code modifier UINT8 name[8]; // Semaphore name } lock_semaphore_command; // // Semaphore Unlock command // struct { UINT8 code; // Command code UINT8 modifier; // Command code modifier UINT8 name[8]; // Semaphore name } unlock_semaphore_command; // // Semaphore Lock/Unlock response // struct { UINT8 status; // Disk access status UINT8 result; // Semaphore action status UINT8 unused[10]; // Unused } semaphore_locking_response; // // Initialize Semaphore table command // struct { UINT8 code; // Command code UINT8 modifier; // Command code modifier UINT8 unused[3]; // Unused } init_semaphore_command; // // Semaphore Status command // struct { UINT8 code; // Command code UINT8 modifier; // Command code modifier UINT8 zero_three; // Don't ask me... UINT8 unused[2]; // Unused } semaphore_status_command; // // Semaphore Status response // struct { UINT8 status; // Disk access status UINT8 table[256]; // Contents of the semaphore table } semaphore_status_response; // // Get Drive Parameters command (0x10) // struct { UINT8 code; // Command code UINT8 drive; // Drive number (starts at 1) } get_drive_parameters_command; // // Get Drive Parameters command response // struct { UINT8 status; // Status code returned by command executed UINT8 firmware_desc[31]; // Firmware string description UINT8 firmware_rev; // Firmware revision number UINT8 rom_version; // ROM Version struct { UINT8 sectors_per_track; // Sectors/Track UINT8 tracks_per_cylinder; // Tracks/Cylinder (heads) struct { UINT8 lsb; UINT8 msb; } cylinders_per_drive; // Byte-flipped Cylinders/Drive } track_info; struct { UINT8 lsb; // Least significant byte UINT8 midb; // Middle byte UINT8 msb; // Most significant byte } capacity; // 24-bit value, byte-flipped (lsb..msb) UINT8 unused[16]; UINT8 interleave; // Interleave factor struct { UINT8 mux_parameters[12]; UINT8 pipe_name_table_ptr[2]; // Pointer to table of 64 entries, 8 bytes each (table of names) UINT8 pipe_ptr_table_ptr[2]; // Pointer to table of 64 entries, 8 bytes each. See pp. 29 - Mass Storage GTI UINT8 pipe_area_size[2]; // Size of pipe area (lsb, msb) struct { UINT8 track_offset[2]; } vdo_table[7]; // Virtual drive table UINT8 lsi11_vdo_table[8]; UINT8 lsi11_spare_table[8]; } table_info; UINT8 drive_number; // Physical drive number struct { UINT8 lsb; // Least UINT8 midb; // Middle UINT8 msb; // Most } physical_capacity; // Physical capacity of drive } drive_param_response; // // 2-byte Boot command (0x14) // struct { UINT8 code; // Command code UINT8 boot_block; // Which boot block to read (0-7) } old_boot_command; // // Put drive into prep mode command (0x11) // struct { UINT8 code; // Command code UINT8 drive; // Drive number (starts at 1) UINT8 prep_block[512]; // Machine code payload } prep_mode_command; // // Read Firmware command (Prep Mode 0x32) // struct { UINT8 code; // Command Code UINT8 encoded_h_s;// Encoded Head (bits 7-5) / Sector (bits 4-0) } read_firmware_command; // // Write Firmware command (Prep Mode 0x33) // struct { UINT8 code; // Command Code UINT8 encoded_h_s; // Encoded Head (bits 7-5) / Sector (bits 4-0) UINT8 data[512]; // Data to be written } write_firmware_command; // // Format Drive command (Prep Mode 0x01) // // Note that the following is a BLATANT ASSUMPTION. Technically, the Format Drive command // uses a variable-length buffer for the pattern. Unfortunately, the docs don't explain how to determine the // length of the buffer passed. I assume it's a timeout; however, the docs happen to say that // all Corvus diagnostic programs send 513 bytes total, including the command, so I'm going with that. // struct { UINT8 code; // Command Code UINT8 pattern[512]; // Pattern to be written } format_drive_revbh_command; // // Verify Drive command (Prep Mode 0x07) // // On the real Corvus controller, this is a variable length response. If the // number of bad sectors is greater than zero, an additional four bytes will // follow for each bad sector. We don't emulate bad sectors, so we always // return a count of 0. That makes this a fixed length response of 2 bytes. // struct { UINT8 status; // Disk access status UINT8 bad_sectors; // Number of bad sectors (always zero) } verify_drive_response; } m_buffer; // Structure of Block #1, the Disk Parameter Block struct disk_parameter_block_t { struct { UINT8 lsb; UINT8 msb; } spared_track[8]; // Spared track table (0xffff indicates end) UINT8 interleave; // Interleave factor UINT8 reserved; struct { UINT8 track_offset[2]; // Virtual drive offsets (lsb, msb) 0xffff indicates unused } vdo_table[7]; UINT8 lsi11_vdo_table[8]; UINT8 lsi11_spare_table[8]; UINT8 reserved2[432]; struct { UINT8 lsb; UINT8 msb; } revh_spare_table[16]; }; // Structure of Block #3, the Constellation Parameter Block struct constellation_parameter_block_t { UINT8 mux_parameters[12]; UINT8 pipe_name_table_ptr[2]; UINT8 pipe_ptr_table_ptr[2]; UINT8 pipe_area_size[2]; UINT8 reserved[470]; UINT8 software_protection[12]; UINT8 serial_number[12]; }; // Structure of Block #7, the Semaphore Table Block struct semaphore_table_block_t { union { UINT8 semaphore_table[256]; // Table consists of 256 bytes struct { UINT8 semaphore_name[8]; // Each semaphore name is 8 bytes } semaphore_entry[32]; // 32 Entries } semaphore_block; UINT8 unused[256]; // Remaining half of block is unused }; // Command size structure (number of bytes to xmit and recv for each command) struct corvus_cmd_t { UINT16 recv_bytes; // Number of bytes from host for this command UINT16 xmit_bytes; // Number of bytes to return to host }; void dump_buffer(UINT8 *buffer, UINT16 length); bool parse_hdc_command(UINT8 data); UINT8 corvus_write_sector(UINT8 drv, UINT32 sector, UINT8 *buffer, int len); UINT8 corvus_write_logical_sector(dadr_t *dadr, UINT8 *buffer, int len); UINT8 corvus_read_sector(UINT8 drv, UINT32 sector, UINT8 *buffer, int len); UINT8 corvus_read_logical_sector(dadr_t *dadr, UINT8 *buffer, int len); UINT8 corvus_lock_semaphore(UINT8 *name); UINT8 corvus_unlock_semaphore(UINT8 *name); UINT8 corvus_init_semaphore_table(); UINT8 corvus_get_drive_parameters(UINT8 drv); UINT8 corvus_read_boot_block(UINT8 block); UINT8 corvus_enter_prep_mode(UINT8 drv, UINT8 *prep_block); UINT8 corvus_exit_prep_mode(); UINT8 corvus_read_firmware_block(UINT8 head, UINT8 sector); UINT8 corvus_write_firmware_block(UINT8 head, UINT8 sector, UINT8 *buffer); UINT8 corvus_format_drive(UINT8 *pattern, UINT16 len); hard_disk_file *corvus_hdc_file(int id); void corvus_process_command_packet(bool local_invalid_command_flag); corvus_cmd_t corvus_cmd[0xf5][0xc1]; // Command sizes and their return sizes corvus_cmd_t corvus_prep_cmd[0x82]; // Prep Command sizes and their return sizes }; // device type definition extern const device_type CORVUS_HDC; #endif /* CORVUSHD_H_ */