TECHNICAL GRAPHICS
by Toni Baker
part 3 of 3, ZX Computing April 1987

Part 3: Toni Baker goes wandering into some 3D landscapes.


[NOTE: Although the code starts at 33024 (8100h) it requires a]
[CLEAR 32767 beforehand, as it needs space for "column table" ]
[data starting at 32768 (8000h).                          JimG]


Welcome to the final part of this series. Take a look at the screen
dump included in this article [TECH3_1.GIF] - this is just one
possible output of the program listed here. As you can see, it has a
number of interesting properties. Firstly, it is a three-dimensional
graph. Specifically it is a graph of the function:

	Z=SIN(R)
	Where R=SQR(X^2+Y^2)

The function, as well as the boundaries and scales of the graph, can
be easily changed. The program is therefore capable of handling almost
any three-dimensional graph. There are of course restrictions on what
is possible. The function must be of the form Z=f(X,Y) - that is to
say, there must exist an algorithm to convert given values of X and Y
to a single value of Z.

But notice something else about the graph. Hidden detail really is
hidden. Anything which falls behind a peak or ridge is hidden from
view, and is not drawn on the screen. Of course - this isn't a true
hidden line algorithm. It cannot draw solid figures - it is restricted
to drawing one very special kind of three dimensional landscape, a
graph of a function of X and Y.

