/*
* JR-IDE Project
* - (c) 2017 Alan Hightower
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "pimount.h"
#include "codepages.h"
#include "jride.h"
#include "flash_api.h"
int verbose = 0;
static uint8_t numdrives = 0;
static uint8_t far *drivetable = 0:>0;
/*
* 2F:11:00 - Get network redirector installation status
* AL = 00h : OK to install if not installed
* AL = 01h : not OK to install if not installed
* AL = FFh : installed
*/
static uint8_t _redirector_install_state (void);
#pragma aux _redirector_install_state = \
"mov ax, 1100h" \
"int 2fh" \
value [al];
extern uint16_t get_ds (void);
#pragma aux get_ds = \
"mov ax,ds" \
value [ax];
static int _get_drivetable (void)
{
union REGS r;
struct SREGS s;
uint8_t far *CVT;
r.h.ah = 0x52; // Get Disk List
r.x.bx = 0;
segread (&s);
s.es = 0;
intdosx(&r, &r, &s);
if (!s.es && !r.x.bx)
return 1;
// Configuration Variable Table
CVT = (uint8_t far *) MK_FP(s.es, r.x.bx);
numdrives = CVT[0x21];
drivetable = (uint8_t far *) FPDRF(&CVT[0x16]);
return 0;
}
static int _get_drive (int drive_letter)
{
dlist_t far *entry;
int idx = drive_letter - 'A';
if (drive_letter)
{
if (idx >= numdrives)
{
LOGERR("Requested drive %c: not available (LASTDRIVE is %c)",
drive_letter, numdrives + 'A' - 1);
return -1;
}
entry = &((dlist_t far *) drivetable)[idx];
if (entry->flags & 0xc000)
{
LOGERR("Drive %c: already in use\n", drive_letter);
return -1;
}
}
else
{
for (idx = 3; idx < numdrives; idx++) // start w/ D:
{
entry = &((dlist_t far *) drivetable)[idx];
if ((entry->flags & 0xc000) == 0)
break;
}
if (idx == numdrives)
{
LOGERR("No drive letter available (LASTDRIVE is %c)",
numdrives + 'A' - 1);
return -1;
}
}
entry->flags = 0xc080; // network physical
entry->magic = PIMOUNT_MAGIC;
entry->bsoffset = 2;
entry->context = MK_FP(get_ds(), &ctx);
entry->path[0] = idx + 'A';
entry->path[1] = ':';
entry->path[2] = '\\';
entry->path[3] = 0;
ctx.drive = entry;
ctx.path = &entry->path[2];
ctx.driveno = idx;
return 0;
}
static int _find_self (void)
{
dlist_t far *entry;
int idx;
for (idx = 0; idx < numdrives; idx++)
{
entry = &((dlist_t far *) drivetable)[idx];
if (entry->magic == PIMOUNT_MAGIC)
break;
}
return (idx == numdrives) ? -1 : idx;
}
static void _uninstall_driver (int idx)
{
dlist_t far *drive = &((dlist_t far *) drivetable)[idx];
ctx_t far *ptr = (ctx_t far *) drive->context;
uint16_t psp = ptr->psp;
if (ptr->curr_handler != ctx.prev_handler)
{
LOGERR("Unable to uninstalled - not last 2Fh handler");
return;
}
_dos_setvect (0x2f, ptr->prev_handler);
drive->flags = 0;
drive->magic = 0;
drive->context = 0;
// Free previously allocated DOS memory
_asm {
mov es, psp
mov es, es:[0x2C] // Address of environment block allocation
mov ah, 0x49
int 0x21
mov es, psp // Free PSP allocation
mov ah, 0x49
int 0x21
}
printf (" ***** Uninstalled *****\n");
return;
}
/*
* Dereference several commonly referenced areas of DOS internals
* including current file operation arguments from the DOS Swappable
* Data Area (SDA).
*/
static void _DOS_dereference (void)
{
union REGS r;
struct SREGS s;
r.w.ax = 0x5d06;
segread (&s);
s.ds = r.x.si = 0;
intdosx (&r, &r, &s);
ctx.sda = (uint8_t far *) MK_FP(s.ds, r.x.si);
ctx.filename1 = (uint8_t far *) (ctx.sda + 0x9e + 2); // skip drive:
ctx.filename2 = (uint8_t far *) (ctx.sda + 0x11e + 2); // skip drive:
ctx.fcbname1 = (char far *) (ctx.sda + 0x22b);
ctx.fcbname2 = (char far *) (ctx.sda + 0x237);
ctx.sdb = (SDB_t far *) (ctx.sda + 0x19e);
ctx.fdb = (FDB_t far *) (ctx.sda + 0x1b3);
return;
}
static void _load_unicode_page (void)
{
const struct _st_codelink *entry = codelink;
union REGS r;
int i;
r.w.ax = 0x6601; // get active codepage = BX
intdos (&r, &r);
if (r.x.cflag)
{
memcpy (ctx.unicode, CODEPAGE_DEFAULT, 256);
return;
}
for (i = 0; i < CODEPAGES; i++)
{
if (r.w.bx == entry->codepage)
break;
}
if (i == CODEPAGES)
{
fprintf (stderr, " WARNING: Active code page %d unsupported\n",
r.w.bx);
memcpy (ctx.unicode, CODEPAGE_DEFAULT, 256);
return;
}
memcpy (ctx.unicode, entry->translation, 256);
return;
}
static void _print_usage (const char *progname)
{
fprintf (stderr,
"\n"
"%s \n"
"\n"
"Options:\n"
"\t/? - Print this help\n"
"\t/V - Increase logging verbosity\n"
"\t/L: - Assign drive letter (default=next avail)\n"
"\t/U - Uninstall\n"
"\t/R - Set DOS Date/Time from RPi Zero\n",
progname);
return;
}
int main (int argc, char *argv[])
{
const char *progname = basename (argv[0]);
int uninstall = 0, drive_letter = 0, paragraphs = 0, setrtc = 0;
printf ("JR-IDE Raspberry Pi Zero Redirector - Version %d.%02d\n",
VERSION_MAJOR, VERSION_MINOR);
argc--; argv++;
while (argc)
{
if (strcmp (argv[0], "/?") == 0)
{
_print_usage (progname);
return 0;
}
else if (strcasecmp (argv[0], "/V") == 0)
{
verbose++;
}
else if (strcasecmp (argv[0], "/U") == 0)
{
uninstall = 1;
}
else if (strcasecmp (argv[0], "/R") == 0)
{
setrtc = 1;
}
else if (strncasecmp (argv[0], "/L:", 3) == 0)
{
drive_letter = argv[0][3];
if ((drive_letter >= 'a') && (drive_letter <= 'z'))
drive_letter -= 'a' - 'A';
if ((drive_letter < 'A') && (drive_letter > 'Z'))
{
LOGERR("Invalid drive letter '%c'", drive_letter);
_print_usage (progname);
return -1;
}
}
else
{
LOGERR("Unknown argument '%s'", argv[0]);
_print_usage (progname);
return -1;
}
argc--; argv++;
}
do
{
int idx;
uint16_t segment;
redir_info_t *info = (redir_info_t *) ctx.scratch;
/*
* DOS structures (and their sizes) varied from major version
* to major verion. For the most part, we only use members
* that were consistent from V3+. Sizes could be also be
* smartly adjusted based on DOS major number. However the
* Int2F 11xx redirector table went through a large migration
* from 3.x to 4.x and back to 5.x shifting a lot of the
* remote network management from the redirector to IFSFUNC.EXE
* and back. For now, the redirector table is only setup for
* 5.x entry. Check for it here:
*/
if (_osmajor < 5)
{
LOGERR("Usupported DOS version %d.%02d (DOS 5+ required)",
_osmajor, _osminor);
break;
}
if (_get_drivetable ())
{
LOGERR("Unable to get DOS drive table");
break;
}
if (_redirector_install_state() == 1)
{
LOGERR("Redirector service not available");
break;
}
memset (&ctx, 0, sizeof (ctx));
ctx.prev_handler = _dos_getvect (0x2f);
if ((idx = _find_self ()) >= 0)
{
if (uninstall)
{
_uninstall_driver (idx);
break;
}
else
{
LOGERR("Already installed as drive %c:", idx + 'A');
break;
}
}
else if (uninstall)
{
LOGERR("Redirector not installed");
break;
}
memcpy (ctx.signature, SIGNATURE_TEXT, sizeof (SIGNATURE_TEXT));
_load_unicode_page ();
ctx.psp = _psp;
ctx.curr_handler = MK_FP(get_ds(), redirector);
_DOS_dereference ();
// Perform detection of daemon process on Pi side
if (!(jride_flash_poll (&segment, NULL)))
{
LOGERR("Unable to locate JR-IDE BIOS address");
break;
}
ide_sector = (volatile uint16_t far *) MK_FP(segment, JRIDE_IDE_DATA_WIN);
ide_window = (volatile uint8_t far *) MK_FP(segment, JRIDE_IDE_REG_BASE);
if (_redir_pull (REDIR_FEAT_GET_STATUS))
{
LOGERR("Unable to get redirector server status");
break;
}
if (info->protocol_magic != REDIR_PROTOCOL_MAGIC)
{
LOGERR("Protocol version mismatch (MAGIC)");
break;
}
if (info->protocol_ver != REDIR_PROTOCOL_VERSION)
{
LOGERR("Protocol version mismatch (%d/%d)",
info->protocol_ver, REDIR_PROTOCOL_VERSION);
break;
}
printf (" Found server @ %04X0 - version %d.%02d (CPLD %d.%02d)\n",
segment, info->server_major, info->server_minor,
info->cpld_major, info->cpld_minor);
if (_get_drive (drive_letter))
break;
_dos_setvect (0x2f, ctx.curr_handler);
if (setrtc && info->date_year)
{
struct dostime_t dos_time;
struct dosdate_t dos_date;
dos_time.second = info->time_sec;
dos_time.minute = info->time_min;
dos_time.hour = info->time_hour;
dos_time.hsecond = 0;
dos_date.dayofweek = info->date_wday;
dos_date.day = info->date_mday;
dos_date.month = info->date_mon;
dos_date.year = info->date_year;
if ((_dos_settime (&dos_time) == 0) &&
(_dos_setdate (&dos_date) == 0))
{
printf (" Set DOS Date/Time: %s %s %d, %d - ",
(dos_date.dayofweek == 0) ? "Sunday" :
(dos_date.dayofweek == 1) ? "Monday" :
(dos_date.dayofweek == 2) ? "Tuesday" :
(dos_date.dayofweek == 3) ? "Wednesday" :
(dos_date.dayofweek == 4) ? "Thursday" :
(dos_date.dayofweek == 5) ? "Friday" :
(dos_date.dayofweek == 6) ? "Saturday" : "Unknown",
(dos_date.month == 1) ? "January" :
(dos_date.month == 2) ? "February" :
(dos_date.month == 3) ? "March" :
(dos_date.month == 4) ? "April" :
(dos_date.month == 5) ? "May" :
(dos_date.month == 6) ? "June" :
(dos_date.month == 7) ? "July" :
(dos_date.month == 8) ? "August" :
(dos_date.month == 9) ? "September" :
(dos_date.month == 10) ? "October" :
(dos_date.month == 11) ? "November" :
(dos_date.month == 12) ? "December" : "Unknown",
dos_date.day, dos_date.year);
printf ("%d:%02d:%02d%c\n",
(dos_time.hour % 12) ? (dos_time.hour % 12) : 12,
dos_time.minute, dos_time.second,
(dos_time.hour > 11) ? 'p' : 'a');
}
}
paragraphs = _get_resident_psize ();
printf (" Mounted '%s' as drive %c: - %d bytes resident\n",
info->mount_path, ctx.driveno + 'A', paragraphs << 4);
flushall ();
_dos_keep (0, paragraphs);
return 0;
} while (0);
return -1;
}