From 0dd7b0c3a5204ac96ae6067f3d48cbfc4f62ec05 Mon Sep 17 00:00:00 2001
From: Colin Leroy-Mira <colin@colino.net>
Date: Thu, 25 Jan 2024 09:12:46 +0100
Subject: [PATCH] Implement __sysremove for sim65

This will allow using unlink()/remove() in sim65 programs
Use it to unlink fgets' test output file
---
 libsrc/sim6502/paravirt.s |  5 ++++
 src/sim65/paravirt.c      | 43 ++++++++++++++++++++++++++++--
 src/sim65/paravirt.h      |  6 ++---
 test/ref/test_fgets.c     |  2 ++
 test/val/remove.c         | 55 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 106 insertions(+), 5 deletions(-)
 create mode 100644 test/val/remove.c

diff --git a/libsrc/sim6502/paravirt.s b/libsrc/sim6502/paravirt.s
index 0d8e528b1..3bd40fbe4 100644
--- a/libsrc/sim6502/paravirt.s
+++ b/libsrc/sim6502/paravirt.s
@@ -8,10 +8,15 @@
 ;
 
         .export         exit, args, _open, _close, _read, _write
+        .export         __sysremove, ___osmaperrno
 
+__sysremove     := $FFF2
+___osmaperrno   := $FFF3
 _open           := $FFF4
 _close          := $FFF5
 _read           := $FFF6
 _write          := $FFF7
 args            := $FFF8
 exit            := $FFF9
+
+                ; $FFFA-FFFF are hardware vectors, extend before not after!
diff --git a/src/sim65/paravirt.c b/src/sim65/paravirt.c
index 2e52d6e7e..141bcd2bd 100644
--- a/src/sim65/paravirt.c
+++ b/src/sim65/paravirt.c
@@ -163,7 +163,7 @@ static void PVArgs (CPURegs* Regs)
 
 static void PVOpen (CPURegs* Regs)
 {
-    char Path[PVOPEN_PATH_SIZE];
+    char Path[PV_PATH_SIZE];
     int OFlag = O_INITIAL;
     int OMode = 0;
     unsigned RetVal, I = 0;
@@ -184,7 +184,7 @@ static void PVOpen (CPURegs* Regs)
             break;
         }
         ++I;
-        if (I >= PVOPEN_PATH_SIZE) {
+        if (I >= PV_PATH_SIZE) {
             Error("PVOpen path too long at address $%04X",Name);
         }
     }
@@ -253,6 +253,35 @@ static void PVClose (CPURegs* Regs)
 
 
 
+static void PVSysRemove (CPURegs* Regs)
+{
+    char Path[PV_PATH_SIZE];
+    unsigned RetVal, I = 0;
+
+    unsigned Name  = GetAX (Regs);
+
+    Print (stderr, 2, "PVSysRemove ($%04X)\n", Name);
+
+    do {
+        if (!(Path[I] = MemReadByte ((Name + I) & 0xFFFF))) {
+            break;
+        }
+        ++I;
+        if (I >= PV_PATH_SIZE) {
+            Error("PVSysRemove path too long at address $%04X", Name);
+        }
+    }
+    while (1);
+
+    Print (stderr, 2, "PVSysRemove (\"%s\")\n", Path);
+
+    RetVal = remove (Path);
+
+    SetAX (Regs, RetVal);
+}
+
+
+
 static void PVRead (CPURegs* Regs)
 {
     unsigned char* Data;
@@ -305,7 +334,17 @@ static void PVWrite (CPURegs* Regs)
 
 
 
+static void PVOSMapErrno (CPURegs* Regs)
+{
+    unsigned err = GetAX(Regs);
+    SetAX (Regs, err != 0 ? -1 : 0);
+}
+
+
+
 static const PVFunc Hooks[] = {
+    PVSysRemove,
+    PVOSMapErrno,
     PVOpen,
     PVClose,
     PVRead,
diff --git a/src/sim65/paravirt.h b/src/sim65/paravirt.h
index 3badb50ea..f3281705e 100644
--- a/src/sim65/paravirt.h
+++ b/src/sim65/paravirt.h
@@ -44,11 +44,11 @@
 
 
 
-#define PARAVIRT_BASE        0xFFF4
+#define PARAVIRT_BASE        0xFFF2
 /* Lowest address used by a paravirtualization hook */
 
-#define PVOPEN_PATH_SIZE       1024
-/* Maximum path size supported by PVOpen */
+#define PV_PATH_SIZE         1024
+/* Maximum path size supported by PVOpen/PVSysRemove */
 
 
 
diff --git a/test/ref/test_fgets.c b/test/ref/test_fgets.c
index 72ea308dd..0529b1651 100644
--- a/test/ref/test_fgets.c
+++ b/test/ref/test_fgets.c
@@ -10,6 +10,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 
 FILE *in, *out;
 char buf[32];
@@ -34,6 +35,7 @@ int main(int argc,char **argv)
       printf("Error: file pointer should be in error state\n");
     }
     fclose(out);
+    unlink(outfile_path);
 
     in = fopen(INFILE, "rb");
     if (in == NULL) {
diff --git a/test/val/remove.c b/test/val/remove.c
new file mode 100644
index 000000000..eecf8be8f
--- /dev/null
+++ b/test/val/remove.c
@@ -0,0 +1,55 @@
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+int fails = 0;
+
+
+static void create_out_file(const char *outfile_path) {
+    FILE *out;
+
+
+    out = fopen(outfile_path, "wb");
+    if (out == NULL) {
+        printf("Could not create %s\n", outfile_path);
+        fails++;
+        return;
+    }
+    fclose(out);
+}
+
+int main (int argc, char **argv)
+{
+    int r;
+    static char outfile_path[FILENAME_MAX+1];
+
+    sprintf(outfile_path, "%s.test.out", argv[0]);
+
+    create_out_file(outfile_path);
+    r = remove(outfile_path);
+    if (r != 0) {
+        printf("could not remove() %s\n", outfile_path);
+        fails++;
+    }
+
+    create_out_file(outfile_path);
+    r = unlink(outfile_path);
+    if (r != 0) {
+        printf("could not unlink() %s\n", outfile_path);
+        fails++;
+    }
+
+    r = remove("klsdfjqlsjdflkqjdsoizu");
+    if (r == 0) {
+        printf("remove()ing non-existent file succeeded\n");
+        fails++;
+    }
+
+    r = unlink("klsdfjqlsjdflkqjdsoizu");
+    if (r == 0) {
+        printf("unlink()ing non-existent file succeeded\n");
+        fails++;
+    }
+
+    return fails;
+}