Last time we ended with FASMLIB win32 program template:
include "%FASMINC%/win32axp.inc"
SYSTEM equ win32
include "fasmlib/fasmlib.inc"
include "fasmlib/process.inc"
include "fasmlib/mem.inc"
include "fasmlib/str.inc"
.code
start:
;initialize modules
call mem.init
jc error
call str.init
jc error
; ... do the work ...
;uninitialize modules
call str.uninit
jc error
call mem.uninit
jc error
;exit process
push dword 0
call exit
error:
;try to uninitialize what we can, without further error checking
call str.uninit
call mem.uninit
;exit with code -1
push dword -1
call exit
.data
IncludeIData
IncludeUData
.end start
Today we are gonna display some string on console. First we need to create a console. There is a flag in windows PE (win32 EXE) file header, that says Windows to create console for us. You can set this flag by using
console keyword in
format PE line. In previous example there was no
format PE, so
win32axp.inc used default
format PE GUI. Now we will set up format ourselves, like this:
format PE console
;------------ rest of code stays same: ----------------
include "%FASMINC%/win32axp.inc"
SYSTEM equ win32
include "fasmlib/fasmlib.inc"
include "fasmlib/process.inc"
include "fasmlib/mem.inc"
include "fasmlib/str.inc"
.code
start:
;initialize modules
call mem.init
jc error
call str.init
jc error
; ... do the work ...
;uninitialize modules
call str.uninit
jc error
call mem.uninit
jc error
;exit process
push dword 0
call exit
error:
;try to uninitialize what we can, without further error checking
call str.uninit
call mem.uninit
;exit with code -1
push dword -1
call exit
.data
IncludeIData
IncludeUData
.end start
Try to run this example, not from command line, you should see console window appear.
Now we have console created, how to access it? Right now, it can be done with
stream module. I hope you know what is stream, i am not going to explain right now,i just can say it's something you read/write data from/to. For example file can be stream too, but when you take it as stream you only can read it once, and you cannot seek in it.
You access streams through
stream handles (they are just 32bit dwords). These are either returned from some procedures, or you can use predefined system handles:
When we have console created, Windows creates 3 default streams for us:
STDIN - standard input - you can read input from here, eg. what user writes to console
STDOUT - standard output - you write all your output (except errors) to console through this stream
STDERR - standard error - you write error information and debugging information to this stream. Normaly this stream is same as STDOUT, but if user wants he can redirect it so he can separate normal and error/debug output.
After you initialize
stream module, you can access these streams thorugh these variables containing predefined handles:
stream.stdin,
stream.stdout,
stream.stderr.
First we will only write to stdout. To write to stream we can use procedure
stream.writebuf with arguments:
stream_handle, buffer_address, buffer_size, which writes to stream data from buffer of given size. So the code will be:
format PE console
include "%FASMINC%/win32axp.inc"
SYSTEM equ win32
include "fasmlib/fasmlib.inc"
include "fasmlib/process.inc"
include "fasmlib/mem.inc"
include "fasmlib/str.inc"
include "fasmlib/stream.inc" ;we are using this module
.code
start:
;initialize modules
call mem.init
jc error
call str.init
jc error
call stream.init ;initialize "stream" module
jc error
;display _string
;remember arguments are pushed in reversed order (right to left)
push dword _string_length ;size of data
push dword _string ;pointer to buffer with data
push [stream.stdout]
call stream.writebuf
jc error
;uninitialize modules
call stream.uninit ;uninitialize "stream" module
jc error
call str.uninit
jc error
call mem.uninit
jc error
;exit process
push dword 0
call exit
error:
;try to uninitialize what we can, without further error checking
call stream.uninit ;don't forget it here
call str.uninit
call mem.uninit
;exit with code -1
push dword -1
call exit
.data
_string db "Hell o' world" ;data we will write to console
_string_length = $ - _string ;numeric constant holding data length
IncludeIData
IncludeUData
.end start
so in data segment we defined data we wanted to write, and we used stream.writebuf to display it, i hope it's clear. This is working HelloWorld example.
We can also signal error, in a simple way (for now). In case of error, we can try to write "Error" to stderr. It isn't always possible, like when stream module isn't initialized yet, so we won't check error on that one:
format PE console
include "%FASMINC%/win32axp.inc"
SYSTEM equ win32
include "fasmlib/fasmlib.inc"
include "fasmlib/process.inc"
include "fasmlib/mem.inc"
include "fasmlib/str.inc"
include "fasmlib/stream.inc"
.code
start:
;initialize modules
call mem.init
jc error
call str.init
jc error
call stream.init
jc error
;display _string
push dword _string_length
push dword _string
push [stream.stdout]
call stream.writebuf
jc error
;uninitialize modules
call stream.uninit
jc error
call str.uninit
jc error
call mem.uninit
jc error
;exit process
push dword 0
call exit
error:
;try to write error message to stderr
push dword _error_length ;new things
push dword _error ;
push [stream.stderr] ;
call stream.writebuf ;
;no error checking here. stream.writebuf is free to fail
;try to uninitialize what we can, without further error checking
call stream.uninit
call str.uninit
call mem.uninit
;exit with code -1
push dword -1
call exit
.data
_error db "Error" ;new things here too
_error_length = $ - _error ;
_string db "Hell o' world"
_string_length = $ - _string
IncludeIData
IncludeUData
.end start
I think that's all for now, i don't want to mess too much things into this chapter. Next time we will learn how to use dynamic string library, get deeper on errors, and then later we will see how things are really used in FASMLIB, eg. i will explain some high-level macros to ease your work.
PS: if you didn't know what redirecting stream is, try compiling this example, like
fasm.exe test.asm test.exe
and then run it with
test.exe >output.txt
You will see all output goes to file
output.txt, handy thing. This is what streams are for.