summaryrefslogblamecommitdiffstatshomepage
path: root/src/devices/machine/corvushd.cpp
blob: 491c4ce59709cf547e855175a6d39f0d4206e352 (plain) (tree)
1
2
                       
                                              


























































                                                                                                   





                                                                                                  
                             

 




























































































































































                                                                                                                       

                           
                      












                                     

                           
                                                         








                                                                                                                              


                                                                       














                                                   
                                                                       
                         
                               





                                                                                           
                                                                                


















                                                                                                                    
                                                                            







                                                                                 
                                                                       
  
                                                         
                                                                                    
 
                                                                                                     
 
                          




















                                                               

                                                                              
                                                                                                                                    
                                                                     



















                                                  
                                                           
                                                                                                            

                                      
                                                                                         

                                                 
                                                                                                      
                                            





                                        
                                              





                                                  

                                                                                
                                                                                                                             
                                                                     

                                      
                                                                                              

                                                 
                                                                                                           
                                            
                 
                             
 
                     









                                                       
                                       






                                                              
                                                                                                        
                      
                                                                                       

                                                                        


                                                                                               
                                    







                                                                                                

                                                                                                   









                                                                                                         

                                                                                                               
                                                                                                               
                                                                                

         
                                   





















                                                                                              
                                                                                                


                                                                                  








                                                                                               
                                                   




                                                                                                                 
                                                            
                             
 


                                                                                                                            
                                                                                                           
 
                                                               

                                  
                                 










                                                        
                                       






                                                          
                                                                                                       
                      
                                                                                       

                                                                               


                                                                                             
                                    







                                                                                               

                                                                                                   




                                              
                                   





















                                                                                     
                                                                                               


                                                                                                  








                                                                                               
                                                   




                                                                                                                
                                                               
                             
 


                                                                                                                            
                                                                                                           
 
                                                              

                                  
                                 



















                                             
                                                                 

                                        
                             
                              

                                                                                  



                                                  
                                                                                                

                                                                                                            
                                                                            










                                                                                                                                        
                                     













                                                                          
                                                                                                                                     
                        
                                                                                                                                 
                                                                                                                                  
                                                                                                                 

                                                                                                                             
                                                                                            



                                              
                                                                                                                                  




















                                               
                                                                   

                                        
                             
                              
                                                                                  



                                                  
                                                                                                

                                                                                                              
                                                                            







                                                                                                                       
                                     












                                                                        
                                                                                                                             
                
                                                                                                                  
                                                                                                               
                                                                                                         

                                                                                                                       
                                                                                    




















                                                                
                                                          

                                        
                         


                                                                           
                                                                                                 















                                                                                                                   
                                       



                         
                                                                     

                                                                                         
               
                       


                                      
                                                                                          
               
                       


                                               
                                                                                                   
                                                                         



                                                    
                                       
         
                                                                                                                           
                                 





                                                                  
                                                                                  

                                                                                                                                         
                                 





                                                                           
                                                                                           

                                                                                                                                                  
                                 





                                        


                                                                                
                                                                                                                                                              





                                                                
                            
          



                                                                                                                 



                                                                                                                 




                                                                                                                             



                                                                                                  


                                                                                                                                  
                                                                              
                                                                           
                                                                             



                                                                                                                                 
 
                                                         


                                                                                              

                                                                                
                                           
















                                                  
                                                                  

                                                                         
                                                                                       




  



                                                                           
                                                                         
                                                                         
                                                                           
                                
  






                                                                             
                                                                         
                                                                            
                                                                          

                                  

                                                         
                                                               



                         
                                                                                     
                                                                    





                                                                                                    










                                                                                                    
                            











                                                         
                                                    


                                                           
                            





                                              









                                                                  
                                                                                     

                                                                                   
 
                                                              



                                                                                                                       
                                                                                                       





                      
                                               










                                                                 
                                                                                                       

                                                                                    
 
                                                              



                                                                                                                        
                                                                               





                      
                                       








                                                                                   
                                                                                



                               
 
                                                               
                                    
 
                                                                                         








                                                                                                                              
                                                                                                                                           


                                                         
                                                                               

















                                                                                                                                                 
                                       



                             
                                                             
                                           
                                                                  
          

                                                                                

                                                                               
         
                               

         
                                                                                         

                   
                               

                           
                               
 


                                                         


                                                
 
                                                                                                                                   

                    














                                                                                          
                                                                                  


                                                                                       
                                                        


                                   

                                                       



                                                              

                                                                                                                                                     


                                                     

                                                                                                                                                     

                                                    

                                                                                                                                                     

                                                     

                                                                                                                                                    


                                                      

                                                                                                                                                    

                                                     

                                                                                                                                                    







                                                         
                                                                           
                                                                        
                                                                                                                                                                 

                                                                          

                                                                                                                                

                                                                        
                                                                                                                             

                                                                          
                                                                                                   
                                                                                                                                        

                                                              
                                                                                    





                                                         

                                                                                                             

                                                          

                                                                                                                         

                                                      
                                                                              
                                                                                                        
                                                                                               

                                              

                                                                                                           


                                                                                                                              
                                           
                                                       
                                                      

                                                                                                       
                                                                              
                                                                                                        
                                                                                               

                                                      
                                                                              
                                                                        

                                                        


                                                                                                                                    

                                                         


                                                                                                                                                  

                                                       

                                                                                                                                 
                                              

                                                                                             
                                                                                       
                                              
                                        


                                                                                                                                                               




                                                                                                                         

                                                                       










                                                                                                         
                                                      
                                                                                                      
 
                                                                                                                                                       



                                                             
                                                                                                   
 
                                                                                                                        



                                                                     


                                                                                                                    
 
                                                                                       














                                                          
                                                                                                

                             


                                       

                                                                                                               




                                                                                                          
                                                            
                                                                                                                    




                                                                                                          
                                                                                                          

                              
                                                                                                                 





                                                                                                       
                                                                                                                                             
                                                                                                          
                                                                                                                          
                                                                                                 






                                                                                                                  
                                                                                                               













                                                                              
                                               
  
                                        





                                                                                                                                 
 


                                                                                                                             
 
                                                 
 
































                                                                                 

                                                                    




                                                                     
                                                                                                                    


















































                                                                                    
                                                    





                                                                 

 











                                                                                      
                                              
                        
















                                                                                                  
                                          
                       
 

                                                                                                                                


                         

                                                                                                          


                         
                                               
 

                                                                                                                             
 


                                                                             
 
                                                                                             
 
                                                                                                         



                                                                                                           
                                                                                              

















                                                                    
                                            


                                                                      
                                                                                                                         
                                                                                                                                       
                                        


                       
                                                                                                                 
                                                                                                                                 
                                        





                                                                                          









                                                                                                                                                 

         
                                             
 
                                                                                                                                





                                                                                                                             

                                                                                                      



                                                                          
                                                                    



                                                                                                 

                                                                                              

         
// license:BSD-3-Clause
// copyright-holders:Brett Wyer, Raphael Nabet
//
//  corvus_hd
//
//  Implementation of a Corvus Hard Drive / Host Bus Adapter pair.  The drive
//  being emulated is a Rev B drive, functionally speaking, rather than an Omnidrive.
//
//  The Corvus Flat Cable HBA is a very simplistic device due to the fact that most
//  of the smarts are in the Hard Drive itself.  What's in the hard drive includes a
//  Z80 processor, 4K of EPROM and 5KB of RAM.  Ultimately, a true emulation would include
//  the on-boad controller; however, that is outside the current scope of this code.  Maybe
//  if I could get a Rev. B/H drive, it could be reverse-engineered to do this.
//
//  The Flat Cable controller has two registers:
//
//  Data -              Single byte bidirectional data transfer
//  Status Register -   Bit 7 - Controller Ready -- off = ready, on = not ready
//                      Bit 6 - Bus Direction -- off = host-to-controller, on = controller-to-host
//
//  Layout of a Corvus Hard Disk is as follows:
//
//  Blk Len Description
//  --- --- -----------
//  0   1   Boot Block
//  1   1   Disk Parameter Block
//  2   1   Diagnostic Block (prep code)
//  3   1   Constellation Parameter Block
//  4   2   Dispatcher Code
//  6   2   Pipes and Semaphores code (Semaphore table contained in block 7, bytes 1-256)
//  8   10  Mirror Controller Code
//  18  2   LSI-11 Controller Code
//  20  2   Pipes Controller Code
//  22  3   Reserved for Future Use
//  25  8   Boot Blocks 0-7.  Apple II uses 0-3, Concept uses 4-7
//  33  4   Active User Table
//  37  3   Reserved
//
//  All of the above blocks are initialized by the DDIAG program.  This can be found on the
//  Concept FSYSGEN floppy.
//      - Boot blocks and code blocks are initialized using the "Update Firmware on Disk" function.
//      - Disk Parameter Block is initialized using the "Display/Modify Drive Parameters" function
//
//  An on-disk structure is written with the SYSGEN utility on the same disk.  Password is "HAI"
//
//
//  Corvus Hard Disk performance characteristics (from a 6MB Rev B-E drive)
//
//      Average Latency: 6.25ms
//      Average Access Time: 125ms (and you thought YOUR drive was slow...)
//      Maximum Access Time: 240ms
//      Maximum Access Time (single track): 3ms
//      Data Transfer Rate: 960Kb/sec
//      Rotational Speed: 4800RPM
//
//  Brett Wyer
//
//
//  TODO:
//      Implement READY line glitch after last byte of command (Disk System Tech Ref pp. 3)
//      Implement Read-after-Write (always happens on Rev B/H drives per Mass Storage GTI pp. 12)
//      Implement Drive Illegal Addresses (seek past last sector)
//      Implement Switches on front of drive (LSI-11, MUX, Format, Reset)
//      Implement an inter-sector delay during the FORMAT command (format happens too quickly now)
//

