VOGONS


First post, by QuestionmarkMan

User metadata
Rank Newbie
Rank
Newbie

Hi, I'm using 16-bit TASM compiler with DOSBox and would like to know how to include the dosbox printing function in my assembly code.
What I'm trying to do is similar to the following (however that is with NASM and I need TASM. i.e. something that would work with an 8086):

global _main
extern _printf ; What would be its equivalent in TASM?
section .data
msg db "Hello World!", 0Dh, 0Ah, 0
section .bss
section .text
_main:
push ebp
mov ebp, esp

push msg ;How do we do
call _printf ; this with TASM?

add esp, 4
mov esp, ebp
pop ebp
ret

Reply 1 of 1, by reenigne

User metadata
Rank Oldbie
Rank
Oldbie

The APIs that DOSBox provides to user programs are the same as those provided by DOS, so it seems like this question is really about DOS programming, not about DOSBox.

Btw, NASM works fine for targeting 8086/8088 - you just need a "cpu 8086" directive at the top of your program. Either way, you will have to use the 16-bit registers (like bp and sp) instead of the 32-bit ones (like ebp and esp) if you want to target 8086.

The 8086 doesn't have a "push iw" instruction, so instead of:

push msg

you will have to use:

mov ax,msg
push ax

.

You'll also need a definition of the printf function, which you can get by assembling your program to an .obj file and then linking it with a C runtime library and startup. Exactly how to do that depends on the exact assembler, linker and C library you're using. It may be as simple as adding c0s.obj and cs.lib to the linker command line.

If you want to write the entire program in assembly and not link with external libraries (like the C runtime library) you'll need to supply your own printing function. If you don't need to do substitutions like %s then it's fairly straightforward to call the BIOS or DOS output interrupts directly. However, none of these work with zero-terminated strings (that's a C thing, not so much an OS thing) so you'll either need to write your own loop to print character by character or count the number of characters to output yourself. Here's some example code for the latter:

push di ; Standard 8086 calling conventions mandate that di and si are callee-save.
mov ax,ds
mov es,ax ; scasb works with es:di. Assume string is at DS:msg
mov di,msg
mov al,0
mov cx,-1
repnz scasb ; Find end of string in DI
lea cx,[di-msg] ; Subtract to find length
mov bx,1 ; Handle for stdout
mov ah,40h
mov dx,msg
int 21h ; DOS call to write to file/stream handle
pop di

I'm not sure offhand if ES is also a callee-save register on any/all x86 calling conventions - if calling this code from compiled code you should check this.