1 #include <stdint.h>
2 #include <cstring>
3 #include <string>
4 
5 #include "bootloader.h"
6 
7 namespace gambatte {
8 
Bootloader()9 Bootloader::Bootloader() {
10    get_raw_bootloader_data = NULL;
11 }
12 
patch_gbc_to_gba_mode()13 void Bootloader::patch_gbc_to_gba_mode() {
14    /*moves one jump over another and puts ld b,0x01 into the original position*/
15    uint16_t patchloc = 0xF2;
16    uint8_t patch[0x7] = {0xCD,0xD0,0x05/*<-call systemsetup*/,0x06,0x01/*<-ld b,0x1*/,0x00/*<-nop*/,0x00/*<-nop*/};
17    std::memcpy(bootromswapspace + patchloc, patch, 0x7);
18 }
19 
load(bool isgbc,bool isgba)20 void Bootloader::load(bool isgbc, bool isgba) {
21    if (get_raw_bootloader_data == NULL) {
22       using_bootloader = false;
23       return;
24    }
25 
26    //the gba only uses the gbc bios
27    if (isgba)
28       isgbc = true;
29 
30    bool bootloaderavail = get_raw_bootloader_data((void*)this, isgbc, bootromswapspace, 0x900/*buf_size*/);
31    if (!bootloaderavail) {
32       using_bootloader = false;
33       return;
34    }
35 
36    if (isgbc)
37       bootloadersize = 0x900;
38    else
39       bootloadersize = 0x100;
40 
41    if (isgba)//patch bootloader to fake gba mode
42       patch_gbc_to_gba_mode();
43 
44    //backup rom segment that is shared with bootloader
45    std::memcpy(rombackup, (uint8_t*)addrspace_start, bootloadersize);
46 
47    //put back cartridge data in a 256 byte window of the bios that is not mapped(GBC only)
48    if (isgbc)
49       std::memcpy(bootromswapspace + 0x100, rombackup + 0x100, 0x100);
50 
51    //put bootloader in main memory
52    std::memcpy((uint8_t*)addrspace_start, bootromswapspace, bootloadersize);
53 
54    using_bootloader = true;
55 }
56 
reset()57 void Bootloader::reset() {
58    bootloadersize = 0;
59    has_called_FF50 = false;
60    addrspace_start = NULL;
61    using_bootloader = false;
62 }
63 
set_bootloader_getter(bool (* getter)(void * userdata,bool isgbc,uint8_t * data,uint32_t buf_size))64 void Bootloader::set_bootloader_getter(bool (*getter)(void* userdata, bool isgbc, uint8_t* data, uint32_t buf_size)) {
65    get_raw_bootloader_data = getter;
66 }
67 
set_address_space_start(void * start)68 void Bootloader::set_address_space_start(void* start) {
69    addrspace_start = start;
70 }
71 
choosebank(bool inbootloader)72 void Bootloader::choosebank(bool inbootloader) {
73    //inbootloader = (state.mem.ioamhram.get()[0x150] != 0xFF);//do not uncomment this is just for reference
74    if (using_bootloader) {
75 
76       //switching from game to bootloader with savestate
77       if (inbootloader && has_called_FF50)
78          uncall_FF50();
79 
80       //switching from bootloader to game with savestate
81       else if (!inbootloader && !has_called_FF50)
82          call_FF50();
83 
84       //switching from game to game or bootloader to bootloader needs no changes
85 
86    }
87 }
88 
call_FF50()89 void Bootloader::call_FF50() {
90    if (!has_called_FF50 && using_bootloader) {
91       //put rom back in main memory when bootloader has finished
92       std::memcpy((uint8_t*)addrspace_start, rombackup, bootloadersize);
93       has_called_FF50 = true;
94    }
95 }
96 
97 //this is a developer function only,a real gameboy can never undo calling 0xFF50,this function is for savestate functionality
uncall_FF50()98 void Bootloader::uncall_FF50() {
99    std::memcpy((uint8_t*)addrspace_start, bootromswapspace, bootloadersize);
100    has_called_FF50 = false;
101 }
102 
103 }
104