First Commit of my working state
[simh.git] / VAX / vax780_fload.c
1 /* vax780_fload.c: VAX780 FLOAD command
2
3 Copyright (c) 2006-2008, Robert M Supnik
4
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:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
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 ROBERT M SUPNIK 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.
21
22 Except as contained in this notice, the name of Robert M Supnik shall not be
23 used in advertising or otherwise to promote the sale, use or other dealings
24 in this Software without prior written authorization from Robert M Supnik.
25
26 This code is based on the CP/M RT11 utility, which bears the following
27 copyrights:
28
29 copyright (c) 1980, William C. Colley, III
30
31 Rev. 1.2 -- Craig Davenport - Incitec Ltd (Feb 1984)
32 P O Box 140
33 Morningside,
34 Qld 4170,
35 Australia.
36 -- Modified for Digital Research C compiler under CP/M-86
37 -- Assebmbly language routines added for BIOS calls etc.
38
39 Thanks to Phil Budne for the original adaptation of RT11 to SimH.
40
41 28-May-08 RMS Inlined physical memory routines
42 */
43
44 #include "vax_defs.h"
45 #include <ctype.h>
46
47 #define BLK_SIZE 256 /* RT11 block size */
48
49 /* Floppy disk parameters */
50
51 #define BPT 26 /* blocks/track */
52 #define NTRACKS 77
53 #define SECTOR_SKEW 2
54 #define TRACK_SKEW 6
55 #define TRACK_OFFSET 1 /* track 0 unused */
56
57 /* RT11 directory segment (2 blocks = 512 16b words) */
58
59 #define DS_TOTAL 0 /* segments available */
60 #define DS_MAX 31 /* segment max */
61 #define DS_NEXT 1 /* zero for last segment */
62 #define DS_HIGHEST 2 /* only in 1st segment */
63 #define DS_EXTRA 3 /* extra bytes/entry */
64 #define DS_FIRST 4 /* first block */
65 #define DS_ENTRIES 5 /* start of entries */
66 #define DS_SIZE (2 * BLK_SIZE) /* segment size, words */
67
68 /* RT11 directory entry offsets */
69
70 #define DE_STATUS 0 /* status (odd byte) */
71 #define TENTAT 001 /* tentative */
72 #define EMPTY 002
73 #define PERM 004
74 #define ENDSEG 010 /* end of segment */
75 #define DE_NAME 1 /* file name */
76 #define DE_FLNT 4 /* file length */
77 #define DE_SIZE 7 /* entry size in words */
78 #define DE_GET_STAT(x) (((x) >> 8) & 0377)
79
80 extern UNIT cpu_unit;
81 extern UNIT fl_unit;
82
83 t_bool rtfile_parse (char *pntr, uint16 *file_name);
84 uint32 rtfile_lookup (uint16 *file_name, uint32 *start);
85 uint32 rtfile_ator50 (uint32 ascii);
86 t_bool rtfile_read (uint32 block, uint32 count, uint16 *buffer);
87 uint32 rtfile_find (uint32 block, uint32 sector);
88
89 /* FLOAD file_name {file_origin} */
90
91 t_stat vax780_fload (int flag, char *cptr)
92 {
93 char gbuf[CBUFSIZE];
94 uint16 file_name[3], blkbuf[BLK_SIZE];
95 t_stat r;
96 uint32 i, j, start, size, origin;
97
98 if ((fl_unit.flags & UNIT_ATT) == 0) /* floppy attached? */
99 return SCPE_UNATT;
100 if (*cptr == 0) return SCPE_2FARG;
101 cptr = get_glyph (cptr, gbuf, 0); /* get file name */
102 if (!rtfile_parse (gbuf, file_name)) /* legal file name? */
103 return SCPE_ARG;
104 if ((size = rtfile_lookup (file_name, &start)) == 0) /* file on floppy? */
105 return SCPE_ARG;
106 if (*cptr) { /* origin? */
107 origin = (uint32) get_uint (cptr, 16, MEMSIZE, &r);
108 if ((r != SCPE_OK) || (origin & 1)) /* must be even */
109 return SCPE_ARG;
110 }
111 else origin = 512; /* no, use default */
112
113 for (i = 0; i < size; i++) { /* loop thru blocks */
114 if (!rtfile_read (start + i, 1, blkbuf)) /* read block */
115 return SCPE_FMT;
116 for (j = 0; j < BLK_SIZE; j++) { /* loop thru words */
117 if (ADDR_IS_MEM (origin))
118 WriteW (origin, blkbuf[j]);
119 else return SCPE_NXM;
120 origin = origin + 2;
121 }
122 }
123 return SCPE_OK;
124 }
125
126 /* Parse an RT11 file name and convert it to radix-50 */
127
128 t_bool rtfile_parse (char *pntr, uint16 *file_name)
129 {
130 char c;
131 uint16 d;
132 uint32 i, j;
133
134 file_name[0] = file_name[1] = file_name[2] = 0; /* zero file name */
135 for (i = 0; i < 2; i++) { /* 6 characters */
136 for (j = 0; j < 3; j++) {
137 c = *pntr;
138 if ((c == '.') || (c == 0)) d = 0; /* fill if . or end */
139 else {
140 if ((d = rtfile_ator50 (c)) == 0) return FALSE;
141 pntr++;
142 }
143 file_name[i] = (file_name[i] * 050) + d; /* merge into name */
144 }
145 }
146 if (file_name[0] == 0) return FALSE; /* no name? lose */
147 while ((c = *pntr++) != '.') { /* scan for . */
148 if (c == 0) return TRUE; /* end? done */
149 }
150 for (i = 0; i < 3; i++) { /* 3 characters */
151 c = *pntr;
152 if (c == 0) d = 0; /* fill if end */
153 else {
154 if ((d = rtfile_ator50 (c)) == 0) return FALSE;
155 pntr++;
156 }
157 file_name[2] = (file_name[2] * 050) + d; /* merge into ext */
158 }
159 return TRUE;
160 }
161
162 /* ASCII to radix-50 conversion */
163
164 uint32 rtfile_ator50 (uint32 ascii)
165 {
166 static char *r50 = " ABCDEFGHIJKLMNOPQRSTUVWXYZ$._0123456789";
167 char *fptr;
168
169 ascii = toupper (ascii);
170 if ((fptr = strchr (r50, toupper (ascii))) != NULL)
171 return ((uint32) (fptr - r50));
172 else return 0;
173 }
174
175 /* Lookup an RT11 file name in the directory */
176
177 uint32 rtfile_lookup (uint16 *file_name, uint32 *start)
178 {
179 uint16 dirseg[DS_SIZE];
180 uint32 segnum, dirent;
181
182 for (segnum = 1; /* loop thru segments */
183 (segnum != 0) && (segnum <= DS_MAX);
184 segnum = dirseg[DS_NEXT]) {
185 if (!rtfile_read ((segnum * 2) + 4, 2, dirseg)) /* read segment */
186 return 0; /* error? */
187 *start = dirseg[DS_FIRST]; /* init file start */
188 for (dirent = DS_ENTRIES; /* loop thru entries */
189 (dirent < DS_SIZE) &&
190 (DE_GET_STAT (dirseg[dirent + DE_STATUS]) != ENDSEG);
191 dirent += DE_SIZE + (dirseg[DS_EXTRA] / 2)) {
192 if ((DE_GET_STAT (dirseg[dirent + DE_STATUS]) == PERM) &&
193 (dirseg[dirent + DE_NAME + 0] == file_name[0]) &&
194 (dirseg[dirent + DE_NAME + 1] == file_name[1]) &&
195 (dirseg[dirent + DE_NAME + 2] == file_name[2]))
196 return dirseg[dirent + DE_FLNT];
197 *start += dirseg[dirent + DE_FLNT]; /* incr file start */
198 }
199 }
200 return 0;
201 }
202
203 /* Read blocks */
204
205 t_stat rtfile_read (uint32 block, uint32 count, uint16 *buffer)
206 {
207 uint32 i, j;
208 uint32 pos;
209 uint8 *fbuf = fl_unit.filebuf;
210
211 for (; count > 0; count--, block++) {
212 for (i = 0; i < 4; i++) { /* 4 sectors/block */
213 pos = rtfile_find (block, i); /* position */
214 if ((pos + 128) >= (uint32) fl_unit.capac) /* off end of disk? */
215 return FALSE;
216 for (j = 0; j < 128; j = j + 2) /* copy 128 bytes */
217 *buffer++ = (((uint16) fbuf[pos + j + 1]) << 8) |
218 ((uint16) fbuf[pos + j]);
219 }
220 }
221 return TRUE;
222 }
223
224 /* Map an RT-11 block number to a physical byte number */
225
226 uint32 rtfile_find (uint32 block, uint32 sector)
227 {
228 uint32 ls, lt, pt, ps;
229 uint32 off, bb;
230
231 /* get logical block, track & sector */
232
233 bb = (block * 4) + sector;
234
235 lt = bb / BPT;
236 ls = bb % BPT;
237
238 /* logic from 4.3BSD rx.c
239 * calculate phys track & sector
240 * 2:1 skew, 6 sector skew for each track
241 */
242
243 pt = lt + TRACK_OFFSET;
244 ps = ((ls * SECTOR_SKEW) + (ls / (BPT / SECTOR_SKEW)) + (TRACK_SKEW * lt)) % BPT;
245
246 /* byte offset in logical disk */
247
248 off = (pt * BPT + ps) * 128;
249 return off;
250 }