comment <------------------------------------------------------------------

NewBasic Assembler

Small Tutorial

Freeware

Forever Young Software

(C)opyright 1984-2001

All rights reserved

19 Mar 2001

 

This is a small tutorial on how to program in assembler. All the code,

unless otherwise stated, is assembled with NBASM. This tutorial in no

way will teach you the complex language of assembler. However, it is

my goal to teach you the basics of assembler and how to you NBASM, or

another assembler, to create your programs. We will discuss how to

create workable, productable, .COM files. Once you know how to create

.COM files, the move to .OBJ files is not to difficult.

I think the first thing we will discuss is program layout. How does

DOS load and execute your program, what are the limits to .COM files,

and what are the capabilities of .COM files.

DOS first allocates all available memory and creates a 256 byte block

of memory at the beginning of this block. The block is called the PSP.

The Program Segment Prefix is filled with information that is needed

by DOS as well as your program.

Then DOS loads a mirror image of your .COM file in to memory just after

this PSP and points CS:IP to your code (100h). DS, ES, and SS are all

pointed to the same segment that CS points to. Since this happens, your

.COM file can only be one segment in length (65536) and all code, data,

and stack must be in this segment. For this reason, this puts a few re-

strictions on a .COM file:

1. - A valid .COM file can only be 65536 - 256 bytes in length.

Since DOS places a 256 byte PSP at the first of the segment, your

program can only by up to 65280 bytes in length.

2. - Since SS:SP points to the last word in the segment, if you intend

to use the stack, you can not occupy the area at the end of this

segment or your stack will overwrite it.

3. - Since DOS points CS:IP to offset 100h, you must have a valid asm

instruction at offset 100h. This means the first thing in your

program must be a valid code instruction.

4. - As mentioned before, all your code and data must be in this one

segment. Therefore, you can not write extremely large programs.

However, unless you have a lot of strings in your data, you can

write a fairly large program using this .COM format.

A few benefits of a .COM file are that you can relocate the stack to a

different place. The stack never contains predefined values (with one

exception described later). This means that if you know you are going

to use a larger stack that is left available by your program, you can

create a small amount of code to allocate a different block, and point

SS:SP to the end of this block. Now your program can occupy all of the

64k of the memory allocated. *A note* For that matter, you can always

have your data saved in a file and load it into a different segment and

point DS to this segment. However, if you where going to do this, you

might as well have just created an valid .EXE file.

Another benefit of a .COM file is that it is loaded much quicker. DOS

must only load 64k or less, doesn't have to calculate relocation items,

and doesn't have to calculate other program operations at start up.

Also, most .EXE files must have a 256 byte header while a .COM has no

such header. Therefore .COM file can be much smaller.

Let us start by thinking of a program we would like to create. This task

must be simple enough for a beginner just learning to program assembly,

but large enough to make a good tutorial covering a lot of areas.

What would you say to a small file viewer? Just a simple viewer that

would display a file allowing you to scroll up and down. Maybe we could

add small enhancements to it that would send it to the printer or some-

thing like that.

Well, first we must realize what we will need to do. To create a small

file viewer like this, we will need to do the following:

- get command line

- memory allocation

- open/read/close a file

- screen output

- keyboard input

<---------------------------------------------------------------------------

; Shall we start? First we must tell the assembler how and what we are

; going to do. We want a .COM file, we will be using x186 instructions,

; and we want to start at offset 100h (256d) to allow room for the PSP.

;

; Open a new file and name it V.ASM. Now enter the following at the start

; of the file: (Note that this nbasmtut.doc file is assemble-able as is)

.model tiny

.186

.code

org 100h

; We have told the assembler to create a .COM file with the 'model tiny'

; directive. A model is how the program will use segments. The tiny

; model uses one segment for all items.

;

; Then we tell the assembler to allow all instructions up to and includ-

; ing the 186. Next is the org 100h. This tells the assembler to that

; the following data/code will start at offset 100h in the segment. Then

; the .code directive telling the assembler this is the start of the

; code area, start assembling.

