Is anybody working on GUI ASM -> C?

Strictly for discussing ZSNES development and for submitting code. You can also join us on IRC at irc.libera.chat in #zsnes.
Please, no requests here.

Moderator: ZSNES Mods

Post Reply
Noxious Ninja
Dark Wind
Posts: 1271
Joined: Thu Jul 29, 2004 8:58 pm
Location: Texas
Contact:

Is anybody working on GUI ASM -> C?

Post by Noxious Ninja »

So I'm in an Intro to Computer Architecture class right now, and we happen to be studying x86 assembly. So now I have some skills, and I want something more than our simple assignments to work on, and I'm back here, as Nach or whoever predicted I would be eventually.

If nobody's started, I'll dive in. I might drown, but hey.

EDIT: A start.

Code: Select all

GUIHLine:
  cmp ecx,0
  jl near .nodraw
  cmp eax,255
  jg near .nodraw
  cmp eax,0
  jnl .noofx
  mov eax,0
.noofx
  cmp ecx,255
  jng .noofx2
  mov ecx,255
.noofx2
  cmp ebx,0
  jl .nodraw
  cmp ebx,223
  jg .nodraw
  sub ecx,eax
  inc ecx
  mov edi,[vidbuffer]
  add edi,eax
  add edi,16
  mov eax,ebx
  shl eax,8
  add edi,eax
  mov eax,ebx
  shl eax,5
  add edi,eax
  mov eax,edx
  rep stosb
.nodraw
  ret

Code: Select all

const int vidBufferWidth = 256;
const int vidBufferHeight = 224;

