Refactoring code for better readability
authorSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Tue, 15 Oct 2024 18:59:53 +0000 (21:59 +0300)
committerSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Tue, 15 Oct 2024 18:59:53 +0000 (21:59 +0300)
Graphics/3D/Galaxy explorer/galaxyexplorer.bas

index 0a58d6d..6074a44 100755 (executable)
-' Program to render 3D pointcloud galaxy.\r
-' By Svjatoslav Agejenko.\r
-' Email: svjatoslav@svjatoslav.eu\r
-' Homepage: http://www.svjatoslav.eu\r
-'\r
-' Changelog:\r
-' 2003, Initial version.\r
-' 2024, Improved program readability using AI.\r
-'\r
-' User can navigate through the galaxy using mouse or keyboard controls.\r
-' To use mouse controls, program requires special TSR (terminate and stay resident)\r
-' program to be loaded. The TSR makes mouse input available to the QBasic program.\r
-'\r
-' Navigation controls:\r
-' - Press left and right mouse buttons simultaneously to move in X and Z axis.\r
-' - Press right mouse button to move in Y axis.\r
-' - Use keyboard keys 'a', 'd', 'w', 's' to move in X and Z axis.\r
-' - Press 'q' to quit the program.\r
-\r
-DECLARE SUB temp ()\r
-DECLARE SUB mkGalaxy (x!, y!, z!)\r
-DECLARE SUB rndInit ()\r
-DECLARE FUNCTION rn! ()\r
-DECLARE SUB disp ()\r
-DECLARE SUB starText ()\r
-DECLARE SUB control ()\r
-DECLARE SUB putByte (addr!, dat!)\r
-DECLARE SUB putWord (addr!, dat!)\r
-DECLARE FUNCTION getWord! (addr!)\r
-DECLARE FUNCTION getByte! (addr!)\r
-DECLARE SUB start ()\r
-DECLARE SUB animate ()\r
-\r
-DIM SHARED angle1, angle2, angle3\r
-\r
-DIM SHARED time\r
-\r
-DIM SHARED externalSegment, externalAddress\r
-\r
-DIM SHARED myX, myY, myZ\r
-DIM SHARED speedX, speedY, speedZ\r
-DIM SHARED buttonLeft, buttonRight\r
-DIM SHARED maxMove\r
-\r
-DIM SHARED zoom\r
-DIM SHARED randomValue(0 TO 10000)\r
-DIM SHARED randomPointer\r
-\r
-DIM SHARED pointX(1 TO 12000)\r
-DIM SHARED pointY(1 TO 12000)\r
-DIM SHARED pointZ(1 TO 12000)\r
-DIM SHARED pointColor(1 TO 12000)\r
-DIM SHARED numPoints\r
-\r
-DIM SHARED temporaryRegister(0 TO 10)\r
-\r
-newLine = 0\r
-newPage = 0\r
-\r
-start\r
-\r
-currentX = 0\r
-currentY = 0\r
-currentZ = 0\r
-\r
-numPoints = 0\r
-mkGalaxy 0, 0, 0\r
-1\r
-\r
-variableAngle = INT(RND * 3)\r
-\r
-SELECT CASE variableAngle\r
-CASE 0\r
-    currentX = RND * 500 - 250\r
-CASE 1\r
-    currentY = RND * 100 - 50\r
-CASE 2\r
-    currentZ = RND * 500 - 250\r
-END SELECT\r
-\r
-control\r
-disp\r
-\r
-PCOPY 0, 1\r
-CLS\r
-GOTO 1\r
-\r
-SUB control\r
-\r
-IF getByte(8) <> 0 THEN\r
-    putByte 8, 0\r
-    xPosition = getWord(2)\r
-    putWord 2, 0\r
-    yPosition = getWord(4)\r
-    putWord 4, 0\r
-    button = getWord(6)\r
-    putWord 6, 0\r
-    buttonLeft = 0\r
-    buttonRight = 0\r
-    IF button = 1 THEN buttonLeft = 1\r
-    IF button = 2 THEN buttonRight = 1\r
-    IF button = 3 THEN buttonLeft = 1: buttonRight = 1\r
-\r
-    ' Handle mouse movements\r
-    IF buttonRight = 1 THEN\r
-        IF buttonLeft = 1 THEN\r
-            speedX = speedX + SIN(angle1) * yPosition / 4\r
-            speedZ = speedZ - COS(angle1) * yPosition / 4\r
-            GOTO 3\r
-        END IF\r
-        speedY = speedY + yPosition / 4\r
-    3\r
-        yPosition = 0\r
-    END IF\r
-\r
-END IF\r
-\r
-' Limit the position values to prevent overflow\r
-IF xPosition < -maxMove THEN xPosition = -maxMove\r
-IF xPosition > maxMove THEN xPosition = maxMove\r
-angle1 = angle1 - xPosition / 150\r
-\r
-IF yPosition < -maxMove THEN yPosition = -maxMove\r
-IF yPosition > maxMove THEN yPosition = maxMove\r
-angle2 = angle2 - yPosition / 150\r
-\r
-' Check for keyboard input\r
-keyInput$ = INKEY$\r
-\r
-IF keyInput$ = "a" THEN speedX = speedX - COS(angle1): speedZ = speedZ - SIN(angle1)\r
-IF keyInput$ = "d" THEN speedX = speedX + COS(angle1): speedZ = speedZ + SIN(angle1)\r
-IF keyInput$ = "w" THEN speedX = speedX - SIN(angle1): speedZ = speedZ + COS(angle1)\r
-IF keyInput$ = "s" THEN speedX = speedX + SIN(angle1): speedZ = speedZ - COS(angle1)\r
-IF keyInput$ = "q" THEN SYSTEM\r
-\r
-' Decelerate the movement\r
-speedX = speedX / 1.1\r
-speedY = speedY / 1.1\r
-speedZ = speedZ / 1.1\r
-\r
-myX = myX + speedX\r
-myZ = myZ + speedZ\r
-myY = myY + speedY\r
-\r
-END SUB\r
-\r
-SUB disp\r
-\r
-sinAngle1 = SIN(angle1)\r
-cosAngle1 = COS(angle1)\r
-sinAngle2 = SIN(angle2)\r
-cosAngle2 = COS(angle2)\r
-\r
-' Loop through all points to calculate and display their positions\r
-FOR pointIndex = 1 TO numPoints\r
-\r
-    xCoordinate = pointX(pointIndex) - myX\r
-    yCoordinate = pointY(pointIndex) - myY\r
-    zCoordinate = pointZ(pointIndex) - myZ\r
-\r
-    ' Rotate the points\r
-    rotatedX = xCoordinate * cosAngle1 + zCoordinate * sinAngle1\r
-    rotatedZ = zCoordinate * cosAngle1 - xCoordinate * sinAngle1\r
-\r
-    rotatedY = yCoordinate * cosAngle2 + rotatedZ * sinAngle2\r
-    finalZ = rotatedZ * cosAngle2 - yCoordinate * sinAngle2\r
-\r
-    ' Draw points that are sufficiently close\r
-    IF finalZ > 3 THEN\r
-        screenX = rotatedX / finalZ * 130 + 160\r
-        screenY = rotatedY / finalZ * 130 + 100\r
-        PSET (screenX, screenY), pointColor(pointIndex)\r
-    END IF\r
-\r
-NEXT pointIndex\r
-END SUB\r
-\r
-FUNCTION getByte (address)\r
-getByte = PEEK(externalAddress + address)\r
-END FUNCTION\r
-\r
-FUNCTION getWord (address)\r
-firstByte = PEEK(externalAddress + address)\r
-secondByte = PEEK(externalAddress + address + 1)\r
-\r
-combinedHex$ = HEX$(firstByte)\r
-IF LEN(combinedHex$) = 1 THEN combinedHex$ = "0" + combinedHex$\r
-IF LEN(combinedHex$) = 0 THEN combinedHex$ = "00"\r
-\r
-combinedValue = VAL("&H" + HEX$(secondByte) + combinedHex$)\r
-\r
-getWord = combinedValue\r
-END FUNCTION\r
-\r
-SUB mkGalaxy (localX, localY, localZ)\r
-\r
-randomAngle1 = rn * 10\r
-randomAngle2 = rn * 10\r
-\r
-galaxySin1 = SIN(randomAngle1)\r
-galaxyCos1 = COS(randomAngle1)\r
-galaxySin2 = SIN(randomAngle2)\r
-galaxyCos2 = COS(randomAngle2)\r
-\r
-randomPointer = 0\r
-size = 100\r
-piValue = 3.14\r
-spiralBarsMultiplier = 3\r
-\r
-FOR pointIndex = 1 TO 10000\r
-\r
-    randomVariable = rn * 10\r
-    distanceFromCenter = randomVariable * randomVariable / 30\r
-\r
-    spiralOffset = rn * (11.5 - randomVariable) / 3\r
-    halfSpiralOffset = spiralOffset / 2\r
-\r
-    angleExponent = rn * (distanceFromCenter / 2) / spiralBarsMultiplier * 2\r
-    spiralBarAngle = 2 * piValue / spiralBarsMultiplier * INT(rn * spiralBarsMultiplier)\r
-\r
-    xCoordinate = (SIN(randomVariable - spiralBarAngle + angleExponent) * distanceFromCenter + rn * spiralOffset - halfSpiralOffset) * size\r
-    zCoordinate = (COS(randomVariable - spiralBarAngle + angleExponent) * distanceFromCenter + rn * spiralOffset - halfSpiralOffset) * size\r
-    yCoordinate = (rn * spiralOffset - halfSpiralOffset) * size\r
-\r
-    rotatedX = xCoordinate * galaxyCos1 + zCoordinate * galaxySin1\r
-    rotatedZ = zCoordinate * galaxyCos1 - xCoordinate * galaxySin1\r
-\r
-    rotatedY = yCoordinate * galaxyCos2 + rotatedZ * galaxySin2\r
-    finalZ = rotatedZ * galaxyCos2 - yCoordinate * galaxySin2\r
-\r
-    numPoints = numPoints + 1\r
-\r
-    pointX(numPoints) = rotatedX + localX\r
-    pointY(numPoints) = rotatedY + localY\r
-    pointZ(numPoints) = finalZ + localZ\r
-    pointColor(numPoints) = INT(RND * 15) + 1\r
-NEXT pointIndex\r
-\r
-END SUB\r
-\r
-SUB mouseDemo\r
-\r
-currentX = 150\r
-currentY = 100\r
-maxMove = 50\r
-100\r
-frameCounter = frameCounter + 1\r
-\r
-LOCATE 1, 1\r
-PRINT currentX, currentY\r
-PRINT frameCounter\r
-\r
-CIRCLE (currentX, currentY), 10, 0\r
-xPosition = getWord(2)\r
-putWord 2, 0\r
-yPosition = getWord(4)\r
-putWord 4, 0\r
-\r
-IF xPosition < -maxMove THEN xPosition = -maxMove\r
-IF xPosition > maxMove THEN xPosition = maxMove\r
-currentX = currentX + xPosition\r
-\r
-IF yPosition < -maxMove THEN yPosition = -maxMove\r
-IF yPosition > maxMove THEN yPosition = maxMove\r
-currentY = currentY + yPosition\r
-\r
-CIRCLE (currentX, currentY), 10, 10\r
-\r
-SOUND 0, .05\r
-GOTO 100\r
-\r
-END SUB\r
-\r
-SUB putByte (address, dataByte)\r
-\r
-POKE (externalAddress + address), dataByte\r
-END SUB\r
-\r
-SUB putWord (address, dataWord)\r
-\r
-hexValue$ = HEX$(dataWord)\r
-\r
-2\r
-IF LEN(hexValue$) < 4 THEN hexValue$ = "0" + hexValue$: GOTO 2\r
-\r
-firstByteValue = VAL("&H" + LEFT$(hexValue$, 2))\r
-secondByteValue = VAL("&H" + RIGHT$(hexValue$, 2))\r
-\r
-POKE (externalAddress + address), secondByteValue\r
-POKE (externalAddress + address + 1), firstByteValue\r
-\r
-END SUB\r
-\r
-FUNCTION rn\r
-\r
-randomPointer = randomPointer + 1\r
-IF randomPointer > 10000 THEN randomPointer = 0\r
-rn = randomValue(randomPointer)\r
-\r
-END FUNCTION\r
-\r
-SUB rndInit\r
-\r
-FOR index = 0 TO 10000\r
-    randomValue(index) = RND\r
-NEXT index\r
-\r
-randomPointer = 0\r
-END SUB\r
-\r
-SUB start\r
-\r
-starText\r
-\r
-SCREEN 7, , , 1\r
-\r
-maxMove = 50\r
-rndInit\r
-\r
-END SUB\r
-\r
-SUB starText\r
-\r
-DEF SEG = 0     ' read first from interrupt table\r
-\r
-externalSegment = PEEK(&H79 * 4 + 3) * 256\r
-externalSegment = externalSegment + PEEK(&H79 * 4 + 2)\r
-\r
-PRINT "Segment is: " + HEX$(externalSegment)\r
-\r
-externalAddress = PEEK(&H79 * 4 + 1) * 256\r
-externalAddress = externalAddress + PEEK(&H79 * 4 + 0)\r
-\r
-PRINT "relative address is:"; externalAddress\r
-\r
-DEF SEG = externalSegment\r
-\r
-IF getWord(0) <> 1983 THEN\r
-    PRINT "FATAL ERROR:  you must load"\r
-    PRINT "QBasic extension TSR first!"\r
-    SYSTEM\r
-END IF\r
-\r
-END SUB
\ No newline at end of file
+' Program to render a 3D point cloud galaxy.
+' By Svjatoslav Agejenko.
+' Email: svjatoslav@svjatoslav.eu
+' Homepage: http://www.svjatoslav.eu
+
+' Changelog:
+' 2003, Initial version.
+' 2024, Improved program readability using AI.
+
+' User can navigate through the galaxy using mouse or keyboard controls.
+' To use mouse controls, program requires a special TSR (terminate and stay resident)
+' program to be loaded. The TSR makes mouse input available to the QBasic program.
+
+' Navigation controls:
+' - Press left and right mouse buttons simultaneously to move in X and Z axis.
+' - Press right mouse button to move in Y axis.
+' - Use keyboard keys 'a', 'd', 'w', 's' to move in X and Z axis.
+' - Press 'q' to quit the program.
+
+DECLARE SUB temp ()
+DECLARE SUB mkGalaxy (x!, y!, z!)
+DECLARE SUB rndInit ()
+DECLARE FUNCTION rn! ()
+DECLARE SUB disp ()
+DECLARE SUB control ()
+DECLARE SUB putByte (addr!, dat!)
+DECLARE SUB putWord (addr!, dat!)
+DECLARE FUNCTION getWord! (addr!)
+DECLARE FUNCTION getByte! (addr!)
+DECLARE SUB start ()
+DECLARE SUB animate ()
+
+DIM SHARED angle1, angle2, angle3
+
+DIM SHARED time
+
+DIM SHARED externalSegment, externalAddress
+
+' Variables for the camera position and movement
+DIM SHARED myX, myY, myZ
+DIM SHARED speedX, speedY, speedZ
+DIM SHARED buttonLeft, buttonRight
+DIM SHARED maxMove
+
+' Variable for zoom level
+DIM SHARED zoom
+
+' Array to store random values
+DIM SHARED randomValue(0 TO 10000)
+DIM SHARED randomPointer
+
+' Arrays to store point coordinates and colors. These points make up galaxy.
+DIM SHARED pointX(1 TO 12000)
+DIM SHARED pointY(1 TO 12000)
+DIM SHARED pointZ(1 TO 12000)
+DIM SHARED pointColor(1 TO 12000)
+DIM SHARED numPoints
+
+' Temporary register array
+DIM SHARED temporaryRegister(0 TO 10)
+
+newLine = 0
+newPage = 0
+
+start
+
+currentX = 0
+currentY = 0
+currentZ = 0
+
+numPoints = 0
+mkGalaxy 0, 0, 0
+1
+
+' Generate a random angle for initial camera position
+variableAngle = INT(RND * 3)
+
+SELECT CASE variableAngle
+CASE 0
+    currentX = RND * 500 - 250
+CASE 1
+    currentY = RND * 100 - 50
+CASE 2
+    currentZ = RND * 500 - 250
+END SELECT
+
+control
+disp
+
+PCOPY 0, 1
+CLS
+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
+    END IF
+
+END IF
+
+' Limit the position values to prevent overflow
+IF xPosition < -maxMove THEN xPosition = -maxMove
+IF xPosition > maxMove THEN xPosition = maxMove
+angle1 = angle1 - xPosition / 150
+
+IF yPosition < -maxMove THEN yPosition = -maxMove
+IF yPosition > maxMove THEN yPosition = maxMove
+angle2 = angle2 - yPosition / 150
+
+' Check for keyboard input
+keyInput$ = INKEY$
+
+' Handle keyboard controls
+IF keyInput$ = "a" THEN speedX = speedX - COS(angle1): speedZ = speedZ - SIN(angle1)
+IF keyInput$ = "d" THEN speedX = speedX + COS(angle1): speedZ = speedZ + SIN(angle1)
+IF keyInput$ = "w" THEN speedX = speedX - SIN(angle1): speedZ = speedZ + COS(angle1)
+IF keyInput$ = "s" THEN speedX = speedX + SIN(angle1): speedZ = speedZ - COS(angle1)
+IF keyInput$ = "q" THEN SYSTEM
+
+' Decelerate the movement
+speedX = speedX / 1.1
+speedY = speedY / 1.1
+speedZ = speedZ / 1.1
+
+myX = myX + speedX
+myZ = myZ + speedZ
+myY = myY + speedY
+
+END SUB
+
+SUB disp
+
+' Calculate sine and cosine values for rotation angles
+sinAngle1 = SIN(angle1)
+cosAngle1 = COS(angle1)
+sinAngle2 = SIN(angle2)
+cosAngle2 = COS(angle2)
+
+' Loop through all points to calculate and display their positions
+FOR pointIndex = 1 TO numPoints
+
+    ' Calculate the distance from the camera
+    xCoordinate = pointX(pointIndex) - myX
+    yCoordinate = pointY(pointIndex) - myY
+    zCoordinate = pointZ(pointIndex) - myZ
+
+    ' Rotate the points
+    rotatedX = xCoordinate * cosAngle1 + zCoordinate * sinAngle1
+    rotatedZ = zCoordinate * cosAngle1 - xCoordinate * sinAngle1
+
+    rotatedY = yCoordinate * cosAngle2 + rotatedZ * sinAngle2
+    finalZ = rotatedZ * cosAngle2 - yCoordinate * sinAngle2
+
+    ' Draw points that are sufficiently close
+    IF finalZ > 3 THEN
+        screenX = rotatedX / finalZ * 130 + 160
+        screenY = rotatedY / finalZ * 130 + 100
+        PSET (screenX, screenY), pointColor(pointIndex)
+    END IF
+
+NEXT pointIndex
+END SUB
+
+FUNCTION getByte (address)
+getByte = PEEK(externalAddress + address)
+END FUNCTION
+
+FUNCTION getWord (address)
+firstByte = PEEK(externalAddress + address)
+secondByte = PEEK(externalAddress + address + 1)
+
+' Combine the two bytes into a single value
+combinedHex$ = HEX$(firstByte)
+IF LEN(combinedHex$) = 1 THEN combinedHex$ = "0" + combinedHex$
+IF LEN(combinedHex$) = 0 THEN combinedHex$ = "00"
+
+combinedValue = VAL("&H" + HEX$(secondByte) + combinedHex$)
+
+getWord = combinedValue
+END FUNCTION
+
+SUB mkGalaxy (localX, localY, localZ)
+
+' Generate random angles for stars within galaxy
+randomAngle1 = rn * 10
+randomAngle2 = rn * 10
+
+galaxySin1 = SIN(randomAngle1)
+galaxyCos1 = COS(randomAngle1)
+galaxySin2 = SIN(randomAngle2)
+galaxyCos2 = COS(randomAngle2)
+
+randomPointer = 0
+size = 100
+piValue = 3.14
+spiralBarsMultiplier = 3
+
+FOR pointIndex = 1 TO 10000
+
+    ' Generate a random star distance from the center
+    randomVariable = rn * 10
+    distanceFromCenter = randomVariable * randomVariable / 30
+
+    ' Calculate spiral offset and half of it
+    spiralOffset = rn * (11.5 - randomVariable) / 3
+    halfSpiralOffset = spiralOffset / 2
+
+    ' Generate angle exponent and spiral bar angle
+    angleExponent = rn * (distanceFromCenter / 2) / spiralBarsMultiplier * 2
+    spiralBarAngle = 2 * piValue / spiralBarsMultiplier * INT(rn * spiralBarsMultiplier)
+
+    ' Calculate x, z, and y coordinates
+    xCoordinate = (SIN(randomVariable - spiralBarAngle + angleExponent) * distanceFromCenter + rn * spiralOffset - halfSpiralOffset) * size
+    zCoordinate = (COS(randomVariable - spiralBarAngle + angleExponent) * distanceFromCenter + rn * spiralOffset - halfSpiralOffset) * size
+    yCoordinate = (rn * spiralOffset - halfSpiralOffset) * size
+
+    ' Rotate the coordinates
+    rotatedX = xCoordinate * galaxyCos1 + zCoordinate * galaxySin1
+    rotatedZ = zCoordinate * galaxyCos1 - xCoordinate * galaxySin1
+
+    rotatedY = yCoordinate * galaxyCos2 + rotatedZ * galaxySin2
+    finalZ = rotatedZ * galaxyCos2 - yCoordinate * galaxySin2
+
+    ' Store the point in arrays
+    numPoints = numPoints + 1
+
+    pointX(numPoints) = rotatedX + localX
+    pointY(numPoints) = rotatedY + localY
+    pointZ(numPoints) = finalZ + localZ
+    pointColor(numPoints) = INT(RND * 15) + 1
+NEXT pointIndex
+
+END SUB
+
+SUB putByte (address, dataByte)
+
+POKE (externalAddress + address), dataByte
+END SUB
+
+SUB putWord (address, dataWord)
+
+' Convert the word to a hexadecimal string
+hexValue$ = HEX$(dataWord)
+
+' Ensure the string has at least 4 characters
+2
+IF LEN(hexValue$) < 4 THEN
+    hexValue$ = "0" + hexValue$: GOTO 2
+END IF
+
+' Extract the first and second bytes
+firstByteValue = VAL("&H" + LEFT$(hexValue$, 2))
+secondByteValue = VAL("&H" + RIGHT$(hexValue$, 2))
+
+' Store the bytes in memory
+POKE (externalAddress + address), secondByteValue
+POKE (externalAddress + address + 1), firstByteValue
+
+END SUB
+
+FUNCTION rn
+
+' Increment the random pointer and get a new random value
+randomPointer = randomPointer + 1
+IF randomPointer > 10000 THEN randomPointer = 0
+rn = randomValue(randomPointer)
+
+END FUNCTION
+
+SUB rndInit
+
+' Initialize the array of random values
+FOR index = 0 TO 10000
+    randomValue(index) = RND
+NEXT index
+
+randomPointer = 0
+END SUB
+
+SUB start
+
+starText
+
+' Set up graphics mode
+SCREEN 7, , , 1
+
+' Initialize maximum movement and random values
+maxMove = 50
+rndInit
+
+END SUB
+
+SUB starText
+
+' Read the segment and address from the interrupt table
+DEF SEG = 0     ' read first from interrupt table
+
+externalSegment = PEEK(&H79 * 4 + 3) * 256
+externalSegment = externalSegment + PEEK(&H79 * 4 + 2)
+
+PRINT "Segment is: " + HEX$(externalSegment)
+
+externalAddress = PEEK(&H79 * 4 + 1) * 256
+externalAddress = externalAddress + PEEK(&H79 * 4 + 0)
+
+PRINT "relative address is:"; externalAddress
+
+' Read the word at the specified address
+DEF SEG = externalSegment
+
+IF getWord(0) <> 1983 THEN
+    PRINT "FATAL ERROR: you must load"
+    PRINT "QBasic extension TSR first!"
+    SYSTEM
+END IF
+
+END SUB