[alt.sources] Test MS-DOS file truncate, worry with 'lds si,[bp].n'

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