; emu8086.inc

; emu8086.inc - macro definitions library for easy input/output 

; Note, that some declarations of "emu8086.inc" are macro procedure declarations, and you 
; have to use "DEFINE_..." macro somewhere in your program if you want to use these functions: 

;   CALL SCAN_NUM 
;   CALL PRINT_STRING 
;   CALL PTHIS 
;   CALL GET_STRING 
;   CALL CLEAR_SCREEN 
;   CALL PRINT_NUM 
;   CALL PRINT_NUM_UNS 

; You can define all these procedures in your source code, but compilation time may slow down 
; sufficiently because of that, only declare functions that you plan to use: 


;   DEFINE_SCAN_NUM 
;   DEFINE_PRINT_STRING 
;   DEFINE_PTHIS 
;   DEFINE_GET_STRING 
;   DEFINE_CLEAR_SCREEN 
;   DEFINE_PRINT_NUM 
;   DEFINE_PRINT_NUM_UNS 

;  The above declarations should be made in  your code once only! Better somewhere 
;  in the end of your file, but before "END"  directive. You can also declare them 
;  in the beginning of the file, but it should  be after "ORG 100h" directive for COM files, 
;  or inside the code segment for EXE files. 


; this macro prints a char in AL and advances 
; the current cursor position: 
PUTC    MACRO   char
        PUSH    AX
        MOV     AL, char
        MOV     AH, 0Eh
        INT     10h
        POP     AX
ENDM


; this macro prints a string that is given as a parameter, example: 
; PRINT 'hello world!' 
; new line is NOT added. 
PRINT   MACRO   sdat
LOCAL   next_char, s_dcl, printed, skip_dcl

PUSH    AX      ; store registers... 
PUSH    SI      ; 

JMP     skip_dcl        ; skip declaration. 
        s_dcl DB sdat, 0

skip_dcl:
        LEA     SI, s_dcl

next_char:
        MOV     AL, CS:[SI]
        CMP     AL, 0
        JZ      printed
        INC     SI
        MOV     AH, 0Eh ; teletype function. 
        INT     10h
        JMP     next_char
printed:

POP     SI      ; re-store registers... 
POP     AX      ; 
ENDM


; this macro prints a string that is given as a parameter, example: 
; PRINTN 'hello world!' 
; the same as PRINT, but new line is automatically added. 
PRINTN   MACRO   sdat
LOCAL   next_char, s_dcl, printed, skip_dcl

PUSH    AX      ; store registers... 
PUSH    SI      ; 

JMP     skip_dcl        ; skip declaration. 
        s_dcl DB sdat, 13, 10, 0

skip_dcl:
        LEA     SI, s_dcl

next_char:
        MOV     AL, CS:[SI]
        CMP     AL, 0
        JZ      printed
        INC     SI
        MOV     AH, 0Eh ; teletype function. 
        INT     10h
        JMP     next_char
printed:

POP     SI      ; re-store registers... 
POP     AX      ; 
ENDM


; turns off the cursor: 
CURSOROFF       MACRO
        PUSH    AX
        PUSH    CX
        MOV     AH, 1
        MOV     CH, 28h
        MOV     CL, 09h
        INT     10h
        POP     CX
        POP     AX
ENDM



; turns on the cursor: 
CURSORON        MACRO
        PUSH    AX
        PUSH    CX
        MOV     AH, 1
        MOV     CH, 08h
        MOV     CL, 09h
        INT     10h
        POP     CX
        POP     AX
ENDM

; sets current cursor 
; position: 
GOTOXY  MACRO   col, row
        PUSH    AX
        PUSH    BX
        PUSH    DX
        MOV     AH, 02h
        MOV     DH, row
        MOV     DL, col
        MOV     BH, 0
        INT     10h
        POP     DX
        POP     BX
        POP     AX
ENDM


;*************************************************************** 

; This macro defines a procedure that gets the multi-digit SIGNED number from the keyboard, 
; and stores the result in CX register: 
DEFINE_SCAN_NUM         MACRO
LOCAL make_minus, ten, next_digit, set_minus
LOCAL too_big, backspace_checked, too_big2
LOCAL stop_input, not_minus, skip_proc_scan_num
LOCAL remove_not_digit, ok_AE_0, ok_digit, not_cr

