Author Topic: Simple (16-bit, COM) Helper Macros for Newbies  (Read 10489 times)

Offline dreamCoder

  • Full Member
  • **
  • Posts: 107
Simple (16-bit, COM) Helper Macros for Newbies
« on: January 07, 2014, 08:21:42 AM »
Hi newbies :D

Attached is a macro file named lib16.inc that you can use and learn from so that you don't have to start from nothing. It provides many useful utilities like printing string, converting numbers, checking the registers and so on. It also provides simple I/O mostly from the keyboard. This macro file should enable you to learn ASM fast and get to program in NASM in a short time. It consists of 25 macros (+ 2 sub macros).

For example, below is a sample program demonstrating the use of various macros, with their possible switches;

Code: [Select]
org 100h
%include 'lib16.inc'
section .text
    prts "Enter your name: ",-line             ;print message with no line
    gets nm                                           ;get string from kboard
    prts "Hello, Welcome to NASM ",-line   ;print welcome and name on the sam line
    prts nm
    mov ax,100h                                   ;Put something in AX
    prtr ax                                            ;confirm it VISUALLY
    prtr ax,-b                                        ;AX with binary switch. Prints binary equivalence
    prtr ax,-o,-n                                    ;prints AX, with octal, but not the name
    shl ax,1                                           ;do something to AX.
    prtr ax,-b                                        ;confirm what you just did VISUALLY
    prts "Enter an integer: ",-line             ;Prompt user for an integer
    geti x                                              ;Get integer string from keyboard
    add word[x],10                                ;add 10 to it
    prts "The value you entered + 10 is: ",-line
    prti [x]
    prts "x in binary is: ",-line
    prtb [x],-t
    prts "x in octal is: ",-line
    prto [x]
    prts "Evaluating integral expression -34*5/2 (signed)"
    prti -34*5/2,-s            ;prti with -s switch (signed)
    prts "Evaluating integral expression -34*5/2 (unsigned)"
    prti -34*5/2
    prts "Converting 56h to Base 32... answer is: ",-line
    bconv 56h,32                    ;convert 56h to base 32
    prts "Register CX in multiple bases..."
    prts "In octal: ",-line
    prto cx
    prts "In decimal: ",-line
    prti cx
    prts "In binary: ",-line
    prtb cx

exit           ;must include this every time

section .data
nm: times 30 db '0',0
x dw 0

This is the output:

Code: [Select]
Enter your name: Frank
Hello, Welcome to NASM Frank
AX:0100
AX:0100 [00000001 00000000b]
0100 [400o]
AX:0200 [00000010 00000000b]
Enter an integer: 34
The value you entered + 10 is: 44
x in binary is: 101100
x in octal is: 54
Evaluating integral expression -34*5/2 (signed)
-85
Evaluating integral expression -34*5/2 (unsigned)
65451
Converting 56h to Base 32... answer is: 2M
Register CX in multiple bases...
In octal: 377
In decimal: 255
In binary: 00000000 11111111

No restrictions on the usage. Use it until you can come up with your own macros. Some of these macros here are readily convertible to 32-bit.
Read the internal descriptions on how to use the macros and the switches.

Thanks to Frank and Bryant for helping me out with the conversions to NASM.

Hope you can benefit from it.

Regards
Soffian

UPDATE: 13th Feb 2014

I attached another macro file named lib16x.inc which is functionally equivalent to lib16.inc but this time I changed some of the macro files names to more descriptive names such as prtstr (prts), prtreg(prtr), prtchar, getstr, getoct etc. Plus, I also added a few macros (such as getnum, convert etc), removed some hidden bug and did a little optimization. Therefore lib16x.inc is not compatible with lib16.inc. Read the description at the end of the macro file (lib16x.inc). I strongly recommend you use lib16x.inc

Regards.

« Last Edit: February 12, 2014, 05:10:45 PM by dreamCoder »

Offline dreamCoder

  • Full Member
  • **
  • Posts: 107
Re: Simple (16-bit, COM) Helper Macros for Newbies
« Reply #1 on: January 07, 2014, 08:26:20 AM »
This is the list of the macros in lib16.inc

Code: [Select]
;================== LIST OF MACROS ======================;
;prtr     - Display 16-bit register
;dumpreg  - Dump register
;flags    - Display Flags register
;geti     - Get and save decimal from kboard
;prti     - Display Decimal string
;geto     - Get and save Octal string from kboard.
;prto     - Display Oct string
;getb     - Get and save Bin string from kboard.
;prtb     - Display Bin string (formatted)
;pbin     - Display Bin string (raw format)
;geth     - Get and save Hex string from kboard.
;prth     - Display Hex string
;getc     - Get a character from kboard and put in var
;getch    - Get character from kboard and put in AL
;prtc     - Display a character to screen
;gets     - Get string from keyboard
;prts     - Print string using 2 sub macros
;lines    - Print line(s)
;line     - Alternative to line - Print single line
;exit     - Pause and quit to OS
;pause    - Pause the screen
;bitf     - Extract bit field from a word, with copy
;bconv    - Base converter (16-bit) to any base up to 36
;pow      - 16-bit power function
;cls      - Clear 80x25 console screen


