Dark Bit Factory & Gravity

PROGRAMMING => Freebasic => Topic started by: Clyde on March 20, 2007

Title: Using ASM to Plot Pixels.
Post by: Clyde on March 20, 2007
Hi,

Does anyone know what instructions in Assembler I'd need to use in order to be able to plot pixels, using TinyPTC and ScreenBuffer(XRES*YRES). And also what the instructions are doing please?

Many thanks,
Clyde.
Title: Re: Using ASM to Plot Pixels.
Post by: ferris on March 20, 2007
I've seen your sourcecode, so I'm sure you have a freebasic routine like drawpixel(x,y,color). I'm writing this here and it's the basic idea of how to do it, but unfortunately I can't test it here. I am also assuming that "scrw" is a constant set to the screen width.

Code: [Select]
Sub drawpixel(x,y,color)
asm
mov ecx,[color]
mov eax,y
imul eax,scrw
add eax,x
shl eax,2
mov ebx,eax
mov eax,[ScreenBufferArrayPointer]
mov [eax + ebx],ecx
End asm
End Sub

should work ;)

Reply if you have problems.
Title: Re: Using ASM to Plot Pixels.
Post by: Paul on March 20, 2007
I'm not gettign this  ???

what is the ScreenBufferArrayPointer ??

Title: Re: Using ASM to Plot Pixels.
Post by: ferris on March 20, 2007
A memory pointer pointing to the start of the screenbuffer array.
Title: Re: Using ASM to Plot Pixels.
Post by: Shockwave on March 20, 2007
Use the stosd instruction for plotting single pixels and stosb for lines of them and whay thygrion said :)
Title: Re: Using ASM to Plot Pixels.
Post by: ferris on March 20, 2007
If it were pure asm for tinycoding I'd use stosb, but this is good enough for inline asm.

The person to talk to for this is Fryer/Stonemonkey.
Title: Re: Using ASM to Plot Pixels.
Post by: Clyde on March 20, 2007
Quote from: Shockwave
Use the stosd instruction for plotting single pixels and stosb for lines of them and whay thygrion said

What and how do I know what is stosd and stosb instructions?

Thanks,
Clyde.
Title: Re: Using ASM to Plot Pixels.
Post by: ferris on March 20, 2007
stosd and stosb are kinda strange.

stosb stores al into the memory address [es:di], and I am unfamiliar with stosd. Kinda like small macros.
Title: Re: Using ASM to Plot Pixels.
Post by: Clyde on March 20, 2007
Ok, say if I am using the following one dimensional array method of drawing directly to the screenbuffer:

Example:
ScreenBuffer(PosX+PosY*ScreenWidth)=Colour

What would the instructions be for that please, and if possible could you give comments on what it's doing?

Cheers and many thanks,
Clyde.
Title: Re: Using ASM to Plot Pixels.
Post by: ferris on March 20, 2007
(hehe...looks liek my crap code worked ;D )

First, you need an extra variable for your screenbuffer. This is why I don't usually use asm to plot pixels in FB. Someone PLEASE post a way around this :) :

Code: [Select]
dim screenpointer as unsigned integer ptr = @ScreenBuffer(0) ' I think "@" is the right symbol ;)

Then the code would be:

Code: [Select]
asm
mov ecx,[Colour] ' Move color into ecx register
mov eax,PosY ' "PosY * ScreenWidth"
imul eax,ScreenWidth ' ^ cotd.
add eax,x ' "PosX + "
shl eax,2 ' * 4 because they're integers
mov ebx,eax
mov eax,[ScreenBufferArrayPointer]
mov [eax + ebx],ecx
End asm
Title: Re: Using ASM to Plot Pixels.
Post by: Shockwave on March 20, 2007
Ok Clyde, first you need a pointer.

    DIM PNTR AS UINTEGER PTR
    PNTR = @BUFFER(LOCATION)   

Where LOCATION is the location to start drawing.

The STOSD command is not strange, it just means "Store String"

You could have;

STOSB, STOSW, STOSD.
They all use different registers to grab the data from.

STOSB  uses AL And stores a BYTE.
STOSW uses AX And stores a WORD.
STOSD  uses EAX And stores a DOUBLE WORD.

What they all have in common is that they store the word, byte or double word they grab at the memory address it gets from the register EDI.

So... you could just have (to plot one pixel at 100 * 100 in white);

Code: [Select]
    DIM PNTR AS UINTEGER PTR

    PNTR = @BUFFER(100*(100*XRES))   
    CLR=&HFFFFFF

asm
        MOV EAX,DWORD PTR[CLR]
        MOV EDI, [PNTR]
        STOSD
end asm

You can repeat the storestring in a kind of loop by preceeding the STOSB, STOSW, STOSD instruction with rep.
Rep looks in the register ECX to see how many times to repeat it (incrimenting the address it writes to each time). So to draw a 50 pixel long line in white at 100 * 100 do;

Code: [Select]
    DIM PNTR AS UINTEGER PTR

    PNTR = @BUFFER(100*(100*XRES))   
    CLR=&HFFFFFF
    WIDTH = 50
