From 5b2f229bb04a17c0442df1ae38ab9c0fb95a8076 Mon Sep 17 00:00:00 2001 From: Peter Schorn Date: Sat, 29 Mar 2014 07:21:59 +0100 Subject: [PATCH] Added 88-HDSK support from Mike Douglas to AltairZ80 --- AltairZ80/altairz80_mhdsk.c | 404 ++++++++++++++++++++++++++++++++++++ AltairZ80/altairz80_sys.c | 7 +- 2 files changed, 409 insertions(+), 2 deletions(-) create mode 100755 AltairZ80/altairz80_mhdsk.c diff --git a/AltairZ80/altairz80_mhdsk.c b/AltairZ80/altairz80_mhdsk.c new file mode 100755 index 00000000..c4f33321 --- /dev/null +++ b/AltairZ80/altairz80_mhdsk.c @@ -0,0 +1,404 @@ +/* altairz80_mhdsk.c: MITS 88-HDSK Hard Disk simulator + + Copyright (c) 2002-2014, Peter Schorn + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Peter Schorn shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Peter Schorn. + +------------------------------------------------------------------------------ + + The 88-HDSK from MITS/Pertec consists of a 5mb removable platter and + a fixed 5mb platter. Each platter is double sided. Head 0 and 1 are the + top and bottom surface of the removable platter and head 2 and 3 are the + top and bottom surface of the fixed platter. Hard disk BASIC treats the + two platters as two separate drives. Each platter has 406 cylinders + with 24 sectors per track and 256 bytes per sector. + + The disk image file starts with head 0, track 0, sector 0 (0,0,0) through + (0,0,23), followed by head 1, track 0, sector 0 (1,0,0) through (1,0,23). + The pattern then repeats starting with (0,1,0). + + The external hard disk is accessed through eight ports of a 4-PIO card + at I/O addresses A0h-A7h. + + Written by Mike Douglas March, 2014 + Disk images provided by Martin Eberhard + +------------------------------------------------------------------------------- +*/ +#include "altairz80_defs.h" + +/** Typedefs & Defines **************************************/ +#define HDSK_SECTOR_SIZE 256 /* size of sector */ +#define HDSK_SECTORS_PER_TRACK 24 /* sectors per track */ +#define HDSK_NUM_HEADS 2 /* heads per disk */ +#define HDSK_NUM_TRACKS 406 /* tracks per surface */ +#define HDSK_TRACK_SIZE (HDSK_SECTOR_SIZE * HDSK_SECTORS_PER_TRACK) +#define HDSK_CYLINDER_SIZE (HDSK_TRACK_SIZE * 2) +#define HDSK_CAPACITY (HDSK_CYLINDER_SIZE * HDSK_NUM_TRACKS) +#define HDSK_NUMBER 8 /* number of hard disks */ +#define IO_IN 0 /* I/O operation is input */ +#define IO_OUT 1 /* I/O operation is output */ +#define UNIT_V_DSK_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_DSK_WLK (1 << UNIT_V_DSK_WLK) + +// Disk controller commands are in upper nibble of command high byte. + +#define CMD_SHIFT 4 // shift right 4 places +#define CMD_MASK 0x0f // mask after shifting +#define CMD_SEEK 0 // seek to track +#define CMD_WRITE_SEC 2 // write sector from buf n +#define CMD_READ_SEC 3 // read sector into buf n +#define CMD_WRITE_BUF 4 // load buffer n from CPU +#define CMD_READ_BUF 5 // read buffer n into CPU +#define CMD_READ_STATUS 6 // read controller IV byte +#define CMD_SET_IV_BYTE 8 // set controller IV byte +#define CMD_READ_UNFMT 10 // read unformatted sector +#define CMD_FORMAT 12 +#define CMD_INITIALIZE 14 + +// Other disk controller bit fields + +#define UNIT_SHIFT 2 // shift right 2 places +#define UNIT_MASK 0x03 // mask after shifting + +#define BUFFER_MASK 0x03 // mask - no shift needed + +#define TRACK_SHIFTH 8 // shift left 8 places into MSbyte +#define TRACK_MASKH 0x01 // msb of track number +#define TRACK_MASKL 0xff // entire lsb of track number + +#define HEAD_SHIFT 5 // shift right 5 places +#define HEAD_MASK 0x03 // mask after shifting (no heads 4-7) + +#define SECTOR_MASK 0x1f // mask - no shift needed + +// Command status equates + +#define CSTAT_WRITE_PROTECT 0x80 // disk is write protected +#define CSTAT_NOT_READY 0x01 // drive not ready +#define CSTAT_BAD_SECTOR 0x02 // invalid sector number + + +/** Module Globals - Private ********************************/ +static uint32 selectedDisk = 0; // current active disk +static uint32 selectedSector = 0; // current sector +static uint32 selectedTrack = 0; // current track +static uint32 selectedHead = 0; // current head +static uint32 selectedBuffer = 0; // current buffer # in use +static uint32 bufferIdx = 0; // current index into selected buffer +static uint32 maxBufferIdx = 256; // maximum buffer index allowed +static uint32 cmdLowByte = 0; // low byte of command + +// Controller status bytes + +static uint8 cstat = 0; // command status from controller + +// The hard disk controller support four 256 byte disk buffers */ + +static uint8 diskBuf1[HDSK_SECTOR_SIZE]; +static uint8 diskBuf2[HDSK_SECTOR_SIZE]; +static uint8 diskBuf3[HDSK_SECTOR_SIZE]; +static uint8 diskBuf4[HDSK_SECTOR_SIZE]; +static uint8 *diskBuf[] = { diskBuf1, diskBuf2, diskBuf3, diskBuf4 }; + +/** Forward and external Prototypes **************************************/ + +static int32 hdReturnReady(const int32 port, const int32 io, const int32 data); +static int32 hdCstat(const int32 port, const int32 io, const int32 data); +static int32 hdAcmd(const int32 port, const int32 io, const int32 data); +static int32 hdCdata(const int32 port, const int32 io, const int32 data); +static int32 hdAdata(const int32 port, const int32 io, const int32 data); +static void doRead(void); +static void doWrite(void); +static t_stat dsk_reset(DEVICE *dptr); +extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, + int32 (*routine)(const int32, const int32, const int32), uint8 unmap); + +/* 88DSK Standard I/O Data Structures */ + +static UNIT dsk_unit[] = { + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) }}; + +static MTAB dsk_mod[] = { + { UNIT_DSK_WLK, 0, "WRTENB", "WRTENB", NULL }, + { UNIT_DSK_WLK, UNIT_DSK_WLK, "WRTLCK", "WRTLCK", NULL }, + { 0 } +}; + +DEVICE mhdsk_dev = { + "MHDSK", dsk_unit, NULL, dsk_mod, + HDSK_NUMBER, 10, 31, 1, 8, 8, + NULL, NULL, &dsk_reset, + NULL, NULL, NULL, + NULL, (DEV_DISABLE | DEV_DEBUG), 0, + NULL, NULL, "MITS Hard Disk MHDSK" +}; + + +/*---------------------------------------------------------------------------------- + + dsk_reset - install I/O handlers and initialize variables. + + ----------------------------------------------------------------------------------*/ + +static t_stat dsk_reset(DEVICE *dptr) { + sim_map_resource(0xA0, 1, RESOURCE_TYPE_IO, &hdReturnReady, dptr->flags & DEV_DIS); + sim_map_resource(0xA1, 1, RESOURCE_TYPE_IO, &hdCstat, dptr->flags & DEV_DIS); + sim_map_resource(0xA2, 1, RESOURCE_TYPE_IO, &hdReturnReady, dptr->flags & DEV_DIS); + sim_map_resource(0xA3, 1, RESOURCE_TYPE_IO, &hdAcmd, dptr->flags & DEV_DIS); + sim_map_resource(0xA4, 1, RESOURCE_TYPE_IO, &hdReturnReady, dptr->flags & DEV_DIS); + sim_map_resource(0xA5, 1, RESOURCE_TYPE_IO, &hdCdata, dptr->flags & DEV_DIS); + sim_map_resource(0xA6, 1, RESOURCE_TYPE_IO, &hdReturnReady, dptr->flags & DEV_DIS); + sim_map_resource(0xA7, 1, RESOURCE_TYPE_IO, &hdAdata, dptr->flags & DEV_DIS); + + selectedSector = 0; // current sector + selectedTrack = 0; // current track + selectedHead = 0; // current head + selectedBuffer = 0; // current buffer # in use + bufferIdx = 0; // current index into selected buffer + maxBufferIdx = 256; // maximum buffer index allowed + cmdLowByte = 0; // low byte of command + + return SCPE_OK; +} + +/*------------------------------------------------------------------------------------- + hdReturnReady - common I/O handler for several hard disk status ports which set + bit 7 when the corresponding hard disk function is ready. In the emulator, + we're always ready for the next step, so we simply return ready all the time. + + 0xA0 - CREADY register. Accessed through the status/control register of 4-PIO + port 1-A. Returns the "ready for command" status byte. + + 0xA2 - ACSTA register. Accessed through the status/control register of 4-PIO + port 1-B. Returns the "command received" status byte. + + 0xA4 - CDSTA register. Accessed through the status/control register of 4-PIO + port 2-A. Returns the "command data available" status byte. + + 0xA6 - ADSTA register. Accessed through the status/control register of 4-PIO + port 2-B. Returns the "available to write" status byte. + +---------------------------------------------------------------------------------------*/ +static int32 hdReturnReady(const int32 port, const int32 io, const int32 data) +{ + return(0x80); // always indicate ready + +// output operations have no effect +} + +/*------------------------------------------------------------ + hdCstat (0xA1) CSTAT register. Accessed through the + data register of 4-PIO port 1-A. + + Comments: Returns error code byte of the most recent + operation. Reading this byte also clears + the CRDY bit, but this isn't actually done + in the emulation since we're always ready. +-------------------------------------------------------------*/ +static int32 hdCstat(const int32 port, const int32 io, const int32 data) +{ + return(cstat); + +// output operations have no effect +} + +/*------------------------------------------------------------ + hdAcmd (0xA3) ACMD register. Accessed through the + data register of 4-PIO port 1-B. + + Comments: The high byte of a command is written to + this register and initiates the command. + The low byte of a command is assumed to + have already been written and stored in + cmdLowByte; +-------------------------------------------------------------*/ +static int32 hdAcmd(const int32 port, const int32 io, const int32 data) +{ + uint32 command; // command field from command msb + uint32 unit; // unit number from command msb + uint32 buffer; // buffer number from command msb + +// if not an OUT command, exit + + if (io != IO_OUT) + return(0); + +// extract command and possible unit and buffer fields. + + cstat = 0; // assume command success + command = (data >> CMD_SHIFT) & CMD_MASK; + unit = (data >> UNIT_SHIFT) & UNIT_MASK; + buffer = data & BUFFER_MASK; + +// SEEK command. Updated selectedTrack. + + if (command == CMD_SEEK) { + selectedTrack = cmdLowByte + ((data & TRACK_MASKH) << TRACK_SHIFTH); + if (selectedTrack >= HDSK_NUM_TRACKS) + selectedTrack = HDSK_NUM_TRACKS-1; + } + +// READ, READ UNFORMATTED or WRITE SECTOR command. + + else if ((command==CMD_WRITE_SEC) || (command==CMD_READ_SEC) || (command==CMD_READ_UNFMT)) { + selectedHead = (cmdLowByte >> HEAD_SHIFT) & HEAD_MASK; + selectedDisk = (selectedHead >> 1) + unit * 2 ; + selectedSector = cmdLowByte & SECTOR_MASK; + selectedBuffer = buffer; + if (mhdsk_dev.units[selectedDisk].fileref == NULL) // make sure a file is attached + cstat = CSTAT_NOT_READY; + else { + if (command == CMD_WRITE_SEC) + doWrite(); + else + doRead(); + } + } + +// READ or WRITE BUFFER command. Initiates reading/loading specified buffer. + + else if ((command == CMD_WRITE_BUF) || (command == CMD_READ_BUF)) { + selectedBuffer = buffer; + maxBufferIdx = cmdLowByte; + if (maxBufferIdx == 0) + maxBufferIdx = 256; + bufferIdx = 0; + } + +// READ STATUS command (read IV byte) + + else if (command == CMD_READ_STATUS) { + } + +// SET IV byte command + + else if (command == CMD_SET_IV_BYTE) { + } + +// FORMAT command + + else if (command == CMD_FORMAT) { + } + +// INITIALIZE command + + else if (command == CMD_INITIALIZE) { + } + + return(0); +} + +/*------------------------------------------------------------ + hdCdata (0xA5) Cdata register. Accessed through the + data register of 4-PIO port 1-B. + + Comments: Returns data from the read buffer +-------------------------------------------------------------*/ +static int32 hdCdata(const int32 port, const int32 io, const int32 data) +{ + if (io == IO_IN) { + if (bufferIdx < maxBufferIdx) + return(diskBuf[selectedBuffer][bufferIdx++]); + } + return(0); + +// output operations have no effect +} + + +/*------------------------------------------------------------ + hdAdata (0xA7) ADATA register. Accessed through the + data register of 4-PIO port 2-B. + + Comments: Accepts data into the current buffer + and is also the low byte of a command. +-------------------------------------------------------------*/ +static int32 hdAdata(const int32 port, const int32 io, const int32 data) +{ + if (io == IO_OUT) { + cmdLowByte = data & 0xff; + if (bufferIdx < maxBufferIdx) + diskBuf[selectedBuffer][bufferIdx++] = data; + } + return(0); +} + +/*-- doRead ------------------------------------------------- + Performs read from MITS Hard Disk image file + + Params: nothing + Uses: selectedTrack, selectedHead, selectedSector + selectedDisk, diskBuf[], mhdsk_dev + Returns: nothing (updates cstat directly) + Comments: +-------------------------------------------------------------*/ +static void doRead(void) +{ + UNIT *uptr; + uint32 fileOffset; + + uptr = mhdsk_dev.units + selectedDisk; + fileOffset = HDSK_CYLINDER_SIZE * selectedTrack + + HDSK_TRACK_SIZE * (selectedHead & 0x01) + + HDSK_SECTOR_SIZE * selectedSector; + if (sim_fseek(uptr->fileref, fileOffset, SEEK_SET)) + cstat = CSTAT_NOT_READY; /* seek error */ + else if (sim_fread(diskBuf[selectedBuffer], 1, HDSK_SECTOR_SIZE, uptr->fileref) != HDSK_SECTOR_SIZE) + cstat = CSTAT_NOT_READY; /* write error */ +} + + +/*-- doWrite ------------------------------------------------ + Performs write to MITS Hard Disk image file + + Params: none + Uses: selectedTrack, selectedHead, selectedSector + selectedDisk, diskBuf[], mhdsk_dev + Returns: nothing (updates cstat directly) + Comments: +-------------------------------------------------------------*/ +static void doWrite(void) +{ + UNIT *uptr; + uint32 fileOffset; + + uptr = mhdsk_dev.units + selectedDisk; + if (((uptr->flags) & UNIT_DSK_WLK) == 0) { /* write enabled */ + fileOffset = HDSK_CYLINDER_SIZE * selectedTrack + + HDSK_TRACK_SIZE * (selectedHead & 0x01) + + HDSK_SECTOR_SIZE * selectedSector; + if (sim_fseek(uptr->fileref, fileOffset, SEEK_SET)) + cstat = CSTAT_NOT_READY; /* seek error */ + else if (sim_fwrite(diskBuf[selectedBuffer], 1, HDSK_SECTOR_SIZE, uptr->fileref) != HDSK_SECTOR_SIZE) + cstat = CSTAT_NOT_READY; /* write error */ + } + else + cstat = CSTAT_WRITE_PROTECT; +} \ No newline at end of file diff --git a/AltairZ80/altairz80_sys.c b/AltairZ80/altairz80_sys.c index ced454b6..8319a838 100644 --- a/AltairZ80/altairz80_sys.c +++ b/AltairZ80/altairz80_sys.c @@ -1,6 +1,6 @@ /* altairz80_sys.c: MITS Altair system interface - Copyright (c) 2002-2013, Peter Schorn + Copyright (c) 2002-2014, Peter Schorn Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -25,6 +25,8 @@ Based on work by Charles E Owen (c) 1997 Disassembler from Marat Fayzullin ((c) 1995, 1996, 1997 - Commercial use prohibited) + + 03/27/14 -- MWD Add MITS Hard Disk device (mhdsk_dev) */ #include @@ -40,6 +42,7 @@ extern DEVICE simh_device; extern DEVICE ptr_dev; extern DEVICE ptp_dev; extern DEVICE dsk_dev; +extern DEVICE mhdsk_dev; extern DEVICE hdsk_dev; extern DEVICE net_dev; @@ -100,7 +103,7 @@ int32 sim_emax = SIM_EMAX; DEVICE *sim_devices[] = { /* AltairZ80 Devices */ &cpu_dev, &sio_dev, &simh_device, &ptr_dev, &ptp_dev, &dsk_dev, - &hdsk_dev, &net_dev, + &mhdsk_dev, &hdsk_dev, &net_dev, /* Advanced Digital (ADC) Devices */ &adcs6_dev, &hdc1001_dev,