Apple Assembly Line
Volume 7 -- Issue 11 August 1987

In This Issue...

New Chips for the IIgs

Apple is now shipping version 2 of the IIgs system ROM, and you are supposed to be able to get a free upgrade at your dealer. If your IIgs has the color fringe problem, they will also change a graphics chip. The new ROM fixes a lot of bugs in the various ROM-based tools, and adds some new features. One new feature I like is a new monitor command ("#") which installs the monitor as a desk accessory. Once done, the monitor is always available in a few keystrokes, and you can easily return to exactly the point from which you called it.

Naturally, the new ROM makes the "$" command in the S-C Macro Assembler non-functional again. I will have to dig into the new code and see why, and fix it again. Unfortunately, as of the day I am typing this, I have not actually been able to upgrade MY IIgs. Mine is a prototype, and I have the "airplane" ROM board where Apple's single ROM chip is supposed to be. Maybe they will send me the appropriate upgrade soon, or maybe I'll be able to make a copy into 27256 EPROMs.

New Apple Accessories Coming Soon

You have read about Applied Engineering's PC Transporter in A+ magazine. But how about the ZIP CHIP from ZIP Technology? Or the GSX IIgs Accelerator from MDIdeas? Or the Memory Saver from Checkmate? ZIP CHIP is one of several new accelerators for pre-IIgs machines which are each a single chip that replace the 6502 or 65C02. The replacement contains the equivalent of a 4 MHz 65C02 with its own clock circuit and some super fast cache RAM; it is supposed to be inexpensive, compatible with everything, and make it all happen 3 or 4 times faster. GSX is a more traditional accelerator, fitting in any IIgs slot (1-7), and having a faster 65816 and RAM than is on the motherboard. It can run at 5.6 MHz and can include as much as 1 Mbyte of fast static RAM. Memory Saver turns an ordinary Apple RAM card or AE gsRAM card into a battery-backed up RAM, much like RamCharger does for RamFactor. All available soon, they say....

Using Double Lo-Res Graphics Bob Sander-Cederlof

That's right, low resolution, not high. Apple never talks about it, but the //e, //c, and IIgs all have a double low resolution mode, displaying either 40 or 48 lines of 80 pixels, in 16 colors. I know it is not in fashion, but lo-res still has some advantages over hi-res. For one, it is faster; for another, it takes less memory.

Subroutines to plot in lo-res graphics are included in the monitor firmware, and statements are included in both Integer BASIC and Applesoft. I have written some subroutines which provide all the same functions in double lo-res mode, and also added BOX and LINE subroutines. Since all //c and IIgs machines can execute the 65C02 extensions, and since all enhanced //e's also can, I decided to include some 65C02 instructions. They make the code a lot easier to write. I used the PHY, PLY, and BRA opcodes; I also used the INC opcode with no operand to increment the A-register. If you have an old unenhanced //e, you will have to re-write the code where these instructions occur.

Turning Double Lo-Res On and Off:

The same soft switch that changes normal hi-res into double hi-res does the trick for lo-res also. Reading or writing $C05E will double the resolution, and $C05F will put it back to normal. You also have to be in 80-column mode.

In 40-column mode with the "double" switch turned on you get a weird result: You still have only 40 pixels per line, and only four colors rather than 16. Furthermore, somehow which color is displayed for a particular pixel value depends on which column you are in! This is not a particularly useful mode, so I am not going to talk about it any more. Let's just stay in 80-column mode when we want to use double lo-res.

Applesoft's "GR" statement turns on the Lo-Res mode, clears the top 40 graphics lines, and sets the text window to the bottom four text lines. My subroutine "DLR", in lines 1700-1780, only turns on the double lo-res mode. I did not include clearing the graphics screen, or setting the text window. I included a separate subroutine "CLRTOP" for clearing the graphics screen, and "SETTOP" for setting the entire graphics screen to whatever the current color is (lines 2220-2250).

Lines 1640-1670 turn the double lo-res graphics back off, resetting to text mode. You could add a line here to clear the text screen and restore a full text window, like the Applesoft TEXT statement, if you wish.

Setting the Plotting Color:

The monitor firmware contains two entry points that my code uses: SETCOL ($F864) and PLOT ($F800). SETCOL uses the low-order four bits in the A-register to store a color value at COLOR ($30). This value is formed by stuffing the 4-bit color value in both the left and right nybbles of COLOR.

Plotting Single Points:

The monitor PLOT subroutine at $F800 will plot a single point on the normal Lo-Res screen. It expects the Y-coordinate in the A-register, and the X-coordinate in the Y-register. I set up my DPLOT subroutine the same way, but the X-coordinate can be any value from 0 to 79 instead of just 0 to 39. Double Lo-Res works the same way 80-column text does: columns 0, 2, 4, ...78 are stored in AuxRAM, and columns 1, 3, 5, ...79 are stored in MainRAM. Since the softswitches 80COL and 80STORE are both in 80-mode, the PAGE2 switch controls whether text screen addresses reference AUX or MAIN memory.

My DPLOT subroutine is in lines 1800-1960. I look at the least significant bit of the X-coordinate to determine whether the pixel will be in MAIN or AUX memory. If it will be in AUX memory, then I flip to AUX memory before calling PLOT. I also divide the X-coordinate by two, so that PLOT will put it in the right position in the selected bank. After PLOT has painted the pixel, I make sure I am back in MAIN memory at line 1930. I made my DPLOT subroutine save and restore the A- and Y-registers, even though the monitor PLOT subroutine did not. This made some of the other subroutines cleaner.

Lines 1800-1820 are a special entry to DPLOT, and plot the point whose coordinates are stored in the X1 and Y1 variables.