#include "emu.h"
#include "machine/corvushd.h"


//
// 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


DEFINE_DEVICE_TYPE(CORVUS_HDC, corvus_hdc_device, "corvus_hdc", "Corvus Flat Cable HDC")

corvus_hdc_device::corvus_hdc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	device_t(mconfig, CORVUS_HDC, tag, owner, clock),
	m_status(0),
	m_prep_mode(false),
	m_prep_drv(0),
	m_sectors_per_track(0),
	m_tracks_per_cylinder(0),
	m_cylinders_per_drive(0),
	m_offset(0),
	m_awaiting_modifier(false),
	m_recv_bytes(0),
	m_xmit_bytes(0),
	m_last_cylinder(0),
	m_delay(0),
	m_invalid_command_flag(false)
{
}

#define VERBOSE 0
#define VERBOSE_RESPONSES 0
#define ROM_VERSION 1           // Controller ROM version
#define MAX_COMMAND_SIZE 4096   // The maximum size of a command packet (the controller only has 5K of RAM...)
#define SPARE_TRACKS 7          // This is a Rev B drive, so 7 it is
#define CALLBACK_CTH_MODE 1     // Set to Controller-to-Host mode when callback fires
#define CALLBACK_HTC_MODE 2     // Set to Host-to-Controller mode when callback fires
#define CALLBACK_SAME_MODE 3    // Leave mode the same when callback fires
#define CALLBACK_TIMEOUT 4      // Four seconds have elapsed.  We're timing out
#define TRACK_SEEK_TIME 1667    // Track-to-track seek time in microseconds (Maximum Access Time / Total Cylinders)
#define INTERBYTE_DELAY 5       // Inter-byte delay in microseconds communicating between controller and host
#define INTERSECTOR_DELAY 25000 // 25ms delay between sectors (4800 RPM = 80 Rev/Second.  Maximum 2 sectors transferred / Rev)

#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
#define LOG_BUFFER(p,s) do { if (VERBOSE) dump_buffer(p,s); } while (0)



//
// Dump_Buffer
//
// Dump a buffer to the error log in a nice format.
//
// Pass:
//      buffer: Data to be dumped
//      length: Number of bytes to be dumped
//
// Returns:
//      nada
//
void corvus_hdc_device::dump_buffer(uint8_t *buffer, uint16_t length) {
	uint16_t  offset;
	char    ascii_dump[16];

	logerror("dump_buffer: Dump of %d bytes:\n", length);
	logerror("Base  00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f ASCII\n");
	logerror("----  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ----------------");

	for(offset=0; offset < length; offset++) {
		if(offset % 16 == 0) {                  // WHY IS 0 % 16 == 0???
			if(offset > 0 && offset % 16 == 0)
				logerror("%16.16s", ascii_dump);
			logerror("\n%4.4x: %2.2x ", offset, *(buffer + offset));
		} else {
			logerror("%2.2x ", *(buffer + offset));
		}
		ascii_dump[offset % 16] = isprint(*(buffer + offset)) ? *(buffer + offset) : '.';
	}
	if(offset % 16)
		logerror("%.*s", (16 - (offset % 16)) * 3, "                                                     ");
	logerror("%.*s\n", (offset % 16) ? (offset % 16) : 16, ascii_dump);
}



//
// Parse_HDC_Command
//
// Process the first byte received from the host.  Do some initial evaluation and
// return either true or false as to whether the command was invalid or not.
//
// Note that recv_bytes and xmit_bytes in the corvus_hdc structure are updated as
// a side-effect of this command, as is awaiting_modifier.
//
// Pass:
//      data:   Initial byte received from the host in Host to Controller mode
//
// Returns:
//      Whether the command was invalid or not (true = invalid command)
//
bool corvus_hdc_device::parse_hdc_command(uint8_t data) {
	m_awaiting_modifier = false;               // This is the case by definition

	LOG(("parse_hdc_command: Called with data: 0x%2.2x, Prep mode is: %d\n", data, m_prep_mode));

	if(!m_prep_mode) {
		switch(data) {
			//
			// Single-byte commands - Non-Prep mode
			//
			case READ_SECTOR_256:
			case WRITE_SECTOR_256:
			case READ_CHUNK_128:
			case READ_CHUNK_256:
			case READ_CHUNK_512:
			case WRITE_CHUNK_128:
			case WRITE_CHUNK_256:
			case WRITE_CHUNK_512:
			case READTEMPBLOCK:
			case WRITETEMPBLOCK:
			case BOOT:
			case READ_BOOT_BLOCK:
			case GET_DRIVE_PARAMETERS:
		//  case PARK_HEADS_REVH:
			case PARK_HEADS_OMNI:
			case ECHO:
			case PREP_MODE_SELECT:
				m_recv_bytes = corvus_cmd[data][0].recv_bytes;
				m_xmit_bytes = corvus_cmd[data][0].xmit_bytes;
				LOG(("parse_hdc_command: Single byte command recognized: 0x%2.2x, to recv: %d, to xmit: %d\n", data,
					m_recv_bytes, m_xmit_bytes));
				break;
			//
			// Double-byte commands
			//
			case SEMAPHORE_LOCK_CODE:
		//  case SEMAPHORE_UNLOCK_CODE:
			case SEMAPHORE_INIT_CODE:
		//  case PIPE_READ_CODE:
		//  case PIPE_WRITE_CODE:
		//  case PIPE_CLOSE_CODE:
		//  case PIPE_STATUS_CODE:
		//  case SEMAPHORE_STATUS_CODE:
			case PIPE_OPEN_WRITE_CODE:
		//  case PIPE_AREA_INIT_CODE:
		//  case PIPE_OPEN_READ_CODE:
			case ADDACTIVE_CODE:
		//  case DELACTIVEUSR_REVBH_CODE:
		//  case DELACTIVEUSR_OMNI_CODE:
		//  case DELACTIVENUM_OMNI_CODE:
		//  case FINDACTIVE_CODE:
				m_awaiting_modifier = true;
				LOG(("parse_hdc_command: Double byte command recognized: 0x%2.2x\n", data));
				break;

			default:                            // This is an INVALID command
				m_recv_bytes = 1;
				m_xmit_bytes = 1;
				LOG(("parse_hdc_command: Invalid command detected: 0x%2.2x\n", data));
				return true;
		}
	} else {
		switch(data) {
			//
			// Prep Commands
			//
			case PREP_MODE_SELECT:
			case PREP_RESET_DRIVE:
			case PREP_FORMAT_DRIVE:
			case PREP_FILL_DRIVE_OMNI:
			case PREP_VERIFY:
			case PREP_READ_FIRMWARE:
			case PREP_WRITE_FIRMWARE:
				m_recv_bytes = corvus_prep_cmd[data].recv_bytes;
				m_xmit_bytes = corvus_prep_cmd[data].xmit_bytes;
				LOG(("parse_hdc_command: Prep command recognized: 0x%2.2x, to recv: %d, to xmit: %d\n", data,
					m_recv_bytes, m_xmit_bytes));
				break;

			default:                            // This is an INVALID prep command
				m_recv_bytes = 1;
				m_xmit_bytes = 1;
				LOG(("parse_hdc_command: Invalid Prep command detected: 0x%2.2x\n", data));
				return true;
		}
	}   // if(!prep_mode)

	return false;
}



