Πέμπτη 21 Νοεμβρίου 2024

M2000 Programming using threads

Information (27 Nov 2024, new release): Version 12 Revision 49 release at Git

This program show how we can make three or more threads with one blocking function, the KEY$ which wait for  a keypress (but internal the wait function allow threads to run).

By design when a module exit, the threads erased. Some time a thread maybe can't erased, because is in use at the time we erase them, so we use the Threads Erase statement, which erase every thread.

A thread is a part of code of a module. There are four differences from module: A thread has own stack of values (can work as FIFO or LIFO), has own Static variables, has the Interval time,  has a number and not a name.

About the no name of s thread: because we don't have a name for a thread the name space is the same as the module's which we create it. So from a thread the module's variables can be used. We can't have static variables and local variables with same name. A static variable hold the value for the next run.

From a thread we can send commands to thread master to erase it, or to change the interval, or to Hold it using This if we don't save the thread handler (the number) to a static variable. We can handle other threads if we know the thread handler.

The Thread Plan is the way the threads works. We can't change the plan if we have a running/waiting thread. The actual program , M2000 Interpreter not tun in a M2000 thread (but in a process thread), Because the program is a window program, has own event loop, and process events. Here the M2000 thread mechanism is an internal software building, with own thread loop. A Wait statement process threads/events. Also the Ask() function (the M2000 message box) also process threads/events. 

There are two plans: 

The Sequential plan execute a thread at the time interval and when the code end, the process return to thread master (the thread manager). If we use inside a thread something which wait using the thread manager then we will see other thread to run before that thread finish. We don't want to have loops inside threads because each thread is a loop by a timer which the interval set the next run. A thread maybe need more time that the interval have, so a delay inserted as needed to start the next time at the same phase.

The Concurrent plan execute one statement then switch to task manager to check if another task has to start or continue to execute. If we make block of codes using { } or they are blocks like the Repeat Until, While End While, then these blocks aren't break in parts, and executed as one statement.

Some times we want something to run from one only thread and the other just skip it. This can be done with the Part { } statement. When the block executed the control variable change state and the other threads just check the state and skip the block.

From threads we can use subs and functions without switching at that level of code, but a blocking statement  in a sub with an internal thread loop also process threads. The example have no call to user function/sub/module/simple function/lambda function/OOP members.

So this example set a thread plan randomly and start the threads.

The Sequential Plan:



The Concurrent Plan:

See above the one and one more for 21469 thread that have something between from another task. The load of threads (how many, and how fast they became active) make the thread manager (or master) to switch more times to other tasks.  The If Then Else End If also processed with interrupts (switches), because the didn't use blocks (but an If using blocks { } is different, and an if in a line without end if is like one statement too).

You can find in INFO files programs with threads: 

PEND, CONC, P, M2000_EDITOR_INFORMATION, FORM44, DEMO1, KB,  PONG, COMMON, DOWN4, CHESSGAME, PHILO, WIND4, FIRSTWINDOW, HOOKEVENT, PHONEBOOTH.

