0% found this document useful (0 votes)
26 views123 pages

Tran T. Assembly Examples. Learn Assembly by Examples 2018

The document contains a collection of assembly language programs, including examples for calculating Fibonacci numbers, setting alarms, and handling various system functions. Each program is accompanied by detailed comments explaining its functionality and implementation. The document serves as a practical guide for learning assembly language through real-world examples.

Uploaded by

edwardruj
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
26 views123 pages

Tran T. Assembly Examples. Learn Assembly by Examples 2018

The document contains a collection of assembly language programs, including examples for calculating Fibonacci numbers, setting alarms, and handling various system functions. Each program is accompanied by detailed comments explaining its functionality and implementation. The document serves as a practical guide for learning assembly language through real-world examples.

Uploaded by

edwardruj
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 123

EXAMPLES

Learn Assembly by examples

I Thanh Tran

2018
CONTENTS:

1. A small program that calculates and prints terms of the Fibonacci


series
2. Alarm
3. An implementation of SLIP (Serial Link IP), RFC 1055 in
assembly language
4. Assembly language program which shows the current date
5. This program provides BASIC programs with access to the
program loader (LOAD)
6. This program is used to set the PSP address for a compiled
BASIC program
7. BIOS-based disk I-O to access MS-DOS file structure
8. Boot loader for a roll-your-own operating system
9. Control-Break handler for Lattice C programs
10. Break
11. Break Handling Utilities Module
12. Burnout
13. Calculator
14. Cd Check
15. Char
16. MSDOS 2.00 Function Library forLattice C
17. Checks the validity of an ISBN byverifying the checksum
18. Chips
19. Circle
20. This is a simple litteassembler programthat cleans out
A small program that calculates and
prints terms of the Fibonacci series
A small program that calculates and prints terms of the Fibonacci series

; 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

; this is just the number of digits for the term counter


cntDigits equ 6 ; number of digits for counter

org 100h ; this is a DOS com file


;****************************************************************
;****************************************************************
main:
; initializes the two numbers and the counter. Note that this assumes
; that the counter and num1 and num2 areas are contiguous!
;
mov ax,'00' ; initialize to all ASCII zeroes
mov di,counter ; including the counter
mov cx,digits+cntDigits/2 ; two bytes at a time
cld ; initialize from low to high memory
rep stosw ; write the data
inc ax ; make sure ASCII zero is in al
mov [num1 + digits - 1],al ; last digit is one
mov [num2 + digits - 1],al ;
mov [counter + cntDigits - 1],al

jmp .bottom ; done with initialization, so begin

.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 ;

; add num2 to num1


mov di,num2+digits-1
mov si,num1+digits-1
mov cx,digits ;
call AddNumbers ; num1 += num2
.bottom
mov bp,num1 ;
call PrintLine ;
dec dword [term] ; decrement loop counter
jnz .top ;
.done
call CRLF ; finish off with CRLF
mov ax,4c00h ; terminate
int 21h ;

;****************************************************************
;
; 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 ;

mov di,counter ; print counter


mov cx,cntDigits ;
call PrintNumericString

call IncrementCount ; also increment the counter


mov dx,msg2 ; print msg2
mov cx,msg2len ;
call PrintString ;
mov di,bp ; recall address of number
mov cx,digits ;
; deliberately fall through to PrintNumericString

;
; 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

msg2 db '): ' ;


msg2len equ $ - msg2
;****************************************************************
; initialized data
;****************************************************************
term dd maxTerms ;
;****************************************************************
; unallocated data
;
; A better way to do this would be to actually ask for a memory
; allocation and use that memory space, but this is a DOS COM file
; and so we are given the entire 64K of space. Technically, this
; could fail since we *might* be running on a machine which doesn't
; have 64K free. If you're running on such a memory poor machine,
; my advice would be to not run this program.
;
;****************************************************************
; static data
counter: ;
num1 equ counter+cntDigits ;
num2 equ num1+digits ;
Alarm
Alarm

cseg segment para public 'code'


org 100h
alarm proc far

; Memory-resident program to intercept the timer interrupt and display the


; system time in the upper right-hand corner of the display.
; This program is run as 'ALARM hh:mm x', where hh:mm is the alarm time and
; x is '-' to turn the display off. Any other value of x or no value will
; turn the clock on

intaddr equ 1ch*4 ; interrupt address


segaddr equ 62h*4 ; segment address of first copy
mfactor equ 17478 ; minute conversion factor * 16
whozat equ 1234h ; signature
color equ 14h ; color attribute

assume cs:cseg,ds:cseg,ss:nothing,es:nothing
jmp p150 ; start-up code

jumpval dd 0 ; address of prior interrupt


signature dw whozat ; program signature
state db 0 ; '-' = off, all else = on
wait dw 18 ; wait time - 1 second or 18 ticks
hour dw 0 ; hour of the day
atime dw Offffh ; minutes past midnite for alarm
acount dw 0 ; alarm beep counter - number of seconds (5)
atone db 5 ; alarm tone - may be from 1 to 255 - the
; higher the number, the lower the frequency
aleng dw 8080h ; alarm length (loop count) may be from 1-FFFF

dhours dw 0 ; display hours


db ':'
dmins dw 0 ; display minutes
db ':'
dsecs dw 0 ; display seconds
db '-'
ampm db 0 ; 'A' or 'P' for am or pm
db 'm'

tstack db 16 dup('stack ') ; temporary stack


estack db 0 ; end of stack
holdsp dw 0 ; original sp
holdss dw 0 ; original ss

p000: ; interrupt code


push ax ; save registers
push ds
pushf

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

p010: cli ; disable interrupts


mov ax,ss ; save stack
mov holdss,ax
mov holdsp,sp
mov ax,ds
mov ss,ax ; point to internal stack
mov sp,offset estack
sti ; allow interrupts

push bx ; save other registers


push cx
push dx
push es
push si
push di
push bp

