1 /* altairz80_hdsk.c: simulated hard disk device to increase capacity
3 Copyright (c) 2002-2008, Peter Schorn
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following conditions:
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 Except as contained in this notice, the name of Peter Schorn shall not
23 be used in advertising or otherwise to promote the sale, use or other dealings
24 in this Software without prior written authorization from Peter Schorn.
26 Contains code from Howard M. Harte for defining and changing disk geometry.
29 #include "altairz80_defs.h"
32 /* The following routines are based on work from Howard M. Harte */
33 static t_stat
set_geom(UNIT
*uptr
, int32 val
, char *cptr
, void *desc
);
34 static t_stat
show_geom(FILE *st
, UNIT
*uptr
, int32 val
, void *desc
);
35 static t_stat
set_format(UNIT
*uptr
, int32 val
, char *cptr
, void *desc
);
36 static t_stat
show_format(FILE *st
, UNIT
*uptr
, int32 val
, void *desc
);
38 static t_stat
hdsk_reset(DEVICE
*dptr
);
39 static t_stat
hdsk_attach(UNIT
*uptr
, char *cptr
);
41 #define UNIT_V_HDSK_WLK (UNIT_V_UF + 0) /* write locked */
42 #define UNIT_HDSK_WLK (1 << UNIT_V_HDSK_WLK)
43 #define UNIT_V_HDSK_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */
44 #define UNIT_HDSK_VERBOSE (1 << UNIT_V_HDSK_VERBOSE)
45 #define HDSK_MAX_SECTOR_SIZE 1024 /* maximum size of a sector */
46 #define HDSK_SECTOR_SIZE u5 /* size of sector */
47 #define HDSK_SECTORS_PER_TRACK u4 /* sectors per track */
48 #define HDSK_NUMBER_OF_TRACKS u3 /* number of tracks */
49 #define HDSK_FORMAT_TYPE u6 /* Disk Format Type */
50 #define HDSK_CAPACITY (2048*32*128) /* Default Altair HDSK Capacity */
51 #define HDSK_NUMBER 8 /* number of hard disks */
52 #define CPM_OK 0 /* indicates to CP/M everything ok */
53 #define CPM_ERROR 1 /* indicates to CP/M an error condition */
54 #define CPM_EMPTY 0xe5 /* default value for non-existing bytes */
60 #define HDSK_BOOT_ADDRESS 0x5c00
61 #define DPB_NAME_LENGTH 15
62 #define BOOTROM_SIZE_HDSK 256
64 extern char messageBuffer
[];
69 extern void install_ALTAIRbootROM(void);
70 extern void printMessage(void);
71 extern void PutBYTEWrapper(const uint32 Addr
, const uint32 Value
);
72 extern uint8
GetBYTEWrapper(const uint32 Addr
);
73 extern t_stat
install_bootrom(int32 bootrom
[], int32 size
, int32 addr
, int32 makeROM
);
74 extern int32 bootrom_dsk
[];
75 extern t_stat
set_iobase(UNIT
*uptr
, int32 val
, char *cptr
, void *desc
);
76 extern t_stat
show_iobase(FILE *st
, UNIT
*uptr
, int32 val
, void *desc
);
77 extern uint32
sim_map_resource(uint32 baseaddr
, uint32 size
, uint32 resource_type
,
78 int32 (*routine
)(const int32
, const int32
, const int32
), uint8 unmap
);
80 static t_stat
hdsk_boot(int32 unitno
, DEVICE
*dptr
);
81 int32
hdsk_io(const int32 port
, const int32 io
, const int32 data
);
83 static int32 hdskLastCommand
= HDSK_NONE
;
84 static int32 hdskCommandPosition
= 0;
85 static int32 paramcount
= 0;
86 static int32 selectedDisk
;
87 static int32 selectedSector
;
88 static int32 selectedTrack
;
89 static int32 selectedDMA
;
90 static int32 trace_level
= 0;
93 char name
[DPB_NAME_LENGTH
+ 1]; /* name of CP/M disk parameter block */
94 t_addr capac
; /* capacity */
95 uint16 spt
; /* sectors per track */
96 uint8 bsh
; /* data allocation block shift factor */
97 uint8 blm
; /* data allocation block mask */
98 uint8 exm
; /* extent mask */
99 uint16 dsm
; /* maximum data block number */
100 uint16 drm
; /* total number of directory entries */
101 uint8 al0
; /* determine reserved directory blocks */
102 uint8 al1
; /* determine reserved directory blocks */
103 uint16 cks
; /* size of directory check vector */
104 uint16 off
; /* number of reserved tracks */
105 uint8 psh
; /* physical record shift factor, CP/M 3 */
106 uint8 phm
; /* physical record mask, CP/M 3 */
110 PNP_INFO pnp
; /* Plug and Play */
113 static HDSK_INFO hdsk_info_data
= { { 0x0000, 0, 0xFD, 1 } };
114 /* static HDSK_INFO *hdsk_info = &hdsk_info_data; */
116 /* Note in the following CKS = 0 for fixed media which are not supposed to be changed while CP/M is executing */
118 /* name capac spt bsh blm exm dsm drm al0 al1 cks off psh phm */
119 { "HDSK", HDSK_CAPACITY
, 32, 0x05, 0x1F, 0x01, 0x07f9, 0x03FF, 0xFF, 0x00, 0x0000, 0x0006, 0x00, 0x00 }, /* AZ80 HDSK */
120 { "EZ80FL", 131072, 32, 0x03, 0x07, 0x00, 127, 0x003E, 0xC0, 0x00, 0x0000, 0x0000, 0x02, 0x03 }, /* 128K FLASH */
121 { "P112", 1474560, 72, 0x04, 0x0F, 0x00, 710, 0x00FE, 0xF0, 0x00, 0x0000, 0x0002, 0x02, 0x03 }, /* 1.44M P112 */
122 { "SU720", 737280, 36, 0x04, 0x0F, 0x00, 354, 0x007E, 0xC0, 0x00, 0x0020, 0x0002, 0x02, 0x03 }, /* 720K Super I/O */
123 { "OSB1", 102400, 20, 0x04, 0x0F, 0x01, 45, 0x003F, 0x80, 0x00, 0x0000, 0x0003, 0x02, 0x03 }, /* Osborne1 5.25" SS SD */
124 { "OSB2", 204800, 40, 0x03, 0x07, 0x00, 184, 0x003F, 0xC0, 0x00, 0x0000, 0x0003, 0x02, 0x03 }, /* Osborne1 5.25" SS DD */
125 { "NSSS1", 179200, 40, 0x03, 0x07, 0x00, 0xA4, 0x003F, 0xC0, 0x00, 0x0010, 0x0002, 0x02, 0x03 }, /* Northstar SSDD Format 1 */
126 { "NSSS2", 179200, 40, 0x04, 0x0F, 0x01, 0x51, 0x003F, 0x80, 0x00, 0x0010, 0x0002, 0x02, 0x03 }, /* Northstar SSDD Format 2 */
127 { "NSDS2", 358400, 40, 0x04, 0x0F, 0x01, 0xA9, 0x003F, 0x80, 0x00, 0x0010, 0x0002, 0x02, 0x03 }, /* Northstar DSDD Format 2 */
128 { "VGSS", 315392, 32, 0x04, 0x0F, 0x00, 149, 0x007F, 0xC0, 0x00, 0x0020, 0x0002, 0x02, 0x03 }, /* Vector SS SD */
129 { "VGDS", 632784, 32, 0x04, 0x0F, 0x00, 299, 0x007F, 0xC0, 0x00, 0x0020, 0x0004, 0x02, 0x03 }, /* Vector DS SD */
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
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. */
132 { "DISK1A", 630784, 64, 0x04, 0x0F, 0x00, 299, 0x007F, 0xC0, 0x00, 0x0020, 0x0002, 0x02, 0x03 }, /* CompuPro Disk1A 8" SS SD */
133 { "SSSD8", 256256, 26, 0x03, 0x07, 0x00, 242, 0x003F, 0xC0, 0x00, 0x0000, 0x0002, 0x00, 0x00 }, /* Standard 8" SS SD */
137 static UNIT hdsk_unit
[] = {
138 { UDATA (NULL
, UNIT_FIX
+ UNIT_ATTABLE
+ UNIT_DISABLE
+ UNIT_ROABLE
, HDSK_CAPACITY
) },
139 { UDATA (NULL
, UNIT_FIX
+ UNIT_ATTABLE
+ UNIT_DISABLE
+ UNIT_ROABLE
, HDSK_CAPACITY
) },
140 { UDATA (NULL
, UNIT_FIX
+ UNIT_ATTABLE
+ UNIT_DISABLE
+ UNIT_ROABLE
, HDSK_CAPACITY
) },
141 { UDATA (NULL
, UNIT_FIX
+ UNIT_ATTABLE
+ UNIT_DISABLE
+ UNIT_ROABLE
, HDSK_CAPACITY
) },
142 { UDATA (NULL
, UNIT_FIX
+ UNIT_ATTABLE
+ UNIT_DISABLE
+ UNIT_ROABLE
, HDSK_CAPACITY
) },
143 { UDATA (NULL
, UNIT_FIX
+ UNIT_ATTABLE
+ UNIT_DISABLE
+ UNIT_ROABLE
, HDSK_CAPACITY
) },
144 { UDATA (NULL
, UNIT_FIX
+ UNIT_ATTABLE
+ UNIT_DISABLE
+ UNIT_ROABLE
, HDSK_CAPACITY
) },
145 { UDATA (NULL
, UNIT_FIX
+ UNIT_ATTABLE
+ UNIT_DISABLE
+ UNIT_ROABLE
, HDSK_CAPACITY
) }
148 static REG hdsk_reg
[] = {
149 { DRDATA (HDCMD
, hdskLastCommand
, 32), REG_RO
},
150 { DRDATA (HDPOS
, hdskCommandPosition
, 32), REG_RO
},
151 { DRDATA (HDDSK
, selectedDisk
, 32), REG_RO
},
152 { DRDATA (HDSEC
, selectedSector
, 32), REG_RO
},
153 { DRDATA (HDTRK
, selectedTrack
, 32), REG_RO
},
154 { DRDATA (HDDMA
, selectedDMA
, 32), REG_RO
},
155 { HRDATA (TRACELEVEL
, trace_level
, 16), },
159 static MTAB hdsk_mod
[] = {
160 { MTAB_XTD
|MTAB_VDV
, 0, "IOBASE", "IOBASE", &set_iobase
, &show_iobase
, NULL
},
161 { MTAB_XTD
|MTAB_VUN
|MTAB_VAL
, 0, "FORMAT", "FORMAT", &set_format
, &show_format
, NULL
},
162 { UNIT_HDSK_WLK
, 0, "WRTENB", "WRTENB", NULL
},
163 { UNIT_HDSK_WLK
, UNIT_HDSK_WLK
, "WRTLCK", "WRTLCK", NULL
},
164 /* quiet, no warning messages */
165 { UNIT_HDSK_VERBOSE
, 0, "QUIET", "QUIET", NULL
},
166 /* verbose, show warning messages */
167 { UNIT_HDSK_VERBOSE
, UNIT_HDSK_VERBOSE
, "VERBOSE", "VERBOSE", NULL
},
168 { MTAB_XTD
|MTAB_VUN
|MTAB_VAL
, 0, "GEOM", "GEOM", &set_geom
, &show_geom
, NULL
},
173 "HDSK", hdsk_unit
, hdsk_reg
, hdsk_mod
,
175 NULL
, NULL
, &hdsk_reset
,
176 &hdsk_boot
, &hdsk_attach
, NULL
,
177 &hdsk_info_data
, (DEV_DISABLE
), 0,
182 static t_stat
hdsk_reset(DEVICE
*dptr
) {
183 PNP_INFO
*pnp
= (PNP_INFO
*)dptr
->ctxt
;
184 if (dptr
->flags
& DEV_DIS
) {
185 sim_map_resource(pnp
->io_base
, pnp
->io_size
, RESOURCE_TYPE_IO
, &hdsk_io
, TRUE
);
187 /* Connect HDSK at base address */
188 if (sim_map_resource(pnp
->io_base
, pnp
->io_size
, RESOURCE_TYPE_IO
, &hdsk_io
, FALSE
) != 0) {
189 printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__
, pnp
->mem_base
);
190 dptr
->flags
|= DEV_DIS
;
198 static t_stat
hdsk_attach(UNIT
*uptr
, char *cptr
) {
203 r
= attach_unit(uptr
, cptr
); /* attach unit */
204 if ( r
!= SCPE_OK
) /* error? */
207 /* Step 1: Determine capacity of this disk */
208 uptr
-> capac
= sim_fsize(uptr
-> fileref
); /* the file length is a good candidate */
209 if (uptr
-> capac
== 0) { /* file does not exist or has length 0 */
210 uptr
-> capac
= uptr
-> HDSK_NUMBER_OF_TRACKS
*
211 uptr
-> HDSK_SECTORS_PER_TRACK
* uptr
-> HDSK_SECTOR_SIZE
;
212 if (uptr
-> capac
== 0)
213 uptr
-> capac
= HDSK_CAPACITY
;
214 } /* post condition: uptr -> capac > 0 */
215 assert(uptr
-> capac
);
217 /* Step 2: Determine format based on disk capacity */
218 uptr
-> HDSK_FORMAT_TYPE
= -1; /* default to unknown format type */
219 for (i
= 0; dpb
[i
].capac
!= 0; i
++) { /* find disk parameter block */
220 if (dpb
[i
].capac
== uptr
-> capac
) { /* found if correct capacity */
221 uptr
-> HDSK_FORMAT_TYPE
= i
;
226 /* Step 3: Set number of sectors per track and sector size */
227 if (uptr
-> HDSK_FORMAT_TYPE
== -1) { /* Case 1: no disk parameter block found*/
228 for (i
= 0; i
< hdsk_dev
.numunits
; i
++) /* find affected unit number */
229 if (&hdsk_unit
[i
] == uptr
)
232 uptr
-> HDSK_FORMAT_TYPE
= 0;
233 printf("HDSK%c: WARNING: Unsupported disk capacity, assuming HDSK type with capacity %iKB.\n",
234 unitChar
, uptr
-> capac
/ 1000);
235 uptr
-> flags
|= UNIT_HDSK_WLK
;
236 printf("HDSK%c: WARNING: Forcing WRTLCK.\n", unitChar
);
237 /* check whether capacity corresponds to setting of tracks, sectors per track and sector size */
238 if (uptr
-> capac
!= (uptr
-> HDSK_NUMBER_OF_TRACKS
*
239 uptr
-> HDSK_SECTORS_PER_TRACK
* uptr
-> HDSK_SECTOR_SIZE
)) {
240 printf("HDSK%c: WARNING: Fixing geometry.\n", unitChar
);
241 if (uptr
-> HDSK_SECTORS_PER_TRACK
== 0) uptr
-> HDSK_SECTORS_PER_TRACK
= 32;
242 if (uptr
-> HDSK_SECTOR_SIZE
== 0) uptr
-> HDSK_SECTOR_SIZE
= 128;
245 else { /* Case 2: disk parameter block found */
246 uptr
-> HDSK_SECTORS_PER_TRACK
= dpb
[uptr
-> HDSK_FORMAT_TYPE
].spt
>> dpb
[uptr
-> HDSK_FORMAT_TYPE
].psh
;
247 uptr
-> HDSK_SECTOR_SIZE
= (128 << dpb
[uptr
-> HDSK_FORMAT_TYPE
].psh
);
249 assert(uptr
-> HDSK_SECTORS_PER_TRACK
&& uptr
-> HDSK_SECTOR_SIZE
);
251 /* Step 4: Number of tracks is smallest number to accomodate capacity */
252 uptr
-> HDSK_NUMBER_OF_TRACKS
= (uptr
-> capac
+ uptr
-> HDSK_SECTORS_PER_TRACK
*
253 uptr
-> HDSK_SECTOR_SIZE
- 1) / (uptr
-> HDSK_SECTORS_PER_TRACK
* uptr
-> HDSK_SECTOR_SIZE
);
254 assert( ( (t_addr
) ((uptr
-> HDSK_NUMBER_OF_TRACKS
- 1) * uptr
-> HDSK_SECTORS_PER_TRACK
*
255 uptr
-> HDSK_SECTOR_SIZE
) < uptr
-> capac
) &&
256 (uptr
-> capac
<= (t_addr
) (uptr
-> HDSK_NUMBER_OF_TRACKS
*
257 uptr
-> HDSK_SECTORS_PER_TRACK
* uptr
-> HDSK_SECTOR_SIZE
) ) );
261 /* Set disk geometry routine */
262 static t_stat
set_geom(UNIT
*uptr
, int32 val
, char *cptr
, void *desc
) {
263 uint32 numberOfTracks
, numberOfSectors
, sectorSize
;
266 if (cptr
== NULL
) return SCPE_ARG
;
267 if (uptr
== NULL
) return SCPE_IERR
;
268 result
= sscanf(cptr
, "%d/%d/%d%n", &numberOfTracks
, &numberOfSectors
, §orSize
, &n
);
269 if ((result
!= 3) || (result
== EOF
) || (cptr
[n
] != 0)) {
270 result
= sscanf(cptr
, "T:%d/N:%d/S:%d%n", &numberOfTracks
, &numberOfSectors
, §orSize
, &n
);
271 if ((result
!= 3) || (result
== EOF
) || (cptr
[n
] != 0)) return SCPE_ARG
;
273 uptr
-> HDSK_NUMBER_OF_TRACKS
= numberOfTracks
;
274 uptr
-> HDSK_SECTORS_PER_TRACK
= numberOfSectors
;
275 uptr
-> HDSK_SECTOR_SIZE
= sectorSize
;
276 uptr
-> capac
= numberOfTracks
* numberOfSectors
* sectorSize
;
280 /* Show disk geometry routine */
281 static t_stat
show_geom(FILE *st
, UNIT
*uptr
, int32 val
, void *desc
) {
282 if (uptr
== NULL
) return SCPE_IERR
;
283 fprintf(st
, "T:%d/N:%d/S:%d", uptr
-> HDSK_NUMBER_OF_TRACKS
,
284 uptr
-> HDSK_SECTORS_PER_TRACK
, uptr
-> HDSK_SECTOR_SIZE
);
288 #define QUOTE1(text) #text
289 #define QUOTE2(text) QUOTE1(text)
290 /* Set disk format routine */
291 static t_stat
set_format(UNIT
*uptr
, int32 val
, char *cptr
, void *desc
) {
292 char fmtname
[DPB_NAME_LENGTH
+ 1];
295 if (cptr
== NULL
) return SCPE_ARG
;
296 if (uptr
== NULL
) return SCPE_IERR
;
297 if (sscanf(cptr
, "%" QUOTE2(DPB_NAME_LENGTH
) "s", fmtname
) == 0) return SCPE_ARG
;
298 for (i
= 0; dpb
[i
].capac
!= 0; i
++) {
299 if (strncmp(fmtname
, dpb
[i
].name
, strlen(fmtname
)) == 0) {
300 uptr
-> HDSK_FORMAT_TYPE
= i
;
301 uptr
-> capac
= dpb
[i
].capac
; /* Set capacity */
303 /* Configure physical disk geometry */
304 uptr
-> HDSK_SECTOR_SIZE
= (128 << dpb
[uptr
-> HDSK_FORMAT_TYPE
].psh
);
305 uptr
-> HDSK_SECTORS_PER_TRACK
= dpb
[uptr
-> HDSK_FORMAT_TYPE
].spt
>> dpb
[uptr
-> HDSK_FORMAT_TYPE
].psh
;
306 uptr
-> HDSK_NUMBER_OF_TRACKS
= (uptr
-> capac
+
307 uptr
-> HDSK_SECTORS_PER_TRACK
* uptr
-> HDSK_SECTOR_SIZE
- 1) /
308 (uptr
-> HDSK_SECTORS_PER_TRACK
* uptr
-> HDSK_SECTOR_SIZE
);
316 /* Show disk format routine */
317 static t_stat
show_format(FILE *st
, UNIT
*uptr
, int32 val
, void *desc
) {
318 if (uptr
== NULL
) return SCPE_IERR
;
319 fprintf(st
, "%s", dpb
[uptr
-> HDSK_FORMAT_TYPE
].name
);
323 static int32 bootrom_hdsk
[BOOTROM_SIZE_HDSK
] = {
324 0xf3, 0x06, 0x80, 0x3e, 0x0e, 0xd3, 0xfe, 0x05, /* 5c00-5c07 */
325 0xc2, 0x05, 0x5c, 0x3e, 0x16, 0xd3, 0xfe, 0x3e, /* 5c08-5c0f */
326 0x12, 0xd3, 0xfe, 0xdb, 0xfe, 0xb7, 0xca, 0x20, /* 5c10-5c17 */
327 0x5c, 0x3e, 0x0c, 0xd3, 0xfe, 0xaf, 0xd3, 0xfe, /* 5c18-5c1f */
328 0x06, 0x20, 0x3e, 0x01, 0xd3, 0xfd, 0x05, 0xc2, /* 5c20-5c27 */
329 0x24, 0x5c, 0x11, 0x08, 0x00, 0x21, 0x00, 0x00, /* 5c28-5c2f */
330 0x0e, 0xb8, 0x3e, 0x02, 0xd3, 0xfd, 0x3a, 0x37, /* 5c30-5c37 */
331 0xff, 0xd6, 0x08, 0xd3, 0xfd, 0x7b, 0xd3, 0xfd, /* 5c38-5c3f */
332 0x7a, 0xd3, 0xfd, 0xaf, 0xd3, 0xfd, 0x7d, 0xd3, /* 5c40-5c47 */
333 0xfd, 0x7c, 0xd3, 0xfd, 0xdb, 0xfd, 0xb7, 0xca, /* 5c48-5c4f */
334 0x53, 0x5c, 0x76, 0x79, 0x0e, 0x80, 0x09, 0x4f, /* 5c50-5c57 */
335 0x0d, 0xc2, 0x60, 0x5c, 0xfb, 0xc3, 0x00, 0x00, /* 5c58-5c5f */
336 0x1c, 0x1c, 0x7b, 0xfe, 0x20, 0xca, 0x73, 0x5c, /* 5c60-5c67 */
337 0xfe, 0x21, 0xc2, 0x32, 0x5c, 0x1e, 0x00, 0x14, /* 5c68-5c6f */
338 0xc3, 0x32, 0x5c, 0x1e, 0x01, 0xc3, 0x32, 0x5c, /* 5c70-5c77 */
339 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c78-5c7f */
340 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c80-5c87 */
341 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c88-5c8f */
342 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c90-5c97 */
343 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c98-5c9f */
344 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5ca0-5ca7 */
345 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5ca8-5caf */
346 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cb0-5cb7 */
347 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cb8-5cbf */
348 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cc0-5cc7 */
349 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cc8-5ccf */
350 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cd0-5cd7 */
351 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cd8-5cdf */
352 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5ce0-5ce7 */
353 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5ce8-5cef */
354 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cf0-5cf7 */
355 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cf8-5cff */
358 static t_stat
hdsk_boot(int32 unitno
, DEVICE
*dptr
) {
359 if (MEMORYSIZE
< 24*KB
) {
360 printf("Need at least 24KB RAM to boot from hard disk.\n");
363 if (cpu_unit
.flags
& (UNIT_CPU_ALTAIRROM
| UNIT_CPU_BANKED
)) {
364 /* check whether we are really modifying an LD A,<> instruction */
365 if (bootrom_dsk
[UNIT_NO_OFFSET_1
- 1] == LDA_INSTRUCTION
)
366 bootrom_dsk
[UNIT_NO_OFFSET_1
] = (unitno
+ NUM_OF_DSK
) & 0xff; /* LD A,<unitno> */
367 else { /* Attempt to modify non LD A,<> instructions is refused. */
368 printf("Incorrect boot ROM offset detected.\n");
371 install_ALTAIRbootROM(); /* install modified ROM */
373 assert(install_bootrom(bootrom_hdsk
, BOOTROM_SIZE_HDSK
, HDSK_BOOT_ADDRESS
, FALSE
) == SCPE_OK
);
374 *((int32
*) sim_PC
->loc
) = HDSK_BOOT_ADDRESS
;
378 /* returns TRUE iff there exists a disk with VERBOSE */
379 static int32
hdsk_hasVerbose(void) {
381 for (i
= 0; i
< HDSK_NUMBER
; i
++)
382 if (hdsk_dev
.units
[i
].flags
& UNIT_HDSK_VERBOSE
) return TRUE
;
386 /* The hard disk port is 0xfd. It understands the following commands.
397 cmd: db HDSK_READ or HDSK_WRITE
398 hd: db 0 ; 0 .. 7, defines hard disk to be used
399 sector: db 0 ; 0 .. 31, defines sector
400 track: dw 0 ; 0 .. 2047, defines track
401 dma: dw 0 ; defines where result is placed in memory
404 ld b,7 ; size of parameter block
405 ld hl,cmd ; start address of parameter block
406 l: ld a,(hl) ; get byte of parameter block
407 out (0fdh),a ; send it to port
408 inc hl ; point to next byte
409 dec b ; decrement counter
410 jp nz,l ; again, if not done
411 in a,(0fdh) ; get result code
413 3. Retrieve Disk Parameters from controller (Howard M. Harte)
414 Reads a 19-byte parameter block from the disk controller.
415 This parameter block is in CP/M DPB format for the first 17 bytes,
416 and the last two bytes are the lsb/msb of the disk's physical
420 ld a,hdskParam ; hdskParam = 4
421 out (hdskPort),a ; Send 'get parameters' command, hdskPort = 0fdh
423 out (hdskPort),a ; Send selected HDSK number
425 1: in a,(hdskPort) ; Read 17-bytes of DPB
429 in a,(hdskPort) ; Read LSB of disk's physical sector size.
431 in a,(hdskPort) ; Read MSB of disk's physical sector size.
436 /* check the parameters and return TRUE iff parameters are correct or have been repaired */
437 static int32
checkParameters(void) {
438 UNIT
*uptr
= &hdsk_dev
.units
[selectedDisk
];
440 if ((selectedDisk
< 0) || (selectedDisk
>= HDSK_NUMBER
)) {
441 if (hdsk_hasVerbose()) {
442 MESSAGE_2("HDSK%d does not exist, will use HDSK0 instead.", selectedDisk
);
446 currentFlag
= hdsk_dev
.units
[selectedDisk
].flags
;
447 if ((currentFlag
& UNIT_ATT
) == 0) {
448 if (currentFlag
& UNIT_HDSK_VERBOSE
) {
449 MESSAGE_2("HDSK%d is not attached.", selectedDisk
);
451 return FALSE
; /* cannot read or write */
453 if ((selectedSector
< 0) || (selectedSector
>= uptr
-> HDSK_SECTORS_PER_TRACK
)) {
454 if (currentFlag
& UNIT_HDSK_VERBOSE
) {
455 MESSAGE_4("HDSK%d: 0 <= Sector=%02d < %d violated, will use 0 instead.",
456 selectedDisk
, selectedSector
, uptr
-> HDSK_SECTORS_PER_TRACK
);
460 if ((selectedTrack
< 0) || (selectedTrack
>= uptr
-> HDSK_NUMBER_OF_TRACKS
)) {
461 if (currentFlag
& UNIT_HDSK_VERBOSE
) {
462 MESSAGE_4("HDSK%d: 0 <= Track=%04d < %04d violated, will use 0 instead.",
463 selectedDisk
, selectedTrack
, uptr
-> HDSK_NUMBER_OF_TRACKS
);
467 selectedDMA
&= ADDRMASK
;
469 MESSAGE_7("%s HDSK%d Track=%04d Sector=%02d Len=%04d DMA=%04x",
470 (hdskLastCommand
== HDSK_READ
) ? "Read" : "Write",
471 selectedDisk
, selectedTrack
, selectedSector
, uptr
-> HDSK_SECTOR_SIZE
, selectedDMA
);
476 static int32
doSeek(void) {
477 UNIT
*uptr
= &hdsk_dev
.units
[selectedDisk
];
478 if (fseek(uptr
-> fileref
,
479 (uptr
-> HDSK_SECTORS_PER_TRACK
* uptr
-> HDSK_SECTOR_SIZE
) * selectedTrack
+
480 (uptr
-> HDSK_SECTOR_SIZE
* selectedSector
), SEEK_SET
)) {
481 if ((uptr
-> flags
) & UNIT_HDSK_VERBOSE
) {
482 MESSAGE_4("Could not access HDSK%d Sector=%02d Track=%04d.",
483 selectedDisk
, selectedSector
, selectedTrack
);
490 uint8 hdskbuf
[HDSK_MAX_SECTOR_SIZE
] = { 0 }; /* data buffer */
492 static int32
doRead(void) {
494 UNIT
*uptr
= &hdsk_dev
.units
[selectedDisk
];
495 if (doSeek()) return CPM_ERROR
;
497 if (fread(hdskbuf
, uptr
-> HDSK_SECTOR_SIZE
, 1, uptr
-> fileref
) != 1) {
498 for (i
= 0; i
< uptr
-> HDSK_SECTOR_SIZE
; i
++) hdskbuf
[i
] = CPM_EMPTY
;
499 if ((uptr
-> flags
) & UNIT_HDSK_VERBOSE
) {
500 MESSAGE_4("Could not read HDSK%d Sector=%02d Track=%04d.",
501 selectedDisk
, selectedSector
, selectedTrack
);
503 return CPM_OK
; /* allows the creation of empty hard disks */
505 for (i
= 0; i
< uptr
-> HDSK_SECTOR_SIZE
; i
++) PutBYTEWrapper(selectedDMA
+ i
, hdskbuf
[i
]);
509 static int32
doWrite(void) {
512 UNIT
*uptr
= &hdsk_dev
.units
[selectedDisk
];
513 if (((uptr
-> flags
) & UNIT_HDSK_WLK
) == 0) { /* write enabled */
514 if (doSeek()) return CPM_ERROR
;
515 for (i
= 0; i
< uptr
-> HDSK_SECTOR_SIZE
; i
++) hdskbuf
[i
] = GetBYTEWrapper(selectedDMA
+ i
);
516 if (fwrite(hdskbuf
, uptr
-> HDSK_SECTOR_SIZE
, 1, uptr
-> fileref
) != 1) {
517 if ((uptr
-> flags
) & UNIT_HDSK_VERBOSE
) {
518 MESSAGE_4("Could not write HDSK%d Sector=%02d Track=%04d.",
519 selectedDisk
, selectedSector
, selectedTrack
);
525 if ((uptr
-> flags
) & UNIT_HDSK_VERBOSE
) {
526 MESSAGE_4("Could not write to locked HDSK%d Sector=%02d Track=%04d.",
527 selectedDisk
, selectedSector
, selectedTrack
);
534 static int32
hdsk_in(const int32 port
) {
535 UNIT
*uptr
= &hdsk_dev
.units
[selectedDisk
];
538 if ((hdskCommandPosition
== 6) && ((hdskLastCommand
== HDSK_READ
) || (hdskLastCommand
== HDSK_WRITE
))) {
539 result
= checkParameters() ? ((hdskLastCommand
== HDSK_READ
) ? doRead() : doWrite()) : CPM_ERROR
;
540 hdskLastCommand
= HDSK_NONE
;
541 hdskCommandPosition
= 0;
543 } else if (hdskLastCommand
== HDSK_PARAM
) {
544 DPB current
= dpb
[uptr
-> HDSK_FORMAT_TYPE
];
546 params
[ 0] = current
.spt
& 0xff; params
[ 1] = (current
.spt
>> 8) & 0xff;
547 params
[ 2] = current
.bsh
;
548 params
[ 3] = current
.blm
;
549 params
[ 4] = current
.exm
;
550 params
[ 5] = current
.dsm
& 0xff; params
[ 6] = (current
.dsm
>> 8) & 0xff;
551 params
[ 7] = current
.drm
& 0xff; params
[ 8] = (current
.drm
>> 8) & 0xff;
552 params
[ 9] = current
.al0
;
553 params
[10] = current
.al1
;
554 params
[11] = current
.cks
& 0xff; params
[12] = (current
.cks
>> 8) & 0xff;
555 params
[13] = current
.off
& 0xff; params
[14] = (current
.off
>> 8) & 0xff;
556 params
[15] = current
.psh
;
557 params
[16] = current
.phm
;
558 if (++paramcount
>= 19) hdskLastCommand
= HDSK_NONE
;
559 if (paramcount
<= 17)
560 return params
[paramcount
- 1];
561 else if (paramcount
== 18)
562 return (uptr
-> HDSK_SECTOR_SIZE
& 0xff);
563 else if (paramcount
== 19)
564 return (uptr
-> HDSK_SECTOR_SIZE
>> 8);
566 MESSAGE_2("HDSK%d Get parameter error.", selectedDisk
);
570 else if (hdsk_hasVerbose()) {
571 MESSAGE_4("Illegal IN command detected (port=%03xh, cmd=%d, pos=%d).",
572 port
, hdskLastCommand
, hdskCommandPosition
);
577 static int32
hdsk_out(const int32 port
, const int32 data
) {
578 switch(hdskLastCommand
) {
588 switch(hdskCommandPosition
) {
592 hdskCommandPosition
++;
596 selectedSector
= data
;
597 hdskCommandPosition
++;
601 selectedTrack
= data
;
602 hdskCommandPosition
++;
606 selectedTrack
+= (data
<< 8);
607 hdskCommandPosition
++;
612 hdskCommandPosition
++;
616 selectedDMA
+= (data
<< 8);
617 hdskCommandPosition
++;
621 hdskLastCommand
= HDSK_NONE
;
622 hdskCommandPosition
= 0;
627 if ((HDSK_RESET
<= data
) && (data
<= HDSK_PARAM
))
628 hdskLastCommand
= data
;
630 if (hdsk_hasVerbose()) {
631 MESSAGE_3("Illegal OUT command detected (port=%03xh, cmd=%d).",
634 hdskLastCommand
= HDSK_RESET
;
636 hdskCommandPosition
= 0;
638 return 0; /* ignored, since OUT */
641 int32
hdsk_io(const int32 port
, const int32 io
, const int32 data
) {
642 return io
== 0 ? hdsk_in(port
) : hdsk_out(port
, data
);