The program (just copy this in a module say A, using Edit A, press Esc to exit from editor, and write A and press enter to execute it. Remember that Esc and Ctrl+C can be used to stop the program if you make some changes and has a forever loop. Also use Save ThreadExample to save it as ThreadExample.gsb in M2000 user folder (use Win Dir$ to open explorer at user directory).


module ThreadMax {
profiler
if random(1,2)=1 then
thread.Plan Concurrent
print "thread.Plan Concurrent"
else
thread.Plan Sequential
print "thread.Plan Sequential"
end if

thread {
print "form outer module", timecount
refresh
} as z interval 500
module ThreadsExample {
boolean alfa
for i=1 to 3
m=random(1,6)*50
thread {
static st=1
part {
Threads
pen 15 {
print "wait for pressing space bar at ";k
}
' blocking function - wait a key
do
until key$=" "
pen 15 {
print "Thank you"
}
} as alfa
if st>1 then print "two": i--: thread this erase
st++
print "one", k, l
print "one more", k, l
} as f interval m
thread f execute static k=f, l=m
next
after 100 {
print "this is one time"
after 50 {
print "this is one time, too"
}
}
t=2
main.task 50 {if i<2 then exit
if t then t-- else t=2: print "main.task"
}
print "done"
}
ThreadsExample
Threads
wait 2000
Threads erase
print "Outer module done"
}
ThreadMax
Threads

Πέμπτη 14 Νοεμβρίου 2024

Revision 46, Version 12

 The last fault, for slice(), so ? (1,2,3)#slice(0,0) now print 1 (a slice from 0 item to 0 item)

Check this three routines for sorting. The two last have the same compares and swaps.




From my laptop (Windows 11):




form 80, 60
? $(,5)
Stack new {
\\ data push to end of stack (we use it as FIFO)
for i=1 to 200 : data random(-100, 100): next
ss=[]
? ss
? "----------------------------------"
M=Stack.Size-1
comp=0
nmove=0
s=stack(ss)
profiler
for cc=1 to 1 {
flush
stack ! stack(s)
M=Stack.Size-1
While M>0 {
N=1
For i=1 to M {
\\ if peek item i > peek item i+1 then get i+1 to top, and send to i
\\ stack is a linked list, so moving items done with pointers only
if stackitem(i)>=stackitem(i+1) then Shift i+1 : ShiftBack i : N=i : nmove++
comp++
}
M=N-1
}
}
pen 15{? round(timecount, 0)}
print array([])
print comp, nmove
}
global cswap=0
global compare=0


Group Quick {
Private:
      Function partition {
               Read &A(), p, r
               x = A(r): i = p-1
               For j=p to r-1 {If .LE(A(j), x) Then i++:Swap A(i),A(j):cswap++:compare++
               }
               Swap A(i+1),A(r): cswap++:=i+1
            }
Public:
      LE=Lambda (a, b)->a<=b
      Function quicksort {
           Read &A(), p, r
           If p < r Then {
             q = .partition(&A(), p, r)
             Call .quicksort(&A(), p, q - 1)
             Call .quicksort(&A(), q + 1, r)
          }
      }
}
dim a(), b()
a()=array(stack(ss))
b()=a()
profiler
for cc=1 to 1 {
a()=b()
Call Quick.QuickSort(&a(), 0, len(a())-1)
}
pen 15{? round(timecount, 0)}
? a()
? cswap, compare
Class Quick {
Private:
module quicksort (&a()) {
do If Stackitem()>=Stackitem(2) Then Drop 2:if empty then exit else continue
over 2,2
Read p, r : i = p-1 : x=a(r)
For j=p to r-1:If .LE(a(j), x) Then i++:Swap a(i),a(j): compare++:cswap++
Next j : Swap a(i+1), a(r) : Push i+2, i:shift 3 : cswap++
always
}
Public:
// a and b can be strings or numbers
LE=Lambda (a, b)->a<=b
// this is final, we can't change it
Function Final Sort(&a(), a_min, a_max){
stack new {
.quicksort &a(), a_min, a_max
}
}
}
cswap<=0
compare<=0


Q=Quick()
a()=array(stack(ss))
b()=a()
profiler
for cc=1 to 1 {
a()=b()
Call Q.Sort(&a(), 0, len(a())-1)
}
pen 15{? round(timecount, 0)}
? a()
? cswap, compare


About the developing of Objects for M2000 Interpreter.

Back in 1999, I started to be writing an interpreter named M2000. Letter M is the first letter of Μαθητής the greek wotd for Pupil (the language target the education, from elemantatry stage) and 2000 is the start year of 21 century.

The differences between this language among others are:

  • There is a Stack of Values in every step of our program and we can push values (using Push) and read values (using Read) form the top of stack (like a LIFO) or we can use Data to add  values to the back of stack and when we read them (from top) we make a FIFO. The stack of values ins't the process stack. We can place any type of values.
  • There are a vocabulary of Greek and English words use for writing statements.
  • Modules expand the vocabulary. Passing parameters pushed to stack of values and that stack passed to module. Module's code is responsible to take the parameters and leave the size of stack the way the caller expect (so by using the stack we get values back from module). When interprer call a module actually not known about how and what parameters the module need so send everything we send. 
  • Functions are like modules, we can call then as modules (a non zero value raise error), or we can use them in an expression. Every time we call a function in an expression we pass a new stack of values with all parameters we wish to send to that function (so functions as variadic by design).
  • For modules and functions we can check the stack of values about types before we read them (and popping). Also we can shift values to top (using Shift) or back a distance from top (using ShiftBack), we can make copies using Over to copy  a value or series of values from a distance from top,

The language at those days was a better than an assembler, for writing programs. The implementation was very simple, just to prove that works. Searching of a word was just a word search in a string. The very first interpreter also has only one stack of values (there was no new stack at calling an expression in a function).

Interpreter coded using VB5, and from 2013 using VB6  From 2002 to 2013 was 12 years where I have no interest to expand the language.
At that point (2002) the language was able for 2D graphics, Bitmap manipulation, Sprites,  Video, Music, Sound (Multimedia) and databases (creating access type database, using DAO 3.5), a help system, an editor for big texts (for million of lines), but not Unicode, we have to set the charset for fonts. (Those times I have no idea how to use Unicode from VB6). 

From 2014 I found that the Windows have the VB6 dll (a library) included (and I have Widows 11 now and that dll included, so we can run any VB6 application, including the implementation of M2000 interpreter). Also I found VBForums and I start to think how to expand the interpreter. A make some own controls for unicode input and output., saving/reading files using  UTF8 and many other things. So first I made the GROUP object.

The idea to use a Group object was for passing one thing with some variables by reference.
The example below has the MyGroup with 2 members. What Interpreter did founding the definition? First make MyGroup.C then MyGroup.Y and the real object MyGroup which have a list with the members (as references) So each member is like any other variable. Each time we use Mygroup.C interpreter do a search for a name hidden_module_name.Mygroup.C (ignoring upper/lower letter case), if not found then search without the hidden_mofule_name (check if it is a Global variable).
For some other languages to find a member is a work before the execution, so the object has a "monolithic" use and replace the name with a call to a setter with a specific id, or using a query system (like in COM objects), to return the ID of the member and then using a getter or setter function using that ID.

So for M2000 we have members as defined variables at module, and the actual group has only the list of members. When we push the group to stack of values, a new object created, a floating group. This group has a list of members with values. When we read M in module OneTime the float group create the M group, and the M.C and the M.Y (or more depend of number of group items).

At this point the Group of values is an object as a set of variables.
At the ThisGroup=MyGroup we get a copy of MyGroup as a float group and that float group create the ThisGroup (because ThisGroup is a new name).
So Groups are like variables. The statement MyGroup=ThisGroup do a merging: First a float group created and the merging follow some rules: A numeric or string member take the name as MyGroup and if interpreter found it then place the new value, but if not found it then make a new one.

At checkThis we place a reference so the reference works like this, supposed we pass MyGroup:  we pass the reference (the actual name of  MyGroup) and when the Interpreter found that M is a Group, read the list (which have references and make all the references using the name M (not the MyGroup), So variables M.C is reference of MyGroupC, and M.X is reference of M.Y (a reference point to the same memory as the referenced variable point.
From the point of speed by reference passing is better than by value. Some languages like python, have by value pass only, but for objects actually they pass the pointer to object, so by using the object (and the member id), is like we have the object passing by reference. The difference is that we can't change the object with a new one (because the pointer can't changed on the caller place, and change only on the callee place).  
We can see the member list of group using group.count(), member$() and member,type$(). We can use Read From GroupName, FisrtMember, ....
Using Read From  we make local variables as references to members.  I haven't use this for last 10 years. 

So from that idea (to have something like Records, as values), I made the OOP of M2000 adding more features like members groups (groups inside groups), modules, functions, events, any type of variable including arrays, lists, stacks etc. So far we see the Named Groups (like MyGroup) and the float groups (in a(0) and a(1), and for short time as return value from expression, or from stack of values). Now there is another form, the pointer to group, which hold a group as a float group in a variable and anywhere. So before the introduce of pointer to group, all groups have one only hidden pointer, and only reference can be used (which refer to the actual value). Reference can't changed, they are hard encoded. But pointers to groups are placeholders of pointers, and as that we can change it. A named group deleted at the end of the entity which create it. A float group deleted when the array, the list or the stack deleted or the place which we place it get another group. A group which we get a pointer from that (a real one, and not a pointer as a reference inside- we can do that) deleted when the last pointer to that object deleted (and this call the Remove member of group to do something before actually deleted, using a state of "last pointer"). The Class introduced to make groups by a function, and we can make groups to return values, or to get values, and we can make operators for groups also. And many more, like private and public members, class members, superclass, properties with getters/setters, and types of groups.

So this code show the earlier use of Groups (like records without modules/functions as members)

group MyGroup {
C=10, Y=100
}
module CheckThis (&M) {
M.C++
M.Y+=100
}
ThisGroup=MyGroup ' make a new one, as a copy


CheckThis &MyGroup


Print MyGroup.C=11, MyGroup.Y=200


MyGroup=ThisGroup


Print MyGroup.C=10, MyGroup.Y=100


Group Second {
X=300
}


MyGroup=Second ' Join MyGroup with a copy of Second


Print MyGroup.C=10, MyGroup.Y=100, MyGroup.X=300


' a module with M (is a Read M statement) get a copy on M
Module OneTime (M) {
Print M.C, M.Y,
if valid(M.X) then Print M.X else Print
}


OneTime MyGroup ' has three members  print  10, 100, 300


OneTime ThisGroup ' has only 2 members  print 10, 100
Print "MyGroup"
for i=1 to group.count(MyGroup)
Print member$(MyGroup, i)+"="+eval(member$(MyGroup, i)), member.type$(MyGroup,i)
next
Print "members:"+(i-1)
Print "ThisGroup"
for i=1 to group.count(ThisGroup)
Print member$(ThisGroup, i)+"="+eval(member$(ThisGroup, i)), member.type$(ThisGroup,i)
next
Print "members:"+(i-1)


Dim a(2)
a(0)=ThisGroup
a(1)=MyGroup
a(0).Y+=1000
Print a(0).C=10, a(0).Y=1100
OneTime a(0) ' 10 1100
' passing by reference an array item was a newer work
' in 2014 we have to pass by reference the array and mark the item to change
CheckThis &a(1)
OneTime a(1) ' 11 200 300
Print a(1).C=11, a(1).Y=200, a(1).X=300


Module OldCheckThis (&k(), i) {
for k(i) {
.c++
.Y+=100
}
}
OldCheckThis &a(), 0
OneTime a(0) ' 11 1200
' those days i have an idea to link members to variables as references:
' We use the For object { } which delete all new variables
' We have to delete them because a reference can get new reference.
for this {
Read from ThisGroup, myC, myY
myC=1234
myY=2100
}
Print ThisGroup.C=1234, ThisGroup.Y=2100
' so now myC and myY not exist, we can define them again.
for this {
Read from MyGroup, myC, myY
myC=1234
myY=2100
}
Print MyGroup.C=1234, MyGroup.Y=2100


Module PassMemberByReference (&a) {
a+=50000
}
PassMemberByReference &MyGroup.C
Print MyGroup.C=51234
' we can't pass by reference something without a name like a variable
' in a(0) group is in a Float state, all members are inside and not as variables
' Using  the For object { } we open and fix the members as variables
' because we din't know the actual name we use dot or This dot
' and interpreter add the name and fix it as variable.
For a(0) {
PassMemberByReference &.C
Print .C=50011
PassMemberByReference &this.C
Print .C=100011
}





Τρίτη 12 Νοεμβρίου 2024

Converting plain text to HTML (A rosettacode.org task)

 This module print this (and open default browser and show this as an html page):

<html>
<head>
<title> Sample Text</title>
</head>
<body>
<p>This is an example of converting plain text to HTML which demonstrates extracting a title and escaping certain characters within bulleted and numbered lists.</p>
<ul>
<li>This is a bulleted list with a less than sign (&lt;)</li>
<li>And this is its second line with a greater than sign (&gt;)</li>
</ul>
<p>A &#39;normal&#39; paragraph between the lists. </p>
<ol>
<li>This is a numbered list with an ampersand (&amp;)</li>
<li>&quot;Second line&quot; in double quotes</li>
<li>&#39;Third line&#39; in single quotes</li>
</ol>
<p>That&#39;s all folks.</p>
</body>
</html>


Module CheckIt {
d$={        Sample Text

This is an example of converting plain text to HTML which demonstrates extracting a title and escaping certain characters within bulleted and numbered lists.

* This is a bulleted list with a less than sign (<)

   * And this is its second line with a greater than sign (>)

A 'normal' paragraph between the lists.

1. This is a numbered list with an ampersand (&)

2. "Second line" in double quotes

3. 'Third line' in single quotes

That's all folks.
}

dim par()
nl={
}
d$+=nl
do k=len(d$)
d$=replace$("  ", " ", d$)
until k=len(d$)
do k=len(d$)
d$=replace$(nl+" ", nl, d$)
until k=len(d$)
do k=len(d$)
d$=replace$(nl+nl, nl, d$)
until k=len(d$)
d$=left$(d$, len(d$)-len(nl))
let par()=piece$(@escapeHTML(d$), nl)
endline=len(par())+1
dim par(1 to endline)
string t={<html>
<head>
<title>+++</title>
</head>
<body>
}
par(1)=replace$("+++", par(1), t)
par(endline)={</body>
</html>
}
flush
boolean onelevel_list, onelevel_numeric_list
for i=1 to endline
select case left$(par(i),1)
case "*"
{ CheckFlags(1)
data "<li>"+ltrim$(mid$(par(i), 2))+"</li>"
}
case "<"
{ CheckFlags(0)
while right$(par(i), 2)=nl
par(i)=left$(par(i), len(par(i))-2)
end while
data par(i)
}
case "1" to "9"
{ CheckFlags(2)
m=0
j=val(par(i), 1033, m)
data "<li>"+ltrim$(mid$(par(i), m+1))+"</li>"
}
case else
{ CheckFlags(0)
data "<p>"+par(i)+"</p>"
}
end select
next
Document doc$
if not empty then Doc$=letter$

while not empty
Doc$=nl+letter$
end while
Report Doc$
Clipboard Doc$
const UTF=2, UTF_no_BOM=-2
' insert a BOM
const CRLF=0, LF=10, CR=10
' CRLF is ok
Save.Doc Doc$, "this.html", UTF+CRLF
' Open the default browser
Win file.app$("html"), dir$+"this.html"
End
Sub CheckFlags(c as byte)
if c=1 then
if onelevel_list else
data "<ul>"
onelevel_list=true
end if
else.if c=2 then
if onelevel_numeric_list else
data "<ol>"
onelevel_numeric_list=true
end if
else
if onelevel_list then
data "</ul>"
onelevel_list=false
end if
if onelevel_numeric_list then
data "</ol>"
onelevel_numeric_list=false
end if
end if
End Sub
Function escapeHTML(text As String)
    Local String result = text
    result = replace$( "&", "&", result)
    result = replace$( "<", "<", result)
    result = replace$( ">", ">", result)
    result = replace$( """", """, result)
    result = replace$( "'", "'", result)
    = result
End Function
}
CheckIt


Τετάρτη 6 Νοεμβρίου 2024

Revision 43, Version 12

1. A Bug removed. Passing Enum value with minus sing as parameter to a numeric variable now has the proper sign.

enum aa {one=1, two=2}
def foo(x as long)=x
print foo(-one)=-1 ' previous was 1

2. Using READ and LET for arrays with square brackets

Single a[4]
// the Let evaluate first the expression right from assign symbol =
// then evaluate the index(es) of the array
Let a[4]=1.343 ' not supported before
// without Let evaluation of index(es) of array and then expression after =
a[5]=1.343 ' had no problem
Print a[4], a[5]
data 1.4545
k=random(0, 4)
// Read not supported before for array items of this type of array
read a[k]
print k, a[k]
// still the by reference pass of an array item of this type not supported

3. New programs in INFO file

function multidim_integer(v) {
select case stack.size
case 0
error "Missing Parameters"
case 1
integer a[number]=v:=a
case 2
integer a[number][number]=v:=a
else case ' after each case One line or One Block
{
read k: bb=[]: object b[0]
for j=0 to k: b[j]=lambda(v, !stack(bb)): next
=b
}
end select
}
k=multidim_integer(500, 2, 4, 2, 3)
Print "K has ";3*5*3*4; " items with starting value:";500 ' 180 items
k[0][1][2][0]=1,2,3,4
Print k[0][0][0][0]-99=k[0][1][2][0]+400 ' true
flush
data 100,200,300,400
for i=0 to 3
read k[0][1][1][i]
k[0][1][1][i]++
k[2][1][1][i]+=1000
Let k[1][1][2][i] = k[0][1][1][i] - 50
print k[0][1][1][i], k[0][1][2][i], k[1][1][2][i], k[2][1][1][i]
next

Δευτέρα 4 Νοεμβρίου 2024

STAR TREK GAME (AN OLD BASIC PROGRAM) UPDATED

Run m2000 interpreter (m2000.exe), then write EDIT a press enter and copy the program to module a, then press Esc end write a and press enter to execute module a (to run the program) 

Code was found for BASIC. I made some functions and a simple function at the end (for running the code inside function on the same name space, so the SQR((K(I,1)-S1)^2+(K(I,2)-S2)^2) can be evaluated. Also I use SET SWITCHES which alter the M2000 Interpreter, the +DIM make all DIM to start from 1 and +FOR for a For Next as in BASIC (these switches can be included at the running command). Also we use Clear statement when we want to restart the program, because the DIM function of M2000 works as REDIM too (if the array exist), so the Clear statement erase all variables which defined, so all DIM work for new arrays. SET SLOW don't need (we can erase it). Escape Off make ESC key to not interrupt the progam (although Ctrl+C can stop it)









Program Updated:  Nov 7, 2024

GLOBAL OLD_BOLD=BOLD:BOLD 1 
Module StarTrek {
// ADAPDET FROM http://www.vintage-basic.net/bcg/superstartrek.bas
FUNCTION GLOBAL SQR(){=SQRT(NUMBER)}
FUNCTION GLOBAL RND(X AS INTEGER){=IF(X<=1->RND, RANDOM(1, X))}
FUNCTION GLOBAL TAB(A){=IF$(POS<A->STRING$(" ", A-POS), "")}
FONT "COURIER NEW"
SET SLOW
ESCAPE OFF
10 CLS 0: PEN 14: FORM 70,32;: CLEAR
20 REM SUPER STARTREK - MAY 16,1978 - REQUIRES 24K MEMORY
30 REM
40 REM **** **** STAR TREK **** ****
50 REM **** SIMULATION OF A MISSION OF THE STARSHIP ENTERPRISE,
60 REM **** AS SEEN ON THE STAR TREK TV SHOW.
70 REM **** ORIGIONAL PROGRAM BY MIKE MAYFIELD, MODIFIED VERSION
80 REM **** PUBLISHED IN DEC΄S "101 BASIC GAMES", BY DAVE AHL.
90 REM **** MODIFICATIONS TO THE LATTER (PLUS DEBUGGING) BY BOB
100 REM *** LEEDOM - APRIL & DECEMBER 1974,
110 REM *** WITH A LITTLE HELP FROM HIS FRIENDS . . .
120 REM *** COMMENTS, EPITHETS, AND SUGGESTIONS SOLICITED --
130 REM *** SEND TO: R. C. LEEDOM
140 REM *** WESTINGHOUSE DEFENSE & ELECTRONICS SYSTEMS CNTR.
150 REM *** BOX 746, M.S. 338
160 REM *** BALTIMORE, MD 21203
170 REM ***
180 REM *** CONVERTED TO MICROSOFT 8 K BASIC 3/16/78 BY JOHN GORDERS
190 REM *** LINE NUMBERS FROM VERSION STREK7 OF 1/12/75 PRESERVED AS
200 REM *** MUCH AS POSSIBLE WHILE USING MULTIPLE STATEMENTS PER LINE
205 REM *** SOME LINES ARE LONGER THAN 72 CHARACTERS; THIS WAS DONE
210 REM *** BY USING "?" INSTEAD OF "PRINT" WHEN ENTERING LINES
215 REM ***
220 PRINT: REM:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT
221 PRINT"                                    ,------*------,"
222 PRINT"                    ,-------------   '---  ------'"
223 PRINT"                     '-------- --'      / /"
224 PRINT"                         ,---' '-------/ /--,"
225 PRINT"                          '----------------'":PRINT
226 PRINT"                    THE USS ENTERPRISE --- NCC-1701"
227 CLS #226622, ROW: PEN #00FF00: BOLD 0
260 REM CLEAR 600
261 SET SWITCHES "+DIM +FOR"
270 Z$="                         "
330 DIM G(8,8),C(9,2),K(3,3),N(3),Z(8,8),D(8)
370 T=INT(RND(1)*20+20)*100:T0=T:T9=25+INT(RND(1)*10):D0=0:E=3000:E0=E
440 P=10:P0=P:S9=200:S=0:B9=2:K9=0:X$="":X0$=" IS "
470 REM DEF FND(D)=SQR((K(I,1)-S1)^2+(K(I,2)-S2)^2)
475 DEF FNR(R)=INT(RND(R)*7.98+1.01)
480 REM INITIALIZE ENTERPRIZE'S POSITION
490 Q1=FNR(1):Q2=FNR(1):S1=FNR(1):S2=FNR(1)
530 FOR I=1TO 9:C(I,1)=0:C(I,2)=0:NEXT I
540 C(3,1)=-1:C(2,1)=-1:C(4,1)=-1:C(4,2)=-1:C(5,2)=-1:C(6,2)=-1
600 C(1,2)=1:C(2,2)=1:C(6,1)=1:C(7,1)=1:C(8,1)=1:C(8,2)=1:C(9,2)=1
670 FOR I=1TO 8:D(I)=0:NEXT
710 A1$="NAVSRSLRSPHATORSHEDAMCOMXXX"
810 REM SETUP WHAT EXHISTS IN GALAXY . . .
815 REM K3= # KLINGONS B3= # STARBASES S3 = # STARS
820 FOR I=1TO 8:FOR J=1TO 8:K3=0:Z(I,J)=0:R1=RND(1)
850 IF R1>0.98 THEN K3=3:K9=K9+3:GOTO 980
860 IF R1>0.95 THEN K3=2:K9=K9+2:GOTO 980
870 IF R1>0.80 THEN K3=1:K9=K9+1
980 B3=0:IF RND(1)>0.96 THEN B3=1:B9=B9+1
1040 G(I,J)=K3*100+B3*10+FNR(1)
1050 NEXT J
1060 NEXT I : IF B9=0 THEN 820
1070 IF K9>T9 THEN T9=K9+1
1100 IF B9<>0 THEN 1200
1150 IF G(Q1,Q2)<200THEN G(Q1,Q2)=G(Q1,Q2)+120:K9=K9+1
1160 B9=1:G(Q1,Q2)=G(Q1,Q2)+10:Q1=FNR(1):Q2=FNR(1)
1200 K7=K9:IF B9<>1THEN X$="S":X0$=" ARE "
1230 PEN #77FF77:PRINT"YOUR ORDERS ARE AS FOLLOWS:"
1240 PRINT"  DESTROY THE ";K9;" KLINGON WARSHIPS WHICH HAVE INVADED"
1252 PRINT"  THE GALAXY BEFORE THEY CAN ATTACK FEDERATION HQ"
1260 PRINT"  ON STARDATE ";T0+T9;
1270 PRINT" THIS GIVES YOU ";T9;" DAYS. THERE";X0$
1280 PRINT"  ";B9;" STARBASE";X$;
1290 PRINT" IN THE GALAXY FOR RESUPPLYING YOUR SHIP":PRINT:PEN #00FF00
1300 I=RND(1)
1310 REM HERE ANY TIME NEW QUADRANT ENTERED
1320 Z4=Q1:Z5=Q2:K3=0:B3=0:S3=0:G5=0:D4=0.5*RND(1):Z(Q1,Q2)=G(Q1,Q2)
1390 IF Q1<1OR Q1>8OR Q2<1OR Q2>8THEN 1600
1430 GOSUB 9030:PRINT:IF T0<>T THEN 1490
1460 PRINT"YOUR MISSION BEGINS WITH YOUR STARSHIP LOCATED"
1470 PRINT"IN THE GALACTIC QUADRANT, '";G2$;"'.":GOTO 1500
1490 PRINT"NOW ENTERING ";G2$;" QUADRANT . . ."
1500 PRINT:K3=INT(G(Q1,Q2)*0.01):B3=INT(G(Q1,Q2)*0.1)-10*K3
1540 S3=G(Q1,Q2)-100*K3-10*B3:IF K3=0THEN 1590
1560 PRINT"COMBAT AREA      CONDITION RED":IF S>200THEN 1590
1580 PRINT"   SHIELDS DANGEROUSLY LOW"
1590 FOR I=1TO 3:K(I,1)=0:K(I,2)=0:NEXT I
1600 FOR I=1TO 3:K(I,3)=0:NEXT I:Q$=Z$+Z$+Z$+Z$+Z$+Z$+Z$+LEFT$(Z$,17)
1660 REM POSITION ENTERPRISE IN QUADRANT, THEN PLACE "K3" KLINGONS, &
1670 REM "B3" STARBASES, & "S3" STARS ELSEWHERE.
1680 A$="<*>":Z1=S1:Z2=S2:GOSUB 8670:IF K3<1THEN 1820
1720 FOR I=1TO K3:GOSUB 8590:A$="+K+":Z1=R1:Z2=R2
1780 GOSUB 8670:K(I,1)=R1:K(I,2)=R2:K(I,3)=S9*(0.5+RND(1))
1790 NEXT I
1820 IF B3<1THEN 1910
1880 GOSUB 8590:A$=">!<":Z1=R1:B4=R1:Z2=R2:B5=R2:GOSUB 8670
1910 FOR I=1TO S3:GOSUB 8590:A$=" * ":Z1=R1:Z2=R2:GOSUB 8670
1920 NEXT I
1980 GOSUB 6430
1990 IF S+E>10 THEN IF E>10 OR D(7)=0 THEN 2060
2020 PRINT
2025 PRINT"** FATAL ERROR **   YOU'VE JUST STRANDED YOUR SHIP IN SPACE"
2030 PRINT"YOU HAVE INSUFFICIENT MANEUVERING ENERGY,";
2040 PRINT" AND SHIELD CONTROL":PRINT"IS PRESENTLY INCAPABLE OF CROSS";
2050 PRINT"-CIRCUITING TO ENGINE ROOM!!":GOTO 6220
2060 INPUT"COMMAND? ";A$: A$=UCASE$(A$) ' M2000 - UCASE$()
2080 FOR I=1 TO 9
2090 IF LEFT$(A$,3)=MID$(A1$,3*I-2,3) THEN EXIT FOR 2150
2100 NEXT
2150 ON I GOTO 2300,1980,4000,4260,4700,5530,5690,7290,6270
2160 CLS: PEN #77FF77:PRINT"ENTER ONE OF THE FOLLOWING:"
2180 PRINT"  NAV  (TO SET COURSE)"
2190 PRINT"  SRS  (FOR SHORT RANGE SENSOR SCAN)"
2200 PRINT"  LRS  (FOR LONG RANGE SENSOR SCAN)"
2210 PRINT"  PHA  (TO FIRE PHASERS)"
2220 PRINT"  TOR  (TO FIRE PHOTON TORPEDOES)"
2230 PRINT"  SHE  (TO RAISE OR LOWER SHIELDS)"
2240 PRINT"  DAM  (FOR DAMAGE CONTROL REPORTS)"
2250 PRINT"  COM  (TO CALL ON LIBRARY-COMPUTER)"
2260 PRINT"  XXX  (TO RESIGN YOUR COMMAND)"
2270 PEN #00FF00:PRINT: GOTO 1990
2290 REM COURSE CONTROL BEGINS HERE
2300 GOSUB 9270:PRINT "TIP:"
2301 PRINT " Course 1.5 is half way between 1 and 2."
2302 PRINT " From 6,5 to 5,5 course:3, warp factor 1."

2305 INPUT"COURSE (0-9):";C1:IF C1=9THEN C1=1
2310 IF C1>=1AND C1<9 THEN 2360
2330 PRINT"   LT. SULU REPORTS, 'INCORRECT COURSE DATA, SIR!'":GOTO 1990
2350 X$="8":IF D(1)<0THEN X$="0.2"
2360 PRINT"WARP FACTOR (0-";X$;")";:INPUT W1:IF D(1)<0 AND W1>0.2 THEN 2470
2380 IF W1>0 AND W1<=8 THEN 2490
2390 IF W1=0 THEN 1990
2420 PRINT"   CHIEF ENGINEER SCOTT REPORTS 'THE ENGINES WON'T TAKE";
2430 PRINT" WARP ";W1;"!'":GOTO 1990
2470 PRINT"WARP ENGINES ARE DAMAGED.  MAXIUM SPEED = WARP 0.2":GOTO 1990
2490 N=INT(W1*8+0.5):IF E-N>=0THEN 2590
2500 PRINT"ENGINEERING REPORTS   'INSUFFICIENT ENERGY AVAILABLE"
2510 PRINT"                       FOR MANEUVERING AT WARP";W1;"!'"
2530 IF S<N-E OR D(7)<0 THEN 1990
2550 PRINT"DEFLECTOR CONTROL ROOM ACKNOWLEDGES ";S;" UNITS OF ENERGY"
2560 PRINT"                         PRESENTLY DEPLOYED TO SHIELDS."
2570 GOTO 1990
2580 REM KLINGONS MOVE/FIRE ON MOVING STARSHIP . . .
2590 FOR I=1 TO K3:IF K(I,3)=0 THEN 2700
2610 A$="   ":Z1=K(I,1):Z2=K(I,2):GOSUB 8670:GOSUB 8590
2660 K(I,1)=Z1:K(I,2)=Z2:A$="+K+":GOSUB 8670
2700 NEXT I:GOSUB 6000:D1=0:D6=W1:IF W1>=1THEN D6=1
2770 FOR I=1 TO 8:IF D(I)>=0 THEN 2880
2790 D(I)=D(I)+D6:IF D(I)>-.1AND D(I)<0THEN D(I)=-.1:GOTO 2880
2800 IF D(I)<0THEN 2880
2810 IF D1<>1THEN D1=1:PRINT"DAMAGE CONTROL REPORT:  ";
2840 PRINT TAB(8);:R1=I:GOSUB 8790:PRINT G2$;" REPAIR COMPLETED."
2880 NEXT I:IF RND(1)>0.2THEN 3070
2910 R1=FNR(1):IF RND(1)>=0.6THEN 3000
2930 D(R1)=D(R1)-(RND(1)*5+1):PRINT"DAMAGE CONTROL REPORT:  ";
2960 GOSUB 8790:PRINT G2$;" DAMAGED":PRINT:GOTO 3070
3000 D(R1)=D(R1)+RND(1)*3+1:PRINT"DAMAGE CONTROL REPORT:  ";
3030 GOSUB 8790:PRINT G2$;" STATE OF REPAIR IMPROVED":PRINT
3060 REM BEGIN MOVING STARSHIP
3070 A$="   ":Z1=INT(S1):Z2=INT(S2):GOSUB 8670
3110 X1=C(C1,1)+(C(C1+1,1)-C(C1,1))*(C1-INT(C1)):X=S1:Y=S2
3140 X2=C(C1,2)+(C(C1+1,2)-C(C1,2))*(C1-INT(C1)):Q4=Q1:Q5=Q2
3170 FOR I=1TO N:S1=S1+X1:S2=S2+X2:IF S1<1OR S1>=9OR S2<1OR S2>=9 THEN EXIT FOR 3500
3240 S8=INT(S1)*24+INT(S2)*3-26:IF MID$(Q$,S8,2)="  " THEN 3360
3320 S1=INT(S1-X1):S2=INT(S2-X2):PRINT"WARP ENGINES SHUT DOWN AT ";
3350 PRINT"SECTOR ";S1;", ";S2;"DUE TO BAD NAVIGATION":EXIT FOR 3370
3360 NEXT I:S1=INT(S1):S2=INT(S2)
3370 A$="<*>":Z1=INT(S1):Z2=INT(S2):GOSUB 8670:GOSUB 3910:T8=1
3430 IF W1<1THEN T8=0.1*INT(10*W1)
3450 T=T+T8:IF T>T0+T9 THEN 6220
3470 REM SEE IF DOCKED, THEN GET COMMAND
3480 GOTO 1980
3490 REM EXCEEDED QUADRANT LIMITS
3500 X=8*Q1+X+N*X1:Y=8*Q2+Y+N*X2:Q1=INT(X/8):Q2=INT(Y/8):S1=INT(X-Q1*8)
3550 S2=INT(Y-Q2*8):IF S1=0THEN Q1=Q1-1:S1=8
3590 IF S2=0THEN Q2=Q2-1:S2=8
3620 X5=0:IF Q1<1THEN X5=1:Q1=1:S1=1
3670 IF Q1>8THEN X5=1:Q1=8:S1=8
3710 IF Q2<1THEN X5=1:Q2=1:S2=1
3750 IF Q2>8THEN X5=1:Q2=8:S2=8
3790 IF X5=0THEN 3860
3800 PRINT"LT. UHURA REPORTS MESSAGE FROM STARFLEET COMMAND:"
3810 PRINT"  'PERMISSION TO ATTEMPT CROSSING OF GALACTIC PERIMETER"
3820 PRINT"  IS HEREBY *DENIED*0.  SHUT DOWN YOUR ENGINES.'"
3830 PRINT"CHIEF ENGINEER SCOTT REPORTS  'WARP ENGINES SHUT DOWN"
3840 PRINT"  AT SECTOR ";S1;", ";S2;"OF QUADRANT ";Q1;", ";Q2;".'"
3850 IF T>T0+T9 THEN 6220
3860 IF 8*Q1+Q2=8*Q4+Q5 THEN 3370
3870 T=T+1:GOSUB 3910:GOTO 1320
3900 REM MANEUVER ENERGY S/R **
3910 E=E-N-10:IF E>=0THEN RETURN
3930 PRINT"SHIELD CONTROL SUPPLIES ENERGY TO COMPLETE THE MANEUVER."
3940 S=S+E:E=0:IF S<=0THEN S=0
3980 RETURN
3990 REM LONG RANGE SENSOR SCAN CODE
4000 IF D(3)<0THEN PRINT"LONG RANGE SENSORS ARE INOPERABLE":GOTO 1990
4030 PRINT"LONG RANGE SCAN FOR QUADRANT ";Q1;",";Q2
4035 PRINT TAB(4);"KLINGONS/STARBASES/STARS"
4040 O1$="-------------------":PRINT TAB(6);O1$
4060 FOR I=Q1-1TO Q1+1:N(1)=-1:N(2)=-2:N(3)=-3:PRINT TAB(6);:FOR J=Q2-1TO Q2+1
4120 IF I>0AND I<9 AND J>0 AND J<9THEN N(J-Q2+2)=G(I,J):Z(I,J)=G(I,J)
4180 NEXT J:FOR L=1TO 3:PRINT": ";:IF N(L)<0THEN PRINT"*** ";:GOTO 4230
4210 PRINT RIGHT$(STR$(N(L)+1000),3);" ";
4230 NEXT L:PRINT":":PRINT TAB(6);O1$:NEXT I:GOTO 1990
4250 REM PHASER CONTROL CODE BEGINS HERE
4260 IF D(4)<0THEN PRINT"PHASERS INOPERATIVE":GOTO 1990
4265 IF K3>0THEN 4330
4270 PRINT"SCIENCE OFFICER SPOCK REPORTS  'SENSORS SHOW NO ENEMY SHIPS"
4280 PRINT"                                IN THIS QUADRANT'":GOTO 1990
4330 IF D(8)<0THEN PRINT"COMPUTER FAILURE HAMPERS ACCURACY"
4350 PRINT"PHASERS LOCKED ON TARGET;  ";
4360 PRINT"ENERGY AVAILABLE =";E;" UNITS"
4370 INPUT"NUMBER OF UNITS TO FIRE:";X:IF X<=0THEN 1990
4400 IF E-X<0THEN 4360
4410 E=E-X:IF D(7)<0THEN X=X*RND(1)
4450 H1=INT(X/K3):FOR I=1TO 3:IF K(I,3)<=0THEN 4670
4480 H=INT((H1/@FND(0))*(RND(1)+2)):IF H>0.15*K(I,3)THEN 4530
4500 PRINT"SENSORS SHOW NO DAMAGE TO ENEMY AT ";K(I,1);",";K(I,2):GOTO 4670
4530 K(I,3)=K(I,3)-H:PRINT H;" UNIT HIT ON KLINGON AT SECTOR ";K(I,1);",";
4550 PRINT K(I,2):IF K(I,3)<=0 THEN PRINT"*** KLINGON DESTROYED ***":GOTO 4580
4560 PRINT"   (SENSORS SHOW:";K(I,3);" UNITS REMAINING)":GOTO 4670
4580 K3=K3-1:K9=K9-1:Z1=K(I,1):Z2=K(I,2):A$="   ":GOSUB 8670
4650 K(I,3)=0:G(Q1,Q2)=G(Q1,Q2)-100:Z(Q1,Q2)=G(Q1,Q2)
4660 IF K9<=0THEN EXIT FOR 6370
4670 NEXT I:GOSUB 6000:GOTO 1990
4690 REM PHOTON TORPEDO CODE BEGINS HERE
4700 IF P<=0THEN PRINT"ALL PHOTON TORPEDOES EXPENDED":GOTO 1990
4730 IF D(5)<0THEN PRINT"PHOTON TUBES ARE NOT OPERATIONAL":GOTO 1990
4740 PRINT"SCOPE ASSISTANT (YOU MAY USE DECIMAL NUMBER)":GOSUB 9270
4760 INPUT"PHOTON TORPEDO COURSE (1-9):";C1:IF C1=9THEN C1=1
4780 IF C1>=1AND C1<9THEN 4850
4790 PRINT"ENSIGN CHEKOV REPORTS,  'INCORRECT COURSE DATA, SIR!'"
4800 GOTO 1990
4850 X1=C(C1,1)+(C(C1+1,1)-C(C1,1))*(C1-INT(C1)):E=E-2:P=P-1
4860 X2=C(C1,2)+(C(C1+1,2)-C(C1,2))*(C1-INT(C1)):X=S1:Y=S2
4910 PRINT"TORPEDO TRACK:"
4920 X=X+X1:Y=Y+X2:X3=INT(X+0.5):Y3=INT(Y+0.5)
4960 IF X3<1OR X3>8OR Y3<1OR Y3>8THEN 5490
5000 PRINT TAB(12);X3;",";Y3:A$="   ":Z1=X:Z2=Y:GOSUB 8830
5050 IF Z3<>0THEN 4920
5060 A$="+K+":Z1=X:Z2=Y:GOSUB 8830:IF Z3=0THEN 5210
5110 PRINT"*** KLINGON DESTROYED ***":K3=K3-1:K9=K9-1:IF K9<=0THEN EXIT FOR 6370
5150 FOR I=1TO 3:IF X3=K(I,1)AND Y3=K(I,2) THEN EXIT FOR 5190
5180 NEXT I:I=3
5190 K(I,3)=0:GOTO 5430
5210 A$=" * ":Z1=X:Z2=Y:GOSUB 8830:IF Z3=0THEN 5280
5260 PRINT"STAR AT ";X3;", ";Y3;" ABSORBED TORPEDO ENERGY.":GOSUB 6000:GOTO 1990
5280 A$=">!<":Z1=X:Z2=Y:GOSUB 8830:IF Z3=0 THEN 4760
5330 PRINT"*** STARBASE DESTROYED ***":B3=B3-1:B9=B9-1
5360 IF B9>0OR K9>T-T0-T9 THEN 5400
5370 PRINT"THAT DOES IT, CAPTAIN!!  YOU ARE HEREBY RELIEVED OF COMMAND"
5380 PRINT"AND SENTENCED TO 99 STARDATES AT HARD LABOR ON CYGNUS 12!!"
5390 GOTO 6270
5400 PRINT"STARFLEET COMMAND REVIEWING YOUR RECORD TO CONSIDER"
5410 PRINT"COURT MARTIAL!":D0=0
5430 Z1=X:Z2=Y:A$="   ":GOSUB 8670
5470 G(Q1,Q2)=K3*100+B3*10+S3:Z(Q1,Q2)=G(Q1,Q2):GOSUB 6000:GOTO 1990
5490 PRINT"TORPEDO MISSED":GOSUB 6000:GOTO 1990
5520 REM SHIELD CONTROL
5530 IF D(7)<0THEN PRINT"SHIELD CONTROL INOPERABLE":GOTO 1990
5560 PRINT"ENERGY AVAILABLE =";E+S;" ";:INPUT"NUMBER OF UNITS TO SHIELDS:";X
5580 IF X<0OR S=X THEN PRINT"<SHIELDS UNCHANGED>":GOTO 1990
5590 IF X<=E+S THEN 5630
5600 PRINT"SHIELD CONTROL REPORTS  'THIS IS NOT THE FEDERATION TREASURY.'"
5610 PRINT"<SHIELDS UNCHANGED>":GOTO 1990
5630 E=E+S-X:S=X:PRINT"DEFLECTOR CONTROL ROOM REPORT:"
5660 PRINT"  'SHIELDS NOW AT ";INT(S);" UNITS PER YOUR COMMAND.'":GOTO 1990
5680 REM DAMAGE CONTROL
5690 IF D(6)>=0THEN 5910
5700 PRINT"DAMAGE CONTROL REPORT NOT AVAILABLE":IF D0=0THEN 1990
5720 D3=0:FOR I=1 TO 8:IF D(I)<0THEN D3=D3+0.1
5760 NEXT I:IF D3=0THEN 1990
5780 PRINT:D3=D3+D4:IF D3>=1THEN D3=0.9
5810 PRINT"TECHNICIANS STANDING BY TO EFFECT REPAIRS TO YOUR SHIP;"
5820 PRINT"ESTIMATED TIME TO REPAIR:";.01*INT(100*D3);" STARDATES"
5840 INPUT "WILL YOU AUTHORIZE THE REPAIR ORDER (Y/N):";A$
5860 IF A$<>"Y"THEN 1990
5870 FOR I=1TO 8:IF D(I)<0THEN D(I)=0
5890 NEXT I:T=T+D3+0.1
5910 PRINT:PRINT"DEVICE             STATE OF REPAIR":FOR R1=1TO 8
5920 GOSUB 8790:PRINT G2$;LEFT$(Z$,25-LEN(G2$));INT(D(R1)*100)*0.01
5950 NEXT R1:PRINT:IF D0<>0THEN 5720
5980 GOTO 1990
5990 REM KLINGONS SHOOTING
6000 IF K3<=0THEN RETURN
6010 IF D0<>0THEN PRINT"STARBASE SHIELDS PROTECT THE ENTERPRISE":RETURN
6040 FOR I=1TO 3:IF K(I,3)<=0THEN 6200
6060 H=INT((K(I,3)/@FND(1))*(2+RND(1))):S=S-H:K(I,3)=K(I,3)/(3+RND(0))
6080 PRINT H;" UNIT HIT ON ENTERPRISE FROM SECTOR:";K(I,1);",";K(I,2)
6090 IF S<=0 THEN EXIT FOR 6240
6100 PRINT"      <SHIELDS DOWN TO ";S;" UNITS>"
6110 IF H<20 THEN 6200
6120 IF RND(1)>0.6 OR H/S<=0.02 THEN 6200
6140 R1=FNR(1):D(R1)=D(R1)-H/S-.5*RND(1):GOSUB 8790
6170 PRINT"DAMAGE CONTROL REPORTS ";G2$;" DAMAGED BY THE HIT'"
6200 NEXT I:RETURN
6210 REM END OF GAME
6220 PRINT"IT IS STARDATE ";T:GOTO 6270
6240 PRINT:PRINT"THE ENTERPRISE HAS BEEN DESTROYED."
6250 PRINT"THEN FEDERATION WILL BE CONQUERED":GOTO 6220
6270 PRINT"THERE WERE ";K9;" KLINGON BATTLE CRUISERS LEFT AT"
6280 PRINT"THE END OF YOUR MISSION."
6290 PRINT:PRINT:IF B9=0THEN 6360
6310 PRINT"THE FEDERATION IS IN NEED OF A NEW STARSHIP COMMANDER"
6320 PRINT"FOR A SIMILAR MISSION -- IF THERE IS A VOLUNTEER,"
6330 INPUT"LET HIM STEP FORWARD AND ENTER 'AYE':";A$:IF A$="AYE" THEN 10
6360 IF OLD_BOLD THEN BOLD 1 ELSE BOLD 0
6365 END
6370 PRINT"CONGRULATION, CAPTAIN!  THEN LAST KLINGON BATTLE CRUISER"
6380 PRINT"MENACING THE FDERATION HAS BEEN DESTROYED.":PRINT
6400 PRINT"YOUR EFFICIENCY RATING IS ";1000*(K7/(T-T0))^2:GOTO 6290
6420 REM SHORT RANGE SENSOR SCAN & STARTUP SUBROUTINE
6430 FOR I=S1-1TO S1+1:FOR J=S2-1TO S2+1
6450 IF INT(I+0.5)<1OR INT(I+0.5)>8OR INT(J+0.5)<1OR INT(J+0.5)>8THEN 6540
6490 A$=">!<":Z1=I:Z2=J:GOSUB 8830:IF Z3=1THEN EXIT FOR
6540 NEXT J
6550 IF Z3=1THEN EXIT FOR 6580
6560 NEXT I:D0=0:GOTO 6650
6580 D0=1:C$="DOCKED":E=E0:P=P0
6620 PRINT"SHIELDS DROPPED FOR DOCKING PURPOSES":S=0:GOTO 6720
6650 IF K3>0THEN C$="*RED*":GOTO 6720
6660 C$="GREEN":IF E<E0*0.1 THEN C$="YELLOW"
6720 IF D(2)>=0THEN 6770
6730 PRINT:PRINT"*** SHORT RANGE SENSORS ARE OUT ***":PRINT:RETURN
6770 O1$="---------------------------------":PRINT O1$:FOR I=1 TO 8
6820 FOR J=(I-1)*24+1TO (I-1)*24+22 STEP 3:PRINT" ";MID$(Q$,J,3);:NEXT J
6830 ON I GOTO 6850,6900,6960,7020,7070,7120,7180,7240
6850 PRINT"        STARDATE           ";INT(T*10)*0.1:GOTO 7260
6900 PRINT"        CONDITION          ";C$:GOTO 7260
6960 PRINT"        QUADRANT           ";Q1;",";Q2:GOTO 7260
7020 PRINT"        SECTOR             ";S1;",";S2:GOTO 7260
7070 PRINT"        PHOTON TORPEDOES   ";INT(P):GOTO 7260
7120 PRINT"        TOTAL ENERGY       ";INT(E+S):GOTO 7260
7180 PRINT"        SHIELDS            ";INT(S):GOTO 7260
7240 PRINT"        KLINGONS REMAINING ";INT(K9)
7260 NEXT I:PRINT O1$:RETURN
7280 REM LIBRARY COMPUTER CODE
7290 IF D(8)<0THEN PRINT"COMPUTER DISABLED":GOTO 1990
7320 INPUT"COMPUTER ACTIVE AND AWAITING COMMAND (6-LIST): ";A:IF A<0THEN 1990
7350 PRINT:H8=1:ON A+1 GOTO 7540,7900,8070,8500,8150,7400
7360 PRINT"FUNCTIONS AVAILABLE FROM LIBRARY-COMPUTER:"
7370 PRINT"   0 = CUMULATIVE GALACTIC RECORD"
7372 PRINT"   1 = STATUS REPORT"
7374 PRINT"   2 = PHOTON TORPEDO DATA"
7376 PRINT"   3 = STARBASE NAV DATA"
7378 PRINT"   4 = DIRECTION/DISTANCE CALCULATOR"
7380 PRINT"   5 = GALAXY 'REGION NAME' MAP":PRINT:GOTO 7320
7390 REM SETUP TO CHANGE CUM GAL RECORD TO GALAXY MAP
7400 H8=0:G5=1:PRINT TAB(WIDTH/2-5);"THE GALAXY":GOTO 7550
7530 REM CUM GALACTIC RECORD
7540 REM INPUT"DO YOU WANT A HARDCOPY? IS THE TTY ON (Y/N):";A$
7542 REM IF A$="Y"THEN POKE 1229,2:POKE 1237,3:NULL1
7543 PRINT:PRINT"        ";
7544 PRINT"COMPUTER RECORD OF GALAXY FOR QUADRANT ";Q1;", ";Q2
7546 PRINT
7550 PRINT TAB(13);"1     2     3     4     5     6     7     8"
7560 O1$="----- ----- ----- ----- ----- ----- ----- -----"
7570 PRINT TAB(11);O1$:FOR I=1TO 8:PRINT TAB(8);I;:IF H8=0THEN 7740
7630 FOR J=1TO 8:PRINT"   ";:IF Z(I,J)=0THEN PRINT"***";:GOTO 7720
7700 PRINT RIGHT$(STR$(Z(I,J)+1000),3);
7720 NEXT J:GOTO 7850
7740 Z4=I:Z5=1:GOSUB 9030:J0=INT(15-0.5*LEN(G2$))+8:PRINT TAB(J0);G2$;
7800 Z5=5:GOSUB 9030:J0=INT(39-0.5*LEN(G2$))+8:PRINT TAB(J0);G2$;
7850 PRINT:PRINT TAB(11);O1$:NEXT I:PRINT:GOTO 1990
7890 REM STATUS REPORT
7900 PRINT "   STATUS REPORT:":X$="":IF K9>1THEN X$="S"
7940 PRINT"KLINGON";X$;" LEFT: ";K9
7960 PRINT"MISSION MUST BE COMPLETED IN ";.1*INT((T0+T9-T)*10);" STARDATES"
7970 X$="S":IF B9<2THEN X$="":IF B9<1THEN 8010
7980 PRINT"THE FEDERATION IS MAINTAINING ";B9;" STARBASE";X$;" IN THE GALAXY"
7990 GOTO 5690
8010 PRINT"YOUR STUPIDITY HAS LEFT YOU ON YOUR ON IN"
8020 PRINT"  THE GALAXY -- YOU HAVE NO STARBASES LEFT!":GOTO 5690
8060 REM TORPEDO, BASE NAV, D/D CALCULATOR
8070 IF K3<=0THEN 4270
8080 X$="":IF K3>1THEN X$="S"
8090 PRINT"FROM ENTERPRISE TO KLINGON BATTLE CRUSER ";X$
8100 H8=0:FOR I=1TO 3:IF K(I,3)<=0THEN 8480
8110 W1=K(I,1):X=K(I,2)
8120 C1=S1:A=S2:GOTO 8220
8150 PRINT"DIRECTION/DISTANCE CALCULATOR:"
8160 PRINT"YOU ARE AT QUADRANT ";Q1;", ";Q2;" SECTOR ";S1;", ";S2
8170 PRINT"PLEASE ENTER":INPUT"  INITIAL COORDINATES (ROW, COL):";C1,A
8200 INPUT"  FINAL COORDINATES (ROW, COL):";W1,X
8220 X=X-A:A=C1-W1:IF X<0THEN 8350
8250 IF A<0THEN 8410
8260 IF X>0THEN 8280
8270 IF A=0THEN C1=5:GOTO 8290
8280 C1=1
8290 IF ABS(A)<=ABS(X)THEN 8330
8310 PRINT"DIRECTION =";C1+(((ABS(A)-ABS(X))+ABS(A))/ABS(A)):GOTO 8460
8330 PRINT"DIRECTION =";C1+(ABS(A)/ABS(X)):GOTO 8460
8350 IF A>0THEN C1=3:GOTO 8420
8360 IF X<>0THEN C1=5:GOTO 8290
8410 C1=7
8420 IF ABS(A)>=ABS(X)THEN 8450
8430 PRINT"DIRECTION =";C1+(((ABS(X)-ABS(A))+ABS(X))/ABS(X)):GOTO 8460
8450 PRINT"DIRECTION =";C1+(ABS(X)/ABS(A))
8460 PRINT"DISTANCE =";SQR(X^2+A^2):IF H8=1THEN 1990
8480 NEXT I:GOTO 1990
8500 IF B3<>0 THEN PRINT"FROM ENTERPRISE TO STARBASE:":W1=B4:X=B5:GOTO 8120
8510 PRINT"MR. SPOCK REPORTS,  'SENSORS SHOW NO STARBASES IN THIS";
8520 PRINT" QUADRANT.'":GOTO 1990
8580 REM FIND EMPTY PLACE IN QUADRANT (FOR THINGS)
8590 R1=FNR(1):R2=FNR(1):A$="   ":Z1=R1:Z2=R2:GOSUB 8830:IF Z3=0THEN 8590
8600 RETURN
8660 REM INSERT IN STRING ARRAY FOR QUADRANT
8670 S8=INT(Z2-.5)*3+INT(Z1-.5)*24+1
8675 IF LEN(A$)<>3THEN ERROR "ERROR AT 8675"
8680 IF S8=1THEN Q$=A$+RIGHT$(Q$,189):RETURN
8690 IF S8=190THEN Q$=LEFT$(Q$,189)+A$:RETURN
8700 Q$=LEFT$(Q$,S8-1)+A$+RIGHT$(Q$,190-S8):RETURN
8780 REM PRINTS DEVICE NAME
8790 ON R1 GOTO 8792,8794,8796,8798,8800,8802,8804,8806
8792 G2$="WARP ENGINES":RETURN
8794 G2$="SHORT RANGE SENSORS":RETURN
8796 G2$="LONG RANGE SENSORS":RETURN
8798 G2$="PHASER CONTROL":RETURN
8800 G2$="PHOTON TUBES":RETURN
8802 G2$="DAMAGE CONTROL":RETURN
8804 G2$="SHIELD CONTROL":RETURN
8806 G2$="LIBRARY-COMPUTER":RETURN
8820 REM STRING COMPARISON IN QUADRANT ARRAY
8830 Z1=INT(Z1+0.5):Z2=INT(Z2+0.5):S8=(Z2-1)*3+(Z1-1)*24+1:Z3=0
8890 IF MID$(Q$,S8,3)<>A$ THEN RETURN
8900 Z3=1:RETURN
9010 REM QUADRANT NAME IN G2$ FROM Z4,Z5 (=Q1,Q2)
9020 REM CALL WITH G5=1 TO GET REGION NAME ONLY
9030 IF Z5<=4 THEN ON Z4 GOTO 9040,9050,9060,9070,9080,9090,9100,9110
9035 GOTO 9120
9040 G2$="ANTARES":GOTO 9210
9050 G2$="RIGEL":GOTO 9210
9060 G2$="PROCYON":GOTO 9210
9070 G2$="VEGA":GOTO 9210
9080 G2$="CANOPUS":GOTO 9210
9090 G2$="ALTAIR":GOTO 9210
9100 G2$="SAGITTARIUS":GOTO 9210
9110 G2$="POLLUX":GOTO 9210
9120 ON Z4 GOTO 9130,9140,9150,9160,9170,9180,9190,9200
9130 G2$="SIRIUS":GOTO 9210
9140 G2$="DENEB":GOTO 9210
9150 G2$="CAPELLA":GOTO 9210
9160 G2$="BETELGEUSE":GOTO 9210
9170 G2$="ALDEBARAN":GOTO 9210
9180 G2$="REGULUS":GOTO 9210
9190 G2$="ARCTURUS":GOTO 9210
9200 G2$="SPICA"
9210 IF G5<>1THEN ON Z5 GOTO 9230,9240,9250,9260,9230,9240,9250,9260
9220 RETURN
9230 G2$=G2$+" I":RETURN
9240 G2$=G2$+" II":RETURN
9250 G2$=G2$+" III":RETURN
9260 G2$=G2$+" IV":RETURN
9270 PEN #77FF77:PRINT "  4   3   2"
9280 PRINT "   \  |  /"
9290 PRINT "    \ | /"
9300 PRINT "  5 ----- 1"
9310 PRINT "    / | \"
9320 PRINT "   /  |  \"
9330 PRINT "  6   7   8": PEN #22FF22
9340 RETURN
9350 FUNCTION FND(D) ' D NOT NEED  
9360 =SQR((K(I,1)-S1)^2+(K(I,2)-S2)^2)
9370 END FUNCTION
}
try {startrek}
Set switches "-dim -for"
IF MODULE(INFO) THEN KEYBOARD "INFO"+CHR$(13)