Browse Source

ZXCC Cleanup

- I know I said I was done, but I found some more stuff to clean up.  I think I am really done now.
pull/283/head
Wayne Warthen 4 years ago
parent
commit
2c0b818aba
  1. 2
      Binary/Apps/Makefile
  2. 6
      Tools/unix/zxcc/Build-VC.cmd
  3. 242
      Tools/unix/zxcc/cbops.h
  4. 5
      Tools/unix/zxcc/config.h.windows
  5. 62
      Tools/unix/zxcc/cpmdrv.c
  6. 326
      Tools/unix/zxcc/cpmglob.c
  7. 159
      Tools/unix/zxcc/cpmint.h
  8. 204
      Tools/unix/zxcc/cpmparse.c
  9. 747
      Tools/unix/zxcc/cpmredir.c
  10. 237
      Tools/unix/zxcc/cpmredir.h
  11. 217
      Tools/unix/zxcc/dirent.c
  12. 44
      Tools/unix/zxcc/dirent.h
  13. 62
      Tools/unix/zxcc/drdos.c
  14. 24
      Tools/unix/zxcc/track.c
  15. 370
      Tools/unix/zxcc/util.c
  16. 306
      Tools/unix/zxcc/xlt.c
  17. 2
      Tools/unix/zxcc/z80.c
  18. 34
      Tools/unix/zxcc/z80.h
  19. 290
      Tools/unix/zxcc/zxbdos.c
  20. 15
      Tools/unix/zxcc/zxbdos.h
  21. 25
      Tools/unix/zxcc/zxcbdos.c
  22. 6
      Tools/unix/zxcc/zxcbdos.h
  23. 382
      Tools/unix/zxcc/zxcc.c
  24. 53
      Tools/unix/zxcc/zxcc.h
  25. 48
      Tools/unix/zxcc/zxdbdos.c
  26. 10
      Tools/unix/zxcc/zxdbdos.h
  27. BIN
      Tools/zxcc/zxcc-src.zip
  28. BIN
      Tools/zxcc/zxcc.exe
  29. BIN
      Tools/zxcc/zxccdbg.exe

2
Binary/Apps/Makefile

@ -8,4 +8,4 @@ all::
mkdir -p Tunes
clobber::
@rm -f *.bin *.com *.img *.rom *.pdf *.log *.eeprom *.ovr *.hlp *.doc *.COM *.BIN Tunes/*.mym Tunes/*.pt?
@rm -f *.bin *.com *.img *.rom *.pdf *.log *.eeprom *.ovr *.hlp *.doc *.COM *.BIN Tunes/*.mym Tunes/*.pt? Tunes/*.vgm

6
Tools/unix/zxcc/Build-VC.cmd

@ -5,7 +5,7 @@ setlocal
:: Visual Studio x86 Native Tools Command Prompt is assumed
::
:: Below configures VS2012 to target Windows XP and beyond
:: Below configures VS2012 to target Windows XP.
:: Not sure if it will work in later versions of VS, but seems
:: to do no harm.
set INCLUDE=%ProgramFiles(x86)%\Microsoft SDKs\Windows\7.1A\Include;%INCLUDE%
@ -16,10 +16,10 @@ set LINK=/SUBSYSTEM:CONSOLE,5.01 %LINK%
copy config.h.windows config.h
cl zxcc.c cpmdrv.c cpmglob.c cpmparse.c cpmredir.c drdos.c util.c xlt.c zxbdos.c zxcbdos.c zxdbdos.c z80.c dirent.c track.c
cl -I. zxcc.c cpmdrv.c cpmglob.c cpmparse.c cpmredir.c drdos.c util.c xlt.c zxbdos.c zxcbdos.c zxdbdos.c z80.c dirent.c track.c
if errorlevel 1 exit /b 255
cl /DDEBUG /Fe"zxccdbg.exe" zxcc.c cpmdrv.c cpmglob.c cpmparse.c cpmredir.c drdos.c util.c xlt.c zxbdos.c zxcbdos.c zxdbdos.c z80.c dirent.c track.c
cl -I. /DDEBUG /Fe"zxccdbg.exe" zxcc.c cpmdrv.c cpmglob.c cpmparse.c cpmredir.c drdos.c util.c xlt.c zxbdos.c zxcbdos.c zxdbdos.c z80.c dirent.c track.c
if errorlevel 1 exit /b 255
copy cpm\bios.bin .

242
Tools/unix/zxcc/cbops.h

@ -33,128 +33,128 @@
#define res(n,x) (x&=~(1<<n))
{
unsigned short addr;
unsigned char op,reg,val;
if(ixoriy){
addr=(ixoriy==1?ix:iy)+(signed char)fetch(pc);
pc++;
tstates+=8;
op=fetch(pc);
reg=op&7;
op=(op&0xf8)|6;
}
else{
op=fetch(pc);
tstates+=4;
radjust++;
addr=hl;
}
pc++;
unsigned short addr;
unsigned char op, reg, val;
if (ixoriy) {
addr = (ixoriy == 1 ? ix : iy) + (signed char)fetch(pc);
pc++;
tstates += 8;
op = fetch(pc);
reg = op & 7;
op = (op & 0xf8) | 6;
}
else {
op = fetch(pc);
tstates += 4;
radjust++;
addr = hl;
}
pc++;
if(op<64)switch(op){
case 0: rlc(b); break;
case 1: rlc(c); break;
case 2: rlc(d); break;
case 3: rlc(e); break;
case 4: rlc(h); break;
case 5: rlc(l); break;
case 6: tstates+=7;val=fetch(addr);rlc(val);store(addr,val);break;
case 7: rlc(a); break;
case 8: rrc(b); break;
case 9: rrc(c); break;
case 10: rrc(d); break;
case 11: rrc(e); break;
case 12: rrc(h); break;
case 13: rrc(l); break;
case 14: tstates+=7;val=fetch(addr);rrc(val);store(addr,val);break;
case 15: rrc(a); break;
case 0x10: rl(b); break;
case 0x11: rl(c); break;
case 0x12: rl(d); break;
case 0x13: rl(e); break;
case 0x14: rl(h); break;
case 0x15: rl(l); break;
case 0x16: tstates+=7;val=fetch(addr);rl(val);store(addr,val);break;
case 0x17: rl(a); break;
case 0x18: rr(b); break;
case 0x19: rr(c); break;
case 0x1a: rr(d); break;
case 0x1b: rr(e); break;
case 0x1c: rr(h); break;
case 0x1d: rr(l); break;
case 0x1e: tstates+=7;val=fetch(addr);rr(val);store(addr,val);break;
case 0x1f: rr(a); break;
case 0x20: sla(b); break;
case 0x21: sla(c); break;
case 0x22: sla(d); break;
case 0x23: sla(e); break;
case 0x24: sla(h); break;
case 0x25: sla(l); break;
case 0x26: tstates+=7;val=fetch(addr);sla(val);store(addr,val);break;
case 0x27: sla(a); break;
case 0x28: sra(b); break;
case 0x29: sra(c); break;
case 0x2a: sra(d); break;
case 0x2b: sra(e); break;
case 0x2c: sra(h); break;
case 0x2d: sra(l); break;
case 0x2e: tstates+=7;val=fetch(addr);sra(val);store(addr,val);break;
case 0x2f: sra(a); break;
case 0x30: sll(b); break;
case 0x31: sll(c); break;
case 0x32: sll(d); break;
case 0x33: sll(e); break;
case 0x34: sll(h); break;
case 0x35: sll(l); break;
case 0x36: tstates+=7;val=fetch(addr);sll(val);store(addr,val);break;
case 0x37: sll(a); break;
case 0x38: srl(b); break;
case 0x39: srl(c); break;
case 0x3a: srl(d); break;
case 0x3b: srl(e); break;
case 0x3c: srl(h); break;
case 0x3d: srl(l); break;
case 0x3e: tstates+=7;val=fetch(addr);srl(val);store(addr,val);break;
case 0x3f: srl(a); break;
}
else{
unsigned char n=(op>>3)&7;
switch(op&0xc7){
case 0x40: bit(n,b); break;
case 0x41: bit(n,c); break;
case 0x42: bit(n,d); break;
case 0x43: bit(n,e); break;
case 0x44: bit(n,h); break;
case 0x45: bit(n,l); break;
case 0x46: tstates+=4;val=fetch(addr);bit(n,val);store(addr,val);break;
case 0x47: bit(n,a); break;
case 0x80: res(n,b); break;
case 0x81: res(n,c); break;
case 0x82: res(n,d); break;
case 0x83: res(n,e); break;
case 0x84: res(n,h); break;
case 0x85: res(n,l); break;
case 0x86: tstates+=4;val=fetch(addr);res(n,val);store(addr,val);break;
case 0x87: res(n,a); break;
case 0xc0: set(n,b); break;
case 0xc1: set(n,c); break;
case 0xc2: set(n,d); break;
case 0xc3: set(n,e); break;
case 0xc4: set(n,h); break;
case 0xc5: set(n,l); break;
case 0xc6: tstates+=4;val=fetch(addr);set(n,val);store(addr,val);break;
case 0xc7: set(n,a); break;
}
}
if(ixoriy)switch(reg){
case 0:b=val; break;
case 1:c=val; break;
case 2:d=val; break;
case 3:e=val; break;
case 4:h=val; break;
case 5:l=val; break;
case 7:a=val; break;
}
if (op < 64)switch (op) {
case 0: rlc(b); break;
case 1: rlc(c); break;
case 2: rlc(d); break;
case 3: rlc(e); break;
case 4: rlc(h); break;
case 5: rlc(l); break;
case 6: tstates += 7; val = fetch(addr); rlc(val); store(addr, val); break;
case 7: rlc(a); break;
case 8: rrc(b); break;
case 9: rrc(c); break;
case 10: rrc(d); break;
case 11: rrc(e); break;
case 12: rrc(h); break;
case 13: rrc(l); break;
case 14: tstates += 7; val = fetch(addr); rrc(val); store(addr, val); break;
case 15: rrc(a); break;
case 0x10: rl(b); break;
case 0x11: rl(c); break;
case 0x12: rl(d); break;
case 0x13: rl(e); break;
case 0x14: rl(h); break;
case 0x15: rl(l); break;
case 0x16: tstates += 7; val = fetch(addr); rl(val); store(addr, val); break;
case 0x17: rl(a); break;
case 0x18: rr(b); break;
case 0x19: rr(c); break;
case 0x1a: rr(d); break;
case 0x1b: rr(e); break;
case 0x1c: rr(h); break;
case 0x1d: rr(l); break;
case 0x1e: tstates += 7; val = fetch(addr); rr(val); store(addr, val); break;
case 0x1f: rr(a); break;
case 0x20: sla(b); break;
case 0x21: sla(c); break;
case 0x22: sla(d); break;
case 0x23: sla(e); break;
case 0x24: sla(h); break;
case 0x25: sla(l); break;
case 0x26: tstates += 7; val = fetch(addr); sla(val); store(addr, val); break;
case 0x27: sla(a); break;
case 0x28: sra(b); break;
case 0x29: sra(c); break;
case 0x2a: sra(d); break;
case 0x2b: sra(e); break;
case 0x2c: sra(h); break;
case 0x2d: sra(l); break;
case 0x2e: tstates += 7; val = fetch(addr); sra(val); store(addr, val); break;
case 0x2f: sra(a); break;
case 0x30: sll(b); break;
case 0x31: sll(c); break;
case 0x32: sll(d); break;
case 0x33: sll(e); break;
case 0x34: sll(h); break;
case 0x35: sll(l); break;
case 0x36: tstates += 7; val = fetch(addr); sll(val); store(addr, val); break;
case 0x37: sll(a); break;
case 0x38: srl(b); break;
case 0x39: srl(c); break;
case 0x3a: srl(d); break;
case 0x3b: srl(e); break;
case 0x3c: srl(h); break;
case 0x3d: srl(l); break;
case 0x3e: tstates += 7; val = fetch(addr); srl(val); store(addr, val); break;
case 0x3f: srl(a); break;
}
else {
unsigned char n = (op >> 3) & 7;
switch (op & 0xc7) {
case 0x40: bit(n, b); break;
case 0x41: bit(n, c); break;
case 0x42: bit(n, d); break;
case 0x43: bit(n, e); break;
case 0x44: bit(n, h); break;
case 0x45: bit(n, l); break;
case 0x46: tstates += 4; val = fetch(addr); bit(n, val); store(addr, val); break;
case 0x47: bit(n, a); break;
case 0x80: res(n, b); break;
case 0x81: res(n, c); break;
case 0x82: res(n, d); break;
case 0x83: res(n, e); break;
case 0x84: res(n, h); break;
case 0x85: res(n, l); break;
case 0x86: tstates += 4; val = fetch(addr); res(n, val); store(addr, val); break;
case 0x87: res(n, a); break;
case 0xc0: set(n, b); break;
case 0xc1: set(n, c); break;
case 0xc2: set(n, d); break;
case 0xc3: set(n, e); break;
case 0xc4: set(n, h); break;
case 0xc5: set(n, l); break;
case 0xc6: tstates += 4; val = fetch(addr); set(n, val); store(addr, val); break;
case 0xc7: set(n, a); break;
}
}
if (ixoriy)switch (reg) {
case 0:b = val; break;
case 1:c = val; break;
case 2:d = val; break;
case 3:e = val; break;
case 4:h = val; break;
case 5:l = val; break;
case 7:a = val; break;
}
}
#undef var_t

5
Tools/unix/zxcc/config.h.windows

@ -1,5 +1,10 @@
#define HAVE_WINDOWS_H
#define HAVE_FCNTL_H
#ifdef _MSC_VER
#define HAVE_DIRENT_H
#endif
#define HAVE_DIRECT_H
#define HAVE_IO_H
#define WINVER _WIN32_WINNT_WINXP // target Windows XP
#define _WIN32_WINNT _WIN32_WINNT_WINXP // target Windows XP
//#define FILETRACKER 1

62
Tools/unix/zxcc/cpmdrv.c

@ -1,42 +1,42 @@
/*
CPMREDIR: CP/M filesystem redirector
Copyright (C) 1998,2003 John Elliott <jce@seasip.demon.co.uk>
CPMREDIR: CP/M filesystem redirector
Copyright (C) 1998,2003 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 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.
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
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
This file deals with drive-based functions.
This file deals with drive-based functions.
*/
#include "cpmint.h"
#ifdef _WIN32
static char *drive_to_hostdrive(int cpm_drive)
static char* drive_to_hostdrive(int cpm_drive)
{
static char prefix[CPM_MAXPATH];
char *lpfp;
dword dw;
char* lpfp;
dword dw;
if (!redir_drive_prefix[cpm_drive]) return NULL;
dw = GetFullPathName(redir_drive_prefix[cpm_drive], sizeof(prefix),
prefix, &lpfp);
prefix, &lpfp);
if (!dw) return NULL;
if (prefix[1] == ':') /* If path starts with a drive, limit it */
{ /* to just that drive */
prefix[2] = '\\'; /* GetDiskFreeSpace should have trailing backslash */
prefix[2] = '\\'; /* GetDiskFreeSpace should have trailing backslash */
prefix[3] = 0;
}
return prefix;
@ -50,8 +50,8 @@ cpm_byte fcb_reset(void)
bdos(0x0D, 0, 0);
#endif
redir_l_drives = 0;
redir_cpmdrive = 0; /* A reset forces current drive to A: */
redir_l_drives = 0;
redir_cpmdrive = 0; /* A reset forces current drive to A: */
/* redir_ro_drives = 0; Software write protect not revoked by func 0Dh.
*
* This does not follow true CP/M, but does match many 3rd-party replacements.
@ -60,7 +60,7 @@ cpm_byte fcb_reset(void)
}
cpm_word fcb_drive (cpm_byte drv)
cpm_word fcb_drive(cpm_byte drv)
{
if (redir_drive_prefix[drv][0])
{
@ -77,11 +77,11 @@ cpm_byte fcb_getdrv(void)
}
cpm_byte fcb_user (cpm_byte usr)
cpm_byte fcb_user(cpm_byte usr)
{
if (usr != 0xFF) redir_cpmuser = usr % 16;
redir_Msg("User: parameter %d returns %d\r\n", usr, redir_cpmuser);
DBGMSGV("User: parameter %d returns %d\n", usr, redir_cpmuser);
return redir_cpmuser;
}
@ -152,35 +152,31 @@ static cpm_byte exdpb[0x11] = {
0x02, 0x03 /* 512-byte sectors */
};
cpm_word fcb_getdpb(cpm_byte *dpb)
cpm_word fcb_getdpb(cpm_byte* dpb)
{
/* Return the example dpb */
memcpy(dpb, &exdpb, 0x11);
return 0x11;
}
/* Create an entirely bogus ALV
* TODO: Make it a bit better */
cpm_word fcb_getalv(cpm_byte *alv, cpm_word max)
cpm_word fcb_getalv(cpm_byte* alv, cpm_word max)
{
if (max > 1024) max = 1024;
memset(alv, 0xFF, max / 2);
memset(alv + (max / 2), 0, max / 2);
memset(alv, 0xFF, max / 2);
memset(alv + (max / 2), 0, max / 2);
return max;
}
/* Get disk free space */
cpm_word fcb_dfree (cpm_byte drive, cpm_byte *dma)
cpm_word fcb_dfree(cpm_byte drive, cpm_byte* dma)
{
/* Return half of disk capacity */
redir_wr24(dma, 0x8000L); /* 8MB / 128 / 2 */
return 0;
}

326
Tools/unix/zxcc/cpmglob.c

