BrickHacks

8-bit robotics and the official home of the Lego Chevy 454 V-8

In memory of Dan Roganti

About, Notes, YouTube, Instagram



BASIC programming for Apple II series, BBC Micro, Commodore (PET, VIC-20, 64, 128), ISA PC (and clones)

This page covers the basics :) of programming Lego robotics in BASIC on the Apple II series, BBC Micro, Commodore (PET, VIC-20, 64, 128), or ISA-equipped PC (IBM and clones).

Applesoft BASIC - BBC BASIC - Commodore BASIC - PC BASIC (or QBASIC, etc.)

Jump to my YouTube video explaining all of this.


Page 11 of the Lego 9767 (Apple II card) manual states, "The machine's BASIC software is too slow to control the interface by means of normal subroutines." THIS IS FALSE. In fact, Lego documents using Applesoft BASIC in an appendix of the Lego Lines teacher's manual (see the Archive.org link on the BrickHacks homepage). As explained on pages 4.13-4.16 of that manual, programs must begin with an initialization routine. I like to start it at line 1000 but that is just personal preference.

1000 REM Activate the Lego interface card
1010 S = 7:L = 49280 + S * 16
1020 POKE L + 3,1: POKE L + 2,63: POKE L + 1,0
1030 POKE L,0: REM CLEAR ALL PORTS
1040 RETURN

Line 1000 is a comment to remind you of the routine’s purpose: write it however you like.

Line 1010 designates the slot number of the card and its memory address. Change the slot number to match your machine configuration. Don’t forgot order of operations when referring to L in your program -- multiply first, then add -- so address L will be (S*16)+49280.

Line 1020 activates each chip on the card. As explained in the manual, "This sets up the 6522 registers to that bits 0-5 are output bits, and bits 6-7 are input bits. All I/O is then done through address L."

Line 1030 ensures that you program begins with memory address L set to value 0.

Line 1040 exits the subroutine and returns to your main program. Or you could insert the code atop your program and avoid needing a subroutine if you wish.

Now that your program enabled the Lego interface card, you can enter the routines that your program will cite to turn on ports, turn off ports, and read sensors.

Here is the official code followed by my explanation of each line.

1100 REM OUTPUT DATA
1101 DB=0 : REM INITIALIZE DATA
1102 FOR I=0 TO 5 : REM CONVERT BINARY DATA
1103 DB=DB+DB(I)*2^I
1104 NEXT I
1105 POKE L,DB : REM SEND DATA TO INTERFACE
1106 RETURN
1110 REM TURN ON SPECIFIC BITS
1111 FOR I=0 TO 5 : REM TURN OFF ALL BITS FIRST
1112 DB(I)=0
1113 NEXT I : REM FALLS THROUGH TO NEXT PART
1115 FOR I=0 TO 5
1116 DB(I)=DB(I) OR T(I) : REM TURNS ON REQUIRED BITS
1117 NEXT I
1118 GOSUB 1100 : REM SEND DATA
1119 RETURN

Line 1100 introduces the output routine with a comment.

Line 1101 sets the array variable to value 0.

Line 1102 starts a FOR-NEXT loop to read whichever port(s) assignments you’re going to select and convert them into binary.

Line 1103 does the binary conversion.

Line 1104 completes the loop.

Line 1105 sends the array value to the interface card at address L.

Line 1106 exits the routine.

Line 1110 is a routine to turn on specific ports.

Line 1111 starts a FOR-NEXT loop to ensure all the ports are off for a clear start.

Line 1112 executes the clearing.

Line 1113 completes the loop.

Line 1115 starts another loop to prepare the necessary bits.

Line 1116 tells the array which ports your want to turn on.

Line 1117 completes the port-reading loop.

Line 1118 goes back to line 1100 to convert the decimal value to binary and send it.

Line 1119 exits the routine.

Thus, the code T(0-5):(0 or 1) is used to turn ports off/on. Substitute 0-5 for your port number and pick 0 or 1 after the colon to turn a port off/on.

