First Commit of my working state
[simh.git] / AltairZ80 / altairz80_hdsk.c
CommitLineData
196ba1fc
PH
1/* altairz80_hdsk.c: simulated hard disk device to increase capacity\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 Contains code from Howard M. Harte for defining and changing disk geometry.\r
27*/\r
28\r
29#include "altairz80_defs.h"\r
30#include <assert.h>\r
31\r
32/* The following routines are based on work from Howard M. Harte */\r
33static t_stat set_geom(UNIT *uptr, int32 val, char *cptr, void *desc);\r
34static t_stat show_geom(FILE *st, UNIT *uptr, int32 val, void *desc);\r
35static t_stat set_format(UNIT *uptr, int32 val, char *cptr, void *desc);\r
36static t_stat show_format(FILE *st, UNIT *uptr, int32 val, void *desc);\r
37\r
38static t_stat hdsk_reset(DEVICE *dptr);\r
39static t_stat hdsk_attach(UNIT *uptr, char *cptr);\r
40\r
41#define UNIT_V_HDSK_WLK (UNIT_V_UF + 0) /* write locked */\r
42#define UNIT_HDSK_WLK (1 << UNIT_V_HDSK_WLK)\r
43#define UNIT_V_HDSK_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */\r
44#define UNIT_HDSK_VERBOSE (1 << UNIT_V_HDSK_VERBOSE)\r
45#define HDSK_MAX_SECTOR_SIZE 1024 /* maximum size of a sector */\r
46#define HDSK_SECTOR_SIZE u5 /* size of sector */\r
47#define HDSK_SECTORS_PER_TRACK u4 /* sectors per track */\r
48#define HDSK_NUMBER_OF_TRACKS u3 /* number of tracks */\r
49#define HDSK_FORMAT_TYPE u6 /* Disk Format Type */\r
50#define HDSK_CAPACITY (2048*32*128) /* Default Altair HDSK Capacity */\r
51#define HDSK_NUMBER 8 /* number of hard disks */\r
52#define CPM_OK 0 /* indicates to CP/M everything ok */\r
53#define CPM_ERROR 1 /* indicates to CP/M an error condition */\r
54#define CPM_EMPTY 0xe5 /* default value for non-existing bytes */\r
55#define HDSK_NONE 0\r
56#define HDSK_RESET 1\r
57#define HDSK_READ 2\r
58#define HDSK_WRITE 3\r
59#define HDSK_PARAM 4\r
60#define HDSK_BOOT_ADDRESS 0x5c00\r
61#define DPB_NAME_LENGTH 15\r
62#define BOOTROM_SIZE_HDSK 256\r
63\r
64extern char messageBuffer[];\r
65extern uint32 PCX;\r
66extern REG *sim_PC;\r
67extern UNIT cpu_unit;\r
68\r
69extern void install_ALTAIRbootROM(void);\r
70extern void printMessage(void);\r
71extern void PutBYTEWrapper(const uint32 Addr, const uint32 Value);\r
72extern uint8 GetBYTEWrapper(const uint32 Addr);\r
73extern t_stat install_bootrom(int32 bootrom[], int32 size, int32 addr, int32 makeROM);\r
74extern int32 bootrom_dsk[];\r
75extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc);\r
76extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc);\r
77extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type,\r
78 int32 (*routine)(const int32, const int32, const int32), uint8 unmap);\r
79\r
80static t_stat hdsk_boot(int32 unitno, DEVICE *dptr);\r
81int32 hdsk_io(const int32 port, const int32 io, const int32 data);\r
82\r
83static int32 hdskLastCommand = HDSK_NONE;\r
84static int32 hdskCommandPosition = 0;\r
85static int32 paramcount = 0;\r
86static int32 selectedDisk;\r
87static int32 selectedSector;\r
88static int32 selectedTrack;\r
89static int32 selectedDMA;\r
90static int32 trace_level = 0;\r
91\r
92typedef struct {\r
93 char name[DPB_NAME_LENGTH + 1]; /* name of CP/M disk parameter block */\r
94 t_addr capac; /* capacity */\r
95 uint16 spt; /* sectors per track */\r
96 uint8 bsh; /* data allocation block shift factor */\r
97 uint8 blm; /* data allocation block mask */\r
98 uint8 exm; /* extent mask */\r
99 uint16 dsm; /* maximum data block number */\r
100 uint16 drm; /* total number of directory entries */\r
101 uint8 al0; /* determine reserved directory blocks */\r
102 uint8 al1; /* determine reserved directory blocks */\r
103 uint16 cks; /* size of directory check vector */\r
104 uint16 off; /* number of reserved tracks */\r
105 uint8 psh; /* physical record shift factor, CP/M 3 */\r
106 uint8 phm; /* physical record mask, CP/M 3 */\r
107} DPB;\r
108\r
109typedef struct {\r
110 PNP_INFO pnp; /* Plug and Play */\r
111} HDSK_INFO;\r
112\r
113static HDSK_INFO hdsk_info_data = { { 0x0000, 0, 0xFD, 1 } };\r
114/* static HDSK_INFO *hdsk_info = &hdsk_info_data; */\r
115\r
116/* Note in the following CKS = 0 for fixed media which are not supposed to be changed while CP/M is executing */\r
117static DPB dpb[] = {\r
118/* name capac spt bsh blm exm dsm drm al0 al1 cks off psh phm */\r
119 { "HDSK", HDSK_CAPACITY, 32, 0x05, 0x1F, 0x01, 0x07f9, 0x03FF, 0xFF, 0x00, 0x0000, 0x0006, 0x00, 0x00 }, /* AZ80 HDSK */\r
120 { "EZ80FL", 131072, 32, 0x03, 0x07, 0x00, 127, 0x003E, 0xC0, 0x00, 0x0000, 0x0000, 0x02, 0x03 }, /* 128K FLASH */\r
121 { "P112", 1474560, 72, 0x04, 0x0F, 0x00, 710, 0x00FE, 0xF0, 0x00, 0x0000, 0x0002, 0x02, 0x03 }, /* 1.44M P112 */\r
122 { "SU720", 737280, 36, 0x04, 0x0F, 0x00, 354, 0x007E, 0xC0, 0x00, 0x0020, 0x0002, 0x02, 0x03 }, /* 720K Super I/O */\r
123 { "OSB1", 102400, 20, 0x04, 0x0F, 0x01, 45, 0x003F, 0x80, 0x00, 0x0000, 0x0003, 0x02, 0x03 }, /* Osborne1 5.25" SS SD */\r
124 { "OSB2", 204800, 40, 0x03, 0x07, 0x00, 184, 0x003F, 0xC0, 0x00, 0x0000, 0x0003, 0x02, 0x03 }, /* Osborne1 5.25" SS DD */\r
125 { "NSSS1", 179200, 40, 0x03, 0x07, 0x00, 0xA4, 0x003F, 0xC0, 0x00, 0x0010, 0x0002, 0x02, 0x03 }, /* Northstar SSDD Format 1 */\r
126 { "NSSS2", 179200, 40, 0x04, 0x0F, 0x01, 0x51, 0x003F, 0x80, 0x00, 0x0010, 0x0002, 0x02, 0x03 }, /* Northstar SSDD Format 2 */\r
127 { "NSDS2", 358400, 40, 0x04, 0x0F, 0x01, 0xA9, 0x003F, 0x80, 0x00, 0x0010, 0x0002, 0x02, 0x03 }, /* Northstar DSDD Format 2 */\r
128 { "VGSS", 315392, 32, 0x04, 0x0F, 0x00, 149, 0x007F, 0xC0, 0x00, 0x0020, 0x0002, 0x02, 0x03 }, /* Vector SS SD */\r
129 { "VGDS", 632784, 32, 0x04, 0x0F, 0x00, 299, 0x007F, 0xC0, 0x00, 0x0020, 0x0004, 0x02, 0x03 }, /* Vector DS SD */\r
130 /* Note on DISK1A Images: this is a bit of a mess. The first track on the disk is 128x26 bytes (SD) and to make this work\r
131 I had to "move" the data from 0x2d00 in the DSK image file down to 0x4000 (2-tracks in). I used WinHex to do it. */\r
132 { "DISK1A", 630784, 64, 0x04, 0x0F, 0x00, 299, 0x007F, 0xC0, 0x00, 0x0020, 0x0002, 0x02, 0x03 }, /* CompuPro Disk1A 8" SS SD */\r
133 { "SSSD8", 256256, 26, 0x03, 0x07, 0x00, 242, 0x003F, 0xC0, 0x00, 0x0000, 0x0002, 0x00, 0x00 }, /* Standard 8" SS SD */\r
134 { "", 0 }\r
135};\r
136\r
137static UNIT hdsk_unit[] = {\r
138 { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) },\r
139 { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) },\r
140 { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) },\r
141 { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) },\r
142 { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) },\r
143 { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) },\r
144 { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) },\r
145 { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) }\r
146};\r
147\r
148static REG hdsk_reg[] = {\r
149 { DRDATA (HDCMD, hdskLastCommand, 32), REG_RO },\r
150 { DRDATA (HDPOS, hdskCommandPosition, 32), REG_RO },\r
151 { DRDATA (HDDSK, selectedDisk, 32), REG_RO },\r
152 { DRDATA (HDSEC, selectedSector, 32), REG_RO },\r
153 { DRDATA (HDTRK, selectedTrack, 32), REG_RO },\r
154 { DRDATA (HDDMA, selectedDMA, 32), REG_RO },\r
155 { HRDATA (TRACELEVEL, trace_level, 16), },\r
156 { NULL }\r
157};\r
158\r
159static MTAB hdsk_mod[] = {\r
160 { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL },\r
161 { MTAB_XTD|MTAB_VUN|MTAB_VAL, 0, "FORMAT", "FORMAT", &set_format, &show_format, NULL },\r
162 { UNIT_HDSK_WLK, 0, "WRTENB", "WRTENB", NULL },\r
163 { UNIT_HDSK_WLK, UNIT_HDSK_WLK, "WRTLCK", "WRTLCK", NULL },\r
164 /* quiet, no warning messages */\r
165 { UNIT_HDSK_VERBOSE, 0, "QUIET", "QUIET", NULL },\r
166 /* verbose, show warning messages */\r
167 { UNIT_HDSK_VERBOSE, UNIT_HDSK_VERBOSE, "VERBOSE", "VERBOSE", NULL },\r
168 { MTAB_XTD|MTAB_VUN|MTAB_VAL, 0, "GEOM", "GEOM", &set_geom, &show_geom, NULL },\r
169 { 0 }\r
170};\r
171\r
172DEVICE hdsk_dev = {\r
173 "HDSK", hdsk_unit, hdsk_reg, hdsk_mod,\r
174 8, 10, 31, 1, 8, 8,\r
175 NULL, NULL, &hdsk_reset,\r
176 &hdsk_boot, &hdsk_attach, NULL,\r
177 &hdsk_info_data, (DEV_DISABLE), 0,\r
178 NULL, NULL, NULL\r
179};\r
180\r
181/* Reset routine */\r
182static t_stat hdsk_reset(DEVICE *dptr) {\r
183 PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt;\r
184 if (dptr->flags & DEV_DIS) {\r
185 sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &hdsk_io, TRUE);\r
186 } else {\r
187 /* Connect HDSK at base address */\r
188 if (sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &hdsk_io, FALSE) != 0) {\r
189 printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->mem_base);\r
190 dptr->flags |= DEV_DIS;\r
191 return SCPE_ARG;\r
192 }\r
193 }\r
194 return SCPE_OK;\r
195}\r
196\r
197/* Attach routine */\r
198static t_stat hdsk_attach(UNIT *uptr, char *cptr) {\r
199 t_stat r;\r
200 uint32 i;\r
201 char unitChar;\r
202\r
203 r = attach_unit(uptr, cptr); /* attach unit */\r
204 if ( r != SCPE_OK) /* error? */\r
205 return r;\r
206\r
207 /* Step 1: Determine capacity of this disk */\r
208 uptr -> capac = sim_fsize(uptr -> fileref); /* the file length is a good candidate */\r
209 if (uptr -> capac == 0) { /* file does not exist or has length 0 */\r
210 uptr -> capac = uptr -> HDSK_NUMBER_OF_TRACKS *\r
211 uptr -> HDSK_SECTORS_PER_TRACK * uptr -> HDSK_SECTOR_SIZE;\r
212 if (uptr -> capac == 0)\r
213 uptr -> capac = HDSK_CAPACITY;\r
214 } /* post condition: uptr -> capac > 0 */\r
215 assert(uptr -> capac);\r
216\r
217 /* Step 2: Determine format based on disk capacity */\r
218 uptr -> HDSK_FORMAT_TYPE = -1; /* default to unknown format type */\r
219 for (i = 0; dpb[i].capac != 0; i++) { /* find disk parameter block */\r
220 if (dpb[i].capac == uptr -> capac) { /* found if correct capacity */\r
221 uptr -> HDSK_FORMAT_TYPE = i;\r
222 break;\r
223 }\r
224 }\r
225\r
226 /* Step 3: Set number of sectors per track and sector size */\r
227 if (uptr -> HDSK_FORMAT_TYPE == -1) { /* Case 1: no disk parameter block found*/\r
228 for (i = 0; i < hdsk_dev.numunits; i++) /* find affected unit number */\r
229 if (&hdsk_unit[i] == uptr)\r
230 break; /* found */\r
231 unitChar = '0' + i;\r
232 uptr -> HDSK_FORMAT_TYPE = 0;\r
233 printf("HDSK%c: WARNING: Unsupported disk capacity, assuming HDSK type with capacity %iKB.\n",\r
234 unitChar, uptr -> capac / 1000);\r
235 uptr -> flags |= UNIT_HDSK_WLK;\r
236 printf("HDSK%c: WARNING: Forcing WRTLCK.\n", unitChar);\r
237 /* check whether capacity corresponds to setting of tracks, sectors per track and sector size */\r
238 if (uptr -> capac != (uptr -> HDSK_NUMBER_OF_TRACKS *\r
239 uptr -> HDSK_SECTORS_PER_TRACK * uptr -> HDSK_SECTOR_SIZE)) {\r
240 printf("HDSK%c: WARNING: Fixing geometry.\n", unitChar);\r
241 if (uptr -> HDSK_SECTORS_PER_TRACK == 0) uptr -> HDSK_SECTORS_PER_TRACK = 32;\r
242 if (uptr -> HDSK_SECTOR_SIZE == 0) uptr -> HDSK_SECTOR_SIZE = 128;\r
243 }\r
244 }\r
245 else { /* Case 2: disk parameter block found */\r
246 uptr -> HDSK_SECTORS_PER_TRACK = dpb[uptr -> HDSK_FORMAT_TYPE].spt >> dpb[uptr -> HDSK_FORMAT_TYPE].psh;\r
247 uptr -> HDSK_SECTOR_SIZE = (128 << dpb[uptr -> HDSK_FORMAT_TYPE].psh);\r
248 }\r
249 assert(uptr -> HDSK_SECTORS_PER_TRACK && uptr -> HDSK_SECTOR_SIZE);\r
250\r
251 /* Step 4: Number of tracks is smallest number to accomodate capacity */\r
252 uptr -> HDSK_NUMBER_OF_TRACKS = (uptr -> capac + uptr -> HDSK_SECTORS_PER_TRACK *\r
253 uptr -> HDSK_SECTOR_SIZE - 1) / (uptr -> HDSK_SECTORS_PER_TRACK * uptr -> HDSK_SECTOR_SIZE);\r
254 assert( ( (t_addr) ((uptr -> HDSK_NUMBER_OF_TRACKS - 1) * uptr -> HDSK_SECTORS_PER_TRACK *\r
255 uptr -> HDSK_SECTOR_SIZE) < uptr -> capac) &&\r
256 (uptr -> capac <= (t_addr) (uptr -> HDSK_NUMBER_OF_TRACKS *\r
257 uptr -> HDSK_SECTORS_PER_TRACK * uptr -> HDSK_SECTOR_SIZE) ) );\r
258 return SCPE_OK;\r
259}\r
260\r
261/* Set disk geometry routine */\r
262static t_stat set_geom(UNIT *uptr, int32 val, char *cptr, void *desc) {\r
263 uint32 numberOfTracks, numberOfSectors, sectorSize;\r
264 int result, n;\r
265\r
266 if (cptr == NULL) return SCPE_ARG;\r
267 if (uptr == NULL) return SCPE_IERR;\r
268 result = sscanf(cptr, "%d/%d/%d%n", &numberOfTracks, &numberOfSectors, &sectorSize, &n);\r
269 if ((result != 3) || (result == EOF) || (cptr[n] != 0)) {\r
270 result = sscanf(cptr, "T:%d/N:%d/S:%d%n", &numberOfTracks, &numberOfSectors, &sectorSize, &n);\r
271 if ((result != 3) || (result == EOF) || (cptr[n] != 0)) return SCPE_ARG;\r
272 }\r
273 uptr -> HDSK_NUMBER_OF_TRACKS = numberOfTracks;\r
274 uptr -> HDSK_SECTORS_PER_TRACK = numberOfSectors;\r
275 uptr -> HDSK_SECTOR_SIZE = sectorSize;\r
276 uptr -> capac = numberOfTracks * numberOfSectors * sectorSize;\r
277 return SCPE_OK;\r
278}\r
279\r
280/* Show disk geometry routine */\r
281static t_stat show_geom(FILE *st, UNIT *uptr, int32 val, void *desc) {\r
282 if (uptr == NULL) return SCPE_IERR;\r
283 fprintf(st, "T:%d/N:%d/S:%d", uptr -> HDSK_NUMBER_OF_TRACKS,\r
284 uptr -> HDSK_SECTORS_PER_TRACK, uptr -> HDSK_SECTOR_SIZE);\r
285 return SCPE_OK;\r
286}\r
287\r
288#define QUOTE1(text) #text\r
289#define QUOTE2(text) QUOTE1(text)\r
290/* Set disk format routine */\r
291static t_stat set_format(UNIT *uptr, int32 val, char *cptr, void *desc) {\r
292 char fmtname[DPB_NAME_LENGTH + 1];\r
293 int32 i;\r
294\r
295 if (cptr == NULL) return SCPE_ARG;\r
296 if (uptr == NULL) return SCPE_IERR;\r
297 if (sscanf(cptr, "%" QUOTE2(DPB_NAME_LENGTH) "s", fmtname) == 0) return SCPE_ARG;\r
298 for (i = 0; dpb[i].capac != 0; i++) {\r
299 if (strncmp(fmtname, dpb[i].name, strlen(fmtname)) == 0) {\r
300 uptr -> HDSK_FORMAT_TYPE = i;\r
301 uptr -> capac = dpb[i].capac; /* Set capacity */\r
302\r
303 /* Configure physical disk geometry */\r
304 uptr -> HDSK_SECTOR_SIZE = (128 << dpb[uptr -> HDSK_FORMAT_TYPE].psh);\r
305 uptr -> HDSK_SECTORS_PER_TRACK = dpb[uptr -> HDSK_FORMAT_TYPE].spt >> dpb[uptr -> HDSK_FORMAT_TYPE].psh;\r
306 uptr -> HDSK_NUMBER_OF_TRACKS = (uptr -> capac +\r
307 uptr -> HDSK_SECTORS_PER_TRACK * uptr -> HDSK_SECTOR_SIZE - 1) /\r
308 (uptr -> HDSK_SECTORS_PER_TRACK * uptr -> HDSK_SECTOR_SIZE);\r
309\r
310 return SCPE_OK;\r
311 }\r
312 }\r
313 return SCPE_ARG;\r
314}\r
315\r
316/* Show disk format routine */\r
317static t_stat show_format(FILE *st, UNIT *uptr, int32 val, void *desc) {\r
318 if (uptr == NULL) return SCPE_IERR;\r
319 fprintf(st, "%s", dpb[uptr -> HDSK_FORMAT_TYPE].name);\r
320 return SCPE_OK;\r
321}\r
322\r
323static int32 bootrom_hdsk[BOOTROM_SIZE_HDSK] = {\r
324 0xf3, 0x06, 0x80, 0x3e, 0x0e, 0xd3, 0xfe, 0x05, /* 5c00-5c07 */\r
325 0xc2, 0x05, 0x5c, 0x3e, 0x16, 0xd3, 0xfe, 0x3e, /* 5c08-5c0f */\r
326 0x12, 0xd3, 0xfe, 0xdb, 0xfe, 0xb7, 0xca, 0x20, /* 5c10-5c17 */\r
327 0x5c, 0x3e, 0x0c, 0xd3, 0xfe, 0xaf, 0xd3, 0xfe, /* 5c18-5c1f */\r
328 0x06, 0x20, 0x3e, 0x01, 0xd3, 0xfd, 0x05, 0xc2, /* 5c20-5c27 */\r
329 0x24, 0x5c, 0x11, 0x08, 0x00, 0x21, 0x00, 0x00, /* 5c28-5c2f */\r
330 0x0e, 0xb8, 0x3e, 0x02, 0xd3, 0xfd, 0x3a, 0x37, /* 5c30-5c37 */\r
331 0xff, 0xd6, 0x08, 0xd3, 0xfd, 0x7b, 0xd3, 0xfd, /* 5c38-5c3f */\r
332 0x7a, 0xd3, 0xfd, 0xaf, 0xd3, 0xfd, 0x7d, 0xd3, /* 5c40-5c47 */\r
333 0xfd, 0x7c, 0xd3, 0xfd, 0xdb, 0xfd, 0xb7, 0xca, /* 5c48-5c4f */\r
334 0x53, 0x5c, 0x76, 0x79, 0x0e, 0x80, 0x09, 0x4f, /* 5c50-5c57 */\r
335 0x0d, 0xc2, 0x60, 0x5c, 0xfb, 0xc3, 0x00, 0x00, /* 5c58-5c5f */\r
336 0x1c, 0x1c, 0x7b, 0xfe, 0x20, 0xca, 0x73, 0x5c, /* 5c60-5c67 */\r
337 0xfe, 0x21, 0xc2, 0x32, 0x5c, 0x1e, 0x00, 0x14, /* 5c68-5c6f */\r
338 0xc3, 0x32, 0x5c, 0x1e, 0x01, 0xc3, 0x32, 0x5c, /* 5c70-5c77 */\r
339 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c78-5c7f */\r
340 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c80-5c87 */\r
341 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c88-5c8f */\r
342 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c90-5c97 */\r
343 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c98-5c9f */\r
344 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5ca0-5ca7 */\r
345 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5ca8-5caf */\r
346 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cb0-5cb7 */\r
347 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cb8-5cbf */\r
348 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cc0-5cc7 */\r
349 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cc8-5ccf */\r
350 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cd0-5cd7 */\r
351 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cd8-5cdf */\r
352 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5ce0-5ce7 */\r
353 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5ce8-5cef */\r
354 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cf0-5cf7 */\r
355 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cf8-5cff */\r
356};\r
357\r
358static t_stat hdsk_boot(int32 unitno, DEVICE *dptr) {\r
359 if (MEMORYSIZE < 24*KB) {\r
360 printf("Need at least 24KB RAM to boot from hard disk.\n");\r
361 return SCPE_ARG;\r
362 }\r
363 if (cpu_unit.flags & (UNIT_CPU_ALTAIRROM | UNIT_CPU_BANKED)) {\r
364 /* check whether we are really modifying an LD A,<> instruction */\r
365 if (bootrom_dsk[UNIT_NO_OFFSET_1 - 1] == LDA_INSTRUCTION)\r
366 bootrom_dsk[UNIT_NO_OFFSET_1] = (unitno + NUM_OF_DSK) & 0xff; /* LD A,<unitno> */\r
367 else { /* Attempt to modify non LD A,<> instructions is refused. */\r
368 printf("Incorrect boot ROM offset detected.\n");\r
369 return SCPE_IERR;\r
370 }\r
371 install_ALTAIRbootROM(); /* install modified ROM */\r
372 }\r
373 assert(install_bootrom(bootrom_hdsk, BOOTROM_SIZE_HDSK, HDSK_BOOT_ADDRESS, FALSE) == SCPE_OK);\r
374 *((int32 *) sim_PC->loc) = HDSK_BOOT_ADDRESS;\r
375 return SCPE_OK;\r
376}\r
377\r
378/* returns TRUE iff there exists a disk with VERBOSE */\r
379static int32 hdsk_hasVerbose(void) {\r
380 int32 i;\r
381 for (i = 0; i < HDSK_NUMBER; i++)\r
382 if (hdsk_dev.units[i].flags & UNIT_HDSK_VERBOSE) return TRUE;\r
383 return FALSE;\r
384}\r
385\r
386/* The hard disk port is 0xfd. It understands the following commands.\r
387\r
388 1. Reset\r
389 ld b,32\r
390 ld a,HDSK_RESET\r
391 l: out (0fdh),a\r
392 dec b\r
393 jp nz,l\r
394\r
395 2. Read / write\r
396 ; parameter block\r
397 cmd: db HDSK_READ or HDSK_WRITE\r
398 hd: db 0 ; 0 .. 7, defines hard disk to be used\r
399 sector: db 0 ; 0 .. 31, defines sector\r
400 track: dw 0 ; 0 .. 2047, defines track\r
401 dma: dw 0 ; defines where result is placed in memory\r
402\r
403 ; routine to execute\r
404 ld b,7 ; size of parameter block\r
405 ld hl,cmd ; start address of parameter block\r
406 l: ld a,(hl) ; get byte of parameter block\r
407 out (0fdh),a ; send it to port\r
408 inc hl ; point to next byte\r
409 dec b ; decrement counter\r
410 jp nz,l ; again, if not done\r
411 in a,(0fdh) ; get result code\r
412\r
413 3. Retrieve Disk Parameters from controller (Howard M. Harte)\r
414 Reads a 19-byte parameter block from the disk controller.\r
415 This parameter block is in CP/M DPB format for the first 17 bytes,\r
416 and the last two bytes are the lsb/msb of the disk's physical\r
417 sector size.\r
418\r
419 ; routine to execute\r
420 ld a,hdskParam ; hdskParam = 4\r
421 out (hdskPort),a ; Send 'get parameters' command, hdskPort = 0fdh\r
422 ld a,(diskno)\r
423 out (hdskPort),a ; Send selected HDSK number\r
424 ld b,17\r
425 1: in a,(hdskPort) ; Read 17-bytes of DPB\r
426 ld (hl), a\r
427 inc hl\r
428 djnz 1\r
429 in a,(hdskPort) ; Read LSB of disk's physical sector size.\r
430 ld (hsecsiz), a\r
431 in a,(hdskPort) ; Read MSB of disk's physical sector size.\r
432 ld (hsecsiz+1), a\r
433\r
434*/\r
435\r
436/* check the parameters and return TRUE iff parameters are correct or have been repaired */\r
437static int32 checkParameters(void) {\r
438 UNIT *uptr = &hdsk_dev.units[selectedDisk];\r
439 int32 currentFlag;\r
440 if ((selectedDisk < 0) || (selectedDisk >= HDSK_NUMBER)) {\r
441 if (hdsk_hasVerbose()) {\r
442 MESSAGE_2("HDSK%d does not exist, will use HDSK0 instead.", selectedDisk);\r
443 }\r
444 selectedDisk = 0;\r
445 }\r
446 currentFlag = hdsk_dev.units[selectedDisk].flags;\r
447 if ((currentFlag & UNIT_ATT) == 0) {\r
448 if (currentFlag & UNIT_HDSK_VERBOSE) {\r
449 MESSAGE_2("HDSK%d is not attached.", selectedDisk);\r
450 }\r
451 return FALSE; /* cannot read or write */\r
452 }\r
453 if ((selectedSector < 0) || (selectedSector >= uptr -> HDSK_SECTORS_PER_TRACK)) {\r
454 if (currentFlag & UNIT_HDSK_VERBOSE) {\r
455 MESSAGE_4("HDSK%d: 0 <= Sector=%02d < %d violated, will use 0 instead.",\r
456 selectedDisk, selectedSector, uptr -> HDSK_SECTORS_PER_TRACK);\r
457 }\r
458 selectedSector = 0;\r
459 }\r
460 if ((selectedTrack < 0) || (selectedTrack >= uptr -> HDSK_NUMBER_OF_TRACKS)) {\r
461 if (currentFlag & UNIT_HDSK_VERBOSE) {\r
462 MESSAGE_4("HDSK%d: 0 <= Track=%04d < %04d violated, will use 0 instead.",\r
463 selectedDisk, selectedTrack, uptr -> HDSK_NUMBER_OF_TRACKS);\r
464 }\r
465 selectedTrack = 0;\r
466 }\r
467 selectedDMA &= ADDRMASK;\r
468 if (trace_level) {\r
469 MESSAGE_7("%s HDSK%d Track=%04d Sector=%02d Len=%04d DMA=%04x",\r
470 (hdskLastCommand == HDSK_READ) ? "Read" : "Write",\r
471 selectedDisk, selectedTrack, selectedSector, uptr -> HDSK_SECTOR_SIZE, selectedDMA);\r
472 }\r
473 return TRUE;\r
474}\r
475\r
476static int32 doSeek(void) {\r
477 UNIT *uptr = &hdsk_dev.units[selectedDisk];\r
478 if (fseek(uptr -> fileref,\r
479 (uptr -> HDSK_SECTORS_PER_TRACK * uptr -> HDSK_SECTOR_SIZE) * selectedTrack +\r
480 (uptr -> HDSK_SECTOR_SIZE * selectedSector), SEEK_SET)) {\r
481 if ((uptr -> flags) & UNIT_HDSK_VERBOSE) {\r
482 MESSAGE_4("Could not access HDSK%d Sector=%02d Track=%04d.",\r
483 selectedDisk, selectedSector, selectedTrack);\r
484 }\r
485 return CPM_ERROR;\r
486 }\r
487 else return CPM_OK;\r
488}\r
489\r
490uint8 hdskbuf[HDSK_MAX_SECTOR_SIZE] = { 0 }; /* data buffer */\r
491\r
492static int32 doRead(void) {\r
493 int32 i;\r
494 UNIT *uptr = &hdsk_dev.units[selectedDisk];\r
495 if (doSeek()) return CPM_ERROR;\r
496\r
497 if (fread(hdskbuf, uptr -> HDSK_SECTOR_SIZE, 1, uptr -> fileref) != 1) {\r
498 for (i = 0; i < uptr -> HDSK_SECTOR_SIZE; i++) hdskbuf[i] = CPM_EMPTY;\r
499 if ((uptr -> flags) & UNIT_HDSK_VERBOSE) {\r
500 MESSAGE_4("Could not read HDSK%d Sector=%02d Track=%04d.",\r
501 selectedDisk, selectedSector, selectedTrack);\r
502 }\r
503 return CPM_OK; /* allows the creation of empty hard disks */\r
504 }\r
505 for (i = 0; i < uptr -> HDSK_SECTOR_SIZE; i++) PutBYTEWrapper(selectedDMA + i, hdskbuf[i]);\r
506 return CPM_OK;\r
507}\r
508\r
509static int32 doWrite(void) {\r
510 int32 i;\r
511\r
512 UNIT *uptr = &hdsk_dev.units[selectedDisk];\r
513 if (((uptr -> flags) & UNIT_HDSK_WLK) == 0) { /* write enabled */\r
514 if (doSeek()) return CPM_ERROR;\r
515 for (i = 0; i < uptr -> HDSK_SECTOR_SIZE; i++) hdskbuf[i] = GetBYTEWrapper(selectedDMA + i);\r
516 if (fwrite(hdskbuf, uptr -> HDSK_SECTOR_SIZE, 1, uptr -> fileref) != 1) {\r
517 if ((uptr -> flags) & UNIT_HDSK_VERBOSE) {\r
518 MESSAGE_4("Could not write HDSK%d Sector=%02d Track=%04d.",\r
519 selectedDisk, selectedSector, selectedTrack);\r
520 }\r
521 return CPM_ERROR;\r
522 }\r
523 }\r
524 else {\r
525 if ((uptr -> flags) & UNIT_HDSK_VERBOSE) {\r
526 MESSAGE_4("Could not write to locked HDSK%d Sector=%02d Track=%04d.",\r
527 selectedDisk, selectedSector, selectedTrack);\r
528 }\r
529 return CPM_ERROR;\r
530 }\r
531 return CPM_OK;\r
532}\r
533\r
534static int32 hdsk_in(const int32 port) {\r
535 UNIT *uptr = &hdsk_dev.units[selectedDisk];\r
536\r
537 int32 result;\r
538 if ((hdskCommandPosition == 6) && ((hdskLastCommand == HDSK_READ) || (hdskLastCommand == HDSK_WRITE))) {\r
539 result = checkParameters() ? ((hdskLastCommand == HDSK_READ) ? doRead() : doWrite()) : CPM_ERROR;\r
540 hdskLastCommand = HDSK_NONE;\r
541 hdskCommandPosition = 0;\r
542 return result;\r
543 } else if (hdskLastCommand == HDSK_PARAM) {\r
544 DPB current = dpb[uptr -> HDSK_FORMAT_TYPE];\r
545 uint8 params[17];\r
546 params[ 0] = current.spt & 0xff; params[ 1] = (current.spt >> 8) & 0xff;\r
547 params[ 2] = current.bsh;\r
548 params[ 3] = current.blm;\r
549 params[ 4] = current.exm;\r
550 params[ 5] = current.dsm & 0xff; params[ 6] = (current.dsm >> 8) & 0xff;\r
551 params[ 7] = current.drm & 0xff; params[ 8] = (current.drm >> 8) & 0xff;\r
552 params[ 9] = current.al0;\r
553 params[10] = current.al1;\r
554 params[11] = current.cks & 0xff; params[12] = (current.cks >> 8) & 0xff;\r
555 params[13] = current.off & 0xff; params[14] = (current.off >> 8) & 0xff;\r
556 params[15] = current.psh;\r
557 params[16] = current.phm;\r
558 if (++paramcount >= 19) hdskLastCommand = HDSK_NONE;\r
559 if (paramcount <= 17)\r
560 return params[paramcount - 1];\r
561 else if (paramcount == 18)\r
562 return (uptr -> HDSK_SECTOR_SIZE & 0xff);\r
563 else if (paramcount == 19)\r
564 return (uptr -> HDSK_SECTOR_SIZE >> 8);\r
565 else {\r
566 MESSAGE_2("HDSK%d Get parameter error.", selectedDisk);\r
567 }\r
568\r
569 }\r
570 else if (hdsk_hasVerbose()) {\r
571 MESSAGE_4("Illegal IN command detected (port=%03xh, cmd=%d, pos=%d).",\r
572 port, hdskLastCommand, hdskCommandPosition);\r
573 }\r
574 return CPM_OK;\r
575}\r
576\r
577static int32 hdsk_out(const int32 port, const int32 data) {\r
578 switch(hdskLastCommand) {\r
579\r
580 case HDSK_PARAM:\r
581 paramcount = 0;\r
582 selectedDisk = data;\r
583 break;\r
584\r
585 case HDSK_READ:\r
586\r
587 case HDSK_WRITE:\r
588 switch(hdskCommandPosition) {\r
589\r
590 case 0:\r
591 selectedDisk = data;\r
592 hdskCommandPosition++;\r
593 break;\r
594\r
595 case 1:\r
596 selectedSector = data;\r
597 hdskCommandPosition++;\r
598 break;\r
599\r
600 case 2:\r
601 selectedTrack = data;\r
602 hdskCommandPosition++;\r
603 break;\r
604\r
605 case 3:\r
606 selectedTrack += (data << 8);\r
607 hdskCommandPosition++;\r
608 break;\r
609\r
610 case 4:\r
611 selectedDMA = data;\r
612 hdskCommandPosition++;\r
613 break;\r
614\r
615 case 5:\r
616 selectedDMA += (data << 8);\r
617 hdskCommandPosition++;\r
618 break;\r
619\r
620 default:\r
621 hdskLastCommand = HDSK_NONE;\r
622 hdskCommandPosition = 0;\r
623 }\r
624 break;\r
625\r
626 default:\r
627 if ((HDSK_RESET <= data) && (data <= HDSK_PARAM))\r
628 hdskLastCommand = data;\r
629 else {\r
630 if (hdsk_hasVerbose()) {\r
631 MESSAGE_3("Illegal OUT command detected (port=%03xh, cmd=%d).",\r
632 port, data);\r
633 }\r
634 hdskLastCommand = HDSK_RESET;\r
635 }\r
636 hdskCommandPosition = 0;\r
637 }\r
638 return 0; /* ignored, since OUT */\r
639}\r
640\r
641int32 hdsk_io(const int32 port, const int32 io, const int32 data) {\r
642 return io == 0 ? hdsk_in(port) : hdsk_out(port, data);\r
643}\r