/* 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, CONST 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 const char *r50 = " ABCDEFGHIJKLMNOPQRSTUVWXYZ$._0123456789"; | |
const 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 = (uint8 *)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; | |
} |