-
Notifications
You must be signed in to change notification settings - Fork 2.6k
/
GBA.cpp
153 lines (131 loc) · 4.76 KB
/
GBA.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Copyright 2010 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Core/HW/DSPHLE/UCodes/GBA.h"
#include "Common/Align.h"
#include "Common/ChunkFile.h"
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
#include "Core/HW/DSP.h"
#include "Core/HW/DSPHLE/DSPHLE.h"
#include "Core/HW/DSPHLE/MailHandler.h"
#include "Core/HW/DSPHLE/UCodes/UCodes.h"
#include "Core/System.h"
namespace DSP::HLE
{
void ProcessGBACrypto(Memory::MemoryManager& memory, u32 address)
{
// Nonce challenge (first read from GBA, hence already little-endian)
const u32 challenge = HLEMemory_Read_U32LE(memory, address);
// Palette of pulsing logo on GBA during transmission [0,6]
const u32 logo_palette = HLEMemory_Read_U32(memory, address + 4);
// Speed and direction of palette interpolation [-4,4]
const u32 logo_speed_32 = HLEMemory_Read_U32(memory, address + 8);
// Length of JoyBoot program to upload
const u32 length = HLEMemory_Read_U32(memory, address + 12);
// Address to return results to game
const u32 dest_addr = HLEMemory_Read_U32(memory, address + 16);
// Unwrap key from challenge using 'sedo' magic number (to encrypt JoyBoot program)
const u32 key = challenge ^ 0x6f646573;
HLEMemory_Write_U32(memory, dest_addr, key);
// Pack palette parameters
u16 palette_speed_coded;
const s16 logo_speed = static_cast<s8>(logo_speed_32);
if (logo_speed < 0)
palette_speed_coded = ((-logo_speed + 2) * 2) | (logo_palette << 4);
else if (logo_speed == 0)
palette_speed_coded = (logo_palette * 2) | 0x70;
else // logo_speed > 0
palette_speed_coded = ((logo_speed - 1) * 2) | (logo_palette << 4);
// JoyBoot ROMs start with a padded header; this is the length beyond that header
const s32 length_no_header = Common::AlignUp(length, 8) - 0x200;
// The JoyBus protocol transmits in 4-byte packets while flipping a state flag;
// so the GBA BIOS counts the program length in 8-byte packet-pairs
const u16 packet_pair_count = (length_no_header < 0) ? 0 : length_no_header / 8;
palette_speed_coded |= (packet_pair_count & 0x4000) >> 14;
// Pack together encoded transmission parameters
u32 t1 = (((packet_pair_count << 16) | 0x3f80) & 0x3f80ffff) * 2;
t1 += (static_cast<s16>(static_cast<s8>(t1 >> 8)) & packet_pair_count) << 16;
const u32 t2 = ((palette_speed_coded & 0xff) << 16) + (t1 & 0xff0000) + ((t1 >> 8) & 0xffff00);
u32 t3 = palette_speed_coded << 16 | ((t2 << 8) & 0xff000000) | (t1 >> 16) | 0x80808080;
// Wrap with 'Kawa' or 'sedo' (Kawasedo is the author of the BIOS cipher)
t3 ^= ((t3 & 0x200) != 0 ? 0x6f646573 : 0x6177614b);
HLEMemory_Write_U32(memory, dest_addr + 4, t3);
// Done!
DEBUG_LOG_FMT(DSPHLE,
"\n{:08x} -> challenge: {:08x}, len: {:08x}, dest_addr: {:08x}, "
"palette: {:08x}, speed: {:08x} key: {:08x}, auth_code: {:08x}",
address, challenge, length, dest_addr, logo_palette, logo_speed_32, key, t3);
}
GBAUCode::GBAUCode(DSPHLE* dsphle, u32 crc) : UCodeInterface(dsphle, crc)
{
}
void GBAUCode::Initialize()
{
m_mail_handler.PushMail(DSP_INIT);
}
void GBAUCode::Update()
{
// check if we have something to send
if (m_mail_handler.HasPending())
{
m_dsphle->GetSystem().GetDSP().GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP);
}
}
void GBAUCode::HandleMail(u32 mail)
{
if (m_upload_setup_in_progress)
{
PrepareBootUCode(mail);
// The GBA ucode ignores the first 3 mails (mram_dest_addr, mram_size, mram_dram_addr)
// but we currently don't handle that (they're read when they shoudln't be, but DSP HLE doesn't
// implement them so it's fine).
return;
}
switch (m_mail_state)
{
case MailState::WaitingForRequest:
{
if (mail == REQUEST_MAIL)
{
INFO_LOG_FMT(DSPHLE, "GBAUCode - Recieved request mail");
m_mail_state = MailState::WaitingForAddress;
}
else
{
WARN_LOG_FMT(DSPHLE, "GBAUCode - Expected request mail but got {:08x}", mail);
}
break;
}
case MailState::WaitingForAddress:
{
const u32 address = mail & 0x0fff'ffff;
ProcessGBACrypto(m_dsphle->GetSystem().GetMemory(), address);
m_mail_handler.PushMail(DSP_DONE);
m_mail_state = MailState::WaitingForNextTask;
break;
}
case MailState::WaitingForNextTask:
{
// The GBA uCode checks that the high word is cdd1, so we compare the full mail with
// MAIL_NEW_UCODE/MAIL_RESET without doing masking
switch (mail)
{
case MAIL_NEW_UCODE:
m_upload_setup_in_progress = true;
break;
case MAIL_RESET:
m_dsphle->SetUCode(UCODE_ROM);
break;
default:
WARN_LOG_FMT(DSPHLE, "GBAUCode - unknown 0xcdd1 command: {:08x}", mail);
break;
}
}
}
}
void GBAUCode::DoState(PointerWrap& p)
{
DoStateShared(p);
p.Do(m_mail_state);
}
} // namespace DSP::HLE