Apple Assembly Line
Volume 8 -- Issue 7April 1988

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 TransformerBob 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 AuxTypesBob 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 DirectoriesBob 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.)