1 /* altairz80_dsk.c: MITS Altair 88-DISK Simulator
3 Copyright (c) 2002-2008, Peter Schorn
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following conditions:
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 Except as contained in this notice, the name of Peter Schorn shall not
23 be used in advertising or otherwise to promote the sale, use or other dealings
24 in this Software without prior written authorization from Peter Schorn.
26 Based on work by Charles E Owen (c) 1997
28 The 88_DISK is a 8-inch floppy controller which can control up
29 to 16 daisy-chained Pertec FD-400 hard-sectored floppy drives.
30 Each diskette has physically 77 tracks of 32 137-byte sectors
33 The controller is interfaced to the CPU by use of 3 I/O addresses,
34 standardly, these are device numbers 10, 11, and 12 (octal).
39 10 Out Selects and enables Controller and Drive
40 10 In Indicates status of Drive and Controller
41 11 Out Controls Disk Function
42 11 In Indicates current sector position of disk
46 Drive Select Out (Device 10 OUT):
48 +---+---+---+---+---+---+---+---+
49 | C | X | X | X | Device |
50 +---+---+---+---+---+---+---+---+
52 C = If this bit is 1, the disk controller selected by 'device' is
53 cleared. If the bit is zero, 'device' is selected as the
54 device being controlled by subsequent I/O operations.
56 Device = value zero thru 15, selects drive to be controlled.
58 Drive Status In (Device 10 IN):
60 +---+---+---+---+---+---+---+---+
61 | R | Z | I | X | X | H | M | W |
62 +---+---+---+---+---+---+---+---+
64 W - When 0, write circuit ready to write another byte.
65 M - When 0, head movement is allowed
66 H - When 0, indicates head is loaded for read/write
67 X - not used (will be 0)
68 I - When 0, indicates interrupts enabled (not used by this simulator)
69 Z - When 0, indicates head is on track 0
70 R - When 0, indicates that read circuit has new byte to read
72 Drive Control (Device 11 OUT):
74 +---+---+---+---+---+---+---+---+
75 | W | C | D | E | U | H | O | I |
76 +---+---+---+---+---+---+---+---+
78 I - When 1, steps head IN one track
79 O - When 1, steps head OUT one track
80 H - When 1, loads head to drive surface
81 U - When 1, unloads head
82 E - Enables interrupts (ignored by this simulator)
83 D - Disables interrupts (ignored by this simulator)
84 C - When 1 lowers head current (ignored by this simulator)
85 W - When 1, starts Write Enable sequence: W bit on device 10
86 (see above) will go 1 and data will be read from port 12
87 until 137 bytes have been read by the controller from
88 that port. The W bit will go off then, and the sector data
89 will be written to disk. Before you do this, you must have
90 stepped the track to the desired number, and waited until
91 the right sector number is presented on device 11 IN, then
94 Sector Position (Device 11 IN):
96 As the sectors pass by the read head, they are counted and the
97 number of the current one is available in this register.
99 +---+---+---+---+---+---+---+---+
100 | X | X | Sector Number | T |
101 +---+---+---+---+---+---+---+---+
104 Sector number = binary of the sector number currently under the
106 T = Sector True, is a 1 when the sector is positioned to read or
111 #include "altairz80_defs.h"
114 #define UNIT_V_DSK_WLK (UNIT_V_UF + 0) /* write locked */
115 #define UNIT_DSK_WLK (1 << UNIT_V_DSK_WLK)
116 #define UNIT_V_DSK_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */
117 #define UNIT_DSK_VERBOSE (1 << UNIT_V_DSK_VERBOSE)
118 #define DSK_SECTSIZE 137 /* size of sector */
119 #define DSK_SECT 32 /* sectors per track */
120 #define MAX_TRACKS 254 /* number of tracks,
121 original Altair has 77 tracks only */
122 #define DSK_TRACSIZE (DSK_SECTSIZE * DSK_SECT)
123 #define MAX_DSK_SIZE (DSK_TRACSIZE * MAX_TRACKS)
124 #define TRACE_IN_OUT 1
125 #define TRACE_READ_WRITE 2
126 #define TRACE_SECTOR_STUCK 4
127 #define TRACE_TRACK_STUCK 8
128 #define NUM_OF_DSK_MASK (NUM_OF_DSK - 1)
129 #define BOOTROM_SIZE_DSK 256 /* size of boot rom */
132 int32
dsk10(const int32 port
, const int32 io
, const int32 data
);
133 int32
dsk11(const int32 port
, const int32 io
, const int32 data
);
134 int32
dsk12(const int32 port
, const int32 io
, const int32 data
);
135 static t_stat
dsk_boot(int32 unitno
, DEVICE
*dptr
);
136 static t_stat
dsk_reset(DEVICE
*dptr
);
137 static t_stat
dsk_set_verbose(UNIT
*uptr
, int32 value
, char *cptr
, void *desc
);
140 extern UNIT cpu_unit
;
141 extern char messageBuffer
[];
144 extern void printMessage(void);
145 extern t_stat
install_bootrom(int32 bootrom
[], int32 size
, int32 addr
, int32 makeROM
);
146 void install_ALTAIRbootROM(void);
148 /* global data on status */
150 /* currently selected drive (values are 0 .. NUM_OF_DSK)
151 current_disk < NUM_OF_DSK implies that the corresponding disk is attached to a file */
152 static int32 current_disk
= NUM_OF_DSK
;
153 static int32 current_track
[NUM_OF_DSK
] = {0, 0, 0, 0, 0, 0, 0, 0};
154 static int32 current_sector
[NUM_OF_DSK
] = {0, 0, 0, 0, 0, 0, 0, 0};
155 static int32 current_byte
[NUM_OF_DSK
] = {0, 0, 0, 0, 0, 0, 0, 0};
156 static int32 current_flag
[NUM_OF_DSK
] = {0, 0, 0, 0, 0, 0, 0, 0};
157 static uint8 tracks
[NUM_OF_DSK
] = { MAX_TRACKS
, MAX_TRACKS
, MAX_TRACKS
, MAX_TRACKS
,
158 MAX_TRACKS
, MAX_TRACKS
, MAX_TRACKS
, MAX_TRACKS
};
159 static int32 trace_level
= 0;
160 static int32 in9_count
= 0;
161 static int32 in9_message
= FALSE
;
162 static int32 dirty
= FALSE
; /* TRUE when buffer has unwritten data in it */
163 static int32 warnLevelDSK
= 3;
164 static int32 warnLock
[NUM_OF_DSK
] = {0, 0, 0, 0, 0, 0, 0, 0};
165 static int32 warnAttached
[NUM_OF_DSK
] = {0, 0, 0, 0, 0, 0, 0, 0};
166 static int32 warnDSK10
= 0;
167 static int32 warnDSK11
= 0;
168 static int32 warnDSK12
= 0;
169 static int8 dskbuf
[DSK_SECTSIZE
]; /* data Buffer */
171 /* Altair MITS modified BOOT EPROM, fits in upper 256 byte of memory */
172 int32 bootrom_dsk
[BOOTROM_SIZE_DSK
] = {
173 0xf3, 0x06, 0x80, 0x3e, 0x0e, 0xd3, 0xfe, 0x05, /* ff00-ff07 */
174 0xc2, 0x05, 0xff, 0x3e, 0x16, 0xd3, 0xfe, 0x3e, /* ff08-ff0f */
175 0x12, 0xd3, 0xfe, 0xdb, 0xfe, 0xb7, 0xca, 0x20, /* ff10-ff17 */
176 0xff, 0x3e, 0x0c, 0xd3, 0xfe, 0xaf, 0xd3, 0xfe, /* ff18-ff1f */
177 0x21, 0x00, 0x5c, 0x11, 0x33, 0xff, 0x0e, 0x88, /* ff20-ff27 */
178 0x1a, 0x77, 0x13, 0x23, 0x0d, 0xc2, 0x28, 0xff, /* ff28-ff2f */
179 0xc3, 0x00, 0x5c, 0x31, 0x21, 0x5d, 0x3e, 0x00, /* ff30-ff37 */
180 0xd3, 0x08, 0x3e, 0x04, 0xd3, 0x09, 0xc3, 0x19, /* ff38-ff3f */
181 0x5c, 0xdb, 0x08, 0xe6, 0x02, 0xc2, 0x0e, 0x5c, /* ff40-ff47 */
182 0x3e, 0x02, 0xd3, 0x09, 0xdb, 0x08, 0xe6, 0x40, /* ff48-ff4f */
183 0xc2, 0x0e, 0x5c, 0x11, 0x00, 0x00, 0x06, 0x08, /* ff50-ff57 */
184 0xc5, 0xd5, 0x11, 0x86, 0x80, 0x21, 0x88, 0x5c, /* ff58-ff5f */
185 0xdb, 0x09, 0x1f, 0xda, 0x2d, 0x5c, 0xe6, 0x1f, /* ff60-ff67 */
186 0xb8, 0xc2, 0x2d, 0x5c, 0xdb, 0x08, 0xb7, 0xfa, /* ff68-ff6f */
187 0x39, 0x5c, 0xdb, 0x0a, 0x77, 0x23, 0x1d, 0xc2, /* ff70-ff77 */
188 0x39, 0x5c, 0xd1, 0x21, 0x8b, 0x5c, 0x06, 0x80, /* ff78-ff7f */
189 0x7e, 0x12, 0x23, 0x13, 0x05, 0xc2, 0x4d, 0x5c, /* ff80-ff87 */
190 0xc1, 0x21, 0x00, 0x5c, 0x7a, 0xbc, 0xc2, 0x60, /* ff88-ff8f */
191 0x5c, 0x7b, 0xbd, 0xd2, 0x80, 0x5c, 0x04, 0x04, /* ff90-ff97 */
192 0x78, 0xfe, 0x20, 0xda, 0x25, 0x5c, 0x06, 0x01, /* ff98-ff9f */
193 0xca, 0x25, 0x5c, 0xdb, 0x08, 0xe6, 0x02, 0xc2, /* ffa0-ffa7 */
194 0x70, 0x5c, 0x3e, 0x01, 0xd3, 0x09, 0x06, 0x00, /* ffa8-ffaf */
195 0xc3, 0x25, 0x5c, 0x3e, 0x80, 0xd3, 0x08, 0xfb, /* ffb0-ffb7 */
196 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffb8-ffbf */
197 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffc0-ffc7 */
198 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffc8-ffcf */
199 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffd0-ffd7 */
200 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffd8-ffdf */
201 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffe0-ffe7 */
202 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffe8-ffef */
203 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* fff0-fff7 */
204 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* fff8-ffff */
207 /* 88DSK Standard I/O Data Structures */
209 static UNIT dsk_unit
[] = {
210 { UDATA (NULL
, UNIT_FIX
+ UNIT_ATTABLE
+ UNIT_DISABLE
+ UNIT_ROABLE
, MAX_DSK_SIZE
) },
211 { UDATA (NULL
, UNIT_FIX
+ UNIT_ATTABLE
+ UNIT_DISABLE
+ UNIT_ROABLE
, MAX_DSK_SIZE
) },
212 { UDATA (NULL
, UNIT_FIX
+ UNIT_ATTABLE
+ UNIT_DISABLE
+ UNIT_ROABLE
, MAX_DSK_SIZE
) },
213 { UDATA (NULL
, UNIT_FIX
+ UNIT_ATTABLE
+ UNIT_DISABLE
+ UNIT_ROABLE
, MAX_DSK_SIZE
) },
214 { UDATA (NULL
, UNIT_FIX
+ UNIT_ATTABLE
+ UNIT_DISABLE
+ UNIT_ROABLE
, MAX_DSK_SIZE
) },
215 { UDATA (NULL
, UNIT_FIX
+ UNIT_ATTABLE
+ UNIT_DISABLE
+ UNIT_ROABLE
, MAX_DSK_SIZE
) },
216 { UDATA (NULL
, UNIT_FIX
+ UNIT_ATTABLE
+ UNIT_DISABLE
+ UNIT_ROABLE
, MAX_DSK_SIZE
) },
217 { UDATA (NULL
, UNIT_FIX
+ UNIT_ATTABLE
+ UNIT_DISABLE
+ UNIT_ROABLE
, MAX_DSK_SIZE
) }
220 static REG dsk_reg
[] = {
221 { DRDATA (DISK
, current_disk
, 4) },
222 { BRDATA (CURTRACK
, current_track
, 10, 32, NUM_OF_DSK
), REG_CIRC
+ REG_RO
},
223 { BRDATA (CURSECTOR
, current_sector
, 10, 32, NUM_OF_DSK
), REG_CIRC
+ REG_RO
},
224 { BRDATA (CURBYTE
, current_byte
, 10, 32, NUM_OF_DSK
), REG_CIRC
+ REG_RO
},
225 { BRDATA (CURFLAG
, current_flag
, 10, 32, NUM_OF_DSK
), REG_CIRC
+ REG_RO
},
226 { BRDATA (TRACKS
, tracks
, 10, 8, NUM_OF_DSK
), REG_CIRC
},
227 { HRDATA (TRACELEVEL
, trace_level
, 16) },
228 { DRDATA (IN9COUNT
, in9_count
, 4), REG_RO
},
229 { DRDATA (IN9MESSAGE
, in9_message
, 4), REG_RO
},
230 { DRDATA (DIRTY
, dirty
, 4), REG_RO
},
231 { DRDATA (DSKWL
, warnLevelDSK
, 32) },
232 { BRDATA (WARNLOCK
, warnLock
, 10, 32, NUM_OF_DSK
), REG_CIRC
+ REG_RO
},
233 { BRDATA (WARNATTACHED
, warnAttached
, 10, 32, NUM_OF_DSK
), REG_CIRC
+ REG_RO
},
234 { DRDATA (WARNDSK10
, warnDSK10
, 4), REG_RO
},
235 { DRDATA (WARNDSK11
, warnDSK11
, 4), REG_RO
},
236 { DRDATA (WARNDSK12
, warnDSK12
, 4), REG_RO
},
237 { BRDATA (DISKBUFFER
, dskbuf
, 10, 8, DSK_SECTSIZE
), REG_CIRC
+ REG_RO
},
241 static MTAB dsk_mod
[] = {
242 { UNIT_DSK_WLK
, 0, "WRTENB", "WRTENB", NULL
},
243 { UNIT_DSK_WLK
, UNIT_DSK_WLK
, "WRTLCK", "WRTLCK", NULL
},
244 /* quiet, no warning messages */
245 { UNIT_DSK_VERBOSE
, 0, "QUIET", "QUIET", NULL
},
246 /* verbose, show warning messages */
247 { UNIT_DSK_VERBOSE
, UNIT_DSK_VERBOSE
, "VERBOSE", "VERBOSE", &dsk_set_verbose
},
252 "DSK", dsk_unit
, dsk_reg
, dsk_mod
,
254 NULL
, NULL
, &dsk_reset
,
255 &dsk_boot
, NULL
, NULL
,
256 NULL
, (DEV_DISABLE
), 0,
260 static void resetDSKWarningFlags(void) {
262 for (i
= 0; i
< NUM_OF_DSK
; i
++) {
271 static t_stat
dsk_set_verbose(UNIT
*uptr
, int32 value
, char *cptr
, void *desc
) {
272 resetDSKWarningFlags();
276 /* returns TRUE iff there exists a disk with VERBOSE */
277 static int32
hasVerbose(void) {
279 for (i
= 0; i
< NUM_OF_DSK
; i
++)
280 if (((dsk_dev
.units
+ i
) -> flags
) & UNIT_DSK_VERBOSE
) return TRUE
;
284 static char* selectInOut(const int32 io
) {
285 return io
== 0 ? "IN" : "OUT";
288 /* service routines to handle simulator functions */
291 static t_stat
dsk_reset(DEVICE
*dptr
) {
292 resetDSKWarningFlags();
293 current_disk
= NUM_OF_DSK
;
300 void install_ALTAIRbootROM(void) {
301 assert(install_bootrom(bootrom_dsk
, BOOTROM_SIZE_DSK
, ALTAIR_ROM_LOW
, TRUE
) == SCPE_OK
);
304 /* The boot routine modifies the boot ROM in such a way that subsequently
305 the specified disk is used for boot purposes.
307 static t_stat
dsk_boot(int32 unitno
, DEVICE
*dptr
) {
308 if (cpu_unit
.flags
& (UNIT_CPU_ALTAIRROM
| UNIT_CPU_BANKED
)) {
309 /* check whether we are really modifying an LD A,<> instruction */
310 if ((bootrom_dsk
[UNIT_NO_OFFSET_1
- 1] == LDA_INSTRUCTION
) && (bootrom_dsk
[UNIT_NO_OFFSET_2
- 1] == LDA_INSTRUCTION
)) {
311 bootrom_dsk
[UNIT_NO_OFFSET_1
] = unitno
& 0xff; /* LD A,<unitno> */
312 bootrom_dsk
[UNIT_NO_OFFSET_2
] = 0x80 | (unitno
& 0xff); /* LD a,80h | <unitno> */
314 else { /* Attempt to modify non LD A,<> instructions is refused. */
315 printf("Incorrect boot ROM offsets detected.\n");
318 install_ALTAIRbootROM(); /* install modified ROM */
320 *((int32
*) sim_PC
->loc
) = ALTAIR_ROM_LOW
;
324 static int32
dskseek(const UNIT
*xptr
) {
325 return fseek(xptr
-> fileref
, DSK_TRACSIZE
* current_track
[current_disk
] +
326 DSK_SECTSIZE
* current_sector
[current_disk
], SEEK_SET
);
329 /* precondition: current_disk < NUM_OF_DSK */
330 static void writebuf(void) {
333 i
= current_byte
[current_disk
]; /* null-fill rest of sector if any */
334 while (i
< DSK_SECTSIZE
)
336 uptr
= dsk_dev
.units
+ current_disk
;
337 if (((uptr
-> flags
) & UNIT_DSK_WLK
) == 0) { /* write enabled */
338 if (trace_level
& TRACE_READ_WRITE
) {
339 MESSAGE_4("OUT 0x0a (WRITE) D%d T%d S%d", current_disk
, current_track
[current_disk
], current_sector
[current_disk
]);
342 MESSAGE_4("fseek failed D%d T%d S%d", current_disk
, current_track
[current_disk
], current_sector
[current_disk
]);
344 rtn
= fwrite(dskbuf
, DSK_SECTSIZE
, 1, uptr
-> fileref
);
346 MESSAGE_4("fwrite failed T%d S%d Return=%d", current_track
[current_disk
], current_sector
[current_disk
], rtn
);
349 else if ( ((uptr
-> flags
) & UNIT_DSK_VERBOSE
) && (warnLock
[current_disk
] < warnLevelDSK
) ) {
350 /* write locked - print warning message if required */
351 warnLock
[current_disk
]++;
352 /*05*/ MESSAGE_2("Attempt to write to locked DSK%d - ignored.", current_disk
);
354 current_flag
[current_disk
] &= 0xfe; /* ENWD off */
355 current_byte
[current_disk
] = 0xff;
359 /* I/O instruction handlers, called from the CPU module when an
360 IN or OUT instruction is issued.
362 Each function is passed an 'io' flag, where 0 means a read from
363 the port, and 1 means a write to the port. On input, the actual
364 input is passed as the return value, on output, 'data' is written
368 /* Disk Controller Status/Select */
370 /* IMPORTANT: The status flags read by port 8 IN instruction are
371 INVERTED, that is, 0 is true and 1 is false. To handle this, the
372 simulator keeps it's own status flags as 0=false, 1=true; and
373 returns the COMPLEMENT of the status flags when read. This makes
374 setting/testing of the flag bits more logical, yet meets the
375 simulation requirement that they are reversed in hardware.
378 int32
dsk10(const int32 port
, const int32 io
, const int32 data
) {
379 int32 current_disk_flags
;
381 if (io
== 0) { /* IN: return flags */
382 if (current_disk
>= NUM_OF_DSK
) {
383 if (hasVerbose() && (warnDSK10
< warnLevelDSK
)) {
385 /*01*/ MESSAGE_1("Attempt of IN 0x08 on unattached disk - ignored.");
387 return 0xff; /* no drive selected - can do nothing */
389 return (~current_flag
[current_disk
]) & 0xff; /* return the COMPLEMENT! */
392 /* OUT: Controller set/reset/enable/disable */
393 if (dirty
) /* implies that current_disk < NUM_OF_DSK */
395 if (trace_level
& TRACE_IN_OUT
) {
396 MESSAGE_2("OUT 0x08: %x", data
);
398 current_disk
= data
& NUM_OF_DSK_MASK
; /* 0 <= current_disk < NUM_OF_DSK */
399 current_disk_flags
= (dsk_dev
.units
+ current_disk
) -> flags
;
400 if ((current_disk_flags
& UNIT_ATT
) == 0) { /* nothing attached? */
401 if ( (current_disk_flags
& UNIT_DSK_VERBOSE
) && (warnAttached
[current_disk
] < warnLevelDSK
) ) {
402 warnAttached
[current_disk
]++;
403 /*02*/ MESSAGE_2("Attempt to select unattached DSK%d - ignored.", current_disk
);
405 current_disk
= NUM_OF_DSK
;
408 current_sector
[current_disk
] = 0xff; /* reset internal counters */
409 current_byte
[current_disk
] = 0xff;
410 current_flag
[current_disk
] = data
& 0x80 ? 0 /* disable drive */ :
411 (current_track
[current_disk
] == 0 ? 0x5a /* enable: head move true, track 0 if there */ :
412 0x1a); /* enable: head move true */
414 return 0; /* ignored since OUT */
417 /* Disk Drive Status/Functions */
419 int32
dsk11(const int32 port
, const int32 io
, const int32 data
) {
420 if (current_disk
>= NUM_OF_DSK
) {
421 if (hasVerbose() && (warnDSK11
< warnLevelDSK
)) {
423 /*03*/ MESSAGE_2("Attempt of %s 0x09 on unattached disk - ignored.", selectInOut(io
));
425 return 0; /* no drive selected - can do nothing */
428 /* now current_disk < NUM_OF_DSK */
429 if (io
== 0) { /* read sector position */
431 if ((trace_level
& TRACE_SECTOR_STUCK
) && (in9_count
> 2 * DSK_SECT
) && (!in9_message
)) {
433 MESSAGE_2("Looping on sector find %d.", current_disk
);
435 if (trace_level
& TRACE_IN_OUT
) {
436 MESSAGE_1("IN 0x09");
438 if (dirty
) /* implies that current_disk < NUM_OF_DSK */
440 if (current_flag
[current_disk
] & 0x04) { /* head loaded? */
441 current_sector
[current_disk
]++;
442 if (current_sector
[current_disk
] >= DSK_SECT
)
443 current_sector
[current_disk
] = 0;
444 current_byte
[current_disk
] = 0xff;
445 return (((current_sector
[current_disk
] << 1) & 0x3e) /* return 'sector true' bit = 0 (true) */
446 | 0xc0); /* set on 'unused' bits */
447 } else return 0; /* head not loaded - return 0 */
451 /* drive functions */
453 if (trace_level
& TRACE_IN_OUT
) {
454 MESSAGE_2("OUT 0x09: %x", data
);
456 if (data
& 0x01) { /* step head in */
457 if ((trace_level
& TRACE_TRACK_STUCK
) && (current_track
[current_disk
] == (tracks
[current_disk
] - 1))) {
458 MESSAGE_2("Unnecessary step in for disk %d", current_disk
);
460 current_track
[current_disk
]++;
461 if (current_track
[current_disk
] > (tracks
[current_disk
] - 1))
462 current_track
[current_disk
] = (tracks
[current_disk
] - 1);
463 if (dirty
) /* implies that current_disk < NUM_OF_DSK */
465 current_sector
[current_disk
] = 0xff;
466 current_byte
[current_disk
] = 0xff;
469 if (data
& 0x02) { /* step head out */
470 if ((trace_level
& TRACE_TRACK_STUCK
) && (current_track
[current_disk
] == 0)) {
471 MESSAGE_2("Unnecessary step out for disk %d", current_disk
);
473 current_track
[current_disk
]--;
474 if (current_track
[current_disk
] < 0) {
475 current_track
[current_disk
] = 0;
476 current_flag
[current_disk
] |= 0x40; /* track 0 if there */
478 if (dirty
) /* implies that current_disk < NUM_OF_DSK */
480 current_sector
[current_disk
] = 0xff;
481 current_byte
[current_disk
] = 0xff;
484 if (dirty
) /* implies that current_disk < NUM_OF_DSK */
487 if (data
& 0x04) { /* head load */
488 current_flag
[current_disk
] |= 0x04; /* turn on head loaded bit */
489 current_flag
[current_disk
] |= 0x80; /* turn on 'read data available' */
492 if (data
& 0x08) { /* head unload */
493 current_flag
[current_disk
] &= 0xfb; /* turn off 'head loaded' bit */
494 current_flag
[current_disk
] &= 0x7f; /* turn off 'read data available' */
495 current_sector
[current_disk
] = 0xff;
496 current_byte
[current_disk
] = 0xff;
499 /* interrupts & head current are ignored */
501 if (data
& 0x80) { /* write sequence start */
502 current_byte
[current_disk
] = 0;
503 current_flag
[current_disk
] |= 0x01; /* enter new write data on */
505 return 0; /* ignored since OUT */
508 /* Disk Data In/Out */
510 int32
dsk12(const int32 port
, const int32 io
, const int32 data
) {
514 if (current_disk
>= NUM_OF_DSK
) {
515 if (hasVerbose() && (warnDSK12
< warnLevelDSK
)) {
517 /*04*/ MESSAGE_2("Attempt of %s 0x0a on unattached disk - ignored.", selectInOut(io
));
522 /* now current_disk < NUM_OF_DSK */
524 uptr
= dsk_dev
.units
+ current_disk
;
526 if (current_byte
[current_disk
] >= DSK_SECTSIZE
) {
527 /* physically read the sector */
528 if (trace_level
& TRACE_READ_WRITE
) {
529 MESSAGE_4("IN 0x0a (READ) D%d T%d S%d", current_disk
, current_track
[current_disk
], current_sector
[current_disk
]);
531 for (i
= 0; i
< DSK_SECTSIZE
; i
++)
534 fread(dskbuf
, DSK_SECTSIZE
, 1, uptr
-> fileref
);
535 current_byte
[current_disk
] = 0;
537 return dskbuf
[current_byte
[current_disk
]++] & 0xff;
540 if (current_byte
[current_disk
] >= DSK_SECTSIZE
)
541 writebuf(); /* from above we have that current_disk < NUM_OF_DSK */
543 dirty
= TRUE
; /* this guarantees for the next call to writebuf that current_disk < NUM_OF_DSK */
544 dskbuf
[current_byte
[current_disk
]++] = data
& 0xff;
546 return 0; /* ignored since OUT */