;TOTAL 25 macros + 2 sub macros(prtCStr/prtStr)
;
;============================
;Recommended Skeleton for COM
;============================
;       org 100h
;       %include 'lib16.inc'
;       section .text
;               <your codes here>
;       exit
;       section .data
;               <your data here> 
;================= GUIDES ON USAGE ======================;
;-Save this file in the same dir as your source file.    ;
;-To use lib16 in your source, use %include directive.   ;
;-Use a macro by typing the name of the macro and        ;
; supply parameters if necessary. Read descriptions.     ;
;-Strictly 16-bit. For COM.                              ;
;-Macro names,switches and parameters are lower-cased    ;
;-Distribute freely for educational purposes only.       ;
;-Use at own risk. NOT for critical systems. Unoptimized.;
;-Use for small tasks only. Tends to get larger easily   ;
;-Use exit at the end of source to exit properly.        ;
;PARAMETERS:                                             ;
;- [db],[dw]  = mem variable in square bracket           ;
;- db,dw      = Variable without bracket                 ;
;- imm16/8    = Size of immediate (word/byte)            ;
;- r16/r8     = 16-bit register / 8-bit register         ;
;- (opt)      = Optional                                 ;
;ERROR CODES: '#','!'                                    ;
;--------------------------------------------------------;


« Last Edit: January 07, 2014, 08:28:49 AM by dreamCoder »

Offline dreamCoder

  • Full Member
  • **
  • Posts: 107
Re: Simple (16-bit, COM) Helper Macros for Newbies
« Reply #2 on: January 07, 2014, 08:51:35 AM »
Sorry if it is not perfect though. My knowledge of NASM is very limited at this time. And if you found a bug or have a way of redefining the macros, please modify it and share with us your modifications in this thread. But try to keep it simple and descriptive (simple instructions etc). Optimization may not be the main purpose of this macro file. I developed this solely for newbies seeking speedy entry to NASM/ASM programming particularly those starting up from 16-bit COM environment. That may be old, but the educational values provided by 16-bit programming will never grow old.
 

Offline dreamCoder

  • Full Member
  • **
  • Posts: 107
Re: Simple (16-bit, COM) Helper Macros for Newbies
« Reply #3 on: January 08, 2014, 05:40:59 PM »
A slight modification to macro geti. Reason: to get rid of the macro's local data and replace it with dx as the signess marker.

Code: [Select]
;GET DECIMAL STRING FROM KBOARD
;And convert to its true value
;Five digits only. Max: 65535
;Don't supply suffix 'd'
;Valid digits from '0' to '9' only
;%1 = dw
;============
%macro geti 1
;============
        pusha
        mov word[ds:%1],0         ;initialize variable
        xor si,si                 ;si=0 or mov si,0. For the loop
        xor di,di                 ;di=0 or mov di,0. For the exponent
        xor dx,dx             ;**
%%get: 
        getch                     ;get character from kboard (in AL)
        cmp al,'-'                ;if negative sign
            je %%sign
        cmp al,0dh                ;if <ENTER>
            je %%line
        cmp al,39h                ;if bigger than '9'
            jg %%inv
        sub al,30h                ;convert chars to digits
        xor ah,ah                 ;clear AH. We want AL only
        push ax                   ;now save AX (AL=digit. AH cleared)
        inc si                    ;Loop up
        cmp si,6                  ;5 digits only (for 16-bit)
            je %%inv
        jmp %%get                 ;get next char from kboard
%%sign:
        test si,si                ;if invalid negative form
            jnz %%inv             ;equals cmp si,0/je %%inv
        mov dx,1               ;**
        push dx                 ;**
        jmp %%get                 ;get next chars
%%inv: 
        prtc '!'
        jmp %%exit
%%line:
        line                      ;after ENTERed
%%first:
        pop ax                    ;Restore digit off the stack,save in AX
        add word[ds:%1],ax        ;add first digit, to sent var
        cmp si,1                  ;if single digit input
            je %%done             ;done
%%second:                         ;Second digit
        inc di                    ;Power up
        mov ax,10                 ;10^1 = 10
        pop bx                    ;restore digit off the stack,save in BX
        mul bx                    ;AX=AX(10)* BX(digit). Answer in AX
        add word[ds:%1],ax        ;add up second digit, to sent var
        cmp si,2                  ;if two digits input
            je %%done             ;done
        sub si,2                  ;next loop is to ignore two digits
%%next:
        inc di                    ;power up (exponent)
        push di                   ;save exponent onto stack
        mov ax,10                 ;for MUL
        mov bx,10                 ;for MUL
%%pow: 
        mul bx                    ;Generate pow, final answer in AX
        cmp di,2                  ;off-by-one adjustment. If power is completed
            je %%ok
        dec di                    ;else, exponent down
        jmp %%pow                 ;next pow for the same digit
%%ok:   
        pop di                    ;restore exponent
        pop bx                    ;restore the digit
        mul bx                    ;AX=AX(from pow) * BX (digit)
        add word[ds:%1],ax        ;add up to sent var
        dec si                    ;loop down
        test si,si                ;loop check if si=0
            jz %%done
        jmp %%next                ;process next digit
