Volume 8 -- Issue 7 | April 1988

- Peeking Inside AppleWorks 1.3, Part 5: Menu Display and Selection
- ProDOS File Transformer (S-C into TXT)
- Modify CATALOG to Show All AuxTypes
- BLOADing ProDOS Directories

68HC11 Cross Assembler Now Here!

At long last, I have written a cross assembler for the Motorola 68HC11. A lot of you have been asking about it, and thanks to the persistence of one customer it is now available. See our ad on page 3 for pricing and a list of other cross assemblers.

A Visit to Phoenix

The weekend of June 10-12 I attended the AZ Apple Fiesta in Phoenix, Arizona. (How can this be reported in the "April" issue? Sorry, we are still woefully behind in the publishing schedule, it is now late June.) The main attraction for me was a chance to spend a day at Western Design Center with Bill Mensch, the chief designer of the 65816 and its predecessors.

Secondly, I wanted to attend Randy Hyde's mini-conference on a new standard for 6502 family assembly language. Randy was there, with Brian Fitzgerald who now supports and markets the Lisa assembler. Roger Wagner represented the Merlin interests, and Mike Westerfield the ORCA/M and APW. Paul Santa-Maria and Chuck Kelly from ProDev Inc. in Michigan came to see about standardizing a symbol table format for use with symbolic debuggers. I also met P. J. Curran from ProData 21 in Florida. Since one cannot fly the 1000 miles to Phoenix directly from Dallas for less than $250 each way, I drove 200 miles south to Austin to get a round trip ticket for only $138. Bill Morgan lives in Austin now, and he decided to come also. Jeff Creamer and Don Lancaster were also involved in this conference. As far as I could tell, we did not make any real decisions about any changes to existing assemblers, or even future ones. Neither did we gain any hard data about the new opcodes and addressing modes of the 65832. But it was a very positive meeting.

Bill Mensch gave several interesting talks regarding the history of the 6502 family and its future. The 65832 is on the horizon, but not fully defined. We know it will have 32-bit registers, multiply/divide instructions, and probably even floating point arithmetic. We can also be certain it will be plug compatible with the 65816, so we will be able to plop them directly into Apple IIgs sockets. We still have opportunity to send ideas for its features to Bill. Suggestion: do it in writing, be brief, keep any such letter to one or two ideas and no more than two pages. If you have more ideas, send them in separate letters, giving Bill time to digest them. Send your ideas direcly to Bill at Western Design Center, 2166 East Brown Road, Mesa, AZ 85213. If you are VERY serious, give him a call at (602)962-4545.

You might also want to write for information about their new line of microCOMPUTERs: the W65C134 includes a 65C02, RAM, ROM, and oodles of I/O goodies all in one 68-pin package; the W65C265 will be even better, and built around a 65C816; and the W65C365 will include a 65C832.

A real highlight of the trip was meeting Don Lancaster, and his wife and daughter. Don was there to show and sell his many books and disks full of Apple and Postcript knowledge, and both he and wife Bea gave several seminars during the conference. If you are doing ANYTHING with laser printers, you need to look at what Don has. His "Ask the Guru" column in Computer Shopper is a gold mine, and you can get a neatly bound set of reprints for only $24.50. Speaking of gold mines reminds me of caves, and the fact that the Lancasters are avid spelunkers.

Bill and I also enjoyed our lengthy discussions with Jeff Creamer, Steve Stephenson, and John & Ron Wrenholt. Jeff is a teacher in Prescott, AZ, and has published in these pages. Steve is chief programmer for Checkmate Technology, and gave me some wonderful disk files about AppleWorks. Expect to see some of his stuff here in the near future. John is the founder of Big Red Apple Club, now called Big Red Computer Club. He and brother Ron have quite a lot to offer any Print Shop afficionados, including a new program which lets you print labels with color graphics.

"Do not be conformed to this world, but be transformed by the renewing of your mind, that you may discover the good, and acceptable, and perfect will of God."

Peeking Inside AppleWorks 1.3, Part 5: Menu Display and Selection Subroutines | Bob Sander-Cederlof

Part 5 already? This is rapidly turning into a major series, and a popular one at that. A lot of you have called or written telling how much you like it (thank you!), so I will do my best to keep it interesting and useful.

This month I am going to document some subroutines which constitute a major portion of the "AppleWorks Human Interface". Namely, the menu display and selection subroutines.

We hear so much these days about the MacIntosh human interface, and indeed it has become the standard for the IIgs, like it or not. Personally, I can live with it, but I prefer plain old text menus. Why? They are faster, consume less of the computer's resources (leaving more of them for end users), and are quicker (for me) to use. Well-designed text menus with a consistent screen layout and selection method are easier for me than icons and mice.

How can that be? I thought Apple had proven the icon menu to be easier? I suppose they decided that after asking "the rest of us", leaving out the experienced computer people. But isn't text what we are trained from childhood to read? Some languages are written with icons (Chinese, for example), but MOST languages are written with text. And isn't the text-menu drilled into us from childhood also? Even the word "menu" reminds us of a list of textual lines for selecting the components of a meal. And what about multiple-choice tests?

And even some of the Apple folk believe in text, as is evidenced by the Control Panel and other Classic Desk Accessories on the IIgs.

All the above to justify adding my voice to that of Tom Weishaar. Tom is the author of the Open-Apple newsletter, which all Apple II lovers ought to read. We both think the AppleWorks human interface is a great starting point for the Apple II general operating environment. With just a little improvement, mostly in the area of pathname entry, and just a little generalization, we could use it for running all sorts of applications and compilers for all sorts of languages.

AppleWorks has an amazingly consistent human interface throughout. At almost every level you will see a simple list of numbered choices, with the same control scheme. One of the lines will be displayed in inverse mode, and you can either move the "bar" with the up- and down-arrow keys, or by typing the item number. When the bar is on your choice, the RETURN key will select it. ESCAPE will pop out of the menu to the previous level. You can usually get a help screen by typing Apple-?. The directions for getting help are shown at the right end of the bottom line, and the action ESCAPE will cause are shown at the right end of the top line.

If there happen to be more than nine numbered options, so that some of them have two-digit options, AppleWorks even allows you to move the bar to the two-digit lines by typing their numbers. When you type a digit, the bar moves to one of the first nine lines. Then if you type a second digit, AppleWorks tries to move to the line with that two-digit number. If there is not one, you will hear a beep. If there is, the bar will move to that line. You can observe this action even on a menu with fewer than ten lines. When you select a line by typing a digit, you will not only see the bar move, but also the help and escape messages will change. On the top line you will see "Escape: Erase entry", and on the bottom line instead of an offer of help you will see "xxxK Avail." (the number of available memory bytes). When you type a second digit, you hear the beep and the help and escape info changes back to what it was before you typed the first digit.

Pretty slick. Until now I always assumed that numeric selection would either be limited to single-digit items, or that I would have to require a terminating character from the user so I would know when he had typed all the digits. Robert Lissner wins again! (Some of you have pointed out his name used to be "Rupert", but I read somewhere that he now prefers "Robert", or Bob.)

AppleWorks also allows the menu items to be anywhere on the screen. They do not even all have to be in one column, or vertically aligned. You can use single, double, or even variable vertical spacing.

Look ahead to the DISPLAY.MENU.LINE subroutine, lines 4580-5190. When you want to put a menu on the screen, you call this subroutine. The variable LAST.LINE.NUMBER.OF.MENU must be initially zeroed, and this subroutine will increment it each time you call. You call this subroutine once for each numbered line of your menu. Call it like this:

           JSR DISPLAY.MENU.LINE
           .DA #column,#line
           .DA string

The string parameter is the address of a string which begins with a length byte. Column and line numbers are either absolute or relative. If you give values like 0-23 for line and 0-79 for column, they are absolute. If you add $80 to line and/or column, it/they become relative to the coordinates stored in MENU.CORNER.LEFT and MENU.CORNER.TOP. Lines 4850-5000 compute the actual coordinates of the beginning of your menu line.

A block of 93 bytes called MENU.TABLE allows for keeping track of 30 menu lines. Each entry in this table is three bytes, and the zero'th entry is not used. Each time you call DISPLAY.MENU.LINE one entry is added to this table. Each three-byte entry consists of the column, line, and length of the menu item.