Horizontal and Vertical Lines:

Both Integer BASIC and Applesoft include the HLIN and VLIN statements for drawing horizontal and vertical lines. These are faster to draw and easier to implement than the more general line drawing algorithm included in Hi-Res graphics. Lines 1980-2080 are my double Lo-Res HLIN and VLIN subroutines. Like the ones in the monitor firmware, the coordinates for the beginning of the line must be in the A- and Y-registers; the other end of the line is specified by the value in X2 for HLIN, or in Y2 for VLIN.

My version of HLIN is slower than it ought to be, because I used the monitor PLOT subroutine inside DPLOT. This causes GBASCALC to be called for every pixel, even though it does not need to be called after the first pixel. I could write a second version of DPLOT which calls PLOT1 in the firmware, bypassing the GBASCALC call, but I decide not to bother.

Drawing Boxes:

Lines 2100-2200 will draw a rectangle (box) of the currently selected color. The A- and Y-registers specify the lower-right corner of the rectangle; the variables X1 and Y1 specify the upper-left corner.

To make it easier to program BOX-calls, I defined three macros: FROM, BOX, and BOX.TO. ">FROM valueX,valueY" will assemble a call to SET.X1.Y1 in lines 3160-3200. This subroutine makes sure neither coordinate is beyond the screen and stores them in X1 and Y1. For example:

       >FROM 3,10              (the macro call)
       LDA #10                 (the generated lines)
       LDY #3                          "
       JSR SET.X1.Y1                   "

       >BOX.TO 9,13     (assumes X1,Y1 already set)
       LDA #13
       LDY #9
       JSR BOX

       >BOX 3,10,9,13 will generate a >FROM
                      and a >BOX.TO macro call,
                      which will then be expanded.

Drawing Diagonal Lines:

Or, more properly, "Drawing Lines from Any Point to Any Other Point". In Applesoft Hi-Res graphics you can use "HPLOT x,y TO x2,y2". I wrote a similar subroutine in lines 2300 and following. This subroutine assumes you have already stored the coordinates of the beginning of the line in X1 and Y1, and that the coordinates of the end of the line are in the A- and Y-registers. (Just like BOX did.)

To make it easy to write program which draw lines, I defined some more macros. Use >FROM to set up X1 and Y1, just as with BOX. Use >LINE.TO to draw a line from that point to another. Use >LINE if you want to specify both ends at once. After a line has been drawn, X1 and Y1 will contain the coordinates of the end of that line; this means that you can draw a continuous path around the screen by using a string of >LINE.TO calls.

