IOS: add Starlet LLE milestone through HBC

Boot original Wii firmware through boot0, boot1, boot2 and IOS on an emulated ARM Starlet. Model the required IPC, memory, SD, USB and Bluetooth hardware behavior, including IOS reload into IOS58, and add focused tests, launch utilities and architecture documentation.
This commit is contained in:
2026-08-25 17:11:49 +02:00
parent 38e70fda65
commit a77c156bc8
46 changed files with 10556 additions and 158 deletions
@@ -14,8 +14,9 @@
#include "InputCommon/ControllerInterface/DInput/DInput.h"
// (lower would be more sensitive) user can lower sensitivity by setting range
// seems decent here ( at 8 ), I don't think anyone would need more sensitive than this
// and user can lower it much farther than they would want to with the range
// seems decent here ( at 8 ), I don't think anyone would need more sensitive
// than this and user can lower it much farther than they would want to with the
// range
#define MOUSE_AXIS_SENSITIVITY 8
// if input hasn't been received for this many ms, mouse input will be skipped
@@ -68,15 +69,15 @@ void InitKeyboardMouse(IDirectInput8* const idi8, HWND hwnd)
s_hwnd = hwnd;
// Mouse and keyboard are a combined device, to allow shift+click and stuff
// if that's dumb, I will make a VirtualDevice class that just uses ranges of inputs/outputs from
// other devices
// so there can be a separated Keyboard and mouse, as well as combined KeyboardMouse
// if that's dumb, I will make a VirtualDevice class that just uses ranges of
// inputs/outputs from other devices so there can be a separated Keyboard and
// mouse, as well as combined KeyboardMouse
LPDIRECTINPUTDEVICE8 kb_device = nullptr;
LPDIRECTINPUTDEVICE8 mo_device = nullptr;
// These are "virtual" system devices, so they are always there even if we have no physical
// mouse and keyboard plugged into the computer
// These are "virtual" system devices, so they are always there even if we
// have no physical mouse and keyboard plugged into the computer
if (SUCCEEDED(idi8->CreateDevice(GUID_SysKeyboard, &kb_device, nullptr)) &&
SUCCEEDED(kb_device->SetDataFormat(&c_dfDIKeyboard)) &&
SUCCEEDED(kb_device->SetCooperativeLevel(nullptr, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE)) &&
@@ -105,10 +106,12 @@ KeyboardMouse::~KeyboardMouse()
{
s_keyboard_mouse_exists = false;
// Independently of the order in which we do these, if we put a breakpoint on Unacquire() (or in
// any place in the call stack before this), when refreshing devices from the UI, on the second
// attempt, it will get stuck in an infinite (while) loop inside dinput8.dll. Given that it can't
// be otherwise be reproduced (not even with sleeps), we can just ignore the problem.
// Independently of the order in which we do these, if we put a breakpoint on
// Unacquire() (or in any place in the call stack before this), when
// refreshing devices from the UI, on the second attempt, it will get stuck in
// an infinite (while) loop inside dinput8.dll. Given that it can't be
// otherwise be reproduced (not even with sleeps), we can just ignore the
// problem.
// kb
m_kb_device->Unacquire();
@@ -152,7 +155,8 @@ KeyboardMouse::KeyboardMouse(const LPDIRECTINPUTDEVICE8 kb_device,
{
const LONG& ax = (&m_state_in.mouse.lX)[i];
// each axis gets a negative and a positive input instance associated with it
// each axis gets a negative and a positive input instance associated with
// it
AddInput(new Axis(i, ax, (2 == i) ? -1 : -MOUSE_AXIS_SENSITIVITY));
AddInput(new Axis(i, ax, -(2 == i) ? 1 : MOUSE_AXIS_SENSITIVITY));
}
@@ -171,7 +175,8 @@ KeyboardMouse::KeyboardMouse(const LPDIRECTINPUTDEVICE8 kb_device,
void KeyboardMouse::UpdateCursorInput()
{
// Get the size of the current window (in my case Rect.top and Rect.left was zero).
// Get the size of the current window (in my case Rect.top and Rect.left was
// zero).
RECT rect;
GetClientRect(s_hwnd, &rect);
@@ -193,10 +198,11 @@ void KeyboardMouse::UpdateCursorInput()
}
else if (Host_TASInputHasFocus())
{
// When a TAS Input window has focus and "Enable Controller Input" is checked most types of
// input should be read normally as if the render window had focus instead. The cursor is an
// exception, as otherwise using the mouse to set any control in the TAS Input window will also
// update the Wii IR value (or any other input controlled by the cursor).
// When a TAS Input window has focus and "Enable Controller Input" is
// checked most types of input should be read normally as if the render
// window had focus instead. The cursor is an exception, as otherwise using
// the mouse to set any control in the TAS Input window will also update the
// Wii IR value (or any other input controlled by the cursor).
return;
}
@@ -204,8 +210,8 @@ void KeyboardMouse::UpdateCursorInput()
{
GetCursorPos(&point);
// Get the cursor position relative to the upper left corner of the current window
// (separate or render to main)
// Get the cursor position relative to the upper left corner of the current
// window (separate or render to main)
ScreenToClient(s_hwnd, &point);
}
@@ -254,6 +260,17 @@ Core::DeviceRemoval KeyboardMouse::UpdateInput()
// copy over the buttons
std::copy_n(tmp_mouse.rgbButtons, std::size(tmp_mouse.rgbButtons), m_state_in.mouse.rgbButtons);
// DirectInput can miss synthetic and very short clicks even though Win32's
// asynchronous state still reports the button as held. Keeping the two
// sources additive makes mouse-backed Wii Remote mappings reliable without
// disturbing extra DirectInput buttons.
if (GetAsyncKeyState(VK_LBUTTON) & 0x8000)
m_state_in.mouse.rgbButtons[0] |= 0x80;
if (GetAsyncKeyState(VK_RBUTTON) & 0x8000)
m_state_in.mouse.rgbButtons[1] |= 0x80;
if (GetAsyncKeyState(VK_MBUTTON) & 0x8000)
m_state_in.mouse.rgbButtons[2] |= 0x80;
}
HRESULT kb_hr = m_kb_device->GetDeviceState(sizeof(m_state_in.keyboard), &m_state_in.keyboard);