Tran T. Assembly Examples. Learn Assembly by Examples 2018
Tran T. Assembly Examples. Learn Assembly by Examples 2018
I Thanh Tran
2018
CONTENTS:
; fibo.asm
; assemble using nasm:
; nasm -o fibo.com -f bin fibo.asm
;
;****************************************************************
; Alterable Constant
;****************************************************************
; You can adjust this upward but the upper limit is around 150000 terms.
; the limitation is due to the fact that we can only address 64K of memory
; in a DOS com file, and the program is about 211 bytes long and the
; address space starts at 100h. So that leaves roughly 65000 bytes to
; be shared by the two terms (num1 and num2 at the end of this file). Since
; they're of equal size, that's about 32500 bytes each, and the 150000th
; term of the Fibonacci sequence is 31349 digits long.
;
maxTerms equ 15000 ; number of terms of the series to calculate
;****************************************************************
; Number digits to use. This is based on a little bit of tricky math.
; One way to calculate F(n) (i.e. the nth term of the Fibonacci seeries)
; is to use the equation int(phiAn/sqrt(5)) where a means exponentiation
; and phi = (1 + sqrt(5))/2, the "golden number" which is a constant about
; equal to 1.618. To get the number of decimal digits, we just take the
; base ten log of this number. We can very easily see how to get the
; base phi log of F(n) -- it's just n*lp(phi)+lp(sqrt(5)), where lp means
; a base phi log. To get the base ten log of this we just divide by the
; base ten log of phi. If we work through all that math, we get:
;
; digits = terms * log(phi) + log(sqrt(5))/log(phi)
;
; the constants below are slightly high to assure that we always have
; enough room. As mentioned above the 150000th term has 31349 digits,
; but this formula gives 31351. Not too much waste there, but I'd be
; a little concerned about the stack!
;
digits equ (maxTerms*209+1673)/1000
.top
; add num1 to num2
mov di,num1+digits-1
mov si,num2+digits-1
mov cx,digits ;
call AddNumbers ; num2 += num1
mov bp,num2 ;
call PrintLine ;
dec dword [term] ; decrement loop counter
jz .done ;
;****************************************************************
;
; Printline
; prints a single line of output containing one term of the
; Fibonacci sequence. The first few lines look like this:
;
; Fibonacci(1): 1
; Fibonacci(2): 1
; Fibonacci(3): 2
; Fibonacci(4): 3
;
; INPUT: ds:bp ==> number string, cx = max string length
; OUTPUT: CF set on error, AX = error code if carry set
; DESTROYED: ax, bx, cx, dx, di
;
;****************************************************************
PrintLine:
mov dx,eol ; print combined CRLF and msg1
mov cx,msg1len+eollen ;
call PrintString ;
;
; PrintNumericString
; prints the numeric string at DS:DI, suppressing leading zeroes
; max length is CX
;
; INPUT: ds:di ==> number string, cx = max string length
; OUTPUT: CF set on error, AX = error code if carry set
; DESTROYED: ax, bx, cx, dx, di
;
;****************************************************************
PrintNumericString:
; first scan for the first non-zero byte
mov al,'0' ; look for ASCII zero
cld ; scan from MSD to LSD
repe scasb ;
mov dx,di ; points to one byte after
dec dx ; back up one character
inc cx ;
; deliberately fall through to PrintString
;
; PrintString
; prints the string at DS:DX with length CX to stdout
;
; INPUT: ds:dx ==> string, cx = string length
; OUTPUT: CF set on error, AX = error code if carry set
; DESTROYED: ax, bx
;
PrintString:
mov bx, 1 ; write to stdout
mov ah, 040h ; write to file handle
int 21h ; ignore return value
ret ;
;****************************************************************
;
; AddNumbers
; add number 2 at ds:si to number 1 at es:di of width cx
;
;
; INPUT: es:di ==> number1, ds:si ==> number2, cx= max width
; OUTPUT: CF set on overflow
; DESTROYED: ax, si, di
;
;****************************************************************
AddNumbers:
std ; go from LSB to MSB
clc ;
pushf ; save carry flag
.top
mov ax,0f0fh ; convert from ASCII BCD to BCD
and al,[si] ; get next digit of number2 in al
and ah,[di] ; get next digit of number1 in ah
popf ; recall carry flag
adc al,ah ; add these digits
aaa ; convert to BCD
pushf ;
add al,'0' ; convert back to ASCII BCD digit
stosb ; save it and increment both counters
dec si ;
loop .top ; keep going until we've got them all
popf ; recall carry flag
ret ;
;****************************************************************
;
; IncrementCount
; increments a multidigit term counter by one
;
; INPUT: none
; OUTPUT: CF set on overflow
; DESTROYED: ax, cx, di
;
;****************************************************************
IncrementCount:
mov cx,cntDigits ;
mov di,counter+cntDigits-1
std ; go from LSB to MSB
stc ; this is our increment
pushf ; save carry flag
.top
mov ax,000fh ; convert from ASCII BCD to BCD
and al,[di] ; get next digit of counter in al
popf ; recall carry flag
adc al,ah ; add these digits
aaa ; convert to BCD
pushf ;
add al,'0' ; convert back to ASCII BCD digit
stosb ; save and increment counter
loop .top ;
popf ; recall carry flag
ret
;
; CRLF
; prints carriage return, line feed pair to stdout
;
; INPUT: none
; OUTPUT: CF set on error, AX = error code if carry set
; DESTROYED: ax, bx, cx, dx
;
;****************************************************************
CRLF: mov dx,eol ;
mov cx,eollen ;
jmp PrintString ;
;****************************************************************
; static data
eol db 13,10 ; DOS-style end of line
eollen equ $ - eol
msg1 db 'Fibonacci(' ;
msg1len equ $ - msg1
assume cs:cseg,ds:cseg,ss:nothing,es:nothing
jmp p150 ; start-up code
push cs
pop ds ; make ds=cs
mov ax,wait ; check wait time
dec ax ; zero?
jz p010 ; yes - 1 second has elapsed
mov wait, ax ; not this time
jmp p080 ; return
mov hour,ax
call p120 ; beep the speaker once
p080: popf
pop ds
pop ax
jmp cs:[jumpval]
alarm endp
cseg ends
end alarm
An implementation of SLIP (Serial
Link IP), RFC 1055 in assembly
language
An implementation of SLIP (Serial Link IP), RFC 1055 in assembly language
; slip.asm
;
; This is an 8086+ implementation of SLIP (RFC 1055)
;
; It may be assembled using Microsoft's MASM using the command line:
; ml -Fl -c slip.asm
;
; or using Borland's TASM using the command line:
; tasm -la -m2 -jLOCALS slip.asm
;
.model small
.stack 100h
.data
SLIP_END equ 0C0h
SLIP_ESC equ 0DBh
SLIP_ESC_END equ 0DCh
SLIP_ESC_ESC equ 0DDh
; note that these are both sample macros and are very simple
; In both cases, DX is assumed to already be pointing to the
; appropriate I/O port and a character is always assumed to
; be ready.
SEND_CHAR macro char
IFDIFI <char>, <al>
mov al,char
ENDIF
out dx,al
endm
RECV_CHAR macro
in al,dx
endm
.code
send_packet
; Entry:
; DS:SI ==> raw packet to be sent
; CX = length of raw packet to be sent
; direction flag is assumed to be cleared (incrementing)
Exit:
Trashed:
none
;
send_packet proc
push cx
push si
SEND_CHAR SLIP_END ; send an end char to flush any garbage
jcxz @@bailout ; if zero length packet, bail out now
@@nextchar:
lodsb ; load next char
cmp al,SLIP_END ; Q: is it the special END char?
jne @@check_esc ; N: check for ESC
SEND_CHAR SLIP ESC ; Y: send ESC + ESC_END instead
mov al,SLIP_ESC_END ;
jmp @@ordinary ;
@@check_esc:
cmp al,SLIP_ESC ; Q: is it the special ESC char?
jne @@ordinary ; N: send ordinary char
SEND_CHAR SLIP_ESC ; Y: send ESC + ESC_END instead
mov al,SLIP_ESC_ESC ;
; fall through to ordinary character
@@ordinary:
SEND_CHAR al
;****************************************************************
; recv_packet
;
; receives a packet using the macro RECV_CHAR() which must be defined by
; the user and places the received packet into the memory buffer pointed
; to by ES:DI. The final length is returned in BX.
;
; Note that in the case of a buffer overrun, the portion of the packet
; that fit into the buffer is returned and BX and CX are equal. There
; is no way to tell the difference between a packet that just exactly
; fit and one which was truncated due to buffer overrun, so it is
; important to assure that the buffer is big enough to ALWAYS contain
; at least one spare byte.
;
; A sample RECV_CHAR() is defined above.
;
; Entry:
; ES:DI ==> packet buffer
; CX = length of buffer
; direction flag is assumed to be cleared (incrementing)
;
; Exit:
; BX = length of packet received
;
; Trashed:
; none
;
recv_packet proc
push cx
push di
xor bx,bx ; zero received byte count
jcxz @@bailout ; if zero length packet, bail out now
@@nextchar:
RECV_CHAR ; fetch a character into al
cmp al,SLIP_END ; Q: is it the special END char?
jne @@check_esc ; N: check for ESC
or bx,bx ; YQ: is it the beginning of packet?
jz @@nextchar ; Y: keep looking
jmp @@bailout ; N: end of packet, so return it
@@check_esc:
cmp al,SLIP_ESC ; Q: is it the special ESC char?
jne @@ordinary ; N: it's an ordinary char
RECV_CHAR ; Y: get another character
cmp al,SLIP_ESC END ; Q: is it ESC_END?
jne @@check_esc_esc ; N: check for ESC_ESC
mov al,SLIP_END ; Y: convert to ordinary END char
jmp @@ordinary ;
@@check_esc_esc:
cmp al,SLIP_ESC_ESC ; Q: is it ESC_ESC?
mov al,SLIP_ESC ; Y: convert to ordinary ESC char
; protocol violation! fall through to ordinary character
@@ordinary:
stosb ; store character in buffer
inc bx ; got another char
loop @@nextchar ; keep going until we've sent all chars
@@bailout:
pop di
pop cx
ret
recv_packet endp
END
Assembly language program which
shows the current date
Assembly language program which shows the current date
and time in a form identical to that used by Posix ctime()
; showdate.asm
;
; prints the date and time to stdout
; equivalent to the following C++ program:
;
;#include <iostream.h>
;#include <time.h>
;
;int main()
;{
; time_t t;
; time(&t); // get the current time
; cout << ctime(&t); // convert to string and print
; return 0;
;}
;
; This code may be assembled and linked using Borland's TASM:
; tasm /la /m2 showdate
; tlink /Tdc showdate
;
STDOUT equ 01h ; handle of standard output device
DOS_GET_DATE equ 02ah ; get system date
DOS_GET_TIME equ 02ch ; get system time
DOS_WRITE_HANDLE equ 040h ; write to handle
DOS_TERMINATE equ 04ch ; terminate with error code
MODEL tiny
;.STACK 100h
.CODE
;****************************************************************
; main
;
; calls showdate routne and exists with 00 error code
;
; Entry:
;
; Exit:
;
; Trashed:
; none
;
;****************************************************************
main proc far
.STARTUP ; sets up DS and stack
call showdate ;
.EXIT 0 ; return with errcode=0
main endp
;****************************************************************
; showdate
;
; fetches the DOS system date, prints it to stdout and exits
; the format of the output is identical to that of the Posix ctime()
; function:
;
; Thu May 11 16:11:30 2000
;
; The day of week and month are always 3 characters long. The time of
; day is in 24hour form (e.g. 16:11:30 is a few minutes after four in
; the afternoon) and the year is always four digits. The whole thing is
; followed by a newline character (line feed = 0ah), making 25
; characters total.
;
; Note that ctime() returns 26 characters which is all of the above,
; followed by a terminating NUL char but this program does not emit a
; NUL.)
;
; Entry:
; DS points to segment for our data tables
;
; Exit:
; carry may be set if last write failed
;
; Trashed:
; none
;
showdate proc
push ax bx cx dx ;
DOSINT DOS_GET_DATE ;
; returns the following
; cx = year (1980-2099)
; dh = month (1-12) == (Jan..Dec)
; dl = day (1-31)
; al = day of week (0-6) == (Sun..Sat)
push cx ;
push dx ; save the return values
WriteSubstring
; Entry:
DS:DX ==> pointer to base of string array
CL = size of each string
AL = string selector (i.e. which string)
; Exit:
; CY set if there was an error writing last byte
; if CY clear,
; AX = 1 (number of bytes written)
; else
; AX = error code
;
; Trashed:
; BX CX DX
;
WriteSubstring proc
mul cl ; ax = cl * al
add dx,ax ; offset now points to appropriate day string
call PrintIt ;
WriteSubstring endp
; deliberately fall through
; WriteSpace
;
; writes a single space character (20h) to stdout
;
; Entry:
; DS points to data table segment
;
; Exit:
; CY set if there was an error writing last byte
; if CY clear,
; AX = 1 (number of bytes written)
; else
; AX = error code
;
; Trashed:
; BX CX DX
WriteSpace proc
mov dx,offset spacechar;
WriteSpace endp
; deliberately fall through
PrintOne
;
; prints a single character pointed to by DS:DX
;
; Entry:
; DS:DX ==> points to the character to be printed
; Exit:
; CY set if there was an error writing last byte
; if CY clear,
; AX = 1 (number of bytes written)
; else
; AX = error code
;
; Trashed:
; BX CX DX
;
PrintOne proc
mov cx,1 ;
PrintOne endp
; deliberately fall through
; PrintIt
;
; prints the passed string to stdout
;
; Entry:
; DS:DX ==> points to string to be printed
; CX = number of bytes to be printed
; Exit:
; CY set if there was an error writing to stdout
; if CY clear,
; AX = number of bytes written
; else
; AX = error code
;
; Trashed:
; none
;
PrintIt proc
mov bx,STDOUT ;
DOSINT DOS_WRITE_HANDLE ; write to the file
ret
PrintIt endp
; WriteColon
;
; writes a colon character to stdout
;
; Entry:
; DS points to data segment
; Exit:
; CY set if there was an error writing to stdout
; if CY clear,
; AX = 1 (number of bytes written)
; else
; AX = error code
;
; Trashed:
; none
;
WriteColon proc
mov dx,offset colonchar;
jmp PrintOne ;
WriteColon endp
;****************************************************************
; WriteNumber
;
; prints the number in AL to stdout as two decimal digits
;
; Entry:
; AL = number to be printed. It must be in the range 00-99
;
; Exit:
; CY set if there was an error writing to stdout
if CY clear,
AX = 2 (number of bytes written)
else
AX = error code
;
; Trashed:
; BX CX DX
;
WriteNumber proc
xor ah,ah ; clear out high half
mov cl,10 ; prepare to convert to decimal (base 10)
div cl ; divide it out
or ax,3030h ; convert to ASCII digits
push ds ; remember DS for later
push ax ; push converted chars on stack
mov dx,ss ;
mov ds,dx ; ds = ss
mov dx,sp ; print data from stack
mov cx,2 ; two characters only
call PrintIt ;
pop bx ; fix stack
pop ds ; restore ds pointer
ret ;
WriteNumber endp
;.DATA
dayname db "SunMonTueWedThuFriSat"
monthname db "JanFebMarAprMayJunJulAugSepOctNovDec"
spacechar db " "
colonchar db":"
newlinechar db 0ah ; in C this is \n
end
This program provides BASIC
programs with access to the program
loader (LOAD)
This program provides BASIC programs with access to the program loader
(LOAD)
by passing parameters via the system parameter area (SYSPARM).
;BASLOAD.ASM
;----------------------
;Inputs:
; FILE SPEC 1 - A string (len <= 80) with the complete name, including
; path, of the file to be loaded and executed.
; Example: 'MAINMENU.EXE' or 'C:\FORMAT.COM'
; PARAMETER 1 - A string (len <= 80) with the command line parameters
; to be passed to the program specified in FILE SPEC 1.
; Example: '' or 'A:'
; FILE SPEC 2 - Same as 1.
; PARAMETER 2 - Same as 1.
;
;Outputs:
; This program gives control to LOAD.
;------------------------------------------------
CODE SEGMENT 'CODE'
ASSUME CS:CODE
;prologue
PUSH BP ;save BP
MOV BP,SP ;set base for parm list
PUSH DS ;DS -> basic work area
PUSH ES ;ES -> basic work area
MOV DX,'dk' ;interrupt verification switch
INT 77H ;get seg address of sysparm area in AX
MOV ES,AX ;ES -> sysparm area
CLD ;set direction for all moves
;exit to BASIC
POP ES
POP DS
POP BP
RET 8
BASLOAD ENDP
CODE ENDS
END BASLOAD
This program is used to set the PSP
address for a compiled BASIC
program
This program is used to set the PSP address for a compiled BASIC program
The PSP segment is saved at 0:4F2H.
extrn $$main:far
cseg segment para public 'code'
; This routine gets control before BASIC, does its handiwork, and then
; passes control to the BASIC program. It must be linked as follows:
; LINK BASMAIN+yourprog,yourprog,NUL.MAP,BASCOM
public basmain
basmain proc far
assume cs:cseg,ds:cseg,ss:nothing,es:nothing
push ds ; save ds
xor ax,ax
mov ds,ax ; ds=0
mov si,4f2h ; dos communications area
mov ax,es ; get psp seg
mov [si],ax ; save psp in dos comm area
pop ds ; restore ds
mov si,80h ; point to command line
mov ch,0
mov cl,[si] ; get length of command line
jcxz p025 ; it's zero
p010: inc si
mov al,[si] ; get char from command line
cmp al,'/' ; is it a slash?
jnz p020 ; no
mov ax,[si+1] ; get next 2 chars
cmp ax,':M' ; is it M: ?
jz p030 ; yes
cmp ax,':m' ; is it m: ?
jz p030 ; yes
basmain endp
cseg ends
end basmain ; must be a main program!
BIOS-based disk I-O to access MS-
DOS file structure
BIOS-based disk I-O to access MS-DOS file structure
; rawread.asm
;
; this program reads a DOS cluster using only BIOS disk calls. All
; of the tasks usually done by DOS, e.g. FAT lookup, cluster to
; logical sector translation, logical to physical translation, are
; all done by this program instead. The idea is to be able to create
; a program that can access DOS disks from a bootable floppy without
; having to have DOS.
;
; well, that's what it used to do. Now it's supposed to do something
; completely different. Its job is to scan the entire surface of the
; hard drive, looking for the specified string. If that string is
; found, it is to print the full path and directory entry, including
; the file date and time.
;
; but wait! There's more. Now what we have is a number of raw
; routines which could prove useful for manipulating a DOS file
; structure outside of the DOS environment. The main routine still
; should be kept (if renamed), since the order in which these things
; are done is important (e.g. later calls depend on data set up by
; earlier calls).
;
; get filename
; parse filename into subdirs
; locate root dir and cluster size
; follow subdir routing to filename
; report file size, date & time
;
.MODEL small
.STACK 0200h
.586P
.DATA
PartEntry STRUC
Bootable db ? ;80h = bootable, 00h = nonbootable
BeginHead db ;beginning head
BeginSector db ? ;beginning sector
BeginCylinder db ;beginning cylinder
FileSystem db ? ;name of file system
EndHead db ? ;ending head
EndSector db ? ;ending sector
EndCylinder db ;ending cylinder
StartSector dd ? ;starting sector (relative to beg. of disk)
PartSectors dd ? ;number of sectors in partition
PartEntry ENDS
BootSector STRUC
Jump db ? ;E9 xx xx or EB xx 90
JumpTarget dw ? ;E9 xx xx or EB xx 90
OemName db '????????' ;OEM name & version
;Start of BIOS parameter block
BytesPerSec dw ;bytes per sector
SecPerClust db ;sectors per cluster
ResSectors dw ;number of reserved sectors
FATs db ? ;number of file allocation tables
RootDirEnts dw ? ;number of root-dir entries
Sectors dw ? ;total number of sectors
Media db ? ;media descriptor byte
FATsecs dw ? ;number of sectors per FAT
SecPerTrack dw ? ;sectors per track
Heads dw ? ;number of heads
HiddenSecs dd ;number of hidden sectors
HugeSectors dd ;num sectors if Sectors==0
;End of BIOS parameter block
BootSector ENDS
DirEntry STRUC
FileName db '????????' ;name
Extension db '???' ;extension
Attributes db ? ;attributes
Reserved db 10 dup (?) ;reserved
Time dw ? ;time stamp
Date dw ? ;date stamp
StartCluster dw ? ;starting cluster
FileSize dd ? ;file size
DirEntry ENDS
.CODE
main PROC
STARTUPCODE ;initialize stuff
call FetchMBR C ;fetch the master boot record
jc @@exit
mov cx,4 ;search up to four partitions
add bx,01aeh ;point to partition table (-10h)
@@FindBootable:
add bx,10h ;point to next entry
cmp BYTE ptr [bx],80h ;is it a bootable partition?
loopnz @@FindBootable
call FetchSector C, \
WORD ptr [(PartEntry PTR bx).BeginHead], \
WORD ptr [(PartEntry PTR bx).BeginSector], \
WORD ptr [(PartEntry PTR bx).BeginCylinder], \
OFFSET MBR, ds ;SEG MBR
;
; here's the point at which our OS loader would begin, with the
; BootSector structure in memory.
;
mov bx, OFFSET MBR
call CalcClustOff C, \
WORD ptr [(BootSector PTR bx).ResSectors], \
WORD ptr [(BootSector PTR bx).FATsecs], \
WORD ptr [(BootSector PTR bx).FATs], \
WORD ptr [(BootSector PTR bx).RootDirEnts], \
WORD ptr [(BootSector PTR bx).BytesPerSec], \
WORD ptr [(BootSector PTR bx).SecPerClust]
mov WORD ptr [ClustOffs],ax
mov WORD ptr [ClustOffs+2],dx
call CalcClust2 C, \
WORD ptr [(BootSector PTR bx).ResSectors], \
WORD ptr [(BootSector PTR bx).FATsecs], \
WORD ptr [(BootSector PTR bx).FATs]
; now dx:ax contains the logical sector for cluster 2
call LsectToGeom C, \
ax, dx, \
WORD ptr [(BootSector PTR bx).HiddenSecs] , \
WORD ptr [((BootSector PTR bx).HiddenSecs)+2],\
[(BootSector PTR bx).Heads], \
[(BootSector PTR bx).SecPerTrack]
mov dl,80h
mov bx,offset buff
mov al,[(BootSector PTR MBR).SecPerClust]
mov ah,2h ; get ready to read
int 13h
; now find our desired filename within buffer (which has the root dir)
call FindFile C, \
bx, 200h * 40h, offset BootFileName
xor dh,dh
mov dl,[(BootSector PTR MBR).SecPerClust]
mov si,ax
mov ax,[(DirEntry PTR si).StartCluster]
mul dx
add ax,WORD ptr [ClustOffs]
adc dx,WORD ptr [ClustOffs+2]
; now dx:ax contains logical sector number for start of file
call LsectToGeom C, \
ax, dx, \
WORD ptr [(BootSector PTR MBR).HiddenSecs] , \
WORD ptr [((BootSector PTR MBR).HiddenSecs)+2],\
[(BootSector PTR MBR).Heads], \
[(BootSector PTR MBR).SecPerTrack]
mov dl,80h
mov ax,204h ; read in 2k worth of data
int 13h
@@exit:
EXITCODE ;exit to DOS
ENDP main
;
; FetchMBR - fetches the Master Boot Record from the first physical
; hard disk and stores it in the location MBR.
;
; INPUT: none
; OUTPUT: AX is error code if CY set, ES:BX ==> MBR
; DESTROYED: none
FetchMBR PROC C
USES cx, dx ;save registers we'll use
mov dx,80h ;first physical disk
mov cx,1 ;head 1, sector 0
mov bx,ds ;
mov es,bx ;point to boot record buffer
mov bx,OFFSET MBR ;read into boot record
mov ax,0201h ;read one sector
int 13h ;BIOS read
ret ;return to main
FetchMBR ENDP
;
; FetchSector - fetches the physical sector described by the passed
; parameters and stores it in the named buffer
;
; INPUT: head, sector, cylinder, buffer
; OUTPUT: AX is error code if CY set, ES:BX ==> Boot
; DESTROYED: none
;
FetchSector PROC C head:BYTE, sector:BYTE, cylinder:BYTE,
buffer:DWORD
USES cx, dx ;save registers we'll use
mov ch, [cylinder] ;
mov cl, [sector] ;
mov dh, [head] ;
mov dl, 80h ;first physical hard drive
les bx, [buffer] ;
mov ax,0201h ;read one sector
int 13h ;BIOS read
ret ;return to main
FetchSector ENDP
;
; GeomToLsect - converts to logical sector number from the physical
; geometry (head, cylinder, track). See LsectToGeom.
;
; INPUT: cx, dx are set with cylinder/track, and head respectively
; HiddenSecs, Heads, SecPerTrack
; OUTPUT: lsect
; DESTROYED: none
;
GeomToLsect PROC C lsect:DWORD, dHiddenSecs:DWORD, \
dHeads:WORD, dSecPerTrack:WORD, buffer:DWORD
USES ax ;save registers we'll use
mov ax, WORD ptr [lsect] ;load lsect into DX:AX
mov dx, WORD ptr [lsect+2] ;
stc ;add one additional
adc ax, WORD ptr [dHiddenSecs] ;add starting sector
adc dx, WORD ptr [dHiddenSecs+2] ;
div [dSecPerTrack] ;
mov cl,dl ;store sector in cl
xor dx,dx ;
div [dHeads] ;
mov dh,dl ;store head in dh
mov ch,al ;store low 8 bits of cylinder in ch
shr ax,1 ;
shr ax,1 ;
and al,0c0h ;pass through two hi bits only
or cl,ah ;mov bits into location
ret ;
GeomToLsect ENDP
;
; LsectToGeom - converts from logical sector number to the physical
; geometry (head, cylinder, track) in the form required
; by the BIOS (Int 13h) disk read and write calls.
;
; INPUT: lsect, HiddenSecs, Heads, SecPerTrack
; OUTPUT: cx, dx are set with cylinder/track, and head respectively
; DESTROYED: none
;
LsectToGeom PROC C lsect:DWORD, lHiddenSecs:DWORD, \
lHeads:WORD, lSecPerTrack:WORD, buffer:DWORD
USES ax ;save registers we'll use
mov ax, WORD ptr [lsect] ;load lsect into DX:AX
mov dx, WORD ptr [lsect+2] ;
stc ;add one additional
adc ax, WORD ptr [lHiddenSecs] ;add starting sector
adc dx, WORD ptr [lHiddenSecs+2] ;
div [lSecPerTrack] ;
mov cl,dl ;store sector in cl
xor dx,dx ;
div [lHeads] ;
mov dh,dl ;store head in dh
mov ch,al ;store low 8 bits of cylinder in ch
shr ax,1 ;
shr ax,1 ;
and al,0c0h ;pass through two hi bits only
or cl,ah ;mov bits into location
ret ;
LsectToGeom ENDP
;
; CalcClust2 - calculates the starting logical sector number of
; cluster 2, (the beginning of data space for
; partitions).
;
; INPUT: ResSectors, FATsecs, FATs
; OUTPUT: dx:ax contains the starting logical sector number
; DESTROYED: none
;
CalcClust2 PROC C cResSectors:WORD, cFATsecs:WORD, cFATs:BYTE
xor dx,dx ;
mov ax,[cFATsecs] ;
mul [cFATs] ;
add ax,[cResSectors] ;
adc dx,0 ;
ret
CalcClust2 ENDP
;
; CalcClustOff - calculates the starting logical sector number of
; cluster 0, which isn't really a cluster, but the
; number returned is useful for calculations converting
; cluster number to logical sector
;
; INPUT: ResSectors, FATsecs, FATs
; OUTPUT: dx:ax contains the starting logical sector number
; DESTROYED: none
;
CalcClustOff PROC C dResSectors:WORD, dFATsecs:WORD, dFATs:BYTE,
\
dRootDirEnts:WORD, dBytesPerSec:WORD, dSecPerClust:BYTE
LOCAL clustLo:WORD, clustHi:WORD
xor dh,dh
mov ax,[dFatSecs]
mov dl,[dFATs]
mul dx
add ax,[dResSectors]
adc dx,0
call CalcClust2 C, [dResSectors], [dFATsecs], [dFATs]
; now dx:ax = FATs * FATsecs + ResSectors
mov [clustLo],ax
mov [clustHi],dx
mov dx,20h ; bytes per dir entry
mov ax,[dRootDirEnts] ;
mul dx ; multiply 'em out
div [dBytesPerSec] ; and divide by bytes/sec
add [clustLo],ax ;
adc [clustHi],dx ; create the aggregate
mov al,[dSecPerClust] ;
xor ah,ah ;
shl ax,1 ; AX = SecPerClust * 2
sub [clustLo],ax ;
sbb [clustHi],0 ; propagate carry flag
mov ax,[clustLo] ;
mov dx,[clustHi] ;
ret
CalcClustOff ENDP
;
; FindFile - given a memory buffer containing the directory data
; and a static file name for which to search, this routine
; finds the file and returns a pointer to its directory
; entry in ds:si
;
; INPUT: dirbuffer, filespec
; OUTPUT: ax contains pointer to directory entry (or NULL)
; DESTROYED: none
;
FindFile PROC C dirbuffer:WORD, limit:WORD, filespec:WORD
USES cx, dx, di, si, es
mov cx,ds ;
mov es,cx ; es and ds point to same segment
cld ; always count forward
mov ax,[dirbuffer] ; load 'em up
add [limit],ax
mov dx,[filespec] ;
keepsearching:
mov cx,11 ; size of dos filename (8.3)
mov si,dx ;
mov di,ax ;
repe cmpsb ; compare 'em
jz foundit ;
add ax,20h ; size of directory entry
cmp ax,[limit]
jb keepsearching
xor ax,ax
foundit:
ret
FindFile ENDP
END
Boot loader for a roll-your-own
operating system
Boot loader for a roll-your-own operating system
; loader.asm
PartEntry STRUC
Bootable db ? ;80h = bootable, 00h = nonbootable
BeginHead db ;beginning head
BeginSector db ? ;beginning sector
BeginCylinder db ;beginning cylinder
FileSystem db ? ;name of file system
EndHead db ? ;ending head
EndSector db ? ;ending sector
EndCylinder db ;ending cylinder
StartSector dd ? ;starting sector (relative to beg. of disk)
PartSectors dd ? ;number of sectors in partition
PartEntry ENDS
BootSector STRUC
bsJump db 0EBh, (extra - bsJump), 090h
; E9 XX XX or EB xx 90
OemName db 8 dup (?) ; OEM name and version
; start of BIOS parameter block
BytesPerSec dw ? ; bytes per sector
SecPerClust db ? ; sectors per cluster
ResSectors dw ; number of reserved sectors
FATs db ? ; number of FATs
RootDirEnts dw ; number of root directory entries
Sectors dw ; total number of sectors (see HugeSectors)
Media db ? ; media descriptor byte (0f0h for floppies)
FATsecs dw ; number of sectors per FAT
SecPerTrack dw ; sectors per track
Heads dw ? ; number of heads
HiddenSecs dd ; number of hidden sectors
HugeSectors dd ; number of sectors if Sectors equals 0
; end of BIOS parameter block
DriveNumber db ;
Reserved1 db ? ;
BootSignature db ;
VolumeID dd ;
VolumeLabel db 11 dup (?)
FileSysType db 8 dup (?)
extra dw
BootSector ENDS
DirEntry STRUC
FileName db '????????' ;name
Extension db '???' ;extension
Attributes db ? ;attributes
Reserved db 10 dup (?) ;reserved
Time dw ? ;time stamp
Date dw ? ;date stamp
StartCluster dw ? ;starting cluster
FileSize dd ? ;file size
DirEntry ENDS
CR EQU 0DH
LF EQU 0AH
;
; CalcClustOff - calculates the starting logical sector number of
; cluster 0, which isn't really a cluster, but the
; number returned is useful for calculations converting
; cluster number to logical sector
;
; INPUT: ResSectors, FATsecs, FATs
; OUTPUT: dx:ax contains the starting logical sector number
; DESTROYED: none
;
;****************************************************************
CalcClustOff PROC
xor dh,dh
mov ax,[Boot.FatSecs]
mov dl,[Boot.FATs]
mul dx
add ax,[Boot.ResSectors]
adc dx,0
; now dx:ax = FATs * FATsecs + ResSectors
mov word ptr [ClustOffs],ax
mov word ptr [ClustOffs+2],dx
mov dx,20h ; bytes per dir entry
mov ax,[Boot.RootDirEnts]
mul dx ; multiply 'em out
div word ptr [Boot.BytesPerSec] ; and divide by bytes/sec
add word ptr [ClustOffs],ax
adc word ptr [ClustOffs+2],dx ; create the aggregate
mov al,[Boot.SecPerClust] ;
xor ah,ah ;
shl ax,1 ; AX = SecPerClust * 2
sub word ptr [ClustOffs],ax ;
sbb word ptr [ClustOffs+2],0 ; propagate carry flag
mov ax,word ptr [ClustOffs] ;
mov dx,word ptr [ClustOffs+2] ;
ret
CalcClustOff ENDP
call FindFile C, \
bx, 200h * 40h, offset BootFileName
xor dh,dh
mov dl,[(BootSector PTR MBR).SecPerClust]
mov si,ax
mov ax,[(DirEntry PTR si).StartCluster]
mul dx
add ax,WORD ptr [ClustOffs]
adc dx,WORD ptr [ClustOffs+2]
; now dx:ax contains logical sector number for start of file
call LsectToGeom C, \
WORD ptr [(BootSector PTR MBR).HiddenSecs] , \
WORD ptr [((BootSector PTR MBR).HiddenSecs)+2],\
[(BootSector PTR MBR).Heads], \
[(BootSector PTR MBR).SecPerTrack]
retry2:
mov si,offset Boot
mov dl,[(BootSector PTR si).DriveNumber]
mov ah,2h
; read in a cluster's worth of data
mov al,[(BootSector PTR si).SecPerClust]
; point to our magic location
mov bx,seg destination
mov es,bx
mov bx,offset destination
int 13h
jc retry2
@@exit:
jmp destination
ENDP main
;
; LsectToGeom - converts from logical sector number to the physical
; geometry (head, cylinder, track) in the form required
; by the BIOS (Int 13h) disk read and write calls.
;
; INPUT: dx:ax=lsect, HiddenSecs, Heads, SecPerTrack
; OUTPUT: cx, dx are set with cylinder/track, and head respectively
; DESTROYED: none
;****************************************************************
LsectToGeom PROC C lHiddenSecs:DWORD, \
lHeads:WORD, lSecPerTrack:WORD, buffer:DWORD
USES ax ;save registers we'll use
stc ;add one additional
adc ax, WORD ptr [lHiddenSecs] ;add starting sector
adc dx, WORD ptr [lHiddenSecs+2] ;
div [lSecPerTrack] ;
mov cl,dl ;store sector in cl
xor dx,dx ;
div [lHeads] ;
mov dh,dl ;store head in dh
mov ch,al ;store low 8 bits of cylinder in ch
shr ax,1 ;
shr ax,1 ;
and al,0c0h ;pass through two hi bits only
or cl,ah ;mov bits into location
ret ;
LsectToGeom ENDP
;
; CalcClust2 - calculates the starting logical sector number of
; cluster 2, (the beginning of data space for
; partitions).
;
; INPUT: ResSectors, FATsecs, FATs
; OUTPUT: dx:ax contains the starting logical sector number
; DESTROYED: none
;
;****************************************************************
CalcClust2 PROC C cResSectors:WORD, cFATsecs:WORD, cFATs:BYTE
xor dx,dx ;
mov ax,[cFATsecs] ;
mul [cFATs] ;
add ax,[cResSectors] ;
adc dx,0 ;
ret
CalcClust2 ENDP
;
; FindFile - given a memory buffer containing the directory data
; and a static file name for which to search, this routine
; finds the file and returns a pointer to its directory
; entry in ds:si
;
; INPUT: dirbuffer, filespec
; OUTPUT: ax contains pointer to directory entry (or NULL)
; DESTROYED: none
FindFile PROC C dirbuffer:WORD, limit:WORD, filespec:WORD
USES cx, dx, di, si, es
mov cx,ds ;
mov es,cx ; es and ds point to same segment
cld ; always count forward
mov ax,[dirbuffer] ; load 'em up
add [limit],ax
mov dx,[filespec] ;
keepsearching:
mov cx,11 ; size of dos filename (8.3)
mov si,dx ;
mov di,ax ;
repe cmpsb ; compare 'em
jz foundit ;
add ax,20h ; size of directory entry
cmp ax,[limit]
jb keepsearching
xor ax,ax
foundit:
ret
FindFile ENDP
END
Control-Break handler for Lattice C
programs
Control-Break handler for Lattice C programs
pseg
;
; The function CAPTURE is called by the C program to
; take over the MS-DOS and keyboard driver Control
; Break interrupts (1BH and 23H). It is passed the
; address of a flag within the C program which is set
; to TRUE whenever a Control-Break or Control-C
; is detected. The function is used in the form:
;
; static int flag;
; capture(&flag)
;
capture proc near ;take over Control-Break
capture endp
;
; The function RELEASE is called by the C program to
; return the MS-DOS and keyboard driver Control-Break
; interrupt vectors to their original state. Int 23h is
; also automatically restored by MS-DOS upon the termination
; of a process, however, calling RELEASE allows the C
; program to restore the default action of a Control-C
; without terminating. The function is used in the form:
;
; release()
;
release endp
;
; This is the actual interrupt handler which is called by
; the ROM BIOS keyboard driver or by MS-DOS when a Control-C
; or Control-Break is detected. Since the interrupt handler
; may be called asynchronously by the keyboard driver, it
; is severely restricted in what it may do without crashing
; the system (e.g. no calls on DOS allowed). In this
; version, it simply sets a flag within the C program to
; TRUE to indicate that a Control-C or Control-Break has
; been detected; the address of this flag was passed
; by the C program during the call to the CAPTURE function.
;
iret
ctrlbrk endp
endps
end
Break
Break
public brkflag
brkflag DW 0
DATA ends
public TrapBrea
assume cs:PGROUP,DS:DGROUP
push ds
push cs
pop ds
mov dx,offset PGROUP:Bret
mov ah,25h
mov al,23h
int 21h
pop ds
ret
TrapBrea endp
push ds
push ax
mov ax,DGROUP
mov ds,ax
mov brkflag,1
pop ax
pop ds
iret
Bret endp
PROG ends
end
Break Handling Utilities Module
Break Handling Utilities Module
PUBLIC CHECK_BREAK
CHECK_BREAK PROC FAR
XOR AX, AX ;clear ax
MOV AL, BREAKFLAG ;return value = breakflag
MOV BREAKFLAG, FALSE ;reset breakflag
RET
CHECK_BREAK ENDP
PUBLIC INST_BRK_HANDLR
INST_BRK_HANDLR PROC FAR
PUSH DS
MOV AL, BREAKINT ;AL = break interrupt
MOV AH, GETVECTOR ;AH = dos function code
INT DOS_FUNCTION ;call dos
MOV WORD PTR SAVEBRK, BX ;save offset in int vector
MOV WORD PTR SAVEBRK+2, ES ;save base in int vector
MOV AL, BREAKINT ;AL = break interrupt
MOV AH, SETVECTOR ;AH = dos function code
MOV DX, OFFSET BRK_HANDLER ;DX = offset of brk handler
MOV BX, CS ;BX = this segment
MOV DS, BX ;DS = this segment
INT DOS_FUNCTION ;call dos
POP DS
RET
INST_BRK_HANDLR ENDP
COMMENT *
Examples:
burnout 5000 (sets time to 5000 ticks)
burnout 5000H+ (time=5000, use hardware blanking)
burnout 5000,h+ (ditto, separators don't matter)
burnout c+h-v+ (continuous clear, software, monitor video)
burnout /C+ /H- /V+ (ditto)
burnout (return status only)
Assembly/link:
masm burnout;
link burnout; (ignore NO STACK warning message)
exe2bin burnout burnout.com
code segment
assume cs:code,ds:code
exit:
int TERMINATE ; Exit to DOS
main endp
code ends
end main
Calculator
Calculator
PAGE ,132
TITLE CALC
CGROUP GROUP CODESEG
CODESEG SEGMENT PARA PUBLIC 'CODE'
ASSUME CS:CGROUP,DS:CGROUP,ES:CGROUP
PUBLIC CALC
ORG 100H
;;
; DATA AREA ;
; ;
;-------------------------------------------------------
SCREEN_HANDLE DW 0001h
TAG DB 'PEMATH'
TAG_LEN EQU $-TAG
;-----------------------------------------------------------
; ;
; CODE AREA ;
; ;
;-----------------------------------------------------------
START:
;-----------------------------------------------------------
; TEST FOR PRESENCE OF CALCULATOR
SUB AX,AX
MOV ES,AX
SUB BH,BH
MOV BL,INT_NUMBER
SHL BX,1
SHL BX,1
MOV DI,ES:[BX]
MOV ES,ES:[BX+2]
ADD DI,4
LEA SI,TAG
MOV CX,TAG_LEN
REPE CMPSB
JE CALL_CALC
MOV BX,SCREEN_HANDLE
MOV CX,MESSAGE_LEN
LEA DX, MESSAGE
MOV AH,40h
INT 21h
JMP SHORT CALC_EXIT
CALL CALCULATOR
CALL_CALC:
MOV AL,INT_NUMBER
MOV BYTE PTR INT_CODE,AL
DB 0CDh ; INT
INT_CODE:
DB 00h
NOP
NOP
CALC_EXIT:
INT 20h
CALC ENDP
CODESEG ENDS
END CALC
Cd Check
Cd Check
.model small ; It's a flaw of mine ... I really like this model
; I know I should do a .com with the tiny model..
; but I just love the small :>
.stack 100h ; Plenty stack ;>
.386
.data
info db 30 dup (0)
right db 'Right CD$'
wrong db 'Wrong CD$'
nomscdex db 'MSCDEX not installed$'
.code
mov ax, @data ; Make DS&ES point to the DATA
mov ds,ax
mov es,ax
continiue:
dec edi ; next drive
jnz nextloop
lea edx, wrong ; not in any drive!
exit:
mov ah, 9h
int 21h
mov ax,4c00h ; terminate!
int 21h
end
Char
Char
subttl -
;;
;;FUNCTION: Sets and returns switch char
;; acter and device availability.
;;
;;
;;CALL:
;;
;; ret= _charop(al,dl)
;; int ret; DL return value,
;; int al; charoper function
;; int dl; charoper data
;;
;;RETURN:
;; See the DOS docs for details.
;;_charop(0,0) returns the ASCII switch char,
;;_charop(1,'-') sets the switch to -,
;;_charop(2,0) returns device availability,
;;_charop(3,i) sets device availability.
;;
;;
DESCRIPTION:
;;
;;EXAMPLE:
;;
;;
;;CAUTIONS:
;;
;;
;;ASSUMPTIONS:
;;
;;LONG 32 bits (4 bytes)
;;INT 16 bits (2 bytes)
;;CHAR 8 bits (1 byte)
;;
page
pgroup group prog
prog segment byte public 'prog'
assume cs:pgroup,ds:pgroup
public _charop
prog ends
end
Checks the validity of an ISBN by
verifying the checksum
Checks the validity of an ISBN by verifying the checksum
; isbnchek.asm
comment A
.MODEL small
public isbncheck
.CODE
;/****************************************************************
;
; Name:
; isbncheck
;
; Purpose:
; Calculates the check digit for a ten digit ISBN, converts that
; digit to its ASCII representation and returns that answer.
;
; Algorithm:
; An ISBN consists of nine digits plus a validation digit.
; Number the digits from left to right as d1, d2, ... d9, with
; d10 being the validation digit. The calculation is then
;
d10 = (1(d1) + 2(d2) + 3(d3) + ... + i(di) + ... + 9(d9))%11
;
or the weighted sum of each digit mod eleven.
; calling convention:
returns:
OR
; >>> A return of 280 means you got an 80286 machine with no NDP, <<<
; >>> 383 means you have an 80386/80387 rig to work with, and a <<<
; >>> return of 81 sez that you have 8088/8086 CPU with an 8087. <<<
; >>> A 200 tells you that you got an NEC V20/V30 without an NDP. <<<
; >>> ETC., Etc., etc. <<<
;
; NOTE:
There are lotsa ways of handling the way this function returns
it's data. For my purposes, I have elected this one because
it requires only int arithmetic on the caller's end to extract
all the info I need from the return value. I think that I'm
well enough 'commented' in the following code so that you will
be able to tinker and Putz until you find the best return tech
nique for Ur purposes without having to reinvent the wheel.
chips PROC
push DI
push SI
push CX ; not really needed for MSC but kinda
; nice to do cuz someone else might
; want to use the function and we do
; use CX later on
call cpu_type ; find out what kinda CPU you got and
; and save it in DX for future reference
call ndp_type ; check for math coprocessor (NDP) type
; and hold that result in AX
cpu_type PROC
; OTHERWISE...
; Here's the BIG one... 'tells the difference between an 80286 and
; an 80386 !!
got386: mov DX, 0380 ; save 380 in DX cuz it's an Intel 80386
jmp SHORT CPUbye ; and bail out
ndp_type PROC
; 'got an 8087 ??
text ends
end
Circle
Circle
cseg segment
assume cs:cseg, ds:cseg, ss:cseg
org 100h
.386
start:
loopdr:
fild angle
fsincos
xor di, di
mov ax, y_co
shl ax, 6
add di, ax
shl ax, 2
add di, ax
add di, x_co
fadd yvel
fxch st(1)
fadd xvel
fxch st(1)
inc angle
jnz loopdr
xor ax, ax
int 16h
mov ax, 3
int 10h
int 20h
x_co dw 0
y_co dw 0
x_rad dw 10
y_rad dw 10
xvel dq 0.001
yvel dq 0.001
angle dw 0
cseg ends
end start
This is a simple litte assembler
program that cleans out
This is a simple litte assembler program that cleans out
;****************************************************************
;* Writes out the NULL terminated text supplied in BX. *
;* OR writes out data,BX and size,CX if called at lwrite. *
write: pusha
mov si,bx ;copy to SI
mov cx,0 ;clear count
wloop: lodsb ;load AL with SI
cmp al,0 ;end of line ?
je lwrite ;yeahh
inc cx ;no, incrase byte count
jmp wloop ;test next byte
lwrite: mov dx,bx ;text address in DX
mov bx,1 ;filehandle standard output = 1
mov ah,writef ;MS-DOS writefile with handle is 040
int msdos ;write buffer to standard output
popa
ret ;done
comseg: dw 0
extseg: dw 0
utext: db "XXX",13,10,0
errt0: db "Could not find current directory !",13,10,0
errt1: db "Directory not found.",13,10,0
errt2: db "Could not delete ",0
path: db " :\WINDOWS\RECENT",0 ;default path without DRIVE
files: db "*.*",0
linefeed: db 13,10,0
END