Calculating memory address

1: void __fastcall fastpixel(long* surface)
2: {
3: int iCount = 1152*864;
4: __asm
5: {
6: // Assign pointers to register
7: push edi;
8: push ecx;
9: push eax;
10: mov edi, [surface] ; //put dest addr to edi reg
11: mov ecx, [iCount] ;// put count to ecx reg
12:
13: codeloop:
14: mov eax, [edi] ; //mov a byte of src data to low
15: xor eax, 0x00ffffff ;
16: mov [edi], eax ;
17: add edi,4;
18: dec ecx ; //decrement count by 1
19: jnz codeloop ; //jump to codeloop if not 0
20: pop edi;
21: pop ecx;
22: pop eax;
23: }
24: }

Ok so I've got this lovely bit of code that I've constructed and I need a few clerifications:

in theory I should have been able to replace lines 14 to 16 with this:

first:
xor [edi], 0x00FFFFFF

how ever this just turns my screen blue :\

second:
I seem to need to use add edi,4 instead of just inc because inc only seems to move it 1 byte forward, is there a way to fix this?

third:
This is in the help docs for MASM32 but I have no clue what idex, scale, and displacement are reffering to.

[ Base Address + Index * Scale + Displacement ]

[ebx + ecx * 4 + 8]

ebx is the base address.
ecx is the index.
4 is the scale based on the data size.
8 is the displacement in BYTES.
[1517 byte] By [barrensoul] at [2007-11-19 7:33:02]
# 1 Re: Calculating memory address
Your function could look like:

void fastpixel(long* surface)
{
int iCount = 1152*864;
__asm
{
// Assign pointers to register
push edi;
push ecx;
push eax;
mov edi, [surface] ; //put dest addr to edi reg
mov ecx, [iCount] ;// put count to ecx reg

codeloop:
//mov eax, [edi] ; //mov a byte of src data to low
//xor eax, 0x00ffffff ;
//mov [edi], eax ;

xor DWORD PTR [edi+ecx*4-4], 0x00ffffff;

//inc edi
//add edi,4;
dec ecx ; //decrement count by 1
jnz codeloop ; //jump to codeloop if not 0
pop edi;
pop ecx;
pop eax;
}
}