mov ax,18 ; reset wait time


mov wait,ax

mov al,state ; are we disabled?


cmp al,'-'
jnz p015 ; no
jmp p070

p015: mov ah,0 ; read time


int 1ah ; get time of day
mov ax,dx ; low part
mov dx,cx ; high part
mov cl,4
shl dx,cl ; multiply by 16
mov bx,ax
mov cl,12
shr bx,cl ; isolate top 4 bits of ax
add dx,bx ; now in upper
mov cl,4
shl ax,cl ; multiply by 16
mov bx,mfactor ; compute minutes
div bx ; minutes in ax, remainder in dx
cmp ax,atime ; time to sound the alarm?
jnz p020 ; no
call p100 ; yes - beep the speaker twice
push ax
mov ax,acount ; get beep count
dec ax ; down by 1
mov acount,ax ; save beep count
cmp ax,0 ; is it zero?
jnz p018 ; no - keep alarm on
mov ax,0ffffh ; turn off alarm
mov atime,ax
p018: pop ax

p020: mov dsecs,dx ; save remainder


mov bx,60 ; compute hours
xor dx,dx ; zero it
div bx ; hours in ax, minutes in dx
mov dmins,dx ; save minutes
cmp ax,0 ; midnight?
jnz p030 ; no
mov ax,12 ; yes
jmp p040a ; set am

p030: cmp ax,12 ; before noon?


jb p040a ; yes - set am
jz p040p ; noon - set pm
sub ax,12 ; convert the rest
p040p: mov bl,'p'
jmp p040x

p040a: mov bl,'a'

p040x: mov ampm,bl


aam ; fix up hour
cmp ax,hour ; top of the hour?
jz p060 ; no

mov hour,ax
call p120 ; beep the speaker once

p060: add ax,3030h ; convert hours to ascii


xchg ah,al
mov dhours,ax

mov ax,dmins ; get minutes


aam
add ax,3030h ; convert to ascii
xchg ah,al
mov dmins,ax

mov ax,dsecs ; get seconds (remainder)


xor dx,dx
mov bx,60
mul bx
mov bx,mfactor
div bx ; seconds in ax
aam
add ax,3030h
xchg ah,al
mov dsecs,ax

xor ax,ax ; check monitor type


mov es,ax
mov ax,es:[410h] ; get config byte
and al,30h ; isolate monitor type
cmp al,30h ; color?
mov ax,0b000h ; assume mono
jz p061 ; its mono

mov ax,0b800h ; color screen address

p061: mov dx,es:[463h] ; point to 6845 base port


add dx,6 ; point to status port

mov es,ax ; point to monitor


mov bh,color ; color in bh
mov si,offset dhours ; point to time
mov di, 138 ; row 1, col 69
cld
mov cx,11 ; loop count

p062: mov bl,[si] ; get next character

p063: in al,dx ; get crt status


test al,1 ; is it low?
jnz p063 ; no - wait
cli ; no interrupts

p064: in al,dx ; get crt status


test al,1 ; is it high?
jz p064 ; no - wait

mov ax,bx ; move color & character


stosw ; move color & character again
sti ; interrupts back on
inc si ; point to next character
loop p062 ; done?

p070: pop bp ; restore registers


pop di
pop si
pop es
pop dx
pop cx
pop bx
cli ; no interrupts
mov ax,holdss
mov ss,ax
mov sp,holdsp
sti ; allow interrupts

p080: popf
pop ds
pop ax
jmp cs:[jumpval]

p100 proc near ; beep the speaker twice


call p120
push cx
mov cx,20000
p105: loop p105 ; wait around
pop cx
call p120
push cx
mov cx,20000
p106: loop p106 ; wait around
pop cx
call p120
ret
p100 endp

p120 proc near ; beep the speaker once


push ax
push cx
mov al,182
out 43h,al ; setup for sound
mov al,0
out 42h,al ; low part
mov al,atone ; get alarm tone
out 42h,al ; high part
in al,61h
push ax ; save port value
or al,3
out 61h,al ; turn speaker on
mov cx,aleng ; get loop count
p125: loop p125 ; wait around
pop ax ; restore original port value
out 61h,al ; turn speaker off
pop cx
pop ax
ret
p120 endp

p150: ; start of transient code


mov dx,offset copyr
call p220 ; print copyright
mov ax,0
mov es,ax ; segment 0
mov di,segaddr+2 ; this program's prior location
mov ax,es:[di] ; get prior code segment
mov es,ax ; point to prior program segment
mov di,offset signature
mov cx,es:[di] ; is it this program?
cmp cx,whozat
jnz p160 ; no - install it
call p200 ; set state & alarm
int 20h ; terminate
p160: mov di,segaddr+2 ; point to int 62h
mov ax,0
mov es,ax ; segment 0
mov ax,ds ; get current ds
mov es:[di],ax ; set int 62h
mov si,offset jumpval
mov di,intaddr ; point to timer interrupt
mov bx,es:[di] ; get timer ip
mov ax,es:[di+2] ; and cs
mov [si],bx ; save prior ip
mov [si+2],ax ; and cs
mov bx,offset p000
mov ax,ds
cli ; clear interrupts
mov es:[di],bx ; set new timer interrupt
mov es:[di+2],ax
sti ; set interrupts
push ds
pop es
call p200 ; set state & alarm
mov dx,offset p150 ; last byte of resident portion
inc dx
int 27h ; terminate

p200 proc near ; set state & alarm


mov si,80h ; point to command line
mov ax,0
mov di,0ffffh ; init hours
mov bh,0
mov ch,0
mov dh,0 ;: counter
mov es:[state],bh ; turn clock on
mov cl,[si] ; get length
jcxz p210 ; it's zero

p203: inc si ; point to next char


mov bl,[si] ; get it
cmp bl,'-' ; is it a minus?
jnz p204 ; no
mov es:[state],bl ; turn clock off
push dx
mov dx,offset msg3 ; print msg
call p220
pop dx
jmp p206

