_Bonkers_ Level #02 (c) Curtis White FREEWARE: NO SELL OF THIS MAGAZINE IS ALLOWED WITHOUT PRIOR WRITTEN PERMISSION. Bridge Ices Before Road We've received a lot of good comments on this magazine, and we hope we can keep it up, Thanks, as usual, to the rest of the Bonker's crew for a great job! This time we will explore deeper into this mysterious land of machine language, and cover even the roughest terrain. Well, no need to delay the show. Let's get to it! The Bonker's Staff Editor: Coolhand < Send your opinions, thanks, Technical Dude: Dokken < and gripes to these clowns! Author: Light < Support Web Site:http://soho.ios.com/~coolhnd/bonkers/planet.htm ******************* Stage #01: Pound cake, anyone? If you remember from the last Level, we covered the statement "lda location", also known as the absolute addressing mode for LDA. We will now learn the immediate form of LDA Yes, it sounds more technical, but it's the same creature we've been dealing with. Also, this immediate form is easier to understand for most people. So get your ml monitors ready.. we will start out with some example code. .49152 LDA #12 - IMMEDIATE FORM OF LDA, COPIES 12 INTO ACCUM .49154 STA 1024 - STORES TO LOCATION 1024, absolute store .49157 RTS - Returns Us TO BASIC This example should be run with SYS 49152 from BASIC, of course. Notice the (#) symbol. This tells us that 12 is NOT a location; instead, it is a value. In other words, it's getting the value "12" immediately into the accumulator, instead of pulling a value from the memory location 12. When you "run" this code, you will get the letter "L" in the upper left hand corner of the screen. If you understand this now, good. But since these basics are the backbone of any machine language program, we will look at them in closer detail. Here is what the IMMEDIATE form of LDA does. Ok, for this example, the value #10 is in address 12. LDA #12 - This copies the value 12 into the accumulator LDA 12 - This copies the value at location 12 into the accumulator. So the accumulator now contains the value #10. Please note that we are using an eight bit computer; the largest value for an eight bit number is 255. Thus, 255 is the largest value we can load directly (with LDA #255) or indirectly (e.g., with LDA 32768); i.e., the value stored at location 32768 can not contain a number larger then 255. If we consider 0-255, this gives us 256 unique combinations or numbers. But, as you recall from Level #1, we can combine bytes together (in low byte, high byte order) to form huge numbers. There is NO such thing as a STA #10; you CANNOT store a value to a value. That is like saying 10=5 which is FALSE. It's illegal; the cops will be knocking at your door with a ? if you try it. One last note. We also have the immediate addressing mode with the X and Y registers indicated by LDX #XX and LDY #XX. These work the same as the immediate mode does for the A register. If you've noticed, we have been storing to the screen a lot directly. This can cause problems when porting programs to various machines for several reasons. First, the length of the screen may be different; hence, you might get an ugly wrap around. Second, suppose the lengths are the same, but the screen memory may be at another location. And third, the layout of screen memory may be different. All of these can cause serious headaches, which is why Commodore supplied us with the kernal subroutines. Whoa, what's a kernal and what's a subroutine? Well a subroutine is a small program that we "call" from our main program to do something for us. This makes a lot of sense when we will be doing something repeatedly. There is no reason to make five or six screen clear routines when we can call one screen clear routine at five or six places. So we now know that a subroutine is a miniature program that we call over and over to do a simple task. Okay, that is simple enough, but what are the kernal subroutines? Well, Commodore was nice enough to supply us with several subroutines stored in ROM memory. All we have to do is know how to call their code from our programs, and several subroutines are ready for us to use. Not only this, several Commodore machines have these same subroutines at the same locations, which makes porting much easier. There is one other source of subroutines. These are the machine language subroutines stored in ROM as part of the BASIC operating system. We will not be using these at all, or if we do, very little. So, then, we have three sources of subroutines: our own that we make, the kernal subroutines, and the subroutines stored in BASIC ROM. First, we must learn how to "call" a subroutine. The instruction Jump Saving Return Address or JSR will do this for us. To end a subroutine, we will use the instruction RTS or Return From Subroutine. Our first kernal subroutine we will learn to call is CHROUT. This allows us to send data to the screen or other devices. We call it at address $FFD2/65490D.. get your monitors ready! .49152 LDA #65 - GET VALUE #65 IMMEDIATE MODE .49154 JSR 65490 - Jump Saving Return Address To CHROUT .49157 RTS - Return To BASIC When you SYS 49152, you will get the letter "A". Notice how the line your cursor is on when you SYS this program is, in fact, where the letter "A" shows up. If you are a BASIC programmer, you will notice that this is how the "Print" command works. Let's examine how this ml program works. LDA #65 - This is the PET ASCII code for the letter "A" JSR 65490 - Here we divert flow of our program to location 65490 which will check the accumulator and print the correct letter to the screen. RTS - Here we return the program to BASIC One more tidbit of information that I've been strongly implying. When we go to a subroutine with JSR, our program "stops" proceeding down the path it was going, and the computer executes the code at the location the JSR points to, until an RTS (Return From Subroutine) is executed. Then the computer returns to our main program at the point that it was when it branched to the subroutine and continues from there. If you've noticed, we have been ending our code with RTS. This is because our program diverts flow from the BASIC code, and when we finish, we want to return to BASIC. So is that overkill or what? But we need to know the PET ASCII codes to make use of this. A chart is most helpful for this, but the ASCII codes 65 through 90 correspond to the alphabet in the order A through Z. These do not correspond to the screen codes, if you were wondering. A TASK..(Author's Rule #1: Never Make Inside Jokes) The big boss has instructed us to print his name on the screen and return to BASIC. So let's get to it. .49152 LDA #66 .49154 JSR 65490 .49157 LDA #79 .49159 JSR 65490 .49162 LDA #66 .49164 JSR 65490 .49167 RTS That was enough typing for me; but the really BIG BOSS - the huge mean dude - wants us to print his name to the screen 3 times and return to BASIC. We will make the code shorter and explore indexing at the same time. .49152 LDX #0 Look at the # sign. We will be copying the value 0 into the x register, NOT the value at the address 0. .49154 LDA 49166,x .49157 JSR 65490 Time to dissect the LDA 49166,X. The X serves as an index to where the LDA will be grabbing data from. The value of X is added to the address 49166, giving us the effective address of where LDA will actually be copying the data from. The first time the code is executed, it will grab the value at address 49166, because we stored the value 0 to the x register and 49166+0=49166. The second time through, we will have done an XREG=XREG+1 (INX), and X will be equal to 1; so then the effective address will be 49167! And so on. Note that we can also index with the y register in the form LDA 49166,y. .49160 INX This is another new instruction. This takes the value in the x register and adds 1 to it. Or for you BASIC nuts, X=X+1. Why would we want to do this? If you thought something along the lines of "this dude is boring me", you are more than likely correct. And if you thought "we want to increase the x register by one so that we can use it as a counter, so that we can end our code when the correct number of letters have been printed", you are also correct! Note we also have the INY instruction which does exactly the same thing, except for the y register. .49161 CPX #12 CPX #12 means Compare X To Value 12. This just happens to be 12 letters, just what we need. We used the instruction INX to move our "window in memory" which we copy into the accumulator using LDA ADDRESS,X. Here, we check and see if we have copied all of what we wanted to. If we wanted, we could have opted for a CPX absolute. We also have the CMP absolute and CMP #IMMEDIATE for the accumulator, which work in the same fashion as CPX absolute and CPX #IMMEDIATE. And finally, we have the CPY absolute and CPY #IMMEDIATE, which work the same as the previous four, except for the y register. .49163 BNE 49154 BNE (branch if not equal). This means branch to 49154 if x is not equal to 12. Here is the logic. The first time the code is executed, X=0, and LDA will load the value at 49166 into the accumulator. We will print the letter to the screen using the kernal subroutine CHROUT. We now increase X by one; so then X now equals one. We compare X to 12, and X is less than 12; we branch back to 49154. This time 49166+X will give us the effective address of 49167, and we will continue with this until X is equal to 12. If we try to branch too far, it will not work, because a branch can only go about 128 locations forward and about 128 locations backward. .49165 RTS And this of course, returns our program to BASIC. We are not quite ready yet, though. We need to store the BOSS' name into locations 49166 through 49178, forgot already? Here is an easy way of doing this: .49166 ` The ` symbol was made by pressing shift 7. You will see a string of garbage. This is an ASCII translation of the memory locations 49166 through 49181 (some monitors may vary). Now type this: .49166 `billbillbill Which is the boss' name three times, of course. We can now execute our program (we are cruel people!) with SYS 49152. You should have three "bill"s on your screen, your lucky day! Here is another program that gets the same results as above, but it does it a little differently. Instead of indexing with the X register, we index with the y register. Soon, we will be making programs that use both X and Y registers for indexing and other processes. .49152 LDY #0 .49154 LDA 49166,Y .49157 JSR 65490 .49160 INY .49161 CPY #12 .49163 BNE 49154 .49165 RTS .49166 `BILLBILLBILL As usually, we execute the program with the BASIC command SYS 49152. Just as we have learned about the INX and INY instructions for increasing each register by one, there also exist instructions for decreasing each register by one. These are DEX (DECREMENT X) for the X register and DEY (DECREMENT Y) for the y register. A DEX or DEY works like X=X-1 or Y=Y-1. Let's re-code the above example, again using the X register, but instead of using INX, we will use DEX. .49152 LDX #11 .49154 LDA 49166,X :12 LETTERS EXIST FROM 49166 to 49166+11 .49157 JSR 65490 .49160 DEX .49161 CPX #255 .49163 BNE 49154 .49165 RTS A few things are of interest in this example. Notice the CPX #255. This implies something of great interest. After the X register has reached 0, it will wrap around at 255. Also, understand that we do this because we have 12 letters. If we had wrapped around with a CPX #0, while the X register would have traversed 12 positions, the LDA and JSR would not have accessed the last letter, because the BNE would have already been executed, thus ending the cycle. And one last detail that is fairly obvious: the 3 Bill's are backwards. If we did not use the kernal but our other method, then things may have looked different. Look at this example: .49152 LDX #11 .49154 LDA 49166,X .49157 STA 1024,X .49160 DEX .49161 CPX #255 .49163 BNE 49154 .49165 RTS .49166 `BILLBILLBILL *RUN WITH SYS 49152* It's too fast to notice, but instead of pasting the letters from the left to the right, it pastes the letters from the right to the left. Suppose we wanted to use this method to print the letters backwards on the screen, then we have the option of using both the X and the Y registers. Remember not to type in the ":" or anything following the character. .49152 LDX #11 :We have 12 chars to read starting at 49169 .49154 LDY #0 :Start pasting to screen at 1024. .49156 LDA 49169,x :49169+11=49180, the first pass .49159 STA 1024,y :Start at left work to the right.. .49162 INY :We move to the next screen column .49163 DEX :We get the next letter .49164 CPX #255 :Have we copied all the letters? .49166 BNE 49156 :If not then lets go back to 49156. .49168 RTS :Return to BASIC. .49169 `BILLBILLBILL *RUN WITH SYS 49152* We could replace .49164 and compare to the Y register instead. In fact, let's do that. Replace .49164 CPX #255 with the following: .49164 CPY #12 Now watch the program again; no visual changes but a small internal change! Let's do a mini-review of what we have learned. We learned about increment instructions (INX,INY - both work the same for each specific register); we learned about decrement instructions (DEX,DEY - both work the same for each specific register); we learned about compare instructions (CPX,CPY,CMP, - all work the same for each specific register); and we learned about subroutines, which include JSR (machine language subroutine call from machine language), SYS (machine language subroutine call from BASIC), and while we do not focus on BASIC, as we are concentrating on 65xx, GOSUB (subroutine call from BASIC). Quick look at BRK. The BRK (or BREAK) instruction is used to stop a machine language program. When you press the run/stop key, a similar action is implemented. All you need to know is that BRK will stop a ML program. This can be used for testing, if you wish. .49152 BRK .49153 LDA #68 .49155 JSR 65490 .49158 RTS SYS 49152, notice how we do not get our D on the screen like we would if we had SYSed 49153. The Monitor Is Your Friend.. Lastly, I want to go over how to save a machine language program and load one, as we are to the point where our programs are getting better. To save a machine language program, you will need to look at your monitor's documentation on the correct format, but it usually is in some sort of format like: .s "filename", start address-end address, device Note that the command above for saving is not any specific format, but your monitor should have a command that is about the same. When you load a machine language program from BASIC, you will need to use the load"file",8,1, or you can load it right into your monitor with a command like .l"filename. Again, it may be different. ___ Power-up Time ____ Questions //\\//\\// 1. What symbol signals immediate mode? 2. Compare and contrast immediate and absolute modes. 3. What do the INX and INY do? What do DEX and DEY do? 4. How far can we pan our "window in memory" with indexing? (for example how many addresses can we access) 5. What are subroutines? How do we call machine language subroutines from machine language? How do we end a subroutine from machine language? What are the kernal subroutines? 6. Code a machine language program to print your name on the screen using the kernal and without using indexing. 7. Code a machine language program to print your name on the screen using the kernal and indexing. ___Stage Boss___ Fill the screen with words, letters, or garbage using indexing. Can you think of other ways to do this? Stage #01 Completed. ______Things You've Learned_____ Well, you learned about immediate addressing (LDA #, LDX #, LDY #). You learned about subroutines, calling them (JSR), ending them, or returning to the caller (RTS), sources of them (KERNAL, our own, BASIC ROM). You learned about two of the increment instructions (INX,INY). And we covered two decrement instructions (DEX,DEY). Also you realized the benefit of compare instructions (CPX,CPY,CMP) and of the BNE (BRANCH IF NOT EQUAL) instruction. In fact, you know enough to make some useful programs, even if you don't know it yet! _____Level #02 Completed_____ I was originally going to do two Stages with this Level, but I've decided to save it for the next time. No fret, as you can bet each issue will be weekly. And who knows, we might even do a double release! We will not be publishing the answers to the cool questions in this magazine. We feel that the questions are important, mainly as thinkers, and are good to work out. As this is the case, I will provide answers to any questions that you can not figure out on a person to person basis. On the same note, if you find something confusing, please let us know. We will work with you until you understand this material. So until next time.. code 65xx! The Bonkers Staff