@ -1,34 +1,33 @@
/*
CPMREDIR: CP/M filesystem redirector
Copyright (C) 1998, John Elliott <jce@seasip.demon.co.uk>
CPMREDIR: CP/M filesystem redirector
Copyright (C) 1998, 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 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.
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
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
This file implements those BDOS functions that use wildcard expansion.
This file implements those BDOS functions that use wildcard expansion.
*/
#include "cpmint.h"
#ifdef _MSC_VER
#define S_ISDIR(mode) (((mode) & _S_IFDIR) != 0)
#define S_ISDIR(mode) (((mode) & _S_IFDIR) != 0)
#endif
static cpm_byte *find_fcb;
static cpm_byte* find_fcb;
static int find_n;
static int find_ext = 0;
static int find_ext = 0;
static int find_xfcb = 0;
static int entryno;
static cpm_byte lastdma[0x80];
@ -48,15 +47,17 @@ static char upper(char c)
* the naive code in the distributed zx will not work everywhere.
*/
/* Does the string "s" match the CP/M FCB? */
/* pattern[0-10] will become a CP/M name parsed from "s" if it matches. */
/* If 1st byte of FCB is '?' then anything matches. */
/*
* Does the string "s" match the CP/M FCB?
* pattern[0-10] will become a CP/M name parsed from "s" if it matches.
* If 1st byte of FCB is '?' then anything matches.
*/
static int cpm_match(char *s, cpm_byte *fcb, cpm_byte *pattern)
static int cpm_match(char* s, cpm_byte* fcb, cpm_byte* pattern)
{
int n;
size_t m;
char *dotpos;
int n;
size_t m;
char* dotpos;
m = strlen(s);
@ -65,6 +66,7 @@ static int cpm_match(char *s, cpm_byte *fcb, cpm_byte *pattern)
* the fcb. we reject any that can't be valid cp/m filenames,
* normalizing case as we go. all this goes into 'pattern'
*/
for (n = 0; n < 11; n++) pattern[n] = ' ';
/* The name must have 1 or 0 dots */
@ -74,7 +76,8 @@ static int cpm_match(char *s, cpm_byte *fcb, cpm_byte *pattern)
for (n = 0; n < m; n++) {
pattern[n] = upper(s[n]) & 0x7F;
}
} else { /* at least one dot */
}
else { /* at least one dot */
if (strchr(dotpos + 1, '.')) { /* More than 1 dot */
return 0;
}
@ -82,7 +85,7 @@ static int cpm_match(char *s, cpm_byte *fcb, cpm_byte *pattern)
return 0;
}
if ((dotpos - s) > 8) { /* "name" > 8 characters */
return 0;
return 0;
}
if (strlen(dotpos + 1) > 3) { /* "type" > 3 characters */
return 0;
@ -100,27 +103,27 @@ static int cpm_match(char *s, cpm_byte *fcb, cpm_byte *pattern)
* handle special case where fcb[0] == '?' or fcb[0] & 0x80
* this is used to return a full directory list on bdos's
*/
if (((fcb[0] & 0x7F) == '?') || (fcb[0] & 0x80)) {
return 1;
}
for (n = 0; n < 11; n++)
{
if (fcb[n+1] == '?') continue;
if ((pattern[n] & 0x7F) != (fcb[n+1] & 0x7F))
{
for (n = 0; n < 11; n++)
{
if (fcb[n + 1] == '?') continue;
if ((pattern[n] & 0x7F) != (fcb[n + 1] & 0x7F))
{
return 0;
}
}
return 1; /* Success! */
}
/* Get the next entry from the host's directory matching "fcb" */
static struct dirent * next_entry(DIR *dir, cpm_byte *fcb, cpm_byte *pattern,
struct stat *st)
static struct dirent* next_entry(DIR* dir, cpm_byte* fcb, cpm_byte* pattern,
struct stat* st)
{
struct dirent *en;
struct dirent* en;
int unsatisfied;
int drv = fcb[0] & 0x7F;
@ -131,29 +134,28 @@ static struct dirent * next_entry(DIR *dir, cpm_byte *fcb, cpm_byte *pattern,
for (unsatisfied = 1; unsatisfied; )
{
/* 1. Get the next entry */
en = readdir(dir);
if (!en) return NULL; /* No next entry */
++entryno; /* 0 for 1st, 1 for 2nd, etc. */
/* 2. See if it matches. We do this first (in preference to
seeing if it's a subdirectory first) because it doesn't
require disc access */
* seeing if it's a subdirectory first) because it doesn't
* require disc access */
if (!cpm_match(en->d_name, fcb, pattern))
{
continue;
}
/* 3. Stat it, & reject it if it's a directory */
/* 3. Stat it, & reject it if it's a directory */
strcpy(target_name, redir_drive_prefix[drv]);
strcat(target_name, en->d_name);
if (stat(target_name, st))
{
redir_Msg("Can't stat %s so omitting it.\n", target_name);
DBGMSGV("Can't stat %s so omitting it.\n", target_name);
continue; /* Can't stat */
}
if (S_ISDIR(st->st_mode))
if (S_ISDIR(st->st_mode))
{
/* Searching for files only */
if (fcb[0] != '?' && fcb[0] < 0x80)
@ -166,22 +168,21 @@ static struct dirent * next_entry(DIR *dir, cpm_byte *fcb, cpm_byte *pattern,
return en;
}
void volume_label(int drv, cpm_byte *dma)
void volume_label(int drv, cpm_byte* dma)
{
struct stat st;
memset(dma, 0x20, 12); /* Volume label */
/* Get label name */
redir_get_label(drv, (char *)(dma + 1));
redir_get_label(drv, (char*)(dma + 1));
/* [0x0c] = label byte
* [0x0d] = password byte (=0)
* [0x10-0x17] = password
* [0x18] = label create date
* [0x1c] = label update date
*/
* [0x0d] = password byte (=0)
* [0x10-0x17] = password
* [0x18] = label create date
* [0x1c] = label update date
*/
#ifdef __MSDOS__
dma[0x0c] = 0x21; /* Under DOS, only "update" */
if (redir_drdos) dma[0x0c] |= 0x80; /* Under DRDOS, passwords allowed */
@ -193,7 +194,7 @@ void volume_label(int drv, cpm_byte *dma)
if (stat(redir_drive_prefix[drv], &st))
{
redir_Msg("stat() fails on '%s'\n", redir_drive_prefix[drv]);
DBGMSGV("stat() fails on '%s'\n", redir_drive_prefix[drv]);
return;
}
@ -201,15 +202,13 @@ void volume_label(int drv, cpm_byte *dma)
redir_wr32(dma + 0x1C, redir_cpmtime(st.st_mtime));
}
cpm_word redir_find(int n, cpm_byte *fcb, cpm_byte *dma)
cpm_word redir_find(int n, cpm_byte* fcb, cpm_byte* dma)
{
DIR *hostdir;
DIR* hostdir;
int drv, attrib;
long recs;
struct stat st;
struct dirent *de;
struct dirent* de;
cpm_word rights;
drv = (fcb[0] & 0x7F);
@ -219,16 +218,16 @@ cpm_word redir_find(int n, cpm_byte *fcb, cpm_byte *dma)
if (find_xfcb) /* Return another extent */
{
memcpy(dma, lastdma, 0x80);
dma[0] |= 0x10; /* XFCB */
dma[0] |= 0x10; /* XFCB */
dma[0x0c] = dma[0x69]; /* Password mode */
dma[0x0d] = 0x0A; /* Password decode byte */
dma[0x0d] = 0x0A; /* Password decode byte */
memset(dma + 0x10, '*', 7);
dma[0x17] = ' '; /* Encoded password */
dma[0x17] = ' '; /* Encoded password */
memcpy(lastdma, dma, 0x80);
find_xfcb = 0;
return 0;
}
}
if (find_ext) /* Return another extent */
{
@ -240,21 +239,19 @@ cpm_word redir_find(int n, cpm_byte *fcb, cpm_byte *dma)
dma[0x0e]++;
}
lastsize -= 0x4000;
recs = (lastsize + 127) / 128;
dma[0x0f] = (recs > 127) ? 0x80 : (recs & 0x7F);
recs = (lastsize + 127) / 128;
dma[0x0f] = (recs > 127) ? 0x80 : (recs & 0x7F);
if (lastsize <= 0x4000) find_ext = 0;
memcpy(lastdma, dma, 0x80);
memcpy(lastdma, dma, 0x80);
return 0;
}
}
memset(dma, 0, 128); /* Zap the buffer */
/*
If returning all entries, return a volume label.
*/
/* If returning all entries, return a volume label. */
if ((fcb[0] & 0x7F) == '?')
{
if (!n)
@ -266,18 +263,18 @@ cpm_word redir_find(int n, cpm_byte *fcb, cpm_byte *dma)
}
/* Note: This implies that opendir() works on a filename with a
trailing slash. It does under Linux, but that's the only assurance
I can give.
*/
* trailing slash. It does under Linux, but that's the only assurance
* I can give. */
entryno = -1;
hostdir = opendir(redir_drive_prefix[drv]);
if (!hostdir)
{
redir_Msg("opendir() fails on '%s'\n", redir_drive_prefix[drv]);
DBGMSGV("opendir() fails on '%s'\n", redir_drive_prefix[drv]);
return 0xFF;
}
/* We have a handle to the directory. */
while (n >= 0)
{
@ -291,39 +288,36 @@ cpm_word redir_find(int n, cpm_byte *fcb, cpm_byte *dma)
}
/* Valid entry found & statted. dma+1 holds filename. */
dma[0] = redir_cpmuser; /* Uid always matches */
dma[0] = redir_cpmuser; /* Uid always matches */
dma[0x0c] = 0; /* Extent counter, low */
dma[0x0d] = st.st_size & 0x7F; /* Last record byte count */
dma[0x0e] = 0; /* Extent counter, high */
#ifdef _WIN32
attrib = GetFileAttributesA(target_name);
attrib = GetFileAttributesA(target_name);
rights = redir_drdos_get_rights(target_name);
if (rights && ((fcb[0] & 0x7F) == '?')) find_xfcb = 1;
#else
attrib = 0;
rights = 0;
#endif
if (attrib & 1) dma[9] |= 0x80; /* read only */
if (attrib & 4) dma[10] |= 0x80; /* system */
if (!(attrib & 0x20)) dma[11] |= 0x80; /* archive */
if (attrib & 1) dma[9] |= 0x80; /* read only */
if (attrib & 4) dma[10] |= 0x80; /* system */
if (!(attrib & 0x20)) dma[11] |= 0x80; /* archive */
/* TODO: Under Unix, work out correct RO setting */
recs = (st.st_size + 127) / 128;
dma[0x0f] = (recs > 127) ? 0x80 : (recs & 0x7F);
dma[0x10] = 0x80;
if (S_ISDIR(st.st_mode)) dma[0x10] |= 0x40;
if (S_ISDIR(st.st_mode)) dma[0x10] |= 0x40;
if (attrib & 2) dma[0x10] |= 0x20;
dma[0x10] |= ((entryno & 0x1FFF) >> 8);
dma[0x11] = dma[0x10];
dma[0x12] = entryno & 0xFF;
redir_wr32(dma + 0x16, (dword)st.st_mtime); /* Modification time. */
/* TODO: It should be in DOS */
/* format */
redir_wr32(dma + 0x16, (dword)st.st_mtime); /* Modification time. */
/* TODO: It should be in DOS format */
/* TODO: At 0x1A, 1st cluster */
redir_wr32(dma + 0x1C, st.st_size); /* True size */
@ -348,25 +342,13 @@ cpm_word redir_find(int n, cpm_byte *fcb, cpm_byte *dma)
return 0;
}
#ifdef DEBUG
#define SHOWNAME(func) \
{ \
char fname[CPM_MAXPATH]; \
redir_fcb2unix(fcb, fname); \
redir_Msg(func "(\"%s\")\n", fname); \
}
#else
#define SHOWNAME(func)
#endif
cpm_word fcb_find1 (cpm_byte *fcb, cpm_byte *dma) /* 0x11 */
cpm_word fcb_find1(cpm_byte* fcb, cpm_byte* dma) /* 0x11 */
{
#ifdef DEBUG
int rv;
#endif
SHOWNAME("fcb_find1")
FCBENT(fcb);
redir_log_fcb(fcb);
@ -374,17 +356,18 @@ cpm_word fcb_find1 (cpm_byte *fcb, cpm_byte *dma) /* 0x11 */
find_fcb = fcb;
find_ext = 0;
find_xfcb = 0;
#ifdef DEBUG
rv = redir_find(find_n, fcb, dma);
if (rv < 4)
{
redir_Msg("Ret: %-11.11s\n", dma + 1);
DBGMSGV("Ret: %-11.11s\n", dma + 1);
}
else redir_Msg("Ret: Fail\n");
return rv;
else DBGMSG("Ret: Fail\n");
FCBRET(rv);
#else
return redir_find(find_n, find_fcb, dma);
FCBRET(redir_find(find_n, find_fcb, dma));
#endif
}
@ -392,137 +375,164 @@ cpm_word fcb_find1 (cpm_byte *fcb, cpm_byte *dma) /* 0x11 */
* programs that do know about it will just pass in the same parameter
* that they did to function 0x11 */
cpm_word fcb_find2 (cpm_byte *fcb, cpm_byte *dma) /* 0x12 */
cpm_word fcb_find2(cpm_byte* fcb, cpm_byte* dma) /* 0x12 */
{
#ifdef DEBUG
int rv;
int rv;
char fname[CPM_MAXPATH];
#endif
FCBENT(find_fcb);
char fname[CPM_MAXPATH];
redir_fcb2unix(find_fcb, fname);
redir_Msg("fcb_find2(\"%s\") no. %d\n", fname, find_n);
#ifdef DEBUG
redir_fcb2unix(find_fcb, fname);
DBGMSGV("file number %d, '%s'\n", find_n, fname);
#endif
++find_n;
#ifdef DEBUG
rv = redir_find(find_n, find_fcb, dma);
if (rv < 4)
{
redir_Msg("Ret: %-11.11s\n", dma + 1);
DBGMSGV("Ret: %-11.11s\n", dma + 1);
}
else redir_Msg("Ret: Fail\n");
return rv;
else DBGMSG("Ret: Fail\n");
FCBRET(rv);
#else
return redir_find(find_n, find_fcb, dma);
FCBRET(redir_find(find_n, find_fcb, dma));
#endif
}
/* Under CP/M, unlinking works with wildcards */
cpm_word fcb_unlink(cpm_byte *fcb, cpm_byte *dma)
cpm_word fcb_unlink(cpm_byte* fcb, cpm_byte* dma)
{
DIR *hostdir;
int drv;
struct dirent *de;
DIR* hostdir;
int drv;
struct dirent* de;
struct stat st;
int handle = 0;
int handle = 0;
int unpasswd = 0;
char fname[CPM_MAXPATH];
char fname[CPM_MAXPATH];
int del_cnt = 0;
SHOWNAME("fcb_unlink")
FCBENT(fcb);
if (fcb[5] & 0x80) unpasswd = 1; /* Remove password rather than file */
redir_log_fcb(fcb);
drv = (fcb[0] & 0x7F);
if (!drv || drv == '?') drv = redir_cpmdrive;
else drv--;
drv = (fcb[0] & 0x7F);
if (!drv || drv == '?') drv = redir_cpmdrive;
else drv--;
if (redir_ro_drv(drv)) return 0x02FF; /* Error: R/O drive */
if (redir_ro_drv(drv))
{
/* Error: R/O drive */
DBGMSG("delete failed - R/O drive\n");
FCBRET(0x02FF);
}
#ifdef DEBUG
redir_fcb2unix(fcb, fname);
redir_Msg("fcb_unlink(\"%s\")\n", fname);
redir_fcb2unix(fcb, fname);
DBGMSGV("fcb_unlink('%s')\n", fname);
#endif
/* Note: This implies that opendir() works on a filename with a
trailing slash. It does under Linux, but that's the only assurance
I can give.
*/
* trailing slash. It does under Linux, but that's the only assurance
* I can give.*/
hostdir = opendir(redir_drive_prefix[drv]);
if (!hostdir)
{
redir_Msg("opendir() fails on '%s'\n", redir_drive_prefix[drv]);
return 0xFF;
DBGMSGV("opendir failed on '%s'\n", redir_drive_prefix[drv]);
FCBRET(0xFF);
}
/* We have a handle to the directory. */
do
{
de = next_entry(hostdir, fcb, (cpm_byte *)fname, &st);
de = next_entry(hostdir, fcb, (cpm_byte*)fname, &st);
if (de)
{
strcpy(target_name, redir_drive_prefix[drv]);
strcat(target_name, de->d_name);
redir_Msg("Deleting %s\n", de->d_name);
DBGMSGV("deleting '%s'\n", de->d_name);
if (unpasswd)
{
#ifdef __MSDOS__
if (redir_drdos)
{
handle = redir_drdos_put_rights (target_name, dma, 0);
handle = redir_drdos_put_rights(target_name, dma, 0);
}
else handle = 0;
#endif
}
else if (fcb[0] & 0x80)
{
DBGMSGV("rmdir '%s'\n", target_name);
handle = rmdir(target_name);
if (handle && redir_password_error())
{
DBGMSGV("rmdir failed (errno=%lu): %s\n", errno, strerror(errno));
redir_password_append(target_name, dma);
DBGMSGV("rmdir '%s'\n", target_name);
handle = rmdir(target_name);
}
if (handle)
DBGMSGV("rmdir failed (errno=%lu): %s\n", errno, strerror(errno));
}
else
{
releaseFile(target_name);
releaseFile(target_name);
DBGMSGV("unlink '%s'\n", target_name);
handle = unlink(target_name);
if (handle && redir_password_error())
{
DBGMSGV("unlink failed (errno=%lu): %s\n", errno, strerror(errno));
redir_password_append(target_name, dma);
releaseFile(target_name);
releaseFile(target_name);
DBGMSGV("unlink '%s'\n", target_name);
handle = unlink(target_name);
}
if (handle)
DBGMSGV("unlink failed (errno=%lu): %s\n", errno, strerror(errno));
}
if (handle) de = NULL; /* Delete failed */
if (handle)
de = NULL; /* Delete failed */
else
del_cnt++;
}
}
while (de != NULL);
if (handle)
{
redir_Msg("Ret: -1\n");
closedir(hostdir);
return 0xFF;
}
redir_Msg("Ret: 0\n");
closedir(hostdir);
return 0;
}
} while (de != NULL);
if (!handle && !del_cnt)
DBGMSG("no matching directory entries\n");
else
DBGMSGV("deleted %i file(s)\n", del_cnt);
if (handle || !del_cnt)
{
DBGMSG("delete processing failed\n");
closedir(hostdir);
FCBRET(0xFF);
}
DBGMSG("delete processing succeeded\n");
closedir(hostdir);
FCBRET(0);
}
#ifdef __MSDOS__
cpm_word redir_get_label(cpm_byte drv, char *pattern)
cpm_word redir_get_label(cpm_byte drv, char* pattern)
{
char strs[10];
struct ffblk fblk;
int done;
char *s;
char* s;
int n;
/* We need the drive prefix to be of the form "C:\etc..." */
@ -531,13 +541,13 @@ cpm_word redir_get_label(cpm_byte drv, char *pattern)
if (!redir_drive_prefix[drv][0] || redir_drive_prefix[drv][1] != ':')
return 0;
sprintf(strs,"%c:/*.*", redir_drive_prefix[drv][0]);
sprintf(strs, "%c:/*.*", redir_drive_prefix[drv][0]);
done = findfirst(strs, &fblk, FA_LABEL);
while (!done)
{
if ((fblk.ff_attrib & FA_LABEL) &&
!(fblk.ff_attrib & (FA_SYSTEM | FA_HIDDEN)))
!(fblk.ff_attrib & (FA_SYSTEM | FA_HIDDEN)))
{
s = strchr(fblk.ff_name, '/');
if (!s) s = strchr(fblk.ff_name, '\\');
@ -562,11 +572,11 @@ cpm_word redir_get_label(cpm_byte drv, char *pattern)
return 0;
}
#else
cpm_word redir_get_label(cpm_byte drv, char *pattern)
cpm_word redir_get_label(cpm_byte drv, char* pattern)
{
char *dname;
size_t l;
int n;
char* dname;
size_t l;
int n;
memset(pattern, ' ', 11);
@ -585,6 +595,4 @@ cpm_word redir_get_label(cpm_byte drv, char *pattern)
return 0;
}
#endif

159
Tools/unix/zxcc/cpmint.h

@ -1,32 +1,26 @@
/*
CPMREDIR: CP/M filesystem redirector
Copyright (C) 1998, John Elliott <jce@seasip.demon.co.uk>
CPMREDIR: CP/M filesystem redirector
Copyright (C) 1998, 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 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.
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
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
This file holds internal declarations for the library.
This file holds internal declarations for the library.
*/
#ifndef _WIN32
#include "config.h"
#define DIRSEP "/"
#else
#include "config.h"
#define DIRSEP "/\\:"
#endif
#include "config.h"
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
@ -47,16 +41,12 @@
#include <errno.h>
#ifdef HAVE_DIRENT_H
#include <dirent.h>
#ifdef HAVE_DIRECT_H
#include <direct.h>
#endif
#else
#ifdef __WATCOMC__
#include <io.h>
#include <direct.h>
#else
#include "dirent.h"
#endif
#endif
#ifdef HAVE_DIRECT_H
#include <direct.h>
#endif
#ifdef HAVE_IO_H
#include <io.h>
#endif
#ifdef HAVE_NDIR_H
#include <ndir.h>
@ -88,25 +78,37 @@
#ifndef _WIN32
#include <sys/param.h>
#include <sys/mount.h>
#define _S_IFDIR S_IFDIR
#define _S_IFDIR S_IFDIR
#endif
/* MSDOS includes removed */
#ifdef _WIN32
#define DIRSEP "/"
#define mkdir(dir, mode) _mkdir(dir)
#define strcasecmp _stricmp
int truncate(const char* path, off_t length); /* see util.c */
#define ftruncate _chsize
/* note Windows build assumes Windows is configured as a non case sensitive filesystem */
/* note Windows build assumes Windows is configured as a non case sensitive filesystem */
#ifndef STDIN_FILENO
#define STDIN_FILENO _fileno(stdin)
#define STDOUT_FILENO _fileno(stdout)
#define STDERR_FILENO _fileno(stderr)
#endif
#else
#define DIRSEP "/\\:"
#define CASE_SENSITIVE_FILESYSTEM 1
#endif
#include "cpmredir.h"
#ifdef _WIN32
int truncate(const char* path, off_t length); /* see util.c */
#endif
typedef unsigned char byte; /* Must be exactly 8 bits */
typedef unsigned short word; /* Must be exactly 16 bits */
typedef unsigned long dword; /* Must be at least 32 bits, and
>= sizeof(int) */
>= sizeof(int) */
#include "cpmredir.h"
#ifdef CPMDEF
#define EXT
@ -146,16 +148,14 @@ EXT cpm_word redir_ro_drives INIT(0);
#undef EXT
#undef INIT
/* Convert FCB to a Unix filename, returning 1 if it's ambiguous */
int redir_fcb2unix(cpm_byte *fcb, char *fname);
int redir_fcb2unix(cpm_byte* fcb, char* fname);
/* Open FCB, set file attributes */
int redir_ofile(cpm_byte * fcb, char *s);
int redir_ofile(cpm_byte* fcb, char* s);
/* Check that the FCB we have is valid */
int redir_verify_fcb(cpm_byte *fcb);
int redir_verify_fcb(cpm_byte* fcb);
#ifndef O_BINARY /* Necessary in DOS, not present in Linux */
#define O_BINARY 0
@ -163,43 +163,67 @@ int redir_verify_fcb(cpm_byte *fcb);
/* Facilities for debug tracing */
long zxlseek(int fd, long offset, int wh);
#ifdef _WIN32
char* GetErrorStr(DWORD);
#endif
#ifdef DEBUG
// long zxlseek(int fd, long offset, int wh);
void redir_Msg(char *s, ...);
void redir_showfcb(cpm_byte *fcb);
// long zxlseek(int fd, long offset, int wh);
// void redir_Msg(char *s, ...);
void DbgMsg(const char* file, int line, const char* func, char* s, ...);
void redir_showfcb(cpm_byte* fcb);
#else
// #define zxlseek lseek
/* Warning: This is a GCC extension */
#define redir_Msg(x, ...)
#define redir_showfcb(x)
// #define zxlseek lseek
/* Warning: This is a GCC extension */
// #define redir_Msg(x, ...)
#define redir_showfcb(x)
#endif
#ifdef DEBUG
#define FCBENT(fcb) \
{ \
char fname[CPM_MAXPATH] = ""; \
redir_fcb2unix(fcb, fname); \
DBGMSGV("entry w/ FCB @ 0x%04X, filename:'%s'\n", fcb-RAM, fname); \
}
#define FCBRET(rc) \
{ \
DBGMSGV("returning 0x%04X\n", rc); \
return rc; \
}
#define DBGMSGV(s, ...) DbgMsg(__FILE__, __LINE__, __func__, s, __VA_ARGS__)
#define DBGMSG(s) DbgMsg(__FILE__, __LINE__, __func__, s)
#else
#define FCBENT(fcb)
#define FCBRET(rc) return rc;
#define DBGMSGV(s, ...)
#define DBGMSG(s)
#endif
/* Get the "sequential access" file pointer out of an FCB */
long redir_get_fcb_pos(cpm_byte *fcb);
long redir_get_fcb_pos(cpm_byte* fcb);
/* Write "sequential access" pointer to FCB */
void redir_put_fcb_pos(cpm_byte *fcb, long npos);
void redir_put_fcb_pos(cpm_byte* fcb, long npos);
/* Convert time_t to CP/M day count/hours/minutes */
dword redir_cpmtime(time_t t);
/* And back */
time_t redir_unixtime(cpm_byte *c);
time_t redir_unixtime(cpm_byte* c);
/* Functions to access 24-bit & 32-bit words in memory. These are always
little-endian. */
void redir_wr24(cpm_byte *addr, dword v);
void redir_wr32(cpm_byte *addr, dword v);
dword redir_rd24(cpm_byte *addr);
dword redir_rd32(cpm_byte *addr);
void redir_wr24(cpm_byte* addr, dword v);
void redir_wr32(cpm_byte* addr, dword v);
dword redir_rd24(cpm_byte* addr);
dword redir_rd32(cpm_byte* addr);
/* If you have 64-bit file handles, you'll need to write separate wrhandle()
and rdhandle() routines */
@ -209,20 +233,19 @@ dword redir_rd32(cpm_byte *addr);
/* Mark a drive as logged in */
void redir_log_drv(cpm_byte drv);
void redir_log_fcb(cpm_byte *fcb);
void redir_log_fcb(cpm_byte* fcb);
/* Check if a drive is software read-only */
int redir_ro_drv(cpm_byte drv);
int redir_ro_fcb(cpm_byte *fcb);
int redir_ro_fcb(cpm_byte* fcb);
/* Translate errno to a CP/M error */
cpm_word redir_xlt_err(void);
/* Get disc label */
cpm_word redir_get_label(cpm_byte drv, char *pattern);
cpm_word redir_get_label(cpm_byte drv, char* pattern);
/* DRDOS set/get access rights - no-ops under MSDOS and Unix:
*
@ -233,17 +256,19 @@ cpm_word redir_drdos_pwmode(cpm_byte b);
cpm_byte redir_cpm_pwmode(cpm_word w);
/* Get DRDOS access rights for a file */
cpm_word redir_drdos_get_rights(char *path);
cpm_word redir_drdos_get_rights(char* path);
/* Set DRDOS access rights and/or password */
cpm_word redir_drdos_put_rights(char *path, cpm_byte *dma, cpm_word rights);
cpm_word redir_drdos_put_rights(char* path, cpm_byte* dma, cpm_word rights);
/* Was the last error caused by invalid password? */
cpm_word redir_password_error(void);
/* Append password to filename (FILE.TYP -> FILE.TYP;PASSWORD) */
void redir_password_append(char *s, cpm_byte *dma);
void redir_password_append(char* s, cpm_byte* dma);
void releaseFile(char *fname);
int trackFile(char *fname, void *fcb, int fd);
void releaseFile(char* fname);
int trackFile(char* fname, void* fcb, int fd);
#define releaseFCB(fcb) trackFile(NULL, fcb, -1)
extern byte RAM[65536]; /* The Z80's address space */

204
Tools/unix/zxcc/cpmparse.c

@ -1,126 +1,124 @@
/*
CPMREDIR: CP/M filesystem redirector
Copyright (C) 1998, John Elliott <jce@seasip.demon.co.uk>
CPMREDIR: CP/M filesystem redirector
Copyright (C) 1998, 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 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.
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
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
This file parses filenames to FCBs.
This file parses filenames to FCBs.
*/
#include "cpmint.h"
#define is_num(c) ((c >= '0') && (c <= '9'))
static int parse_drive_user(char *txt, cpm_byte *fcb)
static int parse_drive_user(char* txt, cpm_byte* fcb)
{
char uid[4], drvid[4];
int up, dp;
char uid[4], drvid[4];
int up, dp;
for (up = dp = 0; *txt != ':'; ++txt)
{
if (is_num (*txt)) uid [up++] = *txt;
if (isalpha(*txt)) drvid[dp++] = *txt;
if (!is_num(*txt) && !isalpha(*txt)) return -1;
}
uid[up] = 0; drvid[dp] = 0;
for (up = dp = 0; *txt != ':'; ++txt)
{
if (is_num(*txt)) uid[up++] = *txt;
if (isalpha(*txt)) drvid[dp++] = *txt;
if (!is_num(*txt) && !isalpha(*txt)) return -1;
}
uid[up] = 0; drvid[dp] = 0;
if (dp > 1) return -1; /* Invalid driveletter */
if (up > 2) return -1; /* Invalid uid */
if (dp > 1) return -1; /* Invalid driveletter */
if (up > 2) return -1; /* Invalid uid */
fcb[0x0d] = atoi(uid) + 1; if (fcb[0x0d] > 16) return -1;
fcb[0x0d] = atoi(uid) + 1; if (fcb[0x0d] > 16) return -1;
if (islower(drvid[0])) drvid[0] = toupper(drvid[0]);
if (islower(drvid[0])) drvid[0] = toupper(drvid[0]);
if (drvid[0] < 'A' || drvid[0] > 'P') return -1;
if (drvid[0] < 'A' || drvid[0] > 'P') return -1;
fcb[0] = drvid[0] - '@';
return 0;
fcb[0] = drvid[0] - '@';
return 0;
}
cpm_word fcb_parse(char *txt, cpm_byte *fcb)
cpm_word fcb_parse(char* txt, cpm_byte* fcb)
{
int nl = 0, tl = 0, pl = 0, phase = 0;
char *ntxt, ch;
memset(fcb, 0, 0x24);
if (txt[1] == ':' || txt[2] == ':' || txt[3] == ':')
{
if (parse_drive_user(txt, fcb)) return 0xFFFF;
/* Move past the colon */
ntxt = strchr(txt, ':') + 1;
}
else ntxt = txt;
while (phase < 3)
{
ch = *ntxt;
if (islower(ch)) ch = toupper(ch);
switch(ch)
{
case 0:
case '\r': /* EOL */
phase = 4;
break;
case '.': /* file.typ */
if (!phase) ++phase;
else phase = 3;
break;
case ';': /* Password */
if (phase < 2) phase = 2;
else phase = 3;
break;
case '[': case ']': case '=': case 9: case ' ':
case '>': case '<': case ':': case ',': case '/':
case '|': /* Terminator */
phase = 3;
default:
switch(phase)
{
case 0:
if (nl >= 8) return 0xFFFF;
fcb[++nl] = ch;
break;
case 1:
if (tl >= 3) return 0xFFFF;
fcb[tl + 9] = ch;
++tl;
break;
case 2:
if (pl >= 8) return 0xFFFF;
fcb[pl + 0x10] = ch;
++pl;
break;
}
break;
}
}
if (!nl) return 0xFFFF;
fcb[0x1A] = pl;
if (phase == 4) return 0;
return (cpm_word)(ntxt - txt);
int nl = 0, tl = 0, pl = 0, phase = 0;
char* ntxt, ch;
memset(fcb, 0, 0x24);
if (txt[1] == ':' || txt[2] == ':' || txt[3] == ':')
{
if (parse_drive_user(txt, fcb)) return 0xFFFF;
/* Move past the colon */
ntxt = strchr(txt, ':') + 1;
}
else ntxt = txt;
while (phase < 3)
{
ch = *ntxt;
if (islower(ch)) ch = toupper(ch);
switch (ch)
{
case 0:
case '\r': /* EOL */
phase = 4;
break;
case '.': /* file.typ */
if (!phase) ++phase;
else phase = 3;
break;
case ';': /* Password */
if (phase < 2) phase = 2;
else phase = 3;
break;
case '[': case ']': case '=': case 9: case ' ':
case '>': case '<': case ':': case ',': case '/':
case '|': /* Terminator */
phase = 3;
default:
switch (phase)
{
case 0:
if (nl >= 8) return 0xFFFF;
fcb[++nl] = ch;
break;
case 1:
if (tl >= 3) return 0xFFFF;
fcb[tl + 9] = ch;
++tl;
break;
case 2:
if (pl >= 8) return 0xFFFF;
fcb[pl + 0x10] = ch;
++pl;
break;
}
break;
}
}
if (!nl) return 0xFFFF;
fcb[0x1A] = pl;
if (phase == 4) return 0;
return (cpm_word)(ntxt - txt);
}

747
Tools/unix/zxcc/cpmredir.c

File diff suppressed because it is too large

237
Tools/unix/zxcc/cpmredir.h

@ -1,22 +1,22 @@
/*
CPMREDIR: CP/M filesystem redirector
Copyright (C) 1998, John Elliott <jce@seasip.demon.co.uk>
CPMREDIR: CP/M filesystem redirector
Copyright (C) 1998, 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 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.
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
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
This file holds the public interface to CPMREDIR.
This file holds the public interface to CPMREDIR.
*/
#ifndef CPMREDIR_H_INCLUDED
@ -31,121 +31,120 @@ typedef unsigned short cpm_word;
/* Maximum length of a directory path */
#ifdef _POSIX_PATH_MAX
#define CPM_MAXPATH _POSIX_PATH_MAX
#define CPM_MAXPATH _POSIX_PATH_MAX
#else
#ifdef _MAX_PATH
#define CPM_MAXPATH _MAX_PATH
#else
#define CPM_MAXPATH 260
#endif
#ifdef _MAX_PATH
#define CPM_MAXPATH _MAX_PATH
#else
#define CPM_MAXPATH 260
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* Initialise this library. Call this function first.
*
* Returns 0 if failed to initialise.
*/
int fcb_init(void);
/* Initialise this library. Call this function first.
*
* Returns 0 if failed to initialise.
*/
int fcb_init(void);
/* Deinitialise the library. */
void fcb_deinit(void);
/* Translate a name from the host FS to a CP/M name. This will (if necessary)
* create a mapping between a CP/M drive and a host directory path.
*
* CP/M drives A: to O: can be mapped in this way. P: is always the current
* drive.
*
*/
void xlt_name(char* localname, char* cpmname);
/* It is sometimes convenient to set some fixed mappings. This will create
* a mapping for a given directory.
* Pass drive = -1 for "first available", or 0-15 for A: to P:
* Returns 1 if OK, 0 if requested drive not available.
*
* NB: It is important that the localname should have a trailing
* directory separator!
*/
int xlt_map(int drive, char* localdir);
/*
* This revokes a mapping. No check is made whether CP/M has files open
* on the drive or not.
*/
int xlt_umap(int drive);
/* Find out if a drive is mapped, and if so to what directory */
char* xlt_getcwd(int drive);
/* BDOS functions. Eventually this should handle all disc-related BDOS
* functions.
*
* I am assuming that your emulator has the CP/M RAM in its normal address
* space, accessible as a range 0-64k. If this is not the case
* (eg: you are emulating banked memory, or using a segmented architecture)
* you will have to use "copy in and copy out" techniques. The "fcb" area
* must be 36 bytes long; the "dma" area should be 128 * the value set
* in fcb_multirec() [default is 1, so 128 bytes].
*
*/
cpm_byte fcb_reset(void); /* 0x0D */
cpm_word fcb_drive(cpm_byte drv); /* 0x0E */
cpm_word fcb_open(cpm_byte* fcb, cpm_byte* dma); /* 0x0F */
cpm_word fcb_close(cpm_byte* fcb); /* 0x10 */
cpm_word fcb_find1(cpm_byte* fcb, cpm_byte* dma); /* 0x11 */
cpm_word fcb_find2(cpm_byte* fcb, cpm_byte* dma); /* 0x12 */
cpm_word fcb_unlink(cpm_byte* fcb, cpm_byte* dma); /* 0x13 */
cpm_word fcb_read(cpm_byte* fcb, cpm_byte* dma); /* 0x14 */
cpm_word fcb_write(cpm_byte* fcb, cpm_byte* dma); /* 0x15 */
cpm_word fcb_creat(cpm_byte* fcb, cpm_byte* dma); /* 0x16 */
cpm_word fcb_rename(cpm_byte* fcb, cpm_byte* dma); /* 0x17 */
cpm_word fcb_logvec(void); /* 0x18 */
cpm_byte fcb_getdrv(void); /* 0x19 */
/* DMA is a parameter to routines, not a separate call */
cpm_word fcb_getalv(cpm_byte* alv, cpm_word max); /* 0x1B */
/* Get alloc vector: caller must provide space and say how big it is. */
cpm_word fcb_rodisk(void); /* 0x1C */
cpm_word fcb_rovec(void); /* 0x1D */
cpm_word fcb_chmod(cpm_byte* fcb, cpm_byte* dma); /* 0x1E */
cpm_word fcb_getdpb(cpm_byte* dpb); /* 0x1F */
cpm_byte fcb_user(cpm_byte usr); /* 0x20 */
cpm_word fcb_randrd(cpm_byte* fcb, cpm_byte* dma); /* 0x21 */
cpm_word fcb_randwr(cpm_byte* fcb, cpm_byte* dma); /* 0x22 */
cpm_word fcb_stat(cpm_byte* fcb); /* 0x23 */
cpm_word fcb_tell(cpm_byte* fcb); /* 0x24 */
cpm_word fcb_resro(cpm_word bitmap); /* 0x25 */
/* Access Drives and Free Drives are not supported. */
cpm_word fcb_randwz(cpm_byte* fcb, cpm_byte* dma); /* 0x28 */
/* Record locking calls not supported (though they could be) */
cpm_word fcb_multirec(cpm_byte rc); /* 0x2C */
/* Set hardware error action must be done by caller */
cpm_word fcb_dfree(cpm_byte drive, cpm_byte* dma); /* 0x2E */
cpm_word fcb_sync(cpm_byte flag); /* 0x30 */
cpm_word fcb_purge(void); /* 0x62 */
cpm_word fcb_trunc(cpm_byte* fcb, cpm_byte* dma); /* 0x63 */
cpm_word fcb_setlbl(cpm_byte* fcb, cpm_byte* dma); /* 0x64 */
cpm_word fcb_getlbl(cpm_byte drive); /* 0x65 */
cpm_word fcb_date(cpm_byte* fcb); /* 0x66 */
cpm_word fcb_setpwd(cpm_byte* fcb, cpm_byte* dma); /* 0x67 */
cpm_word fcb_defpwd(cpm_byte* pwd); /* 0x6A */
cpm_word fcb_sdate(cpm_byte* fcb, cpm_byte* dma); /* 0x74 */
cpm_word fcb_parse(char* txt, cpm_byte* fcb); /* 0x98 */
/* fcb_parse returns length of filename parsed, 0 if EOL, 0xFFFF if error */
/* Deinitialise the library. */
void fcb_deinit(void);
/* Translate a name from the host FS to a CP/M name. This will (if necessary)
* create a mapping between a CP/M drive and a host directory path.
*
* CP/M drives A: to O: can be mapped in this way. P: is always the current
* drive.
*
*/
void xlt_name(char *localname, char *cpmname);
/* It is sometimes convenient to set some fixed mappings. This will create
* a mapping for a given directory.
* Pass drive = -1 for "first available", or 0-15 for A: to P:
* Returns 1 if OK, 0 if requested drive not available.
*
* NB: It is important that the localname should have a trailing
* directory separator!
*/
int xlt_map(int drive, char *localdir);
/*
* This revokes a mapping. No check is made whether CP/M has files open
* on the drive or not.
*/
int xlt_umap(int drive);
/* Find out if a drive is mapped, and if so to what directory */
char *xlt_getcwd(int drive);
/* BDOS functions. Eventually this should handle all disc-related BDOS
* functions.
*
* I am assuming that your emulator has the CP/M RAM in its normal address
* space, accessible as a range 0-64k. If this is not the case
* (eg: you are emulating banked memory, or using a segmented architecture)
* you will have to use "copy in and copy out" techniques. The "fcb" area
* must be 36 bytes long; the "dma" area should be 128 * the value set
* in fcb_multirec() [default is 1, so 128 bytes].
*
*/
cpm_byte fcb_reset (void); /* 0x0D */
cpm_word fcb_drive (cpm_byte drv); /* 0x0E */
cpm_word fcb_open (cpm_byte *fcb, cpm_byte *dma); /* 0x0F */
cpm_word fcb_close (cpm_byte *fcb); /* 0x10 */
cpm_word fcb_find1 (cpm_byte *fcb, cpm_byte *dma); /* 0x11 */
cpm_word fcb_find2 (cpm_byte *fcb, cpm_byte *dma); /* 0x12 */
cpm_word fcb_unlink(cpm_byte *fcb, cpm_byte *dma); /* 0x13 */
cpm_word fcb_read (cpm_byte *fcb, cpm_byte *dma); /* 0x14 */
cpm_word fcb_write (cpm_byte *fcb, cpm_byte *dma); /* 0x15 */
cpm_word fcb_creat (cpm_byte *fcb, cpm_byte *dma); /* 0x16 */
cpm_word fcb_rename(cpm_byte *fcb, cpm_byte *dma); /* 0x17 */
cpm_word fcb_logvec(void); /* 0x18 */
cpm_byte fcb_getdrv(void); /* 0x19 */
/* DMA is a parameter to routines, not a separate call */
cpm_word fcb_getalv(cpm_byte *alv, cpm_word max); /* 0x1B */
/* Get alloc vector: caller must provide space and say how big it is. */
cpm_word fcb_rodisk(void); /* 0x1C */
cpm_word fcb_rovec (void); /* 0x1D */
cpm_word fcb_chmod (cpm_byte *fcb, cpm_byte *dma); /* 0x1E */
cpm_word fcb_getdpb(cpm_byte *dpb); /* 0x1F */
cpm_byte fcb_user (cpm_byte usr); /* 0x20 */
cpm_word fcb_randrd(cpm_byte *fcb, cpm_byte *dma); /* 0x21 */
cpm_word fcb_randwr(cpm_byte *fcb, cpm_byte *dma); /* 0x22 */
cpm_word fcb_stat (cpm_byte *fcb); /* 0x23 */
cpm_word fcb_tell (cpm_byte *fcb); /* 0x24 */
cpm_word fcb_resro (cpm_word bitmap); /* 0x25 */
/* Access Drives and Free Drives are not supported. */
cpm_word fcb_randwz(cpm_byte *fcb, cpm_byte *dma); /* 0x28 */
/* Record locking calls not supported (though they could be) */
cpm_word fcb_multirec(cpm_byte rc); /* 0x2C */
/* Set hardware error action must be done by caller */
cpm_word fcb_dfree (cpm_byte drive, cpm_byte *dma);/* 0x2E */
cpm_word fcb_sync (cpm_byte flag); /* 0x30 */
cpm_word fcb_purge (void); /* 0x62 */
cpm_word fcb_trunc (cpm_byte *fcb, cpm_byte *dma); /* 0x63 */
cpm_word fcb_setlbl(cpm_byte *fcb, cpm_byte *dma); /* 0x64 */
cpm_word fcb_getlbl(cpm_byte drive); /* 0x65 */
cpm_word fcb_date (cpm_byte *fcb); /* 0x66 */
cpm_word fcb_setpwd(cpm_byte *fcb, cpm_byte *dma); /* 0x67 */
cpm_word fcb_defpwd(cpm_byte *pwd); /* 0x6A */
cpm_word fcb_sdate (cpm_byte *fcb, cpm_byte *dma); /* 0x74 */
cpm_word fcb_parse (char *txt, cpm_byte *fcb); /* 0x98 */
/* fcb_parse returns length of filename parsed, 0 if EOL, 0xFFFF if error */
#ifdef __cplusplus
}
#endif
#endif /* def CPMREDIR_H_INCLUDED */

217
Tools/unix/zxcc/dirent.c

@ -1,16 +1,15 @@
/*
Implementation of POSIX directory browsing functions and types for Win32.
Implementation of POSIX directory browsing functions and types for Win32.
Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com)
History: Created March 1997. Updated June 2003 and July 2012.
Rights: See end of file.
Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com)
History: Created March 1997. Updated June 2003 and July 2012.
Rights: See end of file.
*/
#pragma warning(disable : 4996)
#include "dirent.h"
#include <errno.h>
#include <io.h> /* _findfirst and _findnext set errno iff they return -1 */
#include <stdlib.h>
#include <string.h>
@ -19,115 +18,89 @@ extern "C"
{
#endif
//typedef ptrdiff_t handle_type; /* C99's intptr_t not sufficiently portable */
typedef long handle_type; /* C99's intptr_t not sufficiently portable */
struct DIR
{
handle_type handle; /* -1 for failed rewind */
struct _finddata_t info;
struct dirent result; /* d_name null iff first time */
char *name; /* null-terminated char string */
};
DIR *opendir(const char *name)
{
DIR *dir = 0;
if(name && name[0])
{
size_t base_length = strlen(name);
const char *all = /* search pattern must end with suitable wildcard */
strchr("/\\", name[base_length - 1]) ? "*" : "/*";
if((dir = (DIR *) malloc(sizeof *dir)) != 0 &&
(dir->name = (char *) malloc(base_length + strlen(all) + 1)) != 0)
{
strcat(strcpy(dir->name, name), all);
if((dir->handle =
(handle_type) _findfirst(dir->name, &dir->info)) != -1)
{
dir->result.d_name = 0;
}
else /* rollback */
{
free(dir->name);
free(dir);
dir = 0;
}
}
else /* rollback */
{
free(dir);
dir = 0;
errno = ENOMEM;
}
}
else
{
errno = EINVAL;
}
return dir;
}
int closedir(DIR *dir)
{
int result = -1;
if(dir)
{
if(dir->handle != -1)
{
result = _findclose(dir->handle);
}
free(dir->name);
free(dir);
}
if(result == -1) /* map all errors to EBADF */
{
errno = EBADF;
}
return result;
}
struct dirent *readdir(DIR *dir)
{
struct dirent *result = 0;
if(dir && dir->handle != -1)
{
if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1)
{
result = &dir->result;
result->d_name = dir->info.name;
}
}
else
{
errno = EBADF;
}
return result;
}
void rewinddir(DIR *dir)
{
if(dir && dir->handle != -1)
{
_findclose(dir->handle);
dir->handle = (handle_type) _findfirst(dir->name, &dir->info);
dir->result.d_name = 0;
}
else
{
errno = EBADF;
}
}
DIR* opendir(const char* name) {
DIR* dir = 0;
if (name && name[0]) {
size_t base_length = strlen(name);
const char* all = /* search pattern must end with suitable wildcard */
strchr("/\\", name[base_length - 1]) ? "*" : "/*";
if ((dir = (DIR*)malloc(sizeof * dir)) != 0 &&
(dir->name = (char*)malloc(base_length + strlen(all) + 1)) != 0) {
strcat(strcpy(dir->name, name), all);
if ((dir->handle =
(handle_type)_findfirst(dir->name, &dir->info)) != -1) {
dir->result.d_name = 0;
}
else /* rollback */
{
free(dir->name);
free(dir);
dir = 0;
}
}
else /* rollback */
{
free(dir);
dir = 0;
errno = ENOMEM;
}
}
else {
errno = EINVAL;
}
return dir;
}
int closedir(DIR* dir) {
int result = -1;
if (dir) {
if (dir->handle != -1) {
result = _findclose(dir->handle);
}
free(dir->name);
free(dir);
}
if (result == -1) /* map all errors to EBADF */
{
errno = EBADF;
}
return result;
}
struct dirent* readdir(DIR* dir) {
struct dirent* result = 0;
if (dir && dir->handle != -1) {
if (!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) {
result = &dir->result;
result->d_name = dir->info.name;
}
}
else {
errno = EBADF;
}
return result;
}
void rewinddir(DIR* dir) {
if (dir && dir->handle != -1) {
_findclose(dir->handle);
dir->handle = (handle_type)_findfirst(dir->name, &dir->info);
dir->result.d_name = 0;
}
else {
errno = EBADF;
}
}
#ifdef __cplusplus
}
@ -135,15 +108,15 @@ void rewinddir(DIR *dir)
/*
Copyright Kevlin Henney, 1997, 2003, 2012. All rights reserved.
Copyright Kevlin Henney, 1997, 2003, 2012. All rights reserved.
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose is hereby granted without fee, provided
that this copyright and permissions notice appear in all copies and
derivatives.
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose is hereby granted without fee, provided
that this copyright and permissions notice appear in all copies and
derivatives.
This software is supplied "as is" without express or implied warranty.
This software is supplied "as is" without express or implied warranty.
But that said, if there are any problems please get in touch.
But that said, if there are any problems please get in touch.
*/

