Download ONELINER.BAS

One-Liners for TRS-80 Basic

This program is a collection of one-liners written by various authors. That is, each sub-program in this program originally started out as a one-line program. They have been collected together into a single program so that you can experience them all for yourself without having to load each one up separately.

There are a total of 14 different one-liners. Most one-liners run in infinite (or near-infinite) loops. A few of the programs originally exited to the READY prompt; those are handled here by letting them fall into infinite loops inserted between one-liners. At any time, you can hit Shift-Clear to leave a one-liner and return to the initial menu. To exit the program, press Enter at the initial menu — i.e. without providing any input.

Note: it is recommended that you do not exit the program simply by pressing Break. If this does happen, you should press Shift-Clear and then Enter to exit cleanly before returning to the DOS or loading a new Basic program. Otherwise, sooner or later the machine will probably hang.

All the one-liners, except for the last two, were originally published in Softside magazine. They usually appeared as filler material, occupying the space of a small advertisement. My high-school computer programming teacher had a small hoard of various TRS-80 magazines, including numerous Softsides, and this is how I first became acquainted with the challenge of writing one-liners. I immediately fell in love with the idea. (After all, the one-liner mindset was little more than my normal programming mindset, back then, taken to its logical conclusion.) I carefully paged through all of my teacher's Softsides in order to find all the TRS-80 one-liners. Inevitably I eventually decided that I needed to collect them all together into a single program.

In order to provide a way to access each one-liner as a separate program, I needed to add two things: a way for the user to select which one-liner to run, and a way to cleanly exit a one-liner without using Break, which would exit the whole program.

The former problem was solved with a simple one-liner, line 1 of the program. All it does is reset some of the shared state of the Basic interpreter and then displays a prompt. It is notable only for the fact that the prompt is tailored to remind users of the opening prompt of the Scott Adams Adventure Collections.

The latter problem was a bit trickier, and I eventually solved it by writing a short assembly-language routine to hook into the keyboard handler. The hook code simply looks for the Shift-Clear key combination, and when it sees it it forces the Basic interpreter to jump back to line 1 of the program. Providing and installing the machine-language code is done in line 0 of the program. The machine-language bytes are embedded in a REM statement. I didn't want to use the standard USR interface for invoking the code, however, out of concern that it might somehow interfere with a future one-liner, so instead I hijacked the little-used NAME command. If you're curious to understand how it works, I've included a detailed explanation at the end of the page.

Here is a list of the available one-liners:

1 A one-line adventure game [author: Phillip Case]
2 Installing and running an assembly program
3 A personalized Christmas greeting
4 Using control characters to make hidden messages
5 A one-line dodging game
6 Lunar lander (enter your x-velocity at start)
7 Symmetrical line patterns (press R to clear screen)
8 Another foray into TRS-80 art in one line
9 A one-line driving game
A Random kaleidoscope patterns
B Simple shooting gallery (use Space to fire)
C An excursion in non-orthogonal random lines
D Guess my number [authors: Brian Raiter, Rex Wheeler]
E A hex-to-decimal converter [author: Brian Raiter]

(My personal favorites are numbers 1, 6, and 7.)

The following is a listing, or really a representation of the listing, of the complete program. It is provided for reference only; it's much less effort to use the provided link at the top of the page to obtain a usable copy of the program. Manually entering one-liners is often not a straightforward matter: The interpreter will only accept 255 bytes in a single line of Basic, so occasionally one has to omit the last few characters when entering a line, and then append the remainder via the EDIT command after the interpreter has had a chance to parse the input. (EDIT is also used to embed line breaks and other special characters in string literals.) And of course, the bytes of the machine-language routine in line 0 has to be created by entering filler characters and then modifying them afterwards via POKE.

0 B=PEEK(16548)+256*PEEK(16549)+124:FORX=0TO2:POKEB+79+X*2,PEEK(
16782+X):NEXT:POKE16782,195:POKE16783,BAND255:POKE16784,B/256:NA
ME:REM * One-liner key hook
                             *▪@▪χ §K@χqχpχ"@ χ χ"█Aβζ??▪♠: 8▪>α
χ*▪@▪
^                             V
            S@>???2▟A
  S█A