asm
        MOV EAX,DWORD PTR[CLR]
        MOV EDI, [PNTR]
        MOV ECX,WIDTH
        REP STOSD
end asm

That's probably the method you should use tbh Clyde, I don't know of much that's faster.. For information rep stosb,rep stosw,rep stosd are all 2 bytes, and all 3 clock cycles + the count of bytes, words or dwords they have to move, or to put it simply the length of the line you want to draw.

Title: Re: Using ASM to Plot Pixels.
Post by: Clyde on March 20, 2007
Huge thanks dudes!! :)

Cheers,
Clyde.
Title: Re: Using ASM to Plot Pixels.
Post by: Shockwave on March 20, 2007
Ok, say if I am using the following one dimensional array method of drawing directly to the screenbuffer:

Example:
ScreenBuffer(PosX+PosY*ScreenWidth)=Colour

What would the instructions be for that please, and if possible could you give comments on what it's doing?

Cheers and many thanks,
Clyde.

There are thousands and thousands of pages on the web about assembly language syntax that you could use. Don't be put off with asm, it's not hard. In many ways its a lot simpler as you have less commands to remember :P
Title: Re: Using ASM to Plot Pixels.
Post by: Jim on March 20, 2007
Quote
Someone PLEASE post a way around this
Code: [Select]
lea eax,[ScreenBuffer]

Quote
stosb stores al into the memory address [es:di], and I am unfamiliar with stosd. Kinda like small macros.
stosw stores ax (a 16 bit number) to [es:edi], and stosd stores eax (a 32 bit number) to [es:edi].  An RGBA pixel is 32bits so you use stosd.  These instructions are a lot more interesting than just mov(ing) a value because they can be prefixed with REP which says 'repeat this instruction ecx times, each time adding (or subtracting) the size of the value from the address'.
So
Code: [Select]
mov ecx,10
mov eax,$ffffffff
lea esi,[address]
rep stosd
Will store 10 white pixels at address.

Jim
Title: Re: Using ASM to Plot Pixels.
Post by: Stonemonkey on March 20, 2007
If it's more speed you're after I would recommend (not wanting to put you off looking into asm as it is useful to have an understanding of it) looking at other ways of optimising, using asm to replace single instructions or a couple of instructions generally won't make all that much difference.
Title: Re: Using ASM to Plot Pixels.
Post by: Clyde on March 20, 2007
Cheers Guys, very much appreciate the help and tips.
Title: Re: Using ASM to Plot Pixels.
Post by: MrP on March 24, 2007
This may be a bit late in the day for this post, but i'll add something anyway....

Quote
ScreenBuffer(PosX+PosY*ScreenWidth)=Colour

I've found that if your plotting lots of individual pixels using this code the most expensive thing is that multiply by the screenwidth.

For example if your plotting a 100x100 pixel box thats 10,000 multiplies, and 10,000 additions to get them all into the screenbuffer, I wrote some inline assembly to replace the above code and while it was faster than the freebasic commands you can still get it faster by removing all those nasty multiplies...

because the value you get from multiplying posy by the screenwidth will always be the same regardles of which line your on you can precalculate this beforehand, eg if your plotting a pixel at 100, 10 and your screen resolution is 800x600 the (posy*screenwidth) bit will be 10*600 which equals 6000, if your plotting at 100, 5 it will be 3000 so we can store these calculations in an array, then just reference that to avoid doing a multiply every time you want to plot a pixel......

create an array of 600 uinteger values (the height of your screen) then calculate the offset for each line of the screen as in the following code

Code: [Select]
dim y_offset(screen_y) as uinteger
for i = 0 to 599
    y_offset(i) = i * screen_x
next i

once this is done you can then replace your original drawing command with the following

Code: [Select]
scr_buffer(100 + y_offset(100)) = rgb(255, 255, 255)
as you can see you have now eliminated the expensive multiply and you should find that this is even faster than using inline asm to calculate the offset for your pixel... Of course if your trying to write small code the array of 600 integers wont help, but if speed is what your after then I don't think theres a much faster way of plotting single pixels in freebasic than this..

I'll put the complete code listing at the end, but please be aware this uses the standard ptc lib that comes with freebasic. I myself use the gfxlib and  I dont have the fantastic extended lib by Jim and Rbraz (I think thats who did it, if not my apologies for the incorrect credit) Hope this helps in some way.....

Code: [Select]
#include "tinyptc.bi"

const screen_x = 800
const screen_y = 600
const screen_size = screen_x * screen_y

dim shared scr_buffer(0 to screen_size - 1) as integer

ptc_open("test", screen_x, screen_y)

dim y_offset(screen_y) as uinteger
for i = 0 to screen_y - 1
    y_offset(i) = i * screen_x
next i

while inkey$ = ""
    scr_buffer(100 + y_offset(100)) = rgb(255, 255, 255)
    ptc_update @scr_buffer(0)
wend

ptc_close()
end
Title: Re: Using ASM to Plot Pixels.
Post by: Paul on March 24, 2007
I did a little test code to try this and the results where a little strange.

in tinyptc_ext the time with multyplying was 19.6 sec for 1000 frames
in tinyptc_ext the time using the array it  was 19.9 sec for 1000 frames

