This code appears in the following versions (click to see it in the source code):
Code variations between these versions are shown below.
Type: Subroutine Category: Text Summary: Print a character at the text cursor by poking into screen memory Deep dive: Drawing text
Print a character at the text cursor (XC, YC), do a beep, print a newline, or delete left (backspace).
Arguments: A The character to be printed. Can be one of the following: * 7 (beep)
* 32-95 (ASCII capital letters, numbers and punctuation) * 127 (delete the character to the left of the text cursor and move the cursor to the left) XC Contains the text column to print at (the x-coordinate) YC Contains the line number to print on (the y-coordinate)
Returns: A A is preserved X X is preserved Y Y is preserved
C flag The C flag is cleared
Other entry points:
This variation is blank in the Disc (flight), Disc (docked), 6502 Second Processor, Master and Electron versions.
RR3+1 Contains an RTS
RREN Prints the character definition pointed to by P(2 1) at the screen address pointed to by (A SC). Used by the BULB routine
This variation is blank in the Disc (docked), 6502 Second Processor and Master versions.
rT9 Contains an RTS
This variation is blank in the Cassette, Disc (flight), 6502 Second Processor, Master and Electron versions.
R5-1 Contains an RTS
This variation is blank in the Cassette, Disc (flight), Disc (docked) and Electron versions.
Other entry points: RR4 Restore the registers and return from the subroutine
This variation is blank in the Cassette, Disc (flight), 6502 Second Processor, Master and Electron versions.
LDY QQ17 \ Load the QQ17 flag, which contains the text printing \ flags
This variation is blank in the 6502 Second Processor version.
Tap on a block to expand it, and tap it again to revert.
The Master Compact release prints the disc catalogue using different logic, as the Compact uses ADFS rather than DFS.
See below for more variations related to this code.
This variation is blank in the Cassette, Disc (flight), Disc (docked), 6502 Second Processor and Electron versions.
This variation is blank in the Cassette, Disc (flight), Disc (docked), 6502 Second Processor and Electron versions.
LDY #%00001111 \ Set bits 1 and 2 of the Access Control Register at STY VIA+&34 \ SHEILA &34 to switch screen memory into &3000-&7FFF
Code variation 18 of 53
See variation 16 above for details.
This variation is blank in the Cassette, Disc (flight) and Electron versions.
Tap on a block to expand it, and tap it again to revert.
The advanced versions support an extra control code in the standard text token system: control code 11 clears the screen.
This variation is blank in the Cassette, Disc (flight), Disc (docked) and Electron versions.
CMP #11 \ If this is control code 11 (clear screen), jump to cls BEQ cls \ to clear the top part of the screen, draw a white \ border and return from the subroutine via RR4
CMP #32 \ If this is an ASCII character (A >= 32), jump to RR1 BCS RR1 \ below, which will print the character, restore the \ registers and return from the subroutine CMP #10 \ If this is control code 10 (line feed) then jump to BEQ RRX1 \ RRX1, which will move down a line, restore the \ registers and return from the subroutine
The cassette version uses control code 12 for a newline, while the other versions use 13.
Tap on a block to expand it, and tap it again to revert.
\ which we can achieve by moving the text cursor \ to the start of the line (carriage return) and down \ one line (line feed). These two lines do the first \ bit by setting XC = 1, and we then fall through into \ the line feed routine that's used by control code 10
This variation is blank in the Cassette, Disc (flight), 6502 Second Processor, Master and Electron versions.
CMP #13 \ If this is control code 13 (carriage return) then jump BEQ RR4 \ RR4 to restore the registers and return from the \ subroutine
.RRX1
.RR1 \ If we get here, then the character to print is an \ ASCII character in the range 32-95. The quickest way \ to display text on-screen is to poke the character \ pixel by pixel, directly into screen memory, so \ that's what the rest of this routine does \ \ The first step, then, is to get hold of the bitmap \ definition for the character we want to draw on the \ screen (i.e. we need the pixel shape of this \ character). The MOS ROM contains bitmap definitions \ of the system's ASCII characters, starting from &C000 \ for space (ASCII 32) and ending with the £ symbol \ (ASCII 126)
This variation is blank in the Cassette, Disc (flight), Disc (docked), Master and Electron versions.
\ \ To save time looking this information up from the MOS \ ROM a copy of these bitmap definitions is embedded \ into this source code at page FONT%, so page 0 of the \ font is at FONT%, page 1 is at FONT%+1, and page 2 at \ FONT%+3
\ \ There are definitions for 32 characters in each of the \ three pages of MOS memory, as each definition takes up \ 8 bytes (8 rows of 8 pixels) and 32 * 8 = 256 bytes = \ 1 page. So: \ \ ASCII 32-63 are defined in &C000-&C0FF (page 0) \ ASCII 64-95 are defined in &C100-&C1FF (page 1) \ ASCII 96-126 are defined in &C200-&C2F0 (page 2) \ \ The following code reads the relevant character
This variation is blank in the Master version.
Tap on a block to expand it, and tap it again to revert.
\ those values into the correct position in screen \ memory, thus printing the character on-screen \ \ It's a long way from 10 PRINT "Hello world!":GOTO 10
This variation is blank in the Disc (flight), Disc (docked), 6502 Second Processor, Master and Electron versions.
\LDX #LO(K3) \ These instructions are commented out in the original \INX \ source, but they call OSWORD 10, which reads the \STX P+1 \ character bitmap for the character number in K3 and \DEX \ stores it in the block at K3+1, while also setting \LDY #HI(K3) \ P+1 to point to the character definition. This is \STY P+2 \ exactly what the following uncommented code does, \LDA #10 \ just without calling OSWORD. Presumably the code \JSR OSWORD \ below is faster than using the system call, as this \ version takes up 15 bytes, while the version below \ (which ends with STA P+1 and SYX P+2) is 17 bytes. \ Every efficiency saving helps, especially as this \ routine is run each time the game prints a character \ \ If you want to switch this code back on, uncomment \ the above block, and comment out the code below from \ TAY to STX P+2. You will also need to uncomment the \ LDA YC instruction a few lines down (in RR2), just to \ make sure the rest of the code doesn't shift in \ memory. To be honest I can't see a massive difference \ in speed, but there you go
This variation is blank in the Disc (flight), Disc (docked) and Master versions.
TAY \ Copy the character number from A to Y, as we are \ about to pull A apart to work out where this \ character definition lives in memory
\ Now we want to set X to point to the relevant page
This variation is blank in the Master version.
Tap on a block to expand it, and tap it again to revert.
\ The following logic is easier to follow if we look \ at the three character number ranges in binary: \ \ Bit # 76543210 \ \ 32 = %00100000 Page 0 of bitmap definitions \ 63 = %00111111 \ \ 64 = %01000000 Page 1 of bitmap definitions \ 95 = %01011111 \ \ 96 = %01100000 Page 2 of bitmap definitions \ 125 = %01111101 \ \ We'll refer to this below
This variation is blank in the Cassette, Disc (flight), Disc (docked), Master and Electron versions.
\BEQ RR4 \ This instruction is commented out in the original \ source, but it would return from the subroutine if A \ is zero BPL P%+5 \ If the character number is positive (i.e. A < 128) \ then skip the following instruction JMP RR4 \ A >= 128, so jump to RR4 to restore the registers and \ return from the subroutine using a tail call
ASL A \ If bit 6 of the character is clear (A is 32-63) ASL A \ then skip the following instruction BCC P%+4
ASL A \ If bit 5 of the character is clear (A is 64-95) BCC P%+3 \ then skip the following instruction INX \ Increment X \
This variation is blank in the Master version.
Tap on a block to expand it, and tap it again to revert.
\ In other words, X points to the relevant page. But \ what about the value of A? That gets shifted to the \ left three times during the above code, which \ multiplies the number by 8 but also drops bits 7, 6 \ and 5 in the process. Look at the above binary \ figures and you can see that if we cleared bits 5-7, \ then that would change 32-53 to 0-31... but it would \ do exactly the same to 64-95 and 96-125. And because \ we also multiply this figure by 8, A now points to \ the start of the character's definition within its \ page (because there are 8 bytes per character \ definition) \ \ Or, to put it another way, X contains the high byte \ (the page) of the address of the definition that we \ want, while A contains the low byte (the offset into \ the page) of the address
This variation is blank in the Cassette, Disc (flight), Disc (docked), 6502 Second Processor and Master versions.
LDA #128 \ Set SC = 128 for use in the calculation below STA SC LDA YC \ If YC < 24 then we are in the top part of the screen, CMP #24 \ so skip the following two instructions BCC P%+8 JSR TTX66 \ We are off the bottom of the screen, so we don't want \ to print anything, so first clear the screen and draw \ a white border JMP RR4 \ Jump to RR4 to restore the registers and return from \ the subroutine \ The text row is on-screen, so now to calculate the \ screen address we need to write to, as follows: \ \ SC = &5800 + (char row * 256) + (char row * 64) + 32 \ \ See the deep dive on "Drawing pixels in the Electron \ version" for details LSR A \ Set (A SC) = (A SC) / 4 ROR SC \ = (4 * ((char row * 64) + 32)) / 4 LSR A \ = char row * 64 + 32 ROR SC ADC YC \ Set SC(1 0) = (A SC) + (YC 0) + &5800 ADC #&58 \ = (char row * 64 + 32) STA SC+1 \ + char row * 256 \ + &5800 \ \ which is what we want, so SC(1 0) contains the address \ of the first visible pixel on the character row we \ want
LDA XC \ Fetch XC, the x-coordinate (column) of the text cursor \ into A
The standard disc catalogue is just too wide to fit into Elite's special square screen mode, so when printing the catalogue in the enhanced versions, a space is removed from column 17, which is always a blank column in the middle of the catalogue.
This variation is blank in the Cassette, Disc (flight) and Electron versions.
Tap on a block to expand it, and tap it again to revert.
This variation is blank in the Cassette, Disc (docked), 6502 Second Processor, Master and Electron versions.
INC XC \ Move the text cursor to the right by 1 column
LDA YC \ Fetch YC, the y-coordinate (row) of the text cursor
CPY #127 \ If the character number (which is in Y) <> 127, then BNE RR2 \ skip to RR2 to print that character, otherwise this is \ the delete character, so continue on DEC XC \ We want to delete the character to the left of the \ text cursor and move the cursor back one, so let's \ do that by decrementing YC. Note that this doesn't \ have anything to do with the actual deletion below, \ we're just updating the cursor so it's in the right \ position following the deletion
This variation is blank in the Disc (flight) version.
Tap on a block to expand it, and tap it again to revert.
This variation is blank in the Disc (flight) version.
Tap on a block to expand it, and tap it again to revert.
BEQ RR4 \ We are done deleting, so restore the registers and \ return from the subroutine (this BNE is effectively \ a JMP as ZES2 always returns with the Z flag set) .RR2 \ Now to actually print the character INC XC \ Once we print the character, we want to move the text \ cursor to the right, so we do this by incrementing \ XC. Note that this doesn't have anything to do \ with the actual printing below, we're just updating \ the cursor so it's in the right position following \ the print
This variation is blank in the Disc (flight), Disc (docked), 6502 Second Processor, Master and Electron versions.
\LDA YC \ This instruction is commented out in the original \ source. It isn't required because we only just did a \ LDA YC before jumping to RR2, so this is presumably \ an example of the authors squeezing the code to save \ 2 bytes and 3 cycles \ \ If you want to re-enable the commented block near the \ start of this routine, you should uncomment this \ instruction as well
This variation is blank in the Electron version.
Tap on a block to expand it, and tap it again to revert.
.RR3 \ A contains the value of YC - the screen row where we \ want to print this character - so now we need to \ convert this into a screen address, so we can poke \ the character data to the right place in screen \ memory
This variation is blank in the Electron version.
Tap on a block to expand it, and tap it again to revert.
.RREN STA SC+1 \ Store the page number of the destination screen \ location in SC+1, so SC now points to the full screen \ location where this character should go
This variation is blank in the Cassette, Disc (flight), Disc (docked) and Electron versions.
Tap on a block to expand it, and tap it again to revert.
LDY #7 \ We want to print the 8 bytes of character data to the \ screen (one byte per row), so set up a counter in Y \ to count these bytes .RRL1
When we are docked in the disc version, we don't need to worry about displaying text on the space view, so we don't have to implement EOR logic when printing, and instead can use OR logic.
Tap on a block to expand it, and tap it again to revert.
STA (SC),Y \ Store the Y-th byte at the screen address for this \ character location
This variation is blank in the Cassette, Disc (flight), Disc (docked) and Electron versions.
Tap on a block to expand it, and tap it again to revert.
DEY \ Decrement the loop counter BPL RRL1 \ Loop back for the next byte to print to the screen .RR4
RTS \ Return from the subroutine .R5