Contents

Programming Tutorials

This is the imeight programming language. One that resembles those 80s languages when programming was still fun to learn. Do you want to experience, too?

This emulated imaginary machine has these screen modes:

You can use the usual Copy and Paste function in your browser to transfer example codes from the tutorial to the Program Listing panel.

For a test run, press > RUNNER (F9) then see RUN command appear in the Runner Screen, press Enter.

Return to the Program Listing by entering the LIST command.

NEW command will also erase the program text.

Press Esc button for long to kill a running program.

Contents

Programming Instruction by Instruction

Details of the Language

Game Programming Tutorial

Reference Sheet Index

NamesExpressionsOperator SummaryCommand SummaryAbbreviations
Run Time ErrorsColor CodesFactory Sound EffectsBuilt-in Variables

Fun Projects

1234

Programming Instruction by Instruction

Contents

PRINTINPUTNamesLETREM, The Colon, Spaces: Your Readable CodeExpressionsGOTOIF and THENOperator SummaryGOSUB and RETURNEND and STOPCommand SummaryFOR and NEXT

PRINT

PRINT "HELLO WORLD" REMARK: The PRINT instruction is for learning and debugging purposes only. REMARK: See "HELLO WORLD" printed as a result.

An Infix Operator: + Between Numbers

PRINT 6+4 REMARK: See 10 printed on the Runner Screen. REMARK: + is an infix operator and works like in maths. REMARK: There are more operators to be covered later.

A Function: SIN

PRINT SIN(1.571) REMARK: 1.571 is approximately 90 degrees in radians. REMARK: So this will print a number close to 1, sine of 90 degrees. REMARK: You must not leave white space between SIN and the parenthesis.

INPUT

Full Syntax

INPUT "HOW MUCH?", A REMARK: Again an instruction for learning purposes only. REMARK: Displays the question "HOW MUCH?". User may enter the value.

Default Prompt

INPUT A REMARK: A shorter version of the INPUT instruction. REMARK: Also asks the user but the question is generated by the runner.

The Normal Program Flow

INPUT A PRINT A INPUT B PRINT B

Now we wrote a program of multiple lines.

The topmost line will run first. Program waits for input.

Then the 2nd line runs so the value of the variable A is printed.

See that whatever is entered is printed. It's because INPUT instruction has assigned the entered value to the variable A, which kept that value when printed.

Third and fourth line does the same with another variable, B. INPUT B happens after printing A, as if you would read a story.

Assignment: Old Value, New Value

INPUT A PRINT A INPUT A PRINT A

In the previous example A kept its value. Now here is an example how it can drop the value.

Read the story.

1st line prompts the user and assigns the entered value to A. Variable A will keep it for the next line. To prove it, we do a PRINT A in the 2nd line.

In the 3rd line INPUT will assign again. Which means, the old value is replaced by the new. See what gets printed in the 4th line: the new value of A, regardless of its previous value.

Fun Project: PRINT

Write a program with a lot of PRINT instructions to make beautiful ASCII art. Wanna see my submission?

PRINT " , _____ , " PRINT " /,!\ (_ _/_______ /TZ)" PRINT " ` V/\__ _ / / / / / _ \ _ __/\/" PRINT " _(LL||TTT ) / / / / / (/ / ( TTT||JJ)_" PRINT " \, \) (_/\____/\___/ (/ _/" PRINT " \__ /_" PRINT " _______ / /_______________/ /" PRINT " (_ / , \/ _ ) ,_/ _ / __/ /" PRINT " / __/ __/ (/ / / / (/ /_ /_/" PRINT " /___/\___/\___/_/ \__,_\__(_)"

The key take-away from this project task was that the lines appear on the Runner Screen the same order as in the Program Listing, which we call the normal program flow. Another take-away is that the writer of this paper has substandard drawing skills.

I have a forum topic for you in case you want to tell the world how you accomplished this task. Time to contact fellow imeight programmers!

Names

In the INPUT examples we could see that there are variables named A and B, which help us store numbers. But you can name them relatively freely. Let's see the exact rules for naming variables:

  1. A variable name must start with a letter A through Z. It may have a longer name.
    INPUT NUM
  2. From the second position on, the name may contain dots (.), digits 0 to 9 or more letters, in any order.
    INPUT NUMBER2STORE INPUT INCOME2020
  3. There is no length limit for the name.
    INPUT THERE.IS.NO.DEFINED.LENGTH.LIMIT.FOR.THE.VARIABLE.NAMES
  4. The name optionally ends in one $ or one % sign. Convention is that the name ends in $ if it is supposed to have a text value rather than number. The % sign suggests integer number value.
    INPUT N% INPUT TEXT$

These rules not only apply to variable names, but also function names and array names, which are covered later.

LET

LET SUM = 6+4 PRINT SUM

The first line will assign a calculated value, 10, to the variable SUM.

Second line shows that the variable keeps the value.

This way you can give a name to your calculation results, making your intent clear and the code maintainable.

Implicit LET Instruction

SUM = 6+4 PRINT SUM

You can omit the instruction LET and it will still be interpreted as an assignment.

Note: Line 1 is not an equation. The left side must specify the thing to assign to (variable A this time.) It is an error to swap the two sides of the assignment, putting numbers or operators to the left side.

If your variable name starts with an instruction (e.g. PRINTED, LETTER$) then the LET keyword is necessary.

Motivation

SUM = 1+1+1+1+1+1+1+1+1+1 LITTLEBIGGER = SUM+1 MUCHBIGGER = SUM+10 PRINT SUM PRINT LITTLEBIGGER PRINT MUCHBIGGER

Let's pretend that calculating SUM takes a lot of time and effort. This is how you reuse a calculation. The SUM variable keeps its value and is used for calculating two more values. So variables are not only there for code readability, they can make your code more efficient, too.

Referencing The Old Value In LET

INPUT A A = A+1 PRINT A

2nd line says, add 1 to the old value of A, and let the incremented value be the new value of A. So the 3rd line will print the successor of whatever the user enters.

REM, The Colon, Spaces: Your Readable Code

You can make your code self documented by inserting REM instrucions. They make the runner ignore the rest of the line.

REM * USER WILL ENTER A NUMBER * INPUT A REM * COMPUTER WILL INCREMENT THE NUMBER * A = A+1 REM * AND NOW DISPLAY THE RESULT * PRINT A

You can use this instruction to temporarily skip a line or two while developing your program:

PRINT "HELLO" REM PRINT "GOODBYE" REM * I HAVE COMMENTED OUT THE SECOND LINE * REM * RUNS LIKE IT WAS DELETED BUT * REM * MAYBE I WILL WANT TO UNCOMMENT LATER *

You can normally put several instructions on a line, separated by colon:

PRINT "HELLO":PRINT "WORLD":REM BOTH WILL PRINT

But the colon does not break the REM instruction:

PRINT "THIS PRINTS":REM HERE IS A REMARK:PRINT "PART OF REM, NOT PRINTING"

The REMARK lines in the examples before are also REM instructions. The space after the REM or any instruction is optional, so the text starting with ARK had been ignored by the runner. But you should keep your code readable, which means spaces are good for you.

Expressions

A$ = "THIS TEXT BETWEEN QUOTES IS A LITERAL, A SIMPLE EXPRESSION." A = 30:REM THE NUMBER IS ALSO A LITERAL, WHICH IS AN EXPRESSION SUM = A+1:REM A CALCULATION WITH AN OPERATOR IS AN EXPRESSION PRINT A+1:REM EXPRESSION MAY APPEAR ON = RIGHT SIDE OR PRINT ARGUMENT PRINT A$:REM VARIABLE NAME IS EXPRESSION IF ON = RIGHT SIDE OR IS PRINT ARG INPUT A$, C$:REM OR ARGUMENT TO INSTRUCTIONS OTHER THAN PRINT Y = SIN(1 + 0.571):REM FUNCTIONS WITH OPERATORS BUILD EXPRESSIONS, TOO Z = SIN(4.712)+2:REM COMBINE THEM FREELY LIKE IN MATHS

String Expressions

(A) The String Literal

A$ = "HELLO WORLD" PRINT "HELLO WORLD" PRINT A$ REM * THE QUOTED "HELLO WORLD" IS CALLED LITERAL * REM * BECAUSE IT ASSIGNS, PRINTS "HELLO WORLD" LITERALLY * REM * WHEREAS A$ IS NOT A LITERAL BECAUSE * REM * IT PRINTS "HELLO WORLD" RATHER THAN "A$" LITERALLY *

(B) Concatenation

PRINT "HELLO" + "WORLD" REM * PLUS BETWEEN STRING LITERALS MEANS CONCATENATION * REM * PRINTS "WORLD" IMMEDIATELY FOLLOWING "HELLO" * GREETING$ = "HELLO" AUDIENCE$ = "WORLD" PRINT GREETING$ + " " + AUDIENCE$ REM * CONCATENATION OF STRING VARIABLES AND LITERALS ALSO WORKS *

(C) String Functions