;

; To start our veiwer we need to know what file to open. When DOS creates

; the PSP, it puts a count of the characters in the parameter list used

; on the command line at offset 80h. Then it has 127 characters of the

; parameter list at offset 81h. To get this parameter list, we need to

; know the length, so let's get the count at offset 80h. Then let's get

; the filename from this command line and place it in our filename area.

mov si,0080h ; the count byte in the PSP

lodsb ; 'mov al,[si]/inc si'

or al,al ; is al = 0?

jnz short FIsThere ; if 0 then no command line there

mov dx,offset ParmErrS ; so use DOS print string service

mov ah,09 ; to print an error

int 21h ;

.exit ; exit to dos (user directive/macro)

FIsThere: inc si ; else if count > 0 then a command line

mov di,offset FileName ; skip first space and point di to filename

GetFileN: lodsb ; get the command line until a 0Dh

cmp al,13 ;

je short GotFileN ;

stosb ;

jmp short GetFileN ;

GotFileN: xor al,al ; make string asciiz

stosb ;

; Now we have an asciiz filename ready for opening. An asciiz string is a

; string with a NULL char as the last character in the string. This is

; used by most services of DOS to denote eol (End Of Line). Please note

; that DOS service 09h that we used above, uses a dollar sign to denote

; eol. (We will add the data to our code later in this tutorial).

;

; Now that we have a valid filename asciiz string, we can open the file

; for viewing. However, we need to pick the correct service to open it.

; DOS has quite a few FILE OPEN services available to us. Let us pick

; service 3Dh, 'Open Existing File'. This service returns an error if

; the file does not exist.

mov ax,3D00h ; open existing file for read only

mov dx,offset Filename ;

int 21h ;

jnc short File1OK ; if carry, then error occured

mov dx,offset File1ErrS ; print error string

mov ah,09h ;

int 21h ;

.exit ;

File1OK:

; Now that we have opened the file and have a file handle in AX, allow us

; to read in the file. Being that this is a simple program, and we will

; assume all files are 32k or less, let us read only 32k of the file.

mov bx,ax ; put handle in bx

mov ah,3Fh ; read from handle

mov cx,32767 ; read at most 32k

mov dx,offset Buffer1 ; pointer to our buffer

int 21h ;

mov FileLen,ax ; ax = amount read

mov ah,3Eh ; close the file

int 21h ;

; Notice that service 3Fh returns amount read in AX. We will need this

; amount for later.

;

; Now that we have the contents of the file in our buffer, we need to set

; up our display. First let us clear out the part of the data buffer

; that is not being used by the file.

mov di,offset Buffer1 ; clear the data buffer

add di,FileLen ; not being occupied by

mov cx,32768 ; the file

sub cx,FileLen ;

shr cx,1 ; divide by two for words

mov ax,000Dh ;

rep ;

stosw ;

jnc short skip_byte ; if there was a byte left over

stosb ; from shr cx,1 then clear it too

skip_byte:

; Now let us turn the cursor off and put a header line across the the top

; of the screen and a status line across the bottom of the screen. For

; speed, we will write directly to the screen memory rather than the

; slower methods.

mov ah,01h ; turn off cursor

mov ch,20h ; bit number 5

int 10h ;

mov ax,0B800h ; point es to screen memory

mov es,ax ;

xor di,di ; first position on the screen

mov ah,HdrClr ; our background color

mov si,offset Header ; point to our header line

mov cx,80 ; 80 chars across the screen

PrntHdr: lodsb ;

stosw ;

loop PrntHdr ;

mov al,20h ; clear the middle section

mov ah,txtclr ;

mov cx,1840 ;

rep ;

stosw ;

mov ah,HdrClr ; our background color

mov si,offset Status ; point to our status line

mov cx,80 ; 80 chars across the screen

PrntHdr1: lodsb ;

stosw ;

loop PrntHdr1 ;

; Now we must set up our user input area and print the actual data to the

; screen. We first calculate where we are in the data file and then

; we print 23 lines of text.

