March 26, 2010 · Comments Off on Structured Applesoft online supplements

Shortly after Ivan Drucker joined the Juiced.GS staff last fall, he shared with me a concept he was working on: Structured Applesoft. It's a new way of programming in a familiar language that makes the programs easier to develop, read, and edit, all without requiring new software, routines, or patches.

Structured Applesoft screenshot

TextWrangler will use Juiced.GS's plist to colorize Structured Applesoft code.

Ivan started detailing the concepts of Structured Applesoft in the December 2009 issue and finished in March 2010. The limits of the print edition prevented him from fully outlining all the guidelines he'd devised, though, so we decided to save some of the more esoteric ideas for online, where interested parties could explore them without pushing out more general-interest topics from the print edition.

In the process of crafting this final installment, Ivan found an obscure error with some of the code that was printed in this week's issue of Juiced.GS. Though a correction will appear in the June issue, we decided to include this update with the other content that was already intended for our Web site.

The correction and two supplements are now available online. You can view the material as a Web page or as a Juiced.GS-style PDF. Ivan has also provided a sample program that demonstrates these Structured Applesoft concepts. Links to these pages have been added to the issue links for Volume 15, Issue 1.

We hope you find these files to be useful elaborations on this approach to BASIC programming. Thanks to Ivan for pursuing this topic so thoroughly!

March 25, 2010 · Comments Off on Plotter II

Here is the code listing for Plotter II, an update to the original Plotter that demonstrates the concepts of Structured Applesoft, as defined by Juiced.GS staff writer Ivan Drucker in Volume 14, Issue 4, and Volume 15, Issue 1. This code is also available in SHK and ZIP archive formats.

See also the online supplements to the print material.


REM Plotter v1.1, March 2010
REM   by Ivan Drucker

REM This program plots random hi-res pixels.
REM It changes color when a user presses a key.
REM If the key is 0-7, it uses that color.
REM C clears the screen. Q or ESC quits.
REM If it's any other key, the computer beeps.

REM This program demonstrates the syntax of Structured Applesoft.
REM It illustrates most of the concepts described in the Structured Applesoft articles
REM in the December 2009 and March 2010, issues of Juiced.GS, as well the online bonus
REM material at http://www.juiced.gs. Specific concepts demonstrated in this program:

REM   source code written in editor, then pasted into emulator or EXEC'd on Apple II
REM   subroutine-oriented program flow with minimal main routine
REM   organization of code (global variable declaration 10-99, 100-9999 main routine, 10000-> subroutines)
REM   self-contained, specific-purpose subroutines with labels, parameters and return values
REM   IF, FOR, WHILE, and TRY (with bug fix for March 2010 article) structures
REM   global and local variable and array declaration, and constant indication
REM   indentation as part of structures
REM   blank lines for clarity
REM   extensive non-numbered commenting
REM   user comments separated with :: from structure part comments
REM   using QuitFlag to perform shutdown operations along the way to the program end
REM   consistent, clear syntax with regard to parantheses, colons, and other details
REM   one statement per line except for structure parts or logically related statements
REM   temporary variables
REM   no GOTO except in structure parts

REM Not demonstrated:
REM   SWITCH structure
REM   THROWing errors
REM   using FINALLY with THROW or RETURN from a catch block
REM   every variety of IF and WHILE structure