DISPLAY.MENU.LINE also takes care of prefixing the menu line number. You do not include it in your string. (This allows you to use the same strings in different menus.) Lines 4690-4800 start building a display string in what I call the STR.A00 buffer. It begins with token $05, which means "position cursor". Then it inserts the line number in the format " d. " or "dd. ". Lines 4850-5000 insert the chosen cursor position after the $05 token. Lines 5010-5140 append your string to this position and line number string. Finally, lines 5150-5190 display the result.

So you can see that to put a complete menu on the screen we would first zero LAST.LINE.NUMBER.OF.MENU; then clear the window we are using; then call DISPLAY.MENU.LINE once for each menu item; and finally, add any titles and explanations.

Once a menu is on the screen, the next logical step is to call SELECT.MENU.LINE so the user can tell you his choice. This subroutine is shown beginning at line 2000. You call it with a line number in the A-register. The subroutine even expects that you have just loaded that number into the A-register, because it starts with a BEQ instruction. If the EQ status is set, probably meaning you just did a LDA #0, the menu will begin with the bar on the first menu line. You will also get the first item if the value in A is larger than the last menu item number, or if A=1. You can begin with any other line by putting that line number in A. You could keep track of the item selected that last time this menu was used, like Copy II Plus does.

The subroutine finally returns when you type either ESCAPE, Apple-?, or RETURN. In the first two cases MENU.LINE.SELECTED and the A-register will be zero; in the third case they will be, you guessed it, the number of the menu line selected.

Lines 2000-2110 figure out which line you want to begin with, and inverse it. Unless, that is, the value in Z.A4 is non-zero. I think that variable must be an "escape flag" of some sort. If it is non-zero, SELECT.MENU.LINE exits the same way it would if you typed the ESCAPE key.

Lines 2120-2130 display the generic instructions for making a menu selection on the screen. The text of this message is shown in lines 5200-5230.

The rest of SELECT.MENU.LINE divides nicely into two main sections. Lines 2150-2770 handle the selection until you type a digit; lines 2780-3430 take over when you type a digit, and don't let go until you either type an arrow (up-, down, or left-), DELETE, RETURN, or ESCAPE. I noticed a significant chunk of duplicate code in the two sections: see lines 2420-2470 and lines 3330-3380. I feel certain that the code could be re-arranged a little and save space. We don't really need it, but a little here and a little there could make room for many new features.

Lines 2150-2190 invert the current line, move the cursor down to the bottom line at the end of the "Type..." message, and wait for you to type something. If you remember AW.KEYIN from the Feb 88 issue, it will also take its own action if you type certain keys. For example, Apple-/ gets changed into Apple-?; Apple-Q and Apple-S store a non-zero value in Z.A4 and change the character to ESCAPE; and several other interesting things. Look at page 13 in that issue, lines 2580-2790, for a summary.

Look ahead to lines 2900-3000 for an interesting use of the Z.A4 flag. If you typed Apple-Q or Apple-S, it will be changed into ESCAPE by AW.KEYIN and Z.A4 will be set non-zero. Then the test at 2970-2980 will make the jump all the way to SML.ESCAPED. If you typed ESCAPE, that test will merely send you back to SML.WAITING. Interesting.... So if you are at this level, where the escape info line says "Escape: Erase entry": typing one ESCAPE takes you back to SML.WAITING; typing two ESCAPEs would get you to SML.ESCAPED, or typing one Apple-Q would do the same thing. Whoever called SELECT.MENU.LINE can tell the difference, though, because Z.A4 will still be non-zero for Apple-Q or zero for two ESCAPEs. (For some reason I never can remember what Apple-Q and -S are really used for.)

Lines 2240-2310 branch appropriately for up- or down-arrow or RETURN. Lines 2590-2770 handle the up- and down-arrows: simply a matter of changing the display of the current line back to normal mode, and raising or lowering the selected line number. The line number changes circularly, so that up-arrow off the top wraps around to the bottom, and down-arrow at the bottom goes to the top.

Lines 2340-2500 check for a digit and operate on it. Notice that only digits 1-9 are accepted here, while digits 0-9 are accepted at lines 3170-3200. This will be the first digit of a menu line number, so it cannot be zero. The other place it will be the second digit, so it could be zero. If this first digit is acceptable (between 1 and the last menu line number), lines 2420-2470 will echo the character at the cursor on line 23, restore the current line to normal mode, and store the new menu line number.

Lines 2480-2490 call a subroutine in the $D000 area to change the escape and help info. This subroutine decides which pair of messages to display by the letter in the caller's A-register. Letter "E" makes escape say "Escape: Erase entry" and help say how much memory is left. Letter "S", used in lines 1910-1920, makes help say "Apple-? for help" and escape say whatever string was saved in a buffer at $0CFD. The subroutine has several other options.

There appears to be either a bug or a leftover at lines 2520-2530. If you type Apple-? you get to this line. It loads up Z.89, and then acts like you typed ESCAPE. However, the contents of Z.89 are not needed here, because the first instruction after the JMP is another LDA!

I have probably already said enough about lines 2800-3430, especially since it is so similar to the section above. I would, however, like to look at a few lines. The code in lines 3210-3280 multiplies the previous menu line number by ten and adds in the next digit. At first glance it looks reasonably efficient for space, since it calls on a multplication subroutine. Nevertheless, I can recode it in one less byte and many fewer cycles without calling the subroutine. Change lines 3210-3280 to the following:

           3210    AND #$0F        isolate new digit
           3220    STA Z.91        save for later
           3230    LDA MENU.LINE.SELECTED
           3240    ASL             old * 2
           3250    ASL             old * 4
           3260    ADC MENU.LINE.SELECTED  old*5
           3270    ASL             old * 10
           3280    ADC Z.91        plus new digit

Let's move ahead. When you type RETURN, lines 3600-3720 will clear line 23, restore the selected line to normal mode, and draw an arrow over the line number ("-->"). Either ESCAPE or RETURN brings you to lines 3740-3810, which erase the MENU.TABLE and return with the selected line number in the A-register. It makes no sense to me to erase MENU.TABLE, since it will not be used again until it is re-loaded anyway, but maybe there is some reason it is needed.

Lines 3860-4540 will copy the selected line off the screen into a buffer at $0900, and then re-display it in one of three ways: the entire line NORMAL, the entire line INVERSE, or just the portion (A) characters long starting in column (X) in INVERSE.

