Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chriss Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Error: "Internal (system software bug" 1

Status
Not open for further replies.

Alt255

Programmer
May 14, 1999
1,846
US
I don't ask a lot of questions in this forum so I'm really hoping that somebody can help me out here.
My program requires that a number of files are copied from one directory to another. I wanted to keep the headroom low so I decided not to shell to the command interpreter and perform a COPY (please, don't suggest that I try this). I also wanted to avoid wasting valuable program string space by setting up a file buffer and performing the copy with Qbasic GET and PUT statements, so I ended up calling the DOS services with Interrupt &H21 and using the last seven pages of display memory as a file buffer (hey, if it's just sitting there wasting space, why not put it to use?)

The problem is that the FileCopy sub errors out after copying thirteen files (not twelve, not fourteen, exactly thirteen no matter what the files are, how large the files are or where the files are located).... A bit perplexing, to say the least. maybe somebody will have a heart and take a look at the following code. Sorry about the length!

BTW: I'm trying to run this under Windows XP. :)

' $DYNAMIC
DEFINT A-Z
TYPE RegTypeX
AX AS INTEGER
BX AS INTEGER
CX AS INTEGER
DX AS INTEGER
bp AS INTEGER
si AS INTEGER
DI AS INTEGER
flags AS INTEGER
DS AS INTEGER
ES AS INTEGER
END TYPE
DIM InRegs AS RegTypeX, OutRegs AS RegTypeX
DECLARE FUNCTION Dir$ (FileSpec$, Attrib)
DECLARE FUNCTION IsDirectory (FileSpec$)
DECLARE FUNCTION OpenFile (FileSpec$, AccessType)
DECLARE FUNCTION CloseFile (FileHandle)
DECLARE FUNCTION CreateFile (FileSpec$)
DECLARE SUB PUTfile (FileHandle, BufferSeg, BufferOffset, BufferLen)
DECLARE SUB GETfile (FileHandle, BufferSeg, BufferOffset, BufferLen)
DECLARE SUB FileCopy (Source$, Destination$)
DECLARE SUB GetDTAaddress ()
DECLARE FUNCTION ParsedFileName$ (FileSpec$)
DECLARE FUNCTION ParsedDirName$ (FileSpec$)
DECLARE FUNCTION AccessType$ (Acc)
DECLARE FUNCTION FileSize& ()
DECLARE SUB SaveVideo ()
DECLARE SUB RestoreVideo ()
' SegDTA and OffDTA are the
' Disk Transfer Area addresses
' returned by SUB GetDTAaddress

DIM SHARED SegDTA
DIM SHARED OffDTA
CONST ReadAccess = 0
CONST WriteAccess = 1
DIM SHARED ErrorCode

CLS
PRINT "I've written to various display pages and I"
PRINT "don't want to lose the information during the"
PRINT "file copy, so I save all eight pages to a file"
PRINT "so I can restore them right away."
SaveVideo

' I want to copy all BAS files from "c:\testqb\SorceDir"
' to "c:\testqb\DestnDir", so I....

Source$ = "c:\testqb\SorceDir\*.BAS"
Destination$ = "c:\testqb\DestnDir\"
CALL FileCopy(Source$, Destination$)
IF ErrorCode <> 0 THEN
PRINT &quot;Error code: &quot;; HEX$(ErrorCode)
PRINT &quot;Tap a key...&quot;
DO WHILE INKEY$ = &quot;&quot;
LOOP
END IF
RestoreVideo

'================ VARIOUS SUBS AND FUNCTIONS FOLLOW ===================
REM $STATIC
FUNCTION AccessType$ (Acc)
SELECT CASE Acc
CASE ReadAccess
AccessType$ = &quot;READ&quot;
CASE WriteAccess
AccessType$ = &quot;WRITE&quot;
END SELECT
END FUNCTION
'======================================================================
FUNCTION CloseFile (FileHandle)
DIM InRegs AS RegTypeX, OutRegs AS RegTypeX
InRegs.AX = &H3E00
InRegs.BX = FileHandle
CALL InterruptX(&H21, InRegs, OutRegs)
IF OutRegs.flags AND 1 THEN
PRINT &quot;Error closing file #&quot;; FileHandle
ErrorCode = OutRegs.AX
END IF
END FUNCTION
'======================================================================
FUNCTION CreateFile (FileSpec$)
DIM InRegs AS RegTypeX, OutRegs AS RegTypeX
' MyFile$ contains the name of the file we want to create.
MyFile$ = FileSpec$ + CHR$(0)
InRegs.AX = &H3C00
InRegs.CX = &H20
InRegs.DS = VARSEG(MyFile$)
InRegs.DX = SADD(MyFile$)
' Create an empty file

CALL InterruptX(&H21, InRegs, OutRegs)
' Function returns the file handle, unless there was an error :)

IF OutRegs.flags AND 1 THEN
PRINT &quot;Error creating &quot;; FileSpec$
ErrorCode = OutRegs.AX
ELSE
CreateFile = OutRegs.AX
END IF
END FUNCTION
'======================================================================
FUNCTION Dir$ (FileSpec$, Attrib)
' This function works a little like VB's Dir$:
' The first call to Dir$(&quot;*.BAS&quot;,0) returns the first directory
' listing for a BAS file and subsequent calls return the remaining BAS files.
' We know that we have retrieved the last file when Dir$ returns a NUL string.
' The difference with VB's Dir$ is that VB doesn't require you to pass arguments
' after the first call.

