Status update:
Step 1: I changed my C02 Monitor code to enable a "Ctrl-B" key sequence to Load the first block on the Microdrive (which is an IDE device). It checks for the $6502 2-byte signature after it's successfully loaded. If the signature is good, it jumps to the Partition sector loaded in and control is turned over there. Otherwise, it displays an error and returns the to the Monitor warm entry. Note: making this an auto-loader with a timeout will be trivial, if needed.
Step 2: I wrote a Partition Table loader which will get written to the first block of the IDE device. It's written to call some BIOS and Monitor routines only and uses the first 7 bytes of Page zero for pointers. The partition table can be loaded pretty much anywhere in memory (provided it doesn't clobber the system, e.g., page zero, stack, soft vectors, etc.). The loader figures out where it's been loaded, then checks for a proper 2-byte signature at end of the table. If not found, an error is displayed and control returns to the Monitor warm entry. If the signature is good, it scans for an active boot record (total of 16 record are present). If none are found, a message is displayed and again, the Monitor is entered via the warm entry. If an active boot record is found, it attempts to load the boot record into memory at a predefined location (defaults to the defined TEA area for DOS/65, CP/M or whatever). If the block can not be loaded successfully, an error is display and again, back to the Monitor. If the load is good, it jumps to the start of the boot record in memory and we're off to the races.
So far, so good. Next... I need to write a FDISK style utility that will setup the IDE drive with the Partition loader and allow configuring the multiple boot records and marking one as active. Once that's done, the second utility will need to be written, which will access the active boot record and write a boot loader and do the basic formatting of the drive, which is the typical FORMAT utility. Once these are written, installing an OS will be much easier and somewhat standardized perhaps. So quite a ways to go, but I can at least cobble together a boot loader and hardcode the blocks to load a RAM-configured DOS/65. I''l try to get that working near term... and start figuring out how to get the FDISK and FORMAT utilities written.
Just for chagrins, here's the partition loader code:
Code:
;**************************************************************************************************
;* *
;* Microdrive Partition Block for Booting an OS *
;* *
;* *
;* 17/10/2022 (Day/Month/Year) *
;* *
;* Copyright Kevin Maier *
;* *
;* GNU GPL V3 License *
;* *
;**************************************************************************************************
; Partition Block 1.00 *
; - Initial Partition Block format for enabling a boot from the Microdrive. *
; - Based on the Ontrack Disk Manager Partition Table, but with 65C02 boot software. *
; *
;**************************************************************************************************
PL 66 ;Page Length
PW 132 ;Page Width (# of char/line)
CHIP W65C02S ;Enable WDC 65C02 instructions
PASS1 OFF ;Set ON for debugging
INCLIST ON ;Set ON for listing Include files
;**************************************************************************************************
;Include required constants/equates/tables for SIM to assemble
;
INCLUDE C02Constants4.asm ;Constants/Equates - C02 BIOS/Monitor/Hardware
INCLUDE C02JMP_Table.asm ;Jump Table entries for C02 BIOS/Monitor
;
;**************************************************************************************************
;
; Note that the ORG statement below is bogus, it just marks the starting offset of the block
; - The BIOS/Monitor will load the Partition Record into low memory first.
; - The block is test for the signature at the end
; - If invalid, an error is shown and boot code warm boots Monitor
; - If valid, boot code signature is checked at offset 253 ($0265)
; - If invalid, an error is shown and boot code wamr boots Monitor
; - If valid, boot code continues execution to determine active partition entry
; - If none are active, boot code displays a message and warm boots monitor
; - If an active partition entry is found, the Boot Block is loaded into memory
;
; Once the Boot Block from the active partition is loaded into memory:
; - A signature is checked to test for a valid Boot Block
; - If invalid, an error is show and drops back to the Monitor
; - If valid, the Boot loader will continue by loading block into memory
;
; The Block data is part of the Boot loader code, i.e., it knows how many blocks to load
; from the drive into memory and the memory address to load to. It also has a pointer to
; start of executable code, which completes the basic Boot process.
; - Control is now turned over to the executable code that boots the OS.
;
.ORG $1000 ;Start of partition block offset - bogus, can't be $0000
;
LDA #'*' ;Get an asterisk
JSR B_CHROUT ;Send to the console
;
; We send an asterisk to the console first for two reasons:
; - it's a visual sign that the partition record was successfully loaded and executed
; - we need to know where it's loaded, so we can now look at the stack and get the return address
;
TSX ;Get stack pointer
LDA $0100,X ;Get return address high byte
STA $01 ;Save it as a Page Zero pointer
DEX ;Decrement pointer
LDA $0100,X ;Get return address low byte
STA $00 ;Save it as a Page Zero pointer
;
; Now, subtract 4 from the 16-bit address to point to our entry address
SEC ;Set carry for subtraction
LDA $00 ;Get low byte address
SBC #$04 ;Subtract 4
STA $00 ;Store it back
STA $02 ;Save it for Text Printing
LDA $01 ;Get high byte address
SBC #$00 ;Subtract carry flag
STA $01 ;Store it back
STA $03 ;Store it for Text Printing
;
; We have now the location the partition record was loaded to as a Page Zero indirect pointer
; - We now need to ccheck the partition record for the correct signature. The signature is
; - the standard $AA55 at the end, which is on one page further down. So we increment the
; - high byte address pointer to access the second page.
;
INC $01 ;Increment high byte address to get to second page of record
LDY #$FF ;Load index to past byte of partition record
LDA ($00),Y ;Get signature of last byte
CMP #$AA ;Check for correct bit pattern
BNE BAD_PART ;Branch if not equal
DEY ;Decrement index to next signature byte
LDA ($00),Y ;Get signature of next to last byte
CMP #$55 ;Check for correct bit pattern
BNE BAD_PART ;Branch if not equal
;
; Partition Record has the correct signature, yay!
; - Now we need to scan the boot record entries to see if we have an active one
; - If not, we send a message to the console showing no active parition, then
; - we warm boot to the Monitor code.
;
; If we find an active partition, we will check the starting block address, ensure
; - it's within the range of out BIOS, then load the parameters and read the boot block
; - Once loaded, we will jump to the starting address of the boot block, we're done!
;
DEC $01 ;Decrement back to the first page
LDX #16 ;Set count for 16 Boot Records
BOOT_REC_LP
LDY #<BOOT_RECORD ;Get low byte offset to Boot record start
LDA ($00),Y ;Get first byte of Boot record
BMI BOOT_FOUND ;Active Boot record found!
DEX ;Decrement count
BEQ NO_ACTIVE ;No active Boot Record found, branch
;
; Next, add 16 to the page zero indirect address.
; - This is done so we can step through each of the boot records scanning for one
; - that is active. It's just a simple add 16 to the pointers.
;
CLC ;Clear carry for add
LDA $00 ;Get low byte of pointer
ADC #16 ;Add 16 for offset to next boot record
STA $00 ;Store it back
LDA $01 ;Get high byte of pointer
ADC #00 ;Add in carry
STA $01 ;Store it back
;
BRA BOOT_REC_LP ;Loop back for next
;
BOOT_FOUND
CLC ;Clear carry for add
LDA $00 ;Get Boot record offset
ADC #$FE ;Add offset to LBA start
STA $00 ;Store it back
LDA $01 ;Get high byte
ADC #$00 ;Add in carry
STA $01 ;store it back
;
LDY #08 ;Get offset to Boot record first LBA
LDA ($00),Y ;Get first byte
STA $04 ;Store it
INY ;Increment to next byte value
LDA ($00),Y ;Get second byte
STA $05 ;Store it
INY ;Increment to next byte value
LDA ($00),Y ;Get third byte
STA $06 ;Store it
INY ;Increment to next byte value
LDA ($00),Y ;Get fourth byte
STA $07 ;Store it
;
LDA $04 ;Load LBA number to load (24-bit)
LDY $05
LDX $06
JSR B_IDE_SET_LBA ;Call BIOS to Set address
;
LDX #$01 ;Set Block count to 1
LDA #<BOOT_BUFFER ;Set low byte of BOOT Buffer
LDY #>BOOT_BUFFER ;Set high byte of BOOT buffer
JSR B_IDE_SET_ADDR ;Call BIOS to set address/count
;
JSR B_IDE_READ_LBA ;Call BIOS to read block into memory
LDA IDE_STATUS_RAM ;Get Status from BIOS call
LSR A ;Shift error bit to carry
BCS BOOT_ERROR ;Branch if error
JMP BOOT_BUFFER ;Jump to Boot record, we're done!
;
BOOT_ERROR
LDY $03 ;Get High byte to our location
CLC ;Clear carry for add
LDA #<BAD_BLOCK ;Get low byte offset
ADC $02 ;Add any offset from record location
BRA MSG_FINISH ;Use routine above to finish message and Monitor entry
;
; We have a bad partition record! The two signature bytes at the end of the record are
; not correct. Therefore, we send a message out the to console, then enter the Monitor
; via the Warm Boot vector (JMP table call).
;
BAD_PART
LDY $03 ;Get High byte to our location
CLC ;Clear carry for add
LDA #<BAD_REC_MSG ;Get low byte offset
ADC $02 ;Add any offset from record location
MSG_FINISH
JSR M_PROMPTR ;Send message to console
JMP M_WARM_MON ;Warm Boot Monitor
;
; We have not found any of the Boot Records to have an active flag. Therefore, we can not
; attempt to load a boot record and continue booting from the disk. We simply send a message
; to the console and go back to the Monitor via the Warm Boot entry.
;
NO_ACTIVE
LDY $03 ;Get High byte to our location
CLC ;Clear carry for add
LDA #<NO_ACT_PART ;Get low byte offset
ADC $02 ;Add any offset from record location
BRA MSG_FINISH ;Use routine above to finish message and Monitor entry
;
BAD_REC_MSG
.DB 13,10,'Bad Partition Record',13,10,0 ;Bad record message
;
NO_ACT_PART
.DB 13,10,'No Active Partition',13,10,0 ;No active partition record
;
BAD_BLOCK
.DB 13,10,'Bad Boot Block!',13,10,0 ;Bad boot block read
;
COPYRIGHT
.DB 'K.E. Maier'
;
.ORG $10FC ;Offset for 2-byte signature
;
.DW $6502 ;Litte-Endian signature for 6502 partition
;
;**************************************************************************************************
;
;Partition Records are 16 bytes in length and have the following format:
;
; Offset Length Description
; 0x00 1 byte Status: bit 7 used for active (1), all other bits zero
; 0x01 3 bytes CHS address of first sector in partition
; 0x04 1 byte Partition Type: "db" is for CPM
; 0x05 3 bytes CHS addres of last sector in partition
; 0X08 4 bytes LBA of first Block in partition
; 0x0C 4 bytes Number of Blocks in partition
;
; note: if LBA addressing is used, both CHS fields should be zeroed out!
;
; For Partitioning, LBA will is used, as the BIOS only supports LBA addressing for the drive.
;
;**************************************************************************************************
;
; .ORG $10FE ;Offset to boot records
;
BOOT_RECORD ;Start of Boot records
;
;Partition Records start here:
; - The first 12 records are Expanded Partition Entries per Ontrack Disk Manager
; - The last 4 records are the standard Primary partition Entries
;
Partition_0x04
;
.DB %00000000 ;Bit Mask for active partition bit 7 shows as active
.DB $00, $00, $00 ;First CHS Field - zeros as LBA mode is used
.DB $DB ;CPM Partition identifier
.DB $00, $00, $00 ;Last CHS Field - zeros as LBA mode is used
.LONG $0 ;First LBA Block in partition
.LONG $0 ;Number of Blocks in partition
;
Partition_0x05
;
.DB %00000000 ;Bit Mask for active partition bit 7 shows as active
.DB $00, $00, $00 ;First CHS Field - zeros as LBA mode is used
.DB $DB ;CPM Partition identifier
.DB $00, $00, $00 ;Last CHS Field - zeros as LBA mode is used
.LONG $0 ;First LBA Block in partition
.LONG $0 ;Number of Blocks in partition
;
Partition_0x06
;
.DB %00000000 ;Bit Mask for active partition bit 7 shows as active
.DB $00, $00, $00 ;First CHS Field - zeros as LBA mode is used
.DB $DB ;CPM Partition identifier
.DB $00, $00, $00 ;Last CHS Field - zeros as LBA mode is used
.LONG $0 ;First LBA Block in partition
.LONG $0 ;Number of Blocks in partition
;
Partition_0x07
;
.DB %00000000 ;Bit Mask for active partition bit 7 shows as active
.DB $00, $00, $00 ;First CHS Field - zeros as LBA mode is used
.DB $DB ;CPM Partition identifier
.DB $00, $00, $00 ;Last CHS Field - zeros as LBA mode is used
.LONG $0 ;First LBA Block in partition
.LONG $0 ;Number of Blocks in partition
;
Partition_0x08
;
.DB %00000000 ;Bit Mask for active partition bit 7 shows as active
.DB $00, $00, $00 ;First CHS Field - zeros as LBA mode is used
.DB $DB ;CPM Partition identifier
.DB $00, $00, $00 ;Last CHS Field - zeros as LBA mode is used
.LONG $0 ;First LBA Block in partition
.LONG $0 ;Number of Blocks in partition
;
Partition_0x09
;
.DB %00000000 ;Bit Mask for active partition bit 7 shows as active
.DB $00, $00, $00 ;First CHS Field - zeros as LBA mode is used
.DB $DB ;CPM Partition identifier
.DB $00, $00, $00 ;Last CHS Field - zeros as LBA mode is used
.LONG $0 ;First LBA Block in partition
.LONG $0 ;Number of Blocks in partition
;
Partition_0x0A
;
.DB %00000000 ;Bit Mask for active partition bit 7 shows as active
.DB $00, $00, $00 ;First CHS Field - zeros as LBA mode is used
.DB $DB ;CPM Partition identifier
.DB $00, $00, $00 ;Last CHS Field - zeros as LBA mode is used
.LONG $0 ;First LBA Block in partition
.LONG $0 ;Number of Blocks in partition
;
Partition_0x0B
;
.DB %00000000 ;Bit Mask for active partition bit 7 shows as active
.DB $00, $00, $00 ;First CHS Field - zeros as LBA mode is used
.DB $DB ;CPM Partition identifier
.DB $00, $00, $00 ;Last CHS Field - zeros as LBA mode is used
.LONG $0 ;First LBA Block in partition
.LONG $0 ;Number of Blocks in partition
;
Partition_0x0C
;
.DB %00000000 ;Bit Mask for active partition bit 7 shows as active
.DB $00, $00, $00 ;First CHS Field - zeros as LBA mode is used
.DB $DB ;CPM Partition identifier
.DB $00, $00, $00 ;Last CHS Field - zeros as LBA mode is used
.LONG $0 ;First LBA Block in partition
.LONG $0 ;Number of Blocks in partition
;
Partition_0x0D
;
.DB %00000000 ;Bit Mask for active partition bit 7 shows as active
.DB $00, $00, $00 ;First CHS Field - zeros as LBA mode is used
.DB $DB ;CPM Partition identifier
.DB $00, $00, $00 ;Last CHS Field - zeros as LBA mode is used
.LONG $0 ;First LBA Block in partition
.LONG $0 ;Number of Blocks in partition
;
Partition_0x0E
;
.DB %00000000 ;Bit Mask for active partition bit 7 shows as active
.DB $00, $00, $00 ;First CHS Field - zeros as LBA mode is used
.DB $DB ;CPM Partition identifier
.DB $00, $00, $00 ;Last CHS Field - zeros as LBA mode is used
.LONG $0 ;First LBA Block in partition
.LONG $0 ;Number of Blocks in partition
;
Partition_0x0F
;
.DB %00000000 ;Bit Mask for active partition bit 7 shows as active
.DB $00, $00, $00 ;First CHS Field - zeros as LBA mode is used
.DB $DB ;CPM Partition identifier
.DB $00, $00, $00 ;Last CHS Field - zeros as LBA mode is used
.LONG $0 ;First LBA Block in partition
.LONG $0 ;Number of Blocks in partition
;
Partition_0x00
;
.DB %10000000 ;Bit Mask for active partition bit 7 shows as active
.DB $00, $00, $00 ;First CHS Field - zeros as LBA mode is used
.DB $DB ;CPM Partition identifier
.DB $00, $00, $00 ;Last CHS Field - zeros as LBA mode is used
.LONG 16384 ;First LBA Block in partition
.LONG 131072 ;Number of Blocks in partition
;
Partition_0x01
;
.DB %00000000 ;Bit Mask for active partition bit 7 shows as active
.DB $00, $00, $00 ;First CHS Field - zeros as LBA mode is used
.DB $DB ;CPM Partition identifier
.DB $00, $00, $00 ;Last CHS Field - zeros as LBA mode is used
.LONG 147456 ;First LBA Block in partition
.LONG 131072 ;Number of Blocks in partition
;
Partition_0x02
;
.DB %00000000 ;Bit Mask for active partition bit 7 shows as active
.DB $00, $00, $00 ;First CHS Field - zeros as LBA mode is used
.DB $DB ;CPM Partition identifier
.DB $00, $00, $00 ;Last CHS Field - zeros as LBA mode is used
.LONG 278528 ;First LBA Block in partition
.LONG 131072 ;Number of Blocks in partition
;
Partition_0x03
;
.DB %00000000 ;Bit Mask for active partition bit 7 shows as active
.DB $00, $00, $00 ;First CHS Field - zeros as LBA mode is used
.DB $DB ;CPM Partition identifier
.DB $00, $00, $00 ;Last CHS Field - zeros as LBA mode is used
.LONG 409600 ;First LBA Block in partition
.LONG 131072 ;Number of Blocks in partition
;
;**************************************************************************************************
; Partition Block ends with standard 2-byte signature to show valid record
;
.DW $AA55 ;Signature bytes - mark as valid partition record
;
;**************************************************************************************************
.END
It's still a little rough... lipstick needed, but it's functional.
The boot sequence from the Monitor is here:
Code:
;[CTRL-B] Boot from the Microdrive:
; - A Partition Record format has been decided to allow an OS to be booted from the Microdrive.
; - The Partition Record is located at LBA 0 on the Microdrive. This routine will set the block
; - parameters to load the first LBA from the drive and store it at the default buffer location.
; - The Partition Record has a 2-byte signature at an offset of 252 bytes. It's been decided that
; - the 2-byte signature will be $6502 as a hex word, i.e., stored $02, $65. If this is found.
; - the Monitor will jump to the beginning of the partition block loaded and it will be up to the
; - the Parition Record code to either continue a boot from disk or return to the Monitor via a
; - warm boot. The only two reasons to return are:
; - An invalid 2-byte signature was found at the end of the Partition Record ($AA55).
; - No Boot Record was found to be marked as Active, so there's no bootable partition.
;
BOOT_MICRODRIVE
LDA #$00 ;Load low byte LBA address
TAY ;Same for high LBA address
TAX ;Same for extended LBA address
JSR B_IDE_SET_LBA ;Call BIOS to setup LBA number
;
LDA #<LBA_BUFFER ;Set Address low byte
LDY #>LBA_BUFFER ;Set Address high byte
LDX #$01 ;Set Block count to 1
JSR B_IDE_SET_ADDR ;Set Xfer address and block count
;
JSR B_IDE_READ_LBA ;Read Block Zero to Buffer
LDA IDE_STATUS_RAM ;Get Status from BIOS call
LSR A ;Shift error bit to carry
BCS IDE_RD_ERR ;Branch if error
;
LDX #252 ;Get offset to signature
LDA LBA_BUFFER,X ;Get signature byte
CMP #$02 ;Compare to $02
BNE BAD_PART_BLK ;Branch if incorrect
INX ;Increment index to next signature byte
LDA LBA_BUFFER,X ;Get signature byte
CMP #$65 ;Compare to $65
BNE BAD_PART_BLK ;Branch if incorrect
;
;Signature is good! Now just jump to the Partition Record in LBA_BUFFER
JMP LBA_BUFFER
;
IDE_RD_ERR
LDA #$4A ;Microdrive Error message
JMP PROMPT ;Send message and exit
;
BAD_PART_BLK
LDA #$4B ;Partition Error message
JMP PROMPT ;Send message and exit
;
There's a couple messages if things go wrong, but this is pretty failsafe.