in tinyptc the time with multyplying was 18.6 sec for 1000 frames
in tinyptc the time using the array it  was 17.3 sec for 1000 frames

Title: Re: Using ASM to Plot Pixels.
Post by: Shockwave on March 24, 2007
It is faster to do the multiplications outside the loop, I think Mr.p Is right here, what might be clouding your judgement is that tinyptc_ext waits for the vbl.

Also if you have replaced the mmx libs etc you are using a fair chunk of rbraz's code anyway so just use tinyptc ext and precalc as much as possible outside loops :) Same in any language.
Title: Re: Using ASM to Plot Pixels.
Post by: Paul on March 24, 2007
just changing the contents of the array mot flipping or anything, 6000*800*600

time with precalc for 59.7245110761289
time without precalc 59.86356981124747
Title: Re: Using ASM to Plot Pixels.
Post by: Shockwave on March 24, 2007
It's an improvement :)
Title: Re: Using ASM to Plot Pixels.
Post by: Paul on March 24, 2007
yup :)

meybe if you access the precalced aray with asm.
This is only a wild guess and I have no idea if thats gonna be faster 
Title: Re: Using ASM to Plot Pixels.
Post by: Paul on March 25, 2007
If I'm thinking correctly then this might be the fastest way

according to my prog this is 3x faster than multiplying every pixel but it's only fast for drawing rectangles only works for rectangles.

Code: [Select]
for i=0 to 6000
    for y=0 to 599
        for x=0 to 799
            scr_buffer(d) = 255'rgb(255, 255, 255)
            d=d+1
        next
        d=y*800
    next
    d=0
next
Title: Re: Using ASM to Plot Pixels.
Post by: Jim on March 25, 2007
Why do you have two loops?  Why not just loop 800*600 times, then make a pointer to the buffer and get rid of d.  That should be a bit quicker.

Jim
Title: Re: Using ASM to Plot Pixels.
Post by: Paul on March 25, 2007
In case i don't want to draw the whole screen, just a bit of it

would it work to do this?

Code: [Select]
for i=0 to 6000
    for y=0 to 800*600-1
            scr_buffer(y) = 255
    next
next
Title: Re: Using ASM to Plot Pixels.
Post by: MrP on March 25, 2007
if you want to plot out the whole screen the fastest way I know of is to use asm and blit from the start of the screen to the end, much like you do with the for loop... also if your just plotting something that isn't full screen, asm again is way faster than nesting two for loops, don't know why but this was the case in the tests i did.....

Heres a routine i used to clear the screen to any colour when i first started messing around with asm.... Theres still some things you could do to make this a tad faster but its fast enough.....

Code: [Select]
sub pgl_cls(byval col as integer = rgb(0, 0, 0))
    asm
        mov ebx, [pgl_screen_ptr]
        mov edx, [pgl_screen_length]
        mov ecx, [col]
        lea eax, [ebx + edx]
        rep:
            mov [ebx], ecx
            add ebx, 4
            cmp ebx, eax
        jbe rep
    end asm
end sub

and to blit a full screen image with an alpha value using mmx..... again this was written a while ago so theres probably some optimisations that could be done, also it relies on some variables that are pre-determined at the start of the graphics lib I put together, so its pretty useless as usable code to copy and paste but you get the idea..... Jim helped out a lot with this when I was putting it together.... Cheers again Jim... Also dont be surprised if the comments dont make any sense, its been chopped and changed a bit while i was doing it.....

Code: [Select]
sub pgl_blit_screen(byval src_buffer as integer ptr, byval alpha as ubyte)
    dim p_alpha as integer = alpha shl 16 or alpha shl 8 or alpha
    asm
        mov eax, [src_buffer]     
        mov ebx, [pgl_screen_ptr]   
        mov ecx, [pgl_screen_length] 
        lea ecx, [ebx + ecx]
        add eax, 12
       
        pxor mm6, mm6
        movd mm2, [p_alpha]         'mm2 has alpha value
        punpcklbw mm2, mm6          'unpack it for processing
 
        dloop:
            movd mm0, [eax]         'image pixel col in mm0
            movd mm1, [ebx]         'screen pixel col in mm2
           
            punpcklbw mm0, mm6      'unpack mm0 using blank mm6 register as interleave
            punpcklbw mm1, mm6      'same for screen color
   
            psubw mm0, mm1          'subtract image color from screen color
            pmullw mm0, mm2         'multiply resulting color by alpha
            psrlw mm0, 8            'divide that by 255
            add eax, 4              'increment image position
            paddb mm0, mm1          'and then add screen color back in

            packuswb mm0, mm0       'repack image pixel back to lower 32 bits of mm0
            movd [ebx], mm0         'move result back to screen
       
            add ebx, 4              'increment screen position
       
            cmp ebx, ecx            'check for end of screen
        jbe dloop                   'if were not there wrap!!!!
       
        emms                        'restore floating point stuff, fuck this is expensive..
    end asm
end sub
Title: Re: Using ASM to Plot Pixels.
Post by: Paul on March 25, 2007
This is probably really good but i don't have the asm skills to try it :(