%%done:
        pop dx                 ;**
        cmp dx,1             ;**
            je %%signs
        jmp %%exit
%%signs:
        neg word[ds:%1]           ;negate it
%%exit:
        popa                      ;restore general registers
%endmacro

Just replace the entire macro with this one above. Usage:

Code: [Select]
geti x        ;get a 16-bit decimal from keyboard. Sign or unsigned
prti [x],-s    ;Display sign integer (-s switch)
...
...
x dw 0

« Last Edit: January 09, 2014, 08:09:39 AM by dreamCoder »

Offline dreamCoder

  • Full Member
  • **
  • Posts: 107
Re: Simple (16-bit, COM) Helper Macros for Newbies
« Reply #4 on: February 08, 2014, 02:25:55 AM »
Hi again Frank and Bryant

This time it's a 32-bit macro that's giving me problem. I wrote this macro that converts to integer:

Code: [Select]
extern _printf
extern _scanf
extern _putchar
extern _getchar
extern _exit

%macro prtint 1-3
        pushad
        mov eax,%1
        xor esi,esi
        mov ecx,10
        xor edx,edx
        test eax,80000000h
           js %%sign
        jmp %%start
%%sign:
        %ifidn %2,-s
           prtchar '-'
           neg eax
        %endif
%%start:
        div ecx
        push edx
        xor edx,edx
        inc esi
        test eax,eax
            jz %%prt
        jmp %%start
%%prt:
        dec esi
        pop eax
        add al,30h
        prtchar eax
        test esi,esi
            jz %%done
        jmp %%prt
%%done:
        %ifnidn %3,-line
           line
        %endif
        popad
%endmacro

%macro prtchar 1
        pushad
        push dword %1
        call _putchar
        popad
%endmacro

%macro line 0
        prtchar 0dh
        prtchar 0ah
%endmacro

And can be called from like:
Code: [Select]
%include 'lib32.mac'
global _main

section .text
_main:

prtint [x] ;problem buddy?

push 0
call _exit

section .data
x: dd 35h

The macro works but it prints garbage endlessly. Just like the 16-bit version, this is translated from another ASM language. Is there anything that I missed? Thanks for your replies.
 

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2600
  • Country: us
Re: Simple (16-bit, COM) Helper Macros for Newbies
« Reply #5 on: February 08, 2014, 02:51:05 AM »
As a wild guess...
Code: [Select]
%macro prtchar 1
        pushad
        push dword %1
        call _putchar
        add esp, 4 ; <- clean up stack!
        popad
%endmacro

Best,
Frank


Offline dreamCoder

  • Full Member
  • **
  • Posts: 107
Re: Simple (16-bit, COM) Helper Macros for Newbies
« Reply #6 on: February 08, 2014, 03:35:25 AM »
Thanks Frank. It works now. Question: Do I have to clean up the stack after calling printf as well?
 

Offline dreamCoder

  • Full Member
  • **
  • Posts: 107
Re: Simple (16-bit, COM) Helper Macros for Newbies
« Reply #7 on: February 08, 2014, 03:40:46 AM »
ok I get it. Its all about different calling conventions. Oh one more question Frank, do these macros work in Linux as well? Because I have no access to Linux-based computers, just wondering if this could work without int 80h calls. Thanks.
« Last Edit: February 08, 2014, 03:45:27 AM by dreamCoder »

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2600
  • Country: us
Re: Simple (16-bit, COM) Helper Macros for Newbies
« Reply #8 on: February 08, 2014, 05:21:56 AM »
Well... yeah, it should work in Linux. It won't work on my machine at the moment because I (still!) haven't got the 32-bit C libraries installed. You'd think they would be installed by default, but nooo. I've pretty much decided that this distro's broken and I'm going to have to install something else, but I haven't gotten around to doing it yet.

One difference is that Linux doesn't use underscores on "main", "printf", etc. The way I like to deal with this is to spell 'em without underscores and tell Nasm "--prefix _"  if it's to be assembled as "-f win32". ("--postfix _" for OpenWatcom C - yeah, they use a trailing underscore) If you've got code with underscores in it, and you want to assemble it for Linux, you can do:
Code: [Select]
%define _main main
%define _printf printf
; etc...
or similar.

There are some differences (strnicmp vs strncasecmp for example) but 32-bit code that depends on the C libraries should "mostly" work on Windows or Linux. Dr. Carter's tutorial depends on it.

When it comes to 64-bit code, even "just call printf" is completely different. IMO they've screwed things up completely, but I suppose they have reasons... I do not envy the NASMX crew having to deal with this! Fortunately, I like int 80h... :)

Best,
Frank


Offline dreamCoder

  • Full Member
  • **
  • Posts: 107
Re: Simple (16-bit, COM) Helper Macros for Newbies
« Reply #9 on: February 12, 2014, 05:16:44 PM »
Thanks Frank. I am still working on the 32-bit file. Btw I also added lib16x.inc to the attachment (refer to first post). Hope this can help somebody out there.