A FAT16 File Explorer in x86 Assembly

Follow the steps below to create a file explorer program for a FAT16 filesystem in x86 assembly language, and run it in a QEMU virtual machine. It’s kind of like a very, very primitive, and also very buggy, version of DOS.

1. If you have not already done so, download and install FASM and QEMU. Details are given in a previous post.

2. Click the Start button, open the Control Panel, open the Administrative Tools menu, and then click the Computer Management application to start it.

3. In the Computer Management application, expand the tree node named “Storage” in the left pane, and then click the “Disk Management” tree node underneath it.

4. Right-click on a disk with unused space and select the “Shrink Volume” item from the context menu that appears. A dialog will appear with a message saying that the program is “querying volume for available shrink space”. This may take quite a while.

5. Follow the prompts to create a new disk partition, and make sure that it uses the FAT16 filesystem. Make a note of the drive letter of the new partition.

6. Create some test directories and test files in the newly created disk partition. Since this program doesn’t yet support long filenames, it may be better to stick to file and directory names with fewer than 8 characters, and 3 characters in file extensions.

7. In any convenient location, create a new directory named “Filer”.

8. In the newly created “Filer” directory, create a new text file named “Filer-AssembleAndRun.bat”, containing the text shown below. Substitute the name of the directories containing qemu.exe and fasm.exe in the indicated places, and change the “X:” after the “-hda” switch on the next-to-last line to whatever the drive letter of the FAT16 file partition is.

set fasmPath="wherever fasm.exe is"
set qemuPath="wherever qemu.exe is"

for %%* in (.) do (set programName=%%~n*)

%fasmPath%\fasm.exe %programName%.asm %programName%.img

%qemuPath%\qemu.exe -boot a -fda %programName%.img -hda X:

pause

9. Still in the newly created “Filer” directory, create a new text file named “Filer.asm”, containing the text shown below this list of steps.

10. Run the file Filer-AssembleAndRun.bat. The program will be assembled using FASM and executed in a QEMU virtual machine.

11. Type “help” at the prompt to see a list of valid commands. “Dir” will list the contents of the current directory. “cd” will change the current location to the specified directory. The “print” command will display the contents of the specified file.

use16
org 0x7C00
Boot:
;
mov ah,0x00 ; reset disk
mov dl,0 ; drive number
int 0x13 ; call BIOS interrupt 0x13
;
mov ah,0x02 ; read sectors into memory
mov al,1 ; numberOfSectorsToRead
mov dl,0 ; driveNumber
mov ch,0 ; cylinderNumber
mov dh,0 ; headNumber
mov cl,2 ; startingSectorNumber
mov bx,Main ; returnBuffer
int 0x13 ; call BIOS interrupt 0x13
;
mov ah,0x02 ; read sectors into memory
mov al,0x40 ; numberOfSectorsToRead
mov dl,0 ; driveNumber
mov ch,0 ; cylinderNumber
mov dh,0 ; headNumber
mov cl,3 ; startingSectorNumber
mov bx,Definitions ; returnBuffer
int 0x13 ; call BIOS interrupt 0x13
;
jmp Main
;
ret
;

EndOfSectorOneMain:

PadOutWithZeroesSectorOne:
times ((0x200 - 2) - ($ - $$)) db 0x00

BootSectorSignature:
dw 0xAA55

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

org 0x7E00

Main:
;
push TextFileExplorer
call DisplayStringNewline
;
push StringHorizontalRule
call DisplayStringNewline
;
push CommandPromptSession0
call CommandPromptSessionRun
;
TextFileExplorer: db 'File Explorer',0
StringHorizontalRule: db '=============',0

PadOutWithZeroesSectorTwo:
times ((0x200) - ($ - $$)) db 0x00

EndOfSectorTwo:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

org 0x8000

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Command:
Command_Defn EQU 0
Command_Arguments EQU 2

CommandPromptDefn:
CommandPromptDefn_CommandDefnsAvailable EQU 0

CommandDefn:
CommandDefn_Name EQU 0
CommandDefn_Description EQU 2
CommandDefn_ProcedureToCall EQU 4

CommandPromptSession:
CommandPromptSession_CommandPromptDefn EQU 0
CommandPromptSession_CommandCurrent EQU 2
CommandPromptSession_FileInfoForDirectoryCurrent EQU 4
CommandPromptSession_FileInfoTemp EQU 6
CommandPromptSession_IsTerminated EQU 8
;
CommandPromptSession_DirectoryDepthMax EQU 0xA

Disk:
Disk_DiskNumber EQU 0
Disk_CylinderNumberMax EQU 2
Disk_HeadNumberMax EQU 4
Disk_SectorNumberMax EQU 6
Disk_Filesystem EQU 8

DiskLocation:
DiskLocation_Disk EQU 0
DiskLocation_CylinderNumber EQU 2
DiskLocation_HeadNumber EQU 4
DiskLocation_SectorNumber EQU 6
DiskLocation_ByteOffsetWithinSector EQU 8
;
DiskLocation_SizeInBytes EQU 0xA

FileInfo:
FileInfo_Disk EQU 0
FileInfo_ClusterIndexStart EQU 2
FileInfo_IsDirectory EQU 4
FileInfo_IsHidden EQU 6
FileInfo_DirectoryParent EQU 8
FileInfo_NameImmediate EQU 0xA
;
FileInfo_SizeInBytesMinusName EQU 0xA
FileInfo_FileStem_Length EQU 8
FileInfo_FileExtension_Length EQU 3
FileInfo_FileName_SizeInBytes EQU (FileInfo_FileStem_Length + 1 + FileInfo_FileExtension_Length + 1)
FileInfo_SizeInBytesFull EQU (FileInfo_SizeInBytesMinusName + FileInfo_FileName_SizeInBytes)

Filesystem:
Filesystem_Defn EQU 0
Filesystem_BootSectorBytes EQU 2
Filesystem_Descriptor EQU 4

FilesystemDefn:
FilesystemDefn_Name EQU 0
FilesystemDefn_MethodInitialize EQU 2
FilesystemDefn_MethodDescriptorDisplay EQU 4

FilesystemFAT16:
FilesystemFAT16_BytesPerSector EQU 0x0B
FilesystemFAT16_SectorsPerCluster EQU 0x0D
FilesystemFAT16_NumberOfReservedSectors EQU 0x0E
FilesystemFAT16_NumberOfFATs EQU 0x10
FilesystemFAT16_RootDirectoryEntriesMax EQU 0x11
FilesystemFAT16_SectorsPerFAT EQU 0x16
FilesystemFAT16_SectorsPerTrack EQU 0x18
FilesystemFAT16_SectorsTotal EQU 0x20
FilesystemFAT16_NumberOfHeads EQU 0x1A
FilesystemFAT16_NumberOfHiddenSectors EQU 0x1C

FilesystemFAT16_BytesPerDirectoryEntry EQU 0x20
FilesystemFAT16_ClusterIndexFirst EQU 0x02

FilesystemFAT16Descriptor:
FilesystemFAT16Descriptor_BytesPerSector EQU 0
FilesystemFAT16Descriptor_SectorsPerCluster EQU 2
FilesystemFAT16Descriptor_NumberOfReservedSectors EQU 4
FilesystemFAT16Descriptor_NumberOfFATs EQU 6
FilesystemFAT16Descriptor_RootDirectoryEntriesMax EQU 8
FilesystemFAT16Descriptor_SectorsPerFAT EQU 0xA
FilesystemFAT16Descriptor_SectorsPerTrack EQU 0xC
FilesystemFAT16Descriptor_SectorsTotal EQU 0xE
FilesystemFAT16Descriptor_NumberOfHeads EQU 0x10
FilesystemFAT16Descriptor_NumberOfHiddenSectors EQU 0x12
;
FilesystemFAT16Descriptor_RootDirectorySectorLBA EQU 0x14
FilesystemFAT16Descriptor_DataStartingSectorLBA EQU 0x16
FilesystemFAT16Descriptor_DirectoryEntriesPerSector EQU 0x18
;
FilesystemDescriptor_SizeInBytes EQU 0x20

FilesystemFAT16DirectoryEntry:
FilesystemFAT16DirectoryEntry_ShortFileName EQU 0x00
FilesystemFAT16DirectoryEntry_ShortFileExtension EQU 0x08
FilesystemFAT16DirectoryEntry_Attributes EQU 0x0B
FilesystemFAT16DirectoryEntry_ClusterIndexFirst EQU 0x1A
;
FilesystemFAT16DirectoryEntry_LengthOfShortFileName EQU 0x8
FilesystemFAT16DirectoryEntry_LengthOfShortFileExtension EQU 0x3
;
FilesystemFAT16DirectoryEntry_EndOfDirectory EQU 0x00
FilesystemFAT16DirectoryEntry_IsDeleted EQU 0xE5
FilesystemFAT16DirectoryEntry_FileNameStartsWithE5 EQU 0x05
FilesystemFAT16DirectoryEntry_FileNameIsDotOrDotDot EQU 0x2E
;
FilesystemFAT16DirectoryEntry_Attributes_ReadOnly EQU 0x01
FilesystemFAT16DirectoryEntry_Attributes_Hidden EQU 0x02
FilesystemFAT16DirectoryEntry_Attributes_System EQU 0x04
FilesystemFAT16DirectoryEntry_Attributes_VolumeLabel EQU 0x08
FilesystemFAT16DirectoryEntry_Attributes_Subdirectory EQU 0x10
; 5 - archive
; 6 - device
; 7 - reserved
FilesystemFAT16DirectoryEntry_Attributes_VFATLongFileName EQU 0x0F

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Definitions:
CommandPromptSession0: dw CommandPromptDefnTest, CommandPromptSession0Command, CommandPromptSession0FileInfoForDirectoryRoot, CommandPromptSession0FileInfoTemp, 0
CommandPromptSession0FileInfoTemp: db FileInfo_SizeInBytesFull dup (0)
CommandPromptSession0FileInfosForDirectories:
CommandPromptSession0FileInfoForDirectoryRoot:
dw CommandPromptSession0Disk, 0, 1, 0, 0