//
// Corvus_Write_Sector
//
// Write a variably-sized chunk of data to the CHD file
//
// Pass:
//      drv:    Corvus drive id (1..15)
//      sector: Physical sector number to write to
//      buffer: Buffer to write
//      len:    Length of the buffer (amount of data to write)
//
// Returns:
//      status: Command status
//
uint8_t corvus_hdc_device::corvus_write_sector(uint8_t drv, uint32_t sector, uint8_t *buffer, int len) {
	hard_disk_file
			*disk;              // Structures for interface to CHD routines
	uint8_t   tbuffer[512];       // Buffer to hold an entire sector
	uint16_t  cylinder;           // Cylinder this sector resides on

	LOG(("corvus_write_sector: Write Drive: %d, physical sector: 0x%5.5x\n", drv, sector));

	disk = corvus_hdc_file(drv);
	if(!disk) {
		logerror("corvus_write_sector: Failure returned by corvus_hdc_file(%d)\n", drv);
		return STAT_FATAL_ERR | STAT_DRIVE_NOT_ONLINE;
	}

	//
	// Calculate what cylinder the sector resides on for timing purposes
	//
	cylinder = (double) sector / (double) m_sectors_per_track / (double) m_tracks_per_cylinder;
	m_delay = abs(m_last_cylinder - cylinder) * TRACK_SEEK_TIME + INTERSECTOR_DELAY;

	//
	// Corvus supports write sizes of 128, 256 and 512 bytes.  In the case of a write smaller than
	// the sector size of 512 bytes, the sector is read, the provided data is overlayed and then the
	// sector is written back out.  See pp. 5 of the Mass Storage Systems GTI for the details of this
	// wonderful functionality.
	//
	if(len == 512) {
		hard_disk_write(disk, sector, buffer);
	} else {
		hard_disk_read(disk, sector, tbuffer);      // Read the existing data into our temporary buffer
		memcpy(tbuffer, buffer, len);                   // Overlay the data with the buffer passed
		m_delay += INTERSECTOR_DELAY;                  // Add another delay because of the Read / Write
		hard_disk_write(disk, sector, tbuffer);     // Re-write the data
	}

	m_last_cylinder = cylinder;

	LOG(("corvus_write_sector: Full sector dump on a write of %d bytes follows:\n", len));
	LOG_BUFFER(len == 512 ? buffer : tbuffer, 512);

	return STAT_SUCCESS;
}



//
// Corvus_Write_Logical_Sector
//
// Write a variably-sized chunk of data to the user area of the virtual Corvus drive
//
// Pass:
//      dadr:   Corvus-encoded Disk Address -- Logical Sector
//      buffer: Buffer holding the data to be written to the disk
//      len:    Length of the buffer
//
// Returns:
//      status: Corvus status
//
uint8_t corvus_hdc_device::corvus_write_logical_sector(dadr_t *dadr, uint8_t *buffer, int len) {
	uint8_t   status;             // Status returned from Physical Sector read
	uint8_t   drv;                // Corvus drive id (1..15)
	uint32_t  sector;             // Sector number on drive

	//
	// Unencode the first byte of the DADR
	//
	// High-order nibble of first byte is the most-significant nibble of the sector address
	// Low-order nibble of first byte is the drive id
	//
	// For example: 0x23 would decode to Drive ID #3, high-order nibble: 0x02.
	//
	drv = (dadr->address_msn_and_drive & 0x0f);
	sector = (dadr->address_msn_and_drive & 0xf0 << 12) | (dadr->address_mid << 8) | dadr->address_lsb;

	LOG(("corvus_write_logical_sector: Writing based on DADR: 0x%6.6x, logical sector: 0x%5.5x, drive: %d\n",
		dadr->address_msn_and_drive << 16 | dadr->address_lsb << 8 | dadr->address_mid, sector, drv));

	// Set m_tracks_per_cylinder and m_sectors_per_track
	corvus_hdc_file(drv);

	//
	// Shift the logical sector address forward by the number of firmware cylinders (2) + the number of spare tracks (7)
	//
	sector += (m_tracks_per_cylinder * m_sectors_per_track * 2) + (SPARE_TRACKS * m_sectors_per_track);

	status = corvus_write_sector(drv, sector, buffer, len);

	if(status != STAT_SUCCESS)
		m_xmit_bytes = 1;

	return status;
}


//
// Corvus_Read_Sector
//
// Read a variably-sized chunk of data from the CHD file
//
// Pass:
//      drv:    Corvus drive id (1..15)
//      sector: Physical sector number to read from
//      buffer: Buffer to hold the data read from the disk
//      len:    Length of the buffer
//
// Returns:
//      status: Corvus status
//
uint8_t corvus_hdc_device::corvus_read_sector(uint8_t drv, uint32_t sector, uint8_t *buffer, int len) {
	hard_disk_file
			*disk;              // Structures for interface to CHD routines
	uint8_t   tbuffer[512];       // Buffer to store full sector results in
	uint16_t  cylinder;

	LOG(("corvus_read_sector: Read Drive: %d, physical sector: 0x%5.5x\n", drv, sector));

	disk = corvus_hdc_file(drv);
	if(!disk) {
		logerror("corvus_read_sector: Failure returned by corvus_hdc_file(%d)\n", drv);
		return STAT_FATAL_ERR | STAT_DRIVE_NOT_ONLINE;
	}

	//
	// Calculate what cylinder the sector resides on for timing purposes
	//
	cylinder = (double) sector / (double) m_sectors_per_track / (double) m_tracks_per_cylinder;
	m_delay = abs(m_last_cylinder - cylinder) * TRACK_SEEK_TIME + INTERSECTOR_DELAY;

	hard_disk_read(disk, sector, tbuffer);

	memcpy(buffer, tbuffer, len);

	m_last_cylinder = cylinder;

	LOG(("corvus_read_sector: Data read follows:\n"));
	LOG_BUFFER(tbuffer, len);

	return STAT_SUCCESS;
}



//
// Corvus_Read_Logical_Sector
//
// Read a variably-sized chunk of data from the user area of the virtual Corvus drive
//
// Pass:
//      dadr:   Corvus-encoded Disk Address -- Logical Sector
//      buffer: Buffer to hold the data read from the disk
//      len:    Length of the buffer
//
// Returns:
//      status: Corvus status
//
uint8_t corvus_hdc_device::corvus_read_logical_sector(dadr_t *dadr, uint8_t *buffer, int len) {
	uint8_t   status;                             // Status returned from Physical Sector read
	uint8_t   drv;                                // Corvus drive id (1..15)
	uint32_t  sector;                             // Sector number on drive

	//
	// Unencode the first byte of the DADR
	//
	// High-order nibble of first byte is the most-significant nibble of the sector address
	// Low-order nibble of first byte is the drive id
	//
	// For example: 0x23 would decode to Drive ID #3, high-order nibble: 0x02.
	//
	drv = (dadr->address_msn_and_drive & 0x0f);
	sector = (dadr->address_msn_and_drive & 0xf0 << 12) | (dadr->address_mid << 8) | dadr->address_lsb;

	LOG(("corvus_read_logical_sector: Reading based on DADR: 0x%6.6x, logical sector: 0x%5.5x, drive: %d\n",
		dadr->address_msn_and_drive << 16 | dadr->address_lsb << 8 | dadr->address_mid, sector, drv));

	// Set up m_tracks_per_cylinder and m_sectors_per_track
	corvus_hdc_file(drv);

	//
	// Shift the logical sector address forward by the number of firmware cylinders (2) + the number of spare tracks (7)
	//
	sector += (m_tracks_per_cylinder * m_sectors_per_track * 2) + (SPARE_TRACKS * m_sectors_per_track);

	status = corvus_read_sector(drv, sector, buffer, len);

	if(status != STAT_SUCCESS)
		m_xmit_bytes = 1;

	return status;
}