Now if only AppleWorks could handle pathname entry as easily! The human interface presented by such Quit Code replacements as ProSel, Squirt, or the S-C Program Selector would be an improvement.

      1000 *SAVE AW.SRC.V8N7
      1010        .list moff
      1020 *--------------------------------
      1030 PSTR   .EQ $80,81
      1040 CHAR.FROM.KEYIN     .EQ $84
      1050 MENU.LINE.SELECTED  .EQ $8C
      1060 Z.89   .EQ $89
      1070 Z.91   .EQ $91      result of multiply here
      1080 P0     .EQ $9A      parameters from GET.4.PARMS
      1090 P1     .EQ $9B           "
      1100 P2     .EQ $9C           "
      1110 Z.A4   .EQ $A4      escape flag???
      1120 *--------------------------------
      1130 STR.A00    .EQ $A00
      1140 MENU.CORNER.LEFT    .EQ $0FBE
      1150 MENU.CORNER.TOP     .EQ $0FBF
      1160 *--------------------------------
      1170        .DUMMY
      1180        .OR $0EB6
      1190 MENU.TABLE .BS 3*31      Room for 30 3-byte entries
      1200 LAST.LINE.NUMBER.OF.MENU .BS 1
      1210        .ED
      1220 *-------------------------------------Covered in AAL---
      1230 DISPLAY.STRING           .EQ $14D1   Jan 88, page 10
      1240 CONVERT.A.TO.RJBF.STRING .EQ $179D
      1250 BEEP.AND.CLEAR.KEYBUF    .EQ $1818
      1260 MOVE.CURSOR.TO.XY        .EQ $1823   Feb 88, page 17
      1270 MULTIPLY.X.BY.Y          .EQ $1B3A
      1280 COPY.SCRN.LINE.TO.0900   .EQ $187A   Feb 88, page 18
      1290 GET.4.PARMS              .EQ $18AE   Dec 87, page 7
      1300 AW.KEYIN                 .EQ $1D35   Feb 88, page 12
      1310 MOVE.CURSOR.TO.TCOL.TROW .EQ $1E80
      1320 POINT.PSTR.AT.STR.A00    .EQ $1EA9   Feb 88, page 18
      1330 MOVE.STRING              .EQ $1EF8   Dec 87, page 8
      1340 DISPLAY.AT               .EQ $1F3E
      1350 CLEAR.KEYBUF             .EQ $1FE0   Feb 88, page 17
      1360 DISPLAY.STRING.P0        .EQ $2093
      1370 DISPLAY.TOKEN.X          .EQ $1FE9
      1380 DISPLAY.ON.LINE.23       .EQ $1FF5
      1390 *--------------------------------
      1400 SHOW.ESCAPE.AND.HELP.MSGS  .EQ $D023
      1410 *--------------------------------
      1420        .MA MSG      Macro to make a string with
      1430        .DA #:1-*-1  first byte is the length,
      1440        .AS "]1"     the rest is ASCII.
      1450 :1
      1460        .EM
      1470 *--------------------------------
      1480        .PH $18DD
      1490 *--------------------------------
      1500 *   Load X-reg with pointer into MENU.TABLE
      1510 *      which is menu line number times 3.
      1520 * (18DD) 18E4 18F2 1A4D
      1530 GET.MENU.TABLE.INDEX
      1540        LDA MENU.LINE.SELECTED
      1550        ASL
      1560        ADC MENU.LINE.SELECTED
      1570        TAX
      1580        RTS
      1590 *--------------------------------
      1600 *   Re-display entire line in NORMAL mode.
      1610 * (18E4) 1973 198B 199A 1A21 1A39 1A4A
      1620 MAKE.MENU.LINE.NORMAL
      1630        JSR GET.MENU.TABLE.INDEX
      1640        LDA MENU.TABLE+1,X   Get screen line #
      1650        TAY
      1660        LDA #$00     Display entire line NORMAL
      1670        TAX
      1680        JSR REVERSE.A.SCREEN.LINE
      1690        RTS
      1700 *--------------------------------
      1710 *   Re-Display the menu line in INVERSE mode.
      1720 * (18F2) 1936 19AF 1A27
      1730 MAKE.MENU.LINE.INVERSE
      1740        JSR GET.MENU.TABLE.INDEX
      1750        LDA MENU.TABLE+2,X   get length of menu msg
      1760        PHA
      1770        LDA MENU.TABLE+1,X   get screen line #
      1780        TAY
      1790        LDA MENU.TABLE,X     get starting column of msg
      1800        CLC
      1810        ADC #4               skip over "##. "
      1820        TAX               start at blank before msg itself
      1830        PLA               get length again
      1840        CLC
      1850        ADC #2            include leading and trailing blank
      1860        JSR REVERSE.A.SCREEN.LINE
      1870        RTS
      1880 *--------------------------------
      1890 * (190C) 19D8 19E2 19EC 19F6
      1900 RESTORE.ESCAPE.AND.HELP.MSGS
      1910        LDA #$53
      1920        JSR SHOW.ESCAPE.AND.HELP.MSGS
      1930        JSR MOVE.CURSOR.TO.TCOL.TROW
      1940        JSR DISPLAY.STRING.P0
      1950        .DA TWO.BLANKS
      1960        RTS
      1970 *--------------------------------
      1980 TWO.BLANKS .HS 02.20.20
      1990 *--------------------------------
      2000 *      (A) = menu line number; if =0, or >lastline, use 1
      2010 *--------------------------------
      2020 SELECT.MENU.LINE
      2030        BEQ .1       Use top menu line
      2040        CMP LAST.LINE.NUMBER.OF.MENU
      2050        BCC .2       Use selected line
      2060        BEQ .2       Use selected line (bottom)
      2070 .1     LDA #1       Use top menu line
      2080 .2     STA MENU.LINE.SELECTED
      2090        LDA Z.A4
      2100        BEQ .3
      2110        JMP SML.ESCAPED.WITHOUT.INVERSE
      2120 .3     JSR DISPLAY.ON.LINE.23
      2130        .DA MENU.MSG "Type number, or use arrows, then press Return"
      2140 * (1936) 1997 19AC 19DB
      2150 SML.WAITING
      2160        JSR MAKE.MENU.LINE.INVERSE
      2170 .1     JSR CLEAR.KEYBUF
      2180        JSR MOVE.CURSOR.TO.TCOL.TROW
      2190        JSR AW.KEYIN
      2200        CMP #$1B     did he type ESCAPE?
      2210        BNE .2       ...not ESCAPE
      2220        JMP SML.ESCAPED
      2230 *--------------------------------
      2240 .2     CMP #$0D     is it RETURN?
      2250        BNE .3       ...not RETURN
      2260        JMP SML.RETURN
      2270 *--------------------------------
      2280 .3     CMP #$0B     up arrow?
      2290        BEQ SML.UP.ARROW
      2300        CMP #$0A     down arrow?
      2310        BEQ SML.DOWN.ARROW
      2320        CMP #"?"     Apple and "?"
      2330        BEQ .5       ...question mark
      2340        CMP #$31
      2350        BCC .6       ...not digit, beep!
      2360        CMP #$3A
      2370        BCS .6       ...not digit, beep!
      2380        AND #$0F
      2390        CMP LAST.LINE.NUMBER.OF.MENU
      2400        BEQ .4       ...bottom line
      2410        BCS .6       ...beyond the bottom, beep!
      2420 .4     PHA          save the digit typed
      2430        LDX CHAR.FROM.KEYIN
      2440        JSR DISPLAY.TOKEN.X
      2450        JSR MAKE.MENU.LINE.NORMAL
      2460        PLA          get digit typed again
      2470        STA MENU.LINE.SELECTED  select line 1-9
      2480        LDA #$45
      2490        JSR SHOW.ESCAPE.AND.HELP.MSGS
      2500        JMP SML.DIGIT
      2510 *---Apple-? typed----------------
      2520 .5     LDA Z.89     ...why? opcode JMPed to is LDA also!
      2530        JMP SML.ESCAPED.WITHOUT.INVERSE
      2540 *--------------------------------
      2550 .6     JSR BEEP.AND.CLEAR.KEYBUF
      2560        BNE .1       ...always
      2570 *--------------------------------
      2580 * (198B) 1952 19EF
      2590 SML.UP.ARROW
      2600        JSR MAKE.MENU.LINE.NORMAL
      2610        DEC MENU.LINE.SELECTED
      2620        BNE .1       ...still in range
      2630        LDA LAST.LINE.NUMBER.OF.MENU
      2640        STA MENU.LINE.SELECTED
      2650 .1     JMP SML.WAITING
      2660 *--------------------------------
      2670 * (199A) 1956 19F9
      2680 SML.DOWN.ARROW
      2690        JSR MAKE.MENU.LINE.NORMAL
      2700        LDA MENU.LINE.SELECTED
      2710        CMP LAST.LINE.NUMBER.OF.MENU
      2720        BCS .1       ...already on bottom, wrap to top
      2730        INC MENU.LINE.SELECTED   next line down
      2740        BNE .2       ...always
      2750 .1     LDA #1       top line now
      2760        STA MENU.LINE.SELECTED
      2770 .2     JMP SML.WAITING
      2780 *--------------------------------
      2790 * (19AF) 197E
      2800 SML.DIGIT
      2810        JSR MAKE.MENU.LINE.INVERSE
      2820 .1     JSR MOVE.CURSOR.TO.TCOL.TROW
      2830        LDX #$07     CURSOR RIGHT TOKEN
      2840        JSR DISPLAY.TOKEN.X
      2850        LDA MENU.LINE.SELECTED
      2860        CMP #10      on line 1-9?
      2870        BCC .2       ...yes
      2880        LDX #$07     CURSOR RIGHT TOKEN
      2890        JSR DISPLAY.TOKEN.X
      2900 .2     JSR AW.KEYIN get another char, maybe 2nd digit
      2910        CMP #$1B     is it ESCAPE?
      2920        BEQ .3       ...yes
      2930        CMP #$08
      2940        BEQ .3       ...backspace
      2950        CMP #$7F     ...is it DELETE?
      2960        BNE .4       ...yes
      2970 .3     LDA Z.A4
      2980        BNE SML.ESCAPED
      2990        JSR RESTORE.ESCAPE.AND.HELP.MSGS
      3000        JMP SML.WAITING
      3010 *--------------------------------
      3020 .4     CMP #$0D     is it RETURN?
      3030        BNE .5       ...no
      3040        JSR RESTORE.ESCAPE.AND.HELP.MSGS
      3050        JMP SML.RETURN
      3060 *--------------------------------
      3070 .5     CMP #$0B     up arrow?
      3080        BNE .6       ...no
      3090        JSR RESTORE.ESCAPE.AND.HELP.MSGS
      3100        JMP SML.UP.ARROW
      3110 *--------------------------------
      3120 .6     CMP #$0A     down arrow?
      3130        BNE .7       ...no
      3140        JSR RESTORE.ESCAPE.AND.HELP.MSGS
      3150        JMP SML.DOWN.ARROW
      3160 *--------------------------------
      3170 .7     CMP #$30     another digit?
      3180        BCC .10      ...not a digit, beep!
      3190        CMP #$3A
      3200        BCS .10      ...not a digit, beep!
      3210        PHA          save the digit
      3220        LDX MENU.LINE.SELECTED
      3230        LDY #10      multiply previous line# by 10
      3240        JSR MULTIPLY.X.BY.Y
      3250        PLA          add the new digit
      3260        AND #$0F
      3270        CLC
      3280        ADC Z.91     product here
      3290        BCS .10      ...too large, beep!
      3300        CMP LAST.LINE.NUMBER.OF.MENU
      3310        BEQ .8       ...bottom line
      3320        BCS .10      ...too large, beep!
      3330 .8     PHA          save the new line number
      3340        LDX CHAR.FROM.KEYIN
      3350        JSR DISPLAY.TOKEN.X
      3360        JSR MAKE.MENU.LINE.NORMAL
      3370        PLA          get the new line number
      3380        STA MENU.LINE.SELECTED
      3390        JSR MAKE.MENU.LINE.INVERSE
      3400 .9     JMP .1
      3410 *--------------------------------
      3420 .10    JSR BEEP.AND.CLEAR.KEYBUF
      3430        BNE .9       ...always
      3440 *--------------------------------
      3450 * (1A32) 1946 19D6
      3460 SML.ESCAPED
      3470        LDA Z.A4
      3480        PHA
      3490        LDA #$00
      3500        STA Z.A4
      3510        JSR MAKE.MENU.LINE.NORMAL
      3520        PLA
      3530        STA Z.A4
      3540 * (1A3F) 192E 1983
      3550 SML.ESCAPED.WITHOUT.INVERSE
      3560        LDA #$00
      3570        STA MENU.LINE.SELECTED
      3580        BEQ CLEAR.MENU.TABLE.AND.EXIT   ...ALWAYS
      3590 *--------------------------------
      3600 * (1A45) 194D 19E5
      3610 SML.RETURN
      3620        JSR DISPLAY.ON.LINE.23
      3630        .DA I.1A72   1 Byte String, Clear-Line Token
      3640        JSR MAKE.MENU.LINE.NORMAL
      3650        JSR GET.MENU.TABLE.INDEX
      3660        LDA MENU.TABLE+1,X
      3670        TAY          draw an arrow "-->" over number
      3680        LDA MENU.TABLE,X
      3690        TAX
      3700        JSR MOVE.CURSOR.TO.XY
      3710        JSR DISPLAY.STRING.P0
      3720        .DA ARROW
      3730 * (1A60) 1A43
      3740 CLEAR.MENU.TABLE.AND.EXIT
      3750        LDA #0
      3760        LDX #3*31+1  entire table & highest line number
      3770 .1     STA MENU.TABLE-1,X
      3780        DEX
      3790        BNE .1
      3800        LDA MENU.LINE.SELECTED
      3810        RTS
      3820 *--------------------------------
      3830 ARROW  >MSG " -->"
      3840 I.1A72 .HS 01.02    1 BYTE STRING, CLEAR LINE TOKEN
      3850 *--------------------------------
      3860 *   Re-display a screen line in INVERSE or NORMAL mode
      3870 *      (Y) = screen line to be re-displayed
      3880 *      (X) = column number to begin with
      3890 *      (A) = 0 then re-display entire line NORMAL
      3900 *      (A) > 78 then redisplay entire line INVERSE
      3910 *      (A) = length of middle section of line to be
      3920 *            displayed in INVERSE.
      3930 *--------------------------------
      3940 RASL.X .BS 1
      3950 RASL.Y .BS 1
      3960 RASL.A .BS 1
      3970 *--------------------------------
      3980 * (1A77) 103F 18EE 1908
      3990 REVERSE.A.SCREEN.LINE
      4000        STX RASL.X   Save the column
      4010        STY RASL.Y   Save the line
      4020        STA RASL.A   Save the length
      4030        TYA          get line number
      4040        JSR COPY.SCRN.LINE.TO.0900
      4050        LDX #0       start in column 0
      4060        LDY RASL.Y   on line Y
      4070        JSR MOVE.CURSOR.TO.XY
      4080        LDA RASL.A   get specified length
      4090        BEQ .1       ...0 means display it all NORMAL
      4100        CMP #79
      4110        BCC .2       ...it really is a length
      4120        LDX #$0A     ...>78, INVERSE the line
      4130        JSR DISPLAY.TOKEN.X
      4140 .1     LDA HANDLE.0900
      4150        STA PSTR
      4160        LDA HANDLE.0900+1
      4170        STA PSTR+1
      4180        LDA #79      display 79 characters
      4190        JSR DISPLAY.STRING
      4200        LDX #$0B     NORMAL TOKEN
      4210        JSR DISPLAY.TOKEN.X
      4220        JMP .3       ...ONLY AN RTS
      4230 *--------------------------------
      4240 .2     LDA HANDLE.0900   Point to line image
      4250        STA PSTR
      4260        LDA HANDLE.0900+1
      4270        STA PSTR+1
      4280        LDA RASL.X        Display up to X in NORMAL
      4290        JSR DISPLAY.STRING
      4300        LDX #$0A     INVERSE TOKEN
      4310        JSR DISPLAY.TOKEN.X
      4320        LDA RASL.X        Display middle section INVERSE
      4330        STA PSTR
      4340        LDA HANDLE.0900+1
      4350        STA PSTR+1
      4360        LDA RASL.A        # chars in middle section
      4370        JSR DISPLAY.STRING
      4380        LDX #$0B     NORMAL TOKEN
      4390        JSR DISPLAY.TOKEN.X
      4400        LDA RASL.X        Display rest of line NORMAL
      4410        CLC
      4420        ADC RASL.A
      4430        BCS .3       ...line too long, forget the rest
      4440        CMP #79
      4450        BCS .3       ...line too long, forget the rest
      4460        STA PSTR
      4470        LDA HANDLE.0900+1
      4480        STA PSTR+1
      4490        LDA #79      79 chars
      4500        SEC
      4510        SBC PSTR
      4520        BEQ .3       ...no space left on the line
      4530        JSR DISPLAY.STRING
      4540 .3     RTS
      4550 *--------------------------------
      4560 HANDLE.0900 .DA $0900
      4570 *--------------------------------
      4580        .PH $2029
      4590 *--------------------------------
      4600 *   Display string P2 at P0,P1 after "xx.  "
      4610 *      where xx is decimal form of (LAST.LINE.NUMBER.OF.MENU)
      4620 *      LAST.LINE.NUMBER.OF.MENU is incremented by this routine
      4630 *      Stores 3 bytes in MENU.TABLE:
      4640 *          column, line, and string length
      4650 *      MENU.TABLE has room for 31 such entries.
      4660 *--------------------------------
      4670 DISPLAY.MENU.LINE
      4680        JSR GET.4.PARMS   Get P0-P3
      4690        INC LAST.LINE.NUMBER.OF.MENU
      4700        LDA #$05     "GO TO XY" TOKEN
      4710        STA STR.A00
      4720        LDA LAST.LINE.NUMBER.OF.MENU
      4730        JSR CONVERT.A.TO.RJBF.STRING
      4740        STX STR.A00+3       blank or first digit of ##
      4750        STY STR.A00+4       units digit of ##
      4760        LDA #'.'
      4770        STA STR.A00+5       string is "##.  "
      4780        LDA #' '
      4790        STA STR.A00+6
      4800        STA STR.A00+7
      4810        LDA LAST.LINE.NUMBER.OF.MENU
      4820        ASL               multiply by 3
      4830        ADC LAST.LINE.NUMBER.OF.MENU
      4840        TAX               save for index into MENU.TABLE
      4850 *---Column number----------------
      4860        LDA P0       Get specified column
      4870        BPL .1       ...it is absolute
      4880        AND #$7F     ...it is relative
      4890        CLC
      4900        ADC MENU.CORNER.LEFT
      4910 .1     STA STR.A00+1  Put column # into string
      4920        STA MENU.TABLE,X      and save in table
      4930 *---Line number------------------
      4940        LDA P1       Get specified line number
      4950        BPL .2       ...it is absolute
      4960        AND #$7F     ...it is relative
      4970        CLC
      4980        ADC MENU.CORNER.TOP
      4990 .2     STA STR.A00+2  Put line # into string
      5000        STA MENU.TABLE+1,X   and save in table
      5010 *---Append text of menu line-----
      5020        LDY #0
      5030        LDA (P2),Y   Save length of string in table
      5040        STA MENU.TABLE+2,X
      5050        TAY          Point at last char in string
      5060        CLC          Compute length of combined strings
      5070        ADC #8       05.xx.yy "##.  " = 8 chars
      5080        TAX          Point at last char of combination
      5090        PHA          Save total length for display subr.
      5100 .3     LDA (P2),Y   get next char of caller's string
      5110        STA STR.A00-1,X     append to ours
      5120        DEX
      5130        DEY
      5140        BNE .3       ...more to copy
      5150 *---Display the string-----------
      5160        JSR POINT.PSTR.AT.STR.A00
      5170        PLA          Get length off stack
      5180        JSR DISPLAY.STRING
      5190        RTS
      5200 *--------------------------------
      5210        .PH $146A
      5220 MENU.MSG  >MSG "Type number, or use arrows, then press Return  "
      5230 *--------------------------------