Lego documentation explains: "For example, to turn on bits 3 and 4, without changing the other bits, you could use the following line: T(3)=1:T(4)=1:GOSUB 1115".

Lego documentation continutes: "To turn on bits 3 and 4, and the rest off, use: T(3)=1:T(4)=1:GOSUB 1110".

(Section about reading the sensor ports to come here...)

You’re ready to make a program.

IF-THEN statements will be your best friends, for example: IF (some scenario happens) THEN T=(value between 0-5): GOTO 1110 (or 1115) and that’ll turn on your desired ports. Easy!

But suppose you don’t want to use T(0-5)=(0/1) as a go-between. Read about Dan Roganti’s direct method to turn ports on and off.

Here is a sample program I wrote to control the forklift robot using the Roganti method (special thanks to Paul Hagstrom for teaching me a joystick control technique). See how the joystick programming compares to keyboard control in the IBM version.

Here is the main program. It is very simple.

10 GOTO 1000 : REM INITIALIZE THE CARD
20 GOTO 2000 : REM PERFORM THE NAVIGATION
30 GOTO 3000 : REM PERFORM THE FORKLIFT CONTROL
40 GOTO 20 : REM LOOP BACK TO THE NAVIGATION

The 1000 routine is covered above: POKEs initialize chips and set the slot address.


This section is coming soon.


This section is coming soon.


According to the 9771 hardware manual, all programs begin with code to test the interface card. (The manual calls it "initializing" but no real initializing is happening, unlike with BASIC for the 9767 Apple II card where the chips must be configured each time.) I like to start the check-out routine at line 1000 but that is just personal preference.

9950 '
9960 '----
9970 'INIT
9980 '----
9990 '
10000 P=925
10010 OUT P,21
10020 IF (INP(P) AND 63)=21 THEN OUT P,0 ELSE ERC=4 : GOTO 20000
10030 RETURN

Lines 9950-9990 are for programmer comments. I don’t know why they wrote this in five lines: one line would be enough. (You’ll see this become an annoying trend: keep reading!)

Lines 10000-10020 check to make sure there’s a card installed at the correct address. If there is, then it clears all ports. If there isn’t, then it sends you to an error-checking routine at line 20000.

Line 10030 exits the subroutine and returns to your main program. Or you could insert the code atop your program and avoid needing a subroutine if you wish.

Assuming your card is working, then you don't need the rest of it. You can just declare a variable to be 925 (such as P=925), OUTput to that variable (such as OUT P,value), and it will work fine.

Now that your program enabled the Lego interface card, you can enter the routines that your program will cite to turn on ports, turn off ports, read sensors, wait, and perform all the error handling.

As hinted in the initialization section above, Lego’s code in my opinion is unnecessarily long and over-engineered! Here is the official code followed by my explanation of each line. After that, you’ll find my own condensed version which works and saves a lot of space and typing.

10040 '
10950 '
10960 '------------------------
10970 'BITON ENTRY PAR: NUM%
10980 '------------------------
10990 '
11000 IF NUM%>=0 AND NUM%<6 THEN 11020
11010 ERC=1: GOTO 20000
11020 OUT P,(INP(P) OR 2^NUM%)
11030 RETURN
11040 '
11950 '
11960 '-------------------------
11970 'BITOFF ENTRY PAR: NUM%
11980 '-------------------------
11990 '
12000 IF NUM%>=0 AND NUM%<6 THEN 12020
12010 ERC=1: GOTO 20000
12020 OUT P,(INP(P)AND 255-2^NUM%)
12030 RETURN
12040 '
12950 '
12960 '---------------------------------------
12970 'GETBIT ENTRY PAR: NUM%, EXIT PAR: Y%
12980 '---------------------------------------
12990 '
13000 IF NUM%=6 OR NUM%=7 THEN 13020
13010 ERC=2: GOTO 20000
13020 Y%=(INP(P) AND 2^NUM%)/ 2^NUM%
13030 RETURN
13040 '
13950 '
13960 '-----------------------
13970 'WAIT ENTRY PAR: TIM%
13980 '-----------------------
13990 '
14000 IF TIM%>=0 THEN 14020
14010 ERC=3: GOTO 20000
14020 QT=TIMER + TIM%
14030 IF QT>TIMER THEN 14030
14040 RETURN
14050 '
19960 '
19970 '--------------
19980 'ERROR HANDLING
19990 '--------------
20000 CLS:COLOR 20,0 PRINT"PARAMETER ERROR":COLOR 7,0
20010 IF ERC=1 THEN PRINT "OUTPUT BITS MUST BE BETWEEN 0 AND 5":END
20020 IF ERC=2 THEN PRINT "INPUT BITS MUST BE 6 OR 7":END
20030 IF ERC=3 THEN PRINT "WAIT TIME MUST BE POSITIVE":END
20040 IF ERC=4 THEN PRINT "NO INTERFACE CARD IN COMPUTER AT ADDRESS 925":END
20050 END

