Nearly all the subroutine is concerned with calculating parameters to draw an arc, first for 247D CD PRMS1 - which calculates an "arc counter" a and the turning angle for each "arc" - and then for 2420 DRW STEPS, which actually draws the "arcs". "Arc" in quotes means a short straight-line approximation to a small arc. CD PRMS1 requires as parameters only G, the turning angle, which is given from BASIC, and the approximate diameter of the circle of which the line to be drawn will form a part. The true length of this diameter, by fairly simple trigonometry,is SQR (X**2 + Y**2)/SIN (G/2) using absolute values for X, Y and G. The approximation uses X + Y instead of SQR (X**2 + Y**2), which is rather crude - it is always too big, and can be as much as 1.414d... (root two) timestoo big when X and Y are equal. But it is only used to calculatethe_number_of_"arcs" which will be drawn to approximate to the curve, and this isn't at all a critical figure. The program nearly always draws more and shorter "arcs" for a DRAW curve than for a CIRCLE. DRW STEPS requires as input parameters - the PLOT coordinates X0, Y0 from which drawing is to start; easy, these are safely in 5C7D COORDS - the PLOT coordinates X0 + X, Y0 + Y where it is to end;these are the start coordinates plus the X, Y input from BASIC, which are still on the stack - the x and y displacements to draw the first straight- line "arc", called U and V in the notes; these will be repeated a times turning through T = G/a each time. The only ones which are hard to figure are U and V. The calculation for them is also hard to explain - the notes do their best, but without a diagram and without proper mathemati- cal typography it was impossible to be really clear. A little spelling out may help: The_length of the first of the a steps will be longer than it would be if you took a steps along the straight line from start to finish positions. If the length of this straight line is L, the length of the steps would be L/a, but the length of our circuitous steps comes out as L * W where W is more than 1/a, in fact SIN (G/2a) W = ---------- SIN (G/2) SIN (T/2) = --------- (using T = G/a as above). SIN (G/2) The smaller G and T are, the nearer will W be to 1/a andthis length to L/a, but it will always be a little longer - a straight line is after all the shortest distance between two points. The_direction of the first step will be somewhere to theright of the straight-line direction, assuming that the angle G represents a turn to the left, and vice versa. The angle of thisaiming off comes out as G/2 - G/2a = G/2 - T/2 This is a small angle if G and a are small, not many steps to turn through a small angle, and bigger if they are bigger, more steps for a bigger angle; as you would expect. To turn a direction whose coordinates are x, y through an angle F without any change in the length, the standard formulas for the new direction P, Q are P = y SIN F + x COS F Q = y COS F - x SIN F But of course U and V are reduced in length, compared with the X and Y of the straight line step L, by the reduction factor W. Putting XW, YW in the above equations, with G/2 - T/2 for F, you get the equations given in the notes, U = YW SIN (G/2 - G/2a) + XW COS (G/2 - G/2a) V = YW COS (G/2 - G/2a) - XW SIN (G/2 - G/2a) The program uses the calculator: - to get W, which is put in mem-1; - to get X*W, which goes in mem-2; - to get Y*W, which stays on the stack; - to get G/2 - T/2, which the notes call F; SIN F goes inmem-5 and COS F in mem-0; - to put X*W in mem-2 again, because 37BF sin overwrote mem-0 to mem-2, and now Y*W in mem-1; - to calculate U as Y*W SIN F + X*W COS F and put it in mem-1; - to calculate V as Y*W COS F - X*W SIN F and put it in mem-2. Everything needed by DRW STEPS is now in place. If afterall this the first "arc" step comes out at less than one pixel the subroutine again exits to LINE DRAW to draw a straight line. Input parameters: 5C7D COORDS holds the last PLOT position - HL points to the first byte of the second last value Xon the calculator stack, DE to the last value Y - the BASIC pointer in 5C5D CH ADD is on the character after the Y parameter. Action: read the character at CH ADD - if it is 2C comma jump on to DR 3 PRMS - (there is no third operand) call 1BEE CHECK END, whichin syntax checking reports "Nonsense in BASIC" if this isn't theend of a statement or makes a double return to the statement loop at 1B76 STMT RET if it is - (run time) exit to 2477 LINE DRAW to draw a straight line. _238D_DR_3_PRMS (there is a third parameter): move on theBASIC pointer - call 1C82 EXPT 1NUM to read the next expression on to the calculator stack; the turning angle G - call 1BEE CHECK END; with effect as above - (run time; the stack holds, from the top, G, Y, X) usethe calculator to get SIN (G/2) - if it isn't zero jump on to DR SIN NZ; see under 3501 not for the calculator equivalent of JR NZ,DR SIN NZ - (SIN G/2 is zero) delete it and exit to 2477 LINE DRAWto draw a straight line. _23A3_DR_SIN_NZ (the stack holds, from the top, SIN G/2, Y and X): calculate the absolute value of (ABS X + ABS Y)/SIN (G/2), the very approximate diameter of the circle; called Z in the notes - re-stack Z to ensure it is in full FP form; now the stack holds Z, SIN G/2, Y, X; the first two are the required initial parameters for CD PRMS1 - if the exponent of Z is 81h or more jump on to DR PRMS; ie if Z is one or more - (Z less than one) delete Z and SIN G/2 from the stack - exit to 2477 LINE DRAW to draw a straight line. _23C1_DR_PRMS: call 247D CD PRMS1; it returns the calculator stack unchanged, Z, SIN G/2, Y, X, with the arc-count a in the B register mem-0 = the angle T = G/a through which each "arc" must turn mem-1 = SIN (T/2) mem-3 = COS T mem-4 = SIN T - save the arc count on the machine stack - use the calculator again to figure the initial displacements U and V explained above. The steps are - (down to st-mem-1) calculate W = SIN (T/2)/SIN (G/2) and put it in mem-1 - (down to st-mem-2; the stack holds W, Y, X) calculate X * W and put it in mem-2 - (down to multiply; the stack holds X * W, X, Y) calculate Y * W - (down to st-mem-0; the stack holds Y * W, Y, X) calculate F = G/2 - T/2, put SIN F in mem-5 and COS F in mem-0 - (down to st-mem-1; the stack holds COS F, X * W, Y * W, Y, X) put X * W in mem-2 and Y * W in mem-1; this couldn't bedone before because 37BF sin and 37AA cos corrupt mem-0 to mem-2 - (down to st-mem-1; the stack holds Y * W, Y, X) calculate U, the x displacement for the first "arc": U = YW SIN F + XW COS F and put it in mem-1 - (down to st-mem-2; the stack holds U, Y * W, Y, X) calculate V, the y displacement: V = YW COS F - XW SIN F and put it in mem-2 - (to end-calc; the stack holds V, Y, X) calculate ABS U+ ABS V and immediately delete it! This leaves Y, X on the stack, and ABS U + ABS V in the FP number location pointed to by5C65 STKEND - if the exponent of ABS U + ABS V is less than 81h exitto 2477 LINE DRAW; draw a straight line, the "arc" length is less than one - use the calculator just to exchange X and Y on the stack - call 2D28 STACK A to put the x coordinate from 5C7D COORDS of the last PLOT/DRAW on the calculator stack; now it holds X0, X, Y - use the calculator to put X0 in mem-0 - find X + X0, the x coordinate of the end of the curve - call 2D28 STACK A to put Y0 from 5C7D COORDS on to thestack; now the stack holds Y0, Y, X0 + X - put it in mem-5 - find Y + Y0 - exit from the calculator with Y0, X0, Y0 + Y, X0 + X on the stack - recover the arc count. Exit: to 2477 LINE DRAW if a straight line results, otherwise into 2420 DRW STEPS. Output parameters: for DRW STEPS, B holds the arc count - HL and DE hold pointers to the second last and last values on the stack - the stack holds, from the top, the start coordinates of the line to be drawn Y0, X0, and its end coordinates Y + Y0, X + X0 - mem-1 holds U - mem-2 holds V - mem-3 still holds COS T from CD PRMS1 - mem-4 still holds SIN T from CD PRMS1 for LINE DRAW, 5C7D COORDS holds the coordinates of the start of the line, the calculator stack holds the DRAW operands Y, X. Rems: 22DC PLOT main routine called as subroutine by DRAW (actually 24EC D L PLOT in 24B7 DRAW LINE) 2307 STK TO BC used in line drawing subroutine (24B7 DRAW LINE) 2320 CIRCLE jumps into (actually 2420 DRW STEPS) 233B C R GRE 1 jumps into (actually 2420 DRW STEPS) 235A C ARC GE1 jumps into (actually 2420 DRW STEPS) 2420 DRW STEPS requires inputs as above 2439 ARC START uses approximate value found by 247D CD PRMS1 called by DR PRMS, set initial parameters 24B7 DRAW LINE called by DRAW LINE subroutine 24B7 Draws a straight line on screen, starting at the last PLOT position, with DRAW coordinates X and Y; either or both maybe negative. Each one-pixel_step is checked for_range, ie to ensure that the line hasn't gone off the screen; the check of the y coordinate is actually done in the 22E5 PLOT SUB subroutine called from D L PLOT. The algorithm for plotting these pixels is one of the most elegant in the Spectrum ROM; it was taken over from the oldZX80, see Appendix on pages 228-9 for BASIC representations. Two unit steps are computed, a_diagonal_step - one pixelup or down and one left or right - and a_square_step, one pixel vertically or horizontally only. If Y, the vertical displacementin drawing the line, is greater in absolute value than X, the horizontal one, the square step is made vertical, if X is greater it is made horizontal. If Y is positive the step or steps are made to go positive/upwards and if X is positive they are made to go to the positive/right, otherwise downwards and/orto the left. Each position is now looked at successively to see whichunit step, diagonal or square, will move closer to the end pointof the line, and a pixel is plotted accordingly. The total number T of steps to be taken is the larger of the absolute values of X and Y; because both the diagonal and the square stepmove in the "larger" direction. The number D of_diagonal steps will be the smaller of these absolute values; because only the diagonal steps move in the "smaller" direction. If T = D all thesteps will be diagonal. T steps are taken with a "step indicator", adjusted for each step, which signals whether a diagonal or square step is tobe taken. The step indicator: - is set at T/2 initially - is incremented on each step by D - if this makes it bigger than T, signals a diagonal step; if not, a square step - on each_diagonal step is decremented by T. After d diagonal and s square steps have been taken the indicator will hold T/2 ; initial value plus D(d + s) ; D added on every step minus T * d ; T deducted on diagonal steps = D(d + s) - T(d - 1/2) All these numbers are positive integers. If this result is more than T, ie if D(d + s) - T(d - 1/2) >= T, then D(d + s) >= T(d + 1/2), so D(d + s) > dT, or, dividing both sides by Dd, s T 1 + --- > --- d D But if the step indicator is less than T, a very similarcalculation will show that s T D(d + s) <= dT, or 1 + --- <= --- d D T/D represents the slope of the line from its start to its finish, and each plot must stay as close to this slope as possible: in the first case we want to reduce s/d, so we increase d by taking a diagonal step; in the second we want to increase s/d, so we increase s by taking a square step. QED! The routine could be called from m/c, but beware! the registers are switched to alternates on every turn of the step- drawing loop; if there is an odd number of PLOTs they will be switched on exit. Input parameters: 5C7D COORDS holds the coordinates X0, Y0 of the last plot on screen - the DRAW operands X, Y are the last two values on the calculator stack, their first bytes addressed by HL and DE respectively. Action: call 2307 STK TO BC, which puts ABS Y (hi) and ABS X (lo) in BC, and SGN Y (hi) and SGN X (lo) in DE; DE thus represents a diagonal unit step in the correct directions - if ABS X is greater than or equal to ABS Y jump on to DL X GE Y - (ABS Y is greater, so the square step is vertical) putABS X in L; this is the number of diagonal steps - stack the diagonal step - put zero in E, making DE a vertical square step in thecorrect direction - jump on to DL LARGER. _24C4_DL_X_GE_Y (ABS X >= ABS Y; the square step is horizontal): if X and Y are both zero, return; there is no line - put ABS Y in L (the number of diagonal steps) and ABS X in B (the number of all steps) - stack the diagonal step; it couldn't have been done before the jump, because of the RET - put zero in D, making DE a horizontal square step in the correct direction. _24CB_DL_LARGER: put B in H; now HL holds the total number of steps T in H and the number of diagonal steps D in L - make a byte from B shifted one bit to the right; the step indicator INT (T/2). _24CE_D_L_LOOP: add D to the step indicator - if there is carry jump on to D L DIAG; take a diagonalstep. The apparent complication of the increment producing carryis self-correcting: such an increment is certainly big enough tocall for a diagonal step, and then subtracting T, which is bigger than D, comes out the same as if there had been no carry - if the step indicator is now < T jump on to D L HR VT;take a square step. _24D4_D_L_DIAG: decrement the step indicator by T - copy the diagonal step on the stack into B'C' - jump on to D L STEP with the alternate registers. _24DB_D_L_HR_VT: save the step indicator as it is - put the square step in B'C'.