db '[root]',0,0,0,0,0,0,0

db (CommandPromptSession_DirectoryDepthMax * FileInfo_SizeInBytesFull) dup (0)

CommandPromptSession0Disk: dw 0x80, 0, 0, 0, CommandPromptSession0DiskFilesystem
CommandPromptSession0DiskFilesystem: dw FilesystemDefnFAT16, CommandPromptSession0DiskFilesystemBootSectorBytes, CommandPromptSession0DiskFilesystemDescriptor
CommandPromptSession0DiskFilesystemBootSectorBytes: db 0x200 dup (0)
CommandPromptSession0DiskFilesystemDescriptor: db FilesystemDescriptor_SizeInBytes dup (0)

CommandPromptSession0Command: dw CommandHelp, 0

CommandPromptDefnTest: dw CommandPromptDefnTestCommands
CommandPromptDefnTestCommands:
dw CommandDirectoryChange, CommandDirectoryList, CommandDiskDescribe, CommandFilePrint, CommandHelp, 0

CommandDirectoryChange: dw CommandDirectoryChangeName, CommandDirectoryChangeDescription, CommandProcedureDirectoryChange
CommandDirectoryChangeName: db 'cd',0
CommandDirectoryChangeDescription: db 'Changes to another directory.',0

CommandDirectoryList: dw CommandDirectoryListName, CommandDirectoryListDescription, CommandProcedureDirectoryList
CommandDirectoryListName: db 'dir',0
CommandDirectoryListDescription: db 'Lists the contents of the current directory.',0

CommandDiskDescribe: dw CommandDiskDescribeName, CommandDiskDescribeDescription, CommandProcedureDiskDescribe
CommandDiskDescribeName: db 'disk',0
CommandDiskDescribeDescription: db 'Displays the attributes of the current disk and its filesystem.', 0

CommandFilePrint: dw CommandFilePrintName, CommandFilePrintDescription, CommandProcedureFilePrint
CommandFilePrintName: db 'print',0
CommandFilePrintDescription: db 'Displays the contents of the specified file.',0

CommandHelp: dw CommandHelpName, CommandHelpDescription, CommandProcedureHelp
CommandHelpName: db 'help',0
CommandHelpDescription: db 'Displays this help text.',0

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

ArrayEmpty:
dw 0

CommandProcedureDirectoryChange:
; (session, command)
push bp
mov bp,sp
push ax
push bx
push si
push di
;
mov si,[bp+6] ; session
;
mov di,[bp+4] ; command
mov di,[di+Command_Arguments]
mov di,[di+2] ; command.arguments[1] (0 is command name)
;
push CommandProcedureDirectoryChange_Temp
push StringDotDot
push di
call StringCompare
;
CommandProcedureDirectoryChange_IfDotDot:
cmp word [CommandProcedureDirectoryChange_Temp],1
jne CommandProcedureDirectoryChange_IfDotDotEnd
;
mov di,[si+CommandPromptSession_FileInfoForDirectoryCurrent]
mov di,[di+FileInfo_DirectoryParent]
cmp di,0
je CommandProcedureDirectoryChange_End
;
mov [si+CommandPromptSession_FileInfoForDirectoryCurrent],di
;
jmp CommandProcedureDirectoryChange_End
;
CommandProcedureDirectoryChange_IfDotDotEnd:
;
push si ; session
push word [si+CommandPromptSession_FileInfoForDirectoryCurrent]
push word [si+CommandPromptSession_FileInfoTemp]
push di ; fileNameToFind
push FileInfoDirectoryChange
call FileInfoSearchAsDirectory
;
CommandProcedureDirectoryChange_End:
;
pop di
pop si
pop bx
pop ax
pop bp
ret 4
;
CommandProcedureDirectoryChange_Temp: dw 0, 0
StringDotDot: db '..', 0

CommandProcedureDirectoryList:
; (session, command)
push bp
mov bp,sp
push si
;
mov si,[bp+6] ; session
;
push si ; session
push word [si+CommandPromptSession_FileInfoForDirectoryCurrent]
push word [si+CommandPromptSession_FileInfoTemp]
push word 0 ; fileNameToFind
push FileInfoDisplayFileName
call FileInfoSearchAsDirectory
;
pop si
pop bp
ret 4
;

CommandProcedureDiskDescribe:
; (session, command)
push bp
mov bp,sp
push si
;
mov si,[bp+6] ; session
mov si,[si+CommandPromptSession_FileInfoForDirectoryCurrent]
mov si,[si+FileInfo_Disk]
;
push si ; disk
call DiskDisplay
;
pop si
pop bp
ret 4

CommandProcedureHelp:
; (session, command)
push bp
mov bp,sp
push ax
push bx
push si
;
push TextCommands ; array
call DisplayStringNewline
push StringHorizontalRule8
call DisplayStringNewline
;
mov si,[bp+6] ; session
mov si,[si+CommandPromptSession_CommandPromptDefn]
mov si,[si+CommandPromptDefn_CommandDefnsAvailable]
;
ForEachCommandAvailableHelp:
;
lodsw ; ax = command, si++
;
IfNoMoreCommands:
cmp ax,0x0000
je EndForEachCommandAvailableHelp
;
mov bx,ax
push word [bx+CommandDefn_Name]
call DisplayString
;
call DisplaySpace
call DisplaySpace
call DisplaySpace
;
push word [bx+CommandDefn_Description]
call DisplayString
call DisplayNewline
;
jmp ForEachCommandAvailableHelp
EndForEachCommandAvailableHelp:
;
pop si
pop bx
pop ax
pop bp
ret 4
;
TextCommands: db 'Commands',0
StringHorizontalRule8: db '========',0

CommandProcedureFilePrint:
; (session, command)
push bp
mov bp,sp
push ax
push bx
push si
push di
;
mov si,[bp+6] ; session
;
mov di,[bp+4] ; command
mov di,[di+Command_Arguments]
mov di,[di+2] ; command.arguments[1] (0 is command name)
;
push si ; session
push word [si+CommandPromptSession_FileInfoForDirectoryCurrent]
push word [si+CommandPromptSession_FileInfoTemp]
push di ; fileNameToFind
push FileInfoDisplayContents
call FileInfoSearchAsDirectory
;
;
pop di
pop si
pop bx
pop ax
pop bp
ret 4

CommandPromptSessionDisplay:
; (session)
;
push bp
mov bp,sp
push si
push di
;
mov si,[bp+4] ; session
;
call DisplayParenthesisOpen
;
push TextFileInfoForDirectoryCurrent
call DisplayString
call DisplayColon
;
push word [si+CommandPromptSession_FileInfoForDirectoryCurrent]
call FileInfoDisplay
;
call DisplayParenthesisClose
;
pop di
pop si
pop bp
ret 2
;
TextFileInfoForDirectoryCurrent: db 'FileInfoForDirectoryCurrent',0

CommandPromptSessionRun:
; (session)
;
push bp
mov bp,sp
push bx
push si
push di
;
mov si,[bp+4] ; session
;
push word [si+CommandPromptSession_FileInfoTemp]
push word [si+CommandPromptSession_FileInfoForDirectoryCurrent]
call FileInfoCopy
;
mov si,[si+CommandPromptSession_FileInfoForDirectoryCurrent]
mov si,[si+FileInfo_Disk]
;
push word [bp+4] ; session
push si
call DiskInitialize
;
push si
call DiskDisplay
;
mov si,[bp+4] ; session
;
LoopCommandPromptSessionRun:
;
mov di,[si+CommandPromptSession_FileInfoForDirectoryCurrent]
;
mov byte [CommandPromptSessionRunStringTemp], 0
push CommandPromptSessionRunStringTemp
push di ; fileInfo
call FileInfoPath
;
push CommandPromptSessionRunStringTemp
call DisplayString
;
push StringCommandPromptTerminator
call DisplayString
;
push CommandPromptSessionRunUserInputString
push word 22
call InputStringRead
;
; if no command was entered at the prompt
;
cmp byte [CommandPromptSessionRunUserInputString],0
jne ElseIfACommandWasEntered
;
;
jmp EndIfNoCommandEntered
;
ElseIfACommandWasEntered:
;
push word [si+CommandPromptSession_CommandCurrent]
push si ; instance
push CommandPromptSessionRunUserInputString ; stringToParse
call CommandPromptSessionRunParseCommandString
;
EndIfNoCommandEntered:
;
IfCommandEnteredIsNotValid:
mov di,[si+CommandPromptSession_CommandCurrent]
cmp word [di+0],0x0000 ; command.defn
jne ElseCommandEnteredIsValid
;
push TextCommandNotValid
call DisplayStringNewline
;
jmp EndIfCommandEnteredIsNotValid
ElseCommandEnteredIsValid:
;
push si ; instance
mov di,[si+CommandPromptSession_CommandCurrent]
push di
mov di,[di+Command_Defn]
call word [di+CommandDefn_ProcedureToCall]
;
EndIfCommandEnteredIsNotValid:
;
call DisplayNewline
;
cmp word [si+CommandPromptSession_IsTerminated],1
je EndLoopCommandPromptSessionRun
;
jmp LoopCommandPromptSessionRun
EndLoopCommandPromptSessionRun:
;
push CommandPromptSessionRunStringExiting
call DisplayStringNewline
;
pop di
pop si
pop bx
pop bp
ret 2
;
StringCommandPromptTerminator: db '>',0
TextCommandNotValid: db 'The text entered was not a valid command. Enter "help" for help.',0
CommandPromptSessionRunUserInputString: db 0x200 dup (0)
CommandPromptSessionRunStringExiting: db 'Exiting...',0
CommandPromptSessionRunStringTemp: db 0x0100 dup (0)