PRINT LEN("HOW LONG AM I"):REM PRINTS 13 (10 LETTERS + 3 SPACES) PRINT "1" + "2":REM DOES NOT PRINT 3 BUT A CONCATENATED STRING "12" PRINT VAL("1") + VAL("2"):REM PRINTS 3 PRINT ASC("A"):REM PRINTS THE ASCII CODE 65 PRINT CHR$(65):REM PRINTS "A" BEACUSE ITS ASCII CODE IS 65 PRINT LEFT$("HELLO", 3):REM PRINTS "HEL" - 3 CHARACTERS FROM THE LEFT PRINT RIGHT$("HELLO", 3):REM PRINTS "LLO" - 3 CHARACTERS FROM THE RIGHT PRINT MID$("HELLO", 2, 3):REM PRINTS "ELL" - 3 CHARACTERS FROM THE 2ND POS REM * THE COMMA IS AN OPERATOR HERE THAT MAKES A PARAMETER LIST * REM * IT'S ON THE LOWEST PRECEDENCE LEVEL AMONG ALL THE OPERATORS *

Numeric Expressions

Examples below are for your reference, not error free programs to run.

(A) Literals

MOL = 6.022E+23:REM E MEANS "TIMES 10 TO THE POWER" A = 10:REM DECIMAL 10, REALLY TEN B = %10:REM BINARY 10, MEANING TWO C = $10:REM HEXADECIMAL 10, MEANING SIXTEEN D = !10:REM FOUR-BASE 10, MEANING FOUR E = 1.2:REM 1 AND 2 TENTHS F = .2:REM 2 TENTHS G = .2E-2:REM 0.002

(B) Operators

C = A+B:REM ADD C = A-B:REM SUBTRACT C = A*B:REM MULTIPLY C = A/B:REM DIVIDE C = A MOD B:REM REMAINDER OF AN INTEGER DIVISION C = A^B:REM POWER C = A AND B:REM BITWISE LOGICAL AND; SPACE IS NECESSARY AFTER A C = A XOR B:REM BITWISE LOGICAL EXCLUSIVE OR; SPACE IS NECESSARY AFTER A C = A OR B:REM BITWISE LOGICAL OR; SPACE IS NECESSARY AFTER A

(C) Functions

REM * SOME INTERESTING THINGS THAT TECHNICALLY ARE FUNCTIONS * Y = -X:REM UNARY MINUS, MEANING (-1)*X Y = A*(B+C):REM THE () IS A FUNCTION WITHOUT A NAME: RETURNS WHAT IS INSIDE REM * BASIC MATHS * Y = SGN(X):REM -1 FOR A NEGATIVE X, 0 FOR 0, 1 FOR A POSITIVE X Y = INT(X):REM INTEGER PART OF X, BUT STAYS FLOATING POINT Y = FRAC(X):REM FRACTIONAL PART OF X Y = ABS(X):REM ABSOLUTE VALUE: -X IF X<0, OTHERWISE X Y = SQR(X):REM SQUARE ROOT REM * TRANSCENDENT * Y = LOG(X):REM NATURAL LOGARITHM Y = EXP(X):REM NATURAL EXPONENTIAL FUNCTION REM * TRIGONOMETRY * Y = COS(X):REM COSINE Y = SIN(X):REM SINE Y = TAN(X):REM TANGENT Y = ATN(X):REM ARC TANGENT REM * COMPUTING * Y = NOT(X):REM BITWISE LOGICAL NEGATION Y = RND(X):REM NON-NEGATIVE RANDOM NUMBER THAT IS LESS THAN X Y = PEEK(X):REM THE BYTE STORED AT ADDRESS X; SPOILER: USE POKE INSTRUCTION Y$ = STR$(X):REM A DECIMAL REPRESENTATION OF THE NUMBER X B = INI(X):REM TEST WHETHER X IS INITIALIZED

About Evaluation

PRINT RND(8)-RND(8):REM RND EVALUATED TWICE, CAN RETURN DIFFERENT VALUES
X = RND(8):REM EVALUATED ONCE PRINT X - X:REM ALWAYS RETURNS ZERO

Fun Project

Make an ASCII art zebra say something that the user enters.

INPUT "WHAT DOES THE ZEBRA SAY?", MSG$ PRINT " ,, ____________" PRINT " /oo)___/" + LEFT$(MSG$ + " ", 12) + "\" PRINT " _ __/=/ \____________/" PRINT " ( TTT||JJ) " PRINT " || || "

What expressions can you find in my code?

Do you have a better version? Remember the forum topic where you can share it?

GOTO

Flow instructions can break the normal flow of the program. GOTO instructs the runner to immediately go to a specified point in the program, rather than continuing at the next one in reading order. Using the @ instruction you can put a label to a point in the program and reference that label in a GOTO argument.

Jumping over a part of the program:

PRINT "TITLE" PRINT "INTRODUCTION" GOTO RESULTS REM NOONE CARES ABOUT CALCULATIONS PRINT "CALCULATIONS" @RESULTS PRINT "RESULTS"

Jumping back makes an endless loop:

@LOOP INPUT A GOTO LOOP REM * THIS WILL RUN UNTIL USER HITS ESC BUTTON *

Jumping wildly:

PRINT "THEY SAY GOTO IS A CRIME" GOTO INTERMEZZO @CONTINUATION PRINT "CAN WE CONCLUDE, GOTO IS FUN?" GOTO FIN @INTERMEZZO PRINT "I SAY GOTO IS FINE" GOTO CONTINUATION @FIN

Note: there is a built-in label called START, which points to the beginning of the program. Therefore:

IF and THEN

Logical Operators

INPUT A REM * EQUAL AND UNEQUAL SIGNS ARE OPERATORS, TOO: * REM * THEY APPEAR BETWEEN TWO NUMERIC EXPRESSIONS * REM * AND GIVE 1 AS RESULT IF TRUE, 0 IF FALSE * IF A=1 THEN PRINT "A IS ONE" IF A<>2 THEN PRINT "A IS NOT TWO" IF A<10 THEN PRINT "A IS LESS THAN TEN" IF A>5 THEN PRINT "A IS GREATER THAN FIVE" IF A<=8 THEN PRINT "A IS NOT GREATER THAN EIGHT" IF A>=7 THEN PRINT "A IS NOT LESS THAN SEVEN" REM * AND IS AN OPERATOR, JUST LIKE OR * IF A>=4 AND A<=6 THEN PRINT "A IS BETWEEN FOUR AND SIX" IF A<3 OR A>9 THEN PRINT "A IS NOT BETWEEN THREE AND NINE"

Mixing Logic and Numbers

INPUT "PLEASE ENTER 1 HERE.", A IF 1-A THEN PRINT "BAD" IF 1-A<>0 THEN PRINT "BAD" IF 1<>A THEN PRINT "BAD" REM * IF USER DOES NOT ENTER 1 THEN "BAD" APPEARS THREE TIMES * REM * IF USER ENTERS 1 THEN PROGRAM ENDS WITHOUT PRINTING *

Pitfalls

Operators AND, OR work bitwise.

As long as the two sides of them are either 0 or 1, they can be used to combine conditions.

Comparison operators like < and > and = will give either 0 or 1 to support such combinations.

However, NOT function is bitwise and may have astonishing result.

INPUT A:INPUT B IF NOT(A=B) THEN PRINT "A IS NOT B?" REM * ENTER EQUAL NUMBERS * REM * A=B GIVES 1 (TRUE). NOT(A=B) GIVES ALL BITS BUT ONE SET, -2 * REM * IT IS NOT 0, CONDITION IS MET, MESSAGE WILL BE PRINTED *
PRINT 0=0=0:REM 0 (FALSE) BECAUSE 0=0 GIVES 1 (TRUE), 1=0 GIVES 0 (FALSE) PRINT 0=0=1:REM 1 (TRUE) BECAUSE 0=0 GIVES 1 (TRUE), 1=1 GIVES 1 (TRUE)

Colon After THEN

@TASK A = RND(10) B = RND(10) @ASK INPUT A + " + " + B, C IF C = A+B THEN PRINT "CORRECT":GOTO TASK PRINT "TRY AGAIN":GOTO ASK

If the user input is correct then the program will not only PRINT "CORRECT" but also GOTO TASK. If the condition is not met then the rest of the line is skipped, even if it contains several instructions separated with colon.

Fun Project

Have you noticed that you know everything you need to create a text adventure game? Feel free to change or add more options – and then share your work!

