Load, run with F9, go through step by step with F8.
The ZIP has small programs for Windows (Intel and ARM) and DOS. On this page you see them with the code the assembler makes of them, and with the result. The files are in the folder examples of the ZIP; each opens in Asmonite and runs in the debugger with F9.
The examples with func are for 32-bit Windows (.arch i386).
n! = n · (n−1)!, and 0! = 1! = 1. The function calls itself: func makes the frame, if / else the comparison and the jumps. File: examples\func\func_fact.asm.
func Fact(n) uses ebx begin if unsigned n > 1 mov ebx, n dec ebx call Fact(ebx) imul eax, n else mov eax, 1 endif end
Fact: push ebp mov ebp, esp push ebx cmp dword [ebp+8], 1 jbe else1 mov ebx, dword [ebp+8] dec ebx push ebx call Fact imul eax, dword [ebp+8] jmp end1 else1: mov eax, 1 end1: pop ebx pop ebp ret 4
func, if, else, call)
your line, the names are replaced
unchanged
Three things are written that way on purpose. There are no expressions and no calls inside a call: call Fact(n-1) is not possible, so dec ebx counts down first and call Fact(ebx) gets the result. n stays in the frame of the function, so imul eax, n after the call works with its own n. ebx is in uses so the caller gets it back unchanged. An ordered comparison says signed or unsigned.
func FactLoop(n) begin mov ecx, n mov eax, 1 if unsigned ecx > 1 do imul eax, ecx dec ecx while unsigned ecx > 1 endif end
FactLoop: push ebp mov ebp, esp mov ecx, dword [ebp+8] mov eax, 1 cmp ecx, 1 jbe end1 loop1: imul eax, ecx dec ecx cmp ecx, 1 ja loop1 end1: pop ebp ret 4
The if around the loop skips it for 0 and 1; while jumps back to the do as long as ecx is greater than 1. The call in the main part and the output:
start: call Fact(10) call [printf](fmt, 10, eax) call FactLoop(5) call [printf](fmt, 5, eax) call [ExitProcess](0)
10! = 3628800 5! = 120
.import msvcrt.dll cdecl printf says that printf does not remove the arguments itself; call [printf](...) cleans up afterwards. The arguments are in the order of the function: the format first.
if on x64, ARM 64 and Thumbif, else and do … while exist for .arch x64, .arch arm64 and .arch thumb too (func and call Name(args) not yet). You write the operands as the architecture knows them; the assembler makes cmp (or tst) and the jump with the opposite condition around the block of it.
if w1 == 5 add w0, w0, #1 endif if signed w1 < 3 add w0, w0, #100 else add w0, w0, #2 endif if unsigned w2 > w1 add w0, w0, #4 endif
cmp w1, #5 b.ne endif1 add w0, w0, #1 endif1: cmp w1, #3 b.ge else2 add w0, w0, #100 b end2 else2: add w0, w0, #2 end2: cmp w2, w1 b.ls endif3 add w0, w0, #4 endif3:
if, else, endif); the names of the labels are simplified here
unchanged
A word like signed or unsigned says how to compare (see course, lesson 3); if w3 & w4 is tst; if zero uses the flags of the instruction before it. On ARM there are no memory operands in if: load into a register first. The complete programs: examples\windows_intel\if_x64.asm, examples\windows_arm\if_arm64.asm and examples\windows_arm\if_arm32.asm. Each ends with the exit code 31 (= 1 + 2 + 4 + 8 + 16): every block that runs adds its number, so the exit code shows which conditions were true.
Folder examples\func: functions, calls, if and loops for 32-bit Windows. The result is the output or the exit code (echo %errorlevel%).
| File | Shows | Result |
|---|---|---|
func_add.asm | parameters and the stack frame without counting offsets | exit code 12 |
func_call.asm | call Name(arguments): the assembler pushes the arguments and cleans up | exit code 33 |
func_call_locals.asm | locals as arguments: an array, a byte, & for an address | exit code 36 |
func_locals.asm | local variables, an array, uses and an early ret | exit code 39 |
func_cdecl.asm | cdecl functions: the caller cleans up; wsprintfA into a local buffer | message box |
func_if.asm | if / else / endif: the larger of two numbers | exit code 7 |
func_loop.asm | do … while: loops without a label of your own | output |
func_fact.asm | the factorial recursive and as a loop (above) | 10! = 3628800 |
func_printf.asm | printf of the C runtime is cdecl: .import msvcrt.dll cdecl printf | output |
func_console.asm | text to the console: GetStdHandle, lstrlenA, WriteFile | output |
func_file.asm | files: write, read, close and delete with CreateFileA (seven arguments) | exit code 11 |
func_computername.asm | the name of this computer: GetComputerNameA(buffer, &size) | exit code = length |
func_time.asm | the local time in a message box: GetLocalTime, wsprintfA | message box |
func_yesno.asm | MessageBoxA with Yes / No; the answer is the exit code | exit code 6 or 7 |
| Folder | Content |
|---|---|
windows_intel | hello world, a message box and a window, each for 32 bit (.arch i386) and 64 bit (.arch x64); demo.asm counts from 1 to 5 and is good for the first steps through the debugger; if_x64.asm shows if / else |
windows_arm | hello world and a message box for Windows on ARM, 32 bit (Thumb-2) and 64 bit (ARM64); if_arm32.asm and if_arm64.asm show if / else |
dos | hello world for DOS as .COM and as .EXE, and a prebuilt HELLO.EXE to examine in Dismonite |
Open a file from examples in Asmonite (Ctrl+O) and press F9: assemble and start in the debugger. F8 goes one step on; with func, if and call one line of your text is one step, not the generated instructions. Without the window: asmoc func_fact.asm writes the .exe next to the file.
Parameters, locals, uses and call Name(...) with the generated instructions.