p204: cmp dh,2 ; seen 2nd colon?


jz p206 ; yes - ignore seconds
cmp bl,':' ; colon?
jnz p205 ; no
inc dh
cmp dh,2 ; second colon?
jz p206 ; yes - ignore seconds
push cx
push dx
mov cx,60
mul cx ; multiply current ax by 60
pop dx
pop cx
mov di,ax ; save hours
mov ax,0
jmp p206
p205: cmp bl,'0'
jb p206 ; too low
cmp bl,'9'
ja p206 ; too high - can be a problem
sub bl,'0' ; convert it to binary
push cx
push dx
mov cx,10
mul cx ; multiply current value by 10
add ax,bx ; and add latest digit
pop dx
pop cx
p206: loop p203 ; done yet?
cmp di,0ffffh ; any time to set?
jz p210 ; no
add ax,di ; add hours
cmp ax,24*60
jb p209 ; ok
mov dx,offset msg1 ; print error message
call p220
jmp p210

p209: mov es:[atime],ax ; save minutes past midnight


mov ax,5
mov es:[acount],ax ; set alarm count
mov dx,offset msg2 ; print set msg
call p220
p210: ret
p200 endp

p220 proc near ; print message


push ax
mov ah,9
int 21h
pop ax
ret
p220 endp

copyr db 'Alarm - Clock',10,13,'$'


msg1 db 'Invalid time - must be from 00:00 to 23:59',10,13,'$'
msg2 db 'Resetting alarm time',10,13,'$'
msg3 db 'Turning clock display off',10,13,'$'

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

; sends the passed packet (which is in a memory buffer) to the output


; device by using the macro SEND_CHAR() which must be defined by the
; user. A sample SEND_CHAR() is defined above.

; 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

loop @@nextchar ; keep going until we've sent all chars


@@bailout:
SEND_CHAR SLIP_END ; send an end char to signal end of packet
pop si
pop cx
ret
send_packet endp

;****************************************************************
; 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

DOSINT macro function, subfunction


IFB <subfunction>
mov ah,(function AND 0ffh)
ELSE
mov ax,(function SHL 8) OR (subfunction AND 0ffh)
ENDIF
int 21h ; invoke DOS function
endm

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

; write the day of week


mov dx, offset dayname ;
mov cx,3 ; number of bytes to write
call WriteSubstring ;

; write the month


pop ax ; recall month/day
push ax ; and save it again
mov al,ah ; isolate just month
mov dx, offset monthname - 3 ; monthname array is 1-based
mov cx,3 ; number of bytes to write
call WriteSubstring ;

; write the day of the month


pop ax ;
call WriteNumber ;
call WriteSpace ;

; write the hour


DOSINT DOS_GET_TIME ; ch = hour, cl = min,
; dh = sec, dl = hundredths

push dx ; save seconds


push cx ; save minutes
mov al,ch ;
call WriteNumber ;
call WriteColon ;

; write the minutes


pop ax ;
call WriteNumber ;
call WriteColon ;

; write the seconds


pop ax ;
mov al,ah ;
call WriteNumber ;
call WriteSpace ;

; write the year (century first)


pop ax ;
xor dx,dx ; clear other reg before divide
mov cx,100 ; ax = ax/100, dx = remainder
div cx ;
push dx ; save remainder
call WriteNumber ;

; write the year (year within century)


pop ax ;
call WriteNumber ;
mov dx,offset newlinechar
call PrintOne ;
pop dx cx bx ax ; restore stack
ret ;
showdate endp

WriteSubstring

; writes a short substring to stdout


; specifically, prints CL characters, starting at DS:(DX+CL*AL)

; 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

PUBLIC BASLOAD ;make known to BASIC at link time


BASLOAD PROC FAR

;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

;move file spec 1 to sysparm


MOV BX,SS:[BP+12] ;get addr of string descriptor
MOV CX,DS:[BX] ;get length of string into CX
MOV SI,DS:[BX+2] ;get addr of string into SI
MOV DI,0 ;offset into sysparm
REP MOVSB ;move string
MOV BYTE PTR ES:[DI],0 ;make it asciiz string

;move parameter 1 to sysparm


MOV BX,SS:[BP+10] ;get addr of string descriptor
MOV CX,DS:[BX] ;get length of string into CX
MOV SI,DS:[BX+2] ;get addr of string into SI
MOV DI,81 ;offset into sysparm
INC CL ;adjust for cr to be added at end
MOV BYTE PTR ES:[DI],CL ;1st byte is length of string
DEC CL ;re-adjust for move operation
INC DI
REP MOVSB ;move string
MOV BYTE PTR ES:[DI],13 ;add cr to end

;move file spec 2 to sysparm


MOV BX,SS:[BP+8] ;get addr of string descriptor
MOV CX,DS:[BX] ;get length of string into CX
MOV SI,DS:[BX+2] ;get addr of string into SI
MOV DI,163 ;offset into sysparm
REP MOVSB ;move string
MOV BYTE PTR ES:[DI],0 ;make it asciiz string

;move parameter 2 to sysparm


MOV BX,SS:[BP+6] ;get addr of string descriptor
MOV CX,DS:[BX] ;get length of string into CX
MOV SI,DS:[BX+2] ;get addr of string into SI
MOV DI,244 ;offset into sysparm
INC CL ;adjust for cr to be added at end
MOV BYTE PTR ES:[DI],CL ;1st byte is length of string
DEC CL ;re-adjust for move operation
INC DI
REP MOVSB ;move string
MOV BYTE PTR ES:[DI],13 ;add cr to end

;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'

; It can also be used to limit the maximum memory available to a compiled


; BASIC program. The option '/M:nnn' is used on the command line, where
; nnn is the number of K-bytes the program is limited to. If no, /M option
; is specified, no memory limitation takes place. For example, '/M:64' would
; limit the program to 64*1024 bytes. The range for nnn is 64 to 1024.