//
// Corvus_Lock_Semaphore
//
// Lock a semaphore in the semaphore table
//
// Pass:
//      name:   Name of the semaphore to lock
//
// Returns:
//      status: Disk status
//
// Side-effects:
//      Fills in the semaphore result code
//
uint8_t corvus_hdc_device::corvus_lock_semaphore(uint8_t *name) {
	semaphore_table_block_t
			semaphore_table;
	uint8_t   offset = 0;
	bool    found = false;
	uint8_t   blank_offset = 32;  // Initialize to invalid offset
	uint8_t   status;             // Status returned from Physical Sector read

	//
	// Read the semaphore table from the drive
	//
	status = corvus_read_sector(1, 7, semaphore_table.semaphore_block.semaphore_table, 256);
	if(status != STAT_SUCCESS) {
		logerror("corvus_lock_semaphore: Error reading semaphore table, status: 0x%2.2x\n", status);
		m_buffer.semaphore_locking_response.result = SEM_DISK_ERROR;
		return status;
	}

	//
	// Search the semaphore table to see if the semaphore already exists--if so it's locked
	// Also look for the first blank entry to stick the new one into
	//
	do {
		if(blank_offset == 32 && strncmp((char *) &semaphore_table.semaphore_block.semaphore_entry[offset], "        ", 8) == 0)
			blank_offset = offset;
		if(strncmp((char *) &semaphore_table.semaphore_block.semaphore_entry[offset], (char *) name, 8) == 0) {
			found = true;
			break;
		}
	} while( ++offset < 32 );

	//
	// Deal with the found status
	//
	// - Stick it into the table if we didn't find it and there's room
	// - Respond with a "set" status if we did find it
	//
	// Once that's done, write the updated table to the disk
	//
	if(!found) {
		if(blank_offset == 32) {
			m_buffer.semaphore_locking_response.result = SEM_TABLE_FULL;                   // No space for the semaphore!
		} else {
			m_buffer.semaphore_locking_response.result = SEM_PRIOR_STATE_NOT_SET;          // It wasn't there already
			memcpy(&semaphore_table.semaphore_block.semaphore_entry[blank_offset], name, 8);// Stick it into the table
			status = corvus_write_sector(1, 7, semaphore_table.semaphore_block.semaphore_table, 256);
			if(status != STAT_SUCCESS) {
				logerror("corvus_lock_semaphore: Error updating semaphore table, status: 0x%2.2x\n", status);
				m_buffer.semaphore_locking_response.result = SEM_DISK_ERROR;
				return status;
			}
		}
	} else {
		m_buffer.semaphore_locking_response.result = SEM_PRIOR_STATE_SET;                  // It's already locked -- sorry
	}

	return STAT_SUCCESS;
}



//
// Corvus_Unlock_Semaphore
//
// Unock a semaphore in the semaphore table
//
// Pass:
//      name:   Name of the semaphore to unlock
//
// Returns:
//      status: Disk status
//
// Side-effects:
//      Fills in the semaphore result code
//
uint8_t corvus_hdc_device::corvus_unlock_semaphore(uint8_t *name) {
	semaphore_table_block_t
			semaphore_table;
	uint8_t   offset = 0;
	bool    found = false;
	uint8_t   status;             // Status returned from Physical Sector read

	//
	// Read the semaphore table from the drive
	//
	status = corvus_read_sector(1, 7, semaphore_table.semaphore_block.semaphore_table, 256);
	if(status != STAT_SUCCESS) {
		logerror("corvus_unlock_semaphore: Error reading semaphore table, status: 0x%2.2x\n", status);
		m_buffer.semaphore_locking_response.result = SEM_DISK_ERROR;
		return status;
	}

	//
	// Search the semaphore table to see if the semaphore already exists--if so it's locked
	//
	do {
		if(strncmp((char *) &semaphore_table.semaphore_block.semaphore_entry[offset], (char *) name, 8) == 0) {
			found = true;
			break;
		}
	} while( ++offset < 32 );

	//
	// Deal with the found status
	//
	// - If we didn't find it, just respond that it wasn't there
	// - If we did find it, respond with a "set" status and clear it
	//
	// Once that's done, write the updated table to the disk
	//
	if(!found) {
		m_buffer.semaphore_locking_response.result = SEM_PRIOR_STATE_NOT_SET;              // It wasn't there already
	} else {
		m_buffer.semaphore_locking_response.result = SEM_PRIOR_STATE_SET;                  // It was there
		memcpy(&semaphore_table.semaphore_block.semaphore_entry[offset], "        ", 8);    // Clear it
		status = corvus_write_sector(1, 7, semaphore_table.semaphore_block.semaphore_table, 256);
		if(status != STAT_SUCCESS) {
			logerror("corvus_unlock_semaphore: Error updating semaphore table, status: 0x%2.2x\n", status);
			m_buffer.semaphore_locking_response.result = SEM_DISK_ERROR;
			return status;
		}
	}

	return STAT_SUCCESS;
}



//
// Corvus_Init_Semaphore_Table
//
// Zap all of the semaphores from the table (set them to blanks)
//
// Pass:
//      Nothing
//
// Returns:
//      Disk status
//
//
uint8_t corvus_hdc_device::corvus_init_semaphore_table() {
	semaphore_table_block_t
			semaphore_table;
	uint8_t   status;

	memset(semaphore_table.semaphore_block.semaphore_table, 0x20, 256);

	status = corvus_write_sector(1, 7, semaphore_table.semaphore_block.semaphore_table, 256);
	if(status != STAT_SUCCESS) {
		logerror("corvus_init_semaphore_table: Error updating semaphore table, status: 0x%2.2x\n", status);
		return status;
	}

	return STAT_SUCCESS;
}



//
// Corvus_Get_Drive_Parameters
//
// Fills in the Drive Parameter packet based on the opened CHD file
//
// Pass:
//      drv:    Corvus drive id (1..15)
//
// Returns:
//      Status of command
//
uint8_t corvus_hdc_device::corvus_get_drive_parameters(uint8_t drv) {
	uint16_t  capacity;                           // Number of usable 512-byte blocks
	uint16_t  raw_capacity;                       // Number of actual 512-byte blocks
	union {
		uint8_t
			buffer[512];
		disk_parameter_block_t
			dpb;
	} raw_disk_parameter_block;                 // Buffer for the Disk Parameter Block
	union {
		uint8_t
			buffer[512];
		constellation_parameter_block_t
			cpb;
	} raw_constellation_parameter_block;        // Buffer for the Constellation Parameter Block
	uint8_t   status;                             // Status to return

	//
	// Make sure a valid drive is being accessed
	//
	if ( ! corvus_hdc_file( drv ) )
	{
		logerror("corvus_get_drive_parameters: Attempt to retrieve parameters from non-existent drive: %d\n", drv);
		m_xmit_bytes = 1;
		return STAT_FATAL_ERR | STAT_DRIVE_NOT_ONLINE;
	}

	//
	// Read the Disk Parameter Block (Sector 1) from the drive
	//
	status = corvus_read_sector(drv, 1, raw_disk_parameter_block.buffer, 512);
	if(status != STAT_SUCCESS) {
		logerror("corvus_get_drive_parameters: Error status returned reading Disk Parameter Block -- status: 0x%2.2x\n", status);
		m_xmit_bytes = 1;
		return status;
	}

	//
	// Read the Constellation Parameter Block (Sector 3) from the drive
	//
	status = corvus_read_sector(drv, 3, raw_constellation_parameter_block.buffer, 512);
	if(status != STAT_SUCCESS) {
		logerror("corvus_get_drive_parameters: Error status returned reading Constellation Parameter Block -- status: 0x%2.2x\n", status);
		m_xmit_bytes = 1;
		return status;
	}

	//
	// Build up the parameter packet
	//

	// This firmware string and revision were taken from the Corvus firmware
	// file CORVB184.CLR found on the SSE SoftBox distribution disk.
	strncpy((char *) m_buffer.drive_param_response.firmware_desc, "V18.4     -- CONST II - 11/82  ", sizeof(m_buffer.drive_param_response.firmware_desc));
	m_buffer.drive_param_response.firmware_rev = 37;

	// Controller ROM version
	m_buffer.drive_param_response.rom_version = ROM_VERSION;

	//
	// Track information
	//
	m_buffer.drive_param_response.track_info.sectors_per_track = m_sectors_per_track;
	m_buffer.drive_param_response.track_info.tracks_per_cylinder = m_tracks_per_cylinder;
	m_buffer.drive_param_response.track_info.cylinders_per_drive.msb = (m_cylinders_per_drive & 0xff00) >> 8;
	m_buffer.drive_param_response.track_info.cylinders_per_drive.lsb = (m_cylinders_per_drive & 0x00ff);

	//
	// Calculate the user capacity of the drive based on total capacity less spare tracks and firmware tracks
	//
	raw_capacity = m_tracks_per_cylinder * m_cylinders_per_drive * m_sectors_per_track; // Total capacity
	capacity = raw_capacity - ((m_tracks_per_cylinder * m_sectors_per_track * 2) + (SPARE_TRACKS * m_sectors_per_track));
	m_buffer.drive_param_response.capacity.msb = (capacity & 0xff0000) >> 16;
	m_buffer.drive_param_response.capacity.midb = (capacity & 0x00ff00) >> 8;
	m_buffer.drive_param_response.capacity.lsb = (capacity & 0x0000ff);

	//
	// Fill in the information from the Disk Parameter Block and Constellation Parameter Block
	//
	m_buffer.drive_param_response.interleave = raw_disk_parameter_block.dpb.interleave;
	memcpy(m_buffer.drive_param_response.table_info.mux_parameters, raw_constellation_parameter_block.cpb.mux_parameters, 12);
	memcpy(m_buffer.drive_param_response.table_info.pipe_name_table_ptr,
		raw_constellation_parameter_block.cpb.pipe_name_table_ptr, 2);
	memcpy(m_buffer.drive_param_response.table_info.pipe_ptr_table_ptr,
		raw_constellation_parameter_block.cpb.pipe_ptr_table_ptr, 2);
	memcpy(m_buffer.drive_param_response.table_info.pipe_area_size, raw_constellation_parameter_block.cpb.pipe_area_size, 2);
	memcpy(m_buffer.drive_param_response.table_info.vdo_table, raw_disk_parameter_block.dpb.vdo_table, 14);
	memcpy(m_buffer.drive_param_response.table_info.lsi11_vdo_table, raw_disk_parameter_block.dpb.lsi11_vdo_table, 8);
	memcpy(m_buffer.drive_param_response.table_info.lsi11_spare_table, raw_disk_parameter_block.dpb.lsi11_spare_table, 8);

	m_buffer.drive_param_response.drive_number = drv;
	m_buffer.drive_param_response.physical_capacity.msb = (raw_capacity & 0xff0000) >> 16;
	m_buffer.drive_param_response.physical_capacity.midb = (raw_capacity & 0x00ff00) >> 8;
	m_buffer.drive_param_response.physical_capacity.lsb = (raw_capacity & 0x0000ff);

	LOG(("corvus_get_drive_parameters: Drive Parameter packet follows:\n"));
	LOG_BUFFER(m_buffer.raw_data, 110);

	return STAT_SUCCESS;
}



