Author Topic: Pixel on/off, drawing lines  (Read 5522 times)

0 Members and 1 Guest are viewing this topic.

Offline aeTIos

  • Nonbinary computing specialist
  • LV12 Extreme Poster (Next: 5000)
  • ************
  • Posts: 3915
  • Rating: +184/-32
    • View Profile
    • wank.party
Pixel on/off, drawing lines
« on: March 24, 2011, 04:17:05 am »
Hi,

How do you set a pixel on the screen on/ off?
How do you draw a line ?

Thanks!
I'm not a nerd but I pretend:

Offline ZippyDee

  • LV8 Addict (Next: 1000)
  • ********
  • Posts: 729
  • Rating: +83/-8
  • Why not zoidberg?
    • View Profile
Re: Pixel on/off, drawing lines
« Reply #1 on: March 24, 2011, 05:06:16 am »
I'm assuming you mean without using the built in ROM calls? Manipulating pixels is fairly straightforward. Drawing lines, however, is not. In order to draw lines without the use of b_calls you'll have to write an algorithm in assembly to do that yourself. I'd suggest Bresenham's line algorithm for that. I'm fairly new to ASM myself, so I don't think I could easily do or explain how to do that. Pixels, on the other hand, I can do.

Here's an explanation for manipulating pixels for the graph buffer (not directly plotting to the LCD):


Because of the nature of monochrome graphics, instead of representing each pixel with one or more bytes, each byte represents 8 pixels: one pixel for each bit. So in order to manipulate a specific pixel, you have to first find two things: The byte that contains the pixel, and which bit in that byte represents the pixel.

Getting the pixel:
Code: [Select]
GetPixel:
    ; Inputs:
    ;   A = x-coordinate, L = y-coordinate
    ; Outputs:
    ;   A = a bitmask for the pixel, HL = the address of the byte containing the pixel

    ld h, 0
    ld d, h
    ld e, l
    add hl, hl    ; the graph screen is arranged as an array of 12 x 64 bytes (12 bytes per row, 64 rows)
    add hl, de   ; so in order to find the correct byte we first multiply the y coordinate by 12...
    add hl, hl
    add hl, hl

    ld e, a    ; ...then add the x-coordinate/8 (because there are 8 pixels per byte)
    srl e
    srl e
    srl e
    add hl, de

    ld de, PlotSScreen
    add hl, de    ; now hl contains the address of the byte

    and 7    ; this is the tricky part: finding the bit. Because there are 8 bits in each byte, we can do x-coordinate modulo 8
    ld b, a    ; and that will return a number (0-7). That number tells us which bit to use (where "7" represents bit 0, and "0" represents bit 7)

    ld a, $80    ; here we load the initial bitmask into A
    ret z    ; if the "and 7" command returned 0, then the mask doesn't need to be rotated, so we can just return now.

_loop:
    rrca    ; otherwise, rotate the bitmask B times (remember we loaded the result of "and 7" into B earlier)
    djnz _loop
    ret

Turning a pixel on:
Code: [Select]
   call GetPixel
    or (hl)    ; HL holds the byte, and A holds the mask, so we simply use or to turn on the masked pixel...
    ld (hl), a ;...then load it into the buffer

Inverting a pixel:
Code: [Select]
   call GetPixel
    xor (hl)    ; Same idea as before only using xor to invert
    ld (hl), a

Turning a pixel off:
Code: [Select]
   call GetPixel
    cpl    ; because AND clears any pixels that are already set, you have to invert the mask first
    and (hl)
    ld (hl), a

NOTE: These only plot pixels in the graph buffer. You have to update the display before they will actually show up. Easiest way is just with B_CALL(_GrBufCpy), or you could write a custom routine, but that's a bit harder. :P


Hope this helped!
-Zippy Dee

EDIT: Bresenham's line algorithm: http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
« Last Edit: March 24, 2011, 05:07:10 am by ZippyDee »
There's something about Tuesday...


Pushpins 'n' stuff...


Offline aeTIos

  • Nonbinary computing specialist
  • LV12 Extreme Poster (Next: 5000)
  • ************
  • Posts: 3915
  • Rating: +184/-32
    • View Profile
    • wank.party
Re: Pixel on/off, drawing lines
« Reply #2 on: March 24, 2011, 05:26:06 am »
thanx!
I'm not a nerd but I pretend:

Offline ZippyDee

  • LV8 Addict (Next: 1000)
  • ********
  • Posts: 729
  • Rating: +83/-8
  • Why not zoidberg?
    • View Profile
Re: Pixel on/off, drawing lines
« Reply #3 on: March 24, 2011, 06:05:49 am »
No problem! You can use the GetPixel routine for drawing vertical lines pretty easily, too.

Vertical lines:
Code: [Select]
   ; Inputs:
    ;     D is the x-coordinate, L is the starting y-coordinate and E is the ending y-coordinate
    ;        make sure L is less than E, otherwise it'll go KABLOOIE!! or something...

    ld a, e
    sub l    ; get the length of the line by subtracting L from E
    ret z    ; if the length is zero, we don't need to draw it, so just return
    push af    ; save the length of the line into the stack so we don't lose it

    ld a, d    ; put the x-coordinate in A
    call GetPixel    ; now A is the mask that we'll just use for all the rows and HL is the address of the starting byte for the line

    pop BC    ; we pushed AF before, so now we pop to BC, so now B is the line length
    ld de, 12
    ld c, a    ; save the bitmask to C so we don't lose it
_vloop:
    ld a, c    ; restore the mask
    or (hl)    ; turn the pixel on
    ld (hl), a
    add hl, de    ; each row is 12 bytes, so to go to the next row we can just add 12 (which we stored in DE) to HL
    djnz _vloop
    ret

You could potentially just loop GetPixel for each point in horizontal lines, too, but because the bytes group pixels horizontally, there are more efficient ways to do it.
Unfortunately, I don't have a tested horizontal line routine, and I don't know that I'm good enough to really write an efficient one myself yet without doing tests first.

Anyway, hope this helped even more!
-Zippy Dee


EDIT: I wrote out this routine just now, haven't tested it, but it should work. Maybe someone else can confirm. I'm not guaranteeing 100% efficiency, but it should be fine for almost all cases.

Code: [Select]
    ; Inputs:
    ;     L =is the y-coordinate, D is the starting x-coordinate and E is the ending x-coordinate
    ;         again, make sure D is less than E

    ld a, e    ; get length, return if 0
    sub d
    ret z

    srl a    ; divide line length by 8 to find the number of bytes this line will span across (minus 1)
    srl a
    srl a
    push af    ; save it for later

    ld h, 0    ; get the address of the byte containing the first pixel of the line
    ld b, h    ; same code as in GetPixel, just using BC instead of DE because DE is currently occupied by our precious x-coordinates
    ld c, l
    add hl, hl
    add hl, bc
    add hl, hl
    add hl, hl
    ld c, d
    srl c
    srl c
    srl c
    add hl, bc
    ld bc, PlotSScreen
    add hl, bc    ; now hl is the starting byte of the line
   
    call GetMask    ;get the mask for the left end of the line
    pop bc    ; length of the line goes into b
    ld c, a    ; save the mask to c
    ld a, b
    or a    ; if b is 0, mask the end of the line as well
    jr z, _lastbyte
    ld a, c

_hloop:
    or (hl)    ; apply the mask
    ld (hl), a
    inc hl
    ld a, $FF    ; after the first byte, the other bytes (except the last) will be masked with $FF so all pixels are turned on
    djnz _hloop

_lastbyte:
    ld d, e
    dec d
    call GetMask    ; get the mask for the right end of the line
    srl a    ; because GetMask gives us the mask for the left end, we shift it one to the left and invert it to get the mask for the right end
    cpl
    and c    ; get the similar bits between the previous mask and the current one (remember that if the length is less than 8, the masks for both the left and right ends have to be applied to the same byte)
    or (hl)    ; now we apply the mask
    ld (hl), a
    ret    ; and we're done!
   
GetMask:
    ; this is also the same idea as getting the mask in GetPixel, only instead of rotating $80 we do a logical shift right on $FF to retrieve the mask for the ends of the line
    ld a, d
    and 7
    ld b, a
    ld a, $FF
    ret z
_maskloop:
    sra a
    djnz _maskloop:
    ret
« Last Edit: March 24, 2011, 06:57:25 am by ZippyDee »
There's something about Tuesday...


Pushpins 'n' stuff...


