Classic Computer Magazine Archive HI-RES VOL. 1, NO. 2 / JANUARY 1984 / PAGE 66

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.