mov si,offset Buffer1

PrintIt: push si

mov di,160

mov cx,23

PrtItL1: push cx

push di

push di

mov cx,80

mov ax,0720h

rep

stosw

pop di

mov cx,81

PrtItL2: lodsb

cmp al,13

je short PrtItL2D

cmp al,32

jb short PrtItL2l

PrtItL3: mov ah,07h

stosw

PrtItL2l: loop PrtItL2

Skip2end: lodsb

cmp al,13

jne short Skip2end

PrtItL2D: pop di

add di,160

pop cx

loop PrtItL1

pop si

 

; Now let's pause for key input by the user and then parse it accordingly.

 

BadKey: xor ah,ah ;

int 16h ;

cmp ax,011Bh ; is it the ESC key?

je short Done ;

cmp ax,5100h ; page down

jne short NoPD ;

xor bx,bx ;

mov cx,22 ; 22 lines at a time

PDownL: cmp byte [bx+si],00h ; end of data?

je short PIsBot ;

inc bx ;

cmp byte [bx+si],0Ah ; if 0Ah skip

jne short PDownL ;

loop PDownL ;

PIsBot: add si,bx ; move to that position

jmp short PrintIt ;

NoPD: cmp ax,4900h ; page up

jne short NoPU ;

mov bx,si ;

mov cx,22 ; 22 lines at a time

PUpLoop: cmp bx,offset Buffer1 ;

je short PIsTop ;

dec bx ;

cmp byte [bx],0Ah ; skip all 0Ah

jne short PUpLoop ;

loop PUpLoop ;

PIsTop: mov si,bx ; move to that position

jmp short PrintIt ;

NoPU: cmp ax,4F00h ; end key

jne short NotEnd ;

mov si,offset Buffer1 ;

add si,FileLen ; move to the end of the file

jmp short PrintIt ;

NotEnd: cmp ax,4700h ; home key

jne short NotHome ;

mov si,offset Buffer1 ; move to the first of the file

jmp PrintIt ;

NotHome: cmp ax,5000h ; down arrow

jne short NotDown ;

xor bx,bx ;

DownL: cmp byte [bx+si],00h ;

je short IsBot ;

inc bx ;

cmp byte [bx+si],0Ah ;

jne short DownL ;

IsBot: add si,bx ;

jmp PrintIt ;

NotDown: cmp ax,4800h ; up arrow

jne short BadKey ; (must have been a key not needed

mov bx,si ; so just continue.)

UpLoop: cmp bx,offset Buffer1 ;

je short IsTop ;

dec bx ;

cmp byte [bx],0Ah ;

jne short UpLoop ;

IsTop: mov si,bx ;

jmp PrintIt ; loop

Done: mov ax,0003h ; set screen back to text 0003h

int 10h ; (clear the screen)

mov ah,01h ; turn on cursor

mov cx,0607h ; start = 6 finish = 7

int 10h ;

.exit ; exit to dos (user directive/macro)

; Now we need the actual data and data space used by the program we have

; created.

HdrClr db 17h

TxtClr db 07h

FileLen dw 00h

Header db ' A demo Viewer for the Tutorial included with NBASM Forever Young Software '

Status db ' Press ,  or <esc> Line: '

ParmErrS db 13,10,'Error with command line$'

File1ErrS db 13,10,'Error with Source File.$'

FileName dup 128,?

Buffer1 dup 32767,?

.end

; ********** end of file

Assemble with the following:

NBASM V.ASM

Run with the following:

V V.ASM

(Remember to add the valid file name as a parameter: V.ASM)

 

NBasm will return any errors if found.

 

A few things to think about:

-Notice that I left the LINE: part on the status line we created at the

bottom of the screen. You could easily add some code to this program

to keep track of the line number and print it in this location.

-Also note that if you load a file larger than 32k, this program will

still work OK, but you will get garbage as the last part of the file

when it is displayed.

-I'm sure that there are a few more items that I have forgotten or bugs

that I didn't find. Please let me know if you see anything that should

be added or modified.