Flat model version
[BITS 16] ;Set code generation to 16 bit mode
[ORG 0x0100] ;Set code start address to 0100h
; the first 0x100 bytes (0x00-0xff) are for the
; PSP (program segment prefix)
SEGMENT .text ;Main code segment
; (can also be [SECTION .text]
; Sections are a bigger deal in the segmented
; model where sections map into segments
;
; it is unclear to me if segments do anything at
; all in flat-model executables. The binary
; compiled without these directives is identical
START: ; not really needed, since there are no jumps!
mov dx, eatmsg ; put the *memory address* of the eatmsg data
; in dx
mov ah, 9 ; DOS function 9 displays text to stdout.
; DOS expects string address to display to be in dx
int 21H ; makes call into DOS via s/w interrupt
int 21H ; heck, do it again for fun
; ready to quit. we'll call the DOS term_process
; command. If we skip this, really bad things
; happen
; note, this loads the value of the data
; stored at 'term_process'. Without the
; []'s it loads the address
mov ah, [term_process]
; also set the exit code so ERRORLEVEL is peachy
mov al, [exit_code]
int 21H ; make the DOS call happen
SEGMENT .data ; This is the initialized data segment (or section)
; this is a string variable. 13 and 10 are CR LF.
; DOS needs the '$' evidently to end the string
;
; note you cannot use C-style things like \n
eatmsg db "Eat at Joe's", 13, 10, "$"
term_process db 0x04c00
exit_code db 0x00
Segmented version
[BITS 16] ; 16 code
; this is actually optional; the code segment
; gets set up for us.
SEGMENT junk
..start: ; '..' tells linker to tell DOS to start executing
; here. This is needed for the segmented model
; because segments can get shuffled around by the linker
; and this may not be the first nor only segment
; setting up the segments
; We're going to be using the real mode segmented model, so we need
; to set up our three segments in the three corresponding segment
; registers. This code was written for NASM. If it was running
; in MASM, this would be handled by the ASSUME directive.
; Each segment has a name, and the names are identifiers of their
; segment addresses.
; Note you can't move an address directly into a segment register.
; You must first move it into a GP register.
mov ax, data_seg ; load the data's segment address. Note,
; mov ds, data_seg would be illegal.
mov ds, ax ; DOS needs DS to be set right for the INT 21h
; call. It assumes DS is the segment for the
; offset in DX
mov ax, stack_seg ; load the stack's segment address
mov ss, ax
mov sp, stacktop ; point SP to top of the stack
mov dx, eatmsg ; same as before
mov ah, 9
int 0x21
mov ax, 0x04c00
int 0x21
; by convention, this would be called the 'data' segment
; but I've named it conspicuously for clarity (ditto
; for the stack segment). You can have more than
; one of these!
SEGMENT data_seg
eatmsg db "Eat at Joe's!", 13, 10, "$"
; we need to tell the linker that this is a special
; segment - not a data segment.
;
; There must be one, and only one stack segment
SEGMENT stack_seg stack
; means segment of type 'stack'
; that's named 'stack_seg'
resb 64 ; reserve 64 bytes
stacktop: ; this label points to the LAST of the
; reserved 64 bytes. Won't work if it
; was the first!
;
; As this label shows, labels are like
; markers for code *OR* memory, and
; work more informally than with other
; languages
Using procedures and a Macro
Notes
-
CALL / RET work more or less the same as INT / IRET save they don't have to do the TheX86SoftwareInterruptTable indirection for the jump
- Functions can have multiple entry points!
[BITS 16] ;Set code generation to 16 bit mode
[ORG 0x0100]
%macro GotoXY 2 ; new parms - x & y
mov dh, %2 ; Y goes into DH
mov dl, %1 ; X goes into DL
mov ah, 0x02 ; BIOS VIDEO service 2 - posn. cursor
mov bh, 0 ; stay with display page 0
int 10h ; call VIDEO
%endmacro
SEGMENT .text
START:
call ClrScr
; jmp Die
GotoXY 4,4
call DefaultWrite
GotoXY 5,5
mov dx, msg1
call WriteLn
GotoXY 6, 6
mov dx, msg2
call WriteLn
Die:
mov ah, [term_process]
mov al, [exit_code]
int 0x21
DefaultWrite:
mov dx, defaultMsg
Write:
mov ah, 9
int 0x21
ret
WriteLn:
call Write
mov DX, CRLF
call Write
ret
ClrScr:
mov CX, 0
mov DX, [MAX_XY]
ClrWin:
mov AL, 0
ScrlWin:
mov BH, 0x07 ; 'normal' attribute for blanked line(s)
VIDEO6:
mov AH, 0x06
INT 0x10
ret
SEGMENT .data
msg1 db "This is line one", "$"
msg2 db "This is line two", "$"
defaultMsg db "This is a default string", "$"
CRLF db 0x0D, 0x0A, '$'
MAX_XY dw 0x182f ; 80 x 25 (zero based)
term_process db 0x04c00
exit_code db 0x00
Multiple object files!
del *.exe
del *.obj
..\nasmw.exe vidlib.ASM -f obj -o vidlib.obj
..\nasmw.exe eat4.asm -f obj -o eat4.obj
..\alink eat4.obj vidlib.ob
; compile this file like this:
; nasmw.exe vidlib.ASM -f obj -o vidlib.obj
[BITS 16]
; note, no need for an '[ORG 0x0100]' statment
; Note: both the functions/data AND the segments
; must be declared properly for external interaction
; ** IF THE SEGMENTS HAVE DIFFERENT NAMES BETWEENT
; FILES, THIS WON'T ALWAYS WORK! **
;
; e.g., 'defaultMsg' resolved to 0x00 if this data
; segment is defined as anything other than 'data_seg'
; These items are defined externally.
SEGMENT data_seg PUBLIC
EXTERN CRLF, LRXY, defMsg
defaultMsg db "This is a default string", "$"
; This segment can be accessed externally
SEGMENT code_seg PUBLIC
GLOBAL GotoXY, ClrScr, ClrWin, ScrlWin, VIDEO6
GLOBAL DefaultWriteLn, Write, WriteLn
GotoXY:
mov ah, 0x02 ; BIOS VIDEO service 2 - posn. cursor
mov bh, 0 ; stay with display page 0
int 10h ; call VIDEO
ret
ClrScr:
mov CX, 0
mov DX, word [LRXY] ; ? word ?
ClrWin:
mov AL, 0
ScrlWin:
mov BH, 0x07 ; 'normal' attribute for blanked line(s)
VIDEO6:
mov AH, 0x06
INT 0x10
ret
Write:
mov ah, 9
int 0x21
ret
DefaultWriteLn:
mov dx, defaultMsg
WriteLn:
call Write
mov DX, CRLF
call Write
ret
; compile with
; nasmw.exe eat4.asm -f obj -o eat4.obj
;
; then link against vidlib.obj like so
; alink eat4.obj vidlib.obj
; (seems like you have to have the file with the
; entry point listed first)
[BITS 16]
EXTERN GotoXY, Write, WriteLn, DefaultWriteLn, ClrScr
SEGMENT code_seg PUBLIC
..start:
; get our segments all set up
mov ax, data_seg
mov ds, ax
mov ax, stack_seg
mov ss, ax
; point sp to top of stack
mov sp, stacktop
call ClrScr
; Load **WORD** memory address of 'TextPos' with immediate.
; note, there is no 'byte' sized address pointer.; 0x0914 = x @ 20, y @ 9
mov word [TextPos], 0x0914 ; 'word' qualifies the pointer
mov DX, [TextPos] ;
call GotoXY
mov DX, msg1
call Write
; re-use 'TextPos', but overwrite the Y coord
mov DX, [TextPos]
mov DH, 0x0A
call GotoXY
mov DX, msg2
call WriteLn
; re-use 'TextPos', but overwrite the Y coord
mov DX, [TextPos]
mov DH, 0x0b
call GotoXY
call DefaultWriteLn
mov AH, 4CH
mov AL, 0
int 21H
SEGMENT data_seg PUBLIC
GLOBAL LRXY, CRLF, defMsg
LRXY DW 0x184F ; 24x79
TextPos DW 0
msg1 DB "Message number 1", "$"
msg2 DB "Second message number 2", "$"
CRLF DB 0x0D, 0x0A, '$'
SEGMENT stack_seg stack
resb 64
stacktop:
--
MattWalsh - 17 Apr 2004