all repos — xyw @ 7223529dc7fee069426ec32f0759d11f41b16ffc

A minimal virtual machine and assembler for terminals.

Improved file operations by limiting flushing and seeking when unnecessary.
h3rald h3rald@h3rald.com
Wed, 22 Jul 2026 07:26:13 +0200
commit

7223529dc7fee069426ec32f0759d11f41b16ffc

parent

8567a5f04513ef9e87bc2027e27f0d64350780a8

1 files changed, 45 insertions(+), 46 deletions(-)

jump to
M devices/file.cdevices/file.c

@@ -18,7 +18,7 @@ #include "../xyw.h"

extern xyw_byte xyw_memory[]; -/* file device */ +// file device #define FILE_PATH 0x0 #define FILE_TYPE 0x2 #define FILE_SIZE 0x3

@@ -28,33 +28,33 @@ #define FILE_READ 0x9

#define FILE_WRITE 0xB #define FILE_OPERATION 0xD -/* File operations */ +// File operations #define FILE_OP_READ 0x01 #define FILE_OP_WRITE 0x02 #define FILE_OP_APPEND 0x03 #define FILE_OP_DELETE 0x04 -/* File types */ +// File types #define XYW_FILE_TYPE_UNKNOWN 0x00 #define FILE_TYPE_FILE 0x01 #define FILE_TYPE_DIRECTORY 0x02 -/* Error codes (high nibble = 3 for file device) */ +// Error codes (high nibble = 3 for file device) #define FILE_ERROR_NOT_FOUND 0x31 #define FILE_ERROR_ACCESS_DENIED 0x32 #define FILE_ERROR_INVALID_OP 0x33 #define FILE_ERROR_IO_ERROR 0x34 -/* Maximum path length */ +// Maximum path length #define FILE_MAX_PATH 256 -/* Internal state for streaming */ +// Internal state for streaming static FILE *file_handle = NULL; static char file_current_path[FILE_MAX_PATH] = {0}; static long file_cursor = 0; static int file_is_write_mode = 0; -/* Get null-terminated string from memory at given address */ +// Get null-terminated string from memory at given address static void get_path_string(xyw_word addr, char *buf, size_t bufsize) { size_t i;

@@ -65,7 +65,7 @@ }

buf[i] = '\0'; } -/* Close any open file */ +// Close any open file static void file_close(void) { if (file_handle)

@@ -78,7 +78,7 @@ file_cursor = 0;

file_is_write_mode = 0; } -/* Update file type and size based on path */ +// Update file type and size based on path static void update_file_stats(xyw_byte *data) { xyw_word path_addr = XYW_PEEKW(&data[FILE_PATH]);

@@ -109,7 +109,7 @@ }

else if (S_ISREG(st.st_mode)) { data[FILE_TYPE] = FILE_TYPE_FILE; - /* Cap size at 64KB (0xFFFF) */ + // Cap size at 64KB (0xFFFF) xyw_word size = (st.st_size > 0xFFFF) ? 0xFFFF : (xyw_word)st.st_size; XYW_POKEW(&data[FILE_SIZE], size); }

@@ -120,7 +120,7 @@ XYW_POKEW(&data[FILE_SIZE], 0);

} } -/* Read file contents into memory buffer with cursor support */ +// Read file contents into memory buffer with cursor support static void file_read(xyw_byte *data, xyw_byte *error) { xyw_word path_addr = XYW_PEEKW(&data[FILE_PATH]);

@@ -128,7 +128,7 @@ xyw_word buffer_addr = XYW_PEEKW(&data[FILE_READ]);

xyw_word length = XYW_PEEKW(&data[FILE_LENGTH]); char path[FILE_MAX_PATH]; - /* Clear success */ + // Clear success XYW_POKEW(&data[FILE_SUCCESS], 0); if (path_addr == 0 || buffer_addr == 0 || length == 0)

@@ -142,7 +142,7 @@ get_path_string(path_addr, path, FILE_MAX_PATH);

XYW_DBG(" Reading file: %s, length=%u, cursor=%ld\n", path, length, file_cursor); - /* Check if it's a directory - list contents (no cursor support for dirs) */ + // Check if it's a directory - list contents (no cursor support for dirs) if (data[FILE_TYPE] == FILE_TYPE_DIRECTORY) { xyw_word offset = 0;

@@ -162,7 +162,7 @@ *error = FILE_ERROR_ACCESS_DENIED;

return; } - /* Output . and .. first */ + // Output . and .. first char line[FILE_MAX_PATH + 16]; int len = snprintf(line, sizeof(line), "%02X %04X .\n", FILE_TYPE_DIRECTORY, 0); if (len <= (int)length)

@@ -217,7 +217,7 @@ *error = FILE_ERROR_ACCESS_DENIED;

return; } - /* Output . and .. first */ + // Output . and .. first char line[FILE_MAX_PATH + 16]; int len = snprintf(line, sizeof(line), "%02X %04X .\n", FILE_TYPE_DIRECTORY, 0); if (len <= (int)length)

@@ -270,7 +270,7 @@

closedir(dir); #endif - /* Null-terminate if space */ + // Null-terminate if space if (offset < length) { xyw_memory[buffer_addr + offset] = 0;

@@ -280,9 +280,9 @@ XYW_POKEW(&data[FILE_SUCCESS], offset);

return; } - /* Regular file read with cursor */ + // Regular file read with cursor - /* Open file if not already open or if path changed */ + // Open file if not already open or if path changed if (!file_handle || strcmp(file_current_path, path) != 0 || file_is_write_mode) { file_close();

@@ -299,21 +299,21 @@ file_cursor = 0;