ProDOS File Transformer | Bob Sander-Cederlof

Have you ever wanted to convert a file from one filetype to another? I have, and it seems that there should be a command in BASIC.SYSTEM to allow it. Some other command shells, such as Davex by DAL Systems, do have such a command.

Of course, the flexibility of the BLOAD and BSAVE commands does allow you to get the job done. You can BLOAD the source file, specifying the filetype and load address; CREATE an empty file of the new type; and BSAVE into that new file, specifying the filetype, beginning RAM address, and length.

But what if you want to go just a little further, and also make some slight changes to the file's contents? Then you need an intelligent file transformer. You need a program that will ask for an input and an output pathname, read the input file and transform the data appropriately, and write the transformed data on the output file. This article presents a specific file transformer, and you may either use it as is or modify it to your own needs.

I have often been asked, by programmers who did not own the S-C Macro Assembler, for a program which would convert S-C source code files into ordinary text files. Back in the December, 1983, issue of Apple Assembly Line I published such a program, but it was written in Applesoft for running under DOS 3.3. I don't believe that program would operate properly under ProDOS, but even if it did it would be terribly slow.

Under DOS 3.3, S-C source code is stored in type "I" files. DOS does all the actual LOADing and SAVEing, being fooled into believing that the source code is Integer BASIC. Under ProDOS, S-C source code is stored in type $FA files, which Apple has set aside for Integer BASIC source programs. Since Integer BASIC has never been supported under ProDOS, and probably never will be, I chose to use this file type for S-C source code. In fact, when you are using the S-C Macro Assembler this filetype is called "S-C".

