refactor(mouse-driver): slim qbext TSR, ungate programs, refresh media
authorSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Thu, 27 Aug 2026 22:19:07 +0000 (01:19 +0300)
committerSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Thu, 27 Aug 2026 22:19:07 +0000 (01:19 +0300)
- qbext: 506 to 234 bytes — drop the unused update counter, inline the
  startup banner into the source, add build.sh; index.org rewritten to
  document the minimal 8-byte shared-memory protocol
- Galaxy/Universe/Maze explorer and Swapping 3D engine gated all mouse
  reads on the removed counter byte (getByte(8)) and silently froze
  with the new driver — drop the gate, read-and-reset every frame
- recapture showcase media as CRT Basic screencasts/screenshots
  (mp4/png replacing webm/png); rename Windowing system.bas to
  wsystem.bas

28 files changed:
3D GFX/Miscellaneous/3D text.webm [deleted file]
3D GFX/Miscellaneous/Maze explorer - Screencast 0.mp4 [new file with mode: 0644]
3D GFX/Miscellaneous/Maze explorer - Screenshot 0.png [new file with mode: 0644]
3D GFX/Miscellaneous/Maze explorer.bas
3D GFX/Miscellaneous/Maze explorer.png [deleted file]
3D GFX/Space/Galaxy explorer - Screencast 0.mp4 [new file with mode: 0644]
3D GFX/Space/Galaxy explorer - Screenshot 0.png [new file with mode: 0644]
3D GFX/Space/Galaxy explorer.bas
3D GFX/Space/Galaxy explorer.png [deleted file]
3D GFX/Space/Universe explorer/Universe explorer.bas
3D GFX/Swapping 3D engine/engine.bas
Math/Game of life in 3D - Screencast 0.mp4 [new file with mode: 0644]
Math/Game of life in 3D.bas
Math/Game of life in 3D.webm [deleted file]
Math/Multiplication trainer - Screencast 0.mp4 [new file with mode: 0644]
Math/Multiplication trainer - Screenshot 0.png [new file with mode: 0644]
Math/Multiplication trainer.bas
Math/Multiplication trainer.png [deleted file]
Math/Sine computation - Screenshot 0.png [new file with mode: 0644]
Math/Sine computation.png [deleted file]
Miscellaneous/Mouse driver/build.sh [new file with mode: 0755]
Miscellaneous/Mouse driver/index.org
Miscellaneous/Mouse driver/qbext.asm
Miscellaneous/Mouse driver/qbext.com
Miscellaneous/Windowing system.bas [deleted file]
Miscellaneous/Windowing system.webm [deleted file]
Miscellaneous/wsystem - Screencast 0.mp4 [new file with mode: 0644]
Miscellaneous/wsystem.bas [new file with mode: 0755]

diff --git a/3D GFX/Miscellaneous/3D text.webm b/3D GFX/Miscellaneous/3D text.webm
deleted file mode 100644 (file)
index 0846d14..0000000
Binary files a/3D GFX/Miscellaneous/3D text.webm and /dev/null differ
diff --git a/3D GFX/Miscellaneous/Maze explorer - Screencast 0.mp4 b/3D GFX/Miscellaneous/Maze explorer - Screencast 0.mp4
new file mode 100644 (file)
index 0000000..4d7925c
Binary files /dev/null and b/3D GFX/Miscellaneous/Maze explorer - Screencast 0.mp4 differ
diff --git a/3D GFX/Miscellaneous/Maze explorer - Screenshot 0.png b/3D GFX/Miscellaneous/Maze explorer - Screenshot 0.png
new file mode 100644 (file)
index 0000000..63d668b
Binary files /dev/null and b/3D GFX/Miscellaneous/Maze explorer - Screenshot 0.png differ
index 015d32f..2ebb8fb 100755 (executable)
@@ -160,42 +160,39 @@ END SUB
 \r
 SUB handleInput\r
 \r
-' Read mouse data\r
-IF getByte(8) <> 0 THEN\r
-  putByte 8, 0\r
-  ' Read mouse translation along x axis\r
-  mouseXDelta = getWord(2)\r
-  putWord 2, 0\r
-  ' Read mouse translation along y axis\r
-  mouseYDelta = getWord(4)\r
-  putWord 4, 0\r
-  ' Read pressed mouse buttons\r
-  button = getWord(6)\r
-  putWord 6, 0\r
-\r
-  ' Detect if left, right or both mouse buttons were pressed\r
-  leftMouseButton = 0\r
-  rightMouseButton = 0\r
-  IF button = 1 THEN leftMouseButton = 1\r
-  IF button = 2 THEN rightMouseButton = 1\r
-  IF button = 3 THEN leftMouseButton = 1: rightMouseButton = 1\r
-\r
-  IF rightMouseButton = 1 THEN\r
-    IF leftMouseButton = 1 THEN\r
-      ' If mouse left and right buttons are pressed at the same time,\r
-      ' use mouse translation to move avatar around X and Z axis.\r
-      cameraXSpeed = cameraXSpeed + SIN(angleY) * mouseYDelta / 4\r
-      cameraZSpeed = cameraZSpeed - COS(angleY) * mouseYDelta / 4\r
-      GOTO 3\r
-    END IF\r
-    ' If only right button is pressed, move around Y axis\r
-    cameraYSpeed = cameraYSpeed + mouseYDelta / 4\r
-3\r
-    mouseYDelta = 0\r
+' Read mouse data from the driver and reset its counters (every frame)\r
+' Read mouse translation along x axis\r
+mouseXDelta = getWord(2)\r
+putWord 2, 0\r
+' Read mouse translation along y axis\r
+mouseYDelta = getWord(4)\r
+putWord 4, 0\r
+' Read pressed mouse buttons\r
+button = getWord(6)\r
+putWord 6, 0\r
+\r
+' Detect if left, right or both mouse buttons were pressed\r
+leftMouseButton = 0\r
+rightMouseButton = 0\r
+IF button = 1 THEN leftMouseButton = 1\r
+IF button = 2 THEN rightMouseButton = 1\r
+IF button = 3 THEN leftMouseButton = 1: rightMouseButton = 1\r
+\r
+IF rightMouseButton = 1 THEN\r
+  IF leftMouseButton = 1 THEN\r
+    ' If mouse left and right buttons are pressed at the same time,\r
+    ' use mouse translation to move avatar around X and Z axis.\r
+    cameraXSpeed = cameraXSpeed + SIN(angleY) * mouseYDelta / 4\r
+    cameraZSpeed = cameraZSpeed - COS(angleY) * mouseYDelta / 4\r
+    GOTO 3\r
   END IF\r
