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