CommandPromptSessionRunParseCommandString:
; (returnCommand, instance, stringToParse)
;
push bp
mov bp,sp
push ax
push bx
push si
push di
;
mov di,[bp+8] ; returnCommand
mov ax,0x0000
stosw ; returnCommand.defn = null
;
push CommandPromptSessionRunParseCommandStringTokens
push word [bp+4] ; stringToParse
push StringSpace
call StringSplitOnDelimiter
;
mov si,[bp+6] ; instance
mov si,[si+CommandPromptSession_CommandPromptDefn]
mov si,[si+CommandPromptDefn_CommandDefnsAvailable]
;
ForEachCommandDefnAvailable:
;
lodsw ; ax = commandDefnsAvailable[si], si++
;
IfNoMoreCommandDefnsAvailable:
cmp ax,0x0000
je EndForEachCommandDefnAvailable
;
push CommandPromptSessionRunParseCommandStringIsMatch
mov bx,ax
push word [bx+CommandDefn_Name]
push word [CommandPromptSessionRunParseCommandStringTokens+0] ; stringToParseTokens[0]
call StringCompare
;
IfCommandDefnMatches:
;
cmp word [CommandPromptSessionRunParseCommandStringIsMatch],1
jne ElseCommandDefnDoesNotMatch
;
mov di,[bp+8] ; returnCommand
stosw ; returnCommand.defn = commandDefn
mov ax,CommandPromptSessionRunParseCommandStringTokens
stosw ; returnCommand.arguments
jmp EndForEachCommandDefnAvailable ; break
;
ElseCommandDefnDoesNotMatch:
;
jmp ForEachCommandDefnAvailable
EndForEachCommandDefnAvailable:
;
pop di
pop si
pop bx
pop ax
pop bp
ret 6
;
CommandPromptSessionRunParseCommandStringIsMatch: dw 0x0000
CommandPromptSessionRunParseCommandStringTokens: dw 0x10 dup (0)

Debug:
;
inc word [Debug_NumberOfTimesCalled]
;
push TextDebug
call DisplayString
push word [Debug_NumberOfTimesCalled]
call DisplayNumberWord
call DisplayNewline
ret
;
TextDebug: db 'debug',0
Debug_NumberOfTimesCalled: dw 0

DiskLocationConvertToLBA:
; (returnLBA, diskLocationToConvert)
;
; converts a logical block address to a cylinder-head-sector address
;
push bp
mov bp,sp
push ax
push cx
push si
push di
;
mov si,[bp+4] ; diskLocationToConvert
mov di,[si+DiskLocation_Disk]
;
mov ax,[si+DiskLocation_CylinderNumber]
mov cx,[di+Disk_HeadNumberMax]
mul cx
;
add ax,[si+DiskLocation_HeadNumber]
mov cx,[di+Disk_SectorNumberMax]
mul cx
;
add ax,[si+DiskLocation_SectorNumber]
;
mov di,[bp+6]
mov [di],ax
;
pop di
pop si
pop cx
pop ax
pop bp
ret 4

DiskLocationSetFromLBA:
; (returnDiskLocation, lbaToConvert)
;
; converts a logical block address to a cylinder-head-sector address
;
push bp
mov bp,sp
push ax
push bx
push cx
push dx
push si
push di
;
mov di,[bp+6] ; returnDiskLocation
mov si,[di+DiskLocation_Disk]
;
mov dx,0 ; dx is upper 16 bits of dividend for div instruction
mov ax,[bp+4] ; lbaToConvert
;
mov cx,[si+Disk_SectorNumberMax]
div cx ; ax = ax / disk.sectorNumberMax, dx = remainder
mov [di+DiskLocation_SectorNumber],dx
;
mov cx,[si+Disk_HeadNumberMax]
inc cx ; total number of heads
;
mov dx,0 ; dx is upper 16 bits of dividend for div instruction
div cx ; ax = ax / number of heads, dx = remainder
mov [di+DiskLocation_HeadNumber],dx
mov [di+DiskLocation_CylinderNumber],ax
;
pop di
pop si
pop dx
pop cx
pop bx
pop ax
pop bp
ret 4
;
NumberTemp: dw 0
StringTemp: db 'nnnn',0

DiskDisplay:
; (disk)
push bp
mov bp,sp
push ax
push bx
push cx
push dx
push es
push si
push di
;
mov si,[bp+4] ; disk
;
call DisplayNewline
;
push TextDiskNumber
call DisplayString
push word [si+Disk_DiskNumber]
call DisplayNumberByte
call DisplayNewline
;
push TextMaximumCylinderNumber
call DisplayString
push word [si+Disk_CylinderNumberMax]
call DisplayNumberWord
call DisplayNewline
;
push TextMaximumHeadNumber
call DisplayString
push word [si+Disk_HeadNumberMax]
call DisplayNumberWord
call DisplayNewline
;
push TextMaximumSectorNumber
call DisplayString
push word [si+Disk_SectorNumberMax]
call DisplayNumberWord
call DisplayNewline
;
mov si,[si+Disk_Filesystem]
push si
mov si,[si+Filesystem_Defn]
call word [si+FilesystemDefn_MethodDescriptorDisplay]
;
call DisplayNewline
;
pop di
pop si
pop es
pop dx
pop cx
pop bx
pop ax
pop bp
ret 2
;
TextDiskNumber: db 'Disk Number : ',0
TextMaximumHeadNumber: db 'Maximum Head Number : ',0
TextMaximumCylinderNumber: db 'Maximum Cylinder Number: ',0
TextMaximumSectorNumber: db 'Maximum Sector Number : ',0

DiskInitialize:
; (session, disk)
push bp
mov bp,sp
push ax
push bx
push cx
push dx
push es
push si
push di
;
mov si,[bp+4] ; disk
;
push es
mov bx,0
mov es,bx ; "to guard against BIOS bugs"
;
mov ah,0x08 ; read drive parameters
mov bx,si ; disk
mov dl,[bx+Disk_DiskNumber]
mov di,0 ; "to guard against BIOS bugs"
int 0x13
;
pop es
;
; if the read failed
;
jnc EndIfDiskParametersReadFailed
;
push DiskInitialize_StringError
call DisplayStringNewline
;
jmp EndCommandProcedureDiskInitialize
;
EndIfDiskParametersReadFailed:
;
mov bx,cx
push bx
;
mov cx,8
ror bx,cl
rol bh,1 ; move the two highest bits to two lowest of byte
rol bh,1
and bx,0x03FF ; low 10 bits only
mov [si+Disk_CylinderNumberMax],bx
;
pop bx
;
mov [si+Disk_HeadNumberMax],dh
;
and bx,0x3F ; low 6 bits
mov [si+Disk_SectorNumberMax],bx
;
; hack: filesystem currently hardcoded
;
mov [DiskInitialize_DiskLocationBootSector+0],si
;
mov word [si+Disk_Filesystem],CommandPromptSession0DiskFilesystem
;
mov si,[si+Disk_Filesystem]
mov si,[si+Filesystem_BootSectorBytes]
;
push si ; disk.filesystem.bootSectorBytes
push DiskInitialize_DiskLocationBootSector
call DiskLocationSectorRead
;
mov si,[bp+4] ; disk
mov si,[si+Disk_Filesystem]
push word [bp+6] ; session
push si ; filesystem
mov si,[si+Filesystem_Defn]
mov si,[si+FilesystemDefn_MethodInitialize]
call si
;
EndCommandProcedureDiskInitialize:
;
pop di
pop si
pop es
pop dx
pop cx
pop bx
pop ax
pop bp
ret 4
;
DiskInitialize_DiskLocationBootSector: dw 0, 0, 0, 1, 0
DiskInitialize_StringError: db 'An error occurred while attempting to read disk parameters.',0

DiskLocationDisplay:
; (diskLocation)
;
push bp
mov bp,sp
push bx
push cx
push si
push di
;
mov si,[bp+4] ; diskLocation
;
call DisplayParenthesisOpen
;
push TextD
call DisplayString
call DisplayColon
mov di,[si+DiskLocation_Disk]
push word [di+Disk_DiskNumber]
call DisplayNumberByte
call DisplayComma
;
push TextC
call DisplayString
call DisplayColon
push word [si+DiskLocation_CylinderNumber]
call DisplayNumberWord
call DisplayComma
;
push TextH
call DisplayString
call DisplayColon
push word [si+DiskLocation_HeadNumber]
call DisplayNumberWord
call DisplayComma
;
push TextS
call DisplayString
call DisplayColon
push word [si+DiskLocation_SectorNumber]
call DisplayNumberWord
call DisplayComma
;
push TextB
call DisplayString
call DisplayColon
push word [si+DiskLocation_ByteOffsetWithinSector]
call DisplayNumberWord
;
call DisplayParenthesisClose
;
pop di
pop si
pop cx
pop bx
pop bp
ret 2
;
TextB: db 'B',0
TextD: db 'D',0
TextC: db 'C',0
TextH: db 'H',0
TextNN: db 'nn',0
TextS: db 'S',0
TextXX: db 'xx',0

