.PIXEL LDY #0 \ Set Q to byte #0 from the block pointed to by OSSC, LDA (OSSC),Y \ which contains the size of the pixel buffer STA Q INY \ Increment Y to 2, so y now points at the data for the INY \ first pixel in the command block .PXLO LDA (OSSC),Y \ Set P to byte #2 from the Y-th pixel block in OSSC, STA P \ which contains the point's distance value (ZZ) AND #7 \ If ZZ is a multiple of 8 (which will be the case for BEQ PX5 \ pixels sent by the parasite's PIXEL routine), jump to \ PX5 to draw stardust particles and dots on the \ Long-range Chart \ Otherwise this pixel was sent by the parasite's PIXEL3 \ routine and will have an odd value of ZZ, and we use \ the distance value to determine the dot's colour and \ size, as this is an explosion particle TAX \ Set S to the ZZ-th value from the PXCL table, to get LDA PXCL,X \ the correct colour byte for this pixel, depending on STA S \ the distance INY \ Increment Y to 3 LDA (OSSC),Y \ Set X to byte #3 from the Y-th pixel block in OSSC, TAX \ which contains the pixel's x-coordinate INY \ Increment Y to 4 LDA (OSSC),Y \ Set Y to byte #4 from the Y-th pixel block in OSSC, STY T1 \ which contains the pixel's y-coordinate, and store Y, TAY \ the index of this pixel's y-coordinate, in T1, so we \ can restore it at the end of the subroutine LDA ylookup,Y \ Look up the page number of the character row that STA SC+1 \ contains the pixel with the y-coordinate in Y, and \ store it in the high byte of SC(1 0) at SC+1 TXA \ Each character block contains 8 pixel rows, so to get AND #%11111100 \ the address of the first byte in the character block ASL A \ that we need to draw into, as an offset from the start \ of the row, we clear bits 0-1 and shift left to double \ it (as each character row contains two pages of bytes, \ or 512 bytes, which cover 256 pixels). This also \ shifts bit 7 of the x-coordinate into the C flag STA SC \ Store the address of the character block in the low \ byte of SC(1 0), so now SC(1 0) points to the \ character block we need to draw into BCC P%+4 \ If the C flag is clear then skip the next instruction INC SC+1 \ The C flag is set, which means bit 7 of X1 was set \ before the ASL above, so the x-coordinate is in the \ right half of the screen (i.e. in the range 128-255). \ Each row takes up two pages in memory, so the right \ half is in the second page but SC+1 contains the value \ we looked up from ylookup, which is the page number of \ the first memory page for the row... so we need to \ increment SC+1 to point to the correct page TYA \ Set Y = Y mod 8, which is the pixel row within the AND #7 \ character block at which we want to draw the pixel TAY \ (as each character block has 8 rows) TXA \ Copy bits 0-1 of the x-coordinate to bits 0-1 of X, AND #%00000011 \ which will now be in the range 0-3, and will contain TAX \ the two pixels to show in the character row LDA P \ If the pixel's ZZ distance, which we stored in P, is BMI PX3 \ greater than 127, jump to PX3 to plot a one-pixel dot CMP #80 \ If the pixel's ZZ distance is < 80, then the dot is BCC PX2 \ pretty close, so jump to PX2 to draw a four-pixel \ square LDA TWOS2,X \ Fetch a mode 1 two-pixel byte with the pixels set as AND S \ in X, and AND with the colour byte we fetched into S \ so that pixel takes on the colour we want to draw \ (i.e. A is acting as a mask on the colour byte) EOR (SC),Y \ Draw the pixel on-screen using EOR logic, so we can STA (SC),Y \ remove it later without ruining the background that's \ already on-screen LDY T1 \ Set Y to the index of this pixel's y-coordinate byte \ in the command block, which we stored in T1 above INY \ Increment Y, so it now points to the first byte of \ the next pixel in the command block CPY Q \ If the index hasn't reached the value in Q (which BNE PXLO \ contains the size of the pixel buffer), loop back to \ PXLO to draw the next pixel in the buffer RTS \ Return from the subroutine .PX2 \ If we get here, we need to plot a four-pixel square in \ in the correct colour for this pixel's distance LDA TWOS2,X \ Fetch a mode 1 two-pixel byte with the pixels set as AND S \ in X, and AND with the colour byte we fetched into S \ so that pixel takes on the colour we want to draw \ (i.e. A is acting as a mask on the colour byte) EOR (SC),Y \ Draw the pixel on-screen using EOR logic, so we can STA (SC),Y \ remove it later without ruining the background that's \ already on-screen DEY \ Reduce Y by 1 to point to the pixel row above the one BPL P%+4 \ we just plotted, and if it is still positive, skip the \ next instruction LDY #1 \ Reducing Y by 1 made it negative, which means Y was \ 0 before we did the DEY above, so set Y to 1 to point \ to the pixel row after the one we just plotted \ We now draw our second dash LDA TWOS2,X \ Fetch a mode 1 two-pixel byte with the pixels set as AND S \ in X, and AND with the colour byte we fetched into S \ so that pixel takes on the colour we want to draw \ (i.e. A is acting as a mask on the colour byte) EOR (SC),Y \ Draw the pixel on-screen using EOR logic, so we can STA (SC),Y \ remove it later without ruining the background that's \ already on-screen LDY T1 \ Set Y to the index of this pixel's y-coordinate byte \ in the command block, which we stored in T1 above INY \ Increment Y, so it now points to the first byte of \ the next pixel in the command block CPY Q \ If the index hasn't reached the value in Q (which BNE PXLO \ contains the size of the pixel buffer), loop back to \ PXLO to draw the next pixel in the buffer RTS \ Return from the subroutine .PX3 \ If we get here, the dot is a long way away (at a \ distance that is > 127), so we want to draw a \ one-pixel dot LDA TWOS,X \ Fetch a mode 1 one-pixel byte with the pixel set as AND S \ in X, and AND with the colour byte we fetched into S \ so that pixel takes on the colour we want to draw \ (i.e. A is acting as a mask on the colour byte) EOR (SC),Y \ Draw the pixel on-screen using EOR logic, so we can STA (SC),Y \ remove it later without ruining the background that's \ already on-screen LDY T1 \ Set Y to the index of this pixel's y-coordinate byte \ in the command block, which we stored in T1 above INY \ Increment Y, so it now points to the first byte of \ the next pixel in the command block CPY Q \ If the index hasn't reached the value in Q (which BNE PXLO \ contains the size of the pixel buffer), loop back to \ PXLO to draw the next pixel in the buffer RTS \ Return from the subroutine .PX5 \ If we get here then the pixel's distance value (ZZ) is \ a multiple of 8, as set by the parasite's PIXEL \ routine INY \ Increment Y to 3 LDA (OSSC),Y \ Set X to byte #3 from the Y-th pixel block in OSSC, TAX \ which contains the pixel's x-coordinate INY \ Increment Y to 4 LDA (OSSC),Y \ Set Y to byte #4 from the Y-th pixel block in OSSC, STY T1 \ which contains the pixel's y-coordinate, and store Y, TAY \ the index of this pixel's y-coordinate, in T1, so we \ can restore it at the end of the subroutine LDA ylookup,Y \ Look up the page number of the character row that STA SC+1 \ contains the pixel with the y-coordinate in Y, and \ store it in the high byte of SC(1 0) at SC+1 TXA \ Each character block contains 8 pixel rows, so to get AND #%11111100 \ the address of the first byte in the character block ASL A \ that we need to draw into, as an offset from the start \ of the row, we clear bits 0-1 and shift left to double \ it (as each character row contains two pages of bytes, \ or 512 bytes, which cover 256 pixels). This also \ shifts bit 7 of the x-coordinate into the C flag STA SC \ Store the address of the character block in the low \ byte of SC(1 0), so now SC(1 0) points to the \ character block we need to draw into BCC P%+4 \ If the C flag is clear then skip the next instruction INC SC+1 \ The C flag is set, which means bit 7 of X1 was set \ before the ASL above, so the x-coordinate is in the \ right half of the screen (i.e. in the range 128-255). \ Each row takes up two pages in memory, so the right \ half is in the second page but SC+1 contains the value \ we looked up from ylookup, which is the page number of \ the first memory page for the row... so we need to \ increment SC+1 to point to the correct page 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) TXA \ Copy bits 0-1 of the x-coordinate to bits 0-1 of X, AND #%00000011 \ which will now be in the range 0-3, and will contain TAX \ the two pixels to show in the character row LDA P \ Fetch the pixel's distance from P CMP #80 \ If the pixel's ZZ distance is >= 80, then the dot is BCS PX6 \ a medium distance away, so jump to PX6 to draw a \ single pixel LDA TWOS2,X \ Fetch a mode 1 two-pixel byte with the pixels set as AND #WHITE \ in X, and AND with #WHITE to make it white (i.e. \ cyan/red) EOR (SC),Y \ Draw the pixel on-screen using EOR logic, so we can STA (SC),Y \ remove it later without ruining the background that's \ already on-screen DEY \ Reduce Y by 1 to point to the pixel row above the one BPL P%+4 \ we just plotted, and if it is still positive, skip the \ next instruction LDY #1 \ Reducing Y by 1 made it negative, which means Y was \ 0 before we did the DEY above, so set Y to 1 to point \ to the pixel row after the one we just plotted \ We now draw our second dash .PX6 LDA TWOS2,X \ Fetch a mode 1 two-pixel byte with the pixels set as AND #WHITE \ in X, and AND with #WHITE to make it white (i.e. \ cyan/red) EOR (SC),Y \ Draw the pixel on-screen using EOR logic, so we can STA (SC),Y \ remove it later without ruining the background that's \ already on-screen LDY T1 \ Set Y to the index of this pixel's y-coordinate byte \ in the command block, which we stored in T1 above INY \ Increment Y, so it now points to the first byte of \ the next pixel in the command block CPY Q \ If the index has reached the value in Q (which BEQ P%+5 \ contains the size of the pixel buffer), skip the next \ instruction JMP PXLO \ We haven't reached the end of the buffer, so loop back \ to PXLO to draw the next pixel in the buffer RTS \ Return from the subroutineName: PIXEL [Show more] Type: Subroutine Category: Drawing pixels Summary: Implement the OSWORD 241 command (draw space view pixels) Deep dive: Drawing colour pixels on the BBC MicroContext: See this subroutine in context in the source code Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * OSWVECS calls PIXEL
This routine is run when the parasite sends an OSWORD 241 command with parameters in the block at OSSC(1 0). It draws a dot (or collection of dots) in the space view. It can draw two types of dot, depending on bits 0-2 of the dot's distance: * Draw the dot using the dot's distance to determine both the dot's colour and size. This draws a one-pixel dot, two-pixel dash or four-pixel square in a colour that's determined by the distance (as per the colour table in PXCL). These kinds of dot are sent by the PIXEL3 routine in the parasite, which is used to draw explosion particles. * Draw the dot using the dot's distance to determine the dot's size, either a two-pixel dash or four-pixel square. The dot is always drawn in white (which is actually a cyan/red stripe). These kinds of dot are sent by the PIXEL routine in the parasite, which is used to draw stardust particles and dots on the Long-range Chart. The parameters match those put into the PBUF/pixbl block in the parasite.
Arguments: OSSC(1 0) A parameter block as follows: * Byte #0 = The size of the pixel buffer being sent * Byte #2 = The distance of the first dot * Bits 0-2 clear = Draw a two-pixel dash or four-pixel square, as determined by the distance, in white (cyan/red) * Any of bits 0-2 set = Draw a one-pixel dot, two-pixel dash or four-pixel square in the correct colour, as determined by the distance * Byte #3 = The x-coordinate of the first dot * Byte #4 = The y-coordinate of the first dot * Byte #5 = The distance of the second dot * Byte #6 = The x-coordinate of the second dot * Byte #7 = The y-coordinate of the second dot and so on
[X]
When the parasite sends an OSWORD command to the I/O processor (i.e. an OSWORD with A = 240 to 255), then the relevant handler routine in the I/O processor is called with OSSC(1 0) pointing to the OSWORD parameter block (i.e. OSSC(1 0) = (Y X) from the original call in the I/O processor)
[X]
Label PX2 is local to this routine
[X]
Label PX3 is local to this routine
[X]
Label PX5 is local to this routine
[X]
Label PX6 is local to this routine
[X]
Variable PXCL (category: Drawing pixels)
A four-colour mode 1 pixel byte that represents a dot's distance
[X]
Label PXLO is local to this routine
[X]
Variable TWOS (category: Drawing pixels)
Ready-made single-pixel character row bytes for mode 1
[X]
Variable TWOS2 (category: Drawing pixels)
Ready-made double-pixel character row bytes for mode 1
[X]
Configuration variable WHITE = %11111010
Four mode 1 pixels of colour 3, 2, 3, 2 (cyan/red)
[X]
Variable ylookup (category: Drawing pixels)
Lookup table for converting pixel y-coordinate to page number of screen address