As you see, there is no need to modify EDI, although increasing it by 4 is not worse than increasing by 1, compilers create instructions like 'add reg data_type_size' when processing arrays. You also lack type specifier, thats why your 'xor [edi], long_num' did not work. And, in added line, you have example of addresing with index,scale and displacement.
Unfortunately, at this moment I have no time to explain these changes, but if you do not understand them, I will do it today evening (evening of CET+1h :) ).
Hope it helps
Hob
Hobson at 2007-11-10 3:55:54 >
# 2 Re: Calculating memory address
hmm thanks that did help, I'm having another problem though, I have this asm code which for some reason does not work for writing 8bit video memory, instead the middle of my screen generates this image
(resolution is to big to post so you'lk need to use the link, and possibly paint ;) )
http://img.photobucket.com/albums/v51/BarrenSoul/ASMprob.png

all of those objectes are supposed to be a single thing in the middle not all spread out like that. (9 objects)

//index = (X-1+((Y-1)*1152));
__asm
{
mov eax, [X];//this ASM replaces the index calulation
sub eax, 1;
mov ecx, [Y];
sub ecx, 1;
imul ecx, 1152;
add ecx,eax;
//mov ecx, [index]; //index
mov edi, [surface]; //put dest addr to edi reg
mov al, [edi+ecx]; //mov a byte of src data to low
xor al, 0xF;
mov [edi+ecx], al;
barrensoul at 2007-11-10 3:56:56 >
# 3 Re: Calculating memory address
Those ideas are not tested, because I have no compiler here, let me know if it helped:

1. Always when you are reading or storing a value from memory, use size specifier. This does not affect the program, and you will be sure that everything is ok.
2. This code seems to be time-critical for you, or you just like to mess around with assembler. In both cases you could replace 'imul val, 1152' with

mov val, reg1;
mov val, reg2;
shl reg1, 10;
shl reg2, 7;
add reg1, reg2

Rumours say that xMUL is slower than set of movs/shifts/adds, but i do not know hows that with modern procesors. Besides, IMUL is a bit tricky with it source/dest operands.
Instead 'sub reg,1' you could use 'dec reg'.

Thats all what I can help without compiler, Ill check it for more when i am back home.

Hob
Hobson at 2007-11-10 3:57:59 >
# 4 Re: Calculating memory address
I checked your code with compiler and it works in correct way (X and Y should be declared as DWORDS). Probably not index calculation code is your problem. I forgot to mention earlier:
Is 'surface' variable a pointer to memory of DirectX screen surface? If so, you should know that width of DirectX screen surface is NOT equal to its horizontal resolution. In your index calculationd you should use not 1152 (resolution value) but its pitch value.
If its not a DX screen buffer, then your problem is probably located not in index calculation routine. I would expect that X and Y variables are declared as WORD, and in assembly code they are used as DWORD. Just use size specifiers EVERYWHERE where possible. If your problem still persists, attach your project and I will check it (if its not too complicated for me).

Hob.
Hobson at 2007-11-10 3:58:53 >
# 5 Re: Calculating memory address
it is a pointer to the screen memory (void pointer), I'm well aware of pitch (surface.lPitch) how ever I have specified a 8 bit screen thus making the pitch 1, on the other prog I use the *4 in the index cause that was 32bit mode, thus I should not need to specify an index multiplier.
barrensoul at 2007-11-10 4:00:00 >
# 6 Re: Calculating memory address
So if its not pitch, then I'd expect some size mismatch between declaration of coords and their usage in asm code.

By 'size specifier' I did not mean index multiplier. In this case it should be indeed set to 1. I just mean that your function prototype could look like
void setpixel(void* surf, WORD X, WORD Y)
while in asm snippet you use
mov eax, [X]what moves into eax 32-bit value.
If it is also not a reason, then I think that trouble is not located at code posted by you.

If you attach your project, it does not need some specific libs and can be compiled under VC 6.0, I might to take look at it.

Hob
Hobson at 2007-11-10 4:00:59 >
# 7 Re: Calculating memory address
#include <windows.h>
#include <ddraw.h>

HINSTANCE ghinst = 0;
HDC hdc;
HWND mainwindow = 0;
static DDSCAPS ddscaps;
static LPDIRECTDRAW lpDD = 0;
static LPDIRECTDRAWSURFACE lpDDSPrimary;
static LPDIRECTDRAWSURFACE lpDDSBack;
static DDSURFACEDESC ddsd;
static PAINTSTRUCT ps;
static HDC whdc;
static DDSURFACEDESC DDSurfDesc;
static BYTE on_off[999364];
static BYTE off_on[999364];
bool drawnow = 0;
bool CreatePrimarySurface();
bool DirectDrawInit();
void __forceinline updateFrame( void );
void suicide ();

long FAR PASCAL WndProc (HWND myhwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CHAR:
if (wParam == VK_ESCAPE) // VK key code for exit key
PostQuitMessage(0);
else if (wParam == VK_SPACE)
drawnow = 1;
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return(DefWindowProc(myhwnd,message,wParam,lParam));
}
int FAR PASCAL WinMain (HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow)
{
char buffer[10];
WNDCLASS wc;
HWND myhwnd=0;
MSG msg;
ghinst = hInst;
wc.style = CS_HREDRAW|CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.hInstance = ghinst;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hIcon = 0;
wc.lpszMenuName = 0;
wc.hCursor = LoadCursor(0,IDC_ARROW);
wc.hbrBackground = (HBRUSH__ *)GetStockObject(BLACK_BRUSH);
wc.lpszClassName = "DDClass";
if (!RegisterClass(&wc)){MessageBox(mainwindow,"RegisterClass","error",MB_OK); return(1);}

bool mainloop = 1;
if (!DirectDrawInit())
{
suicide();
MessageBox(mainwindow,"DDinit","error",MB_OK);
exit(0);
}
CreatePrimarySurface();
long* colour = 0;
BYTE setpixel = 1;
BYTE neighbour = 0;
unsigned int index = 0;
unsigned int Y = 0; // x
unsigned int X = 0; // xxxooo
ZeroMemory(on_off,sizeof(on_off)); // xx
//on_off[576+(432*1154)] = 1;
on_off[577+(433*1154)] = 1; //x rabit
on_off[576+(433*1154)] = 1; //xxx
on_off[578+(433*1154)] = 1; // x
on_off[577+(434*1154)] = 1;
on_off[576+(432*1154)] = 1;

on_off[580+(432*1154)] = 1; //xxx
on_off[581+(432*1154)] = 1; // x
on_off[582+(432*1154)] = 1;
on_off[581+(433*1154)] = 1;

/*switchen
on_off[566+(431*1154)] = 1; //575 is middle 431 is middle
on_off[565+(431*1154)] = 1;
on_off[564+(431*1154)] = 1;
on_off[565+(430*1154)] = 1;
on_off[564+(428*1154)] = 1;
on_off[562+(430*1154)] = 1;
on_off[561+(429*1154)] = 1;
on_off[562+(428*1154)] = 1;

on_off[587+(433*1154)] = 1;
on_off[588+(433*1154)] = 1;
on_off[587+(434*1154)] = 1;
*/
unsigned long counter = 0;
ZeroMemory(buffer,10);
DDBLTFX ddbltfx;
/*------- MAIN LOOP --------*/
while (mainloop)
{
while (PeekMessage(&msg,0,0,0,PM_REMOVE))
{
if (msg.message == WM_QUIT)
{mainloop = 0;}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
ZeroMemory( &DDSurfDesc, sizeof( DDSurfDesc ));
DDSurfDesc.dwSize = sizeof( DDSurfDesc );
lpDDSBack->Lock( 0, &DDSurfDesc, DDLOCK_WAIT, 0 );
long* surface = reinterpret_cast<long*>(DDSurfDesc.lpSurface);
for(Y = 1; Y<865; ++Y)
{
for(X= 1; X<1153; ++X)
{
neighbour += on_off[(X-1)+((Y-1)*1154)];
neighbour += on_off[(X+1)+((Y-1)*1154)];
neighbour += on_off[(X-1)+((Y+1)*1154)];
neighbour += on_off[(X+1)+((Y+1)*1154)];
neighbour += on_off[(X-1)+(Y*1154)];
neighbour += on_off[(X+1)+(Y*1154)];
neighbour += on_off[X+((Y+1)*1154)];
neighbour += on_off[X+((Y-1)*1154)];
if(neighbour)
{
if(((neighbour == 2)&&(on_off[X+(Y*1154)]))||((neighbour == 3)))
{
//index = (X-1+((Y-1)*1152));
__asm
{
mov eax,DWORD PTR [X];
dec eax;
mov ecx,DWORD PTR [Y];
dec ecx;
imul ecx, 1152; //integer multiplication
add ecx,eax;
xor eax,eax;
//mov ecx, [index]; //index
mov edi, [surface]; //put dest addr to edi reg
mov al, BYTE PTR [edi+ecx]; //mov a byte of src data to low
xor al,BYTE PTR 255;
mov BYTE PTR [edi+ecx], al;

//off_on[X+(Y*1154)] = 1;
mov ecx, [Y];
imul ecx, 1154;
lea edi, off_on;
add ecx,[X];
mov [edi+ecx], 1;
}
}
}
neighbour = 0;
}
}
lpDDSBack->GetDC(&hdc);
itoa(++counter,buffer,10);
SetBkColor(hdc, RGB(0, 0, 0));
SetTextColor(hdc, RGB(255, 255, 255));
TextOut(hdc, 500, 0, buffer, lstrlen(buffer));
lpDDSBack->ReleaseDC(hdc);
lpDDSBack->Unlock(DDSurfDesc.lpSurface);
lpDDSPrimary->Flip( NULL, DDFLIP_WAIT );
for(X = 0; X<1154; ++X)
{
off_on[X] = 0;
off_on[X+(865*1154)] = 0;
}
for(Y = 0; Y<866; ++Y)
{
off_on[Y*1154] = 0;
off_on[1153+(Y*1154)] = 0;
}
CopyMemory(on_off,off_on,sizeof(off_on));
ZeroMemory(off_on,sizeof(off_on));
ddbltfx.dwSize = sizeof(ddbltfx);
ddbltfx.dwFillColor = 0x00;
lpDDSBack->Blt(NULL,NULL,NULL,DDBLT_COLORFILL, &ddbltfx);
}
suicide();
return(1);
}
void suicide ()
{
if (lpDD)
{
if (lpDDSPrimary)
{
lpDDSPrimary->Release();
lpDDSPrimary = 0;
}
lpDD->Release(); lpDD = 0;
}
if (mainwindow)
{
DestroyWindow(mainwindow);
mainwindow = 0;
}
}

bool DirectDrawInit()
{
HDC hdc = GetDC(0);
mainwindow = CreateWindow("DDClass","Escapetoexit",WS_VISIBLE | WS_POPUP,0,0,GetDeviceCaps(hdc,HORZRES),GetDeviceCaps(hdc,VERTRES),0,0,ghinst,0);
ReleaseDC(0,hdc);
ShowCursor(0);
HRESULT ddrval;

ddrval = DirectDrawCreate( NULL, &lpDD, NULL );
if( ddrval != DD_OK )
{
MessageBox(mainwindow,"Direct Draw Creation","error",MB_OK);
return(false);
}

ddrval = lpDD->SetCooperativeLevel( mainwindow, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN );
if( ddrval != DD_OK )
{
lpDD->Release();
MessageBox(mainwindow,"Coop Level","error",MB_OK);
return(false);
}
ddrval = lpDD->SetDisplayMode( 1152, 864, 8);
if( ddrval != DD_OK )
{
lpDD->Release();
MessageBox(mainwindow,"Display Mode","error",MB_OK);
return(false);
}

return(true);
}
bool CreatePrimarySurface()
{
HRESULT ddrval;
ZeroMemory(&ddsd,sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);

ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
ddsd.dwBackBufferCount = 1;

ddrval = lpDD->CreateSurface( &ddsd, &lpDDSPrimary, NULL );
if( ddrval != DD_OK )
{
MessageBox(mainwindow,"Creat Surface","error",MB_OK);
lpDD->Release();
return(false);
}
ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
ddrval = lpDDSPrimary->GetAttachedSurface(&ddscaps, &lpDDSBack);
if( ddrval != DD_OK )
{
MessageBox(mainwindow,"Get Attached Surface","error",MB_OK);
lpDDSPrimary->Release();
lpDD->Release();
return(false);
}

return true;
}

well here it is, you'll need the directX SDK so you can link the directx libs n stuff (don't get the most recent one if your using VC++6.0 because it dosn't support VC++6.0 any more, you'll need to find last months)
link: user32.lib ddraw.lib gdi32.lib
barrensoul at 2007-11-10 4:01:54 >
# 8 Re: Calculating memory address
This is DDSurfaceDesc.lPitch value in your app. For some reason, it is NOT equal 1. You need to multiply all coords by this pitch, instead of 1152. If you have problem with changes, msg me.
Remember, that lPitch can vary depending on Graphic hardware driver, and it should be read every time it will be used.

Hob
Hobson at 2007-11-10 4:03:02 >
# 9 Re: Calculating memory address
I will try pitch as soon as I get home, how ever it seems either you implimented my prog without the same rules or something went wrong because the straight line in your program should not exist, this link shows what you should see starting in the middle of my screen if it works correctly:

http://www.radicaleye.com/lifepage/patterns/rabbits.html

and I thank you for taking the time to help this stubourn noobie :) although I like ASM quite a bit, I'd have to say I love C++/ASM hybrid prorgamming alot.
barrensoul at 2007-11-10 4:04:00 >
# 10 Re: Calculating memory address
Straight line is made by me, for testing purposes. Your life of rabbits is working correctly, just now you need to change ALL occurences of 1152 to pitch, what will fix coords, because now pixels are spread around whole screen. And i'd suggest not hardcode pitch value, just read it each time when ddsdesc is obtained.
Always to help

Hob
Hobson at 2007-11-10 4:04:58 >
# 11 Re: Calculating memory address
heh heh yeah I made a cardinal sin in DirX :) I realised that after a computer at school was experiencing the same glitch in the 32bit working version ;)
barrensoul at 2007-11-10 4:06:00 >