file_is_write_mode = 0; } - /* Seek to cursor position */ - if (fseek(file_handle, file_cursor, SEEK_SET) != 0) + // Seek to cursor position, only if not already there + if (ftell(file_handle) != file_cursor && fseek(file_handle, file_cursor, SEEK_SET) != 0) { XYW_DBG(" Failed to seek in file: %s\n", path); *error = FILE_ERROR_IO_ERROR; return; } - /* Read data */ + // Read data size_t bytes_read = fread(&xyw_memory[buffer_addr], 1, length, file_handle); - /* Advance cursor */ + // Advance cursor file_cursor += bytes_read; - /* Null-terminate if space and we read less than length */ + // Null-terminate if space and we read less than length if (bytes_read < length) { xyw_memory[buffer_addr + bytes_read] = 0;

@@ -323,7 +323,7 @@ XYW_DBG(" Read %zu bytes, cursor now at %ld\n", bytes_read, file_cursor);

XYW_POKEW(&data[FILE_SUCCESS], (xyw_word)bytes_read); } -/* Write memory buffer to file with cursor support */ +// Write memory buffer to file with cursor support static void file_write_op(xyw_byte *data, xyw_byte *error, int append) { xyw_word path_addr = XYW_PEEKW(&data[FILE_PATH]);

@@ -331,7 +331,7 @@ xyw_word buffer_addr = XYW_PEEKW(&data[FILE_WRITE]);

xyw_word length = XYW_PEEKW(&data[FILE_LENGTH]); char path[FILE_MAX_PATH]; - /* Clear success */ + // Clear success XYW_POKEW(&data[FILE_SUCCESS], 0); if (path_addr == 0 || buffer_addr == 0 || length == 0)

@@ -345,7 +345,7 @@ get_path_string(path_addr, path, FILE_MAX_PATH);

XYW_DBG(" Writing to file: %s (append=%d), length=%u, cursor=%ld\n", path, append, length, file_cursor); - /* Open file if not already open, path changed, or mode changed */ + // Open file if not already open, path changed, or mode changed int need_reopen = !file_handle || strcmp(file_current_path, path) != 0 || !file_is_write_mode;

@@ -354,14 +354,14 @@ if (need_reopen)

{ file_close(); - /* For append, open in append mode; otherwise open for read/write or create */ + // For append, open in append mode; otherwise open for read/write or create if (append) { file_handle = fopen(path, "ab"); } else { - /* Try to open existing file for r+b, if fails create with wb */ + // Try to open existing file for r+b, if fails create with wb file_handle = fopen(path, "r+b"); if (!file_handle) {

@@ -382,7 +382,7 @@ file_is_write_mode = 1;

if (append) { - /* Seek to end for append */ + // Seek to end for append fseek(file_handle, 0, SEEK_END); file_cursor = ftell(file_handle); }

@@ -392,15 +392,15 @@ file_cursor = 0;

} } - /* Seek to cursor position (for non-append continuing writes) */ - if (!append && fseek(file_handle, file_cursor, SEEK_SET) != 0) + // Seek to cursor position (for non-append continuing writes), skip if already positioned + if (!append && ftell(file_handle) != file_cursor && fseek(file_handle, file_cursor, SEEK_SET) != 0) { XYW_DBG(" Failed to seek in file: %s\n", path); *error = FILE_ERROR_IO_ERROR; return; } - /* Find actual content length (stop at null terminator if present) */ + // Find actual content length (stop at null terminator if present) xyw_word actual_length = 0; while (actual_length < length && xyw_memory[buffer_addr + actual_length] != 0) {

@@ -413,27 +413,26 @@ XYW_DBG(" Empty content for file_write\n");

return; } - /* Write data */ + // Write data size_t written = fwrite(&xyw_memory[buffer_addr], 1, actual_length, file_handle); - fflush(file_handle); - /* Advance cursor */ + // Advance cursor file_cursor += written; XYW_DBG(" Wrote %zu bytes (of %u max), cursor now at %ld\n", written, length, file_cursor); XYW_POKEW(&data[FILE_SUCCESS], (xyw_word)written); - /* Update stats after write */ - update_file_stats(data); + // Update file size + data[FILE_TYPE] = FILE_TYPE_FILE; + XYW_POKEW(&data[FILE_SIZE], (xyw_word)(file_cursor > 0xFFFF ? 0xFFFF : file_cursor)); } -/* Delete a file */ +// Delete a file static void file_delete(xyw_byte *data, xyw_byte *error) { xyw_word path_addr = XYW_PEEKW(&data[FILE_PATH]); char path[FILE_MAX_PATH]; - /* Clear success */ XYW_POKEW(&data[FILE_SUCCESS], 0); if (path_addr == 0)

@@ -445,7 +444,7 @@ }

get_path_string(path_addr, path, FILE_MAX_PATH); - /* Close file if it's the one being deleted */ + // Close file if it's the one being deleted if (file_handle && strcmp(file_current_path, path) == 0) { file_close();

@@ -460,19 +459,19 @@ *error = FILE_ERROR_ACCESS_DENIED;

return; } - /* Success = 1 for successful delete */ + // Success = 1 for successful delete XYW_POKEW(&data[FILE_SUCCESS], 1); - /* Update stats after delete */ + // Update stats after delete update_file_stats(data); } void file_setup(xyw_byte *data) { - /* Close any open file */ + // Close any open file file_close(); - /* Clear all device memory */ + // Clear all device memory memset(data, 0, 16); }

@@ -492,7 +491,7 @@ file_close();

update_file_stats(data); } - /* When operation is written, execute it */ + // When operation is written, execute it if (addr == FILE_OPERATION) { xyw_byte op = data[FILE_OPERATION];

@@ -519,7 +518,7 @@ }

break; } - /* Clear operation after execution */ + // Clear operation after execution data[FILE_OPERATION] = 0; } }