In This Issue...
Gary Little's new book is finally here: "Exploring the Apple IIgs", published by Addison-Wesley, 534 pages. If you are seriously trying to conquer the IIgs in assembly language, this book is a must. Instead of just telling us the tool numbers, Gary includes working examples of assembly language code which use the tools. Here is a list of the chapter titles, which I think is the easiest way to describe the contents:
The examples are all written using Apple's APW assembler, and make extensive use of the toolbox macros included with that assembler. A coupon in the back of the book lets you order a complete source code disk directly from Gary Little for all of the programs listed in the book for only $20. You can buy the book from us for $21 plus shipping.
Another new book this month is also from Addison-Wesley, in the official Apple IIgs series, "The Apple IIgs Hardware", 288 pages + 8 foldout schematics. This one lists at $24.95, and you can get it from us for $23. Again, if you are serious about programming the IIgs in assembly language you probably need this book. It contains information about the hardware you won't find in the other books.
I loved the article "Vector-to-Raster Algorithms" by Dick Pountain in the September 1987 issue of BYTE. It is the kind of article that explains why I have been a loyal subscriber for ten years or so. Pountain carefully explains how the fundamental graphics subroutines are written, and gives pseudo-code examples for each one. And, wonder of wonders, BYTE printed all of the examples! (It has been so frustrating the last three years to find that the examples must be downloaded from BIX or ordered at extra cost!)
I decided to try my hand at coding a fast circle-drawing subroutine for Apple hi-res graphics. Things like that are already included in the //gs QuickDraw ToolBox for Super Hi-Res, but I am not aware of any generally available circle-drawers for ordinary hi-res.
Since I wrote the double lo-res article last month, I decided to first try adding a circle-drawer to that program. It would be somewhat easier, since all of the variables involved could be 8-bit integers. But even before that I decided to write an Applesoft version, using normal hi-res.
1000 REM DRAW A CIRCLE USING MICHENER'S ALGORITHM 1010 HGR 1020 HCOLOR= 3 1030 INPUT CX,CY,R 1040 X = 0:Y = R:E = 3 - 2 * R 1050 REM PLOT 8 POINTS 1060 HPLOT CX + X,CY + Y: HPLOT CX + X,CY - Y 1070 HPLOT CX - X,CY + Y: HPLOT CX - X,CY - Y 1080 HPLOT CX + Y,CY + X: HPLOT CX + Y,CY - X 1090 HPLOT CX - Y,CY + X: HPLOT CX - Y,CY - X 1100 REM ADVANCE TO NEXT POINT 1110 IF E > 0 THEN E = E + 4 * (X - Y) + 10:Y = Y - 1: GOTO 1130 1120 E = E + 4 * X + 6 1130 X = X + 1: IF X < = Y THEN 1060 1140 GOTO 1030
This program will draw a circle of radius R and center at CX,CY. Line 1030 lets you enter the center and radius. The algorithm actually computes an X-offset and Y-offset from the center for each point in 1/8th of the circle. Lines 1060-1090 use the offsets to plot eight points, one in each of the half-quadrants. The three expressions for calculating E, in lines 1040, 1110, and 1120 are the secret to the program. Pountain says they were derived by "algebraic re-arrangement" of the basic equation of a circle: X^2 + Y^2 = R^2. Maybe so, I'll just take his word for it.
I found it interesting to play with some of the parameters. I changed line 1030 do assign specific values to CX, CY, and R and then ask for a value "A". Then I changed the expression for "E" in line 1110 by substituting "A" in the place of "4". By changing the value of "A" I could draw anything from a square to a diamond with missing points. Try it!
Next I wrote the double lo-res version. The listing which follows can be appended to the listing on pages 7-10 in the August article. After assembly, executing the program named TC will clear the screen and draw four circles. They are drawn fast enough that they appear almost instantaneously.
Lines 3270-3330 define a macro named CIRCLE, which make it easy to call the CIRCLE subroutine. The three parameters are simply CX, CY, and R. SInce this is double lo-res, the legal limits will be small. CX may be 0 to 79, CY may be 0 to 47, and R may be 0 to 23. Any larger R-value would force part of the circle to go off the screen. I did not put in any error checking in this version, so if you do try to go off the edge you will probably clobber something. In my hi-res version I put in code to avoid plotting off the screen.
The code is basically a hand-compilation of the Applesoft version. The comments show the equivalent Applesoft statements.
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 .lin 3270 *-------------------------------- 3280 .MA CIRCLE 3290 LDA #]3 R 3300 LDX #]1 CX 3310 LDY #]2 CY 3320 JSR CIRCLE 3330 .EM 3340 *-------------------------------- 3350 TC 3360 JSR DLR Turn on Double Lo-Res 3370 JSR CLRTOP Clear screen 3380 LDA #15 Set Color = White 3390 JSR SETCOL 3400 *-------------------------------- 3410 >CIRCLE 40,20,19 3420 >CIRCLE 40,27,4 3430 >CIRCLE 30,15,3 3440 >CIRCLE 50,15,3 3450 *-------------------------------- 3460 .2 LDA $C000 3470 BPL .2 3480 STA $C010 3490 *-------------------------------- 3500 JMP TEXT 3510 *-------------------------------- 3520 CIRCLE 3530 STX CX get center coordinates 3540 STY CY 3550 STA Y1 Y = R 3560 *--- D = 3 - 2*R ---------------- 3570 ASL 2*R 3580 EOR #$FF -2*R-1 3590 ADC #4 3-2*R 3600 STA D 3610 *--- X = 0 ---------------------- 3620 LDA #0 3630 STA X1 3640 *---equiv. to lines 1050-1090---- 3650 .1 LDX X1 3660 LDY Y1 3670 JSR PLOT.FOUR.POINTS 3680 LDX Y1 3690 LDY X1 3700 JSR PLOT.FOUR.POINTS 3710 *-------------------------------- 3720 LDA D IF D<=0 THEN GO TO .2 3730 BMI .2 3740 BEQ .2 3750 SEC D = D + 4*(X-Y) + 10 3760 LDA X1 3770 SBC Y1 3780 ASL 3790 ASL 3800 CLC 3810 ADC #10 3820 DEC Y1 3830 BNE .3 3840 *-------------------------------- 3850 .2 LDA X1 D = D + 4*X + 6 3860 ASL 3870 ASL 3880 ADC #6 3890 .3 ADC D 3900 STA D 3910 INC X1 X = X + 1 3920 LDA Y1 IF Y <= X THEN GO TO .1 3930 CMP X1 3940 BCS .1 3950 RTS 3960 *-------------------------------- 3970 PLOT.FOUR.POINTS 3980 STX DX 3990 STY DY 4000 CLC PLOT CX+DX,CY+DY 4010 LDA CX 4020 ADC DX 4030 TAY 4040 LDA CY 4050 ADC DY 4060 JSR DPLOT 4070 SEC PLOT CX+DX,CY-DY 4080 LDA CY 4090 SBC DY 4100 JSR DPLOT 4110 SEC PLOT CX-DX,CY+DY 4120 LDA CX 4130 SBC DX 4140 TAY 4150 CLC 4160 LDA CY 4170 ADC DY 4180 JSR DPLOT 4190 SEC PLOT CX-DX,CY-DY 4200 LDA CY 4210 SBC DY 4220 JMP DPLOT 4230 *-------------------------------- 4240 X1 .BS 1 4250 Y1 .BS 1 4260 SX .BS 1 4270 SY .BS 1 4280 DX .BS 1 4290 DY .BS 1 4300 D .BS 1 4310 CX .BS 1 4320 CY .BS 1 4330 *-------------------------------- 4340 .LIF |
Next I wrote a normal hi-res version. The listing that follows is a stand-alone program, but you could adapt it to work with Applesoft or to incorporate the CIRCLE subroutine in your own program. I used three subroutines in the Applesoft ROM to do the actual plotting, so the Applesoft ROMs have to be switched on for the program to run. Since I ran it from within the DOS 3.3 version of my assembler, I had to put line 1180 at the beginning to turn on the ROM. Line 1430 puts it back to the language card RAM.
The demonstration part of this program starts at line 1170. I used a macro named CIRCLE again, but the definition is slightly different (see lines 1080-1140). Since the X-coordinate can now be from 0 to 279, it will take two bytes to store. Therefore I wrote my macro to first pick up the two bytes of the X-coordinate in X and Y, get the single byte of the Y-coordinate in A, and then call CENTER to store them in some standard variables. Then I pick up the radius in A and call CIRCLE. The demo draws two "faces" and two circles which go off the edge of the screen. The latter two demonstrate the feature I added to detect off-screen points and bypass plotting them.
The Applesoft ROM subroutines I used are HGR, HCOLOR, and HPLOT. HGR turns on the primary hi-res screen and clears it. HCOLOR is inside the HCOLOR= processor, and expects a color value (0...7) in the X-register. HPLOT expects the X-coordinate in the A-register, and the Y-coordinate in the Y- and X-registers. Calling HPLOT is much faster in machine language, because all reference to floating point values is avoided.
I wrote my own caller for HPLOT in lines 2590-2720. It first checks the coordinates to see if they are on the screen. If not, the subroutine returns without trying to plot. If they are on the screen, I save X and Y and call HPLOT. I saved X and Y here so that I could use them again to plot another point with the same X-coordinate.
I also added a tiny bit of error checking to the CIRCLE subroutine. If the radius is larger than 127 line 1560 catches it and rings the bell. I could have handled larger radii, but it would have required more of the arithmetic to be done in 16-bit precision.
The rest of the code in CIRCLE is very similar to the double lo-res version. I had to go to 16-bit precision for the "D" calculations, and that is the main change.
1000 *SAVE S.HIRES.CIRCLES 1010 .OP 65C02 1020 *-------------------------------- 1030 HGR .EQ $F3E2 1040 HPLOT .EQ $F457 1050 HCOLOR .EQ $F6F0 1060 MON.BELL .EQ $FBDD 1070 *-------------------------------- 1080 .MA CIRCLE 1090 LDA #]2 CY 1100 LDX #]1 CX-LOW 1110 LDY /]1 CX-HI 1120 JSR CENTER 1130 LDA #]3 R 1140 JSR CIRCLE 1150 .EM 1160 *-------------------------------- 1170 TC 1180 LDA $C081 SELECT APPLESOFT ROMS 1190 JSR HGR Turn on HI-RES 1200 LDX #3 Set Color = White 1210 JSR HCOLOR 1220 *-------------------------------- 1230 >CIRCLE 40,20,19 1240 >CIRCLE 40,27,4 1250 >CIRCLE 30,15,3 1260 >CIRCLE 50,15,3 1270 *-------------------------------- 1280 >CIRCLE 140,40,39 1290 >CIRCLE 140,47,8 1300 >CIRCLE 130,35,3 1310 >CIRCLE 150,35,3 1320 *-------------------------------- 1330 >CIRCLE 140,60,80 1340 >CIRCLE 260,60,30 1350 *-------------------------------- 1360 .2 LDA $C000 1370 BPL .2 1380 STA $C010 1390 *-------------------------------- 1400 STA $C051 TEXT 1410 STA $C05F SINGLE 1420 STA $C054 1430 STA $C080 BACK TO S-C MACRO IN RAM 1440 RTS 1450 *-------------------------------- 1460 CENTER 1470 STA CY 1480 STX CX 1490 STY CX+1 1500 RTS 1510 *-------------------------------- 1520 BEEP JMP MON.BELL 1530 CIRCLE 1540 STA Y1 Y1 = R 1550 ASL 2*R 1560 BCS BEEP ...RADIUS TOO LARGE 1570 STA D D = 3 - 2*R 1580 SEC 1590 LDA #3 1600 SBC D 1610 STA D 1620 LDA #0 1630 STA X1 X1 = 0 1640 SBC #0 1650 STA D+1 1660 *-------------------------------- 1670 .1 LDX X1 1680 LDY Y1 1690 JSR PLOT.FOUR.POINTS 1700 LDX Y1 1710 LDY X1 1720 JSR PLOT.FOUR.POINTS 1730 *-------------------------------- 1740 LDA D+1 IF D <= 0 THEN GO TO .2 1750 BMI .2 1760 ORA D 1770 BEQ .2 1780 *-------------------------------- 1790 SEC 1800 LDA Y1 1810 SBC X1 1820 JSR TIMES.FOUR 1830 SEC 10 - 4*(Y-X) 1840 LDA #10 1850 SBC TEMP 1860 STA TEMP 1870 LDA #0 1880 SBC TEMP+1 1890 STA TEMP+1 1900 DEC Y1 1910 BNE .3 ...ALWAYS 1920 *-------------------------------- 1930 .2 LDA X1 6 + 4*X 1940 JSR TIMES.FOUR 1950 CLC 1960 LDA TEMP 1970 ADC #6 1980 STA TEMP 1990 LDA TEMP+1 2000 ADC #0 2010 *-------------------------------- 2020 .3 STA TEMP+1 2030 CLC D = D + TEMP 2040 LDA TEMP 2050 ADC D 2060 STA D 2070 LDA TEMP+1 2080 ADC D+1 2090 STA D+1 2100 INC X1 2110 LDA Y1 2120 CMP X1 2130 BCS .1 2140 .99 RTS 2150 *-------------------------------- 2160 TIMES.FOUR 2170 LDY #0 2180 STY TEMP+1 2190 ASL 2200 ROL TEMP+1 2210 ASL 2220 ROL TEMP+1 2230 STA TEMP 2240 RTS 2250 *-------------------------------- 2260 PLOT.FOUR.POINTS 2270 STX DX 2280 STY DY 2290 CLC CX+DX,CY+DY 2300 LDA CX 2310 ADC DX 2320 TAX 2330 LDA CX+1 2340 ADC #0 2350 TAY 2360 LDA CY 2370 ADC DY 2380 JSR CALL.HPLOT 2390 SEC CX+DX,CY-DY 2400 LDA CY 2410 SBC DY 2420 JSR CALL.HPLOT 2430 SEC CX-DX,CY+DY 2440 LDA CX 2450 SBC DX 2460 TAX 2470 LDA CX+1 2480 SBC #0 2490 TAY 2500 CLC 2510 LDA CY 2520 ADC DY 2530 JSR CALL.HPLOT 2540 SEC CX-DX,CY-DY 2550 LDA CY 2560 SBC DY 2570 *** JMP CALL.HPLOT 2580 *-------------------------------- 2590 CALL.HPLOT 2600 CMP #192 2610 BCS .2 OFF THE SCREEN 2620 CPY /280 2630 BCC .1 ON THE SCREEN 2640 BNE .2 OFF THE SCREEN 2650 CPX #280 2660 BCS .2 OFF THE SCREEN 2670 .1 PHX 2680 PHY 2690 JSR HPLOT 2700 PLY 2710 PLX 2720 .2 RTS 2730 *-------------------------------- 2740 X1 .BS 1 2750 Y1 .BS 1 2760 DX .BS 1 2770 DY .BS 1 2780 D .BS 2 2790 CX .BS 2 2800 CY .BS 1 2810 TEMP .BS 2 2820 *-------------------------------- 2830 .LIF |
I thought some of my fellow AAL readers might like these two programs I wrote for my IIgs. Much of the information I needed to write them came from the excellent book by Gary Bond, "Inside the Apple IIgs", from Sybex.
The graphics demo program will display all of the 4096 colors that are available on the Apple IIgs, 256 colors at a time. Pressing any key except ESCAPE will display the next 256 colors. There are 16 pages altogether. Pressing ESCAPE will exit the program.
The little loop in lines 1480-1580 clears the Super Hi-Res picture buffer. Another way which you might think would be faster is to use the MVN instruction with an overlapping move:
STZ $2000 LDA ##$7FFE LDX ##$2000 LDY ##$2001 MVN $E10000,$E10000
However, this takes longer. The MVN takes 7 cycles per byte moved, and we are moving 8191 bytes. The loop I used takes 13 cycles for each of 4096 byte-pairs. That is a savings of about 4096 cycles, but that is not all of the story. MVN is both loading and storing bytes in bank $E1, which is a slow-ram bank. Two cycles out off every seven must run at 1 megahertz instead of 2.8 megahertz. In my loop only two cycles out of every 13 are in slow RAM. I'll let you figure out exactly what this means in microseconds.
Notice above when I wrote the MVN instruction I used 24-bit values to specify the banks. A more intuitive MVN $E1,$E1 will not work. This is because Bob S-C figured the natural thing to do would be to use the actual label names for the areas being moved, and these would be 24-bit addresses. For example:
SHR.PIC .EQ $E12000 STZ >SHR.PIC LDA ##8190 LDX ##SHR.PIC LDY ##SHR.PIC+1 MVN SHR.PIC,SHR.PIC
The sound demo program makes use of the IIgs toolbox. The sound wave is generated by the Applesoft program listed below. I BSAVEd the sine wave data after running the Applesoft program. From the S-C Assembler I loaded and assembled the sound demo program, then BLOADed the sine wave data, and then typed "MGO S" to play the sine wave through the Ensoniq chip. Pressing any key stops the tone.
900 *SAVE SHR.URSCHEL 1000 *-------------------------------- 1020 * Super hires 256 color demo 1040 * 8-6-87 ru 1060 * 1080 .op 65816 1100 * 1120 scb.buff .eq $9d00 Scanline Control Buffer start 1140 pix.buff .eq $2000 Start of pixel buffer 1160 pal.buff .eq $9e00 Palette data 1180 kbd .eq $c000 1200 kbd.strb .eq $c010 1210 shr.sw .eq $c029 super hires switch 1215 border .eq $C034 border color stored here 1220 *-------------------------------- 1240 s clc native mode 1260 xce 1280 lda #$c1 turn on super hires 1300 sta shr.sw 1320 lda border 1340 pha save border color 1360 and #$f0 set border to black 1380 sta border 1400 lda #$e1 super hires in bank $e1 1420 pha 1440 plb 1460 rep #$30 16 bit regs 1480 * clear shr buffer 1500 ldx ##$7ffe clear 32k 1520 .1 stz pix.buff,x 1540 dex 1560 dex 1580 bpl .1 1600 *-------------------------------- 1620 * set up SCB's 1640 * 12 lines per SCB 1660 * 1680 setscb lda ##0 start with palette #0 1700 ldx ##0 1720 .2 ldy ##6 repeat for 12 display lines (a twofer) 1740 .1 sta scb.buff,x index into SCB buffer 1760 inx double inx since acc = 2 bytes 1780 inx 1800 dey done yet? 1820 bne .1 no, else.. 1840 clc 1860 adc ##$0101 set up SCB for next palette 1880 cmp ##$1010 16 palettes yet? 1900 bne .2 no 1920 *-------------------------------- 1940 * set up pixel data 1960 * 1980 setpix ldx ##0 2000 .3 lda ##0 2020 .2 ldy ##5 320 mode/16 per line = 20 bytes 2040 .1 sta pix.buff,x 2060 inx 2080 inx 2100 dey done 20 pixels yet? 2120 bne .1 no, else.. 2140 clc 2160 adc ##$1111 get next palette number 2180 cmp ##$1110 16 palette colors yet? 2200 bne .2 no, else.. 2220 cpx ##$7800 end of display buffer yet? 2240 bne .3 no, else.. 2260 *-------------------------------- 2280 * set up 256 colors in palettes 2300 * 2320 setcol lda ##0 2340 .2 ldx ##0 first palette 2360 .1 sta pal.buff,x save color 2380 clc 2400 adc ##1 inc color value 2420 inx 2440 inx 2460 cpx ##$200 fill 512 byte palette table 2480 bne .1 2500 pha save acc for next 256 colors 2520 sep #$20 8 bit acc 2540 .3 lda kbd 2560 bpl .3 2580 sta kbd.strb clear keyboard strobe 2600 cmp #$9b esc? 2620 beq end 2640 rep #$20 2660 pla get last color+1 2680 bra .2 display next 256 colors 2700 *-------------------------------- 2720 end 2740 sep #$30 back to 8 bit regs 2760 pla clean up stack 2780 pla 2800 lda #0 2820 pha 2840 plb back to bank $00 2860 sec emulation mode 2880 xce 2900 lda #$01 2920 sta shr.sw turn off super hires 2940 pla get back border color 2960 sta border 2980 rts 2990 .listoff |
900 *SAVE SOUND.DEMO.IIGS 1000 *-------------------------------- 1020 * Sound Demo 1040 * 8-1-87 ru 1060 * 1080 .OP 65816 1100 Toolbox .eq $e10000 1120 * 1140 S clc Native Mode 1160 xce 1180 rep #$30 16 bit registers 1200 pea $3000 GCB'S (Used by Toolbox: 256 bytes long) 1220 ldx ##$0208 SoundStartup Tool 1240 jsl Toolbox 1260 pea $0001 push the generator number and $01 1280 pea $0000 Push location of param table: 1300 pea $0300 4 bytes - z,b,h,l 1320 ldx ##$0e08 StartSound tool call 1340 jsl Toolbox 1360 sep #$20 set a reg to 8 bits 1380 .1 lda $c000 Wait for keypress 1400 bpl .1 1420 lda $c010 Reset keyboard strobe 1440 rep #$20 set a reg to 16 bits 1460 pea $ff7f push the mask to stop all generators 1480 ldx ##$0f08 StopSound tool 1500 jsl Toolbox 1520 ldx ##$0308 SoundShutdown tool 1540 jsl Toolbox 1560 sec 1580 xce back to emulation mode 1600 sep #$30 1620 rts 1640 *-------------------------------- 1660 * parameter table 1680 .or $300 1700 .hs 00.20.00.00 Wave Table addr in main ram: l,h,b,z 1720 .hs 04.00 wave table is four pages long 1740 .hs 00.0A frequency: l,h 1760 .hs 00.00 Wave Table addr in dedicated ram: l,h 1780 .hs 02 buffer size in dedicated ram: 256*2^value 1800 .hs 00 always $00 1820 .hs 00.03.00.00 next parameter block: l,h,b,z 1840 .hs ff volume level 1860 .listoff |
1000 REM SINE WAVE GENERATOR 4 CYCL ES X 256 BYTES/CYCLE 1024 BYTES TOTAL 1010 A = 8192 1020 R = (2 * 3.14159) / 360 1030 FOR J = 1 TO 4 1040 FOR I = 0 TO 361 STEP 1.411765 1050 S = INT ( SIN (R * I) * 128 + 128): IF S = 0 THEN S = 1 1060 POKE A,S: PRINT S,A:A = A + 1 1070 NEXT : NEXT
I discovered it the hard way: JMP (addr) and JML (addr) do not act quite like I expected them to.
In these two instructions the assembled address is a 16-bit value, telling the processor where to look to find the address you want to jump to. In the first form, JMP (addr), the effective jump address will be a 16-bit value, so the next instruction will be executed within the same bank as the JMP instruction. In the second form, JML (addr), the effective jump address will be a 24-bit value, specifying the new bank as well as the position in the bank.
But which bank does the processor look in to find the indirect address? There are three possibilities: it could look in the bank specified by the Data Bank Register, the bank specified by the Program Bank Register, or it could always look in Bank $00.
I ran into this after successfully using the JMP (addr,X) and JSR (addr,X) forms, in which the processor looks for an address table in the bank specified by the Program Bank Register. This is natural, because the table of addresses being accessed most likely is in the same bank as the JSR or JMP. It is only slightly restrictive, in that it requires me to be CERTAIN the table of addresses is in the same bank.
Naturally I assumed the JMP (addr) worked the same way. Naturally my assumption was wrong, and wasted a lot of time. Actually, the processor always looks in bank $00 for the indirect address when you use JMP (addr) or JML (addr). Bank $00? I don't know why. But it is right there in black and white on pages 379 and 383 in the Eyes & Lichty book. Strange.
The only rationale I can think of is that the indirect address might be stored in page zero. Come to think of it, this is a pretty common practice and hence it is probably a pretty good thing the processor works this way. One more thing to remember, though.
Here is a table showing all of the JMP and JSR forms, with the page number from the Eyes & Lichty book, the opcode value, the bank used for the indirect address, and the bank jumped into:
indirect destination Eyes Opcode & Syntax bank bank 379 4C JMP addr N/A PBR 383 6C JMP (addr) $00 PBR 382 7C JMP (addr,X) PBR PBR 385 5C JMP long n/a stated or JML long 384 DC JML (addr) $00 stated 379 20 JSR addr n/a PBR 382 FC JSR (addr,X) PBR PBR 385 22 JSR long n/a stated
By the way, I used the syntax JML (addr) for the long absolute-indirect Jump. Other assemblers use the syntax JMP [addr]. I chose the bracketless form because the older Apples have no left-bracket on the keyboard.
The form JMP (addr) is used in some existing programs written for the //e and older machines, referencing addresses which are within the body of the program. One such example is inside the code of BASIC.SYSTEM, where it stores the address of the code to process a command in a variable (not in page zero), and then JMPs indirect. Programs which do things like this will not run correctly if they are relocated to higher banks in a blind fashion, by merely setting the PBR and DBR to the new bank and expecting it to run. As I said, I learned the hard way.
You can make it work by setting X=0 and using the JMP (addr,X) form.
I mentioned using the JSR and JMP (addr,X) forms. These are very useful when you have a command processor that needs to branch or call according to a command index. We used to have to use a method which pushed an address from a table on the stack and then did an RTS. For example, a JSR COMMAND.DISPATCHER could call a command processor like this:
COMMAND.DISPATCHER LDA command.number ASL double it TAX LDA command.table+1,X high byte PHA LDA command.table,X low byte PHA RTS "Jump" to the command routine command.table .DA cmd.0-1,cmd.1-1,cmd.2-1 .DA cmd.3-1,cmd.4-1,cmd.5-1
Now we can do it an easier way. For example, the code segment:
LDA command.number ASL double it TAX JSR (command.table,X)
can call any of a series of subroutines whose addresses are in a list like this:
command.table .DA cmd.0,cmd.1,cmd.2 .DA cmd.3,cmd.4,cmd.5
"Eight Queens" is a classic chess puzzle. The Eight Queens problem is, how many ways can you put eight queens on a chessboard so that no queen can capture another? Obviously, each queen will have to be in a different row. Still, there are 8^8 = 16,777,216 ways to put eight queens on a board with each one in a different row. This is clearly a computation-intensive problem.
If you were to try to solve this problem in LISP on an 8-Megabyte VAX system (as AI programmers have a nasty tendency to do), you would end up with a recursive routine which might give you an answer the same day. I was curious how fast an Apple II can solve it, especially since there are 8 positions in each row of a chessboard, and 8 bits in a byte....
I wrote a brute-force routine. My chessboard is a 8*9 array. At all times, exactly one of the 9 positions in each of the 8 rows has a queen. The rightmost (9th) position in each row (held by the carry) is a placeholder which does not count toward a solution. It works like this:
Put all queens in the 9th position. X=1 Queen: Rotate the queen in row X left. If the queen was rotated from the leftmost position back into the 9th, then: Backtrack: X=X-1 If X=0 then end. Go to Queen. If the queen in row X is in the same column or diagonal as another, go to Queen. If X=8: Print this solution. Go to Backtrack. X=X+1 Go to Queen.
As written, QUEENS takes 19 seconds to display all 92 solutions, and just 1.9 seconds to find them without displaying. The mathematically minded may say, "But 92 is not a multiple of 8!" Since a chessboard can be flipped and rotated to get 8 symmetries, you expect a multiple of 8 solutions. But one of the solutions is the same when rotated 180 degrees, thus it provides only 4 symmetrical solutions. There are 12 unique solutions in all.
My display routine, in lines 1620 through the end, works best in 40-column mode. It displays what really looks like a chess board, using inverse blank or inverse Q for one color and normal blank or Q for the other color.
Bob S-C wrote an alternate display routine, shown after my listing as lines 1900 and following, which displays the solutions in numeric form. This is considerable faster, and all 92 solutions will fit on one 80-column screen. In Bob's notation, one digit is displayed for each row of the chess board. The digit shows the position of the queen on that row, with 0 being the leftmost square and 7 being the rightmost square. Bob used conditional assembly to control which display routine is assembled. When line 1000 says "GOETZ .EQ 1" my routine is assembled; when it says "GOETZ .EQ 0" Bob's routine is assembled.
1000 *SAVE S.EIGHT QUEENS 1010 *-------------------------------- 1020 BOARD .EQ $10 thru $17 1030 MASK .EQ $18 1040 XSAV .EQ $19 1050 COUNTER .EQ $1A 1060 *-------------------------------- 1070 MON.CROUT .EQ $FD8E 1080 MON.COUT .EQ $FDED 1090 *-------------------------------- 1100 QUEENS 1110 LDX #7 CLEAR THE CHESS BOARD 1120 LDA #0 1130 .0 STA BOARD,X 1140 DEX 1150 BPL .0 1160 * X=FF 1170 .1 INX NEW ROW 1180 CPX #8 1190 BEQ .9 FINISHED 1200 SEC INTRODUCE 1 QUEEN INTO ROW 1210 .2 ROL BOARD,X 1220 BCS .8 THIS ROW FAILED, BACKTRACK 1230 TXA 1240 BEQ .1 ALWAYS SUCCEEDS WI 1ST ROW 1250 STX XSAV 1260 LDA BOARD,X 1270 STA MASK 1280 .3 EOR BOARD-1,X WRAP AROUND TO 0-6 1290 DEX 1300 BNE .3 1310 AND MASK 1320 * WON'T HAVE MORE THAN 2 QUEENS IN 1 COLUMN 1330 BEQ .7 FAIL 1340 LDA #$A ASL 1350 .4 STA .5 1360 LDX XSAV 1370 LDA MASK 1380 .5 ASL CHECK DIAGONAL 1390 BCS .6 DIAGONAL RAN OFF EDGE 1400 EOR BOARD-1,X 1410 BEQ .7 FAIL 1420 EOR BOARD-1,X CHANGE BACK TO SHIFTED 8 1430 DEX 1440 BNE .5 1450 .6 LDA #$4A LSR 1460 CMP .5 1470 BNE .4 GO BACK & CHECK OTHER DIAGONAL 1480 LDX XSAV 1490 BNE .1 ALWAYS 1500 .7 LDX XSAV 1510 INX 1520 .8 CLC 1530 DEX BACKTRACK 1540 BPL .2 STILL A CHANCE 1550 RTS NO MORE SOLUTIONS 1560 * PRINT BOARD & GO BACK FOR NEXT ONE 1570 .9 LDX #0 1580 .10 LDA BOARD,X 1590 LDY #8 1600 STY COUNTER 1610 .11 PHA 1620 CLC 1630 TXA 1640 ADC COUNTER A=ROW(0-7) + (8-COLUMN(0-7)) 1650 LSR LOW BIT -> C 1660 LDA #$7F 1670 BCC .12 1680 LDA #$FF BIT 6 WILL = BIT 7 1690 .12 ROR C -> HI BIT 1700 STA MASK 1710 PLA 1720 LDY #$A0 SPACE 1730 ASL 1740 BCC .13 1750 LDY #$D1 Q 1760 .13 PHA 1770 TYA 1780 AND MASK 1790 JSR MON.COUT 1800 PLA 1810 DEC COUNTER 1820 BNE .11 1830 JSR MON.CROUT NEXT ROW 1840 INX 1850 CPX #8 1860 BNE .10 1870 JSR MON.CROUT SPACE BETWEEN BOARDS 1880 JMP .8 WILL GO BACK TO .2 1890 *-------------------------------- |
1000 GOETZ .EQ 1 1010 *SAVE S.EIGHT QUEENS.RBSC 1020 *-------------------------------- 1030 BOARD .EQ $10 thru $17 1040 *-------------------------------- 1050 MON.INVFLG .EQ $32 1060 MON.CROUT .EQ $FD8E 1070 MON.COUT .EQ $FDED 1080 *-------------------------------- 1090 QUEENS 1100 LDX #7 1110 LDA #0 1120 .0 STA BOARD,X 1130 DEX 1140 BPL .0 1150 *---Next row down---------------- 1160 .1 INX NEW ROW 1170 CPX #8 1180 BEQ .9 ...Finished, have a solution 1190 SEC INTRODUCE 1 QUEEN INTO ROW 1200 *---Next position in row--------- 1210 .2 ROL BOARD,X 1220 BCS .8 THIS ROW FAILED, BACKTRACK 1230 TXA 1240 BEQ .1 ...First row, any position okay 1250 *---Test if any in same column--- 1260 TAY START WITH CURRENT ROW 1270 LDA BOARD,Y 1280 .3 CMP BOARD-1,Y 1290 BEQ .7 ...Same column 1300 DEY 1310 BNE .3 1320 *---Test if any in same /-------- 1330 TXA START WITH CURRENT ROW 1340 TAY 1350 LDA BOARD,Y 1360 .4 LSR CHECK "/" DIAGONAL 1370 BCS .5 DIAGONAL RAN OFF EDGE 1380 CMP BOARD-1,Y 1390 BEQ .7 ...ON SAME DIAGONAL 1400 DEY 1410 BNE .4 1420 *---Test if any in same \-------- 1430 .5 TXA START WITH CURRENT ROW 1440 TAY 1450 LDA BOARD,Y 1460 .6 ASL CHECK "\" DIAGONAL 1470 BCS .1 ...Diagonal ran off edge 1480 CMP BOARD-1,Y 1490 BEQ .7 ...ON SAME DIAGONAL 1500 DEY 1510 BNE .6 1520 *---This position looks good----- 1530 BEQ .1 ALWAYS 1540 *---This position failed--------- 1550 .7 CLC 1560 BCC .2 ...Always 1570 *---Backtrack-------------------- 1580 .8 CLC 1590 DEX BACKTRACK 1600 BPL .2 STILL A CHANCE 1610 RTS NO MORE SOLUTIONS 1620 *---Print the solution----------- 1630 .DO GOETZ 1640 .9 JSR MON.CROUT 1650 LDX #0 1660 .10 LDA BOARD,X 1670 PHA 1680 LDY #8 1690 .11 LDA MON.INVFLG 1700 EOR #$C0 1710 STA MON.INVFLG 1720 PLA 1730 ASL 1740 PHA 1750 LDA #" " 1760 BCC .12 1770 LDA #"Q" 1780 .12 JSR MON.COUT 1790 DEY 1800 BNE .11 1810 PLA 1820 LDA MON.INVFLG 1830 EOR #$C0 1840 STA MON.INVFLG 1850 JSR MON.CROUT NEXT ROW 1860 INX 1870 CPX #8 1880 BCC .10 1890 .ELSE 1900 .9 LDX #0 1910 .10 LDA BOARD,X 1920 LDY #0 1930 .11 ASL 1940 BCS .12 1950 INY 1960 BNE .11 1970 .12 TYA 1980 ORA #"0" 1990 JSR MON.COUT 2000 INX 2010 CPX #8 2020 BCC .10 2030 LDA #" " 2040 JSR MON.COUT 2050 JSR MON.COUT 2060 .FIN 2070 JMP .8 Will CLC, set X=7, go to .2 2080 *-------------------------------- |
Some years ago I wrote version 2 of the PromGramer software for SCRG. One of the new features I added, at the request of Phil's wife, was a pair of termination tunes. If an EPROM successfully programs, the program twiddles Apple's speaker just right to make a cheerful four-note fanfare. Failure, on the other hand, sounds a six-note SS siren.
I call the two tunes NICE and NASTY, and I thought you might like them for your own programs. If not, the same driver can be used to make other tunes. You can play the NICE tune by calling NICE, or NASTY by calling NASTY.
The tunes are encoded in lines 1280-1320. Each note takes two bytes, the first specifying the length of a half-cycle and the second specifying the number of half-cycles. In other words, pitch and duration. I arrived at the numbers by counting machine cycles for the loops, figuring in the Apple clock speed and the frequency for middle-A, and calculating a few notes by hand. Not very sophisticated, but it works nicely.
I decided to try writing a different one, using a technique that plays equal length notes. Lines 1600-1730 are the note player. Lines 1750-1820 call on it to play the NICE tune. PLAY.NOTE.A counts down a duration count simultaneously with the pitch count, so that you get the same total duration regardless of the pitch. This method is not original with me, but I don't remember where it came from. (Maybe the original Apple documentation.)
The $C030 Apple speaker may be obsolete beside the IIgs Ensoniq chip, but I still like it. R2D2 was no Caruso, either....
1000 *-------------------------------- 1010 *SAVE S.SIGNAL.TUNES 1020 *-------------------------------- 1030 T1 .EQ $00 1040 T2 .EQ $01 1050 *-------------------------------- 1060 NICE LDY #TUNE.NICE 1070 .HS 2C 1080 NASTY LDY #TUNE.NASTY 1090 PLAY.TUNE 1100 .1 LDA TUNES,Y 1110 BEQ .4 1120 STA T1 1130 LDA TUNES+1,Y 1140 STA T2 1150 *---PLAY THE NOTE---------------- 1160 .2 LDA $C030 1170 LDX T1 1180 .3 DEX 1190 BNE .3 1200 DEC T2 1210 BNE .2 1220 *---NEXT NOTE-------------------- 1230 INY 1240 INY 1250 BNE .1 ...ALWAYS 1260 .4 RTS 1270 *-------------------------------- 1280 TUNES 1290 TUNE.NICE .EQ *-TUNES 1300 .HS BB.8A..94.AD..BB.8A..7C.CE..00 1310 TUNE.NASTY .EQ *-TUNES 1320 .HS 94.AD..BB.8A..94.AD..BB.8A..94.AD..BB.8A..00 1330 TUNE.LO .EQ *-TUNES 1340 .HS BB.8A..00 1350 TUNE.HI .EQ *-TUNES 1360 .HS 94.AD..00 1370 *-------------------------------- 1380 T 1390 .1 LDA $C000 1400 BPL .1 1410 STA $C010 1420 EOR #$B0 1430 CMP #8 1440 BCC .2 1450 RTS 1460 .2 SEC 00000ABC 1 1470 ROR 100000AB C 1480 ROR C100000A B 1490 ROR BC100000 A 1500 .3 PHA 1510 LDY #TUNE.LO 1520 BCC .4 ...LOW 1530 LDY #TUNE.HI 1540 .4 JSR PLAY.TUNE 1550 PLA 1560 ASL B C1000000, C 1000000, 1 0000000 1570 BNE .3 DO B OR C 1580 JMP .1 FINISHED 1590 *-------------------------------- 1600 PLAY.NOTE.A 1610 TAX 1620 LDA /12000 1630 SEC 1640 STX T1 1650 .1 DEX 1660 BNE .3 1670 .2 LDX T1 1680 BIT $C030 1690 .3 DEY 1700 BNE .1 1710 SBC #1 1720 BCS .1 1730 RTS 1740 *-------------------------------- 1750 M.NICE LDA #$5D 1760 JSR PLAY.NOTE.A 1770 LDA #$4A 1780 JSR PLAY.NOTE.A 1790 LDA #$5D 1800 JSR PLAY.NOTE.A 1810 LDA #$3E 1820 JMP PLAY.NOTE.A 1830 *-------------------------------- 1840 M 1850 LDA #20 1860 .1 PHA 1870 BIT $C000 1880 BMI .2 1890 JSR PLAY.NOTE.A 1900 PLA 1910 CLC 1920 ADC #1 1930 BNE .1 1940 RTS 1950 .2 STA $C010 1960 PLA 1970 RTS 1980 *-------------------------------- 9999 .LIF |
Apple has announced their new BASIC interpreter for the IIgs. No, it is not an upgraded Applesoft. It is a totally different version, appearing to me to be based on the old Apple /// Business Basic. GSBASIC runs under ProDOS-16, and supports all of the IIgs tools and features.
Features include 9- or 15-digit precision in floating point with exponent range from -308 to +308, plus short and long integers (sounds like SANE); PRINT USING; variable names up to 30 characters long; use of labels rather than line numbers for GOTO and GOSUB; editing features including search/replace; built-in disk file I/O commands; direct statements for access to the various IIgs Tools; and, you guessed it, more! The scheme for linking to assembly language code is the same as in Apple /// Business Basic, via INVOKE, EXFN, and PERFORM statements.
Negatives include the new tradition of extremely long booting time; total in-compatibility with Applesoft programs; requirement for a minimum (MINIMUM!) of 512K RAM; will not work under DOS, ProDOS, or any machine other than a IIgs. It turns out trying to use GSBASIC with less than 1024K is not really reasonable.
The pre-release disk includes ProDOS-16 version 1.3 with the FINDER, GSBASIC, and a sample program which displays four Super Hi-Res pictures under mouse control.
Some interesting trivia: instead of CATALOG and CAT, we now have CATALOG and DIR. Both display an 80-column list of files in the current directory. CATALOG looks a lot like our old ProDOS-8 favorite, and DIR is just a slight revision of it. DIR displays a tiny icon in front of each filename, leaves out the creation date, rounds up the file size to the nearest Kbytes, and spells out the access bits.
If you hit Ctrl-RESET, you are in for a surprise. You get the marvelous message "CANNOT RESET -- $0602", a big beep, and the see-sawing Apple. If you reset again you end up in the monitor. No problem, just boot again, wait five minutes for it all to reload, and then type in your whole program over again!
GSBASIC is available now in pre-release form (beta version 1.0B4) from APDA (Apple Programmer's Development Association), for $50. This is a very low price for such a large system. Applesoft originally cost $100, but of course it came on a ROM card. Applesoft is only 12K bytes long. GSBASIC is currently about 59K bytes long, and growing.
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.)