Assembler course part 2

by Wanja Gayk

Hi everyone! I hope you got along well with the first part and that you still want to go on. This time we're going to really start off. So get your monitors warmed up! Let's recall the program from last course in which we switched the color of frames and the background from black to white and back again. It looked like this (you had to start it with SYS 8192 from BASIC or G 2000 from your ML monitor):

.A2000 LDA #$00        ; value of black into the accumulator,
.A2002 STA $D020    ; loads value of accumulator into register for frame colors,
.A2005 STA $D021    ; loads value of accumulator into register for background colors,
.A2008 LDA #$01        ; value of white into the accumulator,
.A200A STA $D020    ; loads value of accumulator into register for frame colors,
.A200D STA $D021    ; loads value of accumulator into register for background colors,
.A2010 JMP $2000    ; jump back to $2000

Of course there's a much easier way to do this - because you have various registers: accumulator, X and Y. Load and Store commands exist also for the X and Y registers. They are called LDX, STX and LDY, STY. LDX means LoaD X, STX means STore X. LDY and STY, respectively, are short for LoaD Y and STore Y. We can use them in the same simple way we used LDA and STA earlier on. Here's a short program:

.A2000 LDX #$00        ; loads $00 (black) into the X register,
.A2002 LDY #$01        ; loads $01 (white) into the Y register,
.A2004 STX $D020    ; value of X register into the register for frame colors,
.A2007 STX $D021    ; value of X register into the register for background colors,
.A200A STY $D020    ; value of Y register into the register for frame colors,
.A200D STY $D021    ; value of Y register into the register for background colors,
.A2010 JMP $2004    ; jump back to $2004

Now, where's the difference? Seemingly there is none - the program even has the same length - but it operates faster. That's because there are fewer commands in its loop to change the color from black to white. Compare: the first program has seven commands (including JMP) in its closed loop. Program No.2 has only five of them - by the way, it is one of the fastest programs possible to implement that effect. Let's now try a different approach to the program, so that we can learn some new commands. We'll stick to the same effect not to loose track. We' ll focus exclusively on the X register, and the commands we need now are INX and DEX.

INX/INY

INX stands for INcrement X. It increases the value of X by one. If the value in the X register is $fd (253), INX will raise it to $fe (254). If we use another INX the value will increase to $ff (255). But we know that the accumulator, X and Y register only have a capacity of one byte, which is only fit for representing a value of $00 up to $ff. When trying to increase a value of $ff once more the processor will start again at $00, so there's no problem. In the 6510, the same command is also available for the Y register (INY for INcrement Y), but not for the accumulator.

DEX/DEY

DEX stands for DEcrement X and has the opposite effect of INX. It decreases the value of the X register by one. If the value in the X register is $02, DEX will lower it to $01. If we use DEX once more $01 will be decreased to $00. Again, the value will jump back and the processor will start counting at the other end of the scale if we try to decrease it once more and a value of $00 will be turned into $ff. The corresponding command for the Y register is DEY (DEcrement Y).

Let's write a short program using these two commands:

.A2000 LDX #$00    ; loads $00 into the X register (black),
.A2002 STX $D020    ; X register into frame color,
.A2005 STX $D021    ; X register into background color,
.A2008 INX        ; increases value of X register by one ($00 turns into $01 - white),
.A2009 STX $D020    ; X register into frame color,
.A200C STX $D021    ; X register into background color,
.A200F DEX        ; decreases value of X register by one ($01 turns into $00 - black),
.A2010 JMP $2002    ; and we start again at $2002

This program is of the same length as the previous ones and has the same effect - but it only works as fast as the first one because there are seven commands in the loop that have to be executed, with INX and DEX taking up as much time as LDA - just to tell you. Now, wouldn't it be nice if we could increase or decrease values directly in the main storage? Of course this is also possible by using INC and DEC. INC and DEC work the same way as INX and DEX, with the difference that affect the contents of one single storage location, rather than the contents of the X or Y register. INC means INCrement and DEC DECrement. The next program shows the practical application:

.A2000 LDA #$00
.A2002 STA $D020    ; frame color black,
.A2005 STA $D021    ; background color black,
.A2008 INC $D020    ; adds one to frame color (= $01 - white),
.A200B INC $D021    ; adds one to background color (= $01 - white),
.A200E DEC $D020    ; subtracts one from frame color (= $00 - black),
.A2011 DEC $D021    ; subtracts one from background color (= $00 - black),
.A2014 JMP $2008    ; jump back to $2008

This program is a little longer than the previous ones due to the fact that both storage cells first have to be initialized with $00 in order to be able later on to change between $00 and $01. Even though the program's loop consists of only five commands this program is slower than any of the others, since INC as well as DEC take up six clock cycles each. LDA, LDX and LDY in comparison use only two, STA, STX and STY take up four clock cycles, INX/INY and DEX/DEY also use only two while JMP swallows up three clock cycles. These numbers are only valid for the commands already introduced as there are different variants of LDA and STA commands. This leads us to our last programs for today, for which we'll need two new commands.