The algorithm for drawing a straight line from one point to another is probably not familiar to all. I believe it was developed many years ago (middle 1950's?) for the early XY-plotters. My implementation may be a little hasty, because I seem to remember there is a simplification that involves shuffling variable around to fold two cases into one, but it does work and is quick. It does not require any multiplication or division.

In order to be sure I had it right, I wrote a lo-res line-drawing subroutine in Applesoft first. Here is the listing of that program. It may help you to understand the assembly language in my LINE subroutine.

     100  GR : COLOR= 15
     110  INPUT X1,Y1,X2,Y2
     120  GOSUB 1000
     130  GOTO 110
     200  GR 
     210  COLOR=  INT ( RND (1) * 4) * 5
     220 X1 =  INT ( RND (1) * 40)
     230 Y1 =  INT ( RND (1) * 40)
     240 X2 =  INT ( RND (1) * 40)
     250 Y2 =  INT ( RND (1) * 40)
     260  GOSUB 1000
     270  IF  PEEK ( - 16384) < 128 THEN 210
     280  POKE  - 16368,0
     290  END 
     1000  REM  PLOT X1,Y1 TO X2,Y2
     1020 DX =  ABS (X2 - X1):DY =  ABS (Y2 - Y1)
     1030 SX =  SGN (X2 - X1):SY =  SGN (Y2 - Y1)
     1040 X = X1:Y = Y1
     1050  IF DX < DY THEN 1200
     1060 D = DX - DY
     1070  PLOT X,Y
     1080 D = D - DY:X = X + SX: IF D < 1 THEN Y = Y + SY:D = D + DX
     1090  IF X <  > X2 THEN 1070
     1100  PLOT X2,Y2
     1110  RETURN 
     1200 D = DY - DX
     1210  PLOT X,Y
     1220 D = D - DX:Y = Y + SY: IF D < 1 THEN X = X + SX:D = D + DY
     1230  IF Y <  > Y2 THEN 1210
     1240  PLOT X2,Y2
     1250  RETURN 

Is There Any Future In It?

I think so. One application that comes to mind is educational programs for young children. These do not need all of the sophistication of the IIgs Super Hi-Res, and do not usually warrant the investment it takes to produce nice programs which use the higher resolution. Lots (I might guess over 100,000) of "quick and dirty" programs have been written since 1977 by mothers and fathers for their children to help them learn something or maybe just to play on the Apple. Most of these, if they use graphics at all, use Lo-Res graphics. The double lo-res is just as nice (if not twice as nice), and if it were accessible from Applesoft it would be just as easy to use.

Now we need to put my subroutines into a form that can be easily used from within Applesoft, write some whiz-bang demos and some documentation, and get it into everyone's hands. Maybe next month....

Meanwhile, I did whip up a demo. Lines 1390-1620 will turn on the double lo-res mode, clear the screen, draw a series of diagonals, place a solid square in the center, and then draw a border around the entire screen. When you press any key it will flip back to text mode.

  1000  .LIF
  1020        .OP 65C02
  1030 *--------------------------------
  1040 X2     .EQ $2C
  1050 Y2     .EQ $2D
  1060 COLOR  .EQ $30
  1070 *--------------------------------
  1080 PLOT   .EQ $F800
  1090 SETCOL .EQ $F864
  1100 *--------------------------------
  1110        .MA FROM
  1120        LDA #]2      Y1
  1130        LDY #]1      X1
  1140        JSR SET.X1.Y1
  1150        .EM
  1160 *--------------------------------
  1170        .MA LINE.TO
  1180        LDA #]2      Y2
  1190        LDY #]1      X2
  1200        JSR LINE
  1210        .EM
  1220 *--------------------------------
  1230        .MA LINE
  1240        >FROM ]1,]2     X1,Y1
  1250        >LINE.TO ]3,]4  X2,Y2
  1260        .EM
  1270 *--------------------------------
  1280        .MA BOX.TO
  1290        LDA #]2      Y2
  1300        LDY #]1      X2
  1310        JSR BOX
  1320        .EM
  1330 *--------------------------------
  1340        .MA BOX
  1350        >FROM ]1,]2    X1,Y1
  1360        >BOX.TO ]3,]4  X2,Y2
  1370        .EM
  1380 *--------------------------------
  1390 T
  1400        JSR DLR      Turn on Double Lo-Res
  1410        JSR CLRTOP   Clear screen
  1420        LDA #15      Set Color = White
  1430        JSR SETCOL
  1440 *---DRAW DIAGONAL LINES----------
  1450        >LINE 0,0,39,39
  1460        >LINE 0,39,39,0
  1470        >LINE 79,39,40,0
  1480        >LINE 79,0,40,39
  1490 *---BOX 35,15 TO 44,24-----------
  1500        >BOX 35,15,44,24
  1510 *---BORDER AROUND SCREEN---------
  1520        >LINE 0,0,79,0
  1530        >LINE.TO 79,39
  1540        >LINE.TO 0,39
  1550        >LINE.TO 0,0
  1560 *--------------------------------
  1570 .2     LDA $C000
  1580        BPL .2
  1590        STA $C010
  1600 *--------------------------------
  1610        JSR TEXT
  1620        RTS
  1630 *--------------------------------
  1640 TEXT
  1650        STA $C051    TEXT
  1660        STA $C05F    SINGLE
  1670        STA $C054
  1680        RTS
  1690 *--------------------------------
  1700 DLR
  1710        LDA #$99
  1720        JSR $C300    TURN ON 80-COLUMN MODE
  1730        STA $C050    GRAPHICS
  1740        STA $C053    MIXED TEXT/GRAPHICS
  1750        STA $C054    START IN MAIN MEM
  1760        STA $C056    LO-RES
  1770        STA $C05E    DOUBLE
  1780        RTS
  1790 *--------------------------------
  1800 DPLOT.X1.Y1
  1810        LDA Y1
  1820        LDY X1
  1830 DPLOT  PHY
  1840        PHA          SAVE Y-COORD
  1850        TYA          X-COORD
  1860        LSR
  1870        TAY          X/2
  1880        PLA
  1890        PHA
  1900        BCS .1
  1910        STA $C055
  1920 .1     JSR PLOT
  1930        STA $C054
  1940        PLA
  1950        PLY
  1960        RTS
  1970 *--------------------------------
  1980 HLIN1  INY
  2000        CPY X2
  2010        BCC HLIN1
  2020        RTS
  2030 *--------------------------------
  2040 VLIN1  ADC #1
  2060        CMP Y2
  2070        BCC VLIN1
  2080        RTS
  2090 *--------------------------------
  2100 *   BOX   X2 IN Y, Y2 IN A
  2110 *--------------------------------
  2120 BOX    JSR SET.X2.Y2
  2130        LDA Y1
  2140 .1     LDY X1
  2150        JSR HLIN
  2160        CMP Y2
  2170        INC
  2180        BCC .1
  2190        RTS
  2200 *--------------------------------
  2210 CLRTOP LDA #0
  2220        STA COLOR
  2230 SETTOP >BOX 0,0,79,39
  2240        RTS
  2250 *--------------------------------
  2260 *   LINE FROM X1,Y1 TO X2,Y2
  2270 *         X2 IN Y, Y2 IN A
  2280 *--------------------------------
  2290 LINE   JSR SET.X2.Y2
  2300        LDX #1       First Y's, then X's
  2310 .1     LDY #0       START WITH SGN()=0
  2320        SEC
  2330        LDA X2,X     X2 or Y2
  2340        SBC X1,X     X1 or Y1
  2350        BEQ .3       VERTICAL or HORIZONTAL
  2360        BPL .2       Positive DELTA X or DELTA Y
  2370        DEY          Negative DELTA X or DELTA Y
  2380        EOR #$FF     2's Complement
  2390        INC
  2400        DEY          (overcome following INY)
  2410 .2     INY          SGN() = -1, 0, +1
  2420 .3     STA DX,X     DX or DY
  2430        TYA
  2440        STA SX,X     SX or SY
  2450        DEX
  2460        BPL .1
  2470 *--------------------------------
  2480        SEC
  2490        LDA DY
  2500        SBC DX       DY-DX
  2510        BPL .8       ...DX < DY
  2520        EOR #$FF     2'S COMPLEMENT
  2530        INC
  2540        STA D
  2550 .5     JSR DPLOT.X1.Y1
  2560        CLC
  2570        LDA X1
  2580        ADC SX
  2590        STA X1
  2600        SEC
  2610        LDA D
  2620        SBC DY
  2630        STA D
  2640        BEQ .6
  2650        BPL .7
  2660 .6     CLC          Y = Y + SY
  2670        LDA Y1
  2680        ADC SY
  2690        STA Y1
  2700        LDA D        D = D + DX
  2710        ADC DX
  2720        STA D
  2730 .7     LDY X1
  2740        CPY X2
  2750        BNE .5
  2760        LDA Y2
  2770        STA Y1
  2780        JMP DPLOT
  2790 *--------------------------------
  2800 .8     STA D
  2810 .9     JSR DPLOT.X1.Y1
  2820        CLC
  2830        LDA Y1       Y = Y + SY
  2840        ADC SY
  2850        STA Y1
  2860        SEC          D = D - DX
  2870        LDA D
  2880        SBC DX
  2890        STA D
  2900        BEQ .10      ...D = 0
  2910        BPL .11      ...D > 0
  2920 .10    CLC          X = X + SX
  2930        LDA X1
  2940        ADC SX
  2950        STA X1
  2960        LDA D        D = D + DY
  2970        ADC DY
  2980        STA D
  2990 .11    LDA Y1
  3000        CMP Y2
  3010        BNE .9
  3020        LDY X2
  3030        STY X1
  3040        JMP DPLOT
  3050 *--------------------------------
  3060 LIMIT.XY
  3070        CMP #40
  3080        BCC .1
  3090        LDA #39
  3100 .1     CPY #80
  3110        BCC .2
  3120        LDY #79
  3130 .2     RTS
  3140 *--------------------------------
  3150 SET.X1.Y1
  3160        JSR LIMIT.XY
  3170        STY X1
  3180        STA Y1
  3190        RTS
  3200 *--------------------------------
  3210 SET.X2.Y2
  3220        JSR LIMIT.XY
  3230        STY X2
  3240        STA Y2
  3250        RTS
  3260 *--------------------------------
  3270 X1     .BS 1
  3280 Y1     .BS 1
  3290 SX     .BS 1
  3300 SY     .BS 1
  3310 DX     .BS 1
  3320 DY     .BS 1
  3330 D      .BS 1
  3340 *--------------------------------