-\r
+  ' If only right button is pressed, move around Y axis\r
+  cameraYSpeed = cameraYSpeed + mouseYDelta / 4\r
+3\r
+  mouseYDelta = 0\r
 END IF\r
 \r
+\r
 ' Limit mouse movement to maxMove\r
 IF mouseXDelta < -maxMove THEN mouseXDelta = -maxMove\r
 IF mouseXDelta > maxMove THEN mouseXDelta = maxMove\r
diff --git a/3D GFX/Miscellaneous/Maze explorer.png b/3D GFX/Miscellaneous/Maze explorer.png
deleted file mode 100644 (file)
index 96348a3..0000000
Binary files a/3D GFX/Miscellaneous/Maze explorer.png and /dev/null differ
diff --git a/3D GFX/Space/Galaxy explorer - Screencast 0.mp4 b/3D GFX/Space/Galaxy explorer - Screencast 0.mp4
new file mode 100644 (file)
index 0000000..135fd23
Binary files /dev/null and b/3D GFX/Space/Galaxy explorer - Screencast 0.mp4 differ
diff --git a/3D GFX/Space/Galaxy explorer - Screenshot 0.png b/3D GFX/Space/Galaxy explorer - Screenshot 0.png
new file mode 100644 (file)
index 0000000..db337b2
Binary files /dev/null and b/3D GFX/Space/Galaxy explorer - Screenshot 0.png differ
index ecd8abd..ab2f8c9 100755 (executable)
@@ -98,37 +98,33 @@ GOTO 1
 
 SUB control
 
-' Check for mouse input
-IF getByte(8) <> 0 THEN
-    putByte 8, 0
-    xPosition = getWord(2)
-    putWord 2, 0
-    yPosition = getWord(4)
-    putWord 4, 0
-    button = getWord(6)
-    putWord 6, 0
-
-    ' Reset mouse buttons
-    buttonLeft = 0
-    buttonRight = 0
-
-    ' Handle mouse button states
-    IF button = 1 THEN buttonLeft = 1
-    IF button = 2 THEN buttonRight = 1
-    IF button = 3 THEN buttonLeft = 1: buttonRight = 1
-
-    ' Handle mouse movements
-    IF buttonRight = 1 THEN
-        IF buttonLeft = 1 THEN
-            speedX = speedX + SIN(angle1) * yPosition / 4
-            speedZ = speedZ - COS(angle1) * yPosition / 4
-            GOTO 3
-        END IF
-        speedY = speedY + yPosition / 4
-    3
-        yPosition = 0
+' Read mouse input from the driver, resetting its counters every frame
+xPosition = getWord(2)
+putWord 2, 0
+yPosition = getWord(4)
+putWord 4, 0
+button = getWord(6)
+putWord 6, 0
+
+' Reset mouse buttons
+buttonLeft = 0
+buttonRight = 0
+
+' Handle mouse button states
+IF button = 1 THEN buttonLeft = 1
+IF button = 2 THEN buttonRight = 1
+IF button = 3 THEN buttonLeft = 1: buttonRight = 1
+
+' Handle mouse movements
+IF buttonRight = 1 THEN
+    IF buttonLeft = 1 THEN
+        speedX = speedX + SIN(angle1) * yPosition / 4
+        speedZ = speedZ - COS(angle1) * yPosition / 4
+        GOTO 3
     END IF
-
+    speedY = speedY + yPosition / 4
+3
+    yPosition = 0
 END IF
 
 ' Limit the position values to prevent overflow
diff --git a/3D GFX/Space/Galaxy explorer.png b/3D GFX/Space/Galaxy explorer.png
deleted file mode 100644 (file)
index 28550e6..0000000
Binary files a/3D GFX/Space/Galaxy explorer.png and /dev/null differ
index 37b44ff..634c4d6 100755 (executable)
@@ -143,38 +143,34 @@ GOTO 1
 \r
 SUB control\r
 \r
-' Handle mouse input\r
-IF getbyte(8) <> 0 THEN\r
-  putbyte 8, 0\r
-  xp = getword(2)\r
-  putword 2, 0\r
-  yp = getword(4)\r
-  putword 4, 0\r
-  butt = getword(6)\r
-  putword 6, 0\r
-\r
-  ' Determine which mouse buttons are pressed\r
-  buttL = 0\r
-  buttR = 0\r
-  IF butt = 1 THEN buttL = 1\r
-  IF butt = 2 THEN buttR = 1\r
-  IF butt = 3 THEN buttL = 1: buttR = 1\r
-\r
-  ' Handle right mouse button for up/down movement\r
-  IF buttR = 1 THEN\r
-    ' Handle both buttons pressed for back/front movement\r
-    IF buttL = 1 THEN\r
-      myxs = myxs + SIN(an1) * yp / 4\r
-      myzs = myzs - COS(an1) * yp / 4\r
-      GOTO 3\r
-    END IF\r
-\r
-    ' Handle right button for up/down movement\r
-    myys = myys + yp / 4\r
-3\r
-    yp = 0\r
+' Handle mouse input: read the driver and reset its counters every frame\r
+xp = getword(2)\r
+putword 2, 0\r
+yp = getword(4)\r
+putword 4, 0\r
+butt = getword(6)\r
+putword 6, 0\r
+\r
+' Determine which mouse buttons are pressed\r
+buttL = 0\r
+buttR = 0\r
+IF butt = 1 THEN buttL = 1\r
+IF butt = 2 THEN buttR = 1\r
+IF butt = 3 THEN buttL = 1: buttR = 1\r
+\r
+' Handle right mouse button for up/down movement\r
+IF buttR = 1 THEN\r
+  ' Handle both buttons pressed for back/front movement\r
+  IF buttL = 1 THEN\r
+    myxs = myxs + SIN(an1) * yp / 4\r
+    myzs = myzs - COS(an1) * yp / 4\r
+    GOTO 3\r
   END IF\r
 \r
+  ' Handle right button for up/down movement\r
+  myys = myys + yp / 4\r
+3\r
+  yp = 0\r
 END IF\r
 \r
 ' Clamp user input to maximum movement speed\r
index 52e2132..2af65d6 100755 (executable)
@@ -274,37 +274,34 @@ END SUB
 SUB control\r
 \r
 \r