44
Tools/unix/zxcc/dirent.h

@ -11,37 +11,45 @@
*/
#include <io.h> /* _findfirst and _findnext set errno iff they return -1 */
#ifdef __cplusplus
extern "C"
{
#endif
typedef struct DIR DIR;
struct dirent {
char *d_name;
};
struct dirent
{
char *d_name;
};
typedef ptrdiff_t handle_type; /* C99's intptr_t not sufficiently portable */
DIR *opendir(const char *);
int closedir(DIR *);
struct dirent *readdir(DIR *);
void rewinddir(DIR *);
typedef struct {
handle_type handle; /* -1 for failed rewind */
struct _finddata_t info;
struct dirent result; /* d_name null iff first time */
char *name; /* null-terminated char string */
} DIR;
/*
DIR *opendir(const char *);
int closedir(DIR *);
struct dirent *readdir(DIR *);
void rewinddir(DIR *);
Copyright Kevlin Henney, 1997, 2003. All rights reserved.
/*
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose is hereby granted without fee, provided
that this copyright and permissions notice appear in all copies and
derivatives.
Copyright Kevlin Henney, 1997, 2003. All rights reserved.
This software is supplied "as is" without express or implied warranty.
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose is hereby granted without fee, provided
that this copyright and permissions notice appear in all copies and
derivatives.
But that said, if there are any problems please get in touch.
This software is supplied "as is" without express or implied warranty.
*/
But that said, if there are any problems please get in touch.
*/
#ifdef __cplusplus
}