LDA (X-indexed) and LDA (Y-indexed)

LDA $3000, X is a great command to read tables. It loads the content of the storage cell $3000 plus X into the accumulator. The X register is used as, well, an index. If X = $00, then the content of the storage cell $3000 will be read, if X = $35 the value will come from the storage cell $3035, and so on. The same thing works also for the Y register - with the command LDA $3000, Y.

BEQ (Branch if EQual), BNE (Branch if Not Equal) and the ZERO-FLAG

BEQ is a command that effects a jump similar to JMP, but only if certain conditions are fulfilled, that is, if the so-called ZERO-FLAG is set. BNE also jumps, but only if the ZERO-FLAG is clear. The ZERO-FLAG is a processor bit with which the value of a command resulting in zero is marked. That is, if you execute a DEX while a value of $01 is in the X register, the result will be $00, and therefore the ZERO-FLAG will be set. This is also true if DEC results in $00 or if $ff is turned into $00 by INX, INY or INC. The ZERO-FLAG will also be set if LDA #$00, LDX #$00 or LDY #$00 are executed. If you load a value of $00 from main storage into the accumulator with an LDA, LDX, or LDY, the ZERO-FLAG will be set. It is cleared if the result of a command does not equal zero. The following short program is to clarify the way it works:

.A2000 LDX #$04        ; loads X register with $04,
.A2002 STX $D020    ; value from X register into frame color,
.A2005 DEX         ; decreases X by one,
.A2006 BNE $2002    ; jump to $2002 if result is not zero,
.A2008 BEQ $2000

This is what the program does: the X register is loaded with $04 which then goes into the frame color (setting the frame color to cyan). X is then decreased by one (X is now $03). As DEX resulted not in $00 but in $03 BNE jumps to $2002, which now sends the value of X (= $03) into the register for the frame color (now, it's red). Now X is decreased once more. DEX has still not reduced X to $00, therefore the program jumps back to $2002 where the value of X (= $02) again is loaded into the frame color. This process continues until DEX has decreased the value of the X register to $00, which is when the processor will automatically set the ZERO-FLAG. But as BNE only jumps on the condition that the ZERO-FLAG is CLEAR (that is, that the last operation resulted in something other than a zero), the program continues to $2008. The following command, BEQ $2000, only jumps if the ZERO-FLAG is set - which must the case, otherwise BNE would have jumped before. So BEQ carries us back to $2000 where the program starts anew. Now what effect does this program have on the screen? Same as this one:

.A2000 LDA #$04
.A2002 STA $D020
.A2005 LDA #$03
.A2007 STA $D020
.A200A LDA #$02
.A200C STA $D020
.A200F LDA #$01
.A2011 STA $D020
.A2014 JMP $2000

The difference is that the program that works with jumps controlled by certain conditions is much shorter. But now let's look at our last program with which we're going to read a table for the first time. First, type this nice little table:

.M3000 06 06 04 04 0E 0E 03 03
.M3008 0D 0D 01 0D 0D 03 03 0E
.M3010 0E 04 04 06 06 00 00 00
.M3018 01

What you did just now was to set up a table of color values which occupies storage locations $3000 to $3018, and which we're now going to use to have the screen change to these colors. The program looks like this:

.A2000 LDX #$19        ; number of colors plus one into the X register,
.A2002 LDA $2FFF, X    ; loads value of $2fff into the accumulator (table starts at $3000)
.A2005 STA $D020    ; value of the accumulator into frame color,
.A2008 STA $D021    ; value of accumulator into background colors,
.A200B DEX        ; decreases X register by one,
.A200C BNE $2002
.A200E BEQ $2000

Well, how does this program work? It starts with the last value of the table and continues to the first one, loading the values from the table into the registers for frame and background colors. The X register is used to proceed from one value to the next in the table. First, X is loaded with #$19 so that LDA $2FFF,X loads the last value from the table, which will come from storage location $3018 ($2FFF + X, X=$19), into the accumulator. The value of the accumulator is then loaded into the screen colors ($D020 and $D021). Then X is decreased by one, which results in $18 in the second run. Since $18 does not equal $00, the BNE jumps to $2002. At that point, the value from $3017 ($2FFF + $18) is loaded into the screen colors ... and so forth.

The interesting part of this comes when X reaches a value of $01. At this point, the first value of the table is read, which is $3000. This value is now loaded into the screen colors and X is decreased by one - to $00. Logically, the BNE does not jump anymore. If the command had been LDA $3000,X we now wouldn't be able to read the first value of the table. In any case, BNE is ignored because the ZERO-FLAG is set. But the following BEQ jumps to the beginning of the program at $2000 due to the ZERO-FLAG - and we're back in our old familiar closed loop.

In the next part we will dedicate ourselves to finding out if we can achieve some even nicer effects concerning tables and colors - and by the way we'll have the chance to try some great new commands. Hope you're looking forward to it, see you next time!