Commit | Line | Data |
---|---|---|
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 | |
33 | static t_stat set_geom(UNIT *uptr, int32 val, char *cptr, void *desc);\r | |
34 | static t_stat show_geom(FILE *st, UNIT *uptr, int32 val, void *desc);\r | |
35 | static t_stat set_format(UNIT *uptr, int32 val, char *cptr, void *desc);\r | |
36 | static t_stat show_format(FILE *st, UNIT *uptr, int32 val, void *desc);\r | |
37 | \r | |
38 | static t_stat hdsk_reset(DEVICE *dptr);\r | |
39 | static 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 | |
64 | extern char messageBuffer[];\r | |
65 | extern uint32 PCX;\r | |
66 | extern REG *sim_PC;\r | |
67 | extern UNIT cpu_unit;\r | |
68 | \r | |
69 | extern void install_ALTAIRbootROM(void);\r | |
70 | extern void printMessage(void);\r | |
71 | extern void PutBYTEWrapper(const uint32 Addr, const uint32 Value);\r | |
72 | extern uint8 GetBYTEWrapper(const uint32 Addr);\r | |
73 | extern t_stat install_bootrom(int32 bootrom[], int32 size, int32 addr, int32 makeROM);\r | |
74 | extern int32 bootrom_dsk[];\r | |
75 | extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc);\r | |
76 | extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc);\r | |
77 | extern 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 | |
80 | static t_stat hdsk_boot(int32 unitno, DEVICE *dptr);\r | |
81 | int32 hdsk_io(const int32 port, const int32 io, const int32 data);\r | |
82 | \r | |
83 | static int32 hdskLastCommand = HDSK_NONE;\r | |
84 | static int32 hdskCommandPosition = 0;\r | |
85 | static int32 paramcount = 0;\r | |
86 | static int32 selectedDisk;\r | |
87 | static int32 selectedSector;\r | |
88 | static int32 selectedTrack;\r | |
89 | static int32 selectedDMA;\r | |
90 | static int32 trace_level = 0;\r | |
91 | \r | |
92 | typedef 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 | |
109 | typedef struct {\r | |
110 | PNP_INFO pnp; /* Plug and Play */\r | |
111 | } HDSK_INFO;\r | |
112 | \r | |
113 | static 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 | |
117 | static 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 | |
137 | static 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 | |
148 | static 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 | |
159 | static 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 | |
172 | DEVICE 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 | |
182 | static 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 | |
198 | static 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 | |
262 | static 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, §orSize, &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, §orSize, &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 | |
281 | static 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 | |
291 | static 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 | |
317 | static 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 | |
323 | static 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 | |
358 | static 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 | |
379 | static 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 | |
437 | static 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 | |
476 | static 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 | |
490 | uint8 hdskbuf[HDSK_MAX_SECTOR_SIZE] = { 0 }; /* data buffer */\r | |
491 | \r | |
492 | static 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 | |
509 | static 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 | |
534 | static 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 | |
577 | static 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 | |
641 | int32 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 |