//
// Corvus_Read_Boot_Block
//
// Old-style Boot (0x14) command boot block reader
//
// Pass:
//      block:  Boot block number to read (0-7)
//
// Returns:
//      status: Status of read operation
//
uint8_t corvus_hdc_device::corvus_read_boot_block(uint8_t block) {
	LOG(("corvus_read_boot_block: Reading boot block: %d\n", block));

	return corvus_read_sector(1, 25 + block, m_buffer.read_512_response.data, 512);
}



//
// corvus_enter_prep_mode
//
// Enter prep mode.  In prep mode, only prep mode commands may be executed.
//
// A "prep block" is 512 bytes of machine code that the host sends to the
// controller.  The controller will jump to this code after receiving it,
// and it is what actually implements prep mode commands.  This HLE ignores
// the prep block from the host.
//
// On the Rev B/H drives (which we emulate), a prep block is Z80 machine
// code and only one prep block can be sent.  Sending the "put drive into
// prep mode" command (0x11) when already in prep mode is an error.  The
// prep block sent by the Corvus program DIAG.COM on the SSE SoftBox
// distribution disk returns error 0x8f (unrecognized command) for this case.
//
// On the OmniDrive and Bank, a prep block is 6801 machine code.  These
// controllers allow multiple prep blocks to be sent.  The first time the
// "put drive into prep mode" command is sent puts the drive into prep mode.
// The command can then be sent again up to 3 times with more prep blocks.
// (Mass Storage GTI, pages 50-51)
//
// Pass:
//      drv:        Corvus drive id (1..15) to be prepped
//      prep_block: 512 bytes of machine code, contents ignored
//
// Returns:
//      Status of command
//
uint8_t corvus_hdc_device::corvus_enter_prep_mode(uint8_t drv, uint8_t *prep_block) {
	// on rev b/h drives, sending the "put drive into prep mode"
	// command when already in prep mode is an error.
	if (m_prep_mode) {
		logerror("corvus_enter_prep_mode: Attempt to enter prep mode while in prep mode\n");
		return STAT_FATAL_ERR | STAT_ILL_CMD_OP_CODE;
	}

	// check if drive is valid
	if (!corvus_hdc_file(drv)) {
		logerror("corvus_enter_prep_mode: Failure returned by corvus_hdc_file(%d)\n", drv);
		return STAT_FATAL_ERR | STAT_DRIVE_NOT_ONLINE;
	}

	LOG(("corvus_enter_prep_mode: Prep mode entered for drive %d, prep block follows:\n", drv));
	LOG_BUFFER(prep_block, 512);

	m_prep_mode = true;
	m_prep_drv = drv;
	return STAT_SUCCESS;
}



//
// corvus_exit_prep_mode (Prep Mode Only)
//
// Exit from prep mode and return to normal command mode.
//
// Returns:
//      Status of command (always success)
//
uint8_t corvus_hdc_device::corvus_exit_prep_mode() {
	LOG(("corvus_exit_prep_mode: Prep mode exited\n"));
	m_prep_mode = false;
	m_prep_drv = 0;
	return STAT_SUCCESS;
}



//
// Corvus_Read_Firmware_Block (Prep Mode Only)
//
// Reads firmware information from the first cylinder of the drive
//
// Pass:
//      head:   Head number
//      sector: Sector number
//
// Returns:
//      Status of command
//
uint8_t corvus_hdc_device::corvus_read_firmware_block(uint8_t head, uint8_t sector) {
	uint16_t  relative_sector;    // Relative sector on drive for Physical Read
	uint8_t   status;

	relative_sector = head * m_sectors_per_track + sector;

	LOG(("corvus_read_firmware_block: Reading firmware head: 0x%2.2x, sector: 0x%2.2x, relative_sector: 0x%2.2x\n",
		head, sector, relative_sector));

	status = corvus_read_sector(m_prep_drv, relative_sector, m_buffer.read_512_response.data, 512);
	return status;
}



//
// Corvus_Write_Firmware_Block (Prep Mode Only)
//
// Writes firmware information to the first cylinder of the drive
//
// Pass:
//      head:   Head number
//      sector: Sector number
//      buffer: Data to be written
//
// Returns:
//      Status of command
//
uint8_t corvus_hdc_device::corvus_write_firmware_block(uint8_t head, uint8_t sector, uint8_t *buffer) {
	uint16_t  relative_sector;    // Relative sector on drive for Physical Write
	uint8_t   status;

	relative_sector = head * m_sectors_per_track + sector;

	LOG(("corvus_write_firmware_block: Writing firmware head: 0x%2.2x, sector: 0x%2.2x, relative_sector: 0x%2.2x\n",
		head, sector, relative_sector));

	status = corvus_write_sector(m_prep_drv, relative_sector, buffer, 512);
	return status;
}



//
// Corvus_Format_Drive (Prep Mode Only)
//
// Write the pattern provided across the entire disk
//
// Pass:
//      pattern: 512-byte buffer containing the pattern to write to the whole drive
//
// Returns:
//      Status of command
//
uint8_t corvus_hdc_device::corvus_format_drive(uint8_t *pattern, uint16_t len) {
	uint32_t  sector;
	uint32_t  max_sector;
	uint8_t   status = 0;
	uint8_t   tbuffer[512];

	// Set up m_tracks_per_cylinder and m_sectors_per_track
	corvus_hdc_file(m_prep_drv);

	max_sector = m_sectors_per_track * m_tracks_per_cylinder * m_cylinders_per_drive;

	//
	// If we were passed less than 512 bytes, fill the buffer up with the first byte passed (for Omnidrive Format command)
	//
	if(len < 512) {
		memset(tbuffer, *pattern, 512);
		pattern = tbuffer;
	}

	LOG(("corvus_format_drive: Formatting drive with 0x%5.5x sectors, pattern buffer (passed length: %d) follows\n", max_sector, 512));
	LOG_BUFFER(pattern, 512);

	for(sector = 0; sector <= max_sector; sector++) {
		status = corvus_write_sector(m_prep_drv, sector, pattern, 512);
		if(status != STAT_SUCCESS) {
			logerror("corvus_format_drive: Error while formatting drive in corvus_write_sector--sector: 0x%5.5x, status: 0x%x2.2x\n",
				sector, status);
			break;
		}
	}

	return status;
}