DIM InRegs AS RegTypeX, OutRegs AS RegTypeX
STATIC OldSpec$
IF OldSpec$ <> FileSpec$ THEN
' This is the first search for &quot;FileSpec&quot;
' so call DOS &quot;First Matching File&quot;

OldSpec$ = FileSpec$
GetDTAaddress
NameFile$ = FileSpec$ + CHR$(0)
InRegs.DS = VARSEG(NameFile$)
InRegs.DX = SADD(NameFile$)
InRegs.CX = Attrib
InRegs.AX = &H4E00
CALL InterruptX(&H21, InRegs, OutRegs)
IF OutRegs.flags AND 1 THEN
Dir$ = &quot;&quot;
EXIT FUNCTION
END IF
Dir$ = ParsedFileName$(FileSpec$)
ELSE
' Call DOS &quot;Next Matching File&quot;
GetDTAaddress
InRegs.AX = &H4F00
CALL InterruptX(&H21, InRegs, OutRegs)
IF OutRegs.flags AND 1 THEN
FirstFile = 0
Dir$ = &quot;&quot;
EXIT FUNCTION
END IF
Dir$ = ParsedFileName$(FileSpec$)
END IF
END FUNCTION
'======================================================================
SUB FileCopy (Source$, Destination$)
' I've set up this file copy routine to expect a directory
' name as the destination for the copy.

DestDir$ = ParsedDirName$(Destination$)
' Check to see if the destination is a directory.

IF NOT IsDirectory(DestDir$) THEN
PRINT Destination$; &quot; is not a directory.&quot;
EXIT SUB
END IF
SourceDir$ = ParsedDirName$(Source$)
IF NOT IsDirectory(DestDir$) THEN
PRINT SourceDir$; &quot; is not a directory.&quot;
EXIT SUB
END IF
' Get the first file that matches the Source$ file spec
Fi$ = Dir$(Source$, 0)
IF Fi$ = &quot;&quot; THEN
PRINT Source$; &quot; does not exist.&quot;
EXIT SUB
ELSE
' Process the first file
' and then get the rest of them.

DO WHILE Fi$ <> &quot;&quot;
RemainingBytes& = FileSize&
PRINT Fi$; RemainingBytes&; &quot; bytes&quot;
SourceFile$ = SourceDir$ + &quot;\&quot; + Fi$
SourceHandle = OpenFile(SourceFile$, ReadAccess)
IF SourceHandle = 0 THEN EXIT SUB
DestinationFile$ = DestDir$ + &quot;\&quot; + Fi$
DestHandle = CreateFile(DestinationFile$)
IF ErrorCode <> 0 THEN
Dummy = CloseFile(SourceHandle)
EXIT SUB
END IF
'
'Here's where the error occurs. The sub copies thirteen files flawlessly....
'Then when it tries to open the fourteenth file for write-access (this is a file
'that it just created with the CreateFile function) it bombs out with error number &H4,
'which my copy of The Big Book of Miscellaneous Disk Operating System Errors
'refers to as &quot;Internal (system software bug)&quot;. Whatever THAT means.
'

DestHandle = OpenFile(DestinationFile$, WriteAccess)
IF ErrorCode <> 0 THEN
Dummy = CloseFile(SourceHandle)
EXIT SUB
END IF
DO WHILE RemainingBytes& > 0
' &H7000 (28672) bytes will be the largest chunk of display
' memory we'll use (the last seven pages) for a file buffer.

IF RemainingBytes& > &H7000 THEN
ThisChunk = &H7000
ELSE
ThisChunk = RemainingBytes&
END IF
RemainingBytes& = RemainingBytes& - ThisChunk
GETfile SourceHandle, &HB800, &H1000, ThisChunk
PUTfile DestHandle, &HB800, &H1000, ThisChunk
LOOP
' Close both the original and copied files

Dummy = CloseFile(SourceHandle)
IF ErrorCode <> 0 THEN EXIT SUB
Dummy = CloseFile(DestHandle)
IF ErrorCode <> 0 THEN EXIT SUB
' Get another Source File,
' until all have been copied.

Fi$ = Dir$(Source$, 0)
LOOP
END IF
END SUB
'======================================================================
FUNCTION FileSize&
' This is only set up to work for the file
' currently populating the Disk Transfer Area

DEF SEG = SegDTA
T1& = (PEEK(OffDTA + 26))
T2& = (PEEK(OffDTA + 27)) * &H100
T3& = (PEEK(OffDTA + 28)) * &H10000
T4& = (PEEK(OffDTA + 29)) * &H1000
DEF SEG
FileSize& = T1& + T2& + T3& + T4&
END FUNCTION
'======================================================================
SUB GetDTAaddress
' Get the current segment and offset
' of the Disk Transfer Area

DIM InRegs AS RegTypeX, OutRegs AS RegTypeX
InRegs.AX = &H2F00
CALL InterruptX(&H21, InRegs, OutRegs)
SegDTA = OutRegs.ES
OffDTA = OutRegs.BX
END SUB
'======================================================================
SUB GETfile (FileHandle, BufferSeg, BufferOffset, BufferLen)
' This just reads the specified number of bytes from
' the current position of an open file and places them
' in memory at the specified segment and offset.

