Commit e4e271c5391dd16cd42f33978690accb5d118cdb
Commits[COMMIT BEGIN]commit e4e271c5391dd16cd42f33978690accb5d118cdb Author: 0x4248 <[email protected]> Date: Thu Apr 2 17:05:18 2026 +0100 IBM-PC/Dataworks: init diff --git a/systems/IBM-PC/DataWorks/src/CLI.C b/systems/IBM-PC/DataWorks/src/CLI.C new file mode 100644 index 0000000..48ac5bc --- /dev/null +++ b/systems/IBM-PC/DataWorks/src/CLI.C @@ -0,0 +1,62 @@ +/* C89 Standard only */ + +#include <stdio.h> +#include <string.h> + +#include "DW.H" + +static int dw_interact(const char *path) +{ + char line[DW_MAX_LINE]; + char *tokens[8]; + char *p; + int count; + + printf("DataWorks interactive mode. Type HELP or EXIT.\n"); + for (;;) { + printf("%s> ", path); + if (fgets(line, sizeof(line), stdin) == NULL) { + return 1; + } + dw_trim(line); + if (line[0] == '\0') { + continue; + } + + count = 0; + p = strtok(line, " \t"); + while (p != NULL && count < 8) { + tokens[count++] = p; + p = strtok(NULL, " \t"); + } + if (count == 0) { + continue; + } + + if (dw_stricmp(tokens[0], "EXIT") == 0 || dw_stricmp(tokens[0], "QUIT") == 0) { + return 1; + } + if (dw_stricmp(tokens[0], "HELP") == 0) { + dw_print_help(); + continue; + } + + if (!dw_run_command(path, count, tokens)) { + printf("Command failed.\n"); + } + } +} + +int dw_cli_main(int argc, char **argv) +{ + if (argc < 3) { + dw_print_help(); + return 1; + } + + if (dw_stricmp(argv[2], "INTERACT") == 0) { + return dw_interact(argv[1]) ? 0 : 1; + } + + return dw_run_command(argv[1], argc - 2, argv + 2) ? 0 : 1; +} diff --git a/systems/IBM-PC/DataWorks/src/COMMAND.C b/systems/IBM-PC/DataWorks/src/COMMAND.C new file mode 100644 index 0000000..9827db1 --- /dev/null +++ b/systems/IBM-PC/DataWorks/src/COMMAND.C @@ -0,0 +1,101 @@ +/* C89 Standard only */ + +#include <stdio.h> +#include <stdlib.h> + +#include "DW.H" + +void dw_print_help(void) +{ + printf("DataWorks (simple) commands:\n"); + printf(" DW <file.dw> PRINT\n"); + printf(" DW <file.dw> INSERT <value> $row,col\n"); + printf(" DW <file.dw> DEL_COL [col_index]\n"); + printf(" DW <file.dw> SET_COL_NAME <name> [col_index]\n"); + printf(" DW <file.dw> INTERACT\n"); + printf("Limits: %d rows, %d cols, cell size %d chars\n", DW_MAX_ROWS, DW_MAX_COLS, DW_MAX_CELL - 1); +} + +int dw_run_command(const char *path, int argc, char **argv) +{ + DWTable table; + int row; + int col; + int col_index; + int ok; + + if (argc < 1) { + return 0; + } + + dw_init_table(&table); + + if (!dw_load_file(path, &table)) { + printf("Error: failed to load %s\n", path); + dw_free_table(&table); + return 0; + } + ok = 1; + + if (dw_stricmp(argv[0], "PRINT") == 0) { + dw_print_table(&table); + } else if (dw_stricmp(argv[0], "INSERT") == 0) { + if (argc < 3) { + printf("Usage: DW <file.dw> INSERT <value> $row,col\n"); + ok = 0; + goto cleanup; + } + if (!dw_parse_cell_ref(argv[2], &row, &col)) { + printf("Error: invalid cell ref '%s'. Use $row,col\n", argv[2]); + ok = 0; + goto cleanup; + } + if (!dw_insert(&table, argv[1], row, col)) { + printf("Error: table limit reached\n"); + ok = 0; + goto cleanup; + } + if (!dw_save_file(path, &table)) { + printf("Error: failed to save %s\n", path); + ok = 0; + goto cleanup; + } + } else if (dw_stricmp(argv[0], "DEL_COL") == 0) { + col_index = (argc >= 2) ? atoi(argv[1]) : (table.cols - 1); + if (!dw_del_col(&table, col_index)) { + printf("Error: invalid column index\n"); + ok = 0; + goto cleanup; + } + if (!dw_save_file(path, &table)) { + printf("Error: failed to save %s\n", path); + ok = 0; + goto cleanup; + } + } else if (dw_stricmp(argv[0], "SET_COL_NAME") == 0) { + if (argc < 2) { + printf("Usage: DW <file.dw> SET_COL_NAME <name> [col_index]\n"); + ok = 0; + goto cleanup; + } + col_index = (argc >= 3) ? atoi(argv[2]) : (table.cols - 1); + if (!dw_set_col_name(&table, col_index, argv[1])) { + printf("Error: invalid column index\n"); + ok = 0; + goto cleanup; + } + if (!dw_save_file(path, &table)) { + printf("Error: failed to save %s\n", path); + ok = 0; + goto cleanup; + } + } else { + printf("Error: unknown command '%s'\n", argv[0]); + dw_print_help(); + ok = 0; + } + +cleanup: + dw_free_table(&table); + return ok; +} diff --git a/systems/IBM-PC/DataWorks/src/DW.H b/systems/IBM-PC/DataWorks/src/DW.H new file mode 100644 index 0000000..ca178c7 --- /dev/null +++ b/systems/IBM-PC/DataWorks/src/DW.H @@ -0,0 +1,36 @@ +#ifndef DW_H +#define DW_H + +#define DW_SEP 31 +#define DW_MAX_LINE 4096 +#define DW_MAX_ROWS 256 +#define DW_MAX_COLS 64 +#define DW_MAX_CELL 128 + +typedef struct { + int rows; + int cols; + char ***cells; +} DWTable; + +void dw_trim(char *s); +int dw_stricmp(const char *a, const char *b); +void dw_copy_cell(char *dst, const char *src); +int dw_parse_cell_ref(const char *text, int *row, int *col); + +void dw_init_table(DWTable *table); +void dw_free_table(DWTable *table); +int dw_load_file(const char *path, DWTable *table); +int dw_save_file(const char *path, const DWTable *table); +void dw_print_table(const DWTable *table); +int dw_insert(DWTable *table, const char *value, int data_row, int col); +int dw_del_col(DWTable *table, int col_index); +int dw_set_col_name(DWTable *table, int col_index, const char *name); + +void dw_print_help(void); +int dw_run_command(const char *path, int argc, char **argv); + +int dw_cli_main(int argc, char **argv); + +#endif + diff --git a/systems/IBM-PC/DataWorks/src/MAIN.C b/systems/IBM-PC/DataWorks/src/MAIN.C new file mode 100644 index 0000000..7a91de1 --- /dev/null +++ b/systems/IBM-PC/DataWorks/src/MAIN.C @@ -0,0 +1,8 @@ +/* C89 Standard only */ + +#include "DW.H" + +int main(int argc, char **argv) +{ + return dw_cli_main(argc, argv); +} diff --git a/systems/IBM-PC/DataWorks/src/TABLE.C b/systems/IBM-PC/DataWorks/src/TABLE.C new file mode 100644 index 0000000..ea7000e --- /dev/null +++ b/systems/IBM-PC/DataWorks/src/TABLE.C @@ -0,0 +1,310 @@ +/* C89 Standard only */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "DW.H" + +static int dw_alloc_cell(char **out_cell) +{ + *out_cell = (char *)malloc((size_t)DW_MAX_CELL); + if (*out_cell == NULL) { + return 0; + } + (*out_cell)[0] = '\0'; + return 1; +} + +static void dw_fill_header_names(DWTable *table, int start_col) +{ + int c; + char temp[32]; + + for (c = start_col; c < table->cols; ++c) { + if (table->cells[0][c] != NULL && table->cells[0][c][0] == '\0') { + sprintf(temp, "COL_%d", c); + dw_copy_cell(table->cells[0][c], temp); + } + } +} + +static int dw_ensure_size(DWTable *table, int rows, int cols) +{ + int r; + int c; + int old_rows; + char ***new_rows; + char **new_row; + + if (rows > DW_MAX_ROWS || cols > DW_MAX_COLS || rows < 1 || cols < 1) { + return 0; + } + + old_rows = table->rows; + + if (rows > table->rows) { + new_rows = (char ***)realloc(table->cells, (size_t)rows * sizeof(char **)); + if (new_rows == NULL) { + return 0; + } + table->cells = new_rows; + for (r = table->rows; r < rows; ++r) { + table->cells[r] = NULL; + } + table->rows = rows; + } + + if (table->cols > 0 && table->rows > old_rows) { + for (r = old_rows; r < table->rows; ++r) { + table->cells[r] = (char **)malloc((size_t)table->cols * sizeof(char *)); + if (table->cells[r] == NULL) { + return 0; + } + for (c = 0; c < table->cols; ++c) { + table->cells[r][c] = NULL; + if (!dw_alloc_cell(&table->cells[r][c])) { + return 0; + } + } + } + } + + if (cols > table->cols) { + for (r = 0; r < table->rows; ++r) { + new_row = (char **)realloc(table->cells[r], (size_t)cols * sizeof(char *)); + if (new_row == NULL) { + return 0; + } + table->cells[r] = new_row; + for (c = table->cols; c < cols; ++c) { + table->cells[r][c] = NULL; + if (!dw_alloc_cell(&table->cells[r][c])) { + return 0; + } + } + } + table->cols = cols; + dw_fill_header_names(table, 0); + } + + return 1; +} + +void dw_init_table(DWTable *table) +{ + table->rows = 0; + table->cols = 0; + table->cells = NULL; + + if (!dw_ensure_size(table, 1, 1)) { + return; + } + + dw_copy_cell(table->cells[0][0], "COL_0"); +} + +void dw_free_table(DWTable *table) +{ + int r; + int c; + + if (table->cells != NULL) { + for (r = 0; r < table->rows; ++r) { + if (table->cells[r] != NULL) { + for (c = 0; c < table->cols; ++c) { + free(table->cells[r][c]); + } + free(table->cells[r]); + } + } + free(table->cells); + } + + table->rows = 0; + table->cols = 0; + table->cells = NULL; +} + +int dw_load_file(const char *path, DWTable *table) +{ + FILE *fp; + char line[DW_MAX_LINE]; + int row; + int col; + int pos; + int i; + char delim; + char cell[DW_MAX_CELL]; + + dw_init_table(table); + fp = fopen(path, "rb"); + if (fp == NULL) { + return 1; + } + + row = 0; + while (fgets(line, sizeof(line), fp) != NULL) { + dw_trim(line); + delim = (strchr(line, DW_SEP) != NULL) ? (char)DW_SEP : ','; + + col = 0; + pos = 0; + cell[0] = '\0'; + + for (i = 0;; ++i) { + if (line[i] == delim || line[i] == '\0') { + cell[pos] = '\0'; + dw_trim(cell); + + if (!dw_ensure_size(table, row + 1, col + 1)) { + fclose(fp); + dw_free_table(table); + return 0; + } + dw_copy_cell(table->cells[row][col], cell); + + ++col; + pos = 0; + cell[0] = '\0'; + + if (line[i] == '\0' || col >= DW_MAX_COLS) { + break; + } + } else if (pos < DW_MAX_CELL - 1) { + cell[pos++] = line[i]; + } + } + + ++row; + if (row >= DW_MAX_ROWS) { + break; + } + } + + if (row > 0) { + table->rows = row; + } + dw_fill_header_names(table, 0); + fclose(fp); + return 1; +} + +int dw_save_file(const char *path, const DWTable *table) +{ + FILE *fp; + int r; + int c; + + fp = fopen(path, "wb"); + if (fp == NULL) { + return 0; + } + + for (r = 0; r < table->rows; ++r) { + for (c = 0; c < table->cols; ++c) { + if (table->cells[r][c] != NULL) { + fputs(table->cells[r][c], fp); + } + if (c < table->cols - 1) { + fputc(DW_SEP, fp); + } + } + fputc('\n', fp); + } + + fclose(fp); + return 1; +} + +void dw_print_table(const DWTable *table) +{ + int r; + int c; + int i; + int len; + int pad; + int widths[DW_MAX_COLS]; + + for (c = 0; c < table->cols; ++c) { + widths[c] = 0; + } + + for (r = 0; r < table->rows; ++r) { + for (c = 0; c < table->cols; ++c) { + len = 0; + if (table->cells[r][c] != NULL) { + len = (int)strlen(table->cells[r][c]); + } + if (len > widths[c]) { + widths[c] = len; + } + } + } + + for (r = 0; r < table->rows; ++r) { + for (c = 0; c < table->cols; ++c) { + if (table->cells[r][c] == NULL || table->cells[r][c][0] == '\0') { + printf("~"); + } else { + printf("%s", table->cells[r][c]); + } + if (c < table->cols - 1) { + len = 0; + if (table->cells[r][c] != NULL) { + len = (int)strlen(table->cells[r][c]); + } + if (len == 0) { + len = 1; + } + pad = (widths[c] - len) + 4; + for (i = 0; i < pad; ++i) { + putchar(' '); + } + } + } + printf("\n"); + } +} + +int dw_insert(DWTable *table, const char *value, int data_row, int col) +{ + int real_row; + + real_row = data_row + 1; + if (!dw_ensure_size(table, real_row + 1, col + 1)) { + return 0; + } + dw_copy_cell(table->cells[real_row][col], value); + return 1; +} + +int dw_del_col(DWTable *table, int col_index) +{ + int r; + int c; + + if (col_index < 0 || col_index >= table->cols || table->cols <= 0) { + return 0; + } + + for (r = 0; r < table->rows; ++r) { + for (c = col_index; c < table->cols - 1; ++c) { + dw_copy_cell(table->cells[r][c], table->cells[r][c + 1]); + } + if (table->cells[r][table->cols - 1] != NULL) { + table->cells[r][table->cols - 1][0] = '\0'; + } + } + table->cols -= 1; + return 1; +} + +int dw_set_col_name(DWTable *table, int col_index, const char *name) +{ + if (col_index < 0 || col_index >= table->cols) { + return 0; + } + dw_copy_cell(table->cells[0][col_index], name); + return 1; +} diff --git a/systems/IBM-PC/DataWorks/src/UTIL.C b/systems/IBM-PC/DataWorks/src/UTIL.C new file mode 100644 index 0000000..e4afcd7 --- /dev/null +++ b/systems/IBM-PC/DataWorks/src/UTIL.C @@ -0,0 +1,85 @@ +/* C89 Standard only */ + +#include <string.h> +#include <ctype.h> +#include <stdlib.h> + +#include "DW.H" + +void dw_trim(char *s) +{ + int len; + int i; + int start; + + len = (int)strlen(s); + while (len > 0 && (s[len - 1] == '\n' || s[len - 1] == '\r' || isspace((unsigned char)s[len - 1]))) { + s[len - 1] = '\0'; + --len; + } + + start = 0; + while (s[start] != '\0' && isspace((unsigned char)s[start])) { + ++start; + } + + if (start > 0) { + i = 0; + while (s[start + i] != '\0') { + s[i] = s[start + i]; + ++i; + } + s[i] = '\0'; + } +} + +int dw_stricmp(const char *a, const char *b) +{ + unsigned char ca; + unsigned char cb; + + while (*a != '\0' && *b != '\0') { + ca = (unsigned char)tolower((unsigned char)*a); + cb = (unsigned char)tolower((unsigned char)*b); + if (ca != cb) { + return (int)ca - (int)cb; + } + ++a; + ++b; + } + return (int)(unsigned char)*a - (int)(unsigned char)*b; +} + +void dw_copy_cell(char *dst, const char *src) +{ + if (src == NULL) { + dst[0] = '\0'; + return; + } + strncpy(dst, src, DW_MAX_CELL - 1); + dst[DW_MAX_CELL - 1] = '\0'; +} + +int dw_parse_cell_ref(const char *text, int *row, int *col) +{ + char *endptr; + long r; + long c; + + if (text == NULL || text[0] != '$') { + return 0; + } + + r = strtol(text + 1, &endptr, 10); + if (*endptr != ',' || r < 0) { + return 0; + } + c = strtol(endptr + 1, &endptr, 10); + if (*endptr != '\0' || c < 0) { + return 0; + } + + *row = (int)r; + *col = (int)c; + return 1; +} diff --git a/systems/IBM-PC/PhotonX/VGA.md b/systems/IBM-PC/PhotonX/VGA.md new file mode 100644 index 0000000..fc8ed19 --- /dev/null +++ b/systems/IBM-PC/PhotonX/VGA.md @@ -0,0 +1,101 @@ +# VGA Subsystem + +PhotonX Graphics SDK — VGA driver interface for 8086/8088 systems. + +## Overview + +The VGA subsystem exposes two distinct APIs depending on the active video mode: +a text-mode API for 80x25 character output, and a graphics-mode API for direct +pixel rendering in mode 13h (320x200, 256 colors). + +<!-- #ifndef VGA_H +#define VGA_H + +#include <dos.h> + +#define VGA_SEG 0xA000 +#define VGA_MODE_TEXT_80x25 0x03 +#define VGA_MODE_320x200x256 0x13 + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; + +extern unsigned char far *vga; + +/* Core mode + utility */ +void vga_set_mode(uint8_t mode); + +/* Text mode API (whitepaper-aligned) */ +void vga_text_setcolor(uint8_t fg, uint8_t bg); +void vga_text_gotoxy(uint8_t x, uint8_t y); +void vga_text_putch(char c); +void vga_text_puts(const char *str); +void vga_text_cls(void); + +/* Graphics mode API (whitepaper-aligned) */ +void vga_set_palette(uint8_t index, uint8_t r, uint8_t g, uint8_t b); +void vga_gfx_setclscolor(uint8_t color); +void vga_gfx_cls(void); +void vga_gfx_putpixel(uint16_t x, uint16_t y, uint8_t color); +void vga_gfx_line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint8_t color); +void vga_gfx_rect(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint8_t color); +void vga_gfx_circle(uint16_t x, uint16_t y, uint16_t radius, uint8_t color); +void vga_gfx_putch(char c, int x, int y, uint8_t color); +void vga_gfx_puts(const char *str, int x, int y, uint8_t color); + +/* Compatibility names for existing code */ +void set_mode(unsigned char mode); +void set_palette(unsigned char index, unsigned char r, unsigned char g, unsigned char b); +void clear_screen(unsigned char color); +void put_pixel(int x, int y, unsigned char color); + +#endif --> + +## Setting mode outputs + +To switch between text and graphics modes, use the `vga_set_mode` function with +the appropriate mode constant: + +```c +vga_set_mode(VGA_MODE_TEXT_80x25); // Switch to text mode +vga_set_mode(VGA_MODE_320x200x256); // Switch to graphics mode +``` + +**WARNING**: It is always important to reset the screen mode back to text mode +before returning the user to the MS-DOS environment, otherwise they will be left +almost like a broken state and could lead to unexpected behavior - It is also +noted that on MS-DOS the `cls` command does not reset the screen mode. For simple +way to reset the screen mode you should call these functions: + +```c +vga_set_mode(VGA_MODE_TEXT_80x25); // Reset to text mode +vga_text_setcolor(7, 0); // Set default white on black text color +vga_text_cls(); // Clear the screen +``` +## Text Mode + +In text mode, you get a character grid of 80 columns and 25 rows and 16 colors. + +### Color palette + +The VGA text mode supports a fixed palette of 16 colors, indexed from 0 to 15: + +<table> + <tr><th>Index</th><th>Color name</th><th>Color HTML</th></tr> + <tr><td>0</td><td>Black</td><td style="background:#000000;color:#ffffff;">#000000</td></tr> + <tr><td>1</td><td>Blue</td><td style="background:#0000AA;color:#ffffff;">#0000AA</td></tr> + <tr><td>2</td><td>Green</td><td style="background:#00AA00;color:#ffffff;">#00AA00</td></tr> + <tr><td>3</td><td>Cyan</td><td style="background:#00AAAA;color:#000000;">#00AAAA</td></tr> + <tr><td>4</td><td>Red</td><td style="background:#AA0000;color:#ffffff;">#AA0000</td></tr> + <tr><td>5</td><td>Magenta</td><td style="background:#AA00AA;color:#ffffff;">#AA00AA</td></tr> + <tr><td>6</td><td>Brown</td><td style="background:#AA5500;color:#ffffff;">#AA5500</td></tr> + <tr><td>7</td><td>Light Gray</td><td style="background:#AAAAAA;color:#000000;">#AAAAAA</td></tr> + <tr><td>8</td><td>Dark Gray</td><td style="background:#555555;color:#ffffff;">#555555</td></tr> + <tr><td>9</td><td>Light Blue</td><td style="background:#5555FF;color:#ffffff;">#5555FF</td></tr> + <tr><td>10</td><td>Light Green</td><td style="background:#55FF55;color:#000000;">#55FF55</td></tr> + <tr><td>11</td><td>Light Cyan</td><td style="background:#55FFFF;color:#000000;">#55FFFF</td></tr> + <tr><td>12</td><td>Light Red</td><td style="background:#FF5555;color:#000000;">#FF5555</td></tr> + <tr><td>13</td><td>Light Magenta</td><td style="background:#FF55FF;color:#000000;">#FF55FF</td></tr> + <tr><td>14</td><td>Yellow</td><td style="background:#FFFF55;color:#000000;">#FFFF55</td></tr> + <tr><td>15</td><td>White</td><td style="background:#FFFFFF;color:#000000;">#FFFFFF</td></tr> +</table> \ No newline at end of file diff --git a/systems/IBM-PC/PhotonX/src/demos/TEXTT.C b/systems/IBM-PC/PhotonX/src/demos/TEXTT.C new file mode 100644 index 0000000..48341d4 --- /dev/null +++ b/systems/IBM-PC/PhotonX/src/demos/TEXTT.C @@ -0,0 +1,89 @@ +#include "VGA.H" +#include <conio.h> + +int main() +{ + unsigned char i; + unsigned char j; + const char *color_names[16] = { + "Black", + "Blue", + "Green", + "Cyan", + "Red", + "Magenta", + "Brown", + "Light Gray", + "Dark Gray", + "Light Blue", + "Light Green", + "Light Cyan", + "Light Red", + "Light Magenta", + "Yellow", + "White" + }; + const char *logo[11] = { + " ", + " ", + " `7MMF' ", + " ,mmmmMMmmmm. ", + "6MP MM YMb ", + "8M MM M8 ", + "YMb MM dM9 ", + " `YmmmMMmmmP' ", + " .JMML. ", + " ", + " " + }; + + vga_set_mode(VGA_MODE_TEXT_80x25); + vga_text_setcolor(15, 1); + vga_text_cls(); + vga_text_gotoxy(0, 0); + vga_text_puts("PhotonX Demo: Text Colors\r\n"); + + vga_text_setcolor(14, 0); + vga_text_puts("Index shown with matching foreground color:\r\n\r\n"); + + vga_text_setcolor(15, 0); + for (j = 0; j < 11; ++j) + { + vga_text_gotoxy(60, 5 + j); + vga_text_puts(logo[j]); + } + + vga_text_gotoxy(0, 3); + + for (i = 0; i < 16; ++i) + { + if (i == 0) + vga_text_setcolor(i, 7); + else + vga_text_setcolor(i, 0); + + vga_text_puts("Color "); + + if (i < 10) + vga_text_putch('0' + i); + else + { + vga_text_putch('1'); + vga_text_putch('0' + (i - 10)); + } + + vga_text_puts(": "); + vga_text_puts(color_names[i]); + vga_text_puts("\r\n"); + } + + vga_text_setcolor(15, 0); + vga_text_puts("\r\nPress any key to exit...\r\n"); + getch(); + + vga_set_mode(VGA_MODE_TEXT_80x25); + vga_text_setcolor(15, 0); + vga_text_cls(); + + return 0; +}[COMMIT END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.