| /* vax780_fload.c: VAX780 FLOAD command | |
| Copyright (c) 2006-2008, Robert M Supnik | |
| Permission is hereby granted, free of charge, to any person obtaining a | |
| copy of this software and associated documentation files (the "Software"), | |
| to deal in the Software without restriction, including without limitation | |
| the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| and/or sell copies of the Software, and to permit persons to whom the | |
| Software is furnished to do so, subject to the following conditions: | |
| The above copyright notice and this permission notice shall be included in | |
| all copies or substantial portions of the Software. | |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
| IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
| CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
| Except as contained in this notice, the name of Robert M Supnik shall not be | |
| used in advertising or otherwise to promote the sale, use or other dealings | |
| in this Software without prior written authorization from Robert M Supnik. | |
| This code is based on the CP/M RT11 utility, which bears the following | |
| copyrights: | |
| copyright (c) 1980, William C. Colley, III | |
| Rev. 1.2 -- Craig Davenport - Incitec Ltd (Feb 1984) | |
| P O Box 140 | |
| Morningside, | |
| Qld 4170, | |
| Australia. | |
| -- Modified for Digital Research C compiler under CP/M-86 | |
| -- Assebmbly language routines added for BIOS calls etc. | |
| Thanks to Phil Budne for the original adaptation of RT11 to SimH. | |
| 28-May-08 RMS Inlined physical memory routines | |
| */ | |
| #include "vax_defs.h" | |
| #include <ctype.h> | |
| #define BLK_SIZE 256 /* RT11 block size */ | |
| /* Floppy disk parameters */ | |
| #define BPT 26 /* blocks/track */ | |
| #define NTRACKS 77 | |
| #define SECTOR_SKEW 2 | |
| #define TRACK_SKEW 6 | |
| #define TRACK_OFFSET 1 /* track 0 unused */ | |
| /* RT11 directory segment (2 blocks = 512 16b words) */ | |
| #define DS_TOTAL 0 /* segments available */ | |
| #define DS_MAX 31 /* segment max */ | |
| #define DS_NEXT 1 /* zero for last segment */ | |
| #define DS_HIGHEST 2 /* only in 1st segment */ | |
| #define DS_EXTRA 3 /* extra bytes/entry */ | |
| #define DS_FIRST 4 /* first block */ | |
| #define DS_ENTRIES 5 /* start of entries */ | |
| #define DS_SIZE (2 * BLK_SIZE) /* segment size, words */ | |
| /* RT11 directory entry offsets */ | |
| #define DE_STATUS 0 /* status (odd byte) */ | |
| #define TENTAT 001 /* tentative */ | |
| #define EMPTY 002 | |
| #define PERM 004 | |
| #define ENDSEG 010 /* end of segment */ | |
| #define DE_NAME 1 /* file name */ | |
| #define DE_FLNT 4 /* file length */ | |
| #define DE_SIZE 7 /* entry size in words */ | |
| #define DE_GET_STAT(x) (((x) >> 8) & 0377) | |
| extern UNIT fl_unit; | |
| t_bool rtfile_parse (char *pntr, uint16 *file_name); | |
| uint32 rtfile_lookup (uint16 *file_name, uint32 *start); | |
| uint32 rtfile_ator50 (uint32 ascii); | |
| t_bool rtfile_read (uint32 block, uint32 count, uint16 *buffer); | |
| uint32 rtfile_find (uint32 block, uint32 sector); | |
| /* FLOAD file_name {file_origin} */ | |
| t_stat vax780_fload (int32 flag, char *cptr) | |
| { | |
| char gbuf[CBUFSIZE]; | |
| uint16 file_name[3], blkbuf[BLK_SIZE]; | |
| t_stat r; | |
| uint32 i, j, start, size, origin; | |
| if ((fl_unit.flags & UNIT_ATT) == 0) /* floppy attached? */ | |
| return SCPE_UNATT; | |
| if (*cptr == 0) | |
| return SCPE_2FARG; | |
| cptr = get_glyph (cptr, gbuf, 0); /* get file name */ | |
| if (!rtfile_parse (gbuf, file_name)) /* legal file name? */ | |
| return SCPE_ARG; | |
| if ((size = rtfile_lookup (file_name, &start)) == 0) /* file on floppy? */ | |
| return SCPE_ARG; | |
| if (*cptr) { /* origin? */ | |
| origin = (uint32) get_uint (cptr, 16, MEMSIZE, &r); | |
| if ((r != SCPE_OK) || (origin & 1)) /* must be even */ | |
| return SCPE_ARG; | |
| } | |
| else origin = 512; /* no, use default */ | |
| for (i = 0; i < size; i++) { /* loop thru blocks */ | |
| if (!rtfile_read (start + i, 1, blkbuf)) /* read block */ | |
| return SCPE_FMT; | |
| for (j = 0; j < BLK_SIZE; j++) { /* loop thru words */ | |
| if (ADDR_IS_MEM (origin)) | |
| WriteW (origin, blkbuf[j]); | |
| else return SCPE_NXM; | |
| origin = origin + 2; | |
| } | |
| } | |
| return SCPE_OK; | |
| } | |
| /* Parse an RT11 file name and convert it to radix-50 */ | |
| t_bool rtfile_parse (char *pntr, uint16 *file_name) | |
| { | |
| char c; | |
| uint16 d; | |
| uint32 i, j; | |
| file_name[0] = file_name[1] = file_name[2] = 0; /* zero file name */ | |
| for (i = 0; i < 2; i++) { /* 6 characters */ | |
| for (j = 0; j < 3; j++) { | |
| c = *pntr; | |
| if ((c == '.') || (c == 0)) /* fill if . or end */ | |
| d = 0; | |
| else { | |
| if ((d = rtfile_ator50 (c)) == 0) | |
| return FALSE; | |
| pntr++; | |
| } | |
| file_name[i] = (file_name[i] * 050) + d; /* merge into name */ | |
| } | |
| } | |
| if (file_name[0] == 0) /* no name? lose */ | |
| return FALSE; | |
| while ((c = *pntr++) != '.') { /* scan for . */ | |
| if (c == 0) /* end? done */ | |
| return TRUE; | |
| } | |
| for (i = 0; i < 3; i++) { /* 3 characters */ | |
| c = *pntr; | |
| if (c == 0) /* fill if end */ | |
| d = 0; | |
| else { | |
| if ((d = rtfile_ator50 (c)) == 0) | |
| return FALSE; | |
| pntr++; | |
| } | |
| file_name[2] = (file_name[2] * 050) + d; /* merge into ext */ | |
| } | |
| return TRUE; | |
| } | |
| /* ASCII to radix-50 conversion */ | |
| uint32 rtfile_ator50 (uint32 ascii) | |
| { | |
| static char *r50 = " ABCDEFGHIJKLMNOPQRSTUVWXYZ$._0123456789"; | |
| char *fptr; | |
| ascii = toupper (ascii); | |
| if ((fptr = strchr (r50, toupper (ascii))) != NULL) | |
| return ((uint32) (fptr - r50)); | |
| else return 0; | |
| } | |
| /* Lookup an RT11 file name in the directory */ | |
| uint32 rtfile_lookup (uint16 *file_name, uint32 *start) | |
| { | |
| uint16 dirseg[DS_SIZE]; | |
| uint32 segnum, dirent; | |
| for (segnum = 1; (segnum != 0) && (segnum <= DS_MAX); /* loop thru segments */ | |
| segnum = dirseg[DS_NEXT]) { | |
| if (!rtfile_read ((segnum * 2) + 4, 2, dirseg)) /* read segment */ | |
| return 0; /* error? */ | |
| *start = dirseg[DS_FIRST]; /* init file start */ | |
| for (dirent = DS_ENTRIES; /* loop thru entries */ | |
| (dirent < DS_SIZE) && | |
| (DE_GET_STAT (dirseg[dirent + DE_STATUS]) != ENDSEG); | |
| dirent += DE_SIZE + (dirseg[DS_EXTRA] / 2)) { | |
| if ((DE_GET_STAT (dirseg[dirent + DE_STATUS]) == PERM) && | |
| (dirseg[dirent + DE_NAME + 0] == file_name[0]) && | |
| (dirseg[dirent + DE_NAME + 1] == file_name[1]) && | |
| (dirseg[dirent + DE_NAME + 2] == file_name[2])) | |
| return dirseg[dirent + DE_FLNT]; | |
| *start += dirseg[dirent + DE_FLNT]; /* incr file start */ | |
| } | |
| } | |
| return 0; | |
| } | |
| /* Read blocks */ | |
| t_stat rtfile_read (uint32 block, uint32 count, uint16 *buffer) | |
| { | |
| uint32 i, j; | |
| uint32 pos; | |
| uint8 *fbuf = fl_unit.filebuf; | |
| for (; count > 0; count--, block++) { | |
| for (i = 0; i < 4; i++) { /* 4 sectors/block */ | |
| pos = rtfile_find (block, i); /* position */ | |
| if ((pos + 128) >= (uint32) fl_unit.capac) /* off end of disk? */ | |
| return FALSE; | |
| for (j = 0; j < 128; j = j + 2) /* copy 128 bytes */ | |
| *buffer++ = (((uint16) fbuf[pos + j + 1]) << 8) | | |
| ((uint16) fbuf[pos + j]); | |
| } | |
| } | |
| return TRUE; | |
| } | |
| /* Map an RT-11 block number to a physical byte number */ | |
| uint32 rtfile_find (uint32 block, uint32 sector) | |
| { | |
| uint32 ls, lt, pt, ps; | |
| uint32 off, bb; | |
| /* get logical block, track & sector */ | |
| bb = (block * 4) + sector; | |
| lt = bb / BPT; | |
| ls = bb % BPT; | |
| /* logic from 4.3BSD rx.c | |
| * calculate phys track & sector | |
| * 2:1 skew, 6 sector skew for each track | |
| */ | |
| pt = lt + TRACK_OFFSET; | |
| ps = ((ls * SECTOR_SKEW) + (ls / (BPT / SECTOR_SKEW)) + (TRACK_SKEW * lt)) % BPT; | |
| /* byte offset in logical disk */ | |
| off = (pt * BPT + ps) * 128; | |
| return off; | |
| } |