; protect from wrong definition location: 
JMP     skip_proc_scan_num

SCAN_NUM        PROC    NEAR
        PUSH    DX
        PUSH    AX
        PUSH    SI

        MOV     CX, 0

        ; reset flag: 
        MOV     CS:make_minus, 0

next_digit:

        ; get char from keyboard 
        ; into AL: 
        MOV     AH, 00h
        INT     16h
        ; and print it: 
        MOV     AH, 0Eh
        INT     10h

        ; check for MINUS: 
        CMP     AL, '-'
        JE      set_minus

        ; check for ENTER key: 
        CMP     AL, 13  ; carriage return? 
        JNE     not_cr
        JMP     stop_input
not_cr:


        CMP     AL, 8                   ; 'BACKSPACE' pressed? 
        JNE     backspace_checked
        MOV     DX, 0                   ; remove last digit by 
        MOV     AX, CX                  ; division: 
        DIV     CS:ten                  ; AX = DX:AX / 10 (DX-rem). 
        MOV     CX, AX
        PUTC    ' '                     ; clear position. 
        PUTC    8                       ; backspace again. 
        JMP     next_digit
backspace_checked:


        ; allow only digits: 
        CMP     AL, '0'
        JAE     ok_AE_0
        JMP     remove_not_digit
ok_AE_0:
        CMP     AL, '9'
        JBE     ok_digit
remove_not_digit:
        PUTC    8       ; backspace. 
        PUTC    ' '     ; clear last entered not digit. 
        PUTC    8       ; backspace again.         
        JMP     next_digit ; wait for next input.        
ok_digit:


        ; multiply CX by 10 (first time the result is zero) 
        PUSH    AX
        MOV     AX, CX
        MUL     CS:ten                  ; DX:AX = AX*10 
        MOV     CX, AX
        POP     AX

        ; check if the number is too big 
        ; (result should be 16 bits) 
        CMP     DX, 0
        JNE     too_big

        ; convert from ASCII code: 
        SUB     AL, 30h

        ; add AL to CX: 
        MOV     AH, 0
        MOV     DX, CX      ; backup, in case the result will be too big. 
        ADD     CX, AX
        JC      too_big2    ; jump if the number is too big. 

        JMP     next_digit

set_minus:
        MOV     CS:make_minus, 1
        JMP     next_digit

too_big2:
        MOV     CX, DX      ; restore the backuped value before add. 
        MOV     DX, 0       ; DX was zero before backup! 
too_big:
        MOV     AX, CX
        DIV     CS:ten  ; reverse last DX:AX = AX*10, make AX = DX:AX / 10 
        MOV     CX, AX
        PUTC    8       ; backspace. 
        PUTC    ' '     ; clear last entered digit. 
        PUTC    8       ; backspace again.         
        JMP     next_digit ; wait for Enter/Backspace. 


stop_input:
        ; check flag: 
        CMP     CS:make_minus, 0
        JE      not_minus
        NEG     CX
not_minus:

        POP     SI
        POP     AX
        POP     DX
        RET
make_minus      DB      ?       ; used as a flag. 
ten             DW      10      ; used as multiplier. 
SCAN_NUM        ENDP

skip_proc_scan_num:

DEFINE_SCAN_NUM         ENDM
;*************************************************************** 


;*************************************************************** 
; this macro defines a procedure to print a null terminated 
; string at current cursor position, receives address of string in DS:SI 
DEFINE_PRINT_STRING     MACRO
LOCAL   next_char, printed, skip_proc_print_string

; protect from wrong definition location: 
JMP     skip_proc_print_string

PRINT_STRING PROC NEAR
PUSH    AX      ; store registers... 
PUSH    SI      ; 

next_char:
        MOV     AL, [SI]
        CMP     AL, 0
        JZ      printed
        INC     SI
        MOV     AH, 0Eh ; teletype function. 
        INT     10h
        JMP     next_char
printed:

POP     SI      ; re-store registers... 
POP     AX      ; 

RET
PRINT_STRING ENDP

skip_proc_print_string:

DEFINE_PRINT_STRING     ENDM
;*************************************************************** 


