Tourist C

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

Tourist C is a disassembler written in BASIC that can read any of the TS2068’s memory banks: the HOME bank (255), the EXROM (254), the DOCK cartridge bank (0) and any other bank number from 1 to 253. You give a group name, a bank number, a routine name and a start and stop address, in decimal or hexadecimal, then choose one of four modes: CODE disassembles Z80 instructions, CALC decodes the ROM’s floating-point calculator literals that follow RST 40, DATA lists bytes with their characters, and NOTE adds a comment line. Each line goes to the screen and to the printer, so the result is a printed, titled listing of the code.

William J. Pedersen’s WIDJUP Company sold Tourist C in 1986 for $32.50, describing it as an extended bank-switching disassembler with a database of information about the 2068 ROM and a universal printer driver. The program on the disk carries no credit; the attribution comes from the product listing. A second program, INTFC.BT, sets up the printer interface: it offers TASMAN Type A and B, AERCO/Oliger, A&J or a general Z80 PIO interface, builds a 242-byte machine code driver in the printer buffer at 23296 and saves it as the string array PmodEb.A$.

Program Analysis

Structure

On loading, the program starts at line 9853. It pokes a short bank-reading routine into the TS2068’s RAM-resident bank-switching code (lines 9855–9858), then loads four string arrays: the printer module u$(), the printer command table j$(), and the mnemonic tables x$() and w$(). Line 7770 copies the 242-byte module into the printer buffer and patches the channel pointers so that output goes through it. Reading a byte from another bank goes through a 12-byte routine built at line 350 and placed at 24627 or, if the system is in a different configuration, 38848 higher.

Disassembly

The disassembler at 9729–9784 decodes one instruction at a time from the opcode tables in w$(), handling the CB, DD, FD and ED prefixes, index displacements and relative jumps, whose targets it computes. RST 40 switches to calculator mode, which names each literal, reading the names from address 3941, until the end-calculation byte. Lines 9832–9836 accept hexadecimal input. A line counter at 1111 restarts every 90 printed lines.

Notable Techniques

  • Numeric constants held in variables (i, ii, z0, vii, o) to save memory, a common space-saving style.
  • Subroutine addresses stored in variables (Wscr, Disa, Init, Err) and called with GO SUB.
  • Printer output through a machine code module built from DATA with an FN that computes relocated addresses, so one program serves several interfaces.
  • DEF FN C$ renders control and graphics codes readably (^A, g1, DEL).

Potential Issues

  • In the original, pressing ENTER at the first Group Name prompt stopped the program with “2 Variable not found”, because the flag Lkf was only set when a new group was entered. The tape version sets it at start-up.
  • Every disassembled line is printed, so without a printer connected the program waits after the first line.
  • Line 9998, which lists the program to a wide printer, uses stream 5, which the program never opens.

This copy came from a Larken disk. For tape, the LKDOS prefixes were removed, the option to read the Larken DOS banks 256–263 was taken out (the bank prompt now accepts 0–255), the disk catalogue routine was emptied, and the four arrays follow the program on the tape. The disk extraction had left a three-byte variable header at the start of each array, which was removed. The supplied printer module is set up for the AERCO interface; run the second program on the tape to make one for another interface. Tested in ZEsarUX.

Related Products

Extended bank switching disassembler. Features include offset for relative branches, database of information about the 2068 ROM, universal printer driver.

Related Content

Image Gallery