DIM InRegs AS RegTypeX, OutRegs AS RegTypeX
InRegs.AX = &H3F00
InRegs.BX = FileHandle
InRegs.CX = BufferLen
InRegs.DS = BufferSeg
InRegs.DX = BufferOffset
CALL InterruptX(&H21, InRegs, OutRegs)
IF OutRegs.flags AND 1 THEN
ErrorCode = OutRegs.AX
END IF
' Bytes actually read are returned in OutRegs.AX
' unless there is an error.

END SUB
'======================================================================
FUNCTION IsDirectory (FileSpec$)
' This function checks the DTA to see if the
' entry for FileSpec$ has an attribute of &H10,
' marking it as a directory. We do this because
' FirstMatchingFile and NextMatchingFile return ALL
' files (not just directories) when you pass the
' directory attribute as a parameter.

DIM InRegs AS RegTypeX, OutRegs AS RegTypeX
GetDTAaddress
NameFile$ = FileSpec$ + CHR$(0)
InRegs.DS = VARSEG(NameFile$)
InRegs.DX = SADD(NameFile$)
InRegs.CX = &H10
InRegs.AX = &H4E00
CALL InterruptX(&H21, InRegs, OutRegs)
IF OutRegs.flags AND 1 THEN
' FileSpec$ not found,
' therefore NOT directory

IsDirectory = 0
EXIT FUNCTION
END IF
DEF SEG = SegDTA
FlAttrib = PEEK(OffDTA + 21)
DEF SEG
IF FlAttrib = &H10 THEN
IsDirectory = -1
ELSE
IsDirectory = 0
END IF
END FUNCTION
'======================================================================
FUNCTION OpenFile (FileSpec$, AccessType)
DIM InRegs AS RegTypeX, OutRegs AS RegTypeX
MyFile$ = FileSpec$ + CHR$(0)
InRegs.AX = &H3D00 + AccessType
InRegs.DS = VARSEG(MyFile$)
InRegs.DX = SADD(MyFile$)
CALL InterruptX(&H21, InRegs, OutRegs)
' Open the file....
' Function returns the file handle, unless there was an error :)

IF OutRegs.flags AND 1 THEN
PRINT &quot;Error opening &quot;; FileSpec$; &quot; for &quot;; AccessType$(AccessType)
ErrorCode = OutRegs.AX
ELSE
OpenFile = OutRegs.AX
END IF
END FUNCTION
'======================================================================
FUNCTION ParsedDirName$ (FileSpec$)
IF INSTR(FileSpec$, &quot;\&quot;) < 1 THEN EXIT FUNCTION
IF LEN(FileSpec$) < 1 THEN EXIT FUNCTION
FOR Re = LEN(FileSpec$) TO 1 STEP -1
IF Fs > 0 THEN Tmp$ = MID$(FileSpec$, Re, 1) + Tmp$
IF MID$(FileSpec$, Re, 1) = &quot;\&quot; THEN Fs = Fs + 1
NEXT
ParsedDirName$ = Tmp$
END FUNCTION
'======================================================================
FUNCTION ParsedFileName$ (FileSpec$)
DEF SEG = SegDTA
OffMatch = OffDTA + 29
Match$ = &quot;&quot;
FOR I = 1 TO 13
c$ = CHR$(PEEK(OffMatch + I))
IF c$ = CHR$(0) THEN EXIT FOR
Match$ = Match$ + c$
NEXT
DEF SEG
ParsedFileName$ = Match$
END FUNCTION
'======================================================================
SUB PUTfile (FileHandle, BufferSeg, BufferOffset, BufferLen)
' This just takes the memory contents specified by
' &quot;BufferSeg&quot; and &quot;BufferOffset&quot; and writes the specified
' number of bytes to the current position of an open file.

DIM InRegs AS RegTypeX, OutRegs AS RegTypeX
InRegs.AX = &H4000
InRegs.BX = FileHandle
InRegs.CX = BufferLen
InRegs.DS = BufferSeg
InRegs.DX = BufferOffset
' Write the bytes to the file

CALL InterruptX(&H21, InRegs, OutRegs)
IF OutRegs.flags AND 1 THEN
ErrorCode = OutRegs.AX
END IF
' Bytes actually written are returned in OutRegs.AX
' unless there is an error.

END SUB
'======================================================================
SUB RestoreVideo
DIM InRegs AS RegTypeX, OutRegs AS RegTypeX
' Open the memory image file for READ access

fHandle = OpenFile(&quot;SAVEDVID.MEM&quot;, ReadAccess)
' Read 32768 bytes from the file and place them in RAM
' starting at Segment &HB800, Offset 0

FOR Address& = 0 TO &H7000 STEP &H1000
GETfile fHandle, &HB800, CINT(Address&), &H1000
NEXT
' Close the file

Dummy = CloseFile(fHandle)
END SUB
'======================================================================
SUB SaveVideo
' Create an empty file

fHandle = CreateFile(&quot;SAVEDVID.MEM&quot;)
' Open the file for WRITE access

fHandle = OpenFile(&quot;SAVEDVID.MEM&quot;, WriteAccess)
' Write eight pages of display memory
' starting at Segment &HB800, Offset 0

