.include "File.inc" .module File ;------------------------------------------------------------------------------- ;@doc:routine ; ; === File.Initialise === ; ; Initialises the file handle record block. ; ;@doc:end ;------------------------------------------------------------------------------- Initialise push hl push de push bc ld hl,Handles ld de,Handles+1 ld bc,Handles.Size-1 ld (hl),0 ldir pop bc pop de pop hl ret SafeChkFindSym push ix push iy ld iy,(TIOS.IY) di .bcall _ChkFindSym im 2 ei pop iy pop ix ret SafeDelVar push iy ld iy,(TIOS.IY) di .bcall _DelVar im 2 ei pop iy ret SafeEnoughMem push iy ld iy,(TIOS.IY) di .bcall _EnoughMem im 2 ei pop iy ret SafeCreateProg push iy ld iy,(TIOS.IY) di .bcall _CreateProg im 2 ei pop iy ret SafeCreateProtProg push iy ld iy,(TIOS.IY) di .bcall _CreateProtProg im 2 ei pop iy ret SafeCreatePict push iy ld iy,(TIOS.IY) di .bcall _CreatePict im 2 ei pop iy ret SafeCreateAppVar push iy ld iy,(TIOS.IY) di .bcall _CreateAppVar im 2 ei pop iy ret SafeEditProg push iy ld iy,(TIOS.IY) di .bcall _EditProg im 2 ei pop iy ret SafeCloseProg push iy ld iy,(TIOS.IY) di .bcall _CloseProg im 2 ei pop iy ret SafeInsertMem push iy ld iy,(TIOS.IY) di .bcall _InsertMem im 2 ei pop iy ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === File.Delete === ; ; Deletes a file. ; ; INPUTS: ; MEMORY ; * OP1 - Name of the file to delete. ; ; OUTPUTS: ; REGISTERS ; * A - Error code. ; * F - Carry reset if the file was deleted. ; ; DESTROYED: ; REGISTERS ; * AF, HL ; ;@doc:end ;------------------------------------------------------------------------------- Delete ld a,(OP1) or a jr nz,+ ld hl,Errors.FileNotFound ld a,214 scf ret + push bc push de push ix call SafeChkFindSym jr c,DelFNF ld a,b or a jr z,+ ld hl,Errors.FileIsLocked ld a,194 scf jr ExitDelete + call SafeDelVar call DirtyHandles or a DelFNF ld hl,Errors.FileNotFound ld a,214 ExitDelete pop ix pop de pop bc ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === File.Create === ; ; Creates a file. ; ; INPUTS: ; MEMORY ; * OP1 - Name of the file to create. ; * HL - Size of the file to create. ; ; OUTPUTS: ; REGISTERS ; * F - Carry set on error. ; * HL - Pointer to error description block on error. ; ; DESTROYED: ; REGISTERS ; * AF, BC, DE, HL, IX ; ;@doc:end ;------------------------------------------------------------------------------- Create call DirtyHandles ; Check in case we're creating a file with a device name. ld a,(OP1) or a jr nz,+ ld hl,Errors.FileExists scf ret ; Check if file already exists or not. + push hl call SafeChkFindSym pop hl jr c,+ ld hl,Errors.FileExists scf ret + ; If it's a picture, make sure we have enough room! ld a,(OP1) cp pictObj jr nz,+ ex de,hl ld hl,756 or a sbc hl,de ld hl,756 jp m,OutOfRoom + ; File doesn't exist. Got enough memory? push hl ld de,32 ; Overhead add hl,de call SafeEnoughMem pop hl jr nc,+ OutOfRoom scf ld hl,Errors.DiskFull ret + ld a,(OP1) ; Now create the variable. cp progObj \ jr nz,+ call SafeCreateProg or a ret + cp protProgObj \ jr nz,+ call SafeCreateProtProg or a ret + cp pictObj \ jr nz,+ call SafeCreatePict inc de inc de ld h,d ld l,e inc de ld (hl),0 ld bc,(12*63)-1 ldir or a ret + cp appVarObj \ jr nz,+ call SafeCreateAppVar or a ret + ld hl,Errors.BadName scf ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === File.ReadRom === ; ; Reads a byte from Flash ROM and increments to point at the next byte. ; ; INPUTS: ; REGISTERS ; * HL - Offset to data on ROM page. ; * B - ROM page number. ; ; OUTPUTS: ; REGISTERS ; * A - Data read from ROM. ; ; DESTROYED: ; REGISTERS ; * F ; ;@doc:end ;------------------------------------------------------------------------------- ReadRom push bc .bcall _LoadCIndPaged ld a,c pop bc ; Runs on to File.IncrementRomPointer ;------------------------------------------------------------------------------- ;@doc:routine ; ; === File.IncrementRomPointer === ; ; Increments a data pointer on a ROM page, automatically wrapping onto the ; next page as appropriate. ; ; INPUTS: ; REGISTERS ; * HL - Offset to data on ROM page. ; * B - ROM page number. ; ; OUTPUTS: ; REGISTERS ; * HL - Offset to next byte of data on ROM page. ; * B - ROM page number of the next byte. ; ; DESTROYED: ; REGISTERS ; * F ; ;@doc:end ;------------------------------------------------------------------------------- IncrementRomPointer ; ROM offset points are valid from $4000..$7FFF. ; If incrementing a ROM pointer that goes from $7FFF->$8000, bit 7 of H will ; be set. We need to set this back to $4000. ; ie: ; $7FFF - %0111.... ........ Before. ; $8000 - %1000.... ........ Wrong! Needs correcting... ; $4000 - %0100.... ........ Correct. inc hl bit 7,h ret z inc b res 7,h set 6,h ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === File.DecrementRomPointer === ; ; Decrements a data pointer on a ROM page, automatically wrapping onto the ; previous page as appropriate. ; ; INPUTS: ; REGISTERS ; * HL - Offset to data on ROM page. ; * B - ROM page number. ; ; OUTPUTS: ; REGISTERS ; * HL - Offset to previous byte of data on ROM page. ; * B - ROM page number of the previous byte. ; ; DESTROYED: ; REGISTERS ; * F ; ;@doc:end ;------------------------------------------------------------------------------- DecrementRomPointer ; ROM offset points are valid from $4000..$7FFF. ; If decrementing a ROM pointer that goes from $4000->$3FFF, bit 6 of H will ; be reset. We need to set this back to $7FFF. ; ie: ; $4000 - %0100.... ........ Before. ; $3FFF - %0011.... ........ Wrong! Needs correcting... ; $7FFF - %0111.... ........ Correct. dec hl bit 6,h ret nz dec b set 6,h ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === File.PopulateHandle === ; ; Populate a file handle record. ; ; INPUTS: ; REGISTERS ; * IX - Pointer to file record. ; MEMORY ; * The handle record must have a name field. ; ; OUTPUTS: ; REGISTERS ; * F - Carry flag set on error. ; * HL - Pointer to error block on error. ; MEMORY ; * The handle record is repopulated; the cursor position is reset to zero ; and any size changes are committed. If you are reopening the file, you ; must reposition the cursor and reset the size fields yourself. ; ; DESTROYED: ; REGISTERS ; * AF ; ;@doc:end ;------------------------------------------------------------------------------- PopulateHandle ; Check if file type = 0 (ie, device file). ld a,(ix+Info.Name) or a jr nz,+ .fcall "Device.Initialise" ret + ; File is not a device file, so we need to open re-open it properly. push hl push de push bc push ix push ix \ pop hl inc hl rst rMOV9ToOP1 call SafeChkFindSym jr nc,+ pop ix pop bc pop de pop hl ld hl,Errors.FileNotFound ret + pop ix ; Check if variable is in ROM. If not, skip the next step to find the start of data. ld a,b or a jr z,ReopenFromRAM ; If file is in ROM, we need to do some more work to find the start of the actual data. ld h,d \ ld l,e ; Skip 10 bytes. ld c,10 - call ReadRom dec c jr nz,- ; A = length of name. ld c,a - call ReadRom dec c jr nz,- ; Now, B = page and HL -> size bytes. ld (ix+Info.VarPage),b ld (ix+Info.VarOffset+0),l \ ld (ix+Info.VarOffset+1),h call ReadRom \ ld e,a call ReadRom \ ld d,a ld (ix+Info.VarSize+0),e \ ld (ix+Info.VarSize+1),d ld (ix+Info.DataSize+0),e \ ld (ix+Info.DataSize+1),d ld (ix+Info.CursorPage),b ld (ix+Info.CursorOffset+0),l \ ld (ix+Info.CursorOffset+1),h jr ReopenedFromROM ReopenFromRAM ; We're re-opening from RAM. This is nice and easy. :) ld h,d \ ld l,e ld (ix+Info.VarPage),b ld (ix+Info.VarOffset+0),l \ ld (ix+Info.VarOffset+1),h ld e,(hl) \ inc hl ld d,(hl) \ inc hl ld (ix+Info.VarSize+0),e \ ld (ix+Info.VarSize+1),d ld (ix+Info.DataSize+0),e \ ld (ix+Info.DataSize+1),d ld (ix+Info.CursorPage),b ld (ix+Info.CursorOffset+0),l \ ld (ix+Info.CursorOffset+1),h ReopenedFromROM ; Finally, reset cursor position. xor a ld (ix+Info.Position+0),a ld (ix+Info.Position+1),a pop bc pop de pop hl or a ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === File.GetFreeHandle === ; ; Gets the first free file handle. ; ; OUTPUTS: ; REGISTERS ; * F - Carry flag set if there are no free handles. ; * IX - Points to handle record. ; * E - Handle id. ; ; DESTROYED: ; REGISTERS ; * AF, DE, HL, IX ; ;@doc:end ;------------------------------------------------------------------------------- GetFreeHandle push hl push bc ld c,0 ld b,MaxHandles ld hl,Handles ld de,Sizeof(Info) - ld a,(hl) and ~State.Dirty jr z,FoundFreeHandle inc c add hl,de djnz - ; No free handles. pop bc pop hl ld hl,Errors.Channel scf ret FoundFreeHandle ; Overwrite info block with zeroes. push bc push hl ld (hl),0 ld d,h ld e,l inc de ld bc,SizeOf(Info)-1 ldir pop ix pop de pop bc pop hl or a ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === File.DirtyHandles === ; ; Marks open files on the TI-OS file system as dirty (so they need to be ; re-opened). ; ;@doc:end ;------------------------------------------------------------------------------- DirtyHandles push hl push de push bc push af ld hl,Handles ld de,SizeOf(Info) ld b,MaxHandles - ld a,(hl) and ~State.Dirty jr z,DirtyNotOpen ; Not open. inc hl ld a,(hl) dec hl or a jr z,+ ; It's a device file; no need to mark as dirty! set 7,(hl) + DirtyNotOpen add hl,de djnz - pop af pop bc pop de pop hl ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === File.ResolveHandle === ; ; Given a handle, returns a pointer to its record block, making sure it's ; up to date. ; ; INPUTS: ; REGISTERS ; * E - Handle to resolve. ; ; OUTPUTS: ; REGISTERS ; * F - Carry flag set on invalid or damaged handle. ; * HL - Pointer to error block. ; * IX - Pointer to handle record block. ; ; DESTROYED: ; REGISTERS ; * AF ; ;@doc:end ;------------------------------------------------------------------------------- ResolveHandle push hl ld hl,Handles ld a,e cp MaxHandles jr nc,ResolveHandleError or a jr z,+ push de push bc ld b,e ld de,SizeOf(Info) - add hl,de djnz - pop bc pop de + push hl pop ix ; IX -> record. ld a,(ix+Info.State) and ~State.Dirty jr nz,+ ; The file isn't open! ResolveHandleError pop hl ld hl,Errors.Channel scf ret + ; The file is open. Is it dirty? bit 7,(ix+Info.State) jr nz,ResolveDirty ; Nope, it's clean, so just return. pop hl or a ret ResolveDirty ; Well, the handle is dirty. ; PopulateHandle completely re-opens the file, so we must back up some statistics first! ld l,(ix+Info.Position+0) \ ld h,(ix+Info.Position+1) push hl ld l,(ix+Info.DataSize+0) \ ld h,(ix+Info.DataSize+1) push hl call PopulateHandle jr nc,Resolved ; Couldn't resolve handle. pop hl pop hl pop hl ; Force-close file. ld (ix+Info.State),State.Closed ld hl,Errors.Channel scf ret Resolved pop hl ; Data size. This one's easy. ld (ix+Info.DataSize+0),l \ ld (ix+Info.DataSize+1),h pop hl ; Position. push bc push de ; Set position. ld (ix+Info.Position+0),l \ ld (ix+Info.Position+1),h ; Now we need to update the cursor position variables. ; Is the variable in RAM? ld a,(ix+Info.CursorPage) or a jr z,ResolveSeekRAM ; Variable is in ROM, which makes resetting its cursor ; a little more interesting. ld d,h ld e,l ld l,(ix+Info.CursorOffset+0) \ ld h,(ix+Info.CursorOffset+1) - ld a,d or e jr z,+ call IncrementRomPointer dec de jr - + ld (ix+Info.CursorPage),b ld (ix+Info.CursorOffset+0),l \ ld (ix+Info.CursorOffset+1),h jr FinishedSeekingResolved ResolveSeekRAM ; Seek from RAM ld e,(ix+Info.CursorOffset+0) \ ld d,(ix+Info.CursorOffset+1) add hl,de ld (ix+Info.CursorOffset+0),l \ ld (ix+Info.CursorOffset+1),h FinishedSeekingResolved pop de pop bc pop hl or a ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === File.IsAlreadyOpen === ; ; Checks if a file is already open. ; ; INPUTS: ; REGISTERS ; * OP1 - Name of the file to check. ; ; OUTPUTS: ; REGISTERS ; * F - Carry flag set if open, reset otherwise. ; * HL - Pointer to "Open" error block. ; * DE - If open, pointer to descriptor of the open file. ; ; DESTROYED: ; REGISTERS ; * AF, BC, DE, HL ; ;@doc:end ;------------------------------------------------------------------------------- IsAlreadyOpen ld c,File.MaxHandles ld hl,File.Handles IsAlreadyOpen.CheckHandle ld a,(hl) and ~State.Dirty jr z,IsAlreadyOpen.HandleClosed push hl inc hl ld de,OP1 ld b,9 IsAlreadyOpen.CheckFilename ld a,(de) cp (hl) jr nz,IsAlreadyOpen.MismatchedFilename or a jr z,IsAlreadyOpen.EndOfName IsAlreadyOpen.NotEndOfName inc hl inc de djnz IsAlreadyOpen.CheckFilename IsAlreadyOpen.EndOfName ; We've hit the end of the filename, and both are identical. pop de ld hl,Errors.Open scf ret IsAlreadyOpen.MismatchedFilename pop hl IsAlreadyOpen.HandleClosed ld de,SizeOf(Info) add hl,de dec c jr nz,IsAlreadyOpen.CheckHandle xor a ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === File.OpenRead === ; ; Opens a file for reading. ; ; INPUTS: ; REGISTERS ; * OP1 - Name of the file to read. ; ; OUTPUTS: ; REGISTERS ; * F - Carry flag set on error. ; * HL - Pointer to error block. ; * E - Handle of opened file. ; ; DESTROYED: ; REGISTERS ; * AF, DE, HL, IX ; ;@doc:end ;------------------------------------------------------------------------------- OpenRead call IsAlreadyOpen jr nc,+ ld a,(de) bit 1,a ; Bit 1 = open for writing. jr z,+ ret + call GetFreeHandle ret c ; No free handles! push de push bc ; Copy filename from OP1->record. ld hl,OP1 push ix pop de inc de ld bc,9 ldir pop bc call PopulateHandle jr nc,+ pop de ret + pop de ld (ix+Info.State),State.OpenRead ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === File.OpenWrite === ; ; Opens a file for writing. If the file doesn't exist, it is created. If it ; does exist, it is truncated first. ; ; INPUTS: ; REGISTERS ; * OP1 - Name of the file to write. ; ; OUTPUTS: ; REGISTERS ; * F - Carry flag set on error. ; * HL - Pointer to error block. ; * E - Handle of opened file. ; ; DESTROYED: ; REGISTERS ; * AF, BC, DE, HL, IX ; ;@doc:end ;------------------------------------------------------------------------------- OpenWrite call IsAlreadyOpen ret c call GetFreeHandle ret c ; No free handles! ; Copy name (OP1) to variable. push de push ix pop de inc de ld hl,OP1 ld bc,9 ldir pop de ld a,(OP1) or a jr z,OpenWriteDevice push de push ix call Delete ld hl,0 call Create pop ix pop de ret c ld (ix+Info.State),State.OpenWrite|State.Dirty ret OpenWriteDevice ld (ix+Info.State),State.OpenWrite .fcall "Device.Initialise" ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === File.OpenUpdate === ; ; Opens a file for updating. The file must exist already. ; ; INPUTS: ; REGISTERS ; * OP1 - Name of the file to update. ; ; OUTPUTS: ; REGISTERS ; * F - Carry flag set on error. ; * HL - Pointer to error block. ; * E - Handle of opened file. ; ; DESTROYED: ; REGISTERS ; * AF, BC, DE, HL, IX ; ;@doc:end ;------------------------------------------------------------------------------- OpenUpdate call IsAlreadyOpen ret c call OpenRead ret c ld a,(ix+Info.Name) or a jr nz,+ ld (ix+Info.State),State.OpenUpdate|State.Dirty ret + ; Is it in ROM? ld a,(ix+Info.VarPage) or a jr nz,+ ld (ix+Info.State),State.OpenUpdate|State.Dirty ret + ; Force-close it. ld (ix+Info.State),State.Closed ld hl,Errors.FileIsLocked scf ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === File.Close === ; ; Closes a file handle. ; ; INPUTS: ; REGISTERS ; * E - File handle to close. ; ; OUTPUTS: ; REGISTERS ; * F - Carry flag set on error. ; * HL - Pointer to error block. ; ; DESTROYED: ; REGISTERS ; * AF, BC, DE, HL, IX ; ;@doc:end ;------------------------------------------------------------------------------- Close call ResolveHandle ret c ld a,(ix+Info.Name) or a ld (ix+Info.State),0 ; Mark file as closed. ret z ; Is a device file. ; If DataSize <> VarSize, file needs to be shrunk. ld a,(ix+Info.DataSize+0) cp (ix+Info.VarSize+0) jr nz,CloseSizesAreDifferent ld a,(ix+Info.DataSize+1) cp (ix+Info.VarSize+1) jr nz,CloseSizesAreDifferent or a ret CloseSizesAreDifferent ; Get this out of the way... call DirtyHandles ; Right, now shrink the program. push ix pop hl inc hl rst rMov9ToOp1 call SafeChkFindSym jr nc,+ ; Something really went tits-up here... ld hl,Errors.Channel ret + ld a,b or a jr z,+ ; File is in ROM. Something equally bad has happened. ld hl,Errors.Channel scf ret + call SafeEditProg ; How much to shrink? ld l,(ix+Info.DataSize+0) \ ld h,(ix+Info.DataSize+1) ld e,(ix+Info.VarSize+0) \ ld d,(ix+Info.VarSize+1) or a sbc hl,de ; hl = bytes to add. push hl ld de,(iMathPtr2) add hl,de ld (iMathPtr2),hl pop hl ; Finally, fix size bytes. ld ix,(iMathPtr1) ld e,(ix+0) \ ld d,(ix+1) add hl,de ld (ix+0),l \ ld (ix+1),h call SafeCloseProg or a ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === File.CloseAll === ; ; Closes all file handles. ; ; DESTROYED: ; REGISTERS ; * AF, BC, DE, HL, IX ; ;@doc:end ;------------------------------------------------------------------------------- CloseAll ld b,MaxHandles - push bc ld e,b dec e call Close pop bc djnz - ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === File.GetSize === ; ; Gets the size of an open file. ; ; INPUTS: ; REGISTERS ; * E - Handle of the file to get the size of. ; ; OUTPUTS: ; REGISTERS ; * F - Carry set on error. ; * HL - File size on success; pointer to error block on error. ; ; DESTROYED: ; REGISTERS ; * AF, HL ; ;@doc:end ;------------------------------------------------------------------------------- GetSize push de push ix call ResolveHandle jr nc,+ pop ix pop de ld hl,Errors.Channel ret + ld l,(ix+Info.DataSize+0) ld h,(ix+Info.DataSize+1) ld a,(ix+Info.Name) or a jr nz,+ .fcall "Device.GetSize" + pop ix pop de ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === File.SetSize === ; ; Sets the size of an open file. ; ; INPUTS: ; REGISTERS ; * E - Handle of the file to get the size of. ; * HL - Size to set the file. ; ; OUTPUTS: ; REGISTERS ; * F - Carry set on error. ; ; DESTROYED: ; REGISTERS ; * AF, HL, IX ; ;@doc:end ;------------------------------------------------------------------------------- SetSize ld (TempDE),de ld (TempHL),hl call ResolveHandle ret c ld a,(ix+Info.Name) or a jr nz,+ .fcall "Device.SetSize" ret + bit 1,(ix+Info.State) jr nz,+ scf ld hl,Errors.ReadOnly ret + ; Set the size of the file. push de ld e,(ix+Info.DataSize+0) ld d,(ix+Info.DataSize+1) ld hl,(TempHL) or a sbc hl,de jr nz,+ ; Nothing to do. or a pop de ret + ; Can the file grow? call CanGrow jr z,+ scf pop de ret + ld l,(ix+Info.DataSize+0) ld h,(ix+Info.DataSize+1) or a ld de,(TempHL) sbc hl,de jr c,SetSize.IncreaseSize ; New size is *smaller*. ld (ix+Info.DataSize+0),e ld (ix+Info.DataSize+1),d ex de,hl ld e,(ix+Info.Position+0) ld d,(ix+Info.Position+1) or a sbc hl,de jr nc,+ ld de,(TempDE) ld hl,(TempHL) call SetPosition + pop de ret SetSize.IncreaseSize ld e,(ix+Info.Position+0) ld d,(ix+Info.Position+1) push de ld de,(TempDE) ld hl,(TempHL) call SetPosition pop de ld (ix+Info.Position+0),e ld (ix+Info.Position+1),d pop de ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === File.GetPosition === ; ; Gets the cursor position in an open file. ; ; INPUTS: ; REGISTERS ; * E - Handle of the file to get the position of. ; ; OUTPUTS: ; REGISTERS ; * F - Carry set on error. ; * HL - File size on success; pointer to error block on error. ; ; DESTROYED: ; REGISTERS ; * AF, HL ; ;@doc:end ;------------------------------------------------------------------------------- GetPosition push de push ix call ResolveHandle jr nc,+ pop ix pop de ld hl,Errors.Channel ret + ld a,(ix+Info.Name) or a jr nz,+ .fcall "Device.GetPosition" jr GetPosition.Exit + ld l,(ix+Info.Position+0) ld h,(ix+Info.Position+1) or a GetPosition.Exit pop ix pop de ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === File.IsEOF === ; ; Checks if a file's cursor is at the end of the file. ; ; INPUTS: ; REGISTERS ; * E - Handle of the file to check. ; ; OUTPUTS: ; REGISTERS ; * F - Carry set on error. ; * - Zero flag set if at end of file. ; * HL - Pointer to error block on error. ; ; DESTROYED: ; REGISTERS ; * AF ; ;@doc:end ;------------------------------------------------------------------------------- IsEOF push ix push hl push de call ResolveHandle jr nc,+ pop de pop de pop ix ret + ld a,(ix+Info.Name) or a jr nz,+ .fcall "Device.IsEOF" pop ix pop hl pop de ret + ld a,(ix+Info.Position+1) cp (ix+Info.DataSize+1) jr nz,+ ld a,(ix+Info.Position+0) cp (ix+Info.DataSize+0) + scf ccf pop de pop hl pop ix ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === File.SetPosition === ; ; Sets the cursor position in an open file. ; ; INPUTS: ; REGISTERS ; * E - Handle of the file to get the position of. ; * HL - Position to seek to in the file. ; ; OUTPUTS: ; REGISTERS ; * F - Carry set on error. ; * HL - File size on success; pointer to error block on error. ; ; DESTROYED: ; REGISTERS ; * AF, BC, HL ; ;@doc:end ;------------------------------------------------------------------------------- SetPosition ld (PositionToSet),hl push de push ix call ResolveHandle jr nc,+ pop ix pop de ld hl,Errors.Channel ret + ld a,(ix+Info.Name) or a jr nz,SetPosition.NotDevice .fcall "Device.SetPosition" pop ix pop de ret SetPosition.NotDevice ld l,(ix+Info.DataSize+0) ld h,(ix+Info.DataSize+1) ld de,(PositionToSet) or a sbc hl,de jr nc,SetPosition.WithinFileBounds ; Can the file grow? call CanGrow jr nz,SetPosition.CannotExpandFile ; Check PTR# < 24KB (avoids pointer overflow bugs later, and no file can be 24KB anyway). ld hl,(PositionToSet) ld de,kb(24) or a sbc hl,de ld hl,Errors.DiskFull jr nc,SetPosition.CannotExpandFile ; We're trying to PTR# past the end of the file. ; How much bigger is it going to have to get? ld hl,(PositionToSet) ld e,(ix+Info.DataSize+0) ld d,(ix+Info.DataSize+1) or a sbc hl,de ; hl = size to grow. ld (SizeToGrow),hl ld de,32 ; overhead add hl,de ; Is there enough room? call SafeEnoughMem ld hl,Errors.DiskFull jr c,SetPosition.CannotExpandFile ; Set variable size. ld de,(PositionToSet) ld (ix+Info.Position+0),e ld (ix+Info.Position+1),d ld (ix+Info.DataSize+0),e ld (ix+Info.DataSize+1),d ld l,(ix+Info.VarOffset+0) ld h,(ix+Info.VarOffset+1) ld (hl),e inc hl ld (hl),d inc hl ld e,(ix+Info.VarSize+0) ld d,(ix+Info.VarSize+1) add hl,de ex de,hl ld hl,(SizeToGrow) ; Insert memory to enlarge the file. call SafeInsertMem ; Blank out the inserted memory. ld h,d ld l,e inc de ld (hl),0 ld bc,(SizeToGrow) dec bc ld a,b or c jr z,+ ldir + call DirtyHandles pop ix pop de or a ret SetPosition.CannotExpandFile pop ix pop de scf ret SetPosition.WithinFileBounds ld a,(ix+Info.Name) or a jr z,FinishedSeeking ; Seeking in device. ld hl,(PositionToSet) ld e,(ix+Info.Position+0) ld d,(ix+Info.Position+1) or a sbc hl,de jr z,FinishedSeekingNoMove ; No seeking! ex de,hl ld b,(ix+Info.CursorPage) ld l,(ix+Info.CursorOffset+0) ld h,(ix+Info.CursorOffset+1) ; B = ROM page. ; HL = ROM offset. ; DE = amount to seek. ; F = Z: no seeking, P: seek forwards. jp m,SeekBackwards ; Seek forwards through file. ld a,b or a jr z,SeekRAM ; Seek forwards through ROM. - call IncrementRomPointer dec de ld a,d or e jr nz,- jr FinishedSeekingUpdateRomPointers SeekRAM ld l,(ix+Info.CursorOffset+0) \ ld h,(ix+Info.CursorOffset+1) add hl,de ld (ix+Info.CursorOffset+0),l \ ld (ix+Info.CursorOffset+1),h jr FinishedSeeking SeekBackwards ; Seek backwards through file. ld a,b or a jr z,SeekRam ; Seek backwards through ROM. - call DecrementRomPointer inc de ld a,d or e jr nz,- ; Run on... FinishedSeekingUpdateRomPointers ld (ix+Info.CursorPage),b ld (ix+Info.CursorOffset+0),l ld (ix+Info.CursorOffset+1),h FinishedSeeking ld hl,(PositionToSet) ld (ix+Info.Position+0),l ld (ix+Info.Position+1),h FinishedSeekingNoMove or a pop ix pop de ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === File.ReadByte === ; ; Reads a byte from a file. ; ; INPUTS: ; REGISTERS ; * E - Handle of the file to read. ; ; OUTPUTS: ; REGISTERS ; * A - Value read from the file. ; * F - Carry flag set on error. ; - Zero flag set if a byte was read, non-zero if not read (EOF). ; * HL - Pointer to error block. ; * A - Handle of opened file. ; ; DESTROYED: ; REGISTERS ; * AF, DE, HL ; ;@doc:end ;------------------------------------------------------------------------------- ReadByte push ix call ResolveHandle jr nc,+ pop ix ret + ld a,(ix+Info.Name) or a jr nz,+ .fcall "Device.ReadByte" pop ix ret + push hl push de ld l,(ix+Info.DataSize+0) \ ld h,(ix+Info.DataSize+1) ld e,(ix+Info.Position+0) \ ld d,(ix+Info.Position+1) xor a sbc hl,de jr nz,+ pop de pop hl pop ix inc a ; Clear zero. or a ; Clear carry. ret + ; Increment position. inc de ld (ix+Info.Position+0),e \ ld (ix+Info.Position+1),d ld l,(ix+Info.CursorOffset+0) \ ld h,(ix+Info.CursorOffset+1) ld a,(ix+Info.CursorPage) or a jr z,ReadByteFromRam ld b,a call ReadRom ld (ix+Info.CursorPage),b jr ReadByteFromRom ReadByteFromRam ld a,(hl) inc hl ReadByteFromRom ld (ix+Info.CursorOffset+0),l \ ld (ix+Info.CursorOffset+1),h ld e,a xor a ld a,e pop de pop hl pop ix ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === File.WriteByte === ; ; Writes a byte to a file. ; ; INPUTS: ; REGISTERS ; * A - Value to write to the file. ; * E - Handle of the file to read. ; ; OUTPUTS: ; REGISTERS ; * F - Carry flag set on error. ; * HL - Pointer to error block. ; ; DESTROYED: ; REGISTERS ; * AF, BC, DE, HL, IX ; ;@doc:end ;------------------------------------------------------------------------------- WriteByte ld (ByteToWrite),a ld a,e ld (WriteHandle),a call ResolveHandle ret c bit 1,(ix+Info.State) jr nz,+ scf ld hl,Errors.ReadOnly ret + ld a,(ix+Info.Name) or a jr nz,+ ld a,(ByteToWrite) .fcall "Device.WriteByte" ret + ; Does the variable need inflating? ld a,(ix+Info.Name) or a jr z,NoNeedToInflate ld l,(ix+Info.Position+0) \ ld h,(ix+Info.Position+1) ld e,(ix+Info.VarSize+0) \ ld d,(ix+Info.VarSize+1) or a sbc hl,de jr c,NoNeedToInflate call CanGrow jr z,+ scf ret + call DirtyHandles ; The var needs inflating (uh-oh). SizeToInflate = 64 push bc push de push hl push ix ld hl,SizeToInflate + 32 ; For overhead... call SafeEnoughMem pop ix pop hl pop de pop bc jr nc,+ CannotInflate scf ld hl,Errors.DiskFull ret + push ix pop hl inc hl rst rMov9ToOp1 call SafeChkFindSym jr nc,InflateFoundSym ld hl,Errors.Channel ret InflateFoundSym ex de,hl ld e,(hl) inc hl ld d,(hl) dec hl push hl ; de = Current size. ld b,d ld c,e ld hl,SizeToInflate add hl,de ex de,hl ; de = New size. pop hl ld (hl),e inc hl ld (hl),d inc hl add hl,bc ex de,hl ; de -> end of current data. ld hl,SizeToInflate call SafeInsertMem ld a,(WriteHandle) ld e,a call ResolveHandle ret c ; Hmm. NoNeedToInflate ld l,(ix+Info.CursorOffset+0) \ ld h,(ix+Info.CursorOffset+1) ld a,(ByteToWrite) ld (hl),a inc hl ld (ix+Info.CursorOffset+0),l \ ld (ix+Info.CursorOffset+1),h ; Amend the DataSize field. ld l,(ix+Info.DataSize+0) \ ld h,(ix+Info.DataSize+1) inc hl ld (ix+Info.DataSize+0),l \ ld (ix+Info.DataSize+1),h ; Amend the cursor position. ld l,(ix+Info.Position+0) \ ld h,(ix+Info.Position+1) inc hl ld (ix+Info.Position+0),l \ ld (ix+Info.Position+1),h or a ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === File.CanGrow === ; ; Checks if a file can "grow". ; ; INPUTS: ; REGISTERS ; * IX - Pointer to open file handle. ; ; OUTPUTS: ; REGISTERS ; * F - Z flag set if file can grow, NZ otherwise. ; * HL - Pointer to error block if file can't grow; unchanged otherwise. ; ; DESTROYED: ; REGISTERS ; * AF, HL ; ;@doc:end ;------------------------------------------------------------------------------- CanGrow ; Is the file open for reading? bit 1,(ix+Info.State) jr nz,+ ld a,(ix+Info.State) cp -1 ld hl,Errors.ReadOnly ret + ; Is the variable format "growable"? ld a,(ix+Info.Name) cp progObj ret z cp protProgObj ret z cp appVarObj ret z ld hl,Errors.FileIsFixedSize ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === File.NameToOP1 === ; ; Copies a filename string pointed to by HL into a name suitable for use by ; the file routines. ; The string can have a NUL or CR terminator. ; ; INPUTS: ; REGISTERS ; * HL - Pointer to filename string. ; ; OUTPUTS: ; REGISTERS ; * F - Carry flag set on error. ; * HL - Pointer to error block. ; MEMORY ; * OP1 - Name of the file as a formatted TI-OS variable name. ; ; DESTROYED: ; REGISTERS ; * AF, HL ; ;@doc:end ;------------------------------------------------------------------------------- NameToOP1 push hl push bc push de push hl .bcall _ZeroOP1 pop hl ld a,protProgObj ld (OP1),a ld b,9 ld de,OP1+1 - xor a ld (de),a ld a,(hl) or a jp z,NameCopied cp '\r' jp z,NameCopied cp '.' jr z,DecodeExtension ld (de),a inc hl inc de djnz - NameCopyBadName pop de pop bc pop hl scf ld hl,Errors.BadName ret DecodeExtension inc hl ld de,OP2 ld b,3 - ld a,(hl) or a jr z,NameCopyBadName cp '\r' jr z,NameCopyBadName ld (de),a inc hl inc de djnz - xor a ld (de),a ld de,OP2 ld hl,Extension.Program .fcall "String.Compare" jr nz,+ ld a,protProgObj ld (OP1),a jp NameCopied + ld hl,Extension.AppVar .fcall "String.Compare" jr nz,+ ld a,appVarObj ld (OP1),a jp NameCopied + ld hl,Extension.Picture .fcall "String.Compare" jr nz,NotDotPic ; What's the id of the picture? ld hl,OP1+1 .fcall "String.Length" cp 4 jr nz,NameCopyBadName ld a,pictObj ld (OP1),a ld a,(OP1+4) sub '0' cp 10 jp p,NameCopyBadName dec a cp 255 jr nz,+ ld a,9 + ld b,a xor a ld (OP1+4),a ld hl,OP1+1 ld de,Extension.Picture .fcall "String.Compare" jr nz,NameCopyBadName ld a,tVarPict ld (OP1+1),a ld a,b ld (OP1+2),a xor a ld hl,OP1+3 ld b,4 - ld (hl),a inc hl djnz - ld hl,OP1 jr NameCopied NotDotPic ld hl,Extension.Device .fcall "String.Compare" jr nz,NotDotDev ; We're trying to open a device. Oo-er. xor a ld (OP1),a ld de,OP1+1 ld hl,DeviceName.SIRCS .fcall "String.Compare" jr nz,+ ld a,Device.ID.SIRCS ld (OP1+1),a jr NameCopied + NotDotDev jp NameCopyBadName NameCopied or a pop de pop bc pop hl ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === File.OP1ToName === ; ; Converts a TI-OS variable name in OP1 into a NUL-terminated printable string. ; ; INPUTS: ; MEMORY ; * HL - Pointer to storage of converted filename string. ; * OP1 - Name of the file as a formatted TI-OS variable name. ; ; OUTPUTS: ; REGISTERS ; * DE - Points to NUL terminator. ; ; DESTROYED: ; REGISTERS ; * AF. ; ;@doc:end ;------------------------------------------------------------------------------- OP1ToName push hl push bc ld d,h ld e,l ld a,(OP1) cp pictObj jr nz,OP1ToNameNotPict ; Handle PICn.PIC ld hl,Extension.Picture ld bc,3 ldir ld a,(OP1+2) inc a cp 10 jr nz,+ xor a + add a,'0' .fcall "Char.IsNumeric" jr z,+ ld a,'#' + ld (de),a inc de jr AddExtension OP1ToNameNotPict ld hl,OP1+1 - ld a,(hl) or a jr z,AddExtension ldi jr - AddExtension ld a,'.' ld (de),a inc de ld a,(OP1) ld hl,Extension.Picture cp pictObj \ jr z,+ ld hl,Extension.AppVar cp appVarObj \ jr z,+ ld hl,Extension.Device or a \ jr z,+ ld hl,Extension.Program + ld bc,3 ldir xor a ld (de),a pop bc pop hl ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === File.BlockSave === ; ; Saves a block of memory to a file. ; ; INPUTS: ; REGISTERS ; * DE - Pointer to start of file data. ; * BC - Size of data block to save. ; MEMORY ; * OP1 - Name of the file as a formatted TI-OS variable name. ; ; OUTPUTS: ; REGISTERS ; * F - Carry flag set on error. ; * HL - Pointer to error block. ; ; DESTROYED: ; REGISTERS ; * AF, BC, DE, HL, IX. ; MEMORY ; * OP2-OP6. ; ;@doc:end ;------------------------------------------------------------------------------- BlockSave ld (BlockSave.Size),bc ld (BlockSave.Data),de .bcall _OP1ToOP6 call OpenRead jr nc,BlockSave.OpenedExisting .fcall "Basic.BBCBASIC_GETERR" cp 214 ; File not found. jr z,BlockSave.DeletedOld scf ret BlockSave.OpenedExisting ld (TempDE),de call GetSize ld (TempHL),hl ld de,(TempDE) call Close call DirtyHandles ld hl,(BlockSave.Size) ld de,(TempHL) or a sbc hl,de ; HL = enlarged file size. ld de,32 add hl,de bit 7,h jr nz,BlockSave.NewFileIsSmaller call SafeEnoughMem ld hl,Errors.DiskFull ret c BlockSave.NewFileIsSmaller ; Delete old one first. call Delete jr nc,+ cp 214 jr z,+ scf ret + BlockSave.DeletedOld .bcall _OP6ToOP1 ld hl,(BlockSave.Size) call Create ret c ld bc,(BlockSave.Size) ld a,b or c jr nz,+ or a ret + .bcall _OP6ToOP1 call SafeChkFindSym jr nc,+ ld hl,Errors.FileNotFound ret + inc de inc de ld bc,(BlockSave.Size) ld hl,(BlockSave.Data) ldir or a ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === File.BlockLoad === ; ; Loads a block of memory from a file. ; ; INPUTS: ; REGISTERS ; * DE - address in RAM to load file to. ; * BC - maximum file size that can be loaded. ; MEMORY ; * OP1 - Name of the file as a formatted TI-OS variable name. ; ; OUTPUTS: ; REGISTERS ; * F - Carry flag set on error. ; * HL - Pointer to error block. ; MEMORY ; * BlockSave.Size - Size of loaded file. ; ; DESTROYED: ; REGISTERS ; * AF, BC, DE, HL, IX. ; MEMORY ; * OP2-OP6. ; ;@doc:end ;------------------------------------------------------------------------------- BlockLoad ld (BlockSave.Size),bc ld (BlockSave.Data),de call OpenRead ret c ld a,e ld (LoadHandle),a call GetSize ld de,(BlockSave.Size) ld (BlockSave.Size),hl scf sbc hl,de jp c,+ scf ld hl,Errors.NoRoom jr BlockLoadExit + ld de,(LoadHandle) call ResolveHandle ret c ld a,(ix+Info.Name) or a jr nz,+ scf ld hl,Errors.BadDevice ret + ld a,(ix+Info.VarPage) or a jr z,BlockLoad.FromRam BlockLoad.FromRom ld b,a ld l,(ix+Info.VarOffset+0) ld h,(ix+Info.VarOffset+1) call IncrementRomPointer call IncrementRomPointer ld ix,(BlockSave.Data) ld de,(BlockSave.Size) - call ReadRom ld (ix),a inc ix dec de ld a,d or e jr nz,- or a jr BlockLoadExit BlockLoad.FromRam ld l,(ix+Info.VarOffset+0) ld h,(ix+Info.VarOffset+1) inc hl inc hl ld de,(BlockSave.Data) ld bc,(BlockSave.Size) ldir or a BlockLoadExit push af push hl ld a,(LoadHandle) ld e,a call Close pop hl pop af ret Extension.Program .db "PRG",0 Extension.AppVar .db "VAR",0 Extension.Picture .db "PIC",0 Extension.Device .db "DEV",0 DeviceName.SIRCS .db "SIRCS",0 .endmodule