The source code text is stored in a slightly compressed format in these files. The same format is used for both the DOS and ProDOS versions, so that you can use Apple's CONVERT program or a utility like COPY II PLUS to move files from one operating system to the other. If you were to read a file byte-by-byte under both operating systems, you would notice one difference: under DOS the file's length is stored in the first two bytes; under ProDOS the length is kept in the directory.

Each line of S-C source code begins with a line number. This number may be any value from 0 to 65535. The transformer should write the source lines on the output text file without this line number, because most assemblers using text files for source do not use explicit line numbers.

Each line of S-C source code is stored in the file in a form first defined by Steve Wozniak's Integer BASIC:

           byte 1:  number of bytes in entire line (n)
           byte 2:  lo-byte of line number in binary
           byte 3:  hi-byte of line number in binary
    bytes 4...n-1:  tokenized form of source line
           byte n:  00

For example, an empty line with line number 1000 would be stored as:

           04 E8 03 00

The tokenized form of S-C source is almost plain ASCII. Each byte is stored in ASCII with bit 7 zero, with two exceptions. First, blanks are stored in a compressed form. One blank is stored with the code $81; two blanks are stored with the single code $82; in general, a string of n consecutive blanks is stored with the single code $80+n. The largest compressed blank code is $BF, which stands for 63 consecutive blanks. If there are more than 63 blanks in a row, they will be represented by two or more compressed-blank codes. For example, the line

          1010 STAR   LDA #0     Zero

would be stored as:

          14           number of bytes in line = 20
          F2 03        line number is $03F2 = 1010
          53 54 41 52  "STAR"
          83           three blanks
          4C 44 41 81  "LDA "
          23 30 85     "#0     "
          5A 65 72 6F  "Zero"
          00       end of line token

While blank is the most frequently repeated character in a source program, there are others. The token $C0, followed by a repetition count and an ASCII character, represents any string of the same character. For example, the line

           1020 *--------------------------------

would be stored as:

           08        8 bytes in line
           FC 03     line number 1020
           2A        "*"
           C0 20 2D  means 32 consecutive "-"
           00        end of line

With the above information, you can see that the file transformer will have to read in the first byte of each line, which tells how many bytes are in the rest of that line. Then it should read in the rest of that line. Once the entire line is in RAM, the transformer should scan through the bytes of the line, copying ASCII characters to the new line, and expanding any compressed characters into the new line. Finally, the new line should be written on the output file. When the transformer reaches the end of the input file it is finished.

Let me insert here a reminder that even though this transformer is very specific, you should be able to easily modify it to handle other types of transformations. For example, the subscriber data base I use to produce mailing labels every month is kept on a series of plain text files, understandable only to the mailing list program I wrote about eight years ago. Even though it is plain text, AppleWorks cannot read it into a structured database unless I make some "transformations" first. At a minimum, I need a transformer that will split the city, state, and zip code line into three separate lines. I could easily write such a transformer by modifying the following program a little. Another transformer could read a binary file and write out a text file in the Intel or Motorola hex format, for later transmission through a serial port to an EPROM programmer. The possibilities are endless.

Now back to the program ALREADY written. I wrote two versions, and you can assemble either one of them by changing line 1060. As shown here, the fancier version is selected. The simpler version assumes specific filenames assembled into the code in lines 4340-4480. The fancier version prompts for filenames or pathnames to be typed in when you run the program. The code for both versions is listed, but only the fancy version was actually assembled.

Lines 1200-1400 define four macros I used in the program. The first one is for calling the ProDOS Machine Language Interface, or MLI. The second one creates a call to a subroutine which prints 00-terminated strings, followed by the string to be printed. The third generates a call to a subroutine which prints strings which begin with a byte count. The fourth generates calls to a subroutine which reads a line from the keyboard into a specified buffer, and then stores the length in the first byte of the buffer.

It turns out I do not use both the PRSTR and RDSTR macros, just one or the other. In the simple version, PRSTR is used to display the pre-assembled filenames; in the fancy version, RDSTR is used to let you type in the filenames.

Lines 1450-1550 call on two subroutines to open the input and output files. If they are successful, the file reference numbers returned by MLI will be stored into parameter blocks for reading and writing those files. If not, the transformer program will just close all files and end. I'll discuss the two opening subroutines later.

I decided after a few tests that I wanted some sort of progress indication on the screen while the program was busy. I decided to list the line number on the screen. Line 1570 uses the PRINT macro to display "LINE NUMBER: ". Inside the main loop, at line 1680, I call DISPLAY.LINE.NUMBER to display the current line number in five-digit decimal, and then backspace 5 times. This makes a very attractive (to me) indicator. In a more general transformer, you might change this to display a hexadecimal file position, or some other meaningful parameter.

The main loop runs from line 1590 to line 1790. First lines 1590-1620 try to read three bytes. If there is any error, I assume it is due to reaching the end of the source file, and that ends the loop. If no error, then the input buffer contains the byte count for the whole line, and the line number. Since we already read the first three bytes, lines 1630-1670 compute the number of bytes left in the line and set up the parameter block to read that many. Then I print out the line number as described above. The DISPLAY.LINE.NUMBER subroutine also checks to see if any key has been pressed on the keyboard, which is interpreted to mean you want to abort the tranformer. Line 1690 branches in that case, and the files are closed. Lines 1700-1710 read the rest of the source line.