FOR Address& = 0 TO &H7000 STEP &H1000
PUTfile fHandle, &HB800, CINT(Address&), &H1000
NEXT
' Close the file

Dummy = CloseFile(fHandle)
END SUB
'======================================================================
[/tt]

vcn.gif

Do no harm.​
 
Well my friend...I didn't see all your code (I really hope you didn't expect someone to actually go through all that !!!!)

!!! BUT !!!

If you're using Windows 3.x, 95 or 98 then you should check the CONFIG.SYS file for the following.

FILES= <any number>

If you find a line like that then it means that you DON'T close the file buffers you open for reading thus resulting in trying to open more buffers that it is specified from the OS.

Try to change the line into FILES=255 and be more carefull in your code to actually CLOSE the file buffer you open before opening a new one.

I wish you good luck ..... Let me know how it comes out !!!

ARISTON Engineering Ltd
Michael Vezyrgianopoulos
R&D Manager
 
What os are your using?
&quot;Internal (system software bug)&quot; usually means a glitch in the os, have you played witht he configuration files in any way?

Whenever I get a &quot;Internal (system software bug)&quot; i usually just reboot the computer and it runs fine
 
I'm a little disapointed to see that people don't really read the threads carefully before answering..
BTW: I'm trying to run this under Windows XP. :)
 
Thank you, Oak.

Actually... the OS doesn't seem to be the issue here. I ran the code under Win98 and had identical results. I even played a bit with the FILES line in CONFIG.SYS... no effect.

I've been a little hesitant to post because it seems that I made a stupid error. When I looked up DOS error &H4 I glanced at the wrong table. The actual meaning is &quot;too many handles&quot;. Embarrasing, to say the least.

