/* vax780_fload.c: VAX780 FLOAD command

   Copyright (c) 2006, 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.
*/

#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 cpu_unit;
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);

extern void WriteW (uint32 pa, int32 val);

/* FLOAD file_name {file_origin} */

t_stat vax780_fload (int 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)) d = 0;              /* fill if . or end */
        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) return FALSE;                    /* no name? lose */
while ((c = *pntr++) != '.') {                          /* scan for . */
    if (c == 0) return TRUE;                            /* end? done */
    }
for (i = 0; i < 3; i++) {                               /* 3 characters */
    c = *pntr;
    if (c == 0) d = 0;                                  /* fill if end */
    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;                                        /* loop thru segments */
     (segnum != 0) && (segnum <= DS_MAX);
     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;
}
