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....
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 1010 REM IN LO-RES WITH CURRENT COLOR 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 1010 *SAVE S.DOUBLE LO-RES 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 1990 HLIN JSR DPLOT 2000 CPY X2 2010 BCC HLIN1 2020 RTS 2030 *-------------------------------- 2040 VLIN1 ADC #1 2050 VLIN JSR DPLOT 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 *-------------------------------- |
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.
1000 *SAVE S.PATCH.TF 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 1170 INC PNTR SLIDE WINDOW DOWN ONE 1180 BNE .1 1190 INC PNTR+1 1200 LDA PNTR+1 STOP BEFORE IT GOES TOO FAR 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 1310 LDA (PNTR),Y GET CURRENT FILETYPE 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 *-------------------------------- |
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?
1000 *SAVE LIST.AWP 1010 .LIST CON SEE CONDITIONAL CODE TOO 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 1090 PRBYTE .EQ $FDDA 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 *-------------------------------- |
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 end 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 INIT.SYM.TABLE 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:
-B.USR.ASM LOAD MAIN ASM LOAD OVERLAY.1 USR LOAD OVERLAY.2 USR LOAD OVERLAY.3 USR
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 *-------------------------------- 1170 INSTALL.USR 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 1520 BMI SUBSEQUENT.TIME 1530 *-------------------------------- 1540 FIRST.TIME 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 1610 JMP JUMP.INTO.ASSEMBLER 1620 *-------------------------------- 1630 SUBSEQUENT.TIME 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 *-------------------------------- 2250 JUMP.INTO.ASSEMBLER 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 *-------------------------------- 2360 FIRST.TIME.FLAG .HS 00 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.)