On Windows and other platforms, you can always simulate interrupts by writing a wrapper in exception handler:
%define WIN32_LEAN_AND_MEAN
%include "windows.inc" ; CONTEXT structure, exception constants
section .data
$szHeloText: db "hello world\r\n",0
section .text
global _main
_main:
; setup structured exception handler
; EXCEPTION_REGISTRATION_RECORD struct {next, handler, optional userdata...}, ntddk.inc
; on winXP the EXCEPTION_REGISTRATION_RECORD node MUST be allocated on the stack.
push dword seHandler
push dword[fs:0] ; save old EXCEPTION_REGISTRATION_RECORD pointer
mov dword[fs:0],esp
; display a string using custom implementation for int 16
mov edx,$szHeloText
int 10h ; 00401118 cd10 int 10h access violation
; remove exception handler
pop dword[fs:0]
pop edx ; handler
mov eax,0
ret
seHandler: ; EXCEPTION_DISPOSITION __cdecl _exception_handler(EXCEPTION_RECORD *,EXCEPTION_REGISTRATION_RECORD *,CONTEXT *,void *)
; prototype in excpt.inc
mov eax,ExceptionContinueSearch ; assume unhandled exception
; check if we have access violation
mov edx,[esp+4]
cmp dword[edx+EXCEPTION_RECORD.ExceptionCode],STATUS_ACCESS_VIOLATION
jne .q
; check the instruction, int 16 -> CDh 10h
mov ecx,[edx+EXCEPTION_RECORD.ExceptionAddress]
; TODO: check if memory at ecx is readable
; invoke IsBadCodePtr,ecx; return ExceptionContinueSearch if eax!=0
cmp word[ecx],10CDh ; int 16 ?
jne .q
; yup! Skip it
mov edx,[esp+12]
add dword[edx+CONTEXT.Eip],2
; output the string
extern __imp_puts
push dword[edx+CONTEXT.Edx]
call [__imp_puts]
pop edx ; puts is cdecl
mov eax,ExceptionContinueExecution ; the exception was handled
.q:
ret
Or using a wrapper function:seHandler:
mov eax,ExceptionContinueSearch ; assume unhandled exception
; check if we have access violation
mov edx,[esp+4]
cmp dword[edx+EXCEPTION_RECORD.ExceptionCode],STATUS_ACCESS_VIOLATION
jne .q
; check the failing instruction
mov ecx,[edx+EXCEPTION_RECORD.ExceptionAddress]
; TODO: check if memory at ecx is executable
cmp word[ecx],10CDh ; int 16 ?
jne .q
mov edx,[esp+12] ; load CONTEXT* to edx
; redirect execution to int16wrapper function
mov eax,[edx+CONTEXT.Eip]
add eax,2 ; skip int 16
mov dword[edx+CONTEXT.Eip],int16wrapper
; put ExceptionAddress+2 and CONTEXT.Edx onto stack
mov ecx,[edx+CONTEXT.Esp]
mov [ecx-8],eax ; return address = ExceptionAddress+2
mov eax,[edx+CONTEXT.Edx]
mov [ecx-4],eax ; char* = CONTEXT.Edx
sub dword[edx+CONTEXT.Esp],8
mov eax,ExceptionContinueExecution
.q:
ret
align 4
int16wrapper: ; void __stdcall int16wrapper(char*)
extern __imp_puts
push dword[esp+4]
call [__imp_puts]
pop eax
ret 4