BOREDOM=0:MONEY=0:GF=0 PRINT "----------- TEXT ADVENTURE GAME -----------" PRINT "------ CLICK HERE AND PRINT ALL CAPS ------" @HOME PRINT "YOU ARE AT HOME. LIVING ALONE FEELS LONELY" PRINT "SOMETIMES... BUT YOU CAN JUMP ON YOUR BED" PRINT "IF YOU LIKE." BOREDOM=BOREDOM+1 IF BOREDOM>10 THEN GOTO DEATH IF BOREDOM<>3 THEN GOTO HOMEASK PRINT "YOU STAND UP ON THE BED TO JUMP... YOU FEEL" PRINT "SOMETHING PUSHING YOUR FOOT..." PRINT "HEY! YOU FOUND A COIN BELOW THE BEDSHEET!" MONEY=1 @HOMEASK INPUT "STAY OR LEAVE?", MOVE$ IF MOVE$="STAY" THEN GOTO HOME IF MOVE$="LEAVE" THEN GOTO STREET GOTO HOMEASK @DEATH PRINT "LIFE IS SO BORING THAT YOU DIED. GRAY RATS" PRINT "WHO JUST HAD BEEN LINGERING THERE" PRINT "ARE LISTLESSLY EATING YOUR BODY." PRINT "------------------- END -------------------" END @STREET PRINT "FRESH AIR! THE STREET IS CROWDED AS USUAL." PRINT "YOU STAND JUST IN FRONT OF YOUR HOME" IF BOREDOM>3 AND MONEY=0 THEN GOTO SELL @STREETASK INPUT "WHERE WOULD YOU GO? CENTER, OUTSKIRTS, HOME?", MOVE$ IF MOVE$="CENTER" THEN GOTO CENTER IF LEFT$(MOVE$, 3)="OUT" THEN GOTO OUTSKIRTS IF MOVE$="HOME" THEN GOTO HOME GOTO STREETASK @CENTER PRINT "MALLS AND RESTAURANTS ALL AROUND." PRINT "YOU FEEL LIKE SHOPPING!" IF MONEY=0 THEN PRINT "WISH YOU HAD ANY MONEY :(":GOTO CENTERASK IF GF=1 THEN GOTO FLOWERS PRINT "YOU BOUGHT A GOLDEN PIGLET. IT'S A PERFECT" PRINT "PRESENT... YOU JUST NEED SOMEBODY TO GIVE IT." GOTO BOUGHT @FLOWERS PRINT "YOU BOUGHT SOME FLOWERS FOR YOUR GF." PRINT "IT'S SO NICE TO HAVE HER." PRINT "------------------- END -------------------" END @BOUGHT MONEY=0 @CENTERASK INPUT "STAY OR STREET?", MOVE$ IF MOVE$="STAY" THEN GOTO CENTER IF MOVE$="STREET" THEN GOTO STREET GOTO CENTERASK @OUTSKIRTS IF GF=1 THEN GOTO OUTASK PRINT "YOU REMEMBER A GIRL YOU USED TO KNOW FROM" PRINT "SCHOOL. MAYBE IT'S BECAUSE SHE LIVED HERE. OR" PRINT "MAYBE IT'S BECAUSE YOU ALWAYS THINK OF HER" PRINT "EVER SINCE." @OUTASK PRINT "YOU STAND RIGHT AT THE GIRL'S DOOR." INPUT "KNOCK OR STREET?", MOVE$ IF MOVE$="KNOCK" THEN GOTO KNOCK IF MOVE$="STREET" THEN GOTO STREET GOTO OUTASK @KNOCK IF GF=1 THEN GOTO LOVE PRINT "'HEY, IS THAT REALLY YOU? I REMEMBER" PRINT "THE SHY GUY FROM SCHOOL. HAHA!'" PRINT "YOU TALK A LITTLE. SHE ASKS ABOUT YOUR LIFE." IF MONEY=0 THEN GOTO DECLINE GF=1 PRINT "SHE TELLS YOU THAT SHE LIKES FLOWERS." PRINT "SHE FEELS ALONE. SHE LIKES TO JUMP ON HER BED." PRINT "SHE SHOWS HOW." @LOVE PRINT "YOU STAY UNTIL THE MORNING COMES." @DECLINE PRINT "SHE ASKS YOU TO LEAVE AS SHE HAS" PRINT "IMPORTANT THINGS TO DO." GOTO OUTASK @SELL PRINT "A BAD LOOKING MAN IS APPROACHING." PRINT "HE WANTS TO SELL YOU STOLEN WEAPONS." PRINT "HE WOULD BUY THINGS OF VALUE, TOO." PRINT "YOU THINK OF SELLING THE PIGLET." INPUT "SELL OR LEAVE?", MOVE$ IF MOVE$="SELL" THEN MONEY=1:PRINT "YOU GET A COIN." GOTO STREET

Operator Summary

Precedence

REM ^ LEVEL 1 PRINT 2 * 3^2:REM 18 PRINT (2*3)^2:REM 36 REM / * MOD LEVEL 2 PRINT 1 + 2*3:REM 7 PRINT (1+2)*3:REM 9 REM + - LEVEL 3 PRINT 6 < 10+2:REM 1 PRINT (6<10)+2:REM 3 REM <> >= > <= < LEVEL 4 PRINT 2 = 4<>3:REM 0 PRINT (2=4)<>3:REM 1 REM = LEVEL 5 PRINT 1 AND 3 = 1:REM 0 PRINT (1 AND 3)=1:REM 1 REM AND LEVEL 6 PRINT 1 OR 3 AND 2:REM 3 PRINT (1 OR 3)AND 2:REM 2 REM OR XOR LEVEL 7 PRINT 1, 2 OR 4:REM [1, 6] REM , LEVEL 8
REM * OTHER OPERATORS ON THE SAME PRECEDENCE LEVELS * PRINT 18 / 3^2:REM 2 PRINT 6 + 4/2:REM 8 PRINT 6 - 4/2:REM 4 PRINT 6 < 10-2:REM 1 PRINT 9 > 10-2:REM 1 PRINT 8 <= 10-2:REM 1 PRINT 8 >= 10-2:REM 1 PRINT 1 = 4>3:REM 1 PRINT 3 = 3<2:REM 0 PRINT 1 = 4>=4:REM 1 PRINT 0 = 1<=1:REM 0

Associativity

REM * ALL OPERATORS LEFT-ASSOCIATIVE (INCLUDING POWER, UNLIKE IN MATHS) * PRINT 2^3^2:REM 64 PRINT 2^(3^2):REM 512 PRINT 100/20/5:REM 1 PRINT 100/(20/5):REM 25 PRINT 100-20-5:REM 75 PRINT 100-(20-5):REM 85 PRINT 3>2>1:REM 0 (FALSE) BECAUSE 3>2 GIVES 1 (TRUE), 1>1 GIVES 0 (FALSE) PRINT 3>(2>1):REM 1 (TRUE) BECAUSE 2>1 GIVES 1 (TRUE), 3>1 GIVES 1 (TRUE)

The Unary Minus Sign

REM * UNARY MINUS HAS LOWER PRECEDENCE THAN POWER * PRINT -2^2:REM -4 PRINT (-2)^2:REM 4 REM * UNARY MINUS HAS HIGHER PRECEDENCE THAN ADD OR SUBTRACT * PRINT -2+2:REM 0 PRINT -(2+2):REM -4

GOSUB and RETURN

PRINT "VERSE 1" GOSUB CHORUS:REM GO TO SUBPROGRAM AT @CHORUS BUT COME BACK WHEN IT'S DONE PRINT "VERSE 2" GOSUB CHORUS:REM NOTE THAT THE SAME SUBPROGRAM CAN RETURN HERE, TOO GOTO OUTRO @CHORUS: PRINT "CHORUS" RETURN:REM SUB IS OVER, GO BACK TO THE POINT AFTER GOSUB (THE RIGHT GOSUB) @OUTRO: PRINT "OUTRO"

RETURN from...

Alternative RETURN syntax has a label argument. In case the program would run to such a RETURN instruction by accident, not because a GOSUB deliberately jumped there, it would stop the program with error rather than continuing the wrong flow.

PRINT "VERSE 1" GOSUB CHORUS PRINT "VERSE 2" GOSUB CHORUS GOTO OUTRO @CHORUS: PRINT "CHORUS" RETURN CHORUS:REM MAKES SURE IT RETURNS TO A GOSUB CHORUS INSTRUCTION @OUTRO: PRINT "OUTRO"

END and STOP

PRINT "VERSE 1" GOSUB CHORUS PRINT "VERSE 2" GOSUB CHORUS PRINT "OUTRO" END:REM PREVENTS RUNNING THE CHORUS AGAIN @CHORUS: PRINT "CHORUS" RETURN
PRINT "STEP 1" STOP PRINT "STEP 2" STOP PRINT "STEP 3" REM * ENTER INTERACTIVE COMMAND CONT TO CONTINUE * REM * ENTER INTERACTIVE COMMAND RUN TO START OVER * REM * ?STATUS TO TELL STOP (1) FROM END (0) OR ERROR (-1) *

Command Summary

Here are all the interactive commands that you can use on the Runner Screen:

FOR and NEXT

Wanna do something 10 times?

FOR I=1 TO 10:PRINT "THIS OLD MAN, HE PLAYED " + I:NEXT

What more can FOR loops do?

FOR I=0 TO 9 A$ = " " REM * ANOTHER LOOP INSIDE THE LOOP * REM * THE END OF THE LOOP IS AN EXPRESSION * REM * THE COUNTER J WILL STEP DOWN * FOR J=9 TO 9-I STEP -1 A$ = A$ + J NEXT PRINT A$ NEXT I REM * THIS SYNTAX OF NEXT MAKES SURE THAT IT ENDS THE CORRECT LOOP: IF * REM * THE PROGRAM FLOW ACCIDENTALLY HITS IT WHEN THERE IS NO LOOP FOR I * REM * THEN INSTEAD OF CONTINUING THE WRONG FLOW, IT IMMEDIATELY STOPS *
FOR I=10 TO 45 STEP 10:PRINT I:NEXT PRINT "LOOP EXITED WITH " + I REM I NEVER GETS EXACTLY 45, LAST CYCLE STARTS WITH I=40, EXITS WITH I=50
REM * BREAKING THE LOOP EARLY * INPUT "SELL YOUR STOCKS AFTER HOW MANY DAYS?", CUT FOR I=1 TO RND(30) PRINT "DAY "+I PRINT "STOCKS HIGH":PRICE=I IF I=CUT THEN I=30:NEXT:GOTO OUT PRICE=1:PRINT "STOCKS LOW" NEXT @OUT PRINT "STOCKS SOLD FOR "+PRICE