Line 1730 calls the CONVERT.ONE.LINE subroutine, which scans the source line and builds an expanded output line. Lines 1740-1780 write the expanded line on the output file. Any error returned by MLI from this write ends the main loop. Probably I should have printed out an error message for this condition, because it is abnormal. It could happen if you were trying to write into a write-protected file or on a write-protected disk. Looking at it now, I think I would change line 1780 to "BCS .4", and insert the following lines:

           1862 .4    JSR OPEN.ERROR
           1864      >PRINT "\UNABLE TO WRITE ON OUTPUT FILE"
           1866       RTS

Once the main loop ends, lines 1800-1820 truncate the output file. It may be that your output file already existed, and was longer than necessary to contain the new information. These lines chop off any extra data. Line 1810 reads the current file position, and line 1820 sets that value as the new end-of-file. Finally, line 1840 closes both files.

I ended the program with a simple RTS. You might want a fancier ending. For example, you might want a message on the screen saying "I AM FINISHED" or the like. Then you might want a short menu on the screen allowing a choice of transforming another file, ending with an RTS, or doing a ProDOS QUIT call. See, I left something for you to do!

If you do start adding features, you might also like to add the ability to transform a range of lines from the source file, by specifying a beginning and ending line number. You might want to add tests for the correct file types on the input and output files. You might want to add a menu-style pathname entry, so that you could work with complicated directory structures without a photographic memory.

That last suggestion leads me to warn that the program given here requires that you enter complete pathnames, or else have a legitimate prefix set. It does not allow slot and drive specification using ",Sx" or ",Dx" either. Now don't let me discourage you by listing all the things I left out. As is, the program is quite useful.

Lines 1870-1970 are the subroutine to open the input file. It first prints the prompt message "INPUT: ", and then waits for you type in a pathname. (If you selected the "simple" version, it instead prints out the pre-assembled pathname.) Line 1950 calls on ProDOS to open the file. If there is any error, lines 1990-2050 print out the error number and the subroutine returns with carry set. If there is no error, the subroutine returns with carry clear. You might want to add some code to this subroutine to read the filetype and make sure it is type $FA.

Lines 2070-2270 open the output file. This is a little more complicated, because the output file may or may not already exist. If the file does not yet exist, the open call will return with error number $46. Line 2160 tests for this error number. Any other error will be printed out, and the subroutine will return wiht carry set. Lines 2180-2260 attempt to CREATE a file. First the current date and time are read and stored in the parameter block, and then ProDOS is called to create the file. The parameter block used here assumes you want the output file to be type TXT ($04). If you modify the program for a different transformation you may want to change the file type. You might also want to add some code to check an existing file for the correct file type.

Lines 2290-2510 are the parameter blocks for the various MLI calls I used.

Lines 2560-2830 display the current line number and test for a keypress. The subroutine converts the binary line number to decimal one digit at a time. Nothing spectacular, and in fact it is very similar to the subroutine Woz used inside Integer BASIC over ten years ago. Lines 2740-2780 send out five backspaces to put the cursor back over the first digit. I originally tried to do this by just storing a cursor position back in location $24, but that only works in 40-column mode. Using backspaces it also works in 80-column mode, even with the 80-column cards used in Apple II+ machines. Lines 2790-2830 check for a keypress and return carry set if there was one.

Finally to the heart of this transformer: lines 2870-3330 convert one input line into one output line. Lines 2950-2970 zero two pointers, one for the input line scan and the other for the output line fill. These are used by the GET.CHAR and PUT.CHAR subroutines in lines 3180-3330. GET.CHAR picks up the character pointed to from the input line, and advances the pointer. PUT.CHAR stores the character in the A-register into the output line where the pointer points, and then advances the pointer. PUT.CHAR.MULTIPLE uses the X-register to store multiple copies of the character in the A-register.

Lines 2980-3000 pick up the next character from the input line, and branch according to its type. If the character is 00, this is the end of the line: then lines 3150 tack an ASCII <RETURN code on the end of the output line, and the subroutine is finished. If the character is positive, it is simple ASCII and lines 3010 simply copy it to the output line. If the character is negative, it is either a compressed blank string or a compressed string of some other character. Lines 3030-3090 handle compressed blanks by putting the blank count into the X-register, a blank in the A-register, and calling PUT.CHAR.MULTIPLE. Lines 3100-3130 handle the other kind of repeated character by reading the repeat count and character code from the input line and then calling PUT.CHAR.MULTIPLE.

Lines 3340-3550 are very similar in function to the PRINT subroutine I published last month in my SHOW INDEX program. A difference is the use of the "\" character in the string. Last month's program would have merely printed the "\". The program this month will print a <RETURN> when it sees a "\". This enabled me to use the PRINT macro to generate calls to the PRINT subroutine.

Lines 3560-3830 are a subroutine for printing strings which begin with a length byte. This subroutine is not ever called in the fancy version of my program given here. In the simple version it would be called to print the pre-assembled pathnames. I went ahead and assembled the subroutine anyway, because it is an interesting one. The macro PRSTR will call it, putting the address of the string to be printed as data immediately after the JSR PRSTR.

The subroutine used in the fancy version to read in a pathname is given in lines 3870-4250. The address of the buffer for receiving the pathname is given as data following the JSR RDSTR. Lines 3880-4010 accomplish the task of copying this address into the code below, into lines 4150 and 4240. Where the listing shows $3333, the address of the buffer will be stored. The loop in lines 4030-4210 keeps reading characters, storing them into that buffer, and displaying them on the screen.

If a backspace is read, the character at the end of the buffer is backed out and the string backspace-space-backspace is displayed on the screen. When a <RETURN> is read, the loop terminates and the number of bytes before the <RETURN> is stored in the first byte of the buffer by lines 4200-4250.

The subroutine GET.VIA.PNTR in lines 4270-4320 is called by both PRSTR and RDSTR to pickup the address which follows the JSR calling those subroutines.

Lines 4340-4480 either assemble two 65-byte buffers (fancy version) or two pathnames. Lines 4490 to the end assemble the other buffers used. Since the buffers used by ProDOS for the open files must begin on a page boundary, line 4530 skips ahead to the next page boundary.

