1
0
mirror of https://github.com/open-simh/simh.git synced 2026-04-25 20:01:33 +00:00

Notes For V3.5-0

The source set has been extensively overhauled.  For correct
viewing, set Visual C++ or Emacs to have tab stops every 4
characters.

1. New Features in 3.4-1

1.1 All Ethernet devices

- Added Windows user-defined adapter names (from Timothe Litt)

1.2 Interdata, SDS, HP, PDP-8, PDP-18b terminal multiplexors

- Added support for SET <unit>n DISCONNECT

1.3 VAX

- Added latent QDSS support
- Revised autoconfigure to handle QDSS

1.4 PDP-11

- Revised autoconfigure to handle more casees

2. Bugs Fixed in 3.4-1

2.1 SCP and libraries

- Trim trailing spaces on all input (for example, attach file names)
- Fixed sim_sock spurious SIGPIPE error in Unix/Linux
- Fixed sim_tape misallocation of TPC map array for 64b simulators

2.2 1401

- Fixed bug, CPU reset was clearing SSB through SSG

2.3 PDP-11

- Fixed bug in VH vector display routine
- Fixed XU runt packet processing (found by Tim Chapman)

2.4 Interdata

- Fixed bug in SHOW PAS CONN/STATS
- Fixed potential integer overflow exception in divide

2.5 SDS

- Fixed bug in SHOW MUX CONN/STATS

2.6 HP

- Fixed bug in SHOW MUX CONN/STATS

2.7 PDP-8

- Fixed bug in SHOW TTIX CONN/STATS
- Fixed bug in SET/SHOW TTOXn LOG

2.8 PDP-18b

- Fixed bug in SHOW TTIX CONN/STATS
- Fixed bug in SET/SHOW TTOXn LOG

2.9 Nova, Eclipse

- Fixed potential integer overflow exception in divide
This commit is contained in:
Bob Supnik
2005-09-09 18:09:00 -07:00
committed by Mark Pizzolato
parent ec60bbf329
commit b7c1eae41f
257 changed files with 107140 additions and 97195 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,80 +1,80 @@
/* altairz80_defs.h: MITS Altair simulator definitions
/* altairz80_defs.h: MITS Altair simulator definitions
Copyright (c) 2002-2005, Peter Schorn
Copyright (c) 2002-2005, 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:
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 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.
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.
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.
Based on work by Charles E Owen (c) 1997
Based on work by Charles E Owen (c) 1997
*/
#include "sim_defs.h" /* simulator definitions */
#include "sim_defs.h" /* simulator definitions */
#define MAXMEMSIZE 65536 /* maximum memory size */
#define ADDRMASK (MAXMEMSIZE - 1) /* address mask */
#define bootrom_size 256 /* size of boot rom */
#define MAXBANKS 8 /* max number of memory banks */
#define MAXBANKSLOG2 3 /* log2 of MAXBANKS */
#define BANKMASK (MAXBANKS-1) /* bank mask */
#define MEMSIZE (cpu_unit.capac) /* actual memory size */
#define KB 1024 /* kilo byte */
#define defaultROMLow 0xff00 /* default for lowest address of ROM */
#define defaultROMHigh 0xffff /* default for highest address of ROM */
#define MAXMEMSIZE 65536 /* maximum memory size */
#define ADDRMASK (MAXMEMSIZE - 1) /* address mask */
#define bootrom_size 256 /* size of boot rom */
#define MAXBANKS 8 /* max number of memory banks */
#define MAXBANKSLOG2 3 /* log2 of MAXBANKS */
#define BANKMASK (MAXBANKS-1) /* bank mask */
#define MEMSIZE (cpu_unit.capac) /* actual memory size */
#define KB 1024 /* kilo byte */
#define defaultROMLow 0xff00 /* default for lowest address of ROM */
#define defaultROMHigh 0xffff /* default for highest address of ROM */
#define NUM_OF_DSK 8 /* NUM_OF_DSK must be power of two */
#define LDAInstruction 0x3e /* op-code for LD A,<8-bit value> instruction */
#define unitNoOffset1 0x37 /* LD A,<unitno> */
#define unitNoOffset2 0xb4 /* LD a,80h | <unitno> */
#define NUM_OF_DSK 8 /* NUM_OF_DSK must be power of two */
#define LDAInstruction 0x3e /* op-code for LD A,<8-bit value> instruction */
#define unitNoOffset1 0x37 /* LD A,<unitno> */
#define unitNoOffset2 0xb4 /* LD a,80h | <unitno> */
#define UNIT_V_OPSTOP (UNIT_V_UF+0) /* stop on invalid operation */
#define UNIT_OPSTOP (1 << UNIT_V_OPSTOP)
#define UNIT_V_CHIP (UNIT_V_UF+1) /* 8080 or Z80 CPU */
#define UNIT_CHIP (1 << UNIT_V_CHIP)
#define UNIT_V_MSIZE (UNIT_V_UF+2) /* memory size */
#define UNIT_MSIZE (1 << UNIT_V_MSIZE)
#define UNIT_V_BANKED (UNIT_V_UF+3) /* banked memory is used */
#define UNIT_BANKED (1 << UNIT_V_BANKED)
#define UNIT_V_ROM (UNIT_V_UF+4) /* ROM exists */
#define UNIT_ROM (1 << UNIT_V_ROM)
#define UNIT_V_ALTAIRROM (UNIT_V_UF+5) /* ALTAIR ROM exists */
#define UNIT_ALTAIRROM (1 << UNIT_V_ALTAIRROM)
#define UNIT_V_WARNROM (UNIT_V_UF+6) /* warn if ROM is written to */
#define UNIT_WARNROM (1 << UNIT_V_WARNROM)
#define UNIT_V_OPSTOP (UNIT_V_UF+0) /* stop on invalid operation */
#define UNIT_OPSTOP (1 << UNIT_V_OPSTOP)
#define UNIT_V_CHIP (UNIT_V_UF+1) /* 8080 or Z80 CPU */
#define UNIT_CHIP (1 << UNIT_V_CHIP)
#define UNIT_V_MSIZE (UNIT_V_UF+2) /* memory size */
#define UNIT_MSIZE (1 << UNIT_V_MSIZE)
#define UNIT_V_BANKED (UNIT_V_UF+3) /* banked memory is used */
#define UNIT_BANKED (1 << UNIT_V_BANKED)
#define UNIT_V_ROM (UNIT_V_UF+4) /* ROM exists */
#define UNIT_ROM (1 << UNIT_V_ROM)
#define UNIT_V_ALTAIRROM (UNIT_V_UF+5) /* ALTAIR ROM exists */
#define UNIT_ALTAIRROM (1 << UNIT_V_ALTAIRROM)
#define UNIT_V_WARNROM (UNIT_V_UF+6) /* warn if ROM is written to */
#define UNIT_WARNROM (1 << UNIT_V_WARNROM)
#define AddressFormat "[%04xh]"
#define PCformat "\n" AddressFormat " "
#define message1(p1) \
sprintf(messageBuffer,PCformat p1,PCX); printMessage()
#define message2(p1,p2) \
sprintf(messageBuffer,PCformat p1,PCX,p2); printMessage()
#define message3(p1,p2,p3) \
sprintf(messageBuffer,PCformat p1,PCX,p2,p3); printMessage()
#define message4(p1,p2,p3,p4) \
sprintf(messageBuffer,PCformat p1,PCX,p2,p3,p4); printMessage()
#define message5(p1,p2,p3,p4,p5) \
sprintf(messageBuffer,PCformat p1,PCX,p2,p3,p4,p5); printMessage()
#define message6(p1,p2,p3,p4,p5,p6) \
sprintf(messageBuffer,PCformat p1,PCX,p2,p3,p4,p5,p6); printMessage()
#define AddressFormat "[%04xh]"
#define PCformat "\n" AddressFormat " "
#define message1(p1) \
sprintf(messageBuffer,PCformat p1,PCX); printMessage()
#define message2(p1,p2) \
sprintf(messageBuffer,PCformat p1,PCX,p2); printMessage()
#define message3(p1,p2,p3) \
sprintf(messageBuffer,PCformat p1,PCX,p2,p3); printMessage()
#define message4(p1,p2,p3,p4) \
sprintf(messageBuffer,PCformat p1,PCX,p2,p3,p4); printMessage()
#define message5(p1,p2,p3,p4,p5) \
sprintf(messageBuffer,PCformat p1,PCX,p2,p3,p4,p5); printMessage()
#define message6(p1,p2,p3,p4,p5,p6) \
sprintf(messageBuffer,PCformat p1,PCX,p2,p3,p4,p5,p6); printMessage()
/* The Default is to use "inline". */
/* The default is to use "inline". */
#if defined(NO_INLINE)
#define INLINE
#else

View File