-IF getbyte(8) <> 0 THEN\r
-  putbyte 8, 0\r
-  xp = getword(2)\r
-  putword 2, 0\r
-  yp = getword(4)\r
-  putword 4, 0\r
-  butt = getword(6)\r
-  putword 6, 0\r
-  buttL = 0\r
-  buttR = 0\r
-  IF butt = 1 THEN buttL = 1\r
-  IF butt = 2 THEN buttR = 1\r
-  IF butt = 3 THEN buttL = 1: buttR = 1\r
-\r
-\r
-  IF buttR = 1 THEN\r
-    IF buttL = 1 THEN\r
-      myxs = myxs + SIN(an1) * yp / 4\r
-      myzs = myzs - COS(an1) * yp / 4\r
-      GOTO 3\r
-    END IF\r
-    myys = myys + yp / 4\r
-3\r
-    yp = 0\r
+xp = getword(2)\r
+putword 2, 0\r
+yp = getword(4)\r
+putword 4, 0\r
+butt = getword(6)\r
+putword 6, 0\r
+buttL = 0\r
+buttR = 0\r
+IF butt = 1 THEN buttL = 1\r
+IF butt = 2 THEN buttR = 1\r
+IF butt = 3 THEN buttL = 1: buttR = 1\r
+\r
+\r
+IF buttR = 1 THEN\r
+  IF buttL = 1 THEN\r
+    myxs = myxs + SIN(an1) * yp / 4\r
+    myzs = myzs - COS(an1) * yp / 4\r
+    GOTO 3\r
   END IF\r
-\r
+  myys = myys + yp / 4\r
+3\r
+  yp = 0\r
 END IF\r
 \r
 \r
 \r
 \r
+\r
 IF xp < -maxmove THEN xp = -maxmove\r
 IF xp > maxmove THEN xp = maxmove\r
 an1 = an1 - xp / 150\r
diff --git a/Math/Game of life in 3D - Screencast 0.mp4 b/Math/Game of life in 3D - Screencast 0.mp4
new file mode 100644 (file)
index 0000000..14b5a73
Binary files /dev/null and b/Math/Game of life in 3D - Screencast 0.mp4 differ
index 70652ec..29536a1 100755 (executable)
@@ -36,6 +36,8 @@ InitializeGame
 rotationAngle1 = 1.5
 
 10
+
+SOUND 0, 1
 currentFrameCount = currentFrameCount + 1
 Generate3DScene
 DrawScene
diff --git a/Math/Game of life in 3D.webm b/Math/Game of life in 3D.webm
deleted file mode 100644 (file)
index 2b7e1ed..0000000
Binary files a/Math/Game of life in 3D.webm and /dev/null differ
diff --git a/Math/Multiplication trainer - Screencast 0.mp4 b/Math/Multiplication trainer - Screencast 0.mp4
new file mode 100644 (file)
index 0000000..0fef991
Binary files /dev/null and b/Math/Multiplication trainer - Screencast 0.mp4 differ
diff --git a/Math/Multiplication trainer - Screenshot 0.png b/Math/Multiplication trainer - Screenshot 0.png
new file mode 100644 (file)
index 0000000..2dc69ae
Binary files /dev/null and b/Math/Multiplication trainer - Screenshot 0.png differ
index 1be28bf..a9f2d5a 100644 (file)
@@ -112,10 +112,10 @@ NEXT y
 ' Get user input for their name\r
 LOCATE 5, 1\r
 COLOR 7\r
-INPUT "Enter your name ", userName$\r
+INPUT "Enter your name: ", userName$\r
 LOCATE 5, 1\r
 COLOR 8\r
-PRINT "Enter your name " + userName$\r
+PRINT "Enter your name: " + userName$\r
 \r
 ' Greet the user\r
 LOCATE 6, 1\r
@@ -185,4 +185,3 @@ GOTO 2
 3\r
 CLS\r
 END SUB\r
-\r
diff --git a/Math/Multiplication trainer.png b/Math/Multiplication trainer.png
deleted file mode 100644 (file)
index 7ea027a..0000000
Binary files a/Math/Multiplication trainer.png and /dev/null differ
diff --git a/Math/Sine computation - Screenshot 0.png b/Math/Sine computation - Screenshot 0.png
new file mode 100644 (file)
index 0000000..bb6016f
Binary files /dev/null and b/Math/Sine computation - Screenshot 0.png differ
diff --git a/Math/Sine computation.png b/Math/Sine computation.png
deleted file mode 100644 (file)
index 86fc66c..0000000
Binary files a/Math/Sine computation.png and /dev/null differ
diff --git a/Miscellaneous/Mouse driver/build.sh b/Miscellaneous/Mouse driver/build.sh
new file mode 100755 (executable)
index 0000000..4dc29f5
--- /dev/null
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+# Assemble qbext.asm into qbext.com using FASM.
+# Requires: fasm (Debian package: fasm)
+set -euo pipefail
+
+cd "$(dirname "$0")"
+
+fasm qbext.asm qbext.com
index 02011a6..41a9bec 100644 (file)
 :CUSTOM_ID: overview\r
 :END:\r
 \r
-QBasic, a popular programming language in the DOS era, lacks native\r
-mouse support. This limitation can be a hurdle for developers looking\r
-to create interactive applications. To bridge this gap, I developed a\r
-workaround that allows QBasic to use mouse input.\r
+QBasic, a popular programming language of the DOS era, lacks native\r
+mouse support. This limitation is a hurdle for developers looking to\r
+create interactive applications. To bridge this gap, I developed a\r
+workaround that allows QBasic programs to use mouse input.\r
 \r
 * High-level idea\r
 :PROPERTIES:\r
 :CUSTOM_ID: high-level-idea\r
 :END:\r
 \r
