Prerequisite: you should have a working knowledge of Microsoft BASIC. I will do my best to explain the purpose of each line of code, short of teaching you the language.
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.
Note: Code on the rest of this page may contain bugs. I'm mostly an Apple II guy. I need to do more testing on the IBM version.
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 is a sample program I wrote to control the forklift robot using the Roganti method. It’s adapted from the Applesoft 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.
Think about a bulldozer or Army tank. Each tread is controlled by a separate motor. To go forward or reverse, both motors move in the same direction. To turn, the motors move in opposite directions. I decided to use keyboard navigation instead of joystick navigation. See how it compares to joystick programming in the Applesoft version.
2000 REM KEYBOARD TREAD CONTROL
2010 N$=INKEY$ : REM SET VARIABLES
2020 IF N$=CHR$(73) THEN M = 5
2030 IF N$=CHR$(77) THEN M = 10
2040 IF N$=CHR$(74) THEN M = 9
2050 IF N$=CHR$(75) THEN M = 6
2060 IF N$=CHR$(32) THEN M = 0
2070 OUT P,M: REM SEND COMMANDS
2080 RETURN
Line 2000 starts the routine with a comment.
Line 2010 makes a variable called N$. I picked N for navigation. Then I set the variable to be a keyboard input by stating that it’s equal to the INKET$ command.
Next I scan the keyboard for a press. The values 30, 77, 74, and 75 correspond to the letters I, M, J, and K. Value 32 is the spacebar which I use as a brake to stop the robot.
The M variable POKEs its value the interface card at memory address P which we set in the initialization code. As you learned in the Roganti brute-force method (linked above), M is the decimal equivalent of the ports (bits 0-5) that we want to turn on. So when M=5 then we must be POKEing ports 2 and 0, because those are the only combination that add up to decimal 5. The computer sends binary 5 (101) to activate the ports, which turns on your motors in the right direction, and the robot moves. Similarly, when M=10 we’re turning on ports 3 and 1; when M=9 it is ports 3 and 0; an when M=6 it’s ports 2 and 1.
Here is the routine for the forklift control.
3000 REM FORKLIFT
3010 REM GET DOWN
3010 IF STRIG(1)=-1 AND INP(L) <> 64 THEN OUT L,16
3020 IF INP(L) = 80 THEN GOTO 3080
3030 IF STRIG(1)=-1 > 127 THEN GOTO 3010
3040 REM GO UP
3050 IF STRIG(5)=-1 > 127 AND INP(L) <> 128 THEN OUT L,32
3060 IF INP(L) = 160 THEN GOTO 3080
3070 IF STRIG(5)=-1 > 127 THEN GOTO 3050
3080 RETURN
Here is how it works.
Joystick button 1 is used to lower the forklift. Joystick button 5 is used to raise it.
Line 3010 reads button 1 via INP(49249). If it’s currently being pressed, then its value is in the upper half of the 0-255 scale and is therefore greater than 127. However, you don’t want the forklift to lower if it is already at its physical bottom limit. If it is touching the lower sensor (connected to port 6 of the interface box) then its value is 64. So, if button 1 is on and the value being sent to the card is not 64, then it approves turning on the forklift motor in one direction by poking the value 16 into port 4.
If the forklift activates that sensor, then the sensor value of L becomes 80 — that’16 for the motor at the same time as the sensor adds its own value of 64 since it's in port 6. If that happens the the program jumps to line 3070 and exits the routine.
Otherwise, if button 1 is still being pressed, then the program loops back to line 3010.
The same thing happens with button 5 and the upper touch sensor attached to port 7 (value 128) in lines 3040-3060.
If no buttons are being pressed, then the code falls through to line 3080 and exits the subroutine back to the main program.
Home - History - Universal Buggy - Computers - Interface A - Connections - Peripherals - Code - Analog - Beyond - Lego Chevy V8
Copyright: Evan Koblentz, 2018-2024