Patch .TF to Assemble System Files Bob Sander-Cederlof

The ProDOS version of S-C Macro Assembler can put your object code directly on a target file, by using the .TF directive to specify the file name. The way it is designed, you have no control over the file type: it always comes out type BIN, which is type $06. Sometimes it would be nice to be able to have the file end up a different file type, such as SYS ($FF), but no such capability is available in the S-C Macro Assembler.

Well, let's change it then! At least temporarily, whenever we need it. If such a patch is possible, we could make an EXEC file to install the patch whenever we need it, and simply re-load when we want to go back to normal.

The code which handles the .TF directive is located somewhere between $8F70 and $9000, depending on the particular serial number you have. The way to locate it in yours is to look for the following code somewhere between $8F70 and $9000. (Use the monitor L command to do the looking.)

     A9 00     LDA #0
     8D C8 BE  STA $BEC8
     8D C9 BE  STA $BEC9
     8D CA BE  STA $BECA
     AD D0 BE  LDA $BED0
     85 98     STA $98
     8D C7 BE  STA $BEC7
     A9 CE     LDA #$CE        Set Mark = 000000
     20 70 BE  JSR $BE70
     B0 ..     BCS ...
     A9 D0     LDA #$D0        Set End-of-File = 000000
     20 70 BE  JSR $BE70
     B0 ..     BCS ...
     A9 0A     LDA #$0A        10 Parameters
     8D B4 BE  STA $BEB4
     A9 C4     LDA #$C4        Get File Info
     20 70 BE  JSR $BE70
     B0 ..     BCS ...
     A9 07     LDA #$07        7 Parameters
     8D B4 BE  STA $BEB4
  ** A9 06     LDA #$06        File Type = $06 (BIN)
     8D B8 BE  STA $BEB8
     A5 BC     LDA $BC         Lo-byte of Origin
     8D B9 BE  STA $BEB9          into AuxType
     A5 BD     LDA $BD         Hi-byte of Origin
     8D BA BE  STA $BEBA          into AuxType+1
     A9 C3     LDA #$C3        Set File Info
     20 70 BE  JSR $BE70
     B0 ..     BCS ...         ...error

It is the starred line "LDA #$06" which we would need to change to get a differenct file type. By changing it to "LDA #$FF" we would get type SYS files for .TF directives. We might also want to change the lines which store into the AuxType field. AuxType holds the Load Address for BIN files, but SYS files always load at $2000 so AuxType is not used.

