COMPUTE! #30 / NOVEMBER 1982 / PAGE 209
A letter was published recently in COMPUTE!’s “Ask The Readers” column, regretting the need for “this POKE or that POKE” to accomplish various tasks. The required solution is an “expanded command set.” An enticing prospect, adding commands to a language, and a seemingly impossible one, too.
Atari BASIC, like most microcomputer BASICS, is “burned” into nonvolatile ROM memory. The machine language routines to list, save, edit, and run your program cannot be altered or “patched” in any way. (However, on a 48K Atari, you can copy the BASIC cartridge to disk as a binary file, modify it with a “machine language monitor,” and load it into the top of memory where it will act almost as a ROM cartridge.)
The most common (and easiest to implement) extension of a language is the addition of “immediate mode” commands. These direct commands, which are not usually executed in a program, but from the keyboard, include RUN, SAVE, LIST, NEW, DOS, etc. Thanks to Atari’s modular Operating System (OS), we can easily add this type of command.
To understand how the Atari Wedge works, we’ll have to delve into the mysterious 10K ROM. If you just want to use the program and aren’t concerned about the technical details, feel free to skip ahead. The Operating System (OS) of a computer is responsible for all input and output to and from disk, cassette, printer, and keyboard. It can also perform such chores as memory management and screen display. On many microcomputers, the OS does not exist as a separate entity, but is incorporated into the BASIC interpreter.
The Atari, on the other hand, is the first microcomputer with a general-purpose “plug-in” operating system. This goes hand in hand with the use of program and game cartridges. All programs running on an Atari use a common set of routines, from floating point arithmetic to high-resolution graphics routines such as PLOT, DRAWTO, and FILL.
So, instead of BASIC providing a marginal operating system (which on many machines is a maze of machine language calls, requiring incompatible register setup and initialization), we have a BASIC cartridge which uses “universal” OS routines. A good OS simulates a mini-language. It provides documented, unchanging (between various revisions), unified subroutines with full parameter passing and error-checking.
Furthermore, a good OS is extensible. All the major routines and subroutines are accessed indirectly, through pointers. That is why the Atari is so flexible. If you want to change the personality of your computer, just change one of the vectors of a given routine to point to your machine language routine. Your program can then pass on control to the default program.
This indirection is visible throughout the Atari. At the low end is color indirection, where you can change the color of anything drawn to another color merely by changing one color register. The default character set pointer can be changed to point to a user-designed character set. The system interrupt routines and display list interrupts are all fully accessible via a table of pointers. The BREAK key can be masked; the keyboard scan routine can be modified or bypassed; exotic periphetals can be serviced. And all input/output devices are user-definable, from the keyboard to the disk drive.
A notable peculiarity of the Atari is that not just the disk drive or printer, but also the TV screen and keyboard, are considered “peripherals.” You don’t print a character to the screen on the Atari; you send a character or buffer to the Editor device.
Through the hierarchy of a subset of the OS, the CIO (Central Input/Output), BASIC politely requests a line of input from screen and keyboard. After BASIC makes this request, control is passed to CIO, which calls the Editor. The Editor lets the user enter a “line” of text (which can be up to three screen lines long). The user can use cursor controls to edit the line or to move the cursor anywhere on the screen to edit another line.
When RETURN is pressed, the line the cursor is on is placed into a buffer (block of memory). Next, CIO gives this information to the calling routine via another buffer. The CIO is designed to be easy to use from machine language. If you think it sounds complicated, imagine performing all these tasks without an operating system!
┌──────────────────┐ │ Operating System │ └────────┬─────────┘ ∨ ┌─────┐ │ CIO │ └──┬──┘ ∨ ┌─────────────────┐ │ Handler Table │ └───────┬─────────┘ ┌───┬───┬───┬┴──┬───┬───┬───┬─────────> │ │ │ │ │ │ │ │ up to │ │ │ │ ├─┐ │ │ │ 34 ▼ ▼ ▼ ▼ ▼ │ ▼ ▼ ▼ names D P C S E │ K R User i r a c ┌>d │ b S s i s r │ i │ d 2 k n s e │ t │ 3 t e e │ o │ 2 e t n │ r ∨ r t │ ┌───────┐ └──┤ Wedge │ └───────┘ Figure 1. Wedging into a Vector
We don’t have to modify BASIC at all. We just “wedge” our way into the Editor device, “E:”. As intimated, even the “system” devices such as “E:” or “D:”, the disk “driver,” can be replaced. Usually, however, you don’t want to replace a vectored routine; you just want to insert an additional task. In this case, you point the vector to your routine, which performs the little extra task and then calls the main routine. This “bypass” explains the term wedge.
The Handler table contains the names of all the devices. If you wanted to, you could change the name of the cassette device (C:) to another character, such as T: (for Tape), by finding the “C” in the table and changing it to a “T”. Along with each name, the Handler table includes an address that points to another table of addresses that point to all the functions of that particular device, this is multi-level indirection. There is even a vector that points to a list of vectors!
We want to modify the Editor, so we change the first vector to point to our list of vectors. All we really need to do is change one of the vectors in the Editor’s list of vectors, the “Get Character” address. Since this list is in ROM, at $E400, we need to copy this 16-byte table to RAM, modify it, and re-point the Handler table to our RAM version of the Editor Handler table.
Now that we’ve got the Operating System calling our routine instead of the Editor in ROM, we’ve got total control of almost all console input/output. The Get Character routine, instead of Calling E:, asks us for an ASCII character, presumably from the screen and keyboard. We comply by calling the default routine in ROM.
This seems rather roundabout, doesn’t it? But we reserve the right to monitor all characters returned to the Operating system, and hence, BASIC. We get to examine every line of input before that line is returned to BASIC, where any strange new commands would be scorned with an error message.
So, we just catch the carriage return code and leisurely examine the input buffer, located at $0580. All we have to do is compare it against a table of commands, and, if we find a match, execute the command. If not, we just return the line to CIO (and CIO gives it back to BASIC) on the assumption that it’s either a blank line, a BASIC command, or a syntax error. Sounds simple, but such a “parsing” routine is quite a headache to code and understand.
After we’ve intercepted and executed the line, how do we prevent a syntax error when we return the line to BASIC? (And since we’ve “cut in,” we have to follow protocol and return something.) One solution would be to erase the buffer by filling it with spaces. An easier trick would be to change the first character of the line to a period, e.g., “SCRATCH D:TEMP” would become “.CRATCH D:TEMP”. Since BASIC interprets a leading period as an abbreviation for “REM” (don’t ask me why, it’s just a lucky fluke), BASIC conveniently ignores the command and returns READY (which it wouldn’t if we merely blanked out the line).
The parser routine makes it easy for you to add commands. Just place the name of each command, followed by a zero, and the address where you want control to be transferred after the command is recognized, in COMTBL (COMmand TaBLe, see Program 2). The length of the line is found in LENGTH, and the second character after the command is returned in PARMS (since this is where any parameters would be).
command xxx,yyy,zzz ↑ ↑ PARAMS LENGTH Figure 2.
Note that the length is one character past the end of the string, assuming you number from zero. Your command processor can find the command string in LBUFF ($0580).
Theoretically, this technique can be used to add commands to any language environment. You only have to find a way to make the language processor ignore commands when you return the line (such as blanking it out). Of course, the commands themselves are usually language-specific.
Now the way is open to add a plethora of BASIC utility commands. Of course, these will have to be written in machine language and interfaced with the Wedge. I’ve included the resident DOS commands LOCK, UNLOCK, RENAME, and SCRATCH, as well as DIR to print the directory.
You can study the assembly listing (Program 2). If you have an assembler, try typing it in and modifying it. It contains a wealth of techniques and information, such as pattern matching, indirect subroutine calls, making a routine “RESET-proof,” using CIO for input/output from machine language, long branching, modular programming, calling BASIC’s ERROR routine, even “pressing” SYSTEM RESET from within a program.
A machine language program can be hard to even enter into the Atari without an assembler. Program 1 will write the machine language to disk in the form of an “AUTORUN.SYS” file. Save this program so you can write copies to any disk. When you boot this disk, the AUTORUN file will automatically load and initialize the Wedge. You can use the Wedge’s “console DOS” directly, without waiting for the disk utility package (DUP.SYS) to load in, and without losing any programs in memory.
Commands provided are DIR (lists the directory of drive one), LOCK, UNLOCK, SCRATCH (delete), and RENAME. Remember to include the D: (or D2: for drive two, if you have one) in the filename with all the commands except DIR. With RENAME, use the convention RENAME D:oldname,newname.
The Wedge is “persistent”; in other words, it re-initializes itself when you press SYSTEM RESET, so it’s kind of hard to get rid of it. An additional command, KILL, removes the Wedge. You can bring back the Wedge with: PRINT USR(7936).
These commands are just a start. Many others are possible: RENUMBER. FIND, AUTO line number, UPDATE (removes unused variables from the variable name table), and more. If you come up with a useful BASIC utility in machine language, send it to COMPUTE! to be incorporated into a future version of the Wedge.
We’ve managed to intercept BASIC at the command level. In future issues, we’ll go into how you can tell BASIC what to do from machine language. We’ll even try to pursue that elusive aim — actually adding commands to a running program.
100 REM WEDGE BASIC LOADER 110 GRAPHICS 0:? "Insert a DOS 2.0S diskette" 120 ? "with DOS.SYS in drive 1." 130 ? :? "Press RETURN when you have done this." 140 IF PEEK(764)<>12 THEN 140 150 POKE 764,255 160 ? :? "Now writing the Medge AUTORUN.SYS file" 170 TRAP 190 180 OPEN #1,8,0,"D:AUTORUN.SYS":TRAP 40000:GOTO 200 190 CLOSE #1:? :? "Can't open AUTORUN.SYS for write.":END 200 PUT #1,255:PUT #1,255:REM $FFFF HEADER 210 PUT #1,0:PUT #l,31:REM $1F00 START 220 PUT #1,74:PUT #1,33:REM $214A END 230 FOR I=7936 TO 8522+6: REM INCLUDE 6-BYTE AUTORUN 240 READ A:TRAP 310:PUT #1,A;TRAP 40000 250 CKSUM=CKSUM+A 260 NEXT I 270 IF CKSUM<>60435 THEN ? "{BELL}Bad number in DATA statements.":ERR=1 280 CLOSE #1 290 IF NOT ERR THEN ? :? "DATA ok, write successful." 300 END 310 ? :? "Error-";PEEK(195);" when attempting disk write.":CLOSE #1:END 320 REM 330 REM Following is the decimal 340 REMO equilvalent of Wedge 1.0 350 REM Must be typed in perfectly 360 REM In order to function. 370 REM 7936 DATA 104,165,12,141,37,31 7942 DATA 165,13,141,38,31,169 7948 DATA 36,133,12,169,31,133 7954 DATA 13,32,4 3,31,32,92 7960 DATA 31,169,75,141,231,2 7966 DATA 169,33,14 1,232,2,96 7972 DATA 32,64,21,32,11,31 7978 DATA 96,169,80,141,68,3 7984 DATA 169,31,14 1,69,3,169 7990 DATA 0,141,73,3,169,12 7996 DATA 141,72,3,169,11,141 8002 DATA 66,3,162,0,32,86 8008 DATA 228,152,48,1,96,76 8014 DATA 55,33,65,116,97,114 8020 DATA 105,32,87,101,100,103 8026 DATA 101,155,160,0,185,26 8032 DATA 3,201,69,240,7,200 8038 DATA 200,192,34,208,243,96 8044 DATA 200,169,165,153,26,3 8050 DATA 200,169,31,153,26,3 8056 DATA 162,0,189,0,228,157 8062 DATA 165,31,232,224,16,208 8068 DATA 245,169,184,141,169,31 8074 DATA 169,31,141,170,31,24 8080 DATA 173,4,228,105,1,141 8086 DATA 186,31,173,5,228,105 8092 DATA 0,141,187,31,169,0 8098 DATA 133,203,96,251,243,51 8104 DATA 24 6,184,31,163,246,51 8110 DATA 246,60,24 6,76,228,243 8116 DATA 56,1,1,125,32,32 8122 DATA 62,24 6,8,201,155,240 8128 DATA 4,230,203,40,96,140 8134 DATA 181,31,142,182,31,165 8140 DATA 203,240,86,169,51,133 8146 DATA 205,169,32,133,206,160 8152 DATA 0,177,205,217,120,5 8156 DATA 208,12,200,177,205,240 8164 DATA 40,196,203,208,240,76 8170 DATA 37,32,201,255,240,53 8176 DATA 160,0,177,205,240,9 8182 DATA 230,205,144,2,230,206 8188 DATA 76,242,31,24,165,205 8194 DATA 105,3,133,205,144,2 8200 DATA 230,206,76,215,31,200 8206 DATA 132,204,177,205,141,183 8212 DATA 31,200,177,205,141,184 8210 DATA 31,108,183,31,160,0 8224 DATA 169,46,153,128,5,169 8230 DATA 0,133,203,169,155,172 8236 DATA 101,31,174,182,31,40 8242 DATA 96,68,73,82,0,125 8248 DATA 32,83,67,82,65,84 8254 DATA 67,72,0,22,33,76 8260 DATA 79,67,75,0,27,33 8266 DATA 85,78,76,79,67,75 8272 DATA 0,32,33,82,69,78 8278 DATA 65,77,69,0,37,33 8284 DATA 75,73,76,76,0,42 8290 DATA 33,255,155,50,54,32 8296 DATA 70,82,69,69,32,83 8302 DATA 69,67,84,79,82,83 8308 DATA 155,155,0,0,68,58 8314 DATA 42,46,42,162,80,169 8320 DATA 12,157,66,3,32,86 8326 DATA 228,162,80,169,3,157 8332 DATA 66,3,169,6,157,74 8338 DATA 3,169,120,157,68,3 8344 DATA 169,32,157,69,3,32 8350 DATA 86,228,152,16,3,76 8356 DATA 55,33,162,80,169,5 8362 DATA 157,66,3,169,100,157 8368 DATA 68,3,141,68,3,169 8374 DATA 32,157,69,3,141,69 8380 DATA 3,169,20,157,72,3 8386 DATA 141,72,3,32,86,228 8392 DATA 152,48,13,169,9,141 8398 DATA 66,3,162,0,32,86 8404 DATA 228,76,166,32,162,80 8410 DATA 169,12,157,66,3,32 8416 DATA 86,228,76,30,32,162 8422 DATA 80,157,66,3,169,0 8428 DATA 157,73,3,164,203,153 8434 DATA 128,5,56,152,229,204 8440 DATA 157,72,3,24,169,128 8446 DATA 101,204,157,68,3,169 8452 DATA 5,105,0,157,69,3 8458 DATA 32,86,228,152,16,3 8464 DATA 76,55,33,76,30,32 8470 DATA 169,33,76,229,32,169 8476 DATA 35,76,229,32,169,36 8482 DATA 76,229,32,169,32,76 8488 DATA 229,32,173,37,31,133 8494 DATA 12,173,38,31,133,13 8500 DATA 76,116,228,72,162,80 8506 DATA 169,12,157,66,3,32 8512 DATA 86,228,104,162,255,154 8518 DATA 133,185,76,64,185 9000 REM DATA FOR AUTORUN ADDRESS 9010 DATA 224,2,225,2,1,31 9020 REM END OF DATA STATEMENTS
0100 ; The Atari Wedge 0110 ; 0120 *=$1F00 0130 ICCOM =$0342 0140 ICBADR =$0344 0150 ICBLEN =$034B 0160 ICAUX1 =$034A 0170 COPN =$03 0180 CPTXTR =$09 0190 CGTXTR =$05 0200 CPBINR =$0B 0210 CCLOSE =$0C 0220 CIO =$E456 0230 OPDIR =$06 0240 HATABS =$031A 0250 LBUFF =$0580 0260 LENGTH =$CB 0270 MEMLO =$02E7 0280 PARMS =$CC 0290 COM =$CD 0300 DOSINIT =$0C 0310 ENTRY PLA ;For BASIC initialization 0320 ; Make wedge "RESET-proof" 0330 INIT 0340 LDA DOSINIT ;Save DOS 0350 STA REINIT+1 ;initialization 0360 LDA DOSINIT+1 ;inside the REINIT 0370 STA REINIT+2 ;JSR call 0380 ; 0390 INIT2 LDA #REINIT&255 ;Replace DOS init 0400 STA DOSINIT ;with Wedge 0410 LDA #REINIT/256 ;init 0420 STA DOSINIT+1 0430 JSR MSG ;Print message 0440 JSR ECHANGE ; hookup ne 0450 LDA #ENDWEDGE&255 ; Bump up 0460 STA MEMLO 0470 LDA #ENDWEDGE/256 ;low memory pointer 0480 STA MEMLO+1 0490 RTS 0500 ; 0510 REINIT JSR XXXX ;XXXX is filled in with DOSINIT 0520 JSR INIT2 0530 XXXX RTS 0540 ; 0550 ; Print "welcome" message 0560 ; 0570 MSG LDA #WMSG&255 ; Store address of 0580 STA ICBADR ; message 0590 LDA #WMSG/256 0600 STA ICBADR+1 0610 LDA #0 ;Set length 0620 STA ICBLEN+1 0630 LDA #12 0640 STA ICBLEN 0650 LDA #CPBINR ; Ignore carriage-returns 0660 STA ICCOM 0670 LDX #0 ;File O, the editor 0680 JSR CIO ;Call CIO to print it 0690 TYA 0700 BMI ERR ;If no error, return 0710 RTS 0720 ERR JMP ERROR 0730 ; 0740 WMSG .BYTE "Atari Wedge", 155 0760 ; 0770 ; Following replaces the old E: 0780 ; 0790 ECHANGE LDY #0 ; Search for E: 0800 ELOOP LDA HATABS,Y ; in handler table 0810 CMP #'E 0820 BEQ EFOUND ; Found end? 0830 INY ; no, next entry 0840 INY 0850 CPY #34 ; end of table? 0860 BNE ELOOP 0870 RTS ; return 0880 ; 0890 ; Store new handler table address 0900 ; 0910 EFOUND INY 0920 LDA #WEDGETAB&255 0930 STA HATABS,Y 0940 INY 0950 LDA #WEDGETAB/256 0960 STA HATABS,Y 0970 ; Transfer Editor table to Wedge table 0980 LDX #0 0990 XFER LDA $E400,X 1000 STA WEDGETAB,X 1010 INX 1020 CPX #16 1030 BNE XFER 1040 ; Patch in MYINPUT routine 1050 LDA #MYINPUT-1&255 1060 STA WEDGETAB+4 1070 LDA #MYINPUT-1/256 1080 STA WEDGETAB+5 1090 CLC 1100 LDA $E404 ; Set character address 1110 ADC #1 ; Actual address is +1 1120 STA MYINPUT+1 ; Egads ! 1130 LDA $E405 ; Self-modifying code ! 1140 ADC #0 ;(Accept any carry ) 1150 STA MYINPUT+2 1160 LDA #0 1170 STA LENGTH ;Clear length initially 1180 RTS 1190 ; 1200 ; Wedge handler address table 1210 WEDGETAB *=*+16 1220 YSAVE *=*+1 ;Used to save Y register 1230 XSAVE *=*+1 ;Ditto for X 1240 JUMPADR *=*+2 ;used for indirect JMP 1250 MYINPUT 1260 ; The $F63E address is actually placed here by above code 1270 ; to permit this routine to run on the Revision B OS 1280 ; (where it wouldn't be $F63E) 1290 JSR $F63E ;Get a character from E: 1300 PHP 1310 CMP #155 ;End of line? (CR) 1320 BEQ ENDLINE ;Yes, complete line ready 1330 INC LENGTH 1340 PLP 1350 RTS ;No, let CIO have the character 1360 ENDLINE 1370 STY YSAVE ;Save Y for CIO 1380 STX XSAVE 1390 LDA LENGTH 1400 BEQ RETURN.LINE 1410 LOOKUP 1420 LDA #COMTBL&255 ; Set up indirect pointer for 1430 STA COM 1440 LDA #COMTBL/256 ;command table 1450 STA COM+1 1460 NEXTCOM LDY #0 1470 COMPLOOP 1480 LDA (COM),Y ;Compare command against line buffer 1490 CMP LBUFF,Y ;Okay so far? 1500 BNE NOTSAME ;no match 1510 INY 1520 LDA (COM),Y ;is next character null? 1530 BEQ COMPOUND ;yes, command found 1540 CPY LENGTH ;exceeded limits? 1550 BNE COMPLOOP ;if not, continue comparison 1560 JMP RETURN.LINE ;give line to language 1570 NOTSAME CMP #255 ; End of table? 1580 BEQ RETURN.LINE 1590 LDY #0 ; No, skip over command 1600 FINDEND LDA (COM),Y 1610 BEQ ENDCOM ;Hit the zero yet? 1620 INC COM ;No, next character 1630 BCC NOINC1 1640 INC COM+1 1650 NOINC1 JMP FINDEND ;continue until null byte found 1660 ENDCOM CLC ;Add 3 to skip over null byte 1670 LDA COM ;and JMP address 1680 ADC #3 1690 STA COM 1700 BCC NOINC2 ;Check for carry 1710 INC COM+1 1720 NOINC2 JMP NEXTCOM 1730 COMPOUND 1740 INY 1750 STY PARMS ;Y is index into parameters 1760 LDA (COM),Y ;Load JUMPADR with command address 1770 STA JUMPADR 1780 INY 1790 LDA (COM),Y 1800 STA JUMPADR+1 1810 JMP (JUMPADR) ;Execute! 1820 EXIT LDY #0 ;Commands return here 1830 LDA #'. ;Change first character to 1840 STA LBUFF,Y ;"." or REM 1850 ; Allows BASIC to ignore line 1860 RETURN.LINE 1870 LDA #0 1880 STA LENGTH 1890 LDA #155 ;Return EOL to CIO 1900 LDY YSAVE ;Restore Y 1910 LDX XSAVE ;and X 1920 PLP ;and processor status 1930 RTS ;That's it 1940 COMTBL 1950 ; Wedge commands and command table 1960 ; Format is: 1970 ; .BYTE "COMMAND" ,0 1980 ; .WORD COMMAND,ADDRESS 1990 ; End of table is 2000 ; .BYTE 255 2010 .BYTE "DIR",0 2020 .WORD DIR 2030 .BYTE "SCRATCH",0 2040 .WORD SCRATCH 2050 .BYTE "LOCK",0 2060 .WORD LOCK 2070 .BYTE "UNLOCK",0 2080 .WORD UNLOCK 2090 .BYTE "RENAME",0 2100 .WORD RENAME 2110 .BYTE "KILL",0 2120 .WORD KILL 2130 .BYTE 255 2140 ; 2150 DIRBUF *=*+20 2160 DIRNAME .BYTE "D:*.*" 2170 ; 2180 ; Start of commands: 2190 ; 2200 DIR 2210 LDX #$50 ; IOCB#5 2220 LDA #CCLOSE 2230 STA ICCOM,X 2240 JSR CIO ;CLOSE#5 2250 ; OPEN#5,6,0,"D:*.*" 2260 LDX #$50 ;channel #5 2270 LDA #COPN ;open command 2280 STA ICCOM,X 2290 LDA #OPDIR ;special "directory" command 2300 STA ICAUX1,X 2310 LDA #DIRNAME&255 ;filename (wildcard) 2320 STA ICBADR,X 2330 LDA #DIRNAME/256 2340 STA ICBADR+1,X 2350 JSR CIO ;set it up! 2360 TYA 2370 BPL NOERR1 2380 JMP ERROR 2390 ; Print a line to the Editor 2400 NOERR1 2410 NEXT LDX #$50 ;#5 2420 LDA #CGTXTR ;Get a line 2430 STA ICCOM,X 2440 LDA #DIRBUF&255 ;Put it into the buffer 2450 STA ICBADR,X 2460 STA ICBADR 2470 LDA #DIRBUF/256 2480 STA ICBADR+1,X 2490 STA ICBADR+1 2500 LDA #20 ;Maximum length is 20 2510 STA ICBLEN,X ;(actually 17) 2520 STA ICBLEN 2530 JSR CIO 2540 TYA ;Check for end of file 2550 BMI ENDIR ;On error, finished directory 2560 NOERR2 LDA #CPTXTR ;Put text record (print a line) 2570 STA ICCOM 2580 LDX #0 ;Channel 0 is open to the Editor 2590 JSR CIO 2600 JMP NEXT ;Read next line 2610 ; 2620 ENDIR LDX #$50 ;CLOSE#5 2630 LDA #CCLOSE 2640 STA ICCOM,X 2650 JSR CIO 2660 JMP EXIT 2670 ;End of directory routine 2680 ; 2690 ;Following routine is used by lock 2700 ;unlock, scratch, and rename 2710 ;Filename buffer is in LBUFF 2720 ;e.g. LOCK D:TEMP 2730 ; this ^ portion is used 2740 ; to tell CIO the filename. 2750 CALLCIO 2760 LDX #$50 ;Use file 5 (XID n, #5, etc. ) 2770 STA ICCOM,X ;Store command 2780 LDA #0 ;Clear MSB 2790 STA ICBLEN+1,X ;of length 2800 LDY LENGTH 2810 STA LBUFF,Y 2820 SEC ;Get length 2830 TYA ;of filename 2840 SBC PARMS ;(skip over command name) 2850 STA ICBLEN,X 2860 CLC 2870 LDA #LBUFF&255 ;PARMS is start of parameters, 2880 ADC PARMS ;the space in LBUFF 2890 STA ICBADR,X ;after the command 2900 LDA #LBUFF/256 2910 ADC #0 ;Catch any carry 2920 STA ICBADR+1,X 2930 JSR CIO ; Do the job 2940 TYA 2950 BPL NOERR3 2960 JMP ERROR 2970 NOERR3 JMP EXIT 2980 ; 2990 SCRATCH LDA #33 3000 JMP CALLCIO 3010 LOCK LDA #35 3020 JMP CALLCIO 3030 UNLOCK LDA #36 3040 JMP CALLCIO 3050 RENAME LDA #32 3060 JMP CALLCIO 3070 ; 3080 ;Remove Wedge 3090 ; 3100 KILL LDA REINIT+1 ;Restore old DOS 3110 STA DOSINIT ;vector 3120 LDA REINIT+2 3130 STA DOSINIT+1 3140 JMP $E474 ;"Press" SYSTEM RESET 3150 ; 3160 ; 3170 ; End of current wedge 3180 ; (Although more commands can be added.) 3190 ; See future issues of COMPUTE! 3200 ; 3210 ERROR PHA ;Save error code 3220 LDX #$50 ;close files 3230 LDA #CCLOSE 3240 STA ICCOM,X 3250 JSR CIO 3260 PLA ;retrieve error code 3270 LDX #$FF ;reset stack 3280 TXS 3290 STA $B9 ;tell BASIC the error code 3300 JMP $B940 ;call the ERROR routine 3310 ; in the BASIC cartridge 3320 ; 3330 ENDWEDGE 3340 ; Autorun 3350 ; 3360 *=$02E0 3370 .WORD INIT 3380 ; 3390 .END