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.
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.
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.
2000 REM JOYSTICK TREAD CONTROL
2010 FB = PDL (1):LR = PDL (0):M = 0: REM SET VARIABLES
2020 IF FB < 75 THEN M = 5
2030 IF FB > 180 THEN M = 10
2040 IF LR < 75 THEN M = 9
2050 IF LR > 180 THEN M = 6
2060 POKE L,M: REM SEND COMMANDS
2070 RETURN
Line 2000 starts the routine with a comment.
Line 2010 declares assigns a variable FB (short for "front-back") to paddle 1 (joystick Y axis) and variable LR (short for "left-right") to paddle 0 (joystick X axis). (Apple joystick/paddle control has a range of 0-255.) It then ensures both variables are at 0 to start. It also sets a variable called M (short for "motors") to 0.
Lines 2020-2050 examine the state of the FB/LR variables (joystick X/Y positions) and set variable M based on those states.
Line 2060 is for POKEing the value of M to the interface card at memory address L 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 PEEK (49249) > 127 AND PEEK(L) <> 64 THEN POKE L,16
3020 IF PEEK(L) = 80 GOTO 3080
3030 IF PEEK(49249) > 127 THEN GOTO 3010
3040 REM GO UP
3050 IF PEEK(49250) > 127 AND PEEK(L) <> 128 POKE L,32
3060 IF PEEK(L) = 160 GOTO 3080
3070 IF PEEK(49250) > 127 THEN GOTO 3050
3080 RETURN
Here is how it works.
Joystick button 0 is used to lower the forklift. Joystick button 1 is used to raise it.
Line 3010 reads button 0 via PEEK(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 0 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’s 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 3080 and exits the routine.
Otherwise, if button 0 is still being pressed, then the program loops back to line 3010.
The same thing happens with button 1 and the upper touch sensor attached to port 7 (value 128) in lines 3050-3070.
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