In This Issue...
Latest 65C02 Word
The 65C02 really does exist, and we now have a couple of them. As reported inside, the chips we received work in an Apple //e, but not in a ][+. Well that seems to be a problem with the NCR chips that I have. Don Lancaster reports that his GTE chips work just fine in all flavors of Apples. I'm swapping an NCR processor for one of his GTE's, and will have more details next month. For the time being, if you buy a 65C02, be sure to get a guarantee that it will work in your Apple.
The original Apple II came with a built-in mini-assembler. By typing "F666G" in the monitor, you entered a new realm. The prompt changed from "*" to "!"; errors not only earned a "beep" but also a printed "?"; and monitor commands were still available by typing an initial "$". I learned 6502 programming using this little tool, together with the handy "L" disassembly command. At the time, none of the other computer systems on the market came with either mini-assembler or "L" command.
A mini-assembler allows you to type in mnemonics rather than converting them "by hand". It also will translate branch addresses to the relative offsets needed in relative branch instructions. It will not retain the source code on a file, and will not handle labels. If you want to modify a program, you have to use patches or retype the whole thing. A full assembler will accept labels and comments, and will have some method for working with stored source programs. The S-C Macro Assembler, for example, includes a co-resident source program editor. The extra features a full assembler can include are limited only by the potential market. But mini-assemblers are free.
A long time ago MICRO published a 6502 mini-assembler written in Commodore or OSI BASIC. I started converting it, just for fun, into Applesoft. It wasn't long before I realized that my thought processes were totally incompatible with the author's programming techniques. So I essentially started over. Last month the partially finished listing appeared out of some long forgotten crack, so I dusted it off and finished the program.
It operates a lot like the old "F666G" mini-assembler by Steve Wozniak. (And, even though it is in Applesoft, it is almost as fast.) The initial display is the address "0300" at the left margin, and the cursor in column 20 of the top line on an otherwise empty screen. You can type RETURN to quit, a colon followed by a hex address to change the assembly address, or an instruction mnemonic to be assembled.
I could go into a long-winded explanation of how the program works, describing each subroutine. But you can probably read the listing easily enough, and there are identifying REM statements with each subroutine. The really interesting part to me is the structure of the opcode tables which are contained as strings in OP$, F$, and E$. These tables are set up in lines 2030 through 2050.
OP$ contains the opcodes names. OP$(1) holds the names of all the single byte opcodes. If the input line has no operand data after the opcode mnemonic, the program will search through OP$(1) and had better find your mnemonic. If not, it is "???" for you! Note that the opcode names are three characters each, packed into one long string. Also note that ASL, LSR, ROL, and ROR appear in this string. These four opcodes can have an operand-less mode, as well as any of four modes with operands.
OP$(2) contains the mnemonics for the relative branches. OP$(3) holds "JMP" and "JSR". And OP$(4) holds all the rest, which I call the complex opcodes. These are the ones which can have a variety of addressing modes.
F$(1) through F$(4) correspond to OP$(1) through OP$(4). Each three digit group in one of the F$ strings is the opcode value (in decimal) for the corresponding mnemonic from OP$. F$(4) contains a base value, which will be augmented to obtain a specific value for the particular address mode chosen.
The complex opcodes can be classified in many different ways...I tried so many I lost count. I finally settled on the scheme shown in the two tables below:
Imm Zp Abs Z,X A,X Z,Y A,Y (X) ()Y + 08 04 0C 14 1C -- 18 00 10 Base ------------------------------------------------------ ADC 0 69 65 6D 75 7D -- 79 61 71 61 097 AND 0 29 25 2D 35 3D -- 39 21 31 21 033 CMP 0 C9 C5 CD D5 DD -- D9 C1 D1 C1 193 EOR 0 49 45 4D 55 5D -- 59 41 51 41 065 LDA 0 A9 A5 AD B5 BD -- B9 A1 B1 A1 161 ORA 0 09 05 0D 15 1D -- 19 01 11 01 001 SBC 0 E9 E5 ED F5 FD -- F9 E1 F1 E1 225 STA 1 -- 85 8D 95 9D -- 99 81 91 81 129 Imm Zp Abs Z,X A,X Z,Y A,Y (X) ()Y + 00 04 0C 14 1C 14 1C -- -- Base ------------------------------------------------------ ASL 2 -- 06 0E 16 1E -- -- -- -- 02 002 LSR 2 -- 46 4E 56 5E -- -- -- -- 42 066 ROL 2 -- 26 2E 36 3E -- -- -- -- 22 034 ROR 2 -- 66 6E 76 7E -- -- -- -- 62 098 BIT 3 -- 24 2C -- -- -- -- -- -- 20 032 CPX 4 E0 E4 EC -- -- -- -- -- -- E0 224 CPY 4 C0 C4 CC -- -- -- -- -- -- C0 192 DEC 5 -- C6 CE D6 DE -- -- -- -- C2 194 INC 5 -- E6 EE F6 FE -- -- -- -- E2 226 LDX 6 A2 A6 AE -- -- B6 BE -- -- A2 162 LDY 7 A0 A4 AC B4 BC -- -- -- -- A0 160 STX 8 -- 86 8E -- -- 96 -- -- -- 82 130 STY 9 -- 84 8C 94 -- -- -- -- -- 80 128
The first column of numbers is the opcode class number. These numbers are stored in E$ (see line 2050). The next nine columns show the hex opcode values for each valid combination of opcode and address mode. The last two columns show the "base" value in both hex and decimal.
The top row of numbers (above the dashed lines) shows the augment needed to transform a "base" opcode value into the value for a specific address mode. I broke the data into two separate tables because the Imm and A,Y columns have one pair of values for class 0 and 1 opcodes and another for classes 2 through 9. The class number is used to select which address modes are legal for a given opcode, as well as in selecting the augment values.
If you have ever studied the listing of Wozniak's mini- assembler, you know that his approach was entirely different. If you look inside the S-C Macro Assembler you will find yet another approach. I suppose there are more approaches than existing assemblers. In our line of Cross Assemblers we use about five or six different techniques. The choice depends on the syntax of the operands and the bit structure of the opcodes, as well as whim.
I have also written a disassembler in Applesoft, and the beginnings of a simulator for 6502 code. Maybe they will see print in the near future. There is a lot to be learned from studying or even writing these kinds of programs, and they can even be useful.
100 GOTO 2000: REM MINI-ASSEMBLER FOR APPLE 110 REM --------------------------------- 120 REM PRINT BYTE M AS TWO HEX DIGITS 130 M1 = INT (M / 16):M2 = M - M1 * 16: HTAB Z:Z = Z + 2: PRINT MID$ (G$,M1 + 1,1); MID$ (G$,M2 + 1,1);: RETURN 140 REM --------------------------------- 150 REM POKE AND PRINT OBJECT BYTE 160 Z = Z + 1: GOSUB 110: POKE AD,M:AD = AD + 1: RETURN 170 REM --------------------------------- 180 REM SET UP TWO-BYTE ADDRESS FIELD 190 HI = INT (X / 256):LO = X - 256 * HI 200 REM --------------------------------- 210 REM OUTPUT THE OPCODE AND ADDRESS 220 Z = 7: VTAB PEEK (37):M = OP: GOSUB 160:M = LO: GOSUB 160: IF BY = 3 THEN M = HI: GOSUB 160 225 GOTO 1000 230 REM --------------------------------- 240 REM OUTPUT SINGLE-BYTE OPCODES 250 Z = 7: VTAB PEEK (37):M = OP: GOSUB 160: GOTO 1000 260 REM ---------------------------------- 270 REM PRINT NEXT ADDRESS 280 Z = 1: CALL - 868:M = INT (AD / 256): GOSUB 110:M = AD - M * 256: GOSUB 110: PRINT ":";: HTAB 20: RETURN 290 REM --------------------------------- 300 REM CONVERT C$ FROM HEX TO DECIMAL 310 X = 0:I = 0 320 CL$ = "": GOSUB 500: IF CC$ = "#" OR CC$ = "(" THEN CL$ = CC$ : GOSUB 500 325 IF C < 0 THEN CL$ = "ERR": RETURN 330 X = X * 16 + C: GOSUB 500: IF C > = 0 THEN 330 340 CL$ = CL$ + CC$: GOSUB 500: IF CC$ < > "" THEN 340 350 RETURN 500 REM -------------------------------- 510 REM GET NEXT CHAR FROM C$ 520 I = I + 1: IF I > LEN (C$) THEN CC$ = "":C = - 1: RETURN 530 CC$ = MID$ (C$,I,1):C = ASC (CC$) - 48: IF (C > 9 AND C < 17) OR C > 22 THEN C = - 1: RETURN 540 C = C - 7 * (C > 9): RETURN 900 REM -------------------------------- 910 VTAB PEEK (37): HTAB 7: PRINT "???" CHR$ (7): REM ERROR 990 REM -------------------------------- 1000 PRINT : CALL - 868: GOSUB 260: GOSUB 4000: IF IN$ = "" THEN STOP 1010 IF RIGHT$ (IN$,1) = ":" THEN C$ = IN$: GOSUB 290:AD = X: GOTO 1000 1020 L = LEN (IN$) - 3: IF L < 0 THEN 900 1025 VTAB PEEK (37): HTAB 6: CALL - 868: HTAB 20:L$ = LEFT$ (IN$,3) : IF L > 0 THEN GOTO 1050 1030 C$ = "":J = 1: PRINT L$: GOSUB 2100: IF K = 0 THEN 900 1040 GOTO 230 1050 REM -------------------------------- 1051 REM TRY RELATIVE BRANCHES 1052 C$ = RIGHT$ (IN$,L): PRINT L$" "C$: GOSUB 290: IF CL$ = "ERR" THEN 900 1053 J = 2: GOSUB 2100: IF K = 0 THEN 1060 1054 IF CL$ < > "" THEN 900 1056 X = X - AD - 2: IF X < - 128 OR X > 127 THEN PRINT "CAN'T B RANCH THAT FAR": GOTO 1000 1057 BY = 2:X = X + 256 * (X < 0): GOTO 190 1060 REM -------------------------------- 1070 REM TRY JMP AND JSR OPCODES 1080 J = 3: GOSUB 2100: IF K = 0 THEN 1110 1090 BY = 3: IF CL$ = "" THEN 190 1100 IF CL$ = "()" AND OP = 76 THEN OP = 108: GOTO 190 1105 GOTO 900 1110 REM -------------------------------- 1115 REM TRY COMPLEX OPCODES 1120 J = 4: GOSUB 2100: IF K = 0 THEN 900 1130 CA = VAL ( MID$ (E$,(K + 2) / 3,1)):BY = 2 1140 IF CL$ = "" THEN 1600: REM ZPORABS 1150 IF CL$ = "#" THEN 1200: REM IMMEDIATE MODE 1160 IF CL$ = ",X" THEN 1400: REM ZP,X OR ABS,X 1170 IF CL$ = ",Y" THEN 1500: REM ZP,Y OR ABS,Y 1180 IF CL$ = "(,X)" THEN 1300: REM (ZP,X) 1190 IF CL$ = "(),Y" THEN 1350: REM (ZP),Y 1195 GOTO 900 1200 REM -------------------------------- 1210 REM IMMEDIATE MODE 1220 IF X > 255 THEN 900 1230 IF CA = 0 THEN OP = OP + 8: GOTO 190 1240 IF CA = 4 OR CA = 6 OR CA = 7 THEN 190 1250 GOTO 900 1300 REM -------------------------------- 1310 REM INDIRECT MODE: (...,X) 1320 IF CA < 2 THEN 190 1330 GOTO 900 1350 REM -------------------------------- 1360 REM INDIRECT MODE: (...),Y 1370 IF CA < 2 THEN OP = OP + 16: GOTO 190 1380 GOTO 900 1400 REM -------------------------------- 1410 REM ZP,X OR ABS,X 1420 IF X > 255 THEN 1450: REM ABS,X 1430 IF CA < 3 OR CA = 5 OR CA = 7 OR CA = 9 THEN OP = OP + 20: GOTO 190 1440 GOTO 900 1450 IF CA < 3 OR CA = 5 OR CA = 7 THEN OP = OP + 28:BY = 3: GOTO 190 1460 GOTO 900 1500 REM -------------------------------- 1510 REM ZP,Y OR ABS,Y 1520 IF X > 255 THEN 1540 1530 IF CA = 6 OR CA = 8 THEN OP = OP + 20: GOTO 190 1540 BY = 3: IF CA = 6 THEN OP = OP + 28: GOTO 190 1550 IF CA < 2 THEN OP = OP + 24: GOTO 190 1560 GOTO 900 1600 REM -------------------------------- 1610 REM ZP OR ABS 1620 OP = OP + 4: IF X > 255 THEN OP = OP + 8:BY = 3 1630 GOTO 190 2000 REM -------------------------------- 2010 REM INITIALIZATION 2020 TEXT : HOME : DIM OP$(4),F$(4) 2030 OP$(1) = "ASLBRKCLCCLDCLICLVDEXDEYINXINYLSRNOPPHAPHPPLAPLPROL RORRTIRTSSECSEDSEITAXTAYTSXTXATXSTYA" 2032 OP$(2) = "BCCBCSBEQBMIBNEBPLBVCBVS" 2034 OP$(3) = "JMPJSR" 2035 OP$(4) = "ADCANDCMPEORLDAORASBCSTAASLLSRROLRORBITCPXCPYDECINC LDXLDYSTXSTY" 2040 F$(1) = "0100000242160881842021362322000742340720081040400421 06064096056248120170168186138154152" 2042 F$(2) = "144176240048208016080112" 2044 F$(3) = "076032" 2045 F$(4) = "0970331930651610012251290020660340980322241921942261 62160130128" 2050 E$ = "000000012222344556789" 2060 G$ = "0123456789ABCDEF" 2070 AD = 768: GOTO 1000: REM END OF INITIALIZATION, START AT $030 0 2100 REM -------------------------------- 2110 REM SEARCH OPCODE TABLE J 2120 K = 0: FOR I = 1 TO LEN (OP$(J)) STEP 3: IF L$ = MID$ (OP$( J),I,3) THEN K = I:I = 200:OP = VAL ( MID$ (F$(J),K,3)) 2130 NEXT : RETURN 4000 REM -------------------------------- 4010 REM INPUT A LINE 4020 IN$ = "": CALL 64879:I = 511 4030 I = I + 1: IF I > 550 THEN RETURN 4040 C = PEEK (I) - 128: IF C = 13 THEN RETURN 4050 IF C > 32 THEN IN$ = IN$ + CHR$ (C) 4060 IF C = 3 THEN STOP 4070 GOTO 4030 59000 PR# 1: PRINT CHR$ (27)"B" CHR$ (27)"E" CHR$ (27)"L010": PR# 0 60000 PR# 1: PRINT CHR$ (9)"80N": LIST : PR# 0: CALL 1002: & : END
"That's not a bug, that's a feature!" We've all heard (or said?) that before, but this time it really seems to be true. We have just discovered an undocumented feature in Version 1.1 of the S-C Macro Assembler.
I was trying to see if a program would assemble, and wanted the assembly to be as fast as possible. For some reason I didn't want to do the obvious thing and just switch the listing off, and using a .TA wasn't convenient. So, I stuck a .DU (DUmmy) directive at the beginning of my program, and a .ED (End Dummy)at the end, figuring that would eliminate the time spent writing object code to the disk. When I typed ASM the assembler paused a moment for pass one, then started listing the beginning of the program. But, when the assembler got to the .TF directive the listing stopped and the disk drive started spinning. That wasn't supposed to happen!
When the assembly finished, and the drive stopped turning, I CATALOGed the disk to see what had happened. There was a Binary file, with the filename from my .TF directive, but instead of being much smaller that the source file, it was about twice as big. What could be in that file?
It seemed dangerous to just BLOAD a file that shouldn't exist, so I booted up a disk zap program and inspected the disk. That Binary file contained the text of the assembly listing, starting with the .TF line. Also, the file had no load address and length bytes. The first four bytes were "A0 A0 A0 A0", or the ASCII codes for four spaces. If I had tried to BLOAD the file, it would have loaded at $A0A0, which would have immediately clobbered DOS!
I was preparing a note to warn everybody not to use .TF within a .DU - .ED block, when Bob reminded me of how often we WANT an assembly listing on a Text file, to read into the S-C Word Processor and merge into an article. Why didn't I find out what the Word Processor would make of this file? Well, it read the file just fine, but discarded the first four bytes, since it expects load address and length bytes in a Binary file. In most cases that is no problem, since the first line is the .TF <filename> directive, and will be discarded anyway.
Now we have a Binary file containing the text. That's fine with the S-C Word Processor, but what about other programs, that might require Text files? As it happens, the Macro Assembler creates a Target File as a Text file, then updates the Catalog to turn it into a Binary file. All we have to do is patch the assembler to prevent that change in the file type. That is only a 1-byte patch.
So, all it takes to send the assembly listing to a disk file is to begin the program with a .DU and a .TF, and end it with a .ED. If you want a real Text file, you only have to patch one byte in the assembler.
To make a long story short, here's how to create a Text file containing an assembly listing:
1) At the beginning of your program, put the lines:
0000 .DU 0001 .TF LISTING
and at the end put:
65535 .ED
2) If there is already another .TF directive in your program, insert a "*" at the beginning of the line, to make it into a comment.
3) Enter one of the following patches:
$1000 Versions: $29DF:0 $D000 Versions: $C083 C083 EAF9:0 N C080
4) Type ASM.
5) And restore the patched location:
$1000 Versions: $29DF:4 $D000 Versions: $C083 C083 EAF9:4 N C080
Now you can load LISTING into a word processor, delete the first and last lines, and do whatever you want with it!
I tried creating an EXEC file to do all those steps automatically, but ran into trouble. When the assembly ends the EXEC file loses control, and the Text file LISTING doesn't get closed. When I can solve that one I'll let you know.
In the April 1983 AAL (pages 2-8), Bob Sander-Cederlof presented a small patch that I had sent him almost a year earlier. The patch greatly speeded up LOAD/BLOAD of long files. At the moment, I had recreated a lot of very long assembler source files, such as the source code to DOS and Applesoft. The long assembly times grew annoying, especially when I realized how much time was wasted inside RWTS just waiting for the right sector to pass under the R/W head of the disk drive!
Just one note about what was written on the bottom of page 2 of that issue: my patch does not influence SAVE/BSAVE at all. The read-after-write during a SAVE/BSAVE is made using the VERIFY command, and that command already works at top speed; in fact, VERIFY's speed was a major inspiration for my LOAD/BLOAD patch.
Next I tried to speed up SAVE/BSAVE with an equally simple patch. I found it was not so easy, mainly because SAVE/BSAVE might have to allocate new sectors for the file. I also felt it wasn't worth the trouble writing a more complicated patch, since SAVE/BSAVE isn't really used that often.
Next in line was a speedup of text file read and write. Here I found a great "time-hog" in DOS. The innocent-looking routines at $AE68 and $AE7E each require about 800 cycles to execute. All they do is to swap a 45-byte area back and forth between the file buffers and a local workarea inside the file manager. This is of course necessary when you open/close files or switch from file to file. But if you're reading the same text file, the swapping may not be needed. Nevertheless, file manager swaps the buffer in and out for each and every character you read or write! This amounts to 256*(800+800) = roughly 410,000 cycles or 0.4 seconds for each sector you read or write! This is about six seconds for each track! And all it does during those six seconds is needlessly swap the same 45 bytes back and forth!
The principle of my patch is this: When entering or exiting the file manager, first check to see if you're doing something else besides reading/writing. If so, just go on as usual. If you are reading/writing, check to see if the local workarea belongs to the file being read/written. If so, just exit and save 800 clock cycles. If not, check to see if it belongs to another file. If the workarea contains another file's data, put it back into the file buffer where it belongs and then get the workarea for the current file. All occurs this when you enter the file manager.
Upon exit from the file manager, if you're reading/writing, just set a flag to mark that the local workarea is being used, and save the address of the file buffer it came from. This always saves 800 cycles.
Practical tests show that text file reading/writing is done up to about 40% faster with this patch installed. This is slower than Diversi-DOS, but on the other hand this patch is compatible with S-C assemblers (and almost everything else in sight). Also, this patch works equally well for all file types; it even speeds up the loading of type-R files with RBOOT/RLOAD (from DOS Tool Kit). Diversi-DOS treats T type files in a special way, but does nothing to speed up type-R files. And mine is free!
I put the patch at $300, because there's no free area large enough inside DOS where you can put it...especially if you have already installed the LOAD/BLOAD speedup described by Bob last April. The listing which follows includes code to hook in the patches by overwriting the file manager where it calls the two workarea transfer subroutines.
1000 *SAVE S.FAST TEXT (SCHLYTER) 1010 *-------------------------------- 1020 * SPEEDUP OF DOS TEXT FILE READ/WRITE 1030 * BY PAUL SCHLYTER, SWEDEN, MAY 1983. 1040 *-------------------------------- 1050 PNTR .EQ $42,43 DOS VARIABLE 1060 *-------------------------------- 1070 GET.WRKAREA.FROM.BUFFER .EQ $AE6A 1080 SAVE.WRKAREA.TO.BUFFER .EQ $AE7E 1090 SAVE.OLD.WRKAREA .EQ $AE7E+3 1100 POINT.TO.WRKAREA.BUFFER .EQ $AF08 1110 FM.OPCODE .EQ $B5BB 1120 *-------------------------------- 1130 .OR $300 1140 JMP INSTALL 1150 *-------------------------------- 1160 * PATCH EXECUTED UPON ENTRY TO FILE MANAGER: 1170 * 1. IF READ/WRITE AND CORRECT WORK AREA, RETURN 1180 * 2. IF WRONG WORK AREA, SAVE OLD WORK AREA 1190 * 3. LOAD NEW WORK AREA. 1200 *-------------------------------- 1210 PATCH1 LDA FM.OPCODE IF NOT READ OR WRITE, 1220 CMP #3 DO AS USUAL 1230 BCC .1 ...LESS THAN READ/WRITE 1240 CMP #5 1250 BCS .1 ...HIGHER THAN READ/WRITE 1260 JSR POINT.TO.WRKAREA.BUFFER 1270 LDA PNTR CHECK TO SEE IF CORRECT WORKAREA 1280 CMP PNTR.S ALREAD LOADED 1290 BNE .1 NO 1300 LDA PNTR+1 1310 CMP PNTR.S+1 1320 BNE .1 NO 1330 RTS YES, NOTHING ELSE TO DO, 1340 * SO EXIT NOW AND SAVE 800 CYCLES! 1350 * 1360 * OPCODE NOT READ OR WRITE, OR WRONG WORK AREA. 1370 .1 BIT FLG NEED TO PUT BACK THIS WORK AREA? 1380 BPL .2 NO, JUST GET NEW ONE 1390 CLC YES, CLEAR FLAG 1400 ROR FLG 1410 LDA PNTR.S 1420 STA PNTR 1430 LDA PNTR.S+1 1440 STA PNTR+1 1450 JSR SAVE.OLD.WRKAREA 1460 .2 JMP GET.WRKAREA.FROM.BUFFER 1470 *-------------------------------- 1480 * PATCH EXECUTED WHEN FILE MANAGER IS FINISHED: 1490 * 1. IF READ/WRITE, SET FLAG AND SAVE PNTR 1500 * 2. IF NOT R/W, CLEAR FLAG AND SAVE WORK AREA 1510 *-------------------------------- 1520 PATCH2 LDA FM.OPCODE R/W? 1530 CMP #3 1540 BCC .1 NO 1550 CMP #5 1560 BCS .1 NO 1570 SEC YES, SET FLAG 1580 ROR FLG 1590 JSR POINT.TO.WRKAREA.BUFFER 1600 LDA PNTR AND SAVE POINTER 1610 STA PNTR.S 1620 LDA PNTR+1 1630 STA PNTR.S+1 1640 RTS SAVE ANOTHER 800 CYCLES 1650 .1 CLC CLEAR FLAG 1660 ROR FLG 1670 JMP SAVE.WRKAREA.TO.BUFFER 1680 * 1690 PNTR.S .HS 0000 1700 FLG .HS 00 1710 *-------------------------------- 1720 * TO INSTALL, PATCH DOS LIKE THIS: 1730 * .OR $AB0A 1740 * JSR PATCH1 1750 * 1760 * .OR $B38E 1770 * JSR PATCH2 1780 * 1790 * HERE IS ONE WAY TO DO IT: 1800 *-------------------------------- 1810 INSTALL 1820 LDA #$20 JSR OPCODE 1830 STA $AB0A 1840 STA $B38E 1850 LDA #PATCH1 1860 STA $AB0B 1870 LDA /PATCH1 1880 STA $AB0C 1890 LDA #PATCH2 1900 STA $B38F 1910 LDA /PATCH2 1920 STA $B390 1930 RTS 1940 *-------------------------------- |
Don't tell me it won't fit! It is so good, it MUST fit!
Let's see...there are 74 bytes available from $B6B3 thru $B6FC. But Paul's patches are 93 bytes long. Maybe if I twist it sideways and then hold my mouth just right....
Ha! It worked!
Let me tell you how, but please don't think I am trying to pick Paul apart. His analysis and creative programming are terrific! He has taught me a lot.
First I noticed some common code in PATCH1 and PATCH2. I made a subroutine called CHECK.OPCODE to test for the read or write command. I used the carry status to pass back the answer to the caller. Then I put the call to POINT.TO.WORKAREA (which loads an address into $42 and $43) at the top of the subroutine. There's no need to duplicate it in the two callers. These changes saved two or three bytes, for a tiny penalty in speed.
I noticed Paul used CLC, ROR FLAG to clear the sign bit of FLAG. I save one byte two times by replacing these with LSR FLAG. I set up the carry status info in CHECK.OPCODE so that carry SET means read/write...this lets me omit the SEC before ROR FLAG when I want to turn on the sign bit.
I noticed that both patches used the current contents of PNTR: PATCH1 compared PNTR to PNTR.SAVE, while PATCH2 copied PNTR into PNTR.SAVE. So I loaded up the contents of PNTR into the A- and X-registers inside my CHECK.OPCODE subroutine. This saves a few more bytes.
At lines 1320-1330 in Paul's program he uses BNE to jump around an RTS. I changed that to BEQ to an existing RTS further down in the program, saving one byte.
I moved the PNTR.SAVE variable, two bytes, to another area. $B5CF and $B5D0 are unused, at the end of the file manager parameter list. Conveniently, the subroutines which load addresses into PNTR refer to three such addresses inside the parameter list. (See the code at $AF08-$AF1C.) The X-register is loaded with 0, 2, or 4 to index into the list. By putting PNTR.SAVE at the end of the list, I can load the X-register with 8 (PNTR.SAVE-$B5C7) and use the same subroutine, entering at $AF12. This takes five bytes instead of twelve for LDA-STA- LDA-STA.
The final shortener I applied was to make the code which clears FLAG and copies the workarea to a buffer into a subroutine. This is called PATCH4 in my listing. The two lines at PATCH4 look just like what was in line inside the PATCH1 code, but different from what was done by the PATCH2 code.
PATCH2 falls into PATCH4 if the opcode was not read/write. This used to clear the flag and call $AE7E; now it is $AE81. Since the difference between $AE7E and $AE81 is a JSR to setup PNTR with the workarea address, and since that was already arranged by CHECK.OPCODE, I can safely enter at $AE81.
No doubt if you followed me this far, you can see even more ways to save bytes. In fact, I see one extra byte myself! But the program is now just the right size for that hole at $B6B3, so enough is enough.
My listing includes some code to install the patches. If you assemble my version, and BSAVE it on a binary file (A$300,L$6A), you can BRUN it whenever you want to install the patches. Or, with version 1.1 of the Macro Assembler just add these lines:
1195 .TF B.FAST TEXT 1380 .PH $B6B3 1790 .EP
I also worked out the code for using Applesoft POKEs to patch it all in, and here it is:
100 REM TEXT FILE SPEEDUP PATCH 110 READ N : IF N = 0 THEN END 120 READ A : FOR I = 0 TO N - 1 : READ X : POKE A + I,X : NEXT : GOTO 110 200 DATA 74,46771,32,210,182,144,10,205,207,181, 208,5,236,208,181,240,51,44,252,182,16, 8,162,8,32,18,175,32,246,182,76,106,174, 32,8,175,173 210 DATA 187,181,56,73,3,240,5,73,7,240,1,24,165, 66,166,67,96,32,210,182,144,10,110,252,182, 141,207,181,142,208,181,96,78,252,182,76, 129,174,0 220 DATA 2,43787,179,182 230 DATA 2,45967,231,182 235 DATA 1,41393,20 240 DATA 0
I tested the patches on a 24-sector text file. The file was created by using the TEXT command in the S-C Macro Assembler. I used EXEC to read it back in. I also wrote a short Applesoft program which read the whole file with GET A$ in a loop. Here are the results:
NORMAL PATCHED CHANGE --------------------------- TEXT 24 sec 18 sec 25% faster EXEC 52 sec 34 sec 35% faster GET A$ 30 sec 21 sec 30% faster
I think you get the most benefit if the un-patched DOS has to work so long between calls to RWTS that the disk motor stops, but the patched DOS keeps the motor alive. You save 0.4 seconds per sector anyway, but you can also save waiting for the motor to come up to speed.
Warning: One danger I noted, and which I am wary of, is that FLAG could get out of sync with reality. For example, if somehow FLAG was set with the sign bit on before ever calling the file manager, it could try to copy the workarea to any-old-place in RAM (or ROM, or I/O space). If you install the patches after booting, there should be no problem. But what happens if you initialize a disk with the patched DOS? I think the flag MIGHT turn out wrong. Maybe a little patch is needed to insure FLAG starts out clear, and is cleared after abnormal exits from file manager (such as RESET).
Last-Minute Warning:
DO NOT DO "BRUN B.FAST TEXT" FROM AN EXEC FILE.
IT'S NOT A GOOD IDEA TO USE A TEXT FILE
TO CHANGE THE WAY TEXT FILES WORK!!!
1000 *SAVE S.FAST TEXT (RBS-C) 1010 *-------------------------------- 1020 * PAUL SCHLYTER'S TEXT FILE SPEED-UP 1030 * AS MODIFIED BY BOB SANDER-CEDERLOF 1040 * JUNE 8, 1983 1050 *-------------------------------- 1060 PNTR .EQ $42,43 1070 *-------------------------------- 1080 COPY.BUFFER.TO.WORKAREA .EQ $AE6A 1090 SAVE.WORKAREA .EQ $AE81 1100 POINT.TO.WORKAREA .EQ $AF08 1110 SETUP.PNTR .EQ $AF12 1120 FM.OPCODE .EQ $B5BB 1130 PNTR.SAVE .EQ $B5CF,B5D0 1140 *-------------------------------- 1150 PATCH.AREA .EQ $B6B3 1160 PATCH.LINK1 .EQ $AB0B 1170 PATCH.LINK2 .EQ $B38F 1180 *-------------------------------- 1190 .OR $300 1200 *-------------------------------- 1210 INSTALL.PATCHES 1220 LDX #PATCH.SIZE-1 1230 .1 LDA PATCH.CODE,X 1240 STA PATCH.AREA,X 1250 DEX 1260 BPL .1 1270 LDA #PATCH1 1280 STA PATCH.LINK1 1290 LDA /PATCH1 1300 STA PATCH.LINK1+1 1310 LDA #PATCH2 1320 STA PATCH.LINK2 1330 LDA /PATCH2 1340 STA PATCH.LINK2+1 1342 LDA #20 1344 STA $A1B1 1350 RTS 1360 *-------------------------------- 1370 PATCH.CODE 1380 .OR $B6B3 1390 .TA PATCH.CODE 1400 PATCH1 JSR CHECK.OPCODE 1410 BCC .1 NOT READ/WRITE 1420 CMP PNTR.SAVE 1430 BNE .1 NO 1440 CPX PNTR.SAVE+1 1450 BEQ PATCH3 YES, RETURN NOW 1460 .1 BIT FLAG 1470 BPL .2 1480 LDX #PNTR.SAVE-$B5C7 1490 JSR SETUP.PNTR 1500 JSR PATCH4 CLEAR FLAG, SAVE WORKAREA 1510 .2 JMP COPY.BUFFER.TO.WORKAREA 1520 *-------------------------------- 1530 PATCH2 JSR CHECK.OPCODE 1540 BCC PATCH4 NOT READ OR WRITE 1550 ROR FLAG SET SIGN BIT 1560 STA PNTR.SAVE 1570 STX PNTR.SAVE+1 1580 PATCH3 RTS 1590 *-------------------------------- 1600 PATCH4 LSR FLAG CLEAR SIGN BIT 1610 JMP SAVE.WORKAREA 1620 *-------------------------------- 1630 CHECK.OPCODE 1640 JSR POINT.TO.WORKAREA 1650 LDA FM.OPCODE 1660 SEC 1670 EOR #3 READ? 1680 BEQ .1 YES 1690 EOR #7 WRITE? 1700 BEQ .1 1710 CLC 1720 .1 LDA PNTR 1730 LDX PNTR+1 1740 RTS 1750 *-------------------------------- 1760 FLAG .HS 00 MUST START WITH FLAG=0 1770 *-------------------------------- 1780 PATCH.SIZE .EQ *-PATCH1 |
I am holding a brand new NCR65C02A. Now I finally believe that there is such a creature as a 65C02! NCR's version of this processor seems to be the same as GTE's. That is, it has all of the enhancements described in the December '82 issue of AAL, except for the single bit set, reset and branch instructions.
We have tested the chip in the computers here, and there's good news and bad news. As Don Lancaster reported last month, the new chip works perfectly in an Apple //e. You just swap processors and start using new opcodes. However, 65C02 chips do not work in the Apple ][ or Apple ][+.
I am told that the problem lies in the execution of instructions like ASL or INC, which read memory, modify the contents, and write the result back to the same address. The 6502 processor does one read and two writes during such an instruction, which is really incorrect. In the 65C02 this has been changed to the proper combination of two reads and one write.
Unfortunately, the Apple ][s rely on the timing of the read-write-write cycle, and the read-read-write cycle is just different enough to cause the system to fail. Hopefully some of the hardware specialists can come up with a modification to the older Apples to allow the use of the enhanced processors.
Let's talk about programming the 65C02. With the new chip in a //e, Bob and I started tearing into the S-C Word Processor. We just went through the code, looking for places to substitute a new instruction for several old ones. Come to find out, the most useful change is the true Indirect addressing mode, in place of Indexed Indirect. That means replacing
STY YSAVE LDY #0 LDA (POINTER),Y LDY YSAVE with LDA (POINTER).
That's replacing 8 bytes with 2 bytes. BRA (BRanch Always) and STZ (STore Zero) also came in very handy.
All things considered, Bob has decided to wait for the Rockwell version of the 65C02, because he really wants those single bit set, reset, and branch instructions. At last word Rockwell was expecting to start shipping in August, so it will be at least that long before we have any. NCR's chip costs about $10. The Rockwell chip may cost a little more, if and when. We have noticed ads offering 65C02's for $35, which just goes to show how expensive advertising can be.
Peter Bartlett gave us a nice patch to the Apple Monitor to add ASCII display to the memory dump command. It was published in the Dec 1981 issue of Apple Assembly Line, pages 18-20. You may remember that Peter's patch over-wrote the cassette tape code. Last summer I received two suggested modifications to Peter's code, and at last I pass them on to you.
Bruce Field, from Rockville, Maryland: "I finally got around to building my own EPROM burner the other day, and one of the first things I did was to modify my F8 ROM to include an ASCII listing with the hex dump. I used the routine originally submitted by Peter Bartlett. I found a minor problem with this code.
"The problem is that I have the modified ROM on an Integer BASIC card, and an unmodified ROM on the mother board. If I am in the modified ROM and want to soft-switch back to the mother board, typing 'C081' should do it. But with Peter's patch location C081 is accessed inside the patch itself, so the card switches off with PC pointing inside the cassette tape code!
"My solution is to leave the loading of the memory location in its original position. This makes the patch slightly longer, but it still fits inside the cassette tape space. Also, since I detest flashing characters, I filter these out. I force control characters to inverse mode, and all others to normal video."
1000 *SAVE S.MON ASCII DISPLAY (FIELD) 1010 *--------------------------------- 1020 * PATCHES TO ADD ASCII DUMP TO APPLE MONITOR 1030 * ORIGINAL BY PETER BARTLETT 1040 * MODIFIED BY BRUCE FIELD 1050 *--------------------------------- 1060 A1L .EQ $3C 1070 COUT .EQ $FDED 1080 PRBYTE .EQ $FDDA 1090 *--------------------------------- 1100 .OR $FDBD 1110 .TA $0DBD 1120 JSR PATCH CALL MY PATCH CODE 1130 *--------------------------------- 1140 .OR $FCC9 1150 .TA $0CC9 1160 PATCH PHA SAVE BYTE 1170 LDA A1L LOW BYTE OF DUMP ADDRESS 1180 AND #7 MASK LINE POSITION 1190 CLC 1200 ADC #31 COMPUTE HORIZONTAL OFFSET 1210 TAY 1220 PLA GET BYTE FROM STACK 1230 PHA KEEP COPY ON STACK 1240 ORA #$80 FORCE NORMAL VIDEO 1250 CMP #$A0 MAKE CONTROL-CHARS INVERSE 1260 BCS .1 ...NOT CTRL 1270 AND #$7F ...CTRL 1280 .1 STA ($28),Y STORE IT ON THE SCREEN 1290 LDY #0 RESTORE Y 1300 PLA RECOVER BYTE AGAIN 1310 JMP PRBYTE |
Brooke Boering, from Schaumburg, Illinois: "Here is a slightly modified version of Peter Bartlett's monitor patch. I modify control characters to display as an underline character, and lower case codes to inverse video. Other characters display in normal video."
1000 *SAVE S.MON ASCII DISPLAY (BOERING) 1010 *--------------------------------- 1020 * PATCHES TO ADD ASCII DUMP TO APPLE MONITOR 1030 * ORIGINAL BY PETER BARTLETT 1040 * MODIFIED BY BRUCE FIELD 1050 * MODIFIED AGAIN BY BROOKE BOERING 1060 *--------------------------------- 1070 A1L .EQ $3C 1080 COUT .EQ $FDED 1090 PRBYTE .EQ $FDDA 1100 *--------------------------------- 1110 .OR $FDBD 1120 .TA $0DBD 1130 JSR PATCH CALL MY PATCH CODE 1140 *--------------------------------- 1150 .OR $FCC9 1160 .TA $0CC9 1170 PATCH PHA SAVE BYTE 1180 LDA A1L LOW BYTE OF DUMP ADDRESS 1190 AND #7 MASK LINE POSITION 1200 CLC 1210 ADC #31 COMPUTE HORIZONTAL OFFSET 1220 TAY 1230 PLA GET BYTE FROM STACK 1240 PHA KEEP COPY ON STACK 1250 ORA #$80 FORCE NORMAL VIDEO 1260 CMP #$A0 MAKE CONTROL-CHARS INVERSE 1270 BCS .1 ...NOT CTRL 1280 LDA #$DF MAKE CONTROL INTO UNDERLINE 1290 .1 CMP #$E0 IN LOWER-CASE RANGE? 1300 BCC .2 ...NO, DISPLAY NORMAL VIDEO 1310 AND #$1F ...YES, FORCE INVERSE VIDEO 1320 .2 STA ($28),Y STORE IT ON THE SCREEN 1330 LDY #0 RESTORE Y 1340 PLA RECOVER BYTE AGAIN 1350 JMP PRBYTE 1360 *-------------------------------- |
After assembling Bruce's version above, using the S-C Macro Assembler resident in my language card, I installed the patch by typing:
$C083 C083 (write enable RAM card) $FCC9<CC9.CE3M (move the patch into the cassette space) $FDBE:C9 FC (install patch address in JSR)
And it worked! To install Brooke's code I had to move a few more bytes:
$FCC9<CC9.CE9M
If you have an Apple //e, or a lower case display adapter in an older Apple, you will not want to display lower case characters in inverse mode. Everyone seems to have their own preferences about how to display the 256 possible hex values on Apple's screen. Choose your own favorite!
Last month's headlines bemoaned our burglary, with equipment worth over $11000 missing from our offices. And un-measurable amounts of software. And a damaged Spinwriter. And no insurance. We didn't even have all of the serial numbers recorded. The police indicated we should have no hope of recovering anything.
I know that God, who made the heavens and the earth and all that is in them, is sovereign. I said, "Thank you for this, too. And thank you that we still have enough left to continue business. And that nothing irreplaceable was taken."
And we tried to put the pieces back together. We bought insurance, and recorded all the remaining serial numbers. We made backup copies of critical software to be kept at other locations. We engraved our driver's license numbers on our equipment. We even installed an alarm system.
After about two hours with a screwdriver and needlenose pliers the Spinwriter was back in working condition. Almost as good as new...just one crippled foot where it landed when dropped. NEC makes durable gear. I spent another 8 hours figuring out how to talk to it with a serial interface card (with no documentation), and writing the driver program. Once it all worked, we were able to print the mailing labels for last month's AAL.
The burglary occured sometime after 8:30 pm, Wednesday night, May 25th. The next Wednesday night, after choir practice, we took some time to pray. Among other concerns, we prayed about the burglary. I suggested, "Let's pray that the burglars be caught and the things they took be recovered. It can't hurt to ask!" So we did.
The next day the police received a tip from an informer. They went to investigate, just in time to catch two 18-year-olds carrying computers from apartment to car. One of them was a well-known burglar, with at least six-year record. The equipment matched the description I had given them. Friday morning the investigator called: "We have some of your computers. You can come and pick them up at noon." Although a little dirty, none of the equipment or software was damaged. Two thirds of all that had been stolen was recovered! "A miracle", the police said. "Amen."
The following Monday the police called again. "We have some more." The third computer system, a brand new Apple //e with extended 80-column card, two disk drives, monitor, and Epson printer had been sold to a technically-minded friend (of the burglars) for only $100. Responding to the alternatives offered by our excellent police ("Return the computer, or go to jail"), the friend brought in all he had bought. Almost everything was back in our office!
Thursday, June 16th, I was called a third time. "We have another disk drive." They also had another FlipFile with about 15 more diskettes. Now all that is missing is a TI Programmer calculator and an old Panasonic Cassette Recorder.
Yes, God is sovereign, and also He cares about us as individuals. He allowed our things to be taken, but not everything. He gave us faith to ask for them to be returned. And He caused them to be returned. "Trust in the Lord with all your heart, and do not lean on your own understanding. In all your ways acknowledge Him, and He shall direct your paths." [Proverbs 3-5,6]
I make frequent use of the SHOW command for text files (see AAL July 1982), and I wanted to see it in 80-column glory on my shiny new //e. If you've tried it, you will have noticed that the command places a character in every other column on the 80-column screen, so you still only see 40-columns of data per line!
The reason is that the SHOW command code calls COUT1 at $FDF0 for its character output, and COUT1 knows nothing about 80-column output. By calling COUT ($FDED) instead, the text file output will be routed to whatever your current output device happens to be (including printer, 80-column display, etc.).
If you use the Applesoft on page 27 of that issue to load SHOW, all you need to do is change the ninth item on line 100 from 240 to 237.
Here is the modified POKEr, complete with the additions made by Bil Morgan in the June 83 issue, to save you hunting through all those back issues:
100 DATA 21,42319,32,163,162,169,141,32,237,253,32,142, 174,240,5,32,140,166,208,243,76,252,162 110 DATA 23,44686,173,0,192,16,17,141,16,192,201,141, 240,10,173,0,192,16,251,141,16,192,201,141,96 115 DATA 13,44709,224,0,240,4,162,2,208,2,162,4,76,3,171 116 DATA 3,43773,76,165,174 120 DATA 4,43140,83,72,79,215 130 DATA 2,43273,32,48 140 DATA 0 150 READ N : IF N THEN READ A : FOR I = 1 TO N : READ D : POKE A+I-1,D : NEXT : GO TO 150
[ Tom is author of ProntoDOS, published by Beagle Bros, an excellent speed-enhanced DOS which happens to be compatible with nearly everything. He also writes the monthly DOStalk column in Softalk Magazine. ]
I was behind on my reading when I wrote, in the April Softalk DOStalk, about the changes Apple made to DOS 3.3 in the new //e release. At that time I noticed the routine used to calculate random access file position at $B331 had been modified, but the change looked insignificant to me.
It turns out this change was supposed to fix another bug in the Append command! The change was very well documented by Art Schumer in the August 1982 Call APPLE, page 57.
In pre-//e DOS, Append called on this random-access file position calculator to reset the position-in-file pointer. As you know, Append simply looks through a file byte-by-byte until it finds the end, which can be indicated either by a zero byte or by a lack of additional sectors.
When Append finds a zero byte in the file, it knows it has reached the end, but by then the position-in-file pointer is one byte beyond the zero and has to be backed up. Somebody once thought a call to the random-access file position calculator would be a good way to do this.
But on sequential files (the only kind you can append to) the File Manager uses a record length of one. Thus files longer than 32767 bytes come to this routine with more than 32767 "records", which is beyond what DOS normally allows. The calculation fails.
Schumer's patch gets it to calculate correctly right up to 65535. At that point it stops working for good. Apple tried to get around this in //e-DOS by throwing out Append's reliance on the random-access calculator. Instead they go back in and change the position-in-file pointer directly, then trick the File Manager into re-saving his workarea.
Problem: they only decrement the low byte of the position-in- file pointer. If the file-ending zero comes in the last byte of a sector, the high byte will have been advanced to point at the next sector. Since they don't decrement it, the position- in-file pointer is 256 bytes beyond where it should be. Uh Oh...!
I've been trying to get folks at Apple to recognize the problem, but Append doesn't appear to be one of their priorities. If they don't do something soon I'll publish a patch in Softalk. I'd do it now, but I fear treading where so many have failed before me.
Co-incident with the release of the //e, Apple started shipping a slightly modified version of DOS 3.3. Three changes are evident: the sample programs have been moved to a separate diskette; a few instructions to kill 80-column display during a boot were added; and yet another patch to the APPEND command.
I booted an old DOS 3.3, and then used monitor move to make a copy in memory running from 5D00-7FFF of the DOS image. Then I booted the new DOS, which loaded into 9D00-BFFF. Using the monitor "V" command, I located all of the changes. It was a little tricky skipping over the variables and buffers, but with the aid of a well-worn copy of "Beneath Apple DOS" I managed. Here are all the changes I found:
Old DOS 3.3 New DOS 3.3 ---------------------- ---------------------- A6BB:EA NOP A6BB:20 69 BA JSR $BA69 A6BC:EA NOP A6BD:EA NOP A6BE:A2 00 LDX #0 A6BE:unchanged A6C0:8E C3 B5 STX $B5C3 A6C3:60 RTS
The code above is jumped to from one of the older APPEND patches at $B6A8. It used to be JMP $A6BC, and has been changed to JMP $A6BB to pick up the new JSR there.
The latter part of the file position calculator has been re-written to assure carry is clear before adding record size to previous position.
Old DOS 3.3 New DOS 3.3 ---------------------- ---------------------- B33E:AD BF B5 LDA $B5BF B33E:18 CLC B341:8D EC B5 STA $B5EC B33F:AD BF B5 LDA $B5BF B344:6D E6 B5 ADC $B5E6 B342:8D EC B5 STA $B5EC B347:8D E6 B5 STA $B5E6 B345:6D E6 B5 ADC $B5E6 B34A:AD C0 B5 LDA $B5C0 B348:8D E6 B5 STA $B5E6 B34D:8D ED B5 STA $B5ED B34B:AD C0 B5 LDA $B5C0 B350:6D E4 B5 ADC $B5E4 B34E:8D ED B5 STA $B5ED B353:8D E4 B5 STA $B5E4 B351:6D E4 B5 ADC $B5E4 B356:A9 00 LDA #0 B354:8D E4 B5 STA $B5E4 B358:6D E5 B5 ADC $B5E6 BE57:90 03 BCC $B35C B35B:8D E5 B5 STA $B5E5 BE59:EE E5 B5 INC $B5E5 B35E:60 RTS BE5C:60 RTS BE5D:00 00 filler
Note that there was room for adding the CLC at the top, because of the in-efficiency of the original code.
Code executed at the end of a boot has been modified to clear 80-column mode in case you are booting in an Apple //e.
Old DOS 3.3 New DOS 3.3 ---------------------- ---------------------- BFD6:4C 44 B7 JMP $B744 BFD6:20 76 BA JSR $BA76 BFD9:4C 44 B7 JMP $B744
Three patches were stuffed into the hole at $BA69.
Called from $A6BB: BA69:AE 5F AA LDX $AA5F If last command was BA6C:E0 1C CPX #$1C APPEND, clear flag BA6E:FO 05 BEQ $BA75 Not APPEND BA70:A2 00 LDX #0 Yes, APPEND BA72:8E 5D B6 STX $B65D Clear APPEND flag BA75:60 RTS Called from $BFD6: BA76:A9 FF LDA #$FF BA78:8D FB 04 STA $04FB MODE = $FF BA7B:8D 0C C0 STA $C00C 80-column display OFF BA7E:8D 0E C0 STA $C00E Alternate Char Set OFF BA81:4C 2F FB JMP $FB2F Exit via monitor INIT Called from $B683: BA84:AD BD B5 LDA $B5BD Previous file position BA87:8D E6 B5 STA $B5E6 LSB of fle position BA8A:8D EA B5 STA $B5EA Record # BA8D:BA TSX BA8E:8E 9B B3 STX $B39B Save stack position BA91:4C 7F B3 JMP $B37F Leave File Manager
Note that this last patch jumps into the file manager exit routine even though the file manager had not been entered. The purpose is to save a copy of the file manager workarea in the file buffer after patching the file position low-order byte. Seems to me that jumping directly to $AE7E, without the two lines saving the stack pointer above, would avoid the very dangerous step of jumping into the middle of a subroutine. But in any case, as Tom Weishaar points out, the code is wrong in that it does not recover the higher bytes of the file position. Will APPEND ever really be fixed?
A few months back I published a patch for faster LOAD, etc) in these pages which used the space from $BA69 through $BA95. I suggest you use the older version of DOS 3.3 for the time being. But eventually you may be forced to find another home for the fast LOAD patch.
While using the assembler I felt that it was a pity that the BGE and BLT instructions had not been incorporated especially as it would only have meant an extra 6 bytes of code. This minimal extra overhead is because of the way opcodes are handled in the assembler.
Take for example the BRANCH opcode table, which resides in locations $EF5B-EF93. ($2E29-2E47 for RAM version.) [ These addresses are for version 1.0 ]
This table is preceded by a 2-byte descriptor and ends as one would expect with a 00 as end-of-table marker. The descriptor in this case is 0302, i.e. 3-byte entries having a 2-byte name. The table holds the standard 8 6502-opcodes and the 10 Sweet-16 opcodes, interestingly the B of each instruction name has been dropped, saving 18 bytes.
The entries in the table consist of the last 2 letters of the instruction name followed by the hex code. In the case of 2-letter names the entry consists of the second letter plus a space ($20) followed by its hex code.
I decided that I could dispense with the SW-16 codes BM1 and BNM1 without suffering too much if I wanted to write Sweet-16 code in my programs. However, I found that to incorporate the new codes they would have to be placed between the 6502 codes and the SW-16 codes in the table.
It was just a matter of pushing the code for BR to BNZ up in RAM 6 bytes and slotting in the code for BLT and BGE.
To install the code for the two new opcodes just enter the following at any convenient location e.g.$4000 and BSAVE as BLT/BGE.CODE,A$4000,L$1F
:$4000:47 45 B0 4C 54 90 ("G E B0 L T 90") :$:52 20 01 4E 43 02 43 20 03 50 20 04 :$:4D 20 05 5A 20 06 4E 5A 07 53 20 0C 00To install in LC-Version just enter:
:$C083 C083 write enable card. :BLOAD BLT/BGE.CODE,A$EF75 :$C080 write protect card.
To install in RAM-Version just enter:
:BLOAD BLT/BGE.CODE,A$2E29If you never use Sweet-16 you only need to use the first 6 bytes of the above code. However, this will wipe out the BR and BNC codes in the table.
Now you can use either BCC or its synonym BLT (Branch if Less Than) and BCS or BGE (Branch if Greater than or Equal to) in your programs and have them assembled correctly without using macro definitions.
The load address for the patch file for version 1.1 will vary depending on which of the 8 versions you are patching:
40-col //e Videx STB-80 Motherboard $31A9 318D 3274 329D RAM Card $F2C3 F2A7 F397 F3C0
Bobby Deen's Latest Stuff
Bobby Deen is a name you may remember seeing in these pages in several past issues. He will be entering Texas A & M University this fall. Bobby programmed most of the cross assembler modules for the S-C Macro Assembler, some parts of the S-C Word Processor, about half of the yet-to-be-released 18-digit commercial math package (S-C DP18), and parts of the CPR Training System we did for the American Heart Association. A man of many interests, Bobby also has produced some excellent music disks for the ALF music synthesizers (or any Alf compatibles, such as Applied Engineering), and now a fantastic "Othello" game.
His six-voice renditions of the William Tell Overture by Rossini and Tchaikovsky's Nutcracker Suite are outstanding, and the price is only $10. If you have a synthesizer, you ought to have Bobby's music.
Bobby's Othello program is available now for an introductory price of only $20. Of course he wrote it in assembly language, so it is FAST, and has excellent hi-res graphics. You select among six skill levels; Apple can suggest your next move; you can swap sides with the computer; you can modify the board in mid-game (cheat?); you can pit the computer against itself. Whenever two or more moves tie for the machine's best next move, Apple randomizes its choice. This way you never play the same identical game twice.
Order either of these disks from S-C Software.
Osborne Raises Book Prices
Osborne/McGraw-Hill has raised the prices of most of their books. In particular, all of their books which we carry have new higher prices. They are still bargains when you consider how good they are, and how packed with information:
Title Was Is Now Our Price 6502 Assembly Lang Prog $16.99 $18.95 $18.00 6502 Subroutines $15.95 $17.95 $17.00 Z-80 Assembly Lang Prog $16.99 $18.95 $18.00 Z-80 Subroutines $15.95 $17.95 $17.00
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 $15 per year in the USA, sent Bulk Mail; add $3 for First Class postage in USA, Canada, and Mexico; add $13 postage for other countries. Back issues are available for $1.50 each (other countries add $1 per back issue for postage).
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.)