; File DIR_SUBS.S contains the SIO2PC subroutines used for ; the get directory functions: ; ; CONTAINS THE SUBROUTINES: ; DODIR; ATAR_DIR; SETDTA; GETDTA; RESETDTA ; GET_DIR; GET_DRIVE; FIND1ST; FINDNEXT; EXTRACT ; NOT_ENUF; PUT_DIRSEC; SPARTA_DIR; LINE_FEED; GETPATH ; FILELIST; REGLITE; HILITE; LIST2SCRN; LIST2RAM; ; Directory Subroutines ; DODIR - A routine to search the directory (current or specified) ; and list filenames on the screen. Note, this function doesn't use ; GET_TORK for input, so it can't be run from REMOTE. DODIR: CALL CLR_SCRN CURSET 0005 MOV FIELD_COUNT,0; 3.06 ; first ask if user wants PC dir, or to extract Atari dir from image PRINTL DIR_FIRST CALL GET_TORK CMP AL,'1' JE >L1 CMP AL, '2' JNE DODIR JMP ATAR_DIR; Finish up in subroutine for Atari directory ; at this point, the user wants a PC directory L1: PRINTL DIR_INSTR CURSET 0605h CALL SETDTA MOV AH,0Ah; DOS buffered input function PUSH CS POP DS MOV DX, OFFSET DPTHHDR INT 021h CMP B[DIRPATH],0Dh; just ENTER pressed? JNE >L1 JMP SHORT FINDIR ; Buffered input function 0Ah ends with a CR (0Dh), then 0. The ; directory search functions won't stand for the CR, so the code ; below changes it to a 0. (ASCIIZ string). L1: MOV BH,0 MOV BL,[DPTHHDR+1]; get # of bytes MOV B[DIRPATH+BX],0 CURSET 0800h CALL FIND1ST; Get first directory match JC ABN L1: CALL UPDATE CALL FINDNEXT JNC L1 JMP TTSALF ABN: CMP AX,03; Invalid path error JNE TTSALF; "That's all folks" PRINTL BADPATH TICKS 45 JMP DODIR TTSALF: PRINTL END_O_DIR DIRDUN: MOV AH,0Bh; Check STDIN status (is a keypress ready?) INT 021h CMP AH,0 JE DIRDUN; 0 means no char waiting L1: MOV AH,08h; Now, get the char. and throw it away INT 021h CMP AL,0; Extended code? JE L1; If so, get the rest of it FINDIR: ; CALL RESETDTA ; comment out via 4.11d RET; We're outta here! ; GET_DIR gets original directory and stores in OLDPATH for permanent. ; GET_CDIR gets current directory and stores in NEWPATH for current use. GET_DIR: MOV AH,047h; DOS function: get current directory MOV DL, 0 ; current default drive 3.16. NOTE default ; drive gotten from function 19h DOESN'T match that expected by ; function 047h !! ; MOV DL,B[CUR_DRIVE]; for current drive (gotten w/ function 19h) PUSH CS POP DS MOV SI,OFFSET OLDPATH; PATH will be stored in OLDPATH as ASCIIZ INT 021h; note: carry set if error... RET GET_CDIR: MOV AH,047h; DOS function: get current directory MOV DL, 0 ; current default drive 3.16. NOTE default ; drive gotten from function 19h DOESN'T match that expected by ; function 047h !! PUSH CS POP DS MOV SI,OFFSET NEWPATH; PATH will be stored in NEWPATH as ASCIIZ INT 021h; note: carry set if error... RET GET_DRIVE: MOV AH,19h; DOS get drive function INT 021h MOV [CUR_DRIVE],AL; A = 0, B = 1, Z = 25 RET ; Find first directory match: FIND1ST: PUSH CX,DX FILL ' ', DIRFNAME, 12 MOV AH,4Eh; DOS find first match MOV CX,10h; Attribute to find normal files & directories PUSH CS POP DS MOV DX,OFFSET DIRPATH INT 021h ; Note: carry set on error. AX = 2 means no match found; 3 means ; invalid path L1: POP DX,CX RET ; Find next directory match: FINDNEXT: PUSH AX FILL ' ', DIRFNAME, 12 MOV AH,4Fh; DOS find next match function INT 021h; File info goes to, carry set if no match POP AX RET ; Set DTA to my DIRDTA data area: SETDTA: MOV AH,1Ah; DOS set DTA function PUSH CS POP DS MOV DX,OFFSET DIRDTA INT 021h RET ; Get and save original DTA: GETDTA: PUSH AX,BX,ES MOV AH,2Fh INT 021h MOV [SEG_ODTA],ES MOV [OFF_ODTA],BX POP ES,BX,AX RET ; Restore DTA to as-found: RESETDTA: PUSH AX,DS,DX MOV AH,1Ah MOV DS,SEG_ODTA MOV DX,OFF_ODTA INT 021h POP DX,DS,AX RET ; This routine puts the found file name into a line field and prints ; it when it is full: UPDATE: CLD MOV CX,12; # of bytes to move MOV DI,OFFSET DLPLUS5 MOV SI,OFFSET DIRFNAME PUSH CS PUSH CS POP DS POP ES REP MOVSB; Move filename from DTA to DIRLINE field CMP B[ATTDIR],010h; Directory? JNE >L1 MOV CX,5; Length of {dir} string MOV SI,OFFSET DIRSTR MOV DI,OFFSET DIRSPOT REP MOVSB L1: MOV AH,040h; put bytes to file MOV BX,1; Handle for screen MOV DX,OFFSET DIRLINE MOV CX,20; # of bytes to write INT 021h FILL ' ', DIRLINE, 20 RET ; Routine to extract the Atari directory from a disk image: ATAR_DIR: CALL SETDTA ; First, get the user to give the search spec: CALL CLR_SCRN CURSET 0000 PRINTL ENT_IMG MOV AH,0Ah; DOS buffered input function PUSH CS POP DS MOV DX, OFFSET DPTHHDR INT 021h CMP B[DIRPATH],0Dh; just ENTER pressed? JNE >L1 JMP FINDIR1 ; See if user put ,P at end of pathname, if so output ; also goes to printer L1: MOV W[HANDLP],0; Assume no output to printer MOV BH,0 MOV BL,[DPTHHDR+1]; # of bytes MOV AL,[DIRPATH+BX-1] AND AL, 11011111xB; Force uppercase CMP AL,'P' JNE >L1 MOV AL,[DIRPATH+BX-2]; Comma before P? CMP AL,',' JNE >L1 MOV W[HANDLP],4; Handle for printer MOV B[DIRPATH+BX-2],0; Fix pathname for DOS ; Buffered input function 0Ah ends with a CR (0Dh), then 0. The ; directory search functions won't stand for the CR, so the code ; below changes it to a 0. (ASCIIZ string). L1: MOV BH,0 MOV BL,[DPTHHDR+1]; get # of bytes MOV B[DIRPATH+BX],0 CURSET 0400h MOV B[DIR1],0; flag that this is first match CALL FIND1ST; Get first directory match JC ABZ L1: CALL EXTRACT; Now, extract the Atari directory from the file JNC ACB; No carry means no error PRINTL ERRDIR JMP TTSXLF ACB: INC DIR1; flag this will not be first match CALL FINDNEXT JNC L1 JMP TTSXLF ABZ: CMP AX,03; Invalid path error JNE TTSXLF; "That's all folks" PRINTL BADPATH TICKS 45 JMP DODIR TTSXLF: PRINTL END_O_DIR CALL WAIT4KEY FINDIR1: ; CALL RESETDTA ; 4.11d commented out RET; We're outta here! ; A routine to extract Atari directory information from a disk image EXTRACT: MOV N_SUBDIR, -6; Moves in incrs. of 6; stays at 6 below next ; free location. ; If this is not the first match, ask the user to press a key ; to continue to next: CMP B[DIR1],0; 0 means first match JE >L1 PRINTL CRLF PRINTL KEYWAIT PRINTL CRLF CALL WAIT4KEY CMP AL, 27 ; 3.12 - user can bail out here JNE > L1 JMP BADRET ; MOV FIELD_COUNT,0 ; Put the found filename into the pathname spec ; First, find last backslash, if there was one: L1: MOV BH,0 MOV BL,[DPTHHDR+1]; number of bytes in pathname from user L2: DEC BX CMP B[DIRPATH+BX],'\' JE >L1 CMP BX,0 JNE L2 JMP SHORT ACE; never found \, so start from BX=0 L1: INC BX; found \ so inc past it ; Now, move found filename into pathname ACE: MOV SI,0 L0: MOV AL, [DIRFNAME+SI] MOV [DIRPATH+BX],AL CMP AL,0; 0 marks end of ASCIIZ string JE >L1 INC SI INC BX JMP L0 ; Tell the user what atari image dir is being listed: L1: MOV AL, [DIRPATH+BX-1]; last char of filename - rev 3.04 MOV DISK_TYPE, AL; 'S' will mean SPARTADOS format MOV CX,BX; Get # of bytes to print PUSH CX PUSH CX; 2nd copy of output to printer PRINTL DIR_OF POP CX MOV AH,040h; PRINT MOV BX,1; Screen handle MOV DX, DIRPATH; ADDRESS OF - DIRPATH IS A LABEL INT 021h CALL LINE_FEED CALL LINE_FEED POP CX; retrieve length for hard copy output CMP W[HANDLP],0 JE >L1; No printer PUSH CX; Again save length of filename MOV AH,040h; put bytes to file MOV BX,4; Handle for printer MOV DX,DIR_OF; OFFSET MOV CX,18; Length of string GO2DOS BADRET POP CX; Now get back length of filename field: MOV AH,040h; put bytes to file MOV BX,4; Handle for printer MOV DX, OFFSET DIRPATH GO2DOS BADRET CALL CRLF2P; Linefeed to printer CALL CRLF2P; and another L1: MOV W[HANDL],0; Initialize to show if opened ; Now, open the file: MOV AH,03Dh; Open file MOV AL,0; Read access MOV DX, OFFSET DIRPATH INT 021h JNC >L1; no error PRINTL EOPF MOV AH,0; AX contains error code ADD AL,'0'; Convert to ASCII digit CALL PRINT1 TICKS 54 JMP BADRET L1: MOV BP, BLOCK_SIZE; 3.04 "5th" disk MOV CL, 2 SHL BP, CL ; BLOCK_SIZE * 4 MOV HANDL,AX; save file handle MOV [HANDLE+BP], AX L1: MOV [HANDL],AX; save file handle ; First, read the header to see what kind of file this is: MOV AH,03Fh; Read from file MOV BX, HANDL MOV CX,16; Number of bytes to read MOV DX, OFFSET D_HEADR; where to park 'em INT 021h JNC >L1; Jmp if no error JMP BADRET L1: MOV B[SEC_PTR+BP],1; pointer for disk 5 is now 1: r3.04 MOV AX, CATARI; Atari disk identifier CMP WORD PTR [D_HEADR], AX JE >L1 PRINTL NATDSK TICKS 54 JMP BADRET L1: CMP DISK_TYPE, 'S' ; SPARTA? rev 3.04 JNE >L1 CALL SPARTA_DIR JNC >L2 JMP BADRET L2: JMP L8 ; Good return L1: MOV CX,0; MSB of offset; Assume single density MOV DX,46096; LSB of offset; this points to sector 361 MOV AL,0; Access from start of file CMP B[D_HEADR+4], 080h; single density? JE >L1; If it was single density, bypass next... MOV CX, 1; for 64K part; MSB MOV DX, 26256; LSB ; Now, position the pointer to sector 361: L1: MOV DI, N_SUBDIR; Point DI to next stack loc ADD DI,6 CMP DI,96; 96 means stack is full JE >L1 MOV [FILOSH+DI],CX; save for subdir expansion MOV [FILOSL+DI],DX L1: PUSH DI MOV AH,042h; Position file pointer MOV BX, HANDL INT 021h POP DI JNC >L1 PRINTL ERRRF TICKS 54 JMP BADRET L1: MOV CX,64; counter for # of 16 byte fields ACA: CMP DI,96; Subdir stack full? JE >L1 MOV [FIELD+DI],CX; for possible subdir expansion L1: PUSH CX READ_FILE HANDL, 16, DIRBUF, >L1 POP CX; part of error return PRINTL ERRRF TICKS 54 JMP BADRET L1: MOV B[SDFLAG],0; Assume not a subdir MOV AL, B[DIRBUF] AND AL, 11000000xB CMP AL, 01000000xB; In use: b6 set, not deleted: b7 clear JE >L1; Jump if valid filename MOV AL,B[DIRBUF]; check for MYDOS subdir signature (010H) CMP AL, 010h JNE ACF MOV B[SDFLAG],0FFh; Flag entry is a subdir CMP N_SUBDIR,96; Is subdir stack already full? JE ACF; if so, this one won't get expanded... ; Here, we KNOW this is a MYDOS subdir. We have already put the ; pointer info on the subdir stack, but it's not officially on ; the stack until the stack pointer has been moved. So: ADD N_SUBDIR, 6 ACF: POP CX LOOP ACA JMP GOODRET; end of dir data ; now, put dirline to screen: L1: CALL PUT_DIRLINE; 3.04 POP CX ; 3.12 - moved location of POP CX so BADRET JC BADRET ; will also get it JMP >L0 L4: JMP ACA ; fix out of range jmp for LOOP instr L0: LOOP L4 GOODRET: ; First, are there any subdir entries on the stack to be ; expanded? CMP N_SUBDIR, -6 JE >L8; -6 means the stack is empty CALL EXSUB L8: CALL CLOSDIRF CLC; Carry clear means good return RET BADRET: CALL CLOSDIRF STC RET ; REV 3.01 ADDED: LINE_FEED: PRINTL CRLF RET ; sub to put the dir name, separator, and ext. to screen and/or ; printer. This was in line before 3.04 PUT_DIRLINE: MOV AH,040h; put bytes to file MOV BX,1; Handle for screen MOV DX,OFFSET ATNAM MOV CX,8; # of bytes to write INT 021h CMP W[HANDLP],0 JE >D1; No printer MOV AH,040h; put bytes to file MOV BX,4; Handle for printer MOV DX,OFFSET ATNAM MOV CX,8; # of bytes to write GO2DOS AEF ; Now put the dot, if there's an extension D1: CMP B[SDFLAG],0 JE >C1 ; 3.04: previously was JNE ... MOV B[DOT],'*'; Use * to indicate subdir... JMP SHORT ACG C1: MOV B[DOT],'.'; Assume extension CMP B[ATEXT],' ' JNE ACG CMP B[ATEXT+1],' ' JNE ACG CMP B[ATEXT+2],' ' JNE ACG MOV B[DOT], ' '; No extension ACG: MOV AH,040h MOV BX,1 MOV DX, OFFSET DOT MOV CX,1 INT 021h CMP W[HANDLP],0 JE >B1; No printer MOV AH,040h; put bytes to file MOV BX,4; Handle for printer MOV DX,OFFSET DOT MOV CX,1; # of bytes to write GO2DOS AEF ; Now put the extension B1: MOV AH,040h MOV BX,1 MOV DX, OFFSET ATEXT MOV CX,7; 3 plus 4 for padding to 16 byte field INT 021h INC FIELD_COUNT CMP FIELD_COUNT, 90; 18 lines; 3.17 - WAS 20 LINES JB >L1 CALL LINE_FEED PRINTL KEYWAIT CALL LINE_FEED ; 4.11 added CALL WAIT4KEY CMP AL, 27 ; ESCAPE? REV 3.12 CHANGE JE AEF MOV FIELD_COUNT, 0 L1: CMP W[HANDLP],0 JE >A1; No printer MOV AH,040h; put bytes to file MOV BX,4; Handle for printer MOV DX,OFFSET ATEXT MOV CX,7; # of bytes to write GO2DOS AEF A1: CLC RET AEF: STC ; flag bad return RET ; A routine to set the file pointers for extraction of a MYDOS ; subdirectory and print the subdir name to the output devices: EXSUB: RET; EXSUB NOT PROGRAMMED YET ; close dir file, if it was opened: CLOSDIRF: CMP W[HANDL],0 JNE >L1 RET L1: MOV AH,3Eh MOV BX,HANDL INT 021h RET ; A routine to extract the directory of a SPARTA dos disk image SPARTA_DIR: ; First, fill in some info from the header into the disk #5 info ; area: MOV AX, WORD PTR[D_HEADR+4]; Sector size MOV SEC_SIZE[BP], AX MOV AX, WORD PTR [D_HEADR+2]; low word of disk size in paras MOV LO_SIZE[BP], AX MOV AX, WORD PTR [D_HEADR+6]; hi word of size in paras MOV HI_SIZE[BP], AX MOV FIELD_COUNT, 0 MOV N_SUBDIR, 0 ; init stack pointer for subdirs MOV DIR_NEXT, 0 ; offset where next sector will go MOV AX, RAM; # of paragraphs free, make a copy MOV DIR_RAM, AX; for this function to calculate on V1: MOV SECTOR, 1 ; Now, read in the first boot sector CALL PUT_DIRSEC MOV DX, ES:[09] ; sector # of MAIN dir sector MAP ; Below is where subdir recursion will re-enter: JUMP_IN: MOV SECTOR, DX CALL PUT_DIRSEC; read in the sector map ; Now, starting at the 3rd word of the sector map, read in the ; identified sectors until 0 is found. MOV DX, DIR_NEXT ; First save adr. of where dir string MOV SPDIR, DX ; will start MOV DI, 4 ; 3rd word of sector map L1: MOV DX, ES:[128 + DI]; 128 skips boot sector CMP DX, 0 JE >N1 MOV SECTOR, DX INC DI INC DI PUSH DI CALL PUT_DIRSEC POP DI JNC L1; RET N1: MOV SI, SPDIR ; get offset of dir string MOV AX, ES:[SI+3]; Low word of file (dir) length XOR DH,DH ; This is a 1 1/2 word length MOV DL, ES:[SI+5]; high half word MOV CX, 23 DIV CX ; get # of entries total into AX ; SPARTA entries are 23 bytes long MOV CX, AX ; CX <- number of entries MOV BX, 0 ; BX will count offset to current entry AEH: MOV B[SDFLAG], 0; Not a directory (?) TEST BYTE PTR ES:[SI+BX], 8 ; entry in use? 3.17 was SI+BX+7 JNE >L1 ; bit 3 clear means deleted ADD BX, 23 JMP AEQ L1: TEST BYTE PTR ES:[SI+BX], 020h; directory? 3.17: was +7 JE >P1 MOV B[SDFLAG], 0FFh ; Save Sector Map reference of subdirectories: MOV MAIN_FLAG, BX; hold on to this fact... CMP BX, 0 ; Don't save ref for MAIN or Current dir JE >P1 CMP N_SUBDIR, 32 * 2 ; Stack full? JA >P1 MOV AX, ES:[SI+BX+1]; Sector # of Dir Map PUSH SI MOV SI, N_SUBDIR MOV [FILOSL+SI], AX; Put it on stack ADD N_SUBDIR, 2; Inc stack pointer; it grows UP POP SI P1: PUSH CX MOV DI, 0 MOV CX, 4; 4 words in 8 char filename W1: MOV AX, ES:[SI+BX+6] MOV WORD PTR [ATNAM+DI], AX INC DI INC DI INC BX INC BX LOOP W1 ; Add name to name stack, if subdir, if there's room: CMP B[SDFLAG], 0 JE >Q1 CMP MAIN_FLAG, 0 JE >Q1; don't save main or current, either CMP W[N_STACK_PTR], 32*8; 1 past top already? JE >Q1 MOV CX, 4 PUSH ES PUSH DS POP ES PUSH DI PUSH SI MOV DI, OFFSET NAME_STACK ADD DI, W[N_STACK_PTR]; 3.06 MOV SI, OFFSET ATNAM CLD REP MOVSW POP SI POP DI POP ES ADD W[N_STACK_PTR], 8 Q1: POP CX ; now, move the extension: MOV AX, ES:[SI+BX+6] MOV WORD PTR [ATEXT], AX MOV AL, ES:[SI+BX+8] MOV [ATEXT+2], AL ; now, point to next entry ADD BX, 15 ; (already added 8 above) PUSH BX PUSH CX CALL PUT_DIRLINE; Print filename to screen/printer POP CX POP BX JNC AEQ ; Error return from PUT_DIRLINE RET R1: JMP AEH AEQ: LOOP R1 ; Now, is subdirectory stack empty? CMP N_SUBDIR, 0 JNE >S1 CLC RET S1: PUSH BX MOV DIR_NEXT, 128; Start just past boot sector again... MOV AX, RAM MOV DIR_RAM, AX SUB DIR_RAM, 8 SUB N_SUBDIR, 2; Point to last entry CALL LINE_FEED CALL LINE_FEED PRINTL SAY_SUB SUB W[N_STACK_PTR], 8 MOV DX, OFFSET NAME_STACK ADD DX, W[N_STACK_PTR]; 3.06 CORRECTION? PUSH BP PUSH ES WRITE_SCREEN 8 CALL LINE_FEED CALL LINE_FEED ADD FIELD_COUNT, 15 POP ES POP BP CMP W[HANDLP],0 JE AEP; No printer PUSH BP PUSH ES CALL CRLF2P CALL CRLF2P MOV AH, 040h; put bytes to file MOV BX, 4; Handle for printer MOV DX, OFFSET SAY_SUB MOV CX, OFFSET FILOSL- OFFSET SAY_SUB ; # of bytes to write GO2DOS AEO MOV AH, 040h; put bytes to file MOV BX, 4; Handle for printer MOV DX, OFFSET NAME_STACK ADD DX, W[N_STACK_PTR]; 3.06 MOV CX, 8 ; # of bytes to write GO2DOS AEO CALL CRLF2P POP ES POP BP AEP: POP BX MOV SI, N_SUBDIR MOV DX, [FILOSL+SI]; Get sector map reference JMP JUMP_IN AEO: POP BX STC ; Error return RET ; PUT_DIRSEC reads a sector from the physical disk into SECBUF, ; then transfers it to free ram area PUT_DIRSEC: MOV AX, SECTOR; Set CFAUXes for CX_SIZE routine MOV CFAUX1, AL MOV CFAUX2, AH CALL RD_FSEC ; Data will go to SECBUF JNC >L1 JMP AEL ; error exit L1: CALL CX_SIZE ; Assume SECTOR and BP are still right MOV AX, CX ; I need CL to do my shift MOV BX, CX MOV CL, 4 SHR AX, CL ; AX <- paras; BX <- bytes SUB DIR_RAM, AX JA >U1 JMP NOT_ENUF U1: MOV CX, BX ; move dir sector from SECBUF into next free ram spot: ; CX contains the number of bytes CLD MOV ES, NEXTS MOV DI, DIR_NEXT MOV SI, OFFSET SECBUF REP MOVSB ADD DIR_NEXT, BX ; Point to next free address CLC RET NOT_ENUF: PRINTL DIR_RAM TICKS 45 AEL: STC RET ; ***************************** FILE LIST ***************************** ; COMMENT\ This function will present a list of file names in scrolling column format. The selected name will be hi-lited. Directories will show with backslash The user will be able to select a file or change directories by pressing ENTER on the desired selection. Carry clear means file or directory name has been placed in RFSPEC. Carry set and AX = 0 means user decided to cancel the file request. ENDOFCOMMENT\ FILELIST: ; Clear the screen and put the path on the top line: CALL CLR_SCRN CURSET 0 WRITE_TSTR NEWDRIVE, 0 ;; write ASCIIZ string CALL DIR_BOX PRINT_LM 27, 5, DROPSAY ; now, get file list and put in RAM MOV TOP_NOW, 0 ; # of field @ top of list range 0 -> TOTALD MOV SELECTED, 0 ; field in list selected -> 0 - 21 fields CALL LIST2RAM JNC > C1 CMP AX, 03 ; if carry set and AX=3, need new path. 4.12 JNE > D1 JMP ATP2 D1: JMP BADO C1: CALL BUBBLE ; sort the list CALL LIST2SCRN CALL HILITE ; make first entry be hilited ZL3: L3: CALL GET_UP ; look for up arrow, down arrow, ESC, or CR L5: CMP AX, 04800h ; arrow UP? JNE > L1 CMP SELECTED, 0 JE > L2 ; here, arrow up and not at top already CALL REGLITE ; first, deselect current entry DEC SELECTED ; not at top of list, so move up one CALL HILITE ; and select next one up JMP SHORT L3 ; here, arrow up but we are at top of list, so scroll if possible L2: CMP TOP_NOW, 0 ; are we at beginning of list? JE L3 ; can't scroll if already at top and at start of list DEC TOP_NOW ; so now, scroll away CALL LIST2SCRN ; we won't need to change hilited field (at top) JMP SHORT L3 L1: CMP AX, 05000h ; arrow DOWN? JNE > L6 CMP SELECTED, 21 ; already at bottom of list box? JNE > L7 ; here, we are at bottom of list box, so scroll if possible MOV AX, TOP_NOW ; top field in list box ADD AX, 21 ; see which field is on bottom CMP AX, TOTALD JAE L3 ; if last field already fits, can't scroll ; here, more fields left, so scroll down INC TOP_NOW CALL LIST2SCRN ; again, hilited field stays on bottom JMP SHORT L3 ; here, not at bottom of box, so move down if not on last field L7: XOR AX, AX MOV AL, SELECTED ADD AX, TOP_NOW ; what field in ram are we on? CMP AX, TOTALD JAE L3 ; can't scroll if at last field CALL REGLITE ; restore hilited field INC SELECTED ; go ahead and scroll down CALL HILITE ; hilite new selected field JMP SHORT L3 L6: CMP AL, 27 ; ESCAPE? JNE > Q1 BADO: XOR AX, AX STC JNC > Q1 JMP LP4 ; bail out Q1: CMP AL, 13 ; ENTER? JNE > L4 JMP CL4 L4: CMP AL, 020h ; spacebar?? (4.13 added this option) JNE > L5 JMP ATP2 ; Now, check for PGUP and PGDN keys L5: CMP AX, 04900 ; Page UP?? JNE > R1 BUGN1: CALL REGLITE CMP TOP_NOW, 0 ; already at top?? JNE > R6 ; Here, top of list is at top of ram, so just move selected field up to ; top if necessary CMP SELECTED, 0 JE > R3 ; jmp if already at top and top field selected MOV SELECTED, 0 ; window at top, but top field not selected JNE > R5 ; forced branch ; here, top of list is not at top of ram, so move up whole or part page ; as approprtiate R6: CMP TOP_NOW, 22 ; room to move up a whole page? JB > R4 SUB TOP_NOW, 22 ; move up a page JMP NEAR > R7 R4: MOV TOP_NOW, 0 ; if can't do whole page, go to top of list R7: CALL LIST2SCRN ;R5: CALL REGLITE ; unhilite current field R5: MOV SELECTED, 0 R3: CALL HILITE JMP ZL3 R1: CMP AX, 05100 ; Page DN? JNE > R2 CALL REGLITE MOV SELECTED, 0 MOV AX, 22 ; was 43 ADD AX, TOP_NOW CMP TOTALD, AX JB > P3 ; if can't do full page down ; was > P1 ADD TOP_NOW, 22 P3: CALL LIST2SCRN CALL HILITE JMP ZL3 ; Here, can't do a full page down ;P1: SUB AX, 22 ; CMP TOTALD, AX ; JBE > P2 ; MOV TOP_NOW, TOTALD ; SUB TOP_NOW, 21 ; JMP NEAR P3 ;P2: MOV SELECTED, 21 ; if can't page down, just hilite bottom field ; JMP NEAR P3 ;P2: JMP ZL3 ; Below, assume char is the first char of a filename or dir, so jump ; to next entry with that 1st char R2: CMP AL, 0 ; 4.14 - but first, if extended char, it's invalid ... JNE > R3 JMP ZL3 R3: PUSH AX CALL REGLITE ; unhilite current field MOV AL, SELECTED ; CMP AX, TOP_NOW ; JNE > B1 ; MOV AL, 1 ; start with field 3 if at top already INC AL ; start search with SELECTED + 1 XOR AH, AH ADD AX, TOP_NOW ; what field in ram are we on? MOV BL, 13 ; length of field MUL BL ; multiply AX * BL, results to DX:AX MOV DI, AX ; Now, if we started at last field, need to rollover to 1st ... CMP NXT_DRAM, DI ; if equal, one beyond list JNE > E1 MOV DI, 0 ; reset to top of list E1: MOV START_PT, DI ; record starting point ; Now ES:DI points to current field + 1, so start hunting for a match POP AX ; get back char to find ... MOV ES, NEXTS B6: CMP ES:[DI], AL JE > B2 ; B2 means found it! ADD DI, 13 CMP START_PT, DI ; full circle, no match? JE > B7 ; re-hilite and go back for next char from user CMP NXT_DRAM, DI ; if equal, one beyond list JNE B6 MOV DI, -0D ; reset to top of list 4.13 -> -0D + 0D = 0000 JMP B6 ; forced branch (was JE) ; Here, item was found so, ... B2: MOV AX, TOP_NOW ; calculate ram top and bottom of list box MOV BX, 13 ; field length MUL BX ; result in DX:AX, assume 16 bit, so in AX ... MOV WIN_S, AX ; start of window, ram address ADD AX, 13 * 21 ; end of window MOV WIN_F, AX CMP DI, WIN_S JB > C1 ; out of window, above CMP DI, WIN_F JA > C1 ; out of window, above SUB DI, WIN_S ; calculate # of new selected field MOV AX, DI ; prepare to divide MOV BL, 13 DIV BL ; divide AX by 13 - results in new field in window MOV SELECTED, AL B7: CALL HILITE JMP ZL3 ; Below, found field known to be outside of window, so shift TOP_NOW ; to make found field be bottom of list. DI still holds found field ; address ... C1: MOV DX, 0 ; going to do big divide - may be more than 256 fields MOV AX, DI MOV BX, 13 DIV BX ; divide DX:AX by BX, result in AX SUB AX, 21 MOV TOP_NOW, AX MOV SELECTED, 21 ; found field at bottom CALL LIST2SCRN JMP B7 ; below, Enter has been depressed, so get filename CL4: CALL TAKE_IT ; get field name to RFSPEC JNC > Z2 ; carry means it's a directory MOV B [DI-1], 0 ; change trailing \ to 0 for ASCIIZ string CH1: CALL CHDIR ; change directory to that in RFSPEC string JNC > C3 CALL CLR_SCRN PRINTL ERRDIR CALL WAIT4KEY JMP FILELIST ; start over with list box ; carry set means error, AX = 03h means path not found C3: CALL GETPATH ; get current dir to NEWPATH, add search spec too JMP FILELIST ; if successful, start over Z2: CMP AX, 0 ; field 0 means CANCEL JNE > P3 STC JNC > P3 ; return with carry set T4: JMP LP4 P3: CMP AX, 1 ; field 1 means manual entry of search spec JE > P2 ; AX not 0 or 1 means normal return status JMP LP1 ; **************** MANUAL ENTRY OF SEARCH SPEC / DIR *********************** ; Now, ask user for search spec or ENTER to cancel ATP2: P2: CALL CLR_SCRN CURSET 0605h PRINTL WHATSPEC ; CALL SETDTA MOV AH,0Ah; DOS buffered input function PUSH CS ; read string into DIRPATH POP DS MOV DX, OFFSET DPTHHDR INT 021h CMP B[DIRPATH],0Dh; just ENTER pressed? JNE >L1 D4: XOR AX, AX ; signal cancel function requested STC JC T4 ; long jump to P4 ; now search string user put at DIRPATH, see if there is a drive spec L1: CMP B [DIRPATH+1], ':' ; colon second means drive letter included JNE > D1 MOV AL, B [DIRPATH] ; get drive letter CALL TO_UPPER MOV DL, AL MOV B [DIRPATH], AL ; make drive letter be uppercase CMP B [NEWDRIVE], DL ; is drive same as current? JE > D1 ; skip if no drive change required SUB DL, 'A' ; make DL have drive # as A = 0 MOV AH, 0Eh ; change drive function GO2DOS BADO ;; Jumps to next line unless crit error ignored ; then goes to BADO for error exit ; now, verify drive change was successful CALL GET_DRIVE; Store drive # @ CUR_DRIVE MOV AL,B[CUR_DRIVE] ADD AL,'A'; Change drive # to drive letter, 0 = A. CMP AL, B [ DIRPATH] ; should be same if success JE > D3 PRINTL BAD_DRIVE CALL WAIT4KEY JMP D4 ; error exit D3: MOV [NEWDRIVE], AL ; here, either no drive spec, or drive change has been processed. So now ; parse the rest of the string ATD1: D1: XOR AX, AX MOV AL, B [DPTHHDR+1]; get length of string DEC AX ; convert to 0 based PUSH DS POP ES ; first, all chars from end back to \ or : or beginning are search spec ... ; count length of filespec if any, then move to FILENAME ... MOV CX, 0 MOV SI, DIRPATH ADD SI, AX ; point to last char CMP B [SI], '\' ; if \, no spec to get JE > F3 CMP B [SI], ':' ; if :, no spec or path ... JNE > E2 ; go back get current path and reenter JMP C3 E2: MOV AL, B [SI] CMP AL, ':' JE > E1 CMP AL, '\' JE > E1 CMP SI, DIRPATH ; beyond end of string? JB > E1 INC CX DEC SI JMP SHORT E2 ; at this point we've counted the filename, if there was one. Now SI is at ; the start of the filename - 1 and CX has the total number of bytes, so ; move it to FILENAME E1: CMP CX, 0 JE > E3 ; nothing to move MOV DI, FILENAME PUSH SI INC SI REP MOVSB ; move the string to FILENAME POP SI MOV B [DI], 0; now add 0 to end ; Here drive change has been processed and filename extracted. Now get ; directory if there is one. Here, SI points to \ at end of path ... ; replace it with a 0 for a marker ; If the \ at the end of the path comes right after the drive spec, then ; it's not just a directory marker, it means root directory - so don't ; replace with a 0 in that case! Same if \ was first char of string! ; I'm going to copy to RFSPEC from left to right E3: CMP B [SI], '\' ; if no \, then there is no path JE > F3 CALL CONCPATH JMP FILELIST F3: CMP B [SI-1], ':' ; is the \ a root directory indicator? JE > F2 ; if not, replace it with 0, otherwise CMP SI, DIRPATH ; else was \ the first char? JNE > F1 F2: INC SI F1: MOV B [SI], 0 MOV DI, DIRPATH MOV SI, RFSPEC CMP B [DI+1], ':' ; If there was a drive spec, skip it JNE > L1 INC DI INC DI ; point to 1 beyond : L1: MOV AL, B [DI] MOV B [SI], AL INC DI INC SI CMP AL, 0 JNE L1 ; here, path name has been moved to RFSPEC, so change dir to path listed JMP CH1 ; re-enter FILELIST at change dir point ; Now, after manual entry of new search spec, re-do the file list box JMP FILELIST LP1: P1: CLC ; good return ; lastly, fix screen if needed while preserving state of carry and AX LP4: P4: PUSH AX LAHF ; store flags CMP TAG_RD3, 0FFh ; FF means don't restore normal SIO2PC screen JE > P5 PUSH AX CALL FIX_SCRN POP AX P5: SAHF ; restore flags (for carry status) POP AX RET ; CHDIR changes the current directory to the one in the string in RFSPEC CHDIR: MOV DX, RFSPEC MOV AH, 03Bh ; set directory function INT 021h RET ; carry set means bad; AX = 3 means path not found ; TAKE_IT will get the file or dir name selected from the list and move ; it to RFSPEC to get to the DS segment ... ; Because the first two fields (0 and 1) mean CANCEL and TYPE IT, the ; field number is returned in AX ; all 13 bytes are moved and if NOT a directory, 0 is appended ; Returns with carry set if the field is a directory name ... ; Also has DI pointing to \ + 1 if a directory TAKE_IT: CALL BLANK_RFS ; fill RFSPEC with blanks XOR AX, AX MOV AL, SELECTED ADD AX, TOP_NOW ; what field in ram are we on? PUSH AX ; hold that thought - fields 0 and 1 have special meaning MOV BX, 13 ; length of field [4.18 -- WAS BL] MUL BX ; multiply AX * BX, results to DX:AX [4.18, WAS MUL BL] MOV ES, NEXTS MOV SI, AX ; start of name in ES ram MOV DI, RFSPEC ; place to put it MOV CX, 13 ; # of bytes to move U1: MOV AL, ES:[SI] MOV [DI], AL INC DI INC SI LOOP U1 ; now, see if it's a directory FIND_BYTE RFSPEC, '\', 55 ;; Zero flag clear means not found STC JZ > Z1 ; zero condit means \ found, means directory ... FIND_BYTE RFSPEC, ' ', 55 ;; find 1st blank MOV B [DI-1], 0 ; replace with 0 for ASCIIZ CLC Z1: POP AX RET ; LIST2RAM searches the path/filespec starting at NEWDRIVE and puts list ; plus two options to ram starting at NEXTS:0000 ; returns with NXT_DRAM pointing to 1 field beyond last LIST2RAM: MOV TOTALD, 1 ; for CANCEL and TYPE IT (TOTALD 0 = 1) SMOVE NEWDRIVE, DIRPATH, 65 ;; FIND1ST needs it in DIRPATH MOV ES, NEXTS ; point to start of next free segment MOV CX, 26 ; start with CANCEL and TYPE SPEC choices MOV DI, 0 ; start at offset 0 of NEXTS MOV SI, CAN_DIR CLD REP MOVSB ; move two 13 byte strings MOV NXT_DRAM, DI ; assume DI points to byte following ; next, get actual file list from DOS; file goes to DIRFNAME in my DTA CALL FIND1ST ; C = error, AX = 2, no match, 3 = badpath JNC > L1 CMP AX, 2 ; return if no match JE > L2 CMP AX, 012h ; also means no match JE > L2 CALL CLR_SCRN ; only error besides no match is bad path, so CURSET 0202h PRINTL PATH_NG TICKS 36 MOV AX, 3 ; indicate bad path error. 4.12 STC JC > L2 ; return error status ; HERE, GO TO ROUTINE WHICH ASKS FOR MANUAL PATH ENTRY ; below copies the file entry to ram, doesn't copy the 0, fills out ; unused spaces with space char (13 space field) L1: INC TOTALD ; TOTALD is the total entries -1 in the list MOV ES, NEXTS MOV SI, DIRFNAME MOV DI, NXT_DRAM MOV CX, 13 ; max number to move, NAME, ., EXT, AND \ = 13 L4: MOV AL, B[SI] ; get entry char CMP AL, 0 ; 0 marks end of string JNE > L5 MOV AL, ' ' ; put space instead of 0 ... (assume not directory) CMP B[ATTDIR],010h; Directory? JNE > L5 MOV AL, '\' ; mark entry as directory L5: ES:MOV B[DI], AL INC SI INC DI CMP AL, ' ' ; if space, we're in pad out phase ... JE > L6 LOOP L4 ; forced branch JMP SHORT > L7 L3: MOV AL, ' ' ; pad out with blanks L6: LOOP L5 L7: MOV NXT_DRAM, DI CALL FINDNEXT JNC L1 ; if not end of DIR, move entry to ram CLC ; else report good status L2: RET ; after return, NXT_DRAM is also last entry + 1. ; BUBBLE will do a bubble sort on the dir list, not counting the first two ; entries. List expected to start at NEXTS:0000 and entry length is 13 ; bytes long. BUBBLE: CMP TOTALD, 3 JB > L1 ; must be at least 2 dir entries for a sort MOV ES, NEXTS L5: MOV CX, TOTALD ; get total - 1 SUB CX, 2 ; less two for 1st two entries MOV DID_BS, 0 ; flag no swaps required MOV DI, 26 ; first entry MOV SI, 39 ; 2nd entry L4: MOV AL, ES:[DI] CMP AL, ES:[SI] JBE > L2 ; no need to swap if 1st is >= 2nd ; here we swap the two entries PUSH CX, SI, DI MOV CX, 13 L3: MOV BL, ES:[SI] MOV AL, ES:[DI] MOV ES:[DI], BL MOV ES:[SI], AL INC DI INC SI LOOP L3 MOV DID_BS, 0FFh ; flag swap did occur POP DI, SI, CX L2: ADD DI, 13 ; go to next entry ADD SI, 13 LOOP L4 ; check next pair ; Now, if any swaps occurred we must go thru again CMP DID_BS, 0FFh JE L5 ; run thru list again L1: PUSH DS POP ES RET ; LIST2SCRN takes the list gotten by LIST2RAM and puts it to the screen ; in the list box. LIST2SCRN: CALL CLRDLIST ; 4.12 ; MOV SI, FIRST_ONE ; offset of 1st dir entry to list MOV AX, TOP_NOW ; offset (in fields) from start of list MOV BX, 13 ; length of a field MUL BX ; computes AX * BX, result in DX:AX, but I figure 16 bits max MOV SI, AX ; calculate actual offset of starting field MOV DI, (DB_ROW + 1)*160 + (DB_COL + 2) * 2 MOV ES, SCR_SEG MOV DS, NEXTS MOV CX, 22 ; # of fields available in box N2: PUSH CX MOV CX, 13 ; # of characters per field N1: MOV AL, [SI] MOV ES:[DI], AL INC SI INC DI INC DI;; Skip attribute byte on screen LOOP N1 ADD DI, 160 - 26 ; down one row and back one field POP CX CMP SI, CS:[NXT_DRAM] JE > L1 ; pointing to last entry + 1 if equal LOOP N2 L1: PUSH CS POP DS ; CALL WAIT4KEY RET ; Clear the directory list to all spaces; 4.12 added CLRDLIST: MOV DI, (DB_ROW + 1)*160 + (DB_COL + 2) * 2 MOV ES, SCR_SEG MOV CX, 22 ; # of fields available in box MOV AL, ' ' N2: PUSH CX MOV CX, 13 ; # of characters per field N1: MOV ES:[DI], AL INC DI INC DI;; Skip attribute byte on screen LOOP N1 ADD DI, 160 - 26 ; down one row and back one field POP CX LOOP N2 L1: PUSH CS POP ES RET ; HILITE hilites the selected entry in the list box. HILITE: PUSH ES MOV ES, SCR_SEG MOV DI, (DB_ROW + 1)*160 + (DB_COL + 2) * 2; points to field 0 MOV AL, SELECTED MOV BL, 160 ; add 160 bytes per row MUL BL ; product goes in AX ADD DI, AX INC DI ; point to attribute which is char + 1 MOV AL, INV_ATTR ; get inverse video attribute MOV CX, 13 ; length of field to hilite L1: MOV B ES:[DI], AL INC DI INC DI ; skip char LOOP L1 POP ES RET ; REGLITE restores normal attribute to SELECTED field ... REGLITE: PUSH ES MOV ES, SCR_SEG MOV DI, (DB_ROW + 1)*160 + (DB_COL + 2) * 2; points to field 0 MOV AL, SELECTED MOV BL, 160 ; add 160 bytes per row MUL BL ; product goes in AX ADD DI, AX INC DI ; point to attribute which is char + 1 MOV AL, ATTR1 ; get normal video attribute MOV CX, 13 ; length of field to hilite L1: MOV B ES:[DI], AL INC DI INC DI LOOP L1 POP ES RET ; CONCPATH adds the search spec @ FILENAME to the end of the NEWPATH string CONCPATH: ; first search NEWPATH upward until 0 is found ... FIND_BYTE NEWPATH, 0, 64 ;; Now DI points to 0 plus 1 ; now go from there back to the last \ mark ... ; note that we might go all the way back to the \ on the end of ; NEWDRIVE, but that should be OK if it happens (in root directory) DEC DI ; point to the 0 string terminator STD ; this time search backwards MOV CX, 64 ; max length to be searched MOV AL, '\' ; AL is the byte we are looking for REPNE SCASB ; now, DI wil be at last \ - 1 CLD ; fix direction flag INC DI ; point to \ + 1 INC DI ; now, copy the filespec string from FILENAME to this place MOV BX, FILENAME L2: MOV AL, [BX] ; get filename byte MOV [DI], AL ; put on end of path, even if 0 CMP AL, 0 ; but 0 marks end of string so JE > L1 ; quit after moving it INC BX ; else, get next one INC DI JMP SHORT L2 L1: RET ; GETPATH gets the current path including drive letter to string starting ; at NEWDRIVE. It also appends a \ and default *.* search spec. GETPATH: CALL GET_DRIVE; Store drive # @ CUR_DRIVE MOV AL,B[CUR_DRIVE] ADD AL,'A'; Change drive # to drive letter, 0 = A. MOV [NEWDRIVE], AL CALL GET_CDIR; Store path at NEWPATH FIND_BYTE NEWPATH, 0, 64 ;; find end of string and add \ unless CMP B [DI-2], '\' ; theres already a \ at end of existing string JE > L1 MOV B [DI-1], '\' ; as preface to adding search spec MOV B [DI], 0 ; mark new end L1: CALL CONCPATH ; add default *.* to search spec RET ; return to original default drive ... RES_DRIVE: MOV DL, B[OLDRIVE] SUB DL, 'A' ; make DL have drive # as A = 0 MOV AH, 0Eh ; change drive function INT 021h RET ; return to original path ... RES_PATH: MOV DX, OLDPATH - 1 ; point to \ root symbol for start of string MOV AH, 03Bh ; set directory function INT 021h RET ; carry set means bad; AX = 3 means path not found