DiskLocationSectorRead:
; (returnByteBuffer, diskLocation)
push bp
mov bp,sp
push ax
push bx
push cx
push dx
push si
push di
;
mov si,[bp+4] ; diskLocation
;
mov ah,0x00 ; reset disk
mov di,[si+DiskLocation_Disk]
mov dl,[di+Disk_DiskNumber] ; drive number
int 0x13
;
mov ah,0x02 ; read sectors into memory
mov al,1 ; numberOfSectorsToRead
mov bx,[si+DiskLocation_Disk]
mov bx,[bx+Disk_DiskNumber]
mov dl,bl
;
mov bx,[si+DiskLocation_CylinderNumber]
mov ch,bl ; cylinderNumber (low 8 bits)
mov cl,bh ; cylinderNumber (high 2 bits)
and cl,0x03 ; mask all except low two bits
ror cl,1 ; shift the lowest two bits to the highest two positions
ror cl,1 ;
;
mov dh,[si+DiskLocation_HeadNumber] ; headNumber
or cl,[si+DiskLocation_SectorNumber] ; startingSectorNumber
mov bx,[bp+6] ; returnByteBuffer
int 0x13
;
; if there was an error during read
;
jnc EndIfErrorOnSectorRead
;
push TextErrorOccurredDuringReadAttempt
call DisplayStringNewline
;
jmp EndIfErrorOnSectorRead
;
EndIfErrorOnSectorRead:
;
pop di
pop si
pop dx
pop cx
pop bx
pop ax
pop bp
ret 4
;
TextErrorOccurredDuringReadAttempt: db 'An error occurred during the read attempt.',0

DiskLocationSectorAdvance:
; (diskLocation)
;
push bp
mov bp,sp
push cx
push si
push di
;
mov si,[bp+4] ; diskLocation
mov di,[si+DiskLocation_Disk]
;
inc word [si+DiskLocation_SectorNumber]
mov word [si+DiskLocation_ByteOffsetWithinSector],0
;
; if sector is out of range
;
mov cx,[di+Disk_SectorNumberMax]
cmp word [si+DiskLocation_SectorNumber],cx
jbe EndDiskLocationSectorAdvance
;
mov word [si+DiskLocation_SectorNumber],1
inc word [si+DiskLocation_HeadNumber]
;
; if head is out of range
;
mov cx,[di+Disk_HeadNumberMax]
cmp word [si+DiskLocation_HeadNumber],cx
jbe EndDiskLocationSectorAdvance
;
mov word [si+DiskLocation_HeadNumber],0
inc word [si+DiskLocation_CylinderNumber]
;
; if cylinder is out of range
;
mov cx,[di+Disk_CylinderNumberMax]
cmp word [si+DiskLocation_CylinderNumber],cx
jbe EndDiskLocationSectorAdvance
;
mov word [si+DiskLocation_CylinderNumber],0
;
EndDiskLocationSectorAdvance:
;
pop di
pop si
pop cx
pop bp
;
ret 2

DisplayBackspace:
push ax
push bx
push cx
push dx
;
mov ah,0x03 ; get the cursor position
mov bh,0x00 ; page 0
int 0x10
;
dec dl ; cursorPos.x--
;
mov ah,0x02 ; set the cursor position back
mov bh,0x00 ; page 0
int 0x10
;
call DisplaySpace
;
mov ah,0x02 ; set the cursor position back (again)
mov bh,0x00 ; page 0
int 0x10
;
pop dx
pop cx
pop bx
pop ax
ret

DisplayBytesASCII:
; (byteBuffer)
;
push bp
mov bp,sp
push bx
push cx
push si
;
mov si,[bp+4] ; byteBuffer
;
call DisplayNewline
;
mov cx,4
ForEach128BytesReadFromDiskAscii:
;
push cx
;
mov cx,4
ForEachThirtyTwoBytesReadFromDiskAscii:
;
push cx
;
mov cx,0x20
ForEachByteReadFromDiskAscii:
;
mov bl,[si]
mov bh,0
mov word [DisplayBytesASCII_StringTemp],bx
;
push DisplayBytesASCII_StringTemp
call DisplayString
;
inc si
;
loop ForEachByteReadFromDiskAscii
;
call DisplayNewline
;
pop cx
;
loop ForEachThirtyTwoBytesReadFromDiskAscii
;
call DisplayNewline
;
pop cx
;
loop ForEach128BytesReadFromDiskAscii
EndCommandProcedureReadAscii:
;
pop si
pop cx
pop bx
pop bp
ret 2
;
DisplayBytesASCII_StringTemp: db 'x',0

DisplayBytesHexadecimal:
; (byteBuffer, startIndex, numberOfBytesToDisplay)
;
push bp
mov bp,sp
push ax
push bx
push cx
push dx
push si
push di
;
mov si,[bp+8] ; byteBuffer
mov bx,si
add bx,[bp+6] ; startIndex
mov dx,bx
add dx,[bp+4] ; numberOfBytesToDisplay
dec dx
;
call DisplayNewline
;
mov cx,4
ForEach128BytesReadFromDiskHexadecimal:
;
push cx
;
mov cx,4
ForEachThirtyTwoBytesReadFromDiskHexadecimal:
;
push cx
;
mov cx,0x8
ForEachFourBytesReadFromDiskHexadecimal:
;
push cx
;
mov cx,4
ForEachByteReadFromDiskHexadecimal:
;
; if byte index is between specified start and end
;
cmp si,bx ; startIndex
jb ElseIfByteNotWithinRangeSpecified
;
cmp si,dx ; endIndex
ja ElseIfByteNotWithinRangeSpecified
;
push si ; not al
call DisplayNumberByte
;
jmp EndIfByteIsWithinRangeSpecified
;
ElseIfByteNotWithinRangeSpecified:
;
push StringDotDot
call DisplayString
;
EndIfByteIsWithinRangeSpecified:
;
inc si
;
loop ForEachByteReadFromDiskHexadecimal
;
call DisplaySpace
;
pop cx
;
loop ForEachFourBytesReadFromDiskHexadecimal
;
call DisplayNewline
;
pop cx
;
loop ForEachThirtyTwoBytesReadFromDiskHexadecimal
;
call DisplayNewline
;
pop cx
;
loop ForEach128BytesReadFromDiskHexadecimal
EndCommandProcedureReadHexadecimal:
;
pop di
pop si
pop dx
pop cx
pop bx
pop ax
pop bp
ret 6
;

DisplayAngleBracketClose:
;
push StringAngleBracketClose
call DisplayString
ret
;
StringAngleBracketClose: db '>',0

DisplayAngleBracketOpen:
;
push StringAngleBracketOpen
call DisplayString
ret
;
StringAngleBracketOpen: db '<',0

DisplayCharacter:
; (charToWrite)
;
push bp
mov bp,sp
push ax
;
mov al,[bp+4] ; charToWrite
;
mov ah,0x0E ; write character in teletype mode
int 0x10
;
pop ax
pop bp
ret 2

DisplayColon:
;
push StringColon
call DisplayString
ret
;
StringColon: db ':',0

DisplayComma:
;
push StringComma
call DisplayString
ret
;
StringComma: db ',',0

DisplayDot:
;
push StringDot
call DisplayString
ret
;
StringDot: db '.',0

DisplayEqualsSign:
;
push StringEqualsSign
call DisplayString
ret
;
StringEqualsSign: db '=',0

DisplayNewline:
;
push StringNewline
call DisplayString
ret
;
StringNewline: db 0x0D,0x0A,0

DisplayParenthesisClose:
;
push StringParenthesisClose
call DisplayString
ret
;
StringParenthesisClose: db ')',0

DisplayParenthesisOpen:
;
push StringParenthesisOpen
call DisplayString
ret
;
StringParenthesisOpen: db '(',0

DisplaySpace:
;
push StringSpace
call DisplayString
ret
;
StringSpace: db ' ',0

DisplayTab:
;
push StringTab
call DisplayString
ret
;
StringTab: db ' ',0

DisplayNumber:
; (valueToWrite, numberOfBytes)
;
push bp
mov bp,sp
;
push DisplayNumber_StringTemp
push word [bp+6] ; valueToWrite
push word [bp+4] ; numberOfBytes
call NumberConvertToStringHexadecimalImmediate
;
push DisplayNumber_StringTemp
call DisplayString
;
pop bp
ret 4
;
DisplayNumber_StringTemp: db '123456789ABCDEF',0

DisplayNumberByte:
; (valueToWrite)
;
push bp
mov bp,sp
;
push word [bp+4] ; valueToWrite
push word 1 ; numberOfBytes
call DisplayNumber
;
pop bp
ret 2

DisplayNumberWord:
; (valueToWrite)
;
push bp
mov bp,sp
;
push word [bp+4] ; valueToWrite
push word 2 ; numberOfBytes
call DisplayNumber
;
pop bp
ret 2

DisplayNumberDoubleWord:
; (valueToWrite)
;
push bp
mov bp,sp
;
push word [bp+4] ; valueToWrite
push word 4 ; numberOfBytes
call DisplayNumber
;
pop bp
ret 2