//
// Corvus_HDC_File
//
// Returns a hard_disk_file object for a given virtual hard drive device in the concept
//
// Pass:
//      drv:    Corvus drive id (1..15)
//
// Returns:
//      hard_disk_file object
//
hard_disk_file *corvus_hdc_device::corvus_hdc_file(int drv) {
	static const char *const tags[] = {
		"harddisk1", "harddisk2", "harddisk3", "harddisk4"
	};

	// we only support 4 drives, as per the tags[] table, so prevent a crash
	// Corvus drive id numbers are 1-based so we check 1..4 instead of 0..3
	if (drv < 1 || drv > 4)
	{
		return nullptr;
	}

	harddisk_image_device *img = siblingdevice<harddisk_image_device>(tags[drv - 1]);

	if ( !img )
		return nullptr;

	if (!img->exists())
		return nullptr;

	// Pick up the Head/Cylinder/Sector info
	hard_disk_file *file = img->get_hard_disk_file();
	hard_disk_info *info = hard_disk_get_info(file);
	m_sectors_per_track = info->sectors;
	m_tracks_per_cylinder = info->heads;
	m_cylinders_per_drive = info->cylinders;

	LOG(("corvus_hdc_file: Attached to drive %u image: H:%d, C:%d, S:%d\n", drv, info->heads, info->cylinders, info->sectors));

	return file;
}



//
// Corvus_Process_Command_Packet
//
// Having received a complete packet from the host, process it
//
// Pass:
//      Invalid_Command_Flag:   Invalid command flag responses are handled in this routine
//
// Returns:
//      Nothing
//
void corvus_hdc_device::corvus_process_command_packet(bool invalid_command_flag) {
	if (VERBOSE_RESPONSES)
	{
		LOG(("corvus_hdc_data_w: Complete packet received.  Dump follows:\n"));
		LOG_BUFFER(m_buffer.raw_data, m_offset);
	}

	if(!invalid_command_flag) {
		if(!m_prep_mode) {
			switch(m_buffer.command.code) {
				//
				// Read / Write Chunk commands
				//
				case READ_CHUNK_128:
					m_buffer.read_128_response.status =
						corvus_read_logical_sector(&m_buffer.read_sector_command.dadr, m_buffer.read_128_response.data, 128);
					break;
				case READ_SECTOR_256:
				case READ_CHUNK_256:
					m_buffer.read_256_response.status =
						corvus_read_logical_sector(&m_buffer.read_sector_command.dadr, m_buffer.read_256_response.data, 256);
					break;
				case READ_CHUNK_512:
					m_buffer.read_512_response.status =
						corvus_read_logical_sector(&m_buffer.read_sector_command.dadr, m_buffer.read_512_response.data, 512);
					break;
				case WRITE_CHUNK_128:
					m_buffer.single_byte_response.status =
						corvus_write_logical_sector(&m_buffer.write_128_command.dadr, m_buffer.write_128_command.data, 128);
					break;
				case WRITE_SECTOR_256:
				case WRITE_CHUNK_256:
					m_buffer.single_byte_response.status =
						corvus_write_logical_sector(&m_buffer.write_256_command.dadr, m_buffer.write_256_command.data, 256);
					break;
				case WRITE_CHUNK_512:
					m_buffer.single_byte_response.status =
						corvus_write_logical_sector(&m_buffer.write_512_command.dadr, m_buffer.write_512_command.data, 512);
					break;
				//
				// Semaphore commands
				//
				case SEMAPHORE_LOCK_CODE:
			//  case SEMAPHORE_UNLOCK_CODE:
				case SEMAPHORE_INIT_CODE:
			//  case SEMAPHORE_STATUS_CODE:
					switch(m_buffer.command.modifier) {
						case SEMAPHORE_LOCK_MOD:
							m_buffer.semaphore_locking_response.status = corvus_lock_semaphore(m_buffer.lock_semaphore_command.name);
							break;
						case SEMAPHORE_UNLOCK_MOD:
							m_buffer.semaphore_locking_response.status =
								corvus_unlock_semaphore(m_buffer.unlock_semaphore_command.name);
							break;
						case SEMAPHORE_INIT_MOD:
							m_buffer.single_byte_response.status = corvus_init_semaphore_table();
							break;
						case SEMAPHORE_STATUS_MOD:
							m_buffer.semaphore_status_response.status =
								corvus_read_sector(1, 7, m_buffer.semaphore_status_response.table, 256);
							break;
						default:
							invalid_command_flag = true;
					}
					break;
				//
				// Miscellaneous commands
				//
				case BOOT:
					m_buffer.read_512_response.status =
						corvus_read_boot_block(m_buffer.old_boot_command.boot_block);
					break;
				case GET_DRIVE_PARAMETERS:
					m_buffer.drive_param_response.status =
						corvus_get_drive_parameters(m_buffer.get_drive_parameters_command.drive);
					break;
				case PREP_MODE_SELECT:
					m_buffer.single_byte_response.status =
						corvus_enter_prep_mode(m_buffer.prep_mode_command.drive,
							m_buffer.prep_mode_command.prep_block);
					break;
				default:
					m_xmit_bytes = 1;                      // Return a fatal status
					m_buffer.single_byte_response.status = STAT_FAULT | STAT_FATAL_ERR;
					logerror("corvus_hdc_data_w: Unimplemented command, returning FATAL FAULT status!\n");
					break;
			}
		} else {    // In Prep mode
			switch(m_buffer.command.code) {
				case PREP_MODE_SELECT:
					// when already in prep mode, some drives allow this command to
					// be sent again.  see corvus_enter_prep_mode() for details.
					m_buffer.single_byte_response.status =
						corvus_enter_prep_mode(m_buffer.prep_mode_command.drive,
							m_buffer.prep_mode_command.prep_block);
					break;
				case PREP_RESET_DRIVE:
					m_buffer.single_byte_response.status =
						corvus_exit_prep_mode();
					break;
				case PREP_READ_FIRMWARE:
					m_buffer.drive_param_response.status =
						corvus_read_firmware_block((m_buffer.read_firmware_command.encoded_h_s & 0xe0) >> 5,
							m_buffer.read_firmware_command.encoded_h_s & 0x1f);
					break;
				case PREP_WRITE_FIRMWARE:
					m_buffer.drive_param_response.status =
						corvus_write_firmware_block((m_buffer.write_firmware_command.encoded_h_s & 0xe0) >> 5,
							m_buffer.write_firmware_command.encoded_h_s & 0x1f, m_buffer.write_firmware_command.data);
					break;
				case PREP_FORMAT_DRIVE:
					m_buffer.drive_param_response.status =
						corvus_format_drive(m_buffer.format_drive_revbh_command.pattern, m_offset - 512);
					break;
				case PREP_VERIFY:
					m_buffer.verify_drive_response.status = STAT_SUCCESS;
					m_buffer.verify_drive_response.bad_sectors = 0;
					break;
				default:
					m_xmit_bytes = 1;
					m_buffer.single_byte_response.status = STAT_FAULT | STAT_FATAL_ERR;
					logerror("corvus_hdc_data_w: Unimplemented Prep command %02x, returning FATAL FAULT status!\n", m_buffer.command.code);
			}
		}
		if (VERBOSE_RESPONSES)
		{
			LOG(("corvus_hdc_data_w: Command execution complete, status: 0x%2.2x.  Response dump follows:\n",
				m_buffer.single_byte_response.status));
			LOG_BUFFER(m_buffer.raw_data, m_xmit_bytes);
		}

	} // if(!invalid_command_flag)

	//
	// Use a separate "if" in case the Invalid Command Flag was set as a result of a two-byte command
	//
	if(invalid_command_flag) {
		//
		// An Illegal command was detected (Truly invalid, not just unimplemented)
		//
		m_buffer.single_byte_response.status =
			STAT_FATAL_ERR | STAT_ILL_CMD_OP_CODE;      // Respond with an Illegal Op Code

		logerror("corvus_hdc_data_w: Illegal command 0x%2.2x, status: 0x%2.2x\n", m_buffer.command.code, m_buffer.single_byte_response.status);
	}
	//
	// Command execution complete, free up the controller
	//
	m_offset = 0;                                  // Point to beginning of buffer for response

	LOG(("corvus_hdc_data_w: Setting one-time mame timer of %d microseconds to simulate disk function\n", m_delay));

	//
	// Set up timers for command completion and timeout from host
	//
	//machine.scheduler().timer_set(attotime::from_usec(m_delay), FUNC(corvus_hdc_callback), CALLBACK_CTH_MODE);
	m_cmd_timer->adjust(attotime::from_usec(m_delay), CALLBACK_CTH_MODE);
	m_timeout_timer->enable(0);            // We've received enough data, disable the timeout timer

	m_delay = 0;                                   // Reset delay for next function
}



