You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
494 lines
11 KiB
494 lines
11 KiB
; COMBINE.ASM Interrupt List combiner
|
|
; by Ralf Brown
|
|
; last edit: 22mar98
|
|
|
|
NAME COMBINE
|
|
TITLE Combine Interrupt List sections
|
|
|
|
; declare all the segments in the order in which they are to appear in the
|
|
; executable
|
|
CODE SEGMENT 'CODE'
|
|
CODE ENDS
|
|
STACKSEG SEGMENT PUBLIC WORD 'STACK'
|
|
STACKSEG ENDS
|
|
BUFFERSEG SEGMENT PUBLIC WORD 'DATA'
|
|
BUFFERSEG ENDS
|
|
;
|
|
DGROUP GROUP CODE,STACKSEG,BUFFERSEG
|
|
|
|
;;------------------------------------------------------------------------
|
|
|
|
FFBLK struc
|
|
ff_reserved db 15h dup (?)
|
|
ff_attrib db ?
|
|
ff_ftime dw ?
|
|
ff_fdate dw ?
|
|
ff_fsize dd ?
|
|
ff_fname db 13 dup (?)
|
|
FFBLK ends
|
|
|
|
;;------------------------------------------------------------------------
|
|
|
|
CODE SEGMENT 'CODE'
|
|
ORG 100h ; this is a .COM file
|
|
ASSUME CS:DGROUP,DS:DGROUP,ES:DGROUP,SS:DGROUP
|
|
|
|
combine:
|
|
jmp near ptr main
|
|
|
|
banner db 13,"COMBINE v2.10",9,"Ralf Brown 1996,1998",13,10,"$",26
|
|
usage_msg db "Usage:",9,"COMBINE [options] dest-dir",13,10
|
|
db 9,"where {dest-dir} is the directory in which to place",13,10
|
|
db 9," the combined list ('.' for the current directory)",13,10
|
|
db 10
|
|
db 9,"options:",13,10
|
|
db 9,9,"-d",9,"delete sections after copying",13,10
|
|
db 9,9,"-p",9,"combine PORTS.LST instead of INTERRUP.LST",13,10
|
|
db 10
|
|
db "All sections of the interrupt/ports list must be in the current directory."
|
|
db "$"
|
|
|
|
bad_dos_msg db "Need DOS 2.0+$"
|
|
bad_drive_msg db "Invalid destination drive$"
|
|
no_mem_msg db "Insufficient memory$"
|
|
no_files_msg db "No section files found!$"
|
|
readerr_msg db "Read Error$"
|
|
writeerr_msg db "Write Error$"
|
|
diskfull_msg db "Disk full? while writing$"
|
|
no_disk_msg db "Out of space on destination drive",13,10,"$"
|
|
retry_msg db "Try again with -d to delete while copying$"
|
|
|
|
cant_create_msg db "Check directory name -- unable to create "
|
|
combined_file db "INTERRUP.LST",0,"$"
|
|
combined_file2 db "PORTS.LST",0
|
|
combined_file2_len equ $-combined_file2
|
|
section_file1 db "INTERRUP.A",0,"$"
|
|
section_letter equ section_file1+9
|
|
section_file2 db " PORTS"
|
|
section_file2_len equ $-section_file2
|
|
section_file2_ofs equ 3
|
|
missing_msg db "unavailable (skipped)"
|
|
crlf db 13,10,"$"
|
|
section_heading1 db "Interrupt List, part "
|
|
section_hdr_len1 equ $-section_heading1
|
|
section_heading2 db "Ports List, part "
|
|
section_hdr_len2 equ $-section_heading2
|
|
complete_msg db "Done.$"
|
|
|
|
;
|
|
; flags affecting operation
|
|
;
|
|
del_after_copy db 0
|
|
section_file dw offset section_file1
|
|
section_heading dw offset section_heading1
|
|
section_hdr_len dw section_hdr_len1
|
|
|
|
;
|
|
; data needed while processing
|
|
;
|
|
filehandle equ di ; output file's handle
|
|
numsections db 26
|
|
dest_drive db 0
|
|
nondefault_dest db 0
|
|
ftime dw 0
|
|
fdate dw 0
|
|
filesize_lo dw 0
|
|
filesize_hi equ bp
|
|
|
|
; (since we don't use disk_buffer until after FindFirst is no longer needed,
|
|
; save memory by overlaying the two)
|
|
FindFirst equ DGROUP:disk_buffer
|
|
|
|
;;------------------------------------------------------------------------
|
|
|
|
write_string:
|
|
mov ah,9
|
|
int 21h
|
|
ret
|
|
|
|
;;------------------------------------------------------------------------
|
|
|
|
skip_whitespace:
|
|
lodsb
|
|
cmp al,' '
|
|
je skip_whitespace
|
|
cmp al,9
|
|
je skip_whitespace
|
|
dec si ; unget the last character
|
|
; set ZF to indicate whether we got to end of cmdline
|
|
cmp al,0Dh
|
|
ret
|
|
|
|
;;------------------------------------------------------------------------
|
|
|
|
get_destination_file:
|
|
mov bx,si ; remember start of destination name
|
|
get_dest_file_loop:
|
|
lodsb
|
|
cmp al,' '
|
|
je got_dest_end
|
|
cmp al,9
|
|
je got_dest_end
|
|
cmp al,0Dh
|
|
jne get_dest_file_loop
|
|
got_dest_end:
|
|
dec si ; unget last character
|
|
mov di,si
|
|
mov al,[si-1] ; check end of path -- is it terminated
|
|
cmp al,'\' ; by a slash or backslash?
|
|
je dest_has_slash
|
|
cmp al,'/'
|
|
je dest_has_slash
|
|
cmp al,':'
|
|
je dest_has_slash
|
|
mov al,'\'
|
|
stosb
|
|
dest_has_slash:
|
|
mov si,offset combined_file
|
|
dest_copy_loop:
|
|
lodsb
|
|
stosb
|
|
cmp al,0
|
|
jne dest_copy_loop
|
|
; OK, now open the destination file
|
|
; (BX is still pointing at start of pathname)
|
|
cmp byte ptr [bx+1],':'
|
|
jne got_dest_drive
|
|
mov al,[bx]
|
|
and al,0DFh ; force to uppercase
|
|
sub al,'A'
|
|
jb got_dest_drive
|
|
cmp al,dest_drive
|
|
je got_dest_drive
|
|
mov dest_drive,al
|
|
mov nondefault_dest,al
|
|
got_dest_drive:
|
|
mov ah,3Ch ; create the output file
|
|
xor cx,cx ; no special file attributes
|
|
mov dx,bx
|
|
int 21h
|
|
mov dx,offset cant_create_msg
|
|
jc exit_with_err_2
|
|
mov filehandle,ax
|
|
ret
|
|
|
|
;;------------------------------------------------------------------------
|
|
|
|
check_total_size:
|
|
mov byte ptr section_letter,'A'-1
|
|
mov ah,1Ah ; set DTA
|
|
mov dx,offset FindFirst
|
|
int 21h
|
|
xor si,si ; keep track of # of sections found
|
|
check_size_loop:
|
|
inc byte ptr section_letter
|
|
cmp byte ptr section_letter,'Z'
|
|
ja short get_free_space
|
|
mov ah,4Eh ; find first
|
|
mov cx,001Fh ; ...regardless of attribute
|
|
mov dx,section_file
|
|
int 21h
|
|
jc check_size_loop
|
|
inc si ; another section found
|
|
mov ax,FindFirst.ff_ftime
|
|
mov ftime,ax
|
|
mov ax,FindFirst.ff_fdate
|
|
mov fdate,ax
|
|
mov ax,word ptr FindFirst.ff_fsize
|
|
mov dx,word ptr FindFirst.ff_fsize+2
|
|
cmp del_after_copy,0
|
|
je count_full_size
|
|
cmp nondefault_dest,0
|
|
jnz count_full_size
|
|
cmp dx,filesize_hi
|
|
jb check_size_loop
|
|
ja check_size_bigger
|
|
cmp ax,filesize_lo
|
|
jbe check_size_loop
|
|
check_size_bigger:
|
|
mov filesize_lo,ax
|
|
mov filesize_hi,dx
|
|
jmp check_size_loop
|
|
|
|
count_full_size:
|
|
add filesize_lo,ax
|
|
adc filesize_hi,dx
|
|
jmp check_size_loop
|
|
|
|
get_free_space:
|
|
test si,si ; check number of sections found
|
|
mov dx,offset no_files_msg
|
|
jz short exit_with_err_2
|
|
mov dl,dest_drive
|
|
inc dx
|
|
mov ah,36h ; get free disk space
|
|
int 21h
|
|
cmp ax,0FFFFh
|
|
jne got_free_space
|
|
mov dx,offset bad_drive_msg
|
|
exit_with_err_2:
|
|
jmp near ptr exit_with_errmsg
|
|
got_free_space:
|
|
mul cx ; DX:AX <- AX*CX
|
|
mov cx,dx ; store high half of intermediate
|
|
mul bx ; DX:AX <- low(AX*CX)*BX
|
|
xchg ax,bx ; store low half of second interm.
|
|
xchg cx,dx ; store high half of second interm.
|
|
mul dx ; DX:AX <- high(AX*CX)*BX
|
|
xchg ax,bx ; DX:BX:0000h + CX:AX = result
|
|
add bx,cx
|
|
adc dx,0 ; DX:BX:AX = AX*BX*CX = free space
|
|
jnz plenty_free_space ; >4G free?
|
|
sub ax,filesize_lo
|
|
sbb bx,filesize_hi
|
|
jnb plenty_free_space
|
|
not_enough_space:
|
|
mov dx,offset no_disk_msg
|
|
call write_string
|
|
cmp nondefault_dest,0
|
|
jnz size_check_failed
|
|
cmp del_after_copy,0
|
|
jne size_check_failed
|
|
mov dx,offset retry_msg
|
|
call write_string
|
|
size_check_failed:
|
|
mov al,2
|
|
jmp exit
|
|
plenty_free_space:
|
|
ret
|
|
|
|
;;------------------------------------------------------------------------
|
|
|
|
check_section_header:
|
|
push si
|
|
push di
|
|
mov si,offset DGROUP:disk_buffer
|
|
mov di,section_heading
|
|
mov cx,section_hdr_len
|
|
or cx,cx
|
|
rep cmpsb
|
|
jnz not_section_heading
|
|
scan_curr_section:
|
|
lodsb
|
|
cmp al,' ' ; scan for the " of "
|
|
jne scan_curr_section
|
|
add si,3 ; skip "of "
|
|
xor cl,cl
|
|
num_sections_loop:
|
|
lodsb
|
|
sub al,'0'
|
|
jb num_sections_done
|
|
cmp al,9
|
|
ja num_sections_done
|
|
mov ch,al
|
|
mov al,10
|
|
mul cl
|
|
mov cl,al
|
|
add cl,ch
|
|
jmp num_sections_loop
|
|
num_sections_done:
|
|
mov numsections,cl
|
|
got_num_sections:
|
|
not_section_heading:
|
|
pop di
|
|
pop si
|
|
ret
|
|
|
|
;;------------------------------------------------------------------------
|
|
|
|
; in: SI = file handle for current section
|
|
copy_section:
|
|
mov ax,4201h
|
|
xor cx,cx
|
|
xor dx,dx
|
|
mov bx,filehandle
|
|
int 21h
|
|
mov filesize_lo,ax
|
|
mov filesize_hi,dx
|
|
copy_section_loop:
|
|
mov ah,3Fh
|
|
mov bx,si
|
|
mov cx,disk_buffer_end - disk_buffer
|
|
mov dx,offset DGROUP:disk_buffer
|
|
int 21h
|
|
jc copy_read_error
|
|
mov cx,ax ; write same number of bytes read
|
|
mov ah,40h
|
|
;; mov dx,offset DGROUP:disk_buffer
|
|
mov bx,filehandle
|
|
int 21h
|
|
mov dx,offset writeerr_msg
|
|
jc copy_error
|
|
mov dx,offset diskfull_msg
|
|
cmp ax,cx
|
|
jb copy_error
|
|
; check for section header at start of buffer, and extract number
|
|
; of sections from it
|
|
push cx
|
|
call check_section_header
|
|
pop ax
|
|
cmp ax,disk_buffer_end - disk_buffer ; continue until only partial
|
|
je copy_section_loop ; buffer read (EOF hit)
|
|
ret
|
|
|
|
copy_read_error:
|
|
mov dx,offset readerr_msg
|
|
copy_error:
|
|
; truncate output to size before section was started
|
|
push dx ; store error message
|
|
mov ax,4200h
|
|
mov cx,filesize_hi
|
|
mov dx,filesize_lo
|
|
mov bx,filehandle
|
|
int 21h
|
|
mov ah,40h
|
|
xor cx,cx ; write zero bytes to truncate
|
|
int 21h
|
|
pop dx ; get back error message
|
|
;; fall through to exit_with_errmsg ;;
|
|
|
|
;;------------------------------------------------------------------------
|
|
|
|
exit_with_errmsg:
|
|
call write_string
|
|
; exit with errorlevel 1
|
|
mov al,01h
|
|
jmp near ptr exit
|
|
|
|
main:
|
|
ASSUME CS:DGROUP, DS:DGROUP, ES:DGROUP, SS:DGROUP
|
|
mov dx,offset banner
|
|
call write_string
|
|
; relocate the stack
|
|
mov sp,offset DGROUP:stackbot
|
|
; ensure that we have enough memory
|
|
mov ax,cs
|
|
add ax,1000h ; require 64K memory
|
|
cmp ax,ds:[0002h] ; is end of mem at least 64K above CS?
|
|
mov dx,offset no_mem_msg
|
|
ja exit_with_errmsg
|
|
mov si,81h ; point at start of cmdline
|
|
mov bl,[si-1] ; get length of cmdline
|
|
mov bh,0
|
|
mov byte ptr [bx+si],0Dh ; ensure cmdline properly terminated
|
|
cld
|
|
call skip_whitespace
|
|
mov dx,offset usage_msg
|
|
jz exit_with_errmsg
|
|
get_cmdline_switches:
|
|
call skip_whitespace
|
|
jz not_a_switch
|
|
cmp al,'-' ; is it a switch?
|
|
jne not_a_switch
|
|
lodsb ; get the switch character
|
|
lodsb ; get the switch itself
|
|
and al,0DFh ; force to uppercase
|
|
cmp al,'P'
|
|
je want_ports
|
|
cmp al,'D'
|
|
;; mov dx,offset usage_msg
|
|
jne exit_with_errmsg
|
|
mov del_after_copy,1
|
|
jmp get_cmdline_switches
|
|
want_ports:
|
|
jmp config_for_ports
|
|
not_a_switch:
|
|
mov ah,19h ; get default drive
|
|
int 21h
|
|
mov dest_drive,al
|
|
mov ah,30h
|
|
int 21h
|
|
cmp al,2
|
|
mov dx,offset bad_dos_msg
|
|
jb exit_with_errmsg
|
|
call get_destination_file
|
|
xor filesize_hi,filesize_hi
|
|
call check_total_size
|
|
;
|
|
; OK, all the preliminaries are done now, so go concatenate the
|
|
; sections of the interrupt list
|
|
;
|
|
mov al,'A'-1
|
|
concat_loop:
|
|
inc ax
|
|
mov section_letter,al
|
|
sub al,'A'-1
|
|
cmp al,numsections
|
|
ja concat_done
|
|
mov dx,section_file
|
|
call write_string
|
|
mov ax,3D00h
|
|
int 21h
|
|
mov dx,offset missing_msg
|
|
jc concat_loop_end
|
|
mov si,ax
|
|
call copy_section
|
|
mov bx,si ; BX <- section file's handle
|
|
mov ah,3Eh ; DOS function: close file handle
|
|
int 21h
|
|
cmp del_after_copy,0
|
|
je concat_no_del
|
|
mov ah,41h ; DOS function: delete file
|
|
mov dx,section_file
|
|
int 21h
|
|
concat_no_del:
|
|
mov dx,offset crlf
|
|
concat_loop_end:
|
|
call write_string
|
|
mov al,section_letter
|
|
jmp concat_loop
|
|
|
|
concat_done:
|
|
mov dx,offset complete_msg
|
|
call write_string
|
|
mov al,00h ; successful completion
|
|
exit:
|
|
push ax
|
|
mov dx,offset crlf
|
|
call write_string
|
|
mov ax,5701h ; (set file time & date)
|
|
mov bx,filehandle
|
|
mov cx,ftime ; set timestamp of combined file to
|
|
mov dx,fdate ; be that of the last of the sections
|
|
int 21h
|
|
mov ah,3Eh ; DOS function: close file handle
|
|
int 21h
|
|
pop ax
|
|
mov ah,4Ch
|
|
int 21h
|
|
|
|
config_for_ports:
|
|
mov section_heading,offset section_heading2
|
|
mov section_hdr_len,section_hdr_len2
|
|
push di
|
|
push si
|
|
push cx
|
|
;; copy combined_file2 over combined_file
|
|
mov cx,combined_file2_len
|
|
mov si,offset combined_file2
|
|
mov di,offset combined_file
|
|
rep movsb
|
|
;; copy section_file2 over section_file1
|
|
mov cx,section_file2_len
|
|
mov si,offset section_file2
|
|
mov di,offset section_file1
|
|
rep movsb
|
|
pop cx
|
|
pop si
|
|
pop di
|
|
add section_file,section_file2_ofs
|
|
jmp get_cmdline_switches
|
|
|
|
CODE ENDS
|
|
|
|
STACKSEG SEGMENT PUBLIC WORD 'STACK'
|
|
stacktop dw 160 dup (?)
|
|
stackbot label byte
|
|
STACKSEG ENDS
|
|
|
|
BUFFERSEG SEGMENT PUBLIC WORD 'DATA'
|
|
disk_buffer db 62*1024 dup (?)
|
|
disk_buffer_end label byte
|
|
BUFFERSEG ENDS
|
|
|
|
END combine
|