DisplayQuote:
;
push StringQuote
call DisplayString
ret
;
StringQuote: db '"',0

DisplayString:
; (stringToWrite)
;
push bp
mov bp,sp
push ax
push si
;
mov si,[bp+4] ; stringToWrite
;
mov ah,0x0E ; write character in teletype mode
;
ForEachChar:
lodsb
cmp al,0x00
je EndForEachChar
int 0x10
jmp ForEachChar
EndForEachChar:
;
pop si
pop ax
pop bp
ret 2

DisplayStringForFAT16FileName:
; (stringToWrite, maxLengthOfString)
;
push bp
mov bp,sp
push ax
push cx
push si
;
mov si,[bp+6] ; stringToWrite
;
mov ah,0x0E ; write character in teletype mode
;
mov cx,[bp+4] ; maxLengthOfString
DisplayStringForFAT16FileName_ForEachChar:
;
lodsb
cmp al,0x20 ; space ends the string
je DisplayStringForFAT16FileName_EndForEachChar
;
int 0x10
;
loop DisplayStringForFAT16FileName_ForEachChar
;
DisplayStringForFAT16FileName_EndForEachChar:
;
pop si
pop cx
pop ax
pop bp
ret 4

DisplayStringNewline:
; (stringToWrite)
;
pop word [DisplayStringNewlineReturnAddress]
;
call DisplayString
call DisplayNewline
;
push word [DisplayStringNewlineReturnAddress]
ret
;
DisplayStringNewlineReturnAddress: dw 0x0000

DisplayStringQuoted:
; (stringToWrite)
;
push bp
mov bp,sp
;
call DisplayQuote
push word [bp+4]
call DisplayString
call DisplayQuote
;
pop bp
ret 2
;

FileInfoCopy:
; (fileInfoTarget, fileInfoSource)
;
push bp
mov bp,sp
push ax
push cx
push si
push di
;
mov si,[bp+4] ; fileInfoSource
mov di,[bp+6] ; fileInfoTarget
;
mov cx,FileInfo_SizeInBytesFull
rep movsb
;
pop di
pop si
pop cx
pop ax
pop bp
ret 4

FileInfoDisplay:
; (fileInfo)
;
push bp
mov bp,sp
push si
push di
;
mov si,[bp+4] ; fileInfo
;
call DisplayParenthesisOpen
;
push TextDiskNumber_Short
call DisplayString
call DisplayColon
mov di,[si+FileInfo_Disk]
push word [di+Disk_DiskNumber]
call DisplayNumberByte
call DisplayComma
;
push TextClusterIndexStart
call DisplayString
call DisplayColon
push word [si+FileInfo_ClusterIndexStart]
call DisplayNumberWord
call DisplayComma
;
push TextName
call DisplayString
call DisplayColon
;
mov di,si
add di,FileInfo_NameImmediate
push di
call DisplayString
;
call DisplayParenthesisClose
;
pop di
pop si
pop bp
ret 2
;
TextClusterIndexStart: db 'ClusterIndexStart',0
TextDiskNumber_Short: db 'DiskNumber',0
TextName: db 'Name',0

FileInfoDisplayContents:
; (session, fileInfo)
;
push bp
mov bp,sp
push si
push di
;
mov si,[bp+4] ; fileInfo
;
mov di,[bp+4] ; fileInfo
mov di,[di+FileInfo_Disk]
mov di,[di+Disk_Filesystem]
push word di ; filesystem
push FileInfoSearchAsDirectory_DiskLocation
push word [si+FileInfo_ClusterIndexStart]
call FilesystemFAT16SetDiskLocationFromClusterIndex
;
push FileInfoSearchAsDirectory_ByteBufferForSector
push FileInfoSearchAsDirectory_DiskLocation
call DiskLocationSectorRead
;
mov si,FileInfoSearchAsDirectory_ByteBufferForSector
FileInfoDisplayContents_While:
;
lodsb
cmp ax,0
je FileInfoDisplayContents_While_End
;
push ax
call DisplayCharacter
;
jmp FileInfoDisplayContents_While
FileInfoDisplayContents_While_End:
;
pop di
pop si
pop bp
ret 4

FileInfoPath:
; (returnString, fileInfo)
;
push bp
mov bp,sp
push si
push di
push bx
;
mov di,[bp+6] ; returnString
mov si,[bp+4] ; fileInfo
;
FileInfoDisplayPath_While:
;
cmp si,0
je FileInfoDisplayPath_While_End
;
push di
push word FileInfoPath_Delimiter
call StringPrepend
;
mov bx,si
add bx,FileInfo_NameImmediate
push di
push bx
call StringPrepend
;
mov si,[si+FileInfo_DirectoryParent]
;
jmp FileInfoDisplayPath_While
;
FileInfoDisplayPath_While_End:
;
pop bx
pop di
pop si
pop bp
ret 4
;
FileInfoPath_Delimiter: db '/',0

FileInfoSearchAsDirectory:
; (session, fileInfoForDirectory, fileInfoForResult, nameOfFileToFind, operationToPerform)
push bp
mov bp,sp
push si
push di
push ax
push bx
push cx
push dx
;
mov si,[bp+0xA] ; fileInfoForDirectory
;
mov di,[si+FileInfo_Disk]
;
mov [FileInfoSearchAsDirectory_DiskLocation+DiskLocation_Disk],di
;
mov di,[di+Disk_Filesystem]
push word di ; filesystem
push FileInfoSearchAsDirectory_DiskLocation
push word [si+FileInfo_ClusterIndexStart]
call FilesystemFAT16SetDiskLocationFromClusterIndex
;
mov di,[di+Filesystem_Descriptor]
mov ax,[di+FilesystemFAT16Descriptor_RootDirectoryEntriesMax]
mov cx,FilesystemFAT16_BytesPerDirectoryEntry
mul cx
mov cx,[di+FilesystemFAT16Descriptor_BytesPerSector]
div cx
mov dx,ax
;
WhileDirectorySectorsRemain:
;
push FileInfoSearchAsDirectory_ByteBufferForSector
push FileInfoSearchAsDirectory_DiskLocation
call DiskLocationSectorRead
;
mov si,FileInfoSearchAsDirectory_ByteBufferForSector
;
mov cx,[di+FilesystemFAT16Descriptor_DirectoryEntriesPerSector]
WhileDirectoryEntriesRemain:
;
cmp byte [si],FilesystemFAT16DirectoryEntry_EndOfDirectory
je WhileDirectorySectorsRemain_End
;
push word [bp+0xA] ; fileInfoForDirectory
mov bx,[bp+8] ; fileInfoForResult
push bx
push si ; directoryEntry
call FileInfoSearchAsDirectory_DirectoryEntry
;
IfFileNameToFindSpecified:
;
mov bx,[bp+6] ; nameOfFileToFind
cmp bx,0
je IfFileNameToFindSpecified_Else
;
push FileInfoSearchAsDirectory_DoesFileNameMatch
mov bx,[bp+8] ; fileInfoForResult
add bx,FileInfo_NameImmediate
push bx ; nameOfFileCurrent
push word [bp+6] ; nameOfFileToFind
call StringCompare
;
jmp IfFileNameToFindSpecified_End
;
IfFileNameToFindSpecified_Else:
;
mov word [FileInfoSearchAsDirectory_DoesFileNameMatch],1
;
IfFileNameToFindSpecified_End:
;
IfFileNameMatches:
;
cmp word [FileInfoSearchAsDirectory_DoesFileNameMatch],1
jne IfFileNameMatches_End
;
push word [bp+0xC] ; session
push word [bp+8] ; fileInfoForResult
call word [bp+4] ; operationToPerform
;
IfFileNameMatches_End:
;
add si,FilesystemFAT16_BytesPerDirectoryEntry
;
loop WhileDirectoryEntriesRemain
;
WhileDirectoryEntriesRemain_End:
;
push FileInfoSearchAsDirectory_LBA
push FileInfoSearchAsDirectory_DiskLocation
call DiskLocationConvertToLBA
;
inc word [FileInfoSearchAsDirectory_LBA]
;
push FileInfoSearchAsDirectory_DiskLocation
push word [FileInfoSearchAsDirectory_LBA]
call DiskLocationSetFromLBA
;
dec dx
cmp dx,0
jne WhileDirectorySectorsRemain
;
WhileDirectorySectorsRemain_End:
;
pop dx
pop cx
pop bx
pop ax
pop di
pop si
pop bp
ret 0xA
;
FileInfoSearchAsDirectory_DoesFileNameMatch: dw 0
FileInfoSearchAsDirectory_LBA: dw 0
FileInfoSearchAsDirectory_ByteBufferForSector: db 0x200 dup (0)
FileInfoSearchAsDirectory_DiskLocation: db DiskLocation_SizeInBytes dup (0)