The last subroutine in the machine code listing (the part labelled
DEMO_FN at address 827A) is not actually part of the main program at
all. It is merely the algorithm for calculating Z, given X and Y for
the function:
	Z=SIN(R)/R (with R = SQR(X^2+Y^2)
You can replace, or supplement, this subroutine with similar functions
of your own, providing you follow the RULES, which I shall now explain:

Rule one - the subroutine must assume that on entry the calculator
stack is empty, containing no entries whatsoever.

Rule two - you are allowed to make use of calculator memories M0 to
M3, but memories M4 upwards are strictly out of bounds, and must not
be corrupted.

Rule three - sixteen memories altogether are used by the main program,
numbered M0 to MF. You will need to read at least two of them - memory
ME contains the variable X, and memory MF contains the variable Y.
Your algorithm must read these memories whenever X and Y are required.

Such a subroutine may be placed at any address, and therefore it is
possible to contain a library of such functions in memory simultaneously.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Figure 1

  10 RANDOMIZE FN g(-10,-10,10,10,1,1,40,33402)
  20 STOP
  30 DEF FN g(x,x,x,x,x,x,x,x)=USR 33380
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Once such a function subroutine exists in memory, it is then easy to
draw the graph of it. Figure One shows the BASIC program required to
draw the screen image shown. More generally, Figure Two shows the
BASIC line required to draw any graph; it is a use of the BASIC
function FN G, requiring eight parameters. The last of these
parameters must be the address of the machine code subroutine for
calculating the Z values. Figure Two explains the purpose of each of
the eight parameters, with reference to Figure Three [TECH3_3.GIF],
which explains six of them diagrammatically.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Figure 2

DEF FN G(X,X,X,X,X,X,X,X)=USR 33380

RANDOMIZE FN G(X1,Y1,X2,Y2,XH,YH,Zscale,FNaddr)
              \_________________/   |      |
                       |            |      |
                       |            |      +--- FNaddr = address of machine code
                       |            |                    subroutine to calc.
                       |            |                    Z values of function
                       |            |
                       |            +---------- Zscale = factor by which
                       |                                 Z coordinates are to be
                       |                                 multiplied
                       |
                       +----------------------- See Figure Three [TECH3_3.GIF]

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Figure Three in fact shows a bare grid, without a function imposed on
it. (You could argue of course that it shows a graph of the function Z
= 0, but that would be trivial). This grid is converted into a 3D
graph (such as the one whose screen dump is shown) by elevating (or
perhaps lowering) each point on the grid by the Z value calculated for
that point. The grid will therefore "warp" into a three dimensional
graph. Although the scale for the Z axis must be included by hand as
one of the parameters for FN G, the scales for the X and Y axes are
worked out automatically by the program. The scales are chosen so that
the graph fits exactly across the full width of the screen ensuring
maximum possible resolution. All you actually have to worry about is
the range of values allowed by X, and the range of values allowed by
Y. In this program the X values range from X1 to X2, in steps of XH;
while the Y values range from Y1 to Y2 in steps of YH, so you don't
necessarily have to start at the origin (although of course you can if
you want to).


Algorithms

Let's have a look at the main program itself, and see how its
algorithm works. The first thing to note is that the graph is drawn in
two passes. In pass one the lines parallel to the X axis are drawn,
and in pass two the lines parallel to the Y axis are drawn. In each
case the lines (or to be more precise, curves) are drawn from front to
back. It is because the curves are drawn in this order that the
elimination of hidden detail is possible. If any part of a line falls
behind already existing detail, then the hidden part must not be
drawn. By working from front to back this decision is easy to make. If
a line Is intended to be drawn above all other parallel lines, then
the line will be visible, and must be drawn. On the other hand, if a
line is intended to be drawn below any of the parallel lines, then it
is hidden and must not be drawn.

In practice, however, whole lines are not always either completely
above, or completely below, other detail. In practice only part of a
line must be drawn. This means that the DRAW algorithm in the ROM is
no good to us, and we must rewrite one of our own. The decision
process of whether an individual dot composing part of a line is to be
plotted or not is still, however, tremendously easy.

What we do is to construct a table, at address 8000h, of 100h bytes.
Each entry in the table corresponds to a pixel-column from the screen.
Whenever we plot, or attempt to plot, a point in the screen, then we
must examine the table entry corresponding to the column number of the
point. The table contains the height, in pixels, of the uppermost
plotted point on the screen in the given column. If the point to be
plotted lies above this point then it may be safely plotted and the
table entry updated. If the point to be plotted lies below this point
then it is hidden, and should not be plotted. If we now take a look at
the machine code itself, we shall see this algorithm in detail. I
shall explain the workings of each subroutine as we come to it.


Routines

The first subroutine is called G_PLOT (address 8100). This is the
subroutine which attempts to PLOT a point (whose screen coordinates
are held in memories M4 and M5). It compares the coordinates given
with the column table, in the manner described above, and PLOTs the
point only if it is not hidden.

The second subroutine is G_DRAWTO (address 8128). This is my own
version of the DRAW algorithm. It will draw a line beginning at the
point whose screen coordinates are held in memories M4 and M5, and
ending at the point whose screen coordinates are at the top of the
calculator stack, in the order X,Y. Instead of actually plotting each
point on the line, however, it will subject each point to the G_PLOT
algorithm above, to decide whether or not it is hidden.

Next we have a nice easy subroutine, G_CLEAR (address 818B). All this
does is to empty the column table ready for each pass.

The next subroutine is called G_CONVERT (address 8198). Its purpose is
to convert three- dimensional graph coordinates to two-dimensional
screen coordinates. It takes the graph coordinates X and Y from
memories ME and MF respectively, calculates the graph coordinate Z
using the subroutine provided by the user, and will then proceed to
calculate the screen coordinates plotX and plotY from the graph
coordinates X, Y and Z, using an isometric projection algorithm
similar to that in Part Two of this series. On exit, the screen
coordinates plotX, plotY, are left on the calculator stack, in that order.

Then we have the subroutine G_PLOT_3D (address 81C4), which is used to
plot the end points of the lines. On entry the graph coordinates X,Y
must be stored in memories ME and MF. The subroutine will convert
these to screen coordinates before attempting to plot the point.

G_DRAWTO_3D (address 81D0) is very similar. It takes the graph
coordinates X and Y from memories ME and MF, and converts them to
screen coordinates, and then proceeds to draw a line from screen
coordinates M4,M5 to the point calculated, hiding any points which
need to be hidden. The subroutine also tests whether or not BREAK is
pressed, so that you can break out [of] the program before it's
finished by pressing BREAK or CAPS-SHIFT/SPACE.

The subroutine G_GRID (address 81DD) is the main algorithm for drawing
the grid. It is in three parts. The first part is concerned with
initialisation. It creates the sixteen memories required by the
program and initialises memories M6 to MD. The values for M6 to MC are
taken from the calculator stack, and are the first seven parameters
supplied by FN G The value for MD has to be calculated, and this is
done here. The final two parts are the first and second pass of grid
drawing. On each pass an outer loop varies Y (or X) for each curve,
whilst an inner loop varies X (or Y) for different point along that
curve. Finally, the calculator memories are restored to normal. Note
that on entry HL must contain the function subroutine address.

The final subroutine in the program is called FN_GRID (address 8264),
and it is this subroutine which transfers the parameters supplied by
FN G onto the calculator stack (and the last one into HL) before
leaping into G_GRID to draw the actual graph.

The subroutine DEMO_FN (address 827A) is not a part of the main
program. It is, rather, an example program, intended to be replaced by
your own efforts, as described earlier.

This program concludes the TECHNICAL GRAPHICS series, We shall give
graphics a break, for a while, but graphics enthusiasts have no fear.
A new series entitled 3D-GRAPHICS is planned for the not-too-distant
future. Until then, there'll be some surprises. Good programming everyone.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Figure Four

These are the calculator memories used by the program. Note that the
first six of these variables are multi-purpose, so that different
variables are stored there at different times.

Any line-segment drawn by the program is defined as being either
"horizontal-ish or "vertical-ish" depending on its slope. for
"horizontal-ish" lines, the FORWARD coordinate is always the X
coordinate, and the TRANSVERSE coordinate is always the Y coordinate.
For "vertical-ish" lines the reverse is true, with the FORWARD
coordinate being the Y coordinate, and the TRANSVERSE coordinate being
the X coordinate.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
M0  drawX    DRAW displacement in X direction
    dispF    DRAW displacement in forward direction
M1  drawY    DRAW displacement in Y direction
    dispT    DRAW displacement in transverse direction
M2  lineX    X coordinate of next point on line
    lineF    Forward coordinate of next point on line
M3  lineY    Y coordinate of next point on line
    lineT    Transverse coordinate of next point on line
M4  plotX    X coordinate of point to plot
    plotF    Forward coordinate of point to plot
M5  plotY    Y coordinate of point to plot
    plotT    Transverse coordinate of point to plot
M6  X1       Lower bound for X (see Figure 3)
M7  Y1       Lower bound for Y (see Figure 3)
M8  X2       Upper bound for X (see Figure 3)
M9  Y2       Upper bound for Y (see Figure 3)
MA  XH       Step size dividing X axis (see Figure 3)
MB  YH       Step size dividing Y axis (see Figure 3)
MC  Zscale   Amount by which Z coordinates must be multiplied
MD  XYscale  Half of amount by which X and Y coordinates must be multiplied
ME  X        General X coordinate (3D)
ME  Y        General Y coordinate (3D)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Listing

8100 EF       G_PLOT    RST  #28           ;Switch on calculator
8101 E4                 recall M4          plotX
     E5                 recall M5          plotX,plotY
     38                 endcalc            ;Switch off calculator
8104 CDD52D             CALL FP_TO_A       ;A= plotY coordinate
8107 F5                 PUSH AF            ;Stack this value
8108 CDD52D             CALL FP_TO_A       ;A= plotX coordinate
810B 3819               JR   C,G_PL_EXIT   ;Jump if X coord is too big
810D 2017               JR   NZ,G_PL_EXIT  ;Jump if X coord negative
810F 6F                 LD   L,A           ;L= plotX coordinate
8110 2680               LD   H,#80         ;HL points to entry in column table
8112 F1                 POP  AF            ;A= plotY coordinate
8113 C0                 RET  NZ            ;Return if Y coord negative
8114 3804               JR   C,G_PL_MAX    ;Jump if Y coord much too big
8116 FEB0               CP   #B0
8118 3802               JR   C,G_PL_OK     ;Jump unless Y coord too big
811A 3EFF     G_PL_MAX  LD   A,#FF         ;A= FF to signal "above screen"
811C BE       G_PL_OK   CP   (HL)
811D D8                 RET  C             ;Return if point to be plotted
                                           ;is "hidden"
811E 77                 LD   (HL),A        ;Store new value for this column
811F 47                 LD   B,A
8120 4D                 LD   C,L           ;C,B= plot coordinates
8121 3C                 INC  A
8122 C4E522             CALL NZ,PLOT_SUB   ;Plot point unless above screen
8125 C9                 RET                ;Return
8126 F1       G_PL_EXIT POP  AF            ;Balance the stack
8127 C9                 RET                ;Return

8128 EF       G_DRAWTO  RST  #28           toX,toY
8129 E5                 recall M5          toX,toY,plotY
812A C3                 store M3           (M3 stores lineY, =plotY)
     03                 subtract           toX,drawY
     C1                 store M1           (M1 stores drawY, = Y disp for DRAW)
812D 01                 exchange           drawY,toX
     E4                 recall M4          drawY,toX,plotX
     C2                 store M2           (M2 stores lineX, =plotX)
8130 03                 subtract           drawY,drawX
8131 C0                 store M0           (M0 stores drawX, = X disp for DRAW)
8132 07                 or                 drawX<>0 OR drawY<>0?
8133 0004               jump true,G_DRCON1 Jump unless both displacements zero
8135 38                 end calc
8136 C9                 RET                ;Return
8137 EF                 RST  #28
8138 E1       G_DRCON1  recall M1          drawY
8139 2A                 abs                ABS(drawY)
     E0                 recall M0          ABS(drawY),drawX
     2A                 abs                ABS(drawY),ABS(drawX)
813C 03                 subtract           ABS(drawY)-ABS(drawX)
813D 36                 lt zero            ABS(drawY)<ABS(drawX)?
                                           =HV, say
     31                 duplicate          HV,HV
813F 000D               jump true,G_DRCON2 Jump if line horizontal-ish
8141 E0                 recall M0          HV,drawX
8142 E1                 recall M1          HV,drawX,drawY
8143 C0                 store M0           (M0= number of points to plot)
                                           (-ve if line goes [?text missing?]
8144 02                 delete             HV,drawX
8145 C1                 store M1           (M1= transverse displacement)
8146 02                 delete             HV
8147 E2                 recall M2          HV,lineX
     E3                 recall M3          HV,lineX,lineY
     C2                 store M2           (M2= forward coord of last plot)
814A 02                 delete             HV,lineX
814B C3                 store M3           (M3= transverse coord of last plot)
     02                 delete             HV
     E1       G_DRCON2  recall M1          HV,dispT
814E E0                 recall M0          HV,dispT,dispF
814F 05                 divide             HV,dispT/dispF
8150 C1                 store M1           (M1= ratio)
8151 02                 delete             HV
8152 E0                 recall M0          HV,dispT
8153 38                 endcalc            ;Switch off calculator
8154 00                 NOP
8155 CDD52D             CALL FP_TO_A       ;A= number of points to plot
8158 F5       G_DR_LOOP PUSH AF            ;Stack number and direction
8159 2009               JR   NZ,G_DR_NEG   ;Jump if line to be drawn backwards
815B EF                 RST  #28           ;Switch on calculator
815C E2                 recall M2          HV,lineF
     A1                 const one          HV,lineF,1
     0F                 add                HV,lineF+1
815F E3                 recall M3          HV,lineF+1,lineT
8160 E1                 recall M1          HV,lineF+1,lineT,ratio
8161 0F                 add                HV,lineF+1,lineT+ratio
8162 3308               jump G_DRCON3      (Jump forward)
8164 EF       G_DR_NEG  RST  #28           ;Switch on calculator
8165 E2                 recall M2          HV,lineF
     A1                 const one          HV,lineF,1
     03                 subtract           HV,lineF-1
8168 E3                 recall M3          HV,lineF-1,lineT
8169 E1                 recall M1          HV,lineF-1,lineT,ratio
816A 03                 subtract           HV,lineF-1,lineT-ratio
816B C3       G_DRCON3  store M3           (M3= new transverse plot position)
     C5                 store M5           (M5= new transverse plot position)
     02                 delete             HV,lineF11
816E C2                 store M2           (M2= new forward plot position)
     C4                 store M4           (M4= new forward plot position)
     02                 delete             HV
8171 31                 duplicate          HV,HV
     0007               jump true,G_DRCON4 (Jump if line horizontal-ish)
8174 E4                 recall M4          HV,plotF
     E5                 recall M5          HV,plotF,plotT
     C4                 store M4           (M4= new plot X coordinate)
8177 02                 delete             HV,plotF
8178 C5                 store M5           (M5= new plot Y coordinate)
8179 02                 delete             HV
817A 38       G_DRCON4  endcalc            ;Switch off calculator
817B CD0081             CALL G_PLOT        ;Plot next point on line
817E C1                 POP  BC
817F 05                 DEC  B             ;B= no. of remaining points to plot
8180 C5                 PUSH BC
8181 2803               JR   Z,G_DR_EXIT   ;Jump if all points plotted
8183 F1                 POP  AF            ;Zero flag = direction of line
8184 18D2               JR   G_DR_LOOP     ;Loop back to draw all points
8186 EF       G_DR_EXIT RST  #28
8187 02                 delete
8188 38                 endcalc
8189 F1                 POP  AF            ;Balance the stack
818A C9                 RET                ;Return

818B 210080   G_CLEAR   LD   HL,#8000      ;HL points to 1st byte of col. table
818E 110180             LD   DE,#8001      ;DE points to 2nd byte of col. table
8191 01FF00             LD   BC,#00FF      ;BC= length of col.table less one
8194 70                 LD   (HL),B        ;Reset first byte
8195 EDB0               LDIR               ;Reset remaining bytes
8197 C9                 RET                ;Return

8198 2A925C   G_CONVERT LD   HL,(MEMBOT)   ;HL= address of function subroutine
819B CD2C16             CALL CALL_JUMP     ;Call this subroutine
819E EF                 RST  #28           Z
819F EC                 recall MC          Z,Zscale
     04                 multiply           Z' (=Z*Zscale)
     E8                 recall M8          Z',X2
81A2 E9                 recall M9          Z',X2,Y2
81A3 0F                 add                Z',X2+Y2
81A4 EE                 recall ME          Z',X2+Y2,X
     EF                 recall MF          Z',X2+Y2,X,Y
81A6 0F                 add                Z',X2+Y2,X+Y
81A7 03                 subtract           Z',(X2+Y2)-(X+Y)
81A8 ED                 recall MD          Z',(X2+Y2)-(X+Y),XYscale
81A9 04                 multiply           Z',((X2+Y2)-(X+Y))*XYscale
81AA 0F                 add                Z'+((X2+Y2)-(X+Y))*XYscale
81AB A2                 const half         Z'+((X2+Y2)-(X+Y))*XYscale,1/2
81AC 0F                 add                Z'+((X2+Y2)-(X+Y))*XYscale+1/2
81AD 27                 int                plotY
81AE E8                 recall M8          plotY,X2
81AF E7                 recall M7          plotY,X2,Y1
81B0 03                 subtract           plotY,X2-Y1
81B1 EE                 recall ME          plotY,X2-Y1,X
     EF                 recall MF          plotY,X2-Y1,X,Y
81B3 03                 subtract           plotY,X2-Y1,X-Y
81B4 03                 subtract           plotY,(X2-Y1)-(X-Y)
81B5 34F15DB3D743       stk data SQR(3)    plotY,(X2-Y1)-(X-Y),SQR(3)
                        (81 5D B3 D7 43)
81BB 04                 multiply           plotY,((X2-Y1)-(X-Y))*SQR(3)
81BC ED                 recall MD          plotY,((X2-Y1)-(X-Y))*SQR(3),XYscale
81BD 04                 multiply           plotY,((X2-Y1)-(X-Y))*SQR(3)*XYscale
81BE A2                 const half         plotY,((X2-Y1)-(X-Y))*SQR(3)*XYscale
                                           ,1/2
81BF 0F                 add                plotY,((X2-Y1)-(X-Y))*SQR(3)*XYscale
                                           +1/2
81C0 27                 int                plotY,plotX
81C1 01                 exchange           plotX,plotY
     38                 endcalc            ;Switch off calculator
81C3 C9                 RET                ;Return

81C4 CD9881   G_PLOT_3D CALL G_CONVERT     ;Calculate PLOT coordinates
81C7 EF                 RST  #28           plotX,plotY
81C8 C5                 store M5           (M5= plotY)
81C9 02                 delete             plotX
81CA C4                 store M4           (M4= plotX)
     02                 delete
     38                 endcalc            ;Switch off calculator
81CD C30081             JP   G_PLOT        ;Jump to plot point

81D0 CD9881   GDRAWTO3D CALL G_CONVERT     ;Calculate DRAW_TO coordinates
81D3 CD2881             CALL G_DRAWTO      ;Draw the line
81D6 CD541F             CALL BREAK_KEY     ;Is BREAK key pressed?
81D9 D27B1B             JP   NC,REPORT_L   ;Generate BREAK report if so
81DC C9                 RET                ;Otherwise return

81DD E5       G_GRID    PUSH HL            ;Stack subroutine address
81DE CD8B81             CALL G_CLEAR       ;Clear column table
81E1 015000             LD   BC,#0050      ;BC= no. of bytes reqd. for memories
81E4 F7                 RST  #30           ;Create room for memories
81E5 EB                 EX   DE,HL         ;HL points to memory zero
81E6 22685C             LD   (MEM),HL      ;Store in system variable
81E9 0E41               LD   C,#41         ;BC= 0041
81EB 09                 ADD  HL,BC         ;HL points to memory MD
81EC EB                 EX   DE,HL         ;DE points to memory MD
81ED 0E24               LD   C,#24
81EF 2A655C             LD   HL,(STKEND)   ;HL points beyond calculator stack
81F2 EDB8               LDDR               ;Copy top seven items from calc.
                                           ;stack into memories M6 to MC
81F4 23                 INC  HL
81F5 22655C             LD   (STKEND),HL   ;Remove these items from calc. stack
81F8 E1                 POP  HL            ;HL= subroutine address
81F9 22925C             LD   (MEMBOT),HL   ;Store in (MEMBOT) temporarily
81FC EF                 RST  #28           ;Switch on calculator
81FD 34F813CD3A2C       stk data 256/SQR(3)256/SQR(3)
                        (88 13 CD 3A 2C)
8203 E8                 recall M8          256/SQR(3),X2
8204 E6                 recall M6          256/SQR(3),X2,X1
     03                 subtract           256/SQR(3),X2-X1
8206 E9                 recall M9          256/SQR(3),X2-X1,Y2
8207 E7                 recall M7          256/SQR(3),X2-X1,Y2,Y1
8208 03                 subtract           256/SQR(3),X2-X1,Y2-Y1
8209 0F                 add                256/SQR(3),(X2-X1)+(Y2-Y1)
820A 05                 divide             256/SQR(3)/((X2-X1)+(Y2-Y1))
820B CD                 store MD           (MD= XYscale)
     02                 delete
     E9                 recall M9          Y2
820E CF                 store MF           (MF contains Y, =Y2)
820F 02                 delete
8210 E8       G_LOOPY1  recall M8          X2
8211 CE                 store ME           (ME contains X, =X2)
     02                 delete
8213 38                 end calc           ;Switch off calculator
8214 CDC481             CALL G_PLOT_3D     ;Plot first point on curve
8217 EF                 RST  #28           ;Switch on calculator
8218 EE       G_LOOPX1  recall ME          X
     EA                 recall MA          X,XH
821A 03                 subtract           X-XH
821B CE                 store ME           (Decrement X value by XH)
     E6                 recall M6          X,X1
821D 03                 subtract           X-X1
821E 36                 lt zero            X<X1?
     0008               jump true,G_EXIT_1 Jump if X now less than X1
8221 38                 endcalc            ;Switch off calculator
8222 CDD081             CALL GDRAWTO3D     ;Draw next segment of curve
8225 EF                 RST  #28           ;Switch on calculator
8226 33F1               jump G_LOOPX1      ;Jump back to continue drawing curve
8228 EF       G_EXIT_1  recall MF          Y
8229 EB                 recall MB          Y,YH
822A 03                 subtract           Y-YH
822B CF                 store MF           (Decrement Y value by YH)
822C E7                 recall M7          Y,Y1
822D 03                 subtract           Y-Y1
822E 36                 lt zero            Y<Y1?
     30                 not                Y>=Y1?
8230 00DF               jump true,G_LOOPY1 Loop back if Y still in range
8232 38                 endcalc            ;Switch off calculator
8233 CD8B81             CALL G_CLEAR       ;Clear column table again
8236 EF                 RST  #28           ;Switch on calculator
8237 E8                 recall M8          X2
8238 CE                 store ME           (ME contains X, =X2)
     02                 delete
823A E9       G_LOOPX2  recall M9          Y2
823B CF                 store MF           (MF contains Y, =Y2)
823C 02                 delete
823D 38                 endcalc            ;Switch off calculator
823E CDC481             CALL G_PLOT_3D     ;Plot first point on curve
8241 EF                 RST  #28           ;Switch on calculator
8242 EF       G_LOOPY2  recall MF          Y
8243 EB                 recall MB          Y,YH
8244 03                 subtract           Y-YH
8245 CF                 store MF           (Decrement Y value by YH)
8246 E7                 recall M7          Y,Y1
8247 03                 subtract           Y-Y1
8248 36                 lt zero            Y<Y1?
     0008               jump true,G_EXIT_2 Jump if Y now less than Y1
824B 38                 endcalc            ;Switch off calculator
824C CDD081             CALL GDRAWTO3D     ;Draw next segment of curve
824F EF                 RST  #28           ;Switch on calculator
8250 33F1               jump G_LOOPY2      Jump back to continue drawing curve
8252 EE       G_EXIT_2  recall ME          X
     EA                 recall MA          X,XH
8254 03                 subtract           X-XH
8255 CE                 store ME           (Decrement X value by XH)
     E6                 recall M6          X,X1
8257 03                 subtract           X-X1
8258 36                 lt zero            X<X1?
     30                 not                X>=X1?
825A 00DF               jump true,G_LOOPX2 Loop back if X still in range
                                           to draw next X curve
825C 38                 endcalc            ;Switch off calculator
825D 21925C             LD   HL,MEMBOT
8260 22685C             LD   (MEM),HL      ;Restore calculator memories
8263 C9                 RET                ;Return

8264 2A0B5C   FN_GRID   LD   HL,(DEFADD)   ;HL points to user-defined FN records
8267 23       FN_G_LOOP INC  HL
8268 23                 INC  HL            ;HL points to next argument
8269 CDB433             CALL STACK_NUM     ;Push argument onto calc. stack
826C 7E                 LD   A,(HL)
826D 23                 INC  HL            ;HL points to next record
826E FE2C               CP   #2C
8270 28F5               JR   Z,FN_G_LOOP   ;Loop back until all args on calc stk
8272 CD991E             CALL FIND_INT2     ;BC= function subroutine address
8275 60                 LD   H,B
8276 69                 LD   L,C           ;HL= function subroutine address
8277 C3DD81             JP   G_GRID        ;Jump to draw grid

827A EF       DEMO_FN   RST  #28           ;Switch on calculator
827B EE                 recall ME          X
     31                 duplicate          X,X
827D 04                 multiply           X^2
827E EF                 recall MF          X^2,Y
827F 31                 duplicate          X^2,Y,Y
     04                 multiply           X^2,Y^2
     0F                 add                X^2+Y^2
8282 28                 sqr                R (=SQR(X^2+Y^2))
     31                 duplicate          R,R
8284 30                 eq zero            R,R=0?
     0008               jump true,DF_ZERO  Jump if R=0
8287 31                 duplicate          R,R
     1F                 sin                R,SIN(R)
     01                 exchange           SIN(R),R
828A 05                 divide             SIN(R)/R
828B 38                 endcalc            ;Switch off calculator
828C C9                 RET                ;Return
828D EF                 RST  #28
     02       DF_ZERO   delete
828F A1                 const one          1 (=limit of SIN(R)/R as R->0)
8290 38                 endcalc            ;Switch off calculator
8291 C9                 RET                ;Return
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

