In This Issue...
Another Way to Assemble into SYS Files
In the August 1987 issue of AAL I told how to patch the ProDOS version of S-C Macro Assembler so that target files (.TF directive) could be type SYS rather than BIN. Unfortunately the patches only work if the file is not already on the disk. In order to avoid an error message you can simply delete the target file before each assembly, and the easiest way to do this is by making an EXEC file. For example, I was working on a large program. SC.ACF is the file with a lot of include (.IN) directives. SCWP.SYSTEM is the target file. With the following three lines of text in an EXEC file named ASM, I can do an assembly by merely typing "-ASM":
DELETE SCWP.SYSTEM LOAD SC.ACF ASM
Unless the assembler is patched, SCWP.SYSTEM will be a type BIN file after assembly. With the following three lines in an EXEC file named SYS, I can change the filetype of SCWP.SYSTEM from BIN to SYS by typing "-SYS":
VERIFY SCWP.SYSTEM $800:A9 07 8D B4 BE A9 FF 8D B8 BE 20 00 BF C3 B4 BE 4C DA FD MGO$800
The VERIFY command reads the file info into a buffer at $BEB4. The code being poked into $800 changes the buffer and does an MLI call to SET FILE INFO. It then prints out the error code returned, which we hope is 00. If not 00, there was an error.
In this, the third of the series delving into the AppleWorks code, we will look into the low-level keyboard input code and a few associated routines. It is probably not extremely exciting, but it is fundamental, and just the right amount of code for this issue of AAL.
This time I decided to give an exact disassembly, rather than rewriting routines to my own liking as I worked through them. Nevertheless, I could not resist showing you my revisions of two subroutines, as you will see below. Until now, I have been working from a "raw" disassembly listing: that is, one produced with the Apple monitor "L" command, and my pencil. After figuring out what I wanted to reproduce, I sat down to the keyboard and typed in an equivalent program. This time I used the S-C DisAssembler to produce a set of source files, and then worked them over with the editing facilities of the S-C Macro Assembler. The result will re-assemble to an exact copy of APLWORKS.SYSTEM (version 1.3). Finally, I lifted out the subroutines I wanted to discuss this month and put them together in one source file. The result is on the following pages.
The S-C DisAssembler made my work a lot easier, but it did not do everything. It does work from a script which can detail which address ranges are code, and which are data. The version I was using also could differentiate between hex strings, ASCII strings, and address tables. Furthermore, SCDA lets me give label names in advance when I know what I want to call them. However, SCDA does not handle all the parameters following JSR XXXX lines which are part of the AppleWorks code. It does handle JSR $BF00, which is the ProDOS MLI call, but not the multitude of AppleWorks calls which I discussed in the December 1987 issue of AAL. SCDA also does not automatically generate local labels within subroutines. All labels not already given names are generated as "I.xxxx", where xxxx is the hexadecimal address. I have gone through the source code and replaced these with either meaningful names or local labels. I did leave one "I." label, because I did not know what to call it, in line 1490. SCDA also generates "X." labels for variables or subroutines outside the range being disassembled, and "Z." labels for page-zero references. I have left some of these in that form, pending more information about what they ought to be called.
If you look at the listing that follows, you may notice some strange comment lines that are filled with hexadecimal numbers. For example, see line 1640:
1640 * (1D35) 1066 1192 137F 193F 19C5 1BFD
These lines were produced by the S-C DisAssembler, and are embedded cross-reference lines. The number in parentheses is the address of the label which follows on the next line, in this case "AW.KEYIN". The list of numbers after the parentheses are addresses of instructions which refer to AW.KEYIN. In this program, they just happen to all be JSR or JMP instructions. The disassembler produces lines like this for every label, and I left in the important ones.
Another thing you will notice in the listing is the heavy use of .PH and .EP directives. Each subroutine is surrounded by a pair of these. I wanted the listing here to show the same addresses as I found inside AppleWorks, and the .PH directive lets me do so. The object code that is stored in RAM during assembly of this source code will not be useful, because it will not be located in the addresses shown; rather, it will be all packed together starting at $0800, the default object code address. But I do not intend to use the code in this form, just display it. If you want to use one or more of these subroutines, include them in your own code WITHOUT the .PH and .EP directives.
In order to assemble these subroutines without the rest of the body of APLWORKS.SYSTEM, I had to define some subroutines not included here. Lines 1450-1470 show these names. DISPLAY.STRING and BASE.CALC.A were included in the January 1988 issue of AAL. I intend to include PRINTER.DRIVER in a future issue.
The main subroutine I want to talk about this issue begins at line 1510, and I have called it AW.KEYIN. As line 1640 shows, this is called from a lot of places; its main purpose is to get the next character from the keyboard. It also does a lot of other stuff, as we will see.
I broke the KEYIN subroutine into four parts. The first part is lines 1640-1720, and is the only entry point used by AppleWorks. The second part (lines 1730-2570) handles the actual character input, getting a character either from the type-ahead buffer or from the keyboard. The third part (lines 2580-3560) checks for certain special characters and acts on them. The last part (lines 3570-3630) stores the character in $84 and returns.
Looking at the first section first, notice lines 1660-1680. For reasons I have not yet determined, a flag is kept in pagezero location $A4 which can cause all keyboard input to be bypassed. You may remember, this same location also controls screen display (see the Jan 88 article). In both cases, if $A4 is non-zero, the operation is bypassed. You will get no display on the screen, and no keystrokes will be waited for. KEYIN will tell the world you typed an ESCAPE key, no matter what you may have really done. I don't know why all this is here yet.
Assuming $A4 is zero, Lines 1690-1720 will save the current cursor position. Apparently the cursor position may be changed in some circumstances within the KEYIN routine, and so we save them for later restoration.
The second part of KEYIN gets the next character. The "next character" may have already been typed a while ago, and tucked away in the type-ahead buffer. If so, KEYIN will get it from there. If not, it will wait until you type one.
You will recall that I talked about the POLL.KEYBOARD subroutine and the type-ahead buffer in the December 1987 issue. Lines 3970-4300 of this month's code show two more subroutines associated with the type-ahead buffer. CLEAR.KEYBUF will empty the type-ahead buffer, and CHECK.KEYBUF will test whether there are any characters in the buffer or not. For some reason there does not appear to be any subroutine to get a character out of the type-ahead buffer; instead, that code is included in-line wherever it is needed. This is not very efficient, but it "is what is". Lines 1760-1850 are one example of this. If it were up to me, I think I would have written one subroutine which returned Carry Clear if there were no characters in the buffer, or Carry Set if there was a least one character. In the latter case, I would also return the next character from the buffer in the A-register. Something like this:
GET.CHAR.FROM.KEYBUF.IF.ANY LDA KEYBUF.OUT CMP KEYBUF.IN BEQ .2 ...buffer is empty TAX Use the "out" index LDA KEYBUF,X Get character from buffer INX Advance buffer index CPX #10 Compare to buffer size BCC .1 ...not off the end yet LDX #0 Wrap-around to beginning .1 STX KEYBUF.OUT New "out" index SEC Indicate we got a character RTS .2 CLC Indicate buffer was empty RTS
If that subroutine existed, I could replace lines 1760-1850 with
1760 JSR GET.CHAR.FROM.KEYBUF.IF.ANY 1770 BCS .11 ...got a char
and eliminate the CHECK.KEYBUF subroutine. Cleaner code, in my estimation, and shorter too.
If there is no character waiting in the type-ahead buffer, lines 1860 and following will get one from the keyboard. This is not as simple as it sounds, because there are a lot of options. First, there are three possible choices for the kind of cursor display during keyboard input. As the comments in lines 1860-1940 say, you may choose a blinking underline cursor, a flashing block cursor, or no cursor at all. If you are an AppleWorks user, you will recognize the first two options as the insert and overwrite cursors. The third, no cursor at all, used when a message like "Press Any Key to Continue" is displayed. The options are selected by values in two variables I have named KEYIN.CURSOR.FLAG and KEYIN.CURSOR.TYPE.
Just in case we are going to display a cursor, lines 1960-2050 retreive the current screen character at the cursor position and save it. If the current cursor position is in AUX RAM, then screen memory is left switched in the AUX position. Line 2540 will later switch it back to MAIN RAM, after a keystroke has been read.
Lines 2070-2080 test whether we have chosen to display a cursor or not. If not, lines 2090-2140 will call on the READ.KEYBOARD subroutine to wait for you to type something. READ.KEYBOARD (lines 3670-3950) is a little smarter than the average keyboard reader. First, it does not wait forever. You call it with a timeout value in the Y-register. The loop which polls the keyboard counts down the timeout value. If it reaches zero, READ.KEYBOARD returns with a FALSE status; if you type a key before timeout, READ.KEYBOARD returns with the character in the A-register and a TRUE status. In addition, notice at line 3750 that READ.KEYBOARD stores whatever character was in the A-register on the screen. This character is determined by your choice of a cursor display. Finally, this subroutine reads the Open- and Solid-Apple keys. If either one of them is depressed, bit 7 of the character value is made 1; if neither is depressed, bit 7 is made 0.
If you do want a cursor display, lines 2150-2170 decide which type you want. Lines 2180-2310 handle the blinking underline, and lines 2320-2460 handle the flashing block. The blinking underline is in reality a repeating series of three characters: an underline, a blank, and the original screen character. The timeout values are chosen so that the underline and blank each are on the screen for about 17% of the time, and the original character the remaining 66%. If you like changing such things, this is the place to do it. You can change the overall speed of the blink, or change the ratio, or change the characters. You might choose one of the mousetext characters here, just for fun, instead of the underline. For example, $5C instead of $DF would display an under- and over-line character.
The flashing block cursor display involves change the character retrieved from the screen to the inverse equivalent character, and alternating between it and the original character. Lines 2330-2420 put up the inverse character for 79% of the time, and lines 2430-2460 put up the normal character for 21% of the time.
Regardless of which of the two displays you choose, when you type a key control will go to ".10" at line 2480. Here the original screen character will be restored.
And, regardless of which of three cursor-types, you eventually wind up at ".11", line 2540. Here we turn screen RAM back to MAIN, and test whether you have opted for further analysis of the input character or not. The variable at $FC4, which I did not give a meaningful name yet, controls that option. If $FC4 is non-zero, the game is all over; if zero, the KEYIN.ANALYSIS section gets to do some work.
I have detailed what KEYIN.ANALYSIS does in the comments in lines 2580-2790. Any normal printing character will simply be passed to you without further action. Any control character will clear the type-ahead buffer and then be passed to you. Any Apple-character will also clear the type-ahead buffer, and if it is not one of the special ones it will be passed to you.
The "special" Apple-characters are what KEYIN.ANALYSIS is here for. Lines 2990-3040 map lower-case letters to the upper-case range. Apple-E is used to change the cursor-type. If you have a blinking underline cursor on the screen, typing Apple-E makes it change to a flashing block cursor. And vice versa. The cursor-toggling code is in lines 3500-3560. Apple-H is used to print the screen contents on your printer. The code to do the screen dump is located right here, in lines 3170-3400. Apple-Q, -S, and -Y are also looked for. If you type and Apple-/, lines 2900-2980 change it to an Apple-? and pass the new code on to you.
By the way, that code for the Apple-/ is sure strange. It tests for the "/" character THREE TIMES! Why? I can think of two reasonable answers. First, maybe there used to be two other keys besides "/" which were changed to "?"; if so, maybe the author simply patched the code the way it is now rather than revising and re-assembling it. Naw.... More likely is that this is a "hook" for catching knockoffs of the code. I have found other hooks, such as deliberate use of a 3-byte reference to a pagezero variable when a 2-byte instruction could have been used. Anyone copying the code to create an illegal copy of AppleWorks for sale under their own name might miss these hooks, and find themselves waking up in court.
The Screen Print code is rather interesting. It calls on a subroutine I call PRINTER.DRIVER for printing each line, but that subroutine is not listed here. There are four different types of calls to PRINTER.DRIVER, controlled by the value in the A-register. If (A) is zero, the printer is initialized. On an Apple //e this means sending out a control-I code, and "80N", which turns off the screen echo many printer interfaces would otherwise perform. On an Apple //c it sends out the controls to set up the baud rate and LF after CR for a serial ImageWriter printer. More on this in a future issue.
Lines 3230-3290 copy a line from the screen to a buffer starting at $900, and then print it on the printer. At lines 3250-3270 PRINTER.DRIVER is called with (A)=79, the line length, which means to print a string whose address follows the JSR instruction. Then at lines 3280-3290 PRINTER.DRIVER is called with (A)=$FE, which means to print a carriage return. Finally, Lines 3340-3350 call PRINTER.DRIVER with (A)=$FF, meaning to "close" the driver.
After the screen print is completed, lines 3370-3390 make sure the cursor is back where it started. However, I did not notice anywhere it ever got moved, so this may be redundant code. The subroutine called here, MOVE.CURSOR.TO.XY, is shown in lines 4320-4480. It uses a round-about method, building a string for last month's DISPLAY.STRING subroutine. The setup for DISPLAY.STRING is also handled in a round-about way, using the code I called POINT.PSTR.AT.0A00, lines 4500-4600. I haven't yet found any reason why this code is not simpler. Why go through the back bedroom to get from the kitchen to the dining room? I think I would have written MOVE.CURSOR.TO.XY like this:
SC.MOVE.CURSOR.TO.XY TXA CLC ADC AW.LEFT STA AW.CH TYA ADC AW.TOP STA AW.CV RTS
The screen print code also calls on the subroutine I show in lines 4620-4940, COPY.SCRN.LINE.TO.0900. This subroutine picks up one line of characters, translating them from display codes to printer codes, and places the result in a buffer at $0900. Another subroutine, MAP.SCRN.CHARS.TO.INTERNAL, does the translation. That MAP... subroutine is never called from anywhere else, so it really should be considered part of the COPY... subroutine. Together they take 72 bytes. I rewrote COPY..., and my simpler, shorter version is shown in lines 5240-5600. Mine only takes 50 bytes. If someone had time to go over the whole program the same way, there might be room for a lot of new features.
1000 *SAVE AW.SUBS.3 1010 *-------------------------------- 1020 AW.CH .EQ $14 1030 AW.CV .EQ $15 1040 AW.BASE .EQ $16,17 1050 PSTR .EQ $80,81 1060 Z.84 .EQ $84 Current Keyin Character 1070 PNTR .EQ $98,99 1080 Z.A4 .EQ $A4 1090 *-------------------------------- 1100 X.0900 .EQ $0900 Used during Screen Print (Apple-H) 1110 X.0901 .EQ $0901 1120 *-------------------------------- 1130 X.0A00 .EQ $0A00 Used for building little strings 1140 X.0A01 .EQ $0A01 1150 X.0A02 .EQ $0A02 1160 *-------------------------------- 1170 X.0EA7 .EQ $0EA7 1180 *-------------------------------- 1190 X.0F3B .EQ $0F3B 1200 X.0F3D .EQ $0F3D 1210 X.0FC4 .EQ $0FC4 1220 *-------------------------------- 1230 .PH $1099 1240 HANDLE.0A00 .DA X.0A00 1250 .EP 1260 1270 *-------------------------------- 1280 .PH $1176 1290 KEYIN.CURSOR.TYPE .HS 01 00=underline, 01=flashing 1300 KEYIN.CURSOR.FLAG .HS 01 00=no cursor, 01=cursor 1310 .BS 2 other variables 1320 KEYBUF .BS 10 type-ahead buffer 1330 KEYBUF.IN .HS 00 1340 KEYBUF.OUT .HS 00 1350 .EP 1360 1370 *-------------------------------- 1380 KEYBOARD .EQ $C000 1390 STROBE .EQ $C010 1400 SCRN.MAIN .EQ $C054 1410 SCRN.AUX .EQ $C055 1420 APPLE.OPEN .EQ $C061 1430 APPLE.SOLID .EQ $C062 1440 *-------------------------------- 1450 DISPLAY.STRING .EQ $14D1 subroutine in AAL Jan 88 1460 BASE.CALC.A .EQ $1717 subroutine in AAL Jan 88 1470 PRINTER.DRIVER .EQ $1C21 subroutine in future AAL 1480 *-------------------------------- 1490 I.1D0E .EQ $1D0E 1500 *-------------------------------- 1510 .PH $1D30 1520 *-------------------------------- 1530 * (1D30) 1D71 1D7B 1D8C 1D9A 1DA6 1DBC 1DCA 1540 KEYIN.CHAR.UNDER.CURSOR .BS 1 1550 * (1D31) 1D6C 1DC7 1F0F 1560 KEYIN.COLUMN.INDEX .BS 1 1570 * (1D32) 1E27 1E2A 1E3C 1E3F 1580 KEYIN.APPLE.H.LINE.NUMBER .BS 1 1590 * (1D33) 1D3E 1E4E 1600 KEYIN.CURSOR.CH .BS 1 1610 * (1D34) 1D43 1E51 1620 KEYIN.CURSOR.CV .BS 1 1630 *-------------------------------- 1640 * (1D35) 1066 1192 137F 193F 19C5 1BFD 1650 AW.KEYIN 1660 LDA Z.A4 If non-zero, exit now as if 1670 BEQ .1 ...you typed <ESC> 1680 JMP KEYIN.EXIT.ESCAPE 1690 .1 LDA AW.CH Save current cursor position 1700 STA KEYIN.CURSOR.CH 1710 LDA AW.CV 1720 STA KEYIN.CURSOR.CV 1730 *-------------------------------- 1740 * (1D46) 1E1A 1E57 1E78 1750 KEYIN.ANOTHER.CHAR 1760 JSR CHECK.KEYBUF Any characters in key-buffer? 1770 BEQ .2 ...no 1780 LDX KEYBUF.OUT ...yes, get char from keybuf 1790 LDA KEYBUF,X 1800 INX Bump keybuf out-index 1810 CPX #10 At end of buffer? 1820 BCC .1 ...no 1830 LDX #$00 ...yes, wrap around to beginning 1840 .1 STX KEYBUF.OUT Save new keybuf out-index 1850 JMP .11 Use the character 1860 *-------------------------------- 1870 * Key-buffer is empty, so we need to get a character 1880 * directly from the keyboard. Therefore, we must: 1890 * 1. Save character now on screen under cursor 1900 * 2. Put up an appropriate cursor 1910 * a. blinking underline 1920 * b. flashing screen char 1930 * c. no cursor at all 1940 * 3. Get a keystroke. 1950 *-------------------------------- 1960 .2 LDA AW.CV Point to the character at the cursor 1970 JSR BASE.CALC.A 1980 LDA AW.CH 1990 LSR 2000 BCS .3 ...Odd column, main RAM 2010 STA SCRN.AUX ...Even column, aux RAM 2020 .3 TAY 2030 STY KEYIN.COLUMN.INDEX 2040 LDA (AW.BASE),Y 2050 STA KEYIN.CHAR.UNDER.CURSOR 2060 *---Select type of cursor-------- 2070 LDX KEYIN.CURSOR.FLAG 2080 BNE .5 ...we do want a cursor display 2090 *---No cursor at all------------- 2100 .4 LDY #$FF Long time-out count 2110 LDA KEYIN.CHAR.UNDER.CURSOR 2120 JSR READ.KEYBOARD 2130 BEQ .4 ...No key yet 2140 BNE .11 ...got a keystroke! 2150 *---Some type of cursor---------- 2160 .5 LDX KEYIN.CURSOR.TYPE 2170 BNE .8 ...Use flashing character 2180 *---Use blinking underline------- 2190 * Repeat loop of underline, char, blank, char 2200 .6 LDA #$DF Underline character 2210 CMP KEYIN.CHAR.UNDER.CURSOR 2220 BNE .7 ...screen not now an underline 2230 LDA #" " ...now underline, change to blank 2240 .7 LDY #28 Short time-out 2250 JSR READ.KEYBOARD 2260 BNE .10 ...got a key! 2270 LDA KEYIN.CHAR.UNDER.CURSOR 2280 LDY #108 Long time-out 2290 JSR READ.KEYBOARD 2300 BNE .10 ...got a key! 2310 BEQ .6 ...always (no keystroke yet) 2320 *---Use flashing character------- 2330 .8 LDA KEYIN.CHAR.UNDER.CURSOR 2340 AND #$7F Change character to inverse 2350 CMP #$40 2360 BCC .9 2370 CMP #$60 2380 BCS .9 2390 AND #$BF 2400 .9 LDY #$6C 2410 JSR READ.KEYBOARD 2420 BNE .10 ...got a key! 2430 LDA KEYIN.CHAR.UNDER.CURSOR 2440 LDY #28 Short time-out 2450 JSR READ.KEYBOARD 2460 BEQ .8 ...no keystroke yet 2470 *---Got a key, restore scrnchar-- 2480 .10 PHA 2490 LDY KEYIN.COLUMN.INDEX 2500 LDA KEYIN.CHAR.UNDER.CURSOR 2510 STA (AW.BASE),Y 2520 PLA 2530 *-------------------------------- 2540 .11 STA SCRN.MAIN Be sure in main RAM 2550 LDX X.0FC4 Should we analyze the char? 2560 BEQ KEYIN.ANALYSIS ...yes 2570 JMP KEYIN.EXIT ...no, just store and return 2580 *-------------------------------- 2590 * Analyze the Character 2600 * 2610 * 1. If character is $20-7F, just store it and return. 2620 * 2. Otherwise, start by clearing the key-buffer. 2630 * 3. If character is $00-1F, it will be fall through all 2640 * other tests and return after being stored. 2650 * 4. If character is $80-FF, an Apple key was down. 2660 * 5. If character is $E1-FA, it is lower-case and will 2670 * be changed to upper-case ($C1-DA). 2680 * 6. If character is not one of the following, just 2690 * store it and return. 2700 * 2710 * Apple-/ Change to Apple-?, and return. 2720 * Apple-H Print the screen, get another char. 2730 * Apple-Q Clobber Z.A4, substitute <ESC>, return. 2740 * Apple-Y Change to Control-Y and return. 2750 * Apple-S If $EA7 non-zero, just store and return. 2760 * Else, clobber Z.A4, substitute <ESC>, 2770 * and return. 2780 * Apple-E Toggle Cursor Mode, get another char. 2790 *-------------------------------- 2800 KEYIN.ANALYSIS 2810 CMP #$20 2820 BCC .1 ...Control Character, analyze it. 2830 CMP #$7F 2840 BCS .1 ...Apple Character, analyze it. 2850 JMP KEYIN.EXIT 2860 *---Analyze the keychar---------- 2870 .1 PHA Save character temporarily... 2880 JSR CLEAR.KEYBUF 2890 PLA ...and get the character back. 2900 *---Check for Apple-Slash-------- 2910 CMP #"/" 2920 BEQ .2 ...Apple-Slash 2930 CMP #"/" <<<I don't know why they do it 3 times>>> 2940 BEQ .2 <<<Maybe just for the fun of it.......>>> 2950 CMP #"/" 2960 BNE .3 ...not Apple-/ 2970 .2 LDA #"?" ...Substitute Apple-? 2980 JMP KEYIN.EXIT 2990 *---Map Lower-Case to Upper------ 3000 .3 CMP #"a" 3010 BCC .4 ...not lower-case letter 3020 CMP #"z"+1 3030 BCS .4 ...not lower-case letter 3040 AND #$DF 3050 *---If Apple-Y, make Ctrl-Y------ 3060 .4 CMP #"Y" Check for Apple-Y 3070 BNE .5 ...no 3080 AND #$1F Changes $D9 to $19, control-Y 3090 *---Check for Apple-H------------ 3100 .5 CMP #"H" Check for Apple-H 3110 BNE .9 ...not Apple-H 3120 LDA I.1D0E ??? 3130 BNE .6 ...ignore the Apple-H 3140 LDA X.0F3B 3150 BNE .7 ...go ahead and print 3160 .6 JMP KEYIN.ANOTHER.CHAR 3170 *---Print the screen------------- 3180 .7 STA X.0F3D 3190 LDA #$00 "OPEN" Printer 3200 JSR PRINTER.DRIVER 3210 LDA #0 For LINE = 0 to 23 3220 STA KEYIN.APPLE.H.LINE.NUMBER 3230 .8 LDA KEYIN.APPLE.H.LINE.NUMBER 3240 JSR COPY.SCRN.LINE.TO.0900 3250 LDA #79 Print 79 characters from 0900 3260 JSR PRINTER.DRIVER 3270 .DA X.0900 3280 LDA #$FE Print CRLF 3290 JSR PRINTER.DRIVER 3300 INC KEYIN.APPLE.H.LINE.NUMBER Next Line 3310 LDA KEYIN.APPLE.H.LINE.NUMBER 3320 CMP #24 Last line yet? 3330 BCC .8 ...no, keep printing 3340 LDA #$FF "CLOSE" Printer 3350 JSR PRINTER.DRIVER 3360 JSR CLEAR.KEYBUF 3370 LDX KEYIN.CURSOR.CH Put cursor back... 3380 LDY KEYIN.CURSOR.CV ...but I don't know how 3390 JSR MOVE.CURSOR.TO.XY it could have moved. 3400 JMP KEYIN.ANOTHER.CHAR Get another character. 3410 *-------------------------------- 3420 .9 CMP #"Q" Check for Apple-Q 3430 BEQ .10 3440 CMP #"S" Check for Apple-S 3450 BNE .11 3460 LDX X.0EA7 3470 BNE .11 3480 .10 STA Z.A4 Apple-Q or -S clobbers Z.A4 3490 JMP KEYIN.EXIT.ESCAPE 3500 *---If Apple-E, change cursor---- 3510 .11 CMP #"E" Check for Apple-E 3520 BNE KEYIN.EXIT 3530 LDA KEYIN.CURSOR.TYPE 3540 EOR #$01 Toggle btwn $00 and $01 3550 STA KEYIN.CURSOR.TYPE 3560 JMP KEYIN.ANOTHER.CHAR 3570 *-------------------------------- 3580 * (1E7B) 1D39 1E69 3590 KEYIN.EXIT.ESCAPE 3600 LDA #$1B Say you typed <ESC> 3610 KEYIN.EXIT 3620 STA Z.84 Store character here too 3630 RTS 3640 *-------------------------------- 3650 .EP 3660 3670 .PH $1F0A 3680 *-------------------------------- 3690 KEYBOARD.TIMEOUT .BS 2 3700 *-------------------------------- 3710 * (1F0C) 1D7E 1D95 1D9F 1DB7 1DC1 3720 READ.KEYBOARD 3730 STY KEYBOARD.TIMEOUT+1 3740 LDY KEYIN.COLUMN.INDEX 3750 STA (AW.BASE),Y 3760 .1 LDA KEYBOARD 3770 BMI .2 ...got a keystroke 3780 LDA KEYIN.CURSOR.FLAG 3790 BEQ .1 If no cursor, then no time-out either 3800 DEC KEYBOARD.TIMEOUT 3810 BNE .1 ...more time left 3820 DEC KEYBOARD.TIMEOUT+1 3830 BNE .1 ...more time left 3840 LDX #$00 timed out, return false 3850 BEQ .4 ...always 3860 .2 STA STROBE Clear the strobe 3870 LDX APPLE.OPEN 3880 BMI .3 Apple, leave bit 7 = 1 3890 LDX APPLE.SOLID 3900 BMI .3 Apple, leave bit 7 = 1 3910 AND #$7F No Apple, make bit 7 = 0 3920 .3 LDX #$01 Return TRUE 3930 .4 RTS 3940 *-------------------------------- 3950 .EP 3960 3970 .PH $1FE0 3980 *-------------------------------- 3990 * (1FE0) 1084 181D 1939 1BF1 1DE7 1E4B 4000 * Clear type-ahead buffer 4010 *-------------------------------- 4020 CLEAR.KEYBUF 4030 LDA #$00 4040 STA KEYBUF.IN 4050 STA KEYBUF.OUT 4060 RTS 4070 *-------------------------------- 4080 .EP 4090 4100 .PH $13B2 4110 *-------------------------------- 4120 * (13B2) 137A 1D46 4130 * Check whether any characters are queued up in the 4140 * keyboard buffer. If so, return TRUE (status .NE.). 4150 * If not, return FALSE (status .EQ.). 4160 *-------------------------------- 4170 CHECK.KEYBUF 4180 LDA KEYBUF.IN If pointers are same, the buffer 4190 CMP KEYBUF.OUT is empty. 4200 BEQ .1 ...it is empty 4210 LDA #$01 ...not empty, return TRUE (.NE.) 4220 BNE .2 4230 .1 LDA #$00 4240 .2 RTS 4250 *---Alternate code to do same---- 4260 *** LDA KEYBUF.IN If pointers are same, the buffer 4270 *** CMP KEYBUF.OUT is empty. 4280 *** RTS .NE. if not empty, .EQ. if empty 4290 *-------------------------------- 4300 .EP 4310 4320 .PH $1823 4330 *-------------------------------- 4340 * (1823) 1024 13A2 1A58 1A89 1B1B 1E54 1E86 1E90 20B6 2B66 2B82 4350 * Move cursor to column (X), line (Y) 4360 * Works by building a string for STRING.DISPLAY 4370 *-------------------------------- 4380 MOVE.CURSOR.TO.XY 4390 STX X.0A01 Build string "05.XX.YY" 4400 STY X.0A02 4410 LDA #$05 "GoToXY" code 4420 STA X.0A00 4430 JSR POINT.PSTR.AT.0A00 4440 LDA #3 String has 3 characters 4450 JSR DISPLAY.STRING 4460 RTS 4470 *-------------------------------- 4480 .EP 4490 4500 .PH $1EA9 4510 *-------------------------------- 4520 * (1EA9) 182E 1F85 1FEC 208B 20CC 4530 POINT.PSTR.AT.0A00 4540 LDA HANDLE.0A00 4550 STA PSTR 4560 LDA HANDLE.0A00+1 4570 STA PSTR+1 4580 RTS 4590 *-------------------------------- 4600 .EP 4610 4620 .PH $187A 4630 *-------------------------------- 4640 * (187A) 1036 1A81 1E2D 4650 * Used by Apple-H Screen Print function 4660 * (A) = line to be copied 4670 * Copies 80 characters to buffer at $0900 4680 *-------------------------------- 4690 COPY.SCRN.LINE.TO.0900 4700 JSR BASE.CALC.A 4710 LDX #0 4720 LDY #0 4730 .1 LDA (AW.BASE),Y 4740 BMI .2 80-FF 4750 JSR MAP.SCRN.CHARS.TO.INTERNAL 4760 JMP .3 4770 .2 AND #$7F 4780 .3 STA X.0901,X 4790 STA SCRN.AUX 4800 LDA (AW.BASE),Y 4810 BMI .4 4820 JSR MAP.SCRN.CHARS.TO.INTERNAL 4830 JMP .5 4840 .4 AND #$7F 4850 .5 STA X.0900,X 4860 STA SCRN.MAIN 4870 INX 4880 INX 4890 INY 4900 CPY #40 4910 BCC .1 ...more on this line 4920 RTS 4930 *-------------------------------- 4940 .EP 4950 4960 .PH $1E94 4970 *-------------------------------- 4980 * (1E94) 1885 1897 4990 * 5000 * Only called from subroutine which copies 5010 * a screen line to $0900, and only for 5020 * character values $00-7F. 5030 * 5040 * 00-1F to 40-5F 5050 * 20-3F no change 5060 * 40-5F to 80-9F (Mouse Graphics) 5070 * 60-7F no change 5080 *-------------------------------- 5090 MAP.SCRN.CHARS.TO.INTERNAL 5100 CMP #$20 5110 BCS .1 not 00-1F 5120 ORA #$40 Change 00-1F to 40-5F 5130 BNE .2 ...always 5140 .1 CMP #$40 5150 BCC .2 ...20-3F 5160 CMP #$60 5170 BCS .2 ...60-7F 5180 AND #$BF Change 40-5F to 00-1F 5190 ORA #$80 and then to 80-9F 5200 .2 RTS 5210 *-------------------------------- 5220 .EP 5230 5240 *-------------------------------- 5250 * A Shorter Version of COPY.SCRN.LINE.TO.0900 5260 *-------------------------------- 5270 SC.COPY.SCRN.LINE.TO.0900 5280 JSR BASE.CALC.A 5290 LDX #0 5300 LDY #0 5310 .1 STA SCRN.AUX Even char first 5320 JSR GET.MAP.PUT.SCRN.CHAR 5330 STA SCRN.MAIN Then odd char 5340 JSR GET.MAP.PUT.SCRN.CHAR 5350 INY 5360 CPY #40 5370 BCC .1 ...more on this line 5380 RTS 5390 *-------------------------------- 5400 * 00-1F to 40-5F 5410 * 20-3F no change 5420 * 40-5F to 80-9F (Mouse Graphics) 5430 * 60-7F no change 5440 * 80-FF to 00-7F 5450 *-------------------------------- 5460 GET.MAP.PUT.SCRN.CHAR 5470 LDA (AW.BASE),Y 5480 BMI .1 Change 80-FF to 00-7F 5490 CMP #$60 now have 00-7F 5500 BCS .2 ...60-7F no change 5510 ADC #$40 Change 00-5F to 40-9F 5520 BMI .2 ...40-5F became 80-9F 5530 CMP #$60 5540 BCC .2 ...00-1F became 40-5F 5550 AND #$3F Change 60-7F back to 20-3F 5560 .1 AND #$7F Change 80-FF to 00-7F 5570 .2 INX 5580 STA X.0900,X 5590 RTS 5600 *-------------------------------- |
The following program will print the ratio M/N as a percentage, assuming M and N are 24-bit integers. The percentage will be rounded to the nearest integer value, and print as one, two, or three digits followed by the "%" symbol. If M is more than 999% of N, the value printed will be garbled.
The straightforward way to compute this percentage would be to compute P = 100M/N, convert the quotient to decimal, and print it. To accomplish rounding, you could check the remainder after the division: if twice the remainder is greater than or equal to N, increment the quotient.
After a little head-scratching, I discovered another method. I accomplish the division and the conversion to decimal at the same time, and never multiply by 100. For rounding I cheated a little, and added N/256 to M before dividing.
N/256 is a breeze to compute, because it mearely means offseting the addition loop by one byte. However, the CORRECT rounding term would be N/200. The difference between N/256 and N/200 is 56N/51200, or about .0011N. This is "negligible", at least to me. It in effect means that I am rounding up percentages with fractional parts above .4989, instead of just those with fractions .5 or higher. Who will ever notice? Since my main use for this routine is to print what fraction of my hard disk is in use, it will be plenty close enough. (In fact, maybe I don't even need to round at all!)
The division/conversion loop works by using partial division. Each time I call the division loop, I divide M by N; however, I only loop enough times to get the next decimal digit of the quotient. Then I multiply the remainder by ten, so that the next division will generate the next digit. Trust me, it really works!
I added the digit printout to the tail end of the same subroutine which generates the next digit, and put in some logic to suppress leading zeroes. It looks funny if 10% prints out as 010%, so I ignore that leading zero. On the other hand, the logic makes sure 0% does not print as just "%".
To use the subroutine, you first have to store the 24-bit value for N. I wrote a subroutine for this, but you don't necessarily have to use it. If you do use it, load the most significant byte in the A-register, the middle byte in X, and the low byte in Y. I call this loading a 24-bit value into AXY. Then do a JSR STORE.N. In contrast to the usual way you see multi-byte values stored in most 6502 code, I store M and N in High-to-Low order. This made the various loops inside the GET.DIGIT subroutine shorter. Lines 1100-1160 are the STORE.N subroutine. Nothing fancy here!
After storing the N-value, load up the M-value in AXY and do a JSR PERCENT.CALC. The percentage that M is of N will be printed, and the subroutine will return. I wrote a demonstration program, shown in lines 2000-2260. This program sets N=255 and then prints out all percentages for M = 0 to 255. You can vary the value of N and see different effects.
When you call PERCENT.CALC, lines 1180-1200 store your M-value. Lines 1210-1230 initialize the leading zero flag so that those zeroes will be suppressed. Lines 1240-1350 accomplish the pseudo-rounding I described above. I append another byte to the M-value, which is in effect after the radix point. [Radix point? What's a radix point? In decimal numbers, we call it a decimal point. In binary numbers, we could call it a binary point. In general, it is the demarcation between the integral and fractional parts of a number.] Finally, lines 1370-1410 generate and print three digits of the answer followed by the %-sign.
GET.DIGIT, lines 1430-1800, does all the real work. Lines 1440-1530 subtract N from M until M goes negative. The Y-register counts how many times this takes, less one. Unless M was greater than or equal to 10N, the number in Y will be a value from 0-9, and will be the first or next digit of the percentage. But before printing that digit, I need to modify the remainder.
Lines 1540-1610 add N back one time, so that the remainder is positive. We subtracted once too often, so this fixes things. Then lines 1620-1710 multiply the remainder by 10. Next time GET.DIGIT is called, we will generate the next digit of the percentage.
Lines 1720-1790 print the digit, unless it is a leading zero. If the digit is not zero, it is obviously not a leading zero, so it is printed. Any time a digit gets printed, I store a negative value in my leading zero flag, indicating that any future zeroes cannot be called "leading" (see line 1780). The leading zero flag started out at 2, and each time I test it gets decremented in line 1750. If the first two digits are both zero, it will go negative forcing the third digit to print even it is also zero.
If you are interested, it is very simple to modify this program so that is prints out a rounded percentage in the format xxx.x%, to the nearest tenth of a percent. All we have to do is change the rounding term from N/256 to N/2048, which affects the code in lines 1240-1350, and then add the follow lines:
1392 LDA #"." Print a decimal point 1394 JSR MON.COUT 1396 JSR GET.DIGIT Print the tenths digit
Other simple modifications could change the variable size from 24-bits to 16-bits, 32-bits, or whatever you need.
1000 *SAVE S.PERCENT.CALC 1010 *-------------------------------- 1020 * Subroutine for printing M/N as a percentage 1030 * where M and N are 24-bit integers. 1040 * 1. With (AXY)=N, do JSR STORE.N 1050 * 2. With (AXY)=M, do JSR PERCENT.CALC 1060 *-------------------------------- 1070 MON.CROUT .EQ $FD8E 1080 MON.PRBYTE .EQ $FDDA 1090 MON.COUT .EQ $FDED 1100 *-------------------------------- 1110 STORE.N 1120 STA N MOST SIGNIFICANT 1130 STX N+1 1140 STY N+2 LEAST " 1150 RTS 1160 *-------------------------------- 1170 PERCENT.CALC 1180 STA M MOST SIGNIFICANT 1190 STX M+1 1200 STY M+2 LEAST " 1210 *---Init Leading Zero Flag------- 1220 LDA #2 Start with LZ-flag = 2 1230 STA Z 1240 *---Add N/256 to Round Result---- 1250 LDA N+2 Accuracy would demand adding 1260 STA M+3 N/200, but N/256 is close enough. 1270 CLC N N 56N 1280 LDX #1 --- = --- + ------- 1290 .1 LDA M+1,X 200 256 200*256 1300 ADC N,X 1310 STA M+1,X And 56/51200 = .0010937 (very small) 1320 DEX 1330 BPL .1 1340 BCC .2 1350 INC M 1360 *---Compute & Print Digits------- 1370 .2 JSR GET.DIGIT Hundreds digit 1380 JSR GET.DIGIT Tens digit 1390 JSR GET.DIGIT Units digit 1400 LDA #"%" 1410 JMP MON.COUT 1420 *-------------------------------- 1430 GET.DIGIT 1440 LDY #-1 Y will be the quotient 1450 SEC 1460 .1 INY Increment Quotient 1470 LDX #2 1480 .2 LDA M,X Subtract Denominator 1490 SBC N,X 1500 STA M,X 1510 DEX 1520 BPL .2 1530 BCS .1 This goes around once to often... 1540 *---Add N back in once----------- 1550 LDX #2 1560 .3 LDA M,X So we need to add it back once. 1570 ADC N,X 1580 STA M,X 1590 STA T,X Save copy of M in T, to make it 1600 DEX easier to multiply by 10. 1610 BPL .3 1620 *---Multiply M by 10------------- 1630 JSR M.TIMES.2 M = 2 (4M + T ) 1640 JSR M.TIMES.2 ...now we have 4M 1650 LDX #3 ...add T (copy of original M) 1660 .4 LDA M,X 1670 ADC T,X 1680 STA M,X 1690 DEX 1700 BPL .4 1710 JSR M.TIMES.2 5M times 2 is 10M 1720 *---Print digit if not leading zero--- 1730 TYA digit is quotient from above 1740 BNE .5 ...digit not zero, so print it 1750 DEC Z Is it a leading zero? 1760 BPL .6 ...yes, don't print it. 1770 .5 ORA #"0" Make it ASCII 1780 STA Z Kill LZ-flag by setting bit 7 = 1 1790 JSR MON.COUT Print the digit 1800 .6 RTS 1810 *-------------------------------- 1820 M.TIMES.2 1830 LDX #3 Double 4-byte value in M 1840 CLC 1850 .1 ROL M,X 1860 DEX 1870 BPL .1 1880 RTS 1890 *-------------------------------- 1900 M .BS 4 1910 N .BS 3 1920 T .BS 4 1930 Z .BS 1 LEADING ZERO FLAG 1940 *-------------------------------- 1950 * Test Routine for PERCENT.CALC 1960 * For M = 0 to 255 1970 * Print M,PERCENT(M/255) 1980 * Next M 1990 *-------------------------------- 2000 TT 2010 LDA #0 2020 STA TM Start TM=0 2030 TAX 2040 LDY #255 Set N = 255 2050 JSR STORE.N 2060 .1 LDY TM Set M to current TM 2070 TYA 2080 AND #7 If TM Mod 8 = 0, start new line 2090 BNE .2 ...same line 2100 JSR MON.CROUT 2110 .2 TYA Get M again 2120 JSR MON.PRBYTE Print M-value too 2130 LDA #" " followed by one blank 2140 JSR MON.COUT 2150 LDA #0 2160 TAX Leading bytes of M = 00 00 2170 JSR PERCENT.CALC print M/N percent 2180 LDA #" " 2190 JSR MON.COUT followed by two blanks 2200 JSR MON.COUT 2210 INC TM Next TM 2220 BNE .1 ...until wraps around to 00 2230 RTS Finished 2240 *-------------------------------- 2250 TM .BS 1 2260 *-------------------------------- 2270 .LIF |
I have written a number of articles in the past about converting values from binary to decimal and printing or displaying them. Usually such routines need to handle large numbers, but sometimes they are limited to single-byte values. And once in a while, you know in advance the value will be between 0 and 99 decimal.
For example, when you are printing the date you know in advance that the day, month, and year numbers are only two digits each. The following program is actually used in one such date printer, to print the day number and year number. For simplicity, it always prints two digits, even if the first digit is a zero. This is the way I want it to be when printing the year, but the day would probably look better without a leading zero.
Lines 1000-1140 in the listing which follows are a test routine which call on my PD subroutine to print every possible value from 00 to 99. Lines 1150 to the end are the PD subroutine. Lines 1240-1290 are not actually assembled, because the variable "blank.fill" is zero. If you change line 1010 to make "blank.fill" equal to 1, lines 1240-1290 will be assembled. Then values less than 10 will print with a leading blank rather than a leading zero.
1000 *SAVE Q2D.DECIMAL 1001 .list con 1010 blank.fill .eq 0 1020 *-------------------------------- 1030 COUT .EQ $FDED 1040 T 1050 LDY #0 For Y = 0 to 99 1060 .1 TYA A = Y 1070 JSR PD Print two digits decimal 1080 LDA #" " Print two spaces 1090 JSR COUT 1100 JSR COUT 1110 INY Next Y 1120 CPY #100 1130 BCC .1 1140 RTS Finished! 1150 *-------------------------------- 1160 PD LDX #"0"-1 Start with ASCII zero-1 1170 SEC Set up subtraction 1180 .1 INX Increment ten's digit 1190 SBC #10 Take out ten 1200 BCS .1 Still more tens 1210 ADC #"0"+10 Add back one ten, and make ASCII 1220 PHA Save unit's digit 1230 TXA Get ten's digit 1240 .do blank.fill 1250 cmp #"0" If these are assembled, print 1260 bne .2 00-09 as " 0" through " 9" 1270 lda #" " 1280 .2 1290 .fin 1300 JSR COUT Print ten's digit 1310 PLA Get unit's digit 1320 JMP COUT and print it 1330 *-------------------------------- |
A long time ago I wished there were some sort of word processor for the Apple II. Paul Lutus wrote AppleWriter, and I bought a copy. It was limited to a 40-column display, and only showed upper-case on the screen, due to limitations in the old Apple II and II Plus machines. It cost $50, and that seemed like a pretty good price.
After a while I found out about the Paymar Lower-Case Adapter, and added lower-case display to the screen. Some patches inside AppleWriter made it work with the adapter. Then the shift-key mod was invented, and I installed that also. I started hoping for even more....
So, I wrote my own word processor, based on AppleWriter's features, and using some of the internal techniques Paul Lutus developed which made AppleWriter faster than any other word processors available. I simplified the editing commands, speeded up and enhanced a lot of the features, and significantly shortened the code. I gave it the ability to use standard text files, with blazingly fast disk load and save. It gradually grew into a product, which we sold with all the source code for $50. More than a pretty good price.
However, the S-C Word Processor was still limited to a 40-column display. Bob Deen, a high school student at the time, was doing some programming work for us. He did a lot of work on our Cross Assemblers, for example. He was also using the S-C Word Processor a lot, so I asked him to make an 80-column version for the Apple //e. He succeeded, and also added "widow" and "orphan" protection. (See the article in AAL, July, 1984.) By the way, Bob Deen is now a computer scientist at Jet Propulsion Laboratories in Pasadena, California, doing image enhancement software.
Meanwhile, Apple made a widow out of DOS 3.3 by bringing out the ProDOS system. The S-C Word Processor was still tied to DOS, partly because of my fancy disk I/O and the catalog-menu system. Then last December Bob Gardner sent us a ProDOS version! (Bob, who lives in Washington state, has been a loyal customer and friend since at least 1983.) He did a terrific job, making the ProDOS version even better than the DOS one. The only drawback was that it only worked in 40-columns. Well, in March he sent an 80-column version.
Now, still for only $50, you get both DOS and ProDOS versions which work in both 40- and 80-columns. To use the 80-column versions you have to have an Apple //e, //c, or IIgs. The 40-column versions will work on those or older Apples, but the older Apples do need lower-case display and shift-key mods.
I wouldn't want you to think the S-C Word Processor has all the features of the Word Perfect, or other such major products. No, it is not that sophisticated. But it does have all the basic features we need for everyday work, it is very fast, and you get all the source code so you can personalize it.
Some of you have done some extensive personalizing already. Horst Schneider, a retired businessman in Denver, modified it to become a part of his business management package, adding mail merge and other features along the way. Larry Skutchan, who works at American Printing House for the Blind, made a talking version which works with Street Electronics' "Echo" speech synthesizers. If you are interested in either of these, I could put you in touch with Horst or Larry.
A curious bit of history: all three of the programmers who have made the major contributions to SCWP are named Bob! I guess we could give it the nickname of the Three-Bob Word Processor! No, it is inexpensive, but we do charge more than three shillings.
If you already have an earlier version of the S-C Word Processor and would like to get an update to the latest, send $7.50 (or only $5 if you only want the ProDOS disk).
ProDOS-8 stores the date and time information from in four bytes in the Global Page starting at $BF90:
$BF90: MMMDDDDD Low-order bits of Month, Day (1-31) $BF91: YYYYYYYM Year (0-99), high bit of Month $BF92: 00mmmmmm Minute (0-59) $BF93: 00hhhhhh Hour (0-23)
The following subroutine, lines 1000-1590, will print out the date in the form DD-MMM-YY. Lines 1600-1800 are an alternative method for printing out the 3-letter month name abbreviation. Lines 1810-1910 print the time in the form hh:mm.
The value stored in the Global Page may not be current. It is automatically updated every time you close or flush a file, or you can force it to be updated by using the MLI call shown in lines 1920-end. If you have looked into the Global Page description in the manuals, you may have noticed that $BF06 is a vector to the date/time update code. Don't try to use it directly unless you are sure the Language Card is properly switched before and after the call. The best way is to use the MLI call, as I did.
1000 *SAVE PRINT.DATE 1010 .LIST MOFF 1020 *-------------------------------- 1030 * Subroutine to print date from ProDOS Global Page 1040 * in form DD-MMM-YY. 1050 * Two different methods for printing the 3-letter month 1060 * name are shown, with month-name table in normal and 1070 * transposed order. 1080 *-------------------------------- 1090 DATE .EQ $BF90,BF91 Date in form: MMMDDDDD, YYYYYYYM 1100 * Time in form: 00mmmmmm, 000hhhhh 1110 COUT .EQ $FDED 1120 *-------------------------------- 1130 PRINT.DATE 1140 LDA DATE Get MMMDDDDD 1150 AND #$1F Isolate Day of Month 1160 JSR PD Print the day number 1170 LDA #"- Print a dash 1180 JSR COUT 1190 *----PRINT MONTH FROM TABLE------ 1200 LDA DATE+1 Get YYYYYYYM 1210 LSR High bit of Month-number into Carry 1220 PHA Save 0YYYYYYY on stack 1230 LDA DATE Get MMMDDDDD 1240 ROR MMMMDDDD 1250 LSR 0MMMMDDD 1260 LSR 00MMMMDD 1270 LSR 000MMMMD 1280 LSR 0000MMMM Month number (1-12) 1290 TAX 1300 LDA MONTH.TBL.1-1,X 1st letter 1310 JSR COUT 1320 LDA MONTH.TBL.2-1,X 2nd letter 1330 JSR COUT 1340 LDA MONTH.TBL.3-1,X 3rd letter 1350 JSR COUT 1360 LDA #"- Print dash 1370 JSR COUT 1380 *----PRINT YEAR------------------ 1390 PLA GET 0YYYYYYY FROM STACK 1400 *---Fall into PD subroutine------ 1410 PD LDX #"0"-1 Start with ASCII zero-1 1420 SEC Set up subtraction 1430 .1 INX Increment ten's digit 1440 SBC #10 Take out ten 1450 BCS .1 Still more tens 1460 ADC #"0"+10 Add back one ten, and make ASCII 1470 PHA Save unit's digit 1480 TXA Get ten's digit 1490 JSR COUT Print ten's digit 1500 PLA Get unit's digit 1510 JMP COUT and print it 1520 *-------------------------------- 1530 .MA AS 1540 .AS -/]1/ 1550 .EM 1560 *-------------------------------- 1570 MONTH.TBL.1 >AS "JFMAMJJASOND" 1580 MONTH.TBL.2 >AS "AEAPAUUUECOE" 1590 MONTH.TBL.3 >AS "NBRRYNLGPTVC" 1600 *-------------------------------- 1610 ALTERNATIVE.MONTH.PRINTER 1620 LDA DATE+1 GET YYYYYYYM 1630 LSR M INTO CARRY 1640 LDA DATE GET MMMDDDDD 1650 ROR MMMMDDDD 1660 LSR 0MMMMDDD 1670 LSR 00MMMMDD 1680 LSR 000MMMMD 1690 LSR 0000MMMM 1700 STA TEMP Multiply month number by 3 1710 ASL 1720 ADC TEMP 1730 TAX Index is 3,6,9,... 1740 LDY #3 Print 3 consectutive letters 1750 .1 LDA MONTH.TABLE-3,X 1760 JSR COUT 1770 INX Next letter 1780 DEY 1790 BNE .1 1800 RTS Finished 1810 *-------------------------------- 1820 TEMP .BS 1 1830 MONTH.TABLE >AS "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC" 1840 *-------------------------------- 1850 PRINT.TIME 1860 LDA DATE+3 Get 00hhhhhh 1870 JSR PD 1880 LDA #":" 1890 JSR COUT 1900 LDA DATE+2 Get 00mmmmmm 1910 JMP PD 1920 *-------------------------------- 1930 UPDATE.DATE.AND.TIME 1940 JSR $BF00 MLI ENTRY POINT 1950 .DA #$82,0000 GET DATE/TIME, NO PARMS 1960 RTS 1970 *-------------------------------- 1980 .LIST OFF |
Apple Assembly Line (ISSN 0889-4302) is published monthly by S-C SOFTWARE CORPORATION,
P. O. Box 280300, Dallas, TX 75228 Phone (214) 324-2050.
Subscription rate is $24 per year in the USA, Canada, and Mexico, or $36 in other countries.
Back issues are $1.80 each for Volumes 1-7, $2.40 each for Volume 8
(plus postage). A subscription tothe newsletter with a Monthly
Disk containing all program source code and article text is $64 per year in
the USA, Canada and Mexico, and $90 to other countries.
All material herein is copyrighted by S-C SOFTWARE, all rights reserved.
Unless otherwise indicated, all material herein is authored by Bob Sander-Cederlof.
(Apple is a registered trademark of Apple Computer, Inc.)