-Workaround to access mouse involves a Terminate and Stay Resident\r
-(TSR) program written in x86 assembly. This TSR program must be\r
-started before running QBasic program that depends on mouse. This TSR\r
-program hooks into the system's interrupt mechanism, specifically the\r
-timer interrupt (IRQ 0), allowing it to regularly check for mouse\r
-activity several times per second.\r
-\r
-When this timer interrupt triggers, the TSR reads the latest mouse's\r
-horizontal and vertical movements and button states using mouse\r
-interrupts. This data is then stored in a dedicated memory location —\r
-a data table within the TSR's memory space. The TSR uses interrupt 79h\r
-as a pointer to this data table, making it accessible to other\r
-programs, including the QBasic application.\r
-\r
-While QBasic originally is not able to read mouse, it is able to read\r
-(and write) arbitrary location in system RAM. The QBasic demonstration\r
-program begins by retrieving the address of the TSR mouse data table\r
-from the interrupt vector table using interrupt 79h. By checking a\r
-predefined magic number (1983) in the data table, the program confirms\r
-that the mouse driver is loaded. Once verified, the QBasic program\r
-continuously reads mouse data from this shared memory location, while\r
-TSR keeps updating it with latest mouse state simultaneously.\r
+The workaround is a Terminate and Stay Resident (TSR) program written\r
+in x86 assembly. It must be started before running any QBasic program\r
+that depends on the mouse. The TSR hooks into the system timer\r
+interrupt (IRQ 0), which fires about 18.2 times per second, so it can\r
+poll the mouse at that rate.\r
+\r
+On every timer tick, the TSR reads the mouse motion counters and\r
+button states through the mouse driver interrupt (INT 33h) and\r
+accumulates them into a small data table in its own memory. It then\r
+stores the address of this table in the interrupt vector table slot\r
+of interrupt 79h, making the table discoverable by other programs.\r
+Interrupt 79h is never actually invoked — its vector slot is simply\r
+reused as a well-known storage location for the pointer. This assumes\r
+no other software occupies interrupt 79h.\r
+\r
+While QBasic cannot access the mouse directly, it can read (and\r
+write) arbitrary RAM locations using DEF SEG, PEEK and POKE. The\r
+QBasic program retrieves the data table address from the interrupt\r
+79h vector slot and verifies a predefined magic number (1983) at the\r
+start of the table to confirm the driver is loaded. Once verified,\r
+the QBasic program continuously reads mouse data from this shared\r
+memory while the TSR keeps it updated in the background.\r
 \r
 * Terminate and Stay Resident module\r
 :PROPERTIES:\r
 :CUSTOM_ID: terminate-and-stay-resident-module\r
 :END:\r
 \r
-A DOS TSR program that hooks into the system's interrupt mechanism to\r
-regularly read mouse input and store it in a dedicated memory\r
-location.\r
+A DOS TSR program that hooks the timer interrupt to regularly read\r
+mouse input and store it in a dedicated memory table.\r
 \r
 Files:\r
-- [[file:qbext.asm][qbext.asm - x86 Assembly source code]]\r
+- [[file:qbext.asm][qbext.asm - x86 assembly source code (FASM)]]\r
 - [[file:qbext.com][qbext.com - binary COM executable for DOS]]\r
+- [[file:build.sh][build.sh - build script that assembles qbext.asm into qbext.com]]\r
 \r
+** Data table\r
 \r
-Here is the detailed technical specification for an in-memory table\r
-used to exchange mouse coordinates between a TSR program and a QBasic\r
-program.\r
+In-memory table used to exchange mouse state between the TSR and a\r
+QBasic program:\r
 \r
-| Offset | Size (bytes) | Description             |\r
-|--------+--------------+-------------------------|\r
-|   0x00 |            2 | Magic Number (1983)     |\r
-|   0x02 |            2 | Horizontal Movement (X) |\r
-|   0x04 |            2 | Vertical Movement (Y)   |\r
-|   0x06 |            2 | Button Status           |\r
-|   0x08 |            1 | Update counter          |\r
+| Offset | Size (bytes) | Description                          |\r
+|--------+--------------+--------------------------------------|\r
+|   0x00 |            2 | Magic Number (1983)                  |\r
+|   0x02 |            2 | Horizontal Movement (X), accumulated |\r
+|   0x04 |            2 | Vertical Movement (Y), accumulated   |\r
+|   0x06 |            2 | Button Status, latched               |\r
 \r
-- Update counter :: Signals to the QBasic program that new mouse data\r
-  is available in the shared memory table. It ensures that the QBasic\r
-  program only reads fresh data and avoids processing outdated or\r
-  repeated data. When the TSR updates the mouse data, it increments\r
-  this flag by 1 to signal the QBasic program that new data is\r
-  available. QBasic can compare this number against last retrieved\r
-  value. If value has been increased, then there had been update\r
-  meanwhile.\r
+** Transfer protocol\r
+\r
+The TSR /accumulates/: on every timer tick it adds the latest motion\r
+deltas to the X and Y fields and ORs the current button state into the\r
+Button Status field.\r
+\r
+The consumer therefore uses a read-and-reset cycle: it reads a field\r
+and then writes 0 back to it (see the =getword= / =putword= routines\r
+in mousedrv.bas). A field that reads as 0 simply means "no new\r
+activity since the last read".\r
+\r
+Button Status bits (as reported by INT 33h function 03h):\r
+- bit 0 :: left button\r
+- bit 1 :: right button\r
+- bit 2 :: middle button\r
+\r
+Bits stay set (latched) until the consumer clears the field.\r
+\r
+* Building\r
+:PROPERTIES:\r
+:CUSTOM_ID: building\r
+:END:\r
+\r
+The TSR is written for the flat assembler (FASM). To produce\r
+=qbext.com=, run:\r
+\r
+#+begin_src sh\r
+./build.sh\r
+#+end_src\r
+\r
+* Usage\r
+:PROPERTIES:\r
+:CUSTOM_ID: usage\r
+:END:\r
+\r
+1. Boot DOS (or start DOSBox).\r
+2. Load the TSR by running =QBEXT.COM=. A startup banner is printed\r
+   and the program stays resident in memory.\r
+3. Start QBasic and run a program that uses the driver, for example\r
+   =mousedrv.bas=.\r
+\r
+If the TSR is not loaded, the demo detects the missing magic number\r
+and exits with:\r
+\r
+: FATAL ERROR: you must load\r
+: QBasic extension TSR first!\r
 \r
 * QBasic demonstration program\r
 :PROPERTIES:\r
 :CUSTOM_ID: qbasic-demonstration-program\r
 :END:\r
 \r
-A QBasic program that reads mouse data from the memory location\r
+A QBasic program that reads mouse data from the memory table\r
 populated by the TSR and demonstrates mouse movement and button\r
-clicks.\r
-\r
+clicks. It switches to 320x200 256-color graphics mode (SCREEN 13)\r
+and draws a circle that follows the mouse. Current coordinates and a\r
+frame counter are displayed at the top of the screen; button presses\r
+are printed as they occur. Press any key to exit.\r
 \r
 #+attr_html: :class responsive-img\r
 #+attr_latex: :width 1000px\r
index 89fb13f..d7b246c 100644 (file)
@@ -8,6 +8,7 @@
 ; Changelog:\r
 ; 2004.01, Initial version\r
 ; 2025, Improved program readability\r
+; 2026, Inlined startup message into source, removed unused update counter\r
 \r
 \r
 \r