; 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

; If BASMAIN is unable to limit memory as requested, a message is displayed


; and the execution of the program is continued.

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

p020: loop p010 ; check next char


p025: jmp p080 ; no /m: or /M: found

p030: ; found /m: or /M:


add si,3 ; point to first number
mov ax,0
mov bx,0
mov cx,10
p040: mov bl,[si] ; get character
cmp bl,'0' ; out of range?
jb p050 ; yes
cmp bl,'9' ; out of range?
ja p050 ; yes
sub bl,'0' ; convert to binary
mul cx ; multiply ax by 10
add ax,bx ; add new digit
inc si ; point to next char
jmp p040 ; continue

p050: ; got value in ax


cmp ax,64 ; less than 64K?
jb p060 ; yes - print msg
cmp ax,1024 ; greater than 1024K?
ja p060 ; yes - print msg
mov cl,6
sal ax,cl ; convert from KB to paragraphs (*64)
mov bx,es ; get psp
add bx,ax ; new top of memory
mov si,2 ; point to top of memory in psp
mov ax,[si] ; get current top of memory
cmp ax,bx ; is new setting larger?
jae p055 ; no
mov dx,offset msg2 ; yes - print msg
jmp p065
p055: mov [si],bx ; save new top of memory
jmp p080

p060: mov dx,offset msg1 ; print the message


p065: add dx,100h ; fudge for the psp
mov ah,9
int 21h

p080: jmp $$main ; jump to BASIC's start point

msg1 db 'Memory specification must be from 64 to 1024',7,10,13,'$'


msg2 db 'Unable to limit memory',7,10,13,'$'

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

BootFileName db "CONFIG SYS" ;the boot loader for this OS


MBR DB 0200h DUP (?)
buff DB 0200h * 40h DUP (?)
ClustOffs dd ?
CR EQU 0DH
LF EQU 0AH

.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

yonder segment para public use16 at 2000h


org 0h
destination proc far
destination endp
yonder ends

code segment para public use16 '_CODE'


.386
assume cs:code, ds:code, es:code, ss:code
org 7c00h
main PROC
MBR:
Boot bootsector < ,'BEROSET ',512,1,1,2,224,2880,0f0h,9,18,2,\
0,0,0,0,29h,02a04063ch,'BEROSET 001',\
'FAT12 ',07df1h>
over:
mov ax,cs ;
cli
mov ss,ax ; point ss:sp to CS:7c00h
mov sp,7c00h ; which sets up a stack in first 64K
sti
mov ds,ax
mov es,ax

;
; 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

; mov WORD ptr [ClustOffs],ax


; mov WORD ptr [ClustOffs+2],dx
mov bx,offset Boot
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, \
WORD ptr [(BootSector PTR bx).HiddenSecs] , \
WORD ptr [((BootSector PTR bx).HiddenSecs)+2],\
[(BootSector PTR bx).Heads], \
[(BootSector PTR bx).SecPerTrack]

mov dl,[(BootSector PTR bx).DriveNumber]


mov bx,offset buff
retry1:
mov al,[(BootSector PTR MBR).SecPerClust]
mov ah,2h ; get ready to read
int 13h
jc retry1
; 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, \
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

BootFileName db "BEROSET SYS" ;the boot loader for this OS


; MBR db 0200h DUP (?)
buff db 0200h * 40h DUP (?)
ClustOffs dd ?
org 7dfeh
dw 0AA55h ; signature byte
code ends

END
Control-Break handler for Lattice C
programs
Control-Break handler for Lattice C programs

title Control-Break handler for Lattice C programs


name break
include dos.mac

; Control-Break Interrupt Handler for Lattice C programs


; running on IBM PCs (and ROM BIOS compatibles)
;
; This module allows C programs running on the IBM PC
; to retain control when the user enters a Control-Break
; or Control-C. This is accomplished by taking over the
; Int 23H (MS-DOS Control-Break) and Int 1BH (IBM PC
; ROM BIOS Keyboard Driver Control-Break) interrupt
; vectors. The interrupt handler sets an internal
; flag (which must be declared STATIC INT) to TRUE within
; the C program; the C program can poll or ignore this
; flag as it wishes.
;
; The module follows the Lattice C parameter passing
; conventions, and also relies on the Lattice file DOS.MAC
; for the definition of certain constants and macros.
;
; The Int 23H Control-Break handler is a function of MS-DOS
; and is present on all MS-DOS machines, however, the Int 1BH
; handler is a function of the IBM PC ROM BIOS and will not
; necessarily be present on other machines.
;
if lprog
args equ 6 ;offset of arguments, Large models
else
args equ 4 ;offset of arguments, Small models
endif

cr equ 0dh ;ASCII carriage return


lf equ 0ah ;ASCII line feed

pseg

public capture,release ;function names for C

;
; 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

push bp ;interrupt vectors


mov bp,sp
push ds

mov ax,word ptr [bp+args]


mov cs:flag,ax ;save address of integer
mov cs:flag+2,ds ;flag variable in C program

;pick up original vector contents


mov ax,3523h ;for interrupt 23H (MS-DOS
int 21h ;Control-Break handler)
mov cs:int23,bx
mov cs:int23+2,es

mov ax,351bh ;and interrupt 1BH


int 21h ;(IBM PC ROM BIOS keyboard driver
mov cs:int1b,bx ;Control-Break interrupt handler)
mov cs:int1b+2,es

push cs ;set address of new handler


pop ds
mov dx,offset ctrlbrk
mov ax,02523H ;for interrupt 23H
int 21h
mov ax,0251bH ;and interrupt 1BH
int 21h

pop ds ;restore registers and


pop bp ;return to C program
ret

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 proc near ;restore Control-Break interrupt


;vectors to their original state
push bp
mov bp,sp
push ds

