247 lines
7.9 KiB
C
247 lines
7.9 KiB
C
|
/* RETRO --------------------------------------------------------------
|
||
|
A personal, minimalistic forth
|
||
|
Copyright (c) 2016 - 2019 Charles Childers
|
||
|
|
||
|
This implements a file I/O device for use with RETRO.
|
||
|
---------------------------------------------------------------------*/
|
||
|
|
||
|
#define MAX_OPEN_FILES 128
|
||
|
|
||
|
/*---------------------------------------------------------------------
|
||
|
Begin by including the various C headers needed.
|
||
|
---------------------------------------------------------------------*/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
//#include <unistd.h>
|
||
|
#include <string.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <errno.h>
|
||
|
|
||
|
typedef void (*Handler)(void);
|
||
|
|
||
|
/*---------------------------------------------------------------------
|
||
|
First, a few things that relate to the Nga virtual machine that RETRO
|
||
|
runs on.
|
||
|
---------------------------------------------------------------------*/
|
||
|
|
||
|
#define CELL int32_t /* Cell size (32 bit, signed integer */
|
||
|
#define IMAGE_SIZE 524288 * 48 /* Amount of RAM. 12MiB by default. */
|
||
|
#define ADDRESSES 2048 /* Depth of address stack */
|
||
|
#define STACK_DEPTH 512 /* Depth of data stack */
|
||
|
|
||
|
extern CELL sp, rp, ip; /* Stack & instruction pointers */
|
||
|
extern CELL data[STACK_DEPTH]; /* The data stack */
|
||
|
extern CELL address[ADDRESSES]; /* The address stack */
|
||
|
extern CELL memory[IMAGE_SIZE + 1]; /* The memory for the image */
|
||
|
|
||
|
#define TOS data[sp] /* Shortcut for top item on stack */
|
||
|
#define NOS data[sp-1] /* Shortcut for second item on stack */
|
||
|
#define TORS address[rp] /* Shortcut for top item on address stack */
|
||
|
|
||
|
/*---------------------------------------------------------------------
|
||
|
Function prototypes.
|
||
|
---------------------------------------------------------------------*/
|
||
|
|
||
|
CELL stack_pop();
|
||
|
void stack_push(CELL value);
|
||
|
char *string_extract(CELL at);
|
||
|
|
||
|
/*---------------------------------------------------------------------
|
||
|
I keep an array of file handles. RETRO will use the index number as
|
||
|
its representation of the file.
|
||
|
---------------------------------------------------------------------*/
|
||
|
|
||
|
FILE *ioFileHandles[MAX_OPEN_FILES];
|
||
|
|
||
|
/*---------------------------------------------------------------------
|
||
|
`ioGetFileHandle()` returns a file handle, or 0 if there are no
|
||
|
available handle slots in the array.
|
||
|
---------------------------------------------------------------------*/
|
||
|
|
||
|
CELL ioGetFileHandle() {
|
||
|
CELL i;
|
||
|
for(i = 1; i < MAX_OPEN_FILES; i++)
|
||
|
if (ioFileHandles[i] == 0)
|
||
|
return i;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*---------------------------------------------------------------------
|
||
|
`ioOpenFile()` opens a file. This pulls from the RETRO data stack:
|
||
|
|
||
|
- mode (number, TOS)
|
||
|
- filename (string, NOS)
|
||
|
|
||
|
Modes are:
|
||
|
|
||
|
| Mode | Corresponds To | Description |
|
||
|
| ---- | -------------- | -------------------- |
|
||
|
| 0 | rb | Open for reading |
|
||
|
| 1 | w | Open for writing |
|
||
|
| 2 | a | Open for append |
|
||
|
| 3 | rb+ | Open for read/update |
|
||
|
|
||
|
The file name should be a NULL terminated string. This will attempt
|
||
|
to open the requested file and will return a handle (index number
|
||
|
into the `ioFileHandles` array).
|
||
|
---------------------------------------------------------------------*/
|
||
|
|
||
|
void ioOpenFile() {
|
||
|
CELL slot, mode, name;
|
||
|
char *request;
|
||
|
slot = ioGetFileHandle();
|
||
|
mode = data[sp]; sp--;
|
||
|
name = data[sp]; sp--;
|
||
|
request = string_extract(name);
|
||
|
if (slot > 0) {
|
||
|
if (mode == 0) ioFileHandles[slot] = fopen(request, "rb");
|
||
|
if (mode == 1) ioFileHandles[slot] = fopen(request, "w");
|
||
|
if (mode == 2) ioFileHandles[slot] = fopen(request, "a");
|
||
|
if (mode == 3) ioFileHandles[slot] = fopen(request, "rb+");
|
||
|
}
|
||
|
if (ioFileHandles[slot] == NULL) {
|
||
|
ioFileHandles[slot] = 0;
|
||
|
slot = 0;
|
||
|
}
|
||
|
stack_push(slot);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*---------------------------------------------------------------------
|
||
|
`ioReadFile()` reads a byte from a file. This takes a file pointer
|
||
|
from the stack and pushes the character that was read to the stack.
|
||
|
---------------------------------------------------------------------*/
|
||
|
|
||
|
void ioReadFile() {
|
||
|
CELL slot = stack_pop();
|
||
|
CELL c = fgetc(ioFileHandles[slot]);
|
||
|
stack_push(feof(ioFileHandles[slot]) ? 0 : c);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*---------------------------------------------------------------------
|
||
|
`ioWriteFile()` writes a byte to a file. This takes a file pointer
|
||
|
(TOS) and a byte (NOS) from the stack. It does not return any values
|
||
|
on the stack.
|
||
|
---------------------------------------------------------------------*/
|
||
|
|
||
|
void ioWriteFile() {
|
||
|
CELL slot, c, r;
|
||
|
slot = stack_pop();
|
||
|
c = stack_pop();
|
||
|
r = fputc(c, ioFileHandles[slot]);
|
||
|
c = (r == EOF) ? 0 : 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*---------------------------------------------------------------------
|
||
|
`ioCloseFile()` closes a file. This takes a file handle from the
|
||
|
stack and does not return anything on the stack.
|
||
|
---------------------------------------------------------------------*/
|
||
|
|
||
|
void ioCloseFile() {
|
||
|
fclose(ioFileHandles[data[sp]]);
|
||
|
ioFileHandles[data[sp]] = 0;
|
||
|
sp--;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*---------------------------------------------------------------------
|
||
|
`ioGetFilePosition()` provides the current index into a file. This
|
||
|
takes the file handle from the stack and returns the offset.
|
||
|
---------------------------------------------------------------------*/
|
||
|
|
||
|
void ioGetFilePosition() {
|
||
|
CELL slot = stack_pop();
|
||
|
stack_push((CELL) ftell(ioFileHandles[slot]));
|
||
|
}
|
||
|
|
||
|
|
||
|
/*---------------------------------------------------------------------
|
||
|
`ioSetFilePosition()` changes the current index into a file to the
|
||
|
specified one. This takes a file handle (TOS) and new offset (NOS)
|
||
|
from the stack.
|
||
|
---------------------------------------------------------------------*/
|
||
|
|
||
|
void ioSetFilePosition() {
|
||
|
CELL slot, pos, r;
|
||
|
slot = stack_pop();
|
||
|
pos = stack_pop();
|
||
|
r = fseek(ioFileHandles[slot], pos, SEEK_SET);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*---------------------------------------------------------------------
|
||
|
`ioGetFileSize()` returns the size of a file, or 0 if empty. If the
|
||
|
file is a directory, it returns -1. It takes a file handle from the
|
||
|
stack.
|
||
|
---------------------------------------------------------------------*/
|
||
|
|
||
|
void ioGetFileSize() {
|
||
|
CELL slot, current, r, size;
|
||
|
struct stat buffer;
|
||
|
int status;
|
||
|
slot = stack_pop();
|
||
|
status = fstat(fileno(ioFileHandles[slot]), &buffer);
|
||
|
if (!S_ISDIR(buffer.st_mode)) {
|
||
|
current = ftell(ioFileHandles[slot]);
|
||
|
r = fseek(ioFileHandles[slot], 0, SEEK_END);
|
||
|
size = ftell(ioFileHandles[slot]);
|
||
|
fseek(ioFileHandles[slot], current, SEEK_SET);
|
||
|
} else {
|
||
|
r = -1;
|
||
|
}
|
||
|
stack_push((r == 0) ? size : 0);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*---------------------------------------------------------------------
|
||
|
`ioDeleteFile()` removes a file. This takes a file name (as a string)
|
||
|
from the stack.
|
||
|
---------------------------------------------------------------------*/
|
||
|
|
||
|
void ioDeleteFile() {
|
||
|
char *request;
|
||
|
CELL name = data[sp]; sp--;
|
||
|
CELL result;
|
||
|
request = string_extract(name);
|
||
|
result = (unlink(request) == 0) ? -1 : 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*---------------------------------------------------------------------
|
||
|
`ioFlushFile()` flushes any pending writes to disk. This takes a
|
||
|
file handle from the stack.
|
||
|
---------------------------------------------------------------------*/
|
||
|
|
||
|
void ioFlushFile() {
|
||
|
CELL slot;
|
||
|
slot = stack_pop();
|
||
|
fflush(ioFileHandles[slot]);
|
||
|
}
|
||
|
|
||
|
|
||
|
Handler FileActions[10] = {
|
||
|
ioOpenFile,
|
||
|
ioCloseFile,
|
||
|
ioReadFile,
|
||
|
ioWriteFile,
|
||
|
ioGetFilePosition,
|
||
|
ioSetFilePosition,
|
||
|
ioGetFileSize,
|
||
|
ioDeleteFile,
|
||
|
ioFlushFile
|
||
|
};
|
||
|
|
||
|
void io_filesystem_query() {
|
||
|
stack_push(0);
|
||
|
stack_push(4);
|
||
|
}
|
||
|
|
||
|
void io_filesystem_handler() {
|
||
|
FileActions[stack_pop()]();
|
||
|
}
|