Skip to navigation


Drawing lines: HLOIN

[Elite-A, Flight]

Name: HLOIN [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a horizontal line from (X1, Y1) to (X2, Y1) Deep dive: Drawing monochrome pixels on the BBC Micro
Context: See this subroutine in context in the source code References: This subroutine is called as follows: * NLIN2 calls HLOIN * SUN (Part 3 of 4) calls HLOIN * TT15 calls HLOIN * TTX66 calls HLOIN

We do not draw a pixel at the right end of the line.
Returns: Y Y is preserved
.HLOIN STY YSAV \ Store Y into YSAV, so we can preserve it across the \ call to this subroutine LDX X1 \ Set X = X1 CPX X2 \ If X1 = X2 then the start and end points are the same, BEQ HL6 \ so return from the subroutine (as HL6 contains an RTS) BCC HL5 \ If X1 < X2, jump to HL5 to skip the following code, as \ (X1, Y1) is already the left point LDA X2 \ Swap the values of X1 and X2, so we know that (X1, Y1) STA X1 \ is on the left and (X2, Y1) is on the right STX X2 TAX \ Set X = X1 .HL5 DEC X2 \ Decrement X2 so we do not draw a pixel at the end \ point LDA Y1 \ Set A = Y1 / 8, so A now contains the character row LSR A \ that will contain our horizontal line LSR A LSR A ORA #&60 \ As A < 32, this effectively adds &60 to A, which gives \ us the screen address of the character row (as each \ character row takes up 256 bytes, and the first \ character row is at screen address &6000, or page &60) STA SCH \ Store the page number of the character row in SCH, so \ the high byte of SC is set correctly for drawing our \ line LDA Y1 \ Set A = Y1 mod 8, which is the pixel row within the AND #7 \ character block at which we want to draw our line (as \ each character block has 8 rows) STA SC \ Store this value in SC, so SC(1 0) now contains the \ screen address of the far left end (x-coordinate = 0) \ of the horizontal pixel row that we want to draw our \ horizontal line on TXA \ Set Y = bits 3-7 of X1 AND #%11111000 TAY .HL1 TXA \ Set T = bits 3-7 of X1, which will contain the AND #%11111000 \ character number of the start of the line * 8 STA T LDA X2 \ Set A = bits 3-7 of X2, which will contain the AND #%11111000 \ character number of the end of the line * 8 SEC \ Set A = A - T, which will contain the number of SBC T \ character blocks we need to fill - 1 * 8 BEQ HL2 \ If A = 0 then the start and end character blocks are \ the same, so the whole line fits within one block, so \ jump down to HL2 to draw the line \ Otherwise the line spans multiple characters, so we \ start with the left character, then do any characters \ in the middle, and finish with the right character LSR A \ Set R = A / 8, so R now contains the number of LSR A \ character blocks we need to fill - 1 LSR A STA R LDA X1 \ Set X = X1 mod 8, which is the horizontal pixel number AND #7 \ within the character block where the line starts (as TAX \ each pixel line in the character block is 8 pixels \ wide) LDA TWFR,X \ Fetch a ready-made byte with X pixels filled in at the \ right end of the byte (so the filled pixels start at \ point X and go all the way to the end of the byte), \ which is the shape we want for the left end of the \ line EOR (SC),Y \ Store this into screen memory at SC(1 0), using EOR STA (SC),Y \ logic so it merges with whatever is already on-screen, \ so we have now drawn the line's left cap TYA \ Set Y = Y + 8 so (SC),Y points to the next character ADC #8 \ block along, on the same pixel row as before TAY LDX R \ Fetch the number of character blocks we need to fill \ from R DEX \ Decrement the number of character blocks in X BEQ HL3 \ If X = 0 then we only have the last block to do (i.e. \ the right cap), so jump down to HL3 to draw it CLC \ Otherwise clear the C flag so we can do some additions \ while we draw the character blocks with full-width \ lines in them .HLL1 LDA #%11111111 \ Store a full-width 8-pixel horizontal line in SC(1 0) EOR (SC),Y \ so that it draws the line on-screen, using EOR logic STA (SC),Y \ so it merges with whatever is already on-screen TYA \ Set Y = Y + 8 so (SC),Y points to the next character ADC #8 \ block along, on the same pixel row as before TAY DEX \ Decrement the number of character blocks in X BNE HLL1 \ Loop back to draw more full-width lines, if we have \ any more to draw .HL3 LDA X2 \ Now to draw the last character block at the right end AND #7 \ of the line, so set X = X2 mod 8, which is the TAX \ horizontal pixel number where the line ends LDA TWFL,X \ Fetch a ready-made byte with X pixels filled in at the \ left end of the byte (so the filled pixels start at \ the left edge and go up to point X), which is the \ shape we want for the right end of the line EOR (SC),Y \ Store this into screen memory at SC(1 0), using EOR STA (SC),Y \ logic so it merges with whatever is already on-screen, \ so we have now drawn the line's right cap LDY YSAV \ Restore Y from YSAV, so that it's preserved across the \ call to this subroutine RTS \ Return from the subroutine .HL2 \ If we get here then the entire horizontal line fits \ into one character block LDA X1 \ Set X = X1 mod 8, which is the horizontal pixel number AND #7 \ within the character block where the line starts (as TAX \ each pixel line in the character block is 8 pixels \ wide) LDA TWFR,X \ Fetch a ready-made byte with X pixels filled in at the STA T \ right end of the byte (so the filled pixels start at \ point X and go all the way to the end of the byte) LDA X2 \ Set X = X2 mod 8, which is the horizontal pixel number AND #7 \ where the line ends TAX LDA TWFL,X \ Fetch a ready-made byte with X pixels filled in at the \ left end of the byte (so the filled pixels start at \ the left edge and go up to point X) AND T \ We now have two bytes, one (T) containing pixels from \ the starting point X1 onwards, and the other (A) \ containing pixels up to the end point at X2, so we can \ get the actual line we want to draw by AND'ing them \ together. For example, if we want to draw a line from \ point 2 to point 5 (within the row of 8 pixels \ numbered from 0 to 7), we would have this: \ \ T = %00111111 \ A = %11111100 \ T AND A = %00111100 \ \ So we can stick T AND A in screen memory to get the \ line we want, which is what we do here by setting \ A = A AND T EOR (SC),Y \ Store our horizontal line byte into screen memory at STA (SC),Y \ SC(1 0), using EOR logic so it merges with whatever is \ already on-screen LDY YSAV \ Restore Y from YSAV, so that it's preserved RTS \ Return from the subroutine