Offline Xeda112358

  • they/them
  • Moderator
  • LV12 Extreme Poster (Next: 5000)
  • ************
  • Posts: 4704
  • Rating: +719/-6
  • Calc-u-lator, do doo doo do do do.
    • View Profile
Re: Pixel on/off, drawing lines
« Reply #4 on: March 24, 2011, 10:30:55 am »
EDIT: As a note, the vertical and horizontal line commands draw across the whole screen
The easiest horizontal line I could come up  with...
Code: [Select]
;============================================
DrawHoriz:
;============================================
;Input:
;     l is the row to draw to
;     a is the type of line to draw
;        0 is a white line
;        1 is a black line
;        2 is an inverted line
;============================================
 ld h,0           ;2600        7
 ld b,h           ;44          4
 ld c,l           ;4D          4
 add hl,bc        ;09         11
 add hl,bc        ;09         11
 add hl,hl        ;29         11
 add hl,hl        ;29         11
 ld bc,9340h      ;014093     10
 add hl,bc        ;09         11
 ld b,12          ;060C        7
 or a             ;B7          4          Total: 91
 jr z,DrawHoriz   ;2804       White: 12+318+91
 dec a            ;3C          4
 jr nz,DrawInvH   ;2006       Black: 4+4+7+7+318+91
 dec a            ;3C          4

DrawHoriz:        Total: 5 bytes, 318 cycles
 ld (hl),a        ;77          7          84
 inc hl           ;23          6          72
 djnz DrawHoriz   ;10FC       13*11+8    152
 ret              ;C9         10          10

DrawInv:          Total: 7 bytes, 450 cycles Invert: 4+7+12+450+91
 ld a,(hl)        ;7E          7          84
 cpl              ;2F          4          48
 ld (hl),a        ;77          7          84
 inc hl           ;23          6          72
 djnz DrawInv     ;10FA       13*11+8    152
 ret              ;C9         10          10
;============================================
;Stats:
;============================================
;Size: 33 bytes
;Speed:
;     White: 12+318+91             421 cycles
;     Black: 4+4+7+7+318+91        431 cycles
;     Inv:   4+7+12+450+91         564 cycles
;============================================
Here is a vertical line:
Code: [Select]
;============================================
DrawVert:
;============================================
;Inputs:
;     a is the column
;     c is the method
;        0 draws a white line
;        1 draws a black line
;        2 draws an inverted line
;============================================
    ld b,a               ;47
    rrca \ rrca \ rrca   ;0F0F0F
    and 1Fh              ;E60F
    add 40h              ;C640
    ld l,a               ;6F
    ld h,93h             ;2693
    ld a,b               ;78
    and 7                ;E607
    inc a                ;3C
    ld b,a               ;47
    ld a,1               ;3E01
GetPixelMask:          
    rrca                 ;0F
    djnz GetPixelMask    ;10FD

    dec c                ;0D
    jr nz,DrawVertW       ;200F
     ld e,a               ;5F
     ld bc,400Ch          ;010C40
DrawVertBLoop:
      ld a,e               ;7B
      or (hl)              ;B6
      ld (hl),a            ;77
      ld a,b               ;78
      ld b,0               ;0600
      add hl,bc            ;09
      ld b,a               ;47
      djnz DrawVertBLoop   ;10F6
    ret                    ;C9
DrawVertW:
    dec c                ;0D
    jr z,DrawVertI       ;2001
     cpl a                ;2F
DrawVertI:
     ld e,a               ;5F
     ld bc,400Ch          ;010C40
DrawVertILoop:
      ld a,e               ;7B
      xor (hl)             ;AE
      ld (hl),a            ;77
      ld a,b               ;78
      ld b,0               ;0600
      add hl,bc            ;09
      ld b,a               ;47
      djnz DrawVertBLoop   ;10F6
    ret                    ;C9