mov dx,cs:int1b ;set interrupt 1BH


mov ds,cs:int1b+2 ;(MS-DOS Control-Break
mov ax,251bh ;interrupt handler)
int 21h

mov dx,cs:int23 ;set interrupt 23H


mov ds,cs:int23+2 ;(IBM PC ROM BIOS keyboard driver
mov ax,2523h ;Control-Break interrupt handler)
int 21h

pop ds ;restore registers and


pop bp ;return to C program
ret

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.
;

ctrlbrk proc far ;Control-Break interrupt handler

push bx ;save affected registers


push ds

mov bx,cs:flag ;set flag within C program


mov ds,cs:flag+2 ;to "True"
mov word ptr ds:[bx],-1

pop ds ;restore registers and exit


pop bx

iret

ctrlbrk endp

flag dw 0,0 ;long address of C program's


;Control-Break detected flag

int23 dw 0,0 ;original contents of MS-DOS


;Control-Break Interrupt 23H
;vector

int1b dw 0,0 ;original contents of ROM BIOS


;keyboard driver Control-Break
;Interrupt 1BH vector

endps

end
Break
Break

PGROUP Group PROG


DGROUP Group DATA

DATA Segment Public 'DATA'

public brkflag

brkflag DW 0

DATA ends

PROG Segment Para Public 'PROG'

public TrapBrea

assume cs:PGROUP,DS:DGROUP

TrapBrea proc near

push ds
push cs
pop ds
mov dx,offset PGROUP:Bret
mov ah,25h
mov al,23h
int 21h
pop ds
ret

TrapBrea endp

Bret proc far

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

TITLE BRK2 -- Break Handling Utilities Module

TRUE EQU 01H ;boolean true


FALSE EQU 00H ;boolean false
BREAKINT EQU 23H ;dos control-break intrpt
GETVECTOR EQU 35H ;dos get vector function
SETVECTOR EQU 25H ;dos set vector function
DOS_FUNCTION EQU 21H ;dos function call

BREAK SEGMENT PUBLIC 'CODE'


BREAKFLAG DB 0H ;break key hit flag
SAVEBRK DD 0H ;saved break vec. contents
ASSUME CS:BREAK
ASSUME DS:NOTHING

;CHECK_BREAK checks if ctrl-break has been pressed. It returns


;true if ctrl-break has been pressed and false if it hasn't.

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

;INSTALL_BREAK_HANDLER sets up a ctrl-break interrupt handler. It


;also saves the address of the former break handler so that it can
;be restored later.

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

;BRK_HANDLER is invoked by the bios when ctrl_break is pressed

BRK_HANDLER PROC FAR


MOV BREAKFLAG, TRUE ;breakflag = yes, break hit
IRET
BRK_HANDLER ENDP
;REMOVE_BREAK_HANDLER restores the previous ctrl-break handler

REM_BRK_HANDLR PROC FAR


PUSH DS
MOV AL, BREAKINT ;AL = break interrupt
MOV AH, SETVECTOR ;AH = dos function code
MOV DX, WORD PTR SAVEBRK ;DX = saved 1st word
MOV BX, WORD PTR SAVEBRK+2 ;BX = saved 2nd word
MOV DS, BX ;DS = saved 2nd word
INT DOS_FUNCTION ;call dos
POP DS
RET
REM_BRK_HANDLR ENDP
BREAK ENDS
END
Burnout
Burnout

COMMENT *

Demo (and semi-useful) program to read/set burnout device parameters.

Usage: burnout [ticks] [C+-] [V+-] [H+-]

Parameters can be separated by almost anything.


With no parameters, program simply returns current status.

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

stdout equ 1 ; DOS output files


stderr equ 2

---- General equates


DOS equ 21H ; DOS interrupt
TERMINATE equ 20H ; Exit to DOS

PRINT equ 09H ; DOS "print" string to stdout


FOPEN equ 3D02H ; DOS file open, read/write
FREAD equ 3FH ; DOS file read
FWRITE equ 40H ; DOS file write

CR equ 13 ; ASCII carriage return


LF equ 10 ; ASCII line fine

code segment
assume cs:code,ds:code

org 80H ; Parm storage area in PSP


ParmLength label byte ; Length of parms
org 81H
Parameters label byte ; Start of parms

org 100H ; Org for .COM


main proc far
jmp start ; Hate to execute data

DevName db 'BRNDEV',0 ; Burnout device name


handle dw ? ; Storage for handle
Flush db '@' ; Char to flush device I/O
Execute db '#' ; Char to execute device commands
Notlnstalled db 'Burnout device is not installed',13,10
NotInstalledL equ $ - NotInstalled

Status db 'Current status: ' ; Status message


StatInsert db 40 dup (?) ; brndev will store status here

; ---- Open the device


start:
mov dx,offset DevName ; DS:DX => device name
mov ax,FOPEN
int DOS
jnc A1 ; Continue if no error
mov bx,stderr ; Message to stderr
mov cx,NotInstalledL
mov dx,offset NotInstalled
mov ah,FWRITE
int DOS
jmp exit

; Flush any pending I/O to/from the device


A1:
mov handle,ax ; Save device handle
mov dx,offset Flush ; Point to the "@"
mov cx,1 ; Writing one byte
mov bx,handle ; Device handle
mov ah,FWRITE ; Write "@" to device
int DOS

; ---- Send and execute parameters if present


mov cl,ParmLength ; Parm length to CL
or cl,cl ; Any parms present?
jz A2 ; Skip if not
xor ch,ch ; CX = parm length
mov dx,offset Parameters ; DS:DX => parms
mov bx,handle ; BX = device handle
mov ah,FWRITE ; Write parms to device
int DOS

mov dx,offset Execute ; Execute the parms


mov cx,1 ; Writing one byte
mov bx,handle ; Device handle
mov ah,FWRITE ; Write "#" to device
int DOS

; Get and display device status


