How To Get Started In Machine Code
And Not Go Crazy With A Routine For Two Joysticks
This article is for people who are taking their first steps in machine code. The ideas are illustrated by a relocatable subroutine for two joysticks that are attached to the Pet exactly as suggested by Harvey Herman in COMPUTE!, vol. 1, #4, p. 89. It is written for a new-ROM Pet, with untested modifications for the old-ROM Pet.
My goal for the joystick routine was quick interpretation of joystick positions into numeric keypad equivalents of those positions. The details of conversion have been described by Harvey Herman. Location 59471 ($E84F) contains 255 ($FF) when joysticks haven't moved or another 8-bit number when they have. Four bits belong to each joystick. A Basic routine that examines the 8-bit number and converts it to a numeric keypad equivalent takes a long time. The process is instantaneous in machine code. I've added a feature of wait option. Some games require motion to occur before continuing while others do not. Instead of having to write or omit an equivalent Basic statement the wait option is entered into the program. Finally, I wanted this routine to be relocatable and coexistent with other machine code routines I have.
The January 1980 issue of COMPUTE! contained a plea from Mr. Schiller to make assembly listings more accessible to users. I share Mr. Schiller's concern. Note that the three different looking programs are all the same. The disassembled listing in figure 1 begins at hex location $7000 (28672 decimal). The double letter machine code sits between the address ($7000) and the assembler code (LDA © $34). The code on the first line consists of A9 34. Monitor listing in figure 2 also begins with A9 34 and so do the DATA lines in figure 3 where that pair of machine instructions has been changed to decimal 169 52.
How you feed your Pet is up to you. The quickest and most error prone way is the DATA lines. This method, however, facilitates moving code from one place to another. Entry via the Monitor is the easiest, but without a relocating program the code can't move. Follow the excellent instructions of Charles Brannon in his KEYPRINT routine (COMPUTE!, Nov.-Dec. 1980) to enter the code via the monitor. Save 69 bytes (program and table +1).
Where to put the code is also up to you. The listing is at $7000. The code does not have to be at $7000. You may put it in the second cassette buffer (decimal 826 or hex $033A), behind your Basic program, at the top of your Pet or any other reasonable place. Watch out - the program flags the change in top of the Pet pointer by POKEing 894 with 1. This is to prevent Pet from losing its memory during successive corrections and reruns. Should you need to make a correction, reset the pointers to their original value and POKE 894 with any non-one value. Final caution — because of the way I implemented relocatability the subroutine must be entered by a SYS call. Independently of location and manner of entry of this code, you must tell Basic where the code is located by setting variable AD properly. My Basic program takes care of it, otherwise you're on your own.
The routine ends at $7033. Locations $7034 to $7043 contain a lookup table of key numbers corresponding to joystick motions. This table is referenced by the address in $11 and $12. Numeric keypad equivalents are returned to Basic in locations 824 and 825 ($0338 and $0339, at the tail end of the first cassette buffer).
The logic of the joystick routine will not tax your intelligence, thus you can spend all of it on learning some simple machine code. This is exactly what I did and will now describe some of my experiences.
Machine language coding is rough when you jump from a higher level language. You seem to chase your own tail going in circles for need of an instruction that will do what you want it to do. Little tools I took for granted in Basic, like addition or handling arrays become a pain when they have to be considered with the same level of seriousness as the payroll program. And if this doesn't send you to the looney bin, the counters that count to 256 and reset to zero when you least expect it and the two byte addresses coexisting with one byte accumulator will get you for sure. You will not find unconditional GOTO in the repertoire, you will not find subscripted variables (arrays), and you'll have to learn to count backwards in hex...
The fact is all these tools are in the language. You have to find them and improvise, and that's the fun part. What comes out is a different matter. When I first wrote this joystick routine, it worked but I had a nasty feeling that it was too big and complicated for such an undemanding task.
I got the nerve to contact Jim Butterfield and it was a very rewarding experience. Jim helped me come to grips with instructions. He pointed out the sloppy code and gave suggestions for making it better. He also gave me SUPERMON, which contains an assembler, disassembler and all sorts of other goodies. That helped tremendously, as hand assembly is not one of my favorite activities.
Some of the code that might have looked like this:
CPX T (compare T to X-reg.) BEQ STP (if result = 0, goto STP) JMP PK (if not, got PK) STP: ...
was fixed up to a reasonable
CPX T BNE PK STP: ...
while avoiding branching around branches and stepping on your own feet.
STX KY1 JMP END (direct or indirect)
was changed to;
STX KY1 BPL END
as an appropriate way to write an unconditional GOTO. I wanted to store a value in KY1 and go to END. It's unnecessary to use a JMP instruction in a program this size and use of JMP makes it harder to move the code. Indirect jump, while helping relocatability, is an overkill in a minor procedure. Again the solution is simple. If you know that the flag at that point in the program is, for instance, always positive, a simple "branch if plus" improvises the unconditional GOTO. You may want to read Butterfield's article in Nov.-Dec, 1980 COMPUTE! which describes addressing modes and a "reach of instructions".
These examples do not now exist in the program, as they have been superseded by better code. But they illustrate silly trouble one can get in. Needless to say, after Jim Butterfield helped me the code became clearer and shorter.
Now, there is more to programming than just uncluttered, short code, whether the code is in machine, Basic, Pascal, or any other language. And this was the most important bit of advice I got. Combine a program and its tables into a coherent structure. Its simplicity should leap at you. And simple means less work, and perhaps no backward counting in hex...
I will illustrate this concept with a simple Basic example. It shows how a little bit of code can do lots of work. First, the clumsy way. We may set up a table of 10 values from Herman's list and loop through the table until the value of the bit pattern on the user port equals one of the values in the array, in which case the loop index (J) becomes the result. Like this:
JS = value of 4 bits PV( ): 3 9 11 10 13 15 14 5 7 6 FOR J = 0 TO 9 IF JS = PV(J) then K1 = J: get out NEXT J
There is nothing wrong with this type of coding except that it is inefficient. The program may have to loop ten times for each joystick before finding one matching value. A much neater structure is the one used in the routine shown in this article. Its Basic equivalent is:
JS = value of 4 bits KY(): x x x 0 x 7 9 8 x 1 3 2 x 6 5 K1 = KY(JS)
Note the absence of the unnecessary loop. Note the reduction in code. We now have a table of sixteen values, ten of which are the numeric keypad values (0 to 9). In my program x's have been filled with 10 ($0A). Depending on the value of the four bits at the user port the program addresses that position in the table which corresponds to the port value. Thus if the four-bit value at the user port is 9 the program, by use of the Y register, picks up the 9th value in the table (counting from zero) which in this case is 1. This kind of coding is in the disassembled listing at $7023-7027 and $702E-7032.
The rest of the code has to do with housekeeping: testing the stop key at $FFE1 and shifting bits around. The value seen at the user port is saved on the stack (line $701F) so that subsequent shifting and, masking will not destroy the value, as it is needed in testing both joysticks. It is brought back in $7028 and reused.
I would like to add two more ideas that will prevent you from going crazy. When you start coding leave lots of room between logical sections of your code and fill the gaps with $EA, that is, for no operation, it is a filler. (The less you know the more EAs you need. Start with fifty). As you discover mistakes you will be able to use the room while expanding code. When the program works, eliminate EAs to create a compact package of code. Try to get an assembler-disassembler. It's invaluable. It makes the work possible. You will be able to concentrate on thinking instead of hassling with hand assembly. SUPERMON, for instance, is available from Pet Program Exchange, P.O. Box 561, Montgomeryville, Pa. 18936. (Editor's Note: Price is $1.00 for tape and $1.00 for program.)
If your sanity is still intact after a couple dozen lines of code you'll have a good laugh as you come to grips with that very strange, but useful, language of your Pet.
Changes for old-ROM Pets:
1. In BASIC program:
replace PEEKs and POKEs to locations 52 and 53 by 134 and 135.
first DATA line has two 17-s and one 18, Replace 17 by 8. Replace 18 by 9.
third DATA line has two 17-s. Replace by 8.
2. In the ASSEMBLY listing: replace $11 by $08 in four places and replace $12 by $09 in one place.
3. In the MONITOR listing: same thing as in the assembler.
B* PC IRQ SR AC XR YR SP "; 784B 7E11 30 78 5E 34 F0 " 7000 A9 34 LDA #$34 7002 18 CLC 7003 65 11 ADC $11 7005 85 11 STA $11 7007 90 02 BCC $700B 7009 E6 12 INC $12 700B A9 00 LDA #$00 700D 8D 43 E8 STA $E843 7010 20 E1 FF JSR $FFE1 7013 AD 4F E8 LDA $E84F 7016 AE 37 03 LDX $0337 7019 F0 04 BEQ $701F 701B C9 FF CMP #$FF 701D F0 F1 BEQ $7010 701F 48 PHA 7020 29 0F AND #$0F 7022 A8 TAY 7023 B1 11 LDA ($11), Y 7025 8D 38 03 STA $0338 7028 68 PLA 7029 4A LSR 702A 4A LSR 702B 4A LSR 702C 4A LSR 702D A8 TAY 702E B1 11 LDA ($11), Y 7030 8D 39 03 STA $0339 7033 60 RTS "? " " : 7034 0A 0A 0A 00 0A 07 09 08 " : 703C 0A 01 03 02 0A 04 06 05 " ?
" " : 7000 A9 34 18 65 11 85 11 90 " : 7008 02 E6 12 A9 00 8D 43 E8 " : 7010 20 E1 FF AD 4F E8 AE 37 " : 7018 03 F0 04 C9 FF F0 F1 48 " : 7020 29 0F A8 B1 11 8D 38 03 " : 7028 68 4A 4A 4A 4A A8 B1 11 " : 7030 8D 39 03 60 0A 0A 0A 00 " : 7038 0A 07 09 08 0A 01 03 02 " : 7040 0A 04 06 05 20 20 20 20
I'd like to thank Jim Butterfield for helping me take the plunge into the machine code ... for the SUPERMON ... lots of patience ... and an incredible ability to share his knowledge.
100 REM------------------------------- 110 REM SUBROUTINE FOR TWO JOYSTICKS 120 REM ATTACHED AS IN H.HERMAN'S 130 REM ARTICLE - COMPUTE V.1, #4, P.89 140 REM 150 REM BY ELIZABETH DEAL 160 REM 170 REM FOR LOADING MACHINE CODE INTO 180 REM SECOND TAPE BUFFER REMOVE 190 REM 'REM' FROM LINE 250 200 REM TO PUT CODE AT TOP OF PET 210 REM USE LINES 260-300 220 REM SEE ARTICLE FOR CHANGES TO 230 REM OLD ROM PETS 240 REM------------------------------- 250 :REM SP = 67 : AD = 826 : FORI = ADTOAD + SP : &neg;READV : POKEI, V : NEXT : GOTO500 : &neg; < TO POKE823, 0 260 IFPEEK(894) = 1 GOTO 300 270 SP = 67 : AD = PEEK(52) + 256 * PEEK(53) - SP 280 FOR I = ADTOAD + SP : READV : POKEI, V : NEXT 290 AH% = AD/256 : AL = AD - 256 * AH% : POKE52, AL : &neg;POKE53, AH% : POKE894, 1 : CLR 300 AD = PEEK(52) + 256 * PEEK(53) 310 : 320 DATA 169, 52, 24, 101, 17, 133, 17, 144, 2, &neg;230, 18, 169, 0, 141.67, 232 330 DATA 32, 225, 255, 173, 79, 232, 174, 55, 3, &neg;240, 4, 201, 255, 240, 241, 72 340 DATA 41, 15, 168, 177, 17, 141, 56, 3, 104, &neg;74, 74, 74, 74, 168, 177, 17 350 DATA 141, 57, 3, 96, 10, 10, 10, 0, 10, 7, 9, &neg;8, 10, 1, 3, 2, 10, 4, 6, 5 360 : 370 REM------------------------------- 380 REM POKE823, W 390 REM W=0 NO WAIT FOR JS MOTION 400 REM W<>0 WAIT UNTIL MOVED 410 REM GET KEYPAD EQUIVALENTS OF JOY- 420 REM STICK MOTION FROM 824 AND 825 430 : 440 REM 0 = BUTTON 1 = LEFT-DN 2 = DN 450 REM 3 = RIGHT-DN 4 = LEFT 5 = NONE 460 REM 6 = RIGHT 7 = LEFT-UP 8 = UP 470 REM 9 = RIGHT-UP 480 REM------------------------------------ 490 : 500 POKE823, 0 510 SYS(AD) : PRINTPEEK(824), PEEK(825) : &neg;GOTO510