Copy Link
Add to Bookmark
Report
Bonkers 02
_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