// 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 MAME_MACHINE_CORVUSHD_H #define MAME_MACHINE_CORVUSHD_H #pragma once #include "imagedev/harddriv.h" #include class corvus_hdc_device : public device_t { public: // Status Register Bits static constexpr uint8_t CONTROLLER_BUSY = 0x80; // Set = Busy, Clear = Ready static constexpr uint8_t CONTROLLER_DIRECTION = 0x40; // Set = Controller->Host, Clear = Host->Controller // construction/destruction corvus_hdc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t 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: static constexpr unsigned MAX_COMMAND_SIZE = 4096; // The maximum size of a command packet (the controller only has 5K of RAM...) 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_t address_msn_and_drive;// Most significant nibble: Most significant nibble of sector address, Least significant nibble: Drive # uint8_t address_lsb; // Least significant byte of sector address uint8_t address_mid; // Middle byte of sector address }; uint8_t m_status; // Controller status byte (DIRECTION + BUSY/READY) // Prep mode bool m_prep_mode; // Whether the controller is in Prep Mode or not uint8_t m_prep_drv; // If in prep mode, Corvus drive id (1..15) being prepped // Physical drive info uint8_t m_sectors_per_track; // Number of sectors per track for this drive uint8_t m_tracks_per_cylinder;// Number of tracks per cylinder (heads) uint16_t m_cylinders_per_drive;// Number of cylinders per drive // Command Processing uint16_t 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_t m_recv_bytes; // Number of bytes expected to be received from Host uint16_t m_xmit_bytes; // Number of bytes expected to be transmitted to host // Timing-related values uint16_t m_last_cylinder; // Last cylinder accessed - for calculating seek times uint32_t 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_t raw_data[MAX_COMMAND_SIZE]; // // Basic interpretation of code and modifier // struct { uint8_t code; // First byte of data is the code (command) uint8_t modifier; // Second byte of data is the modifier } command; // // Basic response code // struct { uint8_t status; // Status code returned by the command executed } single_byte_response; // // Read sector command // struct { uint8_t code; // Command code dadr_t dadr; // Encoded drive and sector to read } read_sector_command; // // 128-byte Read Sector response // struct { uint8_t status; // Status code returned by command executed uint8_t data[128]; // Data returned from read } read_128_response; // // 256-byte Read Sector response // struct { uint8_t status; // Status code returned by command executed uint8_t data[256]; // Data returned from read } read_256_response; // // 512-byte Read Sector response // struct { uint8_t status; // Status code returned by command executed uint8_t data[512]; // Data returned by read } read_512_response; // // Write 128-byte sector command // struct { uint8_t code; // Command code dadr_t dadr; // Encoded drive and sector to write uint8_t data[128]; // Data to be written } write_128_command; // // Write 256-byte sector command // struct { uint8_t code; // Command code dadr_t dadr; // Encoded drive and sector to write uint8_t data[256]; // Data to be written } write_256_command; // // Write 512-byte sector command // struct { uint8_t code; // Command Code dadr_t dadr; // Encoded drive and sector to write uint8_t data[512]; // Data to be written } write_512_command; // // Semaphore Lock command // struct { uint8_t code; // Command code uint8_t modifier; // Command code modifier uint8_t name[8]; // Semaphore name } lock_semaphore_command; // // Semaphore Unlock command // struct { uint8_t code; // Command code uint8_t modifier; // Command code modifier uint8_t name[8]; // Semaphore name } unlock_semaphore_command; // // Semaphore Lock/Unlock response // struct { uint8_t status; // Disk access status uint8_t result; // Semaphore action status uint8_t unused[10]; // Unused } semaphore_locking_response; // // Initialize Semaphore table command // struct { uint8_t code; // Command code uint8_t modifier; // Command code modifier uint8_t unused[3]; // Unused } init_semaphore_command; // // Semaphore Status command // struct { uint8_t code; // Command code uint8_t modifier; // Command code modifier uint8_t zero_three; // Don't ask me... uint8_t unused[2]; // Unused } semaphore_status_command; // // Semaphore Status response // struct { uint8_t status; // Disk access status uint8_t table[256]; // Contents of the semaphore table } semaphore_status_response; // // Get Drive Parameters command (0x10) // struct { uint8_t code; // Command code uint8_t drive; // Drive number (starts at 1) } get_drive_parameters_command; // // Get Drive Parameters command response // struct { uint8_t status; // Status code returned by command executed uint8_t firmware_desc[31]; // Firmware string description uint8_t firmware_rev; // Firmware revision number uint8_t rom_version; // ROM Version struct { uint8_t sectors_per_track; // Sectors/Track uint8_t tracks_per_cylinder; // Tracks/Cylinder (heads) struct { uint8_t lsb; uint8_t msb; } cylinders_per_drive; // Byte-flipped Cylinders/Drive } track_info; struct { uint8_t lsb; // Least significant byte uint8_t midb; // Middle byte uint8_t msb; // Most significant byte } capacity; // 24-bit value, byte-flipped (lsb..msb) uint8_t unused[16]; uint8_t interleave; // Interleave factor struct { uint8_t mux_parameters[12]; uint8_t pipe_name_table_ptr[2]; // Pointer to table of 64 entries, 8 bytes each (table of names) uint8_t pipe_ptr_table_ptr[2]; // Pointer to table of 64 entries, 8 bytes each. See pp. 29 - Mass Storage GTI uint8_t pipe_area_size[2]; // Size of pipe area (lsb, msb) struct { uint8_t track_offset[2]; } vdo_table[7]; // Virtual drive table uint8_t lsi11_vdo_table[8]; uint8_t lsi11_spare_table[8]; } table_info; uint8_t drive_number; // Physical drive number struct { uint8_t lsb; // Least uint8_t midb; // Middle uint8_t msb; // Most } physical_capacity; // Physical capacity of drive } drive_param_response; // // 2-byte Boot command (0x14) // struct { uint8_t code; // Command code uint8_t boot_block; // Which boot block to read (0-7) } old_boot_command; // // Put drive into prep mode command (0x11) // struct { uint8_t code; // Command code uint8_t drive; // Drive number (starts at 1) uint8_t prep_block[512]; // Machine code payload } prep_mode_command; // // Read Firmware command (Prep Mode 0x32) // struct { uint8_t code; // Command Code uint8_t encoded_h_s;// Encoded Head (bits 7-5) / Sector (bits 4-0) } read_firmware_command; // // Write Firmware command (Prep Mode 0x33) // struct { uint8_t code; // Command Code uint8_t encoded_h_s; // Encoded Head (bits 7-5) / Sector (bits 4-0) uint8_t 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_t code; // Command Code uint8_t 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_t status; // Disk access status uint8_t 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_t lsb; uint8_t msb; } spared_track[8]; // Spared track table (0xffff indicates end) uint8_t interleave; // Interleave factor uint8_t reserved; struct { uint8_t track_offset[2]; // Virtual drive offsets (lsb, msb) 0xffff indicates unused } vdo_table[7]; uint8_t lsi11_vdo_table[8]; uint8_t lsi11_spare_table[8]; uint8_t reserved2[432]; struct { uint8_t lsb; uint8_t msb; } revh_spare_table[16]; }; // Structure of Block #3, the Constellation Parameter Block struct constellation_parameter_block_t { uint8_t mux_parameters[12]; uint8_t pipe_name_table_ptr[2]; uint8_t pipe_ptr_table_ptr[2]; uint8_t pipe_area_size[2]; uint8_t reserved[470]; uint8_t software_protection[12]; uint8_t serial_number[12]; }; // Structure of Block #7, the Semaphore Table Block struct semaphore_table_block_t { union { uint8_t semaphore_table[256]; // Table consists of 256 bytes struct { uint8_t semaphore_name[8]; // Each semaphore name is 8 bytes } semaphore_entry[32]; // 32 Entries } semaphore_block; uint8_t 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_t recv_bytes; // Number of bytes from host for this command uint16_t xmit_bytes; // Number of bytes to return to host }; void dump_buffer(uint8_t *buffer, uint16_t length); bool parse_hdc_command(uint8_t data); uint8_t corvus_write_sector(uint8_t drv, uint32_t sector, uint8_t *buffer, int len); uint8_t corvus_write_logical_sector(dadr_t *dadr, uint8_t *buffer, int len); uint8_t corvus_read_sector(uint8_t drv, uint32_t sector, uint8_t *buffer, int len); uint8_t corvus_read_logical_sector(dadr_t *dadr, uint8_t *buffer, int len); uint8_t corvus_lock_semaphore(uint8_t *name); uint8_t corvus_unlock_semaphore(uint8_t *name); uint8_t corvus_init_semaphore_table(); uint8_t corvus_get_drive_parameters(uint8_t drv); uint8_t corvus_read_boot_block(uint8_t block); uint8_t corvus_enter_prep_mode(uint8_t drv, uint8_t *prep_block); uint8_t corvus_exit_prep_mode(); uint8_t corvus_read_firmware_block(uint8_t head, uint8_t sector); uint8_t corvus_write_firmware_block(uint8_t head, uint8_t sector, uint8_t *buffer); uint8_t corvus_format_drive(uint8_t *pattern, uint16_t 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 DECLARE_DEVICE_TYPE(CORVUS_HDC, corvus_hdc_device) #endif // MAME_MACHINE_CORVUSHD_H