But the error adds to my list of questions. Function &H3E seems to be doing its job, closing the files and releasing the handles (any attempt to use the handles after &quot;CloseFile&quot; results in an &quot;Invalid Handle&quot; error... and the files appear to be closed because they can be copied, moved, deleted, etc. from the command line. Obviously, though, this is not the case. I decided to test a hunch by allowing the program to have more open files....
[tt]
' Increase the File Handle Count to 375 (&H177).
' The FILES directive in CONFIG.SYS allows a maximum
' value of 255, whereas Interrupt &h21, Function &h67
' will (hypothetically) allow you to set it to 65536.
' LOL I haven't tried it yet. LOL

InRegs.AX = &H6700
' (allow 375 open files)

InRegs.BX = &H177
CALL InterruptX(&H21, InRegs, OutRegs)
IF OutRegs.flags AND 1 THEN
PRINT &quot;Error setting file handle count&quot;
PRINT &quot;Error: &quot;; HEX$(OutRegs.AX)
SYSTEM
END IF
[/tt]
Can anybody guess the result of making this call?

I ended up copying 35 files instead of 13. What is going on here?

vcn.gif

Do no harm.​
 
Hi,
I didn't have much time to look at your code yet. However, I detected a little something in the FileCopy() sub. I don't think that it can solve the problem but it might help correcting &quot;Not a directory&quot; error handling.

SourceDir$ = ParsedDirName$(Source$)
IF NOT IsDirectory(DestDir$) THEN
PRINT SourceDir$; &quot; is not a directory.&quot;
EXIT SUB
END IF

I'll keep looking at this. Pretty interesting by the way. ;-)
 
Good catch, Oak. It wasn't interfering with program execution... but the display certainly would have been confusing!

I found the problem. The source of the error was pretty stupid (aren't they all?) I noticed, by tracing the open handles, that the program wasn't having a problem opening and closing the handles for the source files (the handle was always &H5). But the handles for the destination files incremented until they reached the limit of the open file table provided by the OS, then looped back to &H4. Since the OS reserves the first five handles (&H0 - &H4) for the standard devices (input, output, error, auxiliary and printer) this just could not happen without creating a problem.

If I had simply looked at the line following my original comment that &quot;This is were the error occurs...&quot;, I would have saved myself a bit of grief. I was creating a file and then opening it for WRITE access, thereby placing two open file handles in the table. It seems that Interrupt &H21, function &H3C opens files for ANY access and provides a valid handle.

As I said... pretty stupid. I guess I could have corrected my code and hoped that this Tek-Tips thread would just disappear... but that wouldn't be very nice. I'm posting the corrected code in its entirety, in case somebody can find a use for parts of it.

' $DYNAMIC
DEFINT A-Z
TYPE RegTypeX
AX AS INTEGER
BX AS INTEGER
CX AS INTEGER
DX AS INTEGER
bp AS INTEGER
si AS INTEGER
DI AS INTEGER
flags AS INTEGER
DS AS INTEGER
ES AS INTEGER
END TYPE
DIM InRegs AS RegTypeX, OutRegs AS RegTypeX
DECLARE FUNCTION Dir$ (FileSpec$, Attrib)
DECLARE FUNCTION IsDirectory (FileSpec$)
DECLARE FUNCTION OpenFile (FileSpec$, AccessNumber)
DECLARE FUNCTION CloseFile (FileHandle)
DECLARE FUNCTION CreateFile (FileSpec$)
DECLARE SUB PUTfile (FileHandle, BufferSeg, BufferOffset, BufferLen)
DECLARE SUB GETfile (FileHandle, BufferSeg, BufferOffset, BufferLen)
DECLARE SUB FileCopy (Source$, Destination$)
DECLARE SUB GetDTAaddress ()
DECLARE FUNCTION ParsedFileName$ (FileSpec$)
DECLARE FUNCTION ParsedDirName$ (FileSpec$)
DECLARE FUNCTION AccessType$ (Acc)
DECLARE FUNCTION FileSize& ()
DECLARE SUB SaveVideo ()
DECLARE SUB RestoreVideo () '
' SegDTA and OffDTA are the
' Disk Transfer Area addresses
' returned by SUB GetDTAaddress'

DIM SHARED SegDTA
DIM SHARED OffDTA
CONST ReadAccess = 0
CONST WriteAccess = 1
DIM SHARED ErrorCode

CLS
PRINT &quot;I've written to various display pages and I&quot;
PRINT &quot;don't want to lose the information during the&quot;
PRINT &quot;file copy, so I save all eight pages to a file&quot;
PRINT &quot;so I can restore them right away.&quot;
SaveVideo
'
' I want to copy all BAS files from &quot;c:\testqb\SorceDir&quot;
' to &quot;c:\testqb\DestnDir&quot;, so I....'

Source$ = &quot;c:\testqb\SorceDir\*.BAS&quot;
Destination$ = &quot;c:\testqb\DestnDir\&quot;
CALL FileCopy(Source$, Destination$)
IF ErrorCode <> 0 THEN
PRINT &quot;Error code: &quot;; HEX$(ErrorCode)
PRINT &quot;Tap a key...&quot;
DO WHILE INKEY$ = &quot;&quot;
LOOP
END IF
RestoreVideo
''======================================================================'
'[/tt]

''================ VARIOUS SUBS AND FUNCTIONS FOLLOW ==================='
REM $STATIC
FUNCTION AccessType$ (Acc)
SELECT CASE Acc
CASE ReadAccess
AccessType$ = &quot;READ&quot;
CASE WriteAccess
AccessType$ = &quot;WRITE&quot;
END SELECT
END FUNCTION

''======================================================================'
FUNCTION CloseFile (FileHandle)
DIM InRegs AS RegTypeX, OutRegs AS RegTypeX
InRegs.AX = &H3E00
InRegs.BX = FileHandle
CALL InterruptX(&H21, InRegs, OutRegs)
IF OutRegs.flags AND 1 THEN
PRINT &quot;Error closing file #&quot;; FileHandle
ErrorCode = OutRegs.AX
END IF
END FUNCTION

''======================================================================'
FUNCTION CreateFile (FileSpec$)
DIM InRegs AS RegTypeX, OutRegs AS RegTypeX
' MyFile$ contains the name of the file we want to create.
MyFile$ = FileSpec$ + CHR$(0)
InRegs.AX = &H3C00
InRegs.CX = &H20
InRegs.DS = VARSEG(MyFile$)
InRegs.DX = SADD(MyFile$)'
' Create an empty file'

CALL InterruptX(&H21, InRegs, OutRegs)'
' Function returns the file handle, unless there was an error :)'

IF OutRegs.flags AND 1 THEN
PRINT &quot;Error creating &quot;; FileSpec$
ErrorCode = OutRegs.AX
ELSE
CreateFile = OutRegs.AX
END IF
END FUNCTION

''======================================================================'
FUNCTION Dir$ (FileSpec$, Attrib) '
' This function works a little like VB's Dir$:
' The first call to Dir$(&quot;*.BAS&quot;,0) returns the first directory
' listing for a BAS file and subsequent calls return the remaining BAS files.
' We know that we have retrieved the last file when Dir$ returns a NUL string.
' The difference with VB's Dir$ is that VB doesn't require you to pass arguments
' after the first call.'

DIM InRegs AS RegTypeX, OutRegs AS RegTypeX
STATIC OldSpec$
IF OldSpec$ <> FileSpec$ THEN '
' This is the first search for &quot;FileSpec&quot;
' so call DOS &quot;First Matching File&quot;'

OldSpec$ = FileSpec$
GetDTAaddress
NameFile$ = FileSpec$ + CHR$(0)
InRegs.DS = VARSEG(NameFile$)
InRegs.DX = SADD(NameFile$)
InRegs.CX = Attrib
InRegs.AX = &H4E00
CALL InterruptX(&H21, InRegs, OutRegs)
IF OutRegs.flags AND 1 THEN
Dir$ = &quot;&quot;
EXIT FUNCTION
END IF
Dir$ = ParsedFileName$(FileSpec$)
ELSE
' Call DOS &quot;Next Matching File&quot;
GetDTAaddress
InRegs.AX = &H4F00
CALL InterruptX(&H21, InRegs, OutRegs)
IF OutRegs.flags AND 1 THEN
FirstFile = 0
Dir$ = &quot;&quot;
EXIT FUNCTION
END IF
Dir$ = ParsedFileName$(FileSpec$)
END IF
END FUNCTION

''======================================================================'
SUB FileCopy (Source$, Destination$) '
' I've set up this file copy routine to expect a directory
' name as the destination for the copy.'

DestDir$ = ParsedDirName$(Destination$)'
' Check to see if the destination is a directory.'

IF NOT IsDirectory(DestDir$) THEN
PRINT Destination$; &quot; is not a directory.&quot;
EXIT SUB
END IF
SourceDir$ = ParsedDirName$(Source$)'
' The following change, courtesy of
OAK
'''''''IF NOT IsDirectory(DestDir$) THEN'

IF NOT IsDirectory(SourceDir$) THEN
PRINT SourceDir$; &quot; is not a directory.&quot;
EXIT SUB
END IF
' Get the first file that matches the Source$ file spec
Fi$ = Dir$(Source$, 0)
IF Fi$ = &quot;&quot; THEN
PRINT Source$; &quot; does not exist.&quot;
EXIT SUB
ELSE '
' Process the first file
' and then get the rest of them.'

DO WHILE Fi$ <> &quot;&quot;
RemainingBytes& = FileSize&
PRINT Fi$; RemainingBytes&; &quot; bytes&quot;
SourceFile$ = SourceDir$ + &quot;\&quot; + Fi$
SourceHandle = OpenFile(SourceFile$, ReadAccess)
IF SourceHandle = 0 THEN EXIT SUB
DestinationFile$ = DestDir$ + &quot;\&quot; + Fi$
DestHandle = CreateFile(DestinationFile$)
IF ErrorCode <> 0 THEN
Dummy = CloseFile(SourceHandle)
EXIT SUB
END IF
DO WHILE RemainingBytes& > 0'
' &H7000 (28672) bytes will be the largest chunk of display
' memory we'll use (the last seven pages) for a file buffer.'

IF RemainingBytes& > &H7000 THEN
ThisChunk = &H7000
ELSE
ThisChunk = RemainingBytes&
END IF
RemainingBytes& = RemainingBytes& - ThisChunk
GETfile SourceHandle, &HB800, &H1000, ThisChunk
PUTfile DestHandle, &HB800, &H1000, ThisChunk
LOOP'
' Close both the original and copied files'

Dummy = CloseFile(SourceHandle)
IF ErrorCode <> 0 THEN EXIT SUB
Dummy = CloseFile(DestHandle)
IF ErrorCode <> 0 THEN EXIT SUB'
' Get another Source File,
' until all have been copied.'

Fi$ = Dir$(Source$, 0)
LOOP
END IF
END SUB

''======================================================================'
FUNCTION FileSize& '
' This is only set up to work for the file
' currently populating the Disk Transfer Area'

DEF SEG = SegDTA
T1& = (PEEK(OffDTA + 26))
T2& = (PEEK(OffDTA + 27)) * 256&
T3& = (PEEK(OffDTA + 28)) * &H10000
T4& = (PEEK(OffDTA + 29)) * &H1000
DEF SEG
FileSize& = T1& + T2& + T3& + T4&
END FUNCTION

''======================================================================'
SUB GetDTAaddress '
' Get the current segment and offset
' of the Disk Transfer Area'

DIM InRegs AS RegTypeX, OutRegs AS RegTypeX
InRegs.AX = &H2F00
CALL InterruptX(&H21, InRegs, OutRegs)
SegDTA = OutRegs.ES
OffDTA = OutRegs.BX
END SUB

''======================================================================'
SUB GETfile (FileHandle, BufferSeg, BufferOffset, BufferLen) '
' This just reads the specified number of bytes from
' the current position of an open file and places them
' in memory at the specified segment and offset.'

DIM InRegs AS RegTypeX, OutRegs AS RegTypeX
InRegs.AX = &H3F00
InRegs.BX = FileHandle
InRegs.CX = BufferLen
InRegs.DS = BufferSeg
InRegs.DX = BufferOffset
CALL InterruptX(&H21, InRegs, OutRegs)
IF OutRegs.flags AND 1 THEN
ErrorCode = OutRegs.AX
END IF'
' Bytes actually read are returned in OutRegs.AX
' unless there is an error.'

END SUB

''======================================================================'
FUNCTION IsDirectory (FileSpec$) '
' This function checks the DTA to see if the
' entry for FileSpec$ has an attribute of &H10,
' marking it as a directory. We do this because
' FirstMatchingFile and NextMatchingFile return ALL
' files (not just directories) when you pass the
' directory attribute as a parameter.'

DIM InRegs AS RegTypeX, OutRegs AS RegTypeX
GetDTAaddress
NameFile$ = FileSpec$ + CHR$(0)
InRegs.DS = VARSEG(NameFile$)
InRegs.DX = SADD(NameFile$)
InRegs.CX = &H10
InRegs.AX = &H4E00
CALL InterruptX(&H21, InRegs, OutRegs)
IF OutRegs.flags AND 1 THEN '
' FileSpec$ not found,
' therefore NOT directory'

IsDirectory = 0
EXIT FUNCTION
END IF
DEF SEG = SegDTA
FlAttrib = PEEK(OffDTA + 21)
DEF SEG
IF FlAttrib = &H10 THEN
IsDirectory = -1
ELSE
IsDirectory = 0
END IF
END FUNCTION

''======================================================================'
FUNCTION OpenFile (FileSpec$, AccessNumber)
DIM InRegs AS RegTypeX, OutRegs AS RegTypeX
MyFile$ = FileSpec$ + CHR$(0)
InRegs.AX = &H3D00 + AccessNumber
InRegs.DS = VARSEG(MyFile$)
InRegs.DX = SADD(MyFile$)
CALL InterruptX(&H21, InRegs, OutRegs)'
' Open the file....
' Function returns the file handle, unless there was an error :)'

IF OutRegs.flags AND 1 THEN
PRINT &quot;Error opening &quot;; FileSpec$; &quot; for &quot;; AccessType$(AccessNumber)
ErrorCode = OutRegs.AX
ELSE
OpenFile = OutRegs.AX
END IF
END FUNCTION

''======================================================================'
FUNCTION ParsedDirName$ (FileSpec$)
IF INSTR(FileSpec$, &quot;\&quot;) < 1 THEN EXIT FUNCTION
IF LEN(FileSpec$) < 1 THEN EXIT FUNCTION
FOR Re = LEN(FileSpec$) TO 1 STEP -1
IF Fs > 0 THEN Tmp$ = MID$(FileSpec$, Re, 1) + Tmp$
IF MID$(FileSpec$, Re, 1) = &quot;\&quot; THEN Fs = Fs + 1
NEXT
ParsedDirName$ = Tmp$
END FUNCTION

''======================================================================'
FUNCTION ParsedFileName$ (FileSpec$)
DEF SEG = SegDTA
OffMatch = OffDTA + 29
Match$ = &quot;&quot;
FOR I = 1 TO 13
c$ = CHR$(PEEK(OffMatch + I))
IF c$ = CHR$(0) THEN EXIT FOR
Match$ = Match$ + c$
NEXT
DEF SEG
ParsedFileName$ = Match$
END FUNCTION

''======================================================================'
SUB PUTfile (FileHandle, BufferSeg, BufferOffset, BufferLen) '
' This just takes the memory contents specified by
' &quot;BufferSeg&quot; and &quot;BufferOffset&quot; and writes the specified
' number of bytes to the current position of an open file.'

DIM InRegs AS RegTypeX, OutRegs AS RegTypeX
InRegs.AX = &H4000
InRegs.BX = FileHandle
InRegs.CX = BufferLen
InRegs.DS = BufferSeg
InRegs.DX = BufferOffset'
' Write the bytes to the file'

CALL InterruptX(&H21, InRegs, OutRegs)
IF OutRegs.flags AND 1 THEN
ErrorCode = OutRegs.AX
END IF'
' Bytes actually written are returned in OutRegs.AX
' unless there is an error.'

END SUB

''======================================================================'
SUB RestoreVideo
DIM InRegs AS RegTypeX, OutRegs AS RegTypeX'
' Open the memory image file for READ access'

fHandle = OpenFile(&quot;SAVEDVID.MEM&quot;, ReadAccess)'
' Read 32768 bytes from the file and place them in RAM
' starting at Segment &HB800, Offset 0'

FOR Address& = 0 TO &H7000 STEP &H1000
GETfile fHandle, &HB800, CINT(Address&), &H1000
NEXT'
' Close the file'

Dummy = CloseFile(fHandle)
END SUB

''======================================================================'
SUB SaveVideo '
' Create an empty file'

fHandle = CreateFile(&quot;SAVEDVID.MEM&quot;)'
' Open the file for WRITE access'

fHandle = OpenFile(&quot;SAVEDVID.MEM&quot;, WriteAccess)'
' Write eight pages of display memory
' starting at Segment &HB800, Offset 0'

FOR Address& = 0 TO &H7000 STEP &H1000
PUTfile fHandle, &HB800, CINT(Address&), &H1000
NEXT'
' Close the file'

Dummy = CloseFile(fHandle)
END SUB
[/tt]

Now I guess my task should be to optimize the code for speed. Does anybody know how to hook into the Windows file caching scheme? (LOL) Or would I be better off not using video memory for a disk buffer and, instead, allocate a significant chunk of conventional memory outside of Qbasic space?


vcn.gif

Do no harm.​
 
Hmmm; could you allocate a chunk of EMS/XMS instead of conventional memory....on modern wywstems with 128MB-1GB or more of EMS/XMS memory, there should be plenty of room fow a buffer floating around in there.

-Just a though ;-) -Robherc
robherc@hotmail.com
(Anybody remember me? ;-)
 
Yikes! I'm not sure that the use of XMS would help to speed up the process. The sweet thing about the Interrupt &H21 file functions is that they read and write from and to physical memory address with no questions asked. An XMS driver can be asked to allocate a chunk of extended memory but it returns a handle to the block rather than a physical address. The only way the Int&H21 functions could use it would be by modifying an area of conventional memory, copying it to entended memory, and then copying it back to conventional memory to use it again. Ext mem would be great for storage but Int&H21 can't use it in any direct way.

This is more in the way of what I had in mind. It ceates a file from the first &H1000 bytes of memory (so we have a known file with a known size to start with). Then it tries to allocate &H1000 bytes of conventional memory to use as a file buffer, reads the first file into the allocated buffer and then writes it to a separate file.
[tt]
' $DYNAMIC
DEFINT A-Z
TYPE RegTypeX
AX AS INTEGER
BX AS INTEGER
CX AS INTEGER
DX AS INTEGER
bp AS INTEGER
si AS INTEGER
DI AS INTEGER
flags AS INTEGER
DS AS INTEGER
ES AS INTEGER
END TYPE
DIM InRegs AS RegTypeX, OutRegs AS RegTypeX

' Create an empty file

MemFile1$ = &quot;DUMP.MEM&quot; + CHR$(0)
InRegs.AX = &H3C00
InRegs.CX = &H20
InRegs.DS = VARSEG(MemFile1$)
InRegs.DX = SADD(MemFile1$)
CALL InterruptX(&H21, InRegs, OutRegs)
IF OutRegs.flags AND 1 THEN
PRINT &quot;Error creating&quot;: SYSTEM
ELSE
FileHandle = OutRegs.AX
END IF

' Write the first &H1000 bytes of memory to it

InRegs.AX = &H4000
InRegs.BX = FileHandle
InRegs.CX = &H1000
InRegs.DS = 0 'Segment 0
InRegs.DX = 0 'Offset 0
CALL InterruptX(&H21, InRegs, OutRegs)

' Close the file

InRegs.AX = &H3E00
InRegs.BX = FileHandle
CALL InterruptX(&H21, InRegs, OutRegs)

' Try to allocate &H100 paragraphs of memory
' (That's 4096 bytes that QB won't have to manage)

MemSize = &H1000
InRegs.AX = &H4800
InRegs.BX = MemSize \ &H10
CALL InterruptX(&H21, InRegs, OutRegs)
IF OutRegs.flags AND 1 THEN
MemSize = OutRegs.BX * &H10
END IF
StartSeg = OutRegs.AX
IF StartSeg < &H10 THEN
IF StartSeg = &H8 THEN
' Couldn't allocate all of the requested memory,
' so allocate the block that function &H48 says is free.

InRegs.BX = MemSize \ &H10
CALL InterruptX(&H21, InRegs, OutRegs)
StartSeg = OutRegs.AX
ELSE
PRINT &quot;ERROR: &quot;; HEX$(StartSeg)
SYSTEM
END IF
IF OutRegs.flags AND 1 THEN PRINT &quot;No can do&quot;: SYSTEM
END IF
' Open the original file

InFile$ = &quot;DUMP.MEM&quot; + CHR$(0)
InRegs.AX = &H3D00
InRegs.DS = VARSEG(InFile$)
InRegs.DX = SADD(InFile$)
CALL InterruptX(&H21, InRegs, OutRegs)
ReadFileHandle = OutRegs.AX
IF OutRegs.flags AND 1 THEN
PRINT &quot;error opening DUMP.MEM: &quot;; HEX$(OutRegs.AX)
SYSTEM
END IF

' Create an empty file where we
' can copy the first file

OutFile$ = &quot;DUMP-2.MEM&quot; + CHR$(0)
InRegs.AX = &H3C00
InRegs.CX = &H20
InRegs.DS = VARSEG(OutFile$)
InRegs.DX = SADD(OutFile$)
CALL InterruptX(&H21, InRegs, OutRegs)
WriteFileHandle = OutRegs.AX
IF OutRegs.flags AND 1 THEN
PRINT &quot;error creating DUMP-2.MEM: &quot;; HEX$(OutRegs.AX)
SYSTEM
END IF

' Loop through the first file, reading
' the contents in chunks no larger than the
' amount of newly-allocated memory.

RemainingBytes& = &H1000
DO WHILE RemainingBytes& > 0
PRINT MemSize, RemainingBytes&
spin
IF RemainingBytes& > MemSize THEN
ThisChunk = MemSize
ELSE
ThisChunk = RemainingBytes&
END IF
RemainingBytes& = RemainingBytes& - ThisChunk

' Get a chunk of DUMP.MEM and stuff it in the
' memory location we just allocated.

InRegs.AX = &H3F00
InRegs.BX = ReadFileHandle
InRegs.CX = ThisChunk
InRegs.DS = StartSeg
InRegs.DX = 0
CALL InterruptX(&H21, InRegs, OutRegs)

' Read the chunk from memory and
' write it to the new file, DUMP-2.MEM.

InRegs.AX = &H4000
InRegs.BX = WriteFileHandle
InRegs.CX = ThisChunk
InRegs.DS = StartSeg
InRegs.DX = 0
CALL InterruptX(&H21, InRegs, OutRegs)
LOOP

' Close both files

InRegs.AX = &H3E00
InRegs.BX = ReadFileHandle
CALL InterruptX(&H21, InRegs, OutRegs)
InRegs.BX = WriteFileHandle
CALL InterruptX(&H21, InRegs, OutRegs)

' Release the allocated memory

InRegs.AX = &H4900
InRegs.ES = StartSeg
CALL InterruptX(&H21, InRegs, OutRegs)
[/color]
[/tt]
Actually, this strategy might not be quite as efficient as my first program sample, which used a large chunk of video RAM for a file buffer. In this case, function &H48 might not be able to allocate a large, contiguous block and we will be stuck with using a small buffer, with many file reads and writes.

Also, I suspect that the true bottleneck might not lie in getting and putting the data from and into the files.... The real slow-down may come from opening and closing the files.

What are your thoughts.
vcn.gif

Do no harm.​
 
Rob, I don't think any of this stuff is over your head. Calling InterruptX is just the way we use the DOS API in QB. Everything I did in the preceeding code has equivalents in Qbasic... even allocating and releasing memory (say, with DIM and ERASE). The advantage to using functions &H48 and &H49 is that you can use areas of memory outside of QB space.
vcn.gif

Do no harm.​
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top