Skip to navigation

Elite on the BBC Micro and NES

Drawing lines: LOIN (Part 5 of 7)

[NES version, Bank 7]

Name: LOIN (Part 5 of 7) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a line: Line has a steep gradient, step up along y-axis Deep dive: Bresenham's line algorithm Drawing lines in the NES version
Context: See this subroutine in context in the source code References: No direct references to this subroutine in this source file

This routine draws a line from (X1, Y1) to (X2, Y2). It has multiple stages. If we get here, then: * |delta_y| >= |delta_x| * The line is closer to being vertical than horizontal * We are going to step up along the y-axis * We potentially swap coordinates to make sure Y1 >= Y2
.STPY LDY Y1 ; Set A = Y = Y1 TYA LDX X1 ; Set X = X1 CPY Y2 ; If Y1 = Y2, jump up to loin18 to return from the BEQ loin18 ; subroutine as there is no line to draw BCS LI15 ; If Y1 > Y2, jump down to LI15, as the coordinates are ; already in the order that we want DEC SWAP ; Otherwise decrement SWAP from 0 to $FF, to denote that ; we are swapping the coordinates around (though note ; that we don't use this value anywhere, as in the ; original versions of Elite it is used to omit the ; first pixel of each line, which we don't have to do ; in the NES version as it doesn't use EOR plotting) LDA X2 ; Swap the values of X1 and X2 STA X1 STX X2 TAX ; Set X = X1 LDA Y2 ; Swap the values of Y1 and Y2 STA Y1 STY Y2 TAY ; Set Y = A = Y1 .LI15 ; By this point we know the line is vertical-ish and ; Y1 >= Y2, so we're going from top to bottom as we go ; from Y1 to Y2 ; The following section calculates: ; ; P = P / Q ; = |delta_x| / |delta_y| ; ; using the log tables at logL and log to calculate: ; ; A = log(P) - log(Q) ; = log(|delta_x|) - log(|delta_y|) ; ; by first subtracting the low bytes of the logarithms ; from the table at LogL, and then subtracting the high ; bytes from the table at log, before applying the ; antilog to get the result of the division and putting ; it in P LDX P ; Set X = |delta_x| BEQ LIfudge ; If |delta_x| = 0, jump to LIfudge to return 0 as the ; result of the division LDA logL,X ; Set A = log(P) - log(Q) LDX Q ; = log(|delta_x|) - log(|delta_y|) SEC ; SBC logL,X ; by first subtracting the low bytes of log(P) - log(Q) BMI LIloG ; If A > 127, jump to LIloG LDX P ; And then subtracting the high bytes of log(P) - log(Q) LDA log,X ; so now A contains the high byte of log(P) - log(Q) LDX Q SBC log,X BCS LIlog3 ; If the subtraction fitted into one byte and didn't ; underflow, then log(P) - log(Q) < 256, so we jump to ; LIlog3 to return a result of 255 TAX ; Otherwise we set A to the A-th entry from the antilog LDA antilog,X ; table so the result of the division is now in A JMP LIlog2 ; Jump to LIlog2 to return the result .LIlog3 LDA #255 ; The division is very close to 1, so set A to the BNE LIlog2 ; closest possible answer to 256, i.e. 255, and jump to ; LIlog2 to return the result (this BNE is effectively a ; JMP as A is never zero) .LIfudge LDA #0 ; Set A = 0 and jump to LIlog2 to return 0 as the result BEQ LIlog2 ; (this BNE is effectively a JMP as A is always zero) .LIloG LDX P ; Subtract the high bytes of log(P) - log(Q) so now A LDA log,X ; contains the high byte of log(P) - log(Q) LDX Q SBC log,X BCS LIlog3 ; If the subtraction fitted into one byte and didn't ; underflow, then log(P) - log(Q) < 256, so we jump to ; LIlog3 to return a result of 255 TAX ; Otherwise we set A to the A-th entry from the LDA antilogODD,X ; antilogODD so the result of the division is now in A .LIlog2 STA P ; Store the result of the division in P, so we have: ; ; P = |delta_x| / |delta_y| LDA X1 ; Set SC2(1 0) = (nameBufferHi 0) + yLookup(Y) + X1 / 8 LSR A ; LSR A ; where yLookup(Y) uses the (yLookupHi yLookupLo) table LSR A ; to convert the pixel y-coordinate in Y into the number CLC ; of the first tile on the row containing the pixel ADC yLookupLo,Y ; STA SC2 ; Adding nameBufferHi and X1 / 8 therefore sets SC2(1 0) LDA nameBufferHi ; to the address of the entry in the nametable buffer ADC yLookupHi,Y ; that contains the tile number for the tile containing STA SC2+1 ; the pixel at (X1, Y), i.e. the line we are drawing TYA ; Set Y = Y mod 8, which is the pixel row within the AND #7 ; character block at which we want to draw the start of TAY ; our line (as each character block has 8 rows) SEC ; Set A = X2 - X1 LDA X2 ; SBC X1 ; This sets the C flag when X1 <= X2 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 TWOS,X ; Fetch a 1-pixel byte from TWOS where pixel X is set STA R ; Store the pixel byte in R LDX Q ; Set X = Q + 1 INX ; = |delta_y| + 1 ; ; We will use Q as the y-axis counter, and we add 1 to ; ensure we include the pixel at each end BCS loin24 ; If X1 <= X2 (which we calculated above) then jump to ; loin24 to draw the line to the left and up JMP loin36 ; If we get here then X1 > X2, so jump to loin36, as we ; need to draw the line to the left and down