Skip to content

Commit

Permalink
Merge pull request #4 from jfharden/update-rom-patch
Browse files Browse the repository at this point in the history
Update option rom patch
  • Loading branch information
jfharden authored Mar 17, 2024
2 parents 75c67d1 + d08cc9b commit 79f48c2
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 84 deletions.
10 changes: 5 additions & 5 deletions .github/workflows/post-merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ jobs:
needs:
- test
outputs:
release: "${{ steps.tag-release.outputs.release }}"
version: "${{ steps.tag-release.outputs.version }}"
release: "${{ steps.check-for-existing-release.outputs.release }}"
version: "${{ steps.check-for-existing-release.outputs.version }}"

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- shell: bash
id: tag-release
name: tag-release
id: check-for-existing-release
name: check-for-existing-release
run: |
VERSION=$(yq -oy '.package.version' Cargo.toml)
Expand All @@ -37,7 +37,7 @@ jobs:
exit 0
fi
echo "Continuing to create release tag for $VERSION"
echo "Release required for $VERSION"
echo "release=true" >> $GITHUB_OUTPUT
echo "version=$VERSION" >> $GITHUB_OUTPUT
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bridgeboard-pc-boot-patcher"
version = "0.1.1"
version = "0.2.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
50 changes: 46 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,55 @@
Release: [latest](https://github.com/jfharden/bridgeboard-pc-boot-patcher/releases/latest)

Patch the Amiga Bridgeboard option rom in the pc.boot file in order to not use the autoboot functionality of the
bridgeboard. This should allow an XTIDE set in the 0xC000 memory range to be used.
bridgeboard. This should allow an XTIDE set in the 0xC000 memory range to be used, specifically I found 0xCC00 to work.

It can also validate option rom checksums, and update the checksum (in the final byte of the rom).

## Requirements

1. An XTIDE using a recent version of the ROM (tested with r625) and using one of the ROMs which has VeryLateInit (this
is the default in the XT rom in the r625 XTIDE rom)
2. An up to date version of the Bridgeboard software, tested with Janus Handler Version 36.85 and Janus Library Version
36.83), I used The AmigaJanus 2.1 package from https://amiga.resource.cx/exp/a2286at

## Usage

To create a new pc.boot file with the patch applied:

```
$ bridgeboard-pc-boot-patcher <path_to_pc.boot> write-rom <new_pc.boot_file_name> --patch-rom
```

For example, if the file is pc.boot and you want to create pc.boot.new

```
$ bridgeboard-pc-boot-patcher pc.boot write-rom pc.boot.new --patch-rom
ORIGINAL_ROM_SIZE: 0x2000
PATCHED_ROM_SIZE: 0x2000
Rom written to pc.boot.new
```

You should then take that pc.boot file and copy it into SYS:PC/System/pc.boot, I strongly suggest keeping a backup of
pc.boot on the amiga, and also if you have an aboot.ctrl file to rename it:

For example, the file pc.boot.new is already in PC/System:

```
cd SYS:PC/System
copy pc.boot pc.boot.original
delete pc.boot
copy pc.boot.new pc.boot
# Optionally if you have an aboot.ctrl:
rename aboot.ctrl aboot.ctrl.original
```

Make sure you have the Memory map in PCPrefs set to the D000 range.

Now reboot the Amiga with the XTIDE in.

## Current Status

The ROM validation and checksum updating works. The current status of the patch to the option rom contained in the
pc.boot file doesn't work and is under current development.
The ROM patch has been tested with Amiga Janus 2.1 only, and only on an Amiga 2000 with an A2286 Bridgeboard.

A more comprehensive README update will come soon.
I've only been able to test the built executable on macOS.
146 changes: 73 additions & 73 deletions src/option_rom_patcher.rs
Original file line number Diff line number Diff line change
@@ -1,52 +1,59 @@
use std::fmt;

use crate::option_rom::{OptionRom, OptionRomError, OPTION_ROM_HEADER};
use crate::option_rom::{OptionRom, OptionRomError};

#[derive(Debug)]
pub enum OptionRomPatcherError {
CouldntLocateInitialJump,
OptionRomGenerationError(OptionRomError),
CouldntLocateReturnToBIOS,
NotEnoughBytesAfterReturnToBIOS,
BytesAfterReturnToBIOSDontLookEmpty,
CouldntLocateHddReadyCheck,
CouldntLocateAfterInt13Set,
JumpLengthTooBig
}

impl fmt::Display for OptionRomPatcherError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
OptionRomPatcherError::CouldntLocateInitialJump => write!(f, "Couldn't find the initial JMP SHORT instruction."),
OptionRomPatcherError::CouldntLocateHddReadyCheck => write!(f, "Couldn't find the HDD ready check."),
OptionRomPatcherError::CouldntLocateAfterInt13Set => write!(f, "Couldn't find the end of the code which sets the INT13 handler."),
OptionRomPatcherError::JumpLengthTooBig => write!(f, "The distance to JMP to avoid setting INT13 is too big."),
OptionRomPatcherError::OptionRomGenerationError(e) => write!(f, "{}", e),
OptionRomPatcherError::CouldntLocateReturnToBIOS => write!(f, "Coulnd't find the expected INT 18h followed by IRET instructions"),
OptionRomPatcherError::NotEnoughBytesAfterReturnToBIOS => write!(f, "Not enough bytes after the return (INT 18h followed by IRET) to BIOS"),
OptionRomPatcherError::BytesAfterReturnToBIOSDontLookEmpty => write!(f, "The bytes after the return (INT 18h followed by IRET) to BIOS do not look empty"),
}
}
}

