PRINT A FLOATING POINT NUMBER SUBROUTINE see 2DE3 PRINT FP PRINT ALL CHARACTERS SUBROUTINE see 0B7F PR ALL (0B24 PO ANY) PRINT ANY CHARACTERS SUBROUTINE see 0B24 PO ANY PRINT A QUESTION MARK SUBROUTINE see 0A69 PO QUEST PRINT A WHOLE BASIC LINE SUBROUTINE see 1855 OUT LINE PRINT A 1 subroutine (restart) 0010 Sends a single byte to output. The output routine of the Spectrum system; although we usually call it "printing", what actually happens to the bytes output depends on the channel selected at the moment. Channel S will put them on the main screen, channel K into the lower screen, channel P will send them to the ZX printer, channel R will copy them to the work space; other channels selected through Interface 1 etc will send them to a network, to a DATA file on Microdrive, etc. Even when printing to the screen, thereis an important distinction between this output routine and the PRINT command routine, see under_printing. The routine makes in effect a "computed GO SUB" call by going into CALL SUB with a pointer to the current output addressfrom the channel data, which will be 09F4 PRINT OUT for channels 'K', 'S' and 'P' 150F ADD CHAR for channel 'R'; only available to ROM See 15AF INITIAL CHANNEL INFORMATION. The output subroutine address is pointed to by 5C51 CURCHL; from m/c, though not from BASIC, you can put any address you like in this system variable, and this is no doubt how Interface 1 and other peripherals manipulate the output of the Spectrum. PRINT A 1 operates with the alternate registers, and as a consequence cannot be called "recursively", ie from within itsown routines, without getting the main registers back and probably corrupting them: this is handled by calling 0C3B PO SAVE instead of PRINT A 1 whenever recursive printing is required, see the index entry. Input parameters: A holds the byte to be output. Action: jump on to PRINT A 2; only eight bytes are available for a restart. [The five bytes of PRINT A 2 could wellhave been put here, followed by a jump to CALL SUB.] _15F2_PRINT_A_2: save all the main registers by EXX - put HL' on the stack; very often the vital return address from calculator operations - get a pointer to the current channel output routine address in 5C51 CURCHL _15F7_CALL_SUB: read the address from the channel information - call 162C CALL JUMP, which is just a JP (HL); thus in effect it calls the subroutine addressed in 5C51 CURCHL - restore the registers. Exit: RET, from 15F7 CALL SUB. Output parameters: none - the main registers, provided the output routine doesn't use EXX, and HL' are saved, but not the A register. Called from: 07AD LD CH PR (twice) 0C3B PO SAVE 133C MAIN 5 (twice) 1835 LIST ALL 1865 OUT LINE1 196D OUT CH 3 1FF5 PRINT CR 201E PR AT TAB (3 times) 203C PR STRING 204E PR POSN 1 21FC CO TEMP 4 (twice) 2DE3 PRINT FP 2DF2 PF NEGTVE 2F64 PF DEC 0$ 2F6C PF E FRMT 2F85 PF E SGN PRINT A 2 15F2 (0010 PRINT A 1) Jumps from: 0010 PRINT A 1 print buffer (ad hoc) see 2DE3 PRINT FP PRINT comma (06) see control characters PRINT COMMA SUBROUTINE see 0A5F PO COMMA PRINT CR subroutine 1FF5 See 1FCD PRINT. Called from: 2061 PR POSN 2 Exit from: 1FF2 PRINT 4 printer (ZX printer), printer buffer, printer 'column', printer motor, printer position Output to the ZX printer is executed by opening channel P and using 0010 PRINT A 1, which calls 09F4 PRINT OUT. The pixels picked up by 0B65 PO CHAR and following are output by 0BD3 PR ALL 6 to the print column position kept in 5C80 PR CC, in the print buffer, instead of to the screen. For the OUT commands of 0EF4 COPY LINE etc used to operate the motor and stylus, see ports. The ZX printer is much more a toy printer than the Spectrum is a toy computer. Many more sophisticated, but more expensive, printers can be bought, with interfaces which divert the output of channel P to their own routines, so that LPRINT, LLIST and even COPY are effective with them. The print buffer is a blank space of 100h/256d bytes at 5B00h/23296d in RAM; enough for 20h/32d characters each of 8 pixels. It is undisturbed by all operations of the Spectrum except those concerned with the ZX printer, which leaves it available for m/c programs not using the printer; a good place to keep variables, flags etc. If IX is placed at 5B7Fh, all its bytes are accessible to the IX indexing system. Every time the buffer is filled, or a newline is output,its bits are output to the printer by 0ECD COPY BUFF, and it is blanked to zeroes by 0EDF CLEAR PRB; this is also done by 1303 MAIN 4 on completion of any command, even if the buffer isn't full, providing bit 2 of FLAGS2 signals it has been used. 09F4 PRINT OUT handles output to printer or screen 0A23 PO BACK 1 cannot backspace to last line on printer 0A4F PO ENTER carriage return clears print buffer 0A87 PO CONT line numbers ignored in output to printer 0ADC PO STORE position values read differently 0AFC PO ST PR stores position values for printer 0B03 PO FETCH position values read differently 0B1D PO F PR fetches position values for printer 0B7F PR ALL print buffer cleared after 20h characters 0BA4 PR ALL 2 flags signalling use of printer 0BB7 PR ALL 4 pixel prepared for in print buffer 0BD3 PR ALL 6 pixel put in print buffer 0C55 PO SCR skipped when outputting to printer 0DD9 CL SET prepares position value for PO STORE 0ECD COPY BUFF copies eight pixel lines to printer 0EDA COPY END stops motor 0EDF CLEAR PRB prepares to blank printer buffer 0EE7 PRB BYTES blanks printer buffer 0EF4 COPY LINE slows motor for last two lines 0EFD COPY L 1 handles BREAK during line printing 0F0C COPY L 2 checks that printer present and ready 0F1E COPY L 5 outputs bits to printer 1219 RAM SET clears printer buffer 1303 MAIN 4 empties printer buffer after execution 1646 CHAN S 1 signals "not using printer" 164D CHAN P signals "using printer" print format for numbers see 2DE3 PRINT FP below PRINT FP subroutine 2DE3 Part of the PRINT command routine: converts the last value on the calculator stack, which can be any kind of number handled by the floating-point calculator, to the standard_14- _character_number_format, and outputs this version to screen, printer etc, through the current output channel. For the format, see page 46 of the old Spectrum handbook, page 59-60 of the Plus 2 book; or try this demo program: 100 LET x=EXP 1: REM the number e, 2.718281828 200 PRINT 1/x/100, x: LET x = -10*x: GO TO 200 Another demonstration program is given on page 167 of "ROM Disassembled". You will observe that when numbers are shown on screen, - only 8 significant figures are shown - numbers greater than 100,000,000d or less than 0.00001dare shown in E format - numbers greater than 10**38 or less than 10**-39 cause over- or underflow. Thus the maximum number of characters required to outputany number is 8 digits + 1 decimal point + 1 minus sign + 4 (eg "E-38"), 14 characters in all. Most of the subroutine deals only with positive values; if the number is negative, a minus sign is printed and the subroutine continues with the absolute value of the number. Nine significant digits of the number to be output are first compiled in the calculator memory's mem-3 and mem-4, the ten bytes starting at 5CA1h/23713d, and two counters are set up in mem-5; mem-3 to mem-5 are called the_ad_hoc_print_buffer, nothing to do with the main print buffer, see_printer. The counters are - the significant figures counter: the overall number of digits to be output excluding leading or trailing zeroes - the magnitude counter: a positive number shows the number of digits before the decimal point, a negative number thenumber of_leading_zeroes, ie zeroes after the decimal point and before the significant figures of the number. The magnitude counter is calculated from the logarithm of X to the base ten, using its integer part only: ie INT log X,or strictly speaking INT ABS log X. If X has N digits before thedecimal point, N must be between 10**(N - 1) and 10**N; for example 69.7 is between 10**1 = 10 and 10**2 = 100. So log X is between N - 1 and N; log 69.7 is 1.8432328. If X has N leading zeroes after the decimal point, N must be between 10**-(N + 1) and 10**-N; for example 0.00697 is between 10**-3 = 0.001 and 10**-2 = 0.01. Now ABS log X is between N and N + 1; log 0.00697 is -3 + 0.8432328, so ABS log 0.00697 is 2.1567672. The log calculations are performed by calls to LOG (2**A), which works from the binary exponent B of X. For a number X greater than one, B is positive or zero, and the logarithm of X to the base 2 is B plus a fraction; then log X tothe base ten is B * log 2 to base 10 = 0.3010d * B plus a fraction; see under the subroutine LOG(2**A). For X less than one, B is negative, but the essential calculation is the same. Actually the routine sometimes works with the normalized versionof B, but the effect is as described here. LOG (2**A) gives results which are often one off, because they take no account of the fractions; the adjustment required is usually plus one for positive B, minus one for negative, but this is simplified by using B + 2 for negative B, so the later correction is always in the same direction. The nine digits are assembled by PF LOOP to PF FR EXX, rounded to eight by PF ROUND to PF R BACK and output from the buffer by PF E SBRN and following. If the number is less than one, its representation should have an_initial_zero before the decimal point; the routine fails to output this in some cases, see PF NOT E below. The subroutine can readily be used by m/c programmers; its length and complexity make this extremely worth while! However, much simpler and faster routines can be devised for outputting restricted classes of numbers, eg integers or positive numbers only; there are some in the ROM. Much of the complexity is the consequence of the restriction to 14d output characters. Input parameters: none - the number X to be output is on the calculator stack. Action: use the less-0 and greater-0 routines of the calculator to check the sign of X - if X is negative, jump on to PF NEGTVE - if positive to PF POSTVE - (that leaves zero) output 30h "zero" and return. _2DF2_PF_NEGTVE: use the calculator to replace X by ABS X - output 2D -. _2DF8_PF_POSTVE: put zeroes in mem-3, mem-4 and mem-5; this creates an ad hoc print buffer of 15d bytes in the calculator memory area - save HL'; this routine is called by the calculator, from 361F str$, and using it "recursively" could corrupt the saved return address in HL'. _2E01_PF_LOOP: use the calculator to split X into an integer part iX and a fractional part fX - if the exponent byte of iX isn't zero jump on to PF LARGE; iX is more than 10000h/65536d, otherwise 36AF int would return it in small integer form, with zero exponent byte - (iX is a small integer) make a bit counter 10h/16d forthe binary digits of iX - call 2D7F INT FETCH to get iX into DE - if its hi byte isn't zero jump on to PF SAVE - (hi byte is zero) if both its bytes are zero jump on to PF SMALL; iX is zero, X is a pure fraction - (iX is a one-byte integer) move the lo byte to the hi byte of the register - reduce the bit counter to 08. _2E1E_PF_SAVE: save iX in the alternate registers - jump on to PF BITS. _2E24_PF_SMALL (iX is zero, ie X is a pure fraction) readthe exponent of fX [an error here: the zero iX ought to have been cleared from the stack before proceeding. For its unfortunate effects see under 361F str$] - subtract 7Eh/126d; this makes B + 2, two more than thenormalized binary exponent of fX. B is negative, ie a byte 01 ->7F, because X is a pure fraction, so B + 2 is 03 -> 81h - call 2DC1 LOG(2**A) which returns the number N of leading zeroes required after the decimal point - subtract N from the twelfth byte of the ad hoc print buffer, mem-5 byte 2; it was zeroed at the start of the routine,but if this isn't the first turn round from PF LOOP there may bea number in it. This is the magnitude counter, now zero or negative, indicating zeroes after the decimal point - call 2D4F E TO FP to get fX * 10**N from the last value on the stack fX and N in the A register; this ought to be fX decimal shifted left until it has no leading zeroes, but N may be one too large. The notes call it y - use the calculator to separate y into its integer and fractional parts; the notes call them i2 and f2. If N was too large i2 won't be zero, but it can only be a single decimal digit - put i2 in the first byte of the print buffer; it will be the first to be output after the leading zeroes, unless it iszero - decrement it, shift its hi bit into the carry, then subtract it from itself with carry and increment it again. This produces zero from zero: 00 -> FF -> FE with carry -> FF -> 00 and one from anything else: X -> (X - 1) -> 2(X - 1) without carry -> 00 -> 01 Doubling X - 1 can't produce carry when X is just a decimal digit - put this zero or one in byte 11d of the buffer, the significant figures counter, and add it to the number in the magnitude counter, which presumably is negative - jump on to PF FRACTN with a pointer to f2, the last value on the stack. _2E56_PF_LARGE (X is in full FP format, and so more than 65535d): take 80h/128d from the exponent of iX; now a true exponent - if this leaves less than 1Ch/28d jump on to PF MEDIUM - (iX is more than 2**27 = 134,217,728d, nine or more decimal digits) call 2DC1 LOG(2**A) to get the number N of decimal digits; 9 -> 38d - subtract seven; N - 7 - add the result to the magnitude counter - negate it; now -(N - 7) - call 2D4F E TO FP to get iX/10**(N - 7) on the stack from iX on the stack and -(N - 7) in A; this is a number, probably not an integer, of eight or possibly seven decimal digits - jump back to PF LOOP to go round again; the reduced number will be split again into integer and fractional parts. The old fractional part gets lost, but it couldn't be output in the 14-character format anyway.