mirror of
https://github.com/SEPPDROID/Digital-Research-Source-Code.git
synced 2025-10-22 16:04:18 +00:00
194 lines
5.0 KiB
C
194 lines
5.0 KiB
C
/* Convert the Personal CP/M-86 BDOS (compiled as a small-model EXE)
|
|
* to CMD.
|
|
* Copyright (C) 2003, 2007 John Elliott <jce@seasip.demon.co.uk>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the Free
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#define DS_LEN 0xF0 /* Length of data segment (paras) */
|
|
|
|
int gl_base = 0x60;
|
|
|
|
/* EXE header (plus first 8 bytes of EXE file) */
|
|
unsigned char exe_header[0x208];
|
|
|
|
/* CMD header */
|
|
unsigned char cmd_header[0x80];
|
|
|
|
|
|
/* Read a word from the EXE header */
|
|
unsigned short peek(int offset)
|
|
{
|
|
unsigned short l = exe_header[offset];
|
|
unsigned short h = exe_header[offset + 1];
|
|
return (h << 8) | l;
|
|
}
|
|
|
|
/* Add a group to the CMD header */
|
|
void addgroup(int type, unsigned short paras, unsigned short base,
|
|
unsigned short minparas, unsigned short maxparas)
|
|
{
|
|
static unsigned char *group = cmd_header;
|
|
*group++ = type;
|
|
*group++ = (paras & 0xFF);
|
|
*group++ = (paras >> 8);
|
|
*group++ = (base & 0xFF);
|
|
*group++ = (base >> 8);
|
|
*group++ = (minparas & 0xFF);
|
|
*group++ = (minparas >> 8);
|
|
*group++ = (maxparas & 0xFF);
|
|
*group++ = (maxparas >> 8);
|
|
printf("Group %d: len=%04x base=%04x min=%04x max=%04x\n",
|
|
type, paras, base, minparas, maxparas);
|
|
}
|
|
|
|
/* Copy bytes from one file to another.
|
|
* TODO: Warn if error. */
|
|
void mvbytes(FILE *fpexe, FILE *fpcmd, long count)
|
|
{
|
|
int c;
|
|
|
|
while (count)
|
|
{
|
|
c = fgetc(fpexe);
|
|
fputc(c, fpcmd);
|
|
--count;
|
|
}
|
|
}
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
FILE *fpexe, *fpcmd;
|
|
unsigned short cseg,dseg,sseg,dstop;
|
|
unsigned short dsmin, dsmax, exeparas;
|
|
unsigned long exelen, dsbytes;
|
|
long pos;
|
|
int n;
|
|
|
|
if (argc < 3)
|
|
{
|
|
fprintf(stderr, "Syntax: exe2cmd <exefile> <cmdfile>\n");
|
|
return 1;
|
|
}
|
|
for (n = 3; n < argc; n++)
|
|
{
|
|
if (!strncmp(argv[n], "base=", 5))
|
|
{
|
|
if (!sscanf(&argv[n][5], "%x", &gl_base))
|
|
{
|
|
fprintf(stderr, "base= option incorrect.\n");
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Load EXE header, and extract the segment info. This code depends
|
|
* on the word at CS:6 being the length of the code segment in
|
|
* paragraphs. */
|
|
fpexe = fopen(argv[1], "rb");
|
|
if (!fpexe)
|
|
{
|
|
perror(argv[1]);
|
|
return 1;
|
|
}
|
|
if (fread(exe_header, 1, sizeof(exe_header), fpexe) < (int)sizeof(exe_header)
|
|
/* Check for CP/M-86 BDOS:
|
|
* 32 header paragraphs
|
|
* First relocation is at address 6 or less */
|
|
|| peek(8) != 32 || peek(32) > 6)
|
|
{
|
|
fclose(fpexe);
|
|
fprintf(stderr, "%s is not a BDOS image\n", argv[1]);
|
|
return 1;
|
|
}
|
|
cseg = 0;
|
|
dseg = peek(0x206);
|
|
sseg = peek(14);
|
|
exelen = 512L * peek(4);
|
|
if (peek(2)) exelen -= (512 - peek(2));
|
|
exeparas = (exelen + 15) >> 4;
|
|
/*
|
|
dstop = exeparas - peek(8);
|
|
ssparas = dstop - sseg;
|
|
dsmin = dstop - dseg + peek(10);
|
|
dsmax = dstop - dseg + peek(12);
|
|
*/
|
|
/* This is CP/M-86, the DSEG is a fixed size */
|
|
dsmin = dsmax = DS_LEN;
|
|
dstop = dseg + DS_LEN;
|
|
printf("EXE length = %04lx\n", exelen);
|
|
printf("EXE header len = %04x\n", peek(8));
|
|
printf("EXE paras = %04x\n", exeparas);
|
|
printf("DS top = %04x\n", dstop);
|
|
printf("CS: %04x length %04x\n", cseg, dseg - cseg);
|
|
printf("DS: %04x length %04x\n", dseg, dstop - dseg);
|
|
|
|
/* Now generate CP/M-86 header. */
|
|
addgroup(1, dseg - cseg, 0, dseg - cseg, 0);
|
|
addgroup(2, dstop - dseg, 0, dsmin, 0);
|
|
|
|
fpcmd = fopen(argv[2], "wb");
|
|
if (fpcmd == NULL)
|
|
{
|
|
perror(argv[2]);
|
|
fclose(fpexe);
|
|
return 2;
|
|
}
|
|
if (fwrite(cmd_header, 1, sizeof(cmd_header), fpcmd) < (int)sizeof(cmd_header)
|
|
|| fwrite(exe_header+0x200, 1, 8, fpcmd) < 8)
|
|
{
|
|
fclose(fpcmd);
|
|
fclose(fpexe);
|
|
perror(argv[2]);
|
|
return 2;
|
|
}
|
|
/* Write the code segment. The first 8 bytes have been done. */
|
|
mvbytes(fpexe, fpcmd, 16L * (dseg - cseg) - 8);
|
|
/* Write the data segment. */
|
|
dsbytes = 16L * (dstop - dseg);
|
|
mvbytes(fpexe, fpcmd, 40);
|
|
/* Apply fixups to offsets within the data segment */
|
|
for (n = 0; n < 8; n++)
|
|
{
|
|
unsigned seg;
|
|
if (n == 6)
|
|
{
|
|
mvbytes(fpexe, fpcmd, 4);/* Address of DOS module */
|
|
continue;
|
|
}
|
|
mvbytes(fpexe, fpcmd, 2); /* Offset */
|
|
fread(exe_header, 1, 2, fpexe);
|
|
seg = peek(0) + gl_base;
|
|
fputc(seg & 0xFF, fpcmd);
|
|
fputc(seg >> 8, fpcmd);
|
|
}
|
|
mvbytes(fpexe, fpcmd, dsbytes - 72);
|
|
/* Pad the CMD file out to a whole number of records */
|
|
pos = ftell(fpcmd);
|
|
while (pos & 0x7F)
|
|
{
|
|
fputc(0, fpcmd);
|
|
++pos;
|
|
}
|
|
fclose(fpcmd);
|
|
fclose(fpexe);
|
|
return 0;
|
|
}
|