62
Tools/unix/zxcc/drdos.c

@ -1,23 +1,23 @@
/*
CPMREDIR: CP/M filesystem redirector
Copyright (C) 1998, John Elliott <jce@seasip.demon.co.uk>
CPMREDIR: CP/M filesystem redirector
Copyright (C) 1998, 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 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.
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
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
This file holds DRDOS-specific password code.
This file holds DRDOS-specific password code.
*/
#include "cpmint.h"
@ -46,10 +46,10 @@ cpm_byte redir_cpm_pwmode(cpm_word w)
#ifdef __MSDOS__
#ifdef __GO32__ /* The GO32 extender doesn't understand DRDOS password
* functions, so these are done with __dpmi_int() rather
* than intdos() */
* functions, so these are done with __dpmi_int() rather
* than intdos() */
cpm_word redir_drdos_get_rights(char *path)
cpm_word redir_drdos_get_rights(char* path)
{
__dpmi_regs r;
@ -71,7 +71,7 @@ cpm_word redir_drdos_get_rights(char *path)
}
cpm_word redir_drdos_put_rights(char *path, cpm_byte *dma, cpm_word rights)
cpm_word redir_drdos_put_rights(char* path, cpm_byte* dma, cpm_word rights)
{
__dpmi_regs r;
@ -79,7 +79,7 @@ cpm_word redir_drdos_put_rights(char *path, cpm_byte *dma, cpm_word rights)
redir_Msg("Put rights for file %s: %04x %-8.8s %-8.8s\n\r", path, rights, dma, dma + 8);
dosmemput(dma+8, 8, __tb); /* Point DTA at password */
dosmemput(dma + 8, 8, __tb); /* Point DTA at password */
r.x.ax = 0x1A00;
r.x.dx = (__tb & 0x0F);
r.x.ds = (__tb) >> 4;
@ -117,7 +117,7 @@ cpm_word redir_drdos_put_rights(char *path, cpm_byte *dma, cpm_word rights)
#else /* __GO32__ */
cpm_word redir_drdos_get_rights(char *path)
cpm_word redir_drdos_get_rights(char* path)
{
union REGS r;
struct SREGS s;
@ -140,7 +140,7 @@ cpm_word redir_drdos_get_rights(char *path)
}
cpm_word redir_drdos_put_rights(char *path, cpm_byte *dma, cpm_word rights)
cpm_word redir_drdos_put_rights(char* path, cpm_byte* dma, cpm_word rights)
{
union REGS r;
struct SREGS s;
@ -149,10 +149,10 @@ cpm_word redir_drdos_put_rights(char *path, cpm_byte *dma, cpm_word rights)
redir_Msg("Put rights for file %s: %04x\n\r", path, rights);
dosmemput(dma, 8, __tb); /* Point DTA at password */
dosmemput(dma, 8, __tb); /* Point DTA at password */
r.w.ax = 0x1A00;
r.w.dx = (__tb & 0x0F);
s.ds = (__tb) >> 4;
s.ds = (__tb) >> 4;
intdosx(&r, &r, &s);
dosmemput(path, strlen(path) + 1, __tb + 0x10);
@ -198,14 +198,14 @@ cpm_word redir_password_error(void)
intdos(&r, &r);
redir_Msg("Last error was: %04x\r\n", r.w.ax);
redir_Msg("Last error was: %04x\n", r.w.ax);
if (r.w.ax == 0x56) return 1; /* Bad password */
return 0;
}
void redir_password_append(char *s, cpm_byte *dma)
void redir_password_append(char* s, cpm_byte* dma)
{
int n, m;
@ -226,11 +226,11 @@ void redir_password_append(char *s, cpm_byte *dma)
}
#else /* __MSDOS__ */
void redir_password_append(char *s, cpm_byte *dma) {}
void redir_password_append(char* s, cpm_byte* dma) {}
cpm_word redir_password_error(void) { return 0; }
cpm_word redir_drdos_put_rights(char *path, cpm_byte *dma, cpm_word rights)
{ return 0; }
cpm_word redir_drdos_get_rights(char *path) { return 0; }
cpm_word redir_drdos_put_rights(char* path, cpm_byte* dma, cpm_word rights)
{
return 0;
}
cpm_word redir_drdos_get_rights(char* path) { return 0; }
#endif /* __MSDOS__ */

24
Tools/unix/zxcc/track.c

@ -22,8 +22,8 @@
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
//#include "cpmint.h"
#include "zxcc.h"
#include "cpmint.h"
/* CP/M does not require that files opened for reading need to be closed,
* this has two impacts
@ -81,8 +81,22 @@
* a problem. I am not aware of any real programs that do this.
* Please let me know if the situation arises.
*/
/* windows needs to use file tracking, for unix/linux it is optional */
/*
* The FILETRACKER functionality was implemented primarily because
* MSDOS file interface does not allow opening files in shared mode.
* This port of zxcc deprecates MSDOS and uses WIN32 API calls to handle
* all file I/O. So, this means that FILETRACKER is now optional for
* for Windows as well as Unix. I have found some edge cases where
* FILETRACKER caused a CP/M program to misbehave. Specifically, ZSM4
* reuses FCBs if files are included and does it such a way that the
* FILETRACKER is unable to solve the problem. For maximum
* compatibility, FILETRACKER may now be left off with the implication
* that a lot of file handles will be left open.
*/
#ifdef FILETRACKER
typedef struct _track {
struct _track* next;
int handle;
@ -111,10 +125,9 @@ void releaseFile(char* fname) {
s = s->next;
}
int trackFile(char* fname, void* fcb, int fd) {
track_t* s = (track_t*)&openFiles;
Msg("trackFile: \"%s\", FCB=0x%X, Handle=%i\n", fname, (byte *)fcb - RAM, fd);
Msg("trackFile: \"%s\", FCB=0x%X, Handle=%i\n", fname, (byte*)fcb - RAM, fd);
while (s->next) { /* find any existing fcb or fd */
if (s->next->fcb == fcb || s->next->handle == fd) {
if (s->next->handle != fd) {
@ -147,4 +160,3 @@ void releaseFile(char* fname) {}
int trackFile(char* fname, void* fcb, int fd) { return fd; }
#endif

370
Tools/unix/zxcc/util.c

@ -1,110 +1,125 @@
/*
CPMREDIR: CP/M filesystem redirector
Copyright (C) 1998, John Elliott <jce@seasip.demon.co.uk>
CPMREDIR: CP/M filesystem redirector
Copyright (C) 1998, 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 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.
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
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
This file holds miscellaneous utility functions.
This file holds miscellaneous utility functions.
*/
#include "cpmint.h"
#ifdef _WIN32
char* GetErrorStr(dword dwErr)
{
LPVOID lpMsgBuf;
static char ErrStr[256] = "";
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_MAX_WIDTH_MASK,
NULL,
dwErr,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
sizeof(ErrStr), NULL);
strncpy(ErrStr, lpMsgBuf, sizeof(ErrStr));
LocalFree(lpMsgBuf);
/* In debug mode, lseek()s can be traced. */
return ErrStr;
}
#ifdef DEBUG
#endif
char* whence(int wh)
{
switch (wh)
{
case SEEK_SET: return("SEEK_SET");
case SEEK_CUR: return("SEEK_CUR");
case SEEK_END: return("SEEK_END");
default: return("SEEK_???");
}
}
/* In debug mode, lseek()s can be traced. */
long zxlseek(int fd, long offset, int wh)
{
#ifdef _WIN32
long v;
redir_Msg(">SetFilePointer() Handle=%lu, Offset=%lu, Method=%lu\n", fd, offset, wh);
DBGMSGV("seek on file #%i to 0x%lX using %s\n", fd, offset, whence(wh));
v = SetFilePointer((HANDLE)fd, offset, NULL, wh);
redir_Msg("<SetFilePointer() FilePos=%lu, LastErr=%lu\n", v, GetLastError());
if (v == INVALID_SET_FILE_POINTER)
return -1;
return v;
#else
long v = lseek(fd, offset, wh);
if (v >= 0) return v;
redir_Msg("lseek fails with errno = %d\n", errno);
if (errno == EBADF) redir_Msg(" (bad file descriptor %d)\n", fd);
if (errno == ESPIPE) redir_Msg(" (file %d is a pipe)\n", fd);
if (errno == EINVAL) redir_Msg(" (bad parameter %d)\n", wh);
if (v != INVALID_SET_FILE_POINTER) return v;
DBGMSGV("seek failed (Error=%lu): %s\n", GetLastError(), GetErrorStr(GetLastError()));
return -1;
#endif
}
#else
long zxlseek(int fd, long offset, int wh)
{
#ifdef _WIN32
return SetFilePointer((HANDLE)fd, offset, NULL, wh);
#else
return lseek(fd, offset, wh);
#endif
}
DBGMSGV("seek on #%i to 0x%lX using %s\n", fd, offset, whence(wh));
long v = lseek(fd, offset, wh);
if (v >= 0) return v;
DBGMSGV("seek failed (errno=%lu): %s\n", errno, strerror(errno));
return -1;
#endif
}
#ifdef DEBUG
void redir_showfcb(cpm_byte *fd)
void redir_showfcb(cpm_byte* fd)
{
int n;
for (n = 0; n < 32; n++)
{
if (!n || n>= 12) printf("%02x ", fd[n]);
else printf("%c", fd[n] & 0x7F);
if (!n || n >= 12) printf("%02x ", fd[n]);
else printf("%c", fd[n] & 0x7F);
}
printf("\r\n");
printf("\n");
}
#endif
/* Get the "sequential access" file pointer out of an FCB */
long redir_get_fcb_pos(cpm_byte *fcb)
long redir_get_fcb_pos(cpm_byte* fcb)
{
long npos;
npos = 524288L * fcb[0x0E]; /* S2 */
npos += 16384L * fcb[0x0C]; /* Extent */
npos += 128L * fcb[0x20]; /* Record */
npos = 524288L * fcb[0x0E]; /* S2 */
npos += 16384L * fcb[0x0C]; /* Extent */
npos += 128L * fcb[0x20]; /* Record */
return npos;
}
void redir_put_fcb_pos(cpm_byte *fcb, long npos)
void redir_put_fcb_pos(cpm_byte* fcb, long npos)
{
fcb[0x20] = (npos / 128) % 128; /* Record */
fcb[0x0C] = (npos / 16384) % 32; /* Extent */
fcb[0x0E] = (npos / 524288L) % 64; /* S2 */
fcb[0x20] = (npos / 128) % 128; /* Record */
fcb[0x0C] = (npos / 16384) % 32; /* Extent */
fcb[0x0E] = (npos / 524288L) % 64; /* S2 */
}
/*
* find a filename that works.
* note that this is where we handle the case sensitivity/non-case sensitivity
@ -112,13 +127,12 @@ void redir_put_fcb_pos(cpm_byte *fcb, long npos)
* the name that is passed in should be in lower case.
* we'll modify it to the first one that matches
*/
void
swizzle(char *fullpath)
void swizzle(char* fullpath)
{
struct stat ss;
char *slash;
DIR *dirp;
struct dirent *dentry;
char* slash;
DIR* dirp;
struct dirent* dentry;
/* short circuit if ok */
if (stat(fullpath, &ss) == 0) {
@ -144,24 +158,20 @@ swizzle(char *fullpath)
/* Passed a CP/M FCB, convert it to a unix filename. Turn its drive back into
* a path. */
int redir_fcb2unix(cpm_byte *fcb, char *fname)
int redir_fcb2unix(cpm_byte* fcb, char* fname)
{
int n, q, drv, ddrv;
char s[2];
char buf[256];
s[1] = 0;
q = 0;
drv = fcb[0] & 0x7F;
q = 0;
drv = fcb[0] & 0x7F;
if (drv == '?') drv = 0;
ddrv = fcb[0] & 0x7F;
if (ddrv < 0x1F) ddrv += '@';
redir_Msg("%c:%-8.8s.%-3.3s\n",
ddrv,
fcb + 1,
fcb + 9);
if (!drv) strcpy(fname, redir_drive_prefix[redir_cpmdrive]);
else strcpy(fname, redir_drive_prefix[drv - 1]);
@ -176,6 +186,16 @@ int redir_fcb2unix(cpm_byte *fcb, char *fname)
strcat(fname, s);
}
}
sprintf(buf, "'%c:%-8.8s.%-3.3s' --> '%s'", ddrv, fcb + 1, fcb + 9, fname);
for (n = 0; buf[n] != '\0'; n++)
{
buf[n] &= 0x7F;
if (buf[n] < ' ') buf[n] = 'x';
}
DBGMSGV("%s\n", buf);
return q;
}
@ -183,84 +203,117 @@ int redir_fcb2unix(cpm_byte *fcb, char *fname)
#define EROFS EACCES
#endif
int redir_ofile(cpm_byte *fcb, char *s)
int redir_ofile(cpm_byte* fcb, char* s)
{
int h;
/* Software write-protection */
/* Software write-protection */
#ifdef _WIN32
redir_Msg(">CreateFile([OPEN_EXISTING]) Name='%s'\n", s);
h = (int)CreateFile(s, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
redir_Msg("<CreateFile([OPEN_EXISTING]) Handle=%lu, LastErr=%lu\n", h, GetLastError());
releaseFCB(fcb);
if (!redir_ro_fcb(fcb))
{
// Attempt to open existing file with read/write access
DBGMSGV("open existing file '%s' with read/write access\n", s);
h = (int)CreateFile(s, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (h != HFILE_ERROR)
{
DBGMSGV("file '%s' opened R/W as #%i\n", s, h);
return trackFile(s, fcb, h);
}
DBGMSGV("open R/W failed (errno=%lu): %s\n", GetLastError(), GetErrorStr(GetLastError()));
}
// Attempt to open existing file with read-only access
DBGMSGV("open existing file '%s' with read-only access\n", s);
h = (int)CreateFile(s, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == HFILE_ERROR)
{
redir_Msg("Returning -1\n");
DBGMSGV("open R/O failed (errno=%lu): %s\n", GetLastError(), GetErrorStr(GetLastError()));
return -1;
}
DBGMSGV("file '%s' opened R/O as #%i\n", s, h);
fcb[9] |= 0x80;
#else
#ifdef __MSDOS__
#elif defined(__MSDOS__)
int rv;
if (!redir_ro_fcb(fcb))
{
rv = _dos_open(s, O_RDWR, &h);
if (!rv) return h;
redir_Msg("Open of %s fails: error %x\r\n", s, rv);
DBGMSGV("Open of %s fails: error %x\n", s, rv);
}
rv = _dos_open(s, O_RDONLY, &h);
if (rv) return -1;
fcb[9] |= 0x80;
#else
releaseFCB(fcb);
fcb[9] |= 0x80;
#else
releaseFCB(fcb);
swizzle(s);
if (!redir_ro_fcb(fcb))
{
// Attempt to open existing file with read/write access
DBGMSGV("open existing file '%s' with read/write access\n", s);
h = open(s, O_RDWR | O_BINARY);
if (h >= 0 || (errno != EACCES && errno != EROFS))
return trackFile(s, fcb, h);
{
DBGMSGV("file '%s' opened R/W as #%i\n", s, h);
return trackFile(s, fcb, h);
}
DBGMSGV("failed to open R/W (errno=%lu): %s\n", errno, strerror(errno));
}
// Attempt to open existing file with read-only access
DBGMSGV("open existing file '%s' with read-only access\n", s);
h = open(s, O_RDONLY | O_BINARY);
if (h < 0) return -1;
fcb[9] |= 0x80;
#endif
if (h < 0)
{
DBGMSGV("failed to open R/O (errno=%lu): %s\n", errno, strerror(errno));
return -1;
}
DBGMSGV("file '%s' opened R/O as #%i\n", s, h);
fcb[9] |= 0x80;
#endif
return trackFile(s, fcb, h);
}
/* Extract a file handle from where it was stored in an FCB by fcb_open()
or fcb_creat(). Aborts if the FCB has been tampered with.
Note: Some programs (like GENCOM) close FCBs they never opened. This causes
the Corrupt FCB message, but no harm seems to ensue. */
int redir_verify_fcb(cpm_byte *fcb)
int redir_verify_fcb(cpm_byte* fcb)
{
if (fcb[16] != 0xFD || fcb[17] != 0x00)
{
fprintf(stderr,"cpmredir: Corrupt FCB\n");
if (fcb[16] != 0xFD || fcb[17] != 0x00)
{
fprintf(stderr, "cpmredir: Corrupt FCB\n");
return -1;
}
return (int)(redir_rd32(fcb + 18));
return (int)(redir_rd32(fcb + 18));
}
/* Print a trace message */
#ifdef DEBUG
void redir_Msg(char *s, ...)
void DbgMsg(const char* file, int line, const char* func, char* s, ...)
{
va_list ap;
va_list ap;
va_start(ap, s);
fprintf(stderr, "cpmredir trace: ");
vfprintf(stderr, s, ap);
va_end(ap);
fflush(stderr);
va_start(ap, s);
fprintf(stderr, "%s(%s@%i): ", func, file, line);
vfprintf(stderr, s, ap);
va_end(ap);
fflush(stderr);
}
#endif
@ -270,33 +323,33 @@ void redir_Msg(char *s, ...)
/* Convert time_t to CP/M day count/hours/minutes */
dword redir_cpmtime(time_t t)
{
/* Microsoft compiler warned around the conversion from time_t to long
* as to support dates beyond 2038 time_t is set as a long long
* and for the Microsoft compiler sizeof(long) == 4 and sizeof(long long) == 8
* for other compilers both have size 8
* As the result is a dword (unsigned long), the code below is modified to reflect this
*/
/* Microsoft compiler warned around the conversion from time_t to long
* as to support dates beyond 2038 time_t is set as a long long
* and for the Microsoft compiler sizeof(long) == 4 and sizeof(long long) == 8
* for other compilers both have size 8
* As the result is a dword (unsigned long), the code below is modified to reflect this
*/
dword d = (dword)((t / 86400) - 2921); /* CP/M day 0 is unix day 2921 */
dword h = (t % 86400) / 3600; /* Hour, 0-23 */
dword m = (t % 3600) / 60; /* Minute, 0-59 */
return (d | (BCD(h) << 16) | (BCD(m) << 24));
return (d | (BCD(h) << 16) | (BCD(m) << 24));
}
#undef BCD
#define UNBCD(x) (((x % 16) + 10 * (x / 16)) & 0xFF)
time_t redir_unixtime(cpm_byte *c)
time_t redir_unixtime(cpm_byte* c)
{
time_t t;
cpm_word days;
days = (c[0] + 256 * c[1]) + 2921;
t = 60L * UNBCD(c[3]);
t += 3600L * UNBCD(c[2]);
t = 60L * UNBCD(c[3]);
t += 3600L * UNBCD(c[2]);
t += 86400L * days;
return t;
@ -304,26 +357,25 @@ time_t redir_unixtime(cpm_byte *c)
#undef UNBCD
/* Functions to access 24-bit & 32-bit words in memory. These are always
little-endian. */
little-endian. */
void redir_wr24(cpm_byte *addr, dword v)
void redir_wr24(cpm_byte* addr, dword v)
{
addr[0] = v & 0xFF;
addr[1] = (v >> 8) & 0xFF;
addr[0] = v & 0xFF;
addr[1] = (v >> 8) & 0xFF;
addr[2] = (v >> 16) & 0xFF;
}
void redir_wr32(cpm_byte *addr, dword v)
void redir_wr32(cpm_byte* addr, dword v)
{
addr[0] = v & 0xFF;
addr[1] = (v >> 8) & 0xFF;
addr[2] = (v >> 16) & 0xFF;
addr[0] = v & 0xFF;
addr[1] = (v >> 8) & 0xFF;
addr[2] = (v >> 16) & 0xFF;
addr[3] = (v >> 24) & 0xFF;
}
dword redir_rd24(cpm_byte *addr)
dword redir_rd24(cpm_byte* addr)
{
register dword rv = addr[2];
@ -332,25 +384,23 @@ dword redir_rd24(cpm_byte *addr)
return rv;
}
dword redir_rd32(cpm_byte *addr)
dword redir_rd32(cpm_byte* addr)
{
register dword rv = addr[3];
register dword rv = addr[3];
rv = (rv << 8) | addr[2];
rv = (rv << 8) | addr[1];
rv = (rv << 8) | addr[0];
return rv;
rv = (rv << 8) | addr[1];
rv = (rv << 8) | addr[0];
return rv;
}
void redir_log_drv(cpm_byte drv)
{
if (!drv) redir_l_drives |= 1;
else redir_l_drives |= (1L << drv);
else redir_l_drives |= (1L << drv);
}
void redir_log_fcb(cpm_byte *fcb)
void redir_log_fcb(cpm_byte* fcb)
{
int drv = fcb[0] & 0x7F;
@ -358,51 +408,77 @@ void redir_log_fcb(cpm_byte *fcb)
else redir_log_drv(redir_cpmdrive);
}
int redir_ro_drv(cpm_byte drv)
{
if (!drv) return redir_ro_drives & 1;
else return redir_ro_drives & (1L << drv);
else return redir_ro_drives & (1L << drv);
}
int redir_ro_fcb(cpm_byte *fcb)
int redir_ro_fcb(cpm_byte* fcb)
{
int drv = fcb[0] & 0x7F;
if (drv && drv != '?') return redir_ro_drv(drv - 1);
else return redir_ro_drv(redir_cpmdrive);
else return redir_ro_drv(redir_cpmdrive);
}
cpm_word redir_xlt_err(void)
{
if (redir_password_error()) return 0x7FF; /* DRDOS pwd error */
switch(errno)
switch (errno)
{
case EISDIR:
case EBADF: return 9; /* Bad FCB */
case EINVAL: return 0x03FF; /* Readonly file */
case EPIPE: return 0x01FF; /* Broken pipe */
case ENOSPC: return 1; /* No space */
default: return 0xFF; /* Software error */
case EISDIR:
case EBADF: return 9; /* Bad FCB */
case EINVAL: return 0x03FF; /* Readonly file */
case EPIPE: return 0x01FF; /* Broken pipe */
case ENOSPC: return 1; /* No space */
default: return 0xFF; /* Software error */
}
}
#ifdef _WIN32
/* minimal implementation of truncate */
int truncate(const char* path, off_t length)
{
int result;
int fd = open(path, O_BINARY | O_RDWR);
BOOL bResult;
HANDLE hFile;
DWORD dwOffset;
DBGMSGV("truncate file %s to %lu\n", path, length);
if (fd < 0)
hFile = CreateFile(path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
DBGMSGV("truncate failed to open file (Error=%lu): %s\n", GetLastError(), GetErrorStr(GetLastError()));
return -1;
result = ftruncate(fd, length);
return close(fd) == 0 && result == 0 ? 0 : -1;
}
}
dwOffset = SetFilePointer(hFile, length, NULL, FILE_BEGIN);
if (dwOffset == INVALID_SET_FILE_POINTER)
{
DBGMSGV("truncate failed to open file (Error=%lu): %s\n", GetLastError(), GetErrorStr(GetLastError()));
CloseHandle(hFile);
return -1;
}
bResult = SetEndOfFile(hFile);
if (!bResult)
{
DBGMSGV("truncate failed to set end of file (Error=%lu): %s\n", GetLastError(), GetErrorStr(GetLastError()));
CloseHandle(hFile);
return -1;
}
bResult = CloseHandle(hFile);
if (!bResult)
{
DBGMSGV("truncate failed to close file (Error=%lu): %s\n", GetLastError(), GetErrorStr(GetLastError()));
return -1;
}
DBGMSGV("truncate set file length to %lu\n", dwOffset);
return 0;
}
#endif

306
Tools/unix/zxcc/xlt.c

@ -1,24 +1,24 @@
/*
CPMREDIR: CP/M filesystem redirector
Copyright (C) 1998, John Elliott <jce@seasip.demon.co.uk>
CPMREDIR: CP/M filesystem redirector
Copyright (C) 1998, 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 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.
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
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
This file holds functions dealing with name translation; also the
initialisation code.
This file holds functions dealing with name translation; also the
initialisation code.
*/
#include "cpmint.h"
@ -29,63 +29,61 @@ static char* skipUser(char* localname);
static void drdos_init(void)
{
/* The DJGPP DOS extender won't detect DRDOS using intdos(), so we have
to use __dpmi_int() instead. */
/* The DJGPP DOS extender won't detect DRDOS using intdos(), so we have
to use __dpmi_int() instead. */
#ifdef __GO32__
__dpmi_regs ir;
#ifdef __GO32__
__dpmi_regs ir;
ir.x.ax = 0x4452; /* "DR" */
ir.x.ax = 0x4452; /* "DR" */
__dpmi_int(0x21, &ir);
if (ir.x.flags & 1) return; /* Not DRDOS */
__dpmi_int(0x21, &ir);
if (ir.x.flags & 1) return; /* Not DRDOS */
redir_Msg("DRDOS detected.\r\n");
redir_Msg("DRDOS detected.\n");
redir_drdos = 1;
redir_drdos = 1;
#else /* __GO32__ */
#else /* __GO32__ */
union REGS ir, or;
union REGS ir, or ;
ir.w.ax = 0x4452; /* "DR" */
ir.w.ax = 0x4452; /* "DR" */
intdos(&ir, &or);
if (or.w.cflag) return; /* Not DRDOS */
intdos(&ir, &or );
if (or .w.cflag) return; /* Not DRDOS */
redir_Msg("DRDOS detected.\r\n");
redir_Msg("DRDOS detected.\n");
redir_drdos = 1;
#endif /* __GO32__ */
redir_drdos = 1;
#endif /* __GO32__ */
}
#endif /* __MSDOS__ */
#endif /* __MSDOS__ */
int fcb_init(void)
{
int n;
int n;
/* A: to O: free */
for (n = 0; n < 15; n++) redir_drive_prefix[n][0] = 0;
/* A: to O: free */
for (n = 0; n < 15; n++) redir_drive_prefix[n][0] = 0;
strcpy(redir_drive_prefix[15], "./"); /* P: is current directory */
strcpy(redir_drive_prefix[15], "./"); /* P: is current directory */
/* Log on to P:. It is the only drive at this point which we
* know works. */
redir_cpmdrive = 15;
/* Log on to P:. It is the only drive at this point which we
* know works. */
redir_cpmdrive = 15;
#ifdef __MSDOS__
drdos_init();
drdos_init();
#endif
return 1;
return 1;
}
/* Deinitialise the library. */
void fcb_deinit(void)
{
/* Nothing */
/* Nothing */
}
/* Translate a name from the host FS to a CP/M name. This will (if necessary)
@ -96,65 +94,65 @@ void fcb_deinit(void)
*
*/
void xlt_name(char *localname, char *cpmname)
void xlt_name(char* localname, char* cpmname)
{
char ibuf[CPM_MAXPATH + 1];
char nbuf[CPM_MAXPATH + 1];
char *pname = ibuf;
char *s;
int n;
char ibuf[CPM_MAXPATH + 1];
char nbuf[CPM_MAXPATH + 1];
char* pname = ibuf;
char* s;
int n;
sprintf(ibuf, "%-.*s", CPM_MAXPATH, skipUser(localname));
sprintf(ibuf, "%-.*s", CPM_MAXPATH, skipUser(localname));
while ((s = strpbrk(pname, DIRSEP))) { /* find the last directory separator allows mixed \ and / in windows */
while ((s = strpbrk(pname, DIRSEP))) { /* find the last directory separator allows mixed \ and / in windows */
#ifdef _WIN32
if (*s == '\\') /* convert separators to common format so directory tracking works more efficiently */
*s = '/';
if (*s == '\\') /* convert separators to common format so directory tracking works more efficiently */
*s = '/';
#endif
pname = s + 1;
}
if (pname == ibuf) { /* No path separators in the name. It is therefore a
local filename, so map it to drive P: */
strcpy(cpmname, "p:");
strcat(cpmname, ibuf);
return;
}
/* catch user specified current drive a,b,c,p or A,B,C,P only, which map to predefined directories */
if (pname == ibuf + 2 && ibuf[1] == ':' && (s = strchr("aAbBcCpP", ibuf[0]))) {
cpmname[0] = tolower(*s); /* make sure it's lower case */
strcpy(cpmname + 1, ibuf + 1);
return;
}
strcpy(nbuf, pname); /* nbuf holds filename component */
*pname = 0; /* ibuf holds path component */
/* See if the path is one of those already mapped to drives */
for (n = 0; n < 15; n++)
{
if (redir_drive_prefix[n][0] && !strcmp(ibuf, redir_drive_prefix[n]))
{
sprintf(cpmname,"%c:%s", n + 'a', nbuf);
return;
}
}
/* It is not, see if another drive can be allocated */
for (n = 0; n < 15; n++) if (!redir_drive_prefix[n][0])
{
strcpy(redir_drive_prefix[n], ibuf);
sprintf(cpmname,"%c:%s", n + 'a', nbuf);
return;
}
/* No other drive can be allocated */
strcpy(cpmname,"p:");
strcat(cpmname, nbuf);
pname = s + 1;
}
if (pname == ibuf) { /* No path separators in the name. It is therefore a
local filename, so map it to drive P: */
strcpy(cpmname, "p:");
strcat(cpmname, ibuf);
return;
}
/* catch user specified current drive a,b,c,p or A,B,C,P only, which map to predefined directories */
if (pname == ibuf + 2 && ibuf[1] == ':' && (s = strchr("aAbBcCpP", ibuf[0]))) {
cpmname[0] = tolower(*s); /* make sure it's lower case */
strcpy(cpmname + 1, ibuf + 1);
return;
}
strcpy(nbuf, pname); /* nbuf holds filename component */
*pname = 0; /* ibuf holds path component */
/* See if the path is one of those already mapped to drives */
for (n = 0; n < 15; n++)
{
if (redir_drive_prefix[n][0] && !strcmp(ibuf, redir_drive_prefix[n]))
{
sprintf(cpmname, "%c:%s", n + 'a', nbuf);
return;
}
}
/* It is not, see if another drive can be allocated */
for (n = 0; n < 15; n++) if (!redir_drive_prefix[n][0])
{
strcpy(redir_drive_prefix[n], ibuf);
sprintf(cpmname, "%c:%s", n + 'a', nbuf);
return;
}
/* No other drive can be allocated */
strcpy(cpmname, "p:");
strcat(cpmname, nbuf);
}
/* It is sometimes convenient to set some fixed mappings. This will create
@ -162,80 +160,78 @@ void xlt_name(char *localname, char *cpmname)
* Pass drive = -1 for "first available", or 0-15 for A: to P:
*/
int xlt_map(int drive, char *localdir)
int xlt_map(int drive, char* localdir)
{
int n;
if (drive == -1)
{
for (n = 0; n < 15; n++) if (!redir_drive_prefix[n][0])
{
drive = n;
break;
}
if (drive == -1) return 0; /* No space for mappings */
}
if (redir_drive_prefix[drive][0]) return 0; /* Drive taken */
sprintf(redir_drive_prefix[drive], "%-.*s", CPM_MAXPATH, localdir);
return 1;
int n;
if (drive == -1)
{
for (n = 0; n < 15; n++) if (!redir_drive_prefix[n][0])
{
drive = n;
break;
}
if (drive == -1) return 0; /* No space for mappings */
}
if (redir_drive_prefix[drive][0]) return 0; /* Drive taken */
sprintf(redir_drive_prefix[drive], "%-.*s", CPM_MAXPATH, localdir);
return 1;
}
/* Unmap a drive
*/
int xlt_umap(int drive)
{
if (!redir_drive_prefix[drive][0]) return 0; /* Drive not taken */
redir_drive_prefix[drive][0] = 0;
return 1;
if (!redir_drive_prefix[drive][0]) return 0; /* Drive not taken */
redir_drive_prefix[drive][0] = 0;
return 1;
}
char *xlt_getcwd(int drive)
char* xlt_getcwd(int drive)
{
if (drive < 0 || drive > 16) return "";
if (drive < 0 || drive > 16) return "";
return redir_drive_prefix[drive];
return redir_drive_prefix[drive];
}
/* as zxcc doesn't really support user spaces, remove any user specification
*hitech c supports
* hitech c supports
* [[0-9]+[:]][[a-pA-P]:]name[.ext] | [[a-pA-p][[0-9]+]:]name[.ext]
* this function also checks that user is no more than 2 digits and user # <= 31
* the hitech fcb checks for : as char 2, 3, or 4 which aligns to this
*/
static char* skipUser(char* localname) {
char* s;
int user;
int drive;
if (!localname || !(s = strchr(localname, ':')) || s > localname + 3)
return localname;
s = localname;
if (isdigit(*s)) {
user = *s++ - '0';
if (isdigit(*s)) {
user = user * 10 + *s++ - '0';
if (user > 31) /* check sensible user id */
return localname;
}
if (*s == ':') /* just strip the user id assume rest is a filename */
return s + 1;
if ('a' <= (drive = tolower(*s)) && drive <= 'p' && s[1] == ':')
return s; /* was form [0-9]+[a-pA-P] so strip user id */
else
return localname; /* not vaild so don't change */
}
if ((drive = tolower(*s++)) < 'a' || 'p' < drive || !isdigit(*s))
return localname; /* not a valid drive prefix or simple drive spec */
user = *s++ - '0';
if (isdigit(*s))
user = user * 10 + *s++ - '0';
if (*s != ':' || user > 31)
return localname;
*--s = drive; /* reinsert the drive just before the : */
return s;
char* s;
int user;
int drive;
if (!localname || !(s = strchr(localname, ':')) || s > localname + 3)
return localname;
s = localname;
if (isdigit(*s)) {
user = *s++ - '0';
if (isdigit(*s)) {
user = user * 10 + *s++ - '0';
if (user > 31) /* check sensible user id */
return localname;
}
if (*s == ':') /* just strip the user id assume rest is a filename */
return s + 1;
if ('a' <= (drive = tolower(*s)) && drive <= 'p' && s[1] == ':')
return s; /* was form [0-9]+[a-pA-P] so strip user id */
else
return localname; /* not vaild so don't change */
}
if ((drive = tolower(*s++)) < 'a' || 'p' < drive || !isdigit(*s))
return localname; /* not a valid drive prefix or simple drive spec */
user = *s++ - '0';
if (isdigit(*s))
user = user * 10 + *s++ - '0';
if (*s != ':' || user > 31)
return localname;
*--s = drive; /* reinsert the drive just before the : */
return s;
}

2
Tools/unix/zxcc/z80.c

@ -123,7 +123,7 @@ void mainloop(word spc, word ssp){
// if (pc == 0x1177) tr = 1;
// if (pc == 0x1185) tr = 0;
if (tr >= 1) ++id;
if (tr >= 1) printf("%d: PC=%04x %02x AF=%02x:%02x BC=%04x DE=%04x HL=%04x IX=%04x IY=%04x\r\n",
if (tr >= 1) printf("%d: PC=%04x %02x AF=%02x:%02x BC=%04x DE=%04x HL=%04x IX=%04x IY=%04x\n",
id, pc, fetch(pc), a,f, bc, de, hl, ix, iy);
}
*/

34
Tools/unix/zxcc/z80.h

@ -15,16 +15,16 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* [John Elliott, 15 July 2001]
* Copied this file into ZXCC, a CP/M emulator.
* Since ZXCC's memory is a flat 64k space and will never be bank-switched,
* the bank-switching code is removed.
* Since ZXCC has no memory-mapped screen, all the screen management code
* goes as well.
* Since ZXCC doesn't need its speed regulated, all the speed regulation
* code goes as well.
* Since ZXCC doesn't save or load snapshots... OK, you get the idea.
*/
/* [John Elliott, 15 July 2001]
* Copied this file into ZXCC, a CP/M emulator.
* Since ZXCC's memory is a flat 64k space and will never be bank-switched,
* the bank-switching code is removed.
* Since ZXCC has no memory-mapped screen, all the screen management code
* goes as well.
* Since ZXCC doesn't need its speed regulated, all the speed regulation
* code goes as well.
* Since ZXCC doesn't save or load snapshots... OK, you get the idea.
*/
#ifdef HAVE_SYS_PARAM_H
#include<sys/param.h>
@ -61,21 +61,21 @@ void drawborder();
#define store(x,y) do { RAM[(x)] = (y); } while(0)
#define store2b(x,hi,lo) do {\
RAM[(x)]=(lo); \
RAM[((x+1) & 0xFFFF)]=(hi); } while(0)
#define store2b(x,hi,lo) do { \
RAM[(x)]=(lo); \
RAM[((x+1) & 0xFFFF)]=(hi); } while(0)
#define store2(x,y) store2b(x,(y)>>8,y)
#ifdef __GNUC__
static void inline storefunc(unsigned short ad,unsigned char b){
store(ad,b);
static void inline storefunc(unsigned short ad, unsigned char b) {
store(ad, b);
}
#undef store
#define store(x,y) storefunc(x,y)
static void inline store2func(unsigned short ad,unsigned char b1,unsigned char b2){
store2b(ad,b1,b2);
static void inline store2func(unsigned short ad, unsigned char b1, unsigned char b2) {
store2b(ad, b1, b2);
}
#undef store2b
#define store2b(x,hi,lo) store2func(x,hi,lo)

290
Tools/unix/zxcc/zxbdos.c

@ -17,11 +17,11 @@
*/
dword cpmtime(time_t t)
{
dword d = (dword)((t / 86400) - 2921); /* CP/M day 0 is unix day 2921 */
dword h = (t % 86400) / 3600; /* Hour, 0-23 */
dword m = (t % 3600) / 60; /* Minute, 0-59 */
dword d = (dword)((t / 86400) - 2921); /* CP/M day 0 is unix day 2921 */
dword h = (t % 86400) / 3600; /* Hour, 0-23 */
dword m = (t % 3600) / 60; /* Minute, 0-59 */
return (d | (BCD(h) << 16) | (BCD(m) << 24));
return (d | (BCD(h) << 16) | (BCD(m) << 24));
}
@ -41,16 +41,16 @@ byte get_time(cpm_word b)
void wr24(word addr, dword v)
{
RAM[addr ] = v & 0xFF;
RAM[addr + 1] = (v >> 8) & 0xFF;
RAM[addr] = v & 0xFF;
RAM[addr + 1] = (v >> 8) & 0xFF;
RAM[addr + 2] = (v >> 16) & 0xFF;
}
void wr32(word addr, dword v)
{
RAM[addr ] = v & 0xFF;
RAM[addr + 1] = (v >> 8) & 0xFF;
RAM[addr + 2] = (v >> 16) & 0xFF;
RAM[addr] = v & 0xFF;
RAM[addr + 1] = (v >> 8) & 0xFF;
RAM[addr + 2] = (v >> 16) & 0xFF;
RAM[addr + 3] = (v >> 24) & 0xFF;
}
@ -66,12 +66,12 @@ dword rd24(word addr)
dword rd32(word addr)
{
register dword rv = RAM[addr + 3];
register dword rv = RAM[addr + 3];
rv = (rv << 8) | RAM[addr + 2];
rv = (rv << 8) | RAM[addr + 1];
rv = (rv << 8) | RAM[addr];
return rv;
rv = (rv << 8) | RAM[addr + 1];
rv = (rv << 8) | RAM[addr];
return rv;
}
#define peekw(addr) ( (((word)(RAM[addr + 1])) << 8) | RAM[addr])
@ -94,9 +94,9 @@ word cpm_errcde(word DE)
if (DE == 0xFFFF) return real_err;
real_err = DE;
if (DE == 0xFF00) cpm_error = 1;
else if (DE > 0xFF00) cpm_error = (DE & 0xFF);
else cpm_error = 0;
if (DE == 0xFF00) cpm_error = 1;
else if (DE > 0xFF00) cpm_error = (DE & 0xFF);
else cpm_error = 0;
return 0;
}
@ -118,31 +118,31 @@ void gsxwr(gsx_word addr, gsx_byte value)
#undef de
#undef hl
void setw(byte *l, byte *h, word w)
void setw(byte* l, byte* h, word w)
{
*l = (w & 0xFF);
*h = (w >> 8) & 0xFF;
}
void cpmbdos(byte *a, byte *b, byte *c, byte *d, byte *e, byte *f,
byte *h, byte *l, word *pc, word *ix, word *iy)
void cpmbdos(byte* a, byte* b, byte* c, byte* d, byte* e, byte* f,
byte* h, byte* l, word* pc, word* ix, word* iy)
{
word de = ((*d) << 8) | *e;
word hl = ((*h) << 8) | *l;
byte *pde = &RAM[de];
byte *pdma = &RAM[cpm_dma];
word de = ((*d) << 8) | *e;
word hl = ((*h) << 8) | *l;
byte* pde = &RAM[de];
byte* pdma = &RAM[cpm_dma];
word temp;
int retv;
Msg("BDOS service invoked: C=%02x DE=%04x\n", *c, de);
DBGMSGV("BDOS service invoked: C=0x%02X DE=0x%04X\n", *c, de);
switch(*c)
switch (*c)
{
case 0:
case 0:
*pc = 0;
break;
case 1: /* Get a character */
case 1: /* Get a character */
#ifdef USE_CPMIO
retv = cpm_bdos_1();
#else
@ -152,7 +152,7 @@ void cpmbdos(byte *a, byte *b, byte *c, byte *d, byte *e, byte *f,
setw(l, h, retv);
break;
case 2: /* Print a character */
case 2: /* Print a character */
#ifdef USE_CPMIO
if (cpm_bdos_2(*e)) *pc = 0;
#else
@ -160,29 +160,29 @@ void cpmbdos(byte *a, byte *b, byte *c, byte *d, byte *e, byte *f,
#endif
break;
case 3: /* No auxin */
case 3: /* No auxin */
setw(l, h, 0x1A);
break;
case 4: /* No auxout */
case 4: /* No auxout */
break;
case 5: /* No printer */
case 5: /* No printer */
break;
case 6: /* Direct console I/O */
case 6: /* Direct console I/O */
retv = cpm_bdos_6(*e);
if (retv < 0) *pc = 0;
setw(l, h, retv);
break;
case 7: /* No auxist */
case 8: /* No auxost */
case 7: /* No auxist */
case 8: /* No auxost */
break;
case 9: /* Print a $-terminated string */
case 9: /* Print a $-terminated string */
#ifdef USE_CPMIO
if (cpm_bdos_9((char *)pde)) *pc = 0;
if (cpm_bdos_9((char*)pde)) *pc = 0;
# else
for (temp = 0; RAM[de + temp] != '$'; ++temp)
{
@ -191,155 +191,155 @@ void cpmbdos(byte *a, byte *b, byte *c, byte *d, byte *e, byte *f,
#endif
break;
case 0x0A:
case 0x0A:
bdos_rdline(de, &(*pc));
break;
case 0x0B: /* Console status */
//*l = *h = 0; /* No keys pressed */
case 0x0B: /* Console status */
// *l = *h = 0; /* No keys pressed */
*l = cstat();
*h = 0;
break;
case 0x0C: /* Get CP/M version */
case 0x0C: /* Get CP/M version */
/* For GENCOM's benefit, claim to be v3.1 */
/* For GENCOM's benefit, claim to be v3.1 */
*l = 0x31; /* v3.1 */
/* *l = 0x22; * v2.2 */
/* *l = 0x22; * v2.2 */
*h = 0; /* CP/M, no network */
break;
case 0x0D: /* Re-log discs */
case 0x0D: /* Re-log discs */
fcb_reset();
break;
case 0x0E: /* Set default drive */
case 0x0E: /* Set default drive */
setw(l, h, fcb_drive(*e));
break;
case 0x0F: /* Open using FCB */
case 0x0F: /* Open using FCB */
setw(l, h, x_fcb_open(pde, pdma));
break;
case 0x10: /* Close using FCB */
case 0x10: /* Close using FCB */
setw(l, h, fcb_close(pde));
break;
case 0x11: /* Find first */
case 0x11: /* Find first */
setw(l, h, fcb_find1(pde, pdma));
break;
case 0x12:
case 0x12:
setw(l, h, fcb_find2(pde, pdma));
break;
case 0x13: /* Delete using FCB */
case 0x13: /* Delete using FCB */
setw(l, h, fcb_unlink(pde, pdma));
break;
case 0x14: /* Sequential read using FCB */
setw(l, h, fcb_read(pde, pdma));
break;
case 0x14: /* Sequential read using FCB */
setw(l, h, fcb_read(pde, pdma));
break;
case 0x15: /* Sequential write using FCB */
setw(l, h, fcb_write(pde, pdma));
break;
case 0x15: /* Sequential write using FCB */
setw(l, h, fcb_write(pde, pdma));
break;
case 0x16: /* Create using FCB */
case 0x16: /* Create using FCB */
setw(l, h, fcb_creat(pde, pdma));
break;
case 0x17: /* Rename using FCB */
case 0x17: /* Rename using FCB */
setw(l, h, fcb_rename(pde, pdma));
break;
case 0x18: /* Get login vector */
case 0x18: /* Get login vector */
setw(l, h, fcb_logvec());
break;
case 0x19: /* Get default drive */
case 0x19: /* Get default drive */
setw(l, h, cpm_drive);
break;
case 0x1A: /* Set DMA */
Msg("Set DMA to %04x\n", de);
case 0x1A: /* Set DMA */
DBGMSGV("Set DMA to 0x%04X\n", de);
cpm_dma = de;
break;
case 0x1B: /* Get alloc vector */
case 0x1B: /* Get alloc vector */
fcb_getalv(RAM + 0xFF80, 0x40);
setw(l, h, 0xFF80);
break;
case 0x1C: /* Make disc R/O */
case 0x1C: /* Make disc R/O */
setw(l, h, fcb_rodisk());
break;
case 0x1D: /* Get R/O vector */
case 0x1D: /* Get R/O vector */
setw(l, h, fcb_rovec());
break;
case 0x1E: /* Set attributes */
case 0x1E: /* Set attributes */
setw(l, h, fcb_chmod(pde, pdma));
break;
case 0x1F: /* Get DPB */
case 0x1F: /* Get DPB */
fcb_getdpb(RAM + 0xFFC0);
setw(l, h, 0xFFC0);
break; /* Whoops. Missed that 'break'. */
break;
case 0x20: /* Get/set uid */
case 0x20: /* Get/set uid */
setw(l, h, fcb_user(*e));
break;
case 0x21: /* Read a record */
setw(l, h, fcb_randrd(pde, pdma));
break;
case 0x21: /* Read a record */
setw(l, h, fcb_randrd(pde, pdma));
break;
case 0x22: /* Write a record */
setw(l, h, fcb_randwr(pde, pdma));
break;
case 0x22: /* Write a record */
setw(l, h, fcb_randwr(pde, pdma));
break;
case 0x23: /* Get file size */
case 0x23: /* Get file size */
setw(l, h, x_fcb_stat(pde));
break;
case 0x24: /* Get file pointer */
case 0x24: /* Get file pointer */
setw(l, h, fcb_tell(pde));
break;
case 0x25:
case 0x25:
setw(l, h, fcb_resro(de));
break;
/* MP/M drive access functions, not implemented */
case 0x28: /* Write with 0 fill */
case 0x28: /* Write with 0 fill */
setw(l, h, fcb_randwz(pde, pdma));
break;
/* MP/M record locking functions, not implemented */
case 0x2C: /* Set no. of records to read/write */
case 0x2C: /* Set no. of records to read/write */
setw(l, h, fcb_multirec(*e));
break;
case 0x2D: /* Set error mode */
case 0x2D: /* Set error mode */
err_mode = *e;
break;
case 0x2E:
case 0x2E:
setw(l, h, fcb_dfree(*e, pdma));
break; /* Whoops. Missed that 'break'. */
break;
/* 0x2F: Chain */
case 0x30:
case 0x30:
setw(l, h, fcb_sync(*e));
break;
case 0x31:
case 0x31:
if (pde[1] == 0xFE)
{
RAM[0xFE9C + *pde] = pde[2];
@ -356,7 +356,7 @@ void cpmbdos(byte *a, byte *b, byte *c, byte *d, byte *e, byte *f,
}
break;
case 0x32:
case 0x32:
temp = *ix;
*ix = 3 * (pde[0] + 1);
*a = pde[1];
@ -366,110 +366,110 @@ void cpmbdos(byte *a, byte *b, byte *c, byte *d, byte *e, byte *f,
*d = pde[5];
*l = pde[6];
*h = pde[7];
cpmbios(a,b,c,d,e,f,h,l,pc,ix,iy);
*ix = temp;
cpmbios(a, b, c, d, e, f, h, l, pc, ix, iy);
*ix = temp;
break;
case 0x3C: /* Communicate with RSX */
case 0x3C: /* Communicate with RSX */
*h = 0; *l = 0xFF; /* return error */
break;
case 0x62: /* Purge */
case 0x62: /* Purge */
setw(l, h, fcb_purge());
break;
case 0x63: /* Truncate file */
case 0x63: /* Truncate file */
setw(l, h, fcb_trunc(pde, pdma));
break;
case 0x64: /* Set label */
case 0x64: /* Set label */
setw(l, h, fcb_setlbl(pde, pdma));
break;
case 0x65: /* Get label byte */
case 0x65: /* Get label byte */
setw(l, h, fcb_getlbl(*e));
break;
case 0x66: /* Get file date */
setw(l, h, fcb_date(pde));
break;
case 0x66: /* Get file date */
setw(l, h, fcb_date(pde));
break;
case 0x67: /* Set password */
case 0x67: /* Set password */
setw(l, h, fcb_setpwd(pde, pdma));
break;
case 0x68: /* Set time of day */
case 0x68: /* Set time of day */
/* Not advisable to let an emulator play with the clock */
break;
case 0x69: /* Get time of day */
case 0x69: /* Get time of day */
setw(l, h, get_time(de));
break;
case 0x6A: /* Set default password */
case 0x6A: /* Set default password */
setw(l, h, fcb_defpwd(pde));
break;
case 0x6B: /* Get serial number */
case 0x6B: /* Get serial number */
memcpy(pde, SERIAL, 6);
break;
case 0x6C: /* 0.03 set error code */
case 0x6C: /* 0.03 set error code */
setw(l, h, cpm_errcde(de));
break;
#ifdef USE_CPMIO
case 0x6D: /* Set/get console mode */
case 0x6D: /* Set/get console mode */
setw(l, h, cpm_bdos_109(de));
break;
case 0x6E: /* Set/get string delimiter */
case 0x6E: /* Set/get string delimiter */
setw(l, h, cpm_bdos_110(*e));
break;
case 0x6F: /* Send fixed length string to screen */
if (cpm_bdos_111((char *)RAM + peekw(de),
peekw(de + 2)))
case 0x6F: /* Send fixed length string to screen */
if (cpm_bdos_111((char*)RAM + peekw(de),
peekw(de + 2)))
*pc = 0;
break;
case 0x70: /* Send fixed length string to printer */
case 0x70: /* Send fixed length string to printer */
break;
/* 0x71: Strange PCP/M function */
/* 0x71: Strange PCP/M function */
#else
case 0x6D: /* Set/get console mode */
case 0x6D: /* Set/get console mode */
setw(l, h, 0);
break;
#endif
#ifdef USE_CPMGSX
case 0x73: /* GSX */
case 0x73: /* GSX */
setw(l, h, gsx80(gsxrd, gsxwr, de));
break;
#endif
case 0x74: /* Set date stamp */
case 0x74: /* Set date stamp */
setw(l, h, fcb_sdate(pde, pdma));
break;
case 0x98: /* Parse filename */
setw(l, h, fcb_parse((char *)RAM + peekw(de),
(byte *)RAM + peekw(de + 2)));
case 0x98: /* Parse filename */
setw(l, h, fcb_parse((char*)RAM + peekw(de),
(byte*)RAM + peekw(de + 2)));
break;
default:
default:
#ifdef USE_CPMIO
cpm_scr_unit();
#endif
#ifdef USE_CPMGSX
gsx_deinit();
gsx_deinit();
#endif
fprintf(stderr,"%s: Unsupported BDOS call %d\n", progname,
(int)(*c));
dump_regs(stderr,*a,*b,*c,*d,*e,*f,*h,*l,*pc,*ix,*iy);
fprintf(stderr, "%s: Unsupported BDOS call %d\n", progname,
(int)(*c));
dump_regs(stderr, *a, *b, *c, *d, *e, *f, *h, *l, *pc, *ix, *iy);
zxcc_exit(1);
break;
}
@ -478,38 +478,36 @@ void cpmbdos(byte *a, byte *b, byte *c, byte *d, byte *e, byte *f,
*b = *h;
}
void cpmbios(byte *a, byte *b, byte *c, byte *d, byte *e, byte *f,
byte *h, byte *l, word *pc, word *ix, word *iy)
void cpmbios(byte* a, byte* b, byte* c, byte* d, byte* e, byte* f,
byte* h, byte* l, word* pc, word* ix, word* iy)
{
int func = (((*ix) & 0xFF) / 3) - 1;
int func = (((*ix) & 0xFF) / 3) - 1;
Msg("BIOS service invoked: func=%02x\n", func);
DBGMSGV("BIOS service invoked: func=0x%02X\n", func);
switch(func) /* BIOS function */
switch (func) /* BIOS function */
{
case 1:
case 1:
zxcc_exit(zxcc_term()); /* Program termination */
break;
case 2: /* CONST */
case 2: /* CONST */
#ifdef USE_CPMIO
*a = cpm_const();
* a = cpm_const();
#else
*a = cpm_bdos_6(0xFE);
* a = cpm_bdos_6(0xFE);
#endif
break;
case 3: /* CONIN */
case 3: /* CONIN */
#ifdef USE_CPMIO
*a = cpm_conin();
* a = cpm_conin();
#else
*a = cpm_bdos_6(0xFD);
* a = cpm_bdos_6(0xFD);
#endif
break;
case 4: /* CONOUT */
case 4: /* CONOUT */
#ifdef USE_CPMIO
cpm_conout(*c);
#else
@ -517,41 +515,41 @@ void cpmbios(byte *a, byte *b, byte *c, byte *d, byte *e, byte *f,
#endif
break;
case 20: /* DEVTBL */
case 20: /* DEVTBL */
setw(l, h, 0xFFFF);
break;
case 22: /* DRVTBL */
case 22: /* DRVTBL */
setw(l, h, 0xFFFF);
break;
case 26: /* TIME */
case 26: /* TIME */
RAM[0xFEF8] = get_time(0xFEF4);
break;
case 30: /* USERF!!! */
case 30: /* USERF!!! */
#ifdef USE_CPMIO
cpm_bdos_110('$');
cpm_bdos_9("This program has attempted to call USERF, "
"which is not implemented\r\n$");
"which is not implemented\n$");
#else
printf("This program has attempted to call USERF, which "
"is not implemented.\n");
"is not implemented.\n");
#endif
zxcc_term();
zxcc_exit(1);
break;
default:
default:
#ifdef USE_CPMIO
cpm_scr_unit();
cpm_scr_unit();
#endif
#ifdef USE_CPMGSX
gsx_deinit();
gsx_deinit();
#endif
fprintf(stderr,"%s: Unsupported BIOS call %d\n", progname, func);
dump_regs(stderr,*a,*b,*c,*d,*e,*f,*h,*l,*pc,*ix,*iy);
zxcc_exit(1);
fprintf(stderr, "%s: Unsupported BIOS call %d\n", progname, func);
dump_regs(stderr, *a, *b, *c, *d, *e, *f, *h, *l, *pc, *ix, *iy);
zxcc_exit(1);
}
}

15
Tools/unix/zxcc/zxbdos.h

@ -1,5 +1,5 @@
extern char *progname;
extern char **argv;
extern char* progname;
extern char** argv;
extern int argc;
extern byte cpm_drive;
@ -7,14 +7,14 @@ extern byte cpm_user;
extern byte RAM[65536]; /* The Z80's address space */
extern void Msg(char *s, ...);
extern void Msg(char* s, ...);
#ifdef BDOS_DEF
word cpm_dma = 0x80; /* DMA address */
byte err_mode = 0xFF;
word cpm_dma = 0x80; /* DMA address */
byte err_mode = 0xFF;
byte rec_multi = 1;
word rec_len = 128;
word rec_len = 128;
word ffirst_fcb = 0xFFFF;
byte cpm_error = 0; /* Error code returned by CP/M */
@ -26,7 +26,7 @@ extern byte err_mode, rec_multi, cpm_error;
#endif /* BDOS_DEF */
#ifndef O_BINARY /* Necessary in DOS, not present in Linux */
#define O_BINARY 0
#define O_BINARY 0
#endif
typedef unsigned long dword;
@ -47,4 +47,3 @@ void gsxwr(gsx_word addr, gsx_byte value);
void cpmbdos();
void cpmbios();

25
Tools/unix/zxcc/zxcbdos.c

@ -2,36 +2,27 @@
#include "zxbdos.h"
#include "zxcbdos.h"
#ifndef _WIN32
#include <sys/ioctl.h>
#endif
#ifdef _WIN32
#include <conio.h>
#endif
/* Line input */
#ifdef USE_CPMIO
void bdos_rdline(word line, word *PC)
void bdos_rdline(word line, word* PC)
{
unsigned char *buf;
unsigned char* buf;
if (!line) line = cpm_dma;
else RAM[line + 1] = 0;
buf = (unsigned char *)&RAM[line];
buf = (unsigned char*)&RAM[line];
if (cpm_bdos_10(buf)) *PC = 0;
}
#else /* def USE_CPMIO */
void bdos_rdline(word line, word *PC)
void bdos_rdline(word line, word* PC)
{
unsigned char c;
unsigned char *p;
unsigned char* p;
int n;
int maxlen;
@ -39,7 +30,7 @@ void bdos_rdline(word line, word *PC)
maxlen = RAM[line];
// fgets causes extra linefeeds, so we invent our own
//fgets((char *)(RAM + line + 2), maxlen, stdin);
// fgets((char *)(RAM + line + 2), maxlen, stdin);
p = (RAM + line + 2);
n = 0;
@ -72,7 +63,7 @@ void bdos_rdline(word line, word *PC)
//RAM[line + 1] = strlen((char *)(RAM + line + 2)) - 1;
RAM[line + 1] = (unsigned char)n;
Msg("Input: [%d] %-*.*s\n", RAM[line + 1], RAM[line + 1], RAM[line +1], (char *)(RAM+line+2));
DBGMSGV("Input: [%d] %-*.*s\n", RAM[line + 1], RAM[line + 1], RAM[line + 1], (char*)(RAM + line + 2));
}
#endif /* ndef USE_CPMIO */
@ -82,7 +73,7 @@ int cpm_bdos_6(byte e)
{
int c;
switch(e) {
switch (e) {
case 0xFF:
if (cstat()) return cin();
return 0;

6
Tools/unix/zxcc/zxcbdos.h

@ -1,4 +1,6 @@
void bdos_rdline(word line, word *PC);
void bdos_rdline(word line, word* PC);
int cpm_bdos_6(byte e);
byte cin(void);
void cout(byte);
int cstat(void);

382
Tools/unix/zxcc/zxcc.c

@ -2,8 +2,8 @@
/* Global variables */
char *progname;
char **argv;
char* progname;
char** argv;
int argc;
byte cpm_drive;
@ -14,6 +14,10 @@ char bindir80[CPM_MAXPATH] = "";
char libdir80[CPM_MAXPATH] = "";
char incdir80[CPM_MAXPATH] = "";
#ifndef _WIN32
struct termios tc_orig;
#endif
byte RAM[65536]; /* The Z80's address space */
void load_comfile(void); /* Forward declaration */
@ -21,34 +25,25 @@ void load_comfile(void); /* Forward declaration */
static int deinit_term, deinit_gsx;
static void mkpath(char* fullpath, char* path, char* subdir);
#ifndef _WIN32
struct termios tc_orig;
void raw_init(void);
void deinit_raw(void);
#endif
void dump_regs(FILE *fp, byte a, byte b, byte c, byte d, byte e, byte f,
byte h, byte l, word pc, word ix, word iy)
void dump_regs(FILE* fp, byte a, byte b, byte c, byte d, byte e, byte f,
byte h, byte l, word pc, word ix, word iy)
{
fprintf(fp, "\tAF=%02x%02x BC=%02x%02x DE=%02x%02x HL=%02x%02x\n"
"\tIX=%04x IY=%04x PC=%04x\n",
a,f,b,c,d,e,h,l,pc,ix,iy);
"\tIX=%04x IY=%04x PC=%04x\n",
a, f, b, c, d, e, h, l, pc, ix, iy);
}
char *parse_to_fcb(char *s, int afcb)
char* parse_to_fcb(char* s, int afcb)
{
byte *fcb = &RAM[afcb+1];
byte* fcb = &RAM[afcb + 1];
RAM[afcb] = 0;
memset(fcb, ' ', 11);
while (s[0]==' ') /* skip leading spaces */
{
s++;
}
while (s[0] == ' ') /* skip leading spaces */
{
s++;
}
while (1)
{
if (s[0] == 0) break;
@ -57,70 +52,54 @@ char *parse_to_fcb(char *s, int afcb)
{
RAM[afcb] = s[0] - '@';
if (RAM[afcb] > 16) RAM[afcb] -= 0x20;
s+=2;
s += 2;
continue;
}
if (s[0] == '.')
{
++s;
fcb = &RAM[afcb+9];
fcb = &RAM[afcb + 9];
continue;
}
*fcb = *s; if (islower(*fcb)) *fcb = toupper(*fcb);
++s;
++fcb;
if (fcb >= &RAM[afcb+12]) break;
if (fcb >= &RAM[afcb + 12]) break;
}
return s;
}
void Msg(char *s, ...)
{
#ifdef DEBUG
va_list ap;
va_start(ap, s);
fprintf(stderr, "%s trace: ", progname);
vfprintf(stderr, s, ap);
fflush(stderr);
va_end(ap);
#endif
}
void ed_fe(byte *a, byte *b, byte *c, byte *d, byte *e, byte *f,
byte *h, byte *l, word *pc, word *ix, word *iy)
void ed_fe(byte* a, byte* b, byte* c, byte* d, byte* e, byte* f,
byte* h, byte* l, word* pc, word* ix, word* iy)
{
switch(*a)
switch (*a)
{
case 0xC0:
cpmbdos(a,b,c,d,e,f,h,l,pc,ix,iy);
case 0xC0:
cpmbdos(a, b, c, d, e, f, h, l, pc, ix, iy);
break;
case 0xC1:
case 0xC1:
load_comfile();
break;
case 0xC2:
fprintf(stderr,"%s: Incompatible BIOS.BIN\n", progname);
case 0xC2:
fprintf(stderr, "%s: Incompatible BIOS.BIN\n", progname);
zxcc_term();
zxcc_exit(1);
case 0xC3:
cpmbios(a,b,c,d,e,f,h,l,pc,ix,iy);
case 0xC3:
cpmbios(a, b, c, d, e, f, h, l, pc, ix, iy);
break;
default:
default:
fprintf(stderr, "%s: Z80 encountered invalid trap\n", progname);
dump_regs(stderr,*a,*b,*c,*d,*e,*f,*h,*l,*pc,*ix,*iy);
dump_regs(stderr, *a, *b, *c, *d, *e, *f, *h, *l, *pc, *ix, *iy);
zxcc_term();
zxcc_exit(1);
}
}
/*
* load_bios() loads the minimal CP/M BIOS and BDOS.
*
@ -128,37 +107,37 @@ void ed_fe(byte *a, byte *b, byte *c, byte *d, byte *e, byte *f,
void load_bios(void)
{
char dir[CPM_MAXPATH + 1], fname[CPM_MAXPATH + 1];
char* q;
size_t bios_len;
char dir[CPM_MAXPATH + 1], fname[CPM_MAXPATH + 1];
char* q;
size_t bios_len;
FILE* fp = fopen("bios.bin", "rb");
FILE* fp = fopen("bios.bin", "rb");
if (!fp)
{
strcpy(fname, bindir80);
strcat(fname, "bios.bin");
fp = fopen(fname, "rb");
strcpy(fname, bindir80);
strcat(fname, "bios.bin");
fp = fopen(fname, "rb");
}
if (!fp)
{
if (!fp)
{
#ifdef _WIN32
dir[0] = 0; /* use strncat in case the path is very long */
strncat(dir, _pgmptr, CPM_MAXPATH - 8); /* copy the executable path */
dir[0] = 0; /* use strncat in case the path is very long */
strncat(dir, _pgmptr, CPM_MAXPATH - 8); /* copy the executable path */
#elif defined(__APPLE__)
uint32_t size = CPM_MAXPATH - 8;
_NSGetExecutablePath(dir, &size);
uint32_t size = CPM_MAXPATH - 8;
_NSGetExecutablePath(dir, &size);
#else
readlink("/proc/self/exe", dir, CPM_MAXPATH - 8); /* allow room for bios.bin */
readlink("/proc/self/exe", dir, CPM_MAXPATH - 8); /* allow room for bios.bin */
#endif
q = strrchr(dir, DIRSEPCH);
*++q = 0;
strcpy(fname, dir);
strcat(fname, "bios.bin");
fp = fopen(fname, "rb");
q = strrchr(dir, DIRSEPCH);
*++q = 0;
strcpy(fname, dir);
strcat(fname, "bios.bin");
fp = fopen(fname, "rb");
}
if (!fp)
{
fprintf(stderr,"%s: Cannot locate bios.bin\n", progname);
fprintf(stderr, "%s: Cannot locate bios.bin\n", progname);
zxcc_term();
zxcc_exit(1);
}
@ -166,30 +145,30 @@ void load_bios(void)
if (bios_len < 1 || ferror(fp))
{
fclose(fp);
fprintf(stderr,"%s: Cannot load bios.bin\n", progname);
fprintf(stderr, "%s: Cannot load bios.bin\n", progname);
zxcc_term();
zxcc_exit(1);
zxcc_exit(1);
}
fclose(fp);
Msg("Loaded %d bytes of BIOS\n", bios_len);
DBGMSGV("Loaded %d bytes of BIOS\n", bios_len);
}
/*
* try_com() attempts to open file, file.com, file.COM, file.cpm and file.CPM
*
*/
FILE *try_com(char *s)
FILE* try_com(char* s)
{
char fname[CPM_MAXPATH + 1];
FILE *fp;
FILE* fp;
strcpy(fname, s);
fp = fopen(s, "rb"); if (fp) return fp;
sprintf(s,"%s.com", fname); fp = fopen(s, "rb"); if (fp) return fp;
sprintf(s,"%s.COM", fname); fp = fopen(s, "rb"); if (fp) return fp;
sprintf(s,"%s.cpm", fname); fp = fopen(s, "rb"); if (fp) return fp;
sprintf(s,"%s.CPM", fname); fp = fopen(s, "rb"); if (fp) return fp;
fp = fopen(s, "rb"); if (fp) return fp;
sprintf(s, "%s.com", fname); fp = fopen(s, "rb"); if (fp) return fp;
sprintf(s, "%s.COM", fname); fp = fopen(s, "rb"); if (fp) return fp;
sprintf(s, "%s.cpm", fname); fp = fopen(s, "rb"); if (fp) return fp;
sprintf(s, "%s.CPM", fname); fp = fopen(s, "rb"); if (fp) return fp;
strcpy(s, fname);
return NULL;
@ -203,46 +182,46 @@ FILE *try_com(char *s)
void load_comfile(void)
{
size_t com_len;
char fname[CPM_MAXPATH + 1];
FILE *fp;
/* Look in current directory first */
strcpy(fname, argv[1]);
fp = try_com(fname);
if (!fp)
size_t com_len;
char fname[CPM_MAXPATH + 1];
FILE* fp;
/* Look in current directory first */
strcpy(fname, argv[1]);
fp = try_com(fname);
if (!fp)
{
strcpy(fname, bindir80);
strcat(fname, argv[1]);
strcpy(fname, bindir80);
strcat(fname, argv[1]);
fp = try_com(fname);
}
if (!fp)
{
fprintf(stderr,"%s: Cannot locate %s, %s.com, %s.COM, %s.cpm _or_ %s.CPM\r\n",
progname, argv[1], argv[1], argv[1], argv[1], argv[1]);
if (!fp)
{
fprintf(stderr, "%s: Cannot locate %s, %s.com, %s.COM, %s.cpm _or_ %s.CPM\n",
progname, argv[1], argv[1], argv[1], argv[1], argv[1]);
zxcc_term();
zxcc_exit(1);
}
com_len = fread(RAM + 0x0100, 1, 0xFD00, fp);
if (com_len < 1 || ferror(fp))
{
fclose(fp);
fprintf(stderr,"%s: Cannot load %s\n", progname, fname);
zxcc_exit(1);
}
com_len = fread(RAM + 0x0100, 1, 0xFD00, fp);
if (com_len < 1 || ferror(fp))
{
fclose(fp);
fprintf(stderr, "%s: Cannot load %s\n", progname, fname);
zxcc_term();
zxcc_exit(1);
}
zxcc_exit(1);
}
fclose(fp);
/* read() can corrupt buffer area following data read if length
* of data read is less than buffer. Clean it up. */
memset(RAM + 0x0100 + com_len, 0, 0xFD00 - com_len);
Msg("Loaded %d bytes from %s\n", com_len, fname);
DBGMSGV("Loaded %d bytes from %s\n", com_len, fname);
}
unsigned int in() { return 0; }
unsigned int out() { return 0; }
/*
* xltname: Convert a unix filepath into a CP/M compatible drive:name form.
* The unix filename must be 8.3 or the CP/M code will reject it.
@ -251,7 +230,7 @@ unsigned int out() { return 0; }
* the result to the command line.
*/
void zxcc_xltname(char *name, char *pcmd)
void zxcc_xltname(char* name, char* pcmd)
{
char nbuf[CPM_MAXPATH + 1];
@ -264,75 +243,63 @@ void zxcc_xltname(char *name, char *pcmd)
program to load; the remaining arguments are arguments for the CP/M program.
main() also loads the vestigial CP/M BIOS and does some sanity checks
on the endianness of the host CPU and the sizes of data types.
on the endianness of the host CPU and the sizes of data types.
*/
int main(int ac, char **av)
int main(int ac, char** av)
{
int n;
char *pCmd, *str;
char* tmpenv;
char* pCmd, * str;
char* tmpenv;
argc = ac;
argv = av;
#ifdef __PACIFIC__ /* Pacific C doesn't support argv[0] */
progname="ZXCC";
progname = "ZXCC";
#endif
progname = argv[0];
/* DJGPP includes the whole path in the program name, which looks
* untidy...
*/
while ((str = strpbrk(progname, DIRSEP)))
progname = str + 1;
* untidy...
*/
while ((str = strpbrk(progname, DIRSEP)))
progname = str + 1;
#ifdef DEBUG
fprintf(stderr, "\n\n");
Msg("Start of execution: ");
DBGMSG("Start of execution: ");
for (n = 0; n < argc; n++)
fprintf(stderr, " %s", argv[n]);
fprintf(stderr, "\n");
#endif
if (_isatty(STDIN_FILENO))
Msg("Using interactive console mode\n");
else
Msg("Using standard input/ouput mode\n");
term_init();
if (sizeof(int) > 8 || sizeof(byte) != 1 || sizeof(word) != 2)
{
fprintf(stderr,"%s: type lengths incorrect; edit typedefs "
"and recompile.\n", progname);
fprintf(stderr, "%s: type lengths incorrect; edit typedefs "
"and recompile.\n", progname);
zxcc_exit(1);
}
if (argc < 2)
{
fprintf(stderr,"%s: No CP/M program name provided.\n",progname);
fprintf(stderr, "%s: No CP/M program name provided.\n", progname);
zxcc_exit(1);
}
#ifdef _WIN32
setmode(STDIN_FILENO, O_BINARY );
setmode(STDOUT_FILENO, O_BINARY );
#else
if (_isatty(STDIN_FILENO))
raw_init();
#endif
/* Parse arguments. An argument can be either:
* preceded by a '-', in which case it is copied in as-is, less the
dash;
* preceded by a '+', in which case it is parsed as a filename and
then concatenated to the previous argument;
* preceded by a '+-', in which case it is concatenated without
parsing;
* not preceded by either, in which case it is parsed as a filename.
* preceded by a '-', in which case it is copied in as-is, less the
dash;
* preceded by a '+', in which case it is parsed as a filename and
then concatenated to the previous argument;
* preceded by a '+-', in which case it is concatenated without
parsing;
* not preceded by either, in which case it is parsed as a filename.
So, the argument string "--a -b c +-=q --x +/dev/null" would be rendered
into CP/M form as "-a b p:c=q -xd:null" */
So, the argument string "--a -b c +-=q --x +/dev/null" would be rendered
into CP/M form as "-a b p:c=q -xd:null" */
if (!fcb_init())
{
@ -340,35 +307,35 @@ int main(int ac, char **av)
zxcc_exit(1);
}
/* allow environment variables to override default locations */
/* two options are supported, explicit overrides for each directory
* (BINDIR80, LIBDIR80, INCDIR80)
* or a common directory prefix override (CPMDIR80)
* the explict override takes precedence
*/
if ((tmpenv = getenv("CPMDIR80"))) {
mkpath(bindir80, tmpenv, BIN80); /* use CPMDIR80 & std subdirs */
mkpath(libdir80, tmpenv, LIB80);
mkpath(incdir80, tmpenv, INC80);
}
if ((tmpenv = getenv("BINDIR80")))
mkpath(bindir80, tmpenv, "");
if ((tmpenv = getenv("LIBDIR80")))
mkpath(libdir80, tmpenv, "");
if ((tmpenv = getenv("INCDIR80")))
mkpath(incdir80, tmpenv, "");
Msg("BINDIR80=\"%s\"\n", bindir80);
Msg("LIBDIR80=\"%s\"\n", libdir80);
Msg("INCDIR80=\"%s\"\n", incdir80);
/* allow environment variables to override default locations */
/* two options are supported, explicit overrides for each directory
* (BINDIR80, LIBDIR80, INCDIR80)
* or a common directory prefix override (CPMDIR80)
* the explict override takes precedence
*/
if ((tmpenv = getenv("CPMDIR80"))) {
mkpath(bindir80, tmpenv, BIN80); /* use CPMDIR80 & std subdirs */
mkpath(libdir80, tmpenv, LIB80);
mkpath(incdir80, tmpenv, INC80);
}
if ((tmpenv = getenv("BINDIR80")))
mkpath(bindir80, tmpenv, "");
if ((tmpenv = getenv("LIBDIR80")))
mkpath(libdir80, tmpenv, "");
if ((tmpenv = getenv("INCDIR80")))
mkpath(incdir80, tmpenv, "");
DBGMSGV("BINDIR80=\"%s\"\n", bindir80);
DBGMSGV("LIBDIR80=\"%s\"\n", libdir80);
DBGMSGV("INCDIR80=\"%s\"\n", incdir80);
xlt_map(0, bindir80); /* Establish the 3 fixed mappings */
xlt_map(1, libdir80);
xlt_map(2, incdir80);
pCmd = (char *)RAM + 0x81;
pCmd = (char*)RAM + 0x81;
for (n = 2; n < argc; n++)
{
@ -385,23 +352,23 @@ int main(int ac, char **av)
}
else if (argv[n][0] == '+')
{
zxcc_xltname(pCmd, argv[n]+1);
zxcc_xltname(pCmd, argv[n] + 1);
}
else /* Translate a filename */
{
strcat(pCmd, " ");
zxcc_xltname(pCmd, argv[n]);
zxcc_xltname(pCmd, argv[n]);
}
}
pCmd[0x7F] = 0; /* Truncate to fit the buffer */
RAM[0x80] = (byte)strlen(pCmd);
RAM[0x80] = (byte)strlen(pCmd);
str = parse_to_fcb(pCmd, 0x5C);
parse_to_fcb(str, 0x6C);
// This statement is very useful when creating a client like zxc or zxas
Msg("Command tail is \"%s\"\n", pCmd);
DBGMSGV("Command tail is \"%s\"\n", pCmd);
load_bios();
@ -413,7 +380,7 @@ int main(int ac, char **av)
RAM[0xFEAF] = 0x0F; /* CCP drive */
#ifdef USE_CPMIO
RAM[0xFEB6] = cpm_term_direct(CPM_TERM_WIDTH, -1);
RAM[0xFEB6] = cpm_term_direct(CPM_TERM_WIDTH, -1);
RAM[0xFEB8] = cpm_term_direct(CPM_TERM_HEIGHT, -1);
#else
RAM[0xFEB6] = 79;
@ -432,7 +399,7 @@ int main(int ac, char **av)
RAM[0xFEFF] = 0xFE; /* BDOS */
cpm_drive = 0x0F; /* Start logged into P: */
cpm_user = 0; /* and user 0 */
cpm_user = 0; /* and user 0 */
#ifdef USE_CPMIO
cpm_scr_init(); deinit_term = 1;
@ -462,10 +429,6 @@ int zxcc_term(void)
{
word n;
#ifndef _WIN32
deinit_raw();
#endif
//n = RAM[0x81]; /* Get the return code. This is Hi-Tech C */
//n = (n << 8) | RAM[0x80]; /* specific and fails with other COM files */
n = 0;
@ -476,12 +439,15 @@ int zxcc_term(void)
{ /* (my modified Hi-Tech C library uses this */
n = cpm_error; /* call) */
}
if (n < 256 || n == 0xFFFF)
{
Msg("Return code %d\n", n);
return n;
}
else return 0;
DBGMSGV("Return code %d\n", n);
else
n = 0;
term_reset();
return n;
}
/* helper function to build full path */
@ -489,12 +455,12 @@ int zxcc_term(void)
* before appending the subdir
*/
static void mkpath(char* fullpath, char* path, char* subdir) {
char* s;
strcpy(fullpath, path);
s = strchr(fullpath, '\0');
if (*fullpath && !ISDIRSEP(s[-1])) /* make sure we have dir sep */
*s++ = '/';
strcpy(s, subdir);
char* s;
strcpy(fullpath, path);
s = strchr(fullpath, '\0');
if (*fullpath && !ISDIRSEP(s[-1])) /* make sure we have dir sep */
*s++ = '/';
strcpy(s, subdir);
}
#ifndef _WIN32
@ -503,11 +469,11 @@ void raw_init(void)
{
struct termios tc_raw;
Msg("Enabling RAW Terminal IO\n");
DBGMSG("Enabling RAW Terminal IO\n");
if (tcgetattr(STDIN_FILENO, &tc_orig) == -1)
{
Msg("Failed to enable RAW Terminal IO - tcgetattr() failed\n");
DBGMSG("Failed to enable RAW Terminal IO - tcgetattr() failed\n");
zxcc_exit(1);;
}
@ -527,11 +493,11 @@ void raw_init(void)
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &tc_raw) == -1)
{
Msg("Failed to enable RAW Terminal IO - tcsetattr() failed\n");
DBGMSG("Failed to enable RAW Terminal IO - tcsetattr() failed\n");
zxcc_exit(1);
}
Msg("Enabled RAW Terminal IO\n");
DBGMSG("Enabled RAW Terminal IO\n");
return;
}
@ -540,13 +506,39 @@ void deinit_raw(void)
{
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &tc_orig) == -1)
{
Msg("Failed to disable RAW Terminal IO - tcsetattr() failed\n");
DBGMSG("Failed to disable RAW Terminal IO - tcsetattr() failed\n");
return;
}
Msg("Disabled RAW Terminal IO\n");
DBGMSG("Disabled RAW Terminal IO\n");
return;
}
#endif
void term_init(void)
{
#ifdef _WIN32
setmode(STDIN_FILENO, O_BINARY);
setmode(STDOUT_FILENO, O_BINARY);
#else
if (_isatty(STDIN_FILENO))
raw_init();
#endif
if (_isatty(STDIN_FILENO))
DBGMSG("Using interactive console mode\n");
else
DBGMSG("Using standard input/output mode\n");
return;
}
void term_reset(void)
{
#ifndef _WIN32
if (_isatty(STDIN_FILENO))
deinit_raw();
#endif
}

53
Tools/unix/zxcc/zxcc.h

@ -15,16 +15,22 @@
#ifndef CPMDIR80
#ifdef _WIN32
#define CPMDIR80 "d:/local/lib/cpm/"
#define CPMDIR80 "d:/local/lib/cpm/"
#else
#define CPMDIR80 "/usr/local/lib/cpm/"
#define CPMDIR80 "/usr/local/lib/cpm/"
#endif
#endif
/* the default sub directories trailing / is required */
#define BIN80 "bin80/"
#define LIB80 "lib80/"
#define INC80 "include80/"
#ifdef _WIN32
#define BIN80 "bin80\\"
#define LIB80 "lib80\\"
#define INC80 "include80\\"
#else
#define BIN80 "bin80/"
#define LIB80 "lib80/"
#define INC80 "include80/"
#endif
#ifndef BINDIR80
#define BINDIR80 CPMDIR80 BIN80
@ -58,11 +64,12 @@ extern char incdir80[];
#ifdef _WIN32
#include <windows.h>
#include <io.h>
#include <conio.h>
#define strcasecmp _stricmp
#ifndef STDIN_FILENO
#define STDIN_FILENO _fileno(stdin)
#define STDOUT_FILENO _fileno(stdout)
#define STDERR_FILENO _fileno(stderr)
#define STDIN_FILENO _fileno(stdin)
#define STDOUT_FILENO _fileno(stdout)
#define STDERR_FILENO _fileno(stderr)
#endif
#else
#include <termios.h>
@ -77,7 +84,7 @@ extern char incdir80[];
#ifndef _WIN32
#include <sys/param.h>
#include <sys/mount.h>
#define _S_IFDIR S_IFDIR
#define _S_IFDIR S_IFDIR
#endif
/* Library includes */
@ -90,28 +97,37 @@ extern char incdir80[];
#include "cpmgsx.h"
#endif
#include "cpmredir.h" /* BDOS disc simulation */
typedef unsigned char byte; /* Must be exactly 8 bits */
typedef unsigned short word; /* Must be exactly 16 bits */
#include "cpmredir.h" /* BDOS disc simulation */
/* Prototypes */
void ed_fe (byte *a, byte *b, byte *c, byte *d, byte *e, byte *f,
byte *h, byte *l, word *pc, word *ix, word *iy);
byte *h, byte *l, word *pc, word *ix, word *iy);
void cpmbdos(byte *a, byte *b, byte *c, byte *d, byte *e, byte *f,
byte *h, byte *l, word *pc, word *ix, word *iy);
byte *h, byte *l, word *pc, word *ix, word *iy);
void cpmbios(byte *a, byte *b, byte *c, byte *d, byte *e, byte *f,
byte *h, byte *l, word *pc, word *ix, word *iy);
byte *h, byte *l, word *pc, word *ix, word *iy);
void dump_regs(FILE *fp, byte a, byte b, byte c, byte d, byte e, byte f,
byte h, byte l, word pc, word ix, word iy);
byte h, byte l, word pc, word ix, word iy);
void Msg(char *s, ...);
void DbgMsg(const char *file, int line, const char *func, char *s, ...);
int zxcc_term(void);
void zxcc_exit(int code);
byte cin(void);
void cout(byte);
int cstat(void);
void term_init(void);
void term_reset(void);
#ifdef DEBUG
#define DBGMSGV(s, ...) DbgMsg(__FILE__, __LINE__, __func__, s, __VA_ARGS__)
#define DBGMSG(s) DbgMsg(__FILE__, __LINE__, __func__, s)
#else
#define DBGMSGV(s, ...)
#define DBGMSG(s)
#endif
/* Global variables */
@ -123,4 +139,3 @@ extern byte RAM[65536]; /* The Z80's address space */
/* Z80 CPU emulation */
#include "z80.h"

48
Tools/unix/zxcc/zxdbdos.c

@ -10,30 +10,29 @@
properly.
*/
/* If a file could not be found on the default drive, try again on a "search"
drive (A: for .COM files, B: for .LIB and .OBJ files) */
int fcbforce(byte *fcb, byte *odrv)
int fcbforce(byte* fcb, byte* odrv)
{
byte drive;
char nam[9];
char typ[4];
int n;
for (n = 0; n < 8; n++) nam[n] = fcb[n+1] & 0x7F;
for (n = 0; n < 8; n++) nam[n] = fcb[n + 1] & 0x7F;
nam[8] = 0;
for (n = 0; n < 3; n++) typ[n] = fcb[n+9] & 0x7F;
for (n = 0; n < 3; n++) typ[n] = fcb[n + 9] & 0x7F;
typ[3] = 0;
drive = 0;
if (*fcb) return 0; /* not using default drive */
/* Microsoft BASIC compiler run-time */
if (!strcmp(nam,"BCLOAD ") && !strcmp(typ, " ")) drive = 2;
if (!strcmp(nam, "BCLOAD ") && !strcmp(typ, " ")) drive = 2;
/* HI-TECH C options help file */
if (!strcmp(nam,"OPTIONS ") && !strcmp(typ, " ")) drive = 1;
if (!strcmp(nam, "OPTIONS ") && !strcmp(typ, " ")) drive = 1;
/* binaries, libraries and object files */
if (!strcmp(typ, "COM")) drive = 1;
@ -55,11 +54,11 @@ int fcbforce(byte *fcb, byte *odrv)
}
/* zxcc has a trick with some filenames: If it can't find them where they
should be, and a drive wasn't specified, it searches BINDIR80,
LIBDIR80 or INCDIR80 (depending on the type of the file).
should be, and a drive wasn't specified, it searches BINDIR80,
LIBDIR80 or INCDIR80 (depending on the type of the file).
*/
word x_fcb_open(byte *fcb, byte *dma)
word x_fcb_open(byte* fcb, byte* dma)
{
word rv = fcb_open(fcb, dma);
byte odrv;
@ -75,23 +74,18 @@ word x_fcb_open(byte *fcb, byte *dma)
return rv;
}
word x_fcb_stat(byte *fcb)
word x_fcb_stat(byte* fcb)
{
word rv = fcb_stat(fcb);
byte odrv;
if (rv == 0xFF)
{
if (fcbforce(fcb, &odrv))
{
rv = fcb_stat(fcb);
*fcb = odrv;
}
}
return rv;
}
word rv = fcb_stat(fcb);
byte odrv;
if (rv == 0xFF)
{
if (fcbforce(fcb, &odrv))
{
rv = fcb_stat(fcb);
*fcb = odrv;
}
}
return rv;
}

10
Tools/unix/zxcc/zxdbdos.h

@ -1,8 +1,4 @@
int fcbforce(byte* fcb, byte* odrv);
int fcbforce(byte *fcb, byte *odrv);
word x_fcb_open(byte *fcb, byte *dma);
word x_fcb_stat(byte *fcb);
word x_fcb_open(byte* fcb, byte* dma);
word x_fcb_stat(byte* fcb);

BIN
Tools/zxcc/zxcc-src.zip

Binary file not shown.

BIN
Tools/zxcc/zxcc.exe

Binary file not shown.

BIN
Tools/zxcc/zxccdbg.exe

Binary file not shown.
Loading…
Cancel
Save