const X86_INT: u8 = 0xCD;
const X86_IRET: u8 = 0xCF;
const X86_MOV_INTO_AX: u8 = 0xA1;
const X86_MOV_FROM_AX: u8 = 0xA1;
const X86_PUSH_AX: u8 = 0x50;
const X86_MOV_INTO_AH: u8 = 0xb4;
const X86_MOV_INTO_DL: u8 = 0xb2;
const X86_POP_AX: u8 = 0x58;
const X86_JMP_NEAR: u8 = 0xEB;

const PATCH_HEADER: [u8; 8] = [
X86_PUSH_AX,
X86_MOV_INTO_AX, 0x64, 0x00, // MOV AX, [0x0064]
X86_PUSH_AX,
X86_MOV_INTO_AX, 0x66, 0x00, // MOV AX, [0x0066]
const X86_POP_DX: u8 = 0x5a;
const X86_POP_ES: u8 = 0x07;
const X86_JC: u8 = 0x72;
const X86_JMP: u8 = 0xeb;

const X86_MOV_SEGMENT_REGISTER_TO_MEMORY_ADDRESS: u8 = 0x8c;
const X86_MOV_GENERAL_REGISTER_TO_MEMORY_ADDRESS: u8 = 0x89;
const X86_ES_SEGMENT_REGISTER: u8 = 0x06;
const X86_DI_GENERAL_REGISTER: u8 = 0x3e;

const HDD_READY_CHECK_SEARCH: [u8; 9] = [
X86_MOV_INTO_AH, 0x10,
X86_MOV_INTO_DL, 0x80,
X86_INT, 0x13,
X86_POP_DX,
X86_POP_AX,
X86_JC,
];

const PATCH_FOOTER: [u8; 8] = [
X86_MOV_FROM_AX, 0x66, 0x00, // MOV [0x0066], AX
X86_POP_AX,
X86_MOV_FROM_AX, 0x64, 0x00, // MOV [0x0064], AX
X86_POP_AX,
const INT_13_SET_FINISHED_SEARCH: [u8; 9] = [
X86_MOV_SEGMENT_REGISTER_TO_MEMORY_ADDRESS, X86_ES_SEGMENT_REGISTER, 0x1e, 0x20,
X86_MOV_GENERAL_REGISTER_TO_MEMORY_ADDRESS, X86_DI_GENERAL_REGISTER, 0x1c, 0x20,
X86_POP_ES,
];

pub fn patch_rom(option_rom: &OptionRom) -> Result<OptionRom, OptionRomPatcherError> {
println!("ORIGINAL_ROM_SIZE: 0x{:04X}", option_rom.bytes.len());
let patched_rom_bytes: Vec<u8> = generate_patched_rom(option_rom)?;
println!("PATCHED_ROM_SIZE: 0x{:04X}", patched_rom_bytes.len());
let mut patched_rom = match OptionRom::from(patched_rom_bytes, 0) {
Ok(patched_rom) => patched_rom,
Err(e) => {
Expand All @@ -59,67 +66,60 @@ pub fn patch_rom(option_rom: &OptionRom) -> Result<OptionRom, OptionRomPatcherEr
}

fn generate_patched_rom(option_rom: &OptionRom) -> Result<Vec<u8>, OptionRomPatcherError> {
let new_entrypoint_jump_size: u8 = match calculate_new_entrypoint_jump(option_rom) {
let location_of_hdd_not_ready_jump = match find_location_of_hdd_not_ready_jump(option_rom) {
Err(e) => return Err(e),
Ok(new_entrypoint_jump_size) => new_entrypoint_jump_size,
Ok(location) => location,
};
let location_of_int_13_set_finished = match find_location_after_int_13_set(option_rom) {
Err(e) => return Err(e),
Ok(location) => location,
};
let post_header_patch_location: usize = 5 + PATCH_HEADER.len();
let return_to_bios_location = find_location_of_return_to_bios(option_rom)?;
let post_footer_patch_location: usize = return_to_bios_location + PATCH_FOOTER.len() + 1;

let mut new_rom_bytes: Vec<u8> = vec![OPTION_ROM_HEADER[0], OPTION_ROM_HEADER[1], option_rom.bytes[2]]; // Option ROM header
new_rom_bytes.extend_from_slice(&PATCH_HEADER); // Our patch code
new_rom_bytes.push(X86_JMP_NEAR); // JMP SHORT
new_rom_bytes.push(new_entrypoint_jump_size); // JMP Location


new_rom_bytes.extend_from_slice(&option_rom.bytes[post_header_patch_location..return_to_bios_location]); // ROM upto return to bios
new_rom_bytes.extend_from_slice(&PATCH_FOOTER);
new_rom_bytes.push(X86_IRET);
new_rom_bytes.extend_from_slice(&option_rom.bytes[post_footer_patch_location..]);
// Need to add 2 on the location of the jump since thats where the JMP instruction will count from
let jump_length: u8 = match u8::try_from(location_of_int_13_set_finished - (location_of_hdd_not_ready_jump+2)) {
Ok(jump_length) => jump_length,
Err(_) => return Err(OptionRomPatcherError::JumpLengthTooBig),
};

let mut new_rom_bytes: Vec<u8> = option_rom.bytes[0..location_of_hdd_not_ready_jump].to_vec();
new_rom_bytes.push(X86_JMP);
new_rom_bytes.push(jump_length);
new_rom_bytes.extend_from_slice(&option_rom.bytes[location_of_hdd_not_ready_jump+2..]);
Ok(new_rom_bytes)
}

fn calculate_new_entrypoint_jump(option_rom: &OptionRom) -> Result<u8, OptionRomPatcherError> {
let existing_entrypoint_jump_size = get_existing_entrypoint_jump(option_rom)?;

return Ok(existing_entrypoint_jump_size - PATCH_HEADER.len() as u8);
}

fn get_existing_entrypoint_jump(option_rom: &OptionRom) -> Result<u8, OptionRomPatcherError> {
if option_rom.bytes[3] != X86_JMP_NEAR {
return Err(OptionRomPatcherError::CouldntLocateInitialJump)
}

Ok(option_rom.bytes[4])
}

fn find_location_of_return_to_bios(option_rom: &OptionRom) -> Result<usize, OptionRomPatcherError> {
for i in 0..option_rom.bytes.len()-3 {
if option_rom.bytes[i] == X86_INT &&
option_rom.bytes[i+1] == 0x18 &&
option_rom.bytes[i+2] == X86_IRET {
check_for_free_space(option_rom, i+3)?;
return Ok(i+2);
fn find_location_of_hdd_not_ready_jump(option_rom: &OptionRom) -> Result<usize, OptionRomPatcherError> {
for i in 0..option_rom.bytes.len()-10 {
if option_rom.bytes[i] == HDD_READY_CHECK_SEARCH[0] &&
option_rom.bytes[i+1] == HDD_READY_CHECK_SEARCH[1] &&
option_rom.bytes[i+2] == HDD_READY_CHECK_SEARCH[2] &&
option_rom.bytes[i+3] == HDD_READY_CHECK_SEARCH[3] &&
option_rom.bytes[i+4] == HDD_READY_CHECK_SEARCH[4] &&
option_rom.bytes[i+5] == HDD_READY_CHECK_SEARCH[5] &&
option_rom.bytes[i+6] == HDD_READY_CHECK_SEARCH[6] &&
option_rom.bytes[i+7] == HDD_READY_CHECK_SEARCH[7] &&
option_rom.bytes[i+8] == HDD_READY_CHECK_SEARCH[8] {
return Ok(i+8);
}
}

Err(OptionRomPatcherError::CouldntLocateReturnToBIOS)
Err(OptionRomPatcherError::CouldntLocateHddReadyCheck)
}

fn check_for_free_space(option_rom: &OptionRom, start_position: usize) -> Result<(), OptionRomPatcherError> {
if option_rom.bytes.len() < start_position + PATCH_FOOTER.len() {
return Err(OptionRomPatcherError::NotEnoughBytesAfterReturnToBIOS);
}

for i in 0..PATCH_FOOTER.len() {
// I'm not sure why but the pc.boot option rom sometimes has 0 and sometimes has 0x61 as
// blank space
if option_rom.bytes[start_position + i] != 0 && option_rom.bytes[start_position + i] != 0x61 {
return Err(OptionRomPatcherError::BytesAfterReturnToBIOSDontLookEmpty);
fn find_location_after_int_13_set(option_rom: &OptionRom) -> Result<usize, OptionRomPatcherError> {
for i in 0..option_rom.bytes.len()-10 {
if option_rom.bytes[i] == INT_13_SET_FINISHED_SEARCH[0] &&
option_rom.bytes[i+1] == INT_13_SET_FINISHED_SEARCH[1] &&
option_rom.bytes[i+2] == INT_13_SET_FINISHED_SEARCH[2] &&
option_rom.bytes[i+3] == INT_13_SET_FINISHED_SEARCH[3] &&
option_rom.bytes[i+4] == INT_13_SET_FINISHED_SEARCH[4] &&
option_rom.bytes[i+5] == INT_13_SET_FINISHED_SEARCH[5] &&
option_rom.bytes[i+6] == INT_13_SET_FINISHED_SEARCH[6] &&
option_rom.bytes[i+7] == INT_13_SET_FINISHED_SEARCH[7] &&
option_rom.bytes[i+8] == INT_13_SET_FINISHED_SEARCH[8] {
return Ok(i+9);
}
}

Ok(())
Err(OptionRomPatcherError::CouldntLocateAfterInt13Set)
}

0 comments on commit 79f48c2

Please sign in to comment.