reactos/boot/freeldr/bootsect/pc98/fat12fdd.S
Dmitry Borisov 065afd93fd [FREELDR] Add FAT12 file system boot sector for NEC PC-98 series (#2025)
The first part of PC-98 Port - https://reactos.org/wiki/PC-98

- Add FAT12 file system boot sector for NEC PC-98 series.
- Add a new build target for a PC-98 bootable floppy disk.
- Add a new sub-architecture into config.cmake.
2020-01-18 19:16:23 +01:00

502 lines
16 KiB
ArmAsm

/*
* PROJECT: FreeLoader
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: FAT12 file system boot sector for NEC PC-98 series
* NOTES: The source code in this file is based on the Brian Palmer's work
* (boot/freeldr/bootsect/fat.S)
* COPYRIGHT: Copyright 2019 Dmitry Borisov (di.sean@protonmail.com)
*/
#include <asm.inc>
#include <freeldr/include/arch/pc/x86common.h>
#define BP_REL(x) [bp + x - offset start]
#define VGA_WIDTH 80
#define VGA_HEIGHT 25
/*
* See https://www.webtech.co.jp/company/doc/undocumented_mem/memsys.txt
* At segment 0000h
*/
#define BOOT_DAUA HEX(0584)
DataAreaStartHigh = 2
DataAreaStartLow = 4
BiosCHSDriveSizeHigh = 6
BiosCHSDriveSizeLow = 8
BiosCHSDriveSize = 8
ReadSectorsOffset = 10
ReadClusterOffset = 12
PutCharsOffset = 14
BootSectorStackTop = HEX(7C00) - 16
if 0
.macro DEBUG_STOP
hlt
jmp short $ - 1
.endm
.macro DEBUG_STEP
push ax
xor ah, ah
int HEX(18) // Wait for a keypress
pop ax
.endm
endif
// org 7C00h
.code16
start:
jmp main
nop
// After running fatten tool it overwrites (BPB & EBPB)
OEMName:
.ascii "FrLdr1.0"
BytesPerSector:
.word 512
SectsPerCluster:
.byte 1
ReservedSectors:
.word 1
NumberOfFats:
.byte 2
MaxRootEntries:
.word 224
TotalSectors:
.word 2880
MediaDescriptor:
.byte HEX(0f0)
SectorsPerFat:
.word 9
SectorsPerTrack:
.word 18
NumberOfHeads:
.word 2
HiddenSectors:
.long 0
TotalSectorsBig:
.long 0
BootDrive:
.byte HEX(0ff)
Reserved:
.byte 0
ExtendSig:
.byte HEX(29)
SerialNumber:
.long 00000000
VolumeLabel:
.ascii "NO NAME "
FileSystem:
.ascii "FAT12 "
/*
* Real-mode entry point
*/
main:
xor ax, ax // Setup segment registers:
mov ds, ax // Make DS correct
mov es, ax // Make ES correct
mov ss, ax // Make SS correct
mov bp, HEX(7C00)
mov sp, BootSectorStackTop // Stack grows downwards from BootSectorStackTop
if 0 // It would have been nice to have had this check...
mov ax, HEX(1000) // Detecting hardware
/*
* INSTALLATION CHECK interrupt
* See http://www.ctyme.com/intr/rb-2293.htm
*/
int HEX(1A)
cmp ax, HEX(1000)
je HardwareError // An IBM-compatible PC
endif
/*
* IBM PC here: NEC PC-98 here:
* ESI = 000E:0000 ESI = 0000:0000
* EDI = 0000:070C EDI = 0000:0000
* EBP = 0000:7C00 EBP = 0000:7C00
* ESP = 0000:7BF0 ESP = 0000:7BF0
* CS:IP = 0000:7C4E CS:IP = 1FE0:004E
*/
mov ax, word ptr ds:[BOOT_DAUA] // Get the Device Address/Unit Address (DA/UA)
mov si, ax
push si
push cs
pop ds
xor si, si
mov ax, HEX(0000)
mov es, ax
mov di, HEX(7C00)
mov cx, 512
rep movsw // Move our 512 bytes boot sector to the [0000:7C00]
pop si
ljmp16 0, relocated // Jump into relocated code
relocated:
xor ax, ax // Clean-up segments
mov es, ax
mov ds, ax
test byte ptr ds:[HEX(501)], HEX(08) // High-resolution mode check
jz VideoTestNormalMode
mov ax, HEX(0E000)
jmp short VideoTestDone
VideoTestNormalMode:
mov ax, HEX(0A000)
VideoTestDone:
mov word ptr BP_REL(VramSegment), ax
mov ax, si
mov byte ptr BP_REL(BootDrive), al // Save the boot drive
/*
* Now we must find our way to the first sector of the root directory
*
* LBA = NumberOfFats * SectorsPerFat + HiddenSectors + ReservedSectors
*/
xor ax, ax
xor cx, cx
mov al, byte ptr BP_REL(NumberOfFats) // Number of fats
mul word ptr BP_REL(SectorsPerFat) // Times sectors per fat
add ax, word ptr BP_REL(HiddenSectors)
adc dx, word ptr BP_REL(HiddenSectors + 2) // Add the number of hidden sectors
add ax, word ptr BP_REL(ReservedSectors) // Add the number of reserved sectors
adc dx, cx // Add carry bit
mov word ptr [bp - DataAreaStartLow], ax // Save the starting sector of the root directory
mov word ptr [bp - DataAreaStartHigh], dx // Save it in the first 4 bytes before the boot sector
mov si, word ptr BP_REL(MaxRootEntries) // Get number of root dir entries in SI
pusha // Save 32-bit logical start sector of root dir
// DX:AX now has the number of the starting sector of the root directory
/*
* Now calculate the size of the root directory
*
* Root directory sectors = (MaxRootEntries * 32 + BytesPerSector - 1) / BytesPerSector
*/
xor dx, dx
mov ax, 32 // Size of dir entry
mul si // Times the number of entries
mov bx, word ptr BP_REL(BytesPerSector)
add ax, bx
dec ax
div bx // Divided by the size of a sector
// AX now has the number of root directory sectors
add word ptr [bp - DataAreaStartLow], ax // Add the number of sectors of the root directory to our other value
adc word ptr [bp - DataAreaStartHigh], cx // Now the first 4 bytes before the boot sector contain the starting sector of the data area
popa
/*
* Reads root directory into [0000:7E00] and finds 'FREELDR SYS'
*
* Call with:
*
* DX:AX - LBA of the starting sector of the root directory
*/
LoadRootDirSector:
mov bx, HEX(7E0) // We will load the root directory sector
mov es, bx // Right after the boot sector in memory
xor bx, bx // We will load it to [0000:7E00]
xor cx, cx // Zero out CX
inc cx // Now increment it to 1, we are reading one sector
xor di, di // Zero out di
push es // Save ES because it will get incremented by 20h
call ReadSectors // Read the first sector of the root directory
pop es // Restore ES (ES:DI = 7E0:0000)
SearchRootDirSector:
cmp byte ptr es:[di], ch // If the first byte of the directory entry is zero then we have
jz PrintFileNotFound // reached the end of the directory and FREELDR.SYS is not here so reboot
pusha // Save all registers
mov cl, 11 // Put 11 in cl (length of filename in directory entry)
mov si, offset filename // Put offset of filename string in DS:SI
repe cmpsb // Compare this directory entry against 'FREELDR SYS'
popa // Restore all the registers
jz FoundFreeLoader // If we found it then jump
dec si // SI holds MaxRootEntries, subtract one
jz PrintFileNotFound // If we are out of root dir entries then reboot
add di, 32 // Increment DI by the size of a directory entry
cmp di, HEX(0200) // Compare DI to 512 (DI has offset to next dir entry, make sure we haven't gone over one sector)
jc SearchRootDirSector // If DI is less than 512 loop again
jmp short LoadRootDirSector // Didn't find FREELDR.SYS in this directory sector, try again
FoundFreeLoader:
/*
* We found freeldr.sys on the disk
* so we need to load the first 512 bytes of it to [0000:F800]
* ES:DI has dir entry (ES:DI == 07E0:XXXX)
*/
mov ax, word ptr es:[di + HEX(1A)] // Get start cluster
push ax // Save start cluster
push FREELDR_BASE / 16 // Put load segment on the stack and load it
pop es // Into ES so that we load the cluster at [0000:F800]
call ReadCluster // Read the cluster
pop ax // Restore start cluster of FreeLoader
/*
* Save the addresses of needed functions so
* the helper code will know where to call them
*/
mov word ptr [bp - ReadSectorsOffset], offset ReadSectors // Save the address of ReadSectors
mov word ptr [bp - ReadClusterOffset], offset ReadCluster // Save the address of ReadCluster
mov word ptr [bp - PutCharsOffset], offset PrintString // Save the address of PrintString
/*
* Now AX has start cluster of FreeLoader and we
* have loaded the helper code in the first 512 bytes
* of FreeLoader to 0000:F800. Now transfer control
* to the helper code. Skip the first three bytes
* because they contain a jump instruction to skip
* over the helper code in the FreeLoader image
*/
ljmp16 0, FREELDR_BASE + 3
/*
* Reads cluster number in AX into [ES:BX]
*
* Call with:
*
* AX - cluster number
* ES:BX - buffer to read data into
*/
ReadCluster:
/*
* StartSector = ((Cluster - 2) * SectorsPerCluster) + ReservedSectors + HiddenSectors
*/
dec ax // Adjust start cluster by 2
dec ax // Because the data area starts on cluster 2
xor ch, ch
mov cl, byte ptr BP_REL(SectsPerCluster)
mul cx // Times sectors per cluster
add ax, [bp - DataAreaStartLow] // Add start of data area
adc dx, [bp - DataAreaStartHigh] // Now we have DX:AX with the logical start sector of FREELDR.SYS
xor bx, bx // We will load it to [ES:0000], ES loaded before function call
/*
* Reads logical sectors into [ES:BX]
*
* Call with:
*
* DX:AX - logical sector number to read (LBA value)
* ES:BX - buffer to read data into
* CX - number of sectors to read
*/
ReadSectors:
.ReadSectorsLoop:
pusha
/*
* Converting LBA (Linear Block Address) into a format CHS (Cylinder:Head:Sector)
*
* C = (LBA / SPT) / HPC
* H = (LBA / SPT) % HPC
* S = (LBA % SPT) + 1
*/
xchg ax, cx
xchg ax, dx
xor dx, dx
div word ptr BP_REL(SectorsPerTrack)
xchg ax, cx
div word ptr BP_REL(SectorsPerTrack) // Divide logical by SectorsPerTrack
inc dx // Sectors numbering starts at 1 not 0
xchg cx, dx
div word ptr BP_REL(NumberOfHeads) // Number of heads
mov dh, dl // DH - head number (0-1)
mov dl, cl // DL - sector number (1-26)
mov cl, al // CL - cylinder number (0-76)
// TODO: This should be calculated using the equation: BytesPerSector = (CH + 1) * 128
mov ch, 2 // CH - sector size (0-4): 0 (128), 1 (256), 2 (512), 3 (1024), 4 (2048)
mov al, byte ptr BP_REL(BootDrive) // AL - DA/UA
push bp
push bx
mov bx, word ptr BP_REL(BytesPerSector) // BX - bytes to read
pop bp // ES:BP - buffer to read data into
mov ah, HEX(56) // AH - read sectors from a floppy disk with SEEK, and use double-density format (MFM)
/*
* Disk BIOS interrupt
* See http://radioc.web.fc2.com/column/pc98bas/bios/int1b_06.htm
*/
int HEX(1b)
pop bp
jc PrintDiskError // CF set on failure
popa
inc ax // Increment sector to read
jnz .NoCarryCHS
inc dx
.NoCarryCHS:
push bx
mov bx, es
add bx, HEX(20) // Add size of dir entry to the buffer address for the next sector
mov es, bx
pop bx
loop .ReadSectorsLoop // Increment read buffer for next sector, read next sector
ret
/*
* Prints a character
*
* Call with:
*
* AL - ASCII code
*/
PutChar:
push di
push es
push word ptr BP_REL(VramSegment)
pop es
mov di, word ptr BP_REL(VramOffset) // ES:DI = VramSegment:VramOffset
.PutCharWrite:
xor ah, ah
stosw // Write ASCII directly to the VRAM
mov word ptr BP_REL(VramOffset), di
pop es
pop di
ret
/*
* Prints a null-terminated string
*
* Call with:
*
* DS:SI - pointer to a string
*/
PrintString:
xor ah, ah
lodsb // Get a single char from a ptr
or al, al
jz short .PrintEnd // Found NULL
cmp al, HEX(0D)
jz short .PrintStringHandleCR // Found CR
call PutChar
jmp short PrintString
.PrintStringHandleCR:
mov ax, word ptr BP_REL(VramOffset)
mov dl, VGA_WIDTH * 2
div dl
inc ax
mul dl
mov word ptr BP_REL(VramOffset), ax
inc si // Skip the next LF character
jmp short PrintString
.PrintEnd:
ret
if 0
/*
* Displays a hardware error message and reboots
*/
HardwareError:
mov si, offset msgHardwareError
.PrintStringVGA:
lodsb // Get a single char from a ptr
or al, al
jz short .HardwareErrorDone // Found NULL
mov ah, HEX(0E) // Teletype output
mov bx, 7 // BH - video page number, BL - foreground color
int HEX(10) // Display a character via TTY mode
jmp short .PrintStringVGA
.HardwareErrorDone:
xor ax, ax
int HEX(16) // Wait for a keypress
int HEX(19) // Reboot
endif
/*
* Displays a disk error message and reboots
*/
PrintDiskError:
mov si, offset msgDiskError // Disk error message
call PrintString // Display it
jmp short Reboot
/*
* Displays a file not found error message and reboots
*/
PrintFileNotFound:
mov si, offset msgNotFoundError // FreeLdr not found message
call PrintString // Display it
jmp short Reboot
/*
* Reboots the computer after keypress
*/
Reboot:
mov si, offset msgAnyKey // Press any key message
call PrintString // Display it
xor ax, ax
int HEX(18) // Wait for a keypress
/*
* Activate the CPU reset line
* See https://people.freebsd.org/~kato/pc98-arch.html#cpureset
* and http://www.webtech.co.jp/company/doc/undocumented_mem/io_cpu.txt
*/
xor ax, ax
out HEX(0F0), al
hlt
Halt:
jmp short Halt // Spin
VramSegment:
.word 0
VramOffset:
.word 0
msgDiskError:
.ascii "ERR", CR, LF, NUL
msgNotFoundError:
.ascii "NFE", CR, LF, NUL
msgAnyKey:
.ascii "Press any key", NUL
filename:
.ascii "FREELDR SYS"
if 0 // So totally out of space here...
msgHardwareError:
.ascii "It's not PC-98", NUL
endif
.org 509 // Pad to 509 bytes
BootPartition:
.byte 0
BootSignature:
.word HEX(0AA55) // BootSector signature
.endcode16
END