Volume 6 -- Issue 7 | April 1986

In This Issue...

- Tools for Restoring Lost Catalogs
- Writing Messages in Windows
- On Dividing a BCD Value by 4
- Booting into 80 Columns
- Faster Boot and More Space for DOS 3.3
- A "Gotcha!" in the New //c ROMs

65816 Books

The race is on! "Programming the 65816", by David Eyes from Prentice-Hall, originally scheduled for publication last October, is now expected in late April. "65816/65802 Assembly Language Programming", by Michael Fischer from Osborne/McGraw-Hill, scheduled for May publication, is now also due in late April. We have plenty of copies of these books on order, and a long list of patient people waiting for complete information on programming these powerful new chips. Coming Soon...

More Memory Expansion

We'd like to call your attention to the new ad for Applied Engineering's RamFactor board. This is a "Slinky" style memory expansion card for any standard slot of an Apple II, II+, or //e. We've been doing some of the firmware for this product, and it's been a delight to work with.

One thing the ad doesn't really emphasize is the power and flexibility of the program switcher firmware. You can set up the card with a variable number of variable-sized partitions and then switch between them almost instantly. Any partition can be based on any operating system, or on your own program. Couple this with the battery backup option (it's really more of an uninterruptible power suppply for the card) and you have what amounts to a hard disk operating at RAM speed!

Tools for Restoring Lost Catalogs | Bob Sander-Cederlof

