PINBAT
by Dave Rogers, Colin Hogg
from Your Computer, February 1985, pages 56-58

Dave Rogers and Colin Hogg challenge you
to become a pinball wizard on the ZX-81.


WE HAVE TRIED to make a game that is different to traditional Pinball but
which retains the feeling that skill and judgement, as well as a certain
amount of luck, are all involved. Machine code is used for animation
effects, score display and the bats, leaving all control of the "marble" in
BASIC, but using an array system to speed up things.
        As with our other ZX-81 programs nearly everything is done by
direct POKEing and PEEKing, to and from the ZX-81's character-mapped screen
memory so everything in the game "actually exists" on screen, not just as
coordinates. Note that the Demo mode runs at about half speed.
        It is possible for the marble to collide with any of 24 different
types of object - left wall characters, right wall, gates, bats, etc. - and
in most cases a different routine is needed to provide a suitable reaction,
such as a bounce, a bonus, a special effect, etc. The most obvious approach
is to use a series of IF ... THEN checks to sort out which object the
marble has hit and so which routine to call, but even in a fast BASIC this
would give an uneven response and a noticeably "sticky" bounce of the marble.
        Pinbat uses a more structured system, where the codes of the
characters that the marble hits are read directly from [of] the screen and
used to slice out individual elements from an array which in turn contains
pre-arranged line numbers to GOTO.
        An example will make this clearer; Look at line 40; X is the
marble's position on-screen, so X+DI (direction) is the marble's next
intended position, and the PEEK of this gives you the character that the
marble is about to "hit".
        Now assume that the marble has just hit a "minus" character. The
Sinclair Code for this character is 22, so the 22nd element of the array H
gets selected and this contains the line number of the required routine to
GOTO in this case the "lost marble" routine at line 470. In this way the
single line 40 can branch directly to all the necessary routines, most of
which will then go back into the main loop.
        Line 4280 specifies the line numbers associated with each of the
characters that the marble could hit, and is a handy reference. In
practice, all the array subscripts are shifted up by one - by line 4300 -
because Sinclair arrays do not have a zero element.
        A little extra planning-ahead is needed to initially fill up the
GOTO array with the correct line numbers, but the advantages are more than
worth the trouble. The method is practically independent of the number of
choices to be made, and so could be expanded even further without loss of
speed, but at present only Sinclair BASIC allows this particular type of
computed GOTO. In some ways it is more powerful than an ON GOTO command.
        Note that the first element of the GOTO array contains 10, so that
when the marble is hitting CHR$ zeros - ie. travelling through empty
spaces - line 40 will GOTO line 10 to continue the main loop. Also, as a
safety precaution in case of typing errors, all those elements of the GOTO
array that correspond to unused characters have been given a default value
of 460, so that if the marble hits something it shouldn't, say a
score-digit character, then line 40 branches to line 460 which prints
"error" and stops the program before the marble can plough on through
addresses that shouldn't be POKEd! This includes the ZX-81's dreaded
NEWLINE characters, lurking unseen down the side of the screen, the POKEing
of which invariably results in irretrievable crashes.
        Something else that can't be seen on-screen is a "character 90"
just above the launch channel. It is POKEd there by line 670 (it can't be
printed) and it is not seen because there is no bit-pattern for it in the
ZX-81 ROM so it just looks like a space character, but it is used here as
an invisible trigger. When the marble hits it then line 40 branches to line
440, printing a "lid" over the launch channel to seal it off.
        Line 230 looks odd, but it is the shortest way of selecting one of
nine different directions at random, by using the Codes of various CHR$
sliced out of a string to yield values rather than doing calculations.
        Note that when any of the machine-code sub-routines return to BASIC
their value is 0, so wherever you see "+USR" in the listing this is just a
short way of calling a machine-code subroutine, it does not affect the
numerical value of whatever calculation it was included in.

        USR 16514 Scrolls screen line 22 - columns 1 to 28 - sideways by
