/* Copyright (c) 2000, Timothy Mann */
/* $Id: trs_hard.h,v 1.6 2009/06/15 23:41:03 mann Exp $ */

/* This software may be copied, modified, and used for any purpose
 * without fee, provided that (1) the above copyright notice is
 * retained, and (2) modified versions are clearly marked as having
 * been modified, with the modifier's name and the date included.  */

/*
 * Definitions for the Radio Shack TRS-80 Model I/III/4/4P
 * hard disk controller.  This is a Western Digital WD1000/WD1010
 * mapped at ports 0xc8-0xcf, plus control registers at 0xc0-0xc1.
 * 
 * Definitions inferred from various drivers and sketchy documents
 * found in odd corners.  Anyone have a real WD10xx data sheet?
 */

extern void trs_hard_init(void);
extern int trs_hard_in(int port);
extern void trs_hard_out(int port, int value);
extern char* trs_disk_dir;

/* Sector size is always 256 for TRSDOS/LDOS/etc. */
/* Other sizes currently not emulated */
#define TRS_HARD_SECSIZE 256
#define TRS_HARD_SECSIZE_CODE 0x0f /* size code for 256 byte sectors?!! */

/* RSHARD assumes 32 sectors/track */
/* Other sizes currently not emulated */
#define TRS_HARD_SEC_PER_TRK 32

/*
 * Tandy-specific registers
 */

/* Write protect switch register (read only): 
 *  abcd--pi
 *  a = drive 0 write protected
 *  b = drive 1 write protected
 *  c = drive 2 write protected
 *  d = drive 3 write protected
 *  p = at least one drive write protected
 *  i = interrupt request
 */
#define TRS_HARD_WP 0xc0
#define TRS_HARD_WPBIT(d) (0x80 >> (d))
#define TRS_HARD_WPSOME 0x02
#define TRS_HARD_INTRQ  0x01 /* not emulated */

/* Control register (read(?)/write): 
 *  ---sdw--
 *  s = software reset
 *  d = device enable
 *  w = wait enable
 */
#define TRS_HARD_CONTROL 0xc1
#define TRS_HARD_SOFTWARE_RESET 0x10
#define TRS_HARD_DEVICE_ENABLE  0x08
#define TRS_HARD_WAIT_ENABLE    0x04

/* ID register (Model II only!)
 */
#define TRS_HARD_ID0 0xc2
#define TRS_HARD_ID1 0xc3

/* CTC channels (Model II only!)
 */
#define TRS_HARD_CTC0 0xc4
#define TRS_HARD_CTC1 0xc5
#define TRS_HARD_CTC2 0xc6
#define TRS_HARD_CTC3 0xc7

/*
 * WD1010 registers
 */

/* Data register (read/write) */
#define TRS_HARD_DATA 0xc8

/* Error register (read only):
 *  bdin-atm
 *  b = block marked bad
 *  e = uncorrectable error in data 
 *  i = uncorrectable error in id (unused?)
 *  n = id not found
 *  - = unused
 *  a = command aborted
 *  t = track 0 not found
 *  m = bad address mark
 */
#define TRS_HARD_ERROR (TRS_HARD_DATA+1)
#define	TRS_HARD_BBDERR   0x80
#define TRS_HARD_DATAERR  0x40
#define TRS_HARD_IDERR    0x20 /* unused? */
#define TRS_HARD_NFERR    0x10
#define TRS_HARD_MCRERR   0x08 /* unused */
#define TRS_HARD_ABRTERR  0x04
#define TRS_HARD_TRK0ERR  0x02
#define TRS_HARD_MARKERR  0x01

/* Write precompensation register (write only) */
/* Value *4 is the starting cylinder for write precomp */
#define TRS_HARD_PRECOMP (TRS_HARD_DATA+1)

/* Sector count register (read/write) */
/* Used only for multiple sector accesses; otherwise ignored. */
/* Autodecrements when used. */
#define TRS_HARD_SECCNT (TRS_HARD_DATA+2)

/* Sector number register (read/write) */
#define TRS_HARD_SECNUM (TRS_HARD_DATA+3)

/* Cylinder low byte register (read/write) */
#define TRS_HARD_CYLLO (TRS_HARD_DATA+4)

/* Cylinder high byte register (read/write) */
#define TRS_HARD_CYLHI (TRS_HARD_DATA+5)

/* Size/drive/head register (read/write):
 *  essddhhh
 *  e = 0 if CRCs used; 1 if ECC used
 *  ss = sector size; 00=256, 01=512, 10=1024, 11=128
 *  dd = drive
 *  hhh = head
 */
#define TRS_HARD_SDH (TRS_HARD_DATA+6)
#define TRS_HARD_ECCMASK    0x80
#define TRS_HARD_ECCSHIFT   7
#define TRS_HARD_SIZEMASK   0x60
#define TRS_HARD_SIZESHIFT  5
#define TRS_HARD_DRIVEMASK  0x18
#define TRS_HARD_DRIVESHIFT 3
#define TRS_HARD_MAXDRIVES  4
#define TRS_HARD_HEADMASK   0x07
#define TRS_HARD_HEADSHIFT  0
#define TRS_HARD_MAXHEADS   8

/* Status register (read only): 
 *  brwsdcie
 *  b = busy
 *  r = drive ready
 *  w = write error
 *  s = seek complete
 *  d = data request
 *  c = corrected ecc (reserved)
 *  i = command in progress (=software reset required)
 *  e = error
 */
#define TRS_HARD_STATUS (TRS_HARD_DATA+7)
#define TRS_HARD_BUSY     0x80
#define TRS_HARD_READY    0x40
#define TRS_HARD_WRERR    0x20
#define TRS_HARD_SEEKDONE 0x10
#define TRS_HARD_DRQ	  0x08
#define TRS_HARD_ECC	  0x04
#define TRS_HARD_CIP	  0x02
#define TRS_HARD_ERR	  0x01

/* Command register (write only) */
#define TRS_HARD_COMMAND (TRS_HARD_DATA+7)

/*
 * WD1010 commands
 */

#define TRS_HARD_CMDMASK 0xf0

/* Restore: 
 *  0001rrrr
 *  rrrr = step rate; 0000=35us, else rrrr*0.5ms
 */
#define TRS_HARD_RESTORE 0x10

/* Read sector:
 *  0010dm00
 *  d = 0 for interrupt on DRQ, 1 for interrupt at end (DMA style)
 *      TRS-80 always uses programmed I/O, INTRQ not connected, I believe.
 *  m = multiple sector flag (not emulated!)
 */
#define TRS_HARD_READ  0x20
#define TRS_HARD_DMA   0x08
#define TRS_HARD_MULTI 0x04

/* Write sector:
 *  00110m00
 *  m = multiple sector flag (not emulated!) 
 */
#define TRS_HARD_WRITE 0x30

/* Verify sector (or "Scan ID"):
 *  01000000
 */
#define TRS_HARD_VERIFY 0x40

/* Format track:
 *  01010000
 */
#define TRS_HARD_FORMAT 0x50

/* Init (??):
 *  01100000
 */
#define TRS_HARD_INIT 0x60

/* Seek to specified sector/head/cylinder:
 *  0111rrrr
 *  rrrr = step rate; 0000=35us, else rrrr*0.5ms
 */
#define TRS_HARD_SEEK 0x70
