├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── __elfcom.c ├── asm ├── _branch.s ├── _cpu_inst_0.s ├── _cpu_inst_1.s ├── _cpu_inst_2.s ├── _cpu_inst_3.s ├── _cpu_inst_4.s ├── _cpu_inst_5.s ├── _cpu_inst_6.s ├── _cpu_inst_7.s ├── _cpu_inst_8.s ├── _cpu_inst_9.s ├── _cpu_inst_A.s ├── _cpu_inst_B.s ├── _cpu_inst_C.s ├── _cpu_inst_D.s ├── _cpu_inst_E.s ├── _cpu_inst_F.s ├── _cpu_inst_prefix.s ├── _cpu_inst_table.s ├── _debug.s ├── _math.s ├── _mbc.s ├── _mbc7.s ├── _memory.s ├── _registers.s ├── _stopping_point.s ├── cpu.s ├── data.s ├── data_placeholder.s ├── entry.s ├── macros.inc ├── memory.inc ├── registers.inc └── rom_header.s ├── bin └── DUMMY ├── boot.c ├── boot.h ├── controller.c ├── controller.h ├── data ├── PokemonCrystal.rtc ├── cgb_bios_placeholder.asm ├── cgb_bios_placeholder.bin ├── dmg_boot_placeholder.asm ├── dmg_boot_placeholder.bin ├── rom_placeholder.gb └── save │ ├── gb.fla │ ├── gb.fla.hex │ ├── gb.pj │ └── gb.pj.hex ├── dram_stack.c ├── game.c ├── game.h ├── gb64.ld ├── gfx_extend.h ├── gfxinit.c ├── gzip ├── adler32.c ├── crc32.c ├── defl_static.c ├── defl_static.h ├── genlz77.c ├── tinf.h ├── tinf_compat.h ├── tinfgzip.c ├── tinflate.c ├── tinfzlib.c ├── uzlib.h └── uzlib_conf.h ├── memory.c ├── memory.h ├── memory_test.c ├── render.c ├── render.h ├── romwrapper ├── GB64Logo.png ├── crc.js ├── gb.n64 └── romwrapper.html ├── rsp ├── alias.s ├── data.s ├── dma.s ├── gameboy.s ├── main.s ├── ostask.s ├── ppu.s ├── pputask.s ├── registers.s ├── sprite.s └── tilemap.s ├── spec ├── src ├── assert.c ├── assert.h ├── audio.c ├── audio.h ├── bool.h ├── clockmenu.c ├── clockmenu.h ├── cpu.c ├── cpu.h ├── debug_out.c ├── debug_out.h ├── debugger.c ├── debugger.h ├── decoder.c ├── decoder.h ├── erasemenu.c ├── erasemenu.h ├── faulthandler.c ├── faulthandler.h ├── gameboy.c ├── gameboy.h ├── graphics.c ├── graphics.h ├── graphicsmenu.c ├── graphicsmenu.h ├── inputmapping.c ├── inputmapping.h ├── instructions.h ├── mainmenu.c ├── mainmenu.h ├── memory_map.c ├── memory_map.h ├── memory_map_offsets.h ├── menu.c ├── menu.h ├── polyfill.c ├── rom.c ├── rom.h ├── rspppu.c ├── rspppu.h ├── rspppu_includes.h ├── save.c ├── save.h ├── sprite.c ├── sprite.h ├── spritefont.c ├── spritefont.h ├── test │ ├── cpu_test.c │ ├── cpu_test.h │ ├── cpu_tests_0.c │ ├── cpu_tests_1.c │ ├── cpu_tests_2.c │ ├── cpu_tests_3.c │ ├── cpu_tests_4_7.c │ ├── cpu_tests_8_9.c │ ├── cpu_tests_A_B.c │ ├── cpu_tests_C.c │ ├── cpu_tests_D.c │ ├── cpu_tests_E.c │ ├── cpu_tests_F.c │ ├── cpu_tests_prefix_cb.c │ ├── interrupt_test.c │ ├── register_test.c │ ├── test.c │ └── test.h ├── upgrade.c ├── upgrade.h └── version.h ├── static.h ├── tex ├── cbuttons.h ├── dpad.h ├── facebuttons.h ├── gbfont_img.h ├── guiitems.h ├── textures.c ├── textures.h └── triggers.h └── texture.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.out 3 | *.n64 4 | .vscode/* 5 | bin/* 6 | *.gb 7 | *.gbc 8 | data/*.hex 9 | data/*.sav 10 | data/*.txt 11 | data/*.sn1 12 | data/dmg_boot.bin 13 | data/cgb_bios.bin 14 | data/*.s 15 | data/tests/* 16 | debugger/* 17 | !/bin/DUMMY 18 | tmp/ 19 | build/* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 lambertjamesd 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A gameboy emulator meant to run on N64 hardware. 2 | 3 | If you just want to use the emulator go here 4 | [https://lambertjamesd.github.io/gb64/romwrapper/romwrapper.html](https://lambertjamesd.github.io/gb64/romwrapper/romwrapper.html) 5 | 6 | To build, you will need to locate the original gameboy's boot rom and save it to a file named `data/dmg_boot.bin`. As well as `data/cgb_bios.bin` for the gameboy colors. 7 | You should be able to find both of these files here 8 | [https://gbdev.gg8.se/files/roms/bootroms/](https://gbdev.gg8.se/files/roms/bootroms/) 9 | 10 | You will also need to supply the gameboy rom you want to build into an N64 rom. You should put the gameboy rom into the `data` folder 11 | and update the include path in [spec](./spec) under the `gbrom` section -------------------------------------------------------------------------------- /__elfcom.c: -------------------------------------------------------------------------------- 1 | #include 2 | -------------------------------------------------------------------------------- /asm/_branch.s: -------------------------------------------------------------------------------- 1 | 2 | 3 | ####################### 4 | # Pop top of stack and return to it 5 | ####################### 6 | 7 | _GB_RET: 8 | addi ADDR, GB_SP, 0 9 | jal GB_DO_READ_16 10 | addi GB_SP, GB_SP, 2 11 | andi GB_SP, GB_SP, 0xFFFF 12 | jal SET_GB_PC 13 | move Param0, $v0 14 | j DECODE_NEXT 15 | nop 16 | 17 | _GB_CALL: 18 | addi GB_SP, GB_SP, -2 19 | andi GB_SP, GB_SP, 0xFFFF 20 | addi VAL, GB_PC, 2 21 | 22 | lbu $at, 0(PC_MEM_POINTER) 23 | lbu Param0, 1(PC_MEM_POINTER) 24 | sll Param0, Param0, 8 25 | jal SET_GB_PC 26 | or Param0, Param0, $at 27 | j GB_DO_WRITE_16 28 | addi ADDR, GB_SP, 0 29 | 30 | ####################### 31 | # Subtract Param0 to A 32 | ####################### 33 | 34 | _SKIP_JR: 35 | jal SET_GB_PC 36 | addi Param0, GB_PC, 1 37 | j DECODE_NEXT 38 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 39 | -------------------------------------------------------------------------------- /asm/_cpu_inst_0.s: -------------------------------------------------------------------------------- 1 | 2 | #### 0x0X 3 | GB_NOP: # start of jump table 4 | j DECODE_NEXT 5 | nop 6 | GB_LD_BC_D16: 7 | lbu GB_C, 0(PC_MEM_POINTER) 8 | lbu GB_B, 1(PC_MEM_POINTER) 9 | addi PC_MEM_POINTER, PC_MEM_POINTER, 2 10 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 2 # update cycles run 11 | j DECODE_NEXT 12 | addi GB_PC, GB_PC, 2 13 | GB_LD_BC_A: 14 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 15 | add VAL, GB_A, 0 # write the value to store 16 | sll ADDR, GB_B, 8 # write upper address 17 | j GB_DO_WRITE # call store subroutine 18 | or ADDR, ADDR, GB_C # write lower address 19 | GB_INC_BC: 20 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 21 | addi GB_C, GB_C, 1 # incement the register 22 | srl $at, GB_C, 8 23 | andi GB_C, GB_C, 0xFF # keep at 8 bits 24 | add GB_B, GB_B, $at # add carry bit 25 | j DECODE_NEXT 26 | andi GB_B, GB_B, 0xFF # keep at 8 bits 27 | GB_INC_B: 28 | jal GB_INC # call increment 29 | addi Param0, GB_B, 0 # move register to call parameter 30 | j DECODE_NEXT 31 | addi GB_B, Param0, 0 # move register back from call parameter 32 | GB_DEC_B: 33 | jal GB_DEC # call decrement high bit 34 | move Param0, GB_B # move register to call parameter 35 | j DECODE_NEXT 36 | move GB_B, Param0 # move register back from call parameter 37 | GB_LD_B_D8: 38 | lbu GB_B, 0(PC_MEM_POINTER) 39 | addi PC_MEM_POINTER, PC_MEM_POINTER, 1 40 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 41 | j DECODE_NEXT 42 | addi GB_PC, GB_PC, 1 43 | GB_RLCA: 44 | jal GB_RLC_IMPL # do RLC 45 | addi Param0, GB_A, 0 # store A into param 46 | addi GB_A, Param0, 0 # store result back into A 47 | j DECODE_NEXT 48 | clear_flags Z_FLAG 49 | GB_LD_A16_SP: 50 | jal READ_NEXT_INSTRUCTION_16 51 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 4 # update cycles run 52 | move ADDR, $v0 # use immediate address 53 | j GB_DO_WRITE_16 54 | addi VAL, GB_SP, 0 # store value to write 55 | GB_ADD_HL_BC: 56 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 57 | sll Param0, GB_B, 8 # load high order bits 58 | j _ADD_TO_HL 59 | or Param0, Param0, GB_C # load low order bits 60 | GB_LD_A_BC: 61 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 62 | sll ADDR, GB_B, 8 # load upper address 63 | jal GB_DO_READ # call read instruction 64 | or ADDR, ADDR, GB_C # load lower address 65 | j DECODE_NEXT 66 | addi GB_A, $v0, 0 # store result into a 67 | GB_DEC_BC: 68 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 69 | addi GB_C, GB_C, -1 # decrement C 70 | sra $at, GB_C, 8 # shift carry 71 | add GB_B, GB_B, $at # add carry to b 72 | andi GB_C, GB_C, 0xFF # mask C 73 | j DECODE_NEXT 74 | andi GB_B, GB_B, 0xFF # mask B 75 | GB_INC_C: 76 | jal GB_INC # call increment 77 | addi Param0, GB_C, 0 # move register to call parameter 78 | j DECODE_NEXT 79 | addi GB_C, Param0, 0 # move register back from call parameter 80 | GB_DEC_C: 81 | jal GB_DEC # call decrement high bit 82 | addi Param0, GB_C, 0 # move register to call parameter 83 | j DECODE_NEXT 84 | addi GB_C, Param0, 0 # move register back from call parameter 85 | GB_LD_C_D8: 86 | lbu GB_C, 0(PC_MEM_POINTER) 87 | addi PC_MEM_POINTER, PC_MEM_POINTER, 1 88 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 89 | j DECODE_NEXT 90 | addi GB_PC, GB_PC, 1 91 | GB_RRCA: 92 | jal GB_RRC_IMPL # call rotate 93 | addi Param0, GB_A, 0 # move register to call parameter 94 | addi GB_A, Param0, 0 # store paramter back 95 | j DECODE_NEXT 96 | clear_flags Z_FLAG 97 | -------------------------------------------------------------------------------- /asm/_cpu_inst_1.s: -------------------------------------------------------------------------------- 1 | 2 | #### 0x1X 3 | GB_STOP: 4 | # STOP will skip the next instruction 5 | addi GB_PC, GB_PC, 1 6 | jal CHECK_FOR_SPEED_SWITCH 7 | addi PC_MEM_POINTER, PC_MEM_POINTER, 1 8 | bnez $v0, DECODE_NEXT 9 | addi $at, $zero, STOP_REASON_STOP 10 | j GB_SIMULATE_HALTED # exit early 11 | sb $at, CPU_STATE_STOP_REASON(CPUState) 12 | GB_LD_DE_D16: 13 | lbu GB_E, 0(PC_MEM_POINTER) 14 | lbu GB_D, 1(PC_MEM_POINTER) 15 | addi PC_MEM_POINTER, PC_MEM_POINTER, 2 16 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 2 # update cycles run 17 | j DECODE_NEXT 18 | addi GB_PC, GB_PC, 2 19 | GB_LD_DE_A: 20 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 21 | add VAL, GB_A, 0 # write the value to store 22 | sll ADDR, GB_D, 8 # write upper address 23 | j GB_DO_WRITE # call store subroutine 24 | or ADDR, ADDR, GB_E # write lower address 25 | GB_INC_DE: 26 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 27 | addi GB_E, GB_E, 1 # incement the register 28 | srl $at, GB_E, 8 29 | andi GB_E, GB_E, 0xFF # keep at 8 bits 30 | add GB_D, GB_D, $at # add carry bit 31 | j DECODE_NEXT 32 | andi GB_D, GB_D, 0xFF # keep at 8 bits 33 | GB_INC_D: 34 | jal GB_INC # call increment 35 | addi Param0, GB_D, 0 # move register to call parameter 36 | j DECODE_NEXT 37 | addi GB_D, Param0, 0 # move register back from call parameter 38 | GB_DEC_D: 39 | jal GB_DEC # call decrement high bit 40 | addi Param0, GB_D, 0 # move register to call parameter 41 | j DECODE_NEXT 42 | addi GB_D, Param0, 0 # move register back from call parameter 43 | GB_LD_D_D8: 44 | lbu GB_D, 0(PC_MEM_POINTER) 45 | addi PC_MEM_POINTER, PC_MEM_POINTER, 1 46 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 47 | j DECODE_NEXT 48 | addi GB_PC, GB_PC, 1 49 | GB_RLA: 50 | jal GB_RL_IMPL # do RLC 51 | addi Param0, GB_A, 0 # store A into param 52 | addi GB_A, Param0, 0 # store result back into A 53 | j DECODE_NEXT 54 | clear_flags Z_FLAG 55 | GB_JR: 56 | lbu $v0, 0(PC_MEM_POINTER) 57 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 2 # update cycles run 58 | sll $v0, $v0, 24 # sign extend the bytes 59 | sra $v0, $v0, 24 60 | add Param0, GB_PC, $v0 61 | jal SET_GB_PC 62 | addi Param0, Param0, 1 # increment PC from reading byte 63 | j DECODE_NEXT 64 | GB_ADD_HL_DE: 65 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 66 | sll Param0, GB_D, 8 # load high order bits 67 | j _ADD_TO_HL 68 | or Param0, Param0, GB_E # load low order bits 69 | GB_LD_A_DE: 70 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 71 | sll ADDR, GB_D, 8 # load upper address 72 | jal GB_DO_READ # call read instruction 73 | or ADDR, ADDR, GB_E # load lower address 74 | j DECODE_NEXT 75 | addi GB_A, $v0, 0 # store result into a 76 | GB_DEC_DE: 77 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 78 | addi GB_E, GB_E, -1 # decrement E 79 | sra $at, GB_E, 8 # shift carry 80 | add GB_D, GB_D, $at # add carry to D 81 | andi GB_E, GB_E, 0xFF # mask E 82 | j DECODE_NEXT 83 | andi GB_D, GB_D, 0xFF # mask D 84 | GB_INC_E: 85 | jal GB_INC # call increment 86 | addi Param0, GB_E, 0 # move register to call parameter 87 | j DECODE_NEXT 88 | addi GB_E, Param0, 0 # move register back from call parameter 89 | GB_DEC_E: 90 | jal GB_DEC # call decrement high bit 91 | addi Param0, GB_E, 0 # move register to call parameter 92 | j DECODE_NEXT 93 | addi GB_E, Param0, 0 # move register back from call parameter 94 | GB_LD_E_D8: 95 | lbu GB_E, 0(PC_MEM_POINTER) 96 | addi PC_MEM_POINTER, PC_MEM_POINTER, 1 97 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 98 | j DECODE_NEXT 99 | addi GB_PC, GB_PC, 1 100 | GB_RRA: 101 | jal GB_RR_IMPL 102 | move Param0, GB_A 103 | move GB_A, Param0 104 | j DECODE_NEXT 105 | clear_flags Z_FLAG | N_FLAG | H_FLAG 106 | -------------------------------------------------------------------------------- /asm/_cpu_inst_2.s: -------------------------------------------------------------------------------- 1 | 2 | ### 0x2X 3 | GB_JR_NZ: 4 | andi $at, GB_F, Z_FLAG # check z flag 5 | bnez $at, _SKIP_JR # if Z_FLAG != 0 skip jump 6 | nop 7 | j GB_JR 8 | nop 9 | GB_LD_HL_D16: 10 | lbu GB_L, 0(PC_MEM_POINTER) 11 | lbu GB_H, 1(PC_MEM_POINTER) 12 | addi PC_MEM_POINTER, PC_MEM_POINTER, 2 13 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 2 # update cycles run 14 | j DECODE_NEXT 15 | addi GB_PC, GB_PC, 2 16 | GB_LDI_HL_A: 17 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 18 | add VAL, GB_A, 0 # write the value to store 19 | sll ADDR, GB_H, 8 # write upper address 20 | or ADDR, ADDR, GB_L # write lower address 21 | 22 | addi GB_L, ADDR, 1 # increment address 23 | srl GB_H, GB_L, 8 # store upper bits 24 | andi GB_L, GB_L, 0xFF # mask lower bits 25 | andi GB_H, GB_H, 0xFF # mask upper bits bits 26 | j GB_DO_WRITE # call store subroutine 27 | nop 28 | GB_INC_HL: 29 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 30 | addi GB_L, GB_L, 1 # incement the register 31 | srl $at, GB_L, 8 32 | andi GB_L, GB_L, 0xFF # keep at 8 bits 33 | add GB_H, GB_H, $at # add carry bit 34 | j DECODE_NEXT 35 | andi GB_H, GB_H, 0xFF # keep at 8 bits 36 | nop 37 | GB_INC_H: 38 | jal GB_INC # call increment 39 | addi Param0, GB_H, 0 # move register to call parameter 40 | j DECODE_NEXT 41 | addi GB_H, Param0, 0 # move register back from call parameter 42 | GB_DEC_H: 43 | jal GB_DEC # call decrement high bit 44 | addi Param0, GB_H, 0 # move register to call parameter 45 | j DECODE_NEXT 46 | addi GB_H, Param0, 0 # move register back from call parameter 47 | GB_LD_H_D8: 48 | lbu GB_H, 0(PC_MEM_POINTER) 49 | addi PC_MEM_POINTER, PC_MEM_POINTER, 1 50 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 51 | j DECODE_NEXT 52 | addi GB_PC, GB_PC, 1 53 | GB_DAA: 54 | j _GB_DAA 55 | nop 56 | GB_JR_Z: 57 | andi $at, GB_F, Z_FLAG # check z flag 58 | beqz $at, _SKIP_JR # skip jump if not zero 59 | nop 60 | j GB_JR 61 | nop 62 | GB_ADD_HL_HL: 63 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 64 | sll Param0, GB_H, 8 # load high order bits 65 | j _ADD_TO_HL 66 | or Param0, Param0, GB_L # load low order bits 67 | GB_LDI_A_HL: 68 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 69 | sll ADDR, GB_H, 8 # load upper address 70 | or ADDR, ADDR, GB_L # load lower address 71 | addi GB_L, ADDR, 1 # increment L 72 | jal GB_DO_READ # call read instruction 73 | srl GB_H, GB_L, 8 # store incremented H 74 | addi GB_A, $v0, 0 # store result into a 75 | andi GB_L, GB_L, 0xFF # mask lower bits 76 | j DECODE_NEXT 77 | andi GB_H, GB_H, 0xFF # mask upper bits bits 78 | GB_DEC_HL: 79 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 80 | addi GB_L, GB_L, -1 # decrement E 81 | sra $at, GB_L, 8 # shift carry 82 | add GB_H, GB_H, $at # add carry to D 83 | andi GB_L, GB_L, 0xFF # mask E 84 | j DECODE_NEXT 85 | andi GB_H, GB_H, 0xFF # mask D 86 | nop 87 | GB_INC_L: 88 | jal GB_INC # call increment 89 | addi Param0, GB_L, 0 # move register to call parameter 90 | j DECODE_NEXT 91 | addi GB_L, Param0, 0 # move register back from call parameter 92 | GB_DEC_L: 93 | jal GB_DEC # call decrement high bit 94 | addi Param0, GB_L, 0 # move register to call parameter 95 | j DECODE_NEXT 96 | addi GB_L, Param0, 0 # move register back from call parameter 97 | GB_LD_L_D8: 98 | lbu GB_L, 0(PC_MEM_POINTER) 99 | addi PC_MEM_POINTER, PC_MEM_POINTER, 1 100 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 101 | j DECODE_NEXT 102 | addi GB_PC, GB_PC, 1 103 | GB_CPL: 104 | xori GB_A, GB_A, 0xFF 105 | j DECODE_NEXT 106 | set_flags N_FLAG | H_FLAG 107 | -------------------------------------------------------------------------------- /asm/_cpu_inst_3.s: -------------------------------------------------------------------------------- 1 | 2 | ### 0x3X 3 | GB_JR_NC: 4 | andi $at, GB_F, C_FLAG # check z flag 5 | bnez $at, _SKIP_JR # skip jump if not zero 6 | nop 7 | j GB_JR 8 | nop 9 | GB_LD_SP_D16: 10 | lbu $at, 0(PC_MEM_POINTER) 11 | lbu GB_SP, 1(PC_MEM_POINTER) 12 | sll GB_SP, GB_SP, 8 13 | or GB_SP, GB_SP, $at 14 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 2 # update cycles run 15 | addi PC_MEM_POINTER, PC_MEM_POINTER, 2 16 | j DECODE_NEXT 17 | addi GB_PC, GB_PC, 2 18 | GB_LDD_HL_A: 19 | add VAL, GB_A, 0 # write the value to store 20 | sll ADDR, GB_H, 8 # write upper address 21 | or ADDR, ADDR, GB_L # write lower address 22 | 23 | addi GB_L, ADDR, -1 # decrement address 24 | srl GB_H, GB_L, 8 # store upper bits 25 | andi GB_L, GB_L, 0xFF # mask lower bits 26 | andi GB_H, GB_H, 0xFF # mask upper bits bits 27 | j GB_DO_WRITE # call store subroutine 28 | # intentially leave off nop to overflow to next instruction 29 | GB_INC_SP: 30 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 31 | addi GB_SP, GB_SP, 1 32 | j DECODE_NEXT 33 | andi GB_SP, GB_SP, 0xFFFF 34 | GB_INC_HL_ADDR: 35 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 2 # update cycles run 36 | sll ADDR, GB_H, 8 # write upper address 37 | jal GB_DO_READ 38 | or ADDR, ADDR, GB_L # write lower address 39 | jal GB_INC # call increment 40 | move Param0, $v0 # move loaded value to call parameter 41 | j GB_DO_WRITE 42 | move VAL, Param0 43 | GB_DEC_HL_ADDR: 44 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 2 # update cycles run 45 | sll ADDR, GB_H, 8 # write upper address 46 | jal GB_DO_READ 47 | or ADDR, ADDR, GB_L # write lower address 48 | jal GB_DEC # call decrement 49 | move Param0, $v0 # move loaded value to call parameter 50 | j GB_DO_WRITE 51 | move VAL, Param0 52 | GB_LD_HL_ADDR_D8: 53 | jal READ_NEXT_INSTRUCTION # read immediate value 54 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 2 # update cycles run 55 | add VAL, $v0, 0 # write the value to store 56 | sll ADDR, GB_H, 8 # write upper address 57 | j GB_DO_WRITE 58 | or ADDR, ADDR, GB_L # write lower address 59 | GB_SCF: 60 | clear_flags N_FLAG | H_FLAG 61 | j DECODE_NEXT 62 | set_flags C_FLAG 63 | GB_JR_C: 64 | andi $at, GB_F, C_FLAG # check z flag 65 | beqz $at, _SKIP_JR # skip jump if not zero 66 | nop 67 | j GB_JR 68 | nop 69 | GB_ADD_HL_SP: 70 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 71 | j _ADD_TO_HL 72 | move Param0, GB_SP 73 | GB_LDD_A_HL: 74 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 75 | sll ADDR, GB_H, 8 # load upper address 76 | or ADDR, ADDR, GB_L # load lower address 77 | addi GB_L, ADDR, -1 # decrement L 78 | jal GB_DO_READ # call read instruction 79 | srl GB_H, GB_L, 8 # store incremented H 80 | addi GB_A, $v0, 0 # store result into a 81 | andi GB_L, GB_L, 0xFF # mask lower bits 82 | j DECODE_NEXT 83 | andi GB_H, GB_H, 0xFF # mask upper bits bits 84 | GB_DEC_SP: 85 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 86 | addi GB_SP, GB_SP, -1 87 | j DECODE_NEXT 88 | andi GB_SP, GB_SP, 0xFFFF 89 | GB_INC_A: 90 | jal GB_INC # call increment 91 | addi Param0, GB_A, 0 # move register to call parameter 92 | j DECODE_NEXT 93 | addi GB_A, Param0, 0 # move register back from call parameter 94 | GB_DEC_A: 95 | jal GB_DEC # call decrement high bit 96 | addi Param0, GB_A, 0 # move register to call parameter 97 | j DECODE_NEXT 98 | addi GB_A, Param0, 0 # move register back from call parameter 99 | GB_LD_A_D8: 100 | lbu GB_A, 0(PC_MEM_POINTER) 101 | addi PC_MEM_POINTER, PC_MEM_POINTER, 1 102 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 103 | j DECODE_NEXT 104 | addi GB_PC, GB_PC, 1 105 | GB_CCF: 106 | clear_flags N_FLAG | H_FLAG 107 | j DECODE_NEXT 108 | xori GB_F, GB_F, C_FLAG 109 | -------------------------------------------------------------------------------- /asm/_cpu_inst_4.s: -------------------------------------------------------------------------------- 1 | 2 | ### 0x4X 3 | GB_LD_B_B: 4 | j DECODE_NEXT 5 | nop 6 | GB_LD_B_C: 7 | j DECODE_NEXT 8 | addi GB_B, GB_C, 0 9 | GB_LD_B_D: 10 | j DECODE_NEXT 11 | addi GB_B, GB_D, 0 12 | GB_LD_B_E: 13 | j DECODE_NEXT 14 | addi GB_B, GB_E, 0 15 | GB_LD_B_H: 16 | j DECODE_NEXT 17 | addi GB_B, GB_H, 0 18 | GB_LD_B_L: 19 | j DECODE_NEXT 20 | addi GB_B, GB_L, 0 21 | GB_LD_B_HL: 22 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 23 | sll ADDR, GB_H, 8 # load upper address 24 | jal GB_DO_READ # call read instruction 25 | or ADDR, ADDR, GB_L # load lower address 26 | j DECODE_NEXT 27 | addi GB_B, $v0, 0 28 | GB_LD_B_A: 29 | j DECODE_NEXT 30 | addi GB_B, GB_A, 0 31 | GB_LD_C_B: 32 | j DECODE_NEXT 33 | addi GB_C, GB_B, 0 34 | GB_LD_C_C: 35 | j DECODE_NEXT 36 | nop 37 | GB_LD_C_D: 38 | j DECODE_NEXT 39 | addi GB_C, GB_D, 0 40 | GB_LD_C_E: 41 | j DECODE_NEXT 42 | addi GB_C, GB_E, 0 43 | GB_LD_C_H: 44 | j DECODE_NEXT 45 | addi GB_C, GB_H, 0 46 | GB_LD_C_L: 47 | j DECODE_NEXT 48 | addi GB_C, GB_L, 0 49 | GB_LD_C_HL: 50 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 51 | sll ADDR, GB_H, 8 # load upper address 52 | jal GB_DO_READ # call read instruction 53 | or ADDR, ADDR, GB_L # load lower address 54 | j DECODE_NEXT 55 | addi GB_C, $v0, 0 56 | GB_LD_C_A: 57 | j DECODE_NEXT 58 | addi GB_C, GB_A, 0 59 | -------------------------------------------------------------------------------- /asm/_cpu_inst_5.s: -------------------------------------------------------------------------------- 1 | 2 | ### 0x5X 3 | GB_LD_D_B: 4 | j DECODE_NEXT 5 | addi GB_D, GB_B, 0 6 | GB_LD_D_C: 7 | j DECODE_NEXT 8 | addi GB_D, GB_C, 0 9 | GB_LD_D_D: 10 | j DECODE_NEXT 11 | addi GB_D, GB_D, 0 12 | GB_LD_D_E: 13 | j DECODE_NEXT 14 | addi GB_D, GB_E, 0 15 | GB_LD_D_H: 16 | j DECODE_NEXT 17 | addi GB_D, GB_H, 0 18 | GB_LD_D_L: 19 | j DECODE_NEXT 20 | addi GB_D, GB_L, 0 21 | GB_LD_D_HL: 22 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 23 | sll ADDR, GB_H, 8 # load upper address 24 | jal GB_DO_READ # call read instruction 25 | or ADDR, ADDR, GB_L # load lower address 26 | j DECODE_NEXT 27 | addi GB_D, $v0, 0 28 | GB_LD_D_A: 29 | j DECODE_NEXT 30 | addi GB_D, GB_A, 0 31 | GB_LD_E_B: 32 | j DECODE_NEXT 33 | addi GB_E, GB_B, 0 34 | GB_LD_E_C: 35 | j DECODE_NEXT 36 | addi GB_E, GB_C, 0 37 | GB_LD_E_D: 38 | j DECODE_NEXT 39 | addi GB_E, GB_D, 0 40 | GB_LD_E_E: 41 | j DECODE_NEXT 42 | addi GB_E, GB_E, 0 43 | GB_LD_E_H: 44 | j DECODE_NEXT 45 | addi GB_E, GB_H, 0 46 | GB_LD_E_L: 47 | j DECODE_NEXT 48 | addi GB_E, GB_L, 0 49 | GB_LD_E_HL: 50 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 51 | sll ADDR, GB_H, 8 # load upper address 52 | jal GB_DO_READ # call read instruction 53 | or ADDR, ADDR, GB_L # load lower address 54 | j DECODE_NEXT 55 | addi GB_E, $v0, 0 56 | GB_LD_E_A: 57 | j DECODE_NEXT 58 | addi GB_E, GB_A, 0 59 | -------------------------------------------------------------------------------- /asm/_cpu_inst_6.s: -------------------------------------------------------------------------------- 1 | 2 | ### 0x6X 3 | GB_LD_H_B: 4 | j DECODE_NEXT 5 | addi GB_H, GB_B, 0 6 | GB_LD_H_C: 7 | j DECODE_NEXT 8 | addi GB_H, GB_C, 0 9 | GB_LD_H_D: 10 | j DECODE_NEXT 11 | addi GB_H, GB_D, 0 12 | GB_LD_H_E: 13 | j DECODE_NEXT 14 | addi GB_H, GB_E, 0 15 | GB_LD_H_H: 16 | j DECODE_NEXT 17 | addi GB_H, GB_H, 0 18 | GB_LD_H_L: 19 | j DECODE_NEXT 20 | addi GB_H, GB_L, 0 21 | GB_LD_H_HL: 22 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 23 | sll ADDR, GB_H, 8 # load upper address 24 | jal GB_DO_READ # call read instruction 25 | or ADDR, ADDR, GB_L # load lower address 26 | j DECODE_NEXT 27 | addi GB_H, $v0, 0 28 | GB_LD_H_A: 29 | j DECODE_NEXT 30 | addi GB_H, GB_A, 0 31 | GB_LD_L_B: 32 | j DECODE_NEXT 33 | addi GB_L, GB_B, 0 34 | GB_LD_L_C: 35 | j DECODE_NEXT 36 | addi GB_L, GB_C, 0 37 | GB_LD_L_D: 38 | j DECODE_NEXT 39 | addi GB_L, GB_D, 0 40 | GB_LD_L_E: 41 | j DECODE_NEXT 42 | addi GB_L, GB_E, 0 43 | GB_LD_L_H: 44 | j DECODE_NEXT 45 | addi GB_L, GB_H, 0 46 | GB_LD_L_L: 47 | j DECODE_NEXT 48 | addi GB_L, GB_L, 0 49 | GB_LD_L_HL: 50 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 51 | sll ADDR, GB_H, 8 # load upper address 52 | jal GB_DO_READ # call read instruction 53 | or ADDR, ADDR, GB_L # load lower address 54 | j DECODE_NEXT 55 | addi GB_L, $v0, 0 56 | GB_LD_L_A: 57 | j DECODE_NEXT 58 | addi GB_L, GB_A, 0 59 | -------------------------------------------------------------------------------- /asm/_cpu_inst_7.s: -------------------------------------------------------------------------------- 1 | 2 | ### 0x7X 3 | GB_LD_HL_B: 4 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 5 | sll ADDR, GB_H, 8 # load upper address 6 | or ADDR, ADDR, GB_L # load lower address 7 | j GB_DO_WRITE # call read instruction 8 | addi VAL, GB_B, 0 9 | GB_LD_HL_C: 10 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 11 | sll ADDR, GB_H, 8 # load upper address 12 | or ADDR, ADDR, GB_L # load lower address 13 | j GB_DO_WRITE # call read instruction 14 | addi VAL, GB_C, 0 15 | GB_LD_HL_D: 16 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 17 | sll ADDR, GB_H, 8 # load upper address 18 | or ADDR, ADDR, GB_L # load lower address 19 | j GB_DO_WRITE # call read instruction 20 | addi VAL, GB_D, 0 21 | GB_LD_HL_E: 22 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 23 | sll ADDR, GB_H, 8 # load upper address 24 | or ADDR, ADDR, GB_L # load lower address 25 | j GB_DO_WRITE # call read instruction 26 | addi VAL, GB_E, 0 27 | GB_LD_HL_H: 28 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 29 | sll ADDR, GB_H, 8 # load upper address 30 | or ADDR, ADDR, GB_L # load lower address 31 | j GB_DO_WRITE # call read instruction 32 | addi VAL, GB_H, 0 33 | GB_LD_HL_L: 34 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 35 | sll ADDR, GB_H, 8 # load upper address 36 | or ADDR, ADDR, GB_L # load lower address 37 | j GB_DO_WRITE # call read instruction 38 | addi VAL, GB_L, 0 39 | GB_HALT: 40 | li $at, STOP_REASON_HALT 41 | jal CHECK_FOR_HALT_BUG 42 | sb $at, CPU_STATE_STOP_REASON(CPUState) 43 | nop 44 | j GB_SIMULATE_HALTED 45 | nop 46 | GB_LD_HL_A: 47 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 48 | sll ADDR, GB_H, 8 # load upper address 49 | or ADDR, ADDR, GB_L # load lower address 50 | j GB_DO_WRITE # call read instruction 51 | addi VAL, GB_A, 0 52 | GB_LD_A_B: 53 | j DECODE_NEXT 54 | addi GB_A, GB_B, 0 55 | GB_LD_A_C: 56 | j DECODE_NEXT 57 | addi GB_A, GB_C, 0 58 | GB_LD_A_D: 59 | j DECODE_NEXT 60 | addi GB_A, GB_D, 0 61 | GB_LD_A_E: 62 | j DECODE_NEXT 63 | addi GB_A, GB_E, 0 64 | GB_LD_A_H: 65 | j DECODE_NEXT 66 | addi GB_A, GB_H, 0 67 | GB_LD_A_L: 68 | j DECODE_NEXT 69 | addi GB_A, GB_L, 0 70 | GB_LD_A_HL: 71 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 72 | sll ADDR, GB_H, 8 # load upper address 73 | jal GB_DO_READ # call read instruction 74 | or ADDR, ADDR, GB_L # load lower address 75 | j DECODE_NEXT 76 | addi GB_A, $v0, 0 77 | GB_LD_A_A: 78 | j DECODE_NEXT 79 | addi GB_A, GB_A, 0 80 | -------------------------------------------------------------------------------- /asm/_cpu_inst_8.s: -------------------------------------------------------------------------------- 1 | 2 | ### 0x8X 3 | GB_ADD_A_B: 4 | j _ADD_TO_A 5 | addi Param0, GB_B, 0 6 | GB_ADD_A_C: 7 | j _ADD_TO_A 8 | addi Param0, GB_C, 0 9 | GB_ADD_A_D: 10 | j _ADD_TO_A 11 | addi Param0, GB_D, 0 12 | GB_ADD_A_E: 13 | j _ADD_TO_A 14 | addi Param0, GB_E, 0 15 | GB_ADD_A_H: 16 | j _ADD_TO_A 17 | addi Param0, GB_H, 0 18 | GB_ADD_A_L: 19 | j _ADD_TO_A 20 | addi Param0, GB_L, 0 21 | GB_ADD_A_HL: 22 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 23 | sll ADDR, GB_H, 8 # load upper address 24 | jal GB_DO_READ # call read instruction 25 | or ADDR, ADDR, GB_L # load lower address 26 | j _ADD_TO_A 27 | addi Param0, $v0, 0 28 | GB_ADD_A_A: 29 | j _ADD_TO_A 30 | addi Param0, GB_A, 0 31 | GB_ADC_A_B: 32 | j _ADC_TO_A 33 | addi Param0, GB_B, 0 34 | GB_ADC_A_C: 35 | j _ADC_TO_A 36 | addi Param0, GB_C, 0 37 | GB_ADC_A_D: 38 | j _ADC_TO_A 39 | addi Param0, GB_D, 0 40 | GB_ADC_A_E: 41 | j _ADC_TO_A 42 | addi Param0, GB_E, 0 43 | GB_ADC_A_H: 44 | j _ADC_TO_A 45 | addi Param0, GB_H, 0 46 | GB_ADC_A_L: 47 | j _ADC_TO_A 48 | addi Param0, GB_L, 0 49 | GB_ADC_A_HL: 50 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 51 | sll ADDR, GB_H, 8 # load upper address 52 | jal GB_DO_READ # call read instruction 53 | or ADDR, ADDR, GB_L # load lower address 54 | j _ADC_TO_A 55 | addi Param0, $v0, 0 56 | GB_ADC_A_A: 57 | j _ADC_TO_A 58 | addi Param0, GB_A, 0 59 | -------------------------------------------------------------------------------- /asm/_cpu_inst_9.s: -------------------------------------------------------------------------------- 1 | 2 | ### 0x9X 3 | GB_SUB_B: 4 | j _SUB_FROM_A 5 | addi Param0, GB_B, 0 6 | GB_SUB_C: 7 | j _SUB_FROM_A 8 | addi Param0, GB_C, 0 9 | GB_SUB_D: 10 | j _SUB_FROM_A 11 | addi Param0, GB_D, 0 12 | GB_SUB_E: 13 | j _SUB_FROM_A 14 | addi Param0, GB_E, 0 15 | GB_SUB_H: 16 | j _SUB_FROM_A 17 | addi Param0, GB_H, 0 18 | GB_SUB_L: 19 | j _SUB_FROM_A 20 | addi Param0, GB_L, 0 21 | GB_SUB_HL: 22 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 23 | sll ADDR, GB_H, 8 # load upper address 24 | jal GB_DO_READ # call read instruction 25 | or ADDR, ADDR, GB_L # load lower address 26 | j _SUB_FROM_A 27 | addi Param0, $v0, 0 28 | GB_SUB_A: 29 | addi GB_A, $zero, 0 30 | j DECODE_NEXT 31 | addi GB_F, $zero, Z_FLAG | N_FLAG 32 | GB_SBC_B: 33 | j _SBC_FROM_A 34 | addi Param0, GB_B, 0 35 | GB_SBC_C: 36 | j _SBC_FROM_A 37 | addi Param0, GB_C, 0 38 | GB_SBC_D: 39 | j _SBC_FROM_A 40 | addi Param0, GB_D, 0 41 | GB_SBC_E: 42 | j _SBC_FROM_A 43 | addi Param0, GB_E, 0 44 | GB_SBC_H: 45 | j _SBC_FROM_A 46 | addi Param0, GB_H, 0 47 | GB_SBC_L: 48 | j _SBC_FROM_A 49 | addi Param0, GB_L, 0 50 | GB_SBC_HL: 51 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 52 | sll ADDR, GB_H, 8 # load upper address 53 | jal GB_DO_READ # call read instruction 54 | or ADDR, ADDR, GB_L # load lower address 55 | j _SBC_FROM_A 56 | addi Param0, $v0, 0 57 | GB_SBC_A: 58 | j _SBC_FROM_A 59 | addi Param0, GB_A, 0 60 | -------------------------------------------------------------------------------- /asm/_cpu_inst_A.s: -------------------------------------------------------------------------------- 1 | 2 | ### 0xAX 3 | GB_AND_B: 4 | and GB_A, GB_A, GB_B 5 | bne GB_A, $zero, DECODE_NEXT 6 | li GB_F, H_FLAG 7 | j DECODE_NEXT 8 | set_flags Z_FLAG 9 | GB_AND_C: 10 | and GB_A, GB_A, GB_C 11 | bne GB_A, $zero, DECODE_NEXT 12 | li GB_F, H_FLAG 13 | j DECODE_NEXT 14 | set_flags Z_FLAG 15 | GB_AND_D: 16 | and GB_A, GB_A, GB_D 17 | bne GB_A, $zero, DECODE_NEXT 18 | li GB_F, H_FLAG 19 | j DECODE_NEXT 20 | set_flags Z_FLAG 21 | GB_AND_E: 22 | and GB_A, GB_A, GB_E 23 | bne GB_A, $zero, DECODE_NEXT 24 | li GB_F, H_FLAG 25 | j DECODE_NEXT 26 | set_flags Z_FLAG 27 | GB_AND_H: 28 | and GB_A, GB_A, GB_H 29 | bne GB_A, $zero, DECODE_NEXT 30 | li GB_F, H_FLAG 31 | j DECODE_NEXT 32 | set_flags Z_FLAG 33 | GB_AND_L: 34 | and GB_A, GB_A, GB_L 35 | bne GB_A, $zero, DECODE_NEXT 36 | li GB_F, H_FLAG 37 | j DECODE_NEXT 38 | set_flags Z_FLAG 39 | GB_AND_HL: 40 | jal READ_HL 41 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 42 | and GB_A, GB_A, $v0 43 | bne GB_A, $zero, DECODE_NEXT 44 | li GB_F, H_FLAG 45 | j DECODE_NEXT 46 | set_flags Z_FLAG 47 | GB_AND_A: 48 | bne GB_A, $zero, DECODE_NEXT 49 | li GB_F, H_FLAG 50 | j DECODE_NEXT 51 | set_flags Z_FLAG 52 | GB_XOR_B: 53 | xor GB_A, GB_A, GB_B 54 | bne GB_A, $zero, DECODE_NEXT 55 | li GB_F, 0 56 | j DECODE_NEXT 57 | set_flags Z_FLAG 58 | GB_XOR_C: 59 | xor GB_A, GB_A, GB_C 60 | bne GB_A, $zero, DECODE_NEXT 61 | li GB_F, 0 62 | j DECODE_NEXT 63 | set_flags Z_FLAG 64 | GB_XOR_D: 65 | xor GB_A, GB_A, GB_D 66 | bne GB_A, $zero, DECODE_NEXT 67 | li GB_F, 0 68 | j DECODE_NEXT 69 | set_flags Z_FLAG 70 | GB_XOR_E: 71 | xor GB_A, GB_A, GB_E 72 | bne GB_A, $zero, DECODE_NEXT 73 | li GB_F, 0 74 | j DECODE_NEXT 75 | set_flags Z_FLAG 76 | GB_XOR_H: 77 | xor GB_A, GB_A, GB_H 78 | bne GB_A, $zero, DECODE_NEXT 79 | li GB_F, 0 80 | j DECODE_NEXT 81 | set_flags Z_FLAG 82 | GB_XOR_L: 83 | xor GB_A, GB_A, GB_L 84 | bne GB_A, $zero, DECODE_NEXT 85 | li GB_F, 0 86 | j DECODE_NEXT 87 | set_flags Z_FLAG 88 | GB_XOR_HL: 89 | jal READ_HL 90 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 91 | xor GB_A, GB_A, $v0 92 | bne GB_A, $zero, DECODE_NEXT 93 | li GB_F, 0 94 | j DECODE_NEXT 95 | set_flags Z_FLAG 96 | GB_XOR_A: 97 | addi GB_A, $zero, 0 98 | j DECODE_NEXT 99 | li GB_F, Z_FLAG 100 | -------------------------------------------------------------------------------- /asm/_cpu_inst_B.s: -------------------------------------------------------------------------------- 1 | 2 | # 0xBX 3 | GB_OR_B: 4 | or GB_A, GB_A, GB_B 5 | bnez GB_A, DECODE_NEXT 6 | li GB_F, 0 7 | j DECODE_NEXT 8 | set_flags Z_FLAG 9 | GB_OR_C: 10 | or GB_A, GB_A, GB_C 11 | bnez GB_A, DECODE_NEXT 12 | li GB_F, 0 13 | j DECODE_NEXT 14 | set_flags Z_FLAG 15 | GB_OR_D: 16 | or GB_A, GB_A, GB_D 17 | bnez GB_A, DECODE_NEXT 18 | li GB_F, 0 19 | j DECODE_NEXT 20 | set_flags Z_FLAG 21 | GB_OR_E: 22 | or GB_A, GB_A, GB_E 23 | bnez GB_A, DECODE_NEXT 24 | li GB_F, 0 25 | j DECODE_NEXT 26 | set_flags Z_FLAG 27 | GB_OR_H: 28 | or GB_A, GB_A, GB_H 29 | bnez GB_A, DECODE_NEXT 30 | li GB_F, 0 31 | j DECODE_NEXT 32 | set_flags Z_FLAG 33 | GB_OR_L: 34 | or GB_A, GB_A, GB_L 35 | bnez GB_A, DECODE_NEXT 36 | li GB_F, 0 37 | j DECODE_NEXT 38 | set_flags Z_FLAG 39 | GB_OR_HL: 40 | jal READ_HL 41 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 42 | or GB_A, GB_A, $v0 43 | bnez GB_A, DECODE_NEXT 44 | li GB_F, 0 45 | j DECODE_NEXT 46 | set_flags Z_FLAG 47 | GB_OR_A: 48 | bnez GB_A, DECODE_NEXT 49 | li GB_F, 0 50 | j DECODE_NEXT 51 | set_flags Z_FLAG 52 | GB_CP_B: 53 | j _CP_A 54 | addi Param0, GB_B, 0 55 | GB_CP_C: 56 | j _CP_A 57 | addi Param0, GB_C, 0 58 | GB_CP_D: 59 | j _CP_A 60 | addi Param0, GB_D, 0 61 | GB_CP_E: 62 | j _CP_A 63 | addi Param0, GB_E, 0 64 | GB_CP_H: 65 | j _CP_A 66 | addi Param0, GB_H, 0 67 | GB_CP_L: 68 | j _CP_A 69 | addi Param0, GB_L, 0 70 | GB_CP_HL: 71 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 72 | sll ADDR, GB_H, 8 # load upper address 73 | jal GB_DO_READ # call read instruction 74 | or ADDR, ADDR, GB_L # load lower address 75 | j _CP_A 76 | addi Param0, $v0, 0 77 | GB_CP_A: 78 | j DECODE_NEXT 79 | addi GB_F, $zero, Z_FLAG | N_FLAG 80 | _SKIP_JP: 81 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 2 # update cycles run 82 | addi Param0, GB_PC, 2 83 | jal SET_GB_PC 84 | andi Param0, Param0, 0xFFFF 85 | j DECODE_NEXT 86 | nop 87 | -------------------------------------------------------------------------------- /asm/_cpu_inst_C.s: -------------------------------------------------------------------------------- 1 | 2 | ### 0xCX 3 | GB_RET_NZ: 4 | andi $at, GB_F, Z_FLAG 5 | bne $at, $zero, DECODE_NEXT # if Z_FLAG != 0 skip return 6 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 7 | j _GB_RET 8 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 3 # update cycles run 9 | GB_POP_BC: 10 | addi ADDR, GB_SP, 0 11 | jal GB_DO_READ_16 12 | addi GB_SP, GB_SP, 2 13 | andi GB_SP, GB_SP, 0xFFFF 14 | srl GB_B, $v0, 8 # store B 15 | andi GB_C, $v0, 0xFF # store C 16 | j DECODE_NEXT 17 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 2 # update cycles run 18 | GB_JP_NZ: 19 | andi $at, GB_F, Z_FLAG 20 | bne $at, $zero, _SKIP_JP # if Z_FLAG != 0 skip jump 21 | nop 22 | jal GB_JP 23 | nop 24 | GB_JP: 25 | lbu $at, 0(PC_MEM_POINTER) 26 | lbu Param0, 1(PC_MEM_POINTER) 27 | sll Param0, Param0, 8 28 | jal SET_GB_PC 29 | or Param0, Param0, $at 30 | j DECODE_NEXT 31 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 3 # update cycles run 32 | GB_CALL_NZ: 33 | andi $at, GB_F, Z_FLAG 34 | bne $at, $zero, _SKIP_JP # if Z_FLAG != 0 skip the call 35 | nop 36 | j _GB_CALL 37 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 5 # update cycles run 38 | GB_PUSH_BC: 39 | addi GB_SP, GB_SP, -2 40 | andi GB_SP, GB_SP, 0xFFFF 41 | addi ADDR, GB_SP, 0 42 | sll VAL, GB_B, 8 43 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 3 # update cycles run 44 | j GB_DO_WRITE_16 45 | or VAL, VAL, GB_C 46 | GB_ADD_A_d8: 47 | jal READ_NEXT_INSTRUCTION 48 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 49 | j _ADD_TO_A 50 | addi Param0, $v0, 0 51 | GB_RST_00H: 52 | addi GB_SP, GB_SP, -2 53 | andi ADDR, GB_SP, 0xFFFF 54 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 3 # update cycles run 55 | addi VAL, GB_PC, 0 56 | jal SET_GB_PC 57 | addi Param0, $zero, 0x0000 58 | j GB_DO_WRITE_16 59 | move GB_SP, ADDR 60 | GB_RET_Z: 61 | andi $at, GB_F, Z_FLAG 62 | beq $at, $zero, DECODE_NEXT # if Z_FLAG == 0 skip RET 63 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 64 | j _GB_RET 65 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 3 # update cycles run 66 | GB_RET: 67 | j _GB_RET 68 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 3 # update cycles run 69 | GB_JP_Z: 70 | andi $at, GB_F, Z_FLAG 71 | beq $at, $zero, _SKIP_JP # if Z_FLAG == 0 skip jump 72 | nop 73 | jal GB_JP 74 | nop 75 | GB_PREFIX_CB: 76 | j _GB_PREFIX_CB 77 | nop 78 | GB_CALL_Z: 79 | andi $at, GB_F, Z_FLAG 80 | beq $at, $zero, _SKIP_JP # if Z_FLAG == 0 skip call 81 | nop 82 | j _GB_CALL 83 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 5 # update cycles run 84 | GB_CALL: 85 | j _GB_CALL 86 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 5 # update cycles run 87 | GB_ADC_A_d8: 88 | jal READ_NEXT_INSTRUCTION 89 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 90 | j _ADC_TO_A 91 | addi Param0, $v0, 0 92 | GB_RST_08H: 93 | addi GB_SP, GB_SP, -2 94 | andi ADDR, GB_SP, 0xFFFF 95 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 3 # update cycles run 96 | addi VAL, GB_PC, 0 97 | jal SET_GB_PC 98 | addi Param0, $zero, 0x0008 99 | j GB_DO_WRITE_16 100 | move GB_SP, ADDR 101 | -------------------------------------------------------------------------------- /asm/_cpu_inst_D.s: -------------------------------------------------------------------------------- 1 | 2 | ### 0XDX 3 | GB_RET_NC: 4 | andi $at, GB_F, C_FLAG 5 | bne $at, $zero, DECODE_NEXT # if C_FLAG != 0 skip return 6 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 7 | j _GB_RET 8 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 3 # update cycles run 9 | GB_POP_DE: 10 | addi ADDR, GB_SP, 0 11 | jal GB_DO_READ_16 12 | addi GB_SP, GB_SP, 2 13 | andi GB_SP, GB_SP, 0xFFFF 14 | srl GB_D, $v0, 8 # store B 15 | andi GB_E, $v0, 0xFF # store C 16 | j DECODE_NEXT 17 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 2 # update cycles run 18 | GB_JP_NC: 19 | andi $at, GB_F, C_FLAG 20 | bne $at, $zero, _SKIP_JP # if Z_FLAG != 0 skip jump 21 | nop 22 | jal GB_JP 23 | nop 24 | GB_ERROR_0: 25 | j OPEN_DEBUGGER # exit early 26 | li TMP2, 0 27 | GB_CALL_NC: 28 | andi $at, GB_F, C_FLAG 29 | bne $at, $zero, _SKIP_JP # if Z_FLAG != 0 skip the call 30 | nop 31 | j _GB_CALL 32 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 5 # update cycles run 33 | GB_PUSH_DE: 34 | addi GB_SP, GB_SP, -2 35 | andi GB_SP, GB_SP, 0xFFFF 36 | addi ADDR, GB_SP, 0 37 | sll VAL, GB_D, 8 38 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 3 # update cycles run 39 | j GB_DO_WRITE_16 40 | or VAL, VAL, GB_E 41 | GB_SUB_A_d8: 42 | jal READ_NEXT_INSTRUCTION 43 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 44 | j _SUB_FROM_A 45 | addi Param0, $v0, 0 46 | GB_RST_10H: 47 | addi GB_SP, GB_SP, -2 48 | andi ADDR, GB_SP, 0xFFFF 49 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 3 # update cycles run 50 | addi VAL, GB_PC, 0 51 | jal SET_GB_PC 52 | addi Param0, $zero, 0x0010 53 | j GB_DO_WRITE_16 54 | move GB_SP, ADDR 55 | GB_RET_C: 56 | andi $at, GB_F, C_FLAG 57 | beq $at, $zero, DECODE_NEXT # if Z_FLAG == 0 skip RET 58 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 59 | j _GB_RET 60 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 3 # update cycles run 61 | GB_RETI: 62 | li $at, INTERRUPTS_ENABLED 63 | jal CHECK_FOR_INTERRUPT 64 | sb $at, CPU_STATE_INTERRUPTS(CPUState) 65 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 3 # update cycles run 66 | j _GB_RET 67 | nop 68 | GB_JP_C: 69 | andi $at, GB_F, C_FLAG 70 | beq $at, $zero, _SKIP_JP # if Z_FLAG == 0 skip jump 71 | nop 72 | jal GB_JP 73 | nop 74 | GB_ERROR_1: 75 | addi $at, $zero, STOP_REASON_ERROR 76 | j GB_BREAK_LOOP # exit early 77 | sb $at, CPU_STATE_STOP_REASON(CPUState) 78 | GB_CALL_C: 79 | andi $at, GB_F, C_FLAG 80 | beq $at, $zero, _SKIP_JP # if Z_FLAG == 0 skip call 81 | nop 82 | j _GB_CALL 83 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 5 # update cycles run 84 | GB_ERROR_2: 85 | addi $at, $zero, STOP_REASON_ERROR 86 | j GB_BREAK_LOOP # exit early 87 | sb $at, CPU_STATE_STOP_REASON(CPUState) 88 | GB_SBC_A_d8: 89 | jal READ_NEXT_INSTRUCTION 90 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 91 | j _SBC_FROM_A 92 | addi Param0, $v0, 0 93 | GB_RST_18H: 94 | addi GB_SP, GB_SP, -2 95 | andi ADDR, GB_SP, 0xFFFF 96 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 3 # update cycles run 97 | addi VAL, GB_PC, 0 98 | jal SET_GB_PC 99 | addi Param0, $zero, 0x0018 100 | j GB_DO_WRITE_16 101 | move GB_SP, ADDR 102 | -------------------------------------------------------------------------------- /asm/_cpu_inst_E.s: -------------------------------------------------------------------------------- 1 | 2 | ### 0xEX 3 | GB_LDH_a8_A: 4 | jal READ_NEXT_INSTRUCTION 5 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 2 # update cycles run 6 | ori ADDR, $v0, 0xFF00 7 | j GB_DO_WRITE_REGISTERS 8 | move VAL, GB_A 9 | GB_POP_HL: 10 | addi ADDR, GB_SP, 0 11 | jal GB_DO_READ_16 12 | addi GB_SP, GB_SP, 2 13 | andi GB_SP, GB_SP, 0xFFFF 14 | srl GB_H, $v0, 8 # store B 15 | andi GB_L, $v0, 0xFF # store C 16 | j DECODE_NEXT 17 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 2 # update cycles run 18 | GB_LDH_C_A: 19 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 20 | addi ADDR, GB_C, 0 21 | ori ADDR, ADDR, 0xFF00 22 | j GB_DO_WRITE_REGISTERS 23 | addi VAL, GB_A, 0 24 | GB_ERROR_3: 25 | addi $at, $zero, STOP_REASON_ERROR 26 | j GB_BREAK_LOOP # exit early 27 | sb $at, CPU_STATE_STOP_REASON(CPUState) 28 | GB_ERROR_4: 29 | addi $at, $zero, STOP_REASON_ERROR 30 | j GB_BREAK_LOOP # exit early 31 | sb $at, CPU_STATE_STOP_REASON(CPUState) 32 | GB_PUSH_HL: 33 | addi GB_SP, GB_SP, -2 34 | andi GB_SP, GB_SP, 0xFFFF 35 | addi ADDR, GB_SP, 0 36 | sll VAL, GB_H, 8 37 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 3 # update cycles run 38 | j GB_DO_WRITE_16 39 | or VAL, VAL, GB_L 40 | GB_AND_A_d8: 41 | jal READ_NEXT_INSTRUCTION 42 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 43 | and GB_A, GB_A, $v0 44 | bne GB_A, $zero, DECODE_NEXT 45 | ori GB_F, $zero, H_FLAG 46 | j DECODE_NEXT 47 | set_flags Z_FLAG 48 | GB_RST_20H: 49 | addi GB_SP, GB_SP, -2 50 | andi ADDR, GB_SP, 0xFFFF 51 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 3 # update cycles run 52 | addi VAL, GB_PC, 0 53 | jal SET_GB_PC 54 | addi Param0, $zero, 0x0020 55 | j GB_DO_WRITE_16 56 | move GB_SP, ADDR 57 | GB_ADD_SP_d8: 58 | jal READ_NEXT_INSTRUCTION 59 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 3 # update cycles run 60 | sll $v0, $v0, 24 #sign extend 61 | j _ADD_TO_SP 62 | sra Param0, $v0, 24 63 | GB_JP_HL: 64 | sll Param0, GB_H, 8 65 | jal SET_GB_PC 66 | or Param0, Param0, GB_L 67 | j DECODE_NEXT 68 | nop 69 | GB_LD_a16_A: 70 | jal READ_NEXT_INSTRUCTION_16 71 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 3 # update cycles run 72 | addi ADDR, $v0, 0 73 | j GB_DO_WRITE 74 | addi VAL, GB_A, 0 75 | GB_ERROR_5: 76 | addi $at, $zero, STOP_REASON_ERROR 77 | j GB_BREAK_LOOP # exit early 78 | sb $at, CPU_STATE_STOP_REASON(CPUState) 79 | GB_ERROR_6: 80 | addi $at, $zero, STOP_REASON_ERROR 81 | j GB_BREAK_LOOP # exit early 82 | sb $at, CPU_STATE_STOP_REASON(CPUState) 83 | GB_ERROR_7: 84 | addi $at, $zero, STOP_REASON_ERROR 85 | j GB_BREAK_LOOP # exit early 86 | sb $at, CPU_STATE_STOP_REASON(CPUState) 87 | GB_XOR_A_d8: 88 | jal READ_NEXT_INSTRUCTION 89 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 90 | xor GB_A, GB_A, $v0 91 | bne GB_A, $zero, DECODE_NEXT 92 | andi GB_F, $zero, 0 93 | j DECODE_NEXT 94 | set_flags Z_FLAG 95 | GB_RST_28H: 96 | addi GB_SP, GB_SP, -2 97 | andi ADDR, GB_SP, 0xFFFF 98 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 3 # update cycles run 99 | addi VAL, GB_PC, 0 100 | jal SET_GB_PC 101 | addi Param0, $zero, 0x0028 102 | j GB_DO_WRITE_16 103 | move GB_SP, ADDR 104 | -------------------------------------------------------------------------------- /asm/_cpu_inst_F.s: -------------------------------------------------------------------------------- 1 | 2 | ### 0xFX 3 | GB_LDH_A_a8: 4 | jal READ_NEXT_INSTRUCTION 5 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 2 # update cycles run 6 | jal GB_DO_READ_REGISTERS 7 | ori ADDR, $v0, 0xFF00 8 | j DECODE_NEXT 9 | move GB_A, $v0 10 | GB_POP_AF: 11 | addi ADDR, GB_SP, 0 12 | jal GB_DO_READ_16 13 | addi GB_SP, GB_SP, 2 14 | andi GB_SP, GB_SP, 0xFFFF 15 | srl GB_A, $v0, 8 # store A 16 | andi GB_F, $v0, 0xF0 # store F 17 | j DECODE_NEXT 18 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 2 # update cycles run 19 | GB_LDH_A_C: 20 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 21 | jal GB_DO_READ_REGISTERS 22 | ori ADDR, GB_C, 0xFF00 23 | j DECODE_NEXT 24 | move GB_A, $v0 25 | GB_DI: 26 | j DECODE_NEXT 27 | sb $zero, CPU_STATE_INTERRUPTS(CPUState) 28 | GB_ERROR_8: 29 | addi $at, $zero, STOP_REASON_ERROR 30 | j GB_BREAK_LOOP # exit early 31 | sb $at, CPU_STATE_STOP_REASON(CPUState) 32 | GB_PUSH_AF: 33 | addi GB_SP, GB_SP, -2 34 | andi GB_SP, GB_SP, 0xFFFF 35 | addi ADDR, GB_SP, 0 36 | sll VAL, GB_A, 8 37 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 3 # update cycles run 38 | j GB_DO_WRITE_16 39 | or VAL, VAL, GB_F 40 | GB_OR_A_d8: 41 | jal READ_NEXT_INSTRUCTION 42 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 43 | or GB_A, GB_A, $v0 44 | bnez GB_A, DECODE_NEXT 45 | li GB_F, 0 46 | j DECODE_NEXT 47 | set_flags Z_FLAG 48 | GB_RST_30H: 49 | addi GB_SP, GB_SP, -2 50 | andi ADDR, GB_SP, 0xFFFF 51 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 3 # update cycles run 52 | addi VAL, GB_PC, 0 53 | jal SET_GB_PC 54 | addi Param0, $zero, 0x0030 55 | j GB_DO_WRITE_16 56 | move GB_SP, ADDR 57 | GB_LD_HL_SP_d8: 58 | jal READ_NEXT_INSTRUCTION 59 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 2 # update cycles run 60 | sll $v0, $v0, 24 #sign extend 61 | j _ADD_TO_HL_SP 62 | sra Param0, $v0, 24 63 | GB_LD_SP_HL: 64 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 65 | sll GB_SP, GB_H, 8 66 | j DECODE_NEXT 67 | or GB_SP, GB_SP, GB_L 68 | GB_LD_A_a16: 69 | jal READ_NEXT_INSTRUCTION_16 70 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 3 # update cycles run 71 | jal GB_DO_READ 72 | move ADDR, $v0 73 | j DECODE_NEXT 74 | move GB_A, $v0 75 | GB_EI: 76 | li $at, INTERRUPTS_ENABLED 77 | jal CHECK_FOR_INTERRUPT 78 | sb $at, CPU_STATE_INTERRUPTS(CPUState) 79 | j DECODE_NEXT 80 | nop 81 | GB_ERROR_9: 82 | addi $at, $zero, STOP_REASON_ERROR 83 | j GB_BREAK_LOOP # exit early 84 | sb $at, CPU_STATE_STOP_REASON(CPUState) 85 | GB_ERROR_10: 86 | addi $at, $zero, STOP_REASON_ERROR 87 | j GB_BREAK_LOOP # exit early 88 | sb $at, CPU_STATE_STOP_REASON(CPUState) 89 | GB_CP_A_d8: 90 | jal READ_NEXT_INSTRUCTION 91 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 92 | j _CP_A 93 | addi Param0, $v0, 0 94 | GB_RST_38H: 95 | addi GB_SP, GB_SP, -2 96 | andi ADDR, GB_SP, 0xFFFF 97 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 3 # update cycles run 98 | addi VAL, GB_PC, 0 99 | jal SET_GB_PC 100 | addi Param0, $zero, 0x0038 101 | j GB_DO_WRITE_16 102 | move GB_SP, ADDR 103 | -------------------------------------------------------------------------------- /asm/_cpu_inst_prefix.s: -------------------------------------------------------------------------------- 1 | 2 | 3 | ####################### 4 | # Handle the CB_PREFIX command 5 | ####################### 6 | 7 | _GB_PREFIX_CB: 8 | jal READ_NEXT_INSTRUCTION 9 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR # update cycles run 10 | move TMP2, $v0 11 | andi $at, $v0, 0x7 12 | sll $at, $at, 4 13 | la $ra, _GB_PREFIX_DECODE_REGISTER # load jump table address 14 | add $ra, $ra, $at # calculate jump offset 15 | jr $ra # jump to instruction decode 16 | nop 17 | _GB_PREFIX_DECODE_REGISTER: 18 | jal _GB_PREFIX_DECODE_INSTRUCTION 19 | move Param0, GB_B 20 | j DECODE_NEXT 21 | move GB_B, Param0 22 | 23 | jal _GB_PREFIX_DECODE_INSTRUCTION 24 | move Param0, GB_C 25 | j DECODE_NEXT 26 | move GB_C, Param0 27 | 28 | jal _GB_PREFIX_DECODE_INSTRUCTION 29 | move Param0, GB_D 30 | j DECODE_NEXT 31 | move GB_D, Param0 32 | 33 | jal _GB_PREFIX_DECODE_INSTRUCTION 34 | move Param0, GB_E 35 | j DECODE_NEXT 36 | move GB_E, Param0 37 | 38 | jal _GB_PREFIX_DECODE_INSTRUCTION 39 | move Param0, GB_H 40 | j DECODE_NEXT 41 | move GB_H, Param0 42 | 43 | jal _GB_PREFIX_DECODE_INSTRUCTION 44 | move Param0, GB_L 45 | j DECODE_NEXT 46 | move GB_L, Param0 47 | 48 | j _GB_PREFIX_DECODE_HL 49 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 1 # update cycles run 50 | nop 51 | nop 52 | 53 | jal _GB_PREFIX_DECODE_INSTRUCTION 54 | move Param0, GB_A 55 | j DECODE_NEXT 56 | move GB_A, Param0 57 | 58 | _GB_PREFIX_DECODE_HL: 59 | sll ADDR, GB_H, 8 60 | jal GB_DO_READ 61 | or ADDR, ADDR, GB_L 62 | jal _GB_PREFIX_DECODE_INSTRUCTION 63 | move Param0, $v0 64 | addi CYCLES_RUN, CYCLES_RUN, CYCLES_PER_INSTR * 1 # update cycles run separate. This allows the bit instructions to take 3 cycles 65 | sll ADDR, GB_H, 8 66 | or ADDR, ADDR, GB_L 67 | j GB_DO_WRITE 68 | move VAL, Param0 69 | 70 | _GB_PREFIX_DECODE_INSTRUCTION: 71 | andi $at, TMP2, 0xF8 # calculate instruction 72 | la TMP3, _GB_PREFIX_RLC 73 | add TMP3, TMP3, $at # calculate relative jump 74 | jr TMP3 #jump in table 75 | nop 76 | _GB_PREFIX_RLC: 77 | j GB_RLC_IMPL 78 | nop 79 | _GB_PREFIX_RRC: 80 | j GB_RRC_IMPL 81 | nop 82 | _GB_PREFIX_RL: 83 | j GB_RL_IMPL 84 | nop 85 | _GB_PREFIX_RR: 86 | j GB_RR_IMPL 87 | nop 88 | _GB_PREFIX_SLA: 89 | j GB_SLA_IMPL 90 | nop 91 | _GB_PREFIX_SRA: 92 | j GB_SRA_IMPL 93 | nop 94 | _GB_PREFIX_SWAP: 95 | j GB_SWAP_IMPL 96 | nop 97 | _GB_PREFIX_SRL: 98 | j GB_SRL_IMPL 99 | nop 100 | _GB_PREFIX_BIT_0: 101 | j _GB_PREFIX_FINISH_BIT 102 | sll Param0, Param0, 7 103 | _GB_PREFIX_BIT_1: 104 | j _GB_PREFIX_FINISH_BIT 105 | sll Param0, Param0, 6 106 | _GB_PREFIX_BIT_2: 107 | j _GB_PREFIX_FINISH_BIT 108 | sll Param0, Param0, 5 109 | _GB_PREFIX_BIT_3: 110 | j _GB_PREFIX_FINISH_BIT 111 | sll Param0, Param0, 4 112 | _GB_PREFIX_BIT_4: 113 | j _GB_PREFIX_FINISH_BIT 114 | sll Param0, Param0, 3 115 | _GB_PREFIX_BIT_5: 116 | j _GB_PREFIX_FINISH_BIT 117 | sll Param0, Param0, 2 118 | _GB_PREFIX_BIT_6: 119 | j _GB_PREFIX_FINISH_BIT 120 | sll Param0, Param0, 1 121 | _GB_PREFIX_BIT_7: 122 | j _GB_PREFIX_FINISH_BIT 123 | nop 124 | _GB_RES_BIT_0: 125 | jr $ra 126 | andi Param0, Param0, 0xFE 127 | _GB_RES_BIT_1: 128 | jr $ra 129 | andi Param0, Param0, 0xFD 130 | _GB_RES_BIT_2: 131 | jr $ra 132 | andi Param0, Param0, 0xFB 133 | _GB_RES_BIT_3: 134 | jr $ra 135 | andi Param0, Param0, 0xF7 136 | _GB_RES_BIT_4: 137 | jr $ra 138 | andi Param0, Param0, 0xEF 139 | _GB_RES_BIT_5: 140 | jr $ra 141 | andi Param0, Param0, 0xDF 142 | _GB_RES_BIT_6: 143 | jr $ra 144 | andi Param0, Param0, 0xBF 145 | _GB_RES_BIT_7: 146 | jr $ra 147 | andi Param0, Param0, 0x7F 148 | _GB_SET_BIT_0: 149 | jr $ra 150 | ori Param0, Param0, 0x01 151 | _GB_SET_BIT_1: 152 | jr $ra 153 | ori Param0, Param0, 0x02 154 | _GB_SET_BIT_2: 155 | jr $ra 156 | ori Param0, Param0, 0x04 157 | _GB_SET_BIT_3: 158 | jr $ra 159 | ori Param0, Param0, 0x08 160 | _GB_SET_BIT_4: 161 | jr $ra 162 | ori Param0, Param0, 0x10 163 | _GB_SET_BIT_5: 164 | jr $ra 165 | ori Param0, Param0, 0x20 166 | _GB_SET_BIT_6: 167 | jr $ra 168 | ori Param0, Param0, 0x40 169 | _GB_SET_BIT_7: 170 | jr $ra 171 | ori Param0, Param0, 0x80 172 | 173 | _GB_PREFIX_FINISH_BIT: 174 | clear_flags Z_FLAG | N_FLAG 175 | andi Param0, Param0, Z_FLAG # clear all but the z flag position 176 | xori Param0, Param0, Z_FLAG # negate the z flag 177 | or GB_F, GB_F, Param0 178 | j DECODE_NEXT # don't jr since bit checks don't need to store back 179 | set_flags H_FLAG # set h flag 180 | -------------------------------------------------------------------------------- /asm/_debug.s: -------------------------------------------------------------------------------- 1 | 2 | .data 3 | ERROR_MSG: .asciiz "Invalid CPU State\n" 4 | .text 5 | 6 | CHECK_FOR_INVALID_STATE: 7 | slti $at, GB_A, 0x100 8 | beqz $at, _CHECK_FOR_INVALID_STATE_INVALD 9 | 10 | slti $at, GB_F, 0x100 11 | beqz $at, _CHECK_FOR_INVALID_STATE_INVALD 12 | 13 | slti $at, GB_D, 0x100 14 | beqz $at, _CHECK_FOR_INVALID_STATE_INVALD 15 | 16 | slti $at, GB_E, 0x100 17 | beqz $at, _CHECK_FOR_INVALID_STATE_INVALD 18 | 19 | slti $at, GB_H, 0x100 20 | beqz $at, _CHECK_FOR_INVALID_STATE_INVALD 21 | 22 | slti $at, GB_L, 0x100 23 | beqz $at, _CHECK_FOR_INVALID_STATE_INVALD 24 | 25 | andi $at, GB_PC, 0 26 | bnez $at, _CHECK_FOR_INVALID_STATE_INVALD 27 | 28 | andi $at, GB_SP, 0 29 | bnez $at, _CHECK_FOR_INVALID_STATE_INVALD 30 | 31 | nop 32 | 33 | jr $ra 34 | nop 35 | _CHECK_FOR_INVALID_STATE_INVALD: 36 | la $at, 0x80700000 37 | sb $zero, 0($at) # useful for memory breakpoint 38 | 39 | save_state_on_stack 40 | 41 | la $a0, ERROR_MSG 42 | call_c_fn debugInfo, 1 43 | 44 | restore_state_from_stack 45 | 46 | jr $ra 47 | nop 48 | 49 | OPEN_DEBUGGER: 50 | save_state_on_stack 51 | 52 | jal CALCULATE_DIV_VALUE 53 | addi GB_PC, GB_PC, -1 # PC back to the begginning of current instruction 54 | andi GB_PC, GB_PC, 0xFFFF 55 | jal CALCULATE_TIMA_VALUE 56 | sh GB_PC, CPU_STATE_PC(CPUState) 57 | sw CYCLES_RUN, CPU_STATE_CYCLES_RUN(CPUState) 58 | 59 | lw $fp, (ST_FP + _C_CALLBACK_FRAME_SIZE)($sp) 60 | 61 | # $a0 and $a1 already have the correct values 62 | call_c_fn useDebugger, 2 63 | 64 | restore_state_from_stack 65 | 66 | lhu Param0, CPU_STATE_PC(CPUState) 67 | jal SET_GB_PC 68 | addi Param0, Param0, 1 69 | 70 | # disable frame rendering for the rest of this frame 71 | lw $at, ST_FLAGS($fp) 72 | andi $at, $at, %lo(~RUN_CPU_FLAGS_RENDER) 73 | sw $at, ST_FLAGS($fp) 74 | 75 | j DECODE_V0 76 | nop 77 | 78 | 79 | .global READ_FROM_C 80 | .align 4 81 | READ_FROM_C: 82 | move ADDR, $a1 83 | j GB_DO_READ 84 | move Memory, $a0 85 | -------------------------------------------------------------------------------- /asm/_mbc.s: -------------------------------------------------------------------------------- 1 | 2 | .global mbc3WriteTimer 3 | .align 4 4 | mbc3WriteTimer: 5 | lw TMP2, MEMORY_MISC_RAM_BANK(Memory) 6 | slti $at, TMP2, 0x8 7 | bnez $at, mbc3WriteTimer_error 8 | slti $at, TMP2, 0xD 9 | beqz $at, mbc3WriteTimer_error 10 | nop 11 | add TMP2, TMP2, Memory 12 | sb VAL, (REG_RTC_S+MEMORY_MISC_START-MM_REGISTER_START-0x8)(TMP2) 13 | 14 | # set tmp2 to 0 15 | dsubu TMP2, TMP2, TMP2 16 | # load upper day bits 17 | read_register_direct TMP2, REG_RTC_DH 18 | andi TMP2, TMP2, 0x81 19 | sll TMP2, TMP2, 8 20 | # load lower day bits 21 | read_register_direct $at, REG_RTC_DL 22 | or TMP2, TMP2, $at 23 | # days to hours 24 | li $at, 24 25 | multu TMP2, $at 26 | # hours to minutes 27 | read_register_direct $at, REG_RTC_H 28 | mflo TMP2 29 | add TMP2, TMP2, $at 30 | li $at, 60 31 | multu TMP2, $at 32 | # minutes to second 33 | read_register_direct $at, REG_RTC_M 34 | mflo TMP2 35 | add TMP2, TMP2, $at 36 | li $at, 60 37 | multu TMP2, $at 38 | # seconds to microseconds 39 | dsll TMP2, TMP2, 20 40 | sd TMP2, MEMORY_MISC_TIMER(Memory) 41 | jr $ra 42 | nop 43 | 44 | mbc3WriteTimer_error: 45 | jr $ra 46 | nop 47 | 48 | 49 | .global mbc3ReadTimer 50 | .align 4 51 | mbc3ReadTimer: 52 | lw TMP2, MEMORY_MISC_RAM_BANK(Memory) 53 | slti $at, TMP2, 0x8 54 | bnez $at, mbc3ReadTimer_error 55 | slti $at, TMP2, 0xD 56 | beqz $at, mbc3ReadTimer_error 57 | nop 58 | add TMP2, TMP2, Memory 59 | jr $ra 60 | lbu $v0, (REG_RTC_S+MEMORY_MISC_START-MM_REGISTER_START-0x8)(TMP2) 61 | 62 | mbc3ReadTimer_error: 63 | jr $ra 64 | li $v0, 0xFF 65 | 66 | .global HUC1_READ_IR 67 | .align 4 68 | HUC1_READ_IR: 69 | jr $ra 70 | li $v0, 0xC0 71 | -------------------------------------------------------------------------------- /asm/data.s: -------------------------------------------------------------------------------- 1 | .include "macros.inc" 2 | 3 | .section .data 4 | 5 | glabel _dmg_bootSegmentRomStart 6 | .incbin "data/dmg_boot_placeholder.bin" 7 | .balign 16 8 | glabel _dmg_bootSegmentRomEnd 9 | 10 | glabel _cgb_biosSegmentRomStart 11 | .incbin "data/cgb_bios_placeholder.bin" 12 | .balign 16 13 | glabel _cgb_biosSegmentRomEnd 14 | 15 | glabel _gbromSegmentRomStart 16 | .incbin "data/PokemonYellow.gb" 17 | .balign 16 18 | glabel _gbromSegmentRomEnd -------------------------------------------------------------------------------- /asm/data_placeholder.s: -------------------------------------------------------------------------------- 1 | .include "macros.inc" 2 | 3 | .section .data 4 | 5 | glabel _dmg_bootSegmentRomStart 6 | .incbin "data/dmg_boot_placeholder.bin" 7 | .balign 16 8 | glabel _dmg_bootSegmentRomEnd 9 | 10 | glabel _cgb_biosSegmentRomStart 11 | .incbin "data/cgb_bios_placeholder.bin" 12 | .balign 16 13 | glabel _cgb_biosSegmentRomEnd 14 | 15 | glabel _gbromSegmentRomStart 16 | .incbin "data/rom_placeholder.gb" 17 | .balign 16 18 | glabel _gbromSegmentRomEnd -------------------------------------------------------------------------------- /asm/entry.s: -------------------------------------------------------------------------------- 1 | #include "../boot.h" 2 | # assembler directives 3 | .set noat # allow manual use of $at 4 | .set noreorder # don't insert nops after branches 5 | .set gp=64 6 | 7 | .include "macros.inc" 8 | 9 | .section .text, "ax" 10 | 11 | glabel entry_point 12 | la $t0, _codeSegmentBssStart 13 | la $t1, _codeSegmentBssSize 14 | .bss_clear: 15 | addi $t1, $t1, -8 16 | sw $zero, ($t0) 17 | sw $zero, 4($t0) 18 | bnez $t1, .bss_clear 19 | addi $t0, $t0, 8 20 | lui $t2, %hi(boot) # $t2, 0x8024 21 | lui $sp, %hi(bootStack + STACKSIZE) # $sp, 0x8020 22 | addiu $t2, %lo(boot) # addiu $t2, $t2, 0x6dc4 23 | jr $t2 24 | addiu $sp, %lo(bootStack + STACKSIZE) # addiu $sp, $sp, 0xa00 25 | nop 26 | nop 27 | nop 28 | nop 29 | nop 30 | nop 31 | -------------------------------------------------------------------------------- /asm/macros.inc: -------------------------------------------------------------------------------- 1 | # Assembly Macros 2 | 3 | .set K0BASE, 0x80000000 4 | .set K1BASE, 0xA0000000 5 | .set K2BASE, 0xC0000000 6 | 7 | .macro glabel label 8 | .global \label 9 | .balign 4 10 | \label: 11 | .endm 12 | 13 | .macro .word32 x 14 | .word \x 15 | .endm 16 | -------------------------------------------------------------------------------- /asm/registers.inc: -------------------------------------------------------------------------------- 1 | 2 | .eqv REG_JOYP, 0xFF00 3 | .eqv REG_SERIAL_DATA, 0xFF01 4 | .eqv REG_SERIAL, 0xFF02 5 | 6 | .eqv REG_SERIAL_TRANSFER, 0x80 7 | .eqv REG_SERIAL_SPEED, 0x02 8 | .eqv REG_SERIAL_CLOCK, 0x01 9 | 10 | .eqv _REG_JOYSTATE, 0xFF03 11 | .eqv REG_DIV, 0xFF04 12 | .eqv REG_TIMA, 0xFF05 13 | .eqv REG_TMA, 0xFF06 14 | .eqv REG_TAC, 0xFF07 15 | .eqv _REG_DIV_OFFSET, 0xFF08 16 | 17 | .eqv REG_TAC_STOP_BIT, 0x4 18 | .eqv REG_TAC_CLOCK_SELECT, 0x3 19 | 20 | .eqv REG_INTERRUPTS_REQUESTED, 0xFF0F 21 | .eqv REG_INTERRUPTS_ENABLED, 0xFFFF 22 | 23 | .eqv REG_NR10, 0xFF10 24 | .eqv REG_NR14, 0xFF14 25 | .eqv REG_NR24, 0xFF19 26 | .eqv REG_NR30, 0xFF1A 27 | .eqv REG_NR34, 0xFF1E 28 | .eqv REG_NR44, 0xFF23 29 | 30 | .eqv REG_NR50, 0xFF24 31 | .eqv REG_NR52, 0xFF26 32 | 33 | .eqv REG_NR52_ON_OFF, 0x80 34 | 35 | .eqv REG_LCDC, 0xFF40 36 | .eqv REG_LCDC_LCD_ENABLE, 0x80 37 | 38 | .eqv REG_LCDC_STATUS, 0xFF41 39 | .eqv REG_LCDC_LYC_INT, 0x40 40 | .eqv REG_LCDC_OAM_INT, 0x20 41 | .eqv REG_LCDC_V_BLANK_INT, 0x10 42 | .eqv REG_LCDC_H_BLANK_INT, 0x08 43 | .eqv REG_LCDC_STATUS_LYC, 0x4 44 | .eqv REG_LCDC_STATUS_MODE, 0x3 45 | .eqv REG_LCDC_STATUS_MODE_0, 0x00 46 | .eqv REG_LCDC_STATUS_MODE_1, 0x01 47 | .eqv REG_LCDC_STATUS_MODE_2, 0x02 48 | .eqv REG_LCDC_STATUS_MODE_3, 0x03 49 | 50 | .eqv REG_LCDC_STATUS_MODE_0_CYCLES, 51 51 | .eqv REG_LCDC_STATUS_MODE_1_CYCLES, 114 52 | .eqv REG_LCDC_STATUS_MODE_2_CYCLES, 20 53 | .eqv REG_LCDC_STATUS_MODE_3_CYCLES, 43 54 | .eqv REG_LCDC_STATUS_MODE_LAST_LINE, 113 55 | 56 | .eqv REG_SCY, 0xFF42 57 | .eqv REG_SCX, 0xFF43 58 | .eqv REG_LY, 0xFF44 59 | .eqv REG_LYC, 0xFF45 60 | .eqv REG_DMA, 0xFF46 61 | .eqv REG_BGP, 0xFF47 62 | .eqv REG_OBP0, 0xFF48 63 | .eqv REG_OBP1, 0xFF49 64 | .eqv REG_WY, 0xFF4A 65 | .eqv REG_WX, 0xFF4B 66 | .eqv REG_KEY1, 0xFF4D 67 | 68 | .eqv REG_KEY1_CURRENT_SPEED, 0x80 69 | .eqv REG_KEY1_PREPARE_SWITCH, 0x01 70 | 71 | .eqv REG_VBK, 0xFF4F 72 | 73 | .eqv REG_UNLOAD_BIOS, 0xFF50 74 | .eqv REG_HDMA1, 0xFF51 75 | .eqv REG_HDMA2, 0xFF52 76 | .eqv REG_HDMA3, 0xFF53 77 | .eqv REG_HDMA4, 0xFF54 78 | .eqv REG_HDMA5, 0xFF55 79 | 80 | .eqv _REG_DMA_LAST_CYCLE, 0xFF58 81 | .eqv _REG_DMA_CURRENT, 0xFF5A 82 | 83 | .eqv REG_BCPS, 0xFF68 84 | .eqv REG_BCPD, 0xFF69 85 | 86 | .eqv REG_OCPS, 0xFF6A 87 | .eqv REG_OCPD, 0xFF6B 88 | 89 | .eqv REG_SVBK, 0xFF70 90 | 91 | .eqv REG_RTC_S, 0xFF78 92 | .eqv REG_RTC_M, 0xFF79 93 | .eqv REG_RTC_H, 0xFF7A 94 | .eqv REG_RTC_DL, 0xFF7B 95 | .eqv REG_RTC_DH, 0xFF7C 96 | 97 | .eqv INTERRUPT_V_BLANK, 0x01 98 | .eqv INTERRUPT_LCD_STAT, 0x02 99 | .eqv INTERRUPT_TIMER, 0x04 100 | .eqv INTERRUPT_SERIAL, 0x08 101 | .eqv INTERRUPT_JOYPAD, 0x10 102 | 103 | .eqv GB_SCREEN_H, 144 104 | .eqv GB_SCREEN_W, 160 105 | .eqv GB_SCREEN_LINES, 154 106 | 107 | .macro read_register_direct target, register 108 | lbu \target, (\register+MEMORY_MISC_START-MM_REGISTER_START)(Memory) 109 | .endm 110 | 111 | .macro write_register_direct target, register 112 | sb \target, (\register+MEMORY_MISC_START-MM_REGISTER_START)(Memory) 113 | .endm 114 | 115 | .macro read_register16_direct target, register 116 | lhu \target, (\register+MEMORY_MISC_START-MM_REGISTER_START)(Memory) 117 | .endm 118 | 119 | .macro write_register16_direct target, register 120 | sh \target, (\register+MEMORY_MISC_START-MM_REGISTER_START)(Memory) 121 | .endm 122 | -------------------------------------------------------------------------------- /asm/rom_header.s: -------------------------------------------------------------------------------- 1 | /* 2 | * ROM header 3 | * Only the first 0x18 bytes matter to the console. 4 | */ 5 | 6 | .byte 0x80, 0x37, 0x12, 0x40 /* PI BSD Domain 1 register */ 7 | .word 0x0000000F /* Clockrate setting*/ 8 | .word entry_point /* Entrypoint */ 9 | 10 | /* SDK Revision */ 11 | .word 0x0000144C 12 | 13 | .word 0x00000000 /* Checksum 1 (OVERWRITTEN BY MAKEMASK)*/ 14 | .word 0x00000000 /* Checksum 2 (OVERWRITTEN BY MAKEMASK)*/ 15 | .word 0x00000000 /* Unknown */ 16 | .word 0x00000000 /* Unknown */ 17 | .ascii "GB64 " /* Internal ROM name (Max 20 characters) */ 18 | .word 0x00000000 /* Unknown */ 19 | /* Game ID (EXAMPLE: NSME) Begins here */ 20 | .word 0x0000004E /* Cartridge Type (N)*/ 21 | .ascii "ED" /* Cartridge ID (SM)*/ 22 | .ascii " " /* Region (E)*/ 23 | .byte 0x00 /* Version */ 24 | -------------------------------------------------------------------------------- /bin/DUMMY: -------------------------------------------------------------------------------- 1 | huehueiamdummyhuehue 2 | -------------------------------------------------------------------------------- /boot.h: -------------------------------------------------------------------------------- 1 | 2 | /************************************************************************** 3 | * * 4 | * Copyright (C) 1995, Silicon Graphics, Inc. * 5 | * * 6 | * These coded instructions, statements, and computer programs contain * 7 | * unpublished proprietary information of Silicon Graphics, Inc., and * 8 | * are protected by Federal copyright law. They may not be disclosed * 9 | * to third parties or copied or duplicated in any form, in whole or * 10 | * in part, without the prior written consent of Silicon Graphics, Inc. * 11 | * * 12 | *************************************************************************/ 13 | 14 | /* 15 | * File: boot.h 16 | * Create Date: Thu Dec 14 11:40:21 PST 1995 17 | * 18 | */ 19 | 20 | #define DYNAMIC_SEGMENT 3 21 | 22 | #define SCREEN_HT 480 23 | #define SCREEN_WD 640 24 | 25 | /* this stack size is in bytes, and is a lot larger 26 | * than this program needs. 27 | */ 28 | #define STACKSIZE 0x2000 29 | #define RDP_OUTPUT_LEN (4096*16) 30 | /* 31 | * this number (the depth of the message queue) needs to be equal 32 | * to the maximum number of possible overlapping PI requests. 33 | * For this app, 1 or 2 is probably plenty, other apps might 34 | * require a lot more. 35 | */ 36 | #define NUM_PI_MSGS 8 37 | -------------------------------------------------------------------------------- /controller.c: -------------------------------------------------------------------------------- 1 | 2 | /************************************************************************** 3 | * * 4 | * Copyright (C) 1995, Silicon Graphics, Inc. * 5 | * * 6 | * These coded instructions, statements, and computer programs contain * 7 | * unpublished proprietary information of Silicon Graphics, Inc., and * 8 | * are protected by Federal copyright law. They may not be disclosed * 9 | * to third parties or copied or duplicated in any form, in whole or * 10 | * in part, without the prior written consent of Silicon Graphics, Inc. * 11 | * * 12 | *************************************************************************/ 13 | 14 | #include 15 | 16 | /* app specific includes */ 17 | #include "boot.h" 18 | 19 | OSMesgQueue controllerMsgQ; 20 | OSMesg controllerMsgBuf; 21 | 22 | OSContStatus statusdata[MAXCONTROLLERS]; 23 | OSContPad dummycontrollerdata = { 0, 0, 0, 0 }; 24 | OSContPad controllerdata[MAXCONTROLLERS]; 25 | OSContPad *validcontrollerdata[MAXCONTROLLERS]; 26 | int activeControllers[MAXCONTROLLERS]; 27 | int numControllers=0; 28 | u16 gLastButton[MAXCONTROLLERS]; 29 | 30 | /* 31 | * Return how many controllers are connected 32 | * if there are more than connected, return maxcontrollers 33 | * (ie specify how many controllers you want with maxcontrollers, and 34 | * the return result is the number of controllers actually hooked up) 35 | */ 36 | int initControllers( int maxcontrollers ) 37 | { 38 | int i; 39 | u8 pattern; 40 | OSMesgQueue serialMsgQ; 41 | OSMesg serialMsg; 42 | 43 | osCreateMesgQueue(&serialMsgQ, &serialMsg, 1); 44 | osSetEventMesg(OS_EVENT_SI, &serialMsgQ, (OSMesg)1); 45 | 46 | osContInit(&serialMsgQ, &pattern, &statusdata[0]); 47 | 48 | osCreateMesgQueue(&controllerMsgQ, &controllerMsgBuf, 1); 49 | osSetEventMesg(OS_EVENT_SI, &controllerMsgQ, (OSMesg)0); 50 | 51 | for (i = 0; i < MAXCONTROLLERS; i++) 52 | validcontrollerdata[i] = &dummycontrollerdata; 53 | 54 | for (i = 0; i < MAXCONTROLLERS; i++) { 55 | if ((pattern & (1<button; 82 | validcontrollerdata[i]->button = 83 | button & (~gLastButton[i] | ~oneshot); 84 | gLastButton[i]=button; 85 | } 86 | 87 | return validcontrollerdata; 88 | } 89 | 90 | extern u16 ReadLastButton(int index) 91 | { 92 | return gLastButton[index]; 93 | } 94 | -------------------------------------------------------------------------------- /controller.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _CONTROLLER_INCLD 3 | #define _CONTROLLER_INCLD 4 | 5 | extern int numControllers; 6 | 7 | extern int initControllers(int maxcontrollers); 8 | extern OSContPad **ReadController(int oneshot); 9 | extern u16 ReadLastButton(int index); 10 | 11 | 12 | #endif /* _CONTROLLER_INCLD */ 13 | -------------------------------------------------------------------------------- /data/PokemonCrystal.rtc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambertjamesd/gb64/a5ce8da11a3015926d8cacde4757b1e1af801821/data/PokemonCrystal.rtc -------------------------------------------------------------------------------- /data/cgb_bios_placeholder.asm: -------------------------------------------------------------------------------- 1 | SECTION "entry",ROM0[$0000] 2 | 3 | EntryPoint: 4 | JP SetRegisters 5 | 6 | SECTION "text",ROM0[$0004] 7 | DB "CGB_BIOS" 8 | SetRegisters: 9 | LD A, $87 10 | LDH [$ff14], A 11 | LD A, $77 12 | LDH [$ff24], A 13 | LD A, $80 14 | LDH [$ff26], A 15 | LD A, $91 16 | LDH [$ff40], A 17 | LD A, $C0 18 | LDH [$ff4C], A 19 | LD A, $00 20 | LDH [$ff70], A 21 | JP Finish 22 | 23 | SECTION "finish",ROM0[$00FC] 24 | Finish: 25 | LD A, $11 26 | LDH [$ff50], A 27 | 28 | SECTION "padding",ROM0[$08FF] 29 | DB 0 -------------------------------------------------------------------------------- /data/cgb_bios_placeholder.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambertjamesd/gb64/a5ce8da11a3015926d8cacde4757b1e1af801821/data/cgb_bios_placeholder.bin -------------------------------------------------------------------------------- /data/dmg_boot_placeholder.asm: -------------------------------------------------------------------------------- 1 | SECTION "entry",ROM0[$0000] 2 | 3 | EntryPoint: 4 | JP SetRegisters 5 | 6 | SECTION "text",ROM0[$0004] 7 | DB "DMG_BOOT" 8 | SetRegisters: 9 | LD A, $87 10 | LDH [$ff14], A 11 | LD A, $77 12 | LDH [$ff24], A 13 | LD A, $80 14 | LDH [$ff26], A 15 | LD A, $91 16 | LDH [$ff40], A 17 | LD A, $00 18 | LDH [$ff4C], A 19 | JP Finish 20 | 21 | SECTION "finish",ROM0[$00FC] 22 | Finish: 23 | LD A, $1 24 | LDH [$ff50], A -------------------------------------------------------------------------------- /data/dmg_boot_placeholder.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambertjamesd/gb64/a5ce8da11a3015926d8cacde4757b1e1af801821/data/dmg_boot_placeholder.bin -------------------------------------------------------------------------------- /data/rom_placeholder.gb: -------------------------------------------------------------------------------- 1 | GB64_ROM -------------------------------------------------------------------------------- /data/save/gb.fla: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambertjamesd/gb64/a5ce8da11a3015926d8cacde4757b1e1af801821/data/save/gb.fla -------------------------------------------------------------------------------- /data/save/gb.pj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambertjamesd/gb64/a5ce8da11a3015926d8cacde4757b1e1af801821/data/save/gb.pj -------------------------------------------------------------------------------- /dram_stack.c: -------------------------------------------------------------------------------- 1 | 2 | /************************************************************************** 3 | * * 4 | * Copyright (C) 1995, Silicon Graphics, Inc. * 5 | * * 6 | * These coded instructions, statements, and computer programs contain * 7 | * unpublished proprietary information of Silicon Graphics, Inc., and * 8 | * are protected by Federal copyright law. They may not be disclosed * 9 | * to third parties or copied or duplicated in any form, in whole or * 10 | * in part, without the prior written consent of Silicon Graphics, Inc. * 11 | * * 12 | *************************************************************************/ 13 | 14 | #include 15 | #include "game.h" 16 | 17 | /*---------------------------------------------------------------------* 18 | Copyright (C) 1997,1998 Nintendo. (Originated by SGI) 19 | 20 | $RCSfile: dram_stack.c,v $ 21 | $Revision: 1.1 $ 22 | $Date: 1998/12/24 15:54:00 $ 23 | *---------------------------------------------------------------------*/ 24 | /* 25 | * buffers for RSP tasks: 26 | * buffers used by fifo microcode only 27 | */ 28 | 29 | 30 | /* for CodeWarrior compiler */ 31 | #ifdef __MWERKS__ 32 | #pragma align(16) 33 | #endif 34 | 35 | #if __GNUC__ /* { */ 36 | 37 | /* for GNU compiler */ 38 | u64 dram_stack[SP_DRAM_STACK_SIZE64] __attribute__((aligned (16))); /* used for matrix stack */ 39 | u64 rdp_output[RDP_OUTPUT_LEN] __attribute__((aligned (16))); /* buffer for RDP DL */ 40 | 41 | #else /* }{ */ 42 | 43 | /* for SGI compiler */ 44 | u64 dram_stack[SP_DRAM_STACK_SIZE64]; /* used for matrix stack */ 45 | u64 rdp_output[RDP_OUTPUT_LEN]; /* buffer for RDP DL */ 46 | 47 | #endif /* } */ 48 | -------------------------------------------------------------------------------- /game.h: -------------------------------------------------------------------------------- 1 | 2 | /************************************************************************** 3 | * * 4 | * Copyright (C) 1995, Silicon Graphics, Inc. * 5 | * * 6 | * These coded instructions, statements, and computer programs contain * 7 | * unpublished proprietary information of Silicon Graphics, Inc., and * 8 | * are protected by Federal copyright law. They may not be disclosed * 9 | * to third parties or copied or duplicated in any form, in whole or * 10 | * in part, without the prior written consent of Silicon Graphics, Inc. * 11 | * * 12 | *************************************************************************/ 13 | 14 | /* 15 | * File: game.h 16 | * Create Date: Thu Dec 14 13:52:23 PST 1995 17 | * 18 | */ 19 | 20 | #include "boot.h" -------------------------------------------------------------------------------- /gb64.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_ARCH (mips) 2 | 3 | #define USE_PLACEHOLDER 0 4 | 5 | #define BEGIN_SEG(name, addr) \ 6 | _##name##SegmentStart = ADDR(.name); \ 7 | _##name##SegmentRomStart = __romPos; \ 8 | .name addr : AT(__romPos) 9 | 10 | #define END_SEG(name) \ 11 | _##name##SegmentEnd = ADDR(.name) + SIZEOF(.name); \ 12 | _##name##SegmentRomEnd = __romPos + SIZEOF(.name); \ 13 | __romPos += SIZEOF(.name); 14 | 15 | #define BEGIN_NOLOAD(name) \ 16 | _##name##SegmentBssStart = ADDR(.name.noload); \ 17 | .name.noload (NOLOAD) : 18 | 19 | #define END_NOLOAD(name) \ 20 | _##name##SegmentBssEnd = ADDR(.name.noload) + SIZEOF(.name.noload); \ 21 | _##name##SegmentBssSize = SIZEOF(.name.noload); 22 | 23 | #define DEFINE_LEVEL(humanName, name, theme, maxPlayers, flags) \ 24 | BEGIN_SEG(name, 0x04000000) \ 25 | { \ 26 | build/data/levels/name/level.o(.data); \ 27 | build/data/levels/name/level.o(.bss); \ 28 | } \ 29 | END_SEG(name) \ 30 | BEGIN_SEG(name##_wireframe, 0x07000000) \ 31 | { \ 32 | build/data/levels/name/level.wire.o(.data); \ 33 | build/data/levels/name/level.wire.o(.bss); \ 34 | } \ 35 | END_SEG(name##_wireframe) \ 36 | 37 | #define DEFINE_THEME(theme) \ 38 | BEGIN_SEG(theme, 0x05000000) \ 39 | { \ 40 | build/data/level_themes/theme/the##me.o(.data); \ 41 | build/data/level_themes/theme/the##me.o(.bss); \ 42 | } \ 43 | END_SEG(theme) \ 44 | 45 | 46 | SECTIONS 47 | { 48 | __romPos = 0; 49 | 50 | BEGIN_SEG(boot, 0x04000000) 51 | { 52 | build/asm/rom_header.o(.text); 53 | build/boot.6102.o(.data); 54 | } 55 | END_SEG(boot) 56 | 57 | BEGIN_SEG(code, 0x80000400) SUBALIGN(16) 58 | { 59 | build/asm/entry.o(.text); 60 | CODE_SEGMENT(.text); 61 | /usr/lib/n64/PR/rspboot.o(.text); 62 | /usr/lib/n64/PR/gspF3DEX2.fifo.o(.text); 63 | bin/rsp/ppu.o(.text); 64 | 65 | /* data */ 66 | CODE_SEGMENT(.data*); 67 | /usr/lib/n64/PR/rspboot.o(.data*); 68 | /usr/lib/n64/PR/gspF3DEX2.fifo.o(.data*); 69 | bin/rsp/ppu.o(.data*); 70 | 71 | /* rodata */ 72 | CODE_SEGMENT(.rodata*); 73 | } 74 | END_SEG(code) 75 | BEGIN_NOLOAD(code) 76 | { 77 | CODE_SEGMENT(COMMON); 78 | CODE_SEGMENT(.scommon*); 79 | CODE_SEGMENT(.bss*); 80 | . = ALIGN(0x8); 81 | } 82 | END_NOLOAD(code) 83 | 84 | _codeSegmentBssEnd = .; 85 | 86 | _heapStart = .; 87 | 88 | . = 0x80200000; 89 | 90 | #if USE_PLACEHOLDER 91 | BEGIN_SEG(sound_data, __romPos) 92 | { 93 | build/asm/data_placeholder.o(.data); 94 | build/asm/data_placeholder.o(.bss); 95 | } 96 | END_SEG(sound_data) 97 | #else // USE_PLACEHOLDER 98 | BEGIN_SEG(sound_data, __romPos) 99 | { 100 | build/asm/data.o(.data); 101 | build/asm/data.o(.bss); 102 | } 103 | END_SEG(sound_data) 104 | #endif 105 | 106 | /* Discard everything not specifically mentioned above. */ 107 | /DISCARD/ : 108 | { 109 | *(.eh_frame) 110 | *(.MIPS.abiflags) 111 | } 112 | } -------------------------------------------------------------------------------- /gfx_extend.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __GFX_EXTEND_H__ 3 | #define __GFX_EXTEND_H__ 4 | 5 | #include 6 | 7 | union OtherModes { 8 | struct { 9 | u32 word0; 10 | u32 word1; 11 | }; 12 | 13 | struct { 14 | u64 command:8; 15 | 16 | u64 atomic_prim:1; 17 | u64 reserved0:1; 18 | u64 cycle_type:2; 19 | u64 persp_tex_en:1; 20 | u64 detail_tex_en:1; 21 | u64 sharpen_tex_en:1; 22 | u64 tex_lod_en:1; 23 | 24 | u64 en_tlut:1; 25 | u64 tlut_type:1; 26 | u64 sample_type:1; 27 | u64 mid_texel:1; 28 | u64 bi_lerp_0:1; 29 | u64 bi_lerp_1:1; 30 | u64 convert_one:1; 31 | u64 key_en:1; 32 | 33 | u64 rgb_dither_sel:2; 34 | u64 alpha_dither_sel:2; 35 | u64 reserved1:4; 36 | 37 | u64 m1a0:2; 38 | u64 m1a1:2; 39 | u64 m1b0:2; 40 | u64 m1b1:2; 41 | 42 | u64 m2a0:2; 43 | u64 m2a1:2; 44 | u64 m2b0:2; 45 | u64 m2b1:2; 46 | 47 | u64 reserved2:1; 48 | u64 force_blend:1; 49 | u64 alpha_cvg_select:1; 50 | u64 cvg_times_alpha:1; 51 | u64 z_mode:2; 52 | u64 cvg_dest:2; 53 | 54 | u64 color_on_cvg:1; 55 | u64 image_read_en:1; 56 | u64 z_update_en:1; 57 | u64 z_compare_en:1; 58 | u64 antialias_en:1; 59 | u64 z_source_sel:1; 60 | u64 dither_alpha_en:1; 61 | u64 alpha_compare_en:1; 62 | }; 63 | 64 | long long int force_structure_alignment; 65 | }; 66 | 67 | #endif -------------------------------------------------------------------------------- /gfxinit.c: -------------------------------------------------------------------------------- 1 | 2 | /************************************************************************** 3 | * * 4 | * Copyright (C) 1995, Silicon Graphics, Inc. * 5 | * * 6 | * These coded instructions, statements, and computer programs contain * 7 | * unpublished proprietary information of Silicon Graphics, Inc., and * 8 | * are protected by Federal copyright law. They may not be disclosed * 9 | * to third parties or copied or duplicated in any form, in whole or * 10 | * in part, without the prior written consent of Silicon Graphics, Inc. * 11 | * * 12 | *************************************************************************/ 13 | 14 | /* 15 | * File: gfxinit.c 16 | * 17 | * This file holds display list segments that are 'static' data. 18 | * 19 | */ 20 | 21 | #include 22 | #include "boot.h" 23 | #include "render.h" 24 | 25 | /* 26 | * Remember, viewport structures have 2 bits of fraction in them. 27 | */ 28 | static Vp vp = { 29 | SCREEN_WD*2, SCREEN_HT*2, G_MAXZ/2, 0, /* scale */ 30 | SCREEN_WD*2, SCREEN_HT*2, G_MAXZ/2, 0, /* translate */ 31 | }; 32 | /* 33 | * initialize the RCP state 34 | */ 35 | Gfx init_dl[] = { 36 | /* 37 | * init RSP 38 | */ 39 | gsSPViewport(&vp), 40 | gsSPClearGeometryMode(G_SHADE | G_SHADING_SMOOTH | G_CULL_BOTH | 41 | G_FOG | G_LIGHTING | G_TEXTURE_GEN | 42 | G_TEXTURE_GEN_LINEAR | G_LOD), 43 | gsSPTexture(0, 0, 0, 0, G_OFF), 44 | gsSPSetGeometryMode(G_SHADE | G_SHADING_SMOOTH), 45 | gsSPClipRatio(FRUSTRATIO_2), 46 | 47 | /* 48 | * init RDP 49 | */ 50 | gsDPSetCycleType(G_CYC_1CYCLE), 51 | gsDPPipelineMode(G_PM_NPRIMITIVE), 52 | gsDPSetScissor(G_SC_NON_INTERLACE, 0, 0, SCREEN_WD, SCREEN_HT), 53 | gsDPSetTextureLOD(G_TL_TILE), 54 | gsDPSetTextureLUT(G_TT_NONE), 55 | gsDPSetTextureDetail(G_TD_CLAMP), 56 | gsDPSetTexturePersp(G_TP_NONE), 57 | gsDPSetTextureFilter(G_TF_BILERP), 58 | gsDPSetTextureConvert(G_TC_FILT), 59 | gsDPSetCombineMode(G_CC_SHADE, G_CC_SHADE), 60 | gsDPSetCombineKey(G_CK_NONE), 61 | gsDPSetAlphaCompare(G_AC_NONE), 62 | gsDPSetRenderMode(G_RM_AA_ZB_OPA_SURF, G_RM_AA_ZB_OPA_SURF2), 63 | gsDPSetColorDither(G_CD_BAYER), 64 | gsDPSetAlphaDither(G_AD_NOISE), 65 | gsDPPipeSync(), 66 | gsSPEndDisplayList(), 67 | }; 68 | 69 | /* 70 | * clear the color frame buffer 71 | */ 72 | Gfx clearcfb_dl[] = { 73 | gsDPSetCycleType(G_CYC_FILL), 74 | #if 0 75 | gsDPSetFillColor(GPACK_RGBA5551(64,64,255,1) << 16 | 76 | GPACK_RGBA5551(64,64,255,1)), 77 | #endif 78 | // gsDPFillRectangle(0, 0, SCREEN_WD-1, SCREEN_HT-1), 79 | gsSPEndDisplayList(), 80 | }; 81 | 82 | -------------------------------------------------------------------------------- /gzip/adler32.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Adler-32 checksum 3 | * 4 | * Copyright (c) 2003 by Joergen Ibsen / Jibz 5 | * All Rights Reserved 6 | * 7 | * http://www.ibsensoftware.com/ 8 | * 9 | * This software is provided 'as-is', without any express 10 | * or implied warranty. In no event will the authors be 11 | * held liable for any damages arising from the use of 12 | * this software. 13 | * 14 | * Permission is granted to anyone to use this software 15 | * for any purpose, including commercial applications, 16 | * and to alter it and redistribute it freely, subject to 17 | * the following restrictions: 18 | * 19 | * 1. The origin of this software must not be 20 | * misrepresented; you must not claim that you 21 | * wrote the original software. If you use this 22 | * software in a product, an acknowledgment in 23 | * the product documentation would be appreciated 24 | * but is not required. 25 | * 26 | * 2. Altered source versions must be plainly marked 27 | * as such, and must not be misrepresented as 28 | * being the original software. 29 | * 30 | * 3. This notice may not be removed or altered from 31 | * any source distribution. 32 | */ 33 | 34 | /* 35 | * Adler-32 algorithm taken from the zlib source, which is 36 | * Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler 37 | */ 38 | 39 | #include "tinf.h" 40 | 41 | #define A32_BASE 65521 42 | #define A32_NMAX 5552 43 | 44 | uint32_t uzlib_adler32(const void *data, unsigned int length, uint32_t prev_sum /* 1 */) 45 | { 46 | const unsigned char *buf = (const unsigned char *)data; 47 | 48 | unsigned int s1 = prev_sum & 0xffff; 49 | unsigned int s2 = prev_sum >> 16; 50 | 51 | while (length > 0) 52 | { 53 | int k = length < A32_NMAX ? length : A32_NMAX; 54 | int i; 55 | 56 | for (i = k / 16; i; --i, buf += 16) 57 | { 58 | s1 += buf[0]; s2 += s1; s1 += buf[1]; s2 += s1; 59 | s1 += buf[2]; s2 += s1; s1 += buf[3]; s2 += s1; 60 | s1 += buf[4]; s2 += s1; s1 += buf[5]; s2 += s1; 61 | s1 += buf[6]; s2 += s1; s1 += buf[7]; s2 += s1; 62 | 63 | s1 += buf[8]; s2 += s1; s1 += buf[9]; s2 += s1; 64 | s1 += buf[10]; s2 += s1; s1 += buf[11]; s2 += s1; 65 | s1 += buf[12]; s2 += s1; s1 += buf[13]; s2 += s1; 66 | s1 += buf[14]; s2 += s1; s1 += buf[15]; s2 += s1; 67 | } 68 | 69 | for (i = k % 16; i; --i) { s1 += *buf++; s2 += s1; } 70 | 71 | s1 %= A32_BASE; 72 | s2 %= A32_BASE; 73 | 74 | length -= k; 75 | } 76 | 77 | return ((uint32_t)s2 << 16) | s1; 78 | } 79 | -------------------------------------------------------------------------------- /gzip/crc32.c: -------------------------------------------------------------------------------- 1 | /* 2 | * CRC32 checksum 3 | * 4 | * Copyright (c) 1998-2003 by Joergen Ibsen / Jibz 5 | * All Rights Reserved 6 | * 7 | * http://www.ibsensoftware.com/ 8 | * 9 | * This software is provided 'as-is', without any express 10 | * or implied warranty. In no event will the authors be 11 | * held liable for any damages arising from the use of 12 | * this software. 13 | * 14 | * Permission is granted to anyone to use this software 15 | * for any purpose, including commercial applications, 16 | * and to alter it and redistribute it freely, subject to 17 | * the following restrictions: 18 | * 19 | * 1. The origin of this software must not be 20 | * misrepresented; you must not claim that you 21 | * wrote the original software. If you use this 22 | * software in a product, an acknowledgment in 23 | * the product documentation would be appreciated 24 | * but is not required. 25 | * 26 | * 2. Altered source versions must be plainly marked 27 | * as such, and must not be misrepresented as 28 | * being the original software. 29 | * 30 | * 3. This notice may not be removed or altered from 31 | * any source distribution. 32 | */ 33 | 34 | /* 35 | * CRC32 algorithm taken from the zlib source, which is 36 | * Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler 37 | */ 38 | 39 | #include "tinf.h" 40 | 41 | static const uint32_t tinf_crc32tab[16] = { 42 | 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 43 | 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 44 | 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 45 | 0xbdbdf21c 46 | }; 47 | 48 | /* crc is previous value for incremental computation, 0xffffffff initially */ 49 | uint32_t uzlib_crc32(const void *data, unsigned int length, uint32_t crc) 50 | { 51 | const unsigned char *buf = (const unsigned char *)data; 52 | unsigned int i; 53 | 54 | for (i = 0; i < length; ++i) 55 | { 56 | crc ^= buf[i]; 57 | crc = tinf_crc32tab[crc & 0x0f] ^ (crc >> 4); 58 | crc = tinf_crc32tab[crc & 0x0f] ^ (crc >> 4); 59 | } 60 | 61 | // return value suitable for passing in next time, for final value invert it 62 | return crc/* ^ 0xffffffff*/; 63 | } 64 | -------------------------------------------------------------------------------- /gzip/defl_static.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) uzlib authors 3 | * 4 | * This software is provided 'as-is', without any express 5 | * or implied warranty. In no event will the authors be 6 | * held liable for any damages arising from the use of 7 | * this software. 8 | * 9 | * Permission is granted to anyone to use this software 10 | * for any purpose, including commercial applications, 11 | * and to alter it and redistribute it freely, subject to 12 | * the following restrictions: 13 | * 14 | * 1. The origin of this software must not be 15 | * misrepresented; you must not claim that you 16 | * wrote the original software. If you use this 17 | * software in a product, an acknowledgment in 18 | * the product documentation would be appreciated 19 | * but is not required. 20 | * 21 | * 2. Altered source versions must be plainly marked 22 | * as such, and must not be misrepresented as 23 | * being the original software. 24 | * 25 | * 3. This notice may not be removed or altered from 26 | * any source distribution. 27 | */ 28 | 29 | /* This files contains type declaration and prototypes for defl_static.c. 30 | They may be altered/distinct from the originals used in PuTTY source 31 | code. */ 32 | 33 | struct Outbuf { 34 | unsigned char *outbuf; 35 | int outlen, outsize; 36 | unsigned long outbits; 37 | int noutbits; 38 | int comp_disabled; 39 | }; 40 | 41 | void outbits(struct Outbuf *out, unsigned long bits, int nbits); 42 | void zlib_start_block(struct Outbuf *ctx); 43 | void zlib_finish_block(struct Outbuf *ctx); 44 | void zlib_literal(struct Outbuf *ectx, unsigned char c); 45 | void zlib_match(struct Outbuf *ectx, int distance, int len); 46 | -------------------------------------------------------------------------------- /gzip/genlz77.c: -------------------------------------------------------------------------------- 1 | /* 2 | * genlz77 - Generic LZ77 compressor 3 | * 4 | * Copyright (c) 2014 by Paul Sokolovsky 5 | * 6 | * This software is provided 'as-is', without any express 7 | * or implied warranty. In no event will the authors be 8 | * held liable for any damages arising from the use of 9 | * this software. 10 | * 11 | * Permission is granted to anyone to use this software 12 | * for any purpose, including commercial applications, 13 | * and to alter it and redistribute it freely, subject to 14 | * the following restrictions: 15 | * 16 | * 1. The origin of this software must not be 17 | * misrepresented; you must not claim that you 18 | * wrote the original software. If you use this 19 | * software in a product, an acknowledgment in 20 | * the product documentation would be appreciated 21 | * but is not required. 22 | * 23 | * 2. Altered source versions must be plainly marked 24 | * as such, and must not be misrepresented as 25 | * being the original software. 26 | * 27 | * 3. This notice may not be removed or altered from 28 | * any source distribution. 29 | */ 30 | #include 31 | #include 32 | #include "uzlib.h" 33 | 34 | #if 0 35 | #define HASH_BITS 12 36 | #else 37 | #define HASH_BITS data->hash_bits 38 | #endif 39 | 40 | #define HASH_SIZE (1<dict_size 51 | #endif 52 | 53 | /* Hash function can be defined as macro or as inline function */ 54 | 55 | /*#define HASH(p) (p[0] + p[1] + p[2])*/ 56 | 57 | /* This is hash function from liblzf */ 58 | static inline int HASH(struct uzlib_comp *data, const uint8_t *p) { 59 | int v = (p[0] << 16) | (p[1] << 8) | p[2]; 60 | int hash = ((v >> (3*8 - HASH_BITS)) - v) & (HASH_SIZE - 1); 61 | return hash; 62 | } 63 | 64 | #ifdef DUMP_LZTXT 65 | 66 | /* Counter for approximate compressed length in LZTXT mode. */ 67 | /* Literal is counted as 1, copy as 2 bytes. */ 68 | unsigned approx_compressed_len; 69 | 70 | void literal(void *data, uint8_t val) 71 | { 72 | printf("L%02x # %c\n", val, (val >= 0x20 && val <= 0x7e) ? val : '?'); 73 | approx_compressed_len++; 74 | } 75 | 76 | void copy(void *data, unsigned offset, unsigned len) 77 | { 78 | printf("C-%u,%u\n", offset, len); 79 | approx_compressed_len += 2; 80 | } 81 | 82 | #else 83 | 84 | static inline void literal(void *data, uint8_t val) 85 | { 86 | zlib_literal(data, val); 87 | } 88 | 89 | static inline void copy(void *data, unsigned offset, unsigned len) 90 | { 91 | zlib_match(data, offset, len); 92 | } 93 | 94 | #endif 95 | 96 | 97 | void uzlib_compress(struct uzlib_comp *data, const uint8_t *src, unsigned slen) 98 | { 99 | const uint8_t *top = src + slen - MIN_MATCH; 100 | while (src < top) { 101 | int h = HASH(data, src); 102 | const uint8_t **bucket = &data->hash_table[h & (HASH_SIZE - 1)]; 103 | const uint8_t *subs = *bucket; 104 | *bucket = src; 105 | if (subs && src > subs && (src - subs) <= MAX_OFFSET && !memcmp(src, subs, MIN_MATCH)) { 106 | src += MIN_MATCH; 107 | const uint8_t *m = subs + MIN_MATCH; 108 | int len = MIN_MATCH; 109 | while (*src == *m && len < MAX_MATCH && src < top) { 110 | src++; m++; len++; 111 | } 112 | copy(data, src - len - subs, len); 113 | } else { 114 | literal(data, *src++); 115 | } 116 | } 117 | // Process buffer tail, which is less than MIN_MATCH 118 | // (and so it doesn't make sense to look for matches there) 119 | top += MIN_MATCH; 120 | while (src < top) { 121 | literal(data, *src++); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /gzip/tinf.h: -------------------------------------------------------------------------------- 1 | /* Compatibility header for the original tinf lib/older versions of uzlib. 2 | Note: may be removed in the future, please migrate to uzlib.h. */ 3 | #include "uzlib.h" 4 | -------------------------------------------------------------------------------- /gzip/tinf_compat.h: -------------------------------------------------------------------------------- 1 | /* This header contains compatibility defines for the original tinf API 2 | and uzlib 2.x and below API. These defines are deprecated and going 3 | to be removed in the future, so applications should migrate to new 4 | uzlib API. */ 5 | #define TINF_DATA struct uzlib_uncomp 6 | 7 | #define destSize dest_size 8 | #define destStart dest_start 9 | #define readSource source_read_cb 10 | -------------------------------------------------------------------------------- /gzip/tinfgzip.c: -------------------------------------------------------------------------------- 1 | /* 2 | * uzlib - tiny deflate/inflate library (deflate, gzip, zlib) 3 | * 4 | * Copyright (c) 2003 by Joergen Ibsen / Jibz 5 | * All Rights Reserved 6 | * 7 | * http://www.ibsensoftware.com/ 8 | * 9 | * Copyright (c) 2014-2018 by Paul Sokolovsky 10 | * 11 | * This software is provided 'as-is', without any express 12 | * or implied warranty. In no event will the authors be 13 | * held liable for any damages arising from the use of 14 | * this software. 15 | * 16 | * Permission is granted to anyone to use this software 17 | * for any purpose, including commercial applications, 18 | * and to alter it and redistribute it freely, subject to 19 | * the following restrictions: 20 | * 21 | * 1. The origin of this software must not be 22 | * misrepresented; you must not claim that you 23 | * wrote the original software. If you use this 24 | * software in a product, an acknowledgment in 25 | * the product documentation would be appreciated 26 | * but is not required. 27 | * 28 | * 2. Altered source versions must be plainly marked 29 | * as such, and must not be misrepresented as 30 | * being the original software. 31 | * 32 | * 3. This notice may not be removed or altered from 33 | * any source distribution. 34 | */ 35 | 36 | #include "tinf.h" 37 | 38 | #define FTEXT 1 39 | #define FHCRC 2 40 | #define FEXTRA 4 41 | #define FNAME 8 42 | #define FCOMMENT 16 43 | 44 | void tinf_skip_bytes(TINF_DATA *d, int num); 45 | uint16_t tinf_get_uint16(TINF_DATA *d); 46 | 47 | void tinf_skip_bytes(TINF_DATA *d, int num) 48 | { 49 | while (num--) uzlib_get_byte(d); 50 | } 51 | 52 | uint16_t tinf_get_uint16(TINF_DATA *d) 53 | { 54 | unsigned int v = uzlib_get_byte(d); 55 | v = (uzlib_get_byte(d) << 8) | v; 56 | return v; 57 | } 58 | 59 | int uzlib_gzip_parse_header(TINF_DATA *d) 60 | { 61 | unsigned char flg; 62 | 63 | /* -- check format -- */ 64 | 65 | /* check id bytes */ 66 | if (uzlib_get_byte(d) != 0x1f || uzlib_get_byte(d) != 0x8b) return TINF_DATA_ERROR; 67 | 68 | /* check method is deflate */ 69 | if (uzlib_get_byte(d) != 8) return TINF_DATA_ERROR; 70 | 71 | /* get flag byte */ 72 | flg = uzlib_get_byte(d); 73 | 74 | /* check that reserved bits are zero */ 75 | if (flg & 0xe0) return TINF_DATA_ERROR; 76 | 77 | /* -- find start of compressed data -- */ 78 | 79 | /* skip rest of base header of 10 bytes */ 80 | tinf_skip_bytes(d, 6); 81 | 82 | /* skip extra data if present */ 83 | if (flg & FEXTRA) 84 | { 85 | unsigned int xlen = tinf_get_uint16(d); 86 | tinf_skip_bytes(d, xlen); 87 | } 88 | 89 | /* skip file name if present */ 90 | if (flg & FNAME) { while (uzlib_get_byte(d)); } 91 | 92 | /* skip file comment if present */ 93 | if (flg & FCOMMENT) { while (uzlib_get_byte(d)); } 94 | 95 | /* check header crc if present */ 96 | if (flg & FHCRC) 97 | { 98 | /*unsigned int hcrc =*/ tinf_get_uint16(d); 99 | 100 | // TODO: Check! 101 | // if (hcrc != (tinf_crc32(src, start - src) & 0x0000ffff)) 102 | // return TINF_DATA_ERROR; 103 | } 104 | 105 | /* initialize for crc32 checksum */ 106 | d->checksum_type = TINF_CHKSUM_CRC; 107 | d->checksum = ~0; 108 | 109 | return TINF_OK; 110 | } 111 | -------------------------------------------------------------------------------- /gzip/tinfzlib.c: -------------------------------------------------------------------------------- 1 | /* 2 | * uzlib - tiny deflate/inflate library (deflate, gzip, zlib) 3 | * 4 | * Copyright (c) 2003 by Joergen Ibsen / Jibz 5 | * All Rights Reserved 6 | * 7 | * http://www.ibsensoftware.com/ 8 | * 9 | * Copyright (c) 2014-2018 by Paul Sokolovsky 10 | * 11 | * This software is provided 'as-is', without any express 12 | * or implied warranty. In no event will the authors be 13 | * held liable for any damages arising from the use of 14 | * this software. 15 | * 16 | * Permission is granted to anyone to use this software 17 | * for any purpose, including commercial applications, 18 | * and to alter it and redistribute it freely, subject to 19 | * the following restrictions: 20 | * 21 | * 1. The origin of this software must not be 22 | * misrepresented; you must not claim that you 23 | * wrote the original software. If you use this 24 | * software in a product, an acknowledgment in 25 | * the product documentation would be appreciated 26 | * but is not required. 27 | * 28 | * 2. Altered source versions must be plainly marked 29 | * as such, and must not be misrepresented as 30 | * being the original software. 31 | * 32 | * 3. This notice may not be removed or altered from 33 | * any source distribution. 34 | */ 35 | 36 | #include "tinf.h" 37 | 38 | int uzlib_zlib_parse_header(TINF_DATA *d) 39 | { 40 | unsigned char cmf, flg; 41 | 42 | /* -- get header bytes -- */ 43 | 44 | cmf = uzlib_get_byte(d); 45 | flg = uzlib_get_byte(d); 46 | 47 | /* -- check format -- */ 48 | 49 | /* check checksum */ 50 | if ((256*cmf + flg) % 31) return TINF_DATA_ERROR; 51 | 52 | /* check method is deflate */ 53 | if ((cmf & 0x0f) != 8) return TINF_DATA_ERROR; 54 | 55 | /* check window size is valid */ 56 | if ((cmf >> 4) > 7) return TINF_DATA_ERROR; 57 | 58 | /* check there is no preset dictionary */ 59 | if (flg & 0x20) return TINF_DATA_ERROR; 60 | 61 | /* initialize for adler32 checksum */ 62 | d->checksum_type = TINF_CHKSUM_ADLER; 63 | d->checksum = 1; 64 | 65 | return cmf >> 4; 66 | } 67 | -------------------------------------------------------------------------------- /gzip/uzlib.h: -------------------------------------------------------------------------------- 1 | /* 2 | * uzlib - tiny deflate/inflate library (deflate, gzip, zlib) 3 | * 4 | * Copyright (c) 2003 by Joergen Ibsen / Jibz 5 | * All Rights Reserved 6 | * http://www.ibsensoftware.com/ 7 | * 8 | * Copyright (c) 2014-2018 by Paul Sokolovsky 9 | * 10 | * This software is provided 'as-is', without any express 11 | * or implied warranty. In no event will the authors be 12 | * held liable for any damages arising from the use of 13 | * this software. 14 | * 15 | * Permission is granted to anyone to use this software 16 | * for any purpose, including commercial applications, 17 | * and to alter it and redistribute it freely, subject to 18 | * the following restrictions: 19 | * 20 | * 1. The origin of this software must not be 21 | * misrepresented; you must not claim that you 22 | * wrote the original software. If you use this 23 | * software in a product, an acknowledgment in 24 | * the product documentation would be appreciated 25 | * but is not required. 26 | * 27 | * 2. Altered source versions must be plainly marked 28 | * as such, and must not be misrepresented as 29 | * being the original software. 30 | * 31 | * 3. This notice may not be removed or altered from 32 | * any source distribution. 33 | */ 34 | 35 | #ifndef UZLIB_H_INCLUDED 36 | #define UZLIB_H_INCLUDED 37 | 38 | #include 39 | #include 40 | 41 | #include "defl_static.h" 42 | 43 | #include "uzlib_conf.h" 44 | #if UZLIB_CONF_DEBUG_LOG 45 | #include 46 | #endif 47 | 48 | /* calling convention */ 49 | #ifndef TINFCC 50 | #ifdef __WATCOMC__ 51 | #define TINFCC __cdecl 52 | #else 53 | #define TINFCC 54 | #endif 55 | #endif 56 | 57 | #ifdef __cplusplus 58 | extern "C" { 59 | #endif 60 | 61 | /* ok status, more data produced */ 62 | #define TINF_OK 0 63 | /* end of compressed stream reached */ 64 | #define TINF_DONE 1 65 | #define TINF_DATA_ERROR (-3) 66 | #define TINF_CHKSUM_ERROR (-4) 67 | #define TINF_DICT_ERROR (-5) 68 | 69 | /* checksum types */ 70 | #define TINF_CHKSUM_NONE 0 71 | #define TINF_CHKSUM_ADLER 1 72 | #define TINF_CHKSUM_CRC 2 73 | 74 | /* helper macros */ 75 | #define TINF_ARRAY_SIZE(arr) (sizeof(arr) / sizeof(*(arr))) 76 | 77 | /* data structures */ 78 | 79 | typedef struct { 80 | unsigned short table[16]; /* table of code length counts */ 81 | unsigned short trans[288]; /* code -> symbol translation table */ 82 | } TINF_TREE; 83 | 84 | struct uzlib_uncomp { 85 | /* Pointer to the next byte in the input buffer */ 86 | const unsigned char *source; 87 | /* Pointer to the next byte past the input buffer (source_limit = source + len) */ 88 | const unsigned char *source_limit; 89 | /* If source_limit == NULL, or source >= source_limit, this function 90 | will be used to read next byte from source stream. The function may 91 | also return -1 in case of EOF (or irrecoverable error). Note that 92 | besides returning the next byte, it may also update source and 93 | source_limit fields, thus allowing for buffered operation. */ 94 | int (*source_read_cb)(struct uzlib_uncomp *uncomp); 95 | 96 | unsigned int tag; 97 | unsigned int bitcount; 98 | 99 | /* Destination (output) buffer start */ 100 | unsigned char *dest_start; 101 | /* Current pointer in dest buffer */ 102 | unsigned char *dest; 103 | /* Pointer past the end of the dest buffer, similar to source_limit */ 104 | unsigned char *dest_limit; 105 | 106 | /* Accumulating checksum */ 107 | unsigned int checksum; 108 | char checksum_type; 109 | bool eof; 110 | 111 | int btype; 112 | int bfinal; 113 | unsigned int curlen; 114 | int lzOff; 115 | unsigned char *dict_ring; 116 | unsigned int dict_size; 117 | unsigned int dict_idx; 118 | 119 | TINF_TREE ltree; /* dynamic length/symbol tree */ 120 | TINF_TREE dtree; /* dynamic distance tree */ 121 | }; 122 | 123 | #include "tinf_compat.h" 124 | 125 | #define TINF_PUT(d, c) \ 126 | { \ 127 | *d->dest++ = c; \ 128 | if (d->dict_ring) { d->dict_ring[d->dict_idx++] = c; if (d->dict_idx == d->dict_size) d->dict_idx = 0; } \ 129 | } 130 | 131 | unsigned char TINFCC uzlib_get_byte(TINF_DATA *d); 132 | 133 | /* Decompression API */ 134 | 135 | void TINFCC uzlib_init(void); 136 | void TINFCC uzlib_uncompress_init(TINF_DATA *d, void *dict, unsigned int dictLen); 137 | int TINFCC uzlib_uncompress(TINF_DATA *d); 138 | int TINFCC uzlib_uncompress_chksum(TINF_DATA *d); 139 | 140 | int TINFCC uzlib_zlib_parse_header(TINF_DATA *d); 141 | int TINFCC uzlib_gzip_parse_header(TINF_DATA *d); 142 | 143 | /* Compression API */ 144 | 145 | typedef const uint8_t *uzlib_hash_entry_t; 146 | 147 | struct uzlib_comp { 148 | struct Outbuf out; 149 | 150 | uzlib_hash_entry_t *hash_table; 151 | unsigned int hash_bits; 152 | unsigned int dict_size; 153 | }; 154 | 155 | void TINFCC uzlib_compress(struct uzlib_comp *c, const uint8_t *src, unsigned slen); 156 | 157 | /* Checksum API */ 158 | 159 | /* prev_sum is previous value for incremental computation, 1 initially */ 160 | uint32_t TINFCC uzlib_adler32(const void *data, unsigned int length, uint32_t prev_sum); 161 | /* crc is previous value for incremental computation, 0xffffffff initially */ 162 | uint32_t TINFCC uzlib_crc32(const void *data, unsigned int length, uint32_t crc); 163 | 164 | #ifdef __cplusplus 165 | } /* extern "C" */ 166 | #endif 167 | 168 | #endif /* UZLIB_H_INCLUDED */ 169 | -------------------------------------------------------------------------------- /gzip/uzlib_conf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * uzlib - tiny deflate/inflate library (deflate, gzip, zlib) 3 | * 4 | * Copyright (c) 2014-2018 by Paul Sokolovsky 5 | */ 6 | 7 | #ifndef UZLIB_CONF_H_INCLUDED 8 | #define UZLIB_CONF_H_INCLUDED 9 | 10 | #ifndef UZLIB_CONF_DEBUG_LOG 11 | /* Debug logging level 0, 1, 2, etc. */ 12 | #define UZLIB_CONF_DEBUG_LOG 0 13 | #endif 14 | 15 | #ifndef UZLIB_CONF_PARANOID_CHECKS 16 | /* Perform extra checks on the input stream, even if they aren't proven 17 | to be strictly required (== lack of them wasn't proven to lead to 18 | crashes). */ 19 | #define UZLIB_CONF_PARANOID_CHECKS 0 20 | #endif 21 | 22 | #ifndef UZLIB_CONF_USE_MEMCPY 23 | /* Use memcpy() for copying data out of LZ window or uncompressed blocks, 24 | instead of doing this byte by byte. For well-compressed data, this 25 | may noticeably increase decompression speed. But for less compressed, 26 | it can actually deteriorate it (due to the fact that many memcpy() 27 | implementations are optimized for large blocks of data, and have 28 | too much overhead for short strings of just a few bytes). */ 29 | #define UZLIB_CONF_USE_MEMCPY 0 30 | #endif 31 | 32 | #endif /* UZLIB_CONF_H_INCLUDED */ 33 | -------------------------------------------------------------------------------- /memory.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _MEMORY_H 3 | #define _MEMORY_H 4 | 5 | #include 6 | 7 | // Aligns size to 8 bytes 8 | #define ALIGN_8(size) (((size) + 0x7) & ~0x7) 9 | 10 | extern struct HeapSegment* gFirstHeapSegment; 11 | 12 | #define MALLOC_FREE_BLOCK 0xFEEE 13 | #define MALLOC_USED_BLOCK 0xEEEF 14 | 15 | #define MALLOC_BLOCK_HEAD 0xEEAD0000 16 | #define MALLOC_BLOCK_FOOT 0xF0000000 17 | 18 | struct HeapUsedSegment 19 | { 20 | unsigned int header; 21 | void* segmentEnd; 22 | }; 23 | 24 | struct HeapSegment 25 | { 26 | unsigned int header; 27 | void* segmentEnd; 28 | struct HeapSegment* nextSegment; 29 | struct HeapSegment* prevSegment; 30 | }; 31 | 32 | struct HeapSegmentFooter 33 | { 34 | struct HeapSegment* header; 35 | unsigned int footer; 36 | }; 37 | 38 | #define MIN_HEAP_BLOCK_SIZE (sizeof(struct HeapSegment) + sizeof(struct HeapSegmentFooter)) 39 | 40 | void initHeap(void* heapEnd); 41 | void *cacheFreePointer(void* target); 42 | void *malloc(unsigned int size); 43 | void *realloc(void* target, unsigned int size); 44 | void free(void* target); 45 | int calculateBytesFree(); 46 | int calculateLargestFreeChunk(); 47 | extern void zeroMemory(void* memory, int size); 48 | extern void memCopy(void* target, const void* src, int size); 49 | 50 | #endif -------------------------------------------------------------------------------- /memory_test.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef DEBUG 4 | #define DEBUG 1 5 | #endif 6 | 7 | #include "src/assert.h" 8 | #include "memory.h" 9 | 10 | extern struct HeapSegment* gFirstFreeSegment; 11 | 12 | void test_malloc() 13 | { 14 | struct HeapSegment* startSegAddr = gFirstFreeSegment; 15 | struct HeapSegment startSeg = *gFirstFreeSegment; 16 | 17 | void* a = malloc(16); 18 | void* b = malloc(16); 19 | void* c = malloc(16); 20 | 21 | free(b); 22 | c = realloc(c, 32); 23 | 24 | free(a); 25 | free(b); 26 | free(c); 27 | 28 | b = malloc(16); 29 | 30 | teqassert(a == b); 31 | free(b); 32 | 33 | teqassert(startSegAddr == gFirstFreeSegment); 34 | teqassert(startSeg.segmentEnd == gFirstFreeSegment->segmentEnd); 35 | } -------------------------------------------------------------------------------- /render.c: -------------------------------------------------------------------------------- 1 | #include "render.h" 2 | #include "static.h" 3 | #include "src/debug_out.h" 4 | #include "memory.h" 5 | #include "src/assert.h" 6 | #include "tex/textures.h" 7 | #include "src/sprite.h" 8 | #include "src/spritefont.h" 9 | 10 | /* 11 | * Task header. 12 | */ 13 | OSTask taskHeader = 14 | { 15 | M_GFXTASK, /* type of task */ 16 | OS_TASK_DP_WAIT, /* flags - wait for DP to be available */ 17 | NULL, /* ucode boot (fill in later) */ 18 | 0, /* ucode boot size (fill in later) */ 19 | NULL, /* ucode (fill in later) */ 20 | SP_UCODE_SIZE, /* ucode size */ 21 | NULL, /* ucode data (fill in later) (to init DMEM) */ 22 | SP_UCODE_DATA_SIZE, /* ucode data size */ 23 | &dram_stack[0], /* stack used by ucode */ 24 | SP_DRAM_STACK_SIZE8, /* size of stack */ 25 | &rdp_output[0], /* fifo output buffer start */ 26 | &rdp_output[0]+RDP_OUTPUT_LEN, /* fifo output buffer end */ 27 | NULL, /* display list pointer (fill in later) */ 28 | 0, /* display list size (ignored) */ 29 | NULL, /* yield buffer (used if yield will occur) */ 30 | 0 /* yield buffer length */ 31 | }; 32 | 33 | /* 34 | * global variables 35 | */ 36 | Dynamic dynamic; /* dynamic data */ 37 | int draw_buffer=0; /* frame buffer being updated (0 or 1) */ 38 | int fontcol[4]; /* color for shadowed fonts */ 39 | u16* cfb; 40 | u8 isBlack = 1; 41 | 42 | void renderDebugLog() 43 | { 44 | int x, y; 45 | x = 18; 46 | y = 18; 47 | 48 | spriteSetColor(gGBFont.spriteLayer, 55, 255, 155, 255); 49 | 50 | char *cstring = getDebugString(); 51 | 52 | renderText(&gGBFont, cstring, 18, 18, 0); 53 | } 54 | 55 | void renderFrame(int clear) 56 | { 57 | OSTask* theadp = &taskHeader; 58 | Dynamic* dynamicp = &dynamic; 59 | Gfx* glistp = dynamicp->glist; 60 | 61 | /* 62 | * Tell RCP where each segment is 63 | */ 64 | gSPSegment(glistp++, 0, 0x0); /* physical segment */ 65 | 66 | gDPSetColorImage(glistp++, G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WD, OS_K0_TO_PHYSICAL(getColorBuffer())); 67 | 68 | if (clear) 69 | { 70 | gDPSetFillColor(glistp++, GPACK_RGBA5551(1, 1, 1, 1) << 16 | GPACK_RGBA5551(1, 1, 1, 1)); 71 | gDPSetCycleType(glistp++, G_CYC_FILL); 72 | gDPFillRectangle(glistp++, 0, 0, SCREEN_WD-1, SCREEN_HT-1); 73 | } 74 | 75 | gDPSetScissor(glistp++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WD, SCREEN_HT); 76 | 77 | /* 78 | * Initialize RCP state. 79 | */ 80 | gSPDisplayList(glistp++, init_dl); 81 | 82 | gDPPipeSync(glistp++); 83 | gDPSetCycleType(glistp++, G_CYC_1CYCLE); 84 | 85 | renderDebugLog(); 86 | 87 | finishSprites(&glistp); 88 | 89 | gDPFullSync(glistp++); 90 | gSPEndDisplayList(glistp++); 91 | 92 | teqassert(glistp <= dynamicp->glist + GLIST_LEN); 93 | 94 | theadp->t.ucode_boot = (u64 *) rspbootTextStart; 95 | theadp->t.ucode_boot_size = 96 | (u32) rspbootTextEnd - (u32) rspbootTextStart; 97 | 98 | theadp->t.ucode = (u64 *) gspF3DEX2_fifoTextStart; 99 | theadp->t.ucode_data = (u64 *) gspF3DEX2_fifoDataStart; 100 | 101 | theadp->t.data_ptr = (u64 *) dynamicp->glist; 102 | theadp->t.data_size = 103 | (u32) ((glistp - dynamicp->glist) * sizeof(Gfx)); 104 | 105 | osWritebackDCache(&dynamic, sizeof(dynamic)); 106 | osSpTaskStart(theadp); 107 | (void)osRecvMesg(&rdpMessageQ, NULL, OS_MESG_BLOCK); 108 | 109 | if (isBlack) 110 | { 111 | isBlack = 0; 112 | osViBlack(0); 113 | } 114 | 115 | osViSwapBuffer(getColorBuffer()); 116 | while (!MQ_IS_EMPTY(&retraceMessageQ)) 117 | { 118 | (void) osRecvMesg(&retraceMessageQ, NULL, OS_MESG_NOBLOCK); 119 | } 120 | (void) osRecvMesg(&retraceMessageQ, NULL, OS_MESG_BLOCK); 121 | 122 | draw_buffer = (draw_buffer + 1) % BUFFER_COUNT; 123 | } 124 | 125 | u16* getColorBuffer() 126 | { 127 | return cfb + draw_buffer * (SCREEN_WD * SCREEN_HT); 128 | } 129 | 130 | void* initColorBuffers(void* memoryEnd) 131 | { 132 | cfb = (u16*)memoryEnd - BUFFER_COUNT * SCREEN_WD * SCREEN_HT; 133 | zeroMemory(cfb, sizeof(u16) * BUFFER_COUNT * SCREEN_WD * SCREEN_HT); 134 | return cfb; 135 | } -------------------------------------------------------------------------------- /render.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _RENDER_H 3 | #define _RENDER_H 4 | 5 | #include 6 | #include "boot.h" 7 | 8 | /* 9 | * Message queues 10 | */ 11 | extern OSMesgQueue 12 | rdpMessageQ, 13 | retraceMessageQ; 14 | 15 | /* 16 | * global variables 17 | */ 18 | extern int rdp_flag; 19 | 20 | /* 21 | * frame buffer symbols 22 | */ 23 | extern u16* cfb; /* RAM address */ 24 | /* 25 | * buffers for RSP tasks: 26 | * buffers used by fifo microcode only 27 | */ 28 | extern u64 dram_stack[SP_DRAM_STACK_SIZE64]; /* used for matrix stack */ 29 | extern u64 rdp_output[RDP_OUTPUT_LEN]; /* buffer for RDP DL */ 30 | 31 | extern int fontcol[4]; /* color for shadowed fonts */ 32 | 33 | #define PACK16BIT(r, g, b) ((u16)(r) << 8 & 0xF800 | (u16)(g) << 3 & 0x07C0 | (u16)(b) >> 2 & 0x003E | 0x01) 34 | #define COL24TO16(rgb24) ((rgb24) >> 8 & 0xF800 | (rgb24) >> 5 & 0x07C0 | (rgb24) >> 2 & 0x003E | 0x01) 35 | #define GET_R(rgb16) ((rgb16) >> 8 & 0xF8) 36 | #define GET_G(rgb16) ((rgb16) >> 3 & 0xF8) 37 | #define GET_B(rgb16) ((rgb16) << 2 & 0xF8) 38 | 39 | #define GLIST_LEN 64 40 | 41 | #define BUFFER_COUNT 2 42 | 43 | /* 44 | * Layout of dynamic data. 45 | * 46 | * This structure holds the things which change per frame. It is advantageous 47 | * to keep dynamic data together so that we may selectively write back dirty 48 | * data cache lines to DRAM prior to processing by the RCP. 49 | * 50 | */ 51 | typedef struct { 52 | Gfx glist[GLIST_LEN]; 53 | } Dynamic; 54 | 55 | extern Dynamic dynamic; 56 | 57 | void renderFrame(int clear); 58 | u16* getColorBuffer(); 59 | void* initColorBuffers(void* memoryEnd); 60 | 61 | #endif -------------------------------------------------------------------------------- /romwrapper/GB64Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambertjamesd/gb64/a5ce8da11a3015926d8cacde4757b1e1af801821/romwrapper/GB64Logo.png -------------------------------------------------------------------------------- /romwrapper/gb.n64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambertjamesd/gb64/a5ce8da11a3015926d8cacde4757b1e1af801821/romwrapper/gb.n64 -------------------------------------------------------------------------------- /rsp/alias.s: -------------------------------------------------------------------------------- 1 | 2 | #define li(target, value) ori target, $0, (value) -------------------------------------------------------------------------------- /rsp/data.s: -------------------------------------------------------------------------------- 1 | .align 16 2 | lsbBitMultiply: 3 | .half 0x0080 4 | .half 0x0100 5 | .half 0x0200 6 | .half 0x0400 7 | .half 0x0800 8 | .half 0x1000 9 | .half 0x2000 10 | .half 0x4000 11 | 12 | .align 16 13 | msbBitMultiply: 14 | .half 0x0004 15 | .half 0x0008 16 | .half 0x0010 17 | .half 0x0020 18 | .half 0x0040 19 | .half 0x0080 20 | .half 0x0100 21 | .half 0x0200 22 | 23 | .align 16 24 | lsbBitMultiplyFlipX: 25 | .half 0x4000 26 | .half 0x2000 27 | .half 0x1000 28 | .half 0x0800 29 | .half 0x0400 30 | .half 0x0200 31 | .half 0x0100 32 | .half 0x0080 33 | 34 | .align 16 35 | msbBitMultiplyFlipX: 36 | .half 0x0200 37 | .half 0x0100 38 | .half 0x0080 39 | .half 0x0040 40 | .half 0x0020 41 | .half 0x0010 42 | .half 0x0008 43 | .half 0x0004 44 | 45 | .align 2 46 | lsbBitMask: 47 | .half 0x40 48 | 49 | .align 2 50 | msbBitMask: 51 | .half 0x200 52 | 53 | .align 16 54 | ppuTask: 55 | .space PPUTask_sizeof 56 | 57 | .align 16 58 | .space 8 # instead of clipping pixels, extra memeory is used that can be overwitten without issue 59 | scanline: 60 | .space GB_SCREEN_WD 61 | .space 8 # instead of clipping pixels, extra memeory is used that can be overwitten without issue 62 | overscanBuffer: 63 | .space 8 # used to restore pixels written over by doing copies in blocks 64 | .align 16 65 | tilemap: 66 | .space GB_TILEMAP_W 67 | tilemapAttrs: 68 | .space GB_TILEMAP_W 69 | 70 | .align 16 71 | window: 72 | .space GB_TILEMAP_W 73 | windowAttrs: 74 | .space GB_TILEMAP_W 75 | 76 | tileAttrCache: 77 | .space GB_MAX_VISIBLE_TILES 78 | 79 | .align 16 80 | tilemapTileCache: 81 | .space GB_TILE_SIZE * (GB_MAX_VISIBLE_TILES + GB_MAX_VISIBLE_SPRITES) 82 | tilemapTileCacheInfo: 83 | .half -1 84 | .half -1 85 | .half -1 86 | .half -1 87 | .half -1 88 | 89 | .half -1 90 | .half -1 91 | .half -1 92 | .half -1 93 | .half -1 94 | 95 | .half -1 96 | .half -1 97 | .half -1 98 | .half -1 99 | .half -1 100 | 101 | .half -1 102 | .half -1 103 | .half -1 104 | .half -1 105 | .half -1 106 | 107 | .half -1 108 | .half -1 109 | .half -1 110 | .half -1 111 | .half -1 112 | 113 | .half -1 114 | .half -1 115 | .half -1 116 | .half -1 117 | .half -1 118 | 119 | .half -1 120 | .half -1 121 | 122 | currentTile: 123 | .half tilemapTileCache 124 | currentTileAttr: 125 | .half tileAttrCache 126 | currentWindowY: 127 | .half 0 128 | 129 | .align 16 130 | sprites: 131 | .space SPRITE_SIZE * SPRITE_MAX_COUNT 132 | 133 | #define STACK_SIZE 64 134 | stackEnd: 135 | .space STACK_SIZE 136 | 137 | startShiftXFlip: 138 | .byte 7 139 | .byte 0 140 | startDirXFlip: 141 | .byte -1 142 | .byte 1 143 | 144 | .align 8 145 | cyclesWaitingForMode2: 146 | .word 0 147 | cyclesWaitingForMode3: 148 | .word 0 -------------------------------------------------------------------------------- /rsp/dma.s: -------------------------------------------------------------------------------- 1 | 2 | 3 | ############################################### 4 | # Main loop algorithm 5 | # 1 setup initial state 6 | # 2 wait for MODE 3 on the CPU to access VRAM 7 | # 3 DMA Needed memory to DEMEM to render a single line 8 | # 4 signal to CPU that VRAM has been read so it can 9 | # continue to MODE 0 10 | # 5 Render single line 11 | # 6 If done with screen, break 12 | # 7 Setup state for another scanline 13 | # 8 goto step 2 14 | 15 | ############################################### 16 | # Procedure to do DMA reads/writes. 17 | # Registers: 18 | # a0 mem_addr 19 | # a1 dram_addr 20 | # a2 dma_len 21 | # a3 iswrite? 22 | DMAproc: # request DMA access: (get semaphore) 23 | mfc0 $at, SP_RESERVED 24 | bne $at, zero, DMAproc 25 | # note delay slot 26 | DMAFull: # wait for not FULL: 27 | mfc0 $at, DMA_FULL 28 | bne $at, zero, DMAFull 29 | nop 30 | # set DMA registers: 31 | mtc0 a0, DMA_CACHE 32 | # handle writes: 33 | bgtz a3, DMAWrite 34 | mtc0 a1, DMA_DRAM 35 | j DMADone 36 | mtc0 a2, DMA_READ_LENGTH 37 | DMAWrite: 38 | mtc0 a2, DMA_WRITE_LENGTH 39 | DMADone: 40 | jr return 41 | # clear semaphore, delay slot 42 | mtc0 zero, SP_RESERVED 43 | 44 | ############################################### 45 | # Procedure to wait for DMA to finish 46 | DMAWait: 47 | mfc0 $at, SP_RESERVED 48 | bne $at, zero, DMAWait 49 | WaitSpin: 50 | mfc0 $at, DMA_BUSY 51 | bne $at, zero, WaitSpin 52 | nop 53 | jr $ra 54 | mtc0 zero, SP_RESERVED 55 | 56 | ############################################### 57 | # Write scanline 58 | # 59 | 60 | writeScanline: 61 | # current the current y position 62 | lbu a0, (ppuTask + PPUTask_ly)(zero) 63 | # need to calculte ly * 160, since there is no 64 | # multiply instruction ly * 128 + ly * 32 is done 65 | # as it can be done with bit shifts and adds 66 | sll a1, a0, 7 67 | sll a0, a0, 5 68 | add a1, a1, a0 69 | 70 | # get the output buffer 71 | lw $at, (ppuTask + PPUTask_output)(zero) 72 | # offset it by ly * 160 73 | add a1, a1, $at 74 | # load the source DMEM 75 | ori a0, zero, scanline 76 | ori a2, zero, GB_SCREEN_WD-1 # dma length 77 | j DMAproc # have DMAProc jump back to $ra 78 | ori a3, zero, 1 # is write -------------------------------------------------------------------------------- /rsp/gameboy.s: -------------------------------------------------------------------------------- 1 | 2 | #define GB_SCREEN_WD 160 3 | #define GB_SCREEN_HT 144 4 | 5 | #define GB_TILEMAP_W 32 6 | 7 | #define GB_TILE_SIZE 16 8 | #define GB_TILE_WIDTH 8 9 | 10 | #define GB_MAX_VISIBLE_TILES 22 11 | 12 | #define GB_MAX_VISIBLE_SPRITES 10 13 | 14 | #define TILE_ATTR_PALETTE 0x07 15 | #define TILE_ATTR_VRAM_BANK 0x08 16 | #define TILE_ATTR_H_FLIP 0x20 17 | #define TILE_ATTR_V_FLIP 0x40 18 | #define TILE_ATTR_PRIORITY 0x80 19 | 20 | #define WINDOW_X_OFFSET 0x07 21 | 22 | #define SPRITE_SIZE 0x4 23 | #define SPRITE_MAX_COUNT 40 24 | #define SPRITE_MAX_PER_LINE 10 25 | 26 | #define SPRITE_Y 0x0 27 | #define SPRITE_X 0x1 28 | #define SPRITE_TILE 0x2 29 | #define SPRITE_FLAGS 0x3 30 | 31 | #define SPRITE_OFFSCREEN_X 168 32 | 33 | #define SPRITE_SHIFT_Y -16 34 | #define SPRITE_SHIFT_X -8 35 | 36 | #define SPRITE_FLAGS_PRIO 0x80 37 | #define SPRITE_FLAGS_FLIP_Y 0x40 38 | #define SPRITE_FLAGS_FLIP_X 0x20 39 | #define SPRITE_FLAGS_DMA_PALETTE 0x10 40 | #define SPRITE_FLAGS_VRAM_BANK 0x08 41 | #define SPRITE_FLAGS_GBC_PALETTE 0x07 42 | 43 | #define OBJ_PALETTE_INDEX_START 32 44 | 45 | #define MODE_3_FLAG 0x080 46 | #define MODE_2_FLAG 0x100 47 | #define MODE_3_FLAG_CLR 0x0200 48 | #define MODE_2_FLAG_CLR 0x0800 49 | 50 | #define EXIT_EARLY_FLAG 0x200 -------------------------------------------------------------------------------- /rsp/ostask.s: -------------------------------------------------------------------------------- 1 | 2 | .data 3 | 4 | # u32 5 | #define osTask_type 0xFC0 6 | # u32 7 | #define osTask_flags 0xFC4 8 | # u64* 9 | #define osTask_ucode_boot 0xFC8 10 | # u32 11 | #define osTask_ucode_boot_size 0xFCC 12 | # u64* 13 | #define osTask_ucode 0xFD0 14 | # u32 15 | #define osTask_ucode_size 0xFD4 16 | # u64* 17 | #define osTask_ucode_data 0xFD8 18 | # u32 19 | #define osTask_ucode_data_size 0xFDC 20 | # u64* 21 | #define osTask_dram_stack 0xFE0 22 | # u32 23 | #define osTask_dram_stack_size 0xFE4 24 | # u64* 25 | #define osTask_output_buff 0xFE8 26 | # u64* 27 | #define osTask_output_buff_size 0xFEC 28 | # u64* 29 | #define osTask_data_ptr 0xFF0 30 | # u32 31 | #define osTask_data_ptr_size 0xFF4 32 | # u64* 33 | #define osTask_yield_data_ptr 0xFF8 34 | # u32 35 | #define osTask_yield_data_size 0xFFC -------------------------------------------------------------------------------- /rsp/ppu.s: -------------------------------------------------------------------------------- 1 | #include "./alias.s" 2 | #include "./ostask.s" 3 | #include "./registers.s" 4 | #include "./pputask.s" 5 | #include "./gameboy.s" 6 | 7 | #include "../src/memory_map_offsets.h" 8 | #include "../src/rspppu_includes.h" 9 | 10 | .data 11 | #include "data.s" 12 | .dmax 4032 13 | 14 | .text 0x0 15 | #include "main.s" 16 | #include "dma.s" 17 | #include "tilemap.s" 18 | #include "sprite.s" 19 | .dmax 4096 -------------------------------------------------------------------------------- /rsp/pputask.s: -------------------------------------------------------------------------------- 1 | 2 | #define PPUTask_output 0 3 | #define PPUTask_memorySource 4 4 | #define PPUTask_graphicsSource 8 5 | #define PPUTask_flags 12 6 | #define PPUTask_lcdc 14 7 | 8 | #define LCDC_BG_ENABLE 0x01 9 | #define LCDC_OBJ_ENABLE 0x02 10 | #define LCDC_OBJ_SIZE 0x04 11 | #define LCDC_BG_TILE_MAP 0x08 12 | #define LCDC_BG_TILE_DATA 0x10 13 | #define LCDC_WIN_E 0x20 14 | #define LCDC_WIN_TILE_MAP 0x40 15 | #define LCDC_LCD_E 0x80 16 | 17 | #define GraphicsMemory_tilemap0 0x1800 18 | #define GraphicsMemory_tilemapOffset 0x2000 19 | 20 | #define PPUTask_ly 15 21 | #define PPUTask_scy 16 22 | #define PPUTask_scx 17 23 | #define PPUTask_wy 18 24 | #define PPUTask_wx 19 25 | #define PPUTask_performance 20 26 | 27 | #define PPUTask_sizeof 24 -------------------------------------------------------------------------------- /rsp/registers.s: -------------------------------------------------------------------------------- 1 | .name DMA_CACHE, $c0 2 | .name DMA_DRAM, $c1 3 | .name DMA_READ_LENGTH, $c2 4 | .name DMA_WRITE_LENGTH, $c3 5 | .name SP_STATUS, $c4 6 | .name DMA_FULL, $c5 7 | .name DMA_BUSY, $c6 8 | .name SP_RESERVED, $c7 9 | .name CMD_START, $c8 10 | .name CMD_END, $c9 11 | .name CMD_CURRENT, $c10 12 | .name CMD_STATUS, $c11 13 | .name CMD_CLOCK, $c12 14 | .name CMD_BUSY, $c13 15 | .name CMD_PIPE_BUSY, $c14 16 | .name CMD_TMEM_BUSY, $c15 17 | 18 | .name zero, $0 19 | .name return, $31 20 | 21 | .name v0, $2 22 | .name v1, $3 23 | 24 | .name a0, $4 25 | .name a1, $5 26 | .name a2, $6 27 | .name a3, $7 28 | 29 | .name t0, $8 30 | .name t1, $9 31 | .name t2, $10 32 | .name t3, $11 33 | .name t4, $12 34 | .name t5, $13 35 | .name t6, $14 36 | .name t7, $15 37 | 38 | .name s0, $16 39 | .name s1, $17 40 | .name s2, $18 41 | .name s3, $19 42 | .name s4, $20 43 | .name s5, $21 44 | .name s6, $22 45 | .name s7, $23 46 | 47 | .name t8, $24 48 | .name t9, $25 49 | 50 | .name k0, $26 51 | .name k1, $27 52 | 53 | .name gp, $28 -------------------------------------------------------------------------------- /spec: -------------------------------------------------------------------------------- 1 | #include "boot.h" 2 | 3 | #define USE_PLACEHOLDER 1 4 | 5 | /* 6 | * ROM spec file 7 | */ 8 | beginseg 9 | name "code" 10 | flags BOOT OBJECT 11 | entry boot 12 | stack bootStack + STACKSIZE 13 | include "codesegment.o" 14 | include "$(ROOT)/usr/lib/PR/rspboot.o" 15 | include "$(ROOT)/usr/lib/PR/gspF3DEX2.fifo.o" 16 | include "bin/rsp/ppu.o" 17 | //include "debugger/dump_rsp_state.o" 18 | endseg 19 | 20 | beginseg 21 | name "static" 22 | flags OBJECT 23 | number STATIC_SEGMENT 24 | include "gfxinit.o" 25 | endseg 26 | 27 | #if USE_PLACEHOLDER 28 | beginseg 29 | name "dmg_boot" 30 | flags RAW 31 | include "data/dmg_boot_placeholder.bin" 32 | endseg 33 | 34 | beginseg 35 | name "cgb_bios" 36 | flags RAW 37 | include "data/cgb_bios_placeholder.bin" 38 | endseg 39 | 40 | beginseg 41 | name "gbrom" 42 | flags RAW 43 | include "data/rom_placeholder.gb" 44 | endseg 45 | 46 | #else // USE_PLACEHOLDER 47 | 48 | beginseg 49 | name "dmg_boot" 50 | flags RAW 51 | include "data/dmg_boot_placeholder.bin" 52 | endseg 53 | 54 | beginseg 55 | name "cgb_bios" 56 | flags RAW 57 | include "data/cgb_bios_placeholder.bin" 58 | endseg 59 | 60 | beginseg 61 | name "gbrom" 62 | flags RAW 63 | include "data/PokemonBlue.gb" 64 | endseg 65 | 66 | #endif // USE_PLACEHOLDER 67 | 68 | beginwave 69 | name "game" 70 | include "code" 71 | include "static" 72 | include "dmg_boot" 73 | include "cgb_bios" 74 | include "gbrom" 75 | endwave -------------------------------------------------------------------------------- /src/assert.c: -------------------------------------------------------------------------------- 1 | 2 | #include "assert.h" 3 | 4 | #if DEBUG 5 | 6 | asm( 7 | ".global teqassert\n" 8 | ".balign 4\n" 9 | "teqassert:\n" 10 | "teq $a0, $0\n" 11 | "jr $ra\n" 12 | "nop\n" 13 | ); 14 | 15 | #endif -------------------------------------------------------------------------------- /src/assert.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _ASSERT_H 3 | #define _ASSERT_H 4 | 5 | #if NDEBUG 6 | #define teqassert(assertion) 7 | #else 8 | void teqassert(int assertion); 9 | #endif 10 | 11 | #endif -------------------------------------------------------------------------------- /src/audio.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _AUDIO_H 3 | #define _AUDIO_H 4 | #include 5 | 6 | #define AUDIO_BUFFER_COUNT 4 7 | #define APU_TICKS_PER_SEC 256 8 | #define APU_TICKS_PER_SEC_L2 8 9 | #define CYCLES_PER_TICK 4096 10 | #define CYCLES_PER_TICK_L2 12 11 | 12 | #define RESTART_BIT 0x80 13 | 14 | struct Memory; 15 | 16 | #define GET_WAVE_DUTY(length_pattern) (((length_pattern) & 0xC0) >> 6) 17 | #define GET_SOUND_LENGTH(length_pattern) (64 - ((length_pattern) & 0x3F)) 18 | 19 | #define GET_SWEEP_TIME(sweep) (((sweep) >> 4) & 0x7) 20 | #define GET_SWEEP_DIR(sweep) (((sweep) >> 3) & 0x1) 21 | #define GET_SWEEP_SHIFT(sweep) ((sweep) & 0x7) 22 | 23 | #define GET_ENVELOPE_VOLUME(volume) (((volume) >> 4) & 0xF) 24 | #define GET_ENVELOPE_DIR(volume) (((volume) >> 3) & 0x1) 25 | #define GET_ENVELOPE_STEP_DURATION(volume) ((volume) & 0x7) 26 | #define GET_SOUND_FREQ(frequencyHi, frequencyLo) ((((int)(frequencyHi) & 0x7) << 8) | (int)(frequencyLo)) 27 | #define GET_SOUND_LENGTH_LIMITED(frequencyHi) (((frequencyHi) & 0x40) >> 6) 28 | #define GET_PCM_VOLUME(volume) (((volume) >> 5) & 0x3) 29 | 30 | #define SOUND_LENGTH_INDEFINITE ~0 31 | 32 | #define TICK_LENGTH(rvalue) if ((rvalue) != SOUND_LENGTH_INDEFINITE) --rvalue 33 | 34 | #define NOISE_MAX_CLOCK_SHIFT 13 35 | 36 | struct AudioSample 37 | { 38 | short l; 39 | short r; 40 | }; 41 | 42 | struct AudioSweep { 43 | u8 stepDir; 44 | u8 stepShift; 45 | u8 stepDuration; 46 | u8 stepTimer; 47 | }; 48 | 49 | struct AudioEnvelope { 50 | u8 volume; 51 | u8 step; 52 | u8 stepDuration; 53 | u8 stepTimer; 54 | }; 55 | 56 | struct SquareWaveSound { 57 | u16 cycle; 58 | u16 waveDuty; 59 | u16 frequency; 60 | struct AudioSweep sweep; 61 | struct AudioEnvelope envelope; 62 | u16 length; 63 | }; 64 | 65 | struct PCMSound { 66 | u16 cycle; 67 | u16 volume; 68 | u16 frequency; 69 | u16 length; 70 | }; 71 | 72 | enum LFSRWidth { 73 | LFSRWidth15, 74 | LFSRWidth7 75 | }; 76 | 77 | struct NoiseSound { 78 | struct AudioEnvelope envelope; 79 | u16 length; 80 | u16 lfsr; 81 | // fixed point numbers 8:24 82 | u32 accumulator; 83 | u32 sampleStep; 84 | enum LFSRWidth lfsrWidth; 85 | }; 86 | 87 | enum SoundIndex { 88 | SoundIndexSquare1, 89 | SoundIndexSquare2, 90 | SoundIndexPCM, 91 | SoundIndexNoise, 92 | }; 93 | 94 | struct AudioRenderState 95 | { 96 | struct SquareWaveSound sound1; 97 | struct SquareWaveSound sound2; 98 | struct PCMSound pcmSound; 99 | struct NoiseSound noiseSound; 100 | u32 cyclesEmulated; 101 | u32 nextTickCycle; 102 | }; 103 | 104 | struct AudioState 105 | { 106 | struct AudioSample* buffers[AUDIO_BUFFER_COUNT]; 107 | u16 sampleRate; 108 | u16 samplesPerBuffer; 109 | u16 currentWriteBuffer; 110 | u16 currentSampleIndex; 111 | u16 nextPlayBuffer; 112 | u16 freqLimit; 113 | int tickAdjustment; 114 | }; 115 | 116 | extern struct AudioState gAudioState; 117 | 118 | void initAudio(struct AudioState* audioState, int sampleRate, int frameRate); 119 | void tickAudio(struct Memory* memoryMap, int untilCyles); 120 | void finishAudioFrame(struct Memory* memoryMap); 121 | void updateOnOffRegister(struct Memory* memoryMap); 122 | u32 getAudioWriteHeadLead(struct AudioState* audioState); 123 | 124 | void adjustCyclesEmulator(struct AudioRenderState* audioState, u32 by); 125 | 126 | #endif -------------------------------------------------------------------------------- /src/bool.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _BOOL_H 3 | #define _BOOL_H 4 | 5 | typedef int bool; 6 | #define TRUE 1 7 | #define FALSE 0 8 | 9 | #endif -------------------------------------------------------------------------------- /src/clockmenu.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _CLOCK_MENU_H 3 | #define _CLOCK_MENU_H 4 | 5 | #include "menu.h" 6 | 7 | enum ClockMenuItem { 8 | ClockMenuItemDay, 9 | ClockMenuItemHour, 10 | ClockMenuItemMinute, 11 | ClockMenuItemSecond, 12 | ClockMenuItemCount, 13 | }; 14 | 15 | struct ClockCursorMenuItem { 16 | struct SelectCursorMenuItem cursorMenuItem; 17 | u8* registers; 18 | int registerIndex; 19 | }; 20 | 21 | struct ClockMenu { 22 | struct CursorMenu cursor; 23 | struct CursorMenuItem menuItems[ClockMenuItemCount]; 24 | struct ClockCursorMenuItem day; 25 | struct ClockCursorMenuItem hour; 26 | struct ClockCursorMenuItem minute; 27 | struct ClockCursorMenuItem second; 28 | u8 registers[8]; 29 | }; 30 | 31 | void initClockMenu(struct ClockMenu* menu, struct MenuItem* parent); 32 | void clockMenuRender(struct MenuItem* menuItem, struct MenuItem* highlightedItem); 33 | struct MenuItem* clockMenuHandleInput(struct MenuItem* menuItem, int buttonsDown, int buttonsState); 34 | void setClockMenuActive(struct MenuItem* menuItem, int isActive); 35 | 36 | #endif -------------------------------------------------------------------------------- /src/cpu.c: -------------------------------------------------------------------------------- 1 | #include "cpu.h" 2 | #include "debug_out.h" 3 | 4 | void initializeCPU(struct CPUState* state) 5 | { 6 | state->a = 0; state->f = 0; state->b = 0; state->c = 0; 7 | state->d = 0; state->e = 0; state->h = 0; state->l = 0; 8 | state->sp = 0; state->pc = 0; 9 | state->stopReason = STOP_REASON_NONE; 10 | state->interrupts = 0; 11 | state->gbc = 0; 12 | state->runUntilNextFrame = 0; 13 | state->cyclesRun = 0; 14 | state->nextTimerTrigger = ~0; 15 | state->nextScreenTrigger = ~0; 16 | state->nextInterruptTrigger = ~0; 17 | state->nextStoppingPoint = CPU_STOPPING_POINT_COUNT * sizeof(struct CPUStoppingPoint); 18 | } 19 | 20 | void addStoppingPoint(struct CPUState* state, struct CPUStoppingPoint stoppingPoint) 21 | { 22 | int index = state->nextStoppingPoint; 23 | 24 | while (index < CPU_STOPPING_POINT_COUNT * sizeof(struct CPUStoppingPoint)) 25 | { 26 | if (CPU_STOPPING_POINT_AS_LONG(stoppingPoint) < CPU_STOPPING_POINT_AS_LONG(state->stoppingPoints[index >> 2])) 27 | { 28 | break; 29 | } 30 | else 31 | { 32 | state->stoppingPoints[(index >> 2) - 1] = state->stoppingPoints[index >> 2]; 33 | index += sizeof(struct CPUStoppingPoint); 34 | } 35 | } 36 | 37 | state->stoppingPoints[(index >> 2) - 1] = stoppingPoint; 38 | state->nextStoppingPoint -= sizeof(struct CPUStoppingPoint); 39 | } 40 | 41 | void adjustCPUTimer(struct CPUState* state) 42 | { 43 | if (state->cyclesRun >= MAX_CYCLE_TIME) 44 | { 45 | state->cyclesRun -= MAX_CYCLE_TIME; 46 | 47 | if (state->nextTimerTrigger != ~0) 48 | { 49 | state->nextTimerTrigger -= MAX_CYCLE_TIME; 50 | } 51 | 52 | int index; 53 | for ( 54 | index = state->nextStoppingPoint; 55 | index < CPU_STOPPING_POINT_COUNT * sizeof(struct CPUStoppingPoint); 56 | index += sizeof(struct CPUStoppingPoint) 57 | ) 58 | { 59 | state->stoppingPoints[index >> 2].cycleTime -= MAX_CYCLE_TIME; 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /src/cpu.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _CPU_H 3 | #define _CPU_H 4 | 5 | #include "memory_map.h" 6 | 7 | enum STOP_REASON { 8 | STOP_REASON_NONE, 9 | STOP_REASON_STOP, 10 | STOP_REASON_HALT, 11 | STOP_REASON_INTERRUPT_RET, 12 | STOP_REASON_ERROR, 13 | }; 14 | 15 | enum GB_FLAGS { 16 | GB_FLAGS_Z = 0x80, 17 | GB_FLAGS_N = 0x40, 18 | GB_FLAGS_H = 0x20, 19 | GB_FLAGS_C = 0x10, 20 | }; 21 | 22 | enum GB_INTERRUPTS { 23 | GB_INTERRUPTS_V_BLANK = 0x01, 24 | GB_INTERRUPTS_LCDC = 0x02, 25 | GB_INTERRUPTS_TIMER = 0x04, 26 | GB_INTERRUPTS_SERIAL = 0x08, 27 | GB_INTERRUPTS_INPUT = 0x10, 28 | GB_INTERRUPTS_ENABLED = 0x80, 29 | }; 30 | 31 | enum CPUStoppingPointType { 32 | CPUStoppingPointTypeNone, 33 | CPUStoppingPointTypeScreenMode0, 34 | CPUStoppingPointTypeScreenMode1, 35 | CPUStoppingPointTypeScreenMode2, 36 | CPUStoppingPointTypeScreenMode3, 37 | CPUStoppingPointTypeTimerReset, 38 | CPUStoppingPointTypeInterrupt, 39 | CPUStoppingPointTypeExit, 40 | CPUStoppingPointTypeDMA, 41 | CPUStoppingPointTypeDebugger, 42 | }; 43 | 44 | struct CPUStoppingPoint { 45 | unsigned long cycleTime:24; 46 | unsigned long stoppingPointType:8; 47 | }; 48 | 49 | #define CPU_STOPPING_POINT_AS_LONG(value) *((unsigned long*)&value) 50 | 51 | #define CPU_STOPPING_POINT_COUNT 0x10 52 | #define MAX_CYCLE_TIME 0x00800000 53 | 54 | struct CPUState { 55 | unsigned char a; 56 | unsigned char f; 57 | unsigned char b; 58 | unsigned char c; 59 | unsigned char d; 60 | unsigned char e; 61 | unsigned char h; 62 | unsigned char l; 63 | unsigned short sp; 64 | unsigned short pc; 65 | unsigned char stopReason; 66 | unsigned char interrupts; 67 | unsigned char gbc; 68 | unsigned char runUntilNextFrame; 69 | unsigned long cyclesRun; 70 | unsigned long nextTimerTrigger; 71 | unsigned long nextScreenTrigger; 72 | unsigned long nextInterruptTrigger; 73 | // cpu timer uneffected by speed switching 74 | unsigned long unscaledCyclesRun; 75 | unsigned long nextStoppingPoint; 76 | struct CPUStoppingPoint stoppingPoints[CPU_STOPPING_POINT_COUNT]; 77 | }; 78 | 79 | #define RUN_CPU_FLAGS_RENDER 0x1 80 | 81 | extern int runCPU(struct CPUState* state, struct Memory* memory, int cyclesToRun, int flags); 82 | extern void initializeCPU(struct CPUState* state); 83 | extern void addStoppingPoint(struct CPUState* state, struct CPUStoppingPoint stoppingPoint); 84 | 85 | // We don't want CYCLES_RUN 86 | // to overflow while emulating the CPU 87 | // since CPU_STATE_NEXT_TIMER should always 88 | // be >= CYCLES_RUN 89 | // to prevent this, when exiting the 90 | // emulation we check if CYCLES_RUN is 91 | // above 0x80000000 if it is we 92 | // decrement both CYCLES_RUN and 93 | // by that much CPU_STATE_NEXT_TIMER 94 | extern void adjustCPUTimer(struct CPUState* state); 95 | 96 | #endif -------------------------------------------------------------------------------- /src/debug_out.c: -------------------------------------------------------------------------------- 1 | #include "debug_out.h" 2 | #include "sprite.h" 3 | #include "../render.h" 4 | 5 | #define DEBUG_BUFFER_SIZE 1024 6 | #define MAX_DEBUG_LINES 8 7 | 8 | static char debugBuffer[DEBUG_BUFFER_SIZE]; 9 | static int currentWritePosition = 0; 10 | static int currentSourcePosition = 0; 11 | static int visibleDebugLineCount; 12 | 13 | char debugTMP[100]; 14 | 15 | void wrapBuffer() 16 | { 17 | int targetIndex = 0; 18 | 19 | while (currentSourcePosition < currentWritePosition) 20 | { 21 | debugBuffer[targetIndex++] = debugBuffer[currentSourcePosition++]; 22 | } 23 | 24 | debugBuffer[targetIndex] = 0; 25 | currentSourcePosition = 0; 26 | currentWritePosition = targetIndex; 27 | } 28 | 29 | void removeOldestLine() 30 | { 31 | while (currentSourcePosition < currentWritePosition) 32 | { 33 | if (debugBuffer[currentSourcePosition] == '\n') 34 | { 35 | --visibleDebugLineCount; 36 | ++currentSourcePosition; 37 | break; 38 | } 39 | else 40 | { 41 | ++currentSourcePosition; 42 | } 43 | } 44 | } 45 | 46 | void debugInfo(char *message) 47 | { 48 | while (*message) 49 | { 50 | if (*message == '\n') 51 | { 52 | ++visibleDebugLineCount; 53 | 54 | if (visibleDebugLineCount > MAX_DEBUG_LINES) 55 | { 56 | removeOldestLine(); 57 | } 58 | } 59 | 60 | debugBuffer[currentWritePosition] = *message; 61 | 62 | ++message; 63 | ++currentWritePosition; 64 | 65 | if (currentWritePosition == DEBUG_BUFFER_SIZE) 66 | { 67 | wrapBuffer(); 68 | } 69 | } 70 | 71 | debugBuffer[currentWritePosition] = 0; 72 | } 73 | 74 | void clearDebugOutput() 75 | { 76 | debugBuffer[0] = 0; 77 | currentSourcePosition = 0; 78 | currentWritePosition = 0; 79 | visibleDebugLineCount = 0; 80 | } 81 | 82 | char* getDebugString() 83 | { 84 | return debugBuffer + currentSourcePosition; 85 | } 86 | 87 | void renderDebugFrame() 88 | { 89 | initSprites(); 90 | renderFrame(1); 91 | } -------------------------------------------------------------------------------- /src/debug_out.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _DEBUG_OUT_H 3 | #define _DEBUG_OUT_H 4 | 5 | void debugInfo(char *message); 6 | void clearDebugOutput(); 7 | char* getDebugString(); 8 | 9 | void renderDebugFrame(); 10 | 11 | extern char debugTMP[100]; 12 | 13 | #define DEBUG_PRINT_F(formatString, ...) sprintf(debugTMP, formatString, ##__VA_ARGS__); debugInfo(debugTMP) 14 | 15 | #endif -------------------------------------------------------------------------------- /src/debugger.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _DEBUGGER_H 3 | #define _DEBUGGER_H 4 | 5 | #include "memory_map.h" 6 | #include "cpu.h" 7 | #include "menu.h" 8 | #include "../controller.h" 9 | #include "bool.h" 10 | 11 | enum DebuggerMenuIndices { 12 | DebuggerMenuIndicesInstructions, 13 | DebuggerMenuIndicesMemoryAddress, 14 | DebuggerMenuIndicesMemoryValues, 15 | DebuggerMenuIndicesCPUState, 16 | DebuggerMenuIndicesEditValue, 17 | DebuggerMenuIndicesCount, 18 | }; 19 | 20 | 21 | #define DEBUGGER_FONT_W 8 22 | 23 | #define CPU_STATE_X 8 24 | #define CPU_STATE_Y 10 25 | 26 | #define MEMORY_BLOCK_ROWS 12 27 | #define MEMORY_BLOCK_COLS 4 28 | 29 | #define MEMORY_GRID_X 8 30 | #define MEMORY_GRID_Y 80 31 | #define DEBUG_MENU_ROW_HEIGHT 10 32 | 33 | #define MEMORY_VALUES_X 48 34 | #define MEMORY_VALUES_SPACING 24 35 | 36 | struct EditValueMenuState { 37 | struct MenuItem* returnTo; 38 | u16 x; 39 | u16 y; 40 | u16 nibbleWidth; // number of nibbles 41 | u16 currentNibble; 42 | u16 currentValue; 43 | u16 confirmEdit; // 1 if edit is accepted 44 | }; 45 | 46 | struct DebuggerMenuState { 47 | int cursorX; 48 | int cursorY; 49 | bool isDebugging; 50 | }; 51 | 52 | struct MemoryAddressMenuItem { 53 | u16 addressStart; 54 | struct DebuggerMenuState* menuState; 55 | struct MenuItem* editMenuItem; 56 | }; 57 | 58 | struct MemoryValueMenuItem { 59 | u8 values[MEMORY_BLOCK_ROWS * MEMORY_BLOCK_COLS]; 60 | struct DebuggerMenuState* menuState; 61 | struct Memory* memory; 62 | struct MemoryAddressMenuItem* addressGUI; 63 | u16 addressStart; 64 | struct MenuItem* editMenuItem; 65 | }; 66 | 67 | #define MI_INSTRUCTIONS_Y 10 68 | #define MI_INSTRUCTIONS_X 152 69 | #define MI_INSTRUCTIONS_NAME_X 192 70 | #define MI_INSTRUCTIONS_LINE_COUNT 20 71 | #define MI_INSTRUCTION_Y_OFFSET 8 72 | #define MI_INSTRUCTION_ADDR_COL 3 73 | #define MI_INSTRUCTION_NAME_COL 4 74 | 75 | struct InstructionLine { 76 | u16 address; 77 | u8 instruction[4]; 78 | struct Breakpoint* breakpoint; 79 | }; 80 | 81 | struct InstructionsMenuItem { 82 | u16 startAddress; 83 | struct InstructionLine instructions[MI_INSTRUCTIONS_LINE_COUNT]; 84 | struct DebuggerMenuState* menuState; 85 | struct CPUState* cpu; 86 | struct Memory* memory; 87 | struct MenuItem* editMenuItem; 88 | }; 89 | 90 | struct DebuggerMenu { 91 | struct MenuState menu; 92 | struct MenuItem menuItems[DebuggerMenuIndicesCount]; 93 | struct DebuggerMenuState state; 94 | struct MemoryAddressMenuItem memoryAddresses; 95 | struct MemoryValueMenuItem memoryValues; 96 | struct InstructionsMenuItem instructionMenuItem; 97 | struct EditValueMenuState editMenu; 98 | }; 99 | 100 | extern struct DebuggerMenu gDebugMenu; 101 | 102 | void writeMemoryDirect(struct Memory* memory, u16 address, u8 value); 103 | u8 readMemoryDirect(struct Memory* memory, u16 address); 104 | 105 | struct Breakpoint* addBreakpoint(struct Memory* memory, u16 address, enum BreakpointType type); 106 | void removeBreakpoint(struct Memory* memory, u16 address); 107 | void reapplyBreakpoint(struct Breakpoint* breakpoint); 108 | 109 | u8 useDebugger(struct CPUState* cpu, struct Memory* memory); 110 | 111 | void initDebugMenu(struct DebuggerMenu* menu, struct CPUState* cpu, struct Memory* memory); 112 | 113 | #endif -------------------------------------------------------------------------------- /src/decoder.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _DECODER_H 3 | #define _DECODER_H 4 | 5 | #include 6 | #include "bool.h" 7 | 8 | #define INST_FLAGS_BRANCH 0x01 9 | #define INST_FLAGS_ADDRESS 0x02 10 | #define INST_FLAGS_RELATIVE_ADDRESS 0x04 11 | #define INST_FLAGS_RETURN 0x08 12 | 13 | struct InstructionInformation { 14 | char* formatString; 15 | u8 extraBytes; 16 | u8 flags; 17 | }; 18 | 19 | struct AddressTuple { 20 | u16 nextInstruction; 21 | u16 branchInstruction; 22 | }; 23 | 24 | int decodeInstruction(char* output, u8* input, u16 address); 25 | int getInstructionSize(u8 instructionValue); 26 | u8* scanForInstruction(u8* input, u8* upperBound, u8 instruction); 27 | struct AddressTuple getInstructionBranch(u8* instruction, u16 address, u16 topOfStack, u16 hl); 28 | bool isInstructionCall(u8 instrction); 29 | 30 | #endif -------------------------------------------------------------------------------- /src/erasemenu.c: -------------------------------------------------------------------------------- 1 | 2 | #include "erasemenu.h" 3 | #include "spritefont.h" 4 | #include "save.h" 5 | 6 | void initEraseMenu(struct EraseMenu* menu, struct MenuItem* parent) 7 | { 8 | initCursorMenu(&menu->cursor, menu->menuItems, EraseMenuItemCount); 9 | menu->cursor.parentMenu = parent; 10 | 11 | initCursorMenuItem( 12 | &menu->menuItems[EraseMenuItemNo], 13 | parent, 14 | "Cancel", 15 | 32 16 | ); 17 | 18 | initCursorMenuItem( 19 | &menu->menuItems[EraseMenuItemYes], 20 | parent, 21 | "Erase", 22 | 32 23 | ); 24 | } 25 | 26 | void eraseMenuRender(struct MenuItem* menuItem, struct MenuItem* highlightedItem) 27 | { 28 | struct EraseMenu* eraseMenu = (struct EraseMenu*)menuItem->data; 29 | 30 | if (menuItem == highlightedItem) 31 | { 32 | renderMenuBorder(); 33 | spriteSetColor(gGBFont.spriteLayer, 255, 255, 255, 255); 34 | renderText(&gGBFont, "ERASE", 32, 64, 1); 35 | renderCursorMenu(&eraseMenu->cursor, 32, 112, 272); 36 | } 37 | } 38 | 39 | struct MenuItem* eraseMenuHandleInput(struct MenuItem* menuItem, int buttonsDown, int buttonsState) 40 | { 41 | struct EraseMenu* eraseMenu = (struct EraseMenu*)menuItem->data; 42 | 43 | struct MenuItem* result = inputCursorMenu(&eraseMenu->cursor, buttonsDown, 136); 44 | 45 | if (result == eraseMenu->cursor.parentMenu && eraseMenu->cursor.cursorLocation == EraseMenuItemYes) 46 | { 47 | if (eraseSaveData() == -1) 48 | { 49 | return menuItem; 50 | } 51 | else 52 | { 53 | return result; 54 | } 55 | } 56 | 57 | if (result) 58 | { 59 | return result; 60 | } 61 | else 62 | { 63 | return menuItem; 64 | } 65 | } -------------------------------------------------------------------------------- /src/erasemenu.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _ERASE_MENU_H 3 | #define _ERASE_MENU_H 4 | 5 | #include "menu.h" 6 | 7 | enum EraseMenuItem { 8 | EraseMenuItemNo, 9 | EraseMenuItemYes, 10 | EraseMenuItemCount, 11 | }; 12 | 13 | struct EraseMenu { 14 | struct CursorMenu cursor; 15 | struct CursorMenuItem menuItems[EraseMenuItemCount]; 16 | }; 17 | 18 | void initEraseMenu(struct EraseMenu* menu, struct MenuItem* parent); 19 | void eraseMenuRender(struct MenuItem* menuItem, struct MenuItem* highlightedItem); 20 | struct MenuItem* eraseMenuHandleInput(struct MenuItem* menuItem, int buttonsDown, int buttonsState); 21 | 22 | #endif -------------------------------------------------------------------------------- /src/faulthandler.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "faulthandler.h" 4 | #include "render.h" 5 | #include "debug_out.h" 6 | #include "../controller.h" 7 | #include "version.h" 8 | #include "sprite.h" 9 | 10 | #define FAULT_STACK_SIZE 0x200 11 | #define HANG_DELAY OS_USEC_TO_CYCLES(3 * 1000000) 12 | 13 | static OSThread faultThread; 14 | static u64 faultThreadStack[FAULT_STACK_SIZE / sizeof(u64)]; 15 | static OSTime lastHeartbeatTime; 16 | 17 | static void faultHandlerProc(void *); 18 | 19 | void installFaultHandler(OSThread *targetThread) 20 | { 21 | /* 22 | * Create main thread 23 | */ 24 | osCreateThread(&faultThread, 4, faultHandlerProc, targetThread, 25 | faultThreadStack + FAULT_STACK_SIZE / sizeof(u64), 11); 26 | 27 | osStartThread(&faultThread); 28 | } 29 | 30 | void dumpThreadInfo(OSThread *targetThread) 31 | { 32 | DEBUG_PRINT_F(" pc=0x%08x\n", targetThread->context.pc); 33 | DEBUG_PRINT_F(" badvaddr=0x%08x\n", targetThread->context.badvaddr); 34 | DEBUG_PRINT_F(" ra=0x%x\n", (u32)targetThread->context.ra); 35 | DEBUG_PRINT_F(" cause=0x%x\n", targetThread->context.cause); 36 | // DEBUG_PRINT_F(" GB_PC=0x%x\n", (u32)targetThread->context.t1); 37 | // DEBUG_PRINT_F(" t4=0x%x\n", (u32)targetThread->context.t4); 38 | DEBUG_PRINT_F(" version=" EMU_VERSION "\n"); 39 | } 40 | 41 | static void faultHandlerProc(void* arg) 42 | { 43 | OSThread *targetThread = (OSThread*)arg; 44 | OSMesgQueue timerQueue; 45 | OSMesg timerMessages[10]; 46 | OSTimer timer; 47 | 48 | osCreateMesgQueue(&timerQueue, timerMessages, 10); 49 | osSetTimer(&timer, 0, HANG_DELAY, &timerQueue, NULL); 50 | 51 | faultHandlerHeartbeat(); 52 | 53 | while (1) { 54 | OSMesg msg; 55 | osRecvMesg(&timerQueue, &msg, OS_MESG_BLOCK); 56 | 57 | if (osGetTime() - lastHeartbeatTime >= HANG_DELAY) { 58 | DEBUG_PRINT_F("Main thread frozen:\n"); 59 | dumpThreadInfo(targetThread); 60 | break; 61 | } 62 | 63 | OSThread *curr = __osGetNextFaultedThread(NULL); 64 | 65 | if (curr) { 66 | DEBUG_PRINT_F("Main thread faulted:\n"); 67 | dumpThreadInfo(curr); 68 | break; 69 | } 70 | } 71 | 72 | osSetThreadPri(NULL, 11); 73 | 74 | OSContPad **pad; 75 | 76 | while (1) 77 | { 78 | initSprites(); 79 | pad = ReadController(0); 80 | 81 | if (pad[0]->button & START_BUTTON) 82 | { 83 | DEBUG_PRINT_F("Main thread:\n"); 84 | dumpThreadInfo(targetThread); 85 | } 86 | 87 | renderFrame(1); 88 | } 89 | } 90 | 91 | void faultHandlerHeartbeat() 92 | { 93 | lastHeartbeatTime = osGetTime(); 94 | } -------------------------------------------------------------------------------- /src/faulthandler.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _FAULT_HANDLER_H 3 | #define _FAULT_HANDLER_H 4 | 5 | #include 6 | 7 | void installFaultHandler(OSThread *targetThread); 8 | void faultHandlerHeartbeat(); 9 | 10 | #endif -------------------------------------------------------------------------------- /src/gameboy.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _GAMEBOY_H 3 | #define _GAMEBOY_H 4 | 5 | #include 6 | #include "rom.h" 7 | #include "cpu.h" 8 | #include "memory_map.h" 9 | #include "audio.h" 10 | 11 | #define GB_BUTTON_RIGHT 0x01 12 | #define GB_BUTTON_LEFT 0x02 13 | #define GB_BUTTON_UP 0x04 14 | #define GB_BUTTON_DOWN 0x08 15 | 16 | #define GB_BUTTON_A 0x10 17 | #define GB_BUTTON_B 0x20 18 | #define GB_BUTTON_SELECT 0x40 19 | #define GB_BUTTON_START 0x80 20 | 21 | #define CPU_TICKS_PER_FRAME 781250 22 | #define CPU_TICKS_PER_SECOND 0x100000 23 | #define MAX_FRAME_SKIP 8 24 | 25 | enum InputButtonSetting 26 | { 27 | InputButtonSetting_RC, 28 | InputButtonSetting_LC, 29 | InputButtonSetting_DC, 30 | InputButtonSetting_UC, 31 | 32 | InputButtonSetting_R, 33 | InputButtonSetting_L, 34 | 35 | InputButtonSetting_RD = 8, 36 | InputButtonSetting_LD, 37 | InputButtonSetting_DD, 38 | InputButtonSetting_UD, 39 | 40 | InputButtonSetting_START, 41 | InputButtonSetting_Z, 42 | InputButtonSetting_B, 43 | InputButtonSetting_A, 44 | }; 45 | 46 | #define INPUT_BUTTON_TO_MASK(inputButton) (1 << (inputButton)) 47 | 48 | enum InputButtonIndex 49 | { 50 | InputButtonIndexRight, 51 | InputButtonIndexLeft, 52 | InputButtonIndexUp, 53 | InputButtonIndexDown, 54 | InputButtonIndexA, 55 | InputButtonIndexB, 56 | InputButtonIndexSelect, 57 | InputButtonIndexStart, 58 | InputButtonIndexSave, 59 | InputButtonIndexLoad, 60 | InputButtonIndexOpenMenu, 61 | InputButtonIndexCount, 62 | }; 63 | 64 | struct InputMapping 65 | { 66 | u8 right; 67 | u8 left; 68 | u8 up; 69 | u8 down; 70 | u8 a; 71 | u8 b; 72 | u8 select; 73 | u8 start; 74 | 75 | u8 save; 76 | u8 load; 77 | u8 openMenu; 78 | u8 fastForward; 79 | 80 | u32 reserved2; 81 | }; 82 | 83 | enum ScreenScaleSetting 84 | { 85 | ScreenScale1, 86 | ScreenScale1_25, 87 | ScreenScale1_5, 88 | ScreenScaleSettingCount, 89 | }; 90 | 91 | struct GameboyGraphicsSettings 92 | { 93 | u32 unused:27; 94 | u32 smooth:1; 95 | u32 scaleSetting:4; 96 | }; 97 | 98 | #define GB_SETTINGS_FLAGS_DISABLE_GBC 0x1 99 | 100 | #define GB_SETTINGS_HEADER 0x47423634 101 | #define GB_SETTINGS_CURRENT_VERSION 2 102 | 103 | enum StoredInfoType 104 | { 105 | StoredInfoTypeAll, 106 | StoredInfoTypeSettingsRAM, 107 | StoredInfoTypeRAM, 108 | StoredInfoTypeSettings, 109 | StoredInfoTypeNone, 110 | }; 111 | 112 | struct GameboySettings 113 | { 114 | // Always has the value 0x47423634 (GB64 as an ascii string) 115 | u32 header; 116 | // Used to check save file compatibility 117 | u32 version; 118 | u16 flags; 119 | // color palette to use for non color games 120 | u16 bgpIndex; 121 | u16 obp0Index; 122 | u16 obp1Index; 123 | struct InputMapping inputMapping; 124 | struct GameboyGraphicsSettings graphics; 125 | u64 timer; 126 | enum StoredInfoType storedType; 127 | u32 compressedSize; 128 | }; 129 | 130 | struct GameBoy 131 | { 132 | struct GameboySettings settings; 133 | struct Memory __attribute__((aligned(8))) memory; 134 | struct CPUState cpu; 135 | }; 136 | 137 | extern struct GameBoy gGameboy; 138 | extern struct GameboySettings gDefaultSettings; 139 | extern u16 gBGPColors; 140 | extern u16 gOBP0Colors; 141 | extern u16 gOBP1Colors; 142 | extern u32 gPalleteDirty; 143 | extern u32 gCyclesWaitingForMode0; 144 | 145 | void initGameboy(struct GameBoy* gameboy, struct ROMLayout* rom); 146 | 147 | void requestInterrupt(struct GameBoy* gameboy, int interrupt); 148 | 149 | /** 150 | * if targetMemory is null then the drawing routines are skipped 151 | */ 152 | void emulateFrame(struct GameBoy* gameboy, void* colorBuffer); 153 | 154 | void handleGameboyInput(struct GameBoy* gameboy, OSContPad* pad); 155 | void unloadBIOS(struct Memory* memory); 156 | 157 | enum InputButtonSetting getButtonMapping(struct InputMapping* inputMapping, enum InputButtonIndex buttonIndex); 158 | void setButtonMapping(struct InputMapping* inputMapping, enum InputButtonIndex buttonIndex, enum InputButtonSetting setting); 159 | 160 | int getPaletteCount(); 161 | u16* getPalette(int index); 162 | void updatePaletteInfo(struct GameBoy* gameboy, struct GameboySettings* prevSettings); 163 | 164 | #endif -------------------------------------------------------------------------------- /src/graphics.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _GRAPHICS_H 3 | #define _GRAPHICS_H 4 | 5 | #include "memory_map.h" 6 | #include "gameboy.h" 7 | 8 | #define GB_SCREEN_TILE_H 20 9 | 10 | #define MAX_PALLETE_SIZE (PALETTE_COUNT * GB_SCREEN_H) 11 | 12 | #define GB_SCREEN_W 160 13 | #define GB_SCREEN_H 144 14 | #define GB_SCREEN_LINES 154 15 | 16 | #define GB_RENDER_STRIP_HEIGHT 12 17 | 18 | #define RENDER_TO_X 80 19 | #define RENDER_TO_Y 48 20 | 21 | #define SPRITE_WIDTH 8 22 | #define SPRITE_BASE_HEIGHT 8 23 | #define SPRITE_Y_OFFSET 16 24 | 25 | #define TILE_ATTR_PALETTE 0x07 26 | #define TILE_ATTR_VRAM_BANK 0x08 27 | #define TILE_ATTR_H_FLIP 0x20 28 | #define TILE_ATTR_V_FLIP 0x40 29 | #define TILE_ATTR_PRIORITY 0x80 30 | 31 | /* 32 | Bit 0 - BG Display (for CGB see below) (0=Off, 1=On) 33 | Bit 1 - OBJ (Sprite) Display Enable (0=Off, 1=On) 34 | Bit 2 - OBJ (Sprite) Size (0=8x8, 1=8x16) 35 | Bit 3 - BG Tile Map Display Select (0=9800-9BFF, 1=9C00-9FFF) 36 | Bit 4 - BG & Window Tile Data Select (0=8800-97FF, 1=8000-8FFF) 37 | Bit 5 - Window Display Enable (0=Off, 1=On) 38 | Bit 6 - Window Tile Map Display Select (0=9800-9BFF, 1=9C00-9FFF) 39 | Bit 7 - LCD Display Enable (0=Off, 1=On) 40 | */ 41 | #define LCDC_BG_ENABLE 0x01 42 | #define LCDC_OBJ_ENABLE 0x02 43 | #define LCDC_OBJ_SIZE 0x04 44 | #define LCDC_BG_TILE_MAP 0x08 45 | #define LCDC_BG_TILE_DATA 0x10 46 | #define LCDC_WIN_E 0x20 47 | #define LCDC_WIN_TILE_MAP 0x40 48 | #define LCDC_LCD_E 0x80 49 | 50 | #define WINDOW_X_OFFSET 0x7 51 | 52 | #define MODE_2_CYCLES 20 53 | #define MODE_3_CYCLES 43 54 | #define CYCLES_TIL_LINE_RENDER (MODE_2_CYCLES + 1) 55 | #define CYCLES_PER_LINE 114 56 | #define V_BLANK_LINES 10 57 | #define CYCLES_PER_FRAME (CYCLES_PER_LINE * (GB_SCREEN_H + V_BLANK_LINES)) 58 | 59 | #define READ_PIXEL_INDEX(pixel, x) ((x == 7) ? ((((pixel) >> 8) & 0x1) | (((pixel) << 1) & 0x2)) : ((((pixel) >> (15 - x)) & 0x1) | (((pixel) >> (6 - x)) & 0x2))) 60 | 61 | // GGGRRRRR 1BBBBBGG to RRRRRGGGGGBBBBB1 62 | #define GBC_TO_N64_COLOR(color) (((color) << 3 & 0xF100) | ((color) >> 7 & 0x01C0 >> 7) | ((color) << 9 & 0x0600) | ((color) >> 1 & 0x003E) | 0x1) 63 | 64 | extern u16 gScreenPalette[MAX_PALLETE_SIZE]; 65 | 66 | struct RenderBlockInformation { 67 | u16 row; 68 | u16 palleteWriteIndex; 69 | }; 70 | 71 | struct GraphicsState { 72 | struct Sprite sortedSprites[SPRITE_COUNT]; 73 | u8 spriteIndexBuffer[GB_SCREEN_W]; 74 | struct GameboyGraphicsSettings settings; 75 | int spriteCount; 76 | u16 gbc; 77 | u16 row; 78 | u16 lastRenderedRow; 79 | u16 palleteWriteIndex; 80 | }; 81 | 82 | void applyGrayscalePallete(struct GraphicsState* state); 83 | void beginScreenDisplayList(struct GameboyGraphicsSettings* settings, void* colorBuffer); 84 | void prepareGraphicsPallete(struct GraphicsState* state); 85 | void renderScreenBlock(struct GraphicsState* state); 86 | 87 | void initGraphicsState( 88 | struct Memory* memory, 89 | struct GraphicsState* state, 90 | struct GameboyGraphicsSettings* settings, 91 | int gbc, 92 | void* colorBuffer 93 | ); 94 | 95 | void finishScreen(struct GraphicsState* state); 96 | void rerenderLastFrame(struct GameboyGraphicsSettings* settings, void* colorBuffer); 97 | void waitForRDP(); 98 | int palleteUsedCount(); 99 | 100 | #endif -------------------------------------------------------------------------------- /src/graphicsmenu.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _GRAPHICS_MENU_H 3 | #define _GRAPHICS_MENU_H 4 | 5 | #include "menu.h" 6 | 7 | enum GraphicsMenuItem { 8 | GraphicsMenuItemScale, 9 | GraphicsMenuItemPixel, 10 | GraphicsMenuItemGBP, 11 | GraphicsMenuItemOBP0, 12 | GraphicsMenuItemOBP1, 13 | GraphicsMenuItemCount, 14 | }; 15 | 16 | struct GraphicsMenu { 17 | struct CursorMenu cursor; 18 | struct CursorMenuItem menuItems[GraphicsMenuItemCount]; 19 | struct SelectCursorMenuItem scale; 20 | struct SelectCursorMenuItem pixel; 21 | }; 22 | 23 | void initGraphicsMenu(struct GraphicsMenu* menu, struct MenuItem* parentMenu); 24 | void graphicsMenuRender(struct MenuItem* menuItem, struct MenuItem* highlightedItem); 25 | struct MenuItem* graphicsMenuHandleInput(struct MenuItem* menuItem, int buttonsDown, int buttonsState); 26 | 27 | #endif -------------------------------------------------------------------------------- /src/inputmapping.c: -------------------------------------------------------------------------------- 1 | 2 | #include "inputmapping.h" 3 | #include "gameboy.h" 4 | #include "render.h" 5 | #include "sprite.h" 6 | #include "spritefont.h" 7 | 8 | static char* gInputMappingNames[MAPPED_INPUT_COUNT] = { 9 | "RGHT", 10 | "LEFT", 11 | "UP", 12 | "DOWN", 13 | "A", 14 | "B", 15 | "SLCT", 16 | "STRT", 17 | "SAVE", 18 | "LOAD", 19 | "MENU", 20 | "FAST", 21 | }; 22 | 23 | void renderInputMappingItem(struct CursorMenuItem* menuItem, int x, int y, int selected) 24 | { 25 | enum InputButtonIndex buttonIndex = (enum InputButtonIndex)menuItem->data; 26 | 27 | if (selected) 28 | { 29 | spriteDrawTile( 30 | SPRITE_BORDER_LAYER, 31 | x, y + 8, 32 | 16, 16, 33 | gGUIItemTiles[GUIItemIconRight] 34 | ); 35 | } 36 | 37 | if (buttonIndex != ~0) 38 | { 39 | enum InputButtonSetting buttonSetting = getButtonMapping(&gGameboy.settings.inputMapping, buttonIndex); 40 | 41 | spriteDrawTile( 42 | gButtonIconLayer[buttonSetting], 43 | x + 24, y, 44 | 32, 32, 45 | gButtonIconTile[buttonSetting] 46 | ); 47 | } 48 | 49 | spriteSetColor(gGBFont.spriteLayer, 255, 255, 255, 255); 50 | renderText(&gGBFont, menuItem->label, x + 64, y + 8, 1); 51 | } 52 | 53 | struct MenuItem* inputInputMappingItem(struct CursorMenuItem* menuItem, int buttonDown) 54 | { 55 | return NULL; 56 | } 57 | 58 | 59 | void inputMappingRender(struct MenuItem* menuItem, struct MenuItem* highlightedItem) 60 | { 61 | struct InputMappingMenu* inputMapping = (struct InputMappingMenu*)menuItem->data; 62 | 63 | if (menuItem == highlightedItem) 64 | { 65 | renderMenuBorder(); 66 | spriteSetColor(gGBFont.spriteLayer, 255, 255, 255, 255); 67 | renderText(&gGBFont, "INPUT", 32, 64, 1); 68 | renderCursorMenu(&inputMapping->cursor, 32, 112, 272); 69 | } 70 | } 71 | 72 | struct MenuItem* inputMappingHandleInput(struct MenuItem* menuItem, int buttonsDown, int buttonsState) 73 | { 74 | struct InputMappingMenu* inputMapping = (struct InputMappingMenu*)menuItem->data; 75 | 76 | if (inputMapping->currentMappingIndex != ~0 && buttonsDown) 77 | { 78 | int setting = 0; 79 | 80 | while (buttonsDown & ~0x1) 81 | { 82 | ++setting; 83 | buttonsDown >>= 1; 84 | } 85 | 86 | setButtonMapping(&gGameboy.settings.inputMapping, inputMapping->currentMappingIndex, setting); 87 | 88 | inputMapping->cursor.menuItems[inputMapping->currentMappingIndex].data = (void*)inputMapping->currentMappingIndex; 89 | inputMapping->currentMappingIndex = ~0; 90 | return menuItem; 91 | } 92 | else if (buttonsDown & (START_BUTTON | A_BUTTON)) 93 | { 94 | inputMapping->currentMappingIndex = inputMapping->cursor.cursorLocation; 95 | inputMapping->cursor.menuItems[inputMapping->cursor.cursorLocation].data = (void*)~0; 96 | return menuItem; 97 | } 98 | else if (buttonsDown & INPUT_BUTTON_TO_MASK(gGameboy.settings.inputMapping.openMenu)) 99 | { 100 | return inputMapping->cursor.parentMenu; 101 | } 102 | else 103 | { 104 | struct MenuItem* result = inputCursorMenu(&inputMapping->cursor, buttonsDown, 136); 105 | 106 | if (result) 107 | { 108 | return result; 109 | } 110 | else 111 | { 112 | return menuItem; 113 | } 114 | } 115 | } 116 | 117 | void initInputMappingMenu(struct InputMappingMenu* menu, struct MenuItem* parentMenu) 118 | { 119 | initCursorMenu( 120 | &menu->cursor, 121 | menu->menuItems, 122 | MAPPED_INPUT_COUNT 123 | ); 124 | menu->cursor.parentMenu = parentMenu; 125 | menu->currentMappingIndex = ~0; 126 | 127 | int i; 128 | 129 | for (i = 0; i < MAPPED_INPUT_COUNT; ++i) 130 | { 131 | initCursorMenuItem( 132 | &menu->menuItems[i], 133 | NULL, 134 | gInputMappingNames[i], 135 | 40 136 | ); 137 | menu->menuItems[i].data = (void*)i; 138 | menu->menuItems[i].render = renderInputMappingItem; 139 | menu->menuItems[i].input = inputInputMappingItem; 140 | } 141 | } -------------------------------------------------------------------------------- /src/inputmapping.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _INPUT_MAPPING_H 3 | #define _INPUT_MAPPING_H 4 | 5 | #include "menu.h" 6 | 7 | #define MAPPED_INPUT_COUNT 12 8 | 9 | struct InputMappingMenu { 10 | struct CursorMenu cursor; 11 | struct CursorMenuItem menuItems[MAPPED_INPUT_COUNT]; 12 | int currentMappingIndex; 13 | }; 14 | 15 | void initInputMappingMenu(struct InputMappingMenu* menu, struct MenuItem* parentMenu); 16 | void inputMappingRender(struct MenuItem* menuItem, struct MenuItem* highlightedItem); 17 | struct MenuItem* inputMappingHandleInput(struct MenuItem* menuItem, int buttonsDown, int buttonsState); 18 | 19 | #endif -------------------------------------------------------------------------------- /src/mainmenu.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _MAIN_MENU_H 3 | #define _MAIN_MENU_H 4 | 5 | #include "menu.h" 6 | #include "bool.h" 7 | #include "inputmapping.h" 8 | #include "graphicsmenu.h" 9 | #include "clockmenu.h" 10 | #include "erasemenu.h" 11 | 12 | #define SAVE_TIMER_FRAMES 96 13 | #define SAVE_TIMER_FADE_TIME 64 14 | 15 | #define LOAD_TIMER_START_FRAMES 32 16 | #define LOAD_TIMER_FRAMES 64 17 | 18 | #define LEFT_PANEL_WIDTH 80 19 | 20 | enum MainMenuItems { 21 | MainMenuItemSaveState, 22 | MainMenuItemMainMenu, 23 | MainMenuItemMainInput, 24 | MainMenuItemMainScreen, 25 | MainMenuItemMainErase, 26 | MainMenuItemMainClock, 27 | MainMenuItemsCount, 28 | }; 29 | 30 | enum MainMenuStateItems { 31 | MainMenuStateItemsInput, 32 | MainMenuStateItemsScreen, 33 | MainMenuStateItemsErase, 34 | MainMenuStateItemsClock, 35 | MainMenuStateItemsCount, 36 | }; 37 | 38 | struct MainMenuState { 39 | struct CursorMenu cursorMenu; 40 | struct CursorMenuItem items[MainMenuStateItemsCount]; 41 | }; 42 | 43 | struct SaveState { 44 | u16 showSaveTimer; 45 | u8 isLoading; 46 | u8 showLoadTimer; 47 | u16 isFast; 48 | u16 showFastTimer; 49 | struct MenuItem* mainMenu; 50 | char* saveMessage; 51 | char* loadMessage; 52 | }; 53 | 54 | struct MainMenu { 55 | struct SaveState saveState; 56 | struct MainMenuState mainMenuState; 57 | struct InputMappingMenu inputMapping; 58 | struct GraphicsMenu graphicsMenu; 59 | struct EraseMenu eraseMenu; 60 | struct ClockMenu clockMenu; 61 | struct MenuState menu; 62 | struct MenuItem menuItems[MainMenuItemsCount]; 63 | u16 leftPanelPosition; 64 | u16 leftPanelTarget; 65 | }; 66 | 67 | extern struct MainMenu gMainMenu; 68 | 69 | void initMainMenu(struct MainMenu* mainMenu); 70 | void updateMainMenu(struct MainMenu* mainMenu, OSContPad* pad); 71 | void renderMainMenu(struct MainMenu* mainMenu); 72 | 73 | bool isMainMenuOpen(struct MainMenu* mainMenu); 74 | 75 | #endif -------------------------------------------------------------------------------- /src/memory_map_offsets.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _MEMORY_MAP_OFFSETS_ 3 | #define _MEMORY_MAP_OFFSETS_ 4 | 5 | #define MEMORY_ADDR_TABLE 0x00 6 | #define MEMORY_READ_TABLE 0x40 7 | #define MEMORY_WRITE_TABLE 0x80 8 | #define MEMORY_CART_WRITE 0xC0 9 | #define MEMORY_CART_READ 0xC4 10 | #define MEMORY_BANK_SWITCHING 0xC8 11 | #define MEMORY_MISC_START 0xD0 12 | #define MEMORY_MISC_RAM_BANK 0x178 13 | #define MEMORY_MISC_TIMER 0x180 14 | #define MEMORY_RAM_START 0x2D0 15 | #define MEMORY_ROM 0x82D4 16 | #define MEMORY_CART_RAM 0x82D8 17 | #define MEMORY_VRAM 0x82D8 18 | #define MEMORY_BG_PAL 0xC2D8 19 | #define MEMORY_OBJ_PAL 0xC318 20 | 21 | #endif -------------------------------------------------------------------------------- /src/menu.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _MENU_H 3 | #define _MENU_H 4 | 5 | #include 6 | #include "sprite.h" 7 | 8 | #define INITIAL_TIME_DELAY 500 9 | #define REPEAT_TIME_DELAY 200 10 | 11 | struct MenuItem; 12 | 13 | typedef void (*MenuItemRender)(struct MenuItem* menuItem, struct MenuItem* highlightedItem); 14 | typedef struct MenuItem* (*MenuItemHandleInput)(struct MenuItem* menuItem, int buttonsDown, int buttonsState); 15 | typedef void (*MenuItemSetActive)(struct MenuItem* menuItem, int isActive); 16 | 17 | #define MENU_ITEM_FLAGS_HIDDEN 0x1 18 | 19 | struct MenuItem { 20 | struct MenuItem* toLeft; 21 | struct MenuItem* toRight; 22 | struct MenuItem* toUp; 23 | struct MenuItem* toDown; 24 | void* data; 25 | MenuItemRender renderCallback; 26 | MenuItemHandleInput handleButtonDown; 27 | MenuItemHandleInput handleButtonUp; 28 | MenuItemSetActive setActive; 29 | u32 flags; 30 | }; 31 | 32 | struct MenuState { 33 | struct MenuItem* currentMenuItem; 34 | struct MenuItem* allItems; 35 | int menuItemCount; 36 | int lastButtons; 37 | u32 holdTimer; 38 | }; 39 | 40 | 41 | struct CursorMenuItem; 42 | 43 | typedef struct MenuItem* (*InputCursorMenuItem)(struct CursorMenuItem* menuItem, int buttonDown); 44 | typedef void (*RenderCursorMenuItem)(struct CursorMenuItem* menuItem, int x, int y, int selected); 45 | 46 | struct CursorMenuItem { 47 | struct MenuItem* toMenu; 48 | char* label; 49 | InputCursorMenuItem input; 50 | RenderCursorMenuItem render; 51 | void* data; 52 | u16 height; 53 | u16 flags; 54 | }; 55 | 56 | extern struct SpriteTile gButtonIconTile[]; 57 | extern char gButtonIconLayer[]; 58 | extern struct SpriteTile gGUIItemTiles[]; 59 | 60 | enum GUIItemIcon 61 | { 62 | GUIItemIconRight, 63 | GUIItemIconLeft, 64 | GUIItemIconDown, 65 | GUIItemIconUp, 66 | GUIItemIconHorz, 67 | GUIItemIconVert, 68 | GUIItemIconTopRight, 69 | GUIItemIconBottomRight, 70 | GUIItemIconWhite, 71 | GUIItemIconBorder, 72 | GUIItemIconBlack, 73 | }; 74 | 75 | struct CursorMenu { 76 | struct MenuItem* parentMenu; 77 | struct CursorMenuItem* menuItems; 78 | u16 menuItemCount; 79 | u16 cursorLocation; 80 | u16 scrollOffset; 81 | }; 82 | 83 | 84 | typedef void (*SetItemValue)(struct CursorMenuItem* item, int id, int value); 85 | 86 | struct SelectCursorMenuItem { 87 | SetItemValue changeCallback; 88 | int id; 89 | int value; 90 | int maxValue; 91 | int minValue; 92 | char** labels; 93 | }; 94 | 95 | void initMenuState(struct MenuState* menu, struct MenuItem* items, int itemCount); 96 | 97 | void menuStateHandleInput(struct MenuState* menu, OSContPad* pad); 98 | void menuStateRender(struct MenuState* menu); 99 | void menuItemInit(struct MenuItem* menuItem, void* data, MenuItemRender renderCallback, MenuItemHandleInput handleButtonDown, MenuItemSetActive setActive); 100 | 101 | void menuItemConnectSideToSide(struct MenuItem* left, struct MenuItem* right); 102 | void menuItemConnectUpAndDown(struct MenuItem* up, struct MenuItem* down); 103 | 104 | struct MenuItem* inputCursorMenu(struct CursorMenu* menu, int buttons, int height); 105 | void renderCursorMenu(struct CursorMenu* menu, int x, int y, int height); 106 | void initCursorMenu(struct CursorMenu* menu, struct CursorMenuItem* menuItems, u16 menuItemCount); 107 | void initCursorMenuItem(struct CursorMenuItem* item, struct MenuItem* toMenu, char* label, u16 height); 108 | 109 | void initSelectCursorMenuItem(struct SelectCursorMenuItem* item, int id, int value, int minValue, int maxValue, char** labels); 110 | void renderSelectCursorMenuItem(struct CursorMenuItem* menuItem, int x, int y, int selected); 111 | struct MenuItem* inputSelectCursorMenuItem(struct CursorMenuItem* menuItem, int buttonDown); 112 | 113 | void renderSprite(Bitmap* bitmap, s32 x, s32 y, s32 w, s32 h); 114 | void renderMenuBorder(); 115 | 116 | #endif -------------------------------------------------------------------------------- /src/polyfill.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | void* memset(void * target, int value, size_t length) { 5 | char setTo = (char)value; 6 | 7 | char* curr = target; 8 | 9 | while (length) { 10 | *curr++ = setTo; 11 | --length; 12 | } 13 | 14 | return target; 15 | } 16 | 17 | int memcmp(const void * a, const void * b, size_t length) { 18 | const char* checkA = a; 19 | const char* checkB = b; 20 | 21 | while (length) { 22 | int diff = *checkA - *checkB; 23 | 24 | if (!diff) { 25 | return diff; 26 | } 27 | 28 | checkA++; 29 | checkB++; 30 | --length; 31 | } 32 | 33 | return 0; 34 | } -------------------------------------------------------------------------------- /src/rom.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _ROM_H 3 | #define _ROM_H 4 | 5 | #define ROM_BANK_SIZE 0x4000 6 | #define ROM_BANKS 128 7 | 8 | #define GB_ROM_H_GBC_FLAG 0x143 9 | #define GB_ROM_H_CART_TYPE 0x147 10 | #define GB_ROM_H_SIZE 0x148 11 | #define GB_ROM_H_RAM_SIZE 0x149 12 | 13 | #define GB_ROM_GBC_SUPPORT 0x80 14 | #define GB_ROM_GBC_ONLY 0xC0 15 | 16 | extern char _dmg_bootSegmentRomStart[]; 17 | extern char _dmg_bootSegmentRomEnd[]; 18 | 19 | extern char _cgb_biosSegmentRomStart[]; 20 | extern char _cgb_biosSegmentRomEnd[]; 21 | 22 | struct VirtualBank 23 | { 24 | unsigned char bankMemory[ROM_BANK_SIZE]; 25 | int bankIndex; 26 | struct VirtualBank* nextBank; 27 | struct VirtualBank* prevBank; 28 | }; 29 | 30 | struct ROMLayout 31 | { 32 | unsigned char* mainBank; 33 | struct VirtualBank* firstVirtualBank; 34 | struct VirtualBank* lastVirtualBank; 35 | struct VirtualBank** romBankToVirtualBank; 36 | int romBankCount; 37 | void* romLocation; 38 | }; 39 | 40 | extern struct ROMLayout gGBRom; 41 | 42 | void initRomLayout(struct ROMLayout* romLayout, void *romLocation); 43 | 44 | void finishRomLoad(struct ROMLayout* romLayout); 45 | 46 | char* getROMBank(struct ROMLayout* romLayout, int bankIndex); 47 | 48 | int getROMBankCount(struct ROMLayout* romLayout); 49 | int getRAMBankCount(struct ROMLayout* romLayout); 50 | int getCartType(struct ROMLayout* romLayout); 51 | 52 | void loadBIOS(struct ROMLayout* romLayout, int gbc); 53 | void loadRomSegment(void* target, void *romLocation, int bankNumber); 54 | 55 | #endif -------------------------------------------------------------------------------- /src/rspppu.c: -------------------------------------------------------------------------------- 1 | 2 | #include "rspppu.h" 3 | #include "rspppu_includes.h" 4 | #include "graphics.h" 5 | 6 | #include 7 | 8 | extern long long int ppuTextStart[]; 9 | extern long long int ppuTextEnd[]; 10 | 11 | extern long long int ppuDataStart[]; 12 | extern long long int ppuDataEnd[]; 13 | 14 | static char __attribute__((aligned(8))) outputBuffer[64]; 15 | 16 | extern OSMesgQueue dmaMessageQ; 17 | extern OSMesg dmaMessageBuf; 18 | extern OSPiHandle *handler; 19 | extern OSIoMesg dmaIOMessageBuf; 20 | 21 | struct PPUPerformance __attribute__((aligned(8))) gPPUPerformance; 22 | 23 | struct PPUTask 24 | { 25 | u8* output; 26 | struct Memory* memorySource; 27 | struct GraphicsMemory* graphics; 28 | short flags; 29 | char lcdc; 30 | char ly; 31 | char scy; 32 | char scx; 33 | char wy; 34 | char wx; 35 | struct PPUPerformance* performanceMetrics; 36 | }; 37 | 38 | static struct PPUTask __attribute__((aligned(8))) gPPUTask; 39 | extern u8 __attribute__((aligned(8))) gScreenBuffer[]; 40 | 41 | void setupPPU() 42 | { 43 | 44 | } 45 | 46 | void startPPUFrame(struct Memory* memory, int gbc) 47 | { 48 | OSTask task; 49 | 50 | gPPUTask.output = (u8*)K0_TO_PHYS(gScreenBuffer); 51 | gPPUTask.memorySource = (struct Memory*)K0_TO_PHYS(memory); 52 | gPPUTask.graphics = (struct GraphicsMemory*)K0_TO_PHYS(&memory->vram); 53 | gPPUTask.flags = 0; 54 | gPPUTask.lcdc = READ_REGISTER_DIRECT(memory, REG_LCDC); 55 | gPPUTask.ly = READ_REGISTER_DIRECT(memory, REG_LY); 56 | gPPUTask.scy = READ_REGISTER_DIRECT(memory, REG_SCY); 57 | gPPUTask.scx = READ_REGISTER_DIRECT(memory, REG_SCX); 58 | gPPUTask.wy = READ_REGISTER_DIRECT(memory, REG_WY); 59 | gPPUTask.wx = READ_REGISTER_DIRECT(memory, REG_WX); 60 | gPPUTask.performanceMetrics = (struct PPUPerformance*)K0_TO_PHYS(&gPPUPerformance); 61 | 62 | if (gbc) { 63 | gPPUTask.flags |= PPU_TASK_FLAGS_COLOR; 64 | } 65 | 66 | task.t.type = M_GFXTASK; 67 | task.t.flags = OS_TASK_SP_ONLY; 68 | task.t.ucode_boot = (u64*)ppuTextStart; 69 | task.t.ucode_boot_size = (char*)ppuTextEnd - (char*)ppuTextStart; 70 | task.t.ucode = NULL; 71 | task.t.ucode_size = 0; 72 | task.t.ucode_data = (u64*)ppuDataStart; 73 | task.t.ucode_data_size = (char*)ppuDataEnd - (char*)ppuDataStart; 74 | task.t.dram_stack = 0; 75 | task.t.dram_stack_size = 0; 76 | task.t.output_buff = (u64*)outputBuffer; 77 | task.t.output_buff_size = 0; 78 | task.t.data_ptr = (u64*)&gPPUTask; 79 | task.t.data_size = sizeof(struct PPUTask); 80 | task.t.yield_data_ptr = 0; 81 | task.t.yield_data_size = 0; 82 | 83 | osWritebackDCache(&gPPUTask, sizeof(struct PPUTask)); 84 | osWritebackDCache(&memory->misc.sprites, sizeof(memory->misc.sprites)); 85 | 86 | osSpTaskStart(&task); 87 | // clear mode 3 bit 88 | IO_WRITE(SP_STATUS_REG, SP_CLR_SIG0 | SP_SET_SIG1 | SP_CLR_SIG2); 89 | } 90 | 91 | void renderPPURow(struct Memory* memory, struct GraphicsState* state) 92 | { 93 | gPPUTask.lcdc = READ_REGISTER_DIRECT(memory, REG_LCDC); 94 | gPPUTask.ly = READ_REGISTER_DIRECT(memory, REG_LY); 95 | gPPUTask.scy = READ_REGISTER_DIRECT(memory, REG_SCY); 96 | gPPUTask.scx = READ_REGISTER_DIRECT(memory, REG_SCX); 97 | gPPUTask.wy = READ_REGISTER_DIRECT(memory, REG_WY); 98 | gPPUTask.wx = READ_REGISTER_DIRECT(memory, REG_WX); 99 | osWritebackDCache(&gPPUTask, sizeof(struct PPUTask)); 100 | osWritebackDCache(&memory->vram, sizeof(&memory->vram)); 101 | 102 | prepareGraphicsPallete(state); 103 | 104 | if (state->row - state->lastRenderedRow >= GB_RENDER_STRIP_HEIGHT) 105 | { 106 | renderScreenBlock(state); 107 | } 108 | 109 | // set mode 3 bit 110 | IO_WRITE(SP_STATUS_REG, SP_SET_SIG0); 111 | } 112 | 113 | void enterMode2(struct Memory* memory) { 114 | osWritebackDCache(&memory->misc.sprites, sizeof(memory->misc.sprites)); 115 | } -------------------------------------------------------------------------------- /src/rspppu.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _RSP_PPU_H 3 | #define _RSP_PPU_H 4 | 5 | #include "memory_map.h" 6 | #include "graphics.h" 7 | 8 | struct PPUPerformance { 9 | int mode2StallCount; 10 | int mode3StallCount; 11 | }; 12 | 13 | extern struct PPUPerformance gPPUPerformance; 14 | 15 | void setupPPU(); 16 | void startPPUFrame(struct Memory* memory, int gdc); 17 | void renderPPURow(struct Memory* memory, struct GraphicsState* state); 18 | 19 | #endif -------------------------------------------------------------------------------- /src/rspppu_includes.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _RSPPPU_INCLUDES_H_ 3 | #define _RSPPPU_INCLUDES_H_ 4 | 5 | #define PPU_TASK_FLAGS_COLOR 0x1 6 | 7 | #endif -------------------------------------------------------------------------------- /src/save.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _SAVE_H 3 | #define _SAVE_H 4 | 5 | #include 6 | #include "gameboy.h" 7 | 8 | enum SaveType 9 | { 10 | SaveTypeFlash, 11 | SaveTypeSRAM, 12 | SaveTypeSRAM3X, 13 | }; 14 | 15 | #define SAVE_HEADER_VALUE 0x53564944 16 | 17 | struct SaveTypeSetting 18 | { 19 | u32 header; // always SAVE_HEADER_VALUE to allow rom wrapper to find this setting 20 | enum SaveType saveType; 21 | }; 22 | 23 | extern struct SaveTypeSetting gSaveTypeSetting; 24 | typedef int (*SaveReadCallback)(void* target, int sramOffset, int length); 25 | typedef int (*SaveWriteCallback)(void *from, int sramOffset, int length); 26 | 27 | enum StoredInfoType loadSettings(struct GameBoy* gameboy); 28 | int loadGameboyState(struct GameBoy* gameboy, enum StoredInfoType storeType); 29 | void loadRAM(struct Memory* memory, enum StoredInfoType storeType, int compressedSize); 30 | enum StoredInfoType saveGameboyState(struct GameBoy* gameboy); 31 | int eraseSaveData(); 32 | int getSaveStateSize(struct GameBoy* gameboy); 33 | void initSaveCallbacks(); 34 | enum StoredInfoType getStoredInfoType(struct GameBoy* gameboy); 35 | enum StoredInfoType getDeprecatedStoredInfoType(struct GameBoy* gameboy); 36 | 37 | #endif -------------------------------------------------------------------------------- /src/sprite.c: -------------------------------------------------------------------------------- 1 | 2 | #include "sprite.h" 3 | #include "assert.h" 4 | 5 | #define DL_CHUNK_SIZE 64 6 | #define SPRITE_GL_LENGTH 2098 7 | 8 | Gfx* gLayerSetup[MAX_LAYER_COUNT]; 9 | 10 | u32 gCurrentSpriteColors[MAX_LAYER_COUNT]; 11 | Gfx gSpriteDisplayList[SPRITE_GL_LENGTH]; 12 | Gfx* gLayerDL[MAX_LAYER_COUNT]; 13 | Gfx* gCurrentLayerDL[MAX_LAYER_COUNT]; 14 | Gfx* gLayerChunk[MAX_LAYER_COUNT]; 15 | Gfx* gSpriteNextChunk; 16 | 17 | Gfx* allocateSpriteChunk() 18 | { 19 | Gfx* result = gSpriteNextChunk; 20 | 21 | gSpriteNextChunk += DL_CHUNK_SIZE; 22 | teqassert(gSpriteNextChunk <= gSpriteDisplayList + SPRITE_GL_LENGTH); 23 | 24 | return result; 25 | } 26 | 27 | void writeDL(int layer, Gfx* src, int count) 28 | { 29 | while (count) 30 | { 31 | Gfx* current = gCurrentLayerDL[layer]; 32 | int capacity = DL_CHUNK_SIZE + gLayerChunk[layer] - current; 33 | 34 | if (!current || capacity == 1) 35 | { 36 | Gfx* next = allocateSpriteChunk(); 37 | 38 | if (current) 39 | { 40 | // check if the next chunk is adjacent in memory 41 | if (current + 1 == next) 42 | { 43 | *current++ = *src++; 44 | --count; 45 | --capacity; 46 | } 47 | else 48 | { 49 | gSPBranchList(current++, next); 50 | } 51 | } 52 | else 53 | { 54 | gLayerDL[layer] = next; 55 | } 56 | 57 | gLayerChunk[layer] = next; 58 | gCurrentLayerDL[layer] = next; 59 | current = next; 60 | } 61 | 62 | while (count && capacity > 1) 63 | { 64 | *current++ = *src++; 65 | --count; 66 | --capacity; 67 | } 68 | 69 | gCurrentLayerDL[layer] = current; 70 | } 71 | } 72 | 73 | void setLayerGraphics(int layer, Gfx* graphics) 74 | { 75 | gLayerSetup[layer] = graphics; 76 | } 77 | 78 | void spriteDraw(int layer, int x, int y, int w, int h, int sx, int sy, int sw, int sh) 79 | { 80 | Gfx workingMem[4]; 81 | Gfx* curr = workingMem; 82 | 83 | gSPTextureRectangle( 84 | curr++, 85 | x << 2, 86 | y << 2, 87 | (x + (w << sw)) << 2, 88 | (y + (h << sh)) << 2, 89 | G_TX_RENDERTILE, 90 | sx << 5, sy << 5, 91 | 0x400 >> sw, 92 | 0x400 >> sh 93 | ); 94 | 95 | writeDL(layer, workingMem, curr - workingMem); 96 | } 97 | 98 | void spriteDrawTile(int layer, int x, int y, int w, int h, struct SpriteTile tile) 99 | { 100 | Gfx workingMem[4]; 101 | Gfx* curr = workingMem; 102 | 103 | gSPTextureRectangle( 104 | curr++, 105 | x << 2, 106 | y << 2, 107 | (x + w) << 2, 108 | (y + h) << 2, 109 | G_TX_RENDERTILE, 110 | tile.x << 5, tile.y << 5, 111 | (tile.w << 10) / w, 112 | (tile.h << 10) / h 113 | ); 114 | 115 | writeDL(layer, workingMem, curr - workingMem); 116 | } 117 | 118 | void spriteSetColor(int layer, u8 r, u8 g, u8 b, u8 a) 119 | { 120 | int key = (r << 24) | (g << 16) | (b << 8) | (a << 0); 121 | 122 | if (key != gCurrentSpriteColors[layer]) 123 | { 124 | Gfx workingMem; 125 | Gfx* curr = &workingMem; 126 | gDPSetEnvColor(curr++, r, g, b, a); 127 | writeDL(layer, &workingMem, curr - &workingMem); 128 | gCurrentSpriteColors[layer] = key; 129 | } 130 | } 131 | 132 | void initSprites() 133 | { 134 | for (int i = 0; i < MAX_LAYER_COUNT; ++i) 135 | { 136 | gLayerDL[i] = NULL; 137 | gCurrentLayerDL[i] = NULL; 138 | gLayerChunk[i] = NULL; 139 | gCurrentSpriteColors[i] = ~0; 140 | } 141 | 142 | gSpriteNextChunk = gSpriteDisplayList; 143 | } 144 | 145 | void finishSprites(Gfx** out) 146 | { 147 | Gfx* curr = *out; 148 | 149 | for (int i = 0; i < MAX_LAYER_COUNT; ++i) 150 | { 151 | if (gLayerDL[i] && gLayerSetup[i]) 152 | { 153 | gSPEndDisplayList(gCurrentLayerDL[i]++); 154 | gSPDisplayList(curr++, gLayerSetup[i]); 155 | gSPDisplayList(curr++, gLayerDL[i]); 156 | } 157 | } 158 | 159 | osWritebackDCache(gSpriteDisplayList, sizeof(gSpriteDisplayList)); 160 | 161 | *out = curr; 162 | } 163 | -------------------------------------------------------------------------------- /src/sprite.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _SPRITE_H 3 | #define _SPRITE_H 4 | 5 | #include 6 | 7 | #define MAX_LAYER_COUNT 8 8 | 9 | #define SPRITE_BORDER_LAYER 0 10 | #define SPRITE_CBUTTONS_LAYER 1 11 | #define SPRITE_TRIGGERS_LAYER 2 12 | #define SPRITE_DPAD_LAYER 3 13 | #define SPRITE_FACE_LAYER 4 14 | #define SPRITE_FONT_LAYER 5 15 | 16 | struct SpriteTile 17 | { 18 | char x; 19 | char y; 20 | char w; 21 | char h; 22 | }; 23 | 24 | void setLayerGraphics(int layer, Gfx* graphics); 25 | void spriteDraw(int layer, int x, int y, int w, int h, int sx, int sy, int scaleShiftX, int scaleShiftY); 26 | void spriteDrawTile(int layer, int x, int y, int w, int h, struct SpriteTile tile); 27 | void spriteSetColor(int layer, u8 r, u8 g, u8 b, u8 a); 28 | 29 | void initSprites(); 30 | void finishSprites(Gfx** out); 31 | 32 | #endif -------------------------------------------------------------------------------- /src/spritefont.c: -------------------------------------------------------------------------------- 1 | 2 | #include "spritefont.h" 3 | #include "sprite.h" 4 | 5 | void initFont(struct Font* font, int layer, int spaceWidth, struct CharacterDefinition* chars, int charCount) 6 | { 7 | font->spriteLayer = layer; 8 | font->spaceWidth = spaceWidth; 9 | 10 | for (int i = 0; i < ANSI_CHAR_COUNT; ++i) 11 | { 12 | font->characters[i].w = 0; 13 | } 14 | 15 | for (int i = 0; i < charCount; ++i) 16 | { 17 | font->characters[chars[i].character] = chars[i].data; 18 | } 19 | } 20 | 21 | void setFontColor(struct Font* font, char r, char g, char b) 22 | { 23 | 24 | } 25 | 26 | void renderText(struct Font* font, const char* str, int x, int y, int scaleShift) 27 | { 28 | int startX = x; 29 | 30 | while (*str) 31 | { 32 | struct SpriteTile curr = font->characters[*str]; 33 | if (curr.w) 34 | { 35 | spriteDraw(font->spriteLayer, x, y, curr.w, curr.h, curr.x, curr.y, scaleShift, scaleShift); 36 | x += curr.w << scaleShift; 37 | } 38 | else if (*str == ' ') 39 | { 40 | x += font->spaceWidth << scaleShift; 41 | } 42 | else if (*str == '\n') 43 | { 44 | x = startX; 45 | y += 20; 46 | } 47 | 48 | ++str; 49 | } 50 | } 51 | 52 | struct CharacterDefinition gGBFontDef[] = { 53 | {'A', 0*8, 0*8, 8, 8}, 54 | {'B', 1*8, 0*8, 8, 8}, 55 | {'C', 2*8, 0*8, 8, 8}, 56 | {'D', 3*8, 0*8, 8, 8}, 57 | {'E', 4*8, 0*8, 8, 8}, 58 | {'F', 5*8, 0*8, 8, 8}, 59 | {'G', 6*8, 0*8, 8, 8}, 60 | {'H', 7*8, 0*8, 8, 8}, 61 | {'I', 0*8, 1*8, 8, 8}, 62 | {'J', 1*8, 1*8, 8, 8}, 63 | {'K', 2*8, 1*8, 8, 8}, 64 | {'L', 3*8, 1*8, 8, 8}, 65 | {'M', 4*8, 1*8, 8, 8}, 66 | {'N', 5*8, 1*8, 8, 8}, 67 | {'O', 6*8, 1*8, 8, 8}, 68 | {'P', 7*8, 1*8, 8, 8}, 69 | {'Q', 0*8, 2*8, 8, 8}, 70 | {'R', 1*8, 2*8, 8, 8}, 71 | {'S', 2*8, 2*8, 8, 8}, 72 | {'T', 3*8, 2*8, 8, 8}, 73 | {'U', 4*8, 2*8, 8, 8}, 74 | {'V', 5*8, 2*8, 8, 8}, 75 | {'W', 6*8, 2*8, 8, 8}, 76 | {'X', 7*8, 2*8, 8, 8}, 77 | {'Y', 0*8, 3*8, 8, 8}, 78 | {'Z', 1*8, 3*8, 8, 8}, 79 | {'a', 0*8, 0*8, 8, 8}, 80 | {'b', 1*8, 0*8, 8, 8}, 81 | {'c', 2*8, 0*8, 8, 8}, 82 | {'d', 3*8, 0*8, 8, 8}, 83 | {'e', 4*8, 0*8, 8, 8}, 84 | {'f', 5*8, 0*8, 8, 8}, 85 | {'g', 6*8, 0*8, 8, 8}, 86 | {'h', 7*8, 0*8, 8, 8}, 87 | {'i', 0*8, 1*8, 8, 8}, 88 | {'j', 1*8, 1*8, 8, 8}, 89 | {'k', 2*8, 1*8, 8, 8}, 90 | {'l', 3*8, 1*8, 8, 8}, 91 | {'m', 4*8, 1*8, 8, 8}, 92 | {'n', 5*8, 1*8, 8, 8}, 93 | {'o', 6*8, 1*8, 8, 8}, 94 | {'p', 7*8, 1*8, 8, 8}, 95 | {'q', 0*8, 2*8, 8, 8}, 96 | {'r', 1*8, 2*8, 8, 8}, 97 | {'s', 2*8, 2*8, 8, 8}, 98 | {'t', 3*8, 2*8, 8, 8}, 99 | {'u', 4*8, 2*8, 8, 8}, 100 | {'v', 5*8, 2*8, 8, 8}, 101 | {'w', 6*8, 2*8, 8, 8}, 102 | {'x', 7*8, 2*8, 8, 8}, 103 | {'y', 0*8, 3*8, 8, 8}, 104 | {'z', 1*8, 3*8, 8, 8}, 105 | {'0', 2*8, 3*8, 8, 8}, 106 | {'1', 3*8, 3*8, 8, 8}, 107 | {'2', 4*8, 3*8, 8, 8}, 108 | {'3', 5*8, 3*8, 8, 8}, 109 | {'4', 6*8, 3*8, 8, 8}, 110 | {'5', 7*8, 3*8, 8, 8}, 111 | {'6', 0*8, 4*8, 8, 8}, 112 | {'7', 1*8, 4*8, 8, 8}, 113 | {'8', 2*8, 4*8, 8, 8}, 114 | {'9', 3*8, 4*8, 8, 8}, 115 | {'.', 4*8, 4*8, 8, 8}, 116 | {'?', 5*8, 4*8, 8, 8}, 117 | {'!', 6*8, 4*8, 8, 8}, 118 | {',', 7*8, 4*8, 8, 8}, 119 | {'@', 0*8, 5*8, 8, 8}, 120 | {'[', 1*8, 5*8, 8, 8}, 121 | {']', 2*8, 5*8, 8, 8}, 122 | {'\'', 3*8, 5*8, 8, 8}, 123 | {'\"', 4*8, 5*8, 8, 8}, 124 | {'\\', 5*8, 5*8, 8, 8}, 125 | {'+', 6*8, 5*8, 8, 8}, 126 | {'-', 7*8, 5*8, 8, 8}, 127 | {'=', 0*8, 6*8, 8, 8}, 128 | {'/', 1*8, 6*8, 8, 8}, 129 | {'(', 2*8, 6*8, 8, 8}, 130 | {')', 3*8, 6*8, 8, 8}, 131 | }; 132 | 133 | struct Font gGBFont; 134 | 135 | void initGBFont() 136 | { 137 | initFont(&gGBFont, SPRITE_FONT_LAYER, 8, gGBFontDef, sizeof(gGBFontDef) / sizeof(*gGBFontDef)); 138 | } -------------------------------------------------------------------------------- /src/spritefont.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _SPRITEFONT_H 3 | #define _SPRITEFONT_H 4 | 5 | #define ANSI_CHAR_COUNT 128 6 | 7 | #include "sprite.h" 8 | 9 | struct CharacterDefinition 10 | { 11 | char character; 12 | struct SpriteTile data; 13 | }; 14 | 15 | struct Font 16 | { 17 | struct SpriteTile characters[ANSI_CHAR_COUNT]; 18 | short spriteLayer; 19 | short spaceWidth; 20 | }; 21 | 22 | void initFont(struct Font* font, int layer, int spaceWidth, struct CharacterDefinition* chars, int charCount); 23 | void renderText(struct Font* font, const char* str, int x, int y, int scaleShift); 24 | 25 | void initGBFont(); 26 | extern struct Font gGBFont; 27 | 28 | #endif -------------------------------------------------------------------------------- /src/test/cpu_test.c: -------------------------------------------------------------------------------- 1 | #include "cpu_test.h" 2 | #include "../gameboy.h" 3 | #include "../../memory.h" 4 | 5 | int testInt( 6 | char *testName, 7 | char *testOutput, 8 | int actual, 9 | int expected 10 | ) { 11 | if (actual != expected) 12 | { 13 | sprintf(testOutput, "Failed %s:\n E %d A %d", testName, expected, actual); 14 | return 0; 15 | } 16 | 17 | return 1; 18 | } 19 | 20 | int testCPUState( 21 | char *testName, 22 | char *testOutput, 23 | struct CPUState* actual, 24 | struct CPUState* expected 25 | ) { 26 | if (actual->a != expected->a || actual->f != expected->f || actual->b != expected->b || actual->c != expected->c || 27 | actual->d != expected->d || actual->e != expected->e || actual->h != expected->h || actual->l != expected->l || 28 | actual->pc != expected->pc || actual->sp != expected->sp || actual->stopReason != expected->stopReason || 29 | actual->interrupts != expected->interrupts) 30 | { 31 | sprintf(testOutput, 32 | "Failed cpu: %s\n" 33 | " A F B C D E H L PC SP\n" 34 | " E %02X %02X %02X %02X %02X %02X %02X %02X %04X %04X\n" 35 | " A %02X %02X %02X %02X %02X %02X %02X %02X %04X %04X\n" 36 | " ASR %d ESR %d AI %X EI %X", 37 | testName, 38 | expected->a, expected->f, expected->b, expected->c, 39 | expected->d, expected->e, expected->h, expected->l, 40 | expected->pc, expected->sp, 41 | actual->a, actual->f, actual->b, actual->c, 42 | actual->d, actual->e, actual->h, actual->l, 43 | actual->pc, actual->sp, 44 | actual->stopReason, expected->stopReason, 45 | actual->interrupts, expected->interrupts 46 | ); 47 | 48 | return 0; 49 | } 50 | 51 | return 1; 52 | } 53 | 54 | char* registerNames[] = { 55 | "B", 56 | "C", 57 | "D", 58 | "E", 59 | "H", 60 | "L", 61 | "HL", 62 | "A", 63 | "d8", 64 | }; 65 | 66 | int registerOffset[] = { 67 | offsetof(struct CPUState, b), 68 | offsetof(struct CPUState, c), 69 | offsetof(struct CPUState, d), 70 | offsetof(struct CPUState, e), 71 | offsetof(struct CPUState, h), 72 | offsetof(struct CPUState, l), 73 | 0, 74 | offsetof(struct CPUState, a), 75 | 0, 76 | }; 77 | 78 | unsigned char* getRegisterPointer(struct CPUState* cpu, unsigned char* hlTarget, unsigned char* d8Target, int registerIndex) 79 | { 80 | if (registerIndex == HL_REGISTER_INDEX) 81 | { 82 | return hlTarget; 83 | } 84 | else if (registerIndex == d8_REGISTER_INDEX) 85 | { 86 | return d8Target; 87 | } 88 | else 89 | { 90 | return (unsigned char*)cpu + registerOffset[registerIndex]; 91 | } 92 | } 93 | 94 | void BankSwitchingWriter(struct Memory* memory, int addr, int value) 95 | { 96 | 97 | } 98 | 99 | int runCPUTests(char* testOutput) { 100 | struct CPUState cpu; 101 | int i; 102 | char subTestOutput[200]; 103 | 104 | zeroMemory(&gGameboy.memory, sizeof(struct Memory)); 105 | 106 | for (i = 0; i < MEMORY_MAP_SIZE; ++i) 107 | { 108 | setMemoryBank(&gGameboy.memory, i, gGameboy.memory.internalRam, 0, 0); 109 | } 110 | 111 | gGameboy.memory.bankSwitch = &BankSwitchingWriter; 112 | 113 | initializeCPU(&cpu); 114 | 115 | if ( 116 | !run0x0Tests(&cpu, &gGameboy.memory, subTestOutput) || 117 | !run0x1Tests(&cpu, &gGameboy.memory, subTestOutput) || 118 | !run0x2Tests(&cpu, &gGameboy.memory, subTestOutput) || 119 | !run0x3Tests(&cpu, &gGameboy.memory, subTestOutput) || 120 | !run0x4_7Tests(&cpu, &gGameboy.memory, subTestOutput) || 121 | !run0x8_9Tests(&cpu, &gGameboy.memory, subTestOutput) || 122 | !run0xA_BTests(&cpu, &gGameboy.memory, subTestOutput) || 123 | !run0xCTests(&cpu, &gGameboy.memory, subTestOutput) || 124 | !run0xDTests(&cpu, &gGameboy.memory, subTestOutput) || 125 | !run0xETests(&cpu, &gGameboy.memory, subTestOutput) || 126 | !run0xFTests(&cpu, &gGameboy.memory, subTestOutput) || 127 | !runRegisterTests(&cpu, &gGameboy.memory, subTestOutput) || 128 | !runInterruptTests(&cpu, &gGameboy.memory, subTestOutput) || 129 | 0) 130 | { 131 | sprintf(testOutput, "runCPU 0x%X\n%s", &runCPU, subTestOutput); 132 | return 0; 133 | } 134 | 135 | return 1; 136 | } -------------------------------------------------------------------------------- /src/test/cpu_test.h: -------------------------------------------------------------------------------- 1 | 2 | #include "../cpu.h" 3 | #include "../instructions.h" 4 | #include "../../memory.h" 5 | 6 | #define LOAD_PROGRAM(dest, src) memCopy(dest, src, sizeof(src)); 7 | 8 | int testCPUState( 9 | char *testName, 10 | char *testOutput, 11 | struct CPUState* actual, 12 | struct CPUState* expected 13 | ); 14 | 15 | int testInt( 16 | char *testName, 17 | char *testOutput, 18 | int actual, 19 | int expected 20 | ); 21 | 22 | int testRST(struct CPUState* cpu, struct Memory* memory, char* testOutput, int instruction, int targetAddress); 23 | 24 | unsigned char* getRegisterPointer(struct CPUState* cpu, unsigned char* hlTarget, unsigned char* d8Target, int registerIndex); 25 | 26 | int testSingleADD( 27 | struct CPUState* cpu, 28 | struct Memory* memory, 29 | char* testOutput, 30 | int srcRegister, 31 | int baseInstruction, 32 | int cFlag 33 | ); 34 | 35 | int testSingleBitwise( 36 | struct CPUState* cpu, 37 | struct Memory* memory, 38 | char* testOutput, 39 | int srcRegister, 40 | int baseInstruction 41 | ); 42 | 43 | int run0x0Tests(struct CPUState* cpu, struct Memory* memory, char* testOutput); 44 | int run0x1Tests(struct CPUState* cpu, struct Memory* memory, char* testOutput); 45 | int run0x2Tests(struct CPUState* cpu, struct Memory* memory, char* testOutput); 46 | int run0x3Tests(struct CPUState* cpu, struct Memory* memory, char* testOutput); 47 | int run0x4_7Tests(struct CPUState* cpu, struct Memory* memory, char* testOutput); 48 | int run0x8_9Tests(struct CPUState* cpu, struct Memory* memory, char* testOutput); 49 | int run0xA_BTests(struct CPUState* cpu, struct Memory* memory, char* testOutput); 50 | int runPrefixCBTests(struct CPUState* cpu, struct Memory* memory, char* testOutput); 51 | int run0xCTests(struct CPUState* cpu, struct Memory* memory, char* testOutput); 52 | int run0xDTests(struct CPUState* cpu, struct Memory* memory, char* testOutput); 53 | int run0xETests(struct CPUState* cpu, struct Memory* memory, char* testOutput); 54 | int run0xFTests(struct CPUState* cpu, struct Memory* memory, char* testOutput); 55 | int runRegisterTests(struct CPUState* cpu, struct Memory* memory, char* testOutput); 56 | int runInterruptTests(struct CPUState* cpu, struct Memory* memory, char* testOutput); 57 | 58 | extern char* registerNames[]; 59 | extern int registerOffset[]; 60 | 61 | int runCPUTests(char* testOutput); 62 | 63 | #define REGISTER_COUNT 8 64 | #define B_REGISTER_INDEX 0 65 | #define C_REGISTER_INDEX 1 66 | #define D_REGISTER_INDEX 2 67 | #define E_REGISTER_INDEX 3 68 | #define H_REGISTER_INDEX 4 69 | #define L_REGISTER_INDEX 5 70 | #define HL_REGISTER_INDEX 6 71 | #define A_REGISTER_INDEX 7 72 | #define d8_REGISTER_INDEX 8 73 | -------------------------------------------------------------------------------- /src/test/cpu_tests_4_7.c: -------------------------------------------------------------------------------- 1 | 2 | #include "cpu_test.h" 3 | 4 | int testLD_X_Y(struct CPUState* cpu, struct Memory* memory, char* testOutput) 5 | { 6 | int run; 7 | int expectedRunLength; 8 | int expectedValue; 9 | struct CPUState expected; 10 | int targetRegister; 11 | int srcRegister; 12 | unsigned char* targetByte; 13 | unsigned char* srcByte; 14 | char instructionName[10]; 15 | 16 | for (srcRegister = 0; srcRegister < REGISTER_COUNT; ++srcRegister) 17 | { 18 | for (targetRegister = 0; targetRegister < REGISTER_COUNT; ++targetRegister) 19 | { 20 | if (srcRegister == HL_REGISTER_INDEX && targetRegister == HL_REGISTER_INDEX) 21 | { 22 | continue; 23 | } 24 | 25 | if (srcRegister == H_REGISTER_INDEX) 26 | { 27 | expectedValue = 0x80; 28 | } 29 | else if (srcRegister == L_REGISTER_INDEX) 30 | { 31 | expectedValue = 0x20; 32 | } 33 | else 34 | { 35 | expectedValue = 0x63; 36 | } 37 | 38 | srcByte = getRegisterPointer(cpu, memory->internalRam + 0x20, memory->internalRam + 1, srcRegister); 39 | targetByte = getRegisterPointer(&expected, memory->internalRam + 0x20, memory->internalRam + 1, targetRegister); 40 | 41 | initializeCPU(cpu); 42 | memory->internalRam[0x20] = 0x0; 43 | cpu->h = 0x80; 44 | cpu->l = 0x20; 45 | *srcByte = expectedValue; 46 | expected = *cpu; 47 | expected.pc = 1; 48 | *targetByte = expectedValue; 49 | expectedRunLength = (srcRegister == HL_REGISTER_INDEX || targetRegister == HL_REGISTER_INDEX) ? 2 : 1; 50 | 51 | memory->internalRam[0] = CPU_LD_B_B + REGISTER_COUNT * targetRegister + srcRegister; 52 | 53 | run = runCPU(cpu, memory, 1, 0); 54 | 55 | sprintf(instructionName, "LD %s %s", registerNames[targetRegister], registerNames[srcRegister]); 56 | 57 | if ( 58 | !testCPUState(instructionName, testOutput, cpu, &expected) || 59 | !testInt(instructionName, testOutput, run, expectedRunLength) || 60 | !testInt(instructionName, testOutput, *targetByte, expectedValue) 61 | ) 62 | { 63 | return 0; 64 | } 65 | } 66 | } 67 | 68 | return 1; 69 | } 70 | 71 | int testHALT(struct CPUState* cpu, struct Memory* memory, char* testOutput) 72 | { 73 | int run; 74 | struct CPUState expected; 75 | initializeCPU(cpu); 76 | expected = *cpu; 77 | expected.pc = 1; 78 | expected.stopReason = STOP_REASON_HALT; 79 | 80 | memory->internalRam[0] = CPU_HALT; 81 | 82 | run = runCPU(cpu, memory, 1, 0); 83 | 84 | return 85 | testCPUState("HALT", testOutput, cpu, &expected) && 86 | testInt("HALT run result", testOutput, run, 1) && 87 | 1 88 | ; 89 | } 90 | 91 | int run0x4_7Tests(struct CPUState* cpu, struct Memory* memory, char* testOutput) 92 | { 93 | return 94 | testLD_X_Y(cpu, memory, testOutput) && 95 | // testHALT(cpu, memory, testOutput) && 96 | 1 97 | ; 98 | } -------------------------------------------------------------------------------- /src/test/cpu_tests_A_B.c: -------------------------------------------------------------------------------- 1 | 2 | #include "cpu_test.h" 3 | 4 | int testSingleBitwise( 5 | struct CPUState* cpu, 6 | struct Memory* memory, 7 | char* testOutput, 8 | int srcRegister, 9 | int baseInstruction 10 | ) 11 | { 12 | int run; 13 | unsigned char* srcByte; 14 | char instructionName[32]; 15 | struct CPUState expected; 16 | int aValue; 17 | int srcValue; 18 | int expectedRunLength; 19 | 20 | for (aValue = 0x7; aValue < 0x100; aValue = aValue + 0x08) 21 | { 22 | for (srcValue = 0x7; srcValue < 0x100; srcValue = srcValue + 0x08) 23 | { 24 | if (srcRegister == A_REGISTER_INDEX) 25 | { 26 | srcValue = aValue; 27 | } 28 | 29 | srcByte = getRegisterPointer(cpu, memory->internalRam + 0x20, memory->internalRam + 1, srcRegister); 30 | 31 | initializeCPU(cpu); 32 | cpu->a = aValue; 33 | cpu->h = 0x80; 34 | cpu->l = 0x20; 35 | *srcByte = srcValue; 36 | expected = *cpu; 37 | expected.pc = 1; 38 | expectedRunLength = (srcRegister == HL_REGISTER_INDEX || srcRegister == d8_REGISTER_INDEX) ? 2 : 1; 39 | 40 | if (baseInstruction >= 0xC0) 41 | { 42 | expected.pc = 2; 43 | memory->internalRam[0] = baseInstruction; 44 | } 45 | else 46 | { 47 | memory->internalRam[0] = baseInstruction + srcRegister; 48 | } 49 | 50 | if (baseInstruction == CPU_AND_A_B || baseInstruction == CPU_AND_A_d8) 51 | { 52 | expected.a = aValue & srcValue; 53 | expected.f = GB_FLAGS_H; 54 | sprintf(instructionName, "AND A (%X) %s (%X)", aValue, registerNames[srcRegister], srcValue); 55 | } 56 | else if (baseInstruction == CPU_XOR_A_B || baseInstruction == CPU_XOR_A_d8) 57 | { 58 | expected.a = aValue ^ srcValue; 59 | sprintf(instructionName, "XOR A (%X) %s (%X)", aValue, registerNames[srcRegister], srcValue); 60 | } 61 | else if (baseInstruction == CPU_OR_A_B || baseInstruction == CPU_OR_A_d8) 62 | { 63 | expected.a = aValue | srcValue; 64 | sprintf(instructionName, "OR A (%X) %s (%X)", aValue, registerNames[srcRegister], srcValue); 65 | } 66 | else 67 | { 68 | sprintf(testOutput, "Invalid instruction base %X", baseInstruction); 69 | return 0; 70 | } 71 | 72 | if (expected.a == 0) 73 | { 74 | expected.f |= GB_FLAGS_Z; 75 | } 76 | 77 | run = runCPU(cpu, memory, 1, 0); 78 | 79 | if ( 80 | !testCPUState(instructionName, testOutput, cpu, &expected) || 81 | !testInt(instructionName, testOutput, run, expectedRunLength) 82 | ) 83 | { 84 | return 0; 85 | } 86 | 87 | if (srcRegister == A_REGISTER_INDEX) 88 | { 89 | break; 90 | } 91 | } 92 | } 93 | 94 | return 1; 95 | } 96 | 97 | int testAND_XOR_OR(struct CPUState* cpu, struct Memory* memory, char* testOutput) 98 | { 99 | int srcRegister; 100 | 101 | for (srcRegister = 0; srcRegister < REGISTER_COUNT; ++srcRegister) 102 | { 103 | if ( 104 | !testSingleBitwise( cpu, memory, testOutput, srcRegister, CPU_AND_A_B) || 105 | !testSingleBitwise( cpu, memory, testOutput, srcRegister, CPU_XOR_A_B) || 106 | !testSingleBitwise( cpu, memory, testOutput, srcRegister, CPU_OR_A_B) || 107 | 0 108 | ) 109 | { 110 | return 0; 111 | } 112 | } 113 | 114 | return 1; 115 | } 116 | 117 | int testCP(struct CPUState* cpu, struct Memory* memory, char* testOutput) 118 | { 119 | int srcRegister; 120 | 121 | for (srcRegister = 0; srcRegister < REGISTER_COUNT; ++srcRegister) 122 | { 123 | if ( 124 | !testSingleADD( cpu, memory, testOutput, srcRegister, CPU_CP_A_B, 0) || 125 | !testSingleADD( cpu, memory, testOutput, srcRegister, CPU_CP_A_B, 1) || 126 | 0 127 | ) 128 | { 129 | return 0; 130 | } 131 | } 132 | 133 | return 1; 134 | } 135 | 136 | int run0xA_BTests(struct CPUState* cpu, struct Memory* memory, char* testOutput) 137 | { 138 | return 139 | testCP(cpu, memory, testOutput) && 140 | testAND_XOR_OR(cpu, memory, testOutput) && 141 | 1 142 | ; 143 | } -------------------------------------------------------------------------------- /src/test/interrupt_test.c: -------------------------------------------------------------------------------- 1 | #include "cpu_test.h" 2 | 3 | unsigned char timerInterruptProgram[] = { 4 | // enable timer interrupt 5 | CPU_LD_A_d8, 6 | GB_INTERRUPTS_TIMER, 7 | CPU_LDH_a8_A, 8 | REG_INT_ENABLED & 0xFF, 9 | 10 | // set TIMA to 0xFF 11 | CPU_LD_A_d8, 12 | 0xFF, 13 | CPU_LDH_a8_A, 14 | REG_TIMA & 0xFF, 15 | 16 | // set TAC to 0x5 17 | CPU_LD_A_d8, 18 | 0x5, 19 | CPU_LDH_a8_A, 20 | REG_TAC & 0xFF, 21 | 22 | CPU_LD_A_d8, 23 | 0, 24 | 25 | // infinate loop 26 | CPU_NOP, 27 | CPU_JR, 28 | -3 29 | }; 30 | 31 | int runTimerInterruptTest(struct CPUState* cpu, struct Memory* memory, char* testOutput) 32 | { 33 | initializeCPU(cpu); 34 | cpu->a = 0; 35 | cpu->interrupts = GB_INTERRUPTS_ENABLED; 36 | cpu->sp = 0xFFFE; 37 | 38 | LOAD_PROGRAM(memory->internalRam, timerInterruptProgram); 39 | 40 | memory->internalRam[0x50] = CPU_INC_A; 41 | memory->internalRam[0x51] = CPU_RETI; 42 | 43 | runCPU(cpu, memory, 19, 0); 44 | 45 | if (!testInt("INT TIMER", testOutput, 46 | cpu->pc, 47 | 0x50) || 48 | !testInt("INT TIMER interrupts", testOutput, 49 | cpu->interrupts, 50 | 0) 51 | ) 52 | { 53 | return 0; 54 | } 55 | 56 | runCPU(cpu, memory, 4, 0); 57 | 58 | if (!testInt("INT TIMER CALLED", testOutput, 59 | cpu->a, 60 | 1) || 61 | !testInt("INT TIMER CALLED interrupts", testOutput, 62 | cpu->interrupts, 63 | GB_INTERRUPTS_ENABLED) 64 | ) 65 | { 66 | return 0; 67 | } 68 | 69 | runCPU(cpu, memory, 0x400, 0); 70 | 71 | return testInt("INT TIMER TWICE", testOutput, 72 | cpu->a, 73 | 2); 74 | } 75 | 76 | int runInterruptTests(struct CPUState* cpu, struct Memory* memory, char* testOutput) 77 | { 78 | return 79 | runTimerInterruptTest(cpu, memory, testOutput) && 80 | 1; 81 | } -------------------------------------------------------------------------------- /src/test/register_test.c: -------------------------------------------------------------------------------- 1 | #include "cpu_test.h" 2 | 3 | int runJOYPTests(struct CPUState* cpu, struct Memory* memory, char* testOutput) 4 | { 5 | initializeCPU(cpu); 6 | cpu->a = 0x10; 7 | 8 | WRITE_REGISTER_DIRECT(memory, _REG_JOYSTATE, 0xAB); 9 | 10 | memory->internalRam[0] = CPU_LDH_a8_A; 11 | memory->internalRam[1] = 0x00; 12 | memory->internalRam[2] = CPU_LD_A_d8; 13 | memory->internalRam[3] = 0x20; 14 | memory->internalRam[4] = CPU_LDH_a8_A; 15 | memory->internalRam[5] = 0x00; 16 | 17 | runCPU(cpu, memory, 2, 0); 18 | 19 | if (!testInt("JOYP", testOutput, 20 | READ_REGISTER_DIRECT(memory, REG_JOYP), 21 | 0x1A) 22 | ) 23 | { 24 | return 0; 25 | } 26 | 27 | runCPU(cpu, memory, 4, 0); 28 | 29 | return testInt("JOYP select second", testOutput, 30 | READ_REGISTER_DIRECT(memory, REG_JOYP), 31 | 0x2B 32 | ); 33 | } 34 | 35 | int runDIVTests(struct CPUState* cpu, struct Memory* memory, char* testOutput) 36 | { 37 | initializeCPU(cpu); 38 | cpu->a = 0; 39 | 40 | WRITE_REGISTER_DIRECT(memory, _REG_JOYSTATE, 0xAB); 41 | 42 | memory->internalRam[0] = CPU_INC_A; 43 | memory->internalRam[1] = CPU_JR_NZ; 44 | memory->internalRam[2] = -3; 45 | memory->internalRam[3] = CPU_INC_A; 46 | memory->internalRam[4] = CPU_LDH_a8_A; 47 | memory->internalRam[5] = 0x04; 48 | memory->internalRam[6] = CPU_JR_NZ; 49 | memory->internalRam[7] = -5; 50 | 51 | runCPU(cpu, memory, 512, 0); 52 | 53 | if (!testInt("DIV", testOutput, 54 | READ_REGISTER_DIRECT(memory, REG_DIV), 55 | 0x8) 56 | ) 57 | { 58 | return 0; 59 | } 60 | 61 | cpu->pc = 3; 62 | 63 | runCPU(cpu, memory, 512, 0); 64 | 65 | return testInt("DIV select second", testOutput, 66 | READ_REGISTER_DIRECT(memory, REG_DIV), 67 | 0x0 68 | ); 69 | } 70 | 71 | int runSVBKTests(struct CPUState* cpu, struct Memory* memory, char* testOutput) 72 | { 73 | initializeCPU(cpu); 74 | cpu->a = 0x3; 75 | 76 | memory->internalRam[0] = CPU_LDH_a8_A; 77 | memory->internalRam[1] = 0x70; 78 | memory->internalRam[2] = CPU_LD_A_d8; 79 | memory->internalRam[3] = 0; 80 | memory->internalRam[4] = CPU_LDH_a8_A; 81 | memory->internalRam[5] = 0x70; 82 | 83 | runCPU(cpu, memory, 2, 0); 84 | 85 | if (!testInt("SVBK ram bank", testOutput, 86 | (int)getMemoryBank(memory, MEMORY_RAM_BANK_INDEX), 87 | (int)memory->internalRam + 0x3000) 88 | ) 89 | { 90 | return 0; 91 | } 92 | 93 | runCPU(cpu, memory, 4, 0); 94 | 95 | return testInt("SVBK ram bank 0 bank", testOutput, 96 | (int)getMemoryBank(memory, MEMORY_RAM_BANK_INDEX), 97 | (int)memory->internalRam + 0x1000 98 | ); 99 | } 100 | 101 | int runRegisterTests(struct CPUState* cpu, struct Memory* memory, char* testOutput) 102 | { 103 | return 104 | runJOYPTests(cpu, memory, testOutput) && 105 | runDIVTests(cpu, memory, testOutput) && 106 | runSVBKTests(cpu, memory, testOutput) && 107 | 1; 108 | } -------------------------------------------------------------------------------- /src/test/test.c: -------------------------------------------------------------------------------- 1 | 2 | #include "cpu_test.h" 3 | 4 | int runTests(char* testOutput) 5 | { 6 | if ( 7 | !runCPUTests(testOutput) 8 | ) 9 | { 10 | return 0; 11 | } 12 | 13 | sprintf(testOutput, "Tests Passed %X", &runCPU); 14 | 15 | return 1; 16 | } -------------------------------------------------------------------------------- /src/test/test.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _TEST_H 3 | #define _TEST_H 4 | 5 | int runTests(char* testOutput); 6 | 7 | #endif -------------------------------------------------------------------------------- /src/upgrade.c: -------------------------------------------------------------------------------- 1 | 2 | #include "upgrade.h" 3 | #include "save.h" 4 | 5 | void updateToLatestVersion(struct GameBoy* gameboy) 6 | { 7 | while (gameboy->settings.version < GB_SETTINGS_CURRENT_VERSION) 8 | { 9 | switch (gameboy->settings.version) 10 | { 11 | case 0: 12 | if (gameboy->memory.mbc->bankSwitch == handleMBC3Write) 13 | { 14 | gameboy->memory.misc.ramRomSelect = gameboy->memory.misc.romBankUpper; 15 | gameboy->memory.misc.romBankUpper = 0; 16 | } 17 | break; 18 | case 1: 19 | gameboy->settings.compressedSize = 0; 20 | gameboy->settings.storedType = getDeprecatedStoredInfoType(gameboy); 21 | break; 22 | } 23 | 24 | ++gameboy->settings.version; 25 | } 26 | } -------------------------------------------------------------------------------- /src/upgrade.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _UPGRADE_H 3 | #define _UPGRADE_H 4 | 5 | #include "gameboy.h" 6 | 7 | void updateToLatestVersion(struct GameBoy* gameboy); 8 | 9 | #endif -------------------------------------------------------------------------------- /src/version.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _VERSION_H 3 | #define _VERISON_H 4 | 5 | #define EMU_VERSION "V 3.2" 6 | 7 | #endif -------------------------------------------------------------------------------- /static.h: -------------------------------------------------------------------------------- 1 | 2 | /************************************************************************** 3 | * * 4 | * Copyright (C) 1995, Silicon Graphics, Inc. * 5 | * * 6 | * These coded instructions, statements, and computer programs contain * 7 | * unpublished proprietary information of Silicon Graphics, Inc., and * 8 | * are protected by Federal copyright law. They may not be disclosed * 9 | * to third parties or copied or duplicated in any form, in whole or * 10 | * in part, without the prior written consent of Silicon Graphics, Inc. * 11 | * * 12 | *************************************************************************/ 13 | 14 | /* 15 | * File: static.h 16 | * Create Date: Thu Dec 14 13:52:23 PST 1995 17 | * 18 | */ 19 | 20 | /* 21 | * static display lists 22 | */ 23 | extern Gfx init_dl[]; 24 | extern Gfx clearcfb_dl[]; 25 | -------------------------------------------------------------------------------- /tex/cbuttons.h: -------------------------------------------------------------------------------- 1 | /* 2 | * this image was converted using the CI converter 3 | * this image is 32 pixels wide by 32 pixels tall 4 | */ 5 | unsigned char __attribute__((aligned(8))) tex_cbuttons[] = { 6 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 7 | 0x0, 0x0, 0x0, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 8 | 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 9 | 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 10 | 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x3, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 11 | 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 12 | 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 13 | 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 14 | 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 15 | 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 16 | 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 17 | 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x3, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 18 | 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 19 | 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 20 | 0x0, 0x0, 0x0, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 21 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 22 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 23 | 0x0, 0x0, 0x0, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 24 | 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 25 | 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 26 | 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 27 | 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 28 | 0x1, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 29 | 0x1, 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x1, 30 | 0x1, 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x1, 31 | 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x1, 32 | 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 33 | 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 34 | 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 35 | 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x0, 0x0, 36 | 0x0, 0x0, 0x0, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 37 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0 38 | }; 39 | -------------------------------------------------------------------------------- /tex/dpad.h: -------------------------------------------------------------------------------- 1 | /* 2 | * this image was converted using the CI converter 3 | * this image is 32 pixels wide by 32 pixels tall 4 | */ 5 | unsigned char __attribute__((aligned(8))) tex_dpad[] = { 6 | 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 7 | 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 8 | 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 9 | 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x4, 0x4, 0x4, 0x4, 0x4, 10 | 0x4, 0x4, 0x4, 0x4, 0x5, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x5, 0x4, 0x4, 0x4, 0x4, 11 | 0x4, 0x4, 0x4, 0x5, 0x6, 0x6, 0x6, 0x6, 0x7, 0x7, 0x6, 0x6, 0x6, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x6, 0x6, 0x6, 0x7, 0x7, 0x6, 0x6, 0x6, 0x6, 0x5, 0x4, 0x4, 0x4, 12 | 0x4, 0x4, 0x4, 0x5, 0x6, 0x6, 0x6, 0x7, 0x7, 0x7, 0x6, 0x6, 0x6, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x6, 0x6, 0x6, 0x7, 0x7, 0x7, 0x6, 0x6, 0x6, 0x5, 0x4, 0x4, 0x4, 13 | 0x4, 0x4, 0x4, 0x5, 0x6, 0x6, 0x7, 0x7, 0x7, 0x7, 0x6, 0x6, 0x6, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x6, 0x6, 0x6, 0x7, 0x7, 0x7, 0x7, 0x6, 0x6, 0x5, 0x4, 0x4, 0x4, 14 | 0x4, 0x4, 0x4, 0x5, 0x6, 0x6, 0x7, 0x7, 0x7, 0x7, 0x6, 0x6, 0x6, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x6, 0x6, 0x6, 0x7, 0x7, 0x7, 0x7, 0x6, 0x6, 0x5, 0x4, 0x4, 0x4, 15 | 0x4, 0x4, 0x4, 0x5, 0x6, 0x6, 0x6, 0x7, 0x7, 0x7, 0x6, 0x6, 0x6, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x6, 0x6, 0x6, 0x7, 0x7, 0x7, 0x6, 0x6, 0x6, 0x5, 0x4, 0x4, 0x4, 16 | 0x4, 0x4, 0x4, 0x5, 0x6, 0x6, 0x6, 0x6, 0x7, 0x7, 0x6, 0x6, 0x6, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x6, 0x6, 0x6, 0x7, 0x7, 0x6, 0x6, 0x6, 0x6, 0x5, 0x4, 0x4, 0x4, 17 | 0x4, 0x4, 0x4, 0x4, 0x5, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x5, 0x4, 0x4, 0x4, 0x4, 18 | 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x4, 0x4, 0x4, 0x4, 0x4, 19 | 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 20 | 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 21 | 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 22 | 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 23 | 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 24 | 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 25 | 0x4, 0x4, 0x4, 0x5, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x5, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x4, 0x4, 0x4, 0x4, 0x4, 26 | 0x4, 0x4, 0x4, 0x5, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x5, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x5, 0x4, 0x4, 0x4, 0x4, 27 | 0x4, 0x4, 0x4, 0x5, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x5, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x5, 0x4, 0x4, 0x4, 28 | 0x4, 0x4, 0x4, 0x5, 0x6, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x6, 0x5, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x6, 0x6, 0x6, 0x7, 0x7, 0x6, 0x6, 0x6, 0x5, 0x4, 0x4, 0x4, 29 | 0x4, 0x4, 0x4, 0x5, 0x6, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x6, 0x5, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x6, 0x6, 0x7, 0x7, 0x7, 0x7, 0x6, 0x6, 0x5, 0x4, 0x4, 0x4, 30 | 0x4, 0x4, 0x4, 0x5, 0x6, 0x6, 0x7, 0x7, 0x7, 0x7, 0x6, 0x6, 0x5, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x6, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x6, 0x5, 0x4, 0x4, 0x4, 31 | 0x4, 0x4, 0x4, 0x5, 0x6, 0x6, 0x6, 0x7, 0x7, 0x6, 0x6, 0x6, 0x5, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x6, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x6, 0x5, 0x4, 0x4, 0x4, 32 | 0x4, 0x4, 0x4, 0x5, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x5, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x5, 0x4, 0x4, 0x4, 33 | 0x4, 0x4, 0x4, 0x4, 0x5, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x5, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x5, 0x4, 0x4, 0x4, 34 | 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x5, 0x4, 0x4, 0x4, 35 | 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 36 | 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 37 | 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4 38 | }; 39 | -------------------------------------------------------------------------------- /tex/textures.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _TEXTURES_H 3 | #define _TEXTURES_H 4 | 5 | #include 6 | 7 | extern char __attribute__((aligned(8))) gbfont_img[]; 8 | extern unsigned char __attribute__((aligned(8))) tex_cbuttons[]; 9 | extern unsigned char __attribute__((aligned(8))) tex_dpad[]; 10 | extern unsigned char __attribute__((aligned(8))) tex_facebuttons[]; 11 | extern unsigned char __attribute__((aligned(8))) tex_guiitems[]; 12 | extern unsigned char __attribute__((aligned(8))) tex_triggers[]; 13 | extern unsigned short __attribute__((aligned(8))) gGUIPalette[]; 14 | 15 | extern Gfx gUseFontTexture[]; 16 | extern Gfx gUseGUIItems[]; 17 | extern Gfx gUseCButtons[]; 18 | extern Gfx gUseDPad[]; 19 | extern Gfx gUseFaceButtons[]; 20 | extern Gfx gUseTriggers[]; 21 | 22 | #endif -------------------------------------------------------------------------------- /texture.c: -------------------------------------------------------------------------------- 1 | 2 | #include "tex/guiitems.h" --------------------------------------------------------------------------------