one space, with wraparound - this moves the various bats - either under
INKEY$ control when in play mode, or in response to an automatic routine
when in Demo mode. All the other m-code subroutines return to BASIC via
this bat-move routine, thereby giving more continuous bat control.
        USR 16588 / USR 16608 (separate entry points into the above
routine). Scrolls line 22 left/right respectively. This is done simply by
swapping adjacent pairs of display file bytes and then moving in sideways
increments of one, repeating 28 times in all. Wraparound is achieved
automatically when using this method since after a complete line of swaps
the line itself will have moved one way but the end character will have
been "bucket-brigaded" across to the opposite end - work it out!
        The "autobat" routine starts at 16536. It scans the screen to find
the bat and the marble, works out their relative column positions and then
calls whichever of the above scroll routines is needed to move the bat to
catch the marble.
        USR 16694 Scans the screen (lines 4 to 16) for "asterisk" and
"plus" characters, and wherever one is found swaps it for the other giving
an animation effect. The next time it is called it will swap the characters
back to their originals.
	USR 16689 As above, but scans for left and right brackets characters.
        USR 16699 Same again, but for characters whose Codes are held in
addresses 16690 and 16691 ie. redefinable by POKEing those addresses.
        USR 16638 Adds one to the score (which is held in a permanent store
area, see below) then copies this into the screen.
        USR 16652 Copies the score onto the screen but without adding
anything to it.
        POKE 16660 Spacing between digits when score is copied onto the screen.
        16734 to 16740 Score store area. Before each game, lines 860-880 in
the BASIC reset these bytes to "zero" characters.
        First enter FAST mode then type in, as line one, REM plus exactly
230 of any characters. Now type in the whole listing and, resisting the
temptation to RUN it straight away, SAVE and label two backup copies!!
        When first RUN, the machine code gets POKEd into the REM and is
sum-checked as it goes in. Having corrected any errors, as indicated, and
checked that the program works, you can then erase all lines from 7000
onwards and SAVE a few working copies using the Autosave (goto 3000).
Always CLEAR before SAVEing otherwise you will SAVE the array on tape,
making loading unnecessarily long - the array - is 1k in actual size, since
each number = 5 bytes - also select "SLOW" mode.

        Please check carefully the following lines "gas" indicates
characters obtained using graphic mode and SHIFT: All greys are gsH.

230 		gsW, gsR, gs6, inverse 7, 8,  9, keywords CODE, VAL, LEN,
		NO extra spaces.
960 		inverse right bracket, 30 spaces, inverse left bracket.
970 		inverse left bracket (gsl).
980		gs2, gsR, gsH, gsW, gs4.
2460 		(all inverse) Q, -, Q. Second string, Y.
4280 		(This line is so important it  is checksummed), gsQ, gs7,
		gsW, gs2, gsR, inverse Y, inverse Q, inverse space,
		gsN, gsJ, gsM, gsF, gsG,  gsH, gs0, gsl
		(rest are normal characters).
1010 		gsF, gsJ and inverse zeros are used.
2040 		one space between the brackets.
4090-4100 	gsN and gsM are used.
4410-4430       gsD and gs6 are used. Type exactly as shown, some  words
		"contain" marbles for use on their screen.
5110            2 inverse spaces, inverse 0,  2 inverse spaces, inverse 0,
		inverse space, 7* gsF,  inverse space, inverse  "hogrog",
		7* inverse spaces,  gsJ's and gs B's.
440 		inverse left bracket, 2  inverse spaces.

When playing, use two fingers of one hand rather than two hands, it's much
easier to control the bats. The wraparound can sometimes be used to
advantage to quickly reach marbles on the opposite side of the screen. You
score points by hitting stars or rollover targets (inverse numerals).
        At sidewalls the marble obeys simple laws of reflection, but at
stars or grey blocks the marble gets thrown off at a random angle. At
"triboosters" the marble is deflected whichever way the tribooster
characters are pointing at the time, and then a new tribooster, of random
type, gets printed, this being essential to prevent the marble fron ever
getting stuck in an infinitely recurring pattern of bounces.
        Physicists may like to note that with few exceptions marble motion
