aubrey@rpp386.cactus.org (Aubrey McIntosh) (10/02/89)
I wrote the attached model program to answer questions for myself. It is not well commented, but possibly worth posting anyway. At another time, I might wear my editor hat, and clean up (someone elses) code so that it reads better. If anyone wants to edit this code (in the publishing sense) please do so. Naturally, you woud still attribute me as author. Two threads of thought are explored and affirmatively answered in the program: 1) Can I truncate an existing file to an arbitrary length? 1.a) Can I force MS-DOS to relinquish the disk space while the file is still open. 2) Can I construct any supporting environment for the LDS SI, DWORD PTR label instruction. There are comments on the enter/leave macro. If you have a NEC V20, or '186, '286..., they produce correct code for those opcodes. I was arguing with MASM and found an opcode map before I found the MASM docs. The familiar push bp/sub sp,<size>/mov bp,sp sequence given as comments in the macro is code suitable on an 8088 machine, in this one program. They do not reconcile with the db statements in the macro. I used Symdeb when exploring, and traced through the program. At various times, I would do a t=<other label> and, in effect, have a menu driven interface through Symdeb. Some of the comments [ only | can't otherwise possibly ] make sense unless you keep that in mind. That is also why there are Publics in a self-contained main level program. CUT CUT CUT CUT CUT CUT CUT CUT CUT CUT CUT CUT CUT CUT CUT CUT CUT CUT CUT CUT CUT CUT CUT CUT CUT CUT name Truncate title The File Truncate test program. Verified w/ Symdeb. subttl A style ancillary to LDS dx,mem. page 132,75 .radix 16 ; .286 ; -------------------------------------------------------- ; Aubrey McIntosh ; Verify a method of truncating a file in MS-DOS. ; September 19, 1989. For use with Lilith emulator. ; Modify or comment upon freely. Leave my credits. ; ; MD-DOS functions: ; dos 3d02, <Open named file> ; dos 4200, <Move file pointer.> ; dos 4000, <Write to end of file.> ; dos 4000, <Write zero length record.> ; dos 4500, <Duplicate file handle.> ; dos 3e00, <Close duplicate file> ; dos 3f00, <Read a byte past implied clip point. Should fail.> ; ; I had needed a way to 'clip' a file at some point, so ; that I could emulate the way a mag tape works, i.e. ; when you write on it, and then 'rewind' it, there is ; a file mark immediately past the last data, and earlier ; files are irrelevant (erased). Simply using create on ; a new file was not useful to the third party application. ; ; Misinformation has been on the net that this is not ; possible, and I had tedious work arounds for years. ; Here is how I did it. ; ; Also, I digress into a shallow '86 hack. ; -------------------------------------------------------- data group vars, stack ;no initialized values. text group const, code ;can be in rom. public stklimit, stk, filenm, handle, buffer public start, open, position0, trunc, public position1, dupHandle, close0, read, close, stop ; -------------------------------------------------------- const segment public para 'rom' ; ------ Various string and pointer constants. filenm db 'scratch.tmp',0 nameptr dd filenm bufptr dd buffer stklimit dd stk const ends vars segment public para 'ram' handle dw ? buffer db 100 dup ( ? ) vars ends stack segment stack page 'ram' db 40 dup ('stack ') stk label word stack ends ; -------------------------------------------------------- ; The dos macro is just playing. I was miffed about having to put all ; the message addresses, lengths, and such somewhere before giving ; each error message. I thought LDS dx,xx would be a part of the solution, ; but the setup seemed unusually expensive. I worried with it a while, ; and came up with this. I sometimes still try to write tight code, just ; to keep up an anachronistic skill. Not a deep hack, but still an ; unorthodox way of using the instruction set to get to the goal, and ; an afternoon's pleasure. ; ; The routine messageToAux uses the return address on the stack as ; a pointer to a data packet immediately following the client's call. ; This lets me use an LDS dx,[bp].2 instruction without ; hardcoding pointers. ; ; The string length, the correct return address, and the string ; address are all implied by the packet, and are free with the ; (far) return value on the stack! ; ; This usage of [bp].n, IMHO, nicely compliments the LDS <reg>,<ref> ; instruction. After much searching, it is the only method I know ; to set up for the LDS without eating up memory with ; ptr dd <label> constructs. Although the return address is modified ; on the stack, this is pure code and not self modifying. ; ; I think this saves space. Audit calculations for both ways ; of handling the 'write to aux' task are included. ; ; Two other concerns are not addressed: ; a. Can I muck with the stack in '286 protected mode? ; b. Can I make the code position independent? ; -------------------------------------------------------- call macro proc ; -------------------------------------------------------- ; I can't get mixed far/near call/returns to work out. Since ; I'm determined to save a byte in the call, here goes. ; I get position independent code out of it anyway. ; -------------------------------------------------------- db 0E, 0e8 ;emulate far call to same segment. dw proc-($+2) ;offset. endm ; -------------------------------------------------------- callPacket macro message local nextCode callcount = callcount + 1 ;an audit artifact. call messageToAux dw text : nextCode db 'Error while ' db '&message' db 0DH, 0A, 0 nextCode label near endm ; -------------------------------------------------------- dos macro callid, message local no_error mov ax, callid int 21 jnc no_error callPacket <message> nop ;No corrective action taken. no_error: endm ; -------------------------------------------------------- enter macro size,levels db 0c8 ;push bp dw size ;mov bp, sp db levels ;nop -- other features not used today. endm leave macro db 0c9 ;pop bp endm ; -------------------------------------------------------- code segment public para 'rom' assume cs:text, ds:nothing test: string label byte ;no extent: zero length string. adr dd string lds dx, adr mov cx, string-adr mov ax, 4000 int 21 endtest: temp = endtest - test test2: call messageToAux ;push cs; call near is better. dw text : morecode string2 label byte ;no extent: zero length string. morecode: endtest2: temp2 = endtest2-test2 advantage = temp-temp2 overhead = messageToAuxEnd - messageToAux increment = callcount * advantage total = increment - overhead callcount = 1 code ends ; -------------------------------------------------------- ; -------------------------------------------------------- code segment public para 'rom' assume cs:text, ds:nothing public messageToAux messageToAux proc far enter 0,0 ;NEC v-20 & .286 -- manage bp, sp. lds si, [bp].2 ;load ptr to word following call. cld lodsw ;load return address, inc si,2. mov [bp].2, ax ;replace return address on stack. sub ax, si ;form string size in bytes to ax. mov cx, ax mov dx, si mov ax, 4000 mov bx,2 ;handle for std error int 21 leave ret ;jmp stop messageToAuxEnd: messageToAux endp start: mov ax, seg data mov es, ax assume es: data open: ;This opens your existing long, scratch file. lds dx, nameptr dos 3d02, <Open named file> mov handle, ax position0: ;Then the file is positioned midway through. mov bx, handle mov cx,0 mov dx,1000 dos 4200, <Move file pointer> trunc: ;A zero length record is written. mov bx, handle mov cx,0 lds dx,bufptr dos 4000, <Write zero length record.> ; -------------------------------------------------------- ; This is the deal: ; A read beyond the clip point returns no bytes read. i.e. ; the file has been logically truncated. However, no disk ; activity has occured. ; ; If the file is closed now, the size is diminished, and the ; FAT is adjusted. ; ; If I do a dup handle, the close on the duped handle, this ; happens: ; There is disk activity, and the relinquished space is recovered. ; The original handle is still valid, open, and clipped. I can ; read data from it just fine. ; -------------------------------------------------------- dupHandle: mov bx, handle dos 4500, <Duplicate file handle.> close0: mov bx, ax ;use new handle. dos 3e00, <Close duplicate file> position1: mov bx, handle mov cx,0 mov dx,1010 dos 4200, <Move file pointer> read: ; This will report 0 bytes read, but carry is clear. mov bx, handle mov cx, 1 ;bytes to read. lds dx,bufptr dos 3f00, <Read a byte past implied clip point. Should fail.> close: mov bx, handle dos 3e00, <Close file> stop: mov ax, 4c00 int 21 code ends end start -- Aubrey McIntosh Freelance using Modula-2 Real time, embedded, instruments. Austin, TX 78723 Enquiries welcome 1-(512)-452-1540 aubrey%rpp386.Cactus.org@cs.utexas.edu