mirror of https://github.com/zxdos/zxuno.git
754 lines
17 KiB
C
754 lines
17 KiB
C
/*
|
|
* loadpzx - loads a PZX file into the PZX player.
|
|
*
|
|
* Copyright (C) 2016-2021 Antonio Villena
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, version 3.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*
|
|
* SPDX-FileCopyrightText: Copyright (C) 2016-2021 Antonio Villena
|
|
*
|
|
* SPDX-License-Identifier: GPL-3.0-only
|
|
*/
|
|
|
|
#define PROGRAM "loadpzx"
|
|
#define VERSION "0.1"
|
|
#define DESCRIPTION "Loads a PZX file into the PZX" "\r" "player."
|
|
#define COPYRIGHT "\x7f 2016-2021 Antonio Villena"
|
|
#define LICENSE "License: GNU GPL 3.0"
|
|
|
|
typedef unsigned char BYTE;
|
|
typedef unsigned short WORD;
|
|
|
|
enum {IBLACK=0, IBLUE, IRED, IMAG, IGREEN, ICYAN, IYELLOW, IWHITE};
|
|
enum {PBLACK=0, PBLUE=8, PRED=16, PMAG=24, PGREEN=32, PCYAN=40, PYELLOW=48, PWHITE=56};
|
|
#define BRIGHT 64
|
|
#define FLASH 128
|
|
|
|
__sfr __at (0xfe) ULA;
|
|
__sfr __at (0xff) ATTR;
|
|
__sfr __at (0x1f) KEMPSTONADDR;
|
|
__sfr __at (0x7f) FULLERADDR;
|
|
|
|
__sfr __banked __at (0xf7fe) SEMIFILA1;
|
|
__sfr __banked __at (0xeffe) SEMIFILA2;
|
|
__sfr __banked __at (0xfbfe) SEMIFILA3;
|
|
__sfr __banked __at (0xdffe) SEMIFILA4;
|
|
__sfr __banked __at (0xfdfe) SEMIFILA5;
|
|
__sfr __banked __at (0xbffe) SEMIFILA6;
|
|
__sfr __banked __at (0xfefe) SEMIFILA7;
|
|
__sfr __banked __at (0x7ffe) SEMIFILA8;
|
|
|
|
#define ATTRP 23693
|
|
#define ATTRT 23695
|
|
#define BORDR 23624
|
|
#define LASTK 23560
|
|
|
|
#define WAIT_VRETRACE __asm halt __endasm
|
|
#define WAIT_HRETRACE while(ATTR!=0xff)
|
|
#define SETCOLOR(x) *(BYTE *)(ATTRP)=(x)
|
|
#define LASTKEY *(BYTE *)(LASTK)
|
|
#define ATTRPERMANENT *((BYTE *)(ATTRP))
|
|
#define ATTRTEMPORARY *((BYTE *)(ATTRT))
|
|
#define BORDERCOLOR *((BYTE *)(BORDR))
|
|
|
|
#define MAKEWORD(d,h,l) { ((BYTE *)&(d))[0] = (l) ; ((BYTE *)&(d))[1] = (h); }
|
|
|
|
__sfr __banked __at (0xfc3b) ZXUNOADDR;
|
|
__sfr __banked __at (0xfd3b) ZXUNODATA;
|
|
|
|
#define MASTERCONF 0
|
|
#define SCANCODE 4
|
|
#define KEYBSTAT 5
|
|
#define JOYCONF 6
|
|
#define SRAMDATA 0xf2
|
|
#define SRAMADDRINC 0xf1
|
|
#define SRAMADDR 0xf0
|
|
#define COREID 0xff
|
|
|
|
/* Some ESXDOS system calls */
|
|
#define HOOK_BASE 128
|
|
#define MISC_BASE (HOOK_BASE+8)
|
|
#define FSYS_BASE (MISC_BASE+16)
|
|
#define M_GETSETDRV (MISC_BASE+1)
|
|
#define F_OPEN (FSYS_BASE+2)
|
|
#define F_CLOSE (FSYS_BASE+3)
|
|
#define F_READ (FSYS_BASE+5)
|
|
#define F_WRITE (FSYS_BASE+6)
|
|
#define F_SEEK (FSYS_BASE+7)
|
|
#define F_GETPOS (FSYS_BASE+8)
|
|
#define M_TAPEIN (MISC_BASE+3)
|
|
#define M_AUTOLOAD (MISC_BASE+8)
|
|
|
|
#define FMODE_READ 0x1 // Read access
|
|
#define FMODE_WRITE 0x2 // Write access
|
|
#define FMODE_OPEN_EX 0x0 // Open if exists, else error
|
|
#define FMODE_OPEN_AL 0x8 // Open if exists, if not create
|
|
#define FMODE_CREATE_NEW 0x4 // Create if not exists, if exists error
|
|
#define FMODE_CREATE_AL 0xc // Create if not exists, else open and truncate
|
|
|
|
#define SEEK_START 0
|
|
#define SEEK_CUR 1
|
|
#define SEEK_BKCUR 2
|
|
|
|
#define BUFSIZE 2048
|
|
BYTE errno;
|
|
char buffer[BUFSIZE];
|
|
|
|
void __sdcc_enter_ix (void) __naked;
|
|
void cls (BYTE);
|
|
|
|
void puts (BYTE *);
|
|
void u16tohex (WORD n, char *s);
|
|
void u8tohex (BYTE n, char *s);
|
|
void print8bhex (BYTE n);
|
|
void print16bhex (WORD n);
|
|
|
|
void memset (BYTE *, BYTE, WORD);
|
|
void memcpy (BYTE *, BYTE *, WORD);
|
|
|
|
BYTE open (char *filename, BYTE mode);
|
|
void close (BYTE handle);
|
|
WORD read (BYTE handle, BYTE *buffer, WORD nbytes);
|
|
WORD write (BYTE handle, BYTE *buffer, WORD nbytes);
|
|
void seek (BYTE handle, WORD hioff, WORD looff, BYTE from);
|
|
|
|
/* --------------------------------------------------------------------------------- */
|
|
/* --------------------------------------------------------------------------------- */
|
|
/* --------------------------------------------------------------------------------- */
|
|
BYTE main (char *p);
|
|
void getcoreid(BYTE *s);
|
|
void show_usage (void);
|
|
BYTE commandlinemode (char *p);
|
|
void getfilename (char *p, char *fname);
|
|
BYTE printheader (BYTE handle);
|
|
void readpzx (BYTE handle);
|
|
WORD readblocktag (BYTE handle);
|
|
WORD readword (BYTE handle);
|
|
void convertpaus2puls (BYTE handle);
|
|
void skipblock (BYTE handle, WORD hiskip, WORD loskip);
|
|
void copyblock (BYTE handle, WORD hicopy, WORD locopy);
|
|
void rewindsram (void);
|
|
void writesram (BYTE v);
|
|
void incaddrsram (void);
|
|
void copysram (BYTE *p, WORD l);
|
|
|
|
void init (void) __naked
|
|
{
|
|
__asm
|
|
xor a
|
|
ld (#_errno), a
|
|
push hl
|
|
call _main
|
|
inc sp
|
|
inc sp
|
|
ld a, l
|
|
or a
|
|
jr z, prepare_load
|
|
cp #255
|
|
jr z, no_load
|
|
scf
|
|
ret
|
|
no_load:
|
|
or a
|
|
ret
|
|
prepare_load:
|
|
; close TAPE.IN
|
|
ld b, #1
|
|
rst #8
|
|
.db #M_TAPEIN
|
|
|
|
; auto LOAD ""
|
|
xor a
|
|
rst #8
|
|
.db #M_AUTOLOAD
|
|
|
|
or a
|
|
ret
|
|
__endasm;
|
|
// old code to do LOAD ""
|
|
/*
|
|
ld bc, #3
|
|
ld hl, (#23641)
|
|
push hl
|
|
rst #0x18
|
|
.dw 0x1655
|
|
ld hl, #command_load
|
|
pop de
|
|
ld bc, #3
|
|
ldir
|
|
ld hl, #0x12cf
|
|
jp #0x1ffb
|
|
|
|
command_load:
|
|
.db 239, 34, 34
|
|
*/
|
|
}
|
|
|
|
BYTE main (char *p)
|
|
{
|
|
if (!p)
|
|
{
|
|
show_usage();
|
|
return 255;
|
|
}
|
|
else
|
|
return commandlinemode(p);
|
|
}
|
|
|
|
BYTE commandlinemode (char *p)
|
|
{
|
|
char fname[32];
|
|
BYTE handle, res;
|
|
BYTE noautoload;
|
|
|
|
noautoload = 0;
|
|
while (*p==' ')
|
|
p++;
|
|
if (*p=='-')
|
|
{
|
|
p++;
|
|
if (*p=='n')
|
|
{
|
|
noautoload = 255;
|
|
p++;
|
|
}
|
|
}
|
|
while (*p==' ')
|
|
p++;
|
|
|
|
getfilename (p, fname);
|
|
handle = open (fname, FMODE_READ);
|
|
if (handle==0xff)
|
|
return errno;
|
|
|
|
res = printheader(handle);
|
|
if (res)
|
|
readpzx(handle);
|
|
|
|
close (handle);
|
|
|
|
if (noautoload == 255)
|
|
puts ("\r\rType LOAD \"\" and press PLAY\r");
|
|
else
|
|
{
|
|
ZXUNOADDR = 0xf3;
|
|
ZXUNODATA = 0x1; // software assisted PLAY press
|
|
ZXUNODATA = 0x0;
|
|
}
|
|
|
|
return noautoload;
|
|
}
|
|
|
|
void show_usage (void)
|
|
{
|
|
// 01234567890123456789012345678901
|
|
puts (
|
|
PROGRAM " version " VERSION "\r"
|
|
DESCRIPTION "\r"
|
|
COPYRIGHT "\r"
|
|
LICENSE "\r"
|
|
"\r"
|
|
"Usage:\r"
|
|
" ." PROGRAM " [-n] file.pzx\r"
|
|
"\r"
|
|
"-n : do not autoplay.\r"
|
|
);
|
|
}
|
|
|
|
void getfilename (char *p, char *fname)
|
|
{
|
|
while (*p!=':' && *p!=0xd && *p!=' ')
|
|
*fname++ = *p++;
|
|
*fname = '\0';
|
|
}
|
|
|
|
BYTE printheader (BYTE handle)
|
|
{
|
|
WORD lblock;
|
|
|
|
readblocktag (handle);
|
|
if (buffer[0]!='P' ||
|
|
buffer[1]!='Z' ||
|
|
buffer[2]!='X' ||
|
|
buffer[3]!='T')
|
|
{
|
|
puts ("This is not a PZX valid file\r");
|
|
return 0;
|
|
}
|
|
|
|
MAKEWORD(lblock,buffer[5],buffer[4]);
|
|
read (handle, buffer, lblock);
|
|
if (buffer[0]!=1 || buffer[1]!=0)
|
|
{
|
|
puts ("PZX format not supported\r");
|
|
return 0;
|
|
}
|
|
if (lblock>3)
|
|
{
|
|
puts ("Loading: ");
|
|
puts(buffer+2);
|
|
puts ("\r");
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void readpzx (BYTE handle)
|
|
{
|
|
WORD hi,lo,res;
|
|
BYTE scandblctrl;
|
|
|
|
ZXUNOADDR = 0xb;
|
|
scandblctrl = ZXUNODATA;
|
|
ZXUNODATA = scandblctrl | 0xc0;
|
|
|
|
rewindsram();
|
|
while(1)
|
|
{
|
|
*((BYTE *)(23692)) = 0xff; // to avoid scrolling message
|
|
res = readblocktag (handle);
|
|
if (!res)
|
|
break;
|
|
|
|
if (buffer[0]=='P' &&
|
|
buffer[1]=='U' &&
|
|
buffer[2]=='L' &&
|
|
buffer[3]=='S')
|
|
{
|
|
puts ("P");
|
|
MAKEWORD(lo,buffer[5],buffer[4]);
|
|
MAKEWORD(hi,buffer[7],buffer[6]);
|
|
writesram(0x2);
|
|
incaddrsram();
|
|
copysram(&buffer[4],4);
|
|
copyblock (handle,hi,lo);
|
|
}
|
|
else if (buffer[0]=='D' &&
|
|
buffer[1]=='A' &&
|
|
buffer[2]=='T' &&
|
|
buffer[3]=='A')
|
|
{
|
|
puts ("D");
|
|
MAKEWORD(lo,buffer[5],buffer[4]);
|
|
MAKEWORD(hi,buffer[7],buffer[6]);
|
|
writesram(0x3);
|
|
incaddrsram();
|
|
copysram(&buffer[4],4);
|
|
copyblock (handle,hi,lo);
|
|
}
|
|
else if (buffer[0]=='P' &&
|
|
buffer[1]=='A' &&
|
|
buffer[2]=='U' &&
|
|
buffer[3]=='S')
|
|
{
|
|
puts ("A");
|
|
MAKEWORD(lo,buffer[5],buffer[4]);
|
|
MAKEWORD(hi,buffer[7],buffer[6]);
|
|
convertpaus2puls (handle);
|
|
//writesram(0x4);
|
|
//incaddrsram();
|
|
//copysram(&buffer[4],4);
|
|
//copyblock (handle,hi,lo);
|
|
}
|
|
else if (buffer[0]=='S' &&
|
|
buffer[1]=='T' &&
|
|
buffer[2]=='O' &&
|
|
buffer[3]=='P')
|
|
{
|
|
puts ("S");
|
|
res = readword (handle);
|
|
//MAKEWORD(lo,buffer[5],buffer[4]);
|
|
//MAKEWORD(hi,buffer[7],buffer[6]);
|
|
writesram(0x1);
|
|
incaddrsram();
|
|
copysram (&res, 2);
|
|
writesram(0);
|
|
incaddrsram();
|
|
writesram(0);
|
|
incaddrsram();
|
|
//skipblock(handle,hi,lo);
|
|
}
|
|
else if (buffer[0]=='B' &&
|
|
buffer[1]=='R' &&
|
|
buffer[2]=='W' &&
|
|
buffer[3]=='S')
|
|
{
|
|
puts ("B");
|
|
writesram(0x4);
|
|
incaddrsram();
|
|
writesram(0);
|
|
incaddrsram();
|
|
writesram(0);
|
|
incaddrsram();
|
|
writesram(0);
|
|
incaddrsram();
|
|
writesram(0);
|
|
incaddrsram();
|
|
MAKEWORD(lo,buffer[5],buffer[4]);
|
|
MAKEWORD(hi,buffer[7],buffer[6]);
|
|
skipblock(handle,hi,lo);
|
|
}
|
|
else // skip unsupported block
|
|
{
|
|
puts ("x");
|
|
MAKEWORD(lo,buffer[5],buffer[4]);
|
|
MAKEWORD(hi,buffer[7],buffer[6]);
|
|
skipblock(handle,hi,lo);
|
|
}
|
|
}
|
|
// small pause of 20ms to avoid loading errors
|
|
// due to abrupt switching of the EAR signal
|
|
// from the virtual player to the real input
|
|
|
|
// Add full stop mark to the tape
|
|
writesram(0);
|
|
incaddrsram();
|
|
writesram(0);
|
|
incaddrsram();
|
|
writesram(0);
|
|
incaddrsram();
|
|
writesram(0);
|
|
incaddrsram();
|
|
writesram(0);
|
|
incaddrsram();
|
|
|
|
ZXUNOADDR = 0xb;
|
|
ZXUNODATA = scandblctrl;
|
|
}
|
|
|
|
WORD readblocktag (BYTE handle)
|
|
{
|
|
return read (handle, buffer, 8);
|
|
}
|
|
|
|
WORD readword (BYTE handle)
|
|
{
|
|
WORD res;
|
|
read (handle, &res, 2);
|
|
return res;
|
|
}
|
|
|
|
void skipblock (BYTE handle, WORD hiskip, WORD loskip)
|
|
{
|
|
seek (handle, hiskip, loskip, SEEK_CUR);
|
|
}
|
|
|
|
void copyblock (BYTE handle, WORD hicopy, WORD locopy)
|
|
{
|
|
//print16bhex(hicopy);
|
|
//print16bhex(locopy); puts("\r");
|
|
while (hicopy!=0 || locopy>=BUFSIZE)
|
|
{
|
|
read (handle, buffer, BUFSIZE);
|
|
copysram (buffer, BUFSIZE);
|
|
if ((locopy-BUFSIZE)>locopy)
|
|
hicopy--;
|
|
locopy -= BUFSIZE;
|
|
//print16bhex(hicopy);
|
|
//print16bhex(locopy); puts("\r");
|
|
}
|
|
if (locopy>0)
|
|
{
|
|
//print16bhex(hicopy);
|
|
//print16bhex(locopy); puts("\r");
|
|
read (handle, buffer, locopy);
|
|
copysram (buffer, locopy);
|
|
}
|
|
}
|
|
|
|
void convertpaus2puls (BYTE handle)
|
|
{
|
|
BYTE pause[10] = {0x6, 0, 0, 0, 0x1, 0x80, 0, 0, 0, 0};
|
|
|
|
writesram(0x2);
|
|
incaddrsram();
|
|
read (handle, buffer, 4);
|
|
pause[6] = buffer[2];
|
|
pause[7] = buffer[3] | 0x80;
|
|
pause[8] = buffer[0];
|
|
pause[9] = buffer[1];
|
|
copysram (pause,10);
|
|
}
|
|
|
|
void rewindsram (void)
|
|
{
|
|
ZXUNOADDR = SRAMADDR;
|
|
ZXUNODATA = 0;
|
|
ZXUNODATA = 0;
|
|
ZXUNODATA = 0;
|
|
}
|
|
|
|
void writesram (BYTE v)
|
|
{
|
|
ZXUNOADDR = SRAMDATA;
|
|
ZXUNODATA = v;
|
|
}
|
|
|
|
void incaddrsram (void)
|
|
{
|
|
ZXUNOADDR = SRAMADDRINC;
|
|
ZXUNODATA = 0;
|
|
}
|
|
|
|
void copysram (BYTE *p, WORD l)
|
|
{
|
|
while (l--)
|
|
{
|
|
ZXUNOADDR = SRAMDATA;
|
|
ZXUNODATA = *p++;
|
|
ZXUNOADDR = SRAMADDRINC;
|
|
ZXUNODATA = 0;
|
|
}
|
|
}
|
|
|
|
void getcoreid(BYTE *s)
|
|
{
|
|
BYTE len;
|
|
volatile BYTE c;
|
|
|
|
ZXUNOADDR = COREID;
|
|
len=0;
|
|
|
|
do
|
|
{
|
|
c = ZXUNODATA;
|
|
*(s++) = c;
|
|
len++;
|
|
}
|
|
while (c!=0 && len<32);
|
|
*s='\0';
|
|
}
|
|
|
|
|
|
/* --------------------------------------------------------------------------------- */
|
|
/* --------------------------------------------------------------------------------- */
|
|
/* --------------------------------------------------------------------------------- */
|
|
#pragma disable_warning 85
|
|
#pragma disable_warning 59
|
|
void memset (BYTE *dir, BYTE val, WORD nby)
|
|
{
|
|
__asm
|
|
push bc
|
|
push de
|
|
ld l, 4(ix)
|
|
ld h, 5(ix)
|
|
ld a, 6(ix)
|
|
ld c, 7(ix)
|
|
ld b, 8(ix)
|
|
ld d, h
|
|
ld e, l
|
|
inc de
|
|
dec bc
|
|
ld (hl), a
|
|
ldir
|
|
pop de
|
|
pop bc
|
|
__endasm;
|
|
}
|
|
|
|
void memcpy (BYTE *dst, BYTE *fue, WORD nby)
|
|
{
|
|
__asm
|
|
push bc
|
|
push de
|
|
ld e, 4(ix)
|
|
ld d, 5(ix)
|
|
ld l, 6(ix)
|
|
ld h, 7(ix)
|
|
ld c, 8(ix)
|
|
ld b, 9(ix)
|
|
ldir
|
|
pop de
|
|
pop bc
|
|
__endasm;
|
|
}
|
|
|
|
void puts (BYTE *str)
|
|
{
|
|
__asm
|
|
push bc
|
|
push de
|
|
ld a, (#ATTRT)
|
|
push af
|
|
ld a, (#ATTRP)
|
|
ld (#ATTRT), a
|
|
ld l, 4(ix)
|
|
ld h, 5(ix)
|
|
print_loop:
|
|
ld a, (hl)
|
|
or a
|
|
jp z, end_print
|
|
cp #4
|
|
jr nz, no_attr
|
|
inc hl
|
|
ld a, (hl)
|
|
ld (#ATTRT), a
|
|
inc hl
|
|
jr print_loop
|
|
no_attr:
|
|
rst #16
|
|
inc hl
|
|
jp print_loop
|
|
|
|
end_print:
|
|
pop af
|
|
ld (#ATTRT), a
|
|
pop de
|
|
pop bc
|
|
__endasm;
|
|
}
|
|
|
|
// void u16tohex (WORD n, char *s)
|
|
// {
|
|
// u8tohex((n>>8)&0xFF,s);
|
|
// u8tohex(n&0xFF,s+2);
|
|
// }
|
|
//
|
|
// void u8tohex (BYTE n, char *s)
|
|
// {
|
|
// BYTE i=1;
|
|
// BYTE rest;
|
|
//
|
|
// rest=n&0xF;
|
|
// s[1]=(rest>9)?rest+55:rest+48;
|
|
// rest=n>>4;
|
|
// s[0]=(rest>9)?rest+55:rest+48;
|
|
// s[2]='\0';
|
|
// }
|
|
//
|
|
// void print8bhex (BYTE n)
|
|
// {
|
|
// char s[3];
|
|
//
|
|
// u8tohex(n,s);
|
|
// puts(s);
|
|
// }
|
|
//
|
|
// void print16bhex (WORD n)
|
|
// {
|
|
// char s[5];
|
|
//
|
|
// u16tohex(n,s);
|
|
// puts(s);
|
|
// }
|
|
|
|
void __sdcc_enter_ix (void) __naked
|
|
{
|
|
__asm
|
|
pop hl ; return address
|
|
push ix ; save frame pointer
|
|
ld ix, #0
|
|
add ix, sp ; set ix to the stack frame
|
|
jp (hl) ; and return
|
|
__endasm;
|
|
}
|
|
|
|
BYTE open (char *filename, BYTE mode)
|
|
{
|
|
__asm
|
|
push bc
|
|
push de
|
|
xor a
|
|
rst #8
|
|
.db #M_GETSETDRV ; A = default drive
|
|
ld l, 4(ix) ; HL = pointer to filename (ASCIIZ)
|
|
ld h, 5(ix) ;
|
|
ld b, 6(ix) ; B = file open mode
|
|
rst #8
|
|
.db #F_OPEN
|
|
jr nc, open_ok
|
|
ld (#_errno), a
|
|
ld a, #0xff
|
|
open_ok:
|
|
ld l, a
|
|
pop de
|
|
pop bc
|
|
__endasm;
|
|
}
|
|
|
|
void close (BYTE handle)
|
|
{
|
|
__asm
|
|
push bc
|
|
push de
|
|
ld a, 4(ix) ; A = file handle
|
|
rst #8
|
|
.db #F_CLOSE
|
|
pop de
|
|
pop bc
|
|
__endasm;
|
|
}
|
|
|
|
WORD read (BYTE handle, BYTE *buffer, WORD nbytes)
|
|
{
|
|
__asm
|
|
push bc
|
|
push de
|
|
ld a, 4(ix) ; A = file handle
|
|
ld l, 5(ix) ; HL = buffer address
|
|
ld h, 6(ix) ;
|
|
ld c, 7(ix) ; BC = buffer length
|
|
ld b, 8(ix) ;
|
|
rst #8
|
|
.db #F_READ
|
|
jr nc, read_ok
|
|
ld (#_errno), a
|
|
ld bc, #65535
|
|
read_ok:
|
|
ld h, b
|
|
ld l, c
|
|
pop de
|
|
pop bc
|
|
__endasm;
|
|
}
|
|
|
|
WORD write (BYTE handle, BYTE *buffer, WORD nbytes)
|
|
{
|
|
__asm
|
|
push bc
|
|
push de
|
|
ld a, 4(ix) ; A = file handle
|
|
ld l, 5(ix) ; HL = buffer address
|
|
ld h, 6(ix) ;
|
|
ld c, 7(ix) ; BC = buffer length
|
|
ld b, 8(ix) ;
|
|
rst #8
|
|
.db #F_WRITE
|
|
jr nc, write_ok
|
|
ld (#_errno),a
|
|
ld bc, #65535
|
|
write_ok:
|
|
ld h, b
|
|
ld l, c
|
|
pop de
|
|
pop bc
|
|
__endasm;
|
|
}
|
|
|
|
void seek (BYTE handle, WORD hioff, WORD looff, BYTE from)
|
|
{
|
|
__asm
|
|
push bc
|
|
push de
|
|
ld a, 4(ix) ; A = file handle
|
|
ld c, 5(ix) ; BC = MSW offset
|
|
ld b, 6(ix) ;
|
|
ld e, 7(ix) ; DE = LSW offset
|
|
ld d, 8(ix) ;
|
|
ld l, 9(ix) ; L = whence (0: start, 1: forward current pos, 2: backwards current pos)
|
|
rst #8
|
|
.db #F_SEEK
|
|
pop de
|
|
pop bc
|
|
__endasm;
|
|
}
|