;============================================
;Stats:
;============================================
;Size: 58 bytes
;Speed: ... too lazy to calculate at the moment
;     White:
;     Black:
;     Inv:  
;============================================
Here is a pixel routine:
Code: [Select]
;============================================
PlotPixel:       ;45 bytes
;============================================
;Inputs:
;     b is the x coordinate
;     c is the y coordinate
;     e is the method
;        0 draws a white line
;        1 draws a black line
;        2 draws an inverted line
;============================================
     ld a,b        ;78
     ld b,0        ;0600
     ld h,b        ;60
     ld l,c        ;69
     add hl,hl     ;29
     add hl,bc     ;09
     add hl,hl     ;29
     add hl,hl     ;29
     ld bc,9340h   ;014093
     add hl,bc     ;09
     ld b,a        ;47
     rrca          ;0F
     rrca          ;0F
     rrca          ;0F
     and 1Fh       ;E60F
     ld c,a        ;4F
     ld a,b        ;78
     ld b,0        ;0600
     add hl,bc     ;09
     and 7         ;E607
     ld b,a        ;47
     inc b         ;04
     ld a,1        ;3E01
GetMask:
      rrca          ;0F
      djnz GetMask  ;10FD

     dec e          ;1D
     jr nz,DrawWPix ;2003
      or (hl)       ;B6
      ld (hl),a     ;77
      ret           ;C9
DrawWPix:
     dec e          ;1D
     jr z,DrawIPix  ;2801
      cpl           ;2F
DrawIPix:
      xor (hl)      ;AE
      ld (hl),a     ;77
      ret           ;C9
These can probably be optimised, but I just typed them up :/ I am fairly sure the last can be optimised more.

Offline Deep Toaster

  • So much to do, so much time, so little motivation
  • Administrator
  • LV13 Extreme Addict (Next: 9001)
  • *************
  • Posts: 8217
  • Rating: +758/-15
    • View Profile
    • ClrHome
