Advanced User Forum
by Russ Wetmore
A number of you have asked me to explain further how I use an assembler, rather than just which one. I personally use the Atari Macro Assembler. I have logged thousands of hours of work using AMAC, although it is not flawless, it does a pretty good job. In this three part series, I will try to explain several of the less used or misunderstood commands, show you how some of them can be used in your programming, and even clue you in on some features not mentioned in the manual. In addition, I'll show you my macro library and how to use it.
One quick note: although this series is aimed at AMAC users, several of the concepts will carry over to other macro assemblers, such as MAC/65 and ATMASD.
Commands with Problems
Some AMAC commands just flat out don't work. Why they don't is a mystery to me. Any substantial piece of software like AMAC (especially software designed to be used for program development) should at least work as outlined in the manual. The first time I used AMAC it was a little disheartening to type the examples shown in the documentation only to find they didn't work. One would think they would have at least left the questionable commands out of the manual.
One such command is USE. There are many applications where the USE command would come in handy. It is supposed to be used to designate "consecutive pieces of code in discontiguous source segments." What this means is you can write several sections of your program together in one place that will end up in different areas of memory. I know that you can simulate this by brute-force ORGing your program, but the assembler should be able to do this without having to keep track of absolute addresses. Luckily, you can simulate USE using macros (more on that in the next installment.)
One command that works after a fashion but not as advertised is LIST. In particular, the LIST * function won't perform as stated in the manual. The idea is to be able to "pop" a stack of parameters for the LIST command, and to return the LIST parameters to what they were before the previous LIST command. This would be useful for conditional assemblies where you can control what gets printed based on, say, a value assigned to a label. You might not want to have a data table printed out each time you assemble, for example. It would be nice to be able to say "Go back to what you were doing before," in case you couldn't be sure at a point in the program just what the LIST parameters were.
The only fix for this is to manually keep track of what LIST parameters you are using in all modules. I use lots of IF ENDIF blocks in assemblies to give various listing formats depending on a preset variable.
VFD is a useful command for building data tables where the arbitrary 8-bit "byte" boundaries don't apply. For example, I needed to compact screen images because of their restrictive size. The plan I hit on was this. More than 50% of the data fell into one of four categories:
1 byte of $00
2 bytes of $00's
1 byte of $FF
2 bytes of $FF's
You can represent those four patterns in 2 bits. Anything else would be represented by the byte itself, 8 bits. I needed a "flag" bit also, to let me know what kind of data was involved -- compacted pattern number or byte data. I designated that a 0 bit would mean the next 2 bits that followed represented a pattern number. A 1 bit would mean that the next 8 bits to follow were absolute data. This scheme allowed me to compact my data tables into an area about 60% of their original size.
VFD format is:
[,total\data ... ]
where total is the size of the data that follows in number of bits. AMAC keeps assigning bits sequentially until it fills out a byte, then starts on the next one. So, for example,
breaks down to:
which is the same as saying:
except that the VFD construct more clearly shows the nature of the data format.
So, in my example above, if I had the following data:
and 1 and 2 represented the patterns "2 $FF's" and "1 $00" respectively, I could represent it in compacted form in assembler as:
Trouble is, this only takes up a total of 15 bits. If I have normal data or program following this statement, AMAC is supposed to pad the last byte out with zeroes. It doesn't. In addition, since the *P pointer is never updated (the assembler pointer that points to the last bit position used) the next time you attempt a VFD statement, it'll start putting data in the wrong place.
The fix for this is to end each VFD block with:
This assures that the last byte is padded out with zeroes and the pointers are all updated properly.
Probably the most infamous problem with AMAC is in its symbol table. Its main use, that of being used by the assembler to look up addresses associated with labels, works properly (most of the time) but reference listings often go haywire. AMAC is supposed to list only those SYSTEXT labels that are referenced in your program. Not always so. It's supposed to flag you when a normal label hasn't been referenced. Sometimes, invisible labels are printed, and some normal ones disappear. Granted, most of these errors start creeping in when the symbol table starts filling and many people write programs that never overflow the assembler. I do, however, and, the end of a long program development is no time to start worrying about errors induced by another program.
The listing functions only seem to want to work with the Atari 825 and Okidata 82A printers. If you use an Epson or an NEC/Prowriter, for example, the listings will be too long for the page. Most people have figured out that Epson will page properly if you specify PS = 57 in the AMAC command line. However, you won't be able to use the nice PAGE and TITLE pseudo-opcodes to "pretty print" your listings. Jim Nangano of First Star Software has developed patches to AMAC to fix this. If enough people are interested, I'll reproduce them here (pending Jim's permission, of course).
There are several commands and command synonyms that are not documented in the manual. The most powerful is MSG. The format for the MSG command is:
where (string) is a character string (if standard text, surrounded by single quotes). You can also include labels and constants, using commas, which are represented by 4 hex digits.
MSG prints a message to the screen during assembly, as well as writing a line to the listing over the code columns. Example: when developing programs, I frequently just include my display list in-line with my program. Since a display list cannot cross a page boundary, I put this in before the display list:
IF LOW * > $100-display_list_length
MSG 'Space allocated:$',$100-LOW
DS $100-LOW *
If the assembler has to allocate space for the display list to not cross a page boundary, I get a message like:
'Space allocated: $003D'
on the screen to let me know there's a gap in the program. You could also use this command to let you know which parts of conditional assemblies were executed, and so on. It can be very useful to let you know what's going on when you compile.
There are two synonyms for existing commands. REPT is the same as ECHO. (I use REPT because it makes more sense to me than ECHO; small point, I agree.) INCLUDE can be shortened to INCL (which I use because it makes for neater listings -- the seven letter command plays havoc with tab columns).
In addition, there are several commands that are recognized by the assembler but don't seem to have been implemented. ENTRY and EXT appear to be hooks for implementing a linking system that was never pursued. There are two data-defining commands that seem to produce a pattern of data, but one I can't figure out. They are DBE and DCE. You might try playing around with them to see what they produce.
Why Are You Using This Turkey, Then?
It may seem that I have a lot of gripes with AMAC. Well, they're not really gripes as much as "Gee, I wish those features worked better." Even with all of its problems, I still prefer the extra features AMAC has over other assemblers. And after using MEDIT, I won't use another line editor as long as I live. But even so, I think it behooves anyone who seriously attempts to use AMAC to be aware of these problems and of the possible fixes.
Next time, I'll get into how to use many of the more subtle features of AMAC, and introduce my theory behind macro construction. I'll give some pointers on how to construct them and some tricks I've learned.
Fodder for Thought
Some endorsements: I have spent the past two months getting to know, and love, a new language available for the Atari from Optimized Systems Software, the Basic A+ people. It is called Action! and it is nothing short of amazing. The compiled code it produces rivals pure assembly language for speed, and doesn't produce near the overhead of a Basic or other language compiler. I used to use Basic for Q&D work * (Q&D stands for Quick and Dirty.) Things like data compacting, data entry and graphics file manipulations, used to get written in slow, methodical Basic. The ABC Basic Compiler speeded things up a bit, but it wasn't until Action! came along that I really felt like these mundane tasks weren't getting the best of me. For high-level structure and easily edited source code I get very fast, very compact programs. An unbeatable combination.
Endorsement #2: KoalaPad. For data entry, this is an unbeatable tool. It programs just like two paddles, and can be used for a variety of purposes. Besides entering graphics, you can use it to input data from a menu. (I use it to enter music data for my games.) Using the graphics drawing program that's supplied, you can create very sophisticated pictures for use in your own programs.
As a parting shot, I promised to pass along a useful tidbit of information each issue. I've received more than a few comments from people wanting to know how I managed to get the familiar key click in Preppie! without futzing up the display list. Many of you have no doubt seen Basic programs that used display-list interrupts where the whole screen shook when you typed at the keyboard. As a matter of fact, I am typing this article in using Atari Writer -- and as I type I can see various markers in the text, which are highlighted by players, flash every time I press a key.
Why does this happen? Well, display-list interrupts usually perform a STA WSYNC ($D40A) which in effect shuts off the 6502 until the television beam reaches the right-hand side of the screen. This is done so that colors and other screen parameters can be changed while the beam is off the visible portion of the screen.
Enter the key-click routine. The click sound is performed by pushing/pulling the speaker inside the computer at a given rate. What is the rate? The OS writers thought that the time it takes the beam to draw across the screen, or roughly 114 machine cycles per line, would make a good interval. So, they did a STX WSYNC, 128 times! What happens is that a WSYNC may already be in effect when a display list interrupt kicks in, thus delaying the interrupt by one scan line.
If you have a whole screen of interrupts or your interrupt routines depend on a certain scan-line position, your display may lock up completely. The offensive OS code that does this is:
LDX #$7F X1 STX CONSOL ;$D01F STX WSYNC ;$D40A DEX BPL X1 RTS
There are only two solutions. One is to rewrite a major portion of the key-input routine in the keyboard handler. Luckily, the click routine is only called at one spot in the routine, so you don't have to perform major surgery to correct it.
The second approach is the one I use. Ignore the K: handler completely, and monitor CH ($2FC). When you want to create a "click" sound, JSR through the following subroutine:
LDX #$3F X1 LDA VCOUNT;$D40B X2 CMP VCOUNT BEQ X2 STX CONSOL ;$DO1F DEX BNE X1 RTS
This pretty much does the same thing. VCOUNT is only updated every other scan line, so the sound is a little lower and more hollow, but it passes very nicely for a click. From Basic, you can put this routine in a string, as it is completely relocatable.