A2:
mov dx,offset StatInsert ; DS:DX => where to put status
mov cx,0FFH ; Ask for lots of data; DOS will ...
; ... fetch only until CR found.
mov bx,handle ; Device handle
mov ah,FREAD ; Read device info
int DOS

mov cx,ax ; CX = actual # bytes read


mov di,offset StatInsert ; Where the stat data is stored
add di,cx ; Add length of input read
mov al,CR ; Store a CR/LF/'$' at end
cld
stosb
mov al,LF
stosb
mov al,'$'
stosb
mov dx,offset Status ; Write status to stdout
mov ah,PRINT
int DOS

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

CALC PROC FAR


JMP START

;;
; DATA AREA ;
; ;
;-------------------------------------------------------

DB 'INTERRUPT NUMBER ='


INT_NUMBER DB 61h

SCREEN_HANDLE DW 0001h

MESSAGE DB 'PEMATH is not resident',13,10


MESSAGE_LEN EQU $-MESSAGE

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

lea edx, nomscdex


xor ebx,ebx
mov eax, 1500h ; MSCDEX installed?
int 2fh
test ebx,ebx
jz exit
mov edi,10h
nextloop:
mov ecx,edi
mov ax, 150bh ; is drive ECX supported by MSCDEX (is it a cdrom?)
int 2fh
test ax,ax ; ax!=0 if drive is CDrom
jz continiue

mov ax, 440dh


mov dx, offset info
mov bl,5
mov ch,8
mov cl,66h
int 21h ; Fetch volume serial number (same as when you do dir)

mov eax, dword ptr [info+2]


cmp eax, 0ffb7f724h; ;<<<<calibrate this to fit your own "right" CD
jnz continiue

lea edx, right


jmp exit

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

ctable segment para public 'DATA