FileInfoSearchAsDirectory_DirectoryEntry:
; (fileInfoForDirectory, fileInfoForEntry, directoryEntryBytes)
push bp
mov bp,sp
push bx
push si
push di
;
mov si,[bp+4] ; directoryEntryBytes
;
mov word [bx+FileInfo_IsHidden],0
;
cmp byte [si],FilesystemFAT16DirectoryEntry_IsDeleted ; entry has been erased
je FileInfoSearchAsDirectory1_SetIsHidden
;
cmp byte [si],FilesystemFAT16DirectoryEntry_FileNameStartsWithE5
je FileInfoSearchAsDirectory1_SetIsHidden ; ignore for now
;
cmp byte [si],FilesystemFAT16DirectoryEntry_FileNameIsDotOrDotDot
je FileInfoSearchAsDirectory1_SetIsHidden ; ignore for now
;
mov di,si
add di,FilesystemFAT16DirectoryEntry_Attributes
;
cmp byte [di],FilesystemFAT16DirectoryEntry_Attributes_VolumeLabel
je FileInfoSearchAsDirectory1_SetIsHidden ; ignore for now
;
cmp byte [di],FilesystemFAT16DirectoryEntry_Attributes_VFATLongFileName
je FileInfoSearchAsDirectory1_SetIsHidden ; ignore for now
;
mov bx,[bp+6] ; fileInfoForEntry
mov word [bx+FileInfo_IsDirectory],0
IfIsSubdirectory:
;
cmp byte [di],FilesystemFAT16DirectoryEntry_Attributes_Subdirectory
jne IfIsSubdirectory_End
;
mov word [bx+FileInfo_IsDirectory],1
;
IfIsSubdirectory_End:
;
mov di,[bp+6] ; fileInfoForEntry
add di,FileInfo_NameImmediate
push di ; destination string
mov bx,si
add bx,FilesystemFAT16DirectoryEntry_ShortFileName
push bx ; source string
push word 0 ; fromIndex
push word FileInfo_FileStem_Length ; toIndex
call StringCopyFromTo
;
push di
call StringTrim
;
mov bx,[bp+6] ; fileInfoForEntry
;
push di
mov di,[si+FilesystemFAT16DirectoryEntry_ClusterIndexFirst]
mov [bx+FileInfo_ClusterIndexStart],di
pop di
;
IfIsFileRatherThanDirectory:
cmp word [bx+FileInfo_IsDirectory],1
je IfIsFileRatherThanDirectory_Else
;
push di
push StringDot
call StringAppend
;
push FileInfoSearchAsDirectory1_StringTemp
mov bx,si
add bx,FilesystemFAT16DirectoryEntry_ShortFileExtension
push bx ; source string
push word 0 ; fromIndex
push word FileInfo_FileExtension_Length ; toIndex
call StringCopyFromTo
;
push di
push FileInfoSearchAsDirectory1_StringTemp
call StringAppend
;
push di
call StringLowercase ; hack
;
jmp IfIsFileRatherThanDirectory_End
;
IfIsFileRatherThanDirectory_Else:
;
; call Todo
;
IfIsFileRatherThanDirectory_End:
;
jmp FileInfoSearchAsDirectory1_SetIsHidden_End
;
FileInfoSearchAsDirectory1_SetIsHidden:
;
mov bx,[bp+6] ; fileInfoForEntry
mov word [bx+FileInfo_IsHidden],1
;
FileInfoSearchAsDirectory1_SetIsHidden_End:
;
pop di
pop si
pop bx
pop bp
ret 6
;
FileInfoSearchAsDirectory1_StringLength: dw 0
FileInfoSearchAsDirectory1_StringTemp: db 0x10 dup (0)

FileInfoDirectoryChange:
; (session, fileInfoForDirectoryToChangeTo)
;
push bp
mov bp,sp
push bx
push si
push di
;
mov si,[bp+6] ; session
;
mov bx,[si+CommandPromptSession_FileInfoForDirectoryCurrent]
;
mov di,bx
add di,FileInfo_SizeInBytesFull
;
push di
push word [bp+4] ; fileInfoForDirectoryToChangeTo
call FileInfoCopy
;
mov [di+FileInfo_DirectoryParent],bx
;
mov [si+CommandPromptSession_FileInfoForDirectoryCurrent],di
;
pop di
pop si
pop bx
pop bp
ret 4

FileInfoDisplayFileName:
; (session, fileInfo)
;
push bp
mov bp,sp
push si
push di
;
mov si,[bp+4] ; fileInfo
;
FileInfoDisplayFileName_IsHidden:
;
cmp word [si+FileInfo_IsHidden],1
je FileInfoDisplayFileName_End
;
FileInfoDisplayFileName_IsHidden_End:
;
mov di,si
add di,FileInfo_NameImmediate
push di
call DisplayString
;
FileInfoDisplayFileName_IfIsDirectory:
;
cmp word [si+FileInfo_IsDirectory],1
jne FileInfoDisplayFileName_IfIsDirectory_End
;
call DisplayAngleBracketOpen
push TextDIR
call DisplayString
call DisplayAngleBracketClose
;
FileInfoDisplayFileName_IfIsDirectory_End:
;
call DisplayNewline
;
FileInfoDisplayFileName_End:
;
pop di
pop si
pop bp
ret 4
;
TextDIR: db 'DIR',0

FilesystemDefnFAT16:
dw FilesystemDefnFAT16Name, FilesystemDefnFAT16Initialize, FilesystemDefnFAT16DescriptorDisplay
FilesystemDefnFAT16Name: db 'FAT16', 0

FilesystemDefnFAT16DescriptorDisplay:
; (filesystem)
push bp
mov bp,sp
push si
;
push TextFilesystemType
call DisplayString
;
mov si,[bp+4] ; filesystem
mov si,[si+Filesystem_Defn]
push word [si+FilesystemDefn_Name]
call DisplayStringNewline
;
mov si,[bp+4] ; filesystem
mov si,[si+Filesystem_Descriptor] ; filesystem.descriptor
;
push TextBytesPerSector
call DisplayString
push word [si+FilesystemFAT16Descriptor_BytesPerSector]
call DisplayNumberWord
call DisplayNewline
;
push TextSectorsPerCluster
call DisplayString
push word [si+FilesystemFAT16Descriptor_SectorsPerCluster]
call DisplayNumberByte
call DisplayNewline
;
push TextReservedSectors
call DisplayString
push word [si+FilesystemFAT16Descriptor_NumberOfReservedSectors]
call DisplayNumberWord
call DisplayNewline
;
push TextNumberOfFATs
call DisplayString
push word [si+FilesystemFAT16Descriptor_NumberOfFATs]
call DisplayNumberByte
call DisplayNewline
;
push TextRootDirectoryEntriesMax
call DisplayString
push word [si+FilesystemFAT16Descriptor_RootDirectoryEntriesMax]
call DisplayNumberWord
call DisplayNewline
;
push TextSectorsTotal
call DisplayString
push word [si+FilesystemFAT16Descriptor_SectorsTotal]
call DisplayNumberDoubleWord
call DisplayNewline
;
push TextSectorsPerFAT
call DisplayString
push word [si+FilesystemFAT16Descriptor_SectorsPerFAT]
call DisplayNumberWord
call DisplayNewline
;
push TextSectorsPerTrack
call DisplayString
push word [si+FilesystemFAT16Descriptor_SectorsPerTrack]
call DisplayNumberWord
call DisplayNewline
;
push TextNumberOfHeads
call DisplayString
push word [si+FilesystemFAT16Descriptor_NumberOfHeads]
call DisplayNumberWord
call DisplayNewline
;
push TextNumberOfHiddenSectors
call DisplayString
push word [si+FilesystemFAT16Descriptor_NumberOfHiddenSectors]
call DisplayNumberWord
call DisplayNewline
;
push TextRootDirectorySector
call DisplayString
push word [si+FilesystemFAT16Descriptor_RootDirectorySectorLBA]
call DisplayNumberWord
call DisplayNewline
;
push TextDataStartingSector
call DisplayString
push word [si+FilesystemFAT16Descriptor_DataStartingSectorLBA]
call DisplayNumberWord
call DisplayNewline
;
pop si
pop bp
ret 2
;
CommandProcedureFilesystem_NumberTemp: dw 0x1234
CommandProcedureFilesystem_StringTemp: db 'nnnnnnnn',0
TextBytesPerSector: db 'Bytes per Sector : ',0
TextDataStartingSector: db 'Data Starting Sector : ',0
TextFAT16: db 'FAT16',0
TextFilesystemType: db 'Filesystem Type : ',0
TextReservedSectors: db 'Reserved Sectors : ',0
TextNumberOfFATs: db 'FATs : ',0
TextNumberOfHeads: db 'Heads : ',0
TextNumberOfHiddenSectors: db 'Hidden Sectors : ',0
TextRootDirectoryEntriesMax: db 'Root Dir Entries Max : ',0
TextRootDirectorySector: db 'Root Dir Sector : ',0
TextSectorsPerCluster: db 'Sectors per Cluster : ',0
TextSectorsTotal: db 'Sectors Total : ',0
TextSectorsPerFAT: db 'Sectors per FAT : ',0
TextSectorsPerTrack: db 'Sectors per Track : ',0

FilesystemFAT16SetDiskLocationFromClusterIndex:
; (filesystem, diskLocation, clusterIndex)
push bp
mov bp,sp
push ax
push cx
push si
push di
;
mov si,[bp+8] ; filesystem
mov si,[si+Filesystem_Descriptor]
mov ax,[bp+4] ; clusterIndex
;
IfClusterIndexIsZero:
;
cmp ax,0
jne IfClusterIndexIsZero_End
;
mov ax,[si+FilesystemFAT16Descriptor_RootDirectorySectorLBA]
jmp FilesystemFAT16SetDiskLocationFromClusterIndex_NearEnd
;
IfClusterIndexIsZero_End:
;
sub ax,FilesystemFAT16_ClusterIndexFirst
;
mov cx,0
mov cl,[si+FilesystemFAT16Descriptor_SectorsPerCluster]
mul cx ; ax *= cx
;
add ax,[si+FilesystemFAT16Descriptor_DataStartingSectorLBA]
;
FilesystemFAT16SetDiskLocationFromClusterIndex_NearEnd:
;
push word [bp+6] ; diskLocation
push ax ; lba
call DiskLocationSetFromLBA
;
pop di
pop si
pop cx
pop ax
pop bp
ret 6

