Page 1 of 1

MASM : Disc Housekeeping Serial Access

#1 Martyn.Rae  Icon User is offline

  • The programming dinosaur
  • member icon

Reputation: 540
  • View blog
  • Posts: 1,406
  • Joined: 22-August 09

Posted 25 April 2011 - 08:24 AM

MASM : Disc Housekeeping Serial Access

Introduction

This tutorial (others will follow), provides the dynamic link library code for a set of routines that permit the developer to read and write variable length data to a disc file.

The concept used is some forty years old, and is loosely based upon the ICL 1900 disc housekeeping serial access method. Now, you may well see, if you scrutinize the code, that what I have written is ever so slightly over the top for a set of disc housekeeping routines that permit you to read and write variable length records. Indeed, you would be one hundred percent correct is stating this, but you are seeing a fraction of the finished package.

Buckets

The files that are managed by this set of routines consist of a number of buckets. The term bucket is used to describe a logical block of data held on disc that contains one or more physical disc sectors. Each bucket has a bucket header that contains information pertaining to that bucket, such as the number of bytes used and the number of bytes free. This bucket header is found at the start of each bucket written to the file. When the file is created, the bucket size provided to the CreateSerialFile function fixes the bucket size for that file.

EOF Bucket

There is an end of file bucket which is written to the file when the CloseFile function is called on a file created by the CreateSerialFile function.

Records

Each bucket may contain one or more variable length records. A record must be wholly contained within the bucket (in other words, records cannot span buckets). The record consists of a word header, followed by the actual data for the record.

Writing To A File

The CreateSerialFile function creates a new file for writing. The WriteRecord function is used to write a record to the file, and length of the record being written determines if the record is placed in the current open bucket, or the current bucket is physically written to the disc, and a new bucket created into which that record will be placed.

Reading From A File

The OpenSerialFile function opens an existing file for reading. The ReadRecord function operates in two modes. If the address of the buffer into which the record being read is to be placed is NULL, then the function returns the number of bytes needed to read the current record. If the address of the buffer is not NULL, then it is assumed that it is large enough to contain the record being read.

Handles

Each process may open up to 128 files simultaneously, but as each file is opened exclusively, no other process can open a file that is already opened by a process.

The Code

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
;    D i s c   H  o u s e k e e p i n g   S e r i a l   A c c e s s
;
;    Version 1.0, written for dream-in-code April 2011
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                       .686
                       .model    flat, stdcall
                       option    casemap:none
                       public    _DllMainCRTStartup@12
                       
CREATE_NEW             equ       1
OPEN_EXISTING          equ       3
FILE_ATTRIBUTE_NORMAL  equ       080H
GENERIC_READ           equ       080000000H
GENERIC_WRITE          equ       040000000H
INVALID_HANDLE_VALUE   equ       0FFFFFFFFH
DLL_PROCESS_ATTACH     equ       1
DLL_PROCESS_DETACH     equ       0
MEM_COMMIT             equ       01000H
MEM_RELEASE            equ       08000H
PAGE_READWRITE         equ       04H
SERIAL_WRITE           equ       1
SERIAL_READ            equ       2
SERIAL_FILE            equ       1
EOF_BUCKET             equ       080H
OVERFLOW_BUCKET        equ       040H
MAX_HANDLES            equ       128

Sleep                  proto     :DWORD
VirtualAlloc           proto     :DWORD, :DWORD, :DWORD, :DWORD
VirtualFree            proto     :DWORD, :DWORD, :DWORD
CreateFileA            proto     :DWORD, :DWORD, :DWORD, \
                                 :DWORD, :DWORD, :DWORD, :DWORD
ReadFile               proto     :DWORD, :DWORD, :DWORD, :DWORD, :DWORD
WriteFile              proto     :DWORD, :DWORD, :DWORD, :DWORD, :DWORD
SetFilePointer         proto     :DWORD, :DWORD, :DWORD, :DWORD
CloseHandle            proto     :DWORD

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;    Internal file handle entry - a maximum of 128 handles permitted
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

dfh_handle             struc
    handle             dd        ?
    bucket_sz          dd        ?
    access_mode        dw        ?
    buffer             dd        ?
    write_bucket       dd        ?
    read_bucket        dd        ?
    current_offset     dd        ?
dfh_handle             ends

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;    Bucket header written at the start of each bucket
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

header                 struc
    next_bucket        dd        ?
    oflow_bucket       dd        ?
    used_bytes         dw        ?
    free_bytes         dw        ?
    flags              db        ?
header                 ends
                       
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

                       .data
handle_table           dd        0
handles_open           dd        0
handles_sync           dd        0

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

                       .code
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;    Read the next bucket from the file
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

ReadBucket             proc
                       local     bytes_read
                       invoke    ReadFile, dfh_handle.handle[esi], \
                                           dfh_handle.buffer[esi], \
                                           dfh_handle.bucket_sz[esi], \
                                           ADDR bytes_read, 0
                       mov       edi, dfh_handle.buffer[esi]
                       mov       ecx, dfh_handle.bucket_sz[esi]
                       mov       eax, SIZE header
                       mov       dfh_handle.current_offset[esi], eax
                       ret