Re: Pixel on/off, drawing lines
« Reply #5 on: March 24, 2011, 10:34:27 am »
[code] tags in [spoilers] :(
« Last Edit: March 24, 2011, 10:34:40 am by Deep Thought »




Offline Xeda112358

  • they/them
  • Moderator
  • LV12 Extreme Poster (Next: 5000)
  • ************
  • Posts: 4704
  • Rating: +719/-6
  • Calc-u-lator, do doo doo do do do.
    • View Profile
Re: Pixel on/off, drawing lines
« Reply #6 on: March 24, 2011, 10:40:55 am »
Oh, sorry about that :/ Sometime I need to see what happens in other browsers :/

Offline Deep Toaster

  • So much to do, so much time, so little motivation
  • Administrator
  • LV13 Extreme Addict (Next: 9001)
  • *************
  • Posts: 8217
  • Rating: +758/-15
    • View Profile
    • ClrHome
Re: Pixel on/off, drawing lines
« Reply #7 on: March 24, 2011, 10:47:16 am »
That's something the Chrome guys need to fix :P




Offline aeTIos

  • Nonbinary computing specialist
  • LV12 Extreme Poster (Next: 5000)
  • ************
  • Posts: 3915
  • Rating: +184/-32
    • View Profile
    • wank.party
Re: Pixel on/off, drawing lines
« Reply #8 on: March 24, 2011, 11:28:57 am »
huh, for horizontal lines, isnt there just the B_CALL _HorizCmd?

Oh wait, that requires OP1. I dont use OP1 atm lol.

Thanks for those neat routines!

I'm not a nerd but I pretend:

Offline Xeda112358

  • they/them
  • Moderator
  • LV12 Extreme Poster (Next: 5000)
  • ************
  • Posts: 4704
  • Rating: +719/-6
  • Calc-u-lator, do doo doo do do do.
    • View Profile
Re: Pixel on/off, drawing lines
« Reply #9 on: March 24, 2011, 11:31:43 am »
Ah, usually using bcalls is slow, so I try to make routines that replace them. As an example, the horizontal commands can run over 10000 times in a second at 6MHz
EDIT: And  I stress "usually" because some cases aren't too bad. Also, the faster horizontal command can execute over 14 000 times in a second :D

Offline aeTIos

  • Nonbinary computing specialist
  • LV12 Extreme Poster (Next: 5000)
  • ************
  • Posts: 3915
  • Rating: +184/-32
    • View Profile
    • wank.party
Re: Pixel on/off, drawing lines
« Reply #10 on: March 24, 2011, 11:39:53 am »
wow.
I'm not a nerd but I pretend:

Offline Xeda112358

  • they/them
  • Moderator
  • LV12 Extreme Poster (Next: 5000)
  • ************
  • Posts: 4704
  • Rating: +719/-6
  • Calc-u-lator, do doo doo do do do.
    • View Profile
Re: Pixel on/off, drawing lines
« Reply #11 on: March 24, 2011, 11:42:48 am »
It is pretty impressive when you put it into perspective like that, right ? :D That is why sometimes the difference is negligible when using OS routines.

Offline aeTIos

  • Nonbinary computing specialist
  • LV12 Extreme Poster (Next: 5000)
  • ************
  • Posts: 3915
  • Rating: +184/-32
    • View Profile
    • wank.party
Re: Pixel on/off, drawing lines
« Reply #12 on: March 24, 2011, 11:44:20 am »
Yes, I cee. Nut if you use it a very big program it can just differ like a minute, I think
I'm not a nerd but I pretend:

Offline thepenguin77

  • z80 Assembly Master
  • LV10 31337 u53r (Next: 2000)
  • **********
  • Posts: 1594
  • Rating: +823/-5
  • The game in my avatar is bit.ly/p0zPWu
    • View Profile
Re: Pixel on/off, drawing lines
« Reply #13 on: March 25, 2011, 05:20:33 pm »
Nobody's given a diagonal line routine yet ;) This one comes from Missile.

You need to define (increment) somewhere. I believe you also need a getpixel that takes de = xy for input and doesn't destroy anything.


Code: [Select]
;###############################
;input: de = xy
; hl = xy

line:
push hl
push de
ld b, 2
fastLinePreLoop:
ld a, d
call xClipCheck
ld d, a
ld a, e
call yClipCheck
ld e, a
ex de, hl
djnz fastLinePreLoop

or a
sbc hl, de
add hl, de
jr nc, noSwitch
ex de, hl
noSwitch:

ld a, h
sub d
ld b, a
ld a, l
sub e
ld c, 0
jp p, positivoz
neg
ld c, 1
positivoz:
cp b
jr nc, yMajor

xMajor:
inc b
ld h, a
ld a, b

call setUpForLoop
bit 0, c
jr z, notNeggaz
inc h
notNeggaz:
ld c, d



xMajorLoop:
push bc
ld de, (increment)
add hl, de
push hl
ld d, c
ld e, h
call getPixel

ld a, c
call middlePartOfLoop

pop hl
pop bc
inc c
djnz xMajorLoop

pop de
pop hl
ret




yMajor:
bit 0, c
jr z, alreadySetUp
ex de, hl
alreadySetUp:

inc a
ld h, b
ld b, a

call setUpForLoop

bit 0, c
jr z, notNegga
inc d
notNegga:
ld c, h
ld h, d

yMajorLoop:
push bc
ld de, (increment)
add hl, de
push hl
ld e, c
ld d, h
ld b, h
call getPixel

ld a, b
call middlePartOfLoop

pop hl
pop bc
inc c
djnz yMajorLoop

pop de
pop hl
ret









middlePartOfLoop:
ld c, 128
and %00000111
jr z, alreadyDone
ld b, a
byteLoop:
srl c
djnz byteLoop
alreadyDone:
ld a, c
or (hl)
ld (hl), a
ret


setUpForLoop:
ld l, $FF
push de
push bc
call divHLbyA
pop bc
bit 0, c
call nz, negHL
ld (increment), hl
pop hl
ld d, h
ld h, l
ld l, 0
ret


yClipCheck: ;super efficient, no rets
call isANeg
cp 64
ret c
ld a, 63
xClipCheck:
call isANeg
cp 96
ret c
ld a, 95
isANeg:
or a
ret p
xor a
ret

zStart v1.3.013 9-20-2013 
All of my utilities
TI-Connect Help
You can build a statue out of either 1'x1' blocks or 12'x12' blocks. The 1'x1' blocks will take a lot longer, but the final product is worth it.
       -Runer112

Offline ZippyDee

  • LV8 Addict (Next: 1000)
  • ********
  • Posts: 729
  • Rating: +83/-8
  • Why not zoidberg?
    • View Profile
Re: Pixel on/off, drawing lines
« Reply #14 on: March 25, 2011, 05:38:12 pm »
Is that bresenham's?

Also, can someone who really knows what they're talking about confirm that my horizontal line routine is actually correct? :P
« Last Edit: March 25, 2011, 05:38:49 pm by ZippyDee »
There's something about Tuesday...


Pushpins 'n' stuff...