Source Code

   10 ON ERR GO TO 9854: LET I$=j$(1,1)
  218 LET Wscr=9721
  219 LET Disa=9729: LET Err=9784
  224 LET Init=7770
  255 LET Disk=7000
  300 DEF FN C$(W)=("^" AND W<32)+("g" AND (W>127 AND W<165))+("DEL" AND (W=127 OR W=255))+CHR$ (W+(64 AND W<32)+(-79 AND (W>143 AND W<165))+(-95 AND W=127)+(-216 AND W=255))
  305 DEF FN D$(W)=("^" AND W<32)+("g" AND (W>127 AND W<165))+("DEL" AND (W=127 OR W=255))+CHR$ (W+(64 AND W<32)+(-79 AND (W>143 AND W<165))+(-95 AND W=127)+(-216 AND W=255))
  349 LET Wx=24627+(38848 AND PEEK 23746)
  350 RESTORE 350: DATA VAL "33",z0,z0,VAL "6",VAL "255",VAL "205",VAL "22",VAL "99",VAL "175",VAL "71",VAL "77",VAL "201"
  351 FOR n=Wx TO Wx+11: READ Byte: POKE n,Byte: NEXT n
  352 LET Banknm=VAL "255": LET e$="": LET m$="": LET Lkf=z0: GO SUB Init
  356 GO TO 1000
  449 FOR n=i TO 11: POKE (Ld0+CODE o$(n)),CODE j$(Select,n): NEXT n
  450 RANDOMIZE USR (Ld0+CODE o$(12)): RETURN 
  451 LET select=i: GO TO jj
  452 LET select=ii: GO TO jj
  453 LET select=i+ii: GO TO jj
  454 LET select=ii+ii: GO TO jj
  455 LET select=VAL "5": GO TO jj
  456 LET select=VAL "6": GO TO jj
 1000 LET Line=z0
 1010 GO SUB 9902: GO SUB Wscr: PRINT PAPER 6;" This program titles and disass-embles the contents of any bank.HOME......255","EXROM.....254","DOCK........0","LKDOS..256-263","OTHERS..1 to 253",
 1011 PRINT AT 5,z0;" Group Name:     ";M$,'" Bank # ";BANKNM,,: INPUT " New Group Name or ENTER:";E$: IF E$="" THEN GO TO 1013
 1012 LET Lkf=z0: LET M$=E$: INPUT "Bank # (0-255): ";BANKNM: IF BANKNM>255 THEN GO TO 1012
 1013 POKE Wx+4,BANKNM: PRINT AT 5,z0;" Group Name: ";M$," Bank # ";BANKNM;: INPUT "Routine name:",E$: PRINT "  Name:";E$,: IF Lkf THEN OUT vii,BANKNM-256
 1015 INPUT "From(HEX)(<ENTER>->DEC):";I$: IF LEN I$ THEN GO SUB 9832: LET Wb=Wbb: GO TO 1017
 1016 INPUT "From(DEC)(0,<ENTER>->HEX):";Wb: IF Wb=z0 THEN GO TO 1015
 1017 PRINT AT 8,z0;" From address (DEC): ";Wb,,,
 1018 INPUT "Stop(HEX)(<ENTER>->DEC):";I$: IF LEN I$ THEN GO SUB 9832: GO TO 1020
 1019 INPUT "Stop(DEC)(0,<ENTER>->HEX):";Wbb: IF Wbb=z0 THEN GO TO 1018
 1020 PRINT AT 9,z0;" Stop address (DEC): ";Wbb,,,"1 CODE  2 CALC  3 DATA  4 NOTE"''"SELECT",,
 1021 INPUT "Which? ";Wt: IF Wt<i OR Wt>ii+ii THEN GO TO 1021
 1022 IF Wt=4 THEN INPUT P$(i): PRINT AT o,z0;P$(i): GO SUB 453: GO SUB 1111: GO TO 1011
 1023 LET Dd=Wt=i+ii: LET Calc=Wt=ii: GO SUB 9839
 1024 PRINT AT 13,z0;" READY  Press ENTER to procede."''" To correct entries, BREAK and   GO TO 1011 ": PAUSE z0
 1025 INPUT "Print title? Y?";I$
 1026 IF LEN i$ THEN IF I$( TO i)="Y" OR I$( TO i)="y" THEN GO SUB 1100
 1027 IF Dd THEN GO TO 2000
 1028 GO TO 9822
 1100 PRINT AT o,z0,,,,AT o,z0;"Bank#";Banknm;"\";M$;"\";E$: REM dwdh
 1110 GO SUB 454: LET Line=Line+i
 1111 LET Line=Line+i: IF Line>89 THEN LET Line=z0: LPRINT "..";
 1112 RETURN 
 1120 LET w=INT (Y/16): PRINT AT o,z0,,,,AT o,z0;Wb;TAB 8;H$(W+i);h$(Y-16*W+i);TAB 18;"DC";TAB 33;: IF Y>159 AND Y<251 THEN PRINT ":"; INVERSE i;CHR$ (Y-128); INVERSE z0;
 1125 PRINT ":";FN c$(Y);":";: IF y<32 THEN LET W=3*Y+17: PRINT H$(W TO W+ii)
 1130 GO SUB 453: GO TO 1111
 2000 LET Y=Wb: GO SUB 3000: GO SUB 1120
 2001 LET Wb=Wb+i: IF Wb<Wbb THEN GO TO 2000
 2002 GO TO 1010
 3000 REM LKDOS banks 256-263 need the Larken interface (tape version)
 3001 POKE Wx+ii,INT (Y/256): POKE Wx+i,Y-256*INT (Y/256): LET Y=USR Wx: RETURN 
 6000 CLS : FOR n=i TO xx: PRINT AT n,z0;: FOR l=i TO 11: PRINT CODE j$(n,l);" ";: NEXT l: PRINT : NEXT n: STOP : GO TO 6000
 7000 RETURN 
 7770 GO SUB 7772: GO SUB 7771: FOR j=i TO 242: POKE LD0+j-i,CODE U$(j): NEXT j: RANDOMIZE USR (LD0+49): LET Wa=PEEK 23631+256*PEEK 23632: LET Wc=INT (LD0/256): POKE Wa+15,LD0-256*Wc: POKE Wa+16,Wc: RETURN 
 7771 LET LD0=23296: RETURN 
 7772 RESTORE 7773: DIM O$(13): FOR n=i TO 13: READ i$: LET X=VAL I$: LET O$(n)=CHR$ X: NEXT n: RETURN 
 7773 DATA "161","162","164","165","175","188","235","229","232","233","234","45","49"
 9721 INK z0: PAPER VII: FLASH z0: BRIGHT z0: BORDER 6: CLS : RETURN 
 9729 LET Y=Wb: GO SUB 3000: LET Wp=Y: LET Wia=Wb: LET Wfn=i: LET Wf=z0: LET Wfk=Wf: LET Wfs=Wf: LET Wfw=Wf: LET Wv=Wf: LET G$="": DIM p$(i,64)
 9730 IF Wp<>221 AND Wp<>253 THEN GO TO 9733
 9731 IF Wfk THEN GO TO Err
 9732 LET Wf=1+(1 AND Wp=253): LET Wfk=1: LET Wb=Wb+1: LET Y=Wb: GO SUB 3000: LET Wp=Y: GO TO 9730
 9733 IF Wp<>203 THEN GO TO 9737
 9734 LET Wfn=0: LET Wj=345: LET Wb=Wb+Wfk+1: LET Y=Wb: GO SUB 3000: LET Wp=Y: LET W1=INT (Y/64): LET Y=Y-64*W1: LET W2=INT (Y/8): LET W=Y-8*W2: IF Wfk AND W<>6 THEN GO TO Err
 9735 LET W$(Wj, TO 3)=CHR$ (W2+49)+CHR$ (W+1)+"a": IF W1 THEN LET W$(Wj, TO 3)=CHR$ (W1+97)+CHR$ (W2+17)+CHR$ (W+1)
 9736 LET Y=Wb-1: GO SUB 3000: LET Wdis=Y: GO TO 9743
 9737 IF Wp<>237 THEN GO TO 9741
 9738 LET Wb=Wb+1: LET Y=Wb: GO SUB 3000: LET Ww=Y: LET Wj=193+Ww-(36 AND Ww>159)
 9739 IF Ww>123 AND Ww<160 OR Wj>344 THEN GO TO Err
 9740 GO TO 9742
 9741 LET Wj=1+Wp: LET Ww=0
 9742 LET Y=Wb+1: GO SUB 3000: LET Wdis=Y
 9743 LET V$="": LET W0=CODE W$(Wj,1): IF W0=116 THEN GO TO Err
 9744 IF W0=108 THEN LET Wv=VAL (X$(CODE W$(Wj,2))): LET G$=" "
 9745 GO SUB 9763: LET B$=I$+" ": LET W0=CODE W$(Wj,2): GO SUB 9763: IF I$="" THEN GO TO 9752
 9746 IF Wp=211 THEN LET I$="(N)": LET G$=" "
 9747 LET B$=B$+I$: LET W0=CODE W$(Wj,3): GO SUB 9763: IF I$="" THEN GO TO 9750
 9748 IF Wp=219 THEN LET I$="(N)": LET G$=" "
 9749 LET B$=B$+","+I$
 9750 IF Wfk AND NOT Wfw THEN GO TO Err
 9751 LET V$=STR$ Wv: IF NOT LEN G$ AND Wv=0 THEN LET V$=""
 9752 LET P$(1)=STR$ Wia: LET P$(1,19 TO )=B$: LET P$(1,34 TO )=V$+G$
 9753 PRINT AT o,z0;P$(i)
 9754 LET Wb=Wb+1+Wfs*Wfn
 9757 RETURN 
 9763 LET I$=X$(W0): FOR j=LEN I$ TO i STEP -i: IF I$(j)=" " THEN NEXT j
 9764 LET I$=I$( TO j): IF W0=117 THEN GO SUB 9776
 9765 IF W0<>118 THEN GO TO 9768
 9766 LET Wb=Wb+Wfk+1: LET Wfn=0: LET Y=Wb: GO SUB 3000: LET Wv=Y: IF Wp=211 OR Wp=219 THEN GO TO 9768
 9767 LET G$=":"+FN C$(Wv)+":": IF Wv<32 THEN LET W=3*Wv+17: LET G$=G$+" ("+H$(W TO W+2)+")"
 9768 IF W0<119 THEN GO TO 9770
 9769 LET Wb=Wb+2: LET G$=" ": LET Wfn=0: LET Y=Wb-1: GO SUB 3000: LET Wv=Y: LET Y=Wb: GO SUB 3000: LET Wv=Wv+256*Y
 9770 IF NOT Wfk THEN GO TO 9774
 9771 IF W0=7 OR W0=61 OR W0=112 THEN LET Wfw=1
 9772 IF W0=7 THEN LET Wfs=1: GO SUB 9777: LET I$=X$(112+Wf, TO 3)+I$+")"
 9773 IF W0=61 THEN LET I$=X$(112+Wf,2 TO 3)
 9774 IF W0=112 THEN LET I$=X$(112+Wf)
 9775 RETURN 
 9776 LET Wb=Wb+1
 9777 LET W=Wdis: IF W<128 THEN GO TO 9779
 9778 LET W=W-256
 9779 IF NOT Wfk THEN LET W=W+2
 9780 IF W>=0 THEN LET I$="+"+STR$ W
 9781 IF W<0 THEN LET I$=STR$ W
 9782 IF B$( TO 2)="JR" OR B$( TO 2)="DJ" THEN LET Wv=Wia+W
 9783 RETURN 
 9784 PRINT AT o,z0,,,,AT o,z0;Wia;AT o,18;"CODE ERROR ": RETURN 
 9822 LET Y=Wb: GO SUB 3000: LET Wp=Y: IF Calc THEN PRINT AT o,z0,,,,AT o,z0;Wb;TAB 8;H$(INT (Wp/16)+i);H$(Wp-16*INT (Wp/16)+i);TAB 18;"DC";TAB 33;"CALCULATOR": LET Calc=NOT (Wp=56): LET Wt=CALC+i: GO SUB 9839: LET Wb=Wb+i: GO TO 9826
 9823 GO SUB Disa: GO SUB 9830: IF B$( TO ii)<>"RS" THEN GO TO 9826
 9824 LET B$=B$+" ": IF B$( TO 6)="RST 40" THEN LET Calc=i: LET Wt=ii: GO SUB 9839: GO TO 9826
 9825 IF B$( TO 5)="RST 8" THEN PRINT AT xx,i;"ERROR CALL": GO SUB 9840: LET Wt=i+ii: GO SUB 9839: GO SUB 9841
 9826 GO SUB 9840: IF Wb<Wbb THEN GO TO 9822
 9827 FOR n=i TO vii: BEEP .5,4: BEEP .5,16: NEXT n
 9828 PRINT AT 14,i; INK ii;"Press any key to continue.",: PAUSE z0: GO TO 1010
 9830 PRINT AT o,8;: FOR n=Wia TO Wb-i: LET Y=n: GO SUB 3000: LET k=INT (Y/16): PRINT H$(k+i);H$(Y-16*k+i);: NEXT n
 9831 RETURN 
 9832 LET Wbb=z0: FOR n=i TO LEN I$: FOR j=i TO 16
 9833 IF ((I$(n)=H$(j)) OR (CODE H$(j)=CODE I$(n)+32)) THEN LET W2=j: GO TO 9836
 9834 NEXT j
 9835 PRINT AT xx,z0;"INCORRECT ENTRY  TRY AGAIN",: PAUSE 120: INPUT I$: GO TO 9832
 9836 LET Wbb=16*Wbb+W2-i: NEXT n: RETURN 
 9837 LET W=INT (Wb/256): POKE Wx+ii,W: POKE Wx+i,Wb-256*W: LET Wp=USR Wx: RETURN 
 9839 PRINT AT 11,z0; OVER i,,AT 11,8*Wt-vii;"      ";: RETURN 
 9840 GO SUB 453: GO TO 1111
 9841 LET Y=Wb: GO SUB 3000: PRINT AT o,z0,,,,AT o,z0;Wb;TAB 8;H$(INT (Y/16)+i);H$(Y-16*INT (Y/16)+i);TAB 18;"DC";TAB 33;: LET n=3941: LET Y=Y+i+ii: IF Y=258 THEN LET Y=ii
 9842 LET Y=Y-i
 9843 IF Y=z0 THEN GO TO 9846
 9844 LET Wn=PEEK n: LET n=n+i: IF Wn>127 THEN GO TO 9842
 9845 GO TO 9843
 9846 LET Wn=PEEK n: LET n=n+i: PRINT CHR$ (Wn-(128 AND Wn>127));: IF Wn>127 THEN LET Wb=Wb+i: LET Wt=i: GO TO 9839
 9847 GO TO 9846
 9853 REM tape version: the printer module PmodEb.A$ follows on the tape
 9854 ON ERR RESET : LET z0=0: LET i=1: DIM p$(1,64): LET Ii=i+i: LET Vii=7: LET o=19: LET Xx=20: LET jj=449
 9855 POKE 25408,213: POKE 25610,40: POKE 25611,36: POKE 25614,40: POKE 25615,55: POKE 25617,40: POKE 25618,39: POKE 26128,9: POKE 26474,95
 9856 RESTORE 9856: DATA 193,209,115,35,114,43,14,255,219,255,230,128,40,18,24,8,14,255,219,255,230,128,32,8,219,244,47,24,ii,219,244,79
 9857 FOR j=z0 TO 5: READ W: POKE 25424+j,W: NEXT j
 9858 FOR j=z0 TO 25: READ W: POKE 25648+j,W: NEXT j
 9859 LOAD "PmodEb.A$" DATA u$(): LOAD "J$T.A$" DATA j$()
 9860 LOAD "x$().A$" DATA x$(): LOAD "w$().A$" DATA w$(): GO TO 200
 9902 LET H$="0123456789ABCDEFNULSOHSTXETXEOTENQACKBELBS HT LF VT FF CR SO SI DLEDC1DC2DC3DC4NAKSYNETBCANEM SUBESCFS GS RS US ": RETURN 
 9908 PRINT AT 19,8; PAPER 6; INK II;" PRESS ANY KEY ";: PLOT 72,VII: DRAW 120,z0: DRAW z0,9: DRAW -120,z0: DRAW z0,-9: RETURN 
 9998 INPUT "Date, Comment? ";I$: LPRINT "lN";CHR$ 6;: LPRINT #5'"TURIST.BW Listing, ";I$'': LLIST #5: LPRINT CHR$ 12;: STOP 
 9999 SAVE "w$().A$" DATA w$(): SAVE "x$().A$" DATA x$(): SAVE "J$T.A$" DATA j$(): CLEAR : SAVE "TURIST.BW" LINE 9853: GO TO 9854

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.