diff --git a/Makefile b/Makefile index 12cbb6d..f3d724c 100644 --- a/Makefile +++ b/Makefile @@ -3,9 +3,9 @@ # Builds the full DVX stack: core library, task switcher, # bootstrap loader, text help library, widgets, shell, and apps. -.PHONY: all clean core tasks loader texthelp listhelp widgets shell taskmgr serial apps tools +.PHONY: all clean core tasks loader texthelp listhelp widgets shell taskmgr serial dvxbasic apps tools -all: core tasks loader texthelp listhelp widgets shell taskmgr serial apps tools +all: core tasks loader texthelp listhelp widgets shell taskmgr serial dvxbasic apps tools core: $(MAKE) -C core @@ -34,6 +34,9 @@ taskmgr: shell serial: core tasks $(MAKE) -C serial +dvxbasic: core tasks shell tools + $(MAKE) -C dvxbasic + tools: $(MAKE) -C tools @@ -50,6 +53,7 @@ clean: $(MAKE) -C shell clean $(MAKE) -C taskmgr clean $(MAKE) -C serial clean + $(MAKE) -C dvxbasic clean $(MAKE) -C apps clean $(MAKE) -C tools clean -rmdir obj 2>/dev/null diff --git a/config/basrt.dep b/config/basrt.dep new file mode 100644 index 0000000..2afaca0 --- /dev/null +++ b/config/basrt.dep @@ -0,0 +1,2 @@ +libtasks +libdvx diff --git a/core/platform/dvxPlatformDos.c b/core/platform/dvxPlatformDos.c index 216a9f4..c15fc7c 100644 --- a/core/platform/dvxPlatformDos.c +++ b/core/platform/dvxPlatformDos.c @@ -2315,6 +2315,7 @@ extern unsigned long long __umoddi3(unsigned long long, unsigned long long); extern void __dj_assert(const char *, const char *, int); extern unsigned short __dj_ctype_flags[]; extern unsigned char __dj_ctype_tolower[]; +extern unsigned char __dj_ctype_toupper[]; // GCC emulated thread-local storage extern void *__emutls_get_address(void *); @@ -2424,6 +2425,7 @@ DXE_EXPORT_TABLE(sDxeExportTable) DXE_EXPORT(strcspn) { "_strdup", (void *)dvxStrdup }, DXE_EXPORT(strerror) + DXE_EXPORT(strnicmp) DXE_EXPORT(stricmp) DXE_EXPORT(strlen) DXE_EXPORT(strncasecmp) @@ -2439,6 +2441,7 @@ DXE_EXPORT_TABLE(sDxeExportTable) // --- ctype --- DXE_EXPORT(isalnum) DXE_EXPORT(isalpha) + DXE_EXPORT(isascii) DXE_EXPORT(isdigit) DXE_EXPORT(islower) DXE_EXPORT(isprint) @@ -2451,6 +2454,7 @@ DXE_EXPORT_TABLE(sDxeExportTable) // --- conversion --- DXE_EXPORT(abs) + DXE_EXPORT(ldiv) DXE_EXPORT(atof) DXE_EXPORT(atoi) DXE_EXPORT(atol) @@ -2461,6 +2465,7 @@ DXE_EXPORT_TABLE(sDxeExportTable) // --- formatted I/O --- DXE_EXPORT(fprintf) + DXE_EXPORT(perror) DXE_EXPORT(fputs) DXE_EXPORT(fscanf) DXE_EXPORT(printf) @@ -2521,13 +2526,19 @@ DXE_EXPORT_TABLE(sDxeExportTable) DXE_EXPORT(localtime) DXE_EXPORT(mktime) DXE_EXPORT(strftime) + DXE_EXPORT(asctime) + DXE_EXPORT(ctime) + DXE_EXPORT(sleep) DXE_EXPORT(time) + DXE_EXPORT(usleep) // --- process / environment --- DXE_EXPORT(abort) DXE_EXPORT(atexit) DXE_EXPORT(exit) DXE_EXPORT(getenv) + DXE_EXPORT(setenv) + DXE_EXPORT(putenv) DXE_EXPORT(system) // --- sorting / searching --- @@ -2541,6 +2552,7 @@ DXE_EXPORT_TABLE(sDxeExportTable) // --- setjmp / signal --- DXE_EXPORT(longjmp) DXE_EXPORT(setjmp) + DXE_EXPORT(raise) DXE_EXPORT(signal) // --- libm --- @@ -2561,7 +2573,11 @@ DXE_EXPORT_TABLE(sDxeExportTable) DXE_EXPORT(modf) DXE_EXPORT(pow) DXE_EXPORT(sin) + DXE_EXPORT(sinh) + DXE_EXPORT(cosh) + DXE_EXPORT(tanh) DXE_EXPORT(sqrt) + DXE_EXPORT(strtof) DXE_EXPORT(tan) // --- errno --- @@ -2582,6 +2598,7 @@ DXE_EXPORT_TABLE(sDxeExportTable) DXE_EXPORT(__dj_assert) DXE_EXPORT(__dj_ctype_flags) DXE_EXPORT(__dj_ctype_tolower) + DXE_EXPORT(__dj_ctype_toupper) DXE_EXPORT(__djgpp_exception_state_ptr) // --- GCC internals --- diff --git a/dvxbasic/Makefile b/dvxbasic/Makefile new file mode 100644 index 0000000..ac00247 --- /dev/null +++ b/dvxbasic/Makefile @@ -0,0 +1,89 @@ +# DVX BASIC Makefile for DJGPP cross-compilation +# +# Builds: +# basrt.lib -- BASIC runtime library (VM + values + dlregsym init) +# dvxbasic.app -- BASIC IDE (compiler + UI, links against basrt.lib) +# +# The runtime is a separate library so compiled BASIC apps can use +# it without including the compiler. The library's constructor calls +# dlregsym() to register its exports, making them available to DXEs +# loaded later (apps, widgets, etc.). + +DJGPP_PREFIX = $(HOME)/djgpp/djgpp +CC = $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-gcc +DXE3GEN = PATH=$(DJGPP_PREFIX)/bin:$(PATH) DJDIR=$(DJGPP_PREFIX)/i586-pc-msdosdjgpp $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/dxe3gen +CFLAGS = -O2 -Wall -Wextra -march=i486 -mtune=i586 -I../core -I../core/platform -I../widgets -I../shell -I../tasks -I../core/thirdparty -I. + +OBJDIR = ../obj/dvxbasic +LIBSDIR = ../bin/libs +APPDIR = ../bin/apps/dvxbasic +DVXRES = ../bin/dvxres +SAMPLES = samples/hello.bas + +# Runtime library objects (VM + values) +RT_OBJS = $(OBJDIR)/vm.o $(OBJDIR)/values.o +RT_TARGET = $(LIBSDIR)/basrt.lib + +# Compiler objects (only needed by the IDE) +COMP_OBJS = $(OBJDIR)/lexer.o $(OBJDIR)/parser.o $(OBJDIR)/codegen.o $(OBJDIR)/symtab.o + +# IDE app objects +APP_OBJS = $(OBJDIR)/ideMain.o +APP_TARGET = $(APPDIR)/dvxbasic.app + +.PHONY: all clean + +all: $(RT_TARGET) $(LIBSDIR)/basrt.dep $(APP_TARGET) install-samples + +# Runtime library DXE (exports symbols via dlregsym constructor) +$(RT_TARGET): $(RT_OBJS) | $(LIBSDIR) + $(DXE3GEN) -o $(LIBSDIR)/basrt.dxe \ + -E _bas -E _BAS \ + -U $(RT_OBJS) + mv $(LIBSDIR)/basrt.dxe $@ + +$(LIBSDIR)/basrt.dep: ../config/basrt.dep | $(LIBSDIR) + sed 's/$$/\r/' $< > $@ + +# IDE app DXE (compiler linked in, runtime from basrt.lib) +$(APP_TARGET): $(COMP_OBJS) $(APP_OBJS) dvxbasic.res | $(APPDIR) + $(DXE3GEN) -o $@ -E _appDescriptor -E _appMain -U $(COMP_OBJS) $(APP_OBJS) + $(DVXRES) build $@ dvxbasic.res + +install-samples: $(SAMPLES) | $(APPDIR) + cp $(SAMPLES) $(APPDIR)/ + +# Object files +$(OBJDIR)/codegen.o: compiler/codegen.c compiler/codegen.h compiler/opcodes.h runtime/values.h | $(OBJDIR) + $(CC) $(CFLAGS) -c -o $@ $< + +$(OBJDIR)/ideMain.o: ide/ideMain.c compiler/parser.h runtime/vm.h | $(OBJDIR) + $(CC) $(CFLAGS) -c -o $@ $< + +$(OBJDIR)/lexer.o: compiler/lexer.c compiler/lexer.h | $(OBJDIR) + $(CC) $(CFLAGS) -c -o $@ $< + +$(OBJDIR)/parser.o: compiler/parser.c compiler/parser.h compiler/lexer.h compiler/codegen.h compiler/symtab.h compiler/opcodes.h | $(OBJDIR) + $(CC) $(CFLAGS) -c -o $@ $< + +$(OBJDIR)/symtab.o: compiler/symtab.c compiler/symtab.h compiler/opcodes.h | $(OBJDIR) + $(CC) $(CFLAGS) -c -o $@ $< + +$(OBJDIR)/values.o: runtime/values.c runtime/values.h compiler/opcodes.h | $(OBJDIR) + $(CC) $(CFLAGS) -c -o $@ $< + +$(OBJDIR)/vm.o: runtime/vm.c runtime/vm.h runtime/values.h compiler/opcodes.h | $(OBJDIR) + $(CC) $(CFLAGS) -c -o $@ $< + +# Directories +$(OBJDIR): + mkdir -p $(OBJDIR) + +$(LIBSDIR): + mkdir -p $(LIBSDIR) + +$(APPDIR): + mkdir -p $(APPDIR) + +clean: + rm -f $(RT_OBJS) $(COMP_OBJS) $(APP_OBJS) $(RT_TARGET) $(APP_TARGET) $(LIBSDIR)/basrt.dep $(OBJDIR)/basrt_init.o diff --git a/dvxbasic/dvxbasic.res b/dvxbasic/dvxbasic.res new file mode 100644 index 0000000..3bdd26f --- /dev/null +++ b/dvxbasic/dvxbasic.res @@ -0,0 +1,5 @@ +# dvxbasic.res -- Resource manifest for DVX BASIC +icon32 icon icon32.bmp +name text "DVX BASIC" +author text "DVX Project" +description text "BASIC language IDE and runtime" diff --git a/dvxbasic/icon32.bmp b/dvxbasic/icon32.bmp new file mode 100644 index 0000000..578c822 --- /dev/null +++ b/dvxbasic/icon32.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3b09dfbd05163f80708b1921b7326f17d9ddfe86540e1a50f20a826f296dd2af +size 3126 diff --git a/dvxbasic/ide/ideMain.c b/dvxbasic/ide/ideMain.c new file mode 100644 index 0000000..fefb27a --- /dev/null +++ b/dvxbasic/ide/ideMain.c @@ -0,0 +1,496 @@ +// ideMain.c -- DVX BASIC Runner application +// +// A DVX app that loads, compiles, and runs BASIC programs. +// PRINT output goes to a scrollable TextArea widget. Compile +// errors are displayed with line numbers. +// +// This is Phase 3 of DVX BASIC: proving the compiler and VM +// work on real hardware inside the DVX windowing system. + +#include "dvxApp.h" +#include "dvxDialog.h" +#include "dvxWidget.h" +#include "dvxWm.h" +#include "shellApp.h" +#include "widgetBox.h" +#include "widgetButton.h" +#include "widgetLabel.h" +#include "widgetTextInput.h" +#include "widgetStatusBar.h" + +#include "../compiler/parser.h" +#include "../runtime/vm.h" +#include "../runtime/values.h" + +#include +#include +#include +#include +#include + +// ============================================================ +// Constants +// ============================================================ + +#define IDE_WIN_W 560 +#define IDE_WIN_H 400 +#define IDE_BTN_W 80 +#define IDE_BTN_SPACING 8 +#define IDE_MAX_SOURCE 65536 +#define IDE_MAX_OUTPUT 32768 + +// Menu command IDs +#define CMD_OPEN 100 +#define CMD_RUN 101 +#define CMD_STOP 102 +#define CMD_CLEAR 103 +#define CMD_EXIT 104 + +// ============================================================ +// Prototypes +// ============================================================ + +int32_t appMain(DxeAppContextT *ctx); +static void buildWindow(void); +static void clearOutput(void); +static void compileAndRun(void); +static void loadFile(void); +static void onClose(WindowT *win); +static void onMenu(WindowT *win, int32_t menuId); +static void onOpenClick(WidgetT *w); +static void onRunClick(WidgetT *w); +static void onClearClick(WidgetT *w); +static void printCallback(void *ctx, const char *text, bool newline); +static bool inputCallback(void *ctx, const char *prompt, char *buf, int32_t bufSize); +static bool doEventsCallback(void *ctx); +static void setStatus(const char *text); + +// ============================================================ +// Module state +// ============================================================ + +static DxeAppContextT *sCtx = NULL; +static AppContextT *sAc = NULL; +static WindowT *sWin = NULL; +static WidgetT *sEditor = NULL; // TextArea for source code +static WidgetT *sOutput = NULL; // TextArea for program output +static WidgetT *sStatus = NULL; // Status bar label +static BasVmT *sVm = NULL; // VM instance (non-NULL while running) + +static char sSourceBuf[IDE_MAX_SOURCE]; +static char sOutputBuf[IDE_MAX_OUTPUT]; +static int32_t sOutputLen = 0; +static char sFilePath[260]; + +// ============================================================ +// App descriptor +// ============================================================ + +AppDescriptorT appDescriptor = { + .name = "DVX BASIC", + .hasMainLoop = false, + .multiInstance = false, + .stackSize = SHELL_STACK_DEFAULT, + .priority = 0 +}; + +// ============================================================ +// appMain +// ============================================================ + +int32_t appMain(DxeAppContextT *ctx) { + sCtx = ctx; + sAc = ctx->shellCtx; + + basStringSystemInit(); + buildWindow(); + + sFilePath[0] = '\0'; + sSourceBuf[0] = '\0'; + sOutputBuf[0] = '\0'; + sOutputLen = 0; + + setStatus("Ready. Open a .BAS file or type code and press Run."); + return 0; +} + +// ============================================================ +// buildWindow +// ============================================================ + +static void buildWindow(void) { + int32_t winX = (sAc->display.width - IDE_WIN_W) / 2; + int32_t winY = (sAc->display.height - IDE_WIN_H) / 4; + + sWin = dvxCreateWindow(sAc, "DVX BASIC", winX, winY, IDE_WIN_W, IDE_WIN_H, true); + + if (!sWin) { + return; + } + + sWin->onClose = onClose; + sWin->onMenu = onMenu; + + // Menu bar + MenuBarT *menuBar = wmAddMenuBar(sWin); + MenuT *fileMenu = wmAddMenu(menuBar, "&File"); + wmAddMenuItem(fileMenu, "&Open...\tCtrl+O", CMD_OPEN); + wmAddMenuSeparator(fileMenu); + wmAddMenuItem(fileMenu, "E&xit", CMD_EXIT); + + MenuT *runMenu = wmAddMenu(menuBar, "&Run"); + wmAddMenuItem(runMenu, "&Run\tF5", CMD_RUN); + wmAddMenuSeparator(runMenu); + wmAddMenuItem(runMenu, "&Clear Output", CMD_CLEAR); + + AccelTableT *accel = dvxCreateAccelTable(); + dvxAddAccel(accel, 'O', ACCEL_CTRL, CMD_OPEN); + dvxAddAccel(accel, KEY_F5, 0, CMD_RUN); + sWin->accelTable = accel; + + // Widget tree + WidgetT *root = wgtInitWindow(sAc, sWin); + + // Source code editor (top half) + WidgetT *editorFrame = wgtFrame(root, "Source"); + editorFrame->weight = 100; + sEditor = wgtTextArea(editorFrame, IDE_MAX_SOURCE); + sEditor->weight = 100; + + // Button bar + WidgetT *btnRow = wgtHBox(root); + btnRow->spacing = wgtPixels(IDE_BTN_SPACING); + + WidgetT *openBtn = wgtButton(btnRow, "&Open..."); + openBtn->onClick = onOpenClick; + openBtn->prefW = wgtPixels(IDE_BTN_W); + + WidgetT *runBtn = wgtButton(btnRow, "&Run"); + runBtn->onClick = onRunClick; + runBtn->prefW = wgtPixels(IDE_BTN_W); + + WidgetT *clearBtn = wgtButton(btnRow, "&Clear"); + clearBtn->onClick = onClearClick; + clearBtn->prefW = wgtPixels(IDE_BTN_W); + + // Output area (bottom half) + WidgetT *outputFrame = wgtFrame(root, "Output"); + outputFrame->weight = 100; + sOutput = wgtTextArea(outputFrame, IDE_MAX_OUTPUT); + sOutput->weight = 100; + sOutput->readOnly = true; + + // Status bar + WidgetT *statusBar = wgtStatusBar(root); + sStatus = wgtLabel(statusBar, ""); + sStatus->weight = 100; + + dvxFitWindow(sAc, sWin); +} + +// ============================================================ +// clearOutput +// ============================================================ + +static void clearOutput(void) { + sOutputBuf[0] = '\0'; + sOutputLen = 0; + wgtSetText(sOutput, ""); +} + +// ============================================================ +// compileAndRun +// ============================================================ + +static void compileAndRun(void) { + // Get source from editor + const char *src = wgtGetText(sEditor); + + if (!src || *src == '\0') { + setStatus("No source code to run."); + return; + } + + clearOutput(); + setStatus("Compiling..."); + + // Force a display update so the status is visible + dvxInvalidateWindow(sAc, sWin); + + int32_t srcLen = (int32_t)strlen(src); + + // Compile (heap-allocated -- BasParserT is ~300KB, too large for stack) + BasParserT *parser = (BasParserT *)malloc(sizeof(BasParserT)); + + if (!parser) { + setStatus("Out of memory."); + return; + } + + basParserInit(parser, src, srcLen); + + if (!basParse(parser)) { + int32_t n = snprintf(sOutputBuf, IDE_MAX_OUTPUT, "COMPILE ERROR:\n%s\n", parser->error); + sOutputLen = n; + wgtSetText(sOutput, sOutputBuf); + setStatus("Compilation failed."); + basParserFree(parser); + free(parser); + return; + } + + BasModuleT *mod = basParserBuildModule(parser); + basParserFree(parser); + free(parser); + + if (!mod) { + setStatus("Failed to build module."); + return; + } + + setStatus("Running..."); + + // Create VM + BasVmT *vm = basVmCreate(); + basVmLoadModule(vm, mod); + + // Set up implicit main frame + vm->callStack[0].localCount = mod->globalCount > BAS_VM_MAX_LOCALS ? BAS_VM_MAX_LOCALS : mod->globalCount; + vm->callDepth = 1; + + // Set I/O callbacks + basVmSetPrintCallback(vm, printCallback, NULL); + basVmSetInputCallback(vm, inputCallback, NULL); + basVmSetDoEventsCallback(vm, doEventsCallback, NULL); + + sVm = vm; + + // Run with step limit to prevent infinite loops from freezing the GUI + vm->running = true; + int32_t stepCount = 0; + + while (vm->running) { + BasVmResultE result = basVmStep(vm); + + stepCount++; + + if (result != BAS_VM_OK) { + if (result == BAS_VM_HALTED) { + // Normal completion + } else { + // Runtime error + int32_t pos = sOutputLen; + int32_t n = snprintf(sOutputBuf + pos, IDE_MAX_OUTPUT - pos, "\n[Runtime error: %s]\n", basVmGetError(vm)); + sOutputLen += n; + wgtSetText(sOutput, sOutputBuf); + } + + break; + } + + // Yield to DVX every 10000 steps to keep the GUI responsive + if (stepCount % 10000 == 0) { + dvxUpdate(sAc); + } + } + + sVm = NULL; + + // Update output display + wgtSetText(sOutput, sOutputBuf); + + static char statusBuf[128]; + snprintf(statusBuf, sizeof(statusBuf), "Done. %ld instructions executed.", (long)stepCount); + setStatus(statusBuf); + + basVmDestroy(vm); + basModuleFree(mod); +} + +// ============================================================ +// doEventsCallback +// ============================================================ + +static bool doEventsCallback(void *ctx) { + (void)ctx; + + // Update DVX display and process events + if (sAc) { + dvxUpdate(sAc); + } + + return true; // continue running +} + +// ============================================================ +// inputCallback +// ============================================================ + +static bool inputCallback(void *ctx, const char *prompt, char *buf, int32_t bufSize) { + (void)ctx; + + // Append prompt to output + if (prompt && sOutputLen < IDE_MAX_OUTPUT - 1) { + int32_t n = snprintf(sOutputBuf + sOutputLen, IDE_MAX_OUTPUT - sOutputLen, "%s", prompt); + sOutputLen += n; + wgtSetText(sOutput, sOutputBuf); + } + + // Use DVX input dialog + // For now, a simple message box prompt + // TODO: proper INPUT dialog + buf[0] = '\0'; + return false; // cancel for now +} + +// ============================================================ +// loadFile +// ============================================================ + +static void loadFile(void) { + FileFilterT filters[] = { + { "BASIC Files (*.bas)", "*.bas" }, + { "Form Files (*.frm)", "*.frm" }, + { "All Files (*.*)", "*.*" } + }; + + char path[260]; + + if (dvxFileDialog(sAc, "Open BASIC File", FD_OPEN, NULL, filters, 3, path, sizeof(path))) { + FILE *f = fopen(path, "r"); + + if (!f) { + dvxMessageBox(sAc, "Error", "Could not open file.", MB_OK | MB_ICONERROR); + return; + } + + fseek(f, 0, SEEK_END); + long size = ftell(f); + fseek(f, 0, SEEK_SET); + + if (size >= IDE_MAX_SOURCE - 1) { + fclose(f); + dvxMessageBox(sAc, "Error", "File too large.", MB_OK | MB_ICONERROR); + return; + } + + int32_t bytesRead = (int32_t)fread(sSourceBuf, 1, size, f); + fclose(f); + sSourceBuf[bytesRead] = '\0'; + + wgtSetText(sEditor, sSourceBuf); + strncpy(sFilePath, path, sizeof(sFilePath) - 1); + sFilePath[sizeof(sFilePath) - 1] = '\0'; + + // Update window title + char title[300]; + snprintf(title, sizeof(title), "DVX BASIC - %s", path); + dvxSetTitle(sAc, sWin, title); + + setStatus("File loaded."); + } +} + +// ============================================================ +// onClearClick +// ============================================================ + +static void onClearClick(WidgetT *w) { + (void)w; + clearOutput(); + setStatus("Output cleared."); +} + +// ============================================================ +// onClose +// ============================================================ + +static void onClose(WindowT *win) { + sWin = NULL; + sEditor = NULL; + sOutput = NULL; + sStatus = NULL; + dvxDestroyWindow(sAc, win); +} + +// ============================================================ +// onMenu +// ============================================================ + +static void onMenu(WindowT *win, int32_t menuId) { + (void)win; + + switch (menuId) { + case CMD_OPEN: + loadFile(); + break; + + case CMD_RUN: + compileAndRun(); + break; + + case CMD_CLEAR: + clearOutput(); + break; + + case CMD_EXIT: + if (sWin) { + onClose(sWin); + } + break; + } +} + +// ============================================================ +// onOpenClick +// ============================================================ + +static void onOpenClick(WidgetT *w) { + (void)w; + loadFile(); +} + +// ============================================================ +// onRunClick +// ============================================================ + +static void onRunClick(WidgetT *w) { + (void)w; + compileAndRun(); +} + +// ============================================================ +// printCallback +// ============================================================ + +static void printCallback(void *ctx, const char *text, bool newline) { + (void)ctx; + + if (!text) { + return; + } + + int32_t textLen = (int32_t)strlen(text); + + // Append to output buffer + if (sOutputLen + textLen < IDE_MAX_OUTPUT - 2) { + memcpy(sOutputBuf + sOutputLen, text, textLen); + sOutputLen += textLen; + } + + if (newline && sOutputLen < IDE_MAX_OUTPUT - 2) { + sOutputBuf[sOutputLen++] = '\n'; + } + + sOutputBuf[sOutputLen] = '\0'; +} + +// ============================================================ +// setStatus +// ============================================================ + +static void setStatus(const char *text) { + if (sStatus) { + wgtSetText(sStatus, text); + } +} diff --git a/dvxbasic/samples/hello.bas b/dvxbasic/samples/hello.bas new file mode 100644 index 0000000..949bec6 --- /dev/null +++ b/dvxbasic/samples/hello.bas @@ -0,0 +1,27 @@ +' hello.bas -- DVX BASIC Hello World +' +' A simple program to test the DVX BASIC runner. + +PRINT "Hello from DVX BASIC!" +PRINT +PRINT "Testing arithmetic: 2 + 3 * 4 ="; 2 + 3 * 4 +PRINT "Testing strings: "; "Hello" & " " & "World" +PRINT + +DIM i AS INTEGER +PRINT "Fibonacci sequence:" +DIM a AS INTEGER +DIM b AS INTEGER +DIM temp AS INTEGER +a = 0 +b = 1 +FOR i = 1 TO 15 + PRINT a; + temp = a + b + a = b + b = temp +NEXT i +PRINT + +PRINT +PRINT "Done!" diff --git a/tools/mkicon.c b/tools/mkicon.c index 311993a..320de3a 100644 --- a/tools/mkicon.c +++ b/tools/mkicon.c @@ -194,6 +194,57 @@ static void iconImgview(void) { rect(5, 22, 22, 3, 60, 120, 60); } +// DVX BASIC icon: code brackets with "B" letterform +static void iconBasic(void) { + clear(192, 192, 192); + + // Blue background rectangle (code editor feel) + rect(2, 2, 28, 28, 0, 0, 128); + + // Left bracket { + vline(6, 6, 20, 255, 255, 0); + hline(7, 6, 3, 255, 255, 0); + hline(7, 25, 3, 255, 255, 0); + hline(4, 15, 2, 255, 255, 0); + pixel(5, 14, 255, 255, 0); + pixel(5, 16, 255, 255, 0); + + // "B" letter in white (bold, centered) + // Vertical stroke + vline(14, 7, 18, 255, 255, 255); + + // Top horizontal + hline(14, 7, 7, 255, 255, 255); + hline(14, 8, 7, 255, 255, 255); + + // Middle horizontal + hline(14, 15, 7, 255, 255, 255); + hline(14, 16, 7, 255, 255, 255); + + // Bottom horizontal + hline(14, 23, 7, 255, 255, 255); + hline(14, 24, 7, 255, 255, 255); + + // Right curves of B (top bump) + vline(21, 8, 7, 255, 255, 255); + pixel(20, 8, 255, 255, 255); + pixel(20, 14, 255, 255, 255); + + // Right curves of B (bottom bump) + vline(21, 17, 6, 255, 255, 255); + pixel(20, 17, 255, 255, 255); + pixel(20, 23, 255, 255, 255); + + // Right bracket } + vline(25, 6, 20, 255, 255, 0); + hline(22, 6, 3, 255, 255, 0); + hline(22, 25, 3, 255, 255, 0); + hline(26, 15, 2, 255, 255, 0); + pixel(26, 14, 255, 255, 0); + pixel(26, 16, 255, 255, 0); +} + + static void writeBmp(const char *path) { int32_t rowPad = (4 - (W * 3) % 4) % 4; int32_t rowSize = W * 3 + rowPad; @@ -243,7 +294,7 @@ static void writeBmp(const char *path) { int main(int argc, char **argv) { if (argc < 3) { fprintf(stderr, "Usage: mkicon \n"); - fprintf(stderr, "Types: clock, notepad, cpanel, dvxdemo, imgview\n"); + fprintf(stderr, "Types: clock, notepad, cpanel, dvxdemo, imgview, basic\n"); return 1; } @@ -258,6 +309,8 @@ int main(int argc, char **argv) { iconCpanel(); } else if (strcmp(type, "dvxdemo") == 0) { iconDvxdemo(); + } else if (strcmp(type, "basic") == 0) { + iconBasic(); } else if (strcmp(type, "imgview") == 0) { iconImgview(); } else {