ReadBucket             endp

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;    Write the next bucket to the file
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

WriteBucket            proc
                       local     bytes_written
                       invoke    WriteFile, dfh_handle.handle[esi], \
                                            dfh_handle.buffer[esi], \
                                            dfh_handle.bucket_sz[esi], \
                                            ADDR bytes_written, 0
                       mov       edi, dfh_handle.buffer[esi]
                       mov       ecx, dfh_handle.bucket_sz[esi]
                       mov       ebx, SIZE header
                       mov       header.used_bytes[edi], bx
                       sub       ecx, ebx
                       mov       header.free_bytes[edi], cx
                       inc       dfh_handle.write_bucket[esi]
                       add       edi, SIZE header
                       xor       al, al
                       rep       stosb
                       ret
WriteBucket            endp

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;    Get a free table handle entry
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

GetHandle              proc
                       push      esi
                       xor       eax, eax
                       xor       edx, edx
                       inc       edx
                       cmpxchg   handles_sync, edx
                       jz        got_handle_sync
                       invoke    Sleep, 0
                       jmp       GetHandle
got_handle_sync:       mov       ecx, MAX_HANDLES
                       mov       esi, handle_table
                       xor       eax, eax
find_free_handle:      mov       ebx, dfh_handle.handle[esi]
                       or        ebx, ebx
                       je        found_free_handle
                       add       esi, SIZE dfh_handle
                       inc       eax
                       loop      find_free_handle
                       stc
                       pop       esi
                       ret
found_free_handle:     clc
                       pop       esi
                       ret
GetHandle              endp

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;    Create Serial File for output only
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

CreateSerialFile       proc      file_name:DWORD, bucket_size:DWORD
                       local     disc_handle:DWORD, table_entry:DWORD
                       
                       mov       eax, bucket_size
                       and       eax, 01FFH
                       or        eax, eax
                       jne       CreateSerialFileError
                       invoke    CreateFileA, file_name, \
                                              GENERIC_READ or GENERIC_WRITE, \
                                              0, 0, CREATE_NEW, \
                                              FILE_ATTRIBUTE_NORMAL, 0
                       cmp       eax, INVALID_HANDLE_VALUE
                       je        CreateSerialFileError
                       mov       disc_handle, eax
                       invoke    GetHandle
                       jc        CreateSerialFileError
                       mov       table_entry, eax
                       mov       ebx, handle_table
                       lea       esi, [ebx+eax*8]
                       inc       handles_open
                       mov       eax, disc_handle
                       mov       dfh_handle.handle[esi], eax
                       mov       ax, SERIAL_WRITE
                       mov       dfh_handle.access_mode[esi], ax
                       mov       eax, bucket_size
                       mov       dfh_handle.bucket_sz[esi], eax
                       xor       eax, eax
                       mov       handles_sync, eax
                       invoke    VirtualAlloc, eax, bucket_size, 
                                               MEM_COMMIT, \
                                               PAGE_READWRITE
                       mov       dfh_handle.buffer[esi], eax
                       mov       edi, eax
                       mov       eax, bucket_size
                       mov       bx, SIZE header
                       mov       header.used_bytes[edi], bx
                       sub       ax, bx
                       mov       header.free_bytes[edi], ax
                       mov       al, SERIAL_FILE
                       mov       header.flags[edi], al
                       mov       eax, table_entry
                       inc       eax
                       ret
CreateSerialFileError: xor       eax, eax
                       dec       eax
                       ret
CreateSerialFile       endp

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;    Open Serial File for input only
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

OpenSerialFile         proc      file_name:DWORD
                       local     disc_handle:DWORD, table_entry:DWORD, \
                                 file_header:header, bytes_read:DWORD
                       
                       invoke    CreateFileA, file_name, \
                                              GENERIC_READ or GENERIC_WRITE, \
                                              0, 0, OPEN_EXISTING, \
                                              FILE_ATTRIBUTE_NORMAL, 0
                       cmp       eax, INVALID_HANDLE_VALUE
                       je        OpenSerialFileError
                       mov       disc_handle, eax
                       invoke    GetHandle 
                       jc        OpenSerialFileError
                       mov       table_entry, eax
                       mov       ebx, handle_table
                       lea       esi, [ebx+eax*8]
                       inc       handles_open
                       invoke    ReadFile, disc_handle, \
                                           ADDR file_header, \
                                           SIZE header, \
                                           ADDR bytes_read, 0
                       mov       eax, disc_handle
                       mov       dfh_handle.handle[esi], eax
                       mov       ax, SERIAL_READ
                       mov       dfh_handle.access_mode[esi], ax
                       movzx     eax, file_header.used_bytes
                       add       ax, file_header.free_bytes
                       mov       dfh_handle.bucket_sz[esi], eax
                       xor       eax, eax
                       mov       handles_sync, eax
                       invoke    VirtualAlloc, eax, \
                                               dfh_handle.bucket_sz[esi], 
                                               MEM_COMMIT, \
                                               PAGE_READWRITE
                       mov       dfh_handle.buffer[esi], eax
                       invoke    SetFilePointer, dfh_handle.handle[esi], \
                                                 0, 0, 0
                       invoke    ReadBucket
                       mov       eax, table_entry
                       inc       eax
                       ret