void GUIHLine( unsigned char *vidBuffer, int startX, int endX, int y, unsigned char color ) {
    int width, ctr;
    unsigned char *dest;
    
    if( endX < 0 || startX >= vidBufferWidth ) return;

    if( startX < 0 ) startX = 0;
    if( endX >= vidBufferWidth ) endX = vidBufferWidth - 1;
    
    if( y < 0 || y >= vidBufferHeight ) return;
    
    width = endX - startX + 1;
    
    dest = vidBuffer + startX + 16 + (y * vidBufferWidth);
    dest += y * 32;
    
    for( ctr = width; ctr > 0; --ctr ) {
        *dest = color;
        ++dest;
    }
    
    return;
}
[u][url=http://bash.org/?577451]#577451[/url][/u]
Deathlike2
ZSNES Developer
ZSNES Developer
Posts: 6747
Joined: Tue Dec 28, 2004 6:47 am

Post by Deathlike2 »

It is not going to be ported, rather a GUI lib written from the ground up will be plugged in instead.
Continuing [url=http://slickproductions.org/forum/index.php?board=13.0]FF4[/url] Research...
Noxious Ninja
Dark Wind
Posts: 1271
Joined: Thu Jul 29, 2004 8:58 pm
Location: Texas
Contact:

Post by Noxious Ninja »

I guess I missed a lot while I've been gone. I remember an announcement a long time ago, although that was for a Qt or GTK GUI. So is this effort actually underway? As in, past the planning stage?

I still might do at least some of it for personal edification. Working with this code is not terribly far away from reverse-engineering.
[u][url=http://bash.org/?577451]#577451[/url][/u]
snkcube
Hero of Time
Posts: 2646
Joined: Fri Jul 30, 2004 2:49 am
Location: In front of the monitor
Contact:

Post by snkcube »

Noxious Ninja [gone] wrote:I guess I missed a lot while I've been gone. I remember an announcement a long time ago, although that was for a Qt or GTK GUI. So is this effort actually underway? As in, past the planning stage?
Ah yes, I also remember this. I also wonder the same thing.
Try out CCleaner and other free software at Piriform
Image
grinvader
ZSNES Shake Shake Prinny
Posts: 5632
Joined: Wed Jul 28, 2004 4:15 pm
Location: PAL50, dood !

Post by grinvader »

I'd have done such porting myself years ago if the GUI wasn't so strictly intertwined with DOS shit. Feel free to try.

GUI replacement (whatever it's gonna be) is on hold until we sort more important stuff out.

Oh, and hello there, btw.
皆黙って俺について来い!!

Code: Select all

<jmr> bsnes has the most accurate wiki page but it takes forever to load (or something)
Pantheon: Gideon Zhi | CaitSith2 | Nach | kode54
Noxious Ninja
Dark Wind
Posts: 1271
Joined: Thu Jul 29, 2004 8:58 pm
Location: Texas
Contact:

Post by Noxious Ninja »

Alright. I'm starting on some of the easier stuff. I'll post some patches in a bit.

As a question of style, which would you like better?

Code: Select all

for( ctr = width; ctr > 0; --ctr ) {
    *dest = color;
    ++dest;
}
or

Code: Select all

for( ctr = width; ctr > 0; --ctr, ++dest ) *dest = color;
[u][url=http://bash.org/?577451]#577451[/url][/u]
whicker
Trooper
Posts: 479
Joined: Sat Nov 27, 2004 4:33 am

Post by whicker »

Noxious Ninja [gone] wrote:Alright. I'm starting on some of the easier stuff. I'll post some patches in a bit.

As a question of style, which would you like better?

Code: Select all

for( ctr = width; ctr > 0; --ctr ) {
    *dest = color;
    ++dest;
}
or

Code: Select all

for( ctr = width; ctr > 0; --ctr, ++dest ) *dest = color;
I'm not a developer, but seriously, which one is more understandable and is less likely to contribute to off-by-one errors?

stupid comma (non)operator

and before Nach jumps in to insult my upbringing or intelligence level, is

Code: Select all

for( ctr = width; ctr > 0; --ctr, dest = dest + 1 ) *dest = color;
legal? good practice?
Noxious Ninja
Dark Wind
Posts: 1271
Joined: Thu Jul 29, 2004 8:58 pm
Location: Texas
Contact:

Post by Noxious Ninja »

I think my first form is more quickly comprehensible, but they're not too far apart to someone who knows C.

Your form is legal, but "dest = dest + 1" is unneeded verbosity.

BTW, I now have 21% of guitools.inc by line count converted to C.
[u][url=http://bash.org/?577451]#577451[/url][/u]
Noxious Ninja
Dark Wind
Posts: 1271
Joined: Thu Jul 29, 2004 8:58 pm
Location: Texas
Contact:

Post by Noxious Ninja »

One more thing. I am trying to use a macro like so:

Code: Select all

EXTSYM ccall_retval
; ccall FuncName, return reg32, param1, param2, param 3... ;C: Last-1st, stack cleanup
; puts return value in specified 32-bit register and leaves all other registers unmodified
%macro ccall 2-*                       
%define _f %1
%define _r %2
%assign __numparams %0-2
    pushad
%rep __numparams
    %rotate -1
    push %1
%endrep
    call _f
    %assign __paramsize __numparams * 4
    add esp, __paramsize
    mov ccall_retval, eax
    popad
    mov _r, ccall_retval
%endmacro
I have added

Code: Select all

extern unsigned int ccall_retval;
to gblvars.h, but whenever I try to use the macro, nasm complains:

Code: Select all

gui/guitools.inc:31: error: invalid combination of opcode and operands
I try to use it like this:

Code: Select all

ccall StringLength_cfunc, ecx, eax
because I haven't ported any of the the calling code yet, and the original asm macro places the return value in ecx.

What am I doing wrong?
[u][url=http://bash.org/?577451]#577451[/url][/u]
whicker
Trooper
Posts: 479
Joined: Sat Nov 27, 2004 4:33 am

Post by whicker »

Noxious Ninja wrote:I think my first form is more quickly comprehensible, but they're not too far apart to someone who knows C.

Your form is legal, but "dest = dest + 1" is unneeded verbosity.

BTW, I now have 21% of guitools.inc by line count converted to C.
Urgh, I failed my point am am going to gtfo now. I see now that in this loop it doesn't matter if the expression with the comma evaluates to the pointer plus one. I wasn't even intending anything about dest++ vs. the obvious verbosity in particular.
Noxious Ninja
Dark Wind
Posts: 1271
Joined: Thu Jul 29, 2004 8:58 pm
Location: Texas
Contact:

Post by Noxious Ninja »

Some people would use

Code: Select all

for( ctr = width; ctr > 0; --ctr ) *dest++ = color;
but I find it too easy to overlook what is actually happening here. I try to avoid all cases which would require use of post-increment or decrement, and split them into multiple lines. More code, but more clear.
[u][url=http://bash.org/?577451]#577451[/url][/u]
creaothceann
Seen it all
Posts: 2302
Joined: Mon Jan 03, 2005 5:04 pm
Location: Germany
Contact:

Post by creaothceann »

Noxious Ninja wrote:Alright. I'm starting on some of the easier stuff. I'll post some patches in a bit.

As a question of style, which would you like better?

[...]
Is there a reason it counts backwards? Go for the standard "least surprises" way.

Code: Select all

for (i = 0;  i < width;  i++)  {
        *dest = color;
}
PS: Just in case, see the comment on this page that starts with "I can't".
Last edited by creaothceann on Wed Apr 04, 2007 11:42 am, edited 1 time in total.
vSNES | Delphi 10 BPLs
bsnes launcher with recent files list
Noxious Ninja
Dark Wind
Posts: 1271
Joined: Thu Jul 29, 2004 8:58 pm
Location: Texas
Contact:

Post by Noxious Ninja »

Good point. The only reason I do that is because, if the compiler doesn't recognize it's only a counter and optimize it, it's slightly faster than a count-upward loop, mainly because the x86 doesn't have many registers. Same thing with pre- vs. post-increment - unless the compiler recognizes it's only a counter, post-increment entails making a temporary copy of the variable.

I think this is a good compromise - it's now immediately clear what's going on:

Code: Select all

for( pixelsLeft = width; pixelsLeft > 0; --pixelsLeft ) {
    *dest = (unsigned char)color;
    ++dest;
}
[u][url=http://bash.org/?577451]#577451[/url][/u]
sinamas
Gambatte Developer
Gambatte Developer
Posts: 157
Joined: Fri Oct 21, 2005 4:03 pm
Location: Norway

Post by sinamas »

Noxious Ninja wrote:I think this is a good compromise - it's now immediately clear what's going on:

Code: Select all

for( pixelsLeft = width; pixelsLeft > 0; --pixelsLeft ) {
    *dest = (unsigned char)color;
    ++dest;
}
If that's really a char*, then I'm sure you agree this is preferable: ;)

Code: Select all

memset(dest, color, width);
otherwise:
c++:

Code: Select all

std::fill_n(dest, width, color);
c:
I'd probably do

Code: Select all

for (ctr = width; ctr; --ctr)
        *dest++ = color;
but that's just personal preference of course.
creaothceann
Seen it all
Posts: 2302
Joined: Mon Jan 03, 2005 5:04 pm
Location: Germany
Contact:

Post by creaothceann »

Noxious Ninja wrote:if the compiler doesn't recognize it's only a counter and optimize it, ...
... it isn't worth much and you should use a better one. :wink:

Seriously though, that kind of guessing might not be accurate. Today's x86 CPUs are RISC internally (meaning they have more registers, at least in theory) and have to restructure the code anyway to fill their pipelines.

(If you're unsure you could write a small benchmark program.)
vSNES | Delphi 10 BPLs
bsnes launcher with recent files list
Nach
ZSNES Developer
ZSNES Developer
Posts: 3904
Joined: Tue Jul 27, 2004 10:54 pm
Location: Solar powered park bench
Contact:

Post by Nach »

whicker wrote:

Code: Select all

for( ctr = width; ctr > 0; --ctr, dest = dest + 1 ) *dest = color;
legal? good practice?
Legal, very bad practice. First off, no braces, second off, this is a while loop, not a for loop, third, you believe in decrement operator but not increment?

Here's more what it should be:

Code: Select all

while (width--) { *dest++ = color; }
However, if dest is of type char * (or unsigned char *), and color is of type char (or unsigned char), you want to use memset() here.
Noxious Ninja wrote:One more thing. I am trying to use a macro like so:

Code: Select all

EXTSYM ccall_retval
; ccall FuncName, return reg32, param1, param2, param 3... ;C: Last-1st, stack cleanup
; puts return value in specified 32-bit register and leaves all other registers unmodified
%macro ccall 2-*                       
%define _f %1
%define _r %2
%assign __numparams %0-2
    pushad
%rep __numparams
    %rotate -1
    push %1
%endrep
    call _f
    %assign __paramsize __numparams * 4
    add esp, __paramsize
    mov ccall_retval, eax
    popad
    mov _r, ccall_retval
%endmacro
I have added

Code: Select all

extern unsigned int ccall_retval;
to gblvars.h, but whenever I try to use the macro, nasm complains:

Code: Select all

gui/guitools.inc:31: error: invalid combination of opcode and operands
I try to use it like this:

Code: Select all

ccall StringLength_cfunc, ecx, eax
because I haven't ported any of the the calling code yet, and the original asm macro places the return value in ecx.

What am I doing wrong?
What you're doing wrong is trying to call C with parameters from assembly. We've already told you before to not do this. There is no agreed upon universal standard, and therefor attempting to do so is not portable.
May 9 2007 - NSRT 3.4, now with lots of hashing and even more accurate information! Go download it.
_____________
Insane Coding
Noxious Ninja
Dark Wind
Posts: 1271
Joined: Thu Jul 29, 2004 8:58 pm
Location: Texas
Contact:

Post by Noxious Ninja »

sinamas wrote:If that's really a char*, then I'm sure you agree this is preferable: ;)

Code: Select all

memset(dest, color, width);
Ooh, you're right. I got too caught up in being clever to notice that. D'oh.

I'm passing it in as int and then casting to char because the assembly code apparently kept the color in al, and I didn't want to make my ccall(void)? macros have to worry about ensuring things are 32-bit aligned. So I just pass in all of eax, and cast it, which picks out al. Once the code that calls it has been ported, it will probably change, but I don't understand ZSNES's palette system very well yet.

Nach wrote:What you're doing wrong is trying to call C with parameters from assembly. We've already told you before to not do this. There is no agreed upon universal standard, and therefor attempting to do so is not portable.
If you can show me any of the x86 C compilers we support where this doesn't work, I'll stop trying to do it. I know it's not portable across architectures, and even across all compilers, but I don't think I have to worry about this. I'm pretty sure worst case is a compiler switch to make it default to cdecl.

It wouldn't be terribly difficult to make my macros pass things through global variables. It's just more trouble on the C end that I wish to avoid if possible.
[u][url=http://bash.org/?577451]#577451[/url][/u]
byuu

Post by byuu »

It is possible to invoke a C function from assembly using any native calling convention of Visual C++ (cdecl, stdcall and fastcall), gcc (stdcall and fastcall), Borland (stdcall and fastcall), Watcom (stdcall and fastcall), and more. You can do this with up to two parameters. This will work on Windows, Mac OS X, Linux, Solaris and BSD at a minimum.

Or, if you want to go with only one, you can make this work even with Pascal. Pascal's big problem is left-to-right parameters, whereas everything else is right-to-left. But with one arg, that is not a problem.

Example:

Code: Select all

align 16
call_c_1arg:
  mov ebp,esp ;or copy esp to global if you think there's any chance ebp will be volatile
  and esp,-15
  sub esp,28 ;16-byte stack alignment for OSx86 ... may not be perfect
  mov ecx,%param ;fastcall
  push ecx ;stdcall and cdecl
  call %cfunction
  mov esp,ebp ;fix stack, regardless if stack cleanup is callee or caller
  ret
Return is always in eax, and all known implementations of fastcall use ecx as the first parameter and edx as the second. It's after that where the standards start to deviate.

You really only need one parameter, and you can pass a structure pointer to it, to pull out all of your parameters.

I realize in theory it is possible that there are architectures out there (or will be) where the above will not work. The idea then would be to use separate functions and special case them during compilation.

The alternative of never calling any C functions that take arguments seems absurd. How could you possibly allocate memory from assembly if you can't safely call malloc? Or do you have hardcoded C functions that call malloc for you with global variables? I'm not sure which is worse: possibly not supporting future targets we don't know about, or wrapping libc to work with global variables to reinvent function parameters :/

Anyway, I post this for the sake of discussion.
Bottom line: Nach is of higher seniority, so do what he says and don't pass parameters to C functions in ZSNES, please.
Noxious Ninja
Dark Wind
Posts: 1271
Joined: Thu Jul 29, 2004 8:58 pm
Location: Texas
Contact:

Post by Noxious Ninja »

Is cdecl the same across all those compilers you mentioned?

What I was thinking of doing is just having a set of global ints named param1, param2, etc. Stuff the args in there, then make the first lines of the C function something like

Code: Select all

unsigned char *vidBuffer = (unsigned char *)param1;
int x1 = param2, y1 = param3, x2 = param4, y2 = param5, color = param6;
Ugly, but it lets me still be able to write a ccall macro, and keep semantically meaningful names inside the function. I'm not sure it will work though - do C casts work like reinterpret_cast in C++?

EDIT: Actually, it looks like to be safe I'd have to do something along the lines of

Code: Select all

unsigned char *vidBuffer = *(unsigned char **)&param1;
Ugh.
[u][url=http://bash.org/?577451]#577451[/url][/u]
byuu

Post by byuu »

C casts are more flexible than reinterpret_cast, and far less hideous to look at. But don't let a C++ programmer here that you're using them. They'll bite your head off.

I honestly don't know if anything besides Visual C++ even uses cdecl. They probably do as you'd need that for vararg functions, because the ABI designers were too stupid to just specify that eax should contain the parameter count.

Amazing when you think about it that we can't even define a consistent method for calling functions, let alone ever wanting to standardize on something as complex as a desktop. Hahah.
Noxious Ninja
Dark Wind
Posts: 1271
Joined: Thu Jul 29, 2004 8:58 pm
Location: Texas
Contact:

Post by Noxious Ninja »

Well, I'm building my modified ZSNES with whatever version of g++ zget downloads, and assuming it uses cdecl seems to be working, with at least 6 parameters. I haven't tried it on Linux, though, and I don't have FreeBSD.

I just realized that Mac OS X could be a problem, with it's 16-byte stack alignment requirements, but that shouldn't be too hard to work around in a macro.
[u][url=http://bash.org/?577451]#577451[/url][/u]
sinamas
Gambatte Developer
Gambatte Developer
Posts: 157
Joined: Fri Oct 21, 2005 4:03 pm
Location: Norway

Post by sinamas »

Nach wrote:Here's more what it should be:

Code: Select all

while (width--) { *dest++ = color; }
You're right of course. A while loop is actually clearer in this case.
Noxious Ninja
Dark Wind
Posts: 1271
Joined: Thu Jul 29, 2004 8:58 pm
Location: Texas
Contact:

Post by Noxious Ninja »

Except I detest relying on the effects of post-increment/decrement.
[u][url=http://bash.org/?577451]#577451[/url][/u]
Noxious Ninja
Dark Wind
Posts: 1271
Joined: Thu Jul 29, 2004 8:58 pm
Location: Texas
Contact:

Post by Noxious Ninja »

You know what? If you find a platform where the pseudo-standard Intel C calling convention isn't supported, I'll write specific call macros for that platform. It's not worth dirtying up my C code. That's my final answer.
[u][url=http://bash.org/?577451]#577451[/url][/u]
byuu

Post by byuu »

Well, good luck getting ZSNES to use your code, then.

I agree with you in principle, however. Worry about non-conforming asshats when you find them. With ZSNES written in x86 anyway, you'll have to make at least some changes (ala OSX) to get things working anyway, what's the big deal about writing one more macro?
Post Reply