Here's how to do it:
; test.asm
;
; nasm -fwin32 -o test.obj test.asm
; link test.obj /debug:none /release /subsystem:console msvcrt.lib legacy_stdio_definitions.lib
;
bits 32
section .rodata
str:
db `Hello\n`,0
section .text
; Older MSVCRT exports _cprintf, but you must link with legacy_stdio_definitions.lib.
extern __cprintf
global _main
align 4
_main:
push str
call __cprintf
add esp,4
ret
Now, take a look at:
C:\Work> dumpbin /disasm test.exe
3308 bytes in .text, 512 bytes in .data, 2704 bytes in .rodata and 344 in relocations in a simple 'Hello world' program. And lots of code!
Now, this one, to mimic your previous code (with CORRECTIONS):
; test.asm
;
; Better approach WITHOUT using libc!
; Why better? Because this code don't need to load MSVCRT???.DLL and all C initialization code.
;
; Unfortunately Windows Console applications must use Console API from kernel32.dll.
;
; nasm -fwin32 hello.asm -o hello.o
;
; if using Visual Studio:
; link hello.o /debug:none /release /subsystem:console /defaultlib:kernel32.lib /entry:start
; or, with mingw32:
; ld -s -o hello.exe hello.o -lkernel32
;
; They will create the same image...
;
; Yep, it is the default, but I like to be explicit.
bits 32
; Handle put in .bss not to occupy space in the image.
section .bss
handle:
resd 1
; Notice these buffers won't be changed!
section .rodata
; Little test: Will change '-' to `\r\n` on terminal.
str:
db `Hello-World-`
str_len equ $ - str
crlf:
db `\r\n`
crlf_len equ $ - crlf
section .text
; As defined in Win32 Console API.
%define STD_OUTPUT_HANDLE -11
extern __imp__GetStdHandle@4
extern __imp__WriteConsoleA@20
extern __imp__ExitProcess@4
global _start
align 4 ; always a good ideia to align entrypoints.
_start:
%ifdef OBEY_ABI
push ebx ; Obey MS-ABI and preserve EBX and EDI!
push edi ; Not really necessary here!
%endif
; Gets STDOUT_HANDLE from Console API.
push byte STD_OUTPUT_HANDLE
call [__imp__GetStdHandle@4]
mov [handle],eax
; prepare to scan the string searching for '-' in the loop below.
lea edi,[str]
mov ecx,str_len
align 4 ; always a good idea to align LOOPs entrypoints.
.loop:
mov al,'-'
mov ebx,edi ; keep track of the substring start
repne scasb
jne .exit ; Not found, exit the loop.
call print_substring
call print_crlf
jmp .loop
.exit:
; --- Exit from program
%ifdef OBEY_ABI
pop edi ; Restore EBX and EDI.
pop ebx ; Not really necessary here!
%endif
; Don't need to close the stdout handle!
; Must exit jumping to ExitProceess!
push byte 0
jmp [__imp__ExitProcess@4]
; Prints the substring not including the '-' found.
; Entry: EBX = start of substring,
; EDI = one char past the '-' found.
; Destroys EAX (at least).
; Preserve ECX to continue scanning.
align 4
print_substring:
push ecx
lea eax,[edi-1]
sub eax,ebx
jz .exit
push byte 0
push byte 0
push eax
push ebx
push dword [handle]
call [__imp__WriteConsoleA@20] ; Since ABI allows changes no ECX it was preserved earlier.
.exit:
pop ecx
ret
; Just print CRLF. Preserve ECX to conitnue scanning.
align 4
print_crlf:
push ecx
push byte 0
push byte 0
push crlf_len
push crlf
push dword [handle]
call [__imp__WriteConsoleA@20] ; Since ABI allows changes no ECX it was preserved earlier.
pop ecx
ret
124 bytes in .text, 0 bytes in .data, 4 bytes in .bss, 14 bytes in .rodata, 28 bytes in relocations (7 relocations), and 94 bytes in imported ptrs (from kernel32.dll)...
C:\Work> dir *.exe
Volume in drive C is OS
Volume Serial Number is 9E55-4C71
Directory of C:\Work
09/01/2023 12:03 3.072 test.exe
09/01/2023 12:31 9.216 test2.exe
Again, 6 KiB of unecessary code and data to initialize libc... blagh!