FilesystemDefnFAT16Initialize:
; (session, filesystem)
push bp
mov bp,sp
push ax
push bx
push cx
push dx
push si
push di
;
mov di,[bp+4] ; filesystem
mov di,[di+Filesystem_Descriptor]
;
mov si,[bp+4] ; filesystem
mov si,[si+Filesystem_BootSectorBytes] ; filesystem.bootSectorBytes
;
mov ax,[si+FilesystemFAT16_BytesPerSector]
mov [di+FilesystemFAT16Descriptor_BytesPerSector],ax
;
mov ax,[si+FilesystemFAT16_SectorsPerCluster]
mov [di+FilesystemFAT16Descriptor_SectorsPerCluster],ax
;
mov ax,[si+FilesystemFAT16_NumberOfReservedSectors]
mov [di+FilesystemFAT16Descriptor_NumberOfReservedSectors],ax
;
mov ax,[si+FilesystemFAT16_NumberOfFATs]
mov [di+FilesystemFAT16Descriptor_NumberOfFATs],ax
;
mov ax,[si+FilesystemFAT16_RootDirectoryEntriesMax]
mov [di+FilesystemFAT16Descriptor_RootDirectoryEntriesMax],ax
;
mov ax,[si+FilesystemFAT16_SectorsTotal]
mov [di+FilesystemFAT16Descriptor_SectorsTotal],ax
;
mov ax,[si+FilesystemFAT16_SectorsPerFAT]
mov [di+FilesystemFAT16Descriptor_SectorsPerFAT],ax
;
mov ax,[si+FilesystemFAT16_SectorsPerTrack]
mov [di+FilesystemFAT16Descriptor_SectorsPerTrack],ax
;
mov ax,[si+FilesystemFAT16_NumberOfHeads]
mov [di+FilesystemFAT16Descriptor_NumberOfHeads],ax
;
mov ax,[si+FilesystemFAT16_NumberOfHiddenSectors]
mov [di+FilesystemFAT16Descriptor_NumberOfHiddenSectors],ax
;
mov cx,[di+FilesystemFAT16Descriptor_NumberOfFATs]
mov ax,[di+FilesystemFAT16Descriptor_SectorsPerFAT]
mul cx
add ax,[di+FilesystemFAT16Descriptor_NumberOfReservedSectors]
;
; hidden sectors don't seem to count
;
add ax,1 ; for the boot sector
;
mov [di+FilesystemFAT16Descriptor_RootDirectorySectorLBA],ax
;
; ax still contains the root directory sector
mov bx,[di+FilesystemFAT16Descriptor_RootDirectoryEntriesMax]
push ax
mov ax,bx
mov cx,FilesystemFAT16_BytesPerDirectoryEntry
mul cx
mov cx,[di+FilesystemFAT16Descriptor_BytesPerSector]
div cx
mov bx,ax
pop ax
add ax,bx
;
mov bx,[di+FilesystemFAT16Descriptor_NumberOfHiddenSectors]
; todo - Do hidden sectors count?
;
mov [di+FilesystemFAT16Descriptor_DataStartingSectorLBA],ax
;
mov ax,[di+FilesystemFAT16Descriptor_BytesPerSector]
mov cx,0
mov cl,FilesystemFAT16_BytesPerDirectoryEntry
div cx ; ax /= cx
mov [di+FilesystemFAT16Descriptor_DirectoryEntriesPerSector],ax
;
mov si,[bp+6] ; session
mov si,[si+CommandPromptSession_FileInfoForDirectoryCurrent]
; in FAT16, root directory is "special", sadly
mov word [si+FileInfo_ClusterIndexStart],0
;
;
pop di
pop si
pop dx
pop cx
pop bx
pop ax
pop bp
ret 4
;

FilesystemFAT16SetFileInfoClusterIndexFromLBA:
; (filesystem, fileInfo, lba)
push bp
mov bp,sp
push ax
push cx
push si
push di
;
mov si,[bp+8] ; filesystem
mov si,[si+Filesystem_Descriptor]
mov ax,[bp+4] ; lba
;
sub ax,[si+FilesystemFAT16Descriptor_DataStartingSectorLBA]
;
mov cx,0
mov cl,[si+FilesystemFAT16Descriptor_SectorsPerCluster]
div cx ; ax /= cx;
;
add ax,FilesystemFAT16_ClusterIndexFirst
;
mov di,[bp+6] ; fileInfo
mov [di+FileInfo_ClusterIndexStart],ax
;
pop di
pop si
pop cx
pop ax
pop bp
ret 6

InputKeyPressed:
db 0x00,0x00

InputKeyRead:
push ax
;
mov ah,0x01 ; check for keystroke
int 0x16
;
jz EndIfKeyPressed
;
mov [InputKeyPressed],al
;
mov ah,0x00 ; remove the keystroke from the buffer
int 0x16
;
EndIfKeyPressed:
;
pop ax
ret

InputStringRead:
; (returnString, numberOfCharactersMax)
;
push bp
mov bp,sp
push ax
push bx
push cx
push di
;
mov di,[bp+6] ; returnString
mov cx,0
;
WhileCharEnteredIsNotReturn:
;
call InputKeyRead
;
mov ax,[InputKeyPressed]
mov word [InputKeyPressed],0x0000
cmp al,0x00
je NoKeyPressed
;
IfKeyReturn:
cmp al,0xD
jne IfKeyBackspace
;
mov al,0x00
stosb
;
call DisplayNewline
;
jmp EndWhileCharEnteredIsNotReturn
IfKeyBackspace:
cmp al,0x8
jne IfKeyOther
;
; IfNotAtBeginningOfLineAlready:
cmp di,[bp+6]
jle EndIfNotAtBeginningOfLineAlready
;
dec di ; back up the cursor
dec cx
;
call DisplayBackspace
;
EndIfNotAtBeginningOfLineAlready:
;
jmp EndIfKey
IfKeyOther:
cmp word cx,[bp+4] ; numberOfCharactersMax
jae WhileCharEnteredIsNotReturn
;
push ax
call DisplayCharacter
stosb
;
inc cx
;
EndIfKey:
;
NoKeyPressed:
;
jmp WhileCharEnteredIsNotReturn
EndWhileCharEnteredIsNotReturn:
;
pop di
pop cx
pop bx
pop ax
pop bp
ret 4

NumberConvertToStringHexadecimalImmediate:
; (returnString, valueToConvert, numberOfBytesToConvert)
;
push bp
mov bp,sp
push ax
;
mov ax,[bp+6]
mov [NumberConvertToStringHexadecimalImmediate_Temp],ax
;
push word [bp+8] ; returnString
push NumberConvertToStringHexadecimalImmediate_Temp
push word [bp+4] ; numberOfBytesToConvert
call NumberConvertToStringHexadecimal
;
pop ax
pop bp
ret 6
;
NumberConvertToStringHexadecimalImmediate_Temp: dw 0

NumberConvertToStringHexadecimal:
; (returnString, addressOfValueToConvert, numberOfBytesToConvert)
;
push bp
mov bp,sp
push ax
push bx
push cx
push dx
push si
push di
;
mov si,[bp+6] ; addressOfValueToConvert
;
mov di,[bp+8] ; di = returnString
add di,[bp+4] ; numberOfBytesToConvert
add di,[bp+4] ; di = end of string (2 chars per byte)
dec di ; account for null terminator
;
mov cx,[bp+4] ; numberOfBytesToConvert
ForEachByte:
;
push cx
lodsb ; ax = byte to convert, si = next byte
mov cx,0x0002 ; nibbles per byte
std ; reverse direction
ForEachNibble:
;
push ax ; save the original byte value
;
; shift bits 4 over if on second nibble
cmp cx,0x0001
jne DoNotShift
shr ax,1
shr ax,1
shr ax,1
shr ax,1
DoNotShift:
;
and ax,0x000F ; mask all but last 4 bits
;
cmp ax,0x000A
jb EndIfNibbleGreaterThan9
; if nibble > 9
add ax,0x0007 ; jump from numerals to letters
EndIfNibbleGreaterThan9:
add ax,0x0030 ; ascii '0'
;
stosb ; returnValue += nibble char, di--
;
pop ax ; restore the original byte value
;
loop ForEachNibble
cld ; restore forward direction
pop cx
;
loop ForEachByte
; append a null
mov di,[bp+8] ; di = returnString
add di,[bp+4]
add di,[bp+4] ; di = end of string (2 chars per byte)
mov ax,0x0000
stosb
;
pop di
pop si
pop dx
pop cx
pop bx
pop ax
pop bp
ret 6