//
// Corvus_HDC_Callback
//
// Callback routine for completion of controller functions
//
// Pass:
//      Callback Function
//
// Returns:
//      Nothing
//
void corvus_hdc_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
	int function = param;

	switch(function) {
		case CALLBACK_CTH_MODE:
			m_status |= CONTROLLER_DIRECTION;              // Set to Controller-to-Host, Ready mode
			m_status &= ~(CONTROLLER_BUSY);

			LOG(("corvus_hdc_callback: Callback executed with function CALLBACK_CTH_MODE\n"));

			break;
		case CALLBACK_HTC_MODE:
			m_status &= ~(CONTROLLER_DIRECTION |
				CONTROLLER_BUSY);                           // Set to Host-to-Controller, Ready mode

			LOG(("corvus_hdc_callback: Callback executed with function CALLBACK_HTC_MODE\n"));

			break;
		case CALLBACK_SAME_MODE:
			m_status &= ~(CONTROLLER_BUSY);                // Set the controller to Ready mode

			break;
		case CALLBACK_TIMEOUT:                              // We reached a four-second timeout threshold
			if(m_offset < m_recv_bytes || (m_offset > m_recv_bytes && m_recv_bytes != 0)) {
				m_buffer.single_byte_response.status = STAT_TIMEOUT;
				m_status |= CONTROLLER_DIRECTION;
				m_status &= ~(CONTROLLER_BUSY);
				m_recv_bytes = 0;
				m_xmit_bytes = 1;
				logerror("corvus_hdc_callback: Exceeded four-second timeout for data from host, resetting communications\n");
			} else { // if(m_recv_bytes == 0)                 This was a variable-size command
				LOG(("corvus_hdc_callback: Executing variable-length command via four-second timeout\n"));
				corvus_process_command_packet(0);          // Process the command
			}
			break;
		default:
			logerror("corvus_hdc_callback: FATAL ERROR -- Unknown callback function: %d\n", function);
			assert(0);
	}
	if(function != CALLBACK_SAME_MODE) {
		m_timeout_timer->enable(0);                // Disable the four-second timer now that we're done
	}
}



//
// Corvus_HDC_Init
//
// Global routine to initialize the Hard Disk Controller structures and arrays
//
// Pass:
//      Nothing
//
// Returns:
//      nullptr if there's no file to attach to
//
void corvus_hdc_device::device_start() {
	m_status &= ~(CONTROLLER_DIRECTION | CONTROLLER_BUSY); // Host-to-controller mode, Idle (awaiting command from Host mode)
	m_prep_mode = false;                       // We're not in Prep Mode
	m_offset = 0;                              // Buffer is empty
	m_awaiting_modifier = false;               // We're not in the middle of a two-byte command
	m_xmit_bytes = 0;                          // We don't have anything to say to the host
	m_recv_bytes = 0;                          // We aren't waiting on additional data from the host

	m_timeout_timer = timer_alloc(TIMER_TIMEOUT);  // Set up a timer to handle the four-second host-to-controller timeout
	m_timeout_timer->adjust(attotime::from_seconds(4), CALLBACK_TIMEOUT);
	m_timeout_timer->enable(0);        // Start this timer out disabled

	m_cmd_timer = timer_alloc(TIMER_COMMAND);

	//
	// Define all of the packet sizes for the commands
	//

	// Read / Write commands
	corvus_cmd[READ_SECTOR_256][0].recv_bytes = 4;
	corvus_cmd[READ_SECTOR_256][0].xmit_bytes = 257;
	corvus_cmd[WRITE_SECTOR_256][0].recv_bytes = 260;
	corvus_cmd[WRITE_SECTOR_256][0].xmit_bytes = 1;
	corvus_cmd[READ_CHUNK_128][0].recv_bytes = 4;
	corvus_cmd[READ_CHUNK_128][0].xmit_bytes = 129;
	corvus_cmd[READ_CHUNK_256][0].recv_bytes = 4;
	corvus_cmd[READ_CHUNK_256][0].xmit_bytes = 257;
	corvus_cmd[READ_CHUNK_512][0].recv_bytes = 4;
	corvus_cmd[READ_CHUNK_512][0].xmit_bytes = 513;
	corvus_cmd[WRITE_CHUNK_128][0].recv_bytes = 132;
	corvus_cmd[WRITE_CHUNK_128][0].xmit_bytes = 1;
	corvus_cmd[WRITE_CHUNK_256][0].recv_bytes = 260;
	corvus_cmd[WRITE_CHUNK_256][0].xmit_bytes = 1;
	corvus_cmd[WRITE_CHUNK_512][0].recv_bytes = 516;
	corvus_cmd[WRITE_CHUNK_512][0].xmit_bytes = 1;

	// Semaphore commands
	corvus_cmd[SEMAPHORE_LOCK_CODE][SEMAPHORE_LOCK_MOD].recv_bytes = 10;
	corvus_cmd[SEMAPHORE_LOCK_CODE][SEMAPHORE_LOCK_MOD].xmit_bytes = 12;
	corvus_cmd[SEMAPHORE_UNLOCK_CODE][SEMAPHORE_UNLOCK_MOD].recv_bytes = 10;
	corvus_cmd[SEMAPHORE_UNLOCK_CODE][SEMAPHORE_UNLOCK_MOD].xmit_bytes = 12;
	corvus_cmd[SEMAPHORE_INIT_CODE][SEMAPHORE_INIT_MOD].recv_bytes = 5;
	corvus_cmd[SEMAPHORE_INIT_CODE][SEMAPHORE_INIT_MOD].xmit_bytes = 1;
	corvus_cmd[SEMAPHORE_STATUS_CODE][SEMAPHORE_STATUS_MOD].recv_bytes = 5;
	corvus_cmd[SEMAPHORE_STATUS_CODE][SEMAPHORE_STATUS_MOD].xmit_bytes = 257;

	// Pipe commands
	corvus_cmd[PIPE_READ_CODE][PIPE_READ_MOD].recv_bytes =  5;
	corvus_cmd[PIPE_READ_CODE][PIPE_READ_MOD].xmit_bytes =  516;
	corvus_cmd[PIPE_WRITE_CODE][PIPE_WRITE_MOD].recv_bytes = 517;
	corvus_cmd[PIPE_WRITE_CODE][PIPE_WRITE_MOD].xmit_bytes = 12;
	corvus_cmd[PIPE_CLOSE_CODE][PIPE_CLOSE_MOD].recv_bytes = 5;
	corvus_cmd[PIPE_CLOSE_CODE][PIPE_CLOSE_MOD].xmit_bytes = 12;
	corvus_cmd[PIPE_STATUS_CODE][PIPE_STATUS_MOD].recv_bytes = 5;
	corvus_cmd[PIPE_STATUS_CODE][PIPE_STATUS_MOD].xmit_bytes = 513; // There are actually two possibilities here
	corvus_cmd[PIPE_OPEN_WRITE_CODE][PIPE_OPEN_WRITE_MOD].recv_bytes = 10;
	corvus_cmd[PIPE_OPEN_WRITE_CODE][PIPE_OPEN_WRITE_MOD].xmit_bytes = 12;
	corvus_cmd[PIPE_AREA_INIT_CODE][PIPE_AREA_INIT_MOD].recv_bytes = 10;
	corvus_cmd[PIPE_AREA_INIT_CODE][PIPE_AREA_INIT_MOD].xmit_bytes = 12;
	corvus_cmd[PIPE_OPEN_READ_CODE][PIPE_OPEN_READ_MOD].recv_bytes = 10;
	corvus_cmd[PIPE_OPEN_READ_CODE][PIPE_OPEN_READ_MOD].xmit_bytes = 12;

	// Active User Table Commands
	corvus_cmd[ADDACTIVE_CODE][ADDACTIVE_MOD].recv_bytes = 18;
	corvus_cmd[ADDACTIVE_CODE][ADDACTIVE_MOD].xmit_bytes = 2;
	corvus_cmd[DELACTIVEUSR_REVBH_CODE][DELACTIVEUSR_REVBH_MOD].recv_bytes = 18;
	corvus_cmd[DELACTIVEUSR_REVBH_CODE][DELACTIVEUSR_REVBH_MOD].xmit_bytes = 2;
	corvus_cmd[DELACTIVENUM_OMNI_CODE][DELACTIVENUM_OMNI_MOD].recv_bytes = 18;
	corvus_cmd[DELACTIVENUM_OMNI_CODE][DELACTIVENUM_OMNI_MOD].xmit_bytes = 2;
	corvus_cmd[DELACTIVEUSR_OMNI_CODE][DELACTIVEUSR_OMNI_MOD].recv_bytes = 18;
	corvus_cmd[DELACTIVEUSR_OMNI_CODE][DELACTIVEUSR_OMNI_MOD].xmit_bytes = 2;
	corvus_cmd[FINDACTIVE_CODE][FINDACTIVE_MOD].recv_bytes = 18;
	corvus_cmd[FINDACTIVE_CODE][FINDACTIVE_MOD].xmit_bytes = 17;
	corvus_cmd[READTEMPBLOCK][0].recv_bytes = 2;
	corvus_cmd[READTEMPBLOCK][0].xmit_bytes = 513;
	corvus_cmd[WRITETEMPBLOCK][0].recv_bytes = 514;
	corvus_cmd[WRITETEMPBLOCK][0].xmit_bytes = 1;

	// Miscellaneous Commands
	corvus_cmd[BOOT][0].recv_bytes = 2;
	corvus_cmd[BOOT][0].xmit_bytes = 513;
	corvus_cmd[READ_BOOT_BLOCK][0].recv_bytes = 3;
	corvus_cmd[READ_BOOT_BLOCK][0].xmit_bytes = 513;
	corvus_cmd[GET_DRIVE_PARAMETERS][0].recv_bytes = 2;
	corvus_cmd[GET_DRIVE_PARAMETERS][0].xmit_bytes = 129;
	corvus_cmd[PARK_HEADS_REVH][0].recv_bytes = 514;
	corvus_cmd[PARK_HEADS_REVH][0].xmit_bytes = 1;
	corvus_cmd[PARK_HEADS_OMNI][0].recv_bytes = 1;
	corvus_cmd[PARK_HEADS_OMNI][0].xmit_bytes = 1;
	corvus_cmd[ECHO][0].recv_bytes = 513;
	corvus_cmd[ECHO][0].xmit_bytes = 513;

	// Put Drive in Prep Mode
	corvus_cmd[PREP_MODE_SELECT][0].recv_bytes = 514;
	corvus_cmd[PREP_MODE_SELECT][0].xmit_bytes = 1;

	// Prep Mode Commands
	corvus_prep_cmd[PREP_MODE_SELECT].recv_bytes = 514;
	corvus_prep_cmd[PREP_MODE_SELECT].xmit_bytes = 1;
	corvus_prep_cmd[PREP_RESET_DRIVE].recv_bytes = 1;
	corvus_prep_cmd[PREP_RESET_DRIVE].xmit_bytes = 1;
	corvus_prep_cmd[PREP_FORMAT_DRIVE].recv_bytes = 0;
	corvus_prep_cmd[PREP_FORMAT_DRIVE].xmit_bytes = 1;
	corvus_prep_cmd[PREP_FILL_DRIVE_OMNI].recv_bytes = 3;
	corvus_prep_cmd[PREP_FILL_DRIVE_OMNI].xmit_bytes = 1;
	corvus_prep_cmd[PREP_VERIFY].recv_bytes = 1;
	corvus_prep_cmd[PREP_VERIFY].xmit_bytes = 2;
	corvus_prep_cmd[PREP_READ_FIRMWARE].recv_bytes = 2;
	corvus_prep_cmd[PREP_READ_FIRMWARE].xmit_bytes = 513;
	corvus_prep_cmd[PREP_WRITE_FIRMWARE].recv_bytes = 514;
	corvus_prep_cmd[PREP_WRITE_FIRMWARE].xmit_bytes = 1;

	LOG(("corvus_hdc_init: Drive structures initialized\n"));
}