@@ -1,130 +1,130 @@
/* altairz80_dsk.c: MITS Altair 88-DISK Simulator
/* altairz80_dsk.c: MITS Altair 88-DISK Simulator
Copyright (c) 2002-2005, Peter Schorn
Copyright (c) 2002-2005, 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:
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 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.
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.
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.
Based on work by Charles E Owen (c) 1997
Based on work by Charles E Owen (c) 1997
The 88_DISK is a 8-inch floppy controller which can control up
to 16 daisy-chained Pertec FD-400 hard-sectored floppy drives.
Each diskette has physically 77 tracks of 32 137-byte sectors
each.
The 88_DISK is a 8-inch floppy controller which can control up
to 16 daisy-chained Pertec FD-400 hard-sectored floppy drives.
Each diskette has physically 77 tracks of 32 137-byte sectors
each.
The controller is interfaced to the CPU by use of 3 I/O addresses,
standardly, these are device numbers 10, 11, and 12 (octal).
The controller is interfaced to the CPU by use of 3 I/O addresses,
standardly, these are device numbers 10, 11, and 12 (octal).
Address Mode Function
------- ---- --------
Address Mode Function
------- ---- --------
10 Out Selects and enables Controller and Drive
10 In Indicates status of Drive and Controller
11 Out Controls Disk Function
11 In Indicates current sector position of disk
12 Out Write data
12 In Read data
10 Out Selects and enables Controller and Drive
10 In Indicates status of Drive and Controller
11 Out Controls Disk Function
11 In Indicates current sector position of disk
12 Out Write data
12 In Read data
Drive Select Out (Device 10 OUT):
Drive Select Out (Device 10 OUT):
+---+---+---+---+---+---+---+---+
| C | X | X | X | Device |
+---+---+---+---+---+---+---+---+
+---+---+---+---+---+---+---+---+
| C | X | X | X | Device |
+---+---+---+---+---+---+---+---+
C = If this bit is 1, the disk controller selected by 'device' is
cleared. If the bit is zero, 'device' is selected as the
device being controlled by subsequent I/O operations.
X = not used
Device = value zero thru 15, selects drive to be controlled.
C = If this bit is 1, the disk controller selected by 'device' is
cleared. If the bit is zero, 'device' is selected as the
device being controlled by subsequent I/O operations.
X = not used
Device = value zero thru 15, selects drive to be controlled.
Drive Status In (Device 10 IN):
Drive Status In (Device 10 IN):
+---+---+---+---+---+---+---+---+
| R | Z | I | X | X | H | M | W |
+---+---+---+---+---+---+---+---+
+---+---+---+---+---+---+---+---+
| R | Z | I | X | X | H | M | W |
+---+---+---+---+---+---+---+---+
W - When 0, write circuit ready to write another byte.
M - When 0, head movement is allowed
H - When 0, indicates head is loaded for read/write
X - not used (will be 0)
I - When 0, indicates interrupts enabled (not used by this simulator)
Z - When 0, indicates head is on track 0
R - When 0, indicates that read circuit has new byte to read
W - When 0, write circuit ready to write another byte.
M - When 0, head movement is allowed
H - When 0, indicates head is loaded for read/write
X - not used (will be 0)
I - When 0, indicates interrupts enabled (not used by this simulator)
Z - When 0, indicates head is on track 0
R - When 0, indicates that read circuit has new byte to read
Drive Control (Device 11 OUT):
Drive Control (Device 11 OUT):
+---+---+---+---+---+---+---+---+
| W | C | D | E | U | H | O | I |
+---+---+---+---+---+---+---+---+
+---+---+---+---+---+---+---+---+
| W | C | D | E | U | H | O | I |
+---+---+---+---+---+---+---+---+
I - When 1, steps head IN one track
O - When 1, steps head OUT one track
H - When 1, loads head to drive surface
U - When 1, unloads head
E - Enables interrupts (ignored by this simulator)
D - Disables interrupts (ignored by this simulator)
C - When 1 lowers head current (ignored by this simulator)
W - When 1, starts Write Enable sequence: W bit on device 10
(see above) will go 1 and data will be read from port 12
until 137 bytes have been read by the controller from
that port. The W bit will go off then, and the sector data
will be written to disk. Before you do this, you must have
stepped the track to the desired number, and waited until
the right sector number is presented on device 11 IN, then
set this bit.
I - When 1, steps head IN one track
O - When 1, steps head OUT one track
H - When 1, loads head to drive surface
U - When 1, unloads head
E - Enables interrupts (ignored by this simulator)
D - Disables interrupts (ignored by this simulator)
C - When 1 lowers head current (ignored by this simulator)
W - When 1, starts Write Enable sequence: W bit on device 10
(see above) will go 1 and data will be read from port 12
until 137 bytes have been read by the controller from
that port. The W bit will go off then, and the sector data
will be written to disk. Before you do this, you must have
stepped the track to the desired number, and waited until
the right sector number is presented on device 11 IN, then
set this bit.
Sector Position (Device 11 IN):
Sector Position (Device 11 IN):
As the sectors pass by the read head, they are counted and the
number of the current one is available in this register.
As the sectors pass by the read head, they are counted and the
number of the current one is available in this register.
+---+---+---+---+---+---+---+---+
| X | X | Sector Number | T |
+---+---+---+---+---+---+---+---+
+---+---+---+---+---+---+---+---+
| X | X | Sector Number | T |
+---+---+---+---+---+---+---+---+
X = Not used
Sector number = binary of the sector number currently under the
head, 0-31.
T = Sector True, is a 1 when the sector is positioned to read or
write.
X = Not used
Sector number = binary of the sector number currently under the
head, 0-31.
T = Sector True, is a 1 when the sector is positioned to read or
write.
*/
#include "altairz80_defs.h"
#define UNIT_V_DSKWLK (UNIT_V_UF + 0) /* write locked */
#define UNIT_DSKWLK (1 << UNIT_V_DSKWLK)
#define UNIT_V_DSK_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */
#define UNIT_DSK_VERBOSE (1 << UNIT_V_DSK_VERBOSE)
#define DSK_SECTSIZE 137 /* size of sector */
#define DSK_SECT 32 /* sectors per track */
#define MAX_TRACKS 254 /* number of tracks,
original Altair has 77 tracks only */
#define DSK_TRACSIZE (DSK_SECTSIZE * DSK_SECT)
#define MAX_DSK_SIZE (DSK_TRACSIZE * MAX_TRACKS)
#define TRACE_IN_OUT 1
#define TRACE_READ_WRITE 2
#define TRACE_SECTOR_STUCK 4
#define TRACE_TRACK_STUCK 8
#define NUM_OF_DSK_MASK (NUM_OF_DSK - 1)
#define UNIT_V_DSKWLK (UNIT_V_UF + 0) /* write locked */
#define UNIT_DSKWLK (1 << UNIT_V_DSKWLK)
#define UNIT_V_DSK_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */
#define UNIT_DSK_VERBOSE (1 << UNIT_V_DSK_VERBOSE)
#define DSK_SECTSIZE 137 /* size of sector */
#define DSK_SECT 32 /* sectors per track */
#define MAX_TRACKS 254 /* number of tracks,
original Altair has 77 tracks only */
#define DSK_TRACSIZE (DSK_SECTSIZE * DSK_SECT)
#define MAX_DSK_SIZE (DSK_TRACSIZE * MAX_TRACKS)
#define TRACE_IN_OUT 1
#define TRACE_READ_WRITE 2
#define TRACE_SECTOR_STUCK 4
#define TRACE_TRACK_STUCK 8
#define NUM_OF_DSK_MASK (NUM_OF_DSK - 1)
int32 dsk10(const int32 port, const int32 io, const int32 data);
int32 dsk11(const int32 port, const int32 io, const int32 data);
@@ -149,140 +149,144 @@ extern UNIT cpu_unit;
/* global data on status */
static int32 current_disk = NUM_OF_DSK; /* currently selected drive (values are 0 .. NUM_OF_DSK)
current_disk < NUM_OF_DSK implies that the corresponding disk is attached to a file */
static int32 current_track [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0};
static int32 current_sector [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0};
static int32 current_byte [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0};
static int32 current_flag [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0};
static uint8 tracks [NUM_OF_DSK] = { MAX_TRACKS, MAX_TRACKS, MAX_TRACKS, MAX_TRACKS,
MAX_TRACKS, MAX_TRACKS, MAX_TRACKS, MAX_TRACKS };
static int32 trace_flag = 0;
static int32 in9_count = 0;
static int32 in9_message = FALSE;
static int32 dirty = FALSE; /* TRUE when buffer has unwritten data in it */
static int32 warnLevelDSK = 3;
static int32 warnLock [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0};
static int32 warnAttached [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0};
static int32 warnDSK10 = 0;
static int32 warnDSK11 = 0;
static int32 warnDSK12 = 0;
static int8 dskbuf[DSK_SECTSIZE]; /* data Buffer */
static int32 current_disk = NUM_OF_DSK; /* currently selected drive (values are 0 .. NUM_OF_DSK)
current_disk < NUM_OF_DSK implies that the corresponding disk is attached to a file */
static int32 current_track [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0};
static int32 current_sector [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0};
static int32 current_byte [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0};
static int32 current_flag [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0};
static uint8 tracks [NUM_OF_DSK] = { MAX_TRACKS, MAX_TRACKS, MAX_TRACKS, MAX_TRACKS,
MAX_TRACKS, MAX_TRACKS, MAX_TRACKS, MAX_TRACKS };
static int32 trace_flag = 0;
static int32 in9_count = 0;
static int32 in9_message = FALSE;
static int32 dirty = FALSE; /* TRUE when buffer has unwritten data in it */
static int32 warnLevelDSK = 3;
static int32 warnLock [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0};
static int32 warnAttached [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0};
static int32 warnDSK10 = 0;
static int32 warnDSK11 = 0;
static int32 warnDSK12 = 0;
static int8 dskbuf[DSK_SECTSIZE]; /* data Buffer */
/* Altair MITS modified BOOT EPROM, fits in upper 256 byte of memory */
int32 bootrom[bootrom_size] = {
0xf3, 0x06, 0x80, 0x3e, 0x0e, 0xd3, 0xfe, 0x05, /* ff00-ff07 */
0xc2, 0x05, 0xff, 0x3e, 0x16, 0xd3, 0xfe, 0x3e, /* ff08-ff0f */
0x12, 0xd3, 0xfe, 0xdb, 0xfe, 0xb7, 0xca, 0x20, /* ff10-ff17 */
0xff, 0x3e, 0x0c, 0xd3, 0xfe, 0xaf, 0xd3, 0xfe, /* ff18-ff1f */
0x21, 0x00, 0x5c, 0x11, 0x33, 0xff, 0x0e, 0x88, /* ff20-ff27 */
0x1a, 0x77, 0x13, 0x23, 0x0d, 0xc2, 0x28, 0xff, /* ff28-ff2f */
0xc3, 0x00, 0x5c, 0x31, 0x21, 0x5d, 0x3e, 0x00, /* ff30-ff37 */
0xd3, 0x08, 0x3e, 0x04, 0xd3, 0x09, 0xc3, 0x19, /* ff38-ff3f */
0x5c, 0xdb, 0x08, 0xe6, 0x02, 0xc2, 0x0e, 0x5c, /* ff40-ff47 */
0x3e, 0x02, 0xd3, 0x09, 0xdb, 0x08, 0xe6, 0x40, /* ff48-ff4f */
0xc2, 0x0e, 0x5c, 0x11, 0x00, 0x00, 0x06, 0x08, /* ff50-ff57 */
0xc5, 0xd5, 0x11, 0x86, 0x80, 0x21, 0x88, 0x5c, /* ff58-ff5f */
0xdb, 0x09, 0x1f, 0xda, 0x2d, 0x5c, 0xe6, 0x1f, /* ff60-ff67 */
0xb8, 0xc2, 0x2d, 0x5c, 0xdb, 0x08, 0xb7, 0xfa, /* ff68-ff6f */
0x39, 0x5c, 0xdb, 0x0a, 0x77, 0x23, 0x1d, 0xc2, /* ff70-ff77 */
0x39, 0x5c, 0xd1, 0x21, 0x8b, 0x5c, 0x06, 0x80, /* ff78-ff7f */
0x7e, 0x12, 0x23, 0x13, 0x05, 0xc2, 0x4d, 0x5c, /* ff80-ff87 */
0xc1, 0x21, 0x00, 0x5c, 0x7a, 0xbc, 0xc2, 0x60, /* ff88-ff8f */
0x5c, 0x7b, 0xbd, 0xd2, 0x80, 0x5c, 0x04, 0x04, /* ff90-ff97 */
0x78, 0xfe, 0x20, 0xda, 0x25, 0x5c, 0x06, 0x01, /* ff98-ff9f */
0xca, 0x25, 0x5c, 0xdb, 0x08, 0xe6, 0x02, 0xc2, /* ffa0-ffa7 */
0x70, 0x5c, 0x3e, 0x01, 0xd3, 0x09, 0x06, 0x00, /* ffa8-ffaf */
0xc3, 0x25, 0x5c, 0x3e, 0x80, 0xd3, 0x08, 0xfb, /* ffb0-ffb7 */
0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffb8-ffbf */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffc0-ffc7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffc8-ffcf */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffd0-ffd7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffd8-ffdf */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffe0-ffe7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffe8-ffef */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* fff0-fff7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* fff8-ffff */
0xf3, 0x06, 0x80, 0x3e, 0x0e, 0xd3, 0xfe, 0x05, /* ff00-ff07 */
0xc2, 0x05, 0xff, 0x3e, 0x16, 0xd3, 0xfe, 0x3e, /* ff08-ff0f */
0x12, 0xd3, 0xfe, 0xdb, 0xfe, 0xb7, 0xca, 0x20, /* ff10-ff17 */
0xff, 0x3e, 0x0c, 0xd3, 0xfe, 0xaf, 0xd3, 0xfe, /* ff18-ff1f */
0x21, 0x00, 0x5c, 0x11, 0x33, 0xff, 0x0e, 0x88, /* ff20-ff27 */
0x1a, 0x77, 0x13, 0x23, 0x0d, 0xc2, 0x28, 0xff, /* ff28-ff2f */
0xc3, 0x00, 0x5c, 0x31, 0x21, 0x5d, 0x3e, 0x00, /* ff30-ff37 */
0xd3, 0x08, 0x3e, 0x04, 0xd3, 0x09, 0xc3, 0x19, /* ff38-ff3f */
0x5c, 0xdb, 0x08, 0xe6, 0x02, 0xc2, 0x0e, 0x5c, /* ff40-ff47 */
0x3e, 0x02, 0xd3, 0x09, 0xdb, 0x08, 0xe6, 0x40, /* ff48-ff4f */
0xc2, 0x0e, 0x5c, 0x11, 0x00, 0x00, 0x06, 0x08, /* ff50-ff57 */
0xc5, 0xd5, 0x11, 0x86, 0x80, 0x21, 0x88, 0x5c, /* ff58-ff5f */
0xdb, 0x09, 0x1f, 0xda, 0x2d, 0x5c, 0xe6, 0x1f, /* ff60-ff67 */
0xb8, 0xc2, 0x2d, 0x5c, 0xdb, 0x08, 0xb7, 0xfa, /* ff68-ff6f */
0x39, 0x5c, 0xdb, 0x0a, 0x77, 0x23, 0x1d, 0xc2, /* ff70-ff77 */
0x39, 0x5c, 0xd1, 0x21, 0x8b, 0x5c, 0x06, 0x80, /* ff78-ff7f */
0x7e, 0x12, 0x23, 0x13, 0x05, 0xc2, 0x4d, 0x5c, /* ff80-ff87 */
0xc1, 0x21, 0x00, 0x5c, 0x7a, 0xbc, 0xc2, 0x60, /* ff88-ff8f */
0x5c, 0x7b, 0xbd, 0xd2, 0x80, 0x5c, 0x04, 0x04, /* ff90-ff97 */
0x78, 0xfe, 0x20, 0xda, 0x25, 0x5c, 0x06, 0x01, /* ff98-ff9f */
0xca, 0x25, 0x5c, 0xdb, 0x08, 0xe6, 0x02, 0xc2, /* ffa0-ffa7 */
0x70, 0x5c, 0x3e, 0x01, 0xd3, 0x09, 0x06, 0x00, /* ffa8-ffaf */
0xc3, 0x25, 0x5c, 0x3e, 0x80, 0xd3, 0x08, 0xfb, /* ffb0-ffb7 */
0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffb8-ffbf */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffc0-ffc7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffc8-ffcf */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffd0-ffd7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffd8-ffdf */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffe0-ffe7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffe8-ffef */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* fff0-fff7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* fff8-ffff */
};
/* 88DSK Standard I/O Data Structures */
static UNIT dsk_unit[] = {
{ UDATA (&dsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
{ UDATA (&dsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
{ UDATA (&dsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
{ UDATA (&dsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
{ UDATA (&dsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
{ UDATA (&dsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
{ UDATA (&dsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
{ UDATA (&dsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) } };
{ UDATA (&dsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
{ UDATA (&dsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
{ UDATA (&dsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
{ UDATA (&dsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
{ UDATA (&dsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
{ UDATA (&dsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
{ UDATA (&dsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
{ UDATA (&dsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }
};
static REG dsk_reg[] = {
{ DRDATA (DISK, current_disk, 4) },
{ BRDATA (CURTRACK, current_track, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO },
{ BRDATA (CURSECTOR, current_sector, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO },
{ BRDATA (CURBYTE, current_byte, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO },
{ BRDATA (CURFLAG, current_flag, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO },
{ BRDATA (TRACKS, tracks, 10, 8, NUM_OF_DSK), REG_CIRC },
{ ORDATA (TRACE, trace_flag, 8) },
{ DRDATA (IN9COUNT, in9_count, 4), REG_RO },
{ DRDATA (IN9MESSAGE, in9_message, 4), REG_RO },
{ DRDATA (DIRTY, dirty, 4), REG_RO },
{ DRDATA (DSKWL, warnLevelDSK, 32) },
{ BRDATA (WARNLOCK, warnLock, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO },
{ BRDATA (WARNATTACHED, warnAttached, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO },
{ DRDATA (WARNDSK10, warnDSK10, 4), REG_RO },
{ DRDATA (WARNDSK11, warnDSK11, 4), REG_RO },
{ DRDATA (WARNDSK12, warnDSK12, 4), REG_RO },
{ BRDATA (DISKBUFFER, dskbuf, 10, 8, DSK_SECTSIZE), REG_CIRC + REG_RO },
{ NULL } };
{ DRDATA (DISK, current_disk, 4) },
{ BRDATA (CURTRACK, current_track, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO },
{ BRDATA (CURSECTOR, current_sector, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO },
{ BRDATA (CURBYTE, current_byte, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO },
{ BRDATA (CURFLAG, current_flag, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO },
{ BRDATA (TRACKS, tracks, 10, 8, NUM_OF_DSK), REG_CIRC },
{ ORDATA (TRACE, trace_flag, 8) },
{ DRDATA (IN9COUNT, in9_count, 4), REG_RO },
{ DRDATA (IN9MESSAGE, in9_message, 4), REG_RO },
{ DRDATA (DIRTY, dirty, 4), REG_RO },
{ DRDATA (DSKWL, warnLevelDSK, 32) },
{ BRDATA (WARNLOCK, warnLock, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO },
{ BRDATA (WARNATTACHED, warnAttached, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO },
{ DRDATA (WARNDSK10, warnDSK10, 4), REG_RO },
{ DRDATA (WARNDSK11, warnDSK11, 4), REG_RO },
{ DRDATA (WARNDSK12, warnDSK12, 4), REG_RO },
{ BRDATA (DISKBUFFER, dskbuf, 10, 8, DSK_SECTSIZE), REG_CIRC + REG_RO },
{ NULL }
};
static MTAB dsk_mod[] = {
{ UNIT_DSKWLK, 0, "write enabled", "WRITEENABLED", NULL },
{ UNIT_DSKWLK, UNIT_DSKWLK, "write locked", "LOCKED", NULL },
/* quiet, no warning messages */
{ UNIT_DSK_VERBOSE, 0, "QUIET", "QUIET", NULL },
/* verbose, show warning messages */
{ UNIT_DSK_VERBOSE, UNIT_DSK_VERBOSE, "VERBOSE", "VERBOSE", &dsk_set_verbose },
{ 0 } };
{ UNIT_DSKWLK, 0, "write enabled", "WRITEENABLED", NULL },
{ UNIT_DSKWLK, UNIT_DSKWLK, "write locked", "LOCKED", NULL },
/* quiet, no warning messages */
{ UNIT_DSK_VERBOSE, 0, "QUIET", "QUIET", NULL },
/* verbose, show warning messages */
{ UNIT_DSK_VERBOSE, UNIT_DSK_VERBOSE, "VERBOSE", "VERBOSE", &dsk_set_verbose },
{ 0 }
};
DEVICE dsk_dev = {
"DSK", dsk_unit, dsk_reg, dsk_mod,
8, 10, 31, 1, 8, 8,
NULL, NULL, &dsk_reset,
&dsk_boot, NULL, NULL,
NULL, 0, 0,
NULL, NULL, NULL };
"DSK", dsk_unit, dsk_reg, dsk_mod,
8, 10, 31, 1, 8, 8,
NULL, NULL, &dsk_reset,
&dsk_boot, NULL, NULL,
NULL, 0, 0,
NULL, NULL, NULL
};
static void resetDSKWarningFlags(void) {
int32 i;
for (i = 0; i < NUM_OF_DSK; i++) {
warnLock[i] = 0;
warnAttached[i] = 0;
}
warnDSK10 = 0;
warnDSK11 = 0;
warnDSK12 = 0;
int32 i;
for (i = 0; i < NUM_OF_DSK; i++) {
warnLock[i] = 0;
warnAttached[i] = 0;
}
warnDSK10 = 0;
warnDSK11 = 0;
warnDSK12 = 0;
}
static t_stat dsk_set_verbose(UNIT *uptr, int32 value, char *cptr, void *desc) {
resetDSKWarningFlags();
return SCPE_OK;
resetDSKWarningFlags();
return SCPE_OK;
}
/* returns TRUE iff there exists a disk with VERBOSE */
static int32 hasVerbose(void) {
int32 i;
for (i = 0; i < NUM_OF_DSK; i++) {
if (((dsk_dev.units + i) -> flags) & UNIT_DSK_VERBOSE) {
return TRUE;
}
}
return FALSE;
int32 i;
for (i = 0; i < NUM_OF_DSK; i++) {
if (((dsk_dev.units + i) -> flags) & UNIT_DSK_VERBOSE) {
return TRUE;
}
}
return FALSE;
}
static char* selectInOut(const int32 io) {
return io == 0 ? "IN" : "OUT";
return io == 0 ? "IN" : "OUT";
}
/* service routines to handle simulator functions */
@@ -290,281 +294,280 @@ static char* selectInOut(const int32 io) {
/* service routine - actually gets char & places in buffer */
static t_stat dsk_svc(UNIT *uptr) {
return SCPE_OK;
return SCPE_OK;
}
/* reset routine */
static t_stat dsk_reset(DEVICE *dptr) {
resetDSKWarningFlags();
current_disk = NUM_OF_DSK;
trace_flag = 0;
in9_count = 0;
in9_message = FALSE;
return SCPE_OK;
resetDSKWarningFlags();
current_disk = NUM_OF_DSK;
trace_flag = 0;
in9_count = 0;
in9_message = FALSE;
return SCPE_OK;
}
/* The boot routine modifies the boot ROM in such a way that subsequently
the specified disk is used for boot purposes.
/* The boot routine modifies the boot ROM in such a way that subsequently
the specified disk is used for boot purposes.
*/
static t_stat dsk_boot(int32 unitno, DEVICE *dptr) {
if (cpu_unit.flags & (UNIT_ALTAIRROM | UNIT_BANKED)) {
if (install_bootrom()) {
printf("ALTAIR boot ROM installed.\n");
}
/* check whether we are really modifying an LD A,<> instruction */
if ((bootrom[unitNoOffset1 - 1] == LDAInstruction) && (bootrom[unitNoOffset2 - 1] == LDAInstruction)) {
bootrom[unitNoOffset1] = unitno & 0xff; /* LD A,<unitno> */
bootrom[unitNoOffset2] = 0x80 | (unitno & 0xff); /* LD a,80h | <unitno> */
}
else { /* Attempt to modify non LD A,<> instructions is refused. */
printf("Incorrect boot ROM offsets detected.\n");
return SCPE_IERR;
}
}
saved_PC = defaultROMLow;
return SCPE_OK;
if (cpu_unit.flags & (UNIT_ALTAIRROM | UNIT_BANKED)) {
if (install_bootrom()) {
printf("ALTAIR boot ROM installed.\n");
}
/* check whether we are really modifying an LD A,<> instruction */
if ((bootrom[unitNoOffset1 - 1] == LDAInstruction) && (bootrom[unitNoOffset2 - 1] == LDAInstruction)) {
bootrom[unitNoOffset1] = unitno & 0xff; /* LD A,<unitno> */
bootrom[unitNoOffset2] = 0x80 | (unitno & 0xff); /* LD a,80h | <unitno> */
}
else { /* Attempt to modify non LD A,<> instructions is refused. */
printf("Incorrect boot ROM offsets detected.\n");
return SCPE_IERR;
}
}
saved_PC = defaultROMLow;
return SCPE_OK;
}
/* I/O instruction handlers, called from the CPU module when an
IN or OUT instruction is issued.
/* I/O instruction handlers, called from the CPU module when an
IN or OUT instruction is issued.
Each function is passed an 'io' flag, where 0 means a read from
the port, and 1 means a write to the port. On input, the actual
input is passed as the return value, on output, 'data' is written
to the device.
Each function is passed an 'io' flag, where 0 means a read from
the port, and 1 means a write to the port. On input, the actual
input is passed as the return value, on output, 'data' is written
to the device.
*/
/* Disk Controller Status/Select */
/* IMPORTANT: The status flags read by port 8 IN instruction are
INVERTED, that is, 0 is true and 1 is false. To handle this, the
simulator keeps it's own status flags as 0=false, 1=true; and
returns the COMPLEMENT of the status flags when read. This makes
setting/testing of the flag bits more logical, yet meets the
simulation requirement that they are reversed in hardware.
/* IMPORTANT: The status flags read by port 8 IN instruction are
INVERTED, that is, 0 is true and 1 is false. To handle this, the
simulator keeps it's own status flags as 0=false, 1=true; and
returns the COMPLEMENT of the status flags when read. This makes
setting/testing of the flag bits more logical, yet meets the
simulation requirement that they are reversed in hardware.
*/
int32 dsk10(const int32 port, const int32 io, const int32 data) {
int32 current_disk_flags;
in9_count = 0;
if (io == 0) { /* IN: return flags */
if (current_disk >= NUM_OF_DSK) {
if (hasVerbose() && (warnDSK10 < warnLevelDSK)) {
warnDSK10++;
/*01*/ message1("Attempt of IN 0x08 on unattached disk - ignored.");
}
return 0xff; /* no drive selected - can do nothing */
}
return (~current_flag[current_disk]) & 0xff; /* return the COMPLEMENT! */
}
int32 current_disk_flags;
in9_count = 0;
if (io == 0) { /* IN: return flags */
if (current_disk >= NUM_OF_DSK) {
if (hasVerbose() && (warnDSK10 < warnLevelDSK)) {
warnDSK10++;
/*01*/ message1("Attempt of IN 0x08 on unattached disk - ignored.");
}
return 0xff; /* no drive selected - can do nothing */
}
return (~current_flag[current_disk]) & 0xff; /* return the COMPLEMENT! */
}
/* OUT: Controller set/reset/enable/disable */
if (dirty) { /* implies that current_disk < NUM_OF_DSK */
writebuf();
}
if (trace_flag & TRACE_IN_OUT) {
message2("OUT 0x08: %x", data);
}
current_disk = data & NUM_OF_DSK_MASK; /* 0 <= current_disk < NUM_OF_DSK */
current_disk_flags = (dsk_dev.units + current_disk) -> flags;
if ((current_disk_flags & UNIT_ATT) == 0) { /* nothing attached? */
if ( (current_disk_flags & UNIT_DSK_VERBOSE) && (warnAttached[current_disk] < warnLevelDSK) ) {
warnAttached[current_disk]++;
/* OUT: Controller set/reset/enable/disable */
if (dirty) { /* implies that current_disk < NUM_OF_DSK */
writebuf();
}
if (trace_flag & TRACE_IN_OUT) {
message2("OUT 0x08: %x", data);
}
current_disk = data & NUM_OF_DSK_MASK; /* 0 <= current_disk < NUM_OF_DSK */
current_disk_flags = (dsk_dev.units + current_disk) -> flags;
if ((current_disk_flags & UNIT_ATT) == 0) { /* nothing attached? */
if ( (current_disk_flags & UNIT_DSK_VERBOSE) && (warnAttached[current_disk] < warnLevelDSK) ) {
warnAttached[current_disk]++;
/*02*/message2("Attempt to select unattached DSK%d - ignored.", current_disk);
}
current_disk = NUM_OF_DSK;
}
else {
current_sector[current_disk] = 0xff; /* reset internal counters */
current_byte[current_disk] = 0xff;
current_flag[current_disk] = data & 0x80 ? 0 /* disable drive */ :
(current_track[current_disk] == 0 ? 0x5a /* enable: head move true, track 0 if there */ :
0x1a);/* enable: head move true */
}
return 0; /* ignored since OUT */
}
current_disk = NUM_OF_DSK;
}
else {
current_sector[current_disk] = 0xff; /* reset internal counters */
current_byte[current_disk] = 0xff;
current_flag[current_disk] = data & 0x80 ? 0 /* disable drive */ :
(current_track[current_disk] == 0 ? 0x5a /* enable: head move true, track 0 if there */ :
0x1a); /* enable: head move true */
}
return 0; /* ignored since OUT */
}
/* Disk Drive Status/Functions */
int32 dsk11(const int32 port, const int32 io, const int32 data) {
if (current_disk >= NUM_OF_DSK) {
if (hasVerbose() && (warnDSK11 < warnLevelDSK)) {
warnDSK11++;
/*03*/message2("Attempt of %s 0x09 on unattached disk - ignored.", selectInOut(io));
}
return 0; /* no drive selected - can do nothing */
}
if (current_disk >= NUM_OF_DSK) {
if (hasVerbose() && (warnDSK11 < warnLevelDSK)) {
warnDSK11++;
/*03*/ message2("Attempt of %s 0x09 on unattached disk - ignored.", selectInOut(io));
}
return 0; /* no drive selected - can do nothing */
}
/* now current_disk < NUM_OF_DSK */
if (io == 0) { /* read sector position */
in9_count++;
if ((trace_flag & TRACE_SECTOR_STUCK) && (in9_count > 2 * DSK_SECT) && (!in9_message)) {
in9_message = TRUE;
message2("Looping on sector find %d.", current_disk);
}
if (trace_flag & TRACE_IN_OUT) {
message1("IN 0x09");
}
if (dirty) {/* implies that current_disk < NUM_OF_DSK */
writebuf();
}
if (current_flag[current_disk] & 0x04) { /* head loaded? */
current_sector[current_disk]++;
if (current_sector[current_disk] >= DSK_SECT) {
current_sector[current_disk] = 0;
}
current_byte[current_disk] = 0xff;
return (((current_sector[current_disk] << 1) & 0x3e) /* return 'sector true' bit = 0 (true) */
| 0xc0); /* set on 'unused' bits */
} else {
return 0; /* head not loaded - return 0 */
}
}
/* now current_disk < NUM_OF_DSK */
if (io == 0) { /* read sector position */
in9_count++;
if ((trace_flag & TRACE_SECTOR_STUCK) && (in9_count > 2 * DSK_SECT) && (!in9_message)) {
in9_message = TRUE;
message2("Looping on sector find %d.", current_disk);
}
if (trace_flag & TRACE_IN_OUT) {
message1("IN 0x09");
}
if (dirty) {/* implies that current_disk < NUM_OF_DSK */
writebuf();
}
if (current_flag[current_disk] & 0x04) { /* head loaded? */
current_sector[current_disk]++;
if (current_sector[current_disk] >= DSK_SECT) {
current_sector[current_disk] = 0;
}
current_byte[current_disk] = 0xff;
return (((current_sector[current_disk] << 1) & 0x3e) /* return 'sector true' bit = 0 (true) */
| 0xc0); /* set on 'unused' bits */
} else {
return 0; /* head not loaded - return 0 */
}
}
in9_count = 0;
/* drive functions */
in9_count = 0;
/* drive functions */
if (trace_flag & TRACE_IN_OUT) {
message2("OUT 0x09: %x", data);
}
if (data & 0x01) { /* step head in */
if (trace_flag & TRACE_TRACK_STUCK) {
if (current_track[current_disk] == (tracks[current_disk] - 1)) {
message2("Unnecessary step in for disk %d", current_disk);
}
}
current_track[current_disk]++;
if (current_track[current_disk] > (tracks[current_disk] - 1)) {
current_track[current_disk] = (tracks[current_disk] - 1);
}
if (dirty) { /* implies that current_disk < NUM_OF_DSK */
writebuf();
}
current_sector[current_disk] = 0xff;
current_byte[current_disk] = 0xff;
}
if (trace_flag & TRACE_IN_OUT) {
message2("OUT 0x09: %x", data);
}
if (data & 0x01) { /* step head in */
if (trace_flag & TRACE_TRACK_STUCK) {
if (current_track[current_disk] == (tracks[current_disk] - 1)) {
message2("Unnecessary step in for disk %d", current_disk);
}
}
current_track[current_disk]++;
if (current_track[current_disk] > (tracks[current_disk] - 1)) {
current_track[current_disk] = (tracks[current_disk] - 1);
}
if (dirty) { /* implies that current_disk < NUM_OF_DSK */
writebuf();
}
current_sector[current_disk] = 0xff;
current_byte[current_disk] = 0xff;
}
if (data & 0x02) { /* step head out */
if (trace_flag & TRACE_TRACK_STUCK) {
if (current_track[current_disk] == 0) {
message2("Unnecessary step out for disk %d", current_disk);
}
}
current_track[current_disk]--;
if (current_track[current_disk] < 0) {
current_track[current_disk] = 0;
current_flag[current_disk] |= 0x40; /* track 0 if there */
}
if (dirty) { /* implies that current_disk < NUM_OF_DSK */
writebuf();
}
current_sector[current_disk] = 0xff;
current_byte[current_disk] = 0xff;
}
if (data & 0x02) { /* step head out */
if (trace_flag & TRACE_TRACK_STUCK) {
if (current_track[current_disk] == 0) {
message2("Unnecessary step out for disk %d", current_disk);
}
}
current_track[current_disk]--;
if (current_track[current_disk] < 0) {
current_track[current_disk] = 0;
current_flag[current_disk] |= 0x40; /* track 0 if there */
}
if (dirty) { /* implies that current_disk < NUM_OF_DSK */
writebuf();
}
current_sector[current_disk] = 0xff;
current_byte[current_disk] = 0xff;
}
if (dirty) { /* implies that current_disk < NUM_OF_DSK */
writebuf();
}
if (dirty) { /* implies that current_disk < NUM_OF_DSK */
writebuf();
}
if (data & 0x04) { /* head load */
current_flag[current_disk] |= 0x04; /* turn on head loaded bit */
current_flag[current_disk] |= 0x80; /* turn on 'read data available' */
}
if (data & 0x04) { /* head load */
current_flag[current_disk] |= 0x04; /* turn on head loaded bit */
current_flag[current_disk] |= 0x80; /* turn on 'read data available' */
}
if (data & 0x08) { /* head unload */
current_flag[current_disk] &= 0xfb; /* turn off 'head loaded' bit */
current_flag[current_disk] &= 0x7f; /* turn off 'read data available' */
current_sector[current_disk] = 0xff;
current_byte[current_disk] = 0xff;
}
if (data & 0x08) { /* head unload */
current_flag[current_disk] &= 0xfb; /* turn off 'head loaded' bit */
current_flag[current_disk] &= 0x7f; /* turn off 'read data available' */
current_sector[current_disk] = 0xff;
current_byte[current_disk] = 0xff;
}
/* interrupts & head current are ignored */
/* interrupts & head current are ignored */
if (data & 0x80) { /* write sequence start */
current_byte[current_disk] = 0;
current_flag[current_disk] |= 0x01; /* enter new write data on */
}
return 0; /* ignored since OUT */
if (data & 0x80) { /* write sequence start */
current_byte[current_disk] = 0;
current_flag[current_disk] |= 0x01; /* enter new write data on */
}
return 0; /* ignored since OUT */
}
/* Disk Data In/Out */
static INLINE int32 dskseek(const UNIT *xptr) {
return fseek(xptr -> fileref, DSK_TRACSIZE * current_track[current_disk] +
DSK_SECTSIZE * current_sector[current_disk], SEEK_SET);
return fseek(xptr -> fileref, DSK_TRACSIZE * current_track[current_disk] +
DSK_SECTSIZE * current_sector[current_disk], SEEK_SET);
}
int32 dsk12(const int32 port, const int32 io, const int32 data) {
int32 i;
UNIT *uptr;
int32 i;
UNIT *uptr;
if (current_disk >= NUM_OF_DSK) {
if (hasVerbose() && (warnDSK12 < warnLevelDSK)) {
warnDSK12++;
/*04*/message2("Attempt of %s 0x0a on unattached disk - ignored.", selectInOut(io));
}
return 0;
}
if (current_disk >= NUM_OF_DSK) {
if (hasVerbose() && (warnDSK12 < warnLevelDSK)) {
warnDSK12++;
/*04*/ message2("Attempt of %s 0x0a on unattached disk - ignored.", selectInOut(io));
}
return 0;
}
/* now current_disk < NUM_OF_DSK */
in9_count = 0;
uptr = dsk_dev.units + current_disk;
if (io == 0) {
if (current_byte[current_disk] >= DSK_SECTSIZE) {
/* physically read the sector */
if (trace_flag & TRACE_READ_WRITE) {
message4("IN 0x0a (READ) D%d T%d S%d", current_disk, current_track[current_disk], current_sector[current_disk]);
}
for (i = 0; i < DSK_SECTSIZE; i++) {
dskbuf[i] = 0;
}
dskseek(uptr);
fread(dskbuf, DSK_SECTSIZE, 1, uptr -> fileref);
current_byte[current_disk] = 0;
}
return dskbuf[current_byte[current_disk]++] & 0xff;
}
else {
if (current_byte[current_disk] >= DSK_SECTSIZE) {
writebuf(); /* from above we have that current_disk < NUM_OF_DSK */
}
else {
dirty = TRUE; /* this guarantees for the next call to writebuf that current_disk < NUM_OF_DSK */
dskbuf[current_byte[current_disk]++] = data & 0xff;
}
return 0; /* ignored since OUT */
}
/* now current_disk < NUM_OF_DSK */
in9_count = 0;
uptr = dsk_dev.units + current_disk;
if (io == 0) {
if (current_byte[current_disk] >= DSK_SECTSIZE) {
/* physically read the sector */
if (trace_flag & TRACE_READ_WRITE) {
message4("IN 0x0a (READ) D%d T%d S%d", current_disk, current_track[current_disk], current_sector[current_disk]);
}
for (i = 0; i < DSK_SECTSIZE; i++) {
dskbuf[i] = 0;
}
dskseek(uptr);
fread(dskbuf, DSK_SECTSIZE, 1, uptr -> fileref);
current_byte[current_disk] = 0;
}
return dskbuf[current_byte[current_disk]++] & 0xff;
}
else {
if (current_byte[current_disk] >= DSK_SECTSIZE) {
writebuf(); /* from above we have that current_disk < NUM_OF_DSK */
}
else {
dirty = TRUE; /* this guarantees for the next call to writebuf that current_disk < NUM_OF_DSK */
dskbuf[current_byte[current_disk]++] = data & 0xff;
}
return 0; /* ignored since OUT */
}
}
/* precondition: current_disk < NUM_OF_DSK */
static void writebuf(void) {
int32 i, rtn;
UNIT *uptr;
i = current_byte[current_disk]; /* null-fill rest of sector if any */
while (i < DSK_SECTSIZE) {
dskbuf[i++] = 0;
}
uptr = dsk_dev.units + current_disk;
if (((uptr -> flags) & UNIT_DSKWLK) == 0) { /* write enabled */
if (trace_flag & TRACE_READ_WRITE) {
message4("OUT 0x0a (WRITE) D%d T%d S%d", current_disk, current_track[current_disk], current_sector[current_disk]);
}
if (dskseek(uptr)) {
message4("fseek failed D%d T%d S%d", current_disk, current_track[current_disk], current_sector[current_disk]);
}
rtn = fwrite(dskbuf, DSK_SECTSIZE, 1, uptr -> fileref);
if (rtn != 1) {
message4("fwrite failed T%d S%d Return=%d", current_track[current_disk], current_sector[current_disk], rtn);
}
}
else if ( ((uptr -> flags) & UNIT_DSK_VERBOSE) && (warnLock[current_disk] < warnLevelDSK) ) {
/* write locked - print warning message if required */
warnLock[current_disk]++;
/*05*/
message2("Attempt to write to locked DSK%d - ignored.", current_disk);
}
current_flag[current_disk] &= 0xfe; /* ENWD off */
current_byte[current_disk] = 0xff;
dirty = FALSE;
int32 i, rtn;
UNIT *uptr;
i = current_byte[current_disk]; /* null-fill rest of sector if any */
while (i < DSK_SECTSIZE) {
dskbuf[i++] = 0;
}
uptr = dsk_dev.units + current_disk;
if (((uptr -> flags) & UNIT_DSKWLK) == 0) { /* write enabled */
if (trace_flag & TRACE_READ_WRITE) {
message4("OUT 0x0a (WRITE) D%d T%d S%d", current_disk, current_track[current_disk], current_sector[current_disk]);
}
if (dskseek(uptr)) {
message4("fseek failed D%d T%d S%d", current_disk, current_track[current_disk], current_sector[current_disk]);
}
rtn = fwrite(dskbuf, DSK_SECTSIZE, 1, uptr -> fileref);
if (rtn != 1) {
message4("fwrite failed T%d S%d Return=%d", current_track[current_disk], current_sector[current_disk], rtn);
}
}
else if ( ((uptr -> flags) & UNIT_DSK_VERBOSE) && (warnLock[current_disk] < warnLevelDSK) ) {
/* write locked - print warning message if required */
warnLock[current_disk]++;
/*05*/ message2("Attempt to write to locked DSK%d - ignored.", current_disk);
}
current_flag[current_disk] &= 0xfe; /* ENWD off */
current_byte[current_disk] = 0xff;
dirty = FALSE;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,49 +1,49 @@
/* altairz80_hdsk.c: simulated hard disk device to increase capacity
/* altairz80_hdsk.c: simulated hard disk device to increase capacity
Copyright (c) 2002-2005, Peter Schorn
Copyright (c) 2002-2005, 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:
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 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.
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.
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.
*/
#include "altairz80_defs.h"
#define UNIT_V_HDSKWLK (UNIT_V_UF + 0) /* write locked */
#define UNIT_HDSKWLK (1 << UNIT_V_HDSKWLK)
#define UNIT_V_HDSK_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */
#define UNIT_HDSK_VERBOSE (1 << UNIT_V_HDSK_VERBOSE)
#define HDSK_SECTOR_SIZE 128 /* size of sector */
#define HDSK_SECTORS_PER_TRACK 32 /* sectors per track */
#define HDS_MAX_TRACKS 2048 /* number of tracks */
#define HDSK_TRACK_SIZE (HDSK_SECTOR_SIZE * HDSK_SECTORS_PER_TRACK)
#define HDSK_CAPACITY (HDSK_TRACK_SIZE * HDS_MAX_TRACKS)
#define HDSK_NUMBER 8 /* number of hard disks */
#define CPM_OK 0 /* indicates to CP/M everything ok */
#define CPM_ERROR 1 /* indicates to CP/M an error condition */
#define CPM_EMPTY 0xe5 /* default value for non-existing bytes */
#define hdsk_none 0
#define hdsk_reset 1
#define hdsk_read 2
#define hdsk_write 3
#define hdsk_boot_address 0x5c00
#define UNIT_V_HDSKWLK (UNIT_V_UF + 0) /* write locked */
#define UNIT_HDSKWLK (1 << UNIT_V_HDSKWLK)
#define UNIT_V_HDSK_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */
#define UNIT_HDSK_VERBOSE (1 << UNIT_V_HDSK_VERBOSE)
#define HDSK_SECTOR_SIZE 128 /* size of sector */
#define HDSK_SECTORS_PER_TRACK 32 /* sectors per track */
#define HDS_MAX_TRACKS 2048 /* number of tracks */
#define HDSK_TRACK_SIZE (HDSK_SECTOR_SIZE * HDSK_SECTORS_PER_TRACK)
#define HDSK_CAPACITY (HDSK_TRACK_SIZE * HDS_MAX_TRACKS)
#define HDSK_NUMBER 8 /* number of hard disks */
#define CPM_OK 0 /* indicates to CP/M everything ok */
#define CPM_ERROR 1 /* indicates to CP/M an error condition */
#define CPM_EMPTY 0xe5 /* default value for non-existing bytes */
#define hdsk_none 0
#define hdsk_reset 1
#define hdsk_read 2
#define hdsk_write 3
#define hdsk_boot_address 0x5c00
extern char messageBuffer[];
extern int32 PCX;
@@ -70,8 +70,8 @@ static int32 doSeek(void);
static int32 doRead(void);
static int32 doWrite(void);
static int32 hdskLastCommand = hdsk_none;
static int32 hdskCommandPosition = 0;
static int32 hdskLastCommand = hdsk_none;
static int32 hdskCommandPosition = 0;
static int32 selectedDisk;
static int32 selectedSector;
static int32 selectedTrack;
@@ -79,310 +79,324 @@ static int32 selectedDMA;
static int32 hdskTrace;
static UNIT hdsk_unit[] = {
{ UDATA (&hdsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) },
{ UDATA (&hdsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) },
{ UDATA (&hdsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) },
{ UDATA (&hdsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) },
{ UDATA (&hdsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) },
{ UDATA (&hdsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) },
{ UDATA (&hdsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) },
{ UDATA (&hdsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) } };
{ UDATA (&hdsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) },
{ UDATA (&hdsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) },
{ UDATA (&hdsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) },
{ UDATA (&hdsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) },
{ UDATA (&hdsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) },
{ UDATA (&hdsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) },
{ UDATA (&hdsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) },
{ UDATA (&hdsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) }
};
static REG hdsk_reg[] = {
{ DRDATA (HDCMD, hdskLastCommand, 32), REG_RO },
{ DRDATA (HDPOS, hdskCommandPosition, 32), REG_RO },
{ DRDATA (HDDSK, selectedDisk, 32), REG_RO },
{ DRDATA (HDSEC, selectedSector, 32), REG_RO },
{ DRDATA (HDTRK, selectedTrack, 32), REG_RO },
{ DRDATA (HDDMA, selectedDMA, 32), REG_RO },
{ DRDATA (HDTRACE, hdskTrace, 8), },
{ NULL } };
{ DRDATA (HDCMD, hdskLastCommand, 32), REG_RO },
{ DRDATA (HDPOS, hdskCommandPosition, 32), REG_RO },
{ DRDATA (HDDSK, selectedDisk, 32), REG_RO },
{ DRDATA (HDSEC, selectedSector, 32), REG_RO },
{ DRDATA (HDTRK, selectedTrack, 32), REG_RO },
{ DRDATA (HDDMA, selectedDMA, 32), REG_RO },
{ DRDATA (HDTRACE, hdskTrace, 8), },
{ NULL }
};
static MTAB hdsk_mod[] = {
{ UNIT_HDSKWLK, 0, "write enabled", "WRITEENABLED", NULL },
{ UNIT_HDSKWLK, UNIT_HDSKWLK, "write locked", "LOCKED", NULL },
/* quiet, no warning messages */
{ UNIT_HDSK_VERBOSE, 0, "QUIET", "QUIET", NULL },
/* verbose, show warning messages */
{ UNIT_HDSK_VERBOSE, UNIT_HDSK_VERBOSE, "VERBOSE", "VERBOSE", NULL },
{ 0 } };
{ UNIT_HDSKWLK, 0, "write enabled", "WRITEENABLED", NULL },
{ UNIT_HDSKWLK, UNIT_HDSKWLK, "write locked", "LOCKED", NULL },
/* quiet, no warning messages */
{ UNIT_HDSK_VERBOSE, 0, "QUIET", "QUIET", NULL },
/* verbose, show warning messages */
{ UNIT_HDSK_VERBOSE, UNIT_HDSK_VERBOSE, "VERBOSE", "VERBOSE", NULL },
{ 0 }
};
DEVICE hdsk_dev = {
"HDSK", hdsk_unit, hdsk_reg, hdsk_mod,
8, 10, 31, 1, 8, 8,
NULL, NULL, NULL,
&hdsk_boot, NULL, NULL,
NULL, 0, 0,
NULL, NULL, NULL };
"HDSK", hdsk_unit, hdsk_reg, hdsk_mod,
8, 10, 31, 1, 8, 8,
NULL, NULL, NULL,
&hdsk_boot, NULL, NULL,
NULL, 0, 0,
NULL, NULL, NULL
};
static t_stat hdsk_svc(UNIT *uptr) {
return SCPE_OK;
return SCPE_OK;
}
static const int32 hdskBoot[bootrom_size] = {
0xf3, 0x06, 0x80, 0x3e, 0x0e, 0xd3, 0xfe, 0x05, /* 5c00-5c07 */
0xc2, 0x05, 0x5c, 0x3e, 0x16, 0xd3, 0xfe, 0x3e, /* 5c08-5c0f */
0x12, 0xd3, 0xfe, 0xdb, 0xfe, 0xb7, 0xca, 0x20, /* 5c10-5c17 */
0x5c, 0x3e, 0x0c, 0xd3, 0xfe, 0xaf, 0xd3, 0xfe, /* 5c18-5c1f */
0x06, 0x20, 0x3e, 0x01, 0xd3, 0xfd, 0x05, 0xc2, /* 5c20-5c27 */
0x24, 0x5c, 0x11, 0x08, 0x00, 0x21, 0x00, 0x00, /* 5c28-5c2f */
0x0e, 0xb8, 0x3e, 0x02, 0xd3, 0xfd, 0x3a, 0x37, /* 5c30-5c37 */
0xff, 0xd6, 0x08, 0xd3, 0xfd, 0x7b, 0xd3, 0xfd, /* 5c38-5c3f */
0x7a, 0xd3, 0xfd, 0xaf, 0xd3, 0xfd, 0x7d, 0xd3, /* 5c40-5c47 */
0xfd, 0x7c, 0xd3, 0xfd, 0xdb, 0xfd, 0xb7, 0xca, /* 5c48-5c4f */
0x53, 0x5c, 0x76, 0x79, 0x0e, 0x80, 0x09, 0x4f, /* 5c50-5c57 */
0x0d, 0xc2, 0x60, 0x5c, 0xfb, 0xc3, 0x00, 0x00, /* 5c58-5c5f */
0x1c, 0x1c, 0x7b, 0xfe, 0x20, 0xca, 0x73, 0x5c, /* 5c60-5c67 */
0xfe, 0x21, 0xc2, 0x32, 0x5c, 0x1e, 0x00, 0x14, /* 5c68-5c6f */
0xc3, 0x32, 0x5c, 0x1e, 0x01, 0xc3, 0x32, 0x5c, /* 5c70-5c77 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c78-5c7f */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c80-5c87 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c88-5c8f */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c90-5c97 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c98-5c9f */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5ca0-5ca7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5ca8-5caf */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cb0-5cb7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cb8-5cbf */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cc0-5cc7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cc8-5ccf */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cd0-5cd7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cd8-5cdf */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5ce0-5ce7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5ce8-5cef */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cf0-5cf7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cf8-5cff */
0xf3, 0x06, 0x80, 0x3e, 0x0e, 0xd3, 0xfe, 0x05, /* 5c00-5c07 */
0xc2, 0x05, 0x5c, 0x3e, 0x16, 0xd3, 0xfe, 0x3e, /* 5c08-5c0f */
0x12, 0xd3, 0xfe, 0xdb, 0xfe, 0xb7, 0xca, 0x20, /* 5c10-5c17 */
0x5c, 0x3e, 0x0c, 0xd3, 0xfe, 0xaf, 0xd3, 0xfe, /* 5c18-5c1f */
0x06, 0x20, 0x3e, 0x01, 0xd3, 0xfd, 0x05, 0xc2, /* 5c20-5c27 */
0x24, 0x5c, 0x11, 0x08, 0x00, 0x21, 0x00, 0x00, /* 5c28-5c2f */
0x0e, 0xb8, 0x3e, 0x02, 0xd3, 0xfd, 0x3a, 0x37, /* 5c30-5c37 */
0xff, 0xd6, 0x08, 0xd3, 0xfd, 0x7b, 0xd3, 0xfd, /* 5c38-5c3f */
0x7a, 0xd3, 0xfd, 0xaf, 0xd3, 0xfd, 0x7d, 0xd3, /* 5c40-5c47 */
0xfd, 0x7c, 0xd3, 0xfd, 0xdb, 0xfd, 0xb7, 0xca, /* 5c48-5c4f */
0x53, 0x5c, 0x76, 0x79, 0x0e, 0x80, 0x09, 0x4f, /* 5c50-5c57 */
0x0d, 0xc2, 0x60, 0x5c, 0xfb, 0xc3, 0x00, 0x00, /* 5c58-5c5f */
0x1c, 0x1c, 0x7b, 0xfe, 0x20, 0xca, 0x73, 0x5c, /* 5c60-5c67 */
0xfe, 0x21, 0xc2, 0x32, 0x5c, 0x1e, 0x00, 0x14, /* 5c68-5c6f */
0xc3, 0x32, 0x5c, 0x1e, 0x01, 0xc3, 0x32, 0x5c, /* 5c70-5c77 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c78-5c7f */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c80-5c87 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c88-5c8f */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c90-5c97 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c98-5c9f */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5ca0-5ca7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5ca8-5caf */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cb0-5cb7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cb8-5cbf */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cc0-5cc7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cc8-5ccf */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cd0-5cd7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cd8-5cdf */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5ce0-5ce7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5ce8-5cef */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cf0-5cf7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cf8-5cff */
};
static t_stat hdsk_boot(int32 unitno, DEVICE *dptr) {
int32 i;
if (MEMSIZE < 24*KB) {
printf("Need at least 24KB RAM to boot from hard disk.\n");
return SCPE_ARG;
}
if (cpu_unit.flags & (UNIT_ALTAIRROM | UNIT_BANKED)) {
if (install_bootrom()) {
printf("ALTAIR boot ROM installed.\n");
}
/* check whether we are really modifying an LD A,<> instruction */
if (bootrom[unitNoOffset1 - 1] == LDAInstruction) {
bootrom[unitNoOffset1] = (unitno + NUM_OF_DSK) & 0xff; /* LD A,<unitno> */
}
else { /* Attempt to modify non LD A,<> instructions is refused. */
printf("Incorrect boot ROM offset detected.\n");
return SCPE_IERR;
}
}
for (i = 0; i < bootrom_size; i++) {
PutBYTEBasic(i + hdsk_boot_address, 0, hdskBoot[i] & 0xff);
}
saved_PC = hdsk_boot_address;
protect(hdsk_boot_address, hdsk_boot_address + bootrom_size - 1);
return SCPE_OK;
int32 i;
if (MEMSIZE < 24*KB) {
printf("Need at least 24KB RAM to boot from hard disk.\n");
return SCPE_ARG;
}
if (cpu_unit.flags & (UNIT_ALTAIRROM | UNIT_BANKED)) {
if (install_bootrom()) {
printf("ALTAIR boot ROM installed.\n");
}
/* check whether we are really modifying an LD A,<> instruction */
if (bootrom[unitNoOffset1 - 1] == LDAInstruction) {
bootrom[unitNoOffset1] = (unitno + NUM_OF_DSK) & 0xff; /* LD A,<unitno> */
}
else { /* Attempt to modify non LD A,<> instructions is refused. */
printf("Incorrect boot ROM offset detected.\n");
return SCPE_IERR;
}
}
for (i = 0; i < bootrom_size; i++) {
PutBYTEBasic(i + hdsk_boot_address, 0, hdskBoot[i] & 0xff);
}
saved_PC = hdsk_boot_address;
protect(hdsk_boot_address, hdsk_boot_address + bootrom_size - 1);
return SCPE_OK;
}
/* returns TRUE iff there exists a disk with VERBOSE */
static int32 hdsk_hasVerbose(void) {
int32 i;
for (i = 0; i < HDSK_NUMBER; i++) {
if (((hdsk_dev.units + i) -> flags) & UNIT_HDSK_VERBOSE) {
return TRUE;
}
}
return FALSE;
int32 i;
for (i = 0; i < HDSK_NUMBER; i++) {
if (((hdsk_dev.units + i) -> flags) & UNIT_HDSK_VERBOSE) {
return TRUE;
}
}
return FALSE;
}
/* The hard disk port is 0xfd. It understands the following commands.
/* The hard disk port is 0xfd. It understands the following commands.
1. reset
ld b,32
ld a,hdsk_reset
l out (0fdh),a
dec b
jp nz,l
2. read / write
; parameter block
cmd: db hdsk_read or hdsk_write
hd: db 0 ; 0 .. 7, defines hard disk to be used
sector: db 0 ; 0 .. 31, defines sector
track: dw 0 ; 0 .. 2047, defines track
dma: dw 0 ; defines where result is placed in memory
; routine to execute
ld b,7 ; size of parameter block
ld hl,cmd ; start address of parameter block
l ld a,(hl) ; get byte of parameter block
out (0fdh),a ; send it to port
inc hl ; point to next byte
dec b ; decrement counter
jp nz,l ; again, if not done
in a,(0fdh) ; get result code
1. Reset
ld b,32
ld a,hdsk_reset
l: out (0fdh),a
dec b
jp nz,l
2. Read / write
; parameter block
cmd: db hdsk_read or hdsk_write
hd: db 0 ; 0 .. 7, defines hard disk to be used
sector: db 0 ; 0 .. 31, defines sector
track: dw 0 ; 0 .. 2047, defines track
dma: dw 0 ; defines where result is placed in memory
; routine to execute
ld b,7 ; size of parameter block
ld hl,cmd ; start address of parameter block
l: ld a,(hl) ; get byte of parameter block
out (0fdh),a ; send it to port
inc hl ; point to next byte
dec b ; decrement counter
jp nz,l ; again, if not done
in a,(0fdh) ; get result code
*/
/* check the parameters and return TRUE iff parameters are correct or have been repaired */
static int32 checkParameters(void) {
int32 currentFlag;
if ((selectedDisk < 0) || (selectedDisk >= HDSK_NUMBER)) {
if (hdsk_hasVerbose()) {
message2("HDSK%d does not exist, will use HDSK0 instead.", selectedDisk);
}
selectedDisk = 0;
}
currentFlag = (hdsk_dev.units + selectedDisk) -> flags;
if ((currentFlag & UNIT_ATT) == 0) {
if (currentFlag & UNIT_HDSK_VERBOSE) {
message2("HDSK%d is not attached.", selectedDisk);
}
return FALSE; /* cannot read or write */
}
if ((selectedSector < 0) || (selectedSector >= HDSK_SECTORS_PER_TRACK)) {
if (currentFlag & UNIT_HDSK_VERBOSE) {
message4("HDSK%d: 0 <= Sector=%02d < %d violated, will use 0 instead.",
selectedDisk, selectedSector, HDSK_SECTORS_PER_TRACK);
}
selectedSector = 0;
}
if ((selectedTrack < 0) || (selectedTrack >= HDS_MAX_TRACKS)) {
if (currentFlag & UNIT_HDSK_VERBOSE) {
message4("HDSK%d: 0 <= Track=%04d < %04d violated, will use 0 instead.",
selectedDisk, selectedTrack, HDS_MAX_TRACKS);
}
selectedTrack = 0;
}
selectedDMA &= ADDRMASK;
if (hdskTrace) {
message6("%s HDSK%d Sector=%02d Track=%04d DMA=%04x",
(hdskLastCommand == hdsk_read) ? "Read" : "Write",
selectedDisk, selectedSector, selectedTrack, selectedDMA);
}
return TRUE;
int32 currentFlag;
if ((selectedDisk < 0) || (selectedDisk >= HDSK_NUMBER)) {
if (hdsk_hasVerbose()) {
message2("HDSK%d does not exist, will use HDSK0 instead.", selectedDisk);
}
selectedDisk = 0;
}
currentFlag = (hdsk_dev.units + selectedDisk) -> flags;
if ((currentFlag & UNIT_ATT) == 0) {
if (currentFlag & UNIT_HDSK_VERBOSE) {
message2("HDSK%d is not attached.", selectedDisk);
}
return FALSE; /* cannot read or write */
}
if ((selectedSector < 0) || (selectedSector >= HDSK_SECTORS_PER_TRACK)) {
if (currentFlag & UNIT_HDSK_VERBOSE) {
message4("HDSK%d: 0 <= Sector=%02d < %d violated, will use 0 instead.",
selectedDisk, selectedSector, HDSK_SECTORS_PER_TRACK);
}
selectedSector = 0;
}
if ((selectedTrack < 0) || (selectedTrack >= HDS_MAX_TRACKS)) {
if (currentFlag & UNIT_HDSK_VERBOSE) {
message4("HDSK%d: 0 <= Track=%04d < %04d violated, will use 0 instead.",
selectedDisk, selectedTrack, HDS_MAX_TRACKS);
}
selectedTrack = 0;
}
selectedDMA &= ADDRMASK;
if (hdskTrace) {
message6("%s HDSK%d Sector=%02d Track=%04d DMA=%04x",
(hdskLastCommand == hdsk_read) ? "Read" : "Write",
selectedDisk, selectedSector, selectedTrack, selectedDMA);
}
return TRUE;
}
static int32 doSeek(void) {
UNIT *uptr = hdsk_dev.units + selectedDisk;
if (fseek(uptr -> fileref,
HDSK_TRACK_SIZE * selectedTrack + HDSK_SECTOR_SIZE * selectedSector, SEEK_SET)) {
if ((uptr -> flags) & UNIT_HDSK_VERBOSE) {
message4("Could not access HDSK%d Sector=%02d Track=%04d.",
selectedDisk, selectedSector, selectedTrack);
}
return CPM_ERROR;
}
else {
return CPM_OK;
}
UNIT *uptr = hdsk_dev.units + selectedDisk;
if (fseek(uptr -> fileref,
HDSK_TRACK_SIZE * selectedTrack + HDSK_SECTOR_SIZE * selectedSector, SEEK_SET)) {
if ((uptr -> flags) & UNIT_HDSK_VERBOSE) {
message4("Could not access HDSK%d Sector=%02d Track=%04d.",
selectedDisk, selectedSector, selectedTrack);
}
return CPM_ERROR;
}
else {
return CPM_OK;
}
}
static int32 doRead(void) {
int32 i;
uint8 hdskbuf[HDSK_SECTOR_SIZE]; /* data buffer */
UNIT *uptr = hdsk_dev.units + selectedDisk;
if (doSeek()) {
return CPM_ERROR;
}
if (fread(hdskbuf, HDSK_SECTOR_SIZE, 1, uptr -> fileref) != 1) {
for (i = 0; i < HDSK_SECTOR_SIZE; i++) {
hdskbuf[i] = CPM_EMPTY;
}
if ((uptr -> flags) & UNIT_HDSK_VERBOSE) {
message4("Could not read HDSK%d Sector=%02d Track=%04d.",
selectedDisk, selectedSector, selectedTrack);
}
return CPM_OK; /* allows the creation of empty hard disks */
}
for (i = 0; i < HDSK_SECTOR_SIZE; i++) {
PutBYTEWrapper(selectedDMA + i, hdskbuf[i]);
}
return CPM_OK;
int32 i;
uint8 hdskbuf[HDSK_SECTOR_SIZE]; /* data buffer */
UNIT *uptr = hdsk_dev.units + selectedDisk;
if (doSeek()) {
return CPM_ERROR;
}
if (fread(hdskbuf, HDSK_SECTOR_SIZE, 1, uptr -> fileref) != 1) {
for (i = 0; i < HDSK_SECTOR_SIZE; i++) {
hdskbuf[i] = CPM_EMPTY;
}
if ((uptr -> flags) & UNIT_HDSK_VERBOSE) {
message4("Could not read HDSK%d Sector=%02d Track=%04d.",
selectedDisk, selectedSector, selectedTrack);
}
return CPM_OK; /* allows the creation of empty hard disks */
}
for (i = 0; i < HDSK_SECTOR_SIZE; i++) {
PutBYTEWrapper(selectedDMA + i, hdskbuf[i]);
}
return CPM_OK;
}
static int32 doWrite(void) {
int32 i;
uint8 hdskbuf[HDSK_SECTOR_SIZE]; /* data buffer */
UNIT *uptr = hdsk_dev.units + selectedDisk;
if (((uptr -> flags) & UNIT_HDSKWLK) == 0) { /* write enabled */
if (doSeek()) {
return CPM_ERROR;
}
for (i = 0; i < HDSK_SECTOR_SIZE; i++) {
hdskbuf[i] = GetBYTEWrapper(selectedDMA + i);
}
if (fwrite(hdskbuf, HDSK_SECTOR_SIZE, 1, uptr -> fileref) != 1) {
if ((uptr -> flags) & UNIT_HDSK_VERBOSE) {
message4("Could not write HDSK%d Sector=%02d Track=%04d.",
selectedDisk, selectedSector, selectedTrack);
}
return CPM_ERROR;
}
}
else {
if ((uptr -> flags) & UNIT_HDSK_VERBOSE) {
message4("Could not write to locked HDSK%d Sector=%02d Track=%04d.",
selectedDisk, selectedSector, selectedTrack);
}
return CPM_ERROR;
}
return CPM_OK;
int32 i;
uint8 hdskbuf[HDSK_SECTOR_SIZE]; /* data buffer */
UNIT *uptr = hdsk_dev.units + selectedDisk;
if (((uptr -> flags) & UNIT_HDSKWLK) == 0) { /* write enabled */
if (doSeek()) {
return CPM_ERROR;
}
for (i = 0; i < HDSK_SECTOR_SIZE; i++) {
hdskbuf[i] = GetBYTEWrapper(selectedDMA + i);
}
if (fwrite(hdskbuf, HDSK_SECTOR_SIZE, 1, uptr -> fileref) != 1) {
if ((uptr -> flags) & UNIT_HDSK_VERBOSE) {
message4("Could not write HDSK%d Sector=%02d Track=%04d.",
selectedDisk, selectedSector, selectedTrack);
}
return CPM_ERROR;
}
}
else {
if ((uptr -> flags) & UNIT_HDSK_VERBOSE) {
message4("Could not write to locked HDSK%d Sector=%02d Track=%04d.",
selectedDisk, selectedSector, selectedTrack);
}
return CPM_ERROR;
}
return CPM_OK;
}
static int32 hdsk_in(const int32 port) {
int32 result;
if ((hdskCommandPosition == 6) && ((hdskLastCommand == hdsk_read) || (hdskLastCommand == hdsk_write))) {
result = checkParameters() ? ((hdskLastCommand == hdsk_read) ? doRead() : doWrite()) : CPM_ERROR;
hdskLastCommand = hdsk_none;
hdskCommandPosition = 0;
return result;
}
else if (hdsk_hasVerbose()) {
message4("Illegal IN command detected (port=%03xh, cmd=%d, pos=%d).",
port, hdskLastCommand, hdskCommandPosition);
}
return CPM_OK;
int32 result;
if ((hdskCommandPosition == 6) && ((hdskLastCommand == hdsk_read) || (hdskLastCommand == hdsk_write))) {
result = checkParameters() ? ((hdskLastCommand == hdsk_read) ? doRead() : doWrite()) : CPM_ERROR;
hdskLastCommand = hdsk_none;
hdskCommandPosition = 0;
return result;
}
else if (hdsk_hasVerbose()) {
message4("Illegal IN command detected (port=%03xh, cmd=%d, pos=%d).",
port, hdskLastCommand, hdskCommandPosition);
}
return CPM_OK;
}
static int32 hdsk_out(const int32 data) {
switch(hdskLastCommand) {
case hdsk_read:
case hdsk_write:
switch(hdskCommandPosition) {
case 0:
selectedDisk = data;
hdskCommandPosition++;
break;
case 1:
selectedSector = data;
hdskCommandPosition++;
break;
case 2:
selectedTrack = data;
hdskCommandPosition++;
break;
case 3:
selectedTrack += (data << 8);
hdskCommandPosition++;
break;
case 4:
selectedDMA = data;
hdskCommandPosition++;
break;
case 5:
selectedDMA += (data << 8);
hdskCommandPosition++;
break;
default:
hdskLastCommand = hdsk_none;
hdskCommandPosition = 0;
}
break;
default:
hdskLastCommand = data;
hdskCommandPosition = 0;
}
return 0; /* ignored, since OUT */
switch(hdskLastCommand) {
case hdsk_read:
case hdsk_write:
switch(hdskCommandPosition) {
case 0:
selectedDisk = data;
hdskCommandPosition++;
break;
case 1:
selectedSector = data;
hdskCommandPosition++;
break;
case 2:
selectedTrack = data;
hdskCommandPosition++;
break;
case 3:
selectedTrack += (data << 8);
hdskCommandPosition++;
break;
case 4:
selectedDMA = data;
hdskCommandPosition++;
break;
case 5:
selectedDMA += (data << 8);
hdskCommandPosition++;
break;
default:
hdskLastCommand = hdsk_none;
hdskCommandPosition = 0;
}
break;
default:
hdskLastCommand = data;
hdskCommandPosition = 0;
}
return 0; /* ignored, since OUT */
}
int32 hdsk_io(const int32 port, const int32 io, const int32 data) {
return io == 0 ? hdsk_in(port) : hdsk_out(data);
return io == 0 ? hdsk_in(port) : hdsk_out(data);
}