NumberParseFromString:
; (returnValue, stringToParse)
push bp
mov bp,sp
push ax
push cx
push dx
push si
push di
;
mov dx,0 ; returnValue = 0
;
mov si,[bp+4] ; stringToParse
mov ax,0
;
ForEachCharInStringToParse:
;
lodsb
;
cmp al,0
je EndForEachCharInStringToParse
;
; if digit to parse is a lowercase letter
cmp al,0x60
jb EndIfDigitToParseIsLowercase
;
sub al,0x20 ; make it uppercase
;
EndIfDigitToParseIsLowercase:
;
; if digit to parse is a letter
cmp al,0x40
jb EndIfDigitToParseIsAOrGreater
;
sub ax,0x7 ; jump from ascii letters back to numerals
;
EndIfDigitToParseIsAOrGreater:
;
sub al,0x30 ; ascii code of 0
;
mov cx,4
shl dx,cl
;
add dx,ax
;
jmp ForEachCharInStringToParse
EndForEachCharInStringToParse:
;
mov di,[bp+6] ; returnValue
mov [di],dx ; returnValue = dx
;
pop di
pop si
pop dx
pop cx
pop ax
pop bp
ret 4

StringAppend:
; (stringToAppendTo, stringToAppend)
push bp
mov bp,sp
push ax
push cx
push si
push di
;
mov si,[bp+6] ; stringToAppendTo
;
push StringAppendLengthOriginal
push si
call StringLength
;
add si,[StringAppendLengthOriginal]
;
push StringAppendLengthOriginal
push word [bp+4] ; stringToAppend
call StringLength
;
mov di,si ; di = end of stringToAppendTo
mov si,[bp+4] ; si = stringToAppend
mov cx,[StringAppendLengthOriginal]
inc cx
rep movsb
;
pop di
pop si
pop cx
pop ax
pop bp
ret 4
;
StringAppendLengthOriginal:
dw 0x0000

StringCompare:
; (returnValue, string0, string1)
;
;
push bp
mov bp,sp
push ax
push bx
push cx
push si
push di
;
mov di,[bp+8]
mov word [di],0 ; returnValue = false
;
mov si,[bp+6] ; string0
mov di,[bp+4] ; string1
;
; determine the length of string0 first (hack)
; also, now that StringLength exists, should use that
;
mov bx,si
FindLength:
lodsb
cmp al,0x00
jne FindLength
mov cx,si
sub cx,bx
mov si,bx ; reset si
;
inc cx
;
repe cmpsb
;
mov di,[bp+8]
;
; if strings are equal
cmp cx,0
jne ElseStringsAreNotEqual
;
mov word [di],1 ; returnValue = true
;
jmp EndIfStringsAreEqual
;
ElseStringsAreNotEqual:
;
mov word [di],0 ; returnValue = false
;
EndIfStringsAreEqual:
;
pop di
pop si
pop cx
pop bx
pop ax
pop bp
ret 6

StringCopy:
; (returnValue, stringToCopyFrom)
push bp
mov bp,sp
push ax
push si
push di
;
mov si,[bp+4] ; stringToCopyFrom
mov di,[bp+6] ; returnValue
;
ForEachCharStringCopy:
lodsb
stosb
cmp al,0x00
je EndForEachCharStringCopy
; if char is null, break
jmp ForEachCharStringCopy
EndForEachCharStringCopy:
;
pop di
pop si
pop ax
pop bp
ret 4

StringCopyFromTo:
; (returnValue, stringToCopyFrom, characterIndexFrom, characterIndexTo)
push bp
mov bp,sp
push ax
push cx
push si
push di
;
mov si,[bp+0x8] ; stringToCopyFrom
mov di,[bp+0xA] ; returnValue
;
mov ax,[bp+6] ; characterIndexFrom
mov cx,[bp+4] ; characterIndexTo
sub cx,ax
add si,ax
;
ForEachCharStringCopyFromTo:
lodsb
;
cmp al,0x00
je EndForEachCharStringCopyFromTo
;
stosb
;
loop ForEachCharStringCopyFromTo
EndForEachCharStringCopyFromTo:
;
mov al,0x00
stosb
;
pop di
pop si
pop cx
pop ax
pop bp
ret 8

StringJoinMany:
; (returnString, stringsToJoin, delimiter)
push bp
mov bp,sp
push ax
push dx
push si
push di
;
mov si,[bp+4] ; delimiter
mov dx,[si] ; single delimiter for now
mov si,[bp+6] ; stringsToJoin
mov di,[bp+8] ; returnString
ForEachStringToJoin:
lodsw
cmp ax,0x0000
je EndForEachStringToJoin
; if string is null, break
; else
push si
mov si,ax ; first char of string to join
ForEachCharInStringToJoin:
lodsb
cmp al,0x00
jne ElseFESTJ
; if char is null
mov ax,dx ; delimiter
stosb
jmp EndForEachCharInStringToJoin
ElseFESTJ:
stosb
; endIf
jmp ForEachCharInStringToJoin
EndForEachCharInStringToJoin:
pop si
;endIf
jmp ForEachStringToJoin
EndForEachStringToJoin:
; terminate with a null
mov al,0x00
stosb
;
pop di
pop si
pop dx
pop ax
pop bp
ret 6

StringLength:
; (returnValue, stringToFindLengthOf)
;
;
push bp
mov bp,sp
push ax
push bx
push cx
push si
;
mov si,[bp+4] ; stringToFindLengthOf
mov bx,si
FindStringLength:
lodsb
cmp al,0x00
jne FindStringLength
mov cx,si
sub cx,bx
sub cx,0x0001 ; don't count the terminating null
;
mov si,[bp+6] ; returnValue
mov [si+0],cx ; returnValue (or returnValue.x) = cx
;
pop si
pop cx
pop bx
pop ax
pop bp
ret 4

StringLowercase:
; (stringToConvert)
push bp
mov bp,sp
push ax
push si
push di
;
mov si,[bp+4]
mov di,[bp+4]
;
StringLowercase_WhileCharactersRemain:
;
lodsb
;
StringLowercase_IfIsUppercase:
;
cmp al,0x40
jl StringLowercase_IfIsUppercase_End
cmp al,0x5A
jg StringLowercase_IfIsUppercase_End
;
add al,0x20
;
StringLowercase_IfIsUppercase_End:
;
stosb
;
cmp ax,0
je StringLowercase_WhileCharactersRemain_End
;
jmp StringLowercase_WhileCharactersRemain
;
StringLowercase_WhileCharactersRemain_End:
;
pop di
pop si
pop ax
pop bp
ret 2

StringPrepend:
; (stringToPrependTo, stringToPrepend)
push bp
mov bp,sp
push ax
push cx
push dx
push si
push di
;
push StringPrependLengthOriginal
push word [bp+6] ; stringToPrependTo
call StringLength
mov cx,[StringPrependLengthOriginal]
;
push StringPrependLengthOriginal
push word [bp+4] ; stringToPrepend
call StringLength
mov dx,[StringPrependLengthOriginal] ; not counting the null terminator yet
;
mov si,[bp+6] ; stringToPrependTo
add si,cx ; advance to end of stringToPrependTo
mov di,si
add di,dx ; advance even further to make room for stringToPrepend
inc cx
;
std ; reverse direction
rep movsb ; shift the stringToPrependTo forward by stringToPrepend.length
cld ; forward direction
;
mov si,[bp+4] ; stringToPrepend
mov di,[bp+6] ; stringToPrependTo
mov cx,dx ; stringToPrepend.length
inc dx
rep movsb
;
pop di
pop si
pop dx
pop cx
pop ax
pop bp
ret 4
;
StringPrependLengthOriginal:
dw 0x0000

StringSplitOnDelimiter:
; (returnStringArray, stringToSplit, delimiter)
;
; modifies the original string
;
push bp
mov bp,sp
push ax
push bx
push si
push di
;
mov bx,[bp+4] ; delimiter
mov bx,[bx] ; single delimiter for now
mov si,[bp+6] ; stringToSplit
mov di,[bp+8] ; returnStringArray
;
mov ax,si
stosw ; returnStrings[0] = stringToParse[0], di++
;
ForEachCharInStringToSplit:
lodsb
; IfCharIsNull:
cmp al,0x00
je EndForEachCharInStringToSplit ; break
; ElseIfCharIsDelimiter:
cmp al,bl ; delimiter
jne EndIfCharIsDelimiter
;
mov ax,si ; returnStrings[di] = beginning of next string, di++
stosw
;
push di
;
mov di,si ; in the original string
dec di
mov al,0x00
stosb ; replace the delimiter with a null
;
pop di
;
EndIfCharIsDelimiter:
jmp ForEachCharInStringToSplit
EndForEachCharInStringToSplit:
;
mov ax,0x0000 ; terminate returnStrings with a null
stosw
;
pop di
pop si
pop bx
pop ax
pop bp
ret 6

StringTrim:
; (stringToTrim)
;
push bp
mov bp,sp
push ax
push si
push di
;
mov si,[bp+4] ; stringToTrim
mov di,[bp+4] ; stringToTrim
;
StringTrim_WhileNotNull:
;
lodsb
;
cmp al,0
je StringTrim_WhileNotNull_End
;
StringTrim_IfNotSpace:
;
cmp al,0x20
je StringTrim_IfNotSpace_End
;
stosb
;
StringTrim_IfNotSpace_End:
;
jmp StringTrim_WhileNotNull
;
StringTrim_WhileNotNull_End:
;
mov al,0
stosb
;
pop di
pop si
pop ax
pop bp
ret 2

Todo:
;
push TextTodo
call DisplayStringNewline
ret
;
TextTodo: db 'todo',0

PadOutWithZeroesSectorsThreeThroughN:
times ((0x8000) - ($ - $$)) db 0x00

EndOfSectorsThreeThroughN:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Advertisements
This entry was posted in Uncategorized and tagged , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s