From time to time it happens. One way or another I manage to clobber a catalog track on a disk. I have done it three times to Volume 1 in the DOS partition on my 10-megabyte Sider. (All it takes is "INIT HELLO,V1", forgetting that the last slot I accessed was the Sider's.)

All of the other tracks are still intact, but there is no way to get to them because the catalog is totally wiped out. One solution would be to have an accurate backup floppy for each Sider volume. This should be especially easy for Volume 1, because it is mostly standard Sider utilities. Mostly.... I have modifed several of them, and somehow I almost always have several programs-under-development that end up in V1. Of course, I could just as easily destroy the catalog track on any other volume, or any floppy for that matter.

It is for mistakes like mine that the program FIXCAT in "Bag of Tricks" was invented. FIXCAT looks over a diskette, finds all the sectors which look like they contain track/sector lists, and tries to piece together a new catalog track. Even though it is fairly automatic, I find it very difficult to use. I am always getting confused between old (deleted) copies of files and the current ones, and my disks usually have at least 2 or 3 dozen active files.

Recently it happened again. In fact, while I was working on one of the other articles in this issue of AAL. I decided to write a couple of utilities to help me make more effective use of FIXCAT. My new tools turn out to be useful even without FIXCAT, and you might enjoy just playing with them.

I assume you have a copy of "Beneath Apple DOS", or some other reference work which explains the format of DOS disks, catalog tracks, and track/sector lists.

The first tool I wrote looks through the tracks and sectors of a damaged disk for any sectors containing what could be track/sector lists. When one is found, I display the location of the supposed TS-list, all of the track/sectors in the list, and the first 64 bytes of the first data sector of the supposed file. Here is an example of the display:

    03-5:  03-4 03-3 03-2
           07 02 09 E8 03 81 2E 4C 49 46 00 16 F2 03 2A C0 ...h...LIF..r.*@
           06 08 53 41 56 45 81 42 43 44 81 4D 41 47 49 43 ..SAVE.BCD.MAGIC
           00 08 FC 03 2A C0 20 2D 00 05 06 04 54 00 0B 10 ..|.*@ -....T...
           04 87 4C 44 41 81 23 30 00 0C 1A 04 2E 31 85 53 ..LDA.#0.....1.S

The first 64 bytes are displayed in both hexadecimal and in ASCII, with periods being substituted for unprintable characters.

Having this information on paper before starting up FIXCAT is a big help. I can peacefully analyze the data at my desk, without the fear and panic associated with making "life and death" decisions at the keyboard. The first few bytes of a file will usually reveal what type of file it is.

If it is a source code file for the S-C Macro Assembler, Integer BASIC, or Applesoft, it will begin with a two-byte length for the file. Binary files begin with the load address, then the length. Text files start right in with data in ASCII, normally with all the high bits on. Since I almost always have a line near the beginning of my source files which contains the file name, I can usually read that file name in the dump of the first 64 bytes.

The FIND.TS.LISTS program is fairly short and simple. Starting from the bottom, the subroutine READTS at lines 2370-2430 calls on RWTS to read a particular track and sector. I elected to use my own IOB, rather than the one inside DOS at $B7E8. For simplicity's sake I assembled in the slot, drive, and volume information in my IOB. READTS only has to store the desired track and sector numbers, and call RWTS. I limited error handling to just re-calling RWTS, in the hopes of eventually succeeding. Should this begin to be a problem, I could print out an error message and either quit or continue with the next sector.

The subroutine READ.NEXT.SECTOR, lines 2200-2350, is used to scan through the disk from beginning to end. TS-lists cannot be in track 0, so I start with track 1. Since DOS allocates sectors in a track starting with sector $0F and going backwards to sector $00, I decided to scan the same way. This makes the files found list more closely to the same order as they were in the original catalog. I first advance the track/sector to the next one, then read it. Thus after reading, CUR.TRACK and CUR.SECTOR are pointing to the one we just read.

Now back to the top. Lines 1100-1130 start CUR.TRACK and CUR.SECTOR at 0. The first call to READ.NEXT.SECTOR will advance them to track 1, sector $0F. Successive calls will read the rest of track 1, then advance to track 2, and so on until we have finished track $22. When we try to read track $23, which does not exist, READ.NEXT.SECTOR will return with carry set and our program will end.

Lines 1170-1290 examine the data in the sector just read to see if it might be a track/sector list. The method I use is to require that there be at least one TS-pair, at BUF+12. I also require that all of the bytes beyond BUF+12 are within the range of valid track-sector pairs. If any bytes are out of range, I assume the current sector is not a TS-list. My tests seem to be adequate, because with every disk I have used it on it found all and only the TS-lists.

Having found TS-list, I call DISPLAY.TS.LIST to display it. Lines 1450-1540 display the location of the TS-list. The subroutine PR.TS prints the track and sector numbers from the A- and X-registers in the form "TT-S". Lines 1550-1720 list the TS-pairs in the TS-list, stopping at the first pair with a track number of zero. Up to 8 pairs are listed on a line.

Lines 1330-1430 read the first data sector of the supposed file, and display the first 64 bytes in hex and ASCII. This display is done by calling DISPLAY.NEXT.16 four times.

As it happens, I did have a fairly recently made backup of the clobbered disk. I thought I should also run my program against this good disk, and comparing the two displays would enable me to pinpoint each active file. However, what I really want from the GOOD disk is the information in the CATALOG. I decided to modify FIND.TS.LISTS to be driven from the catalog track, rather than from a search for TS-lists. The result was another useful tool, BIG.CATALOG.DISPLAY.

BIG.CATALOG.DISPLAY has the same kind of output that FIND.TS.LISTS does, except that it also lists the file type, file name, and sector count from the catalog. Information is included for deleted files for which entries are found in the catalog, as well as all the active files.

The subroutines DISPLAY.TS.LIST, DISPLAY.NEXT.16, SEVEN.SPACES, PR.TS, and READTS are used without any changes from the FIND.TS.LISTS program. Instead of READ.NEXT.SECTOR, I have now READ.NEXT.CATALOG.SECTOR. This starts at track $11, sector $0F, and works back as far as sector $01. A better way might be to follow the actual chain, beginning in the VTOC sector, but the current scheme is easier and works with most of my disks.

Lines 1140-1180 set up the initial catalog track and sector. Lines 1190-1210 read the catalog sector. If the returned status is positive we did read a sector, and continue processing; if not, we are finished. Lines 1220-1250 set up the buffer address in the IOB for reading TS-lists and data sectors: we do not want to read them over the top of the catalog sector we are working with.

Lines 1270-1320 set up a loop for processing each of the seven file entries in the current catalog sector. The "NEXT" part of the loop is at lines 1350-1440. Each catalog entry takes 35 bytes, so lines 1350-1440 add 35 to the pointer.

DISPLAY.DATA.FOR.ONE.FILE first checks for a zero entry, meaning the end of the catalog. A catalog is initialized to all zeroes, so as soon as we find a zero entry we know there are no more files. Next, at lines 1520-1550, I check for a deleted file. If the track number is negative, it is a deleted file. The actual track number of a deleted file is saved on top of the 30th character of the file name, so I pick it up there. Lines 1560-1590 save the track and sector of the TS-list, so I can read it later. Lines 1600-1650 display the file type as a hex value, followed by two dashes.

Lines 1660-1700 print the first 29 characters of the file name. I don't print the last character because for a deleted file it will have been clobbered by saving the track number there. Probably what I should do here is print either the last character for an active file, or some special symbol for a deleted file. You can add that code if you like.

Lines 1710-1770 pick up the file size, in number of sectors, and print it as a hex value. The sector count includes the sector for the TS-list.

Lines 1780-1860 read the track/sector list for the file. If either the track number or the sector number is out of range, nothing is read and we skip any further processing for this file.

Lines 1870-1940 read in the first data sector for the file. Again, if either the track or sector number is out of range, we don't try to read it. Finally, lines 1950-2000 display the first 64 bytes of the file.

I hope you find these new tools as useful as I have. Of course, I could hope you will never NEED them, but that would prabably be a vain hope. I also hope you have "Bag of Tricks" or some similar utility to put it all back together after you get the information my tools provide. And if I ever clobber Volume 1 on my Sider again (perish the thought), I intend to modify my copies of DOS so they will not allow me to INIT a volume on the Sider.

      1000 *SAVE S.FIND T/S LISTS
      1010 *--------------------------------
      1020 CUR.SECTOR .EQ 0
      1030 CUR.TRACK  .EQ 1
      1040 *---------------------------------
      1050 COUT   .EQ $FDED
      1060 CROUT  .EQ $FD8E
      1070 PRBYTE .EQ $FDDA
      1080 ENTER.RWTS .EQ $3D9
      1090 *--------------------------------
      1100 FIND.TS.LISTS
      1110        LDA #0
      1120        STA CUR.SECTOR
      1130        STA CUR.TRACK
      1140 .1     JSR READ.NEXT.SECTOR
      1150        BCC .2       GOT A SECTOR, CHECK IT
      1160        RTS          END OF DISK, QUIT
      1170 *---CHECK IF THIS IS T/S LIST----
      1180 .2     LDA BUF+12   TRACK # FOR FIRST DATA SECTOR
      1190        BEQ .1       ...NO, TRY NEXT ONE
      1200        LDY #12
      1210 .3     LDA BUF,Y
      1220        CMP #35
      1230        BCS .1       ...NOT VALID TRACK
      1240        INY
      1250        LDA BUF,Y
      1260        CMP #16
      1270        BCS .1       ...NOT VALID SECTOR
      1280        INY
      1290        BNE .3       ...MORE IN SECTOR TO CHECK
      1300 *---DISPLAY THE T/S LIST---------
      1310        JSR DISPLAY.TS.LIST
      1320 *---READ FIRST DATA SECTOR-------
      1330        LDY BUF+12
      1340        LDX BUF+13
      1350        JSR READTS
      1360 *---DISPLAY FIRST 64 BYTES-------
      1370        LDY #0
      1380        JSR DISPLAY.NEXT.16
      1390        JSR DISPLAY.NEXT.16
      1400        JSR DISPLAY.NEXT.16
      1410        JSR DISPLAY.NEXT.16
      1420        JSR CROUT
      1430        JMP .1
      1440 *--------------------------------
      1450 DISPLAY.TS.LIST
      1460        JSR CROUT
      1470        LDA CUR.TRACK
      1480        LDX CUR.SECTOR
      1490        JSR PR.TS
      1500        LDA #":"
      1510        JSR COUT
      1520        LDA #" "
      1530        JSR COUT
      1540        JSR COUT
      1550        LDY #0
      1560 .1     LDA BUF+13,Y      SECTOR
      1570        TAX
      1580        LDA BUF+12,Y      TRACK
      1590        BEQ .2       ...END OF LIST
      1600        JSR PR.TS
      1610        LDA #" "
      1620        JSR COUT
      1630        TYA
      1640        AND #$0F
      1650        CMP #$0E
      1660        BNE .3
      1670        JSR SEVEN.SPACES
      1680 .3     INY
      1690        INY
      1700        CPY #-12
      1710        BCC .1
      1720 .2     RTS
      1730 *--------------------------------
      1740 DISPLAY.NEXT.16
      1750        JSR SEVEN.SPACES
      1760 .1     LDA BUF,Y
      1770        JSR PRBYTE
      1780        LDA #" "
      1790        JSR COUT
      1800        INY
      1810        TYA
      1820        AND #$0F
      1830        BNE .1
      1840        TYA
      1850        SEC
      1860        SBC #16
      1870        TAY
      1880 .2     LDA BUF,Y
      1890        ORA #$80
      1900        CMP #$A0
      1910        BCS .3
      1920        LDA #"."
      1930 .3     JSR COUT
      1940        INY
      1950        TYA
      1960        AND #$0F
      1970        BNE .2
      1980        RTS
      1990 *--------------------------------
      2000 SEVEN.SPACES
      2010        JSR CROUT
      2020        LDA #" "
      2030        LDX #7
      2040 .4     JSR COUT
      2050        DEX
      2060        BNE .4
      2070        RTS
      2080 *--------------------------------
      2090 PR.TS
      2100        JSR PRBYTE
      2110        LDA #"-"
      2120        JSR COUT
      2130        TXA
      2140        ORA #"0"
      2150        CMP #$BA
      2160        BCC .1
      2170        ADC #6
      2180 .1     JMP COUT
      2190 *--------------------------------
      2200 *   READ NEXT SECTOR
      2210 *--------------------------------
      2220 READ.NEXT.SECTOR
      2230        LDX CUR.SECTOR
      2240        LDY CUR.TRACK
      2250        DEX          NEXT SECTOR
      2260        BPL .1       ...SAME TRACK
      2270        LDX #15      ...NEXT TRACK
      2280        INY
      2290        CPY #35
      2300        BCS .2       ...END OF DISK
      2310 .1     STY CUR.TRACK
      2320        STX CUR.SECTOR
      2330        JSR READTS
      2340        CLC
      2350 .2     RTS
      2360 *--------------------------------
      2370 READTS STX IOB.SECTOR
      2380        STY IOB.TRACK
      2390 .2     LDA /IOB
      2400        LDY #IOB
      2410        JSR ENTER.RWTS
      2420        BCS .2       ...TRY AGAIN IF ERROR
      2430        RTS
      2440 *---------------------------------
      2450 *      IOB FOR RWTS CALLS
      2460 *---------------------------------
      2470 IOB
      2480 IOB.TYPE   .HS 01   0--MUST BE $01
      2490 IOB.SLOT16 .HS 60   1--SLOT # TIMES 16
      2500 IOB.DRIVE  .HS 01   2--DRIVE # (1 OR 2)
      2510 IOB.VOLUME .HS 00   3--DESIRED VOL # (0 MATCHES ANY)
      2520 IOB.TRACK  .BS 1    4--TRACK # (0 TO 34)
      2530 IOB.SECTOR .BS 1    5--SECTOR # (0 TO 15)
      2540 IOB.PNTDCT .DA DCT  6--ADDRESS OF DCT
      2550 IOB.BUFFER .DA BUF  8--ADDRESS OF DATA
      2560 IOB.SECTSZ .DA 256 10--# BYTES IN A SECTOR
      2570 IOB.OPCODE .HS 01  12--0=SEEK, 1=READ, 2=WRITE, OR 4=FORMAT
      2580 IOB.ERROR  .BS 1   13--ERROR CODE: 0, 8, 10, 20, 40, 80
      2590 IOB.ACTVOL .BS 1   14--ACTUAL VOLUME # FOUND
      2600 IOB.PRVSLT .HS 60  15--PREVIOUS SLOT #
      2610 IOB.PRVDRV .HS 01  16--PREVIOUS DRIVE #
      2620 *--------------------------------
      2630 DCT    .HS 0001EFD8
      2640 *--------------------------------
      2650 BUF    .BS 256
      2660 *--------------------------------

      1000 *SAVE S.BIG CATALOG DISPLAY
      1010 *--------------------------------
      1020 CAT.SECTOR .EQ 0
      1030 CAT.TRACK  .EQ 1
      1040 CNTR       .EQ 2
      1050 PNTR       .EQ 3,4
      1060 TS.TRACK   .EQ 5
      1070 TS.SECTOR  .EQ 6
      1080 *---------------------------------
      1090 COUT   .EQ $FDED
      1100 CROUT  .EQ $FD8E
      1110 PRBYTE .EQ $FDDA
      1120 ENTER.RWTS .EQ $3D9
      1130 *--------------------------------
      1140 BIG.CATALOG.DISPLAY
      1150        LDA #15
      1160        STA CAT.SECTOR
      1170        LDA #17
      1180        STA CAT.TRACK
      1190 .1     JSR READ.NEXT.CATALOG.SECTOR
      1200        BPL .2       GOT A SECTOR
      1210 .4     RTS
      1220 .2     LDA #BUF
      1230        STA IOB.BUFFER
      1240        LDA /BUF
      1250        STA IOB.BUFFER+1
      1260 *--------------------------------
      1270        LDA #CAT+11
      1280        STA PNTR
      1290        LDA /CAT+11
      1300        STA PNTR+1
      1310        LDA #7
      1320        STA CNTR
      1330 .3     JSR DISPLAY.DATA.FOR.ONE.FILE
      1340        BCS .4       ...END OF CATALOG
      1350        LDA PNTR
      1360        ADC #35
      1370        STA PNTR
      1380        LDA PNTR+1
      1390        ADC #0
      1400        STA PNTR+1
      1410        DEC CNTR
      1420        BNE .3
      1430        JSR CROUT
      1440        JMP .1
      1450 *--------------------------------
      1460 DISPLAY.DATA.FOR.ONE.FILE
      1470        LDY #0
      1480        LDA (PNTR),Y
      1490        BNE .1
      1500        SEC
      1510        RTS
      1520 .1     BPL .15
      1530        LDY #32
      1540        LDA (PNTR),Y      REAL TRACK OF DELETED FILE
      1550        LDY #0
      1560 .15    STA TS.TRACK
      1570        INY
      1580        LDA (PNTR),Y
      1590        STA TS.SECTOR
      1600        INY
      1610        LDA (PNTR),Y      GET FILE TYPE
      1620        JSR PRBYTE
      1630        LDA #"-"
      1640        JSR COUT
      1650        JSR COUT
      1660 .2     INY
      1670        LDA (PNTR),Y      PRINT FILE NAME
      1680        JSR COUT
      1690        CPY #31           DON'T PRINT LAST CHAR OF NAME
      1700        BCC .2
      1710        INY
      1720        INY
      1730        LDA (PNTR),Y
      1740        JSR PRBYTE
      1750        INY
      1760        LDA (PNTR),Y
      1770        JSR PRBYTE
      1780 *---READ T/S LIST----------------
      1790        LDX TS.SECTOR
      1800        CPX #16
      1810        BCS .9
      1820        LDY TS.TRACK
      1830        CPY #35
      1840        BCS .9
      1850        JSR READTS
      1860        JSR DISPLAY.TS.LIST
      1870 *---READ FIRST DATA SECTOR-------
      1880        LDY BUF+12
      1890        CPY #35
      1900        BCS .9
      1910        LDX BUF+13
      1920        CPX #16
      1930        BCS .9
      1940        JSR READTS
      1950 *---DISPLAY FIRST 64 BYTES-------
      1960        LDY #0
      1970        JSR DISPLAY.NEXT.16
      1980        JSR DISPLAY.NEXT.16
      1990        JSR DISPLAY.NEXT.16
      2000        JSR DISPLAY.NEXT.16
      2010 .9     JSR CROUT
      2020        CLC
      2030        RTS
      2040 *--------------------------------
      2050 DISPLAY.TS.LIST
      2055   .LIST OFF
      2060        JSR CROUT
      2070        LDA TS.TRACK
      2080        LDX TS.SECTOR
      2090        JSR PR.TS
      2100        LDA #":"
      2110        JSR COUT
      2120        LDA #" "
      2130        JSR COUT
      2140        JSR COUT
      2150        LDY #0
      2160 .1     LDA BUF+13,Y      SECTOR
      2170        TAX
      2180        LDA BUF+12,Y      TRACK
      2190        BEQ .2       ...END OF LIST
      2200        JSR PR.TS
      2210        LDA #" "
      2220        JSR COUT
      2230        TYA
      2240        AND #$0F
      2250        CMP #$0E
      2260        BNE .3
      2270        JSR SEVEN.SPACES
      2280 .3     INY
      2290        INY
      2300        CPY #-12
      2310        BCC .1
      2320 .2     RTS
      2325    .LIST ON
      2330 *--------------------------------
      2340 DISPLAY.NEXT.16
      2345    .LIST OFF
      2350        JSR SEVEN.SPACES
      2360 .1     LDA BUF,Y
      2370        JSR PRBYTE
      2380        LDA #" "
      2390        JSR COUT
      2400        INY
      2410        TYA
      2420        AND #$0F
      2430        BNE .1
      2440        TYA
      2450        SEC
      2460        SBC #16
      2470        TAY
      2480 .2     LDA BUF,Y
      2490        ORA #$80
      2500        CMP #$A0
      2510        BCS .3
      2520        LDA #"."
      2530 .3     JSR COUT
      2540        INY
      2550        TYA
      2560        AND #$0F
      2570        BNE .2
      2580        RTS
      2585   .LIST ON
      2590 *--------------------------------
      2600 SEVEN.SPACES
      2605   .LIST OFF
      2610        JSR CROUT
      2620        LDA #" "
      2630        LDX #7
      2640 .4     JSR COUT
      2650        DEX
      2660        BNE .4
      2670        RTS
      2675   .LIST ON
      2680 *--------------------------------
      2690 PR.TS
      2695   .LIST OFF
      2700        JSR PRBYTE
      2710        LDA #"-"
      2720        JSR COUT
      2730        TXA
      2740        ORA #"0"
      2750        CMP #$BA
      2760        BCC .1
      2770        ADC #6
      2780 .1     JMP COUT
      2785   .LIST ON
      2790 *--------------------------------
      2800 *   READ NEXT CATALOG SECTOR
      2810 *--------------------------------
      2820 READ.NEXT.CATALOG.SECTOR
      2830        LDA #CAT
      2840        STA IOB.BUFFER
      2850        LDA /CAT
      2860        STA IOB.BUFFER+1
      2870        LDX CAT.SECTOR
      2880        LDY CAT.TRACK
      2890        JSR READTS
      2900        DEC CAT.SECTOR
      2910        RTS
      2920 *--------------------------------
      2930 READTS STX IOB.SECTOR
      2935    .LIST OFF
      2940        STY IOB.TRACK
      2950 .2     LDA /IOB
      2960        LDY #IOB
      2970        JSR ENTER.RWTS
      2980        BCS .2       ...TRY AGAIN IF ERROR
      2990        RTS
      2995    .LIST ON
      3000 *---------------------------------
      3010 *      IOB FOR RWTS CALLS
      3020 *---------------------------------
      3030 IOB
      3035    .LIST OFF
      3040 IOB.TYPE   .HS 01   0--MUST BE $01
      3050 IOB.SLOT16 .HS 60   1--SLOT # TIMES 16
      3060 IOB.DRIVE  .HS 01   2--DRIVE # (1 OR 2)
      3070 IOB.VOLUME .HS 00   3--DESIRED VOL # (0 MATCHES ANY)
      3080 IOB.TRACK  .BS 1    4--TRACK # (0 TO 34)
      3090 IOB.SECTOR .BS 1    5--SECTOR # (0 TO 15)
      3100 IOB.PNTDCT .DA DCT  6--ADDRESS OF DCT
      3110 IOB.BUFFER .DA BUF  8--ADDRESS OF DATA
      3120 IOB.SECTSZ .DA 256 10--# BYTES IN A SECTOR
      3130 IOB.OPCODE .HS 01  12--0=SEEK, 1=READ, 2=WRITE, OR 4=FORMAT
      3140 IOB.ERROR  .BS 1   13--ERROR CODE: 0, 8, 10, 20, 40, 80
      3150 IOB.ACTVOL .BS 1   14--ACTUAL VOLUME # FOUND
      3160 IOB.PRVSLT .HS 60  15--PREVIOUS SLOT #
      3170 IOB.PRVDRV .HS 01  16--PREVIOUS DRIVE #
      3175    .LIST ON
      3180 *--------------------------------
      3190 DCT    .HS 0001EFD8
      3200 *--------------------------------
      3210 BUF    .BS 256
      3220 CAT    .BS 256
      3230 *--------------------------------
      3235    .LIST OFF

Writing Messages in Windows | Bob Sander-Cederlof

The idea for the following program came from some similar code in the Cirtech Flipster software. Their "program manager" software displays a series of messages and menus in selected windows using a simple subroutine.

The windows are not quite as sophisticated as you may be used to if you are a Macintosh fan. This program divides the screen up vertically, with each window running the full screen width. Calls to the program specify which window to write a message into. The JSR MSG.IN.WINDOW is followed by a single byte specifying which window to use, the ASCII text of the message, and a final 00 byte signifying the end of message. MSG.IN.WINDOW first sets up the window, then clears it, then displays the message in it, and then returns to continue execution right after the 00 byte. MSG.IN.WINDOW does not make any provision for saving the previous contents of the screen inside the window and restoring it later. As I said, this is much simpler than Mac windows.

The Apple monitor has built-in window capability, with the current window being defined by four bytes in page zero. $20 is called LEFT, and defines the starting column of a screen line. This is normally 0, meaning the first column. $21 is called WIDTH, and specifies how many characters are in each line. This is usually 40 ($28), but may be 80 ($50) in a //c or enhanced //e in 80-column mode. MSG.IN.WINDOW does not make any changes to LEFT or WIDTH, although you could modify it to do so.

$22 is called WNDTOP, and specifies the top line of the working window. This is usually 0, meaning to start at the top of the screen. It could be as large as 23 ($17), meaning the bottom line of the screen. $23 is called WNDBOT, and specifies the bottom line of the working window. The number in WNDBOT is actually the number of the next line below the working window, and is usually 24 ($18) to specify a window that goes all the way to the bottom of the screen. MSG.IN.WINDOW stores new values in WNDTOP and WNDBOT, according to a table of line numbers called WINDOW.DATA.

My WINDOW.DATA table lists six different windows, but of course you could have as many as you wish. They can even overlap. The table I used contains the line numbers 0, 24, 0, 3, 9, 18, 20, and 24. This corresponds to the following windows:

         Index     WNDTOP WNDBOT Window
           0          0     24    0-23  <full screen>
           1       <better not use!!!>
           2          0      3    0-2
           3          3      9    3-8
           4          9     18    9-17
           5         18     20   18-19
           6         20     24   20-23

Lines 1080-1130 in the listing below detail the calling sequence for MSG.IN.WINDOW. The test program in lines 1500 and following shows some actual calls, with a "wait for any keystroke" between messages so you can see it happen.

Lines 1140-1180 save the caller's return address, placed on the stack by the JSR MSG.IN.WINDOW. This address will be used to pick up the calling parameters, and then used to return to the calling program. The subroutine in lines 1400-1460 increments the pointer and picks up the next byte from the calling sequence.

When we are finished displaying the message, the pointer will be pointing at the terminal 00 byte. Placing the pointer address back on the stack lets us use an RTS opcode to return to the caller. This is done in lines 1340-1390.

Lines 1200-1250 pick up the window index from the first byte following the JSR instruction. This indexes the WINDOW.DATA table, so two entries from that table are moved into WNDTOP and WNDBOT. The the monitor HOME subroutine can be called to clear the window and place the cursor in the top-left corner of the window.

Lines 1270-1330 display the message, if any. If there is no message, there still must be a terminal 00 byte. By judicious use of 8D (return) and 8A (linefeed) characters, you can display the message any way you like. If the message is too large for the window, lines will be scrolled out the top of the window and lost.

The MSG.IN.WINDOW subroutine illustrates a commonly used technique of placing messages to be printed "in-line", like PRINT "message" statements in Applesoft. I personally prefer to collect all my messages together, and use a message number in a register to select which one to print. One problem with my preferred method is that my programs are then easier to disassemble ... if that is a problem. The 6502 was not designed for easy transfer of calling parameters which follow the JSR. (The 65816 makes this kind of code easier, with its stack-relative address mode.)

      1000 *SAVE S.MSG.INTO.WNDW
      1010 *--------------------------------
      1020 HOME   .EQ $FC58
      1030 COUT   .EQ $FDED
      1040 *--------------------------------
      1050 PNTR   .EQ $00,01
      1060 WNDTOP .EQ $22
      1070 WNDBOT .EQ $23
      1080 *--------------------------------
      1090 *   CALL:   JSR MSG.IN.WINDOW
      1100 *           .DA #<window number>
      1110 *           .AS text of message
      1120 *           .HS 00    <end of msg flag>
      1130 *--------------------------------
      1140 MSG.IN.WINDOW
      1150        PLA          GET RETURN ADDRESS INTO PNTR
      1160        STA PNTR     LO BYTE
      1170        PLA
      1180        STA PNTR+1   HI BYTE
      1190 *---SETUP WINDOW TOP & BOTTOM----
      1200        JSR GET.NEXT.CALL.BYTE
      1210        TAX          WINDOW INDEX
      1220        LDA WINDOW.DATA,X
      1230        STA WNDTOP
      1240        LDA WINDOW.DATA+1,X
      1250        STA WNDBOT
      1260        JSR HOME     CLEAR THE WINDOW
      1270 *---DISPLAY MESSAGE, IF ANY------
      1280        LDY #0
      1290 .1     JSR GET.NEXT.CALL.BYTE
      1300        BEQ .2       END OF MESSAGE
      1310        ORA #$80     ...JUST IN CASE
      1320        JSR COUT
      1330        JMP .1
      1340 *---RETURN TO CALLER-------------
      1350 .2     LDA PNTR+1   HI BYTE
      1360        PHA
      1370        LDA PNTR     LO BYTE
      1380        PHA
      1390        RTS
      1400 *--------------------------------
      1410 GET.NEXT.CALL.BYTE
      1420        INC PNTR     LO BYTE
      1430        BNE .1
      1440        INC PNTR+1   HI BYTE
      1450 .1     LDA (PNTR),Y
      1460        RTS
      1470 *--------------------------------
      1480 WINDOW.DATA
      1490        .DA #0,#24,#0,#3,#9,#18,#20,#24
      1500 *--------------------------------
      1510 T
      1520        JSR MSG.IN.WINDOW
      1530        .DA #2       TOP WINDOW
      1540        .AS -/TOP LINE OF THE SCREEN/
      1550        .HS 8D
      1560        .AS -/SECOND LINE OF THE SCREEN/
      1570        .HS 8A
      1580        .AS -/...AND THE THIRD/
      1590        .HS 00       END MSG
      1600        JSR W
      1610        JSR MSG.IN.WINDOW
      1620        .DA #6       BOTTOM WINDOW
      1630        .AS -/LINE 21/
      1640        .HS 8A
      1650        .AS -/...LINE 22/
      1660        .HS 8A.8A
      1670        .AS -/...AND LINE 24/
      1680        .HS 00       END MSG
      1690        JSR W
      1700        JSR MSG.IN.WINDOW
      1710        .DA #0       FULL SCREEN
      1720        .AS -/MY MESSAGE/
      1730        .HS 00       END MSG
      1740        RTS
      1750 *--------------------------------
      1760 W      LDA $C000    WAIT FOR KEY BEFORE CONTINUING
      1770        BPL W
      1780        STA $C010
      1790        RTS
      1800 *--------------------------------

On Dividing a BCD Value by 4 | Bob Sander-Cederlof

The 6502 allows two kinds of addition and subtraction operations, depending on the state of the D-bit in the status register. After a SED (Set D) instruction, the ADC and SBC instructions will operate in decimal mode; after CLD (CLear D), ADC and SBC will operate in binary mode.

In decimal mode the range of values in a byte is from $00 to $99. The left nybble is the ten's digit, and the right nybble is the unit's digit. The decimal mode makes some programs much easier to write, and others more difficult. Having both modes is nice.

In binary mode, if you want to divide by four you just shift the value right two bit-positions. If by 8, shift 3 times. And so on. In decimal mode, you can very easily divide by powers of ten; however, dividing by four is more difficult.

I needed a quick way to tell if a number in decimal mode was divisible by four. After inspecting the binary values of the decimal-mode numbers between 00 and 99 a, I found a way. If the ten's digit is even and the unit's digit 0, 4, or 8, the number is divisible by four. Also, if the ten's digit is odd and the unit's digit is 2 or 6, the number is divisible by four. This can be tested as follows:

           LDA VALUE
           AND #$13
           BEQ ...     ...TEN'S EVEN, UNITS=0,4,8
           EOR #$12
           BEQ ...     ...TEN'S ODD, UNITS=2,6
           ...         ...NOT DIVISIBLE

Next I needed a way to actually divide by four. Again I started by inspecting the various values involved. Simply shifting right twice does not do the job, except for numbers less than ten. You cannot even divide by two by simply shifting right once, unless the ten's digit is even. Hmmm.... If the ten's digit is odd, I could subtract 6 frist and then shift right once to divide by two. Doing all that twice would result in a division by four. The subtraction must be done in binary mode, not decimal. The subroutine below in lines 1460-1590 will divide the decimal number in VALUE by four, truncating any remainder, and return the quotient in the A-register. Lines 1600-1700 show a shorter way to divide by two, provided you don't mind using the X-register.

To test my subroutines, I wrote some test programs. The first program, lines 1000-1370, runs through the values 00 to 99, printing ten values to a line. Each number that is evenly divisible by four is flagged with an asterisk. The second program, lines 1720-1990, shows the quotient after calling DIVIDE.BCD.VALUE.BY FOUR.

I am sure there must be lots of other neat tricks possible by combining binary and decimal modes in the 6502. Do you know some? Send them in, and we will publish the best!

      1000 *SAVE BCD.MAGIC
      1010 *--------------------------------
      1020 CROUT  .EQ $FD8E
      1030 PRBYTE .EQ $FDDA
      1040 COUT   .EQ $FDED
      1050 *--------------------------------
      1060 VALUE  .EQ 0
      1070 *--------------------------------
      1080 T
      1090        LDA #0       FOR VALUE = 0 TO $FF
      1100 .1     STA VALUE
      1110        LDA #" "
      1120        JSR COUT
      1130        LDA VALUE
      1140        JSR PRBYTE
      1150 *--------------------------------
      1160        JSR IS.BCD.VALUE.DIVISIBLE.BY.FOUR
      1170        BEQ .2       ...YES
      1180        LDA #" "     ...NO
      1190        .HS 2C
      1200 .2     LDA #"*"
      1210        JSR COUT
      1220 *--------------------------------
      1230        LDA #" "     SEPARATE ITEMS IN CHART
      1240        JSR COUT
      1250        LDA VALUE    NEW LINE AFTER TEN VALUES
      1260        AND #$0F
      1270        CMP #9
      1280        BNE .3
      1290        JSR CROUT
      1300 *---NEXT VALUE-------------------
      1310 .3     SED          MUST DO ARITHMETIC
      1320        LDA VALUE       IN DECIMAL MODE
      1330        CLC
      1340        ADC #1
      1350        CLD          BACK TO BINARY
      1360        BCC .1       ...UNTIL WRAP-AROUND
      1370        RTS
      1380 *--------------------------------
      1390 IS.BCD.VALUE.DIVISIBLE.BY.FOUR
      1400        LDA VALUE    RETURN .EQ. STATUS IF YES
      1410        AND #$13            .NE. STATUS IF NOT
      1420        BEQ .1
      1430        EOR #$12
      1440 .1     RTS
      1450 *--------------------------------
      1460 DIVIDE.BCD.VALUE.BY.FOUR
      1470        LDA VALUE
      1480        JSR DIVIDE.BCD.VALUE.BY.TWO
      1490 DIVIDE.BCD.VALUE.BY.TWO
      1500        PHA
      1510        AND #$10
      1520        BEQ .1
      1530        PLA
      1540        SBC #6
      1550        LSR
      1560        RTS
      1570 .1     PLA
      1580        LSR
      1590        RTS
      1600 *--------------------------------
      1610 SHORTER.DIV.BY.TWO
      1620        LSR
      1630        TAX
      1640        AND #8
      1650        BEQ .1
      1660        DEX
      1670        DEX
      1680        DEX
      1690 .1     TXA
      1700        RTS
      1710 *--------------------------------
      1720 D
      1730        LDA #0       FOR VALUE = 0 TO $FF
      1740 .1     STA VALUE
      1750        LDA #" "
      1760        JSR COUT
      1770        LDA VALUE
      1780        JSR PRBYTE
      1790        LDA #"."
      1800        JSR COUT
      1810 *--------------------------------
      1820        JSR DIVIDE.BCD.VALUE.BY.FOUR
      1830        JSR PRBYTE
      1840 *--------------------------------
      1850        LDA #" "     SEPARATE ITEMS IN CHART
      1860        JSR COUT
      1870        LDA VALUE    NEW LINE AFTER TEN VALUES
      1880        AND #$0F
      1890        CMP #9
      1900        BNE .3
      1910        JSR CROUT
      1920 *---NEXT VALUE-------------------
      1930 .3     SED          MUST DO ARITHMETIC
      1940        LDA VALUE       IN DECIMAL MODE
      1950        CLC
      1960        ADC #1
      1970        CLD          BACK TO BINARY
      1980        BCC .1       ...UNTIL WRAP-AROUND
      1990        RTS

Booting into 80 Columns | Bill Morgan

The ProDOS version of the S-C Macro Assembler is carefully written to operate in either 40 or 80 columns. When you boot the disk the assembler starts out in the 40 column mode, because we couldn't take for granted that you would have (or want) the 80 column display. Well it turns out that most people (myself included) are using 80 columns and are getting tired of typing PR#3 every time they start up the assembler.

Marc Wolfgram called up today from Wisconsin to ask how to make the assembler start up in 80 columns, and that finally got me around to finding out how. It's embarassingly easy: just a two-byte patch. Here's the procedure, assuming you're in S-C Macro Assembler ProDOS:

         UNLOCK SCASM.SYSTEM
         BLOAD SCASM.SYSTEM,A$2000,TSYS
         $6001:00 C3
         BSAVE SCASM.SYSTEM,A$2000,TSYS,L17920
         LOCK SCASM.SYSTEM

We just changed the IO.INIT call from JMP MON.HOME to JMP $C300, and that's all there is to it! Now the next time you boot up, the assembler will be in 80 column mode. RESET will return you to 40 columns. PR#3 or NEW will restore 80 columns.

Thanks, Marc, for prompting me to find out about this.

Faster Boot and More Space for DOS 3.3 | Bob Sander-Cederlof

A freshly initialized DOS 3.3 disk has 496 free sectors, less whatever is used by your HELLO program. There are 16 more sectors that are either never used or which are wasted, in tracks 0 and 2. The following program modifies the code which writes the DOS image and the code which reads it back during boot, so that the entire image fits in tracks 0 and 1. A further change makes the space in track 2 available for normal files.

The new boot procedure actually is faster than the standard one, and all the new code takes less space than that which is replaced. All you give up is the ability to boot into machines with less than 48K. Does anyone still have one?

Standard DOS 3.3 stores the DOS image in two pieces. The code destined for $B600-BFFF is on track 0, sectors 0 through 9. The code for $9D00-B500 follows, from track 0/sector 10 through track 2/sector 4. Sectors 5-15 of track 2 are not used. The information stored in sectors 3 and 4 of track 2 (aimed at $B400-B5FF) is useless, because all this space is variables for DOS which do not need to be initialized. The same goes for sector 5 of track 0. The contents of sectors 10 and 11 of track 0 is not used on a "slave" disk, which is what you get with the INIT command. My disks have to stay slave disks, because we are going to reshuffle everything around so all the unused sectors end up in track 2.

My new layout stores $9D00-9DFF in track 0/sector 5, and $9E00-B3FF in track 0/sector 10 through track 1/sector 15. The following table summarizes the old and new layouts.

         Sector    Track 0    Track 1    Track 2
                  Old  New   Old  New   Old  New
           0       B6  B6     A1  A4     B1   ..
           1       B7  B7     A2  A5     B2   ..
           2       B8  B8     A3  A6     B3   ..
           3       B9  B9     A4  A7     B4   ..
           4       BA  BA     A5  A8     B5   ..
           5       BB  9D     A6  A9     ..   ..
           6       BC  BC     A7  AA     ..   ..
           7       BD  BD     A8  AB     ..   ..
           8       BE  BE     A9  AC     ..   ..
           9       BF  BF     AA  AD     ..   ..
          10       ..  9E     AB  AE     ..   ..
          11       ..  9F     AC  AF     ..   ..
          12       9D  A0     AD  B0     ..   ..
          13       9E  A1     AE  B1     ..   ..
          14       9F  A2     AF  B2     ..   ..
          15       A0  A3     B0  B3     ..   ..

I published the complete commented disassembly of the code which writes the DOS image on a disk and the code for the second stage boot in AAL way back in October, 1981. The second stage boot code begins at $B700, and the DOS writer starts at $B74A. They both use a subroutine at $B793 to read/write a range of sectors. I preserved the starting points for these two routines in the program which follows, but there is a lot of new empty space. If you are interested, you could go ahead and shove all the code segments together, patch all the calls for the new locations, and get one big area of free space for adding new features.

I was able to save coding space in several ways. First, by deciding that I would not worry about running in less than 48K. Second, that I could eliminate the extra code used to clobber the language card. This is a very common patch anyway, because most of us do not want to have to keep re-loading the language card area just because we re-boot DOS. Third, by eliminating the redundant calls to $FE89 and $FE93. The first stage boot does both of these just before jumping to the second stage boot, so there is no reason to do them again. And fourth, by being more efficient. If you want to, you can save even more by doing away with the subroutine at $B7C2: part of it is redundant, and the rest can be combined with the code at $B74A.

The standard DOS boot first loads $B600-BFFF from track 0, and then skips out to track 2 to read the rest hind-end-first. The track steps are 0-1-2-1-0. My new version starts in track 0, reads it all, then reads all of track 1, and it is done. The track steps are simply 0-1. It is a lot faster. However, the overall boot time is not significantly faster, due to the time spent finding track 0 in the first place, and the time spent loading the HELLO program.

Lines 1060-1140 install the new code. The entire $B7 page is replaced, as well as a single byte at $AEB3. This byte changes the VTOC on the newly initialized disk so that track 2 is available. While I was looking at this area, I noticed that the VTOC written on the new disk is not necessarily correct. DOS does not create an entirely new VTOC for the new disk. The bitmap area is new, and several other bytes are set up. However, DOS does not store any values in the bytes which tell how many tracks, sectors per track, sector size, and T/S entries per T/S list. This means that if the last access to a disk prior to initializing a new one was to a non-standard disk, the VTOC may be incorrect on the new disk. If I load a file from a large volume on my Sider, and then INIT a floppy, the floppy's VTOC indicates 32 sectors per track and 50 tracks! Ouch! Beware!

Lines 1180-1480 are the second stage boot code. The first stage boot is located at $B601, and actually executed at $801. It loads in sectors 0-9 of track 0 into $B600-BFFF, calls $FE89 and $FE93 to set the standard 40-column input hooks, and then jumps to $B700 with the slot*16 in the X-register. My stage two begins by copying the information which came from sector 5, now found at $BB00-BBFF, to the place it belongs at $9D00-9DFF (lines 1270-1320). Next I set up a call to my RWFT subroutine.

RWFT stands for Read/Write From Table. I have a table that describes all of the segments which must be loaded from the disk during boot, or written during initialization. Stage two boot must read the same things written by initialization, but init-ing requires first writing the stuff which will be loaded by stage one boot. Stage two boot calls RWFT with A=1 (read opcode for the IOB) and Y=2 (skipping the first two entries in the table). Initialization calls RWFT with A=2 (write opcode) and Y=0 (start at the beginning of the table).

RWFT gets four items out of the table for each step. The page number and sector number indicate the end of the range to be read or written. The count tells how many pages (or sectors) need to be read or written. All of the sectors must be in the track specified by the table entry. After one range has been read, RWFT steps to the next. The table terminates when the page address of 0 is found.

For some reason the code at $AEFF looks like this:

           AEFF-  JSR $B7C2
           AF02-  JSR $B74A

Both of these subroutines are never called from any other place, so they could be combined into one. Doing so would save several bytes. Furthermore, at least with my new RWFT program, lines 2120 and 2130 could be deleted, saving six more bytes.

There are still more ways to increase the storage on standard floppies, as you probably know. You can shorten the catalog, make a few other patches, and use some sectors in track 17 ($11).

You can usually use more than 35 tracks, since most drives will handle at least 36 and many a full 40. This also only takes a few simple patches. At $AEB5 you normally find a value $8C. Add 4 to this value for each additional track. This controls the loop that builds the bitmap of available sectors in the VTOC. The byte at $BEFE controls how many tracks the formatter in RWTS lays down. It is normally $23 (decimal 35), so add one for each additional track. Just before you start the INIT command, change the byte at $B3EF. This is normally $23, the number of tracks. Add 1 for each additional track. You have to be sure to do this last patch just prior to the INIT, because reading or writing another disk will cause it to be changed back.

Incidentally, this reminds me of the potential bug I mentioned above regarding writing out an incorrect VTOC. Once today I tried to catalog a disk that had been only partially initialized. The tracks had been written, but no VTOC or catalog sectors were. Of course I got an I/O ERROR. Next I decided to INIT that same disk. It went through the formatting stage, then bombed out with an I/O error when trying to write the catalog. Looking at the VTOC on this disk, the bytes for number of tracks, et cetera, were all zero!.

Now back to extra tracks. After making a disk with the extra tracks, you really need to check them to be sure your drive handles them. Use a disk zap program and try to write on the last track. Then try to write on the previous track. If your drive will go out that far, you will be successful. If you get an error trying to find the next to the last track, keep backing up until you find a track that does work. All the ones in between were written in the same location on the disk surface as the last track. If there were any missing tracks, you need to reformat the disk with fewer tracks.

An interesting side note to this discussion is that you could format a disk with LESS than 35 tracks if you wish. Just so you at least include track 17 ($11), you can reduce the values at $BEFE, $B3EF, and $AEB5 and stop short of a full disk. Some copy protection schemes do this, along with other tricks, to frustrate the making of copies.

      1000 *SAVE S.B700-B7FF DOS 3.3
      1010 *---------------------------------
      1020 FMP.SUBCOD .EQ $B5BC
      1030 FMW.VOLUME .EQ $B5F9
      1040 RWTS       .EQ $BD00
      1050 *---------------------------------
      1060 INSTALL
      1070        LDY #0       COPY NEW CODE INTO DOS
      1080 .1     LDA NEW.B700,Y    $B700...B7FF
      1090        STA $B700,Y
      1100        INY
      1110        BNE .1
      1120        LDA #8       PATCH TO INCLUDE TRACK 2
      1130        STA $AEB3    AS FREE SPACE
      1140        RTS
      1150 *---------------------------------
      1160 NEW.B700  .PH $B700
      1170 *--------------------------------
      1180 BOOT.STAGE2
      1190        STX IOB.SLOT16
      1200        STX IOB.PRVSLT
      1210        TXA          SLOT*16
      1220        LSR          GET SLOT #
      1230        LSR
      1240        LSR
      1250        LSR
      1260        TAX          X = SLOT NUMBER
      1270 *---COPY BB00-FF TO 9D00-FF------
      1280        LDY #0
      1290 .1     LDA $BB00,Y
      1300        STA $9D00,Y
      1310        DEY
      1320        BNE .1
      1330 *---SET CURRENT TRACKS @ 0-------
      1340        TYA          A = Y = 0
      1350        STA $4F8,X
      1360        STA $478,X
      1370 *---BUILD RWFT CALL--------------
      1380        INY          Y = 1
      1390        STY IOB.PRVDRV
      1400        STY IOB.DRIVE     DRIVE = 1
      1410        TYA               A = 1 (READ OPCODE)
      1420        INY               Y = 1 (RWFT INDEX)
      1430        JSR RWFT
      1440 *---COLD START DOS---------------
      1450        LDX #$FF
      1460        TXS          EMPTY STACK
      1470        STX IOB.VOLUME
      1480        JMP $9D84    DOS HARD ENTRY
      1490 *--------------------------------
      1500        .BS $B74A-*       <<<FILLER>>>
      1510 *---------------------------------
      1520 *      WRITE DOS IMAGE ON TRACKS 0-2
      1530 *---------------------------------
      1540 WRITE.DOS.IMAGE
      1550        LDA #2       WRITE OPCODE FOR RWTS
      1560        LDY #0       RWFT INDEX
      1570 *--------------------------------
      1580 *   READ/WRITE FROM TABLE
      1590 *--------------------------------
      1600 RWFT
      1610        STA IOB.OPCODE
      1620 .1     STY RWFT.INDEX
      1630        LDA RWFT.ADDR,Y
      1640        BEQ .3       ...END OF RWFT TABLE
      1650        STA IOB.BUFFER+1
      1660        LDA RWFT.TRACK,Y
      1670        STA IOB.TRACK
      1680        LDA RWFT.SECTOR,Y
      1690        STA IOB.SECTOR
      1700        LDA RWFT.COUNT,Y
      1710        STA RWFT.N
      1720 .2     LDA /IOB
      1730        LDY #IOB
      1740        JSR ENTER.RWTS
      1750        BCS .2       ...TRY AGAIN IF ERROR
      1760        DEC IOB.SECTOR    NEXT SECTOR
      1770        DEC IOB.BUFFER+1  NEXT PAGE
      1780        DEC RWFT.N
      1790        BNE .2
      1800        LDY RWFT.INDEX
      1810        INY
      1820        BNE .1       ...ALWAYS
      1830 .3     RTS
      1840 *--------------------------------
      1850 RWFT.N     .BS 1
      1860 RWFT.INDEX .BS 1
      1870 *--------------------------------
      1880 RWFT.ADDR   .HS BF.9D.A3.B3.00
      1890 RWFT.TRACK  .HS 00.00.00.01
      1900 RWFT.SECTOR .HS 09.05.0F.0F
      1910 RWFT.COUNT  .HS 0A.01.06.10
      1920 *--------------------------------
      1930        .BS $B7B5-*       <<<FILLER>>>
      1940 *---------------------------------
      1950 *      ENTER RWTS
      1960 *---------------------------------
      1970 ENTER.RWTS
      1980        PHP          SAVE STATUS ON STACK
      1990        SEI          DISABLE INTERRUPTS
      2000        JSR RWTS     CALL RWTS
      2010        BCS .1       ERROR RETURN
      2020        PLP          RESTORE STATUS
      2030        CLC          SIGNAL NO RWTS ERROR
      2040        RTS          RETURN TO CALLER
      2050 .1     PLP          RESTORE STATUS
      2060        SEC          SIGNAL RWTS ERROR
      2070        RTS          RETURN TO CALLER
      2080 *---------------------------------
      2090 *      SET UP RWTS TO WRITE DOS
      2100 *---------------------------------
      2110 SETUP.WRITE.DOS
      2120        LDA FMP.SUBCOD  IMAGE ADDRESS
      2130        STA IOB.BUFFER+1
      2140        LDA #0
      2150        STA IOB.BUFFER
      2160        LDA FMW.VOLUME  VOLUME #
      2170        EOR #$FF        UNCOMPLEMENT IT
      2180        STA IOB.VOLUME
      2190        RTS
      2200 *---------------------------------
      2210 *      CLEAR 256 BYTES STARTING AT ($42,43)
      2220 *---------------------------------
      2230 ZERO.CURRENT.BUFFER
      2240        LDA #0
      2250        TAY
      2260 .1     STA ($42),Y
      2270        INY
      2280        BNE .1
      2290        RTS
      2300 *---------------------------------
      2310        .BS $B7E8-*       <<<FILLER>>>
      2320 *---------------------------------
      2330 *      IOB FOR RWTS CALLS
      2340 *---------------------------------
      2350 IOB
      2360 IOB.TYPE   .HS 01   0--MUST BE $01
      2370 IOB.SLOT16 .HS 60   1--SLOT # TIMES 16
      2380 IOB.DRIVE  .HS 01   2--DRIVE # (1 OR 2)
      2390 IOB.VOLUME .HS 00   3--DESIRED VOL # (0 MATCHES ANY)
      2400 IOB.TRACK  .BS 1    4--TRACK # (0 TO 34)
      2410 IOB.SECTOR .BS 1    5--SECTOR # (0 TO 15)
      2420 IOB.PNTDCT .DA DCT  6--ADDRESS OF DCT
      2430 IOB.BUFFER .BS 2    8--ADDRESS OF DATA
      2440 IOB.SECTSZ .DA 256 10--# BYTES IN A SECTOR
      2450 IOB.OPCODE .BS 1   12--0=SEEK, 1=READ, 2=WRITE, OR 4=FORMAT
      2460 IOB.ERROR  .BS 1   13--ERROR CODE: 0, 8, 10, 20, 40, 80
      2470 IOB.ACTVOL .BS 1   14--ACTUAL VOLUME # FOUND
      2480 IOB.PRVSLT .HS 60  15--PREVIOUS SLOT #
      2490 IOB.PRVDRV .HS 01  16--PREVIOUS DRIVE #
      2500            .BS 2
      2510 DCT    .HS 0001EFD8
      2520        .BS 1
      2530 *--------------------------------

A "Gotcha!" in New //c ROMs | Robert H. Bernard

Apple seems to have installed a bug in the new ROM for the Apple //c which affects DOS 3.3. I am talking about the 3.5 ROM that supports Unidisk 3.5 and AppleTalk.

The new bug manifests itself when you use the control-IxxN command to either serial port. The older //c ROMs accumulated the "xx" number in $47F; the new ones do it in $47E. Location $47E is supposed to be dedicated to slot 6, the slot where the disk drives are. DOS uses $47E to keep track of the current track position for drive 1. So, after doing the serial port command to set line length, the next time DOS tries to look at drive 1 it will have to re-calibrate.

Re-calibration is not a disaster, but it is annoying. A needless and not noiseless waste of time. To avoid it with the new ROMs, you have to save and restore the contents of $47E around any serial port command that involves scanning a numeric field.

I have looked through the entire listing of the 3.5 ROM that came with my upgrade kit, and there does not appear to be any reason why this variable was moved. Location $47F is not used for any new value that I can see.

Even though the Apple //c Technical Reference Manual reserves $47E for the firmware, and ProDOS doesn't use the cell, using a "slot 6" screen-hole for a slot 1 and 2 activity is a serious breach of the protocol for their use that dates back to the earliest Wozdays. I know Apple is dropping (or at least decreasing) their support of DOS 3.3, but this is ridiculous!

Apple Assembly Line is published monthly by S-C SOFTWARE CORPORATION, P.O. Box 280300, Dallas, Texas 75228. Phone (214) 324-2050. Subscription rate is $18 per year in the USA, sent Bulk Mail; add $3 for First Class postage in USA, Canada, and Mexico; add $14 postage for other countries. Back issues are available for $1.80 each (other countries add $1 per back issue for postage).

All material herein is copyrighted by S-C SOFTWARE CORPORATION, all rights reserved. (Apple is a registered trademark of Apple Computer, Inc.)