;*************************************************************** 
; This macro defines a procedure to print a null terminated 
; string at current cursor position. 
; The ZERO TERMINATED string should be defined just after the CALL. For example: 
; 
; CALL PTHIS 
; db 'Hello World!', 0 
; 
; Address of string is stored in the Stack as return address. 
; Procedure updates value in the Stack to make return 
; after string definition. 
DEFINE_PTHIS     MACRO
LOCAL   next_char, printed, skip_proc_pthis, temp1

; protect from wrong definition location: 
JMP     skip_proc_pthis

PTHIS PROC NEAR

MOV     CS:temp1, SI  ; store SI register. 

POP     SI            ; get return address (IP). 

PUSH    AX            ; store AX register. 

next_char:
        MOV     AL, CS:[SI]
        INC     SI            ; next byte. 
        CMP     AL, 0
        JZ      printed
        MOV     AH, 0Eh       ; teletype function. 
        INT     10h
        JMP     next_char     ; loop. 
printed:

POP     AX            ; re-store AX register. 

; SI should point to next command after 
; the CALL instruction and string definition: 
PUSH    SI            ; save new return address into the Stack. 

MOV     SI, CS:temp1  ; re-store SI register. 

RET
temp1  DW  ?    ; variable to store original value of SI register. 
PTHIS ENDP

skip_proc_pthis:

DEFINE_PTHIS     ENDM
;*************************************************************** 


;*************************************************************** 
; This macro defines a procedure to get a null terminated 
; string from user, the received string is written to buffer 
; at DS:DI, buffer size should be in DX. 
; Procedure stops the input when 'Enter' is pressed. 
DEFINE_GET_STRING       MACRO
LOCAL   empty_buffer, wait_for_key, skip_proc_get_string
LOCAL   exit, add_to_buffer

; protect from wrong definition location: 
JMP     skip_proc_get_string

GET_STRING      PROC    NEAR
PUSH    AX
PUSH    CX
PUSH    DI
PUSH    DX

MOV     CX, 0                   ; char counter. 

CMP     DX, 1                   ; buffer too small? 
JBE     empty_buffer            ; 

DEC     DX                      ; reserve space for last zero. 


;============================ 
; loop to get and processes key presses: 

wait_for_key:

MOV     AH, 0                   ; get pressed key. 
INT     16h

CMP     AL, 13                  ; 'RETURN' pressed? 
JZ      exit


CMP     AL, 8                   ; 'BACKSPACE' pressed? 
JNE     add_to_buffer
JCXZ    wait_for_key            ; nothing to remove! 
DEC     CX
DEC     DI
PUTC    8                       ; backspace. 
PUTC    ' '                     ; clear position. 
PUTC    8                       ; backspace again. 
JMP     wait_for_key

add_to_buffer:

        CMP     CX, DX          ; buffer is full? 
        JAE     wait_for_key    ; if so wait for 'BACKSPACE' or 'RETURN'... 

        MOV     [DI], AL
        INC     DI
        INC     CX

        ; print the key: 
        MOV     AH, 0Eh
        INT     10h

JMP     wait_for_key
;============================ 

exit:

; terminate by null: 
MOV     [DI], 0

empty_buffer:

POP     DX
POP     DI
POP     CX
POP     AX
RET
GET_STRING      ENDP


skip_proc_get_string:

DEFINE_GET_STRING       ENDM
;*************************************************************** 

;*************************************************************** 
; this macro defines procedure to clear the screen, 
; (done by scrolling entire screen window), 
; and set cursor position to top of it: 
DEFINE_CLEAR_SCREEN     MACRO
LOCAL skip_proc_clear_screen

; protect from wrong definition location: 
JMP     skip_proc_clear_screen

