Advanced User Forum
Russ Wetmore
A like to discuss some philosophy. Before you get all excited, I don't mean Nietzscheism or the like, but rather programming philosophy. How you go about programming many times is as important as the work itself.
Zen and the Programmer
Basic is easy to learn, but its strongest feature--its interactiveness--can foster bad habits when carried over to assembly language (AL for short) programming: indeed, any language which requires assembling or compiling can.
Basic statements, when constructed properly, are grand expressions of ideas. Whole thoughts, whole processes are conceptualized in one line of program code. Statements as simple as:
10 GRAPHICS 7:PLOT O,O:DRAWTO 50,50
represent a very complicated, very code-intensive operation to the computer. You don't have such luxury in AL. In its most base form the Basic program line above would easily translate to hundreds of lines of AL source code. What previously fit on one line of a terminal now occupies five, even 10 screens.
The point I'm trying to make is simple. No one can be expected to keep 100 pages of source code straight in their minds. Yes, 100 pages. Not counting the reference table listing, the source to Preppie! took 126 pages. Programs should be developed in small easy to maintain chunks.
And yes, I wholeheartedly recommend coding ON PAPER first, before reporting to the terminal to key it in. I've seen too many programmers content to do all their work, from start to finish, sitting in front of their computer terminals. For just this reason, for every program that somebody manages to squeak out this way, there are 50 potential programmers that will never make it.
I once mentioned this to a young programmer. His response was, "That's what computers are for." Baloney. When my computer is capable of showing me, all at once, what I can spread out on the top of my desk, then I'll believe it.
Biased Advice
You may or may not have heard of flowcharting, "treeing," topdown, bottom-up or inside-out programming. Well, let's add to the annals of programming history the "shuffled papers" method. Ta Da!
When I begin to program, I tend to break out my problem into a broad outline, written in English. I use "pseudo" programming constructs (If/Then/Else, Do/Until, etc.) to help me understand just what it is I'm trying to accomplish. Let's take an example. I want to write a routine that converts a binary number to an ASCII string, remove any leading zeros, and store it to a buffer--supposedly for transferring to the screen later. My outline looks something like this:
BUFFER is 5 bytes long
NUMBER = number to translate
POSITION = 0
for PWR = 4 downto 0
subtract 10^PWR from NUMBER, until
NUMBER goes negative, counting
each iteration
make count an ASCII number (add $30)
store number at POSITION in BUFFER
inc POSITION
next PWR
clear out BUFFER with spaces
POSITION = 0
do while POSITION in BUFFER is "0" and
POSITION < 4 replace with a space
inc POSITION
repeat
This outline makes several assumptions. For example, when we subtract 10^PWR from NUMBER, we don't want to end up with NUMBER being negative. This means either adding 10^PWR back to NUMBER to make it positive, or storing NUMBER away before each subtraction, as a precaution. Also, we certainly aren't going to use exponentiation to solve this simple problem. Instead we will build a table of values (we only need 5) and use PWR as the index to retrieve the value.
Observant programmers will probably note that we can combine the first and second parts of the program into one, using a flag. Very well, there is always a better way of doing things. Our outline now becomes:
BUFFER is 5 bytes long
NUMBER = number to translate
POSITION = 0
FLAG = OFF
for PWR = 4 downto 0
subtract 10^PWR from NUMBER, until
NUMBER goes negative, counting
each iteration
if count = 0 and FLAG is OFF and PWR < > 0
then DIGIT space
else DIGIT count + $30
FLAG = ON
store DIGIT at POSITION in BUFFER
inc POSITION
next PWR
After I am content that my basic logic is correct, I start to write codes. In AL our routine is shown in Program Listing 1.
NOTE 1: Notice that we are counting UP from zero to four instead of down as in the outline. There is a very good reason for this. In the outline we need to keep separate values for PWR and POSITION. Both numbers are in the range zero to four -- they just travel in opposite directions. Since we are accessing a table to get our powers of 10, we can just turn the table UPSIDE DOWN, and then use one index variable for both. The 6502 only has two such index variables, and we are using one for our counter. This is a good example of tailoring your code to maximize the output for a particular microprocessor.
NOTE 2: A common mistake made by 6502 programmers involves tables of values longer than one byte in length. Many programmers build tables consecutively, such as:
DW 10000, 1000, 100, 10, 1
But the 6502 is an 8-bit oriented fellow, and page oriented to boot. The code necessary to access the above table is much more involved (Listing 2).
In most cases whatever we can do to keep down the number of indices needed, the better.
NOTE 3: We could have eliminated this INX and simply written
SBC PWR 10 + 1,X
the second time. Again, it pays to know your processor and how it addresses memory.
From Simple to Simpleton
Quite honestly, I can't keep straight all the things going on in a program. After writing 5000 lines of program code you're bound to forget a line or two. Usually it is something crucial that you'll spend hours looking for when things go wrong. Likewise when I attempt to write routines that spread over 10 sheets of paper, I tend to forget by the 10th page something I vowed to remember on the first.
The example above was simple, but it's valid. By itself, it's a complete thought, but it isn't too demanding a programming exercise. However, if it's part of a much larger program, I'd just as soon not have to worry about such minor details until I have to. If I am trying to calculate the time remaining and print it to the screen in 40 colors at 42 places in the program, I don't want this mundane little pest confusing me even more.
When I am programming, I set one goal per day. I make it a goal that I can honestly meet and something that will give me some satisfaction (graphics, sounds, etc.)
What does this have to do with the example? Well, when I sit down, I draw out my outline. When I start coding, I limit all sections of code to no more than five pieces of paper. (I've found five pages is about my limit.) If I evolve a complex routine, something like BN2ASC, that becomes an idea by itself. I write it down as "JSR BN2ASC," and make a note that that routine has yet to be written. That routine becomes its own five pages, and, in turn, will generate another five-page routine . If I find that I will only use the routine once I cross out the JSR line and, at the terminal, type the whole routine in its place.
I frequently find, while I'm writing, that I've already written a similar section of code in another module. Here's where the shuffling comes in. Since the original routine is seldom general enough to use as is, I make a note to start another five-page section when I'm done with the current one. Then, I cross out the original routine and put a big "JSR" beside it. Later, I go back and insert a sheet into the original with the changes necessary to access the new subroutine.
If this sounds like a lot of work, it is. But it helps me to keep everything straight.
Happy Trails
So much for how I do things. Lest anyone get the wrong impression, I am not advocating the way I program as a model for anyone to follow. Every programmer needs to find his own style.
As I said last issue, I'll be happy to answer any questions regarding your Atari computer. If you have a question send it to me in care of the address in the front of the magazine. We'll handle as many each issue as space will allow.
Also, I'll end this column each month with some things you may or may not know about your computer, and why they exist.
For example, you probably know that when your computer goes into "attract" mode (when you let it sit for awhile and it starts cycling through different colors) that you can stop it by pressing a key. What happens if you have a program running and it will take your key stroke as input? (How many times have you seen "PRESS ANY KEY TO ABORT" in a program?) Simple: Press [SHIFT] [CNTL] [A]. "Attract" will stop and no input will be registered.
Why?
Well, the operating system maintains a variable in memory called CH (764, or hex $2FC). When you press a key, it stops the computer for a second, jumps to a subroutine that reads the key, stores the key value to CH, then returns to what it was doing.
This value is NOT ASCII, but a code called an "Internal" code. When no key has been pressed, CH holds 255 ($FF). When you call the " K: " handler (either directly, or through the screen editor) it examines CH, and if it isn't 255 the handler translates it to ASCII and returns. It just so happens that the internal code for [SHIFT][CNTL][A] is 255, so the computer doesn't know you've done anything.
Next time, we'll start a multi-part exploration of the Atari innards (ANTIC, POKEY, CTIA/GTIA) and how to make them stand up and do tricks.
ORG 03000 ; NOTE: This source is in Atari Macro Assembler (AMAC) format ; CALLING PROCEDURE: ; Store binary value to be translated to NUMBER ; Then, JSR BN2ASC ; USES: All registers ; AFFECTS: ZEROFLG, NUMBER, BUFFER ZEROFLG DS 1 NUMBER DS 2 BUFFER DS 5 BN2ASC LDX #0 ;X IS OUR "PWR" VARIABLE (SEE NOTE 1) STX ZEROFLG ;MARK AS NOT HAVING HAD A NON-ZERO NUMBER B6 LDY #0 ;Y IS OUR COUNTER B3 LDA NUMBER ;SUBTRACT 10^PWR FROM NUMBER SEC SBC PWR10L,X ;(SEE NOTE 2) PHA ;SAVE IN CASE NUMBER GOES NEGATIVE LDA NUMBER+1 SBC PWR10H,X BCC B2 ;CARRY IS CLEAR IF RESULT IS NEGATIVE STA NUMBER+1 ;IT'S OKAY - STORE NEW RESULT BACK TO NUMBER PLA STA NUMBER INY ;BUMP COUNTER BPL B3 ;UNCONDITIONAL BRANCH B2 PLA ;WHEN DONE, THERE'S GARBAGE ON THE STACK CPY #0 ;IS COUNT ZERO? BNE B4 ;NO, GO AHEAD CPX #4 ;1'S POSITION? (WE NEED AT LEAST 1 "0") BNE B4 ;NO, GO ON LDA ZEROFLG ;HAS THERE BEEN A NON-ZERO NUMBER YET? BNE B4 ;YES, "0" IS OKAY LDA #' ' ;MAKE IT A SPACEI BNE B5 ;UNCONDITIONAL BRANCH B4 TYA ;MAKE COUNT ASCII CLC ADC #'O' INC ZEROFLG ;MARK AS HAVING HAD A NON-ZERO NUMBER B5 STA BUFFER,X INX ;DO UNTIL 5 NUMBERS CPX #5 BCC B6 RTS PWR10L DB .LOW 10000,LOW 1000,LOW 100,LOW 10,LOW 1 PWR10H DB HIGH 10000,HIGH 1000,HIGH 100,HIGH 10,HIGH 1
Program Listing 1. Assembly language routine to convert binary to an ASCII string.
STX TEMPX ;SAVE X! IT WILL BE DESTROYED TXA ASL A ;MULTIPLY INDEX BY 2 FOR WORD OFFSET TAX ;(TWO BYTES PER TABLE ENTRY) LDA NUMBER ;SUBTRACT 10^PWR FROM NUMBER SEC SBC PWR10,X PHA ;SAVE IN CASE NUMBER GOES NEGATIVE INX ;(SEE NOTE 3) LDA NUMBER+1 SBC PWR10,X LDX TEMPX ;LDX DOESN'T AFFECT CY FLAG BCC B2 ;CARRY IS CLEAR IF RESULT IS NEGATIVE
Program Listing 2
Russ Wetmore, author of the popular Preppie! series heads Star Systems Software in Casselberry, Florida. He is contributing columnist to Hi-Res.