1 CLEAR50:CLS:RANDOM:POKE16412,0:POKE16419,95:INPUT"Press SHIFT-
CLEAR to return here.
Which One-liner (1-Z)";L1$:IFL1$="",NAME:ENDELSEONASC(L1$)-48GOT
O2,4,5,6,8,9,11,12,13,1,1,1,1,1,1,1,15,16,17,18,19:GOTO1:REM * O
ne-liner menu
2 CLS:PRINT"Adventure
Visible Items: Trees
Obvious Exits: EAST WEST
"STRING$(30,45):INPUT"Tell me";A$:IFA$<>"E",2ELSEPRINT@0,"I'm in
 a cave."@79,"Nothing";:PRINT@143,"None",;:PRINT@263,"";:INPUTA$
:IFASC(A$)=72ANDMID$(A$,4)="P",PRINT"You win!!"ELSE2
3 GOTO3
4 CLEAR22:A$=STRING$(22,32):J=VARPTR(A$):I=PEEK(J+1)+256*PEEK(J+
2):I=I+65536*(I>32767):FORK=ITOI+21:READZ:POKEK,Z:NEXT:DEFUSR=I:
FORX=1TO2:POKEI+10,RND(63)+128:L=USR(0):X=1:NEXT:DATA33,0,60,17,
1,60,1,255,3,54,0,237,176,6,5,33,0,0,43,124,181,201
5 INPUT"ENTER YOUR NAME";A$:A$=A$+" ":L=LEN(A$):P=30:CLS:PRINT@P
+1,"**":FORI=1TOL:PRINT@P-I+64*I,"* ";:PRINTMID$(A$,1,I);:PRINTM
ID$(A$,1,I)+" *":NEXT:PRINTTAB(P)"MERRY":PRINTTAB(P);"XMAS!":FOR
T=1TO5000:NEXT:PRINTTAB(P-11)"FROM SOFTSIDE PUBLICATIONS":RUN5
6 CLEAR999:CLS:PRINT"M A G I C   C O D E":PRINT"E N T E R   M S 
G":INPUTA$:PRINTCHR$(28):PRINTCHR$(31):X=LEN(A$):FORY=1TOX:B$=B$
+MID$(A$,Y,1):C$=CHR$(RND(58)+31):B$=B$+C$:NEXT:PRINT"E N T E R"
:INPUTD$:PRINTB$:PRINT:PRINT"E N T E R":INPUTD$:PRINTCHR$(23)
7 GOTO7
8 P=28:C$="V":FORT=1TO9E9:Q=RND(12):IFT/4=INT(T/4)PRINT@916+Q,"O
":NEXTELSEA$="I            I":PRINT@P,C$@980,A$@1023,:IFPEEK(154
24+P)=79ORP>32ORP<21PRINT@P,"BOOM";:FORW=1TO9:NEXT:RUN8ELSEK=PEE
K(14400):IFK=32P=P-1:NEXTELSEIFK=64P=P+1:NEXTELSENEXT
9 INPUTH:CLS:G=42:FORT=0TO127:G=G+RND(3+(G=47))-2:FORD=GTO47:SET
(T,D):NEXTD,T:FORF=0TO99-H*5:P=PEEK(14400):H=H+(P=32)/2-(P=64)/2
:V=V+.25+(P=8)/2:RESET(X,Y):X=X+H:Y=Y+V:IFPOINT(X,Y)IFV>1ORHRESE
T(X,Y):RUN9ELSESET(X,Y-1):PRINT@349,"Landed"ELSESET(X,Y):NEXT
10 GOTO10
11 CLS:DEFINTA-Z:RANDOM:X=32:Y=12:FORK=1TO2STEP0:IFINKEY$="R"THE
N11ELSEL=RND(25):XD=RND(3)-2:YD=RND(3)-2:FORA=1TOL:SET(X,Y):SET(
127-X,Y):SET(127-X,47-Y):SET(X,47-Y):X=X+XD:Y=Y+YD:X=X-128*INT(X
/128):Y=Y-48*INT(Y/48):NEXTA,K:REM "R"=RESTART
12 CLS:PRINT@402,"C O M P U T E R  A R T  ! !":PRINT:DEFINTA-Z:R
ANDOM:FORT=1TO1000:NEXTT:CLS:FORT=1TO10:A=RND(62)-1:B=RND(62)+61
:C=RND(22)-1:D=RND(22)+21:FORX=ATOB:SET(X,D):SET(X,C):NEXTX:FORY
=CTOD:SET(B,Y):SET(A,Y):NEXTY,T:FORT=1TO3000:NEXTT:GOTO12
13 CLS:J=27:P=15391:FORM=1TO9E9:T=RND(99):FORL=RND(T)TOTSTEPRND(
9)/9:J=50-ABS(ABS(J+SIN(L))-50):A=PEEK(14400)/32:P=P-((AAND1)-(A
/2AND1))*(PEEK(14464)+1):PRINTTAB(J)"!!!   .   !!!":IFPEEK(P)=33
PRINT"SCORE:"SELSEPOKEP,191:S=S+1:NEXTL,M
14 GOTO14
15 CLS:PRINT"Hit space bar to see a new pattern":FORX=1TO700:NEX
TX:FORT=1TO5000:CLS:FORZ=1TO1160:X=RND(64)-1:Y=RND(24)-1:SET(X,Y
):SET(X,47-Y):SET(127-X,47-Y):SET(127-X,Y):IFINKEY$=""THENNEXTZ,
T:GOTO15ELSENEXTT:GOTO15
16 CLS:CLEAR99:C=RND(62)+33:PRINTSTRING$(63,C):FORX=0TO1E9:IFINK
EY$="",NEXTELSEFORH=0TO1E9:R=RND(63)-1:IFPEEK(15360+R)<>C,NEXTEL
SEFORL=960+RTORSTEP-64:PRINT@L,"!";:PRINT@L," ";:NEXT:FORL=0TO9:
PRINT@R,CHR$(RND(159)+32);:NEXT:PRINT@R," ";:NEXTXELSENEXTX
17 CLS:DEFINTA-Z:X=RND(127):Y=RND(47):FORA=0TO10+RND(60):I=RND(7
)-4:J=RND(5)-3:FORB=0TORND(30):SET(X,Y):I=I+2*I*((I+X>127)OR(I+X
<0)):X=X+I:J=J+J*2*((J+Y>47)OR(J+Y<0)):Y=Y+J:NEXT:NEXT:FORI=0TO9
99:NEXT:RUN17
18 CLS:RANDOM:A=1:B=1E3:Q=RND(1E3):FORX=1TO8E9:PRINTA"-"B:INPUT"
 Guess";C:IFC<AORC>B,X=X-1:NEXTELSEIFC<QPRINT"Too low":A=C+1:NEX
