; Создаем сегмент стека. Размер стека - 256 байт,
; стек выравнен на границу параграфа (para).

stack   segment para stack

; Резервируем 256 байт для стека.

	db      100h dup (?)

stack   ends

; Создаем сегмент данных. Этот сегмент выравнен на
; границу двухбайтового слова (word).

data    segment word

; Строка, которую программа выведет на экран.

msg     db      "Hello, world.", 13, 10, "$"

data    ends

; В этом месте расположен сегмент кода. Он содержит
; выполняющуюся программу.

code    segment

; Директива assume сообщает ассемблеру, как будут
; использоваться сегментные регистры. Эта директива
; не выполняет загрузку сегментных регистров, она
; нужна ассемблеру только для правильного вычисления
; смещений.

	assume  cs:code, ds:data, ss:stack

; При запуске программы управление будет передано
; на оператор с меткой start.
; Первое, что должна сделать программа - правильно
; загрузить сегментные регистры.
;
; Следующие два оператора инициализируют сегментный
; регистр данных DS.

start:  mov     ax, data
	mov     ds, ax

; Инициализируем сегментный регистр стека и
; указатель стека (регистры SS и SP).
; Эта операция должна выполняться в состоянии
; процессора с запрещенными прерываниями, иначе
; если регистр SS будет содержать уже новое значение,
; а SP - старое и если в этот момент произойдет
; прерывание, адрес возврата и значение регистра флагов
; будут записаны в не предназначенную для этого область.

	cli
	mov     ss, ax
	mov     sp, OFFSET stack
	sti

; Выводим сообщение msg из сегмента данных

	mov     ah, 9h
	mov     dx, OFFSET msg
	int     21h

; Завершаем работу программы

	mov     ax, 4C00h
	int     21h

code    ends

	end     start