REM Note: it is strongly recommended that you view this Structured Applesoft
REM source file in TextWrangler (free) or BBEdit (both at http://www.barebones.com)
REM with the Applesoft BASIC language module installed. You can get the module
REM from http://www.juiced.gs in the Dec 2009 Issue Links under Plotter.
REM
REM To install it, right-click on TextWrangler or BBEdit, choose Show Package Contents,
REM and drop it into Contents/Resources/Language Modules. Then restart the editor,
REM open this file, and choose Applesoft BASIC from the language menu at the bottom
REM of the window.

REM ----PROGRAM STARTS HERE-----

NEW

REM lines 0-9 are reserved

10 REM Initial Setup

REM declare global variables
40 LET QT = 0 : REM QT->quitFlag
41 LET PC = 1 : REM PC->pixelColor

REM declare global constants
50 DIM CN$(7) : REM CONSTANT; CN$->colorNames
51 CN$(0) = "BLACK 1" : CN$(1) = "GREEN" : CN$(2) = "VIOLET" : CN$(3) = "WHITE 1"
52 CN$(4) = "BLACK 2" : CN$(5) = "ORANGE" : CN$(6) = "BLUE" : CN$(7) = "WHITE 2"
55 LET E$ = CHR$(27) : REM CONSTANT E$->ESC key

100 REM Main Routine
    110 TEXT : HOME
    115 HGR

    150 P1$ = "INITIAL COLOR?" : GOSUB 11200 : REM AskAndSetColor

    160 REM IF
    161 ON (NOT QT) GOTO 190 : GOTO 300
        190 GOSUB 10400 : REM DisplayColorName
        200 REM WHILE
        201 ON (NOT QT) GOTO 210 : GOTO 289
            210 GOSUB 10100 : REM CheckForKey
            220 IF (R1$<>"") THEN P1$ = R1$ : GOSUB 10200 : REM HandleKeyPress
            240 GOSUB 10000 : REM ChoosePlotSpot
            250 P1 = R1 : P2 = R2 : GOSUB 10700 : REM PlotPixel
            260 P1 = 1 : GOSUB 11100 : REM Delay
        288 GOTO 200
        289 REM END WHILE
    290 GOTO 330
    300 REM ELSE :: initial color entry error, or ESC
        310 GOSUB 11000 : REM DrawBigX
        320 P1 = 1 : GOSUB 11100 : REM Delay
    330 REM END IF
    
    350 GOSUB 10900 : REM SayGoodbye

    499 REM END IF

9999 END

REM Subroutines

10000 REM SUB^ChoosePlotSpot:10000-10099 {}
    REM Randomly picks and returns hi-res coordinate
    REM  in: nothing
    REM out: R1->H coordinate (0-279), R2->V coordinate (0-161)

    REM indicate use of global variable in this routine
    10001 REM global PC

    REM get horizontal position
    10010 REM IF
        10011 ON (PC=0 OR PC=3 OR PC=4 OR PC=7) GOTO 10015 : GOTO 10030
        REM if color is black or white, plot any horizontal pixel
        10015 ZZ = INT(RND(1) * 279)
    10020 GOTO 10060
    10025 REM ELSE
        REM plot color 1 or 5 on odd horizontal pixels, 2 or 6 on even
        10030 ZZ = INT(RND(1) * 139)
        10040 ZZ = (ZZ * 2)
        10050 IF (PC=1 OR PC=5) THEN ZZ = (ZZ + 1)
    10060 REM END IF
    10070 R1 = ZZ

    REM get vertical position 
    10080 R2 = INT(RND(1)*160)   
10099 RETURN

10100 REM SUB^CheckForKey:10100-10199 {}
    REM Checks to see if the user pressed a key, and returns it if so.
    REM  in: nothing
    REM out: R1$ = key pressed or "" if none

    REM declare local variable for this subroutine only
    10105 LET KB = 0 : REM KB->keyboardBufferValue
    
    10110 KB = PEEK(49152)

    10115 REM IF
    10116 ON (KB>127) GOTO 10120 : GOTO 10150
        10120 R1$ = CHR$(KB - 128) : REM convert ASCII value of key to string
        10130 POKE 49168,0 : REM clear KB buffer
        10135 GOSUB 10600 : REM QuickBuzz
    10140 GOTO 10160
    10150 REM ELSE
        10155 R1$ = "" : REM indicate no key pressed
    10160 REM END IF

10199 RETURN

10200 REM SUB^HandleKeyPress:10200-10299 {}
    REM Calls appropriate routine
    REM   based on which key was pressed
    REM  in: P1$ = key
    REM out: nothing
    
    10210 REM IF
    10211 ON (P1$="C" OR P1$="c") GOTO 10215 : GOTO 10230
        10215 HGR : REM Clear HiRes Screen
    10220 GOTO 10290
    10230 REM ELSE IF
    10231 ON (P1$="Q" OR P1$="q" OR P1$=E$) GOTO 10235 : GOTO 10250
        10235 GOSUB 10800 : REM ShutDown
    10240 GOTO 10290
    10250 REM ELSE IF
    10251 ON (ASC(P1$)>47 AND ASC(P1$)<56) GOTO 10260 : GOTO 10280
        REM check for 0 through 7, indicating color should be changed
        10260 P1 = VAL(P1$) : GOSUB 10300 : REM ChangePixelColor
        10265 GOSUB 10400 : REM DisplayColorName
    10270 GOTO 10290
    10280 REM ELSE
        10285 GOSUB 10500 : REM Beep
    10290 REM END IF
10298 RETURN

10300 REM SUB^ChangePixelColor:10300-10399 {}
    REM Assigns the pixel color; persists until changed
    REM in: P1 = color to be set (0-7)
    REM out: nothing
    REM globals: PC will contain new color value

    10301 REM global PC
    10310 PC = P1
    10320 HCOLOR = PC
10399 RETURN

10400 REM SUB^DisplayColorName:11200-11299 {}
    REM Prints name of current color in text area
    REM in/out: nothing

    REM indicate use of global in this routine
    10401 REM GLOBAL PC

    10410 VTAB 22
    10420 PRINT CN$(PC);"      "
10499 RETURN

10500 REM SUB^Beep:10500-10599 {}
    REM Beeps once, and that's all
    REM in/out: nothing
    10510 CALL -1052
10599 RETURN

10600 REM SUB^QuickBuzz:10600-10699 {}
    REM buzzes speaker briefly
    REM in/out: nothing
    10610 FOR ZZ=1 TO 5 : POKE -16336,0 : NEXT ZZ
10699 RETURN

10700 REM SUB^PlotPixel:10700-10799 {}
    REM Draws a dot on the HGR screen, or a big X if quitting
    REM parameters: P1:H position, P2:V position

    REM indicate use of global variable in this routine
    10701 REM GLOBAL QT
    REM Declare local vars for this routine
    10705 LET PX=P1 : REM PH->pixelHPos
    10706 LET PY=P2 : REM PV->pixelVPos

    10710 REM IF
    10711 ON (NOT QT) GOTO 10730 : GOTO 10750 : REM if not quitting
        10730 HPLOT PX,PY : REM plot the point
    10740 GOTO 10770
    10750 REM ELSE; program is quitting, so handle
        10760 GOSUB 11000 : REM DrawBigX
    10770 REM END IF
10799 RETURN

10800 REM SUB^ShutDown:10800-10899 {}
    REM Initiates program exit. quitFlag is set; other
    REM   routines can test for it and clean things up
    REM   if needed.
    REM in/out: nothing
    REM globals: QT set to 1
    
    10801 REM GLOBAL QT
    10810 QT = 1 : REM set quitFlag
10899 RETURN

10900 REM SUB^SayGoodbye:10900-10999 {}
    REM sets text mode, clears screen, says bye to user
    REM in/out: nothing
    10910 TEXT
    10920 HOME
    10930 PRINT "BYE!"
10999 RETURN

11000 REM SUB^DrawBigX:11000-11099 {}
    REM Draws a big white X across the screen and waits for a short duration
    REM    used during quit
    REM in/out: nothing
    REM HCOLOR will be 3; note global PC will remain what it was
    
    11010 P1 = 3 : GOSUB 10300 : REM ChangePixelColor
    11020 HPLOT 0,0 TO 279,159
    11030 HPLOT 0,159 TO 279,0
    11050 P1 = 3 : GOSUB 11100 : REM Delay
11099 RETURN

11100 REM SUB^Delay:11100-11199 {}
    REM Does nothing for a length of time
    REM in: P1->delay multiplier
    REM out: nothing
    11110 FOR ZZ = 1 TO P1
        11115 FOR ZY = 1 TO 75 : NEXT ZY
    11120 NEXT ZZ
11199 RETURN

11200 REM SUB^AskAndSetColor:11200-11499 {}
    REM Prompts user for color (and demonstrates advanced error handling in a while loop)
    REM  in: P1$ = prompt string
    REM out: nothing
    REM for a full explanation of the error handling below, see the article
    REM   bonus material, in the March 2010 Issue Links at http://www.juiced.gs

    11205 LET IC = 0 : REM IC->Initial Color
    11206 LET PS$ = P1$ : REM PS$->Prompt String
    11207 LET OK = 0 : REM OK->if true, ready to return, if false, loops again
    11208 LET R1 = -1 : REM R1$->default return value

    11210 REM WHILE
    11211 ON (NOT OK) GOTO 11230 : GOTO 11489

        11230 REM TRY
        11231 Z9 = PEEK(248) : ONERR GOTO 11300
            11240 VTAB 21 : HTAB 1 : PRINT PS$;
            REM next line could throw a REENTER error on non-numeric input
            11250 INPUT "";IC
            REM GOSUB 10300 could throw an ILLEGAL QUANTITY error if IC > 7
            11260 P1 = IC : GOSUB 10300 : REM ChangePixelColor
            REM clean up text
            11270 HOME
            REM set return value and indicate ready to return
            11280 OK = 1
        11290 POKE 216,0 : GOTO 11400
    
        11300 REM CATCH
        11301 POKE 216,0 : ER = PEEK(222) : POKE 223,Z9 : CALL -3288
    
        11310 REM CATCH ERROR: REENTER :: if user enters a non-number
        11311 ON (ER=254) GOTO 11320 : GOTO 11330
            11320 PRINT : PRINT SPC(39) : HTAB 1
            11321 PRINT "PLEASE ENTER A NUMBER."
        11329 GOTO 11400
    
        11330 REM CATCH ERROR: ILLEGAL QUANTITY :: if HCOLOR > 7
        11331 ON (ER=53) GOTO 11340 : GOTO 11380
            11340 PRINT : PRINT SPC(39) : HTAB 1
            11345 PRINT "VALID COLORS ARE 0 THROUGH 7."
        11349 GOTO 11400
        
        11380 REM CATCH OTHERS
            REM always required, even if it does nothing
            REM in this case, only possible other error is BREAK (CTRL-C)
            REM if we get that, or anything else unexpected,
            REM   quit via QuitFlag (flow out of program, shutting down along the way)
            11382 OK = 1
            11383 GOSUB 10800 : REM ShutDown
        11389 GOTO 11400

        11400 REM FINALLY
        11401 GOSUB 11410 : GOTO 11450
            REM always buzzes after entry
            11410 GOSUB 10600 : REM QuickBuzz
        11420 RETURN
        
        11450 REM END TRY

    11488 GOTO 11210
    11489 REM END WHILE

11499 RETURN
March 25, 2010 · Comments Off on Structured Applesoft

This page is also available as a PDF. See also the Plotter II sample code.

Correction

In the article "Hardcore Applesoft" in the March 2010 issue of Juiced.GS, there is a bug in the TRY structure (Figure 4 in that issue's page 21) which makes it unsafe to use GOSUB or FOR…NEXT from a TRY block inside a subroutine. To fix this problem, change the following lines as shown. Every TRY structure in your program will need its own unique variable name, not used elsewhere, in place of XX.

10101 XX = PEEK(248) : ONERR GOTO 10150
10151 POKE 216,0 : ER = PEEK(222) : POKE 223,XX : CALL -3288

This makes Applesoft forget about returning to any GOSUB or FOR which came after the start of the TRY block, so RETURN from your subroutine works correctly, rather than jumping back inside the TRY block. In technical terms, the PEEK gets the stack index at the outset of the TRY, and the POKE/CALL restores it, thus erasing anything put on the stack by GOSUB or FOR after the TRY started.

SWITCH…CASE

In the second installment of our series on Structured Applesoft, appearing in the March 2010 issue of Juiced.GS, we examined IF…ELSE IF…ELSE structures for decision making. Languages such as C and Java provide an additional decision making structure, SWITCH…CASE, which, after the first true "case" (evaluation), will "fall through" all remaining cases as through they were also true, executing their code blocks until a BREAK (if present) is reached.

The SWITCH structure can also include an optional DEFAULT block at the end which always executes unless there is a BREAK which precedes it. (Other languages permit DEFAULT anywhere in the SWITCH structure, where it will execute only if subsequent cases aren't true, but this style is rarely used and not easily accomplished in Applesoft.)

SWITCH…CASE…DEFAULT is far less commonly used than IF…ELSE IF…ELSE, but for those who wish to use it, it can be structured in Applesoft as shown in Figures 1 and 2. To BREAK out of the SWITCH structure from any CASE, GOTO the END SWITCH line and conclude with REM BREAK. Example: 10290 GOTO 10350 : REM BREAK


10100 REM SWITCH
10101 ZZ = 0
    10110 IF (p1=1) THEN ? 1 : ZZ = 1 : REM CASE
    10120 IF ( ZZ OR (p1=2) ) THEN ? 2 : ZZ = 1 : REM CASE
    10130 IF ( ZZ OR (p1=3) ) THEN ? 3 : ZZ = 1 : REM CASE
    10140 ? 0 : REM DEFAULT
10150 REM END SWITCH

Figure 1. A Structured Applesoft single-line (per case) SWITCH structure.


10100 REM SWITCH
10101 ZZ = 0
10150 REM CASE
10151 ON (condition) GOTO 10152 : GOTO 10200
10152 ZZ = 1
    10160 statement
    10170 etc
10200 REM CASE
10201 ON ( (condition) OR ZZ ) GOTO 10202 : GOTO 10250
10202 ZZ = 1
    10210 statement
    10220 etc
10250 REM CASE
10251 ON ( (condition) OR ZZ ) GOTO 10252 : GOTO 10300
10252 ZZ = 1
    10260 statement
    10270 etc
10300 REM DEFAULT
    10310 statement
    10320 etc
10350 REM END SWITCH

Figure 2. A Structured Applesoft multiple-line (per case) SWITCH structure.


TRY, TRY Again

Our last issue also included a look at how to handle errors in an Applesoft program with a TRY structure. With this method, your program "tries" to execute code in a TRY block, and if an error occurs, it is "thrown" to a subsequent CATCH block, which handles it. (This supplement assumes you've read the article.)

Here, we'll discuss more options for your TRY structure. In addition to TRY and CATCH, you can use one or more CATCH ERROR blocks to act on specific errors, a FINALLY block for concluding code which always executes whether or not an error occurs, and you can THROW an error if you want the user to see it.

To check for specific errors, start with a standard CATCH as discussed in the main article, shown in lines 10150-10151 of Figure 3. This is followed with one or more CATCH ERROR blocks which act on specific errors by evaluating ER. These are followed by a CATCH OTHERS block, which is executed if the error is not anticipated by the previous CATCH ERROR blocks. (This is essentially the same structure as IF…ELSE IF…ELSE.) As shown in Figure 3, each ON line should be preceded with a separate line which says REM CATCH ERROR: followed by the error name(s) being checked for, and each catch block should conclude with a GOTO to FINALLY (discussed next), if present, or END TRY. The full list of errors and their corresponding codes is shown in Figure 4.

If you wish to have concluding code that always executes regardless of whether or not the TRY block throws an error, create a FINALLY block after your CATCH or CATCH OTHERS block. A common use of FINALLY is to close an open file or perform some other "cleanup" operation you want to have happen no matter what. To make this happen, you need a REM FINALLY line, followed by another line which GOSUBs to the subsequent line, and then on the same line (after the GOSUB), GOTOs the END TRY line. Your code then follows, and must conclude with RETURN. You can see this in lines 10350-10399 of Figure 3. The GOTO which concludes the TRY block and any catch blocks should jump to the FINALLY line rather than END TRY. If you want to RETURN or THROW (discussed next) from a catch block, you must GOSUB to the first line of your FINALLY code and append REM DO FINALLY, as shown in lines 10260 and 10310. This ensures there's no way out of your TRY structure without FINALLY executing.

What happens if the code that you tried threw an error, and your CATCH block caught it, but you want the user to see the error, as though it hadn't been caught? No problem; you deliberately THROW it back to the offending statement, which executes it again, and this time, the error will be displayed and the program will stop. To do this, use the RESUME command, followed by REM THROW, as shown in line 10310. Don't use RESUME for any other purpose, because it is a kind of GOTO, which we don't want in our structured program. In fact, because we want to show the error and stop the program, you should only THROW in scenarios where you are reasonably sure that the offending statement will behave the same way it did when it originally threw the error. If you wish to throw a CTRL-C (BREAK), don't use RESUME; instead, use POKE 117,PEEK(218) : POKE 118,PEEK(219) : STOP : RUN, as shown in line 10270 of Figure 3, which will print a BREAK statement with the correct line number, and restart the program if the user types CONT.


10000 REM SUB^OpenFile:10000-10499

    10010 LET D$ = CHR$(4) : REM D$->ctrl-D
    
    10100 REM TRY
    10101 XX = PEEK(248) : ONERR GOTO 10150
        10110 PRINT D$;"VERIFY ";P1$
        10120 PRINT D$;"OPEN ";P1$
        10130 PRINT D$;"CLOSE"
        10135 PRINT "opened and closed file successfully"
    10140 POKE 216,0 : GOTO 10350
    
    10150 REM CATCH
    10151 POKE 216,0 : ER = PEEK(222) : POKE 223,XX : CALL -3288
    
    10160 REM CATCH ERROR: FILE TYPE MISMATCH
    10161 ON (ER=13) GOTO 10170 : GOTO 10200
        10170 PRINT "ERROR: not a text file"
    10180 GOTO 10350

    10200 REM CATCH ERROR: FILE NOT FOUND, SYNTAX ERROR [DOS 3.3]
    10201 ON ( (ER=6) OR (ER=11) ) GOTO 10210 : GOTO 10250
        10210 PRINT "ERROR: invalid filename"
    10220 GOTO 10350

    10250 REM CATCH ERROR: BREAK (CTRL-C)
        10251 ON (ER=255) GOTO 10260 : GOTO 10300
        10260 GOSUB 10360 : REM DO FINALLY
        10270 POKE 117,PEEK(218) : POKE 118,PEEK(219) : STOP : RUN : REM THROW
    10280 GOTO 10350

    10300 REM CATCH OTHERS
        10310 GOSUB 10360 : REM DO FINALLY
        10320 RESUME : REM THROW
    10330 GOTO 10350
    
    10350 REM FINALLY
    10351 GOSUB 10360 : GOTO 10400
        10360 PRINT D$;"CLOSE"
    10399 RETURN

    10400 REM END TRY

10499 RETURN

Figure 3. A Structured Applesoft subroutine containing a TRY structure with multiple CATCH ERROR blocks, a FINALLY block, and a THROW.


A	0	NEXT WITHOUT FOR
DP	1	LANGUAGE NOT AVAILABLE
DP	2	RANGE ERROR
D	3	RANGE ERROR
P	3	NO DEVICE CONNECTED
DP	4	WRITE PROTECTED
DP	5	END OF DATA
D	6	FILE NOT FOUND
P	6	PATH NOT FOUND
D	7	VOLUME MISMATCH
DP	8	I/O ERROR
DP	9	DISK FULL
DP	10	FILE LOCKED
D	11	SYNTAX ERROR
P	11	INVALID PARAMETER
DP	12	NO BUFFERS AVAILABLE
DP	13	FILE TYPE MISMATCH
DP	14	PROGRAM TOO LARGE
DP	15	NOT DIRECT COMMAND
AP	16	SYNTAX ERROR
P	17	DIRECTORY FULL
P	18	FILE NOT OPEN
P	19	DUPLICATE FILE NAME
P	20	FILE BUSY
P	21	FILE(S) STILL OPEN
A	22	RETURN WITHOUT GOSUB
A	42	OUT OF DATA
A	53	ILLEGAL QUANTITY
A	69	OVERFLOW
A	77	OUT OF MEMORY
A	90	UNDEFINED STATEMENT
A	107	BAD SUBSCRIPT
A	120	REDIMENSIONED ARRAY
A	133	DIVIDE BY ZERO
A	163	TYPE MISMATCH
A	176	STRING TOO LONG
A	191	FORMULA TOO COMPLEX
A	224	UNDEFINED FUNCTION
A	254	REENTER
A	255	BREAK (CTRL-C)

Figure 4. Table of PEEK(222) codes and corresponding errors. A=Applesoft, D=DOS 3.3, P=ProDOS.