;uses defStrucsAsMacros

;Allows using structures as macros (i.e. a structure will be unnamed)
;usage: defStrucsAsMacros POINT,LINE
;       POINT
;       LINE <1,2>,<1,4>
macro defStrucsAsMacros [name*]
{
    forward
        macro name [arg]
        \{
            \common
                \local ..lbl
                ..lbl name arg
        \}
}

SECTOR_SIZE							equ $800
VIRTUAL_SECTOR_SIZE					equ $200

BOOT_RECORD							equ 0
PRIMARY_VOLUME_DESCRIPTOR			equ 1
SUPPLEMENTARY_VOLUME_DESCRIPTOR		equ 2
VOLUME_PARTITION_DESCRIPTOR			equ 3
DESCRIPTOR_SET_TERMINATOR			equ 255

STANDARD_VOLUME_DESCRIPTOR			equ 1
ENHANCED_VOLUME_DESCRIPTOR			equ 2

FILE_FLAG_DIRECTORY					equ 2

PLATFORM_X86						equ 0
PLATFORM_PPC						equ 1
PLATFORM_MAC						equ 2

INDICATOR_NOT_BOOTABLE				equ 0
INDICATOR_BOOTABLE					equ $88

BOOT_NO_EMULATION					equ 0
BOOT_FLOPPY_12						equ 1
BOOT_FLOPPY_144						equ 2
BOOT_FLOPPY_288						equ 3
BOOT_HARD_DISK						equ 4

struc DwordBe num*
{
	.:
	dd (((num) and $FF000000) shr 24) or \
	   (((num) and $00FF0000) shr  8) or \
	   (((num) and $0000FF00) shl  8) or \
	   (((num) and $000000FF) shl 24)
	sizeof.#. = $-.
}
virtual at 0
	DwordBe DwordBe 0
end virtual

struc WordBe num*
{
	.:
	dw (((num) and $FF00) shr 8) or \
	   (((num) and $00FF) shl 8)
	sizeof.#. = $-.
}
virtual at 0
	WordBe WordBe 0
end virtual

struc DwordDe num*
{
	.:
	.le dd (num)
	.be dd (((num) and $FF000000) shr 24) or \
	       (((num) and $00FF0000) shr  8) or \
		   (((num) and $0000FF00) shl  8) or \
		   (((num) and $000000FF) shl 24)
	sizeof.#. = $-.
}
virtual at 0
	DwordDe DwordDe 0
end virtual

struc WordDe num*
{
	.:
	.le dw (num)
	.be dw (((num) and $FF00) shr 8) or \
		   (((num) and $00FF) shl 8)
	sizeof.#. = $-.
}
virtual at 0
	WordDe WordDe 0
end virtual

struc DateTime year=0,month=0,day=0,hour=0,minute=0,second=0,gmt=0
{
	.:
	.YearsSince1900           db year
	.MonthOfYear              db month
	.DayOfMonth               db day
	.HourOfDay                db hour
	.MinuteOfHour             db minute
	.SecondOfMinute           db second
	.GreenwichMeanTimeOffset  db gmt
	sizeof.#. = $-.
}
virtual at 0
	DateTime DateTime
end virtual

struc DateTimeString year=0,month=0,day=0,hour=0,minute=0,second=0,gmt=0
{
	.:
    .Year                     db (year/1000) mod 10+'0',(year/100) mod 10+'0',(year/10) mod 10+'0',year mod 10+'0'
    .Month                    db (month/10)	mod 10+'0',month mod 10+'0'
    .Day                      db (day/10) mod 10+'0',day mod 10+'0'
    .Hour                     db (hour/10) mod 10+'0',hour mod 10+'0'
    .Minute                   db (minute/10) mod 10+'0',minute mod 10+'0'
    .Second                   db (second/10) mod 10+'0',second mod 10+'0'
    .MillisecondDecade        db '0','0'
    .GreenwichMeanTimeOffset  db gmt
	sizeof.#. = $-.
}
virtual at 0
	DateTimeString DateTimeString
end virtual

