IO with LLIST #5

Developer(s): William J. Pedersen
Date: 1986
Type: Program
Platform(s): TS 2068

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 23706 to 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:

VariableValueZ80 Meaning
AN62LD A,n (immediate load)
NNA50LD (nn),A
ANN58LD A,(nn)
CALL205CALL nn
JR24JR e
JRZ40JR Z,e
JRNZ32JR NZ,e
JRC56JR C,e
JRNC48JR NC,e
DJNZ16DJNZ e
CP254CP n
RET201RET
RZ200RET Z
RNZ192RET NZ
RC216RET C
RNC208RET NC
SUB214SUB n (ADD A,-n form)
PSHB197PUSH BC
POPB193POP BC
PSHH229PUSH HL
POPH225POP HL
BN6LD B,n
AB120LD A,B
BA71LD 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 STOP followed by unreachable code (PRINT ''" WORKING ": GO TO 2020). The GO TO 2020 is the intended path to the runtime assembler, but it is only reached if the user manually CONTinues 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 q and r as placeholders for the insertion size bytes. Since BASIC reads variable values at runtime during READ, 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

Appears On

Related Products

Related Articles

Related Content

Image Gallery

IO with LLIST #5

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.

Scroll to Top