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