Commit | Line | Data |
---|---|---|
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 | |
132 | int32 dsk10(const int32 port, const int32 io, const int32 data);\r | |
133 | int32 dsk11(const int32 port, const int32 io, const int32 data);\r | |
134 | int32 dsk12(const int32 port, const int32 io, const int32 data);\r | |
135 | static t_stat dsk_boot(int32 unitno, DEVICE *dptr);\r | |
136 | static t_stat dsk_reset(DEVICE *dptr);\r | |
137 | static t_stat dsk_set_verbose(UNIT *uptr, int32 value, char *cptr, void *desc);\r | |
138 | \r | |
139 | extern REG *sim_PC;\r | |
140 | extern UNIT cpu_unit;\r | |
141 | extern char messageBuffer[];\r | |
142 | extern uint32 PCX;\r | |
143 | \r | |
144 | extern void printMessage(void);\r | |
145 | extern t_stat install_bootrom(int32 bootrom[], int32 size, int32 addr, int32 makeROM);\r | |
146 | void 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 | |
152 | static int32 current_disk = NUM_OF_DSK;\r | |
153 | static int32 current_track [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0};\r | |
154 | static int32 current_sector [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0};\r | |
155 | static int32 current_byte [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0};\r | |
156 | static int32 current_flag [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0};\r | |
157 | static uint8 tracks [NUM_OF_DSK] = { MAX_TRACKS, MAX_TRACKS, MAX_TRACKS, MAX_TRACKS,\r | |
158 | MAX_TRACKS, MAX_TRACKS, MAX_TRACKS, MAX_TRACKS };\r | |
159 | static int32 trace_level = 0;\r | |
160 | static int32 in9_count = 0;\r | |
161 | static int32 in9_message = FALSE;\r | |
162 | static int32 dirty = FALSE; /* TRUE when buffer has unwritten data in it */\r | |
163 | static int32 warnLevelDSK = 3;\r | |
164 | static int32 warnLock [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0};\r | |
165 | static int32 warnAttached [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0};\r | |
166 | static int32 warnDSK10 = 0;\r | |
167 | static int32 warnDSK11 = 0;\r | |
168 | static int32 warnDSK12 = 0;\r | |
169 | static int8 dskbuf[DSK_SECTSIZE]; /* data Buffer */\r | |
170 | \r | |
171 | /* Altair MITS modified BOOT EPROM, fits in upper 256 byte of memory */\r | |
172 | int32 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 | |
209 | static 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 | |
220 | static 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 | |
241 | static 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 | |
251 | DEVICE 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 | |
260 | static 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 | |
271 | static 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 | |
277 | static 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 | |
284 | static 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 | |
291 | static 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 | |
300 | void 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 | |
307 | static 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 | |
324 | static 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 | |
330 | static 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 | |
378 | int32 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 | |
419 | int32 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 | |
510 | int32 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 |