Files
Sepp J Morris 31738079c4 Upload
Digital Research
2020-11-06 18:50:37 +01:00

1226 lines
27 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Since this version of the BIOS interfaces with the host OS, we need
* host includes.
*/
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
typedef struct bios_dpb_s {
unsigned short int spt; /* sectors per track */
unsigned char bsh; /* block shift factor */
unsigned char blm; /* block mask */
unsigned char exm; /* extent mask */
unsigned char reserved1;
unsigned short int dsm; /* number of blocks in disk */
unsigned short int drm; /* number of directory entries */
unsigned short int reserved2; /* initial allocation vector in -80 */
unsigned short int cks; /* size of checksum vector */
unsigned short int off; /* reserved tracks */
} bios_dpb_t;
typedef struct bios_dph_s {
unsigned short *xlt; /* sector translation table address */
unsigned short int scratch[ 3 ]; /* scratch words used by BIOS */
unsigned char *dirbuf; /* scratch sector for directory use */
bios_dpb_t *dpb; /* address of disk parameter block */
unsigned char *csv; /* checksum vector */
unsigned char *alv; /* allocation vector */
} bios_dph_t;
typedef struct bios_mrt_s {
unsigned short int count; /* number of regions in table */
void *base; /* base address of first region */
unsigned long int length; /* length of first region */
} bios_mrt_t;
unsigned short int bios_flush( void );
/***
*
* P112 3.5" diskette
*
* dw 72 ;CP/M "sectors"/track
* db 4 ;Block shift
* db 15 ;Block mask
* db 0 ;Extent mask
* dw 715-1 ;Max. allocn. block no.
* dw 128-1 ;No. of directory entries -1
* db 11000000B ;Bit-map for allocn. blocks
* db 00000000B ; used for directory
* dw 32 ;No. of bytes in dir. check buffer
* dw 1 ;No. of tracks before directory
*
***/
bios_dpb_t bios_p112_dpb = {
72, /* 128-byte sectors per track */
4, /* block shift factor */
15, /* block mask */
0, /* extent mask */
0, /* reserved */
714, /* 2048-byte blcoks per disk - 1 */
127, /* number of directory entries - 1 */
0, /* reserved */
32, /* size of check vector */
1 /* reserved tracks */
};
/***
*
* 8" SSSD standard diskette
*
***/
bios_dpb_t bios_8inch_dpb = {
26, /* 128-byte sectors per track */
3, /* block shift */
7, /* block mask */
0, /* extent mask */
0, /* reserved */
242, /* allocation blocks per disk - 1 */
63, /* number of directory entries - 1 */
0, /* reserved; initial allocation vector in CP/M-80 */
16, /* size of check vector */
2 /* track offset */
};
unsigned char bios_dirbuf[ 128 ];
/***
*
* Check vector
*
* The check vector contains one byte for each directory entry.
*
* The directory may occupy up to 16 allocation blocks (at least on
* CP/M-80; I don't know what the limit is on CP/M-68K), because
* directory size is constrained by the fact that there are only
* 16 bits available for the initial allocation vector. The
* largest allocation block is 16K. Since each sector contains
* four directory entries and there are 8 sectors in a K, a
* 16K allocation block holds 16*8*4 = 512 directory directory
* entries; each directory block may therefore require up to
* 512 bytes of check vector space. Since there can be up to
* 16 directory blocks, the wort case check vector occupies
* 8K bytes.
*
* NOTE: This is assuming that CP/M-68K is, like CP/M-80,
* limited to reserving up to 16 allocation blocks for
* the directory!
*
***/
unsigned char bios_drivea_csv[ 8192 ];
/***
*
* Allocation vector
*
* Each allocation block requires one bit in the allocation vector.
* Since there are at most 64K allocation blocks, the allocation
* vector may occupy up to 8K bytes.
*
***/
unsigned char bios_drivea_alv[ 8192 ];
/***
*
* P112 3.5" DPH
*
***/
bios_dph_t bios_p112_dph = {
0, /* sector translation table */
0, 0, 0, /* scratch words */
bios_dirbuf, /* Directory buffer */
&bios_p112_dpb, /* disk parameter block */
bios_drivea_csv, /* checksum vector */
bios_drivea_alv /* allocation vector */
};
/***
*
* 8" sector translation vector
*
* A standard 8" disk has 6:1 interleave with sector numbers
* starting at one. We want to keep the interleave, but we want
* sector numbers to start at zero; therefore, this is not quite
* the standard sector translation table.
*
***/
unsigned short int bios_8inch_xlt[ 26 ] = {
0, 6, 12, 18, 24, 4, 10, 16,
22, 2, 8, 14, 20, 1, 7, 13,
19, 25, 5, 11, 17, 23, 3, 9,
15, 21
};
/***
*
* 8" SSSD DPH
*
***/
bios_dph_t bios_8inch_dph = {
bios_8inch_xlt, /* sector translation table */
0, 0, 0, /* scratch words */
bios_dirbuf, /* directory buffer */
&bios_8inch_dpb, /* disk parameter block */
bios_drivea_csv, /* checksum vector */
bios_drivea_alv /* allocation vector */
};
unsigned int bios_currenttrack;
unsigned int bios_currentdrive;
unsigned int bios_currentsector;
unsigned char *bios_currentdmaaddress;
bios_dph_t *bios_current_dph;
FILE *bios_diskimage;
FILE *bios_printer = 0;
/***
*
* This is a pointer to the dph that will be returned when drive A
* is selected. Different disk formats may be supported by
* arranging for this to be properly initialized when the program
* starts.
*
* By default, we use the standard 8" SSSD format.
*
***/
bios_dph_t *bios_valid_dph = &bios_8inch_dph;
/***
*
* TPA
*
* We need some sort of TPA to keep CP/M happy, even though we
* cannot (currently) load executables. Since I don't know what the
* minimum I can get away with, I'm just tossing out 64K.
*
***/
unsigned char bios_tpa[ 65536 ];
/***
*
* MRT
*
* The memory region table describes the TPA to the system.
*
***/
bios_mrt_t bios_mrt = {
1,
bios_tpa,
sizeof( bios_tpa )
};
unsigned short int bios_iobyte;
/***
*
* FUNCTION 0: INITIALIZATION
*
* This routine is entered on cold boot and must initialize the
* BIOS.
*
* formal parameters:
*
* none.
*
* return value:
*
* 1098765432109876 54321098 76543210
* +----------------+--------+--------+
* | | | |
* +----------------+--------+--------+
* ^ ^ ^
* | | |
* | | +-- default drive number
* | +-- initial user number
* +-- MBZ
*
***/
unsigned long int bios_boot( void )
{
return 0;
}
/***
*
* FUNCTION 1: WARM BOOT
*
* This function is called whenever a program terminates. Some
* reinitialization of the hardware or software might occur.
* When this function completes, it jumps directly to the entry
* point of the CCP, named _ccp. Node that _ccp must be declared
* as a global.
*
* formal parameters:
*
* none.
*
* return value:
*
* doesn't return.
*
***/
void bios_wboot( void )
{
extern cpm_ccp( void );
if( bios_printer != NULL ) fclose( bios_printer );
cpm_ccp();
}
/***
*
* FUNCTION 2: CONSOLE STATUS
*
* This functio nreturns the status of the currently assigned
* console device. It returns 0x00FF when a character is ready
* to be read or 0x0000 when no console characters are ready.
*
* formal parameters:
*
* none.
*
* return value:
*
* - 0x0000: no characters are ready from the console.
*
* - 0x00ff: A character may be read from the console.
*
***/
unsigned short int bios_const( void )
{
return 0;
}
/***
*
* FUNCTION 3: READ CONSOLE CHARACTER
*
* This function reads and returns the next console character.
* If no console character is ready, it waits until a character
* is typed before returning.
*
* formal parameters:
*
* none.
*
* return value:
*
* - character read from the console.
*
***/
unsigned char bios_conin( void )
{
return getchar();
}
/***
*
* FUNCTION 4: WRITE CONSOLE CHARACTER
*
* This function sends a character to the console output device.
* The character is in ASCII You might want to include a delay or
* filler characters for a line-feed or carriage return, if your
* console device requires some time interval at the end of the
* line (such as a TI Silent 700 Terminal). You can also filter out
* control characters which have undesirable effects on the console
* device.
*
* formal parameters:
*
* - victim: the character to be displayed.
*
* return value:
*
* none.
*
***/
void bios_conout( unsigned char victim )
{
putchar( victim );
fflush( stdout );
}
/***
*
* FUNCTION 5: LIST CHARACTER OUTPUT
*
* This character sends an ASCII character to the currently
* designated listing device. If your list device requires some
* communication protocol, it must be handled here.
*
* formal parameters:
*
* - victim: the character to be sent to the printer.
*
* return value:
*
* none.
*
***/
void bios_list( unsigned char victim )
{
if( bios_printer == NULL ) {
bios_printer = fopen( "printer.txt", "w" );
if( bios_printer == NULL ) {
perror( "printer.txt" );
exit( EXIT_FAILURE );
}
}
fputc( victim, bios_printer );
}
/***
*
* FUNCTION 6: AUXILIARY OUTPUT
*
* This function sends an ASCII character to the currently assigned
* auxiliary output device.
*
* formal parameters:
*
* - victim: the character to be sent to the punch.
*
* return value:
*
* none.
*
***/
void bios_punch( unsigned char victim )
{
}
/***
*
* FUNCTION 7: AUXILIARY INPUT
*
* This function reads the next character from the currently
* assigned auxiliary input device. It reports an end-of-file
* condition by returning an ASCII CTRL-Z (0x1a).
*
* formal parameters:
*
* none.
*
* return value:
*
* - The character read from the reader.
*
***/
unsigned char bios_reader( void )
{
return 0x1a;
}
/***
*
* FUNCTION 8: HOME
*
* This function returns the disk head of the currently selected
* disk to the track 00 position. If your controller does not have
* a special feature for finding track 00, you can translate teh
* call to a SETTRK function with a parameter of 0.
*
* formal parameters:
*
* none.
*
* return value:
*
* none.
*
***/
void bios_home( void )
{
bios_currenttrack = 0;
}
/***
*
* FUNCTION 9: SELECT DISK DRIVE
*
* This function selects the specified disk drive for further
* operations. The parameter contains 0 for drive A, 1 for drive B,
* up to 15 for drive P.
*
* On each disk select, this function returns the address of the
* selected drive's Disk Parmaeter Header.
*
* If there is an attempt to select a nonexistent drive, this
* functions 0 as an error indicator. Although the function must
* return the header address on each call, it may be advisable to
* postpone the actual physical disk select operation until an I/O
* function (seek, read, or write) is performed. Disk select
* operations can occur withou; a subsequent disk operation. Thsu,
* doing a physical selecte ach time this funciton is called may be
* wasteful of time.
*
* On entry to the Select Disk Drive function, if the least
* significant bit in the second parameter is zero, the disk is not
* currently logged in. If the disk drive is capable of handling
* varying media (such as single and double-sided disks, single- and
* double-density, and so on), the BIOS should check the type of
* media currnetly installed and set up the Disk Parameter Block
* accordingly at this time.
*
* formal parameters:
*
* - drive: the number of the drive to be selected.
*
* - logged: indicates whether the drive is currently logged in.
*
* return value:
*
* - 0: The drive number is invalid.
*
* - else: A pointer to the DPH for the drive.
*
***/
bios_dph_t *bios_seldsk( unsigned char drive, unsigned char logged )
{
switch( drive ) {
case 0:
bios_current_dph = bios_valid_dph; break;
default:
bios_current_dph = 0; break;
}
return bios_current_dph;
}
/***
*
* FUNCTION 10: SET TRACK NUMBER
*
* This function specifies the disk track number for use in
* subsequent disk accesses. The track number remains valid until
* either another Function 10 or a Function 8 (Home) is performed.
*
* You can choose to physically seek to the selected track at this
* time, or delay the physical seek until the next read or write
* actually occurs.
*
* The track number can range from 0 to the maximum track number
* supported by the physical drive. However, the maximum track
* number is limited to 65535 by the fact that it is being passed
* as a 16-bit quantity. Standard floppy disks have tracks number
* from 0 to 76.
*
* formal parameters:
*
* - track: The track number.
*
* return value:
*
* none.
*
***/
void bios_settrk( unsigned short int track )
{
bios_currenttrack = track;
}
/***
*
* FUNCTION 11: SET SECTOR NUMBER
*
* This function specifies the sector number for subsequent disk
* accesses. This number remains in effect until another function
* 11 is performed.
*
* Th function select actual (unskewed) sector numbers. If skewing
* is appropriate, it will have previously been done by a call to
* Function 16. You can send this information to the controller at
* this point or delay sector selection until a read or write
* operation occurs.
*
* formal parmaeters:
*
* - sector: the sector to be used for subsequent I/O.
*
* return value:
*
* none.
*
***/
void bios_setsec( unsigned short int sector )
{
bios_currentsector = sector;
}
/***
*
* FUNCTION 12: SET DMA ADDRESS
*
* This function specifies teh DMA address for subsequent read or
* write operations. Note that the controller need not actually
* support DMA (direct memory access). The BIOS will use the
* 128-byte area starting at the selected DMA address for the
* memory buffer during the following read or write operations.
* This function can be called with either an even or an odd
* address for a DMA buffer.
*
* formal parameters:
*
* - dmaaddress: pointer to the sector buffer.
*
* return value:
*
* none.
*
***/
void bios_setdma( void *dmaaddress )
{
bios_currentdmaaddress = dmaaddress;
}
/***
*
* FUNCTION 13: READ SECTOR
*
* After the drive has been selected, the track has been set, the
* sector has been set, and teh DMA address has been specified, the
* read function uses these parameters to read one sector and
* returns the error code.
*
* Currently, CP/M-68K responds only to a zero or nonzero return
* code value. Thus, if the return value is zero, CP/M-68K assumes
* that the disk operation completed properly. If an error occurs,
* however, the BIOS should attempt at least ten retries to see if
* the error is recoverable.
*
* formal parameters:
*
* none.
*
* return value:
*
* - 0: success.
*
* - 1: failure.
*
***/
unsigned short int bios_read( void )
{
unsigned int offset;
int temp;
offset = ( bios_currenttrack * bios_current_dph->dpb->spt ) +
bios_currentsector;
if( fseek( bios_diskimage, offset*128, SEEK_SET ) != 0 ) {
perror( "seek" );
return 1;
}
temp = fread( bios_currentdmaaddress, 1, 128, bios_diskimage ) ;
if( temp != 128 ) {
fprintf( stderr, "track: %d ", bios_currenttrack );
fprintf( stderr, "sector: %d ", bios_currentsector );
fprintf( stderr, "pos: %d ", offset*128 );
fprintf( stderr, "read: %d ", temp );
perror( "short read" );
return 1;
}
return 0;
}
/***
*
* FUNCTION 14: WRITE SECTOR
*
* This function is used to write 128 bytes of data from the
* currently selected DMA buffer to the currently selected sector,
* track, and disk. The parameter indicates whether the write is an
* ordinary write operation or whether there are special
* considerations.
*
* If the parameter is 0, this is an ordinary write operation. If
* it is 1, this is a write to a directory sector, and the write
* should be physically completed immediately. If the parmaters is
* 2, this is a write to teh first sector of a newly allocated
* block of the disk.
*
* formal parameters:
*
* - typecode: the type of the write.
*
* return value:
*
* - 0: success.
*
* - 1: error.
*
***/
unsigned short int bios_write( unsigned short int typecode )
{
unsigned int offset;
offset = ( bios_currenttrack * bios_current_dph->dpb->spt ) +
bios_currentsector;
if( fseek( bios_diskimage, offset*128, SEEK_SET ) != 0 ) {
perror( "seek" );
return 1;
}
if( fwrite( bios_currentdmaaddress, 1, 128, bios_diskimage ) != 128 ) {
perror( "short write" );
return 1;
}
if( typecode == 1 ) bios_flush();
return 0;
}
/***
*
* FUNCTION 15: RETURN LIST STATUS
*
* This function returns the status of the list device, either 0
* when the list device is not ready to accept a character or 0xff
* when a character can be sent to the list device.
*
* formal parameters:
*
* none
*
* return value:
*
* - 0: printer is not ready to accept a character.
*
* - 0xff: printer is ready to accept a character.
*
***/
unsigned short int bios_listst( void )
{
return 0xff;
}
/***
*
* FUNCTION 16: SECTOR TRANSLATE
*
* This function performs logical-to-physical sector translation.
* The Sector Translate function receives a logical sector number.
* The logical sector number can range from 0 to the number of
* sectors per track - 1. Sector Translate also receives the
* address of teh translate table. The logical sector number is
* used as an index into the translate table. The resulting
* physical sector number is returned.
*
* If the pointer to the translate table is null, implying that
* there is no translate table, the original sector number is
* returned. Note that other algorithms are possible; in
* particular, it is common to increment the logical sector number
* in order to convert the logical range of 0 to n-1 into the
* physical range of 1 to n. Sector Translate is always called by
* the BDOS, whether the translate table address in the Disk
* Parameter Header is zero or nonzero.
*
* formal parameters:
*
* - sector: logical sector number.
*
* - table: pointer to sector number translation table.
*
* return value:
*
* physical sector number.
*
***/
unsigned short int bios_sectran( unsigned short int sector,
unsigned short int *table )
{
if( bios_current_dph->xlt == 0 ) return sector;
return bios_current_dph->xlt[ sector ];
}
/***
*
* FUNCTION 17: There is NO function 17
*
***/
/***
*
* FUNCTION 18: GET ADDRESS OF MEMORY REGION TABLE
*
* This function returns the address of the Memory REgion Table
* (MRT). For compatibility with other CP/M system, CP/M-68K
* maintains a Memory Region Table. However, it contains only one
* region, the Transient Program Area (TPA).
*
* formal parameters:
*
* none.
*
* return value:
*
* A pointer to the memory region table.
*
***/
bios_mrt_t *bios_getmrt( void )
{
return &bios_mrt;
}
/***
*
* FUNCTION 19: GET I/O BYTE
*
* This function returns the currnet value of the logical to
* physical input/output device byte (I/O byte). This 8-bit value
* associates physical devices with CP/M-68k's four logical
* devices.
*
* NOTE: Even though this is a byte value, we are using
* word references. The upper byte should be zero.
*
* formal parameters:
*
* none.
*
* return value:
*
* a copy of the I/O byte.
*
***/
unsigned short int bios_getiobyte( void )
{
return bios_iobyte;
}
/***
*
* FUNCTION 20: SET I/O BYTE
*
* This function sets the I/O byte to the specified value.
*
* NOTE: Even though this is a byte value, we are using word
* references. The upper byte should be zero.
*
* formal parameters:
*
* - iobyte: The value to be stored in the I/O byte.
*
* return value:
*
* none.
*
***/
void bios_setiobyte( unsigned short int iobyte )
{
bios_iobyte = iobyte;
}
/***
*
* FUNCTION 21: FLUSH BUFFERS
*
* This function forces the contents of any disk buffers that have
* been modified to be written. That is, after this funciton has
* been performed, all disk writes have been physically completed.
* After the buffers are written, this function returns a zero.
* However, if the buffers cannot be written or an error occurs,
* the function returns 0xffff.
*
* formal parameters:
*
* none.
*
* return value:
*
* - 0: success.
*
* - 0xffff: failure.
*
***/
unsigned short int bios_flush( void )
{
fflush( bios_diskimage );
return 0;
}
/***
*
* FUNCTION 22: SET EXCEPTION HANDLE ADDRESS
*
* This function sets the specified exception vector such that it
* invokes the specified handler. The previous vector value is
* retruned. Unlike the BDOS Set Exception Vector Function, this
* BIOS function sets any exception vector.
*
* formal parameters:
*
* - vector: The number of the vector to be set.
*
* - handler: A pointer to the routine that is to be invoked.
*
***/
void *bios_setexc( unsigned short int vector, void *handler )
{
return 0;
}
unsigned short int cpm_udiv(
signed long int dividend,
unsigned short int divisor,
unsigned short int *remainder )
{
*remainder = dividend % divisor;
return dividend / divisor;
}
unsigned short int cpm_swap( unsigned short int victim )
{
static int swaptype= -1;
static unsigned short int testpattern=0x0102;
unsigned short int temp;
if( swaptype < 0 ) {
if( *(char *)&testpattern == 0x01 ) {
swaptype = 1;
} else {
swaptype = 0;
}
}
if( swaptype == 0 ) return victim;
temp = ( (victim & 0xff ) << 8 ) |
( ( victim & 0xff00 ) >> 8 );
return temp;
}
unsigned char bios1( int function )
{
return 0;
}
void bios2( int function, unsigned short int parm )
{
}
void bios3( int function, void *parm )
{
}
void *bios4( int function, unsigned short int parm1, unsigned short int parm2 )
{
return (void *)0;
}
unsigned short int bios5( int function, unsigned short int parm1,
unsigned short int parm2 )
{
return 0;
}
signed char *bios6( int function )
{
return (void *)0;
}
unsigned char cpm_submit = 0;
unsigned char cpm_morecmds = 0;
unsigned char cpm_autost = 0;
signed char cpm_usercmd[ 130 ];
unsigned short int cpm_bdos( unsigned short int func,
unsigned long int parm )
{
return cpm__bdos( func, (unsigned short int)parm, (void *)parm );
}
unsigned short int cpm_pgmld( void *infop, void *dmaaddress )
{
return 0;
}
unsigned char *cpm_traphndl( void )
{
return 0;
}
void cpm_initexc( void *parm )
{
}
struct _filetyps
{
signed char *typ;
unsigned short int (*loader) ();
signed char user_c;
signed char user_0;
}
cpm_load_tbl[] = { "\0", 0, 0, 0 };
unsigned short int cpm_load68k( void )
{
return 0;
}
struct termios bios_original_t;
void bios_exit( void )
{
fclose( bios_diskimage );
if( tcsetattr( fileno( stdin ), TCSANOW, &bios_original_t ) != 0 ) {
perror( "tcsetattr" );
exit( EXIT_FAILURE );
}
exit( EXIT_SUCCESS );
}
typedef struct bios_fcb_s {
unsigned char drive;
unsigned char name[ 8 ];
unsigned char ext[ 3 ];
unsigned char reserved[ 20 ];
unsigned char cr;
} bios_fcb_t;
int bios_fcb2path( bios_fcb_t *fcb, char *path )
{
int i;
int pathindex = 0;
for( i = 0; i < 8; i++ ) {
if( ( fcb->name[ i ] & 0xff ) < '!' ) break;
path[ pathindex++ ] = fcb->name[ i ];
}
path[ pathindex++ ] = '.';
for( i = 0; i < 3; i++ ) {
if( ( fcb->ext[ i ] & 0xff ) < '!' ) break;
path[ pathindex++ ] = fcb->ext[ i ];
}
if( pathindex == 1 ) return 0;
path[ pathindex ] = 0;
return 1;
}
void bios_import( void )
{
extern unsigned char cpm_dma[];
extern unsigned short int cpm_fill_fcb(
unsigned short int which_parm,
bios_fcb_t *fcb );
int i;
bios_fcb_t fcb;
int ambiguous;
char path[ 13 ];
FILE *exportfile;
char buffer[ 128 ];
ambiguous = cpm_fill_fcb( 1, &fcb );
if( ambiguous ) {
printf( "Wildcards not supported." );
return;
}
if( !bios_fcb2path( &fcb, path ) ) {
printf( "Cannot import ." );
return;
}
cpm_bdos( 19, (unsigned long int)&fcb );
if( cpm_bdos( 22, (unsigned long int)&fcb ) > 3 ) {
printf( "Cannot make " );
for( i = 0; i < 8; i++ )
printf( "%c", fcb.name[ i ] & 0x7f );
printf( "." );
for( i = 0; i < 3; i++ )
printf( "%c", fcb.ext[ i ] & 0x7f );
return;
}
exportfile = fopen( path, "rb" );
if( exportfile == NULL ) {
perror( path );
return;
}
printf( "import %s", path );
printf( " -> " );
for( i = 0; i < 8; i++ )
printf( "%c", fcb.name[ i ] & 0x7f );
printf( "." );
for( i = 0; i < 3; i++ )
printf( "%c", fcb.ext[ i ] & 0x7f );
cpm_bdos( 26, (unsigned long int)&buffer );
while( 1 ) {
memset( buffer, 0x1a, 128 );
if( fread( buffer, 1, 128, exportfile ) == 0 ) {
break;
}
i = cpm_bdos( 21, (unsigned long int)&fcb );
if( i == 1 ) {
printf( " Directory full." );
break;
}
if( i == 2 ) {
printf( " Disk full." );
break;
}
if( i != 0 ) {
printf( " Unknown error %d", i );
break;
}
}
cpm_bdos( 16, (unsigned long int)&fcb );
fclose( exportfile );
}
void bios_export( void )
{
extern unsigned char cpm_dma[];
extern unsigned short int cpm_fill_fcb(
unsigned short int which_parm,
bios_fcb_t *fcb );
int i;
bios_fcb_t fcb;
int ambiguous;
char path[ 13 ];
FILE *exportfile;
char buffer[ 128 ];
ambiguous = cpm_fill_fcb( 1, &fcb );
if( ambiguous ) {
printf( "Wildcards not supported." );
return;
}
if( !bios_fcb2path( &fcb, path ) ) {
printf( "Cannot export ." );
return;
}
if( cpm_bdos( 15, (unsigned long int)&fcb ) > 3 ) {
printf( "No file." );
return;
}
exportfile = fopen( path, "wb" );
if( exportfile == NULL ) {
perror( path );
return;
}
printf( "export " );
for( i = 0; i < 8; i++ )
printf( "%c", fcb.name[ i ] & 0x7f );
printf( "." );
for( i = 0; i < 3; i++ )
printf( "%c", fcb.ext[ i ] & 0x7f );
printf( " -> " );
printf( "%s", path );
cpm_bdos( 26, (unsigned long int)&buffer );
while( 1 ) {
if( cpm_bdos( 20, (unsigned long int)&fcb ) != 0 ) {
break;
}
if( fwrite( buffer, 1, 128, exportfile ) != 128 ) {
perror( "short write" );
break;
}
}
cpm_bdos( 16, (unsigned long int)&fcb );
fclose( exportfile );
}
void usage( void )
{
fprintf( stderr, "usage: exchange " );
fprintf( stderr, "[-p112] " );
fprintf( stderr, "imagefile\n" );
exit( EXIT_FAILURE );
}
int main( int ArgC, char **ArgV )
{
unsigned short int diskuser;
struct termios t;
int nextarg = 1;
while( 1 ) {
if( nextarg == ArgC ) usage();
if( strcmp( "-p112", ArgV[ nextarg ] ) == 0 ) {
bios_valid_dph = &bios_p112_dph;
nextarg++;
continue;
}
break;
}
if( nextarg != ( ArgC - 1 ) ) usage();
bios_diskimage = fopen( ArgV[ nextarg ], "rb+" );
if( bios_diskimage == NULL ) {
perror( ArgV[ 1 ] );
exit( EXIT_FAILURE );
}
if( tcgetattr( fileno( stdin ), &t ) != 0 ) {
perror( "tcgetattr" );
exit( EXIT_FAILURE );
}
if( tcgetattr( fileno( stdin ), &bios_original_t ) != 0 ) {
perror( "tcgetattr" );
exit( EXIT_FAILURE );
}
cfmakeraw( &t );
if( tcsetattr( fileno( stdin ), TCSANOW, &t ) != 0 ) {
perror( "tcsetattr" );
exit( EXIT_FAILURE );
}
diskuser = bios_boot();
cpm_bdosinit();
cpm_bdos( 32, diskuser >> 8 );
cpm_bdos( 14, diskuser & 0xff );
while( 1 ) cpm_ccp();
}