struc DirectoryRecord ExtentLocation*,ExtentLength*,FileId*,Flags=0
{
	.:
	.DirectoryRecordLength           db 0 ;sizeof.#. stored later to prevent forward referencing
	.ExtendedAttributeRecordLength   db 0
	.ExtentLocation                  DwordDe ExtentLocation
	.DataLength                      DwordDe ExtentLength
	.RecordingDateTime               DateTime
	.FileFlags                       db Flags
	.FileUnitSize                    db 0
	.InterleaveGapSize               db 0
	.VolumeSequenceNumber            WordDe 1
	.FileIdentifierLength            db 0 ;sizeof.#.FileIdentifier stored later to prevent forward referencing
	.FileIdentifier                  db FileId
	sizeof.#.FileIdentifier = $-.FileIdentifier
	.padding                         db (sizeof.#.FileIdentifier+1) and 1 dup 0
	sizeof.#. = $-.
	store byte sizeof.#. at .DirectoryRecordLength
	store byte sizeof.#.FileIdentifier at .FileIdentifierLength
}
virtual at 0
	DirectoryRecord DirectoryRecord 0,0,0,FILE_FLAG_DIRECTORY
	;undefine sizeof.DirectoryRecord
	match ,
	{
		local DirectoryRecord
		sizeof.DirectoryRecord equ sizeof.#DirectoryRecord
	}
end virtual



struc PrimaryVolumeDescriptor VolumeSize*,PathTableSize*,RootDirLocation*,RootDirLength*,\
                              TypeLPathTable=0,TypeMPathTable=0,VolumeId='OEM.NAME'
{
	.:
	.VolumeDescriptorType            db PRIMARY_VOLUME_DESCRIPTOR
	.StandardIdentifier              db 'CD001'
	.VolumeDescriptorVersion         db STANDARD_VOLUME_DESCRIPTOR
	.unused1                         db 0
	.SystemIdentifier                db $20 dup ' '
	.VolumeIdentifier                db VolumeId,$20-($-.VolumeIdentifier) dup ' '
	.unused2                         db 8 dup 0
	.VolumeSpace                     DwordDe VolumeSize
	.unused3                         db $20 dup 0
	.VolumeSetSize                   WordDe 1
	.VolumeSequenceNumber            WordDe 1
	.LogicalBlockSize                WordDe SECTOR_SIZE
	.PathTableSize                   DwordDe PathTableSize
	.TypeLPathTableLocation          dd TypeLPathTable
	.TypeLPathTableOptional          dd 0
	.TypeMPathTableLocation          DwordBe TypeMPathTable
	.TypeMPathTableOptional          DwordBe 0
	.RootDirectory                   DirectoryRecord RootDirLocation,RootDirLength,0,FILE_FLAG_DIRECTORY
	.VolumeSetIdentifier             db 128 dup ' '
	.PublisherIdentifier             db 128 dup ' '
	.DataPreparerIdentifier          db 128 dup ' '
	.ApplicationIdentifier           db 128 dup ' '
	.CopyrightIdentifier             db  37 dup ' '
	.AbstractFileIdentifier          db  37 dup ' '
	.BibliographicFileIdentifier     db  37 dup ' '
	.VolumeCreationDateTime          DateTimeString
	.VolumeModificationDateTime      DateTimeString
	.VolumeExpirationDateTime        DateTimeString
	.VolumeEffectiveDateTime         DateTimeString
	.FileStructureVersion            db STANDARD_VOLUME_DESCRIPTOR
	.reserved1                       db 0
	.ApplicationUse                  db 512 dup 0
	.reserved2                       db 653 dup 0
	sizeof.#. = $-.
}
virtual at 0
	PrimaryVolumeDescriptor PrimaryVolumeDescriptor 0,0,0,0
end virtual

struc BootRecord BootCatalogPointer*
{
	.:
	.VolumeDescriptorType            db BOOT_RECORD
	.StandardIdentifier              db 'CD001'
	.VolumeDescriptorVersion         db STANDARD_VOLUME_DESCRIPTOR
	.BootSystemIdentifier            db 'EL TORITO SPECIFICATION',$20-($-.BootSystemIdentifier) dup 0
	.BootIdentifier                  db $20 dup 0
	.BootCatalogPointer              dd BootCatalogPointer
	.BootSystemUse                   db $7B5 dup 0
	sizeof.#. = $-.
}
virtual at 0
	BootRecord BootRecord 0
end virtual

struc DescriptorSetTerminator
{
	.:
	.VolumeDescriptorType            db DESCRIPTOR_SET_TERMINATOR
	.StandardIdentifier              db 'CD001'
	.VolumeDescriptorVersion         db STANDARD_VOLUME_DESCRIPTOR
	.reserved                        db $7F9 dup 0
	sizeof.#. = $-.
}
virtual at 0
	DescriptorSetTerminator DescriptorSetTerminator 
end virtual

struc LPathTableRecord DirectoryId*, ExtentLocation*, ParentDir*
{
	.:
    .DirectoryIdentifierLength       db 0 ;sizeof.#.DirectoryIdentifier stored later to prevent forward referencing
    .ExtendedAttributeRecordLength   db 0
    .ExtentLocation                  dd ExtentLocation
    .ParentDirectoryNumber           dw ParentDir
    .DirectoryIdentifier             db DirectoryId
	sizeof.#.DirectoryIdentifier = $-.DirectoryIdentifier
	.padding                         db sizeof.#.DirectoryIdentifier and 1 dup 0
	sizeof.#. = $-.
	store byte sizeof.#.DirectoryIdentifier at .DirectoryIdentifierLength
}
virtual at 0
	LPathTableRecord LPathTableRecord 0,0,0
	;undefine sizeof.LPathTableRecord
	match ,
	{
		local LPathTableRecord
		sizeof.LPathTableRecord equ sizeof.#LPathTableRecord
	}
end virtual

struc MPathTableRecord DirectoryId*, ExtentLocation*, ParentDir*
{
	.:
    .DirectoryIdentifierLength       db sizeof.#.DirectoryIdentifier
    .ExtendedAttributeRecordLength   db 0
    .ExtentLocation                  DwordBe ExtentLocation
    .ParentDirectoryNumber           WordBe ParentDir
    .DirectoryIdentifier             db DirectoryId
	sizeof.#.DirectoryIdentifier = $-.DirectoryIdentifier
	.padding                         db sizeof.#.DirectoryIdentifier and 1 dup 0
	sizeof.#. = $-.
}
virtual at 0
	MPathTableRecord MPathTableRecord 0,0,0
	;undefine sizeof.MPathTableRecord
	match ,
	{
		local MPathTableRecord
		sizeof.MPathTableRecord equ sizeof.#MPathTableRecord
	}
end virtual

struc ValidationEntry IdString*
{
	local buf,tmp,checksum
	.:
	.HeaderId                        db 1
	.PlatformId                      db PLATFORM_X86
	.reserved                        dw 0
	.IdString                        db IdString,$18-($-.IdString) dup 0
	.Checksum                        dw checksum
	.BootSignature                   db 055h,0AAh
	sizeof.#. = $-.
	
	load tmp word from .BootSignature
	repeat (.Checksum-.)/2
		load buf word from .+(%-1)*2
		tmp = tmp+buf
	end repeat
	checksum = ($10000 - (tmp and $FFFF)) and $FFFF
}
virtual at 0
	ValidationEntry ValidationEntry ''
end virtual

struc DefaultEntry BootCode*,BootCodeSize*
{
	.:
	.BootIndicator                   db INDICATOR_BOOTABLE
	.BootMediaType                   db BOOT_NO_EMULATION
	.LoadSegment                     dw 0
	.SystemType                      db 0
	.unused1                         db 0
	.SectorCount                     dw (((BootCodeSize)+SECTOR_SIZE-1) and not (SECTOR_SIZE-1))/VIRTUAL_SECTOR_SIZE
	.LoadRelativeBlockAddress        db BootCode
	sizeof.#. = $-.
}
virtual at 0
	DefaultEntry DefaultEntry 0,0
end virtual

struc BootCatalog BootCode*,BootCodeSize*,IdString=''
{
	.:
	.ValidationEntry ValidationEntry IdString
	.DefaultEntry DefaultEntry BootCode,BootCodeSize
	.padding                         db ($-.+SECTOR_SIZE-1) and not (SECTOR_SIZE-1) - ($-.) dup 0
	sizeof.#. = $-.
}
virtual at 0
	BootCatalog BootCatalog 0,0
end virtual

defStrucsAsMacros DwordBe,WordBe,DwordDe,WordDe,\
	DateTime,DateTimeString,DirectoryRecord,\
	PrimaryVolumeDescriptor,BootRecord,DescriptorSetTerminator,\
	LPathTableRecord,MPathTableRecord,ValidationEntry,DefaultEntry,\
	BootCatalog

macro iso_image bootCode*
{
	local ..systemArea,..primaryVolumeDescriptor,..bootRecord,..descriptorSetTerminator,\
			..primaryLPathTable,..primaryMPathTable,..rootDir,\
			..bootCatalog,..bootCode,bootCode.size,..bootSectorPadding,..bootSectorSign,\
			imageSize,sizeof.primaryPathTable,sizeof.rootDir

		if ~ defined imageSize
			display 'Error: the iso image must be closed with endii macro',13,10
			assert 0
		end if
		
		..systemArea:              db $10*SECTOR_SIZE dup 0
		..primaryVolumeDescriptor: PrimaryVolumeDescriptor imageSize,sizeof.primaryPathTable,\
														(..rootDir-..systemArea)/SECTOR_SIZE,sizeof.rootDir,\
														(..primaryLPathTable-..systemArea)/SECTOR_SIZE,\
														(..primaryMPathTable-..systemArea)/SECTOR_SIZE
		..bootRecord:              BootRecord (..bootCatalog-..systemArea)/SECTOR_SIZE
		..descriptorSetTerminator: DescriptorSetTerminator
		
		..bootCatalog: BootCatalog (..bootCode-..systemArea)/SECTOR_SIZE,..bootSectorPadding-..bootCode+2
		
		..bootCode:
			org 7C00h
			bootCode.size = 0
			macro org arg*
			\{
				bootCode.size = bootCode.size + ($-$$)
				org arg
			\}
			
			bootCode
			
			bootCode.size = bootCode.size + ($-$$)
			purge org
			org ..bootCode+bootCode.size
		..bootSectorPadding:	   db SECTOR_SIZE-1-($-..bootCode+1)and(SECTOR_SIZE-1) dup 0
		..bootSectorSign:		   db 055h,0AAh
		
		..primaryLPathTable:
			LPathTableRecord 0,(..rootDir-..systemArea)/SECTOR_SIZE,1
		sizeof.primaryPathTable = $-..primaryLPathTable
		db ($-..primaryLPathTable+SECTOR_SIZE-1) and not (SECTOR_SIZE-1) - ($-..primaryLPathTable) dup 0
		..primaryMPathTable:
			MPathTableRecord 0,(..rootDir-..systemArea)/SECTOR_SIZE,1
		db ($-..primaryMPathTable+SECTOR_SIZE-1) and not (SECTOR_SIZE-1) - ($-..primaryMPathTable) dup 0

		..rootDir:
			DirectoryRecord (..rootDir-..systemArea)/SECTOR_SIZE,sizeof.rootDir,0,FILE_FLAG_DIRECTORY ;dot
			DirectoryRecord (..rootDir-..systemArea)/SECTOR_SIZE,sizeof.rootDir,1,FILE_FLAG_DIRECTORY ;dotdot
		sizeof.rootDir = ($-..rootDir+SECTOR_SIZE-1) and not (SECTOR_SIZE-1)
		db sizeof.rootDir-($-..rootDir) dup 0
	macro endii
	\{
		imageSize = ($-..systemArea)/SECTOR_SIZE
	\}
}