is assumed to be Newtonian, frictionless, perfectly elastic and in zero
gravity(!).

Main variables.
H(N) 		"On Hit GOTO" array.
X 		Position of marble on screen
		(start of display file plus an  offset).
DI 		Direction of marble: 1=right, 33=down, etc.
M$ 		Messages and Marbles Rack.
M 		Pointer, keeping track of  progress along M$.
B$ 		Bats.
SN 		Screen number.
DEM 		Demo mode if set to one,  Play mode if zero.
D$ 		Demo select, input by  player.
ANG 		Angle of score display.
P$ 		Printing strings.
S$ 		Screen storage strings.
D,A		 Down/Across printing co ordinates.
JDR 		Undefined variable to stop  program on errors.

Taped copies of this program [were] available for 2.50 from:
11 Carnarvon Road, Walton, Liverpool, L9 1EB.
The cassette also [included] two of [our] other ZX-81 games.

Line-by-line.
7               New direction is 33 or -33 to "channel" marble
		straight up or down.
8               Reverses the newly picked direction in the case of
		the marble hitting the channeller directly from underneath.
10-40           Main loop. Line 40 is particularly efficient (see text).
50-110          Triboosters. Three entry points for three different
		tribooster directions, but all return via 110 which prints
		a new tribooster of random type over the old one.
130-150 	Dropgates. 130 swaps characters 137 and 138 to animate gate.
160-190         Sidewalls. Simple laws of reflection are obeyed e.g.,
		at left wall, if collision is in south-west direction then DI=32,
		rebound is south-east with DI=34.
200-280         Flingers. Marble is thrown off at any of eight directions.
		To choose these at random a string is sliced which
		contains characters that will yield the required numbers -
		after 164 removed from them.
260             If chosen direction takes marble into a clear space then go
		back to main loop, if not, then stay in flinger routine until
		a clear route is chosen.
290-330         Rollover targets. Line 310 peeks the character and adds one to
		it thus incrementing the rollover number each time marble
		passes over it. The bracketed function resets nines to zeros.
420-430		This caters for the  occasions when the marble hits the edge
		of the score display, causing the marble to run diagonally
		across it.
340-390         Bounces off bat. Bats are made from three different
		characters, each causing the marble to bounce off at a
		different angle.
400-418 	Bonus routine.
460		Stops the program if marble  hits an unspecified  character,
		this especially protects against "NEWLINEs" being
		POKEd and causing a crash. Program is routed to this line
		because all unspecified elements of the GOTO array are filled
		with value 460.
440-450         Prints "lid" over launch channel when marble hits the
		(invisible) character 90 present onscreen just above the channel.
470-492		Marble lost. "Twinkles" marble where it hit the baseline.
600-620 	Start new marble.
630-660         Moves the string M$, which contains "messages and
		marbles", across the bottom of the screen until either: a marble
                character is detected whereupon the "ready to launch" routine at
		680-690 operates or: a fullstop character is detected, which is
		used as a trigger for jumping to the "next screen" routine.
692-696		Slides a gate open at top of  launch tube.
700-800		Miscellaneous decisions.
730-770         Game-over routine. Calls all the animation scans at
		once, until player decides to play again or go into demo mode.
800-820		Demo decisions.
860-880		POKEs "score store" bytes to  character zeros.
900-992		Draws over and thereby erases old screen with a new one.
1000-1070       Select different strings containing draw instructions
		for screens 1-6.
1100-1160       Extracts print co-ordinates from strings and passes
		these to draw subroutines.
2000-2480 	Draw routines: 2010 draws whatever is in P$, 2030 draws a
		"channeller", 2090 draws a "rollover target", at 2400 there is a
		separate routine to draw all of screen 3 - bonus screen.
3000-END	Autosave /Initialisation/ presentation screen/machine code
		data, and loader.