CLEAR_SCREEN PROC NEAR
        PUSH    AX      ; store registers... 
        PUSH    DS      ; 
        PUSH    BX      ; 
        PUSH    CX      ; 
        PUSH    DI      ; 

        MOV     AX, 40h
        MOV     DS, AX  ; for getting screen parameters. 
        MOV     AH, 06h ; scroll up function id. 
        MOV     AL, 0   ; scroll all lines! 
        MOV     BH, 07  ; attribute for new lines. 
        MOV     CH, 0   ; upper row. 
        MOV     CL, 0   ; upper col. 
        MOV     DI, 84h ; rows on screen -1, 
        MOV     DH, [DI] ; lower row (byte). 
        MOV     DI, 4Ah ; columns on screen, 
        MOV     DL, [DI]
        DEC     DL      ; lower col. 
        INT     10h

        ; set cursor position to top 
        ; of the screen: 
        MOV     BH, 0   ; current page. 
        MOV     DL, 0   ; col. 
        MOV     DH, 0   ; row. 
        MOV     AH, 02
        INT     10h

        POP     DI      ; re-store registers... 
        POP     CX      ; 
        POP     BX      ; 
        POP     DS      ; 
        POP     AX      ; 

        RET
CLEAR_SCREEN ENDP

skip_proc_clear_screen:

DEFINE_CLEAR_SCREEN     ENDM
;*************************************************************** 


;*************************************************************** 

; This macro defines a procedure that prints number in AX, 
; used with PRINT_NUM_UNS to print signed numbers: 
; Requires DEFINE_PRINT_NUM_UNS !!! 
DEFINE_PRINT_NUM        MACRO
LOCAL not_zero, positive, printed, skip_proc_print_num

; protect from wrong definition location: 
JMP     skip_proc_print_num

PRINT_NUM       PROC    NEAR
        PUSH    DX
        PUSH    AX

        CMP     AX, 0
        JNZ     not_zero

        PUTC    '0'
        JMP     printed

not_zero:
        ; the check SIGN of AX, 
        ; make absolute if it's negative: 
        CMP     AX, 0
        JNS     positive
        NEG     AX

        PUTC    '-'

positive:
        CALL    PRINT_NUM_UNS
printed:
        POP     AX
        POP     DX
        RET
PRINT_NUM       ENDP

skip_proc_print_num:

DEFINE_PRINT_NUM        ENDM

;*************************************************************** 

; This macro defines a procedure that prints out an unsigned 
; number in AX (not just a single digit) 
; allowed values from 0 to 65535 (0FFFFh) 
DEFINE_PRINT_NUM_UNS    MACRO
LOCAL begin_print, calc, skip, print_zero, end_print, ten
LOCAL skip_proc_print_num_uns

; protect from wrong definition location: 
JMP     skip_proc_print_num_uns

PRINT_NUM_UNS   PROC    NEAR
        PUSH    AX
        PUSH    BX
        PUSH    CX
        PUSH    DX

        ; flag to prevent printing zeros before number: 
        MOV     CX, 1

        ; (result of "/ 10000" is always less or equal to 9). 
        MOV     BX, 10000       ; 2710h - divider. 

        ; AX is zero? 
        CMP     AX, 0
        JZ      print_zero

begin_print:

        ; check divider (if zero go to end_print): 
        CMP     BX,0
        JZ      end_print

        ; avoid printing zeros before number: 
        CMP     CX, 0
        JE      calc
        ; if AX<BX then result of DIV will be zero: 
        CMP     AX, BX
        JB      skip
calc:
        MOV     CX, 0   ; set flag. 

        MOV     DX, 0
        DIV     BX      ; AX = DX:AX / BX   (DX=remainder). 

        ; print last digit 
        ; AH is always ZERO, so it's ignored 
        ADD     AL, 30h    ; convert to ASCII code. 
        PUTC    AL


        MOV     AX, DX  ; get remainder from last div. 

skip:
        ; calculate BX=BX/10 
        PUSH    AX
        MOV     DX, 0
        MOV     AX, BX
        DIV     CS:ten  ; AX = DX:AX / 10   (DX=remainder). 
        MOV     BX, AX
        POP     AX

        JMP     begin_print

print_zero:
        PUTC    '0'

end_print:

        POP     DX
        POP     CX
        POP     BX
        POP     AX
        RET
ten             DW      10      ; used as divider.       
PRINT_NUM_UNS   ENDP

skip_proc_print_num_uns:

DEFINE_PRINT_NUM_UNS    ENDM
;*************************************************************** 




; - Other Assembler Source Codes -



; - asm2html by emu8086 -