OpenSerialFileError:   xor       eax, eax
                       dec       eax
                       ret
OpenSerialFile         endp

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;    Close a file and free the internal handle entry
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

CloseFile              proc      handle:DWORD
                       mov       eax, handle
                       dec       eax
                       mov       ebx, handle_table
                       lea       esi, [ebx+eax*8]
                       mov       ax, dfh_handle.access_mode[esi]
                       cmp       ax, SERIAL_WRITE
                       jne       free_buffer
                       invoke    WriteBucket
                       mov       edi, dfh_handle.buffer[esi]
                       mov       al, EOF_BUCKET
                       mov       header.flags[edi], al
                       invoke    WriteBucket
free_buffer:           invoke    VirtualFree, dfh_handle.buffer[esi], \
                                              0, MEM_RELEASE
                       invoke    CloseHandle, dfh_handle.handle[esi]
                       xor       eax, eax
                       mov       dfh_handle.handle[esi], eax
                       dec       handles_open
                       ret
                       
CloseFile              endp

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;    Read the next record from the file
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

ReadRecord             proc      handle:DWORD, \
                                 user_record:DWORD, \
                                 record_length:DWORD
read_initialze:        mov       eax, handle
                       dec       eax
                       mov       ebx, handle_table
                       lea       esi, [ebx+eax*8]
                       mov       edi, dfh_handle.buffer[esi]
                       add       edi, dfh_handle.current_offset[esi]
                       push      edi
                       mov       eax, DWORD PTR [edi]
                       or        eax, eax
                       jne       transfer_record
                       invoke    ReadBucket
                       mov       eax, handle
                       dec       eax
                       mov       ebx, handle_table
                       lea       esi, [ebx+eax*8]
                       mov       edi, dfh_handle.buffer[esi]
                       mov       al, header.flags[edi]
                       cmp       al, EOF_BUCKET
                       jne       read_initialze
                       xor       eax, eax
                       dec       eax
                       ret
transfer_record:       mov       esi, record_length
                       mov       DWORD PTR [esi], eax
                       mov       edi, user_record
                       or        edi, edi
                       jne       copy_record
                       pop       edi
                       xor       eax, eax
                       ret
copy_record:           pop       esi
                       add       esi, 4
                       mov       edi, user_record
                       mov       ecx, eax
                       rep       movsb
                       mov       ebx, handle_table
                       mov       edx, handle
                       dec       edx
                       lea       esi, [ebx+edx*8]
                       add       eax, 4
                       add       dfh_handle.current_offset[esi], eax
                       xor       eax, eax
                       ret

ReadRecord             endp

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;    Write the next record to the file
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

WriteRecord            proc      handle:DWORD, \
                                 user_record:DWORD, \
                                 record_length:DWORD
                       local     bytes_written:DWORD

transfer_record:       mov       eax, handle
                       dec       eax
                       mov       ebx, handle_table
                       lea       esi, [ebx+eax*8]
                       mov       edi, dfh_handle.buffer[esi]
                       movzx     eax, header.free_bytes[edi]
                       mov       ecx, record_length
                       add       ecx, 4
                       cmp       eax, ecx
                       jb        write_bucket
                       sub       eax, ecx
                       mov       header.free_bytes[edi], ax
                       movzx     eax, header.used_bytes[edi]
                       push      eax
                       add       eax, ecx
                       mov       header.used_bytes[edi], ax
                       pop       ebx
                       mov       eax, record_length
                       mov       DWORD PTR [edi+ebx], eax
                       lea       edi, 4[edi+ebx]
                       mov       esi, user_record
                       sub       ecx, 4
                       rep       movsb
                       xor       eax, eax
                       ret
write_bucket:          invoke    WriteBucket
                       jmp       transfer_record
WriteRecord            endp

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;    DLL main entry point
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

_DllMainCRTStartup@12  proc      instance:DWORD, why:DWORD, na:DWORD
                       mov       eax, why
                       cmp       eax, DLL_PROCESS_ATTACH
                       je        process_attach
                       cmp       eax, DLL_PROCESS_DETACH
                       je        process_detach
                       ret
process_attach:        xor       eax, eax
                       invoke    VirtualAlloc, eax, 4096, MEM_COMMIT, \
                                               PAGE_READWRITE
                       mov       handle_table, eax
                       xor       eax, eax
                       inc       eax
                       ret
process_detach:        invoke    VirtualFree, handle_table, 0, MEM_RELEASE
                       ret
_DllMainCRTStartup@12  endp
                       end



The Module Definition File

LIBRARY
EXPORTS
CreateSerialFile @1
OpenSerialFile @2
WriteRecord @3
ReadRecord @4
CloseFile @5


This post has been edited by Martyn.Rae: 25 April 2011 - 08:47 AM


Is This A Good Question/Topic? 2
  • +

Page 1 of 1