This program, “I/O” by William J. Pedersen (1987), is a channel-management utility that allows users to add new entries to the CHANS area of system memory, extending the operating system’s I/O channel table with custom output routines, input routines, device-type characters, filenames, and optional disk buffers.
It reads the existing CHANS structure by following the system variable pointers at addresses 23631–23636, walking the linked channel entries and displaying their output address, input address, and device character until the end-of-table marker (byte 128) is found.
A 16-byte machine code routine is poked into address 23706 and executed via RANDOMIZE USR to perform the actual memory insertion, using a BC register pair loaded with computed byte counts (variables q and r representing the low and high bytes of the block size). A second, much larger Z80 machine code block (251 bytes) is assembled at runtime starting at address 26739 by loading Z80 opcode values into named BASIC variables (such as CALL, JRZ, DJNZ, NNA) and reading them from DATA statements, implementing routines labeled OUT_CHAR, STYLE, NEW_LINE, PRINT_COMMA, and TAB/AT for a custom I/O driver.
The TS2068 ON ERR keyword is used to trap invalid numeric input when the user enters output and input routine addresses, defaulting gracefully to a predefined error-call address (4543 decimal) if no value is supplied.
Program Analysis
Program Structure
The listing is presented in three logical layers that share the same line numbers. The main body runs from line 1 to line 9999 and is organized as follows:
- Lines 1–14: Entry point and reusable GOSUB display subroutines (device character, output/input routine, filename, data/disc buffer help screens).
- Lines 1000–1003: A 16-byte machine code stub POKEd into address 23706 and executed via
RANDOMIZE USR 23706to insert space into the CHANS area. - Lines 1009–1019: CHANS table walker — reads system variables at 23631/23632 (CHANS pointer) and 23635/23636 (PROG pointer), then iterates through channel entries until the end-of-table sentinel (byte 128) is found.
- Lines 1100–1184: Main interactive loop — collects device character, output address, input address, optional filename, and optional sector size, then writes the new channel entry into CHANS memory.
- Lines 2000–2040: Runtime Z80 assembler — defines opcode values as named BASIC variables, reads a symbolic DATA stream, and POKEs 251 bytes of machine code to address 26739, implementing OUT_CHAR, STYLE, NEW_LINE, PRINT_COMMA, and TAB/AT routines.
- Lines 9997–9999: Author credit (1987, William J. Pedersen) and SAVE/re-run loop.
CHANS Memory Walker (Lines 1009–1019)
The subroutine at line 1009 reconstructs the CHANS table display by reading the 16-bit CHANS base address from system variables 23631–23632 and the PROG (BASIC start) address from 23635–23636. It then steps through channel records in the format: 2 bytes output address, 2 bytes input address, 1 byte device character. If the device character is a digit (‘1’–’9’, ASCII 49–57), a file-type channel is assumed, and the next 10 bytes are read as the filename, followed by a 2-byte data-and-buffer length field. The sentinel for end-of-table is a byte value of 128 at the current pointer, after which the BASIC program area address is reported.
Machine Code Insertion Stub (Lines 1000–1003)
A 16-byte Z80 routine is loaded into the spare system variable area at 23706–23721. It uses the LD HL, LD DE, LD BC, and a ROM LDIR-based block-move call (via CALL 18,187 — the Spectrum ROM’s “make room” routine at 0x12BB) to open a gap in CHANS. The two DATA placeholders q and r (line 1000) are the low and high bytes of the insertion size, computed at runtime and stored in variables q and r before each GO SUB 1000 call. For non-file channels, line 1180 sets r=0 and q=5 (5 bytes: 2 output + 2 input + 1 device). For file channels, lines 1130–1131 compute the full entry size as s = 21 + ss (sector size), split into r (MSB) and q (LSB).
Runtime Z80 Assembler (Lines 2000–2040)
This is the most technically elaborate section. Rather than embedding raw numeric opcodes directly in DATA statements, the program defines Z80 mnemonic abbreviations as BASIC numeric variables at lines 2020–2024:
| Variable | Value | Z80 Meaning |
|---|---|---|
AN | 62 | LD A,n (immediate load) |
NNA | 50 | LD (nn),A |
ANN | 58 | LD A,(nn) |
CALL | 205 | CALL nn |
JR | 24 | JR e |
JRZ | 40 | JR Z,e |
JRNZ | 32 | JR NZ,e |
JRC | 56 | JR C,e |
JRNC | 48 | JR NC,e |
DJNZ | 16 | DJNZ e |
CP | 254 | CP n |
RET | 201 | RET |
RZ | 200 | RET Z |
RNZ | 192 | RET NZ |
RC | 216 | RET C |
RNC | 208 | RET NC |
SUB | 214 | SUB n (ADD A,-n form) |
PSHB | 197 | PUSH BC |
POPB | 193 | POP BC |
PSHH | 229 | PUSH HL |
POPH | 225 | POP HL |
BN | 6 | LD B,n |
AB | 120 | LD A,B |
BA | 71 | LD B,A |
Multi-byte ROM addresses are split across two consecutive DATA items. For example, IOA and IOB together form the 16-bit address of the ROM’s character output routine, and NEW,LINE forms a call to the newline handler. The assembled code is POKEd to 26739–26989 (251 bytes) by line 2025. Lines 2030 then patches system variables 23282 and 23584 to integrate the new driver.
ON ERR Usage
The program makes heavy use of the TS2068 ON ERR keyword to handle invalid numeric input gracefully. At lines 1117–1118 and 1120–1121, ON ERR RESET clears any error trap before an INPUT, then ON ERR GO TO redirects to the same line on a VAL error (e.g., non-numeric input), and ON ERR RESET clears the handler afterward. This pattern is a clean TS2068-specific alternative to manual string validation.
Device Character Validation (Lines 1109–1115)
The device character entered by the user is validated and normalized using ASCII arithmetic. The character code is read with CODE I$, then lowercased letters are converted to uppercase by subtracting 32 if the code is above 96 (n=n-(32 AND n>96)). Codes above 90 (non-alpha, non-digit after normalization) are rejected. Numeric digits (49–57) set the file flag to 1, triggering the extended file-channel entry path.
Sector Size Rounding (Line 1129)
When a sector size is entered, it is rounded up to the next multiple of 256 using the formula ss=256*INT((VAL I$-1)/256)+256. This ensures the disc buffer is always a whole number of 256-byte sectors, accommodating DOS implementations that may use different default sector sizes, while defaulting to 256 if no value is supplied.
Notable Anomalies
- Line 1150 contains
STOPfollowed by unreachable code (PRINT ''" WORKING ": GO TO 2020). TheGO TO 2020is the intended path to the runtime assembler, but it is only reached if the user manuallyCONTinues after the STOP — this appears to be intentional, serving as a deliberate pause before installing the machine code driver. - Line 1000 DATA contains the bare variable names
qandras placeholders for the insertion size bytes. Since BASIC reads variable values at runtime duringREAD, these values are dynamically substituted from whatever was last computed — an elegant if unusual technique that avoids re-POKEing the DATA area. - The DATA at line 2018 (
32,32,32,58) represents a four-byte literal string ” :” (three spaces and a colon), used as a margin/padding constant referenced by the TAB/AT routine. - Line 1137 iterates
FOR f=0 TO 10(11 iterations) to write the 11-byte filename field (1 device character byte + 10 filename bytes), consistent with the CHANS file entry format.
Content
Source Code
1 REM \'.\'."Load Index"\'.\'. GOTO 3 and CONT for first copy. CONT for additional copies. Start recording at 6. ================================
2 CLS : PRINT AT 20,1;"The tape index is being loaded."'" STOP RECORDER when completed.";AT 10,0;"";: LOAD "Index" SCREEN$
3 STOP : SAVE "Load Index" LINE 2: GO TO 3
1 GO TO 1100
2 PRINT AT 20,7;" PRESS ANY KEY ": RETURN
4 PRINT " DEVICE CHARACTER "'" Use an alphabetic character"'" for ordinary channels."'" Use a numeral from ""1"" to ""9"" for file access channels."
5 PRINT INK 2;" If you fail to enter a device character, the job terminates.": RETURN
6 PRINT " OUTPUT ROUTINE "'" Enter the address in decimal.": GO SUB 7: RETURN
7 PRINT INK 2;" Failing to enter an address defaults to an error call.": RETURN
8 PRINT " INPUT ROUTINE "'" Enter the address in decimal.": GO SUB 7: RETURN
10 PRINT " FILENAME "'" Up to ten characters can be"'" used. The first must be alpa- betic, $, @, or &. The rest must be alphanumeric or _.": RETURN
12 PRINT " DATA and DISC BUFFER "'" The filename and data are nor- mally used to hold directory data to minimize seeks. LIOS manages this."'" Two bytes for entry length and four for disc address are used here."
13 PRINT " The disc buffer is 256 bytes unless you enter a different value. Your DOS may differ.": RETURN
14 PRINT " The disc buffer is 256 bytes unless you enter a different value. Consult your DOS.": RETURN
1000 DATA 33,83,92,94,35,86,235,43,43,1,q,r,205,187,18,201
1001 RESTORE 1000: FOR n=23706 TO 23721: READ byte: POKE n,byte: NEXT n
1002 RANDOMIZE USR 23706
1003 RETURN
1009 CLS : PRINT '" CURRENT ASSIGNMENTS "'"Chan Addr OUTPUT INPUT DEVICE "
1010 LET b=PEEK 23631+256*PEEK 23632: LET a=PEEK 23635+256*PEEK 23636: LET k=1: REM CHANS, PROG
1011 PRINT " ";k;TAB 5;b;TAB 11;PEEK b+256*PEEK (b+1);TAB 18;: LET b=b+2
1012 PRINT PEEK b+256*PEEK (b+1);TAB 25;: LET b=b+2
1013 LET dv=PEEK b: PRINT CHR$ dv: LET b=b+1: IF dv>48 AND dv<58 THEN GO TO 1017
1014 IF PEEK b=128 THEN GO TO 1016
1015 LET k=k+1: GO TO 1011
1016 PRINT "EOT ";b;" BASIC @ ";a: LET bb=b: GO SUB 2: RETURN
1017 FOR n=b TO b+9: PRINT INK 1;CHR$ PEEK n;: NEXT n: LET b=b+10
1018 LET len=PEEK b+256*PEEK (b+1): LET b=b+2
1019 PRINT " Data & Buffer ";len: LET b=b+len: GO TO 1014
1100 PAPER 7: INK 0: FLASH 0: BORDER 7: CLS
1101 PRINT '" This program allows you to"'" open up space under BASIC for"'" adding new CHANNELS."''" Each new channel requires:"''" 1. OUTPUT routine address,"'" 2. INPUT routine address,"'" 3. DEVICE TYPE character;"
1102 PRINT '" and sometimes:"''" 4. FILE NAME (ten bytes),"'" 5. ADDITIONAL DATA and"'" 6. DISC BUFFER (one sector)."
1104 PRINT AT 18,7;" STOP RECORDER ": GO SUB 2: PAUSE 0
1105 GO SUB 1009: GO SUB 2
1106 PAUSE 0: CLS : PRINT ': GO SUB 4: GO SUB 6: GO SUB 8: GO SUB 2
1107 PAUSE 0: CLS : PRINT ': GO SUB 10: GO SUB 12: GO SUB 2
1108 PAUSE 0: CLS : PRINT AT 10,0;: GO SUB 4
1109 LET s=0: LET w=4543: LET file=0: INPUT I$: IF LEN I$=0 THEN GO TO 1150
1110 LET n=CODE I$: LET n=n-(32 AND n>96): IF n>90 THEN GO TO 1109
1111 IF n>63 THEN GO TO 1115: REM alphabetic
1112 IF n>48 AND n<58 THEN GO TO 1114: REM numeric
1113 GO TO 1109
1114 LET file=1
1115 LET J$=CHR$ n
1116 CLS : PRINT AT 10,0;: GO SUB 6: PRINT 'J$
1117 ON ERR RESET : INPUT I$: IF LEN I$=0 THEN LET nn=w: GO TO 1119
1118 ON ERR GO TO 1117: LET nn=VAL I$: ON ERR RESET
1119 CLS : PRINT AT 10,0;: GO SUB 8: PRINT 'nn;" ";J$
1120 ON ERR RESET : INPUT I$: IF LEN I$=0 THEN LET m=w: GO TO 1122
1121 ON ERR GO TO 1120: LET m=VAL I$: ON ERR RESET
1122 CLS : PRINT AT 10,0;" CHANS ITEM "'nn;" ";m;" ";J$: PAUSE 120
1123 IF NOT file THEN GO TO 1180
1124 CLS : PRINT AT 10,0;: GO SUB 10: PRINT 'nn;" ";m;" ";J$
1125 INPUT I$: LET I$=I$+" ": LET I$=I$( TO 10): LET J$=J$(1)+I$
1126 CLS : PRINT AT 7,0;: GO SUB 12: PRINT 'nn;" ";m;" ";J$;
1127 INPUT "SECTOR SIZE: ";I$
1128 IF LEN I$=0 THEN LET ss=256: GO TO 1130
1129 LET ss=256*INT ((VAL I$-1)/256)+256
1130 LET s=21+ss: PRINT " ";ss
1131 LET r=INT (s/256): LET q=s-256*r
1133 GO SUB 1000
1134 PRINT AT 21,5; FLASH 1;" WORKING "
1135 LET msb=INT (nn/256): LET lsb=nn-256*msb: POKE bb,lsb: POKE bb+1,msb: LET bb=bb+2
1136 LET msb=INT (m/256): LET lsb=m-256*msb: POKE bb,lsb: POKE bb+1,msb: LET bb=bb+2
1137 FOR f=0 TO 10: POKE f+bb,CODE J$(f+1): NEXT f: LET bb=bb+11
1138 LET msb=INT ((ss+4)/256): LET lsb=ss+4-256*msb: POKE bb,lsb: POKE bb+1,msb: LET bb=bb+2
1139 FOR f=bb TO bb+ss-1: POKE f,32: NEXT f
1140 GO SUB 1009: GO TO 1108
1150 CLS : PRINT AT 10,4;" JOB COMPLETED ": STOP : PRINT ''" WORKING ": GO TO 2020
1180 LET r=0: LET q=5: GO SUB 1000: REM nonfile
1181 LET msb=INT (nn/256): LET lsb=nn-256*msb: POKE bb,lsb: POKE bb+1,msb: LET bb=bb+2
1182 LET msb=INT (m/256): LET lsb=m-256*msb: POKE bb,lsb: POKE bb+1,msb: LET bb=bb+2
1183 POKE bb,CODE J$: LET bb=bb+1
1184 GO TO 1140
2000 DATA BA,AN,0,167,AN,0,NNA,117,104,AB,RNZ,0,0,CP,165,JRC,10,SUB,165,CP,38,JRZ,114,CALL,69,7,RET
2001 DATA CP,144,JRC,4,AN,124,JR,82,CP,128,JRC,4,AN,96,JR,74,CP,32,JRNC,42,CP,12,JRNZ,12,198,110,253,203,48,86,JRZ,-36,SUB,59,JR,54
2002 REM CODES
2003 DATA CP,13,JRZ,109,CP,6,202,63,105,CP,16,RC,CP,24,RNC,CP,22,AN,1,JRC,1,60,NNA,117,104,201
2004 DATA CP,58,JRZ,44,CP,123,JRC,20, 203,71,JRZ,-78,253,203,48,86,JRNZ,4,CP,123,JRNC,-88,CP,127,JRNZ,2,SUB,63
2005 REM OUT_CHAR
2006 DATA 79,AN,10,CP,74,212,NEW,LINE,121,CALL,IOA,IOB,ANN,CUR,SOR,60,NNA,CUR,SOR,RET
2007 REM STYLE
2008 DATA 55,JRC,6,CP,58,JRZ,-27,JR,-123,253,203,48,86,245,204,NEW,LINE,241,CP,38,JRZ,-15
2010 DATA PSHH,33,106,105,PSHB,BN,4,126,CALL,OUT,CHAR,35,DJNZ,-7,POPB,POPH,RET
2011 REM NEW_LINE
2012 DATA AN,13,CALL,IOA,IOB,AN,10,CALL,IOA,IOB,AN,10,NNA,CUR,SOR,167,RZ,PSHB,BA,AN,32,CALL,IOA,IOB,DJNZ,-7,POPB,RET
2013 REM PRINT_COMMA
2014 DATA CALL,84,105,230,15,47,198,17,BA,AN,32,CALL,OUT,CHAR,CALL,84,105,RZ,DJNZ,-11,RET,ANN,MAR,GIN,PSHB,BA,ANN,CUR,SOR,144,POPB,RET
2015 REM TAB/AT
2017 DATA 229,33,104,105,229,42,79,104,233,225,201
2018 DATA 32,32,32,58
2020 LET MAR=46: LET GIN=105: LET PRT=71: LET COM=105: LET OUT=232: LET CHAR=104: LET CUR=234: LET SOR=104: LET IOA=95: LET IOB=105: LET NEW=35: LET LINE=105
2021 LET DJNZ=16: LET JR=24: LET JRNZ=32: LET JRZ=40: LET JRNC=48: LET JRC=56
2022 LET BN=6: LET AN=62: LET NNA=50: LET ANN=58: LET AB=120: LET BA=71
2023 LET CP=254: LET RET=201: LET RNZ=192: LET RZ=200: LET RNC=208: LET RC=216: LET CALL=205: LET SUB=214
2024 LET PSHB=197: LET POPB=193: LET PSHH=229: LET POPH=225
2025 RESTORE 2000: FOR N=26739 TO 26989: READ BYTE: POKE N,BYTE: NEXT N
2030 POKE 23282,21: POKE 23584,26
2040 STOP
9997 REM RESET 1987 William J. Pedersen.
9998 SAVE "I/O" LINE 1100
9999 PAUSE 0: GO TO 9998
1 GO TO 1100
2 PRINT AT 20,7;" PRESS ANY KEY ": RETURN
4 PRINT " DEVICE CHARACTER "'" Use an alphabetic character"'" for ordinary channels."'" Use a numeral from ""1"" to ""9"" for file access channels."
5 PRINT INK 2;" If you fail to enter a device character, the job terminates.": RETURN
6 PRINT " OUTPUT ROUTINE "'" Enter the address in decimal.": GO SUB 7: RETURN
7 PRINT INK 2;" Failing to enter an address defaults to an error call.": RETURN
8 PRINT " INPUT ROUTINE "'" Enter the address in decimal.": GO SUB 7: RETURN
10 PRINT " FILENAME "'" Up to ten characters can be"'" used. The first must be alpa- betic, $, @, or &. The rest must be alphanumeric or _.": RETURN
12 PRINT " DATA and DISC BUFFER "'" The filename and data are nor- mally used to hold directory data to minimize seeks. LIOS manages this."'" Two bytes for entry length and four for disc address are used here."
13 PRINT " The disc buffer is 256 bytes unless you enter a different value. Your DOS may differ.": RETURN
14 PRINT " The disc buffer is 256 bytes unless you enter a different value. Consult your DOS.": RETURN
1000 DATA 33,83,92,94,35,86,235,43,43,1,q,r,205,187,18,201
1001 RESTORE 1000: FOR n=23706 TO 23721: READ byte: POKE n,byte: NEXT n
1002 RANDOMIZE USR 23706
1003 RETURN
1009 CLS : PRINT '" CURRENT ASSIGNMENTS "'"Chan Addr OUTPUT INPUT DEVICE "
1010 LET b=PEEK 23631+256*PEEK 23632: LET a=PEEK 23635+256*PEEK 23636: LET k=1: REM CHANS, PROG
1011 PRINT " ";k;TAB 5;b;TAB 11;PEEK b+256*PEEK (b+1);TAB 18;: LET b=b+2
1012 PRINT PEEK b+256*PEEK (b+1);TAB 25;: LET b=b+2
1013 LET dv=PEEK b: PRINT CHR$ dv: LET b=b+1: IF dv>48 AND dv<58 THEN GO TO 1017
1014 IF PEEK b=128 THEN GO TO 1016
1015 LET k=k+1: GO TO 1011
1016 PRINT "EOT ";b;" BASIC @ ";a: LET bb=b: GO SUB 2: RETURN
1017 FOR n=b TO b+9: PRINT INK 1;CHR$ PEEK n;: NEXT n: LET b=b+10
1018 LET len=PEEK b+256*PEEK (b+1): LET b=b+2
1019 PRINT " Data & Buffer ";len: LET b=b+len: GO TO 1014
1100 PAPER 7: INK 0: FLASH 0: BORDER 7: CLS
1101 PRINT '" This program allows you to"'" open up space under BASIC for"'" adding new CHANNELS."''" Each new channel requires:"''" 1. OUTPUT routine address,"'" 2. INPUT routine address,"'" 3. DEVICE TYPE character;"
1102 PRINT '" and sometimes:"''" 4. FILE NAME (ten bytes),"'" 5. ADDITIONAL DATA and"'" 6. DISC BUFFER (one sector)."
1104 PRINT AT 18,7;" STOP RECORDER ": GO SUB 2: PAUSE 0
1105 GO SUB 1009: GO SUB 2
1106 PAUSE 0: CLS : PRINT ': GO SUB 4: GO SUB 6: GO SUB 8: GO SUB 2
1107 PAUSE 0: CLS : PRINT ': GO SUB 10: GO SUB 12: GO SUB 2
1108 PAUSE 0: CLS : PRINT AT 10,0;: GO SUB 4
1109 LET s=0: LET w=4543: LET file=0: INPUT I$: IF LEN I$=0 THEN GO TO 1150
1110 LET n=CODE I$: LET n=n-(32 AND n>96): IF n>90 THEN GO TO 1109
1111 IF n>63 THEN GO TO 1115: REM alphabetic
1112 IF n>48 AND n<58 THEN GO TO 1114: REM numeric
1113 GO TO 1109
1114 LET file=1
1115 LET J$=CHR$ n
1116 CLS : PRINT AT 10,0;: GO SUB 6: PRINT 'J$
1117 ON ERR RESET : INPUT I$: IF LEN I$=0 THEN LET nn=w: GO TO 1119
1118 ON ERR GO TO 1117: LET nn=VAL I$: ON ERR RESET
1119 CLS : PRINT AT 10,0;: GO SUB 8: PRINT 'nn;" ";J$
1120 ON ERR RESET : INPUT I$: IF LEN I$=0 THEN LET m=w: GO TO 1122
1121 ON ERR GO TO 1120: LET m=VAL I$: ON ERR RESET
1122 CLS : PRINT AT 10,0;" CHANS ITEM "'nn;" ";m;" ";J$: PAUSE 120
1123 IF NOT file THEN GO TO 1180
1124 CLS : PRINT AT 10,0;: GO SUB 10: PRINT 'nn;" ";m;" ";J$
1125 INPUT I$: LET I$=I$+" ": LET I$=I$( TO 10): LET J$=J$(1)+I$
1126 CLS : PRINT AT 7,0;: GO SUB 12: PRINT 'nn;" ";m;" ";J$;
1127 INPUT "SECTOR SIZE: ";I$
1128 IF LEN I$=0 THEN LET ss=256: GO TO 1130
1129 LET ss=256*INT ((VAL I$-1)/256)+256
1130 LET s=21+ss: PRINT " ";ss
1131 LET r=INT (s/256): LET q=s-256*r
1133 GO SUB 1000
1134 PRINT AT 21,5; FLASH 1;" WORKING "
1135 LET msb=INT (nn/256): LET lsb=nn-256*msb: POKE bb,lsb: POKE bb+1,msb: LET bb=bb+2
1136 LET msb=INT (m/256): LET lsb=m-256*msb: POKE bb,lsb: POKE bb+1,msb: LET bb=bb+2
1137 FOR f=0 TO 10: POKE f+bb,CODE J$(f+1): NEXT f: LET bb=bb+11
1138 LET msb=INT ((ss+4)/256): LET lsb=ss+4-256*msb: POKE bb,lsb: POKE bb+1,msb: LET bb=bb+2
1139 FOR f=bb TO bb+ss-1: POKE f,32: NEXT f
1140 GO SUB 1009: GO TO 1108
1150 CLS : PRINT AT 10,4;" JOB COMPLETED ": STOP : PRINT ''" WORKING ": GO TO 2020
1180 LET r=0: LET q=5: GO SUB 1000: REM nonfile
1181 LET msb=INT (nn/256): LET lsb=nn-256*msb: POKE bb,lsb: POKE bb+1,msb: LET bb=bb+2
1182 LET msb=INT (m/256): LET lsb=m-256*msb: POKE bb,lsb: POKE bb+1,msb: LET bb=bb+2
1183 POKE bb,CODE J$: LET bb=bb+1
1184 GO TO 1140
2000 DATA BA,AN,0,167,AN,0,NNA,117,104,AB,RNZ,0,0,CP,165,JRC,10,SUB,165,CP,38,JRZ,114,CALL,69,7,RET
2001 DATA CP,144,JRC,4,AN,124,JR,82,CP,128,JRC,4,AN,96,JR,74,CP,32,JRNC,42,CP,12,JRNZ,12,198,110,253,203,48,86,JRZ,-36,SUB,59,JR,54
2002 REM CODES
2003 DATA CP,13,JRZ,109,CP,6,202,63,105,CP,16,RC,CP,24,RNC,CP,22,AN,1,JRC,1,60,NNA,117,104,201
2004 DATA CP,58,JRZ,44,CP,123,JRC,20, 203,71,JRZ,-78,253,203,48,86,JRNZ,4,CP,123,JRNC,-88,CP,127,JRNZ,2,SUB,63
2005 REM OUT_CHAR
2006 DATA 79,AN,10,CP,74,212,NEW,LINE,121,CALL,IOA,IOB,ANN,CUR,SOR,60,NNA,CUR,SOR,RET
2007 REM STYLE
2008 DATA 55,JRC,6,CP,58,JRZ,-27,JR,-123,253,203,48,86,245,204,NEW,LINE,241,CP,38,JRZ,-15
2010 DATA PSHH,33,106,105,PSHB,BN,4,126,CALL,OUT,CHAR,35,DJNZ,-7,POPB,POPH,RET
2011 REM NEW_LINE
2012 DATA AN,13,CALL,IOA,IOB,AN,10,CALL,IOA,IOB,AN,10,NNA,CUR,SOR,167,RZ,PSHB,BA,AN,32,CALL,IOA,IOB,DJNZ,-7,POPB,RET
2013 REM PRINT_COMMA
2014 DATA CALL,84,105,230,15,47,198,17,BA,AN,32,CALL,OUT,CHAR,CALL,84,105,RZ,DJNZ,-11,RET,ANN,MAR,GIN,PSHB,BA,ANN,CUR,SOR,144,POPB,RET
2015 REM TAB/AT
2017 DATA 229,33,104,105,229,42,79,104,233,225,201
2018 DATA 32,32,32,58
2020 LET MAR=46: LET GIN=105: LET PRT=71: LET COM=105: LET OUT=232: LET CHAR=104: LET CUR=234: LET SOR=104: LET IOA=95: LET IOB=105: LET NEW=35: LET LINE=105
2021 LET DJNZ=16: LET JR=24: LET JRNZ=32: LET JRZ=40: LET JRNC=48: LET JRC=56
2022 LET BN=6: LET AN=62: LET NNA=50: LET ANN=58: LET AB=120: LET BA=71
2023 LET CP=254: LET RET=201: LET RNZ=192: LET RZ=200: LET RNC=208: LET RC=216: LET CALL=205: LET SUB=214
2024 LET PSHB=197: LET POPB=193: LET PSHH=229: LET POPH=225
2025 RESTORE 2000: FOR N=26739 TO 26989: READ BYTE: POKE N,BYTE: NEXT N
2030 POKE 23282,21: POKE 23584,26
2040 STOP
9997 REM RESET 1987 William J. Pedersen.
9998 SAVE "I/O" LINE 1100
9999 PAUSE 0: GO TO 9998
1 GO TO 1100
2 PRINT AT 20,7;" PRESS ANY KEY ": RETURN
4 PRINT " DEVICE CHARACTER "'" Use an alphabetic character"'" for ordinary channels."'" Use a numeral from ""1"" to ""9"" for file access channels."
5 PRINT INK 2;" If you fail to enter a device character, the job terminates.": RETURN
6 PRINT " OUTPUT ROUTINE "'" Enter the address in decimal.": GO SUB 7: RETURN
7 PRINT INK 2;" Failing to enter an address defaults to an error call.": RETURN
8 PRINT " INPUT ROUTINE "'" Enter the address in decimal.": GO SUB 7: RETURN
10 PRINT " FILENAME "'" Up to ten characters can be"'" used. The first must be alpa- betic, $, @, or &. The rest must be alphanumeric or _.": RETURN
12 PRINT " DATA and DISC BUFFER "'" The filename and data are nor- mally used to hold directory data to minimize seeks. LIOS manages this."'" Two bytes for entry length and four for disc address are used here."
13 PRINT " The disc buffer is 256 bytes unless you enter a different value. Your DOS may differ.": RETURN
14 PRINT " The disc buffer is 256 bytes unless you enter a different value. Consult your DOS.": RETURN
1000 DATA 33,83,92,94,35,86,235,43,43,1,q,r,205,187,18,201
1001 RESTORE 1000: FOR n=23706 TO 23721: READ byte: POKE n,byte: NEXT n
1002 RANDOMIZE USR 23706
1003 RETURN
1009 CLS : PRINT '" CURRENT ASSIGNMENTS "'"Chan Addr OUTPUT INPUT DEVICE "
1010 LET b=PEEK 23631+256*PEEK 23632: LET a=PEEK 23635+256*PEEK 23636: LET k=1: REM CHANS, PROG
1011 PRINT " ";k;TAB 5;b;TAB 11;PEEK b+256*PEEK (b+1);TAB 18;: LET b=b+2
1012 PRINT PEEK b+256*PEEK (b+1);TAB 25;: LET b=b+2
1013 LET dv=PEEK b: PRINT CHR$ dv: LET b=b+1: IF dv>48 AND dv<58 THEN GO TO 1017
1014 IF PEEK b=128 THEN GO TO 1016
1015 LET k=k+1: GO TO 1011
1016 PRINT "EOT ";b;" BASIC @ ";a: LET bb=b: GO SUB 2: RETURN
1017 FOR n=b TO b+9: PRINT INK 1;CHR$ PEEK n;: NEXT n: LET b=b+10
1018 LET len=PEEK b+256*PEEK (b+1): LET b=b+2
1019 PRINT " Data & Buffer ";len: LET b=b+len: GO TO 1014
1100 PAPER 7: INK 0: FLASH 0: BORDER 7: CLS
1101 PRINT '" This program allows you to"'" open up space under BASIC for"'" adding new CHANNELS."''" Each new channel requires:"''" 1. OUTPUT routine address,"'" 2. INPUT routine address,"'" 3. DEVICE TYPE character;"
1102 PRINT '" and sometimes:"''" 4. FILE NAME (ten bytes),"'" 5. ADDITIONAL DATA and"'" 6. DISC BUFFER (one sector)."
1104 PRINT AT 18,7;" STOP RECORDER ": GO SUB 2: PAUSE 0
1105 GO SUB 1009: GO SUB 2
1106 PAUSE 0: CLS : PRINT ': GO SUB 4: GO SUB 6: GO SUB 8: GO SUB 2
1107 PAUSE 0: CLS : PRINT ': GO SUB 10: GO SUB 12: GO SUB 2
1108 PAUSE 0: CLS : PRINT AT 10,0;: GO SUB 4
1109 LET s=0: LET w=4543: LET file=0: INPUT I$: IF LEN I$=0 THEN GO TO 1150
1110 LET n=CODE I$: LET n=n-(32 AND n>96): IF n>90 THEN GO TO 1109
1111 IF n>63 THEN GO TO 1115: REM alphabetic
1112 IF n>48 AND n<58 THEN GO TO 1114: REM numeric
1113 GO TO 1109
1114 LET file=1
1115 LET J$=CHR$ n
1116 CLS : PRINT AT 10,0;: GO SUB 6: PRINT 'J$
1117 ON ERR RESET : INPUT I$: IF LEN I$=0 THEN LET nn=w: GO TO 1119
1118 ON ERR GO TO 1117: LET nn=VAL I$: ON ERR RESET
1119 CLS : PRINT AT 10,0;: GO SUB 8: PRINT 'nn;" ";J$
1120 ON ERR RESET : INPUT I$: IF LEN I$=0 THEN LET m=w: GO TO 1122
1121 ON ERR GO TO 1120: LET m=VAL I$: ON ERR RESET
1122 CLS : PRINT AT 10,0;" CHANS ITEM "'nn;" ";m;" ";J$: PAUSE 120
1123 IF NOT file THEN GO TO 1180
1124 CLS : PRINT AT 10,0;: GO SUB 10: PRINT 'nn;" ";m;" ";J$
1125 INPUT I$: LET I$=I$+" ": LET I$=I$( TO 10): LET J$=J$(1)+I$
1126 CLS : PRINT AT 7,0;: GO SUB 12: PRINT 'nn;" ";m;" ";J$;
1127 INPUT "SECTOR SIZE: ";I$
1128 IF LEN I$=0 THEN LET ss=256: GO TO 1130
1129 LET ss=256*INT ((VAL I$-1)/256)+256
1130 LET s=21+ss: PRINT " ";ss
1131 LET r=INT (s/256): LET q=s-256*r
1133 GO SUB 1000
1134 PRINT AT 21,5; FLASH 1;" WORKING "
1135 LET msb=INT (nn/256): LET lsb=nn-256*msb: POKE bb,lsb: POKE bb+1,msb: LET bb=bb+2
1136 LET msb=INT (m/256): LET lsb=m-256*msb: POKE bb,lsb: POKE bb+1,msb: LET bb=bb+2
1137 FOR f=0 TO 10: POKE f+bb,CODE J$(f+1): NEXT f: LET bb=bb+11
1138 LET msb=INT ((ss+4)/256): LET lsb=ss+4-256*msb: POKE bb,lsb: POKE bb+1,msb: LET bb=bb+2
1139 FOR f=bb TO bb+ss-1: POKE f,32: NEXT f
1140 GO SUB 1009: GO TO 1108
1150 CLS : PRINT AT 10,4;" JOB COMPLETED ": STOP : PRINT ''" WORKING ": GO TO 2020
1180 LET r=0: LET q=5: GO SUB 1000: REM nonfile
1181 LET msb=INT (nn/256): LET lsb=nn-256*msb: POKE bb,lsb: POKE bb+1,msb: LET bb=bb+2
1182 LET msb=INT (m/256): LET lsb=m-256*msb: POKE bb,lsb: POKE bb+1,msb: LET bb=bb+2
1183 POKE bb,CODE J$: LET bb=bb+1
1184 GO TO 1140
2000 DATA BA,AN,0,167,AN,0,NNA,117,104,AB,RNZ,0,0,CP,165,JRC,10,SUB,165,CP,38,JRZ,114,CALL,69,7,RET
2001 DATA CP,144,JRC,4,AN,124,JR,82,CP,128,JRC,4,AN,96,JR,74,CP,32,JRNC,42,CP,12,JRNZ,12,198,110,253,203,48,86,JRZ,-36,SUB,59,JR,54
2002 REM CODES
2003 DATA CP,13,JRZ,109,CP,6,202,63,105,CP,16,RC,CP,24,RNC,CP,22,AN,1,JRC,1,60,NNA,117,104,201
2004 DATA CP,58,JRZ,44,CP,123,JRC,20, 203,71,JRZ,-78,253,203,48,86,JRNZ,4,CP,123,JRNC,-88,CP,127,JRNZ,2,SUB,63
2005 REM OUT_CHAR
2006 DATA 79,AN,10,CP,74,212,NEW,LINE,121,CALL,IOA,IOB,ANN,CUR,SOR,60,NNA,CUR,SOR,RET
2007 REM STYLE
2008 DATA 55,JRC,6,CP,58,JRZ,-27,JR,-123,253,203,48,86,245,204,NEW,LINE,241,CP,38,JRZ,-15
2010 DATA PSHH,33,106,105,PSHB,BN,4,126,CALL,OUT,CHAR,35,DJNZ,-7,POPB,POPH,RET
2011 REM NEW_LINE
2012 DATA AN,13,CALL,IOA,IOB,AN,10,CALL,IOA,IOB,AN,10,NNA,CUR,SOR,167,RZ,PSHB,BA,AN,32,CALL,IOA,IOB,DJNZ,-7,POPB,RET
2013 REM PRINT_COMMA
2014 DATA CALL,84,105,230,15,47,198,17,BA,AN,32,CALL,OUT,CHAR,CALL,84,105,RZ,DJNZ,-11,RET,ANN,MAR,GIN,PSHB,BA,ANN,CUR,SOR,144,POPB,RET
2015 REM TAB/AT
2017 DATA 229,33,104,105,229,42,79,104,233,225,201
2018 DATA 32,32,32,58
2020 LET MAR=46: LET GIN=105: LET PRT=71: LET COM=105: LET OUT=232: LET CHAR=104: LET CUR=234: LET SOR=104: LET IOA=95: LET IOB=105: LET NEW=35: LET LINE=105
2021 LET DJNZ=16: LET JR=24: LET JRNZ=32: LET JRZ=40: LET JRNC=48: LET JRC=56
2022 LET BN=6: LET AN=62: LET NNA=50: LET ANN=58: LET AB=120: LET BA=71
2023 LET CP=254: LET RET=201: LET RNZ=192: LET RZ=200: LET RNC=208: LET RC=216: LET CALL=205: LET SUB=214
2024 LET PSHB=197: LET POPB=193: LET PSHH=229: LET POPH=225
2025 RESTORE 2000: FOR N=26739 TO 26989: READ BYTE: POKE N,BYTE: NEXT N
2030 POKE 23282,21: POKE 23584,26
2040 STOP
9997 REM RESET 1987 William J. Pedersen.
9998 SAVE "I/O" LINE 1100
9999 PAUSE 0: GO TO 9998
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.