@@ -74,7 +75,6 @@ custom:
     mov     ax, 3                   ; AH = 3, function to read mouse buttons\r
        int         33h                     ; Call mouse interrupt\r
     or      [CS:mouseButtons], bx   ; Update mouse button states\r
-       inc         byte [CS:updated]       ; Increment update flag. So that QBasic registers mouse update event.\r
        sti                             ; Re-enable interrupts\r
 \r
     ; Restore interrupted program state and return control to it\r
@@ -92,10 +92,10 @@ dataTable:
     mouseHorisontal dw 0            ; Horizontal mouse movement counter\r
     mouseVertical   dw 0            ; Vertical mouse movement counter\r
     mouseButtons    dw 0            ; Mouse button states\r
-       updated             db 0            ; Flag indicating data was updated\r
 \r
 endPointer:                         ; End of resident code pointer\r
 \r
 msg:                                ; Message to display at start\r
-file 'readme.txt'                   ; Include content of readme.txt\r
+       db 'Mouse adapter/driver for QBasic programs.', 0Dh, 0Ah\r
+       db 'Made by Svjatoslav Agejenko: http://svjatoslav.eu', 0Dh, 0Ah\r
        db '$'                          ; End of string marker\r
index ae54fc4..2a9fc20 100755 (executable)
Binary files a/Miscellaneous/Mouse driver/qbext.com and b/Miscellaneous/Mouse driver/qbext.com differ
diff --git a/Miscellaneous/Windowing system.bas b/Miscellaneous/Windowing system.bas
deleted file mode 100755 (executable)
index ff0d067..0000000
+++ /dev/null
@@ -1,274 +0,0 @@
-' Text mode windowing system. Each window can display text file.\r
-' Window content can be scrolled horizontally and vertically.\r
-' Window can have arbitrary size and location on the screen.\r
-'\r
-' This program is free software: released under Creative Commons Zero (CC0) license\r
-' by Svjatoslav Agejenko.\r
-' Email: svjatoslav@svjatoslav.eu\r
-' Homepage: http://www.svjatoslav.eu\r
-\r
-' Changelog:\r
-' 2003, Initial version\r
-' 2024-2025, Improved program readability\r
-\r
-DECLARE SUB demo ()\r
-DECLARE FUNCTION GetLineFromWindow$ (windowNum%, lineNum%)\r
-DECLARE SUB LoadFileIntoWindow (fileName$, windowNum%)\r
-DECLARE SUB SendLineToWindow (windowNum%, lineNum%, newString$)\r
-DECLARE FUNCTION GetFreeLineIndex% ()\r
-DECLARE SUB RefreshAllWindows ()\r
-DECLARE FUNCTION AddWindow% (x%, y%, widt%, haigh%, title$)\r
-DECLARE SUB DrawBox (x%, y%, widt%, haigh%, edgeStyle$)\r
-DEFINT A-Z\r
-DECLARE SUB ShowWindow (windowNum)\r
-DECLARE SUB InitializeSystem ()\r
-\r
-' Global variables for text storage\r
-DIM SHARED maxStorage%\r
-maxStorage% = 5000\r
-DIM SHARED textStorage$(1 TO maxStorage%)\r
-DIM SHARED storagePointer%\r
-\r
-' Window-related global arrays\r
-DIM SHARED windowData(1 TO 10, 1 TO 1000)      ' Stores line indices for each window\r
-DIM SHARED windowX(1 TO 10), windowY(1 TO 10)  ' Position of each window\r
-DIM SHARED windowWidth(1 TO 10), windowHeight(1 TO 10)  ' Size of each window\r
-DIM SHARED windowActiveStatus(1 TO 10)         ' Whether window is active\r
-DIM SHARED windowTitle$(1 TO 10)               ' Title of each window\r
-\r
-' Window scrolling/shifting\r
-DIM SHARED windowShiftX(1 TO 10)      ' Horizontal shift\r
-DIM SHARED windowShiftY(1 TO 10)      ' Vertical shift\r
-\r
-DIM SHARED currentActiveWindow%       ' Currently active window for display\r
-\r
-InitializeSystem\r
-\r
-demo\r
-\r
-FUNCTION AddWindow% (x%, y%, widt%, heigh%, title$)\r
-    ' Adds a new window to the system\r
-\r
-    ' Find an empty window slot\r
-    FOR windowNum% = 1 TO 10\r
-        IF windowActiveStatus(windowNum%) = 0 THEN\r
-            foundWindow% = windowNum%\r
-            GOTO FoundEmptySlot\r
-        END IF\r
-    NEXT windowNum%\r
-\r
-FoundEmptySlot:\r
-    ' Initialize the window properties\r
-    windowActiveStatus(foundWindow%) = 1\r
-    windowX(foundWindow%) = x%\r
-    windowY(foundWindow%) = y%\r
-    windowWidth(foundWindow%) = widt%\r
-    windowHeight(foundWindow%) = heigh%\r
-    windowTitle$(foundWindow%) = title$\r
-\r
-    ' Return the window number\r
-    AddWindow% = foundWindow%\r
-END FUNCTION\r
-\r
-SUB ClearWindowContent (windowNum%)\r
-    ' Clears all content from a window\r
-\r
-    FOR lineNum% = 1 TO 1000\r
-        IF windowData(windowNum%, lineNum%) > 0 THEN\r
-            textStorage$(windowData(windowNum%, lineNum%)) = ""\r
-            windowData(windowNum%, lineNum%) = 0\r
-        END IF\r
-    NEXT lineNum%\r
-END SUB\r
-\r
-SUB demo\r
-    ' Create three windows with different sizes and titles\r
-    window1% = AddWindow%(1, 1, 30, 10, "window 1.")\r
-    window2% = AddWindow%(1, 12, 80, 30, "second window")\r
-    window3% = AddWindow%(31, 2, 30, 10, "last window")\r
-\r
-    ' Load the same file into all windows\r
-    LoadFileIntoWindow "wsystem.bas", window2%\r
-    LoadFileIntoWindow "wsystem.bas", window1%\r
-    LoadFileIntoWindow "wsystem.bas", window3%\r
-\r
-AnimateWindows:\r
-    ' Randomly select an active window to animate\r
-    currentActiveWindow% = INT(RND * 3) + 1\r
-    RefreshAllWindows\r
-\r
-    ' Animate the windows by shifting their content\r
-    FOR animationFrame% = 1 TO 100\r
-        windowShiftX(currentActiveWindow%) = SIN(animationFrame% / 10) * 10 + 10\r
-        windowShiftY(currentActiveWindow%) = animationFrame%\r
-        ShowWindow currentActiveWindow%\r
-        ' split-second delay\r
-        SOUND 0, 1\r
-        IF INKEY$ <> "" THEN SYSTEM\r
-    NEXT animationFrame%\r
-\r
-    GOTO AnimateWindows\r
-END SUB\r
-\r
-FUNCTION GetFreeLineIndex%\r
-    ' Finds a free line in text storage\r
-\r
-FindNextLine:\r
-    IF storagePointer% > 1000 THEN\r
-        storagePointer% = 1\r
-    END IF\r
-\r
-    IF textStorage$(storagePointer%) = "" THEN\r
-        GetFreeLineIndex% = storagePointer%\r
-        storagePointer% = storagePointer% + 1\r
-    ELSE\r
-        storagePointer% = storagePointer% + 1\r
-        GOTO FindNextLine\r
-    END IF\r
-END FUNCTION\r
-\r
-FUNCTION GetLineFromWindow$ (windowNum%, lineNum%)\r
-    ' Retrieve a line from a window's memory\r
-\r
-    IF windowData(windowNum%, lineNum%) = 0 THEN\r
-        GetLineFromWindow$ = ""\r
-    ELSE\r
-        GetLineFromWindow$ = textStorage$(windowData(windowNum%, lineNum%))\r
-    END IF\r
-END FUNCTION\r
-\r
-SUB LoadFileIntoWindow (fileName$, windowNum%)\r
-    ' Load a file into a window's memory\r
-\r
-    OPEN fileName$ FOR INPUT AS #1\r
-    FOR lineNum% = 1 TO 1000\r
-        IF EOF(1) <> 0 THEN\r
-            GOTO FileLoaded\r
-        END IF\r
-        LINE INPUT #1, lineContent$\r
-        SendLineToWindow windowNum%, lineNum%, lineContent$\r
-    NEXT lineNum%\r
-\r
-FileLoaded:\r
-    CLOSE #1\r
-\r
-    ' Fill the remaining lines with empty strings\r
-    FOR blankLineNum% = lineNum% TO 1000\r
-        SendLineToWindow windowNum%, blankLineNum%, ""\r
-    NEXT blankLineNum%\r
-END SUB\r
-\r
-SUB RefreshAllWindows\r
-    ' Redraw all active windows\r
-\r
-    CLS\r
-    FOR windowNum% = 1 TO 10\r
-        IF windowActiveStatus(windowNum%) > 0 THEN\r
-            ShowWindow windowNum%\r
-        END IF\r
-    NEXT windowNum%\r
-END SUB\r
-\r
-SUB SendLineToWindow (windowNum%, lineNum%, newString$)\r
-    ' Stores a string in a window's memory\r
-\r
-    lineContent$ = newString$\r
-\r
-    ' Remove trailing spaces from the string\r
-    IF lineContent$ = SPACE$(LEN(lineContent$)) THEN\r
-        lineContent$ = ""\r
-    END IF\r
-\r
-    IF LEN(lineContent$) > 0 THEN\r
-TrimRight:\r
-        IF RIGHT$(lineContent$, 1) = " " THEN\r
-            lineContent$ = LEFT$(lineContent$, LEN(lineContent$) - 1)\r
-            GOTO TrimRight\r
-        END IF\r
-    END IF\r
-\r
-    ' Update the window memory with the new string\r
-    IF lineContent$ = "" THEN\r
-        IF windowData(windowNum%, lineNum%) > 0 THEN\r
-            textStorage$(windowData(windowNum%, lineNum%)) = "": windowData(windowNum%, lineNum%) = 0\r
-        END IF\r
-    ELSE\r
-        IF windowData(windowNum%, lineNum%) = 0 THEN\r
-            windowData(windowNum%, lineNum%) = GetFreeLineIndex%\r
-        END IF\r
-        textStorage$(windowData(windowNum%, lineNum%)) = lineContent$\r
-    END IF\r
-END SUB\r
-\r
-SUB ShowWindow (windowNum%)\r
-    ' Draws a window on the screen\r
-\r
-    ' Determine background color based on active window\r
-    IF windowNum% = currentActiveWindow% THEN\r
-        bgColor% = 1\r
-    ELSE\r
-        bgColor% = 0\r
-    END IF\r
-\r
-    x% = windowX(windowNum%)\r
-    y% = windowY(windowNum%)\r
-    widt% = windowWidth(windowNum%)\r
-    heigh% = windowHeight(windowNum%)\r
-    title$ = windowTitle$(windowNum%)\r
-\r
-    COLOR 11, bgColor%\r
-\r
-    ' Create border components\r
-    FOR borderSegment% = 1 TO widt% - 2\r
-        topBottom$ = topBottom$ + CHR$(205)\r
-    NEXT borderSegment%\r
-    borderTop$ = CHR$(201) + topBottom$ + CHR$(187)\r
-    borderBottom$ = CHR$(200) + topBottom$ + CHR$(188)\r
-\r
-    ' Draw top border\r
-    LOCATE y%, x%\r
-    PRINT borderTop$\r
-\r
-    ' Draw bottom border\r
-    LOCATE y% + heigh% - 1, x%\r
-    PRINT borderBottom$\r
-\r
-    ' Draw window content\r
-    FOR lineNum% = 1 TO heigh% - 2\r
-        LOCATE y% + lineNum%, x%\r
-        lineContent$ = GetLineFromWindow$(windowNum%, lineNum% + windowShiftY(windowNum%))\r
-        lineContent$ = lineContent$ + SPACE$(300)\r
-        lineContent$ = RIGHT$(lineContent$, LEN(lineContent$) - windowShiftX(windowNum%))\r
-        lineContent$ = LEFT$(lineContent$, widt% - 2)\r
-        PRINT CHR$(186) + lineContent$ + CHR$(186)\r
-    NEXT lineNum%\r
-\r
-    ' Draw window title\r
-    titleX% = INT(x% + (widt% / 2) - (LEN(title$) / 2) - 2)\r
-    LOCATE y%, titleX%\r
-    PRINT "[ "\r
-    titleX% = titleX% + 2\r
-\r
-    COLOR 10\r
-    LOCATE y%, titleX%\r
-    PRINT title$\r
-\r
-    titleX% = titleX% + LEN(title$)\r
-    COLOR 11\r
-    LOCATE y%, titleX%\r
-    PRINT " ]"\r
-    COLOR 7, 0\r
-END SUB\r
-\r
-SUB InitializeSystem\r
-    ' Initialize the screen and shared memory\r
-\r
-    WIDTH 80, 50\r
-    VIEW PRINT 1 TO 50\r
-\r
-    FOR storageIndex% = 1 TO maxStorage%\r
-        textStorage$(storageIndex%) = ""\r
-    NEXT storageIndex%\r
-\r
-    storagePointer% = 1\r
-END SUB
\ No newline at end of file
diff --git a/Miscellaneous/Windowing system.webm b/Miscellaneous/Windowing system.webm
deleted file mode 100644 (file)
index 1a456bb..0000000
Binary files a/Miscellaneous/Windowing system.webm and /dev/null differ
diff --git a/Miscellaneous/wsystem - Screencast 0.mp4 b/Miscellaneous/wsystem - Screencast 0.mp4
new file mode 100644 (file)
index 0000000..03fcdca
Binary files /dev/null and b/Miscellaneous/wsystem - Screencast 0.mp4 differ
diff --git a/Miscellaneous/wsystem.bas b/Miscellaneous/wsystem.bas
new file mode 100755 (executable)
index 0000000..ff0d067
--- /dev/null
@@ -0,0 +1,274 @@
+' Text mode windowing system. Each window can display text file.\r
+' Window content can be scrolled horizontally and vertically.\r
+' Window can have arbitrary size and location on the screen.\r
+'\r
+' This program is free software: released under Creative Commons Zero (CC0) license\r
+' by Svjatoslav Agejenko.\r
+' Email: svjatoslav@svjatoslav.eu\r
+' Homepage: http://www.svjatoslav.eu\r
+\r
+' Changelog:\r
+' 2003, Initial version\r
+' 2024-2025, Improved program readability\r
+\r
+DECLARE SUB demo ()\r
+DECLARE FUNCTION GetLineFromWindow$ (windowNum%, lineNum%)\r
+DECLARE SUB LoadFileIntoWindow (fileName$, windowNum%)\r
+DECLARE SUB SendLineToWindow (windowNum%, lineNum%, newString$)\r
+DECLARE FUNCTION GetFreeLineIndex% ()\r
+DECLARE SUB RefreshAllWindows ()\r
+DECLARE FUNCTION AddWindow% (x%, y%, widt%, haigh%, title$)\r
+DECLARE SUB DrawBox (x%, y%, widt%, haigh%, edgeStyle$)\r
+DEFINT A-Z\r
+DECLARE SUB ShowWindow (windowNum)\r
+DECLARE SUB InitializeSystem ()\r
+\r
+' Global variables for text storage\r
+DIM SHARED maxStorage%\r
+maxStorage% = 5000\r
+DIM SHARED textStorage$(1 TO maxStorage%)\r
+DIM SHARED storagePointer%\r
+\r
+' Window-related global arrays\r
+DIM SHARED windowData(1 TO 10, 1 TO 1000)      ' Stores line indices for each window\r
+DIM SHARED windowX(1 TO 10), windowY(1 TO 10)  ' Position of each window\r
+DIM SHARED windowWidth(1 TO 10), windowHeight(1 TO 10)  ' Size of each window\r
+DIM SHARED windowActiveStatus(1 TO 10)         ' Whether window is active\r
+DIM SHARED windowTitle$(1 TO 10)               ' Title of each window\r
+\r
+' Window scrolling/shifting\r
+DIM SHARED windowShiftX(1 TO 10)      ' Horizontal shift\r
+DIM SHARED windowShiftY(1 TO 10)      ' Vertical shift\r
+\r
+DIM SHARED currentActiveWindow%       ' Currently active window for display\r
+\r
+InitializeSystem\r
+\r
+demo\r
+\r
+FUNCTION AddWindow% (x%, y%, widt%, heigh%, title$)\r
+    ' Adds a new window to the system\r
+\r
+    ' Find an empty window slot\r
+    FOR windowNum% = 1 TO 10\r
+        IF windowActiveStatus(windowNum%) = 0 THEN\r
+            foundWindow% = windowNum%\r
+            GOTO FoundEmptySlot\r
+        END IF\r
+    NEXT windowNum%\r
+\r
+FoundEmptySlot:\r
+    ' Initialize the window properties\r
+    windowActiveStatus(foundWindow%) = 1\r
+    windowX(foundWindow%) = x%\r
+    windowY(foundWindow%) = y%\r
+    windowWidth(foundWindow%) = widt%\r
+    windowHeight(foundWindow%) = heigh%\r
+    windowTitle$(foundWindow%) = title$\r
+\r
+    ' Return the window number\r
+    AddWindow% = foundWindow%\r
+END FUNCTION\r
+\r
+SUB ClearWindowContent (windowNum%)\r
+    ' Clears all content from a window\r
+\r
+    FOR lineNum% = 1 TO 1000\r
+        IF windowData(windowNum%, lineNum%) > 0 THEN\r
+            textStorage$(windowData(windowNum%, lineNum%)) = ""\r
+            windowData(windowNum%, lineNum%) = 0\r
+        END IF\r
+    NEXT lineNum%\r
+END SUB\r
+\r
+SUB demo\r
+    ' Create three windows with different sizes and titles\r
+    window1% = AddWindow%(1, 1, 30, 10, "window 1.")\r
+    window2% = AddWindow%(1, 12, 80, 30, "second window")\r
+    window3% = AddWindow%(31, 2, 30, 10, "last window")\r
+\r
+    ' Load the same file into all windows\r
+    LoadFileIntoWindow "wsystem.bas", window2%\r
+    LoadFileIntoWindow "wsystem.bas", window1%\r
+    LoadFileIntoWindow "wsystem.bas", window3%\r
+\r
+AnimateWindows:\r
+    ' Randomly select an active window to animate\r
+    currentActiveWindow% = INT(RND * 3) + 1\r
+    RefreshAllWindows\r
+\r
+    ' Animate the windows by shifting their content\r
+    FOR animationFrame% = 1 TO 100\r
+        windowShiftX(currentActiveWindow%) = SIN(animationFrame% / 10) * 10 + 10\r
+        windowShiftY(currentActiveWindow%) = animationFrame%\r
+        ShowWindow currentActiveWindow%\r
+        ' split-second delay\r
+        SOUND 0, 1\r
+        IF INKEY$ <> "" THEN SYSTEM\r
+    NEXT animationFrame%\r
+\r
+    GOTO AnimateWindows\r
+END SUB\r
+\r
+FUNCTION GetFreeLineIndex%\r
+    ' Finds a free line in text storage\r
+\r
+FindNextLine:\r
+    IF storagePointer% > 1000 THEN\r
+        storagePointer% = 1\r
+    END IF\r
+\r
+    IF textStorage$(storagePointer%) = "" THEN\r
+        GetFreeLineIndex% = storagePointer%\r
+        storagePointer% = storagePointer% + 1\r
+    ELSE\r
+        storagePointer% = storagePointer% + 1\r
+        GOTO FindNextLine\r
+    END IF\r
+END FUNCTION\r
+\r
+FUNCTION GetLineFromWindow$ (windowNum%, lineNum%)\r
+    ' Retrieve a line from a window's memory\r
+\r
+    IF windowData(windowNum%, lineNum%) = 0 THEN\r
+        GetLineFromWindow$ = ""\r
+    ELSE\r
+        GetLineFromWindow$ = textStorage$(windowData(windowNum%, lineNum%))\r
+    END IF\r
+END FUNCTION\r
+\r
+SUB LoadFileIntoWindow (fileName$, windowNum%)\r
+    ' Load a file into a window's memory\r
+\r
+    OPEN fileName$ FOR INPUT AS #1\r
+    FOR lineNum% = 1 TO 1000\r
+        IF EOF(1) <> 0 THEN\r
+            GOTO FileLoaded\r
+        END IF\r
+        LINE INPUT #1, lineContent$\r
+        SendLineToWindow windowNum%, lineNum%, lineContent$\r
+    NEXT lineNum%\r
+\r
+FileLoaded:\r
+    CLOSE #1\r
+\r
+    ' Fill the remaining lines with empty strings\r
+    FOR blankLineNum% = lineNum% TO 1000\r
+        SendLineToWindow windowNum%, blankLineNum%, ""\r
+    NEXT blankLineNum%\r
+END SUB\r
+\r
+SUB RefreshAllWindows\r
+    ' Redraw all active windows\r
+\r
+    CLS\r
+    FOR windowNum% = 1 TO 10\r
+        IF windowActiveStatus(windowNum%) > 0 THEN\r
+            ShowWindow windowNum%\r
+        END IF\r
+    NEXT windowNum%\r
+END SUB\r
+\r
+SUB SendLineToWindow (windowNum%, lineNum%, newString$)\r
+    ' Stores a string in a window's memory\r
+\r
+    lineContent$ = newString$\r
+\r
+    ' Remove trailing spaces from the string\r
+    IF lineContent$ = SPACE$(LEN(lineContent$)) THEN\r
+        lineContent$ = ""\r
+    END IF\r
+\r
+    IF LEN(lineContent$) > 0 THEN\r
+TrimRight:\r
+        IF RIGHT$(lineContent$, 1) = " " THEN\r
+            lineContent$ = LEFT$(lineContent$, LEN(lineContent$) - 1)\r
+            GOTO TrimRight\r
+        END IF\r
+    END IF\r
+\r
+    ' Update the window memory with the new string\r
+    IF lineContent$ = "" THEN\r
+        IF windowData(windowNum%, lineNum%) > 0 THEN\r
+            textStorage$(windowData(windowNum%, lineNum%)) = "": windowData(windowNum%, lineNum%) = 0\r
+        END IF\r
+    ELSE\r
+        IF windowData(windowNum%, lineNum%) = 0 THEN\r
+            windowData(windowNum%, lineNum%) = GetFreeLineIndex%\r
+        END IF\r
+        textStorage$(windowData(windowNum%, lineNum%)) = lineContent$\r
+    END IF\r
+END SUB\r
+\r
+SUB ShowWindow (windowNum%)\r
+    ' Draws a window on the screen\r
+\r
+    ' Determine background color based on active window\r
+    IF windowNum% = currentActiveWindow% THEN\r
+        bgColor% = 1\r
+    ELSE\r
+        bgColor% = 0\r
+    END IF\r
+\r
+    x% = windowX(windowNum%)\r
+    y% = windowY(windowNum%)\r
+    widt% = windowWidth(windowNum%)\r
+    heigh% = windowHeight(windowNum%)\r
+    title$ = windowTitle$(windowNum%)\r
+\r
+    COLOR 11, bgColor%\r
+\r
+    ' Create border components\r
+    FOR borderSegment% = 1 TO widt% - 2\r
+        topBottom$ = topBottom$ + CHR$(205)\r
+    NEXT borderSegment%\r
+    borderTop$ = CHR$(201) + topBottom$ + CHR$(187)\r
+    borderBottom$ = CHR$(200) + topBottom$ + CHR$(188)\r
+\r
+    ' Draw top border\r
+    LOCATE y%, x%\r
+    PRINT borderTop$\r
+\r
+    ' Draw bottom border\r
+    LOCATE y% + heigh% - 1, x%\r
+    PRINT borderBottom$\r
+\r
+    ' Draw window content\r
+    FOR lineNum% = 1 TO heigh% - 2\r
+        LOCATE y% + lineNum%, x%\r
+        lineContent$ = GetLineFromWindow$(windowNum%, lineNum% + windowShiftY(windowNum%))\r
+        lineContent$ = lineContent$ + SPACE$(300)\r
+        lineContent$ = RIGHT$(lineContent$, LEN(lineContent$) - windowShiftX(windowNum%))\r
+        lineContent$ = LEFT$(lineContent$, widt% - 2)\r
+        PRINT CHR$(186) + lineContent$ + CHR$(186)\r
+    NEXT lineNum%\r
+\r
+    ' Draw window title\r
+    titleX% = INT(x% + (widt% / 2) - (LEN(title$) / 2) - 2)\r
+    LOCATE y%, titleX%\r
+    PRINT "[ "\r
+    titleX% = titleX% + 2\r
+\r
+    COLOR 10\r
+    LOCATE y%, titleX%\r
+    PRINT title$\r
+\r
+    titleX% = titleX% + LEN(title$)\r
+    COLOR 11\r
+    LOCATE y%, titleX%\r
+    PRINT " ]"\r
+    COLOR 7, 0\r
+END SUB\r
+\r
+SUB InitializeSystem\r
+    ' Initialize the screen and shared memory\r
+\r
+    WIDTH 80, 50\r
+    VIEW PRINT 1 TO 50\r
+\r
+    FOR storageIndex% = 1 TO maxStorage%\r
+        textStorage$(storageIndex%) = ""\r
+    NEXT storageIndex%\r
+\r
+    storagePointer% = 1\r
+END SUB
\ No newline at end of file