Machine Language: First Steps, Part II
In the last episode, our hero, F. R. Vescent has started to convert the following bar graph program from BASIC to machine language:
200 DATA 15, 10, 30, 35, 28, 28, 15, 0 210 READ V : IF V = 0 GOTO 300 220 J = 48 : FOR K = 1 TO J 230 J = J + 1 : IF J = 58 THEN J = 48 240 PRINT CHR$(J); 250 NEXT K 260 PRINT 270 GOTO 210 300 END
He has coded lines 220 to 260 inclusive to read:
LDX #$30 LDY #$01 YLOOP INX CPX #$3A BNE SKIP LDX #$30 SKIP TXA JSR $FFD2 INY CPY $0300 BCC YLOOP LDA #$0D JSR $FFD2 RTS
He puzzles for a moment over the BCC YLOOP; he knows that this is equivalent to Branch-Less-Than; and this will cause the loop to be executed one time too few. After a few moments thought, he changes the LDY #$01 to LDY #$00; that should do the job. Now he's ready to translate the above Assembler code into machine code, which is what the computer really needs.
The coding will be placed in the PET's first cassette buffer, which starts at hexadecimal address 027A; this is noted at the left of the first line. Now, F. R. looks up LDX in his table of Op Codes, and finds that there are about five different ways the instruction can work. The one he wants is Immediate Mode – flagged by the "#" sign in the operand. That translates to hex A2, so he adds to the first line to make:
027A A2 30 LDX #$30
Counting off the address bytes, he decides that the next address must start at hex 027C. He writes this value at the left of the second line. Continuing the translation, he gets:
027A A2 30 LDX #$30 027C A0 00 LDY #$00 027E E88 YLOOP INX 027F E0 3A CPX #$3A 0281 D0 02 BNE SKIP 0283 A2 30 LDX #$30 0285 8A SKIP TXA 0286 20 D2 FF JSR $FFD2 0289 C8 INY 028A CC 00 03 CPY $0300 028D 90 EF BCC YLOOP 028F A9 0D LDA #$0D 0291 20 D2 FF JSR $FFD2 0294 60 RTS
There are a few coding tricks that F. R. has to keep in mind to do this translation. First, two-byte addresses are coded "backwards" – that is, hex 0300 is coded as 00 03 and hex FFD2 is coded as D2 FF. Secondly, Branches are coded as a relative offset, so that D0 02 may be read as "if not equal, hop over the next two bytes." A relative branch value of hex EF (see BCC YLOOP) causes the program to branch back 17 locations from the start of the following instruction, if the branch condition is satisfied.
The above "hand assembly" can be greatly helped by the use of assemblers or "tiny assemblers" which work out the Op Code values and calculate the branch values.
Now F. R. is ready to put the machine language program into memory. He calls the machine language Monitor with SYS4, and then asks for a memory display from 027a to 0294 with:
.M 027A 0294
He might get anything. He will go back and type over the memory contents to enter his program. The changed memory map will look like this:
.: 027A A2 30 A0 00 E8 E0 3A D0 .: 0282 02 A2 30 8A 20 D2 FF C8 .: 028A CC 00 03 90 EF A9 0D 20 .: 0292 D2 FF 60 .. .. .. .. ..
Now that the machine language program is in place, the BASIC program can be written to hook in with it. F. R. returns to BASIC (with .X) and writes:
200 DATA 15, 10, 30, 35, 28, 28, 15, 0 210 READ V : IF V = 0 GOTO 300 220 POKE 768, V 230 SYS 634 270 GOTO 210 300 END
Line 220 POKEs the desired value into memory where the machine language program will pick it up. Line 230 calls the machine language program. After the ML program is executed, the BASIC program will resume at line 270.
Try entering the program – both machine language and BASIC – and run it. You'll see the extra speed that machine language gives you.
The program is complete. But it isn't in a form that's very suitable for saving, especially if we want to save to tape. We'll conclude the fearless exploits of F. R. Vescent in the next episode.