Lines 10040-10960 do nothing except create white space to make your code easier to read... in the opinion of whoever wrote it.

Line 10970 is a comment that describes the routine’s purpose (turn a port on) and its entry parameter (a variable called NUM%).

Lines 10980-10990 do nothing.

Line 11000 checks if your decimal value for NUM% is valid (choose from ports 0 through 5). If it’s valid, then it sends you to line 11020 which accesses sends along your data and converts it from decimal to binary. If it’s not valid, then it sends you to the 20000 routine and prints on your screen: "PARAMETER ERROR: OUTPUT BITS MUST BE BETWEEN 0 AND 5". This ENDs your program.

Line 11030 exits the subroutine.

Lines 11040-12030 function the same way as 10040-12030, except this routine turns ports off instead of on. (Notice that 11020 uses a Boolean OR and counts the binary math up from 0, while line 12020 uses a Boolean AND and subtracts the binary math from 255.)

IMPORTANT NOTE: Line 12020 is incorrect in the Lego manual. The manual states 225, not 255. This is a critical error. Your interface box ports would not turn off! I learned the hard way before my friend Dan noticed Lego’s typo. I wonder how many children and teachers in 1987 pounded their keyboards in frustration when their robots would not turn off. :)

Lines 12040-13030 are how you read data from the sensor ports 6 and 7. If your variable called Y% equals nothing, then you know a sensor is off. But if Y% equals 64 (port 6) or 128 (port 7), then you know a sensor is on.

Lines 13040-14040 are a delay timer. I do not understand the reason to use this instead of a simple FOR-NEXT loop, but maybe that’s because I come from the Applesoft world.

Lines 14050-20050 are the error checking routines.

Now here is my version. It is stripped to the essentials! I also changed the line numbers for each routine to be 2000, 3000, etc., consistent with my preference to put initialization routines at 1000.

2000 'BITON
2010 OUT P,(INP(P) OR 2^NUM%)
2010 RETURN
3000 'BITOFF
3010 OUT P,(INP(P)AND 255-2^NUM%)
3020 RETURN
4000 'GETBIT
4010 Y%=(INP(P) AND 2^NUM%)/ 2^NUM%
4020 RETURN

Lego’s code does more, but it is 52 lines, wastes space, and has bizarre numbering. Mine does only what is absolutely necessary, is 9 lines, wastes no space at all, eliminates the superfluous wait routine, and is simply numbered. Which do you prefer? :)

You’re ready to make a program.

IF-THEN statements will be your best friends, for example: IF (some scenario happens) THEN NUM%=(value between 0-5): GOTO 2000 (with my version, or 11000 with the official version) and that’ll turn on your desired port. Easy!

But suppose you don’t want to use NUM% as a go-between. Read about Dan Roganti’s direct method to turn ports on and off.


Here's a video explanation for all four systems:


Home - History - Universal Buggy - Computers - Interface A - Connections - Peripherals - Code - Analog - Beyond - Lego Chevy V8

Copyright: Evan Koblentz, 2018-2024