First Commit of my working state
[simh.git] / AltairZ80 / altairz80_dsk.c
1 /* altairz80_dsk.c: MITS Altair 88-DISK Simulator
2
3 Copyright (c) 2002-2008, Peter Schorn
4
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:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
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.
21
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.
25
26 Based on work by Charles E Owen (c) 1997
27
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
31 each.
32
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).
35
36 Address Mode Function
37 ------- ---- --------
38
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
43 12 Out Write data
44 12 In Read data
45
46 Drive Select Out (Device 10 OUT):
47
48 +---+---+---+---+---+---+---+---+
49 | C | X | X | X | Device |
50 +---+---+---+---+---+---+---+---+
51
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.
55 X = not used
56 Device = value zero thru 15, selects drive to be controlled.
57
58 Drive Status In (Device 10 IN):
59
60 +---+---+---+---+---+---+---+---+
61 | R | Z | I | X | X | H | M | W |
62 +---+---+---+---+---+---+---+---+
63
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
71
72 Drive Control (Device 11 OUT):
73
74 +---+---+---+---+---+---+---+---+
75 | W | C | D | E | U | H | O | I |
76 +---+---+---+---+---+---+---+---+
77
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
92 set this bit.
93
94 Sector Position (Device 11 IN):
95
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.
98
99 +---+---+---+---+---+---+---+---+
100 | X | X | Sector Number | T |
101 +---+---+---+---+---+---+---+---+
102
103 X = Not used
104 Sector number = binary of the sector number currently under the
105 head, 0-31.
106 T = Sector True, is a 1 when the sector is positioned to read or
107 write.
108
109 */
110
111 #include "altairz80_defs.h"
112 #include <assert.h>
113
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 */
130
131
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);
138
139 extern REG *sim_PC;
140 extern UNIT cpu_unit;
141 extern char messageBuffer[];
142 extern uint32 PCX;
143
144 extern void printMessage(void);
145 extern t_stat install_bootrom(int32 bootrom[], int32 size, int32 addr, int32 makeROM);
146 void install_ALTAIRbootROM(void);
147
148 /* global data on status */
149
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 */
170
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 */
205 };
206
207 /* 88DSK Standard I/O Data Structures */
208
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) }
218 };
219
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 },
238 { NULL }
239 };
240
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 },
248 { 0 }
249 };
250
251 DEVICE dsk_dev = {
252 "DSK", dsk_unit, dsk_reg, dsk_mod,
253 8, 10, 31, 1, 8, 8,
254 NULL, NULL, &dsk_reset,
255 &dsk_boot, NULL, NULL,
256 NULL, (DEV_DISABLE), 0,
257 NULL, NULL, NULL
258 };
259
260 static void resetDSKWarningFlags(void) {
261 int32 i;
262 for (i = 0; i < NUM_OF_DSK; i++) {
263 warnLock[i] = 0;
264 warnAttached[i] = 0;
265 }
266 warnDSK10 = 0;
267 warnDSK11 = 0;
268 warnDSK12 = 0;
269 }
270
271 static t_stat dsk_set_verbose(UNIT *uptr, int32 value, char *cptr, void *desc) {
272 resetDSKWarningFlags();
273 return SCPE_OK;
274 }
275
276 /* returns TRUE iff there exists a disk with VERBOSE */
277 static int32 hasVerbose(void) {
278 int32 i;
279 for (i = 0; i < NUM_OF_DSK; i++)
280 if (((dsk_dev.units + i) -> flags) & UNIT_DSK_VERBOSE) return TRUE;
281 return FALSE;
282 }
283
284 static char* selectInOut(const int32 io) {
285 return io == 0 ? "IN" : "OUT";
286 }
287
288 /* service routines to handle simulator functions */
289 /* reset routine */
290
291 static t_stat dsk_reset(DEVICE *dptr) {
292 resetDSKWarningFlags();
293 current_disk = NUM_OF_DSK;
294 trace_level = 0;
295 in9_count = 0;
296 in9_message = FALSE;
297 return SCPE_OK;
298 }
299
300 void install_ALTAIRbootROM(void) {
301 assert(install_bootrom(bootrom_dsk, BOOTROM_SIZE_DSK, ALTAIR_ROM_LOW, TRUE) == SCPE_OK);
302 }
303
304 /* The boot routine modifies the boot ROM in such a way that subsequently
305 the specified disk is used for boot purposes.
306 */
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> */
313 }
314 else { /* Attempt to modify non LD A,<> instructions is refused. */
315 printf("Incorrect boot ROM offsets detected.\n");
316 return SCPE_IERR;
317 }
318 install_ALTAIRbootROM(); /* install modified ROM */
319 }
320 *((int32 *) sim_PC->loc) = ALTAIR_ROM_LOW;
321 return SCPE_OK;
322 }
323
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);
327 }
328
329 /* precondition: current_disk < NUM_OF_DSK */
330 static void writebuf(void) {
331 int32 i, rtn;
332 UNIT *uptr;
333 i = current_byte[current_disk]; /* null-fill rest of sector if any */
334 while (i < DSK_SECTSIZE)
335 dskbuf[i++] = 0;
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]);
340 }
341 if (dskseek(uptr)) {
342 MESSAGE_4("fseek failed D%d T%d S%d", current_disk, current_track[current_disk], current_sector[current_disk]);
343 }
344 rtn = fwrite(dskbuf, DSK_SECTSIZE, 1, uptr -> fileref);
345 if (rtn != 1) {
346 MESSAGE_4("fwrite failed T%d S%d Return=%d", current_track[current_disk], current_sector[current_disk], rtn);
347 }
348 }
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);
353 }
354 current_flag[current_disk] &= 0xfe; /* ENWD off */
355 current_byte[current_disk] = 0xff;
356 dirty = FALSE;
357 }
358
359 /* I/O instruction handlers, called from the CPU module when an
360 IN or OUT instruction is issued.
361
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
365 to the device.
366 */
367
368 /* Disk Controller Status/Select */
369
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.
376 */
377
378 int32 dsk10(const int32 port, const int32 io, const int32 data) {
379 int32 current_disk_flags;
380 in9_count = 0;
381 if (io == 0) { /* IN: return flags */
382 if (current_disk >= NUM_OF_DSK) {
383 if (hasVerbose() && (warnDSK10 < warnLevelDSK)) {
384 warnDSK10++;
385 /*01*/ MESSAGE_1("Attempt of IN 0x08 on unattached disk - ignored.");
386 }
387 return 0xff; /* no drive selected - can do nothing */
388 }
389 return (~current_flag[current_disk]) & 0xff; /* return the COMPLEMENT! */
390 }
391
392 /* OUT: Controller set/reset/enable/disable */
393 if (dirty) /* implies that current_disk < NUM_OF_DSK */
394 writebuf();
395 if (trace_level & TRACE_IN_OUT) {
396 MESSAGE_2("OUT 0x08: %x", data);
397 }
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);
404 }
405 current_disk = NUM_OF_DSK;
406 }
407 else {
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 */
413 }
414 return 0; /* ignored since OUT */
415 }
416
417 /* Disk Drive Status/Functions */
418
419 int32 dsk11(const int32 port, const int32 io, const int32 data) {
420 if (current_disk >= NUM_OF_DSK) {
421 if (hasVerbose() && (warnDSK11 < warnLevelDSK)) {
422 warnDSK11++;
423 /*03*/ MESSAGE_2("Attempt of %s 0x09 on unattached disk - ignored.", selectInOut(io));
424 }
425 return 0; /* no drive selected - can do nothing */
426 }
427
428 /* now current_disk < NUM_OF_DSK */
429 if (io == 0) { /* read sector position */
430 in9_count++;
431 if ((trace_level & TRACE_SECTOR_STUCK) && (in9_count > 2 * DSK_SECT) && (!in9_message)) {
432 in9_message = TRUE;
433 MESSAGE_2("Looping on sector find %d.", current_disk);
434 }
435 if (trace_level & TRACE_IN_OUT) {
436 MESSAGE_1("IN 0x09");
437 }
438 if (dirty) /* implies that current_disk < NUM_OF_DSK */
439 writebuf();
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 */
448 }
449
450 in9_count = 0;
451 /* drive functions */
452
453 if (trace_level & TRACE_IN_OUT) {
454 MESSAGE_2("OUT 0x09: %x", data);
455 }
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);
459 }
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 */
464 writebuf();
465 current_sector[current_disk] = 0xff;
466 current_byte[current_disk] = 0xff;
467 }
468
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);
472 }
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 */
477 }
478 if (dirty) /* implies that current_disk < NUM_OF_DSK */
479 writebuf();
480 current_sector[current_disk] = 0xff;
481 current_byte[current_disk] = 0xff;
482 }
483
484 if (dirty) /* implies that current_disk < NUM_OF_DSK */
485 writebuf();
486
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' */
490 }
491
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;
497 }
498
499 /* interrupts & head current are ignored */
500
501 if (data & 0x80) { /* write sequence start */
502 current_byte[current_disk] = 0;
503 current_flag[current_disk] |= 0x01; /* enter new write data on */
504 }
505 return 0; /* ignored since OUT */
506 }
507
508 /* Disk Data In/Out */
509
510 int32 dsk12(const int32 port, const int32 io, const int32 data) {
511 int32 i;
512 UNIT *uptr;
513
514 if (current_disk >= NUM_OF_DSK) {
515 if (hasVerbose() && (warnDSK12 < warnLevelDSK)) {
516 warnDSK12++;
517 /*04*/ MESSAGE_2("Attempt of %s 0x0a on unattached disk - ignored.", selectInOut(io));
518 }
519 return 0;
520 }
521
522 /* now current_disk < NUM_OF_DSK */
523 in9_count = 0;
524 uptr = dsk_dev.units + current_disk;
525 if (io == 0) {
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]);
530 }
531 for (i = 0; i < DSK_SECTSIZE; i++)
532 dskbuf[i] = 0;
533 dskseek(uptr);
534 fread(dskbuf, DSK_SECTSIZE, 1, uptr -> fileref);
535 current_byte[current_disk] = 0;
536 }
537 return dskbuf[current_byte[current_disk]++] & 0xff;
538 }
539 else {
540 if (current_byte[current_disk] >= DSK_SECTSIZE)
541 writebuf(); /* from above we have that current_disk < NUM_OF_DSK */
542 else {
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;
545 }
546 return 0; /* ignored since OUT */
547 }
548 }