home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CP/M
/
CPM_CDROM.iso
/
cpm
/
utils
/
dirutl
/
menu.mac
< prev
next >
Wrap
Text File
|
1994-07-13
|
12KB
|
461 lines
title MENU
.z80
aseg
;
; Originally published in Creative Computing, December 1979
; by James J. Frantz who wrote this on May 31, 1979.
; Converted to Z80 code by Frank J. Wancho, August, 1980, and
; removed the BASIC dependencies - i.e. .COM files are the only
; file type examined and displayed.
;
; This program is designed to be automatically executed
; by CP/M immediately after a cold (or warm) boot. This
; program then displays the contents of the disk in a
; menu fashion. All files of specified type are sorted and
; displayed alphabetically in four columns. The user then
; selects the desired program by its menu number. The
; selected program is then run.
;
PAGE
org 0100h
;
; First, the CP/M "Search" command is used to find the
; file of the specified type. The pointer to the File
; Control Block is put in <DE>, and the command number is
; put in <C>. The File Control Block is pre-constructed to
; the form '????????XXX0'. The XXX is the specified file
; 'type', and the '?' force a match to any file name of that
; file type.
;
start equ $
ld sp,stack ; Set up a stack
again equ $
ld c,17 ; 'Search First' Comand
;
; This next routine sorts the file names as they are found
; on the disk directory. A name is read from the disk and
; its location is found in the directory table by comparing
; alphabetically.
;
sortlp equ $
ld de,srcfcb ; Point File Control Block
call bdos ; Use CP/M entry point
;
; CP/M returns the disk address of the next match in <A>.
; This is a value between 0 and 64, or -1 if no match was
; found. Test for -1 and quit when no more files of the
; specified file 'type' are found on the disk. The disk
; address is converted to a pointer to the file name
; within the sector by multiplying by 32 and adding the
; base address of the sector.
;
cp 255 ; Test for -1
jp z,assign ; Print empty menu
rrca ; This is the same as
rrca ; 5 ADD A's
rrca
and 60h ; Mask correct bits
add a,80h ; Add base address (0080h)
ld e,a ; Put pointer in <DE>
ld d,0 ; as a 16 bit value
ld hl,dirtab ; Point to start of table of
; sorted names
inc de ; Point past erase field
cmplop equ $
push de ; Save pointer to next
; entry from disk directory
ld c,8 ; Length of compare
push hl ; Save pointer to table
cmp1 equ $
ld a,(de) ; Get trial name char
cp (hl) ; Match?
jr nz,endcmp ; If not, try next entry
inc hl ; Advance pointers
inc de
dec c
jr nz,cmp1 ; Keep testing
endcmp equ $
pop bc ; Restore table pointer
jr c,insnam ; Directory name goes in
; front of the current table
; if lower (CY=1)
ld hl,14 ; Length of table entry
add hl,bc ; <HL> to next table entry
pop de ; Recover trial name point
jr cmplop ; Loop again
;
; This next portion makes room in the directory table for
; the new entry by moving all alphabetically higher names
; upward in memory.
;
insnam equ $
ld hl,filcnt ; Count the number of files
inc (hl) ; to be displayed
ld hl,(eot) ; Get pointer to table end
ex de,hl
ld hl,14 ; Distance to move
add hl,de ; <HL> point destination
ld (eot),hl ; Save the new End of Table
inc hl
inc de
;
moveup equ $
dec de
dec hl
ld a,(de) ; Get byte to move
ld (hl),a ; Put in new spot
ld a,c ; Test for done
cp e ; <BC>=<DE>?
jr nz,moveup
ld a,b
cp d
jr nz,moveup
pop hl ; Recover pointer
ld c,8
call blkmov ; Insert name in table
;
; The menu number field is inserted in the directory table
; at this point but the actual menu number will be assigned
; after all the files are sorted.
;
ld hl,menbuf ; Point menu number block
ld c,6 ; Length of move
call blkmov ; Insert text in table
;
; The command number for subsequent searches of the disk
; directory must be altered to cause CP/M to search from
; where it left off.
;
ld c,18 ; 'Search Next' command
jp sortlp
PAGE
;
;
; This is the second major portion of the program. At this
; point, all files have been inserted in the directory table
; in alphabetical order. Now the menu numbers are assigned
; and inserted in the proper place in preparation for
; display on the terminal.
;
assign equ $
ld a,(filcnt)
ld b,a ; Save in <B>
push af ; and on stack
ld c,0 ; Initial file number
ld hl,dirtab+11 ; Point first file number
ld de,13 ; Offset to other numbers
numfil equ $
ld a,c ; Put file number in <A>
add a,1 ; Increment
daa ; Decimal convert
ld c,a ; Resave in <C>
rrca ; Get tens digit into
rrca ; proper place
rrca
rrca
and 0fh ; and mask
jr z,useblk ; Suppress leading zero by
add a,10h ; add either 20H (ASCII ' ')
useblk equ $
add a,' ' ; or 20H + 10H for numeric
ld (hl),a ; Put in text stream
ld a,c ; Get units portion
and 0fh ; Mask off tens portion
add a,'0' ; Convert to ASCII
inc hl
ld (hl),a
add hl,de ; Repeat until all files
djnz numfil ; are sequentially numbered
;
pop af ; Get FILCNT from stack
push af ; and save again for later
;
; This algorithym ensures the columns are as even in length
; as possible. Don't worry, it works.
;
add a,nbrcol-1
ld b,-1 ; <B> accumulates quotient
; So set to -1 for at least
; one pass thru gives 0
divx equ $
inc b
sub nbrcol ; Divide (FILCNT+3) by
; four to get OFSET1
jp p,divx
add a,nbrcol ; Subtracted once too much
; so add it back in
ld hl,ofset1
ld (hl),b ; Insert OFSET1 into table
inc hl ; Point OFSET2 location
jr nz,setof2 ; Same as OFSET2 if non-
; zero remainder
dec b ; Else OFSET2=OFSET1-1
setof2 equ $
ld (hl),b ; Put OFSET2 in table
inc hl ; Point OFFSET for Col. 3
dec a ; Test for remainder of 1
jr nz,setof3 ; If remainder <> 1, use
; OFSET3=OFSET2
dec b ; Else OFSET3=OFSET2-1
setof3 equ $
ld (hl),b ; Offset to Col. 4
;
; Now that the offsets for the columns have been determined
; the actual print out can be made
;
reprt equ $
pop af ; Recover FILCNT
reprt1 equ $
push af ; Save again for later use
ld (filcnt),a ; Save for counting
ld a,scrhgt ; Set for video display size
ld (lincnt),a
call clear ; Clear screen
ld de,hdg
call print
;
ld hl,dirtab-14 ; Point dummy 0th entry
;
prtlin equ $
push hl ; Save base address
ld de,ofset0 ; Point offset table
ld a,nbrcol ; 4 columns per line
prtnam equ $
ld (colcnt),a ; Save count of columns
push hl ; Save current name pointer
push de ; Save offset table pointer
;
ld de,trplsp ; Print 3 blanks
call print ; Use CP/M facility
pop de ; Get offset table pointer
pop hl ; Get name pointer
ld a,(de) ; Get offset value
;
; The offset value is the number of file names from the
; current name pointer to move the print buffer.
;
ld bc,14 ; Each name is 14 long
mult14 equ $
add hl,bc ; Add 14 for each offset
dec a ; Until offset = 0
jr nz,mult14
push hl ; Save new name pointer
push de ; Save offset pointer
ex de,hl ; Point name to print w/<DE>
call print ; Print the file name and
; its menu number
;
tesfin equ $
ld hl,filcnt ; See if done printing
dec (hl) ; by testing count of files
pop de ; Get offset pointer
pop hl ; Get pointer to last name
jr z,finish ; No more to print
inc de ; Advance offset pointer
ld a,(colcnt)
dec a ; See if columns left = 0
jr nz,prtnam ; Print another same line
call crlf ; Move to next line
pop hl ; Get base of previous line
ld de,14 ; Add offset
add hl,de
jr prtlin
;
; The file names and their menu numbers have been printed.
; This next loop outputs sufficient linefeeds to put the
; heading at the top of a 16 line video display (thereby
; clearing the screen), and puts the request for user
; selection at the bottom of the screen.
;
;
finish equ $
pop hl ; Unjunk stack
lfloop equ $
call crlf
jp p,lfloop ; Omit this line if desired
ld de,prompt ; Point instruction msg
call print ; Again CP/M prints message
ld de,inbuf
ld a,10 ; Ten characters max
ld (de),a
ld c,10 ; 'Read Buffer' Command
call bdos
;
; On return from BDOS line input function, the digits
; typed by the user are in the buffer at INBUF+2.
; Convert to binary
;
ld hl,inbuf+1 ; Point character count
ld a,(hl) ; Get it and see if > 2
cp 3
jr nc,reprt ; Reprint the menu
ld c,a ; Count of digits to <C>
ld b,0 ; Zero <B>
;
getnum equ $
inc hl ; Point ASCII digit
ld a,(hl) ; Get it
call ascbin ; Convert to binary
jr c,reprt ; Redisplay on error
dec c
jr nz,getnum
;
; <B> has the menu number. To be sure this is still a legal menu
; request, compare with FILCNT (still in stack).
;
pop af ; Recover file count
cp b ; FILCNT-request number
jp c,reprt1 ; Redisplay menu if illegal
;
;
ld de,14 ; Increment between names
ld hl,dirtab-14 ; Point dummy 0th entry
finame equ $
add hl,de ; Add offset <B> times
djnz finame
;
; At this point <HL> points to the selected file name. Now find
; the address of CP/M so the proper command name and the selected
; filename can be put into the command buffer.
;
ex de,hl ; Save pointer to file name
ld hl,(6) ; Get BDOS entry point
ld bc,-ccplen ; Offset to start of CP/M
add hl,bc
push hl ; Save CP/M entry point
; on stack for branch.
ld bc,7 ; Offset to command buffer
add hl,bc ; <HL> points place to put
; name of .COM file to be executed.
ld a,8 ; Get default length of file name
ld (hl),a ; Store in CCP
inc hl
push de ; Save pointer to file name
ex de,hl ; <DE> points command buffer
;
; Since the scan pointer is not reset by reentry into CP/M,
; the scan pointer must be reset by this program. The scan
; pointer is stored by CP/M at the end of the command buffer.
;
ld hl,128 ; Offset to end of cmd buff
; where pointer is stored
add hl,de ; <HL> points storage place
ld (hl),e ; Update buffer pointer to
inc hl ; to start of the command
ld (hl),d ; buff so CP/M will read.
;
; (The insertion of the BASIC command name goes here. Removed
; from original version.)
;
pop hl ; Point selected file name
ld c,8 ; Length of file name
call blkmov
;
; (The insertion of file type goes here if your version of BASIC
; requires it.)
;
xor a ; Need a zero at end
ld (de),a ; of command line
;
; The address of CP/M is on the stack, so a simple RETurn
; will execute CP/M and in turn execute 'filename'.
;
ret
;
; Subroutines
;
blkmov equ $
ld a,(hl)
ld (de),a
inc de
inc hl
dec c
jr nz,blkmov
ret
;
ascbin equ $
sub '0' ; Subtract ASCII bias
cp 9+1 ; Be sure it's numeric
ccf
ret c ; Set CY=1 if illegal
ld d,a ; Save in <D>
ld a,b ; Get previous result
rlca ; Multipy by 2
rlca ; then 4
rlca ; then 8
add a,b ; then 9
ret c ; out of bounds
add a,b ; Then finally by 10
ret c ; CY=1 always means error
add a,d ; Add in new result
ld b,a ; Save in <B>
ret
;
crlf equ $
ld de,crlfmsg
call print
ld hl,lincnt
dec (hl)
ret
;
print equ $
ld c,9 ; Buffer print command
call bdos ; CP/M prints heading
ret
;
clear equ $
ld de,clrs
call print
ret
;
crlfmsg equ $
defm 0dh,0ah,'$'
clrs equ $
defm 30,30,'$' ; Clear Screen
hdg equ $
defm 9,9,9,' MENU',0dh,0ah,0ah,'$'
;
prompt equ $
defm 'Enter MENU Number and press RETURN: $'
;
trplsp equ $
defm ' $'
;
menbuf equ $
defm ' - 0$'
;
;
ofset0 equ $
defb 1
ofset1 equ $
defm 0,0,0
;
eot equ $
defw dirtab
;
filcnt equ $
defb 0
colcnt equ $
defb 4
lincnt equ $
defb 0
;
srcfcb equ $
defm 0,'????????COM',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
;
dirtab equ $
defb -1 ; Force first file to be put here
;
stack equ dirtab+64*14+30
;
inbuf equ stack
;
; Equates
;
bdos equ 5
nbrcol equ 4
ccplen equ 3106h-2900h
scrsiz equ 24
scrhgt equ scrsiz-4
end start