//
// Corvus_HDC_Status_R
//
// Global routine to read the Status Register from the Controller (Controller to Host)
//
// Pass:
//      Nothing
//
// Returns:
//      Value in the controller status register
//
READ8_MEMBER ( corvus_hdc_device::status_r ) {
	return m_status;
}



//
// Corvus_HDC_Data_R
//
// Read the Data Register from the Controller (Controller to Host).  If transmission is complete,
// as defined as offset == bytes to transmit, reset the status to Host-to-Controller mode and Idle
// when complete.
//
// Pass:
//      Nothing
//
// Returns:
//      Value in the controller data register
//
READ8_MEMBER ( corvus_hdc_device::read ) {
	uint8_t result;

	if((m_status & CONTROLLER_DIRECTION) == 0) {   // Check to see if we're in Controller-to-Host mode
		logerror("corvus_hdc_data_r: Data register read when in Host-to-Controller mode (status: 0x%2.2x)\n", m_status);
		return 0;
	}

	if((m_status & CONTROLLER_BUSY) != 0) {        // Check to see if we're Busy
		logerror("corvus_hdc_data_r: Data register read when Busy (status: 0x%2.2x)\n", m_status);
		return 0;
	}

	result = m_buffer.raw_data[m_offset++];

	if(m_offset == m_xmit_bytes) {
		LOG(("corvus_hdc_data_r: Finished transmitting %d bytes of data.  Returning to idle mode.\n", m_xmit_bytes));

		m_offset = 0;          // We've reached the end of valid data
		m_xmit_bytes = 0;      // We don't have anything more to say
		m_recv_bytes = 0;      // No active commands

		m_cmd_timer->adjust(attotime::from_usec(INTERBYTE_DELAY), CALLBACK_HTC_MODE);

//      m_status &= ~(CONTROLLER_DIRECTION | CONTROLLER_BUSY); // Put us in Idle, Host-to-Controller mode
	} else {
		//
		// Not finished with this packet.  Insert an interbyte delay and then let the host continue
		//
		m_cmd_timer->adjust(attotime::from_usec(INTERBYTE_DELAY), CALLBACK_SAME_MODE);
	}

	return result;
}



//
// Corvus_HDC_Data_W
//
// Write to the Data Register on the Controller (Host to Controller)
//
// Pass:
//      Value to write to controller data register
//
// Returns:
//      Nothing
//
WRITE8_MEMBER ( corvus_hdc_device::write ) {
	//
	// Received a byte -- check to see if we should really respond
	//
	if((m_status & CONTROLLER_DIRECTION) != 0) {       // System wrote to controller when controller wasn't listening
		logerror("corvus_hdc_data_w: Data register written when in Controller-to-Host mode (status: 0x%2.2x, data: 0x%2.2x)\n",
			m_status, data);
		return;
	}

	if((m_status & CONTROLLER_BUSY) != 0) {            // System wrote to controller when controller was busy
		logerror("corvus_hdc_data_w: Data register written when controller not Ready (status: 0x%2.2x, data: 0x%2.2x)\n",
			m_status, data);
		return;
	}

	//
	// We're supposed to be paying attention.  Make a decision about the data received
	//
	if(m_offset == 0)  {                                                   // First byte of a packet
		LOG(("corvus_hdc_data_w: Received a byte with m_offset == 0.  Processing as command: 0x%2.2x\n", data));
		m_invalid_command_flag = parse_hdc_command(data);
		m_timeout_timer->reset((attotime::from_seconds(4)));
		m_timeout_timer->enable(1);                                // Start our four-second timer
	} else if(m_offset == 1 && m_awaiting_modifier) {                     // Second byte of a packet
		LOG(("corvus_hdc_data_w: Received a byte while awaiting modifier with m_offset == 0.  Processing as modifier: 0x%2.2x\n", data));
		m_awaiting_modifier = false;
		m_recv_bytes = corvus_cmd[m_buffer.command.code][data].recv_bytes;
		m_xmit_bytes = corvus_cmd[m_buffer.command.code][data].xmit_bytes;
	}

	m_buffer.raw_data[m_offset++] = data;

	assert(m_offset <= MAX_COMMAND_SIZE);                                  // Something is wrong, or I undersized the buffer

	//
	// We now have enough information to make a decision whether to execute the command, respond with a fatal response
	// or just wait for more data.  If we can do something, execute the command.  Otherwise, just fall through and return
	// to the user with us Ready for more data and in Host-to-Controller mode.
	//
	if(m_offset == m_recv_bytes) {                        // We've received enough data to process
		corvus_process_command_packet(m_invalid_command_flag);
	} else {
		//
		// Reset the four-second timer since we received some data
		//
		m_timeout_timer->reset((attotime::from_seconds(4)));

		//
		// Make the controller busy for a few microseconds while the command is processed
		//
		m_status |= CONTROLLER_BUSY;
		m_cmd_timer->adjust(attotime::from_usec(INTERBYTE_DELAY), CALLBACK_SAME_MODE);
	}
}