For those of you who do not own the S-C Macro Assembler, I will start including this file transformer in executable form on future issues of the AAL Monthly Disk. Remember, you can save yourself a lot of typing by subscribing to the Monthly Disk. These disks include all of the source code in S-C format and also the text of all of the articles.

      1000 *SAVE SC.2.TEXT
      1010        .LIST MOFF,CON
      1020 ***line missing above was ".LIST MOFF,CON"
      1030 *--------------------------------
      1040        .OR $2000
      1050 *--------------------------------
      1060 FANCY  .EQ 1        =0 to use pre-assembled file names
      1070 *--------------------------------
      1080 RDKEY  .EQ $FD0C         A few handy Monitor subroutines
      1090 CROUT  .EQ $FD8E
      1100 PRBYTE .EQ $FDDA
      1110 COUT   .EQ $FDED
      1120 *--------------------------------
      1130 MLI.DATE   .EQ $BF90 thru BF93  System DATE and TIME
      1140 *--------------------------------
      1150 KEYBOARD   .EQ $C000     For aborting a conversion run
      1160 STROBE     .EQ $C010
      1170 *--------------------------------
      1180 PIN    .EQ $00      Scanning pointer for input line
      1190 POUT   .EQ $01      Stuffing pointer for output line
      1200 *--------------------------------
      1210        .MA MLI
      1220        JSR $BF00
      1230        .DA #$]1,]2
      1240        .EM
      1250 *--------------------------------
      1260        .MA PRINT
      1270        JSR PRINT    Print 00-term'd string after
      1280        .AS -"]1"    here is the string
      1290        .HS 00       here is the 00-terminator
      1300        .EM
      1310 *--------------------------------
      1320        .MA PRSTR
      1330        JSR PRSTR    Print string beginning with byte count
      1340        .DA ]1       address of string
      1350        .EM
      1360 *--------------------------------
      1370        .MA RDSTR    Read a string from keyboard or EXEC
      1380        JSR READ.STRING
      1390        .DA ]1       address of string
      1400        .EM
      1410 *--------------------------------
      1420 *   Program to read an S-C Macro source file
      1430 *      and write it as a text file.
      1440 *--------------------------------
      1450 FILE.TRANSFORMER
      1460        JSR OPEN.INPUT.FILE   Open Source File
      1470        BCS .3            ...unable to open it
      1480        LDA IREF          Get RefNum for READ
      1490        STA IOB.READ+1
      1500 *---Open the text file-----------
      1510        JSR OPEN.OUTPUT.FILE
      1520        BCS .3            ...unable to open it
      1530        LDA OREF          Get RefNum for WRITE
      1540        STA IOB.EOF+1            and for Truncation
      1550        STA IOB.WRITE+1
      1560 *---Print "LINE NUMBER"----------
      1570     >PRINT "\LINE NUMBER:  "  for progress indicator
      1580 *---read byte count from source file
      1590 .1     LDA #3       read 3 bytes: byte count + line #
      1600        STA IOB.READ+4
      1610       >MLI CA,IOB.READ
      1620        BCS .2       END OF FILE
      1630 *---read rest of line from source file
      1640        SEC          rest of line is 3 less
      1650        LDA LINE.SC               than byte count
      1660        SBC #3
      1670        STA IOB.READ+4
      1680        JSR DISPLAY.LINE.NUMBER  to indicate progress
      1690        BCS .2                   ...wants to abort
      1700       >MLI CA,IOB.READ
      1710        BCS .2       END OF FILE
      1720 *---Build Output Line------------
      1730        JSR CONVERT.ONE.LINE
      1740 *---write line on text file------
      1750        LDA POUT
      1760        STA IOB.WRITE+4   # BYTES TO WRITE
      1770       >MLI CB,IOB.WRITE
      1780        BCS .2
      1790        JMP .1
      1800 *---truncate text file-----------
      1810 .2    >MLI CF,IOB.EOF    GET MARK
      1820       >MLI D0,IOB.EOF    SET EOF
      1830 *---Close both files-------------
      1840 .3    >MLI CC,IOB.CLOSE
      1850        JSR CROUT
      1860        RTS
      1870 *--------------------------------
      1880 OPEN.INPUT.FILE
      1890     >PRINT "\ INPUT:  "
      1900   .DO FANCY
      1910     >RDSTR PATHI
      1920   .ELSE
      1930     >PRSTR PATHI
      1940   .FIN
      1950       >MLI C8,IOB.OPENI
      1960        BCS OPEN.ERROR
      1970        RTS
      1980 *--------------------------------
      1990 OPEN.ERROR
      2000        PHA
      2010     >PRINT "\ERROR:  "
      2020        PLA
      2030        JSR PRBYTE
      2040        SEC
      2050        RTS
      2060 *--------------------------------
      2070 OPEN.OUTPUT.FILE
      2080     >PRINT "\OUTPUT:  "
      2090   .DO FANCY
      2100     >RDSTR PATHO
      2110   .ELSE
      2120     >PRSTR PATHO
      2130   .FIN
      2140 .1    >MLI C8,IOB.OPENO
      2150        BCC .3
      2160        CMP #$46     was it FILE NOT FOUND?
      2170        BNE OPEN.ERROR
      2180       >MLI 82,0
      2190        LDY #3
      2200 .2     LDA MLI.DATE,Y
      2210        STA IOB.CREATE+8,Y
      2220        DEY
      2230        BPL .2
      2240       >MLI C0,IOB.CREATE
      2250        BCC .1
      2260        BCS OPEN.ERROR
      2270 .3     RTS
      2280 *--------------------------------
      2290 IOB.CREATE .HS 07
      2300            .DA PATHO
      2310            .HS C3.04.0000.01.0000.0000
      2320 *--------------------------------
      2330 IOB.OPENI  .HS 03
      2340            .DA PATHI
      2350            .DA BUF.I
      2360 IREF       .BS 1
      2370 *--------------------------------
      2380 IOB.OPENO  .HS 03
      2390            .DA PATHO
      2400            .DA BUF.O
      2410 OREF       .BS 1
      2420 *--------------------------------
      2430 IOB.READ   .HS 04.00
      2440            .DA LINE.SC
      2450            .DA 0
      2460            .DA 0
      2470 *--------------------------------
      2480 IOB.WRITE  .HS 04.00
      2490            .DA LINE.TEXT
      2500            .DA 0
      2510            .DA 0
      2520 *--------------------------------
      2530 IOB.EOF    .HS 02.00.00.00.00
      2540 IOB.CLOSE  .HS 01.00
      2550 *--------------------------------
      2560 DISPLAY.LINE.NUMBER
      2570        LDY #4
      2580 .1     LDX #"0"
      2590 .2     LDA LINE.SC+1
      2600        CMP DECTBL,Y
      2610        LDA LINE.SC+2
      2620        SBC DECTBH,Y
      2630        BCC .3
      2640        INX
      2650        STA LINE.SC+2
      2660        LDA LINE.SC+1
      2670        SBC DECTBL,Y
      2680        STA LINE.SC+1
      2690        JMP .2
      2700 .3     TXA
      2710        JSR COUT
      2720        DEY
      2730        BPL .1
      2740        LDY #5
      2750        LDA #$88
      2760 .4     JSR COUT
      2770        DEY
      2780        BNE .4
      2790        LDA KEYBOARD
      2800        CMP #$80     SET CARRY IF ANY KEY
      2810        BCC .5
      2820        STA STROBE
      2830 .5     RTS
      2840 *--------------------------------
      2850 DECTBL .DA #1,#10,#100,#1000,#10000
      2860 DECTBH .DA /1,/10,/100,/1000,/10000
      2870 *--------------------------------
      2880 *---build output line
      2890 *---     ignore line numbers
      2900 *---     expand blanks and multiples
      2910 *---     use low ascii
      2920 *---     end with 0D (return)
      2930 *--------------------------------
      2940 CONVERT.ONE.LINE
      2950        LDY #0
      2960        STY POUT
      2970        STY PIN
      2980 .1     JSR GET.CHAR
      2990        BEQ .4            ...End of Line
      3000        BMI .2            ...blanks or other multiple
      3010        JSR PUT.CHAR      simple ASCII char
      3020        JMP .1
      3030 .2     CMP #$C0
      3040        BCS .3            ...other multiple
      3050        AND #$7F
      3060        TAX
      3070        LDA #$20
      3080        JSR PUT.CHAR.MULTIPLE
      3090        JMP .1
      3100 .3     JSR GET.CHAR      get count
      3110        TAX
      3120        JSR GET.CHAR      get repeated char
      3130        JSR PUT.CHAR.MULTIPLE
      3140        JMP .1
      3150 .4     LDA #$0D
      3160        JMP PUT.CHAR
      3170 *--------------------------------
      3180 GET.CHAR
      3190        LDY PIN
      3200        INC PIN
      3210        LDA LINE.SC,Y
      3220        RTS
      3230 *--------------------------------
      3240 PUT.CHAR
      3250        LDX #1
      3260 PUT.CHAR.MULTIPLE
      3270        LDY POUT
      3280 .1     STA LINE.TEXT,Y
      3290        INY
      3300        DEX
      3310        BNE .1
      3320        STY POUT
      3330        RTS
      3340 *--------------------------------
      3350 PRINT
      3360        PLA          POP RETURN ADDRESS
      3370        STA PNTR+1     BECAUSE IT POINTS TO STRING
      3380        PLA
      3390        STA PNTR+2
      3400        JSR PRINT.VIA.PNTR
      3410        LDA PNTR+2     PUT RETURN ADDRESS ON STACK
      3420        PHA
      3430        LDA PNTR+1
      3440        PHA
      3450        RTS
      3460 *--------------------------------
      3470 PVP    ORA #$80
      3480        CMP #"\"
      3490        BNE .1
      3500        LDA #$8D
      3510 .1     JSR COUT     PRINT CHAR
      3520 PRINT.VIA.PNTR
      3530        JSR GET.VIA.PNTR   GET NEXT CHAR OF STRING
      3540        BNE PVP      00 = END OF STRING
      3550        RTS
      3560 *--------------------------------
      3570 *   Print a string which begins with a byte count
      3580 *      JSR PRSTR
      3590 *      .DA address of byte count
      3600 *--------------------------------
      3610 PRSTR
      3620        PLA
      3630        STA PNTR+1
      3640        PLA
      3650        STA PNTR+2
      3660        JSR GET.VIA.PNTR
      3670        TAX
      3680        JSR GET.VIA.PNTR
      3690        TAY
      3700        LDA PNTR+2     PUT RETURN ADDRESS ON STACK
      3710        PHA
      3720        LDA PNTR+1
      3730        PHA
      3740        STX PNTR+1
      3750        STY PNTR+2   POINT AT STRING NOW
      3760        JSR PNTR     GET BYTE COUNT
      3770        TAX
      3780 .1     JSR GET.VIA.PNTR  GET NEXT CHAR OF STRING
      3790        ORA #$80
      3800        JSR COUT
      3810        DEX
      3820        BNE .1
      3830        RTS
      3840 *--------------------------------
      3850 *   Input a string from the keyboard (or EXEC file)
      3860 *--------------------------------
      3870 READ.STRING
      3880        PLA
      3890        STA PNTR+1
      3900        PLA
      3910        STA PNTR+2
      3920        JSR GET.VIA.PNTR
      3930        STA .2+1
      3940        STA .3+1
      3950        JSR GET.VIA.PNTR
      3960        STA .2+2
      3970        STA .3+2
      3980        LDA PNTR+2     PUT RETURN ADDRESS ON STACK
      3990        PHA
      4000        LDA PNTR+1
      4010        PHA
      4020 *--------------------------------
      4030 .0     LDX #1
      4040 .1     JSR RDKEY
      4050        ORA #$80
      4060        CMP #$88
      4070        BNE .2
      4080        DEX
      4090        BEQ .0
      4100        JSR COUT
      4110        LDA #" "
      4120        JSR COUT
      4130        LDA #$88
      4140        BNE .25
      4150 .2     STA $3333,X
      4160        CPX #64
      4170        BCS .1
      4180        INX
      4190 .25    JSR COUT
      4200        CMP #$8D
      4210        BNE .1
      4220        DEX
      4230        DEX
      4240 .3     STX $3333
      4250        RTS
      4260 *--------------------------------
      4270 GET.VIA.PNTR
      4280        INC PNTR+1     BUMP POINTER TO NEXT CHAR
      4290        BNE PNTR
      4300        INC PNTR+1
      4310 PNTR   LDA $3333    GET NEXT CHAR OF STRING
      4320        RTS
      4330 *--------------------------------
      4340   .DO FANCY
      4350 PATHI  .BS 65
      4360   .ELSE
      4370 PATHI  .DA #PATHI.LEN
      4380        .AS "SC.2.TEXT"
      4390 PATHI.LEN  .EQ *-PATHI-1
      4400   .FIN
      4410 *--------------------------------
      4420   .DO FANCY
      4430 PATHO  .BS 65
      4440   .ELSE
      4450 PATHO  .DA #PATHO.LEN
      4460        .AS "TTT"
      4470 PATHO.LEN  .EQ *-PATHO-1
      4480   .FIN
      4490 *--------------------------------
      4500 LINE.SC    .BS 256
      4510 LINE.TEXT  .BS 256
      4520 *--------------------------------
      4530        .BS *+255/256*256-*   Pad to next page
      4540 BUF.I  .BS $400
      4550 BUF.O  .BS $400
      4560 *--------------------------------
      4570        .LIF

