mirror of
https://github.com/SEPPDROID/Digital-Research-Source-Code.git
synced 2025-10-23 00:14:25 +00:00
Upload
Digital Research
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* B A S E P A G E . H */
|
||||
/* ------------------- */
|
||||
/* */
|
||||
/* This file contains a definition of the CP/M basepage structure, */
|
||||
/* b_page. */
|
||||
/* */
|
||||
/* NOTE: In the portable CP/M environment, it is NOT guaranteed */
|
||||
/* that the location of the base page is known at link-edit time */
|
||||
/* (as it is, for example, in CP/M-80 and CP/M-86.) Instead, a */
|
||||
/* pointer to the current basepage is delivered by the BDOS */
|
||||
/* to each new program which is run. This pointer, _base, is */
|
||||
/* initialized by the C startup function (startup.s) and is */
|
||||
/* available to C programs as an external. */
|
||||
/* */
|
||||
/* This file has been modified to live with the BDOS definitions. */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
struct b_page
|
||||
{
|
||||
XADDR ltpa; /* Low TPA address */
|
||||
XADDR htpa; /* High TPA address */
|
||||
XADDR lcode; /* Start address of code seg*/
|
||||
long codelen; /* Code segment length */
|
||||
XADDR ldata; /* Start address of data seg*/
|
||||
long datalen; /* Data segment length */
|
||||
XADDR lbss; /* Start address of bss seg */
|
||||
long bsslen; /* Bss segment length */
|
||||
long freelen; /* Free segment length */
|
||||
char resvd1[20]; /* Reserved area */
|
||||
struct fcb fcb2; /* Second basepage FCB */
|
||||
struct fcb fcb1; /* First basepage FCB */
|
||||
char buff[128]; /* Default DMA buffer, */
|
||||
/* command line tail */
|
||||
};
|
@@ -0,0 +1,171 @@
|
||||
|
||||
/********************************************************
|
||||
* *
|
||||
* P-CP/M header file *
|
||||
* Copyright (c) 1982 by Digital Research, Inc. *
|
||||
* Structure definitions for BDOS globals *
|
||||
* and BDOS data structures *
|
||||
* *
|
||||
********************************************************/
|
||||
|
||||
/**************************************************************************
|
||||
The BDOS data structures, especially those relating to global variables,
|
||||
are structured in a way that hopefully will enable this BDOS, in the future,
|
||||
to easily become a re-entrant multi-tasking file system. Consequently,
|
||||
the BDOS global variables are divided into two classes. Those that are
|
||||
truly global, even in the case of multiple tasks using the file system
|
||||
concurrently, are simply declared as global variables in bdosmain.c.
|
||||
Only a few "globals" are really global in this sense.
|
||||
|
||||
The majority of the "global" variables are actually state variables that
|
||||
relate to the state of the task using the file system. In CP/M-68K, these
|
||||
are "global", since there's only one task, but in a multi-thread model they're
|
||||
not. These type of variables are put into a data structure, with the
|
||||
intention that in the multi-task environment this structure will be based.
|
||||
|
||||
The following declarations take this philosophy into account, and define
|
||||
a simple structure for the single thread environment while leaving the
|
||||
possibilities open for the multi-thread environment.
|
||||
****************************************************************************/
|
||||
|
||||
#define snglthrd TRUE
|
||||
/* TRUE for single-thread environment
|
||||
undefined to create based structure for re-entrant model */
|
||||
#ifdef snglthrd
|
||||
#define GBL gbls
|
||||
/* In single thread case, GBL just names
|
||||
the structure */
|
||||
#define BSETUP EXTERN struct stvars gbls;
|
||||
/* and BSETUP defines the extern structure */
|
||||
#else
|
||||
|
||||
#define GBL (*statep)
|
||||
/* If multi-task, state vars are based */
|
||||
#define BSETUP REG struct stvars *statep; \
|
||||
statep = &gbls;
|
||||
/* set up pointer to state variables */
|
||||
/* This is intended as an example to show the intent */
|
||||
#endif
|
||||
|
||||
|
||||
/* Note that there are a few critical regions in the file system that must
|
||||
execute without interruption. They pertain mostly to the manipulation of
|
||||
the allocation vector. This isn't a problem in a single-thread model, but
|
||||
must be provided for in a multi-tasking file system. Consequently, the
|
||||
primitives LOCK and UNLOCK are defined and used where necessary in the
|
||||
file system. For the single thread model, they are null routines */
|
||||
|
||||
#define LOCK /**/
|
||||
#define UNLOCK /**/
|
||||
/* Be sure LOCK and UNLOCK are implemented to allow recursive calls to LOCK.
|
||||
That is, if a process that calls LOCK already owns the lock, let it proceed,
|
||||
but remember that only the outer-most call to UNLOCK really releases the
|
||||
file system. */
|
||||
|
||||
|
||||
#define VERSION 0x2022 /* Version number for CP/M-68K */
|
||||
#define robit 0 /* read-only bit in file type field of fcb */
|
||||
#define arbit 2 /* archive bit in file type field of fcb */
|
||||
#define SECLEN 128 /* length of a CP/M sector */
|
||||
|
||||
|
||||
union smallbig
|
||||
{
|
||||
UBYTE small[16]; /* 16 block numbers of 1 byte */
|
||||
WORD big[8]; /* or 8 block numbers of 1 word */
|
||||
};
|
||||
|
||||
/* File Control Block definition */
|
||||
struct fcb
|
||||
{
|
||||
UBYTE drvcode; /* 0 = default drive, 1..16 are drives A..P */
|
||||
UBYTE fname[8]; /* File name (ASCII) */
|
||||
UBYTE ftype[3]; /* File type (ASCII) */
|
||||
UBYTE extent; /* Extent number (bits 0..4 used) */
|
||||
UBYTE s1; /* Reserved */
|
||||
UBYTE s2; /* Module field (bits 0..5), write flag (7) */
|
||||
UBYTE rcdcnt; /* Nmbr rcrds in last block, 0..128 */
|
||||
union smallbig dskmap;
|
||||
UBYTE cur_rec; /* current record field */
|
||||
UBYTE ran0; /* random record field (3 bytes) */
|
||||
UBYTE ran1;
|
||||
UBYTE ran2;
|
||||
};
|
||||
|
||||
|
||||
/* Declaration of directory entry */
|
||||
struct dirent
|
||||
{
|
||||
UBYTE entry; /* 0 - 15 for user numbers, E5 for empty */
|
||||
/* the rest are reserved */
|
||||
UBYTE fname[8]; /* File name (ASCII) */
|
||||
UBYTE ftype[3]; /* File type (ASCII) */
|
||||
UBYTE extent; /* Extent number (bits 0..4 used) */
|
||||
UBYTE s1; /* Reserved */
|
||||
UBYTE s2; /* Module field (bits 0..5), write flag (7) */
|
||||
UBYTE rcdcnt; /* Nmbr rcrds in last block, 0..128 */
|
||||
union smallbig dskmap;
|
||||
};
|
||||
|
||||
|
||||
/* Declaration of disk parameter tables */
|
||||
struct dpb /* disk parameter table */
|
||||
{
|
||||
UWORD spt; /* sectors per track */
|
||||
UBYTE bsh; /* block shift factor */
|
||||
UBYTE blm; /* block mask */
|
||||
UBYTE exm; /* extent mask */
|
||||
UBYTE dpbdum; /* dummy byte for fill */
|
||||
UWORD dsm; /* max disk size in blocks */
|
||||
UWORD drm; /* max directory entries */
|
||||
UWORD dir_al; /* initial allocation for dir */
|
||||
UWORD cks; /* number dir sectors to checksum */
|
||||
UWORD trk_off; /* track offset */
|
||||
};
|
||||
|
||||
struct dph /* disk parameter header */
|
||||
{
|
||||
UBYTE *xlt; /* pointer to sector translate table */
|
||||
UWORD hiwater; /* high water mark for this disk */
|
||||
UWORD dum1; /* dummy (unused) */
|
||||
UWORD dum2;
|
||||
UBYTE *dbufp; /* pointer to 128 byte directory buffer */
|
||||
struct dpb *dpbp; /* pointer to disk parameter block */
|
||||
UBYTE *csv; /* pointer to check vector */
|
||||
UBYTE *alv; /* pointer to allocation vector */
|
||||
};
|
||||
|
||||
|
||||
/* Declaration of structure containing "global" state variables */
|
||||
struct stvars
|
||||
{
|
||||
UBYTE kbchar; /* One byte keyboard type-ahead buffer */
|
||||
UBYTE delim; /* Delimiter for function 9 */
|
||||
BOOLEAN lstecho; /* True if echoing console output to lst: */
|
||||
BOOLEAN echodel; /* Echo char when getting <del> ? */
|
||||
UWORD column; /* CRT column number for expanding tabs */
|
||||
XADDR chainp; /* Used for chain to program call */
|
||||
UBYTE curdsk; /* Currently selected disk */
|
||||
UBYTE dfltdsk; /* Default disk (last selected by fcn 14) */
|
||||
UBYTE user; /* Current user number */
|
||||
struct dph *dphp; /* pointer to disk parm hdr for cur disk */
|
||||
struct dirent *dirbufp; /* pointer for directory buff for process */
|
||||
/* stored here so that each process can */
|
||||
/* have a separate dirbuf. */
|
||||
struct dpb *parmp; /* pointer to disk parameter block for cur */
|
||||
/* disk. Stored here to save ref calc */
|
||||
UWORD srchpos; /* position in directory for search next */
|
||||
XADDR dmaadr; /* Disk dma address */
|
||||
XADDR srchp; /* Pointer to search FCB for function 17 */
|
||||
UBYTE *excvec[18]; /* Array of exception vectors */
|
||||
};
|
||||
|
||||
|
||||
/* Console buffer structure declaration */
|
||||
struct conbuf
|
||||
{
|
||||
UBYTE maxlen; /* Maximum length from calling routine */
|
||||
UBYTE retlen; /* Length actually found by BDOS */
|
||||
UBYTE cbuf[1]; /* Console data */
|
||||
};
|
||||
|
@@ -0,0 +1,249 @@
|
||||
|
||||
;****************************************************************
|
||||
;* *
|
||||
;* CP/M-Z8K Basic Disk Operating System interface module *
|
||||
;* For "C" version of CP/M-Z8K *
|
||||
;* *
|
||||
;* Copyright (c) 1982 Digital Research, Inc. *
|
||||
;* *
|
||||
;* Version 0.2 -- September 22, 1982 *
|
||||
;* Z8000 version -- 821014 *
|
||||
;* *
|
||||
;****************************************************************
|
||||
|
||||
__text: .sect
|
||||
|
||||
;****************************************************
|
||||
;*
|
||||
;* Globals
|
||||
;*
|
||||
;****************************************************
|
||||
|
||||
|
||||
.global _bios1 ; 6 BIOS entry points from BDOS
|
||||
.global _bios2
|
||||
.global _bios3
|
||||
.global _bios4
|
||||
.global _bios5
|
||||
.global _bios6
|
||||
.global _traphnd ; trap #2 handler
|
||||
.global _swap ; byte swapper
|
||||
.global _udiv ; unsigned divide routine
|
||||
|
||||
;****************************************************
|
||||
;*
|
||||
;* Externals and Constants
|
||||
;*
|
||||
;****************************************************
|
||||
|
||||
.global __bdos ; BDOS entry point in bdosmain
|
||||
|
||||
;* The following were put in so that all BDOS modules were
|
||||
;* referenced, so they could be put in a library
|
||||
|
||||
.global _constat ; references conbdos.o
|
||||
.global _dirscan ; references dskutil.o
|
||||
.global _create ; references fileio.o
|
||||
.global _bdosrw ; references bdosrw.o
|
||||
|
||||
biosf .equ 50
|
||||
setsupf .equ 62
|
||||
|
||||
;****************************************************
|
||||
;*
|
||||
;* Trap Handler
|
||||
;*
|
||||
;* rcode = bdos(command, (long)parameter)
|
||||
;*
|
||||
;* rr6: parameter
|
||||
;* r5: command
|
||||
;* returns
|
||||
;* r7: result
|
||||
;*
|
||||
;* called SEGMENTED with caller's registers
|
||||
;* saved on stack, and all but rr0 intact in
|
||||
;* registers.
|
||||
;*
|
||||
;* Calls __bdos(cmd, (word)param, (addr)param)
|
||||
;*
|
||||
;****************************************************
|
||||
|
||||
_traphnd:
|
||||
ldctl r0,FCW ;go non-segmented
|
||||
res r0,#15
|
||||
ldctl FCW,r0
|
||||
;
|
||||
; check for functions handled by assembly
|
||||
; language routines
|
||||
;
|
||||
cp r5,#setsupf ; set system mode
|
||||
jr eq setsup
|
||||
cp r5,#biosf ; call bios direct
|
||||
jr eq bioscall
|
||||
;
|
||||
; If caller was non-segmented user program,
|
||||
; get segment number from his program counter
|
||||
;
|
||||
ldl rr2,rr6
|
||||
ld r0,34+4(r15) ;caller's fcw
|
||||
bit r0,#15
|
||||
jr nz callC ; segmented
|
||||
bit r0,#14
|
||||
jr nz callC ; system
|
||||
ld r2,36+4(r15) ; user nonseg.
|
||||
; set seg from PC
|
||||
;
|
||||
; Call C main routine
|
||||
;
|
||||
callC:
|
||||
pushl @r15,rr2 ; xaddr param.
|
||||
push @r15,r7 ; word param
|
||||
push @r15,r5 ; command
|
||||
call __bdos
|
||||
add r15,#8
|
||||
;
|
||||
; Return result in caller's r7.
|
||||
; Restore segmented mode and return
|
||||
;
|
||||
ld 14+4(r15),r7
|
||||
|
||||
ldctl r0,FCW ;go segmented
|
||||
set r0,#15
|
||||
ldctl FCW,r0
|
||||
|
||||
ret
|
||||
|
||||
;
|
||||
; direct BIOS call function
|
||||
;
|
||||
bioscall:
|
||||
;
|
||||
; If caller was non-segmented user program,
|
||||
; get segment number from his program counter
|
||||
;
|
||||
ld r0,34+4(r15) ;caller's fcw
|
||||
bit r0,#15
|
||||
jr nz callBios ; segmented
|
||||
bit r0,#14
|
||||
jr nz callBios ; system
|
||||
ld r6,36+4(r15) ; user nonseg.
|
||||
ld r2,r6 ; set seg from PC
|
||||
; save in r2, also
|
||||
ldctl r0,FCW ;go segmented
|
||||
set r0,#15
|
||||
ldctl FCW,r0
|
||||
|
||||
ldm r3,@r6,#5 ;get parameters
|
||||
ld r4,r2 ;set segments
|
||||
ld r6,r2
|
||||
|
||||
sc #3 ;call BIOS
|
||||
ret ;done
|
||||
|
||||
callBios:
|
||||
ldctl r0,FCW ;go segmented
|
||||
set r0,#15
|
||||
ldctl FCW,r0
|
||||
|
||||
ldm r3,@r6,#5 ;get parameters
|
||||
|
||||
sc #3 ;call BIOS
|
||||
ret ;done
|
||||
;
|
||||
; Set supervisor mode procedure -- VERY DANGEROUS
|
||||
;
|
||||
; Status is set to SYSTEM, SEGMENTED.
|
||||
; Interrupt status will be that at the time
|
||||
; of the call.
|
||||
;
|
||||
setsup:
|
||||
ld r0,34+4(r15)
|
||||
set r0,#14 ;set system
|
||||
set r0,#15 ; and segmented
|
||||
ld 34+4(r15),r0 ; in user FCW
|
||||
|
||||
ldctl r0,FCW ;go segmented
|
||||
set r0,#15
|
||||
ldctl FCW,r0
|
||||
|
||||
ret ;return
|
||||
|
||||
;****************************************************
|
||||
;*
|
||||
;* BIOS Interface Routines
|
||||
;*
|
||||
;* Note - there are 6 BIOS entry points from the BDOS,
|
||||
;* labelled BIOS1 - BIOS6, depending on the
|
||||
;* parameters passed.
|
||||
;*
|
||||
;****************************************************
|
||||
|
||||
_bios5:
|
||||
; For BIOS functions sectran and set exception vector
|
||||
; (funct, word, long) offsets 2, 4, 6
|
||||
|
||||
ldl rr6,6(r15) ; get 2nd param (long)
|
||||
jp _bios2 ; join common routine
|
||||
|
||||
_bios4:
|
||||
; For BIOS function seldsk
|
||||
; (func, word, word) offsets 2, 4, 6
|
||||
ld r7,6(r15) ; get 2nd param (word)
|
||||
clr r6 ; extend to ulong
|
||||
jp _bios2 ; join common routine
|
||||
|
||||
_bios3:
|
||||
; For BIOS function set dma
|
||||
; (func, long) offsets 2, 4
|
||||
ldl rr4,4(r15) ; get 1st param (long)
|
||||
subl rr6,rr6 ; clear second
|
||||
jp _bios1 ; join common routine
|
||||
|
||||
_bios2:
|
||||
; For all BIOS functions with a word parameter
|
||||
; (func, word) offsets 2, 4
|
||||
ld r5,4(r15) ; get 1st param (word)
|
||||
clr r4 ; extend to ulong
|
||||
|
||||
_bios6:
|
||||
_bios1:
|
||||
; For all BIOS functions that have no parameter
|
||||
; other than function number
|
||||
ld r3,2(r15) ; get function number
|
||||
sc #3 ; do BIOS call
|
||||
ret ; returns value in rr7
|
||||
|
||||
;****************************************************
|
||||
;*
|
||||
;* Utility Subroutines
|
||||
;*
|
||||
;* swap(word) swap bytes of a word
|
||||
;*
|
||||
;* uword udiv((long) dividend,
|
||||
;* (uword) divisor,
|
||||
;* (uword *)rem )
|
||||
;*
|
||||
;****************************************************
|
||||
|
||||
_swap:
|
||||
ld r7,2(r15)
|
||||
exb rh7,rl7
|
||||
ret
|
||||
|
||||
_udiv:
|
||||
ldl rr2,2(r15) ;long dividend
|
||||
subl rr0,rr0 ; as unsigned quad
|
||||
ld r5,6(r15) ;word divisor
|
||||
clr r4 ; as unsigned long
|
||||
ldl rr6,8(r15) ;->result
|
||||
|
||||
divl rq0,rr4
|
||||
|
||||
ld @r7,r1 ; store remainder
|
||||
ld r7,r3 ; return quotient
|
||||
clr r6
|
||||
|
||||
|
||||
ret
|
||||
|
||||
|
@@ -0,0 +1,379 @@
|
||||
|
||||
/****************************************************************
|
||||
* *
|
||||
* P-CP/M BDOS Main Routine *
|
||||
* *
|
||||
* This is the main routine for the BDOS for P-CP/M. *
|
||||
* It has one entry point, _bdos, which is called from *
|
||||
* the assembly language trap handler found in bdosif.s. *
|
||||
* The parameters are a function number (integer) and an *
|
||||
* information parameter, (passed as both an integer and *
|
||||
* a pointer) *
|
||||
* The BDOS can potentially return a pointer, long word, *
|
||||
* or word *
|
||||
* *
|
||||
* Memory management for Z8000, etc. added 821018 by SS. *
|
||||
* One non-obvious change is using tmp_sel to copy in the *
|
||||
* user's FCB, and copying it out again if changed. *
|
||||
* Tmp_sel now handles '?' in the drive field, so it need *
|
||||
* not be called from search (the only place outside of *
|
||||
* this module where it is called) *
|
||||
* *
|
||||
****************************************************************/
|
||||
|
||||
#include "stdio.h" /* Standard I/O declarations */
|
||||
|
||||
#include "bdosdef.h" /* Type and structure declarations for BDOS */
|
||||
|
||||
#include "biosdef.h" /* Declarations of BIOS functions */
|
||||
|
||||
/* Declare EXTERN functions */
|
||||
|
||||
EXTERN warmboot(); /* Warm Boot function */
|
||||
EXTERN BOOLEAN constat(); /* Console status */
|
||||
EXTERN UBYTE conin(); /* Console Input function */
|
||||
EXTERN cookdout(); /* Cooked console output routine */
|
||||
EXTERN UBYTE rawconio(); /* Raw console I/O */
|
||||
EXTERN prt_line(); /* Print line until delimiter */
|
||||
EXTERN readline(); /* Buffered console read */
|
||||
EXTERN seldsk(); /* Select disk */
|
||||
EXTERN BOOLEAN openfile(); /* Open File */
|
||||
EXTERN UWORD close_fi(); /* Close File */
|
||||
EXTERN UWORD search(); /* Search first and next fcns */
|
||||
EXTERN UWORD dirscan(); /* General directory scanning routine */
|
||||
EXTERN UWORD bdosrw(); /* Sequential and Random disk read/write */
|
||||
EXTERN BOOLEAN create(); /* Create file */
|
||||
EXTERN BOOLEAN delete(); /* Delete file */
|
||||
EXTERN BOOLEAN rename(); /* Rename file */
|
||||
EXTERN BOOLEAN set_attr(); /* Set file attributes */
|
||||
EXTERN getsize(); /* Get File Size */
|
||||
EXTERN setran(); /* Set Random Record */
|
||||
EXTERN free_sp(); /* Get Disk Free Space */
|
||||
EXTERN UWORD flushit(); /* Flush Buffers */
|
||||
EXTERN UWORD pgmld(); /* Program Load */
|
||||
EXTERN UWORD setexc(); /* Set Exception Vector */
|
||||
EXTERN set_tpa(); /* Get/Set TPA Limits */
|
||||
EXTERN move(); /* general purpose byte mover */
|
||||
|
||||
|
||||
/* Declare "true" global variables; i.e., those which will pertain to the
|
||||
entire file system and thus will remain global even when this becomes
|
||||
a multi-tasking file system */
|
||||
|
||||
GLOBAL UWORD log_dsk = 0; /* 16-bit vector of logged in drives */
|
||||
GLOBAL UWORD ro_dsk = 0; /* 16-bit vector of read-only drives */
|
||||
GLOBAL UWORD crit_dsk = 0; /* 16-bit vector of drives in "critical"
|
||||
state. Used to control dir checksums */
|
||||
GLOBAL XADDR tpa_lp; /* TPA lower boundary (permanent) */
|
||||
GLOBAL XADDR tpa_lt; /* TPA lower boundary (temporary) */
|
||||
GLOBAL XADDR tpa_hp; /* TPA upper boundary (permanent) */
|
||||
GLOBAL XADDR tpa_ht; /* TPA upper boundary (temporary) */
|
||||
|
||||
/* Declare the "state variables". These are globals for the single-thread
|
||||
version of the file system, but are put in a structure so they can be
|
||||
based, with a pointer coming from the calling process */
|
||||
|
||||
GLOBAL struct stvars gbls;
|
||||
|
||||
struct tempstr
|
||||
{
|
||||
UBYTE tempdisk;
|
||||
BOOLEAN reselect;
|
||||
struct fcb *fptr;
|
||||
XADDR fxptr; /* xaddr of caller's FCB */
|
||||
struct fcb tempfcb; /* added for memory management */
|
||||
/* because caller's fcb may not */
|
||||
/* be directly accessible */
|
||||
};
|
||||
|
||||
/****************************************************************
|
||||
* *
|
||||
* _bdos MAIN ROUTINE *
|
||||
* *
|
||||
* Called with _bdos(func, info, infop) *
|
||||
* *
|
||||
* Where: *
|
||||
* func is the BDOS function number (d0.w) *
|
||||
* info is the parameter as a word *
|
||||
* infop is the parameter as a segmented pointer *
|
||||
* note that info is the word form of infop*
|
||||
* *
|
||||
****************************************************************/
|
||||
|
||||
|
||||
UWORD _bdos(func,info,infop)
|
||||
REG WORD func; /* BDOS function number */
|
||||
REG UWORD info; /* parameter as word */
|
||||
REG XADDR infop; /* parameter as (segmented) pointer */
|
||||
{
|
||||
REG UWORD rtnval;
|
||||
LOCAL struct tempstr temp;
|
||||
BSETUP
|
||||
|
||||
temp.reselect = FALSE;
|
||||
temp.fxptr = infop;
|
||||
rtnval = 0;
|
||||
|
||||
switch (func) /* switch on function number */
|
||||
{
|
||||
case 0: warmboot(0); /* warm boot function */
|
||||
/* break; */
|
||||
|
||||
case 1: return((UWORD)conin()); /* console input function */
|
||||
/* break; */
|
||||
|
||||
case 2: cookdout((UBYTE)info,FALSE);/* "cooked" console output */
|
||||
break;
|
||||
|
||||
case 3: return((UWORD)brdr()); /* get reader from bios */
|
||||
/* break; */
|
||||
|
||||
case 4: bpun((UBYTE)info); /* punch output to bios */
|
||||
break;
|
||||
|
||||
case 5: blstout((UBYTE)info); /* list output from bios */
|
||||
break;
|
||||
|
||||
case 6: return((UWORD)rawconio(info)); /* raw console I/O */
|
||||
/* break; */
|
||||
|
||||
case 7: return(bgetiob()); /* get i/o byte */
|
||||
/* break; */
|
||||
|
||||
case 8: bsetiob(info); /* set i/o byte function */
|
||||
break;
|
||||
|
||||
case 9: uprt_line(infop); /* print line function */
|
||||
break;
|
||||
|
||||
case 10: ureadline(infop); /* read buffered con input */
|
||||
break;
|
||||
|
||||
case 11: return((UWORD)constat()); /* console status */
|
||||
/* break; */
|
||||
|
||||
case 12: return(VERSION); /* return version number */
|
||||
/* break; */
|
||||
|
||||
case 13: log_dsk = 0; /* reset disk system */
|
||||
ro_dsk = 0;
|
||||
crit_dsk= 0;
|
||||
GBL.curdsk = 0xff;
|
||||
GBL.dfltdsk = 0;
|
||||
break;
|
||||
|
||||
case 14: seldsk((UBYTE)info); /* select disk */
|
||||
GBL.dfltdsk = (UBYTE)info;
|
||||
break;
|
||||
|
||||
case 15: tmp_sel(&temp); /* open file */
|
||||
temp.fptr->extent = 0;
|
||||
temp.fptr->s2 = 0;
|
||||
rtnval = dirscan(openfile, temp.fptr, 0);
|
||||
break;
|
||||
|
||||
case 16: tmp_sel(&temp); /* close file */
|
||||
rtnval = close_fi(temp.fptr);
|
||||
break;
|
||||
|
||||
case 17: GBL.srchp = infop; /* search first */
|
||||
tmp_sel(&temp);
|
||||
rtnval = search(temp.fptr, 0, &temp);
|
||||
break;
|
||||
|
||||
case 18: infop = GBL.srchp; /* search next */
|
||||
temp.fxptr = infop;
|
||||
tmp_sel(&temp);
|
||||
rtnval = search(temp.fptr, 1, &temp);
|
||||
break;
|
||||
|
||||
case 19: tmp_sel(&temp); /* delete file */
|
||||
rtnval = dirscan(delete, temp.fptr, 2);
|
||||
break;
|
||||
|
||||
case 20: tmp_sel(&temp); /* read sequential */
|
||||
rtnval = bdosrw(temp.fptr, TRUE, 0);
|
||||
break;
|
||||
|
||||
case 21: tmp_sel(&temp); /* write sequential */
|
||||
rtnval = bdosrw(temp.fptr, FALSE, 0);
|
||||
break;
|
||||
|
||||
case 22: tmp_sel(&temp); /* create file */
|
||||
temp.fptr->extent = 0;
|
||||
temp.fptr->s1 = 0;
|
||||
temp.fptr->s2 = 0;
|
||||
temp.fptr->rcdcnt = 0;
|
||||
/* Zero extent, S1, S2, rcrdcnt. create zeros rest */
|
||||
rtnval = dirscan(create, temp.fptr, 8);
|
||||
break;
|
||||
|
||||
case 23: tmp_sel(&temp); /* rename file */
|
||||
rtnval = dirscan(rename, temp.fptr, 2);
|
||||
break;
|
||||
|
||||
case 24: return(log_dsk); /* return login vector */
|
||||
/* break; */
|
||||
|
||||
case 25: return(UBWORD(GBL.dfltdsk)); /* return current disk */
|
||||
/* break; */
|
||||
|
||||
case 26: GBL.dmaadr = infop; /* set dma address */
|
||||
break;
|
||||
|
||||
/* No function 27 -- Get Allocation Vector */
|
||||
|
||||
case 28: ro_dsk |= 1<<GBL.dfltdsk; /* set disk read-only */
|
||||
break;
|
||||
|
||||
case 29: return(ro_dsk); /* get read-only vector */
|
||||
/* break; */
|
||||
|
||||
case 30: tmp_sel(&temp); /* set file attributes */
|
||||
rtnval = dirscan(set_attr, temp.fptr, 2);
|
||||
break;
|
||||
|
||||
case 31: if (GBL.curdsk != GBL.dfltdsk) seldsk(GBL.dfltdsk);
|
||||
cpy_out( (GBL.parmp), infop, sizeof *(GBL.parmp) );
|
||||
break; /* return disk parameters */
|
||||
|
||||
case 32: if ( (info & 0xff) <= 15 ) /* get/set user number */
|
||||
GBL.user = (UBYTE)info;
|
||||
return(UBWORD(GBL.user));
|
||||
/* break; */
|
||||
|
||||
case 33: tmp_sel(&temp); /* random read */
|
||||
rtnval = bdosrw(temp.fptr, TRUE, 1);
|
||||
break;
|
||||
|
||||
case 34: tmp_sel(&temp); /* random write */
|
||||
rtnval = bdosrw(temp.fptr, FALSE, 1);
|
||||
break;
|
||||
|
||||
case 35: tmp_sel(&temp); /* get file size */
|
||||
getsize(temp.fptr);
|
||||
break;
|
||||
|
||||
case 36: tmp_sel(&temp); /* set random record */
|
||||
setran(temp.fptr);
|
||||
break;
|
||||
|
||||
case 37: info = ~info; /* reset drive */
|
||||
log_dsk &= info;
|
||||
ro_dsk &= info;
|
||||
crit_dsk&= info;
|
||||
break;
|
||||
|
||||
case 40: tmp_sel(&temp); /* write random with 0 fill */
|
||||
rtnval = bdosrw(temp.fptr, FALSE, 2);
|
||||
break;
|
||||
|
||||
case 46: free_sp(info); /* get disk free space */
|
||||
break;
|
||||
|
||||
case 47: GBL.chainp = GBL.dmaadr; /* chain to program */
|
||||
warmboot(0); /* terminate calling program */
|
||||
/* break; */
|
||||
|
||||
case 48: return( flushit() ); /* flush buffers */
|
||||
/* break; */
|
||||
|
||||
case 59: return(pgmld(infop)); /* program load */
|
||||
/* break; */
|
||||
|
||||
case 61: return(setexc(infop)); /* set exception vector */
|
||||
/* break; */
|
||||
|
||||
case 63: set_tpa(infop); /* get/set TPA limits */
|
||||
break;
|
||||
|
||||
default: return(-1); /* bad function number */
|
||||
/* break; */
|
||||
|
||||
}; /* end of switch statement */
|
||||
|
||||
if (temp.reselect){ /* if reselected disk, restore it now */
|
||||
temp.fptr->drvcode = temp.tempdisk;
|
||||
cpy_out(temp.fptr, infop, sizeof *temp.fptr);
|
||||
}
|
||||
|
||||
return(rtnval); /* return the BDOS return value */
|
||||
} /* end _bdos */
|
||||
|
||||
|
||||
/*****************************************************
|
||||
**
|
||||
** tmp_sel(temptr) -- temporarily select disk
|
||||
** pointed to by temptr->fptr.
|
||||
**
|
||||
** make local copy of FCB in caller's space.
|
||||
**
|
||||
*****************************************************/
|
||||
|
||||
tmp_sel(temptr) /* temporarily select disk pointed to by fcb */
|
||||
/* also copy fcb into temp structure */
|
||||
REG struct tempstr *temptr;
|
||||
{
|
||||
REG struct fcb *fcbp;
|
||||
REG UBYTE tmp_dsk;
|
||||
BSETUP
|
||||
|
||||
/* get local copy of caller's FCB, */
|
||||
/* and point temptr->fptr at it */
|
||||
|
||||
cpy_in(temptr->fxptr, &temptr->tempfcb, sizeof(struct fcb));
|
||||
temptr->fptr = &temptr->tempfcb;
|
||||
|
||||
/* get local copy of fcb pointer */
|
||||
fcbp = temptr->fptr;
|
||||
|
||||
/* select disk if necessary */
|
||||
tmp_dsk = fcbp->drvcode;
|
||||
if (tmp_dsk == '?') { /* -- drive '?' for search */
|
||||
seldsk( GBL.dfltdsk);
|
||||
} else { /* -- drive 0 or disk+1 */
|
||||
temptr->tempdisk = tmp_dsk;
|
||||
seldsk( tmp_dsk ? tmp_dsk - 1 : GBL.dfltdsk );
|
||||
|
||||
fcbp->drvcode = GBL.user;
|
||||
temptr->reselect = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************
|
||||
**
|
||||
** uprt_line(ptr) -- print line in user space
|
||||
** ureadline(ptr) -- read line into user space
|
||||
**
|
||||
** The pointer parameter is passed as a long,
|
||||
** since it may be in the user's memory space.
|
||||
**
|
||||
*****************************************************/
|
||||
|
||||
uprt_line(ptr)
|
||||
XADDR ptr;
|
||||
{
|
||||
UBYTE c;
|
||||
XADDR caddr;
|
||||
|
||||
/* At one system-call per byte, this is VERY inefficient. */
|
||||
|
||||
caddr = map_adr((XADDR)&c, 0);
|
||||
while (mem_cpy(ptr++, caddr, 1L), c != GBL.delim)
|
||||
cookdout( c, FALSE);
|
||||
}
|
||||
|
||||
|
||||
ureadline(ptr)
|
||||
XADDR ptr;
|
||||
{
|
||||
char buf[258];
|
||||
|
||||
cpy_in(ptr, buf, 1); /* copy in user's buffer */
|
||||
|
||||
readline(buf); /* read line */
|
||||
|
||||
cpy_out(buf, ptr, 2+(255&(int)buf[1])); /* copy out result */
|
||||
}
|
||||
|
@@ -0,0 +1,327 @@
|
||||
|
||||
/****************************************************************
|
||||
* *
|
||||
* P-CP/M BDOS Miscellaneous Module *
|
||||
* *
|
||||
* This module contains miscellaneous loose ends for *
|
||||
* P-CP/M. Included are: *
|
||||
* *
|
||||
* bdosinit() - BDOS initialization routine *
|
||||
* called from CCP for system init *
|
||||
* warmboot() - BDOS warm boot exit routine *
|
||||
* error() - BDOS error printing routine *
|
||||
* setexc() - BDOS set exception vector *
|
||||
* set_tpa() - BDOS get/set TPA limits *
|
||||
* cpy_bi() - copy byte in *
|
||||
* *
|
||||
* *
|
||||
* Modified for memory management on the Z8000 *
|
||||
* *
|
||||
****************************************************************/
|
||||
|
||||
#include "stdio.h" /* Standard I/O declarations */
|
||||
|
||||
#include "bdosdef.h" /* Type and structure declarations for BDOS */
|
||||
|
||||
#include "biosdef.h" /* BIOS definitions, needed for bios wboot */
|
||||
|
||||
|
||||
/* Declare external functions */
|
||||
EXTERN conout(); /* Console Output function */
|
||||
EXTERN UBYTE conin(); /* Console Input function */
|
||||
EXTERN prt_line(); /* Print String function */
|
||||
EXTERN UWORD _bdos(); /* BDOS main routine */
|
||||
EXTERN UBYTE *traphnd(); /* assembly language trap handler */
|
||||
EXTERN initexc(); /* init the exception handler in */
|
||||
/* exceptn.s */
|
||||
EXTERN UWORD dirscan(); /* Directory scanning routine */
|
||||
EXTERN BOOLEAN set_attr(); /* Set File attributes function */
|
||||
|
||||
/* Declare external variables */
|
||||
EXTERN UWORD log_dsk; /* logged-on disk vector */
|
||||
EXTERN UWORD ro_dsk; /* read-only disk vector */
|
||||
EXTERN UWORD crit_dsk; /* vector of critical disks */
|
||||
EXTERN XADDR tpa_lt; /* TPA lower limit (temporary) */
|
||||
EXTERN XADDR tpa_lp; /* TPA lower limit (permanent) */
|
||||
EXTERN XADDR tpa_ht; /* TPA upper limit (temporary) */
|
||||
EXTERN XADDR tpa_hp; /* TPA upper limit (permanent) */
|
||||
EXTERN BOOLEAN submit; /* external variables from CCP */
|
||||
EXTERN BOOLEAN morecmds;
|
||||
|
||||
|
||||
#define trap2v 34 /* trap 2 vector number */
|
||||
#define ctrlc 3 /* control-c */
|
||||
|
||||
|
||||
/********************************
|
||||
* bdos initialization routine *
|
||||
********************************/
|
||||
|
||||
bdosinit()
|
||||
/* Initialize the File System */
|
||||
{
|
||||
REG struct
|
||||
{
|
||||
WORD nmbr;
|
||||
XADDR low;
|
||||
LONG length;
|
||||
} *segp;
|
||||
BSETUP
|
||||
|
||||
bsetvec(trap2v, map_adr((long)traphnd, 257)); /* set up trap vector */
|
||||
/* (inst. space addr) */
|
||||
GBL.kbchar = 0; /* initialize the "global" variables */
|
||||
GBL.delim = '$';
|
||||
GBL.lstecho = FALSE;
|
||||
GBL.echodel = TRUE;
|
||||
GBL.chainp = XNULL;
|
||||
GBL.user = 0;
|
||||
_bdos(13,0, XNULL); /* reset disk system function */
|
||||
prt_line("
|
||||
\r\nCPM-Z8000 Version 1.2 03/14/83$");
|
||||
prt_line("\r\nCopyright 1982 Digital Research Inc., Zilog Inc.$");
|
||||
segp = bgetseg(); /* get pointer to memory segment table */
|
||||
tpa_lt = tpa_lp = segp->low;
|
||||
tpa_ht = tpa_hp = tpa_lp + segp->length;
|
||||
initexc( &(GBL.excvec[0]) );
|
||||
}
|
||||
|
||||
|
||||
/************************
|
||||
* warmboot entry point *
|
||||
************************/
|
||||
|
||||
warmboot(parm)
|
||||
/* Warm Boot the system */
|
||||
WORD parm; /* 1 to reset submit flag */
|
||||
{
|
||||
BSETUP
|
||||
|
||||
log_dsk &= ~ro_dsk; /* log off any disk marked read-only */
|
||||
/* note that this code is specifically for a single-
|
||||
thread system. It won't work in a multi-task sys */
|
||||
ro_dsk = 0;
|
||||
crit_dsk = 0;
|
||||
if (parm)
|
||||
submit = morecmds = FALSE;
|
||||
tpa_lt = tpa_lp;
|
||||
tpa_ht = tpa_hp;
|
||||
initexc( &(GBL.excvec[0]) );
|
||||
bwboot();
|
||||
}
|
||||
|
||||
|
||||
/*************************/
|
||||
/* disk error handlers */
|
||||
/*************************/
|
||||
|
||||
prt_err(p)
|
||||
/* print the error message */
|
||||
|
||||
BYTE *p;
|
||||
{
|
||||
BSETUP
|
||||
|
||||
prt_line(p);
|
||||
prt_line(" error on drive $");
|
||||
conout(GBL.curdsk + 'A');
|
||||
}
|
||||
|
||||
|
||||
abrt_err(p)
|
||||
/* print the error message and always abort */
|
||||
|
||||
BYTE *p;
|
||||
{
|
||||
prt_err(p);
|
||||
warmboot(1);
|
||||
}
|
||||
|
||||
|
||||
ext_err(p)
|
||||
/* print the error message, and allow for retry, abort, or ignore */
|
||||
|
||||
BYTE *p;
|
||||
{
|
||||
REG UBYTE ch;
|
||||
|
||||
prt_err(p);
|
||||
do
|
||||
{
|
||||
prt_line("\n\rDo you want to: Abort (A), Retry (R),$");
|
||||
prt_line(" or Continue with bad data (C)? $");
|
||||
ch = conin() & 0x5f;
|
||||
prt_line("\r\n$");
|
||||
|
||||
switch ( ch )
|
||||
{
|
||||
case ctrlc: warmboot(1);
|
||||
case 'A': warmboot(1);
|
||||
case 'C': return(1);
|
||||
case 'R': return(0);
|
||||
}
|
||||
} while (TRUE);
|
||||
}
|
||||
|
||||
|
||||
filero(fcbp)
|
||||
/* File R/O error */
|
||||
|
||||
REG struct fcb *fcbp;
|
||||
{
|
||||
REG BYTE *p;
|
||||
REG UWORD i;
|
||||
REG UBYTE ch;
|
||||
|
||||
p = (BYTE *)fcbp;
|
||||
prt_line("file error: $");
|
||||
i = 8;
|
||||
do conout(*++p); while (--i);
|
||||
conout('.');
|
||||
i = 3;
|
||||
do conout(*++p); while (--i);
|
||||
prt_line(" is read-only.$");
|
||||
do
|
||||
{
|
||||
prt_line("\r\nDo you want to: Change it to read/write (C), or Abort (A)? $");
|
||||
ch = conin() & 0x5f;
|
||||
prt_line("\r\n$");
|
||||
|
||||
switch ( ch )
|
||||
{
|
||||
case ctrlc: warmboot(1);
|
||||
case 'A': warmboot(1);
|
||||
case 'C': fcbp->ftype[robit] &= 0x7f;
|
||||
return( dirscan(set_attr, fcbp, 2) );
|
||||
}
|
||||
} while (TRUE);
|
||||
}
|
||||
|
||||
|
||||
/************************
|
||||
* error entry point *
|
||||
************************/
|
||||
|
||||
error(errnum, fcbp) /* VARARGS */
|
||||
/* Print error message, do appropriate response */
|
||||
|
||||
UWORD errnum; /* error number */
|
||||
struct fcb *fcbp; /* pointer to fcb */
|
||||
{
|
||||
BSETUP
|
||||
|
||||
prt_line("\r\nCP/M Disk $");
|
||||
switch (errnum)
|
||||
{
|
||||
case 0: return( ext_err("read$") );
|
||||
/* break; */
|
||||
|
||||
case 1: return( ext_err("write$") );
|
||||
/* break; */
|
||||
|
||||
case 2: abrt_err("select$");
|
||||
/* break; */
|
||||
|
||||
case 3: return( ext_err("select$") );
|
||||
/* break; */
|
||||
|
||||
case 4: abrt_err("change$");
|
||||
/* break; */
|
||||
|
||||
case 5: return filero(fcbp);
|
||||
/* break; */
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*****************************
|
||||
* set exception entry point *
|
||||
*****************************/
|
||||
|
||||
setexc(xepbp)
|
||||
/* Set Exception Vector */
|
||||
REG XADDR xepbp;
|
||||
{
|
||||
REG WORD i;
|
||||
REG struct
|
||||
{
|
||||
WORD vecnum;
|
||||
UBYTE *newvec;
|
||||
UBYTE *oldvec;
|
||||
} epb;
|
||||
|
||||
BSETUP
|
||||
|
||||
cpy_in(xepbp, &epb, sizeof epb); /* copy in param block */
|
||||
|
||||
i = epb.vecnum-2;
|
||||
if ( i==32 || i==33) return(-1);
|
||||
if ( (30 <= i) && (i <= 37) ) i -= 20;
|
||||
else if ( (i < 0) || (i > 9) ) return(-1);
|
||||
epb.oldvec = GBL.excvec[i];
|
||||
GBL.excvec[i] = epb.newvec;
|
||||
|
||||
cpy_out(&epb, xepbp, sizeof epb); /* copy out param block */
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/*****************************
|
||||
* get/set TPA entry point *
|
||||
*****************************/
|
||||
|
||||
set_tpa(xp)
|
||||
/* Get/Set TPA Limits */
|
||||
REG XADDR xp;
|
||||
|
||||
#define set 1
|
||||
#define sticky 2
|
||||
|
||||
{
|
||||
struct
|
||||
{
|
||||
UWORD parms;
|
||||
XADDR low;
|
||||
XADDR high;
|
||||
} p;
|
||||
|
||||
cpy_in(xp, &p, sizeof p); /* copy in param block */
|
||||
|
||||
if (p.parms & set)
|
||||
{
|
||||
tpa_lt = p.low;
|
||||
tpa_ht = p.high;
|
||||
if (p.parms & sticky)
|
||||
{
|
||||
tpa_lp = tpa_lt;
|
||||
tpa_hp = tpa_ht;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
p.low = tpa_lt;
|
||||
p.high = tpa_ht;
|
||||
}
|
||||
|
||||
cpy_out(&p, xp, sizeof p); /* copy out param block */
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************
|
||||
**
|
||||
** ubyte = cpy_bi(xaddr)-- copy byte in
|
||||
**
|
||||
*****************************************************/
|
||||
|
||||
UBYTE cpy_bi(addr)
|
||||
XADDR addr;
|
||||
{
|
||||
UBYTE b;
|
||||
|
||||
cpy_in(addr, &b, 1);
|
||||
return b;
|
||||
}
|
||||
|
@@ -0,0 +1,302 @@
|
||||
|
||||
/****************************************************************
|
||||
* *
|
||||
* P-CP/M BDOS Disk Read/Write Module *
|
||||
* *
|
||||
* This module contains functions to perform sequential *
|
||||
* or random access read or write to the disk for P-CP/M *
|
||||
* *
|
||||
* It includes the following external functions: *
|
||||
* *
|
||||
* bdosrw() - sequential and random disk I/O *
|
||||
* *
|
||||
* *
|
||||
* Modified for memory management on the Z8000 *
|
||||
* *
|
||||
****************************************************************/
|
||||
|
||||
#include "stdio.h" /* Standard I/O declarations */
|
||||
|
||||
#include "bdosdef.h" /* Type and structure declarations for BDOS */
|
||||
|
||||
|
||||
/* External function definitions */
|
||||
EXTERN UWORD rdwrt(); /* disk read/write routine */
|
||||
EXTERN WORD getaloc(); /* allocate a block of disk space */
|
||||
EXTERN UWORD swap(); /* assembly language byte swapper */
|
||||
EXTERN UWORD dirscan(); /* directory scanning routine */
|
||||
EXTERN BOOLEAN openfile(); /* open file function passed to dirscan */
|
||||
EXTERN UWORD close_fi(); /* close file function */
|
||||
EXTERN BOOLEAN create(); /* create file function passed to dirscan */
|
||||
EXTERN UWORD error(); /* error handler */
|
||||
|
||||
/* External variable definitions */
|
||||
EXTERN UWORD ro_dsk; /* read-only disk vector */
|
||||
|
||||
|
||||
/**********************************************************/
|
||||
/* First, some utility functions used by seqio and ranio */
|
||||
/**********************************************************/
|
||||
|
||||
/******************************
|
||||
* FCB block number routines *
|
||||
******************************/
|
||||
|
||||
WORD blkindx(fcbp)
|
||||
/* return index into fcb disk map */
|
||||
|
||||
REG struct fcb *fcbp; /* pointer to fcb */
|
||||
{
|
||||
REG struct dpb *dparmp; /* pointer to disk parameter block */
|
||||
REG WORD i;
|
||||
REG WORD blkshf;
|
||||
BSETUP
|
||||
|
||||
dparmp = GBL.parmp;
|
||||
blkshf = dparmp->bsh;
|
||||
i = ((fcbp->extent) & dparmp->exm) << (7 - blkshf);
|
||||
return (i + (UBWORD(fcbp->cur_rec) >> blkshf) );
|
||||
}
|
||||
|
||||
|
||||
UWORD blknum(fcbp, index, wrdfcb)
|
||||
/* return block number in fcb indicated by index */
|
||||
|
||||
REG struct fcb *fcbp; /* pointer to fcb */
|
||||
REG WORD index; /* index into disk map of fcb */
|
||||
WORD wrdfcb; /* boolean, fcb disk map of words? */
|
||||
{
|
||||
if (wrdfcb)
|
||||
return( swap(fcbp->dskmap.big[index]) );
|
||||
else return( UBWORD(fcbp->dskmap.small[index]) );
|
||||
}
|
||||
|
||||
|
||||
setblk(fcbp, index, wrdfcb, block)
|
||||
/* put block number into fcb */
|
||||
|
||||
REG struct fcb *fcbp; /* pointer to fcb */
|
||||
REG WORD index; /* index into disk map of fcb */
|
||||
WORD wrdfcb; /* boolean, fcb disk map of words? */
|
||||
REG UWORD block; /* block number */
|
||||
{
|
||||
fcbp->s2 &= 0x7f; /* set file write flag */
|
||||
if (wrdfcb)
|
||||
fcbp->dskmap.big[index] = swap(block);
|
||||
else fcbp->dskmap.small[index] = (UBYTE)block;
|
||||
}
|
||||
|
||||
|
||||
/***************************
|
||||
* disk read/write routine *
|
||||
***************************/
|
||||
|
||||
UWORD do_io(block, rcrd, parm)
|
||||
|
||||
UWORD block; /* block number */
|
||||
UBYTE rcrd; /* record number */
|
||||
REG WORD parm; /* write parameter */
|
||||
{
|
||||
REG LONG lsec;
|
||||
REG struct dpb *dparmp;
|
||||
BSETUP
|
||||
|
||||
dparmp = GBL.parmp; /* init dpb pointer */
|
||||
lsec = ((LONG)block << (dparmp->bsh)) +
|
||||
(LONG)(rcrd & (dparmp->blm));
|
||||
return( rdwrt(lsec, GBL.dmaadr, parm) );
|
||||
}
|
||||
|
||||
|
||||
/*******************************************
|
||||
* routine for crossing extent boundaries *
|
||||
*******************************************/
|
||||
|
||||
WORD new_ext(fcbp, reading, ran)
|
||||
/* If sequential I/O, open the next extent */
|
||||
/* If random I/O, compute new extent from random record field */
|
||||
|
||||
REG struct fcb *fcbp; /* pointer to fcb */
|
||||
BOOLEAN reading; /* read/write flag */
|
||||
WORD ran; /* random I/O flag */
|
||||
{
|
||||
REG UBYTE mod; /* module number */
|
||||
REG UBYTE ext; /* extent number */
|
||||
BSETUP
|
||||
|
||||
if (ran)
|
||||
{
|
||||
mod = ( (fcbp->ran0) << 4) | ( (fcbp->ran1) >> 4);
|
||||
ext = ( ((fcbp->ran1) & 0x0f) << 1);
|
||||
if ((fcbp->ran2) & 0x80) ext |= 1;
|
||||
/* the calculation of ext was coded this way because of a */
|
||||
/* compiler bug from Alcyon */
|
||||
}
|
||||
else
|
||||
{
|
||||
mod = (fcbp->s2) & 0x3f;
|
||||
ext = (fcbp->extent) + 1; /* for sequential, incr extent */
|
||||
}
|
||||
if (ext >= 32)
|
||||
{
|
||||
ext = 0;
|
||||
mod += 1;
|
||||
}
|
||||
if (mod >= 64) return(6); /* past maximum file size */
|
||||
if ( mod == ((fcbp->s2) & 0x3f) )
|
||||
if ( ! ((ext ^ (fcbp->extent)) & ~((GBL.parmp)->exm) & 0x1f) )
|
||||
{ /* we're in same logical extent */
|
||||
fcbp->extent = ext;
|
||||
return(0);
|
||||
}
|
||||
/* Extent or Module numbers don't match */
|
||||
/* Close the old extent and open a new one */
|
||||
if ( close_fi(fcbp) >= 255 ) return(3);
|
||||
/* can't close old extent */
|
||||
fcbp->s2 = mod;
|
||||
fcbp->extent = ext;
|
||||
if ( dirscan(openfile, fcbp, 0) >= 255 ) /* open new extent */
|
||||
{
|
||||
if (reading) return(4); /* reading unwritten extent */
|
||||
if ( dirscan(create, fcbp, 8) >= 255 )
|
||||
return(5); /* can't create new extent */
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/************************************
|
||||
* Routine to calculate the maximum *
|
||||
* extent number of an FCB in a *
|
||||
* extent-folded environment *
|
||||
************************************/
|
||||
|
||||
UWORD calcext(fcbp)
|
||||
|
||||
REG struct fcb *fcbp;
|
||||
|
||||
{
|
||||
REG UWORD i;
|
||||
REG BYTE *p;
|
||||
BSETUP
|
||||
|
||||
i = 15;
|
||||
p = &(fcbp->dskmap.small[16]);
|
||||
do
|
||||
{
|
||||
if (*--p) break;
|
||||
i -= 1;
|
||||
} while (i);
|
||||
/* Now i contains the index of the last non-zero block in the FCB */
|
||||
if ((GBL.parmp)->dsm > 255) i >>= 1;
|
||||
i >>= 7 - ((GBL.parmp)->bsh);
|
||||
return ( (fcbp->extent) & ~((GBL.parmp)->exm) & 0x1f | i );
|
||||
}
|
||||
|
||||
|
||||
/*********************************
|
||||
* Routine to get the actual *
|
||||
* record count of the currently *
|
||||
* active logical extent of a FCB *
|
||||
*********************************/
|
||||
|
||||
UWORD get_rc(fcbp)
|
||||
|
||||
REG struct fcb *fcbp;
|
||||
|
||||
{
|
||||
REG UWORD ext;
|
||||
|
||||
ext = calcext(fcbp); /* find last active extent in fcb */
|
||||
if (ext == fcbp->extent) return(UBWORD(fcbp->rcdcnt));
|
||||
/* if this is the last active fcb, return fcb's rc */
|
||||
else if (ext > fcbp->extent) return(128);
|
||||
/* if the fcb has more extents past this one, then */
|
||||
/* the current one is logically full */
|
||||
else return (0);
|
||||
/* if we seeked past the last active extent, rc = 0 */
|
||||
}
|
||||
|
||||
|
||||
/************************
|
||||
* bdosrw entry point *
|
||||
************************/
|
||||
|
||||
UWORD bdosrw(fcbp, reading, random)
|
||||
|
||||
REG struct fcb *fcbp; /* fcbp is a pointer to a fcb */
|
||||
REG BOOLEAN reading; /* boolean to tell whether to read or write */
|
||||
WORD random; /* 0 = sequential, 1 = random (normal), */
|
||||
/* 2 = random with zero fill */
|
||||
{
|
||||
REG UWORD block; /* block number from fcb */
|
||||
REG WORD index; /* index into disk map of fcb */
|
||||
REG XADDR old_dma; /* temp holding spot for dmaadr */
|
||||
REG UBYTE *buf; /* type compatibility kludge */
|
||||
REG WORD parm; /* parameter to do-io */
|
||||
REG WORD bigfile; /* file system is in word mode */
|
||||
REG UWORD rtn; /* return parameter */
|
||||
REG UBYTE rc; /* temp storage for rcdcnt */
|
||||
BSETUP
|
||||
|
||||
bigfile = ((GBL.parmp)->dsm) & ~0xff;
|
||||
if ( ( ! reading) && (fcbp->ftype[robit] & 0x80) ) error(5,fcbp);
|
||||
/* check for read-only file */
|
||||
if (random)
|
||||
{
|
||||
if ( rtn = new_ext(fcbp, reading, TRUE) ) return(rtn);
|
||||
/* open new extent if necessary, return if error */
|
||||
fcbp->cur_rec = (fcbp->ran2) & 0x7f;
|
||||
}
|
||||
else /* sequential */
|
||||
if (fcbp->cur_rec == 128)
|
||||
{ /* time to try next extent */
|
||||
if ( new_ext(fcbp, reading, FALSE) )
|
||||
return(1); /* if can't open new extent, error */
|
||||
fcbp->cur_rec = 0; /* opened new extent, zero cur_rec */
|
||||
}
|
||||
|
||||
/* record is now in active fcb */
|
||||
rc = fcbp->rcdcnt;
|
||||
if ( UBWORD(fcbp->cur_rec) >= get_rc(fcbp) )
|
||||
{
|
||||
if (reading) return(1); /* reading unwritten data */
|
||||
fcbp->s2 &= 0x7f; /* set file write flag */
|
||||
rc = fcbp->cur_rec + 1;
|
||||
}
|
||||
index = blkindx(fcbp); /* get index into fcb disk map */
|
||||
block = blknum(fcbp, index, bigfile);
|
||||
if (block) parm = (reading ? 0 : 1);
|
||||
else /* if allocated block, parm is just read or write */
|
||||
{ /* unallocated block */
|
||||
if (reading) return(1); /* reading unwritten data */
|
||||
/* Writing to new block */
|
||||
block = getaloc();
|
||||
if (block == (UWORD *)~0) return(2); /* out of space */
|
||||
setblk(fcbp, index, bigfile, block);
|
||||
parm = 3;
|
||||
if (random == 2)
|
||||
{ /* Write random with zero fill */
|
||||
old_dma = GBL.dmaadr;
|
||||
GBL.dmaadr = map_adr((XADDR)GBL.dirbufp,0);/* Do DMA from dir_buf */
|
||||
index = SECLEN;
|
||||
buf = (UBYTE *)GBL.dirbufp; /* fix type incompatibility */
|
||||
do buf[--index] = 0;
|
||||
while (index); /* zero the dma buffer */
|
||||
for (index = 0; index <= ((GBL.parmp)->blm); index++)
|
||||
{
|
||||
do_io(block, (UBYTE)index, parm);
|
||||
/* write zeros to the block */
|
||||
parm = 1; /* next write is not to new block */
|
||||
}
|
||||
GBL.dmaadr = old_dma; /* restore dma address */
|
||||
}
|
||||
}
|
||||
rtn = do_io(block, fcbp->cur_rec, parm);
|
||||
if ( rtn == 0 )
|
||||
{
|
||||
fcbp->rcdcnt = rc;
|
||||
if ( ! random ) fcbp->cur_rec += 1;
|
||||
}
|
||||
return(rtn);
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
|
||||
/********************************************************
|
||||
* *
|
||||
* BIOS definitions for P-CP/M *
|
||||
* *
|
||||
* Copyright (c) 1982 Digital Research, Inc. *
|
||||
* *
|
||||
* This include file simply defines the BIOS calls *
|
||||
* *
|
||||
* Memory management added 821018 by SS at Zilog *
|
||||
* *
|
||||
********************************************************/
|
||||
|
||||
EXTERN long bios(); /* main BIOS entry point */
|
||||
EXTERN UBYTE bios1(); /* used for character I/O functions */
|
||||
EXTERN bios2(); /* parm1 is word, no return value */
|
||||
EXTERN bios3(); /* used for set dma only */
|
||||
/* parm1 is a pointer, no return */
|
||||
EXTERN UBYTE *bios4(); /* seldsk only, parm1 and parm2 are */
|
||||
/* words, returns a pointer to dph */
|
||||
EXTERN UWORD bios5(); /* for sectran and set exception */
|
||||
EXTERN BYTE *bios6(); /* for get memory segment table */
|
||||
|
||||
|
||||
#define bwboot() bios1(1) /* warm boot */
|
||||
#define bconstat() bios1(2) /* console status */
|
||||
#define bconin() bios1(3) /* console input */
|
||||
#define bconout(parm) bios2(4,parm) /* console output parm */
|
||||
#define blstout(parm) bios2(5,parm) /* list device output */
|
||||
#define bpun(parm) bios2(6,parm) /* punch char output */
|
||||
#define brdr() bios1(7) /* reader input */
|
||||
#define bhome() bios1(8) /* recalibrate drive */
|
||||
#define bseldsk(parm1,parm2) bios4(9,parm1,parm2)
|
||||
/* select disk and return info */
|
||||
#define bsettrk(parm) bios2(10,parm) /* set track on disk */
|
||||
#define bsetsec(parm) bios2(11,parm) /* set sector for disk */
|
||||
#define bsetdma(parm) bios3(12,parm) /* set dma address */
|
||||
#define bread() bios1(13) /* read sector from disk */
|
||||
#define bwrite(parm) bios2(14,parm) /* write sector to disk */
|
||||
#define blistst() bios1(15) /* list device status */
|
||||
#define bsectrn(parm1,parm2) bios5(16,parm1,(XADDR)parm2)
|
||||
/* sector translate */
|
||||
#define bgetseg() bios6(18) /* get memory segment tbl */
|
||||
#define bgetiob() bios1(19) /* get I/O byte */
|
||||
#define bsetiob(parm) bios2(20,parm) /* set I/O byte */
|
||||
#define bflush() bios1(21) /* flush buffers */
|
||||
#define bsetvec(parm1,parm2) bios5(22,parm1,(XADDR)parm2)
|
||||
/* set exception vector */
|
||||
|
||||
|
||||
/************************/
|
||||
/* MEMORY MANAGEMENT */
|
||||
/*----------------------*/
|
||||
EXTERN XADDR map_adr(); /*(laddr, space)->paddr */
|
||||
EXTERN VOID mem_cpy(); /*(src, dst, len) */
|
||||
/*----------------------*/
|
||||
/* copy in, out (s,d,l) */
|
||||
/* */
|
||||
#define cpy_in(s,d,l) mem_cpy((XADDR)s, map_adr((XADDR)d, 0), (long)l)
|
||||
#define cpy_out(s,d,l) mem_cpy(map_adr((XADDR)s, 0), (XADDR)d, (long)l)
|
||||
/* */
|
||||
/************************/
|
||||
|
||||
|
@@ -0,0 +1,322 @@
|
||||
|
||||
/********************************************************
|
||||
* *
|
||||
* P-CP/M BDOS Character I/O Routines *
|
||||
* *
|
||||
* This module does BDOS functions 1 thru 11 *
|
||||
* *
|
||||
* It contains the following functions which *
|
||||
* are called from the BDOS main routine: *
|
||||
* constat(); *
|
||||
* conin(); *
|
||||
* cookdout(); *
|
||||
* rawconio(); *
|
||||
* prt_line(); *
|
||||
* readline(); *
|
||||
* *
|
||||
* Copyright (c) 1982 Digital Research, Inc. *
|
||||
* *
|
||||
********************************************************/
|
||||
|
||||
#include "stdio.h"
|
||||
|
||||
#include "bdosdef.h"
|
||||
|
||||
#include "biosdef.h"
|
||||
|
||||
|
||||
#define ctrlc 0x03
|
||||
#define ctrle 0x05
|
||||
#define ctrlp 0x10
|
||||
#define ctrlq 0x11
|
||||
#define ctrlr 0x12
|
||||
#define ctrls 0x13
|
||||
#define ctrlu 0x15
|
||||
#define ctrlx 0x18
|
||||
|
||||
#define cr 0x0d
|
||||
#define lf 0x0a
|
||||
#define tab 0x09
|
||||
#define rub 0x7f
|
||||
#define bs 0x08
|
||||
#define space 0x20
|
||||
|
||||
|
||||
EXTERN warmboot(); /* External function definition */
|
||||
EXTERN UBYTE cpy_bi(); /* copy byte in from user space */
|
||||
|
||||
|
||||
/******************/
|
||||
/* console status */
|
||||
/******************/
|
||||
|
||||
BOOLEAN constat()
|
||||
{
|
||||
BSETUP
|
||||
|
||||
return( GBL.kbchar ? TRUE : bconstat() );
|
||||
}
|
||||
|
||||
/********************/
|
||||
/* check for ctrl/s */
|
||||
/* used internally */
|
||||
/********************/
|
||||
conbrk()
|
||||
{
|
||||
REG UBYTE ch;
|
||||
REG BOOLEAN stop;
|
||||
BSETUP
|
||||
|
||||
stop = FALSE;
|
||||
if ( bconstat() ) do
|
||||
{
|
||||
if ( (ch = bconin()) == ctrlc ) warmboot(1);
|
||||
if ( ch == ctrls ) stop = TRUE;
|
||||
else if (ch == ctrlq) stop = FALSE;
|
||||
else if (ch == ctrlp) GBL.lstecho = !GBL.lstecho;
|
||||
else GBL.kbchar = ch;
|
||||
} while (stop);
|
||||
}
|
||||
|
||||
|
||||
/******************/
|
||||
/* console output */
|
||||
/* used internally*/
|
||||
/******************/
|
||||
|
||||
conout(ch)
|
||||
REG UBYTE ch;
|
||||
{
|
||||
BSETUP
|
||||
|
||||
conbrk(); /* check for control-s break */
|
||||
bconout(ch); /* output character to console */
|
||||
if (GBL.lstecho) blstout(ch); /* if ctrl-p on, echo to list dev */
|
||||
if (ch >= ' ') GBL.column++; /* keep track of screen column */
|
||||
else if (ch == cr) GBL.column = 0;
|
||||
else if (ch == bs) GBL.column--;
|
||||
}
|
||||
|
||||
|
||||
/*************************************/
|
||||
/* console output with tab expansion */
|
||||
/*************************************/
|
||||
|
||||
cookdout(ch, ctlout)
|
||||
REG UBYTE ch; /* character to output */
|
||||
BOOLEAN ctlout; /* output ^<char> for control chars? */
|
||||
{
|
||||
BSETUP
|
||||
|
||||
if (ch == tab) do /* expand tabs */
|
||||
conout( ' ' );
|
||||
while (GBL.column & 7);
|
||||
|
||||
else
|
||||
{
|
||||
if ( ctlout && (ch < ' ') )
|
||||
{
|
||||
conout( '^' );
|
||||
ch |= 0x40;
|
||||
}
|
||||
conout(ch); /* output the character */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*****************/
|
||||
/* console input */
|
||||
/*****************/
|
||||
|
||||
UBYTE getch() /* Get char from buffer or bios */
|
||||
/* For internal use only */
|
||||
{
|
||||
REG UBYTE temp;
|
||||
BSETUP
|
||||
|
||||
temp = GBL.kbchar; /* get buffered char */
|
||||
GBL.kbchar = 0; /* clear it */
|
||||
return( temp ? temp : bconin() ); /* if non-zero, return it */
|
||||
/* else get char from bios */
|
||||
}
|
||||
|
||||
UBYTE conin() /* BDOS console input function */
|
||||
{
|
||||
REG UBYTE ch;
|
||||
BSETUP
|
||||
|
||||
conout( ch = getch() );
|
||||
if (ch == ctrlp) GBL.lstecho = !GBL.lstecho;
|
||||
return(ch);
|
||||
}
|
||||
|
||||
/******************
|
||||
* raw console i/o *
|
||||
******************/
|
||||
|
||||
UBYTE rawconio(parm) /* BDOS raw console I/O function */
|
||||
|
||||
REG UWORD parm;
|
||||
{
|
||||
BSETUP
|
||||
|
||||
if (parm == 0xff) return(getch());
|
||||
else if (parm == 0xfe) return(constat());
|
||||
else return(bconout(parm & 0xff)); /* add return to make lint happy */
|
||||
}
|
||||
|
||||
|
||||
/****************************************************/
|
||||
/* print line up to delimiter($) with tab expansion */
|
||||
/****************************************************/
|
||||
|
||||
prt_line(p)
|
||||
REG UBYTE *p;
|
||||
{
|
||||
BSETUP
|
||||
|
||||
while( *p != GBL.delim ) cookdout( *(p++), FALSE);
|
||||
}
|
||||
|
||||
|
||||
/**********************************************/
|
||||
/* read line with editing and bounds checking */
|
||||
/**********************************************/
|
||||
|
||||
/* Two subroutines first */
|
||||
|
||||
newline(startcol)
|
||||
REG UWORD startcol;
|
||||
{
|
||||
BSETUP
|
||||
|
||||
conout(cr); /* go to new line */
|
||||
conout(lf);
|
||||
while(startcol)
|
||||
{
|
||||
conout(' ');
|
||||
startcol -= 1; /* start output at starting column */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
backsp(p, stcol) /* backspace one character position */
|
||||
REG struct conbuf *p; /* pointer to console buffer */
|
||||
UWORD stcol;
|
||||
{
|
||||
REG WORD i;
|
||||
REG WORD length;
|
||||
REG UBYTE ch;
|
||||
BSETUP
|
||||
|
||||
if (p->retlen) length = UBWORD(--(p->retlen));
|
||||
/* if buffer non-empty, decrease it by 1 */
|
||||
else length = 0;
|
||||
i = stcol;
|
||||
while (length--)
|
||||
{
|
||||
ch = p->cbuf[length]; /* get character from buffer */
|
||||
if ( ch == tab )
|
||||
{
|
||||
i += 8; /* i is our column counter */
|
||||
i &= ~7; /* for tab, go to multiple of 8 */
|
||||
}
|
||||
else if ( ch < ' ' ) i += 2;
|
||||
/* control chars put out 2 printable chars */
|
||||
else i += 1;
|
||||
}
|
||||
while (GBL.column > i)
|
||||
{
|
||||
conout(bs); /* backspace until we get to proper column */
|
||||
conout(' ');
|
||||
conout(bs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
readline(p) /* BDOS function 10 */
|
||||
REG struct conbuf *p;
|
||||
|
||||
{
|
||||
REG UBYTE ch;
|
||||
REG UWORD i;
|
||||
REG UWORD j;
|
||||
REG UBYTE *q;
|
||||
UWORD stcol;
|
||||
|
||||
BSETUP
|
||||
|
||||
stcol = GBL.column; /* set up starting column */
|
||||
if (GBL.chainp != XNULL) /* chain to program code */
|
||||
{
|
||||
i = UBWORD(cpy_bi(GBL.chainp++)); /* cpy in from user space */
|
||||
j = UBWORD(p->maxlen);
|
||||
if (j < i) i = j; /* don't overflow console buffer! */
|
||||
p->retlen = (UBYTE)i;
|
||||
q = p->cbuf;
|
||||
while (i)
|
||||
{
|
||||
cookdout( *q++ = cpy_bi(GBL.chainp++), TRUE);
|
||||
i -= 1;
|
||||
}
|
||||
GBL.chainp = XNULL;
|
||||
return;
|
||||
}
|
||||
|
||||
p->retlen = 0; /* start out with empty buffer */
|
||||
while ( UBWORD(p->retlen) < UBWORD(p->maxlen) )
|
||||
{ /* main loop for read console buffer */
|
||||
|
||||
if ( ((ch=getch()) == ctrlc) && !(p->retlen) )
|
||||
{
|
||||
cookdout(ctrlc, TRUE);
|
||||
warmboot(1);
|
||||
}
|
||||
|
||||
else if ( (ch == cr) || (ch == lf) )
|
||||
{ /* if cr or lf, exit */
|
||||
conout(cr);
|
||||
break;
|
||||
}
|
||||
|
||||
else if (ch == bs) backsp(p, stcol); /* backspace */
|
||||
|
||||
else if (ch == rub) /* delete character */
|
||||
{
|
||||
if (GBL.echodel)
|
||||
{
|
||||
if (p->retlen)
|
||||
{
|
||||
i = UBWORD(--(p->retlen));
|
||||
conout( p->cbuf[i] );
|
||||
}
|
||||
}
|
||||
else backsp(p, stcol);
|
||||
}
|
||||
|
||||
else if (ch == ctrlp) GBL.lstecho = !GBL.lstecho;
|
||||
/* control-p */
|
||||
else if (ch == ctrlx) /* control-x */
|
||||
do backsp(p,stcol); while (p->retlen);
|
||||
|
||||
else if (ch == ctrle) newline(stcol); /* control-e */
|
||||
|
||||
else if (ch == ctrlu) /* control-u */
|
||||
{
|
||||
conout('#');
|
||||
newline(stcol);
|
||||
p->retlen = 0;
|
||||
}
|
||||
|
||||
else if (ch == ctrlr) /* control-r */
|
||||
{
|
||||
conout('#');
|
||||
newline(stcol);
|
||||
for (i=0; i < UBWORD(p->retlen); i++)
|
||||
cookdout( p->cbuf[i], TRUE);
|
||||
}
|
||||
|
||||
else /* normal character */
|
||||
cookdout( (p->cbuf[UBWORD((p->retlen)++)] = ch), TRUE );
|
||||
}
|
||||
}
|
@@ -0,0 +1,277 @@
|
||||
|
||||
/****************************************************************
|
||||
* *
|
||||
* P-CP/M BDOS Disk Utilities Module *
|
||||
* *
|
||||
* This module contains the miscellaneous utilities *
|
||||
* for manipulating the disk in P-CP/M. Included are: *
|
||||
* *
|
||||
* dirscan() - general purpose dir scanning *
|
||||
* setaloc() - set bit in allocation vector *
|
||||
* clraloc() - clear bit in allocation vector *
|
||||
* getaloc() - get free allocation block *
|
||||
* dchksum() - directory checksum calculator *
|
||||
* dir_rd() - read directory sector *
|
||||
* dir_wr() - write directory sector *
|
||||
* rdwrt() - read/write disk sector *
|
||||
* *
|
||||
* *
|
||||
* Modified for memory management on Z8000 *
|
||||
* *
|
||||
****************************************************************/
|
||||
|
||||
#include "stdio.h" /* Standard I/O declarations */
|
||||
|
||||
#include "bdosdef.h" /* Type and structure declarations for BDOS */
|
||||
|
||||
#include "pktio.h" /* Packet I/O definitions */
|
||||
|
||||
#include "biosdef.h" /* Bios & mem mapperinterface */
|
||||
|
||||
|
||||
/* declare external functions and variables */
|
||||
EXTERN UWORD do_phio(); /* external physical disk I/O routine */
|
||||
EXTERN UWORD error(); /* external error routine */
|
||||
|
||||
EXTERN UWORD log_dsk; /* logged-on disk vector */
|
||||
EXTERN UWORD ro_dsk; /* read-only disk vector */
|
||||
EXTERN UWORD crit_dsk; /* critical disk vector */
|
||||
|
||||
|
||||
/**********************
|
||||
* read/write routine *
|
||||
**********************/
|
||||
|
||||
UWORD rdwrt(secnum, dma, parm)
|
||||
/* General disk sector read/write routine */
|
||||
/* It simply sets up a I/O packet and sends it to do_phio */
|
||||
|
||||
LONG secnum; /* logical sector number to read/write */
|
||||
XADDR dma; /* dma address */
|
||||
REG WORD parm; /* 0 for read, write parm + 1 for write */
|
||||
|
||||
{
|
||||
struct iopb rwpkt;
|
||||
BSETUP
|
||||
|
||||
rwpkt.devnum = GBL.curdsk; /* disk to read/write */
|
||||
if (parm)
|
||||
{
|
||||
rwpkt.iofcn = (BYTE)write; /* if parm non-zero, we're doing a write */
|
||||
rwpkt.ioflags = (BYTE)(parm-1); /* pass write parm */
|
||||
if ( ro_dsk & (1 << (rwpkt.devnum)) ) error(4);
|
||||
/* don't write on read-only disk */
|
||||
}
|
||||
else
|
||||
{
|
||||
rwpkt.iofcn = (BYTE)read;
|
||||
rwpkt.ioflags = (BYTE)0;
|
||||
}
|
||||
rwpkt.devadr = secnum; /* sector number */
|
||||
rwpkt.xferadr = dma; /* dma address */
|
||||
|
||||
/* parameters that are currently not used by do_phio
|
||||
rwpkt.devtype = disk;
|
||||
rwpkt.xferlen = 1;
|
||||
*/
|
||||
rwpkt.infop = GBL.dphp; /* pass ptr to dph */
|
||||
while ( do_phio(&rwpkt) )
|
||||
if ( error( parm ? 1 : 0 ) ) break;
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/***************************
|
||||
* directory read routine *
|
||||
***************************/
|
||||
|
||||
UWORD dir_rd(secnum)
|
||||
|
||||
UWORD secnum;
|
||||
{
|
||||
BSETUP
|
||||
|
||||
return( rdwrt((LONG)secnum, map_adr((XADDR)GBL.dirbufp, 0), 0) );
|
||||
}
|
||||
|
||||
|
||||
/****************************
|
||||
* directory write routine *
|
||||
****************************/
|
||||
|
||||
UWORD dir_wr(secnum)
|
||||
|
||||
REG WORD secnum;
|
||||
{
|
||||
REG UWORD rtn;
|
||||
UBYTE dchksum();
|
||||
BSETUP
|
||||
|
||||
rtn = rdwrt( (LONG)secnum, map_adr((XADDR)GBL.dirbufp, 0), 2);
|
||||
if ( secnum < (GBL.parmp)->cks )
|
||||
*((GBL.dphp)->csv + secnum) = dchksum();
|
||||
return(rtn);
|
||||
}
|
||||
|
||||
|
||||
/*******************************
|
||||
* directory checksum routine *
|
||||
*******************************/
|
||||
|
||||
UBYTE dchksum()
|
||||
/* Compute checksum over one directory sector */
|
||||
/* Note that this implementation is dependant on the representation */
|
||||
/* of a LONG and is therefore not very portable. But it's fast */
|
||||
{
|
||||
REG LONG *p; /* local temp variables */
|
||||
REG LONG lsum;
|
||||
REG WORD i;
|
||||
|
||||
BSETUP
|
||||
|
||||
p = (LONG *)GBL.dirbufp; /* point to directory buffer */
|
||||
lsum = 0;
|
||||
i = SECLEN / (sizeof lsum);
|
||||
do
|
||||
{
|
||||
lsum += *p++; /* add next 4 bytes of directory */
|
||||
i -= 1;
|
||||
} while (i);
|
||||
lsum += (lsum >> 16);
|
||||
lsum += (lsum >> 8);
|
||||
return( (UBYTE)(lsum & 0xff) );
|
||||
}
|
||||
|
||||
|
||||
/************************
|
||||
* dirscan entry point *
|
||||
************************/
|
||||
|
||||
UWORD dirscan(funcp, fcbp, parms)
|
||||
|
||||
BOOLEAN (*funcp)(); /* funcp is a pointer to a Boolean function */
|
||||
REG struct fcb *fcbp; /* fcbp is a pointer to a fcb */
|
||||
REG UWORD parms; /* parms is 16 bit set of bit parameters */
|
||||
|
||||
/* Parms & 1 = 0 to start at beginning of dir, 1 to continue from last */
|
||||
/* Parms & 2 = 0 to stop when *funcp is true, 1 to go until end */
|
||||
/* Parms & 4 = 0 to check the dir checksum, 1 to store new checksum */
|
||||
/* Parms & 8 = 0 to stop at hiwater, 1 to go until end of directory */
|
||||
|
||||
#define continu 1
|
||||
#define full 2
|
||||
#define initckv 4
|
||||
#define pasthw 8
|
||||
|
||||
{
|
||||
REG UWORD i; /* loop counter */
|
||||
REG struct dpb *dparmp; /* pointer to disk parm block */
|
||||
REG UWORD dirsec; /* sector number we're working on */
|
||||
REG UWORD rtn; /* return value */
|
||||
REG UBYTE *p; /* scratch pointer */
|
||||
REG UWORD bitvec; /* disk nmbr represented as a vector */
|
||||
|
||||
BSETUP
|
||||
|
||||
dparmp = GBL.parmp; /* init ptr to dpb */
|
||||
rtn = 255; /* assume it doesn't work */
|
||||
|
||||
/* Sorry about this FOR loop, but the initialization terms and end test
|
||||
really do depend on the input parameters, so...... */
|
||||
for ( i = ( (parms & continu) ? GBL.srchpos + 1 : 0);
|
||||
i <= ( (parms & pasthw) ? (dparmp->drm) : (GBL.dphp)->hiwater );
|
||||
i++ )
|
||||
{ /* main directory scanning loop */
|
||||
if ( ! (i & 3) )
|
||||
{ /* inside loop happens when we need to
|
||||
read another directory sector */
|
||||
retry: dirsec = i >> 2;
|
||||
dir_rd(dirsec); /* read the directory sector */
|
||||
if ( dirsec < (dparmp->cks) ) /* checksumming on this sector? */
|
||||
{
|
||||
p = ((GBL.dphp)->csv) + dirsec;
|
||||
/* point to checksum vector byte */
|
||||
if (parms & initckv) *p = dchksum();
|
||||
else if (*p != dchksum())
|
||||
{ /* checksum error! */
|
||||
(GBL.dphp)->hiwater = dparmp->drm; /* reset hi water */
|
||||
bitvec = 1 << (GBL.curdsk);
|
||||
if (crit_dsk & bitvec) /* if disk in critical mode */
|
||||
ro_dsk |= bitvec; /* then set it to r/o */
|
||||
else
|
||||
{
|
||||
log_dsk &= ~bitvec; /* else log it off */
|
||||
seldsk(GBL.curdsk); /* and re-select it */
|
||||
goto retry; /* and re-do current op */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GBL.srchpos = i;
|
||||
if ( (*funcp)(fcbp, (GBL.dirbufp) + (i&3), i) )
|
||||
/* call function with parms of (1) fcb ptr,
|
||||
(2) pointer to directory entry, and
|
||||
(3) directory index */
|
||||
{
|
||||
if (parms & full) rtn = 0; /* found a match, but keep going */
|
||||
else return(i & 3); /* return directory code */
|
||||
}
|
||||
}
|
||||
return(rtn);
|
||||
}
|
||||
|
||||
|
||||
/****************************************
|
||||
* Routines to manage allocation vector *
|
||||
* setaloc() *
|
||||
* clraloc() *
|
||||
* getaloc() *
|
||||
****************************************/
|
||||
|
||||
setaloc(bitnum)
|
||||
/* Set bit in allocation vector */
|
||||
REG UWORD bitnum;
|
||||
{
|
||||
BSETUP
|
||||
|
||||
*((GBL.dphp)->alv + (bitnum>>3)) |= 0x80 >> (bitnum & 7);
|
||||
}
|
||||
|
||||
|
||||
clraloc(bitnum)
|
||||
/* Clear bit in allocation vector */
|
||||
REG UWORD bitnum;
|
||||
{
|
||||
BSETUP
|
||||
|
||||
if (bitnum)
|
||||
*((GBL.dphp)->alv + (bitnum>>3)) &= ~(0x80 >> (bitnum & 7));
|
||||
}
|
||||
|
||||
|
||||
UWORD getaloc()
|
||||
/* Get a free block in the file system and set the bit in allocation vector */
|
||||
{
|
||||
REG UWORD i; /* loop counter */
|
||||
REG WORD diskmax; /* # bits in alv - 1 */
|
||||
REG UBYTE *p; /* ptr to byte */
|
||||
|
||||
BSETUP
|
||||
LOCK /* need to lock the file system while messing
|
||||
with the allocation vector */
|
||||
|
||||
diskmax = (GBL.parmp)->dsm;
|
||||
/* get disk max field from dpb */
|
||||
p = (GBL.dphp)->alv;
|
||||
for (i = 0; i <= diskmax; i++)
|
||||
{
|
||||
if ( ~(*(p + (i >> 3))) & (0x80 >> (i&7)) )
|
||||
{ /* found a zero in allocation vector */
|
||||
setaloc(i);
|
||||
UNLOCK /* can unlock file system now */
|
||||
return(i); /* return block number */
|
||||
}
|
||||
}
|
||||
return(~0); /* if no free block found, return -1 */
|
||||
}
|
@@ -0,0 +1,356 @@
|
||||
;************************************************
|
||||
;* *
|
||||
;* P-CP/M Basic Disk Operating System *
|
||||
;* Exception Handling Module *
|
||||
;* *
|
||||
;* Version 0.0 -- July 21, 1982 *
|
||||
;* Version 0.1 -- July 25, 1982 *
|
||||
;* Version 0.2 -- 821014 (Zilog) *
|
||||
;* Version 0.3 -- 821222 (Zilog) *
|
||||
;* *
|
||||
;************************************************
|
||||
|
||||
|
||||
__text: .sect
|
||||
|
||||
;****************************************************
|
||||
;*
|
||||
;* Externals
|
||||
;*
|
||||
;****************************************************
|
||||
|
||||
.global _sysseg
|
||||
.global _tpa_lp
|
||||
.global _tpa_hp
|
||||
|
||||
|
||||
;****************************************************
|
||||
;*
|
||||
;* Globals and Constants
|
||||
;*
|
||||
;****************************************************
|
||||
|
||||
.global _initexc
|
||||
|
||||
BGETSEG .equ 18 ;Bios call to get mem seg tbl
|
||||
BSETEXC .equ 22 ;Bios call to set xvec
|
||||
|
||||
BIOS_SC .equ 3 ;Bios system call
|
||||
|
||||
;****************************************************
|
||||
;*
|
||||
;* initexc(vecarray) -- initialize exception vector
|
||||
;*
|
||||
;* The BDOS maintains its OWN exception vector.
|
||||
;* This routine sets up the BIOS exception
|
||||
;* vectors to point to handlers in this module,
|
||||
;* that vector again using the BDOS vectors.
|
||||
;*
|
||||
;* Only vectors 2-23 and 36-38 are handled by
|
||||
;* the BDOS, leaving 0-1 and 24-35 for the BIOS
|
||||
;*
|
||||
;****************************************************
|
||||
|
||||
_initexc:
|
||||
;* sp -> return addr
|
||||
;* +2 table addr
|
||||
|
||||
ld r3,#BSETEXC ; r3 = BDOS command
|
||||
ldl rr4,#2 ;rr4 = exception #
|
||||
ld r6,_sysseg ;rr6 -> handler
|
||||
lda r7,exchndl ; (segmented)
|
||||
|
||||
dec r15,#10 ;space to save regs
|
||||
; on stack over SC
|
||||
initloop:
|
||||
ldm @r15,r3,#5
|
||||
sc #BIOS_SC
|
||||
ldm r3,@r15,#5
|
||||
inc r5,#1 ;step exception #
|
||||
inc r7,#2 ;and handler addr.
|
||||
cp r5,#24 ;done 23 yet?
|
||||
jr ne init1 ; no
|
||||
ld r5,#36 ; yes--skip to 36
|
||||
inc r7,#8 ; we have entries in
|
||||
; table for 32-36
|
||||
; but system uses
|
||||
; them. Skip.
|
||||
init1: cp r5,#48 ;done all 48?
|
||||
jr ne initloop ; no--loop
|
||||
|
||||
inc r15,#10 ;clean stack
|
||||
|
||||
ld r2,2(r15) ;save vector address
|
||||
ld evec_adr,r2
|
||||
|
||||
ld r0,#18 ; 18 long entries
|
||||
subl rr4,rr4
|
||||
init2: ;clear vector array
|
||||
|
||||
; should check current entry, and keep it if
|
||||
; between old TPA and current TPA limits.
|
||||
|
||||
; The TPA limits are flaky because we have two TPA's,
|
||||
; for split and merged spaces, not to mention
|
||||
; segmented programs.
|
||||
|
||||
; What we are going to do is clear the lot.
|
||||
; Programs that want permanent traps can use the
|
||||
; Bios to set the trap vector.
|
||||
|
||||
ldl rr6,@r2 ; rr6 = exception addr
|
||||
|
||||
ldl @r2,rr4
|
||||
init3:
|
||||
inc r2,#4
|
||||
djnz r0,init2
|
||||
|
||||
ret ;return
|
||||
|
||||
;****************************************************
|
||||
;*
|
||||
;* Exception Handler Routine
|
||||
;*
|
||||
;* An exception handler is called as a subroutine
|
||||
;* in segmented mode, with all registers saved
|
||||
;* on the stack.
|
||||
;*
|
||||
;* Returning (ret) from the handler (in segmented
|
||||
;* mode) will return to the state-restorer.
|
||||
;*
|
||||
;****************************************************
|
||||
|
||||
exchndl:
|
||||
calr except
|
||||
excrtn0:
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
calr except
|
||||
|
||||
|
||||
except: ; general exception handler
|
||||
|
||||
ldctl r0,FCW ; NONSEGMENTED
|
||||
res r0,#15
|
||||
ldctl FCW,r0
|
||||
|
||||
popl rr0,@r15 ; get return address
|
||||
; from above array
|
||||
sub r1,#excrtn0 ; r1 now = 4*encoded
|
||||
srl r2,#2 ; exception number
|
||||
|
||||
; r1/4 = [0..21, 22..37] represents exceptions
|
||||
; [2..23, 32..47].
|
||||
;
|
||||
; If r1/4 is in the range [0..9, 22..29] then the
|
||||
; exception may be redirected by the BDOS.
|
||||
|
||||
cp r1,#36
|
||||
jr le chkredir
|
||||
cp r1,#88
|
||||
jr lt dfltexc
|
||||
cp r1,#116
|
||||
jr gt dfltexc
|
||||
; in range of redirected exceptions
|
||||
sub r1,#48 ; subtract 4*12 to normalize [0..9,22..29]
|
||||
; into [0..9,10..17]
|
||||
chkredir:
|
||||
ld r2,evec_adr ; index into exception
|
||||
; vector array
|
||||
ldl rr4,r2(r1) ; rr4 -> handler
|
||||
testl rr4 ; if non-zero
|
||||
jr nz usrexc ; go do it
|
||||
|
||||
;not redirected,
|
||||
; do default handler
|
||||
cp r1,#40
|
||||
jr lt dfltexc
|
||||
add r1,#48 ; add 4*12 sub'd above
|
||||
dfltexc:
|
||||
srl r1,#2 ; divide r1 by 4
|
||||
; r1 = [0..21,22..37]
|
||||
; meaning [2..23,32..47]
|
||||
add r1,#2 ;get back real except#
|
||||
cp r1,#23
|
||||
jr le lowexc
|
||||
add r2,#12
|
||||
lowexc:
|
||||
push @r15,r1 ; save exception #
|
||||
; print default msg
|
||||
clr r6
|
||||
ld r7,#excmsg1
|
||||
calr print
|
||||
calr prtdollar
|
||||
pop r1,@r15
|
||||
calr prtbyte
|
||||
clr r6
|
||||
ld r7,#excmsg2
|
||||
calr print
|
||||
calr prtdollar
|
||||
popl rr0,@r15
|
||||
calr prtlong
|
||||
clr r6
|
||||
ld r7,#excmsg3
|
||||
calr print
|
||||
|
||||
clr r5 ; warm boot
|
||||
sc #2
|
||||
ret
|
||||
|
||||
usrexc: ; call user exception handler
|
||||
; rr4 -> handler
|
||||
|
||||
; user exception handler called in with FCW restored
|
||||
; to that at the time of the exception, with the
|
||||
; stack containing a copy of the context block.
|
||||
; On top of the context block will be a GARBAGE
|
||||
; return address of the appropriate mode.
|
||||
|
||||
; Handler must exit with XFER system call.
|
||||
|
||||
; stack now contains:
|
||||
; return address (long) to trap_ret
|
||||
; normal r0..r15
|
||||
; reason, fcw, PC (long)
|
||||
|
||||
ld r1,38(r15) ;34+4 r1 = user FCW
|
||||
bit r1,#14 ;system mode?
|
||||
jr nz sysexc ; yes
|
||||
; no
|
||||
ldl rr2,32(r15) ;28+4 rr2 = user stack
|
||||
bit r1,#15 ;segmented?
|
||||
jr nz segusrx ; yes
|
||||
; no -- get seg
|
||||
ld r2,42(r15) ;38+4
|
||||
segusrx:
|
||||
ldctl r0,FCW ;go segmented
|
||||
set r0,#15
|
||||
ldctl FCW,r0
|
||||
|
||||
sub r3,#44 ;copy frame to usr
|
||||
; (with junk PC)
|
||||
ld r0,#22
|
||||
ldir @r2,@r14,r0
|
||||
|
||||
pushl @r14,rr4 ;go
|
||||
pushl @r14,rr0
|
||||
iret
|
||||
|
||||
sysexc: ;system mode exc.
|
||||
set r1,#15 ; go segmented
|
||||
ldctl FCW,r1
|
||||
jp @r4 ;go
|
||||
|
||||
|
||||
|
||||
;****************************************************
|
||||
;*
|
||||
;* Subroutines
|
||||
;*
|
||||
;****************************************************
|
||||
|
||||
print:
|
||||
ld r5,#9 ;PRINTMSG
|
||||
sc #2 ;BDOS
|
||||
ret
|
||||
|
||||
prtdollar:
|
||||
ld r5,#2 ;PRINTCHAR
|
||||
ldl rr6,#24h ; dollarsign
|
||||
sc #2
|
||||
ret
|
||||
|
||||
prtlong: ; print rr0 as hex long
|
||||
push @r15,r1
|
||||
ex r0,r1
|
||||
calr prtword
|
||||
pop r1,@r15
|
||||
|
||||
prtword: ; print r1 as hex word
|
||||
push @r15,r1
|
||||
ldb rl1,rh1
|
||||
calr prtbyte
|
||||
pop r1,@r15
|
||||
|
||||
prtbyte: ; Print rl1 as hex byte
|
||||
push @r15,r1
|
||||
srl r1,#4
|
||||
calr prtnib
|
||||
pop r1,@r15
|
||||
|
||||
prtnib:
|
||||
and r1,#15
|
||||
cp r1,#10
|
||||
jr lt lt10
|
||||
add r1,#7
|
||||
lt10:
|
||||
add r1,#30h
|
||||
ld r7,r1
|
||||
ld r5,#2 ;PRINTBYTE
|
||||
sc #2 ;BDOS
|
||||
ret
|
||||
|
||||
;****************************************************
|
||||
;*
|
||||
;* data
|
||||
;*
|
||||
;****************************************************
|
||||
|
||||
__data: .sect
|
||||
|
||||
excmsg1:
|
||||
.byte 13,10,10,"Exception $"
|
||||
|
||||
excmsg2:
|
||||
.byte " at user address $"
|
||||
|
||||
excmsg3:
|
||||
.byte ". Aborted.$"
|
||||
|
||||
|
||||
;****************************************************
|
||||
;*
|
||||
;* bss
|
||||
;*
|
||||
;****************************************************
|
||||
|
||||
__bss: .sect
|
||||
|
||||
evec_adr:
|
||||
.block 2
|
||||
|
||||
.end
|
@@ -0,0 +1,662 @@
|
||||
|
||||
/****************************************************************
|
||||
* *
|
||||
* P-CP/M BDOS File I/O Module *
|
||||
* *
|
||||
* This module contains all file handling BDOS functions *
|
||||
* except for read and write for P-CP/M. Included are: *
|
||||
* *
|
||||
* seldsk() - select disk *
|
||||
* openfile() - open file *
|
||||
* close_fi() - close file *
|
||||
* search() - search for first/next file match *
|
||||
* create() - create file *
|
||||
* delete() - delete file *
|
||||
* rename() - rename file *
|
||||
* set_attr() - set file attributes *
|
||||
* getsize() - get file size *
|
||||
* setran() - set random record field *
|
||||
* free_sp() - get disk free space *
|
||||
* move() - general purpose byte mover *
|
||||
* *
|
||||
* *
|
||||
* Modified for memory management on Z8000 *
|
||||
* The main change is that search no longer calls tmp_sel *
|
||||
* tmp_sel in bdosmain.c now handles drive number = '?' *
|
||||
* *
|
||||
****************************************************************/
|
||||
|
||||
#include "stdio.h" /* Standard I/O declarations */
|
||||
|
||||
#include "bdosdef.h" /* Type and structure declarations for BDOS */
|
||||
|
||||
#include "biosdef.h" /* and BIOS */
|
||||
|
||||
#include "pktio.h" /* Packet I/O definitions */
|
||||
|
||||
/* declare external fucntions */
|
||||
EXTERN UWORD dirscan(); /* directory scanning routine */
|
||||
EXTERN UWORD error(); /* disk error routine */
|
||||
EXTERN UWORD do_phio(); /* packet disk i/o handler */
|
||||
EXTERN clraloc(); /* clear bit in allocation vector */
|
||||
EXTERN setaloc(); /* set bit in allocation vector */
|
||||
EXTERN UWORD swap(); /* assembly language byte swapper */
|
||||
EXTERN UWORD dir_wr(); /* directory write routine */
|
||||
EXTERN tmp_sel(); /* temporary select disk routine */
|
||||
EXTERN UWORD calcext(); /* calc max extent allocated for fcb */
|
||||
EXTERN UWORD udiv(); /* unsigned divide routine */
|
||||
|
||||
|
||||
/* declare external variables */
|
||||
EXTERN UWORD log_dsk; /* logged-on disk vector */
|
||||
EXTERN UWORD ro_dsk; /* read-only disk vector */
|
||||
EXTERN UWORD crit_dsk; /* vector of disks in critical state */
|
||||
|
||||
|
||||
/************************************
|
||||
* This function passed to dirscan *
|
||||
* from seldsk (below) *
|
||||
************************************/
|
||||
|
||||
BOOLEAN alloc(fcbp, dirp, dirindx) /* ARGSUSED */
|
||||
/* Set up allocation vector for directory entry pointed to by dirp */
|
||||
|
||||
struct fcb *fcbp; /* not used in this function */
|
||||
REG struct dirent *dirp; /* pointer to directory entry */
|
||||
WORD dirindx; /* index into directory for *dirp */
|
||||
{
|
||||
REG WORD i; /* loop counter */
|
||||
BSETUP
|
||||
|
||||
if ( UBWORD(dirp->entry) < 0x10 ) /* skip MP/M 2.x and CP/M 3.x XFCBs */
|
||||
{
|
||||
(GBL.dphp)->hiwater = dirindx; /* set up high water mark for disk */
|
||||
i = 0;
|
||||
if ((GBL.parmp)->dsm < 256)
|
||||
{
|
||||
do setaloc( UBWORD(dirp->dskmap.small[i++]) );
|
||||
while (i <= 15);
|
||||
}
|
||||
else
|
||||
{
|
||||
do setaloc(swap(dirp->dskmap.big[i++]));
|
||||
while (i <= 7);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/************************
|
||||
* seldsk entry point *
|
||||
************************/
|
||||
|
||||
seldsk(dsknum)
|
||||
|
||||
REG UBYTE dsknum; /* disk number to select */
|
||||
|
||||
{
|
||||
struct iopb selpkt;
|
||||
REG WORD i;
|
||||
UWORD j;
|
||||
REG UBYTE logflag;
|
||||
BSETUP
|
||||
|
||||
logflag = ~(log_dsk >> dsknum) & 1;
|
||||
if ((GBL.curdsk != dsknum) || logflag)
|
||||
{ /* if not last used disk or not logged on */
|
||||
selpkt.iofcn = sel_info;
|
||||
GBL.curdsk = (selpkt.devnum = dsknum);
|
||||
if (UBWORD(dsknum) > 15) error(2);
|
||||
selpkt.ioflags = logflag ^ 1;
|
||||
do
|
||||
{
|
||||
do_phio(&selpkt); /* actually do the disk select */
|
||||
if ( (GBL.dphp = (struct dph *)selpkt.infop) != NULL ) break;
|
||||
} while ( ! error(3) );
|
||||
|
||||
GBL.dirbufp = (GBL.dphp)->dbufp;
|
||||
/* set up GBL copies of dir_buf and dpb ptrs */
|
||||
GBL.parmp = (GBL.dphp)->dpbp;
|
||||
}
|
||||
if (logflag)
|
||||
{ /* if disk not previously logged on, do it now */
|
||||
LOCK /* must lock the file system while messing with alloc vec */
|
||||
i = (GBL.parmp)->dsm;
|
||||
do clraloc(i); while (i--); /* clear the allocation vector */
|
||||
i = udiv( (LONG)(((GBL.parmp)->drm) + 1),
|
||||
4 * (((GBL.parmp)->blm) + 1), &j);
|
||||
/* calculate nmbr of directory blks */
|
||||
if (j) i++; /* round up */
|
||||
do setaloc(--i); while (i); /* alloc directory blocks */
|
||||
dirscan(alloc, NULL, 0x0e); /* do directory scan & alloc blocks */
|
||||
log_dsk |= 1 << dsknum; /* mark disk as logged in */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************
|
||||
* General purpose byte mover *
|
||||
*******************************/
|
||||
|
||||
move(p1, p2, i)
|
||||
|
||||
REG BYTE *p1;
|
||||
REG BYTE *p2;
|
||||
REG WORD i;
|
||||
{
|
||||
while (i--)
|
||||
*p2++ = *p1++;
|
||||
}
|
||||
|
||||
|
||||
/*************************************
|
||||
* General purpose filename matcher *
|
||||
*************************************/
|
||||
|
||||
BOOLEAN match(p1, p2, chk_ext)
|
||||
|
||||
REG UBYTE *p1;
|
||||
REG UBYTE *p2;
|
||||
BOOLEAN chk_ext;
|
||||
{
|
||||
REG WORD i;
|
||||
REG UBYTE temp;
|
||||
BSETUP
|
||||
|
||||
i = 12;
|
||||
do
|
||||
{
|
||||
temp = (*p1 ^ '?');
|
||||
if ( ((*p1++ ^ *p2++) & 0x7f) && temp )
|
||||
return(FALSE);
|
||||
i -= 1;
|
||||
} while (i);
|
||||
if (chk_ext)
|
||||
{
|
||||
if ( (*p1 != '?') && ((*p1 ^ *p2) & ~((GBL.parmp)->exm)) )
|
||||
return(FALSE);
|
||||
p1 += 2;
|
||||
p2 += 2;
|
||||
if ((*p1 ^ *p2) & 0x3f) return(FALSE);
|
||||
}
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
|
||||
/************************
|
||||
* openfile entry point *
|
||||
************************/
|
||||
|
||||
BOOLEAN openfile(fcbp, dirp, dirindx) /* ARGSUSED */
|
||||
|
||||
REG struct fcb *fcbp; /* pointer to fcb for file to open */
|
||||
struct dirent *dirp; /* pointer to directory entry */
|
||||
WORD dirindx;
|
||||
|
||||
{
|
||||
REG UBYTE fcb_ext; /* extent field from fcb */
|
||||
REG BOOLEAN rtn;
|
||||
BSETUP
|
||||
|
||||
if ( rtn = match(fcbp, dirp, TRUE) )
|
||||
{
|
||||
fcb_ext = fcbp->extent; /* save extent number from user's fcb */
|
||||
move(dirp, fcbp, sizeof *dirp);
|
||||
/* copy dir entry into user's fcb */
|
||||
fcbp->extent = fcb_ext;
|
||||
fcbp->s2 |= 0x80; /* set hi bit of S2 (write flag) */
|
||||
crit_dsk |= 1 << (GBL.curdsk);
|
||||
}
|
||||
return(rtn);
|
||||
}
|
||||
|
||||
|
||||
/*************************/
|
||||
/* flush buffers routine */
|
||||
/*************************/
|
||||
|
||||
UWORD flushit()
|
||||
{
|
||||
REG UWORD rtn; /* return code from flush buffers call */
|
||||
struct iopb flushpkt; /* I/O packet for flush buffers call */
|
||||
|
||||
flushpkt.iofcn = flush;
|
||||
while ( rtn = do_phio(&flushpkt) )
|
||||
if ( error(1) ) break;
|
||||
return(rtn);
|
||||
}
|
||||
|
||||
|
||||
/*********************************
|
||||
* file close routine for dirscan *
|
||||
*********************************/
|
||||
|
||||
BOOLEAN close(fcbp, dirp, dirindx)
|
||||
|
||||
REG struct fcb *fcbp; /* pointer to fcb */
|
||||
REG struct dirent *dirp; /* pointer to directory entry */
|
||||
WORD dirindx; /* index into directory */
|
||||
|
||||
{
|
||||
REG WORD i;
|
||||
REG UBYTE *fp;
|
||||
REG UBYTE *dp;
|
||||
REG UWORD fcb_ext;
|
||||
REG UWORD dir_ext;
|
||||
BSETUP
|
||||
|
||||
if ( match(fcbp, dirp, TRUE) )
|
||||
{ /* Note that FCB merging is done here as a final
|
||||
confirmation that disks haven't been swapped */
|
||||
LOCK
|
||||
fp = &(fcbp->dskmap.small[0]);
|
||||
dp = &(dirp->dskmap.small[0]);
|
||||
if ((GBL.parmp)->dsm < 256)
|
||||
{ /* Small disk map merge routine */
|
||||
i = 16;
|
||||
do
|
||||
{
|
||||
if (*dp)
|
||||
{
|
||||
if (*fp)
|
||||
{
|
||||
if (*dp != *fp) goto badmerge;
|
||||
}
|
||||
else *fp = *dp;
|
||||
}
|
||||
else *dp = *fp;
|
||||
fp += 1;
|
||||
dp += 1;
|
||||
i -= 1;
|
||||
} while (i);
|
||||
}
|
||||
else
|
||||
{ /* Large disk map merge routine */
|
||||
i = 8;
|
||||
do
|
||||
{
|
||||
if (*(UWORD *)dp)
|
||||
{
|
||||
if (*(UWORD *)fp)
|
||||
{
|
||||
if (*(UWORD *)dp != *(UWORD *)fp) goto badmerge;
|
||||
}
|
||||
else *(UWORD *)fp = *(UWORD *)dp;
|
||||
}
|
||||
else *(UWORD *)dp = *(UWORD *)fp;
|
||||
fp += sizeof (UWORD);
|
||||
dp += sizeof (UWORD);
|
||||
i -= 1;
|
||||
} while (i);
|
||||
}
|
||||
/* Disk map merging complete */
|
||||
fcb_ext = calcext(fcbp); /* calc max extent for fcb */
|
||||
dir_ext = (UWORD)(dirp->extent) & 0x1f;
|
||||
if ( (fcb_ext > dir_ext) ||
|
||||
((fcb_ext == dir_ext) &&
|
||||
(UBWORD(fcbp->rcdcnt) > UBWORD(dirp->rcdcnt))) )
|
||||
/* if fcb points to larger file than dirp */
|
||||
{
|
||||
dirp->rcdcnt = fcbp->rcdcnt; /* set up rc, ext from fcb */
|
||||
dirp->extent = (BYTE)fcb_ext;
|
||||
}
|
||||
dirp->s1 = fcbp->s1;
|
||||
if ( (fcbp->ftype[robit]) & 0x80) error(5,fcbp);
|
||||
/* read-only file error */
|
||||
dirp->ftype[arbit] &= 0x7f; /* clear archive bit */
|
||||
dir_wr(dirindx >> 2);
|
||||
UNLOCK
|
||||
return(TRUE);
|
||||
|
||||
badmerge:
|
||||
UNLOCK
|
||||
ro_dsk |= (1 << GBL.curdsk);
|
||||
return(FALSE);
|
||||
}
|
||||
else return(FALSE);
|
||||
}
|
||||
|
||||
|
||||
/************************
|
||||
* close_fi entry point *
|
||||
************************/
|
||||
|
||||
UWORD close_fi(fcbp)
|
||||
|
||||
struct fcb *fcbp; /* pointer to fcb for file to close */
|
||||
{
|
||||
flushit(); /* first, flush the buffers */
|
||||
if ((fcbp->s2) & 0x80) return(0); /* if file write flag not on,
|
||||
don't need to do physical close */
|
||||
return( dirscan(close, fcbp, 0)); /* call dirscan with close function */
|
||||
}
|
||||
|
||||
|
||||
/************************
|
||||
* search entry point *
|
||||
************************/
|
||||
|
||||
/* First two functions for dirscan */
|
||||
|
||||
BOOLEAN alltrue(p1, p2, i) /* ARGSUSED */
|
||||
UBYTE *p1;
|
||||
UBYTE *p2;
|
||||
WORD i;
|
||||
{
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
BOOLEAN matchit(p1, p2, i) /* ARGSUSED */
|
||||
UBYTE *p1;
|
||||
UBYTE *p2;
|
||||
WORD i;
|
||||
{
|
||||
return(match(p1, p2, TRUE));
|
||||
}
|
||||
|
||||
|
||||
/* search entry point */
|
||||
UWORD search(fcbp, dsparm, p) /* ARGSUSED */
|
||||
|
||||
REG struct fcb *fcbp; /* pointer to fcb for file to search */
|
||||
REG UWORD dsparm; /* parameter to pass through to dirscan */
|
||||
UBYTE *p; /* pointer to pass through to tmp_sel */
|
||||
/* -- now unused -- */
|
||||
|
||||
{
|
||||
REG UWORD rtn; /* return value */
|
||||
BSETUP
|
||||
|
||||
if (fcbp->drvcode == '?')
|
||||
{
|
||||
rtn = dirscan(alltrue, fcbp, dsparm);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fcbp->extent != '?') fcbp->extent = 0;
|
||||
fcbp->s2 = 0;
|
||||
rtn = dirscan(matchit, fcbp, dsparm);
|
||||
}
|
||||
cpy_out( GBL.dirbufp, GBL.dmaadr, SECLEN);
|
||||
return(rtn);
|
||||
}
|
||||
|
||||
|
||||
/************************
|
||||
* create entry point *
|
||||
************************/
|
||||
|
||||
BOOLEAN create(fcbp, dirp, dirindx)
|
||||
|
||||
REG struct fcb *fcbp; /* pointer to fcb for file to create */
|
||||
REG struct dirent *dirp; /* pointer to directory entry */
|
||||
REG WORD dirindx; /* index into directory */
|
||||
|
||||
{
|
||||
REG BYTE *p;
|
||||
REG WORD i;
|
||||
REG BOOLEAN rtn;
|
||||
BSETUP
|
||||
|
||||
if ( rtn = ((dirp->entry) == 0xe5) )
|
||||
{
|
||||
p = &(fcbp->rcdcnt);
|
||||
i = 17;
|
||||
do
|
||||
{ /* clear fcb rcdcnt and disk map */
|
||||
*p++ = 0;
|
||||
i -= 1;
|
||||
} while (i);
|
||||
move(fcbp, dirp, sizeof *dirp); /* move the fcb to the directory */
|
||||
dir_wr(dirindx >> 2); /* write the directory sector */
|
||||
if ( dirindx > (GBL.dphp)->hiwater )
|
||||
(GBL.dphp)->hiwater = dirindx;
|
||||
crit_dsk |= 1 << (GBL.curdsk);
|
||||
}
|
||||
return(rtn);
|
||||
}
|
||||
|
||||
|
||||
/************************
|
||||
* delete entry point *
|
||||
************************/
|
||||
|
||||
BOOLEAN delete(fcbp, dirp, dirindx)
|
||||
|
||||
REG struct fcb *fcbp; /* pointer to fcb for file to delete */
|
||||
REG struct dirent *dirp; /* pointer to directory entry */
|
||||
REG WORD dirindx; /* index into directory */
|
||||
|
||||
{
|
||||
REG WORD i;
|
||||
REG BOOLEAN rtn;
|
||||
BSETUP
|
||||
|
||||
if ( rtn = match(fcbp, dirp, FALSE) )
|
||||
{
|
||||
if ( (dirp->ftype[robit]) & 0x80 ) error(5,fcbp);
|
||||
/* check for read-only file */
|
||||
dirp->entry = 0xe5;
|
||||
LOCK
|
||||
dir_wr(dirindx >> 2);
|
||||
/* Now free up the space in the allocation vector */
|
||||
if ((GBL.parmp)->dsm < 256)
|
||||
{
|
||||
i = 16;
|
||||
do clraloc(UBWORD(dirp->dskmap.small[--i]));
|
||||
while (i);
|
||||
}
|
||||
else
|
||||
{
|
||||
i = 8;
|
||||
do clraloc(swap(dirp->dskmap.big[--i]));
|
||||
while (i);
|
||||
}
|
||||
UNLOCK
|
||||
}
|
||||
return(rtn);
|
||||
}
|
||||
|
||||
|
||||
/************************
|
||||
* rename entry point *
|
||||
************************/
|
||||
|
||||
BOOLEAN rename(fcbp, dirp, dirindx)
|
||||
|
||||
REG struct fcb *fcbp; /* pointer to fcb for file to delete */
|
||||
REG struct dirent *dirp; /* pointer to directory entry */
|
||||
REG WORD dirindx; /* index into directory */
|
||||
|
||||
{
|
||||
REG UWORD i;
|
||||
REG BYTE *p; /* general purpose pointers */
|
||||
REG BYTE *q;
|
||||
REG BOOLEAN rtn;
|
||||
BSETUP
|
||||
|
||||
if ( rtn = match(fcbp, dirp, FALSE) )
|
||||
{
|
||||
if ( (dirp->ftype[robit]) & 0x80 ) error(5,fcbp);
|
||||
/* check for read-only file */
|
||||
p = &(fcbp->dskmap.small[1]);
|
||||
q = &(dirp->fname[0]);
|
||||
i = 11;
|
||||
do
|
||||
{
|
||||
*q++ = *p++ & 0x7f;
|
||||
i -= 1;
|
||||
} while (i);
|
||||
dir_wr(dirindx >> 2);
|
||||
}
|
||||
return(rtn);
|
||||
}
|
||||
|
||||
|
||||
/************************
|
||||
* set_attr entry point *
|
||||
************************/
|
||||
|
||||
BOOLEAN set_attr(fcbp, dirp, dirindx)
|
||||
|
||||
REG struct fcb *fcbp; /* pointer to fcb for file to delete */
|
||||
REG struct dirent *dirp; /* pointer to directory entry */
|
||||
REG WORD dirindx; /* index into directory */
|
||||
|
||||
{
|
||||
REG BOOLEAN rtn;
|
||||
BSETUP
|
||||
|
||||
if ( rtn = match(fcbp, dirp, FALSE) )
|
||||
{
|
||||
move(&fcbp->fname[0], &dirp->fname[0], 11);
|
||||
dir_wr(dirindx >> 2);
|
||||
}
|
||||
return(rtn);
|
||||
}
|
||||
|
||||
|
||||
/****************************
|
||||
* utility routine used by *
|
||||
* setran and getsize *
|
||||
****************************/
|
||||
|
||||
LONG extsize(fcbp)
|
||||
/* Return size of extent pointed to by fcbp */
|
||||
REG struct fcb *fcbp;
|
||||
|
||||
{
|
||||
return( ((LONG)(fcbp->extent & 0x1f) << 7)
|
||||
| ((LONG)(fcbp->s2 & 0x3f) << 12) );
|
||||
}
|
||||
|
||||
|
||||
/************************
|
||||
* setran entry point *
|
||||
************************/
|
||||
|
||||
setran(fcbp)
|
||||
|
||||
REG struct fcb *fcbp; /* pointer to fcb for file to set ran rec */
|
||||
|
||||
{
|
||||
struct
|
||||
{
|
||||
BYTE b3;
|
||||
BYTE b2;
|
||||
BYTE b1;
|
||||
BYTE b0;
|
||||
};
|
||||
LONG random;
|
||||
|
||||
random = (LONG)UBWORD(fcbp->cur_rec) + extsize(fcbp);
|
||||
/* compute random record field */
|
||||
fcbp->ran0 = random.b2;
|
||||
fcbp->ran1 = random.b1;
|
||||
fcbp->ran2 = random.b0;
|
||||
}
|
||||
|
||||
|
||||
/**********************************/
|
||||
/* fsize is a funtion for dirscan */
|
||||
/* passed from getsize */
|
||||
/**********************************/
|
||||
|
||||
BOOLEAN fsize(fcbp, dirp, dirindx) /* ARGSUSED */
|
||||
|
||||
REG struct fcb *fcbp; /* pointer to fcb for file to delete */
|
||||
REG struct dirent *dirp; /* pointer to directory entry */
|
||||
WORD dirindx; /* index into directory */
|
||||
|
||||
{
|
||||
REG BOOLEAN rtn;
|
||||
struct
|
||||
{
|
||||
BYTE b3;
|
||||
BYTE b2;
|
||||
BYTE b1;
|
||||
BYTE b0;
|
||||
};
|
||||
LONG temp;
|
||||
|
||||
if ( rtn = match(fcbp, dirp, FALSE) )
|
||||
{
|
||||
temp = (LONG)UBWORD(dirp->rcdcnt) + extsize(dirp);
|
||||
/* compute file size */
|
||||
fcbp->ran0 = temp.b2;
|
||||
fcbp->ran1 = temp.b1;
|
||||
fcbp->ran2 = temp.b0;
|
||||
}
|
||||
return(rtn);
|
||||
}
|
||||
|
||||
/************************
|
||||
* getsize entry point *
|
||||
************************/
|
||||
|
||||
getsize(fcbp)
|
||||
/* get file size */
|
||||
REG struct fcb *fcbp; /* pointer to fcb to get file size for */
|
||||
|
||||
{
|
||||
LONG maxrcd;
|
||||
LONG temp;
|
||||
REG WORD dsparm;
|
||||
struct
|
||||
{
|
||||
BYTE b3;
|
||||
BYTE b2;
|
||||
BYTE b1;
|
||||
BYTE b0;
|
||||
};
|
||||
|
||||
maxrcd = 0;
|
||||
dsparm = 0;
|
||||
temp = 0;
|
||||
while ( dirscan(fsize, fcbp, dsparm) < 255 )
|
||||
{ /* loop until no more matches */
|
||||
temp.b2 = fcbp->ran0;
|
||||
temp.b1 = fcbp->ran1;
|
||||
temp.b0 = fcbp->ran2;
|
||||
if (temp > maxrcd) maxrcd = temp;
|
||||
dsparm = 1;
|
||||
}
|
||||
fcbp->ran0 = maxrcd.b2;
|
||||
fcbp->ran1 = maxrcd.b1;
|
||||
fcbp->ran2 = maxrcd.b0;
|
||||
}
|
||||
|
||||
|
||||
/************************
|
||||
* free_sp entry point *
|
||||
************************/
|
||||
|
||||
free_sp(dsknum)
|
||||
|
||||
UBYTE dsknum; /* disk number to get free space of */
|
||||
{
|
||||
LONG records;
|
||||
REG UWORD *alvec;
|
||||
REG UWORD bitmask;
|
||||
REG UWORD alvword;
|
||||
REG WORD i;
|
||||
BSETUP
|
||||
|
||||
seldsk(dsknum); /* select the disk */
|
||||
records = (LONG)0; /* initialize the variables */
|
||||
alvec = (GBL.dphp)->alv;
|
||||
bitmask = 0;
|
||||
for (i = 0; i <= (GBL.parmp)->dsm; i++) /* for loop to compute */
|
||||
{
|
||||
if ( ! bitmask)
|
||||
{
|
||||
bitmask = 0x8000;
|
||||
alvword = ~(*alvec++);
|
||||
}
|
||||
if ( alvword & bitmask)
|
||||
records += (LONG)( ((GBL.parmp)->blm) + 1 );
|
||||
bitmask >>= 1;
|
||||
}
|
||||
/* move # records to DMA address */
|
||||
cpy_out(&records, GBL.dmaadr, sizeof records);
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
|
||||
/****************************************************************
|
||||
* *
|
||||
* P-CP/M BDOS Disk I/O System Module *
|
||||
* *
|
||||
* This module translates from the packet oriented I/O *
|
||||
* passed from the other BDOS modules into BIOS calls. *
|
||||
* *
|
||||
* It includes only one external entry point: *
|
||||
* do_phio() - do physical i/o *
|
||||
* *
|
||||
* *
|
||||
* Modified for memory management on Z8000 *
|
||||
* *
|
||||
****************************************************************/
|
||||
|
||||
#include "stdio.h" /* Standard I/O declarations */
|
||||
|
||||
#include "bdosdef.h" /* Type and structure declarations for BDOS */
|
||||
|
||||
#include "pktio.h" /* Packet I/O definitions */
|
||||
|
||||
#include "biosdef.h" /* Declarations for BIOS entry points */
|
||||
|
||||
EXTERN udiv(); /* Assembly language unsigned divide routine */
|
||||
/* in bdosif.s. It's used because Alcyon C */
|
||||
/* can't do / or % without an external */
|
||||
|
||||
/************************
|
||||
* do_phio entry point *
|
||||
************************/
|
||||
|
||||
UWORD do_phio(iop)
|
||||
|
||||
REG struct iopb *iop; /* iop is a pointer to a i/o parameter block */
|
||||
|
||||
{
|
||||
MLOCAL UBYTE last_dsk; /* static variable to tell which disk
|
||||
was last used, to avoid disk selects */
|
||||
REG struct dph *hdrp; /* pointer to disk parameter header */
|
||||
REG struct dpb *dparmp; /* pointer to disk parameter block */
|
||||
REG UWORD rtn; /* return parameter */
|
||||
UWORD iosect; /* sector number returned from divide rtn */
|
||||
|
||||
LOCK /* lock the disk system while doing physical i/o */
|
||||
|
||||
rtn = 0;
|
||||
switch (iop->iofcn)
|
||||
{
|
||||
case sel_info:
|
||||
last_dsk = iop->devnum;
|
||||
iop->infop = bseldsk(last_dsk, iop->ioflags);
|
||||
break;
|
||||
|
||||
case read:
|
||||
case write:
|
||||
if (last_dsk != iop->devnum)
|
||||
bseldsk((last_dsk = iop->devnum), 0);
|
||||
/* guaranteed disk is logged on, because temp_sel in
|
||||
BDOSMAIN does it */
|
||||
hdrp = iop->infop;
|
||||
dparmp = hdrp->dpbp;
|
||||
|
||||
bsettrk( udiv( iop->devadr, dparmp->spt, (long)&iosect )
|
||||
+ dparmp->trk_off );
|
||||
bsetsec( bsectrn( iosect, hdrp->xlt ) );
|
||||
bsetdma(iop->xferadr);
|
||||
if ((iop->iofcn) == read) rtn = bread();
|
||||
else rtn = bwrite(iop->ioflags);
|
||||
break;
|
||||
|
||||
case flush:
|
||||
rtn = bflush();
|
||||
}
|
||||
|
||||
UNLOCK
|
||||
return(rtn);
|
||||
}
|
@@ -0,0 +1,482 @@
|
||||
/****************************************************************
|
||||
* *
|
||||
* CP/M-8000 BDOS Program Loader *
|
||||
* *
|
||||
* Copyright (c) 1982 Zilog Incorporated *
|
||||
* *
|
||||
* This function implements BDOS call 59: Program Load. *
|
||||
* The single parameter passed is a pointer to a space *
|
||||
* where a partially filled LPB (Load Parameter Block) *
|
||||
* can be found. pgmld must fill in the base page *
|
||||
* address and the starting user stack pointer. In *
|
||||
* addition, the Z8000 implementation will set a loader *
|
||||
* flag if the program being loaded uses separate I/D *
|
||||
* space or segmentation. *
|
||||
* *
|
||||
* NOTE! unlike the usual CP/M loader, the Z8000 loader *
|
||||
* returns the actual starting address of the code segment *
|
||||
* (starting PC) in the LPB, clobbering the program load *
|
||||
* address. This is because the segment containing the *
|
||||
* code may not be known until load time. *
|
||||
* *
|
||||
****************************************************************/
|
||||
|
||||
#include "stdio.h" /* Standard declarations for BDOS, BIOS */
|
||||
|
||||
#include "bdosdef.h" /* Type and structure declarations for BDOS */
|
||||
|
||||
#include "biosdef.h" /* Declarations of BIOS functions */
|
||||
|
||||
#include "basepage.h" /* Base page structure */
|
||||
|
||||
#include "x.out.h" /* structure of x.out (".Z8[KS] file") */
|
||||
|
||||
#define SPLIT 0x4000 /* Separate I/D flag for LPB */
|
||||
#define SEG 0x2000 /* Segmented code flag for TPA */
|
||||
#define NSEG 16 /* Maximum number of x.out segments */
|
||||
#define SEGLEN 0x10000 /* Length of a Z8000 segment */
|
||||
/* Address of basepage (near top of TPA)*/
|
||||
#define BPLEN (sizeof (struct b_page))
|
||||
#define DEFSTACK 0x100 /* Default stack length */
|
||||
#define NREGIONS 2 /* Number of regions in the MRT */
|
||||
|
||||
/* return values */
|
||||
|
||||
#define GOOD 0 /* good return value */
|
||||
#define BADHDR 1 /* bad header */
|
||||
#define NOMEM 2 /* not enough memory */
|
||||
#define READERR 3 /* read error */
|
||||
|
||||
#define MYDATA 0 /* Argument for map_adr */
|
||||
#define TPAPROG 5 /* Argument for map_adr */
|
||||
#define TPADATA 4 /* Argument for map_adr */
|
||||
/* Get actual code segment (as opposed */
|
||||
/* to segment where it can be accessed*/
|
||||
/* as data) */
|
||||
#define TRUE_TPAPROG (TPAPROG | 0x100)
|
||||
|
||||
struct lpb /* Load Parameter Block */
|
||||
{
|
||||
XADDR fcbaddr;/* Address of fcb of opened file */
|
||||
XADDR pgldaddr;/* Low address of prog load area */
|
||||
XADDR pgtop; /* High address of prog load area, +1 */
|
||||
XADDR bpaddr; /* Address of basepage; return value */
|
||||
XADDR stackptr;/* Stack ptr of user; return value */
|
||||
short flags; /* Loader control flags; return value */
|
||||
} mylpb;
|
||||
|
||||
struct ustack /* User's initial stack - nonsegmented */
|
||||
{
|
||||
short two; /* "Return address" (actually address */
|
||||
/* of warm boot call in user's startup)*/
|
||||
short bpoffset;/* Pointer to basepage */
|
||||
};
|
||||
|
||||
struct sstack /* User's initial stack - segmented */
|
||||
{
|
||||
XADDR stwo; /* "Return address" (actually address */
|
||||
/* of warm boot call in user's startup)*/
|
||||
XADDR sbpadr; /* Pointer to basepage */
|
||||
};
|
||||
|
||||
|
||||
struct m_rt { /* The Memory Region Table */
|
||||
int count;
|
||||
struct {
|
||||
XADDR tpalow;
|
||||
XADDR tpalen;
|
||||
} m_reg[NREGIONS];
|
||||
};
|
||||
|
||||
#define SPREG 1 /* The MRT region for split I/D programs */
|
||||
#define NSPREG 0 /* The MRT region for non-split programs */
|
||||
#define SDREG 2 /* The MRT region for split I/D data */
|
||||
#define NSDREG 0 /* The MRT region for non-split data */
|
||||
|
||||
#define READ 20 /* Read Sequential BDOS call */
|
||||
#define SETDMA 26 /* Set DMA Address BDOS call */
|
||||
extern UWORD bdos(); /* To do I/O into myself (note this */
|
||||
/* function does not map 2nd param - */
|
||||
/* see mbdos macro below) */
|
||||
|
||||
static XADDR textloc, /* Physical locations of pgm sections. */
|
||||
dataloc,
|
||||
bssloc,
|
||||
stkloc;
|
||||
|
||||
static XADDR textsiz, /* Sizes of the various sections. */
|
||||
datasiz,
|
||||
bsssiz,
|
||||
stksiz;
|
||||
|
||||
static UWORD split, /* Tells if split I/D or not */
|
||||
seg; /* Tells if segmented or not */
|
||||
|
||||
static char *gp; /* Buffer pointer for char input */
|
||||
static char *mydma; /* Local address of read buffer */
|
||||
|
||||
struct x_hdr x_hdr; /* Object File Header structure */
|
||||
struct x_sg x_sg[NSEG]; /* Segment Header structure */
|
||||
|
||||
static XADDR segsiz[NSEG]; /* Segment lengths */
|
||||
static XADDR seglim[NSEG]; /* Segment length limits */
|
||||
static XADDR segloc[NSEG]; /* Segment base physical addresses */
|
||||
|
||||
static short textseg, /* Logical seg # of various segments */
|
||||
dataseg,
|
||||
bssseg,
|
||||
stkseg;
|
||||
|
||||
|
||||
/********************************/
|
||||
/* */
|
||||
/* Start of pgmld function */
|
||||
/* */
|
||||
/********************************/
|
||||
|
||||
UWORD pgmld(xlpbp) /* Load a program from LPB info */
|
||||
XADDR xlpbp;
|
||||
{ register int i,j; /* Temporary counters etc. */
|
||||
struct m_rt *mrp; /* Pointer to a MRT structure */
|
||||
char mybuf[SECLEN]; /* Local buffer for file reading*/
|
||||
|
||||
/* get local LPB copy */
|
||||
cpy_in(xlpbp, &mylpb, (long) sizeof mylpb);
|
||||
|
||||
mydma = mybuf; /* Initialize addr for local DMA*/
|
||||
gp = &mybuf[SECLEN]; /* Point beyond end of buffer */
|
||||
|
||||
mrp = (struct m_rt *) bgetseg();/* Get address of memory region */
|
||||
/* table (note segment # lost)*/
|
||||
if (readhdr() == EOF) /* Get x.out file header */
|
||||
return (READERR); /* Read error on header */
|
||||
|
||||
switch (x_hdr.x_magic) /* Is this acceptable x.out file*/
|
||||
{
|
||||
case X_NXN_MAGIC: /* Non-seg, combined I & D */
|
||||
split = FALSE;
|
||||
seg = FALSE;
|
||||
break;
|
||||
|
||||
case X_NXI_MAGIC: /* Non-seg, separate I & D */
|
||||
split = SPLIT;
|
||||
seg = FALSE;
|
||||
break;
|
||||
|
||||
case X_SX_MAGIC: /* Segmented - must be combined */
|
||||
split = FALSE;
|
||||
seg = SEG;
|
||||
break;
|
||||
|
||||
default:
|
||||
return (BADHDR); /* Sorry, can't load it! */
|
||||
}
|
||||
|
||||
/* Set the user space segment number, from the low address in the */
|
||||
/* appropriate entry of the MRT. */
|
||||
/* m_reg[SPREG] is the region used for split I/D programs in the MRT */
|
||||
/* m_reg[NSPREG] is used for non-split. */
|
||||
/* -1 is used for segmented */
|
||||
|
||||
/* NOTE -- the tpa limits passed in the LPB are ignored. This is */
|
||||
/* incorrect, but saves the caller from having to look at the */
|
||||
/* load module to determine the magic number. */
|
||||
|
||||
map_adr(seg ? -1L
|
||||
: (mrp->m_reg[split ? SPREG : NSPREG].tpalow),
|
||||
0xffff);
|
||||
|
||||
for (i = 0; i < x_hdr.x_nseg; i++) { /* For each segment... */
|
||||
if( readxsg(i) == EOF) /* ...get segment hdr */
|
||||
return(READERR);
|
||||
seglim[i] = SEGLEN; /* ...set max length */
|
||||
segsiz[i] = 0L; /* ...and current size */
|
||||
}
|
||||
|
||||
/* Set section base addresses */
|
||||
|
||||
textloc = dataloc = bssloc = stkloc = 0L;
|
||||
|
||||
/* Zero section sizes */
|
||||
textsiz = datasiz = bsssiz = 0L;
|
||||
stksiz = DEFSTACK;
|
||||
|
||||
|
||||
if (seg) { /* Locate text & data segments */
|
||||
/* if segmented we know nothing */
|
||||
textseg = dataseg = bssseg = stkseg = 0;
|
||||
} else { /* if nonsegmented ... */
|
||||
/* assign segment numbers */
|
||||
textseg = 0;
|
||||
dataseg = (split) ? 1 : 0;
|
||||
stkseg = bssseg = dataseg;
|
||||
|
||||
/* assign locations */
|
||||
segloc[textseg] = map_adr(0L, TPAPROG);
|
||||
if (split)
|
||||
segloc[dataseg] = map_adr(0L, TPADATA);
|
||||
|
||||
/* Assign limits */
|
||||
seglim[textseg] = SEGLEN;
|
||||
seglim[dataseg] = mrp->m_reg[split ? SDREG : NSDREG].tpalen
|
||||
- BPLEN - stksiz;
|
||||
|
||||
/* Assign stack location */
|
||||
stkloc = segloc[dataseg] + seglim[dataseg] + stksiz;
|
||||
}
|
||||
|
||||
for (i = 0; i < x_hdr.x_nseg; i++) /* For each segment... */
|
||||
if( (j = loadseg(i)) != GOOD) /* ...load memory. If */
|
||||
return (j); /* error return, pass */
|
||||
/* it back. */
|
||||
setbase(setaddr(&mylpb)); /* Set addresses in LPB,*/
|
||||
/* Set up base page */
|
||||
cpy_out((XADDR) &mylpb, xlpbp, sizeof mylpb);
|
||||
return (GOOD);
|
||||
}
|
||||
|
||||
|
||||
/* Macro to call BDOS. First parameter is passed unchanged, second */
|
||||
/* is cast into an XADR, then mapped to caller data space. */
|
||||
|
||||
#define mbdos(func, param) (bdos((func), map_adr((XADDR) (param), MYDATA)))
|
||||
|
||||
|
||||
/* Macro to read the next character from the input file (much faster */
|
||||
/* than having to make a function call for each byte) */
|
||||
|
||||
#define fgetch() ((gp<mydma+SECLEN) ? (int)*gp++&0xff : fillbuff())
|
||||
|
||||
|
||||
/* Routine to fill input buffer when fgetch macro detects it is empty */
|
||||
|
||||
|
||||
int fillbuf() /* Returns first char in buffer */
|
||||
{ /* or EOF if read fails */
|
||||
/* Set up address to read into */
|
||||
mbdos(SETDMA, mydma);
|
||||
if (bdos(READ, mylpb.fcbaddr) != 0) /* Have BDOS do the read*/
|
||||
return (EOF);
|
||||
gp = mydma; /* Initialize buffer pointer */
|
||||
return ((int)*gp++ & 0xff); /* Return first character */
|
||||
}
|
||||
|
||||
/* Routine to read the file header */
|
||||
|
||||
int readhdr()
|
||||
{
|
||||
register int n, k;
|
||||
register char *p;
|
||||
|
||||
p = (char *) &x_hdr;
|
||||
for (n = 0; n < sizeof (struct x_hdr); n++) {
|
||||
if( (k = fgetch()) == EOF)
|
||||
return (k);
|
||||
*p++ = (char) k;
|
||||
}
|
||||
return (GOOD);
|
||||
}
|
||||
|
||||
/* Routine to read the header for segment i */
|
||||
|
||||
int readxsg(i)
|
||||
int i;
|
||||
{
|
||||
register int n, k;
|
||||
register char *p;
|
||||
|
||||
p = (char *) &x_sg[i];
|
||||
for(n = 0; n < sizeof (struct x_sg); n++) {
|
||||
if ( (k = fgetch()) == EOF)
|
||||
return (READERR);
|
||||
*p++ = (char) k;
|
||||
}
|
||||
return (GOOD);
|
||||
}
|
||||
|
||||
/* Routine to load segment number i */
|
||||
/* This assumes that the segments occur in load order in the file, and */
|
||||
/* that all initialized data and, in the case of combined I/D programs, */
|
||||
/* text segments, precede all bss segments. */
|
||||
|
||||
/* In the case of segmented programs, the stack segment must exist, */
|
||||
/* and all segments are presumed to be of maximum length. */
|
||||
/* Text, data, bss, and stack lengths are sum of lengths of all such */
|
||||
/* segments, and so may be bigger than maximum segment length. */
|
||||
|
||||
int loadseg(i)
|
||||
int i;
|
||||
{
|
||||
register UWORD l, length; /* Total, incremental length */
|
||||
register int type; /* Type of segment loaded */
|
||||
register short lseg; /* logical segment index */
|
||||
register XADDR phystarg; /* physical target load address */
|
||||
|
||||
l = x_sg[i].x_sg_len; /* number of bytes to load */
|
||||
type = x_sg[i].x_sg_typ; /* Type of segment */
|
||||
|
||||
lseg = textseg; /* try putting in text space */
|
||||
|
||||
if (split) { /* If separate I/D, this may */
|
||||
switch (type) /* be a bad guess */
|
||||
{
|
||||
case X_SG_CON: /* Separate I/D: all data goes */
|
||||
case X_SG_DATA: /* in data space */
|
||||
case X_SG_BSS:
|
||||
case X_SG_STK:
|
||||
lseg = dataseg;
|
||||
}
|
||||
}
|
||||
|
||||
if (seg) { /* If segmented, compute phys. */
|
||||
/* address of segment */
|
||||
|
||||
/* search to see if seg was used already */
|
||||
/* if so, use the same logical segment index. */
|
||||
/* (if not, loop ends with lseg == i) */
|
||||
|
||||
for (lseg = 0;
|
||||
x_sg[lseg].x_sg_no != x_sg[i].x_sg_no;
|
||||
lseg++) ;
|
||||
|
||||
segloc[lseg] = ((long)x_sg[i].x_sg_no) << 24;
|
||||
}
|
||||
|
||||
phystarg = segloc[lseg] + segsiz[lseg]; /* physical target addr */
|
||||
|
||||
switch (type) /* Now load data, if necessary */
|
||||
/* save physical address & size */
|
||||
{
|
||||
case X_SG_BSS: /* BSS gets cleared by runtime */
|
||||
/* startup. */
|
||||
stkloc = (phystarg & 0xffff0000L) + SEGLEN - BPLEN - stksiz;
|
||||
/* ...in case no stack segment */
|
||||
if (bssloc == 0L) bssloc = phystarg;
|
||||
bsssiz += l;
|
||||
if ((segsiz[lseg] += l) >= seglim[lseg])
|
||||
return (NOMEM);
|
||||
return (GOOD); /* Transfer no data */
|
||||
|
||||
case X_SG_STK: /* Stack segment: */
|
||||
if (stkloc == 0L) { /* if segmented, we now */
|
||||
/* know where to put */
|
||||
seglim[lseg] -= BPLEN; /* the base page */
|
||||
stkloc = segloc[lseg] + seglim[lseg];
|
||||
}
|
||||
|
||||
stkseg = lseg;
|
||||
stksiz += l; /* adjust size and */
|
||||
seglim[lseg] -= l; /* memory limit */
|
||||
if (segsiz[lseg] >= seglim[lseg])
|
||||
return (NOMEM);
|
||||
return (GOOD); /* Transfer no data */
|
||||
|
||||
case X_SG_COD: /* Pure text segment */
|
||||
case X_SG_MXU: /* Dirty code/data (better not)*/
|
||||
case X_SG_MXP: /* Clean code/data (be sep I/D)*/
|
||||
if (textloc == 0L) textloc = phystarg;
|
||||
textsiz += l;
|
||||
break;
|
||||
|
||||
case X_SG_CON: /* Constant (clean) data */
|
||||
case X_SG_DAT: /* Dirty data */
|
||||
stkloc = (phystarg & 0xffff0000L) + SEGLEN - BPLEN - stksiz;
|
||||
/* ...in case no stack or */
|
||||
/* bss segments */
|
||||
if (dataloc == 0L) dataloc = segloc[i];
|
||||
datasiz += l;
|
||||
break;
|
||||
}
|
||||
/* Check seg overflow */
|
||||
if ((segsiz[lseg] += l) >= seglim[lseg])
|
||||
return (NOMEM);
|
||||
/* load data from file */
|
||||
|
||||
/* Following loop is optimized for load speed. It knows*/
|
||||
/* about three conditions for data transfer: */
|
||||
/* 1. Data in read buffer: */
|
||||
/* Transfer data from read buffer to target */
|
||||
/* 2. Read buffer empty and more than 1 sector of data */
|
||||
/* remaining to load: */
|
||||
/* Read data direct to target */
|
||||
/* 3. Read buffer empty and less than 1 sector of data */
|
||||
/* remaining to load: */
|
||||
/* Fill read buffer, then proceed as in 1 above */
|
||||
|
||||
while (l) /* Until all loaded */
|
||||
{ /* Data in disk buffer? */
|
||||
if (gp < mydma + SECLEN)
|
||||
{
|
||||
length = min(l, mydma + SECLEN - gp);
|
||||
cpy_out(gp, phystarg, length);
|
||||
gp += length;
|
||||
}
|
||||
else if (l < SECLEN) /* Less than 1 sector */
|
||||
{ /* remains to transfer*/
|
||||
length = 0;
|
||||
mbdos(SETDMA, mydma);
|
||||
fillbuf();
|
||||
gp = mydma;
|
||||
}
|
||||
else /* Read full sector */
|
||||
{ /* into target space */
|
||||
length = SECLEN;
|
||||
bdos(SETDMA, phystarg);
|
||||
bdos(READ, mylpb.fcbaddr);
|
||||
}
|
||||
|
||||
phystarg += length;
|
||||
l -= length;
|
||||
}
|
||||
|
||||
return (GOOD);
|
||||
}
|
||||
|
||||
/* Routine to set the addresses in the Load Parameter Block */
|
||||
/* Unlike normal CP/M, the original load address is replaced on return */
|
||||
/* by the actual starting address of the program (true Code-space addr) */
|
||||
|
||||
int setaddr(lpbp)
|
||||
struct lpb *lpbp;
|
||||
{
|
||||
register int space;
|
||||
|
||||
space = (split) ? TPADATA : TPAPROG;
|
||||
lpbp->pgldaddr = (seg) ? textloc : map_adr(textloc, TRUE_TPAPROG);
|
||||
lpbp->bpaddr = stkloc;
|
||||
lpbp->stackptr = stkloc - (seg? sizeof (struct sstack)
|
||||
: sizeof (struct ustack));
|
||||
lpbp->flags = split | seg;
|
||||
return (space);
|
||||
}
|
||||
|
||||
/* Routine to set up the base page. The parameter indicates whether
|
||||
* the data and bss should be mapped in code space or in data space.
|
||||
*/
|
||||
|
||||
VOID setbase(space)
|
||||
int space;
|
||||
{
|
||||
struct b_page bp;
|
||||
|
||||
if (seg) {
|
||||
bp.lcode = textloc;
|
||||
bp.ltpa = 0L;
|
||||
} else {
|
||||
bp.lcode = bp.ltpa = map_adr(textloc, TRUE_TPAPROG);
|
||||
}
|
||||
|
||||
bp.htpa = mylpb.stackptr; /* htpa is where the stack is */
|
||||
bp.codelen = textsiz;
|
||||
|
||||
bp.ldata = dataloc;
|
||||
bp.datalen = datasiz;
|
||||
|
||||
if (bssloc == 0L) bssloc = dataloc + datasiz;
|
||||
bp.lbss = bssloc;
|
||||
bp.bsslen = bsssiz;
|
||||
|
||||
bp.freelen = seglim[bssseg] - segsiz[bssseg];
|
||||
|
||||
cpy_out(&bp, map_adr((long) stkloc, space), sizeof bp);
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
/********************************************************
|
||||
* *
|
||||
* P-CP/M header file *
|
||||
* Copyright (c) 1982 by Digital Research, Inc. *
|
||||
* Structure definitions for doing I/O in packets *
|
||||
* *
|
||||
* Modified for data in other address space *
|
||||
* *
|
||||
********************************************************/
|
||||
|
||||
/* May use this information structure instead of disk parameter header and
|
||||
disk parameter block in future, but for now it's unused
|
||||
struct dskinfo
|
||||
{
|
||||
UBYTE *dbuffp;
|
||||
UBYTE *csv;
|
||||
UBYTE *alv;
|
||||
UBYTE blksize;
|
||||
UBYTE didummy;
|
||||
UWORD dskmax;
|
||||
UWORD dirmax;
|
||||
UWORD chksize;
|
||||
};
|
||||
*/
|
||||
|
||||
struct iopb
|
||||
{
|
||||
UBYTE iofcn; /* function number, see defines below */
|
||||
UBYTE ioflags; /* used for login flag and write flag */
|
||||
UBYTE devtype; /* device type, see defines below */
|
||||
/* currently unused */
|
||||
UBYTE devnum; /* device number, or, devtype and devnum
|
||||
taken together form int device number */
|
||||
LONG devadr; /* item nmbr on device to start xfer at */
|
||||
/* note -- item is sector for disks, byte for char devs */
|
||||
UWORD xferlen; /* number items to transfer */
|
||||
XADDR xferadr; /* memory address to xfer to/from */
|
||||
struct dph *infop; /* pointer to disk parameter header */
|
||||
/* return parm for fcn 0, input for rest */
|
||||
};
|
||||
|
||||
|
||||
/* Definitions for iofcn, the function number */
|
||||
#define sel_info 0 /* select and return info on device */
|
||||
#define read 1
|
||||
#define write 2
|
||||
#define flush 3
|
||||
#define status 4 /* not currently used */
|
||||
|
||||
|
||||
/* Definitions for devtype, the device type */
|
||||
/* This field not currently used */
|
||||
#define console 0
|
||||
#define printer 1
|
||||
#define disk 2
|
||||
#define memory 3 /* gets TPA boundaries */
|
||||
#define redir 4 /* read/write IOByte */
|
||||
#define exc_vec 5 /* set exception vector */
|
||||
|
@@ -0,0 +1,72 @@
|
||||
/*****************************************************************************
|
||||
*
|
||||
* C P / M C H E A D E R F I L E
|
||||
* -----------------------------------
|
||||
* Copyright 1982 by Digital Research Inc. All rights reserved.
|
||||
*
|
||||
* This is an include file for assisting the user to write portable
|
||||
* programs for C.
|
||||
*
|
||||
*****************************************************************************/
|
||||
#define ALCYON 1 /* NOT using Alcyon compiler*/
|
||||
/* but Zilog has similar bug*/
|
||||
|
||||
/*
|
||||
* Standard type definitions
|
||||
*/
|
||||
/***************************/
|
||||
#define BYTE char /* Signed byte */
|
||||
#define BOOLEAN char /* 2 valued (true/false) */
|
||||
#define WORD short /* Signed word (16 bits) */
|
||||
#define UWORD unsigned int /* unsigned word */
|
||||
#define LONG long /* signed long (32 bits) */
|
||||
#define ULONG unsigned long /* Unsigned long */
|
||||
#define REG register /* register variable */
|
||||
#define LOCAL auto /* Local var on 68000 */
|
||||
#define EXTERN extern /* External variable */
|
||||
#define MLOCAL static /* Local to module */
|
||||
#define GLOBAL /**/ /* Global variable */
|
||||
#define VOID /**/ /* Void function return */
|
||||
#define XADDR long /* Extended (SEG) address */
|
||||
/***************************/
|
||||
#ifdef ALCYON
|
||||
#define UBYTE char
|
||||
#define UBWORD(a) ((UWORD)a & 0xff)
|
||||
/* Unsigned byte to word cast */
|
||||
#else
|
||||
#define UBYTE unsigned char /* Unsigned byte */
|
||||
#define UBWORD(a) (UWORD)a
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/****************************************************************************/
|
||||
/* Miscellaneous Definitions: */
|
||||
/****************************************************************************/
|
||||
|
||||
#define FAILURE (-1) /* Function failure return val */
|
||||
#define SUCCESS (0) /* Function success return val */
|
||||
#define YES 1 /* "TRUE" */
|
||||
#define NO 0 /* "FALSE" */
|
||||
#define FOREVER for(;;) /* Infinite loop declaration */
|
||||
#define NULL (BYTE *)0 /* Null pointer value */
|
||||
#define XNULL 0L /* Null XADDR */
|
||||
#define EOF (-1) /* EOF Value */
|
||||
#define TRUE (1)
|
||||
/* Function TRUE value */
|
||||
#define FALSE (0)
|
||||
/* Function FALSE value */
|
||||
|
||||
|
||||
/****************************************************************************/
|
||||
/* */
|
||||
/* M A C R O S */
|
||||
/* ----------- */
|
||||
/* */
|
||||
/* Define some stuff as macros .... */
|
||||
/* */
|
||||
/****************************************************************************/
|
||||
|
||||
#define abs(x) ((x) < 0 ? -(xIq
|
||||
This directory contains the source and object files for the Z8000 CP/M Bios,
|
||||
and the necessary include files. This includes the floating point emulator
|
Reference in New Issue
Block a user