TELSEIFC>QPRINT"Too high":B=C-1:NEXTELSEPRINTC"is right!!
 It took you"X"tries.
":X=9E9:NEXT:FORX=0TO0:X=INKEY$="":NEXT:RUN18
19 PRINT"Hex: --"STRING$(2,24);:FORA=1TO0STEP-1:FORQ=0TO0:A$=INK
EY$:Q=A$="":NEXT:B(A)=ASC(A$):IFB(A)>47ANDB(A)<58ORB(A)>64ANDB(A
)<71THENPRINTCHR$(B(A));:NEXT:FORA=0TO1:H=H+((-B(A)+55)*(B(A)>64
)+VAL(CHR$(B(A))))*16[A:NEXT:PRINT"
Dec"H:RUN19ELSEA=A+1:NEXT

Once again, to be clear: I am not the author of the majority of this program. I am merely the anthologist. The original authors and/or copyright holders continue to own and reserve all rights to this code. Being the callow youth that I was at the time, I neglected to write down the authors' names (though I think Softside also often neglected to provide them). In any case, I welcome information from anyone who can indicate where credit is due. Contact me at breadbox@muppetlabs.com.


Appendix: How Line 0 Works

The Basic part of line 0 is straightforward. The first statement:

B=PEEK(16548)+256*PEEK(16549)+124
initializes B with the address of the Basic program, plus 124 bytes, which is the offset of the machine-language routine within the REM. Next comes a short loop:
FORX=0TO2:POKEB+79+X*2,PEEK(16782+X):NEXT
which copies the existing NAME vector to a safe place inside the machine-language routines (in the bytes containing question marks below). Once these bytes have been saved, the program can then replace them:
POKE16782,195:POKE16783,BAND255:POKE16784,B/256
with a vector that jumps to the machine-language routine. With this in place, the Basic program can then use the statement:
NAME
to jump to the machine-language routine.

The machine-language code, besides being necessary to hook the keyboard, also allows much more functionality to be packed into a small number of bytes. Turning to the original assembly, the machine language routine at offset 124 (007CH) from the start of the Basic program is as follows:

                ; The hook installer routine
007C  DD2AA440  SETUP:  LD      IX,(40A4H)      ; IX=line 0 pos
0080  119D01            LD      DE,0100H+HOOK   ; DE=offset of
0083  15                DEC     D               ;   hook rtn
0084  DD19              ADD     IX,DE           ; advance IX
0086  ED4B1640          LD      BC,(4016H)      ; BC=keybd rtn
008A  DD7101            LD      (IX+1),C        ; store in CALL
008D  DD7002            LD      (IX+2),B        ;   instruction
0090  DD221640          LD      (4016H),IX      ; replace w/IX
0094  1E19              LD      E,REMOVE-HOOK   ; change NAME
0096  DD19              ADD     IX,DE           ;   vector to
0098  DD228F41          LD      (418FH),IX      ;   remove rtn
009C  C9                RET                     ; setup is done
4016H contains the address of the current keyboard handling routine. The SETUP routine retrieves that address and stores it inside the HOOK routine, which immediately follows this one. It then replaces that value with the address of the start of the HOOK routine, thus installing the keyboard hook. It also modifies the value of the NAME vector, so that instead of pointing to this routine, it will point to REMOVE (described below). After this, a subsequent NAME statement will call REMOVE instead of SETUP.

The keyboard hook routine is as follows:

                ; The keyboard hook
009D  CD3F3F    HOOK:   CALL    3F3FH           ; keybd handler
00A0  FE1F              CP      31              ; Clear key?
00A2  C0                RET     NZ              ; no, let pass
00A3  3A8038            LD      A,(3880H)       ; a shift key
00A6  B7                OR      A               ;   pressed?
00A7  3E1F              LD      A,31            ; (restore acc)
00A9  C8                RET     Z               ; no, let pass
00AA  E1                POP     HL              ; yes, grab it
00AB  2AA440            LD      HL,(40A4H)      ; HL=line 0 pos
00AE  01D801            LD      BC,0100H+NEXT   ; advance HL to
00B1  05                DEC     B               ;   next line
00B2  09                ADD     HL,BC           ;   of program
00B3  C31E1D            JP      1D1EH           ; back to Basic
The address of the original keyboard routine is stored as part of the CALL instruction at top, replacing the original value of 3F3FH (or "??" in ASCII). Thus the hook routine first invokes the normal keyboard handler. When it returns, the accumulator holds the decoded character. ASCII 31 represents the Clear key, so if the accumulator holds some other value, the routine returns directly. Otherwise the byte at 3880H is examined, and if it is nonzero then a Shift key is currently down. If so, the HL register is altered to point to the second line of the current Basic program, and the hook routine jumps directly back to the Basic interpreter instead of returning.

Finally, the REMOVE routine, invoked when a second NAME statement is encountered, is as follows:

                ; The hook uninstaller routine
00B6  DD2AA440  REMOVE: LD      IX,(40A4H)      ; IX=line 0 pos
00BA  119D01            LD      DE,0100H+HOOK   ; DE=offset of
00BD  15                DEC     D               ;   hook rtn
00BE  DD19              ADD     IX,DE           ; advance IX
00C0  DD5E01            LD      E,(IX+1)        ; get original
00C3  DD5602            LD      D,(IX+2)        ;   keybd rtn
00C6  ED531640          LD      (4016H),DE      ; restore it
00CA  3E3F              LD      A,'?'           ; restore the
00CC  1E3F              LD      E,'?'           ;   original
00CE  163F              LD      D,'?'           ;   NAME vector
00D0  328E41            LD      (418EH),A       ;   put here by
00D3  ED538F41          LD      (418FH),DE      ;   Basic prog
00D7  C9                RET                     ; we're done
                NEXT:
This routine simply restores the values that 4016H and 418EH originally had. The original value for 4016H is stored as part of the CALL instruction at the top of the HOOK routine, while the origianl vector value for 418EH was stored inside this routine directly, replacing the ASCII question mark bytes, as part of the SETUP routine. This routine is invoked in line 1 of the Basic program, in the case when the user inputs an empty string, just before the final END statement.

(Note: This article was published in the TRS8BIT newsletter, in the second issue of year 6, a.k.a. the June 2012 edition.)


Software
Brian Raiter
Muppetlabs