1

I don't understand an assembly language, and in my school they do this just to show what is assembly language and how to compile.

Can you guys give me some basic hello world example using assembly language that can run with tccasm?

any help appreciated

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847

1 Answers1

5

Here's a 32 bit Windows hello world:

.global  main

.section .text
main:
    leal    message, %eax
    pushl   %eax // the C convention is params are pushed onto the stack
    call    printf
    popl    %eax // and the caller is responsible for cleanup
    pushl   $0
    call    exit
    hlt // never reached
.data
message:
    .string  "Hello, world!\n\0"

Build with this: tcc test.s -lmsvcrt (the filename has to end with .s for tcc to recognize it as an asm file)

That calls C functions to do the work. You can also use just Windows functions:

.global  main

.section .text
main:
    pushl $-11 // STD_OUTPUT_HANDLE
    call GetStdHandle // the return value will be in eax

    // now we'll call WriteFile. Args are pushed from right to left
    pushl $0 // lpOverlapped == null, don't want async
    pushl $0 // lpNumberOfBytesWritten == null, we don't care
    pushl $14 // nNumberOfBytesToWrite, our hello world has 14 chars.
    leal message, %ebx // load address for lpBuffer, pointer to the string to write
    pushl %ebx // and push it to the stack
    pushl %eax // the return value we got from GetStdHandle
    call WriteFile // and write the file
    // unlike the C convention, the Windows functions clean up their own stack
    // so we don't have to pop that stuff

    // and now we'll call ExitProcess to get out
    pushl   $0 // our main's return value of 0 == success
    call    ExitProcess
    hlt // never reached
.data
message:
    .string  "Hello, world!\n\0"// so we don't have to pop that stuff

no need for the C lib here, so build with a simple tcc test.s, and when you run it from a console, you should see the message in there.

Fun fact: the Windows functions are easy enough to use that you could pop up a message box simply enough:

.global  main

.section .text
main:
    pushl $0 // MB_OK
    pushl $0 // title = null (will use the default of "Error")
    leal message, %eax // load our message
    pushl %eax // and push the string pointer to the argument list
    pushl $0 // hwnd == null, no owning window
    call MessageBoxA // and pop up the message box
    pushl   $0
    call    ExitProcess
    hlt // never reached
.data
message:
    .string  "Hello, world!\n\0" // don't forget the zero terminator!

MessageBoxA is found in user32, so build with tcc test.s -luser32, run and you'll get that.

This is all win32 because I don't know a lot of win64, but the 32 bit program should still work just as well and you can play around with it. tcc uses the AT&T assembler syntax, which isn't as common, but to convert nasm or most other assembler's Intel syntax to it, remember that:

  • it needs the length suffixes on the instruction. so pushl instead of push when pushing a long, aka 32 bit value.

  • immediates, number literals, are prefixed with the $. Actually, now that I write this, I realize I could have written pushl $message above instead of the two-step leal message, %eax pushl %eax. Oh well, both ways work. But if you forget the $, it tries to use it as a pointer and you're likely to see illegal read/write at small looking memory addresses when loading things.

  • register names need the % prefix

  • It goes source, destination, which is the opposite of Intel syntax's destination, source. (I learned the intel way as a newb, so AT&T syntax feels backward to me!)

But if you remember those four differences, you can translate Intel syntax code to AT&T syntax code for tcc fairly easily. A minor change is that tcc uses C style comments ( // comment ), whereas most other assemblers use ; command

Another thing with tcc is you don't have to declare extern functions, nor use the argument length suffix or underscore prefix. You write it almost like in C, though the ASCII or Wide suffix needs to be there (those are macros in C; #define MessageBox MessageBoxA and so in in the Windows headers, changing out the A for W if you are compiling with unicode support. The difference is the A versions take ascii strings - 8 bits per character, can't access all characters, and the W versions take 16 bit unicode strings.).

Side note: the actual mangled name of Win32 functions is something like _MessageBoxA@16. You can see we did use the 'A' in the example code, as I just mentioned, but not the _ nor the @16. Those bits of the name are hidden from C and are used to help catch wrong number of arguments - @16 means it expects 16 bytes of arguments, for Message Box in 32 bit, that's the hwnd (4, HWND), pointer to message (4, char*), pointer to title (4, char*), and the flags (4, an int). If you see example code on the internet calling functions with these changes, that's why it is there, and you do not need it with tcc.

That should get you started!

Thomas S.
  • 5,804
  • 5
  • 37
  • 72
Adam D. Ruppe
  • 25,382
  • 4
  • 41
  • 60
  • 1
    well i tried your code, but there's a little error at line 6, it said "unknow opcode: pushl" – user3005966 Nov 20 '13 at 05:38
  • Maybe it is because my tcc is so old.... mine is tcc version 0.9.26. (edit, the internet tells me that is the newest version...) but still, what version do you have? I can get the same one and update the answer. It might also work to say "push" instead of "pushl". – Adam D. Ruppe Nov 20 '13 at 15:05
  • i just download it recently, it should be the same right?and yeah i tried with push but still no luck – user3005966 Nov 22 '13 at 17:06
  • run tcc without arguments and check the first line of the output for the version number – Adam D. Ruppe Nov 22 '13 at 20:22
  • yep it's same, mine is 0.9.26 – user3005966 Nov 23 '13 at 06:07
  • 1
    All I can imagine that might be different is my 32 bit and your 64 bit. What about `tcc -m32 test.s`, any luck with that? – Adam D. Ruppe Nov 23 '13 at 14:56
  • it said "i386-win32-tcc" not found, so is it not support 32bit? – user3005966 Nov 24 '13 at 03:46
  • apparently not. The one I used is `tcc-0.9.26-win32-bin.zip` from http://download.savannah.gnu.org/releases/tinycc/ Maybe if you download that too you can get 32 bit support. but I don't know a lot of 64 bit Windows assembly (I can do some on Linux but my windows box is 32 bit so never tried that), so if you do want to learn about 64 bit, probably best to wait for another answer or start a new question specifically about that. That said, the 32 bit program should still work fine on win64 so if you download that, you can at least get started with what i wrote here. – Adam D. Ruppe Nov 24 '13 at 03:51
  • oh thanks, will do that then well let me ask again when there is an error or something :D – user3005966 Nov 24 '13 at 07:04
  • it did work with tcc 32bit :D now can you or someone explain this code to me?(why eax, or means for leal,push) – user3005966 Nov 24 '13 at 07:12
  • yeah, it'll take me some time to write it up. You can also search for just about any assembly tutorial - eax is a register, push is an instruction that works with the stack, both pretty basic concepts that a tutorial should cover. I'll probably be able to write up more by the weekend. – Adam D. Ruppe Nov 26 '13 at 16:15
  • It would be perfect, if the examples could be extended to compile to 64-bit executables. – Thomas S. Aug 02 '23 at 15:05