db 9 dup( ')
db 9,10,' ',12,13
db 13 dup( ')
db 27
db 4 dup(' ')
db ' !"#$%&',39,'()*+,-./0123456789:;<=>?@'
db 'ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]a_''
db 'abcdefghij klmnopqrstuvwxyz {|}~'
db 52 dup(' ')
db '|++++++|+++++++++-++++++++-+++++++++++++'
db 36 dup(' ')
ctable ends
end
MSDOS 2.00 Function Library for
Lattice C
MSDOS 2.00 Function Library for Lattice C

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

_charop proc near


push bp
mov bp,sp
mov al,[bp+4]
mov dl,[bp+6]
mov ah,55
int 33
mov al,dl
mov ah,0
pop bp
ret
_charop endp

prog ends

end
Checks the validity of an ISBN by
verifying the checksum
Checks the validity of an ISBN by verifying the checksum

; From Ed Beroset - ISBN validation


; > Anyone know how to validate the ISBN numbers from books?

; isbnchek.asm
comment A

This file contains a C-callable routine which calculates the


check digit (tenth digit) of an ISBN and returns the ASCII
representation of that digit.

This code was written for Borland's TASM


and may be assembled with the following command:

tasm /m2 isbnchek.asm

.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.

; In our assembly language implementation, we simulate the


; multiplications by looping through and summing as in the
; following psuedocode:
;
; sum = 0
; for i=1 to 9
; {
; for j=i to 9
; {
; sum = sum + isbn[j]
; }
; }
;
; Entry:
;
isbn = a nine digit ASCII string containing the ISBN
(with or without the check digit which is not used here)
;
; Register usage within the routine:
;
; AL = current ISBN digit
; AH = sum of digits so far
; BX = start pointer into ISBN for each outer loop
; CX = digit counter (inner loop)
; DX = start value for digit counter
; SI = points to current ISBN digit
;
; Exit:
;
; AX = ASCII representation of calculated check digit
;
; Trashed:
; none
;
;****************************************************************
isbncheck proc C isbn:ptr byte
push bx
push cx
push dx
push si
mov bx,[isbn] ;
mov dx,9 ; number of digits in raw ISBN
xor ax,ax ; clear out our total
cld ; count up
@@bigloop: ;
mov si,bx ; point to a digit in the ISBN
mov cx,dx ; get digit count in CX
@@AddEmUp: ;
lodsb ; fetch digit into AL
and al,0fh ; convert from ASCII
add ah,al ; add it to our total in AH
loop @@AddEmUp ; do all digits
inc bx ; and advance the digit pointer
dec dx ; now decrement digit count
jnz @@bigloop ; keep going if digits left
mov al,ah ; move sum into al
xor ah,ah ; clear out high half
mov cl,11 ; we'll be doing a mod 11 operation
div cl ; ah = sum mod 11
mov al,ah ; move calculated check digit to AL
xor ah,ah ; clear out high half
or al,30h ; convert to ASCII digit
cmp al,3Ah ;
jnz NotTen ;
mov al,'X' ;
NotTen: ;
pop si
pop dx
pop cx
pop bx
ret ; return
isbncheck endp
END
Chips
Chips

; calling convention:

int chips( void);

returns:

tucked away neatly in your AX....

; you get back 8x if an 8088/8086


18x if an 80186/80188
28x if an 80286
38x if an 80386
20x for a NEC V20/V30
AND
xx0 if NO NDP is found
xx1 if an 8087
xx2 if an 80287
xx3 for an 80387

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.

>>>> Please see TEST.C, enclosed in this .ARC. <<<<


;
; REFERENCES:

; _chips is made up of two PROC's, cpu_type and ndp_type.


;
; cpu_type is based on uncopyrighted, published logic by
; Clif (that's the way he spells it) Purkiser of Intel -
; Santa Clara.
;
ndp_type is adopted from Ted Forgeron's article in PC
Tech Journal, Aug '87 p43.
;
In the event of subsequent republication of this function,
please carry forward reference to these two gentlemen as
original authors.
.MODEL SMALL
.CODE
PUBLIC _chips

chips PROC

control dw 0 ; control word needed for the NDP test

push BP ; save where Ur at


mov BP,SP ; going in

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

add AX,DX ; add the two results together and hold


; 'em in AX for Ur return to the caller

pop CX ; put things back the way that you


pop SI ; found 'em when you started this
pop DI ; little drill off....
pop BP
; AND
ret ; go back to where you came from...
; ( ===> the calling program )
; with Ur results sittin' in AX
chips endp

cpu_type PROC

pushf ; pump Ur flags register onto the stack


xor DX,DX ; blow out Ur DX and AX to start off
xor AX,AX ; with a clean slate
push AX ; put AX on the stack
popf ; bring it back in Ur flags
pushf ; try to set bits 12 thru 15 to a zero
pop AX ; get back Ur flags word in AX
and AX, 0f000h ; if bits 12 thru 15 are set then you got
cmp AX, 0f000h ; an Intel 8018x or a 808x or maybe even
jz dig ; a NEC V20/V30 ??? - gotta look more...

; OTHERWISE...
; Here's the BIG one... 'tells the difference between an 80286 and
; an 80386 !!

mov AX, 07000h ; try to set FLAG bits 12 thru 14


; - NT, IOPL
push AX ; put it onto the stack
popf ; and try to pump 07000H into Ur flags
pushf ; push Ur flags, again
pop AX ; and bring back AX for a compare
and AX,07000h ; if Ur bits 12 thru 14 are set
jnz got386 ; then Ur workin' with an 80386
mov DX, 0280 ; save 280 in DX cuz it's an 80286
jmp SHORT CPUbye ; and bail out

got386: mov DX, 0380 ; save 380 in DX cuz it's an Intel 80386
jmp SHORT CPUbye ; and bail out

; here's we try to figger out whether it's an 80188/80186, an 8088/8086


; or an NEC V20/V30 - 'couple of slick tricks from Clif Purkiser....

dig: mov AX, 0ffffh ; load up AX


mov CL, 33 ; HERE's the FIRST TRICK... this will
; shift everything 33 times if it's
; 8088/8086, or once for a 80188/80186!
shl AX, CL ; on a shift of 33, all bits get zeroed
jz digmor ; out so if anything is left ON it's
; gotta be an 80188/80186
mov DX,0180 ; save 180 in DX cuz it's an 80188/80186
jmp SHORT CPUbye ; and bail out

digmor: xor AL,AL ; clean out AL to set ZF


mov AL,40h ; ANOTHER TRICK... mul on an NEC duz NOT
mul AL ; effect the zero flag BUT on an Intel
jz gotNEC ; 8088/8086, the zero flag gets thrown
mov DX,0080 ; 80 into DX cuz it's an Intel 8088/8086
jmp SHORT CPUbye ; and bail out

gotNEC: mov DX,0200 ; it's an NEC V20/V30 so save 200 in DX


CPUbye: popf ; putchur flags back to where they were
ret ; and go back to where you came from
; (i.e., ===> _chips) with the CPU type
; tucked away in DX for future reference
cpu_type endp

; Check for an NDP.


;
; >>>>NOTE: If you are using an MASM version < 5.0, don't forget to
; use the /R option or you will bomb cuz of the coprocessor instruc-
; tions. /R is not needed for version 5.0.<<<<<<<<<<<<<<<<<<<<<<<<<

ndp_type PROC

do_we: fninit ; try to initialize the NDP


mov byte ptr control+1,0 ; clear memory byte
fnstcw control ; put control word in memory
mov AH,byte ptr control+1 ; iff AH is 03h, you got
cmp AH,03h ; an NDP on board !!
je chk_87 ; found somethin', keep goin'
xor AX,AX ; clean out AX to show a zero
jmp SHORT NDPbye ; return (i.e., no NDP)

; 'got an 8087 ??

chk_87: and control,NOT 0080h ; turn ON interrupts (IEM = 0)


fldcw control ; load control word
fdisi ; turn OFF interrupts (IEM = 1)
fstcw control ; store control word
test control,0080h ; iff IEM=1, 8087
jz chk287 ; 'guess not! March on...
mov AX,0001 ; set up for a 1 return to
jmp SHORT NDPbye ; show an 8087 is on board

; if not... would you believe an 80287 maybe ??

chk287: finit ; set default infinity mode


fld1 ; make infinity
fldz ; by dividing
fdiv ; 1 by zero !!
fld st ; now make a
fchs ; negative infinity
fcompp ; compare Ur two infinities
fstsw control ; iff, for 8087 or 80287
fwait ; sit tight 'til status word is put away
mov AX,control ; getchur control word
sahf ; putchur AH into flags
jnz got387 ; NO GOOD... march on !!
mov AX,0002 ; gotta be a 80287 cuz we already tested
jmp SHORT NDPbye ; for an 8087

; We KNOW that there is an NDP on board otherwise we would have bailed


; out after 'do_we'. It isn't an 8087 or an 80287 or we wouldn't have
; gotten this far. It's gotta be an 80387 !!

got387: mov AX,0003 ; call it an 80387 and return 3

NDPbye: ret ; and go back where you came from


; (i.e., ===> _chips) carrying the NDP
; type in Ur AX register
ndp_type endp

text ends
end
Circle
Circle

cseg segment
assume cs:cseg, ds:cseg, ss:cseg
org 100h
.386
start:

mov ax, 13h


int 10h

mov dx, 3c8h


xor al, al
out dx, al
inc dx
mov cx, 256
xor al, al
lopp: out dx, al
out dx, al
out dx, al
inc al
dec cx
jnz lopp

mov ax, 0a000h


mov es, ax
fild y_rad
fild x_rad

loopdr:
fild angle
fsincos

fmul st, st(2)


fistp x_co

fmul st, st(2)


fistp y_co

add x_co, 160


add y_co, 100

xor di, di
mov ax, y_co
shl ax, 6
add di, ax
shl ax, 2
add di, ax
add di, x_co

mov byte ptr es:[di], cl


inc cl

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

This is a simple litte assembler program that cleans out


;* the documents folder in the start menu. *
;* This is version 1.2 *
;* There's no command line switches and stuff. *
;* It gives NO messages if everything goes allright. *
It check for a environment variable called CLEANDIR and CD:s *
;* down to that dir if the variable is found. If not it uses *
;* the default WINDOWS\RECENT directory and deletes(UNLINKS) *

;* EVERYTHING it finds there, and CD:s back to where it started *


;* from. *

;some euqates for readability


kbd equ 16h ;keyboard irq
msdos equ 21h ;MSDOS irq

reset equ 0dh ;disk reset


dfopen equ 0fh ;open disk file
dfclose equ 10h ;close disk file
searchf equ 11h ;search first
searchn equ 12h ;search next
seqread equ 14h ;sequential disk read
seqwrite equ 15h ; " " write
getdisk equ 19h ;get current disk(default)
setdta equ 1ah ;set disk transfer area address
setdir equ 3bh ;set current directory
createf equ 3ch ;create file with handle
openf equ 3dh ;open file with handle
closef equ 3eh ;close file with handle
readf equ 3fh ;read from file with handle
writef equ 40h ;write to file with handle
unlink equ 41h ;UNLINK(delete file)
getdir equ 47h ;get current directory
allocmem equ 48h ;allocate memory
freemem equ 49h ;free memory
changebs equ 4ah ;change block size
findfirst equ 4eh ;find first file
findnext equ 4fh ;find next file
exit equ 4c00h ;msdos exit

envir equ 2ch ;offset ENVIRONMENT block

[BITS 16] ;NASM STUFF


[ORG 100h]

mov ax,cs ;get code segment


mov ds,ax ;use it now
mov [comseg],ds
mov [extseg],es

setup and preparing


main:
mov ah,setdta ;set our DTA-area
mov dx,mydta ;buffer for it
int msdos ;call dos

mov ah,getdisk ;get default drive


int msdos ;call dos
add al,41h ;drive in al, make it ASCII
mov byte [curdir],al ;fill buffer with name (A:..etc)
mov byte [path],al ;and default path

mov word [curdir+1],":\" ;copy separator to path


mov si,curdir ;pointer path buffer
add si,3 ;offset doscall part
mov ah,getdir ;get current dir
mov dl,0 ;0 = default
int msdos ;call dos
jnc diskok ;ok
mov bx,errt0 ;could not find current dir ?? If You
jmp errout ;get an error here You probably have
diskok: ;forgot to turn on Your computer.
call getenv ;check out if any ENV var
cmp dx,-1 ;was it there
je findfile ;yeahh
mov dx,path ;noo way, use default path
mov ah,setdir ;cd down
int msdos ;call dos
jnc findfile ;all ok
mov bx,errt1 ;error
jmp errout ;skip
;*************************** the delete file loop
***********************
findfile:
mov ah,findfirst ;see if the files out there
mov cx,0fh ;all files
mov dx,files ;our NULL terminated filname(*.*)
int msdos ;do the stuff
jnc delit ;all ok, must delete first file
jmp goback ;error, CD back and skip
found: ;found something
mov dx,files ;files (*.*)
mov ah,findnext ;the function
int msdos ;call dos
jc goback ;no more files, quit
delit:
mov ah,unlink ;UNLINK (delete) file
mov dx,mydta ;pointer Disk Transfer Area
add dx,30 ;offset Filename
int msdos ;delete it
jnc found ;deleted ok

mov bx,errt2 ;could not delete it ????


call write ;let us know
mov bx,mydta ;show wich file
add bx,30 ;offset filename in DTA
call write ;write out filename
mov bx,linefeed ;linefeed
jmp errout ;and skip
goback:
mov ah,setdir ;CD back to origin
mov dx,curdir ;path to dir
int msdos ;do it
jnc quit ;all ok, proceed
mov bx,errt1 ;error, get text

errout: call write ;show errormessage


quit:
xor eax,eax ;clean out that
mov ax,exit ;MS-DOS successful exit
int msdos ;back to the operating system

get ENVIRONMENT var if any


getenv:
push es ;now check if there's any
push ds ;environment variable

mov es,[es:+2Ch] ;ES:DI points at environment


xor di,di ;which is paragraph-aligned
floop:
cmp byte [es:di],0 ;if we got 2 zeroes in a row
jne goon ;we are at the end of the ENV
cmp byte [es:di+1],0 ;variables
je eout
goon:
equal: cmp byte [es:di],'C' ;is it our variable ?
jne flop
inc byte di
cmp byte [es:di],'L'
jne flop
inc byte di
cmp byte [es:di],'E'
jne flop
inc byte di
cmp byte [es:di],'A'
jne flop
inc byte di
cmp byte [es:di],'N'
jne flop
inc byte di
cmp byte [es:di],'D'
jne flop
inc byte di
cmp byte [es:di],'I'
jne flop
inc byte di
cmp byte [es:di],'R'
jne flop
sign: inc byte di ;dump the R
inc byte di ;dump the =

mov ax,es ;make DS:DX point to string we found


mov ds,ax
mov si,di
mov bx,si
mov dx,bx
mov ah,setdir ;func Set Current Directory(CD)
int msdos ;do it
jnc envok ;all ok, proceed
mov dx,0 ;clear flag (use default dir)
jmp eout ;return

flop: inc byte di ;next byte


cmp byte [es:di],0 ;a 0 ?
jne flop ;noo
inc byte di ;yeahh, dump it
jmp floop ;check if two
envok: mov dx,-1
eout: pop ds
pop es
ret

;****************************************************************
;* 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

;************************ DATA and BSS stuff

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

mydta times 128 db 0 ;use 128 bytes as DTA NASM stuff


curdir times 68 db 0 ;use 64 + 4 bytes for current dir

END

You might also like