Once you find the code above, and determine the address of the file type value (the 06 in the LDA #$06 instruction), you can change it with a simple monitor command. Or, you could get fancy and write a program to do the searching for you and make the patch.

And I wrote just such a program! The following code will search for the patch location, and if it finds $06 in the file type address it will change it to $FF. If it finds anything else there, it will change it back to $06. And it will print out a message telling which one it did.

  1010 *--------------------------------
  1020 PNTR   .EQ $00,01
  1030 *--------------------------------
  1040 MON.COUT   .EQ $FDED
  1050 MON.CROUT  .EQ $FD8E
  1060 *--------------------------------
  1070 T
  1080        LDA #$8E00   START LOOKING AT $8E00
  1090        STA PNTR
  1100        LDA /$8E00
  1110        STA PNTR+1
  1120 *--------------------------------
  1130 .1     LDY #0
  1140 .2     LDA (PNTR),Y
  1150        CMP KEY,Y
  1160        BEQ .3       ...MATCHES SO FAR
  1180        BNE .1
  1190        INC PNTR+1
  1210        CMP /$9100   $90FF IS PLENTY FAR ENOUGH
  1220        BCC .1       ...NOT THERE YET
  1230        LDY #Q.NOT.FOUND
  1240        JMP MSG.OUT
  1250 *--------------------------------
  1260 .3     INY          NEXT BYTE IN KEY AND WINDOW
  1270        CPY #KEY.LEN
  1280        BCC .2       ...MORE IN THE KEY
  1290 *--------------------------------
  1300        LDY #PLOC-KEY
  1320        CMP #$06
  1330        BEQ .4       ...NOW "BIN", MAKE IT "SYS"
  1340        LDA #$06     ...NOT OTHER, MAKE IT "BIN"
  1350        STA (PNTR),Y
  1360        LDY #Q.MADE.BIN
  1370        JMP MSG.OUT
  1380 *--------------------------------
  1390 .4     LDA #$FF     MAKE IT TYPE "SYS"
  1400        STA (PNTR),Y
  1410        LDY #Q.MADE.SYS
  1420        JMP MSG.OUT
  1430 *--------------------------------
  1440 KEY    LDA #$07
  1450        STA $BEB4
  1460        .HS A9     "LDA" OF "LDA #$06"
  1470 PLOC   .EQ *
  1480 KEY.LEN    .EQ *-KEY
  1490 *--------------------------------
  1500 MSG1   ORA #$80
  1510        JSR MON.COUT
  1520        INY
  1530 MSG.OUT
  1540        LDA QTS,Y
  1550        BPL MSG1
  1560        JSR MON.COUT
  1570        JMP MON.CROUT
  1580 *--------------------------------
  1590 QTS
  1600 Q.NOT.FOUND  .EQ *-QTS
  1610        .AT /KEY NOT FOUND/
  1620 Q.MADE.BIN   .EQ *-QTS
  1630        .AT /PATCHED TO FILETYPE "BIN"/
  1640 Q.MADE.SYS   .EQ *-QTS
  1650        .AT /PATCHED TO FILETYPE "SYS"/
  1660 *--------------------------------

Viewing Appleworks Word Processor Files Bob Sander-Cederlof

I don't happen to be an owner of Appleworks. This may be the tiniest minority of which I am a member. However I do see a lot of type AWP files on disks you send me. Problem, how do I really "see" them? I need a way to list them to the screen so that they look the way you wrote them: a WISIWYS lister (What I See Is What You Saw). It strikes me now that people who are trying to "hear" the files using a Speech Synthesizer might also need some special lister.

I have been doing it by BLOADing the file I need to see, specifying a starting address and file type, like this:

       :BLOAD MSG.TO.SC,A$1000,TAWP

That leading colon is the prompt from the S-C Macro Assembler, which is usually where I am when I do the BLOAD. Then a hex-dump with ASCII on the right, lets me read it. But it is an uncomfortable way to read.

I looked up the format of the Appleworks Word Processor files, and decided to write a quick-and-dirty lister. The first 300 bytes of a type AWP file are of no interest to my lister. This header contains the tab stops and a few flags, and I don't know what else. My documentation is from February 1984, and most of the bytes were undefined at that time. I presume they are better defined now that over three years of improvements have been made to Appleworks, but I don't have that documentation.

If I BLOAD at address $1000, then 300 bytes in starts at $112C. From there to the end of file there are basically two types of data records: commands and text. Commands are two-byte records, and text lines consist of a four-byte header and a variable amount of text.

You distinguish whether a record is a command or text line by looking at the second byte. If it is 00, you have a text line. If it is $D0-$FF, you have a command. If it is anything else, I don't know what you have. Maybe garbage?

If the second byte of a record is $D0 or larger, indicating a command, then the first byte of the record may contain more information for the command. The most frequent command is $D0, which means a carriage return command. In my lister I simply output a carriage return when I see this command. Another popular command is $FF, which means end-of-file. Obviously, when my lister finds this code it is time to end the listing! All the other codes I chose to simply list in hexadecimal. To distinguish them from text they list on separate lines of the form ".$xx.$yy", where xx is the command code (second byte) and yy is the operand value (first byte).

Text lines have the second byte = $00. The first byte of these records contains a byte count, but it is somewhat redundant: it is always two larger than another byte count found in bits 6-0 of the fourth byte of the record. I chose to use this second byte count, which is the actual number of bytes of ASCII and control codes which follow the four-byte header. The third byte of the text line header is supposed to be a leading blank count for the line, but I haven't found anything but zero there in the files I have viewed. Anyway, I chose to ignore this byte in my lister. The sign bit of the fourth byte indicates whether or not a carriage return should be tacked onto the end of this line.

In a paragraph, separate screen lines will be separate text line records. All except the last line will have bit 7 of the fourth header byte = zero, while the last line of the paragraph will have that bit = one. In my lister I decided to allow two ways of viewing the file. If you assemble with WRAP in line 1020 = 0, a file will list with carriage returns at the end of every text line record. This makes it look like it did inside the Appleworks Word Processor. If you change line 1020 to WRAP .EQ 1, no carriage return will be listed until the end of the paragraph.

Within text line records you have both ASCII characters ($20-$7F) and control codes ($00-$1F). The control codes are NOT the same as ASCII control characters. They have special meaning within Appleworks. The documentation I have defines codes $01 through $0B as follows:

      Begin   End

       01      02      Boldface
       03      04      Superscript
       05      06      Subscript
       07      08      Underline

       09 -- Print Page Number
       0A -- Enter Keyboard
       0B -- Sticky Space

I converted $0B (sticky space) to $20 (ASCII space). A sticky space is supposed to print as a space, but not allow splitting at that point at the end of a line. All of the other codes I chose to simply ignore. You could obviously do something special with them. Either act intelligently and do on the screen or printer what they are supposed to do, or simply print them out as "<xx>" or the like.

You could add code to this lister to make it into a super-duper program. I thought about adding the front end to my ProDOS QUIT code program (from July, 1986 AAL). I would change the file type selection from that front end from SYS and BIN to AWP. Then I could easily find any AWP file on any mounted volume using the arrow keys, and BLOAD it with the RETURN key.

The next nice addition would be an output option section that would allow sending the output to a printer, speech synthesizer, or a TXT file.

If you do add either or both of these, or some other nice features, how about sending them here?

  1020 *--------------------------------
  1030 WRAP   .EQ 0 for CR at end of each line
  1040 *          1 for CR only at end of paragraph
  1050 *--------------------------------
  1060 P      .EQ $00,01
  1070 *--------------------------------
  1080 CROUT  .EQ $FD8E
  1100 COUT   .EQ $FDED
  1110 *--------------------------------
  1120 T
  1130        LDA #$1000+300
  1140        STA P
  1150        LDA /$1000+300
  1160        STA P+1
  1170 *--------------------------------
  1180 .1     LDY #1
  1190        LDA (P),Y
  1200        BEQ .2       TEXT LINE
  1210        CMP #$FF
  1220        BEQ .5       END OF FILE
  1230        CMP #$D0
  1240        BEQ .15
  1250        JSR PRB
  1260        DEY
  1270        LDA (P),Y
  1280        JSR PRB
  1290 .15    JSR CROUT
  1300        LDY #1
  1310        JSR BUMP
  1320        JMP .1
  1330 *--------------------------------
  1340 .2     LDY #3
  1350        LDA (P),Y    LENGTH OF TEXT BYTE
  1360   .DO WRAP
  1370        PHA          SAVE FOR CR FLAG
  1380   .FIN
  1390        AND #$7F
  1400        TAX          # CHARS IN LINE
  1410 .3     INY
  1420        LDA (P),Y
  1430        CMP #$20     CONTROL CHAR?
  1440        BCS .35      ...NO
  1450        CMP #$0B     STICKY SPACE?
  1460        BCC .36      ...NO
  1470        LDA #$20     ...YES, JUST PRINT A SPACE
  1480 .35    ORA #$80
  1490        JSR COUT
  1500 .36    DEX
  1510        BNE .3
  1520   .DO WRAP
  1530        PLA
  1540        BPL .4
  1550   .FIN
  1560        JSR CROUT
  1570 .4     JSR BUMP
  1580        JMP .1
  1590 *--------------------------------
  1600 .5     RTS
  1610 *--------------------------------
  1620 PRB    PHA
  1630        LDA #"."
  1640        JSR COUT
  1650        LDA #"$"
  1660        JSR COUT
  1670        PLA
  1680        JMP PRBYTE
  1690 *--------------------------------
  1700 BUMP   TYA
  1710        SEC
  1720        ADC P
  1730        STA P
  1740        BCC .1
  1750        INC P+1
  1760 .1     RTS
  1770 *--------------------------------

Modify S-C Macro For Overlay Assembly Bob Sander-Cederlof

From time to time we have been asked about adding some features to the S-C Macro Assembler that would make it easy to assemble overlay segments. We have finally done it.

Some of you are developing programs that are so large that there is not room in the main 64K memory for everything at once. I have heard of one system that has over 200K bytes of machine language object code! Overlaying code segments is one method of making such a large program fit. A main program always resides in memory, and loads different sections of code when they are needed. These might be read from disk, from RAMdisk, or just copied from another section of extended memory. The main program calls program sections in one overlay, loads in another overlay and calls routines in it, and so on. The main program section also contains common subroutines that are used by more than one overlay. This is a rather standard technique on mainframe computers.

The normal method for building overlay programs on larger computers involves assembling all the parts as separate relocatable object modules, and then building the overlay "tree" of files with a program called a "Link Editor". You can do something like this with the Apple Programmer's Workshop on the IIgs. However, if you are trying to use the S-C Macro Assembler (and we hope you are!) there is no Link Editor and no way of generating relocatable object modules. The S-C Assembler does all its linking during assembly, and does not make it easy to build overlays. Of course, we are about to change that within this very article.

Since code in the main section calls subroutines in the overlays, we need a way of knowing addresses within the overlays. Likewise the overlays need to know the addresses of programs and data within the main section. If everything is assembled at the same time, all this cross linking can be done automatically. However, it requires making sure that there are no duplications of label names between overlays. The result can be huge assembly-time symbol tables that overflow the space available. By using the .INB directive (available in the ProDOS version of the S-C Macro Assembler) you can maximize the space available for symbols, but it still can run out of space.

If you are using Apple's APW on the IIgs you can do it because of the Link Editor. In fact there are Tools included in the System Loader for handling overlays. Of course you are probably trying to write programs that will run on //e's or //c's, since that is still what most people own. And you are most likely not comfortable with APW and the System Loader and all the other IIgs tools yet. So what can you do?

It is already possible to do it with the S-C Assembler, except that with such large programs the symbol table during assembly can get so large that you run out of memory. With the patching program I am about to describe you can use our assembler to build overlays, even with extremely large amounts of code that would normally overflow the space available for the symbol table during assembly. However, the following patches do not provide a COMPLETE overlaying capability. For one thing, you will have to handle loading each overlay yourself when your program executes. For another, there is not capability for using symbolic reference from the main section into the overlay sections. For another, I have only shown here patches for the ProDOS version of the S-C Macro Assembler. A similar program would work with the DOS version, but the addresses for patching would be different.

I do provide the ability for symbolic reference from overlays back to the main, which is the more important direction. For entry points within the overlay sections, you will need to use some sort of jump tables or other fixed address entry point method. Here is an outline of what I am talking about:

Main:  load overlay 1
       call routines in overlay 1 thru JMP table
       load overlay 2
       call routines in overlay 2 thru JMP table
       et cetera

       Common Subroutine A
       Common Subroutine B
       Common Subroutine Z

       Common Data

Overlay n:  Jump Table:  JMP ENTRY1
                         JMP ENTRY2
                         JMP ENTRYN

            ENTRY1:  do stuff, call main routines as
                       necessary, end
            ENTRY2:  et cetera

One way of assembling such a program now without any modifications to the S-C Assembler would be to simply use .IN or .INB to include the main section withing every overlay source file. When you are assembling such a program the main section would be assembled over and over again, needlessly. The new approach lets the main section be assembled once; its object code is written on a file and its symbol table is saved in memory; then each overlay is assembled, beginning with the symbol table of the main program.

The general approach is to assemble the main section; mark the symbol table; assemble the first overlay; prune the symbol table back to the mark; assemble the next overlay; prune, assemble, and so on till the last overlay is assembled. To make it as automatic as possible, the entire assembly is controlled by an EXEC file. The EXEC file loads and executes a file named B.USR.ASM which creates the new overlay assembly capability, loads the main source code or the assembly control file for the main section, assembles the main, and then successive assembles the various overlays.

The key, of course, is B.USR.ASM. This program, when loaded and executed, stores an address into the vector for the USR command into the S-C Macro Assembler. It also searches memory for a particular entry point inside the code which process the "ASM" command, and patches itself with that entry point. The USR command is then enabled.

The USR command works in two ways, depending on whether it is the first use since loading and executing B.USR.ASM, or a subsequent use. The first time it saves the current address for the end of the assembler's symbol table. On subsequent uses it prunes back the assembler's symbol table to that saved address. In both cases, after diddling with the symbol table, it jumps into the ASM command just after the point at which the symbol table would normally be cleared.

Perhaps it is best now to look at the listing of my program. The first section, called INSTALL.USR, is the code that is executed if you "BRUN B.USR.ASM" or "-B.USR.ASM". Lines 1180-1340 search for the spot inside the ASM command code which we need to JMP to. The code at the beginning of the ASM command looks like this:

       STX $60
       STX $EE
       STX $EF
       STX $53
       STX $54
       JSR ...

The place where we want to enter the ASM command is that second JSR. We still have to STX in those five locations, and the value in X needs to be zero. You can see the USR version of this at lines 2250-2340. My search loop uses the five STX lines here as a search key. When it finds the same ten bytes inside the assembler somewhere between $9000 and $92FF, it assumes it has found the ASM command code. By adding $000D it computes the address of that second JSR, and then plugs that address into the JMP instruction at line 2340. Lines 1350-1420 do that computation and plugging. Lines 1430-1480 then install the USR address into the USR vector and return to the assembler.

When you type or EXEC the USR command, the assembler will jump to USR at line 1500. Lines 1510-1520 check a flag to see if this is the first time you did it or not. If it is the first time, lines 1540-1600 save the address of the end of the symbol table, and then line 1610 starts up the ASM command without clearing out the symbol table. This is how you would assemble the first overlay, after assembling the main section.

On subsequent uses of USR, lines 1630-2230 will prune back the symbol table to the same state it was the first time, and start up the assembly with that symbol table. This "pruning back" is not easy. The structure of the symbol tables is a set of 28 linked chains. There is one chain for each letter of the alphabet, and a symbol is placed in a chain based on the first letter of its name. There are two more chains, one used for macro definitions and another for target file information. Within each chain, symbol entries are kept in alphabetical order. This means that when we assemble the first overlay, some new symbols will be linked into existing chains in arbitrary positions. The "pruning back" operations cannot simply store the original "end of symbol table" address; it must also remove any and all symbol entries from each chain which are beyond that address.

Lines 1630-2140 do just that. There are 28 pointers kept at $0130-$0167 which point to the first entry in each of the 28 chains. Then the first two bytes of each symbol entry contain a pointer to the next entry in alphabetical order. The last symbol entry in a chain contains 0000 in these two bytes. If there are no symbols in a particular chain, its pointer in the $0130 area will be 0000. This code loops from 0 to 27, eliminating all entries beyond the marked end of table address for each of the 28 chains.

Finally, lines 2190-2230 restore the saved end of table address, and we fall into the code to enter the assembler without clearing the symbol table.

Using my program with an EXEC file makes all this easy. Here is an example of such an EXEC file:

       LOAD MAIN

Each of the files loaded above could either be complete sections, or merely small assembly control files with a lot of .IN or .INB directives specifying source files to include. I recommend the latter, using the .INB directive. For example, MAIN might look like this:

       1000    .TI 55,TITLE OF MAIN PROGRAM
       1010    .INB MAIN.EQUATES
       1020    .INB MAIN.PART1
       1030    .INB MAIN.PART2
       1040    .INB MAIN.LAST.PART

An OVERLAY.n file would be similar, but would name the particular files involved in that overlay. Since each overlay is separately assembled, there may be identical symbol names in different overlays with no conflict. Since the MAIN symbol table is present during the assembly of each overlay, the code in the overlays may freely reference any symbol names in the MAIN section.

  1000 *SAVE S.USR.ASM
  1010 *--------------------------------
  1020        .OR $300
  1030        .TF B.USR.ASM
  1040 *--------------------------------
  1050 PAGE.NUM   .EQ $53,54    Page number, starts at 0000
  1060 PASS.NUM   .EQ $60       Pass number, starts at 00
  1070 EOT        .EQ $CC,CD    Address of End of Symbol Table
  1080 ERROR.CNT  .EQ $EE,EF    Error count, starts at 0000
  1090 *--------------------------------
  1100 HASH.TBL   .EQ $0130 ... $0167   (28 pointers)
  1110 USER.HOOK  .EQ $8006     Address USR command jumps to
  1120 *--------------------------------
  1130 CHAIN      .EQ $53,54    Use PAGE.NUM temporarily
  1140 FWD        .EQ $EE,EF    Use ERROR.CNT temporarily
  1150 PASM       .EQ $EE,EF    Use ERROR.CNT temporarily
  1160 *--------------------------------
  1180        LDA #$9000   FIND ENTRY INTO "ASM" COMMAND
  1190        STA PASM
  1200        LDA /$9000
  1210        STA PASM+1
  1220        LDX #3
  1230 .1     LDY #9
  1240 .2     LDA (PASM),Y
  1250        CMP KEY,Y
  1260        BEQ .3
  1270        INC PASM
  1280        BNE .1
  1290        INC PASM+1
  1300        DEX
  1310        BNE .1
  1320        BRK          TRAP IF KEY NOT FOUND
  1330 .3     DEY
  1340        BPL .2
  1350 *---Install ASM link address-----
  1360        CLC
  1370        LDA PASM
  1380        ADC #$0D
  1390        STA JMPASM+1
  1400        LDA PASM+1
  1410        ADC #0
  1420        STA JMPASM+2
  1430 *---Install USR now--------------
  1440        LDA #USR
  1450        STA USER.HOOK+1
  1460        LDA /USR
  1470        STA USER.HOOK+2
  1480        RTS
  1490 *--------------------------------
  1500 USR
  1510        BIT FIRST.TIME.FLAG
  1530 *--------------------------------
  1550        SEC          Set Flag Negative for Subsequent
  1560        ROR FIRST.TIME.FLAG
  1570        LDA EOT      Save Address of End of
  1580        STA PNTR          SYMBOL TABLE
  1590        LDA EOT+1
  1600        STA PNTR+1
  1620 *--------------------------------
  1640 ***    Rebuild pointers by starting at beginning of
  1650 ***    each chain in HASH TABLE and traveling to end
  1660 ***    of each chain, lopping off any symbols beyond
  1670 ***    (PNTR).
  1680        LDX #0       FOR X = 0 TO 27
  1690 *--------------------------------
  1700 .1     TXA
  1710        ASL          INDEX * 2, CLC
  1720        ADC #HASH.TBL
  1730        STA CHAIN
  1740        LDA /HASH.TBL
  1750        STA CHAIN+1
  1760 *--------------------------------
  1770 .2     LDY #0       Get address of next symbol
  1780        LDA (CHAIN),Y     in this chain
  1790        STA FWD
  1800        CMP PNTR     See if beyond truncation point
  1810        INY
  1820        LDA (CHAIN),Y
  1830        BEQ .5       ...end of this chain
  1840        STA FWD+1
  1850        SBC PNTR+1
  1860        BCS .3       ...beyond truncation point
  1870        LDA FWD      Step forward to next link
  1880        STA CHAIN
  1890        LDA FWD+1
  1900        STA CHAIN+1
  1910        BCC .2       ...ALWAYS
  1920 *---Found link beyond truncation point---
  1930 .3     LDY #0       Look forward through chain
  1940        LDA (FWD),Y       until find one below
  1950        STA TCHAIN        the truncation point.
  1960        CMP PNTR
  1970        INY
  1980        LDA (FWD),Y
  1990        STA TCHAIN+1
  2000        SBC PNTR+1
  2010        BCC .4       ...below truncation point
  2020        LDA TCHAIN   Step forward to next link
  2030        STA FWD           in this chain
  2040        LDA TCHAIN+1
  2050        STA FWD+1
  2060        BCS .3       ...ALWAYS
  2070 *---Patch out truncated links of chain---
  2080 .4     LDY #0
  2090        LDA TCHAIN
  2100        STA (CHAIN),Y
  2110        INY
  2120        LDA TCHAIN+1
  2130        STA (CHAIN),Y
  2140        BCC .2       ...ALWAYS
  2150 *--------------------------------
  2160 .5     INX          NEXT X   (0...27)
  2170        CPX #28
  2180        BCC .1
  2190 *---Put EOT back too-------------
  2200        LDA PNTR
  2210        STA EOT
  2220        LDA PNTR+1
  2230        STA EOT+1
  2240 *--------------------------------
  2260        LDX #0
  2270 KEY
  2280        STX PASS.NUM
  2290        STX ERROR.CNT
  2300        STX ERROR.CNT+1
  2310        STX PAGE.NUM
  2320        STX PAGE.NUM+1
  2330 JMPASM
  2340        JMP *-*  *** ADDRESS FILLED IN BY PROGRAM ***
  2350 *--------------------------------
  2370 PNTR   .HS 00.00
  2380 TCHAIN .HS 00.00
  2390 *--------------------------------

Apple Assembly Line (ISSN 0889-4302) is published monthly by S-C SOFTWARE CORPORATION, P.O. Box 280300, Dallas, Texas 75228. Phone (214) 324-2050. Subscription rate is $18 per year in the USA, sent Bulk Mail; add $3 for First Class postage in USA, Canada, and Mexico; add $14 postage for other countries. Back issues are available for $1.80 each (other countries add $1 per back issue for postage).

All material herein is copyrighted by S-C SOFTWARE CORPORATION, all rights reserved. (Apple is a registered trademark of Apple Computer, Inc.)