Evaluations:

When breaking a loop in an IF condition, make sure:

  1. that the loop counter is over the TO value – I=30
  2. to say NEXT so that the loop is cleaned up – NEXT
  3. to jump over all the other NEXT instructions – GOTO OUT

Details of the Language

OK, the essentials are now done. You can jump to Game Programming Tutorial if you are in a hurry. You'll be able to follow, but some particular instructions you'll need to look up in this second part. There are still some details about the language, so read on to learn them all.

Contents

DATA and READRESTOREDIMPOKECLRDEFONMultidimensional ArraysNon-Unary FunctionsAbbreviationsPUSH and PULLRun Time ErrorsContrasting C64

DATA and READ

REM * READ ASSIGNS THE NEXT VALUE FROM THE DATA LINE TO ITS ARGUMENT * FOR I=1 TO 5:READ A:PRINT A:NEXT DATA 2,3,5,7,11

Evaluation order

READ A:READ B:A=9:READ C PRINT A,B,C DATA RND(8) DATA A, A + 1

On READ A, first data line is evaluated, a random number gets generated.

On READ B, second data line evaluates (both elements of it at once.) Now variable A holds the randomized number, so B gets the same value as A.

Then A=9 assignment happens. C gets the already evaluated value, which is the random number plus 1 (the new value of A, 9, does not affect C's.)

RESTORE

Restore to the beginning

GOSUB POEM PRINT "*" GOSUB POEM END @POEM RESTORE REM * RESTORE INSTRUCTION MAKES SURE THE POEM STARTS WITH THE FIRST LINE * REM * NO MATTER HOW MANY TIMES THE PROGRAM RUNS THE SUBPROGRAM * FOR LINE=1 TO 5:READ L$:PRINT L$:NEXT LINE RETURN DATA "THERE WAS AN OLD MAN WITH A BEARD," DATA "WHO SAID, 'IT IS JUST AS I FEARED!" DATA "TWO OWLS AND A HEN,", "FOUR LARKS AND A WREN," DATA "HAVE ALL BUILT THEIR NESTS IN MY BEARD!'"

Restore to a label

RESTORE WEEKEND FOR I=1 TO 14 READ DAY$:PRINT DAY$ + " " + I IF DAY$ = "SUN" THEN RESTORE NEXT END DATA "MON", "TUE", "WED", "THU", "FRI" @WEEKEND: DATA "SAT", "SUN"

DIM

Define an indexed array. An array is like a number of variables because it has elements that hold a value each. But unlike variables, you can refer to the elements using an index. This enables e.g. traversing the array in a FOR loop, which you cannot do with individually named variables.

REM * BUBBLE SORT * DIM ARRAY(10) INPUT ARRAY(0) FOR I=1 TO 9 READ ARRAY(I) NEXT FOR K=8 TO 0 STEP -1 FOR I=0 TO K IF ARRAY(I)>ARRAY(I + 1) THEN NEXT I:GOTO OUT SWAP = ARRAY(I) ARRAY(I) = ARRAY(I + 1) ARRAY(I + 1) = SWAP NEXT I @OUT PRINT ARRAY(I) NEXT K PRINT ARRAY(0) END DATA 1, 2, 3, 5, 9, 8, 7, 4, 6

Note: DIM can also erase an already existing array.

POKE

Store a byte in the memory by address.

POKE 5000,6 REM 5000 IS THE ADDRESS, 6 IS THE BYTE PRINT PEEK(5000)

CLR

Clear all variables and arrays. Built-in variables restored.

PI = 3.0 PRINT PI INPUT "PRESS ENTER TO CONTINUE ", A CLR PRINT PI REM BUILT-IN VARIABLE WITH THE INITIAL VALUE 3.141592653589793

Bytes in memory survive a CLR.

POKE 5000,6 CLR PRINT PEEK(5000):REM 6

DEF

Define a function. Code repetition is a source of errors. Here is a means of avoiding it.

LOG2 = LOG(2) DEF LOG2(X) = LOG(X)/LOG2 X=10 PRINT LOG2(32) PRINT X

CLR will erase user-defined functions, too.

DEF TWICE(N) = 2*N DIM ARRAY(10) CLR DIM TWICE(10):REM NOT AN OVERDEFINITION DEF ARRAY(N) = 2*N:REM NOT AN OVERDEFINITION

ON

With IF instruction, you gave your program two possible continuation paths: one to go when the condition is true, one if it is false. What if you want three or more continuation paths, depending on a numeric expression?

INPUT "CHOOSE FROM MENU 1-3", CHOICE ON CHOICE GOTO M1, M2, M3 @M1: PRINT "YOU CHOSE #1" END @M2: PRINT "YOU CHOSE #2" END @M3: PRINT "YOU DIDN'T CHOOSE 1 OR 2 BUT SOMETHING ELSE" END
INPUT "CHOOSE FROM MENU 1-3", CHOICE ON CHOICE GOSUB M1, M2, M3 GOTO START @M1: PRINT "YOU CHOSE #1" RETURN @M2: PRINT "YOU CHOSE #2" RETURN @M3: PRINT "YOU DIDN'T CHOOSE 1 OR 2 BUT SOMETHING ELSE" RETURN

A trick to save a line of code:

INPUT N ON N>0 GOTO POSITIVE, NONPOSITIVE:REM IF N>0 IS TRUE, BRANCH 1 IS SELECTED @POSITIVE:PRINT "POSITIVE":END @NONPOSITIVE:PRINT "NONPOSITIVE":END
INPUT N IF N>0 THEN GOTO POSITIVE GOTO NONPOSITIVE:REM THIS NEEDS TO BE ON A SEPARATE LINE @POSITIVE:PRINT "POSITIVE":END @NONPOSITIVE:PRINT "NONPOSITIVE":END

Multidimensional Arrays

You may use the comma operator in the array index to create a matrix.

DIM MANCALA(6,2) MANCALA(4,1) = 0 MANCALA(5,1) = MANCALA(4,1)+1

Non-Unary Functions

Nullary

D12 = RND(12)+1 PRINT D12 REM * GIVES 12 JUST AS OFTEN AS 7 * INPUT XX CLR D12 = RND(6)+RND(6)+2 REM * RANDOM WITH THE DISTRIBUTION OF ROLLING 2 REGULAR DICE * PRINT D12 PRINT D12 PRINT D12 REM * EVALUATION HAPPENS ONCE, SAME NUMBER IS PRINTED 3 TIMES * INPUT XX CLR DEF D12()=RND(6)+RND(6)+2 REM * NULLARY FUNCTION * PRINT D12() PRINT D12() PRINT D12() REM * ROLLS THREE TIMES *

Higher Arities

DEF MAX(A,B)=(A>=B)*A+(A<B)*B PRINT MAX(-3,MAX(5/2,8/4))

Abbreviations

REM * T. IS SHORT FOR THEN * REM * G. IS SHORT FOR GOTO * REM * N. IS SHORT FOR NEXT * REM * R. STANDS FOR RETURN * REM * W. (COVERED LATER) * 'OH, AND YOU CAN USE APOSTROPHE INSTEAD OF REM INSTRUCTION INPUT "ENTER REQUESTED INDIAN COUNT", N 'NO NEED FOR A COLON IF N>10 T.G.HANDLER S$="" FOR I=1 TO N GOSUB INDIAN S$="S" N. END @HANDLER: PRINT "TOO MANY INDIANS" STOP @INDIAN: PRINT I+" LITTLE INDIAN"+S$ R.

PUSH and PULL

Do you know the stack data structure? You can use it like this:

PUSH 1:PUSH 2:PUSH 3 PULL C:PULL B:PULL A 'REVERSE ORDER PRINT A,B,C
REM * SEND OVER SOMETHING TO A SUB VIA STACK * PUSH "MESSAGE" GOSUB SHOW END @SHOW PULL RA 'AFTER GOSUB THE RETURN ADDRESS IS ON THE STACK TOP! PULL M$ 'HERE IS YOUR MESSAGE PUSH RA 'BEFORE RETURN, YOU NEED TO PUT BACK THE ADDRESS THAT WAS THERE PRINT M$ RETURN

PUSH and PULL may mess up running of other instructions if they are not used in pairs. Use them at your own risk.

Motivation

This technique allows recursive calls.

DIM PEG$(3) PEG$(1)=" A:4321" PEG$(2)=" B:" PEG$(3)=" C:" PRINT PEG$(1) + PEG$(2) + PEG$(3) STEP=1 N=4:SOURCE=1:VIA=3:TARGET=2:GOSUB MOVE END @MOVE IF N=0 THEN RETURN PUSH N:PUSH SOURCE:PUSH VIA:PUSH TARGET N=N-1:S=VIA:VIA=TARGET:TARGET=S:GOSUB MOVE PULL TARGET:PULL VIA:PULL SOURCE:PULL N PEG$(TARGET)=PEG$(TARGET)+RIGHT$(PEG$(SOURCE), 1) PEG$(SOURCE)=LEFT$(PEG$(SOURCE), LEN(PEG$(SOURCE))-1) M$ = STEP + ". MOVE DISC FROM " + CHR$(64+SOURCE) + " TO " PRINT M$ + CHR$(64+TARGET) + ":" + PEG$(1) + PEG$(2) + PEG$(3) STEP = STEP + 1 PUSH N:PUSH SOURCE:PUSH VIA:PUSH TARGET N=N-1:S=SOURCE:SOURCE=VIA:VIA=S:GOSUB MOVE PULL TARGET:PULL VIA:PULL SOURCE:PULL N RETURN

Run Time Errors

ILLEGAL EXPRESSION

PRINT "A"-1

NO VALUE

CLR PRINT NOSUCH
DATA XX+1, YY+1 XX=0 READ Y:REM TRIES TO EVALUATE THE WHOLE DATA LINE, INCLUDING YY+1 YY=0:REM TOO LATE
DIM A$(8) A$(1) = "" PRINT A$(1):REM FINE PRINT A$(2):REM NO VALUE REM * MAKE SURE YOU INITIALIZE ALL MEMBERS OF YOUR ARRAY BEFORE USE *
DIM A$(15) A$(1) = "V" DIM A$(25) PRINT A$(1):REM LOST IN RE-DIM

INVALID REFERENCE

CLR INPUT UNARRAY(0)
SIN(0) = 0

NEXT WITHOUT FOR

FOR I=0 TO 10 IF I=5 THEN I=10:NEXT REM * WHAT HAPPENS WHEN I=5? IF CONDITION MET, I GETS 10 * REM * NEXT ABOVE WILL TERMINATE THE LOOP AND CONTINUE HERE * REM * THE OTHER NEXT WILL ALSO RUN BUT THERE IS NO LOOP ANYMORE * NEXT

RETURN WITHOUT GOSUB

GOSUB SUB REM * NOTE: AFTER RETURNING FROM THE SUB, PROGRAM CONTINUES HERE * @SUB RETURN

LOOP MISMATCH

FOR I=1 TO 10:NEXT J

CALL MISMATCH

GOSUB SUBX END @SUBX PRINT "SUB X ENTERED" @SUBY PRINT "SUB Y ENTERED" RETURN SUBY

OVERDEFINITION

DIM SIN(10)
DEF FN(PM)=-PM DIM FN(10)
DIM ARR(10) DEF ARR(X)=-X

OUT OF DATA

REM * OOPS, OFF BY ONE * FOR I=0 TO 10:READ X:NEXT DATA 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

LABEL DOES NOT EXIST

GOTO PI
GOSUB PI
RESTORE PI

NO DATA AT LABEL

RESTORE LABEL @LABEL:PRINT "NOT A DATA INSTRUCTION" DATA 1

DIVIDE BY ZERO

A = 1/0

Contrasting C64

This programming language is inspired by C64 BASIC V2.

Inspiration means that: But not:

In other words, imeight is not what C64 was, it is what it should have been.

List of Intentional Differences

Dollar or percent sign at the end of names is a convention
A% = "HELLO" A$ = 3 REM * BOTH WORK BUT DISCOURAGED *
The full name of the variable identifies
VAR1 = 1:VAR2 = 2:REM TWO DISTINCT VARIABLES HERE PRINT VAR1,VAR2
Use spaces to separate names from operators
SCORE = 30 SC = 1:E = 2 PRINT SCORE:REM 30 PRINT SC OR E:REM 3
Dimensions in DIM is documentation of intended indexing. It does not limit the usage of the array as it is _not_ memory allocation. Programmer may use zero-based or one-based indexing, interpret DIM numbers as maximal index or number of elements... to taste.
DIM A(2) A(-1)=0 A(8)=0 A(2,1)=0 REM * ALL OF THE ABOVE IS LEGAL ALTHOUGH THE CODE IS MISLEADING *
More differences that are already mentioned:
DATA evaluationAbbreviationsConcept of labelsRESTORE to a labelRETURN from...Unassigned variables: initialization enforcedBuilt-in variablesFRAC and INI functions, MOD and XOR operators addedHexadecimal, binary, 4-based number literalsDistinguishing interactive commands from programming instructions • More sophisticated data structures internally to speed up execution

Game Programming Tutorial

OK, that was boring. Fortunately this imaginary little machine has all the necessary stuff for action game programming, too. You'll need controls, timing and graphics. Let's go!

Contents

WAIT and GETTIME and TIME$Sprites!Colors!Pixel Data AnimationsTilesText LayerMouse/Touch ControlPrioritiesMusic and SoundAssorted Tricks

WAIT and GET

WAIT instruction halts execution of your program until one of the events occur:

GET instruction assigns the key code of the key hit on the keyboard to the variable in argument. Consecutive GETs will process the key hits in chronological order.

Key codes:

SECS=10:AN=10 PRINT "ENTER "+AN+" ANIMALS IN "+SECS+" SECONDS" T$="":TICKS=0 @LOOP:GET KEY IF KEY=27 THEN PRINT "GIVEN UP?":END IF KEY<=0 THEN WAIT:TICKS=TICKS+1 IF KEY>31 AND 128>KEY THEN T$=T$+CHR$(KEY) IF KEY=13 THEN AN=AN-1:PRINT T$+" ACCEPTED, "+AN+" STILL TO GO!":T$="" IF AN=0 THEN PRINT "YOU WIN.":END IF TICKS>50 THEN SECS=SECS-1:PRINT SECS+" SECONDS LEFT...":TICKS=0 IF SECS=0 THEN PRINT "YOU LOSE.":END GOTO LOOP

Motivation

Busy loops without any WAIT in them run for times highly dependent on the configuration that runs the program.

REM * PROCESSOR HEATING CODE RUNS FOR 1 S ON MY LAPTOP, HOW ABOUT YOURS? * FOR I=0 TO 2600000:NEXT
FOR I=0 TO 50:WAIT:NEXT 'CPU IS COOLER AND PROGRAM RUNS FOR 1 SECOND

Clearing Past Events

FOR I=1 TO 500:WAIT:N. 'WAIT 10 SECS: USER MAY HIT KEYS MEANWHILE PRINT "HIT A BUTTON NOW:" @GETLOOP:WAIT:GET A 'PROGRAMMER MAY THINK THIS WAIT AND GET BELONG TOGETHER IF A=0 T.G.GETLOOP PRINT A REM * KEY CODE OF FIRST HIT SINCE PROGRAM START GETS PRINTED * REM * NOT NECESSARILY ONE HIT WHILE WAITING AT GETLOOP *
FOR I=1 TO 500:WAIT:N. @ZAP:GET A:IF A<>0 THEN GOTO ZAP 'CLEAR ALL PAST EVENTS PRINT "HIT A BUTTON NOW:" @LOOP:WAIT:GET A 'NOW THIS WILL GET A YOUNG EVENT IF A=0 T.G.LOOP PRINT A
FOR I=1 TO 500:WAIT:N. CLR 'CLEAR ALL PAST EVENTS (AND ALL VARIABLES ETC.) PRINT "HIT A BUTTON NOW:" @LOOP:WAIT:GET A IF A=0 T.G.LOOP PRINT A

Abbreviation

FOR I=1 TO 50:W.:N. 'WAIT >980 BUT <=1000 MILLISECONDS

TIME and TIME$

PRINT TIME() 'MILLISECONDS SINCE EPOCH, 1 JAN 1970 PRINT TIME$(0) 'THE EPOCH IN A HUMAN READABLE FORMAT PRINT TIME$(TIME()) 'CURRENT DATE AND TIME, UTC FOR I=0 TO 50:W.:N. 'WAIT (MORE THAN) A SECOND... PRINT TIME$() 'CURRENT DATE AND TIME, SHORTER WAY REM * LAST TWO LINES SHOULD DIFFER IN 1 SECOND *

Timing for less than 20 milliseconds:

UNTIL=TIME()+10 '10 MILLISECONDS FROM NOW @BUSY:IF TIME()<UNTIL T.G.BUSY

Time Zones

PRINT TIME$(TIME()+2*3.6E6) 'IN B TIME ZONE OFFSET PRINT TIME$(TIME()+TZO) 'IN LOCAL TIME ZONE OFFSET

Sprites!

My First Animation

SPRX(0)=180+160*COS(TIME()/600) SPRY(0)=96+60*SIN(TIME()/300) WAIT GOTO START

Finally we can and see some graphics on the Runner Screen.

SPRX and SPRY are built-in arrays. SPRX(K) for every K would mean the horizontal coordinate of the sprite K; SPRY(K) is the vertical coordinate of the same. Coordinates are in pixels, horizontal coordinate 0 is the left edge of the screen, screen is 384 pixels wide. Y coordinates go from top to bottom, screen is 216 pixels high. The sprite itself is 24 by 24 pixels.

Physical Animations

SCREENWIDTH=384:SCREENHEIGHT=216 'START IN THE MIDDLE OF THE SCREEN SPRX(0)=SCREENWIDTH/2-12:SPRY(0)=SCREENHEIGHT/2-12 @HIT 'HIT THE BALL IN A RANDOM DIRECTION SPEEDX=RND(40)/10-2:SPEEDY=RND(40)/10-2 'PIXELS PER 20 MILLISECONDS @LOOP 'ADVANCE BALL POSITION ACCORDING TO SPEED SPRX(0)=SPRX(0)+SPEEDX SPRY(0)=SPRY(0)+SPEEDY 'BOUNCE ON SCREEN EDGES IF SPRX(0) <= 0 OR SPRX(0) >= SCREENWIDTH-24 THEN SPEEDX = -SPEEDX IF SPRY(0) <= 0 OR SPRY(0) >= SCREENHEIGHT-24 THEN SPEEDY = -SPEEDY WAIT:GET A ON A>0 GOTO HIT, LOOP 'LOOP IF KEY CODE<=0; GOES TO HIT OTHERWISE

The above algorithm appears in Billiard games, Pong or Breakout.

WT=384:HT=216 DIM SPEEDX(99):DIM SPEEDY(99) N=0 @SPAWN N=N+1 SPRX(N)=0:SPRY(N)=0 SPEEDX(N)=RND(30)/10:SPEEDY(N)=0 @LOOP FOR I=1 TO N IF SPRY(I) < 0 THEN NEXT:GOTO OUT SPRX(I)=SPRX(I)+SPEEDX(I) SPRY(I)=SPRY(I)+SPEEDY(I) IF SPRX(I) <= 0 OR SPRX(I) >= WT-24 THEN SPEEDX(I) = -SPEEDX(I) 'STOP A DEAD BALL IF SPRY(I) >= HT-24 AND ABS(SPEEDY(I)) < .5 THEN SPRY(I)=-24 'BOUNCE FROM THE GROUND AND LOSE SOME ENERGY IF SPRY(I) >= HT-24 THEN SPEEDY(I) = -SPEEDY(I)*.9 'ADD GRAVITY IF SPRY(I) >= 0 THEN SPEEDY(I)=SPEEDY(I)+.1 NEXT:@OUT WAIT:GET A ON A>0 GOTO SPAWN, LOOP

Combine bouncing with gravity for an effect like in Flipper, ballistic games. A jumping character can as well be animated using gravity like above.

Note that setting a coordinate to -24 makes the sprite invisible.

Tired of Balls?

Issue the command DGNS on the Runner Screen to see the names of all built-in designs for your sprites. Use them like this:

SPRDGN(0)=DGNMAN():SPRX(0)=0:SPRY(0)=0 'MAN IS A BUILT-IN DESIGN SPRDGN(1)=DGNWALL():SPRX(1)=0:SPRY(1)=24 'SO IS WALL

How about some art?

Design your own sprites in the Design Util screen mode. You will see 227 slots for designs in the Memory Map. Just choose slots, colors, and paint using the mouse. Once done, copy the number from below the Memory Map, and set SPRDGN(K) to that number in the program.

REM ASSUMING YOU DREW SOMETHING INTO SLOT 2 IN THE DESIGNER SPRX(5)=0:SPRY(5)=0 'THE UPPER LEFT CORNER SPRDGN(5)=288 'THE SECOND DESIGN FROM THE MEMORY

Fun Project

Find one bug in the list below. Correct it and have fun.

WT=384:HT=216 'SCREEN WIDTH AND HEIGHT BORINGNESS=10 REM SPRITES REM 0 TO 9: ROAD LEFT SIDE REM 10 TO 19: ROAD RIGHT SIDE REM 20: CAR UNDER CONTROL REM 21 AND ON: SPARE LIVES DISPLAYED REM LAST: CRASHED CAR @INIT SPRX(20)=WT/2:SPRY(20)=180:SPRDGN(20)=DGNCAR() FOR I=0 TO 9 SPRX(I)=WT/4:SPRX(I+10)=WT/4*3 SPRY(I)=I*24-23:SPRY(I+10)=SPRY(I) SPRDGN(I)=DGNDUST():SPRDGN(I+10)=DGNDUST() N. FOR I=1 TO LIFE:SPRX(20+I)=0:SPRY(20+I)=I*26-25:SPRDGN(20+I)=DGNCAR():N. SPRY(21+LIFE)=0:SPRX(21+LIFE)=-24:SPRDGN(30)=DGNCAR() ROADWT=WT/4 'CURRENT ROAD WIDTH IN 2-PIXEL UNITS CARVX=0:CARVY=0 'VELOCITY COMPONENTS DIRY=2 PRINT "HIT UP ARROW TO ACCELERATE" PRINT "HIT LEFT OR RIGHT ARROW TO STEER" @CONTROL WAIT:GET A IF A=-38 THEN CARVY=CARVY+1 'UP IF CARVY<1 T.G.CONTROL 'WAIT FOR THE PLAYER TO START THE GAME IF A=37 THEN CARVX=-CARVY 'LEFT IF A=39 THEN CARVX=CARVY 'RIGHT IF A=-37 OR A=-39 THEN CARVX=0 'RELEASE SPRX(20)=SPRX(20)+CARVX 'CAR MOVES ACCORDING TO CONTROL FOR TM=1 TO CARVY SPRY(21+LIFE)=SPRY(21+LIFE)+1 'LEAVE CRASHED CAR BEHIND FOR I=0 TO 9 SPRY(I)=SPRY(I)+1:SPRY(I+10)=SPRY(I) 'MOVE ROAD DOWN ON SCREEN IF SPRY(I)<HT T.G.ROLLEND '24 RASTERS SCROLLED OUT? OLDTOP=I+1:IF OLDTOP>9 THEN OLDTOP=0 'WHICH SPRITES WERE ON SCREEN TOP? DIRY=DIRY-1 'Y COORDINATE OF WHERE THE ROAD IS HEADING IF RND(DIRY)=0 THEN DIRX=RND(WT-2*ROADWT)+ROADWT:DIRY=16 'CHANGE HEADING SPRY(I)=-23:SPRY(I+10)=-23 'REUSE THE SPRITES SCROLLED OUT IF ROADWT>40 AND RND(BORINGNESS)=0 THEN ROADWT=ROADWT-2 'LEVEL UP SPRX(I)=(DIRX-ROADWT-SPRX(OLDTOP))/DIRY+SPRX(OLDTOP) 'LINEAR STEP SPRX(I+10)=SPRX(I)+2*ROADWT @ROLLEND IF SPRY(I)<=SPRY(20)-24 OR SPRY(I)>=SPRY(20)+24 T.G.COLLIDEEND IF SPRX(20)>=SPRX(I)+24 AND SPRX(20)<=SPRX(I+10)-24 T.G.COLLIDEEND PRINT "BUMP!" LIFE=LIFE-1 IF LIFE<0 THEN PRINT "CRASH!":GOTO OVER GOSUB DIE:CARVY=1 @COLLIDEEND N.:N. GOTO CONTROL @DIE XMID=(SPRX(I)+SPRX(I+10))/2 DX=XMID/16 DY=(SPRY(20)-SPRY(21+LIFE))/16 FOR J=0 TO 15 SPRX(21+LIFE)=SPRX(21+LIFE)+DX+SIN(J*PI/8)*16 SPRY(21+LIFE)=SPRY(21+LIFE)+DY WAIT:WAIT N. REM CRASHED CAR APPEARS WHERE CAR UNDER CONTROL WAS SPRX(21+LIFE)=SPRX(20):SPRY(21+LIFE)=SPRY(20) SPRX(22+LIFE)=-24 'CAR CRASHED EARLIER STILL ON SCREEN? SPRX(20)=XMID 'THE CONTROLLED CAR KEEPS INDEX 20 R. @OVER FOR I=1 TO 200:W.:N.

Colors!

INPUT BACKGROUND 'YET ANOTHER BUILT-IN VARIABLE
Color Dark Light
black/gray 0   8  
red 1   9  
green 2   10  
yellow 3   11  
blue 4   12  
magenta 5   13  
cyan 6   14  
gray/white 7   15  

Pixel Data Animations

You can use POKE instructions to redesign the sprite while the program is running. With love for fellow coordinate geometry lovers.

DEF NEAR(A,B)=(A-B)^2<2 'DO NOT USE = TO COMPARE FLOATS SPRX(0)=180:SPRY(0)=96 'CENTER SPRDGN(0)=0 @LOOP WAIT:WAIT 'RECALCULATE ONCE PER SCREEN REFRESH X=COS(TIME()/318):Y=SIN(TIME()/318) 'MOVE X,Y IN CIRCLES OVER TIME FOR R=0 TO 23 'FOR ALL ROWS OF THE DESIGN FOR C=0 TO 23 STEP 2 'FOR EVERY 2ND COLUMN OF IT HI=0:LO=0 'HIGH AND LOW NIBBLE OF THE DESIGN BYTE FOR A=-11 TO 11 STEP 2 'PARAMETER OF THE LINE'S EQUATION IF NEAR(C, X*A+12) AND NEAR(R, Y*A+12) THEN HI=10*16 IF NEAR(C+1, X*A+12) AND NEAR(R, Y*A+12) THEN LO=10 NEXT POKE 12*R+INT(C/2),HI OR LO 'PUT PIXELS NEXT NEXT GOTO LOOP

And another animation technique here, using a set of prepared designs, swapping the design pointer of one sprite rapidly.

FOR I=0 TO 5*288-1:READ B:POKE I,B:NEXT SPRY(0)=192 F=0:X=-23 @LOOP SPRDGN(0)=INT(F)*288 SPRX(0)=X X=X+1:IF X>384 THEN X=-23 F=F+0.334:IF F>5 THEN F=0 WAIT:WAIT GOTO LOOP DATA 0,0,0,0,0,221,208,0,0,0,0,0,0,0,0,0,13,0,221,0,0,0,0,0,0,0,0,0,13,0,0,208,0,0,0,0,0,0,0,0,13,0,13,208,0,0,0,0,0,0,0,0,0,208,208,0,0,0,0,0,0,0,0,0,0,13,0,0,0,0,0,0,0,0,0,0,13,221,0,0,0,0,0,0,0,0,0,0,221,0,208,0,0,0,0,0,0,0,0,13,0,208,13,13,0,0,0,0,0,0,0,13,0,208,13,221,208,0,0,0,0,0,0,13,0,208,13,0,0,0,0,0,0,0,0,13,221,0,221,0,0,0,0,0,0,0,0,13,13,0,0,208,0,0,0,0,0,0,0,0,0,221,208,13,208,0,0,0,0,0,0,0,13,13,13,208,13,0,0,0,0,13,221,221,208,208,0,13,208,208,0,0,0,13,0,0,13,0,0,0,13,13,0,0,0,13,13,221,208,0,0,0,13,13,221,0,0,13,13,0,0,0,0,0,0,208,13,0,0,0,208,0,0,0,0,0,0,221,208,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DATA 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,221,208,0,0,0,0,0,0,0,0,0,13,0,221,0,0,0,0,0,0,0,0,0,13,0,0,208,0,0,0,0,0,0,0,0,13,0,13,208,13,0,0,0,0,0,0,0,0,208,208,0,221,0,0,0,0,0,0,0,0,13,0,0,13,0,0,0,0,0,0,221,221,221,0,0,208,0,0,0,0,0,0,208,13,0,221,221,208,0,0,0,0,0,0,208,0,208,13,0,0,0,0,0,0,0,0,221,0,208,13,0,0,0,0,0,0,0,0,208,0,208,13,0,0,0,0,0,0,0,0,0,13,0,13,0,0,0,0,0,0,0,0,0,13,0,0,208,0,0,0,0,0,0,0,0,0,221,0,13,0,0,0,0,0,0,13,208,13,13,221,0,208,0,0,0,0,0,208,13,208,208,0,208,13,0,0,0,0,0,208,208,13,0,0,13,13,0,0,0,0,13,13,13,208,0,0,13,13,0,0,0,0,13,208,0,0,0,0,13,13,0,0,0,0,0,0,0,0,0,0,13,13,221,0,0,0,0,0,0,0,0,0,13,0,13,0,0,0,0,0,0,0,0,0,13,221,208,0,0,0,0,0,0,0,0,0,0,0,0,0,0 REM TOE IS AT X=15, Y=23 IN FRAME #2 DATA 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,221,208,0,0,0,0,0,0,0,0,0,13,0,221,0,0,0,0,0,0,0,0,0,13,0,0,208,0,0,0,0,0,0,0,0,13,0,13,208,0,0,0,0,0,0,0,0,0,208,208,0,0,0,0,0,0,0,0,0,0,13,0,0,0,0,0,0,0,0,0,0,0,221,0,0,0,0,0,0,0,0,0,0,13,0,221,0,0,0,0,0,0,0,0,0,13,208,13,0,0,0,0,0,0,0,0,0,208,208,13,13,0,0,0,0,0,0,0,0,208,208,13,221,208,0,0,0,0,0,0,0,13,0,13,0,0,0,0,0,0,0,0,0,13,0,13,0,0,0,0,0,0,0,0,0,0,208,0,208,0,0,0,0,0,0,0,0,0,221,0,208,0,0,0,0,0,0,0,0,0,208,208,13,0,0,0,0,0,0,0,0,0,13,13,13,0,0,0,0,0,0,0,0,13,208,13,0,208,0,0,0,0,0,0,221,208,13,221,13,0,0,0,0,0,0,0,208,13,208,208,13,0,0,0,0,0,0,0,208,208,0,208,208,0,0,0,0,0,0,0,221,0,13,0,13,0,0,0,0,0,0,0,0,0,13,221,221,0,0,0,0 DATA 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,221,208,0,0,0,0,0,0,0,0,0,13,0,221,0,0,0,0,0,0,0,0,0,13,0,0,208,0,0,0,0,0,0,0,0,13,0,13,208,0,0,0,0,0,0,0,0,0,208,208,0,0,0,0,0,0,0,0,0,0,13,0,0,0,0,0,0,0,0,0,0,0,221,0,0,0,0,0,0,0,0,0,0,13,208,221,0,0,0,0,0,0,0,0,0,13,208,13,0,0,0,0,0,0,0,0,0,13,208,13,0,0,0,0,0,0,0,0,0,13,208,13,208,0,0,0,0,0,0,0,0,13,221,221,221,0,0,0,0,0,0,0,0,13,0,13,0,0,0,0,0,0,0,0,0,0,208,0,208,0,0,0,0,0,0,0,0,0,208,0,208,0,0,0,0,0,0,0,0,0,221,0,221,0,0,0,0,0,0,0,0,0,13,0,208,208,0,0,0,0,0,0,0,221,208,0,208,208,0,0,0,0,0,0,0,208,208,13,221,0,0,0,0,0,0,0,0,221,0,208,0,0,0,0,0,0,0,0,0,221,13,208,0,0,0,0,0,0,0,0,0,221,0,13,0,0,0,0,0,0,0,0,0,13,221,221,0,0,0,0,0 REM TOE IS AT X=9, Y=23 IN FRAME #4 REM TO KEEP FOOT ON THE GROUND, MOVE THE SPRITE 3 PIXELS AHEAD PER FRAME DATA 0,0,0,0,0,221,208,0,0,0,0,0,0,0,0,0,13,0,221,0,0,0,0,0,0,0,0,0,13,0,0,208,0,0,0,0,0,0,0,0,13,0,13,208,0,0,0,0,0,0,0,0,0,208,208,0,0,0,0,0,0,0,0,0,0,13,0,0,0,0,0,0,0,0,0,0,13,221,0,0,0,0,0,0,0,0,0,0,13,208,208,0,0,0,0,0,0,0,0,0,208,208,13,0,0,0,0,0,0,0,0,0,208,208,13,0,0,0,0,0,0,0,0,0,13,208,13,208,0,0,0,0,0,0,0,0,13,221,221,221,0,0,0,0,0,0,0,0,13,0,0,208,0,0,0,0,0,0,0,0,0,221,208,13,208,0,0,0,0,0,0,0,13,13,13,208,13,0,0,0,0,0,0,0,13,13,0,13,0,208,0,0,0,0,0,0,208,208,0,13,0,208,0,0,0,0,0,0,208,208,0,208,13,0,0,0,0,0,0,13,13,0,13,13,208,0,0,0,0,0,0,13,13,0,13,0,208,0,0,0,0,0,0,208,208,0,0,208,13,0,0,0,0,0,13,0,208,0,0,13,208,0,0,0,0,0,0,208,13,0,0,0,0,0,0,0,0,0,0,13,221,0,0,0,0,0,0,0 REM NORMALLY YOU WOULD USE THE DESIGNER TO PREDESIGN ANIMATION FRAMES REM BUT IN THIS TUTORIAL, IT'S EASIER TO COPY INSTRUCTIONS THAN MEMORY

Tiles

You can put any number of sprites on screen... but it may become cumbersome to move a lot of them together. A typical example is when you have a scrolled maze, platform or background in the game. For that purpose, we have smoothly scrollable grid of tiles.

DIM MAZE(16,16) FOR RO=0 TO 15:FOR CO=0 TO 15 READ MAZE(RO,CO) N.:N. TILEX=0:TILEY=0 'BUILT-IN VARIABLE - TRANSLATES THE WHOLE GRID OF DESIGNS OX=0:OY=7 FOR X=0 TO 15:FOR Y=0 TO 8 TILE(Y,X)=-1 'BUILT-IN ARRAY OF DESIGN POINTERS IF MAZE(Y+OY,X+OX) THEN TILE(Y,X)=DGNWALL() N.:N. @TRIG:WAIT:GET A:IF A<=0 THEN GOTO TRIG REM SMOOTH SCROLLING OF TILES FOR OY=OY-1 TO 0 STEP -1 FOR X=0 TO 15:FOR Y=9 TO 1 STEP -1 TILE(Y,X)=TILE(Y-1,X) 'MOVE EVERY TILE A ROW LOWER N.:N. FOR X=0 TO 15 'GET A NEW ROW FROM THE ARRAY TILE(Y,X)=-1 IF MAZE(Y+OY,X+OX) THEN TILE(Y,X)=DGNWALL() N. TILEY=-23 'TILES ARE A ROW = 24 RASTERS LOWER THAN BEFORE, MAKE IT 1 FOR SM=0 TO 23 'SCROLL RASTER BY RASTER UNTIL A ROW GOES OFF SCREEN WAIT:WAIT TILEY=TILEY+1 NEXT NEXT TILEY=0 FOR I=0 TO 20:WAIT:WAIT:NEXT END REM GENERATED: https://sourceforge.net/p/imeight/wiki/Routines/ DATA 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 DATA 1,0,1,1,1,0,1,0,0,0,1,0,1,0,0,1 DATA 1,0,1,0,1,0,1,1,1,0,0,0,0,0,1,1 DATA 1,0,0,0,0,0,0,0,1,1,1,1,1,0,0,1 DATA 1,0,1,1,1,1,0,1,1,0,1,0,1,0,1,1 DATA 1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,1 DATA 1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1 DATA 1,1,1,0,1,0,1,0,1,0,1,0,1,0,0,1 DATA 1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1 DATA 1,0,0,1,0,1,0,1,0,1,1,1,1,0,0,1 DATA 1,1,0,1,1,1,1,1,1,1,1,0,1,0,1,1 DATA 1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1 DATA 1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1 DATA 1,1,0,1,0,1,1,0,1,1,0,0,0,1,0,1 DATA 1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1 DATA 1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1

TILE array is indexed with row and column number. If you fill it with design pointers similar to those of SPRDGN, a grid of designs will appear on the graphical screen, altogether offset by the values in variables TILEX and TILEY. Smooth scrolling means to change the TILEX or TILEY in small steps.

Text Layer

PRINT vs. TEXTLINE$

Each time a PRINT instruction runs, it prints to the text layer. It is convenient if you need to print one line below the other.

For positioning text to a certain line on the screen though is more convenient by TEXTLINE$ array. Lines visible are indexed 0 through 26, and an extra line, number 27, can be made visible by setting TEXTY=-8. TEXTLINE$ elements may be longer than the visible line, which is 48 characters: extra characters are stored in the array but won't appear on screen.

Font

Try pressing the MODE link in the Design Util. It changes the purpose of the selected memory slot. It cycles through the following modes:

Draw characters in font mode, set FONTALNUM variable to the address of the slot and see the letters and numbers appear in your design.

Font can be embedded in the program text using DATA instructions. Demo below with smooth scroll:

FOR I=0 TO 287:READ B:POKE I,B:NEXT FONTALNUM=0 'USER-DEFINED ALPHANUMERIC FONT TEXTCOLORUC=7 'APPLIES TO UPPERCASE LETTERS TEXTCOLORLC=15 'APPLIES TO CHARACTERS C$ WHERE ASC(C$) AND $20 <> 0 TEXTX=0 TEXTY=0 TEXTLINE$(13)=" HELL0" @L:FOR TEXTX=0 TO -7 STEP -1:WAIT:WAIT:NEXT TEXTLINE$(13)=RIGHT$(TEXTLINE$(13), LEN(TEXTLINE$(13))-1) IF LEN(TEXTLINE$(13)) > 0 T.G.L REM REDEFINING THE ZERO IN BINARY DATA %01111100 DATA %11000110 DATA %11001110 DATA %11010110 DATA %11100110 DATA %11000110 DATA %01111100 DATA %00000000 REM REST OF THE FONT IS A COPY OF THE DEFAULTS DATA 56,24,24,24,24,24,126,0,252,6,6,124,192,192,254,0,252,6,6,124,6,6,252,0,12,28,60,108,204,254,12,0,254,192,192,252,6,6,252,0,124,192,192,252,198,198,124,0,254,6,12,24,24,48,48,0,124,198,198,124,198,198,124,0,124,198,198,126,6,6,124,0,124,198,198,198,254,198,198,0,252,198,198,252,198,198,252,0,124,198,192,192,192,198,124,0,248,204,198,198,198,198,252,0,254,192,192,252,192,192,254,0,254,192,192,252,192,192,192,0,124,192,192,222,198,198,126,0,198,198,198,254,198,198,198,0,126,24,24,24,24,24,126,0,30,6,6,6,6,198,124,0,198,204,216,252,198,198,198,0,192,192,192,192,192,192,254,0,130,198,238,214,198,198,198,0,134,198,230,214,206,198,194,0,124,198,198,198,198,198,124,0,252,198,198,198,252,192,192,0,124,198,198,194,218,124,24,14,252,198,198,252,216,204,198,0,124,198,192,124,6,198,124,0,126,24,24,24,24,24,24,0,198,198,198,198,198,198,124,0,198,198,198,108,108,56,16,0,198,198,198,214,214,238,68,0,198,198,108,56,108,198,198,0,102,102,102,60,24,24,24,0,126,6,12,24,48,96,126,0

Mouse/Touch Control

WAIT and GET instructions also deal with mouse and touch events. WAIT stops waiting when a mouse button is pressed or released, much like in the case of a keyboard button. Instead of a key code, GET will assign 1 to the variable when a mouse button is pressed, -1 when released.

When the user touches the touch screen, WAIT stops as well. GET assigns a code greater than 255 to identify the touch point. When that same touch ends, the opposite of its code gets assigned.

Use the code that you GET to index TOUCHX and TOUCHY functions for the coordinates of each touch point. TOUCHX(1) and TOUCHY(1) consequently return the mouse position.

Both TOUCHX and TOUCHY will return -1 for codes that do not correspond to any touch when evaluated. This may happen when a drag goes off screen.

When Shift or Control or both keys are pressed while pressing the mouse button, 0.5 or 0.25 or both will be added to the code assigned by GET, in accordance with keyboard hits.

SPRX(1) = 180 SPRY(1) = 96 GRABX = -1 @L WAIT:GET EVCODE IF EVCODE = 27 THEN END IF EVCODE = 1 THEN GOSUB PRESS 'MOUSE BUTTON PRESSED IF EVCODE = -1 THEN GRABX = -1 'MOUSE BUTTON RELEASED IF GRABX <> -1 THEN GOSUB DRAG G.L @PRESS GRABX = TOUCHX(1) - SPRX(1) GRABY = TOUCHY(1) - SPRY(1) IF (GRABX - 12)^2 + (GRABY - 12)^2 > 144 THEN GRABX = -1 R. @DRAG IF TOUCHX(1) = -1 THEN GRABX = -1:R. 'OFF SCREEN SPRX(1) = TOUCHX(1) - GRABX SPRY(1) = TOUCHY(1) - GRABY R.

Priorities

Objects of the higher priority value will cover the other. Negative priorities are invisible (covered by the background color fill.)

TEXTX=4:TEXTY=0 TEXTCOLORLC=$D TEXTPRIO=4 'PRIORITY OF THE TEXT LAYER TILEX=12:TILEY=0 TILE(4,7)=DGNBALL24() TILEPRIO=3 'PRIORITY OF THE TILE LAYER SPRDGN("P")=DGNBALL10():SPRDGN("M")=DGNBALL4() T=0:U=0 FOR V=0 TO 3*PI STEP .003 T=T+.05:U=U+.02:A=COS(V) TEXTLINE$(13)=" " + INT(V) SPRX("P")=187+60*COS(U) SPRY("P")=103+A*60*SIN(U) SPRPRIO("P")=3+2*SGN(A*SIN(U)) 'PRIORITY OF SPRITE "P" SPRX("M")=SPRX("P")+15*COS(T)+3 SPRY("M")=SPRY("P")-A*15*SIN(T)+3 SPRPRIO("M")=SPRPRIO("P")-SGN(A*SIN(T)) 'PRIORITY OF SPRITE "M" WAIT:WAIT NEXT

Music and Sound

Changing the Background Music

INPUT "SELECT SONG 1 OR 2:", SONG ON SONG GOTO A, B, C @A:MUSIC$="space_blues.mp3":GOTO START @B:MUSIC$="beepbox-song.mp3":GOTO START @C:MUSIC$="":GOTO START 'SILENCE

Sound Effects

MUSIC$="" INPUT "SELECT EFFECT 0-5:", FX MUSIC$="sfx.wav" 'FACTORY SOUND EFFECT FILE MUSICTIME=FX*2 'EFFECTS BEGIN AT EVERY 2ND SECOND FOR I=1 TO 100:W.:N. 'EFFECTS PLAY FOR 2 SECONDS EACH GOTO START
MUSICTIME Effect
0 Jump
2 Shoot
4 Explode
6 Appear
8 Disappear
10 Ding

Extending the Environment

These 2 songs and 6 effects are provided by the factory environment zip.

After downloading the environment zip, you can add more mp3 or wav files to the zip and refer to them in the MUSIC$ variable. In this case though, Runner Screen cannot play the song.

You can specify a URL in the MUSIC$ value in case the mp3 file is available on a server.

Be careful: if you specify the file name in uppercase instead of lower or vice versa then Windows systems will play the song but Linux ones won't.

Assorted Tricks

Okay, with that you get the concepts. Now here are some details that I have not mentioned so far.

The rest of built-in variables

The tutorials so far covered the following built-in variables: PI, TZO, BACKGROUND, MUSIC$, MUSICTIME, TILEX, TILEY, TILEPRIO, TEXTPRIO, FONTALNUM, TEXTX, TEXTY, TEXTCOLORUC, TEXTCOLORLC

Here is the rest:

Palette

Components of color code CC stored in RED(CC), GREEN(CC) and BLUE(CC). Each value ranges from 0 to 255. The built-in designs are not affected. Only the 16 existing color codes can be redefined, CC's outside the range of 0 to 15 won't be effective.

'REPLACE LIGHT COLORS WITH GRAYSCALE FOR I = 0 TO 7 CC = I+8 B = I*255/7 RED(CC) = B GREEN(CC) = B BLUE(CC) = B NEXT

Re-DIM to erase

Quickly erase all sprites, tiles, text:

DIM SPRX()
DIM TILE()
DIM TEXTLINE$():CURSORY=0

That's all. I hope you enjoyed this tutorial. Have fun programming!

Be sure to visit the forum sometimes to share your experience and wisdom. Find the wiki for less simplified sample program lists, and more help in programming imeight.