diff --git a/build.sh b/build.sh index 84bcaa1..8b0fb72 100755 --- a/build.sh +++ b/build.sh @@ -17,6 +17,8 @@ then clang-format -i src/devices/ppu.c clang-format -i src/devices/apu.h clang-format -i src/devices/apu.c + clang-format -i src/devices/file.h + clang-format -i src/devices/file.c clang-format -i src/uxnasm.c clang-format -i src/uxnemu.c clang-format -i src/uxncli.c @@ -49,8 +51,8 @@ fi echo "Building.." cc ${CFLAGS} src/uxnasm.c -o bin/uxnasm -cc ${CFLAGS} ${CORE} src/devices/ppu.c src/devices/apu.c src/uxnemu.c ${UXNEMU_LDFLAGS} -o bin/uxnemu -cc ${CFLAGS} ${CORE} src/uxncli.c -o bin/uxncli +cc ${CFLAGS} ${CORE} src/devices/file.c src/devices/ppu.c src/devices/apu.c src/uxnemu.c ${UXNEMU_LDFLAGS} -o bin/uxnemu +cc ${CFLAGS} ${CORE} src/devices/file.c src/uxncli.c -o bin/uxncli if [ -d "$HOME/bin" ] then diff --git a/projects/examples/blank.tal b/projects/examples/blank.tal index 9ef66e5..24d1808 100644 --- a/projects/examples/blank.tal +++ b/projects/examples/blank.tal @@ -21,7 +21,7 @@ |60 @Audio3 [ &vector $2 &position $2 &output $1 &pad $3 &adsr $2 &length $2 &addr $2 &volume $1 &pitch $1 ] |80 @Controller [ &vector $2 &button $1 &key $1 ] |90 @Mouse [ &vector $2 &x $2 &y $2 &state $1 &wheel $1 ] -|a0 @File [ &vector $2 &success $2 &offset-hs $2 &offset-ls $2 &name $2 &length $2 &load $2 &save $2 ] +|a0 @File [ &vector $2 &name $2 &length $2 &success $2 &load $2 &save $2 &stat $2 &delete $1 ] |b0 @DateTime [ &year $2 &month $1 &day $1 &hour $1 &minute $1 &second $1 &dotw $1 &doty $2 &isdst $1 ] ( variables ) diff --git a/projects/examples/demos/asma-piano.tal b/projects/examples/demos/asma-piano.tal index 1837751..a6ae192 100644 --- a/projects/examples/demos/asma-piano.tal +++ b/projects/examples/demos/asma-piano.tal @@ -2,7 +2,7 @@ |00 @System [ &vector $2 &wst $1 &rst $1 &pad $4 &r $2 &g $2 &b $2 &debug $1 &halt $1 ] |10 @Console [ &vector $2 &read $1 &pad $5 &write $1 &error $1 ] -|a0 @File [ &vector $2 &success $2 &offset-hs $2 &offset-ls $2 &name $2 &length $2 &load $2 &save $2 ] +|a0 @File [ &vector $2 &name $2 &length $2 &success $2 &load $2 &save $2 &stat $2 &delete $1 ] ( vectors ) @@ -51,7 +51,6 @@ that will prevent an infinite loop. ) ;&dest-file .File/name DEO2 - #0000 .File/offset-ls DEO2 #ff00 .File/length DEO2 #0100 .File/load LIT DEO2 #00ff STA diff --git a/projects/examples/demos/drum-rack.tal b/projects/examples/demos/drum-rack.tal index 8b339dc..8c7c2d0 100644 --- a/projects/examples/demos/drum-rack.tal +++ b/projects/examples/demos/drum-rack.tal @@ -30,7 +30,7 @@ |70 @Midi [ &vector $2 &channel $1 ¬e $1 &velocity $1 ] |80 @Controller [ &vector $2 &button $1 &key $1 ] |90 @Mouse [ &vector $2 &x $2 &y $2 &state $1 &wheel $1 ] -|a0 @File [ &vector $2 &success $2 &offset-hs $2 &offset-ls $2 &name $2 &length $2 &load $2 &save $2 ] +|a0 @File [ &vector $2 &name $2 &length $2 &success $2 &load $2 &save $2 &stat $2 &delete $1 ] ( variables ) diff --git a/projects/examples/demos/font.tal b/projects/examples/demos/font.tal index 004a82e..cfb1cf2 100644 --- a/projects/examples/demos/font.tal +++ b/projects/examples/demos/font.tal @@ -12,7 +12,7 @@ |00 @System &vector $2 &pad $6 &r $2 &g $2 &b $2 |20 @Screen &vector $2 &width $2 &height $2 &pad $2 &x $2 &y $2 &addr $2 &pixel $1 &sprite $1 -|a0 @File &vector $2 &success $2 &offset-hs $2 &offset-ls $2 &name $2 &length $2 &load $2 &save $2 +|a0 @File &vector $2 &name $2 &length $2 &success $2 &load $2 &save $2 &stat $2 &delete $1 ( variables ) diff --git a/projects/examples/demos/piano.tal b/projects/examples/demos/piano.tal index e12e257..2baac75 100644 --- a/projects/examples/demos/piano.tal +++ b/projects/examples/demos/piano.tal @@ -27,7 +27,7 @@ |70 @Midi [ &vector $2 &channel $1 ¬e $1 &velocity $1 ] |80 @Controller [ &vector $2 &button $1 &key $1 ] |90 @Mouse [ &vector $2 &x $2 &y $2 &state $1 &wheel $1 ] -|a0 @File [ &vector $2 &success $2 &offset-hs $2 &offset-ls $2 &name $2 &length $2 &load $2 &save $2 ] +|a0 @File [ &vector $2 &name $2 &length $2 &success $2 &load $2 &save $2 &stat $2 &delete $1 ] ( variables ) diff --git a/projects/examples/devices/file.load.tal b/projects/examples/devices/file.load.tal index a3369d8..97372e5 100644 --- a/projects/examples/devices/file.load.tal +++ b/projects/examples/devices/file.load.tal @@ -4,7 +4,7 @@ |00 @System [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ] |20 @Screen [ &vector $2 &width $2 &height $2 &pad $2 &x $2 &y $2 &addr $2 &pixel $1 &sprite $1 ] -|a0 @File [ &vector $2 &success $2 &offset-hs $2 &offset-ls $2 &name $2 &length $2 &load $2 &save $2 ] +|a0 @File [ &vector $2 &name $2 &length $2 &success $2 &load $2 &save $2 &stat $2 &delete $1 ] ( variables ) diff --git a/projects/examples/devices/file.save.tal b/projects/examples/devices/file.save.tal index 3fa9e69..0d24c50 100644 --- a/projects/examples/devices/file.save.tal +++ b/projects/examples/devices/file.save.tal @@ -3,7 +3,7 @@ ( devices ) |00 @System [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ] -|a0 @File [ &vector $2 &success $2 &offset-hs $2 &offset-ls $2 &name $2 &length $2 &load $2 &save $2 ] +|a0 @File [ &vector $2 &name $2 &length $2 &success $2 &load $2 &save $2 &stat $2 &delete $1 ] ( variables ) diff --git a/projects/examples/devices/file.tal b/projects/examples/devices/file.tal index cb8f4fc..8f32eba 100644 --- a/projects/examples/devices/file.tal +++ b/projects/examples/devices/file.tal @@ -8,7 +8,7 @@ |00 @System [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ] |10 @Console [ &pad $8 &write $1 ] |20 @Screen [ &vector $2 &width $2 &height $2 &pad $2 &x $2 &y $2 &addr $2 &pixel $1 &sprite $1 ] -|a0 @File [ &vector $2 &success $2 &offset-hs $2 &offset-ls $2 &name $2 &length $2 &load $2 &save $2 ] +|a0 @File [ &vector $2 &name $2 &length $2 &success $2 &load $2 &save $2 &stat $2 &delete $1 ] ( variables ) diff --git a/projects/examples/gui/picture.tal b/projects/examples/gui/picture.tal index 0878d68..c7ea9c1 100644 --- a/projects/examples/gui/picture.tal +++ b/projects/examples/gui/picture.tal @@ -7,7 +7,7 @@ |00 @System [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ] |20 @Screen [ &vector $2 &width $2 &height $2 &auto $1 &pad $1 &x $2 &y $2 &addr $2 &pixel $1 &sprite $1 ] -|a0 @File [ &vector $2 &success $2 &offset-hs $2 &offset-ls $2 &name $2 &length $2 &load $2 &save $2 ] +|a0 @File [ &vector $2 &name $2 &length $2 &success $2 &load $2 &save $2 &stat $2 &delete $1 ] ( variables ) diff --git a/projects/library/asma.tal b/projects/library/asma.tal index 6ef9ab4..b41b0c6 100644 --- a/projects/library/asma.tal +++ b/projects/library/asma.tal @@ -196,12 +196,11 @@ @asma-init-next-pass ( -- ) ;asma/pass LDA INC ;asma/pass STA ;asma-write-buffer ;asma-output/ptr STA2 - #0000 DUP2k - ;asma-output/offset STA2 + #0000 DUP2 ;asma/addr STA2 ;asma/state STA #01 SWP ( 0100 ) ;asma/written-addr STA2 - ;&preamble-end ;&preamble SUB2k ,asma-assemble-chunk JSR POP2 POP2 + ;&preamble-end ;&preamble SUB2k ;asma-assemble-chunk JSR2 POP2 POP2 JMP2r &preamble @@ -498,15 +497,14 @@ include projects/library/binary-tree.tal NIP2 ( start* ) ,&after-flush JMP -@asma-output [ &ptr $2 &offset $2 ] +@asma-output [ &ptr $2 ] @asma-flush-ignore ( len* -- ) POP2 JMP2r @asma-flush-to-file ( len* -- ) - DUP2 .File/length DEO2 - ,asma-output/offset LDR2 DUP2 .File/offset-ls DEO2 ADD2 ,asma-output/offset STR2 + .File/length DEO2 ;asma/dest-filename LDA2 .File/name DEO2 ;asma-write-buffer .File/save DEO2 JMP2r diff --git a/projects/library/file-read-chunks.tal b/projects/library/file-read-chunks.tal index 7275020..5051cad 100644 --- a/projects/library/file-read-chunks.tal +++ b/projects/library/file-read-chunks.tal @@ -2,6 +2,14 @@ # Summary +*** CAUTION: this library is deprecated! *** + +Chunked file reads are now possible in the File device directly: just use +File/load or File/save multiple times. This library exists for compatibility to +keep asma going until it gets a more substantial rewrite. + +*** + Reads a file in chunks - perfect for when you have a small buffer or when you don't know the file size. Copes with files up to 4,294,967,295 bytes long. @@ -17,9 +25,11 @@ don't know the file size. Copes with files up to 4,294,967,295 bytes long. &loop STH2kr .File/name DEO2 ( F* U* B* OL* OH* SZ* / FN* ) - STH2k .File/length DEO2 ( F* U* B* OL* OH* / FN* SZ* ) - STH2k .File/offset-hs DEO2 ( F* U* B* OL* / FN* SZ* OH* ) - STH2k .File/offset-ls DEO2 ( F* U* B* / FN* SZ* OH* OL* ) + STH2k ,ffwd/length STR2 ( F* U* B* OL* OH* / FN* SZ* ) + STH2 ( F* U* B* OL* / FN* SZ* OH* ) + STH2k ,ffwd/offset STR2 ( F* U* B* / FN* SZ* OH* OL* ) + DUP2 ,ffwd/addr STR2 + ,ffwd JSR SWP2 ( F* B* U* / FN* SZ* OH* OL* ) ROT2k NIP2 ( F* B* U* B* F* / FN* SZ* OH* OL* ) OVR2 .File/load DEO2 ( F* B* U* B* F* / FN* SZ* OH* OL* ) @@ -40,6 +50,25 @@ don't know the file size. Copes with files up to 4,294,967,295 bytes long. STH2r STH2r ( F* U'* B* OL'* OH'* SZ* / FN* ) ,&loop JMP +@ffwd + LIT2 &length $2 + LIT2 &offset $2 + + &coarse ( length* offset* ) + GTH2k ,&fine JCN + OVR2 .File/length DEO2 + ,&addr LDR2 .File/load DEO2 + OVR2 SUB2 + ,&coarse JMP + + &fine ( length* offset* ) + .File/length DEO2 ( length* ) + ,&addr LDR2 .File/load DEO2 + .File/length DEO2 ( ) + JMP2r + + &addr $2 + ( # Arguments diff --git a/projects/library/load-rom.tal b/projects/library/load-rom.tal index 332369c..85b0bac 100644 --- a/projects/library/load-rom.tal +++ b/projects/library/load-rom.tal @@ -11,8 +11,6 @@ ) .File/name DEO2 - #0000 .File/offset-hs DEO2 - #0000 .File/offset-ls DEO2 ( return if file can't be found, or zero length ) #0001 .File/length DEO2 diff --git a/projects/software/asma.tal b/projects/software/asma.tal index 6dabd92..d6b2805 100644 --- a/projects/software/asma.tal +++ b/projects/software/asma.tal @@ -1,8 +1,8 @@ ( devices ) -|00 @System [ &vector $2 &wst $1 &rst $1 &pad $4 &r $2 &g $2 &b $2 &debug $1 &halt $1 ] -|10 @Console [ &vector $2 &read $1 &pad $5 &write $1 &error $1 ] -|a0 @File [ &vector $2 &success $2 &offset-hs $2 &offset-ls $2 &name $2 &length $2 &load $2 &save $2 ] +|00 @System [ &vector $2 &wst $1 &rst $1 &pad $4 &r $2 &g $2 &b $2 &debug $1 &halt $1 ] +|10 @Console [ &vector $2 &read $1 &pad $5 &write $1 &error $1 ] +|a0 @File [ &vector $2 &name $2 &length $2 &success $2 &load $2 &save $2 &stat $2 &delete $1 ] ( vectors ) diff --git a/projects/software/calc.tal b/projects/software/calc.tal index bde84bb..d080849 100644 --- a/projects/software/calc.tal +++ b/projects/software/calc.tal @@ -35,7 +35,7 @@ |30 @Audio0 [ &vector $2 &position $2 &output $1 &pad $3 &adsr $2 &length $2 &addr $2 &volume $1 &pitch $1 ] |80 @Controller [ &vector $2 &button $1 &key $1 ] |90 @Mouse [ &vector $2 &x $2 &y $2 &state $1 &wheel $1 ] -|a0 @File [ &vector $2 &success $2 &offset-hs $2 &offset-ls $2 &name $2 &length $2 &load $2 &save $2 ] +|a0 @File [ &vector $2 &name $2 &length $2 &success $2 &load $2 &save $2 &stat $2 &delete $1 ] ( variables ) diff --git a/projects/software/launcher.tal b/projects/software/launcher.tal index 79af358..b374d76 100644 --- a/projects/software/launcher.tal +++ b/projects/software/launcher.tal @@ -22,7 +22,7 @@ |20 @Screen &vector $2 &width $2 &height $2 &auto $1 &pad $1 &x $2 &y $2 &addr $2 &pixel $1 &sprite $1 |80 @Controller [ &vector $2 &button $1 &key $1 ] |90 @Mouse [ &vector $2 &x $2 &y $2 &state $1 &wheel $1 ] -|a0 @File &vector $2 &success $2 &offset-hs $2 &offset-ls $2 &name $2 &length $2 &load $2 &save $2 +|a0 @File &vector $2 &name $2 &length $2 &success $2 &load $2 &save $2 &stat $2 &delete $1 |b0 @DateTime [ &year $2 &month $1 &day $1 &hour $1 &minute $1 &second $1 &dotw $1 &doty $2 &isdst $1 ] ( variables ) diff --git a/src/devices/file.c b/src/devices/file.c new file mode 100644 index 0000000..1409cbc --- /dev/null +++ b/src/devices/file.c @@ -0,0 +1,135 @@ +#include "../uxn.h" +#include "file.h" + +/* +Copyright (c) 2021 Devine Lu Linvega +Copyright (c) 2021 Andrew Alderwick + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE. +*/ + +#define _POSIX_C_SOURCE 200809L + +#include +#include +#include +#include +#include + +static FILE *f; +static DIR *d; +static int dir_fd; +static char *current_filename; +static enum { IDLE, + FILE_READ, + FILE_WRITE, + DIR_READ } state; +static struct dirent *de; + +static char hex[] = "0123456789abcdef"; + +static void +reset(void) +{ + if(f != NULL) { + fclose(f); + f = NULL; + } + if(d != NULL) { + closedir(d); + d = NULL; + } + de = NULL; + state = IDLE; +} + +void +file_prepare(void *filename) +{ + reset(); + current_filename = (char *)filename; +} + +static Uint16 +write_entry(char *p, Uint16 len, const char *filename, struct stat *st) +{ + if(len < strlen(filename) + 7) return 0; + memcpy(p, "???? ", 5); + strcpy(p + 5, filename); + strcat(p, "\n"); + if(S_ISDIR(st->st_mode)) { + memcpy(p, "---- ", 5); + } else if(st->st_size < 0x10000) { + p[0] = hex[(st->st_size >> 12) & 0xf]; + p[1] = hex[(st->st_size >> 8) & 0xf]; + p[2] = hex[(st->st_size >> 4) & 0xf]; + p[3] = hex[(st->st_size >> 0) & 0xf]; + } + return strlen(p); +} + +Uint16 +file_read(void *dest, Uint16 len) +{ + if(state != FILE_READ && state != DIR_READ) { + reset(); + if((d = opendir(current_filename)) != NULL) { + state = DIR_READ; + dir_fd = dirfd(d); + } else if((f = fopen(current_filename, "rb")) != NULL) + state = FILE_READ; + } + if(state == FILE_READ) + return fread(dest, 1, len, f); + if(state == DIR_READ) { + char *p = dest; + if(de == NULL) de = readdir(d); + for(; de != NULL; de = readdir(d)) { + struct stat st; + Uint16 n; + if(de->d_name[0] == '.' && de->d_name[1] == '\0') + continue; + if(fstatat(dir_fd, de->d_name, &st, 0)) + continue; + n = write_entry(p, len, de->d_name, &st); + if(!n) break; + p += n; + len -= n; + } + return p - (char *)dest; + } + return 0; +} + +Uint16 +file_write(void *src, Uint16 len) +{ + if(state != FILE_WRITE) { + reset(); + if((f = fopen(current_filename, "ab")) != NULL) + state = FILE_WRITE; + } + if(state == FILE_WRITE) + return fwrite(src, 1, len, f); + return 0; +} + +Uint16 +file_stat(void *dest, Uint16 len) +{ + struct stat st; + if(stat(current_filename, &st)) + return 0; + return write_entry((char *)dest, len, current_filename, &st); +} + +Uint16 +file_delete(void) +{ + return unlink(current_filename); +} diff --git a/src/devices/file.h b/src/devices/file.h new file mode 100644 index 0000000..7fd3dfa --- /dev/null +++ b/src/devices/file.h @@ -0,0 +1,17 @@ +/* +Copyright (c) 2021 Devine Lu Linvega +Copyright (c) 2021 Andrew Alderwick + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE. +*/ + +void file_prepare(void *filename); +Uint16 file_read(void *dest, Uint16 len); +Uint16 file_write(void *src, Uint16 len); +Uint16 file_stat(void *dest, Uint16 len); +Uint16 file_delete(); diff --git a/src/uxncli.c b/src/uxncli.c index 0f2c7d4..5407e85 100644 --- a/src/uxncli.c +++ b/src/uxncli.c @@ -2,6 +2,7 @@ #include #include #include "uxn.h" +#include "devices/file.h" /* Copyright (c) 2021 Devine Lu Linvega @@ -78,19 +79,22 @@ console_deo(Device *d, Uint8 port) static void file_deo(Device *d, Uint8 port) { - Uint8 read = port == 0xd; - if(read || port == 0xf) { - char *name = (char *)&d->mem[peek16(d->dat, 0x8)]; - Uint16 result = 0, length = peek16(d->dat, 0xa); - long offset = (peek16(d->dat, 0x4) << 16) + peek16(d->dat, 0x6); - Uint16 addr = peek16(d->dat, port - 1); - FILE *f = fopen(name, read ? "rb" : (offset ? "ab" : "wb")); - if(f) { - if(fseek(f, offset, SEEK_SET) != -1) - result = read ? fread(&d->mem[addr], 1, length, f) : fwrite(&d->mem[addr], 1, length, f); - fclose(f); - } - poke16(d->dat, 0x2, result); + switch(port) { + case 0x3: + file_prepare(&d->mem[peek16(d->dat, 0x2)]); + break; + case 0x9: + poke16(d->dat, 0x6, file_read(&d->mem[peek16(d->dat, 0x8)], peek16(d->dat, 0x4))); + break; + case 0xb: + poke16(d->dat, 0x6, file_write(&d->mem[peek16(d->dat, 0xa)], peek16(d->dat, 0x4))); + break; + case 0xd: + poke16(d->dat, 0x6, file_stat(&d->mem[peek16(d->dat, 0xc)], peek16(d->dat, 0x4))); + break; + case 0xe: + poke16(d->dat, 0x6, file_delete()); + break; } } diff --git a/src/uxnemu.c b/src/uxnemu.c index 34af6e5..28e2fff 100644 --- a/src/uxnemu.c +++ b/src/uxnemu.c @@ -8,6 +8,7 @@ #include #include "devices/ppu.h" #include "devices/apu.h" +#include "devices/file.h" #pragma GCC diagnostic pop /* @@ -370,19 +371,22 @@ screen_deo(Device *d, Uint8 port) static void file_deo(Device *d, Uint8 port) { - Uint8 read = port == 0xd; - if(read || port == 0xf) { - char *name = (char *)&d->mem[peek16(d->dat, 0x8)]; - Uint16 result = 0, length = peek16(d->dat, 0xa); - long offset = (peek16(d->dat, 0x4) << 16) + peek16(d->dat, 0x6); - Uint16 addr = peek16(d->dat, port - 1); - FILE *f = fopen(name, read ? "rb" : (offset ? "ab" : "wb")); - if(f) { - if(fseek(f, offset, SEEK_SET) != -1) - result = read ? fread(&d->mem[addr], 1, length, f) : fwrite(&d->mem[addr], 1, length, f); - fclose(f); - } - poke16(d->dat, 0x2, result); + switch(port) { + case 0x3: + file_prepare(&d->mem[peek16(d->dat, 0x2)]); + break; + case 0x9: + poke16(d->dat, 0x6, file_read(&d->mem[peek16(d->dat, 0x8)], peek16(d->dat, 0x4))); + break; + case 0xb: + poke16(d->dat, 0x6, file_write(&d->mem[peek16(d->dat, 0xa)], peek16(d->dat, 0x4))); + break; + case 0xd: + poke16(d->dat, 0x6, file_stat(&d->mem[peek16(d->dat, 0xc)], peek16(d->dat, 0x4))); + break; + case 0xe: + poke16(d->dat, 0x6, file_delete()); + break; } }