Modify CATALOG to Show All AuxTypes | Bob Sander-Cederlof

Apple Computer has started spreading the word that they are going to make further use of the AuxType field in the ProDOS file directory. Until now it has had three primary purposes, depending on the file type.

Binary files (type BIN, or $06) use the AuxType field to hold the loading address. System files (type SYS, or$FF) files do too, but since they always load at $2000 it is never used. Applesoft files (type BAS, or $FC) also use it for the loading address, but I don't believe it is ever used. I haven't tested it, though. If you use the S-C Macro Assembler under ProDOS, it uses a filetype called "S-C" for source code files. This is type $FA, which ProDOS reserved for Integer BASIC, just in case. S-C used type I files under DOS, so I decided to use type INT files under ProDOS. Anyway, I put the loading address in AuxType for these files too. It is never used.

Text files use AuxType to specify the record length for random-access files; it is 0000 for sequential files.

AppleWorks files (types ADB or $19, AWP or $1A, and ASP or $1B) are the most creative, using 15 of the 16 AuxType bits to modify the file name characters. Each bit corresponds to one of the file name characters, allowing AppleWorks to show and recognize the file name with lower-case letters and spaces in it. Regular ProDOS only displays upper-case letters in the names, and does not allow spaces. AppleWorks could have just stored the real ASCII codes in the filename field, but then the rest of ProDOS would not be able to access the files. By using the AuxType field, all is compatible.

Probably there are some other uses for AuxType that I do not know about. And now Apple has decided to use it to allow more than 256 different file types. It seems that 256 is not going to be enough now that we have ProDOS-16. If you are writing software for the Apple market and you need some private filetypes, you are supposed to write Apple Developer Tech Support and tell them of your needs. They in turn will assign you the appropriate file types. Since there are more requests than 256, they are starting to categorize them. For example, there may be a future file type for all word processors, with different AuxType values to distinguish various kinds of word processor files.

Apple has suggested that we start displaying the AuxType information in CATALOGs for more types than just TXT and BIN. I looked into BASIC.SYSTEM and found a patch that makes it display for all file types. I also looked into the S-C Macro Assembler's SCASM.SYSTEM for the same kind of patch.

In BASIC.SYSTEM, you need to change one byte from $27 to $11. The byte is at $A513 after BASIC.SYSTEM is operating, so you could do a POKE 42259,17 to turn the feature on, and POKE 42259,39 to turn it off. If you want to make a permanent change, you can do it this way:

           ]BLOAD BASIC.SYSTEM,TSYS,A$2000
           ]POKE 12051,17
           ]BSAVE BASIC.SYSTEM,TSYS,A$2000

Location 12051 is $2F13, which is where the code resides when the file is BLOADed as above.

In SCASM.SYSTEM, you need to change one byte from $16 to $00. The byte is near $B000 when the S-C Macro Assembler is operating, but the exact location depends on the particular edition you have. I suggest dis-assembling starting at $AFF0 and looking for the following code:

           C9 04  CMP #$04
           D0 04  BNE ...
           A9 D2  LDA #$D2
           D0 06  BNE ...
           C9 06  CMP #$06
           D0 16  BNE ...     change this "16" to "00"
           A9 C1  LDA #$C1

In the version I was using that "16" byte was at $B00E. From within the assembler I typed "$B00E:00" and that did the trick. If the "$" commands don't work in your computer, you can go to the monitor by typing "MNT" and the make the patch.

If you want to make a permanent change, you need to BLOAD SCASM.SYSTEM, patch the byte, and BSAVE it again just like I did with BASIC.SYSTEM above. In my version, I found the byte at $510E when the file was loaded.

BLOADing Directories | Bob Sander-Cederlof

Did you know that ProDOS will let you BLOAD a directory just like any other kind of file? I did not until today.

For example, if I want to load the directory of my Sider hard disk into memory, I can type BLOAD /HARD1,TDIR,A$1000. I can load in any subdirectory the same way. This works under both BASIC.SYSTEM (Applesoft) and SCASM.SYSTEM (the S-C Macro Assembler) shells.

In both cases, if you care to, you can find the length of the directory in bytes in locations $BEDB and $BEDC. $BEDB will always contain 00, and $BEDC will be the number of pages in the directory, or twice the number of blocks.

I tried BSAVEing... but it is prohibited. You get a FILE LOCKED message for your efforts.

Apple Assembly Line (ISSN 0889-4302) is published monthly by S-C SOFTWARE CORPORATION, P. O. Box 280300, Dallas, TX 75228 Phone (214) 324-2050. Subscription rate is $24 per year in the USA, Canada, and Mexico, or $36 in other countries. Back issues are $1.80 each for Volumes 1-7, $2.40 each for Volume 8 (plus postage). A subscription tothe newsletter with a Monthly Disk containing all program source code and article text is $64 per year in the USA, Canada and Mexico, and $90 to other countries.

All material herein is copyrighted by S-C SOFTWARE, all rights reserved. Unless otherwise indicated, all material herein is authored by Bob Sander-Cederlof. (Apple is a registered trademark of Apple Computer, Inc.)