/* | |
* File I/O, based on DOS int 21h calls | |
* This file is part of the DOSMid project | |
* http://dosmid.sourceforge.net | |
* | |
* Copyright (C) 2018 Mateusz Viste | |
*/ | |
#include <dos.h> /* REGS */ | |
#include <string.h> /* _fmemcpy() */ | |
#include "fio.h" /* include self for control */ | |
static void fio_seek_sync(struct fiofile_t *f) { | |
/* DOS 2+ - LSEEK - SET CURRENT FILE POSITION | |
AH = 42h | |
AL = origin of move | |
00h start of file | |
01h current file position | |
02h end of file | |
BX = file handle | |
CX:DX = (signed) offset from origin of new file position | |
Return on success: | |
CF clear | |
DX:AX = new file position in bytes from start of file | |
Return on error: | |
CF set | |
AX = error code */ | |
union REGS regs; | |
unsigned short *off; | |
long offset; | |
if ((f->flags & FIO_FLAG_SEEKSYNC) == 0) return; | |
offset = f->curpos; | |
off = (unsigned short *)(&offset); | |
regs.x.ax = 0x4200u; | |
regs.x.bx = f->fh; | |
regs.x.cx = off[1]; | |
regs.x.dx = off[0]; | |
int86(0x21, ®s, ®s); | |
f->flags &= ~FIO_FLAG_SEEKSYNC; | |
/* if (regs.x.cflag != 0) return(0 - regs.x.ax); | |
return(((long)regs.x.dx << 16) | regs.x.ax); */ | |
} | |
/* seek to offset position of file pointed at by fhandle. returns current file position on success, a negative error otherwise */ | |
signed long fio_seek(struct fiofile_t *f, unsigned short origin, signed long offset) { | |
switch (origin) { | |
case FIO_SEEK_START: | |
if (offset < 1) { | |
f->curpos = 0; | |
} else { | |
f->curpos = offset; | |
} | |
break; | |
case FIO_SEEK_END: | |
f->curpos = f->flen; | |
case FIO_SEEK_CUR: | |
f->curpos += offset; | |
break; | |
} | |
f->flags |= FIO_FLAG_SEEKSYNC; | |
return(f->curpos); | |
} | |
/* reads a line from file pointed at by fhandle, fill buff up to buflen bytes. returns the line length (possibly longer than buflen), or -1 on EOF */ | |
int fio_getline(struct fiofile_t *f, void far *buff, short buflen) { | |
unsigned char bytebuf; | |
short linelen = 0; | |
buflen--; /* leave space for the zero terminator */ | |
for (;;) { | |
if (fio_read(f, &bytebuf, 1) == 0) { /* EOF */ | |
if (linelen == 0) linelen = -1; | |
break; | |
} | |
if (bytebuf == '\n') break; | |
if (bytebuf == '\r') continue; | |
linelen++; | |
if (buflen > 0) { | |
buflen--; | |
*((unsigned char far *)buff) = bytebuf; | |
buff = ((unsigned char far *)buff) + 1; | |
} | |
} | |
*((unsigned char far *)buff) = 0; | |
return(linelen); | |
} | |
static void loadcache(struct fiofile_t *f) { | |
union REGS regs; | |
struct SREGS sregs; | |
fio_seek_sync(f); | |
f->flags |= FIO_FLAG_SEEKSYNC; | |
f->bufoffs = f->curpos; | |
regs.h.ah = 0x3f; | |
regs.x.bx = f->fh; | |
regs.x.cx = FIO_CACHE; | |
sregs.ds = FP_SEG(f->buff); | |
regs.x.dx = FP_OFF(f->buff); | |
int86x(0x21, ®s, ®s, &sregs); | |
} | |
/* open file fname and set fhandle with the associated file handle. returns 0 on success, non-zero otherwise */ | |
int fio_open(char far *fname, int mode, struct fiofile_t *f) { | |
/* DOS 2+ - OPEN - OPEN EXISTING FILE | |
AH = 3Dh | |
AL = access and sharing modes | |
DS:DX -> ASCIZ filename | |
CL = attribute mask of files to look for (server call only) | |
Return: | |
CF clear if successful | |
AX = file handle or error code | |
CF set on error */ | |
union REGS regs; | |
struct SREGS sregs; | |
/* */ | |
regs.h.ah = 0x3d; | |
regs.h.al = mode; | |
sregs.ds = FP_SEG(fname); | |
regs.x.dx = FP_OFF(fname); | |
int86x(0x21, ®s, ®s, &sregs); | |
f->fh = regs.x.ax; | |
if (regs.x.cflag != 0) return(-1); | |
/* */ | |
f->curpos = 0; | |
loadcache(f); | |
/* fseek to end so I know the file length */ | |
regs.x.ax = 0x4202u; | |
regs.x.bx = f->fh; | |
regs.x.cx = 0; | |
regs.x.dx = 0; | |
int86(0x21, ®s, ®s); | |
f->flen = (((long)regs.x.dx << 16) | regs.x.ax); | |
/* fseek to start */ | |
regs.x.ax = 0x4200u; | |
regs.x.bx = f->fh; | |
regs.x.cx = 0; | |
regs.x.dx = 0; | |
int86(0x21, ®s, ®s); | |
/* */ | |
f->flags = 0; | |
return(0); | |
} | |
/* reads count bytes from file pointed at by fhandle, and writes the data into buff. returns the number of bytes actually read, or a negative number on error */ | |
int fio_read(struct fiofile_t *f, void far *buff, int count) { | |
/* DOS 2+ - READ - READ FROM FILE OR DEVICE | |
* AH = 3Fh | |
* BX = file handle | |
* CX = number of bytes to read | |
* DS:DX -> buffer for data | |
* Return: | |
* CF clear if successful | |
* AX = number of bytes actually read (0 if at EOF before call) | |
* CF set on error | |
* AX = error code (05h,06h) (see #01680 at AH=59h/BX=0000h) */ | |
union REGS regs; | |
struct SREGS sregs; | |
if (f->curpos + count > f->flen) count = f->flen - f->curpos; | |
if (count == 0) return(0); | |
if (count <= FIO_CACHE) { | |
if ((f->curpos < f->bufoffs) || (f->curpos + count > f->bufoffs + FIO_CACHE)) { | |
loadcache(f); | |
} | |
_fmemcpy(buff, f->buff + (f->curpos - f->bufoffs), count); | |
f->curpos += count; | |
return(count); | |
} | |
fio_seek_sync(f); | |
regs.h.ah = 0x3f; | |
regs.x.bx = f->fh; | |
regs.x.cx = count; | |
sregs.ds = FP_SEG(buff); | |
regs.x.dx = FP_OFF(buff); | |
int86x(0x21, ®s, ®s, &sregs); | |
if (regs.x.cflag != 0) return(0 - regs.x.ax); | |
f->curpos += regs.x.ax; | |
return(regs.x.ax); | |
} | |
/* close file handle. returns 0 on success, non-zero otherwise */ | |
int fio_close(struct fiofile_t *f) { | |
/* DOS 2+ - CLOSE - CLOSE FILE | |
AH = 3Eh | |
BX = file handle | |
Return on success: | |
CF clear | |
AX destroyed | |
On error: | |
CF set | |
AX = error code */ | |
union REGS regs; | |
regs.h.ah = 0x3e; | |
regs.x.bx = f->fh; | |
int86(0x21, ®s, ®s); | |
if (regs.x.cflag != 0) return(0 - regs.x.ax); | |
return(0); | |
} |