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 , and the command number is ; put in . 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 . ; 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 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 ; 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 ; 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 ; =? 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 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 add a,1 ; Increment daa ; Decimal convert ld c,a ; Resave in 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 ; 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/ 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 ld b,0 ; Zero ; 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 ; ; 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 times djnz finame ; ; At this point 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 ; 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 ; 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 ; 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 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 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