diff --git a/.github/workflows/deploy_web.yml b/.github/workflows/deploy_web.yml index a881a80ff..201312c8a 100644 --- a/.github/workflows/deploy_web.yml +++ b/.github/workflows/deploy_web.yml @@ -42,3 +42,12 @@ jobs: folder: build/bin # The folder the action should deploy. force: false clean: false + - name: Deploy to dev site 🚀 + if: github.ref == 'refs/heads/dev' + uses: JamesIves/github-pages-deploy-action@v4 + with: + branch: gh-pages # The branch the action should deploy to. + folder: build/bin # The folder the action should deploy. + target-folder: branch/dev + force: false + clean: false diff --git a/src/gb.h b/src/gb.h index 811169318..cd0623d83 100644 --- a/src/gb.h +++ b/src/gb.h @@ -3,6 +3,7 @@ #define SB_IO_JOYPAD 0xff00 #define SB_IO_SERIAL_BYTE 0xff01 +#define SB_IO_SERIAL_CTRL 0xff02 #define SB_IO_DIV 0xff04 #define SB_IO_TIMA 0xff05 #define SB_IO_TMA 0xff06 @@ -86,14 +87,20 @@ mmio_reg_t gb_io_reg_desc[]={ { 1, 1, "Input: Left or B (0=Pressed) (Read Only)"}, { 0, 1, "Input: Right or A (0=Pressed) (Read Only)"}, }}, - { SB_IO_SERIAL_BYTE, "SERIAL_BYTE", { + { SB_IO_SERIAL_CTRL, "SERIAL_CTRL", { { 7, 1, "Transfer Start Flag (0=No transfer is in progress or requested, 1=Transfer in progress, or requested)"}, { 1, 1, "Clock Speed (0=Normal, 1=Fast) ** CGB Mode Only **"}, { 0, 1, "Shift Clock (0=External Clock, 1=Internal Clock)"}, }}, - { SB_IO_DIV, "DIV", { 0 }}, - { SB_IO_TIMA, "TIMA", { 0 }}, - { SB_IO_TMA, "TMA", { 0 }}, + { SB_IO_DIV, "DIV", { + { 0 ,8, "Div Value"}, + }}, + { SB_IO_TIMA, "TIMA", { + { 0 ,8, "Timer Value"}, + }}, + { SB_IO_TMA, "TMA", { + { 0 ,8, "Timer Modulo"}, + }}, { SB_IO_TAC, "TAC", { { 2 ,1, "Timer Enable"}, { 0, 2, "Clock Divider (0: Clk/1024 1: Clk/16 2: Clk/64 3: Clk/256)"} @@ -264,18 +271,33 @@ void sb_lookup_palette_color(sb_gb_t*gb,int color_id, int*r, int *g, int *b); static FORCE_INLINE void sb_process_audio(sb_gb_t *gb, sb_emu_state_t*emu, double delta_time); static FORCE_INLINE uint8_t sb_read8_direct(sb_gb_t *gb, int addr) { - if(addr>=0x4000&&addr<=0x7fff){ - int bank = gb->cart.mapped_rom_bank; - bank %= (gb->cart.rom_size)/0x4000; - if(bank ==0 && gb->cart.mbc_type!= SB_MBC_MBC5)bank = 1; - unsigned int bank_off = 0x4000*(bank); - return gb->cart.data[addr-0x4000+bank_off]; + if(addr>=0x0000&&addr<=0x3fff){ + int cart_addr = SB_BFE(addr,0,14); + if(gb->cart.bank_mode&&gb->cart.mbc_type==SB_MBC_MBC1){ + cart_addr|=SB_BFE(gb->cart.mapped_ram_bank,0,2)<<19; + } + cart_addr%= (gb->cart.rom_size); + return gb->cart.data[cart_addr]; + }else if(addr>=0x4000&&addr<=0x7fff){ + int cart_addr = SB_BFE(addr,0,14); + cart_addr|=(gb->cart.mapped_rom_bank)<<14; + if(gb->cart.mbc_type==SB_MBC_MBC1){ + cart_addr|=SB_BFE(gb->cart.mapped_ram_bank,0,2)<<19; + } + cart_addr%= (gb->cart.rom_size); + return gb->cart.data[cart_addr]; }else if(addr>=0x8000&&addr<=0x9fff){ uint8_t vbank =sb_read8_direct(gb,SB_IO_GBC_VBK)%SB_VRAM_NUM_BANKS; uint8_t data =gb->lcd.vram[vbank*SB_VRAM_BANK_SIZE+addr-0x8000]; return data; } else if(addr>=0xA000&&addr<=0xBfff){ + if(!gb->cart.ram_write_enable)return 0xff; int ram_addr_off = 0x2000*gb->cart.mapped_ram_bank+(addr-0xA000); + if(gb->cart.mbc_type==SB_MBC_MBC1){ + ram_addr_off = SB_BFE(addr,0,13); + if(gb->cart.bank_mode)ram_addr_off|= SB_BFE(gb->cart.mapped_ram_bank,0,2)<<13; + } + ram_addr_off%=gb->cart.ram_size; return gb->cart.ram_data[ram_addr_off]; }else if(addr>=0xD000&&addr<=0xDfff){ int bank =gb->mem.data[SB_IO_GBC_SVBK]%SB_WRAM_NUM_BANKS; @@ -288,6 +310,22 @@ static FORCE_INLINE uint8_t sb_read8_direct(sb_gb_t *gb, int addr) { } return gb->mem.data[addr]; } +uint8_t sb_io_or_mask(int addr){ + if(addr>=SB_IO_AUD1_TONE_SWEEP&&addrcpu.trigger_breakpoint=true; @@ -304,6 +342,7 @@ uint8_t sb_read8(sb_gb_t *gb, int addr) { uint8_t d= sb_read8_direct(gb,addr); return d|0xfe; } + return sb_read8_direct(gb,addr)|sb_io_or_mask(addr); } return sb_read8_direct(gb,addr); } @@ -322,9 +361,14 @@ static FORCE_INLINE void sb_store8_direct(sb_gb_t *gb, int addr, int value) { return; }else if(addr>=0xA000&&addr<=0xBfff){ if(gb->cart.ram_write_enable){ - int ram_addr_off = 0x2000*gb->cart.mapped_ram_bank+(addr-0xA000); + int ram_addr_off = SB_BFE(addr,0,13); + if(gb->cart.mbc_type==SB_MBC_MBC1){ + if(gb->cart.bank_mode)ram_addr_off|= SB_BFE(gb->cart.mapped_ram_bank,0,2)<<13; + }else ram_addr_off|= gb->cart.mapped_ram_bank<<13; + ram_addr_off%=gb->cart.ram_size; gb->cart.ram_data[ram_addr_off]=value; gb->cart.ram_is_dirty = true; + //printf("RAM WRITE: MEM[%04x, %04x] = %02x B:%d BM:%d\n",addr,ram_addr_off,value, gb->cart.mapped_ram_bank,gb->cart.bank_mode); } return; }else if(addr>=0xD000&&addr<=0xDfff){ @@ -389,36 +433,52 @@ void sb_store8(sb_gb_t *gb, int addr, int value) { sb_store8_direct(gb,SB_IO_GBC_OCPS,(index&0x3f)|0x80); } }else if(addr == SB_IO_DIV){ - value = 0; //All writes reset the div timer - sb_store8(gb,SB_IO_TIMA,0);//TIMA and DIV are the same counter + gb->timers.total_clock_ticks = 0; }else if(addr == SB_IO_SERIAL_BYTE){ printf("%c",(char)value); - } else if (addr == SB_IO_SOUND_ON_OFF){ - uint8_t d= sb_read8_direct(gb,addr); - value = (d&0x7f)|(value&0x80); + }else if(addr>=SB_IO_AUD1_TONE_SWEEP&&addraudio.regs_written = true; + if(addr==SB_IO_SOUND_ON_OFF){ + value&=0xf0; + value|=sb_read8_direct(gb,SB_IO_SOUND_ON_OFF)&0xf; + } } }else if(addr >= 0x0000 && addr <=0x1fff){ gb->cart.ram_write_enable = (value&0xf)==0xA; + return; }else if(addr >= 0x2000 && addr <=0x3fff){ //MBC3 rombank select - if(addr>=0x3000&& gb->cart.mbc_type==SB_MBC_MBC5){ - gb->cart.mapped_rom_bank&=0xff; - gb->cart.mapped_rom_bank|= (int)(value&1)<<8; - }else gb->cart.mapped_rom_bank=(gb->cart.mapped_rom_bank&0x100)|value;; + switch(gb->cart.mbc_type){ + case SB_MBC_MBC1: gb->cart.mapped_rom_bank=value%32; if(!gb->cart.mapped_rom_bank)gb->cart.mapped_rom_bank=1;break; + case SB_MBC_MBC2: gb->cart.mapped_rom_bank=value%16; if(!gb->cart.mapped_rom_bank)gb->cart.mapped_rom_bank=1;break; + case SB_MBC_MBC3: gb->cart.mapped_rom_bank=value%256;if(!gb->cart.mapped_rom_bank)gb->cart.mapped_rom_bank=1;break; + case SB_MBC_MBC5: + case SB_MBC_MBC7: + if(addr>=0x3000){ + gb->cart.mapped_rom_bank&=0xff; + gb->cart.mapped_rom_bank|= (int)(value&1)<<8; + }else gb->cart.mapped_rom_bank=(gb->cart.mapped_rom_bank&0x100)|value; + break; + } return; }else if(addr >= 0x4000 && addr <=0x5fff){ gb->cart.rumble = false; if((value&(1<<3))&&gb->cart.has_rumble)gb->cart.rumble=true; //MBC3 rombank select //TODO implement other mappers - value %= (gb->cart.ram_size/0x2000); + if(gb->cart.mbc_type!=SB_MBC_MBC1)value %= (gb->cart.ram_size/0x2000); + else value%=4; gb->cart.mapped_ram_bank = value; return; }else if (addr>=0xfe00 && addr<=0xfe9f ){ //OAM cannot be written to in mode 2 and 3 int stat = sb_read8_direct(gb, SB_IO_LCD_STAT)&0x3; if(stat>=2) return; - } else if (addr>=0x8000 && addr <=0x9fff){ + } else if(addr>=0x6000&&addr<=0x7fff){ + if(gb->cart.mbc_type==SB_MBC_MBC1){ + gb->cart.bank_mode = SB_BFE(value,0,1); + } + }else if (addr>=0x8000 && addr <=0x9fff){ //VRAM cannot be writen to in mode 3 int stat = sb_read8_direct(gb, SB_IO_LCD_STAT)&0x3; if(stat>=3) return; @@ -464,6 +524,13 @@ void sb_update_joypad_io_reg(sb_emu_state_t* state, sb_gb_t*gb){ if(0 == (data & (1<<4))) data |= data_dir; if(0 == (data & (1<<5))) data |= data_action; + switch(SB_BFE(data,4,2)){ + case 0: data|=data_dir|data_action; break; + case 1: data|=data_action; break; + case 2: data|=data_dir; break; + case 3: data|=0xf; break; + } + gb->mem.data[SB_IO_JOYPAD] = data; } @@ -626,11 +693,9 @@ void sb_lookup_palette_color(sb_gb_t*gb,int color_id, int*r, int *g, int *b){ else palette = color_id ==0 ? 0 : sb_read8_direct(gb, SB_IO_PPU_OBP0); color_id = SB_BFE(palette,2*(color_id&0x3),2); - const uint32_t palette_dmg_green[4*3] = { 0x81,0x8F,0x38,0x64,0x7D,0x43,0x56,0x6D,0x3F,0x31,0x4A,0x2D }; - - *r = palette_dmg_green[color_id*3+0]; - *g = palette_dmg_green[color_id*3+1]; - *b = palette_dmg_green[color_id*3+2]; + *r = gb->dmg_palette[color_id*3+0]; + *g = gb->dmg_palette[color_id*3+1]; + *b = gb->dmg_palette[color_id*3+2]; }else if(gb->model == SB_GBC){ int palette = SB_BFE(color_id,2,6); @@ -791,36 +856,33 @@ void sb_update_timers(sb_gb_t* gb, int delta_clocks){ uint8_t tac = sb_read8_direct(gb, SB_IO_TAC); bool tima_enable = SB_BFE(tac, 2, 1); int clk_sel = SB_BFE(tac, 0, 2); - gb->timers.clocks_till_div_inc -=delta_clocks; - int period = 4*1024*1024/16384; - while(gb->timers.clocks_till_div_inc<=0){ - gb->timers.clocks_till_div_inc += period; - uint8_t d = sb_read8_direct(gb, SB_IO_DIV); - sb_store8_direct(gb, SB_IO_DIV, d+1); - } - if(tima_enable)gb->timers.clocks_till_tima_inc -=delta_clocks; - period =0; + int tma_bit = 0; switch(clk_sel){ - case 0: period = 1024; break; - case 1: period = 16; break; - case 2: period = 64; break; - case 3: period = 256; break; + case 0: tma_bit = 9;break; //4khz + case 1: tma_bit = 3;break; //256khz + case 2: tma_bit = 5;break; //64Khz + case 3: tma_bit = 7;break; //16Khz } - while(gb->timers.clocks_till_tima_inc<=0){ - gb->timers.clocks_till_tima_inc += period; - - uint8_t d = sb_read8_direct(gb, SB_IO_TIMA); - // Trigger timer interrupt - if(d == 255){ - uint8_t i_flag = sb_read8_direct(gb, SB_IO_INTER_F); - i_flag |= 1<<2; - sb_store8_direct(gb, SB_IO_INTER_F, i_flag); - d = sb_read8_direct(gb,SB_IO_TMA); - - }else d +=1; - sb_store8_direct(gb, SB_IO_TIMA, d); + for(int i=0;itimers.total_clock_ticks; + uint16_t next = curr+1; + gb->timers.total_clock_ticks=next; + bool tick_tima = SB_BFE(curr,tma_bit,1)&tima_enable; + if(tick_tima==false&&gb->timers.last_tick_tima==true){ + uint8_t d = sb_read8_direct(gb, SB_IO_TIMA); + // Trigger timer interrupt + if(d == 255){ + uint8_t i_flag = sb_read8_direct(gb, SB_IO_INTER_F); + i_flag |= 1<<2; + sb_store8_direct(gb, SB_IO_INTER_F, i_flag); + d = sb_read8_direct(gb,SB_IO_TMA); + }else d +=1; + sb_store8_direct(gb, SB_IO_TIMA, d); + } + gb->timers.last_tick_tima = tick_tima; } + sb_store8_direct(gb, SB_IO_DIV, SB_BFE(gb->timers.total_clock_ticks,8,8)); } int sb_update_dma(sb_gb_t *gb){ @@ -948,9 +1010,36 @@ void sb_reset(sb_gb_t* gb){ gb->mem.data[0xFF4B] = 0x00; // WX gb->mem.data[0xFFFF] = 0x00; // IE - gb->timers.clocks_till_div_inc=0; - gb->timers.clocks_till_tima_inc=0; + gb->timers.total_clock_ticks = 0; gb->cart.mapped_rom_bank=1; + gb->cart.mapped_ram_bank=0; + gb->cart.ram_write_enable=false; + gb->cart.bank_mode = false; +} +static FORCE_INLINE void sb_tick_sio(sb_gb_t* gb, int delta_cycles){ + //Just a stub for now; + uint8_t siocnt= sb_read8_direct(gb,SB_IO_SERIAL_CTRL); + bool active = SB_BFE(siocnt,7,1); + if(active){ + if(gb->serial.last_active==false){ + gb->serial.last_active =true; + gb->serial.ticks_to_complete=4*1024*1024/1024; + bool fast_clock = SB_BFE(siocnt,1,1)&&gb->model == SB_GBC; + if(fast_clock)gb->serial.ticks_to_complete/=2; + } + bool internal_clock = SB_BFE(siocnt,0,1); + if(internal_clock)gb->serial.ticks_to_complete-=delta_cycles; + if(gb->serial.ticks_to_complete<=0){ + siocnt&=0x7f; + sb_store8_direct(gb,SB_IO_SERIAL_CTRL,siocnt); + sb_store8_direct(gb,SB_IO_SERIAL_BYTE,0xff); + uint8_t i_flag = sb_read8_direct(gb,SB_IO_INTER_F); + i_flag |= (1<<3); + sb_store8_direct(gb,SB_IO_INTER_F,i_flag); + active =false; + } + } + gb->serial.last_active =active; } void sb_tick(sb_emu_state_t* emu, sb_gb_t* gb){ gb->lcd.framebuffer = emu->core_temp_storage; @@ -989,7 +1078,6 @@ void sb_tick(sb_emu_state_t* emu, sb_gb_t* gb){ if(masked_interupt & (1<cpu.prefix_op = false; cpu_delta_cycles = 4; bool call_interrupt = false; if(trigger_interrupt!=-1&&request_speed_switch==false){ @@ -1021,13 +1109,18 @@ void sb_tick(sb_emu_state_t* emu, sb_gb_t* gb){ int operand2 = sb_load_operand(gb,inst.op_src2); unsigned pc_before_inst = gb->cpu.pc; + gb->cpu.prefix_op = false; inst.impl(gb, operand1, operand2,inst.op_src1,inst.op_src2, inst.flag_mask); if(gb->cpu.prefix_op==true)i--; unsigned next_op = sb_read8(gb,gb->cpu.pc); if(gb->cpu.prefix_op)next_op+=256; sb_instr_t next_inst = sb_decode_table[next_op]; - cpu_delta_cycles = 4*(gb->cpu.pc==pc_before_inst? next_inst.mcycles : next_inst.mcycles+inst.mcycles_branch_taken-inst.mcycles); + cpu_delta_cycles = 4*((gb->cpu.branch_taken? (inst.mcycles_branch_taken-(inst.mcycles-1)) : 1)+(next_inst.mcycles-1)); + gb->cpu.branch_taken=false; + if(gb->cpu.prefix_op){ + cpu_delta_cycles = 4*(next_inst.mcycles-1); + } }else if(call_interrupt==false&&gb->cpu.wait_for_interrupt==true && request_speed_switch){ gb->cpu.wait_for_interrupt = false; sb_store8(gb,SB_IO_GBC_SPEED_SWITCH,double_speed? 0x00: 0x80); @@ -1038,8 +1131,8 @@ void sb_tick(sb_emu_state_t* emu, sb_gb_t* gb){ int delta_cycles_after_speed = double_speed ? cpu_delta_cycles/2 : cpu_delta_cycles; delta_cycles_after_speed+= dma_delta_cycles; bool vblank = sb_update_lcd(gb,delta_cycles_after_speed,emu->render_frame); - sb_update_timers(gb,cpu_delta_cycles+dma_delta_cycles*2); - + sb_update_timers(gb,dma_delta_cycles?dma_delta_cycles:cpu_delta_cycles); + sb_tick_sio(gb,delta_cycles_after_speed); rumble_cycles+=delta_cycles_after_speed*gb->cart.rumble; total_cylces+=delta_cycles_after_speed; double delta_t = ((double)delta_cycles_after_speed)/(4*1024*1024); @@ -1188,9 +1281,6 @@ static FORCE_INLINE void sb_process_audio(sb_gb_t *gb, sb_emu_state_t*emu, doubl static double current_sim_time = 0; static double current_sample_generated_time = 0; - if(delta_time>1.0/60.)delta_time = 1.0/60.; - current_sim_time +=delta_time; - if(current_sample_generated_time >current_sim_time)return; current_sample_generated_time -= (int)(current_sim_time); current_sim_time -= (int)(current_sim_time); @@ -1204,7 +1294,40 @@ static FORCE_INLINE void sb_process_audio(sb_gb_t *gb, sb_emu_state_t*emu, doubl static float capacitor_l = 0.0; const static float duty_lookup[]={0.125,0.25,0.5,0.75}; + if(gb->audio.regs_written){ + gb->audio.regs_written = false; + int nrf_52 = sb_read8_direct(gb,SB_IO_SOUND_ON_OFF); + bool master_enable = SB_BFE(nrf_52,7,1); + sb_store8_direct(gb,SB_IO_SOUND_ON_OFF,nrf_52); + if(!master_enable){ + nrf_52&=0xf0; + for(int i=SB_IO_AUD1_TONE_SWEEP;i1.0/60.)delta_time = 1.0/60.; + current_sim_time +=delta_time; + if(current_sample_generated_time >current_sim_time)return; + int nrf_52 = sb_read8_direct(gb,SB_IO_SOUND_ON_OFF); + bool master_enable = SB_BFE(nrf_52,7,1); + if(!master_enable)return; float sample_delta_t = 1.0/SE_AUDIO_SAMPLE_RATE; uint8_t freq_sweep1 = sb_read8_direct(gb, SB_IO_AUD1_TONE_SWEEP); float freq_sweep_time_mul1 = SB_BFE(freq_sweep1, 4, 3)/128.; @@ -1212,7 +1335,7 @@ static FORCE_INLINE void sb_process_audio(sb_gb_t *gb, sb_emu_state_t*emu, doubl //float freq_sweep_n1 = 131072./(2048-SB_BFE(freq_sweep1, 0,3)); float freq_sweep_n1 = SB_BFE(freq_sweep1, 0,3); if(SB_BFE(freq_sweep1,0,3)==0){freq_sweep_sign1=0;freq_sweep_time_mul1=0;} - //if(freq_sweep_time_mul1==0)freq_sweep_time_mul1=1.0e6; + if(freq_sweep_time_mul1==0)freq_sweep_time_mul1=1.0e6; uint8_t length_duty1 = sb_read8_direct(gb, SB_IO_AUD1_LENGTH_DUTY); uint8_t freq1_lo = sb_read8_direct(gb,SB_IO_AUD1_FREQ); uint8_t freq1_hi = sb_read8_direct(gb,SB_IO_AUD1_FREQ_HI); @@ -1223,10 +1346,7 @@ static FORCE_INLINE void sb_process_audio(sb_gb_t *gb, sb_emu_state_t*emu, doubl volume_env[0] = compute_vol_env_slope(vol_env1); float duty1 = duty_lookup[SB_BFE(length_duty1,6,2)]; length[0] = (64.-SB_BFE(length_duty1,0,6))/256.; - if(SB_BFE(freq1_hi,7,1)){length_t[0] = 0;} if(SB_BFE(freq1_hi,6,1)==0){length[0] = 1.0e9;} - freq1_hi &=0x7f; - sb_store8_direct(gb, SB_IO_AUD1_FREQ_HI,freq1_hi); uint8_t length_duty2 = sb_read8_direct(gb, SB_IO_AUD2_LENGTH_DUTY); uint8_t freq2_lo = sb_read8_direct(gb,SB_IO_AUD2_FREQ); @@ -1238,11 +1358,7 @@ static FORCE_INLINE void sb_process_audio(sb_gb_t *gb, sb_emu_state_t*emu, doubl volume_env[1] = compute_vol_env_slope(vol_env2); float duty2 = duty_lookup[SB_BFE(length_duty2,6,2)]; length[1] = (64.-SB_BFE(length_duty2,0,6))/256.; - - if(SB_BFE(freq2_hi,7,1)){ length_t[1]=0;} if(SB_BFE(freq2_hi,6,1)==0){length[1] = 1.0e9;} - freq2_hi &=0x7f; - sb_store8_direct(gb, SB_IO_AUD2_FREQ_HI,freq2_hi); uint8_t power3 = sb_read8_direct(gb,SB_IO_AUD3_POWER); uint8_t length3_dat = sb_read8_direct(gb,SB_IO_AUD3_LENGTH); @@ -1255,10 +1371,7 @@ static FORCE_INLINE void sb_process_audio(sb_gb_t *gb, sb_emu_state_t*emu, doubl length[2] = (256.-length3_dat)/256.; int channel3_shift = SB_BFE(vol_env3,5,2)-1; if(SB_BFE(power3,7,1)==0||channel3_shift==-1)channel3_shift=4; - if(SB_BFE(freq3_hi,7,1)){length_t[2]=0.f;} if(SB_BFE(freq3_hi,6,1)==0){length[2] = 1.0e9;} - freq3_hi &=0x7f; - sb_store8_direct(gb, SB_IO_AUD3_FREQ_HI,freq3_hi); uint8_t length_duty4 = sb_read8_direct(gb, SB_IO_AUD4_LENGTH); uint8_t counter4 = sb_read8_direct(gb, SB_IO_AUD4_COUNTER); @@ -1271,14 +1384,9 @@ static FORCE_INLINE void sb_process_audio(sb_gb_t *gb, sb_emu_state_t*emu, doubl volume[3] = SB_BFE(vol_env4,4,4)/15.f; volume_env[3] = compute_vol_env_slope(vol_env4); length[3] = (64.-SB_BFE(length_duty4,0,6))/256.; - if(SB_BFE(counter4,7,1)){ - length_t[3] = 0; - lfsr4 = 0x7FFF; - } + if(SB_BFE(counter4,6,1)==0){length[3] = 1.0e9;} bool sevenBit4 = SB_BFE(poly4,3,1); - counter4 &=0x7f; - sb_store8_direct(gb, SB_IO_AUD4_COUNTER,counter4); uint8_t chan_sel = sb_read8_direct(gb,SB_IO_SOUND_OUTPUT_SEL); //These are type int to allow them to be multiplied to enable/disable @@ -1295,10 +1403,16 @@ static FORCE_INLINE void sb_process_audio(sb_gb_t *gb, sb_emu_state_t*emu, doubl if((sb_ring_buffer_size(&emu->audio_ring_buff)+3>SB_AUDIO_RING_BUFFER_SIZE)) continue; //Advance each channel + for(int i=0;i<4;++i)length_t[i]+=sample_delta_t; freq_hz[0] = freq1_hz_base*pow((1.+freq_sweep_sign1*pow(2.,-freq_sweep_n1)),length_t[0]/freq_sweep_time_mul1); for(int i=0;i<4;++i)chan_t[i] +=sample_delta_t*freq_hz[i]; - for(int i=0;i<4;++i)length_t[i]+=sample_delta_t; - for(int i=0;i<4;++i)if(length_t[i]>length[i]){volume[i]=0;volume_env[i]=0;} + int nrf_52 = sb_read8_direct(gb,SB_IO_SOUND_ON_OFF)&0xf0; + for(int i=0;i<4;++i){ + bool active = length_t[i]=1.0) { @@ -1322,7 +1436,7 @@ static FORCE_INLINE void sb_process_audio(sb_gb_t *gb, sb_emu_state_t*emu, doubl for(int i=0;i<4;++i)v[i] = v[i]>1.0? 1.0 : (v[i]<0.0? 0.0 : v[i]); v[2]=1.0; //Wave channel doesn't have a volume envelop - //Lookup wave table value + //Lookup wave table value unsigned wav_samp = ((unsigned)(chan_t[2]*32))%32; int dat =sb_read8_direct(gb,SB_IO_AUD3_WAVE_BASE+wav_samp/2); int offset = (wav_samp&1)? 0:4; @@ -1339,8 +1453,10 @@ static FORCE_INLINE void sb_process_audio(sb_gb_t *gb, sb_emu_state_t*emu, doubl float sample_volume_l = 0; float sample_volume_r = 0; for(int i=0;i<4;++i){ - sample_volume_l+=channels[i]*chan_l[i]; - sample_volume_r+=channels[i]*chan_r[i]; + float l = channels[i]*chan_l[i]; + float r = channels[i]*chan_r[i]; + if(l>=-2.&&l<=2)sample_volume_l+=l; + if(r>=-2.&&r<=2)sample_volume_r+=r; } sample_volume_l*=0.25; diff --git a/src/gba.h b/src/gba.h index 79703a163..683f606d9 100644 --- a/src/gba.h +++ b/src/gba.h @@ -1269,8 +1269,10 @@ static FORCE_INLINE uint32_t * gba_dword_lookup(gba_t* gba,unsigned addr, int re }else if(gba->cart.backup_type==GBA_BACKUP_EEPROM) ret = (uint32_t*)&gba->mem.eeprom_word; else{ //Flash - if(gba->cart.in_chip_id_mode&&addr<=0xE000001) ret = (uint32_t*)(gba->mem.flash_chip_id); - else ret = (uint32_t*)(gba->mem.cart_backup+(addr&0xfffc)+gba->cart.flash_bank*64*1024); + if(gba->cart.in_chip_id_mode&&addr<=0xE000001){ + gba->mem.openbus_word = *(uint32_t*)gba->mem.flash_chip_id; + ret = &gba->mem.openbus_word; + }else ret = (uint32_t*)(gba->mem.cart_backup+(addr&0xfffc)+gba->cart.flash_bank*64*1024); } gba->mem.openbus_word=(*ret&0xffff)*0x10001; break; @@ -1426,6 +1428,7 @@ static bool gba_process_mmio_write(gba_t *gba, uint32_t address, uint32_t data, return false; } int gba_search_rom_for_backup_string(gba_t* gba){ + int btype = GBA_BACKUP_NONE; for(int b = 0; b< gba->cart.rom_size;++b){ const char* strings[]={"EEPROM_", "SRAM_", "FLASH_","FLASH512_","FLASH1M_"}; int backup_type[]= {GBA_BACKUP_EEPROM,GBA_BACKUP_SRAM,GBA_BACKUP_FLASH_64K, GBA_BACKUP_FLASH_64K, GBA_BACKUP_FLASH_128K}; @@ -1438,10 +1441,16 @@ int gba_search_rom_for_backup_string(gba_t* gba){ else if(str[str_off]!=gba->mem.cart_rom[b+str_off])matches = false; ++str_off; } - if(matches)return backup_type[type]; + if(matches){ + if(btype!=backup_type[type]&&btype!=GBA_BACKUP_NONE){ + printf("Found multiple backup types, defaulting to none\n"); + return GBA_BACKUP_NONE; + } + btype = backup_type[type]; + } } } - return GBA_BACKUP_NONE; + return btype; } void gba_unload(gba_t*gba){ printf("Unloading GBA\n"); @@ -1485,8 +1494,13 @@ bool gba_load_rom(gba_t* gba, const char* filename, const char* save_file){ } // Setup flash chip id (this is not used if the cartridge does not have flash backup storage) - gba->mem.flash_chip_id[1]=0x13; - gba->mem.flash_chip_id[0]=0x62; + if(gba->cart.backup_type==GBA_BACKUP_FLASH_64K){ + gba->mem.flash_chip_id[1]=0xd4; + gba->mem.flash_chip_id[0]=0xbf; + }else{ + gba->mem.flash_chip_id[1]=0x13; + gba->mem.flash_chip_id[0]=0x62; + } return true; } @@ -2501,7 +2515,8 @@ static FORCE_INLINE void gba_tick_audio(gba_t *gba, sb_emu_state_t*emu, double d static float length_t[4]={1e6,1e6,1e6,1e6}; float freq_hz[4] = {0,0,0,0}, length[4]= {0,0,0,0}, volume[4]={0,0,0,0}; float volume_env[4]={0,0,0,0}; - static float last_noise_value = 0; + static uint16_t lfsr4 = 0x7FFF; + gba->audio.current_sample_generated_time -= (int)(gba->audio.current_sim_time); gba->audio.current_sim_time -= (int)(gba->audio.current_sim_time); @@ -2595,11 +2610,14 @@ static FORCE_INLINE void gba_tick_audio(gba_t *gba, sb_emu_state_t*emu, double d uint16_t soundcnt4_h = gba_io_read16(gba, GBA_SOUND4CNT_H); float r4 = SB_BFE(soundcnt4_h,0,3); + bool counter_step = SB_BFE(soundcnt4_h,3,1); //(0=15 bits, 1=7 bits) uint8_t s4 = SB_BFE(soundcnt4_h,4,4); if(r4==0)r4=0.5; freq_hz[3] = 524288.0/r4/pow(2.0,s4+1); if(SB_BFE(soundcnt4_l,14,1)==0){length[3] = 1.0e9;} - if(SB_BFE(soundcnt4_l,15,1)){length_t[3] = 0;} + if(SB_BFE(soundcnt4_l,15,1)){ + length_t[3] = 0; + } soundcnt4_h&=0x7fff; gba_io_store16(gba, GBA_SOUND4CNT_H,soundcnt4_h); @@ -2672,7 +2690,17 @@ static FORCE_INLINE void gba_tick_audio(gba_t *gba, sb_emu_state_t*emu, double d for(int i=0;i<4;++i)if(length_t[i]>length[i]){volume[i]=0;volume_env[i]=0;} //Generate new noise value if needed - if(chan_t[3]>=1.0)last_noise_value = sb_random_float(0,1)*2.-1.; + if(chan_t[3]>=1.0) { + int bit = (lfsr4 ^ (lfsr4 >> 1)) & 1; + + lfsr4 >>= 1; + lfsr4 |= bit << 14; + + if (counter_step) { + lfsr4 &= ~(1 << 7); + lfsr4 |= bit << 6; + } + } //Loop back for(int i=0;i<4;++i)chan_t[i]-=(int)chan_t[i]; @@ -2692,8 +2720,8 @@ static FORCE_INLINE void gba_tick_audio(gba_t *gba, sb_emu_state_t*emu, double d float channels[6] = {0,0,0,0,0,0}; channels[0] = gba_bandlimited_square(chan_t[0],duty[0],sample_delta_t*freq_hz[0])*v[0]; channels[1] = gba_bandlimited_square(chan_t[1],duty[1],sample_delta_t*freq_hz[1])*v[1]; - channels[2] = (dat)*v[2]/16.; - channels[3] = last_noise_value*v[3]; + channels[2] = (dat)*v[2]/8.; + channels[3] = ((lfsr4 & 1))*v[3]; for(int i=0;i<2;++i) channels[4+i] = gba->audio.fifo[i].data[gba->audio.fifo[i].read_ptr&0x1f]/128.; diff --git a/src/main.c b/src/main.c index 8a4a2c53b..79341710e 100644 --- a/src/main.c +++ b/src/main.c @@ -149,6 +149,7 @@ typedef struct { int audio_watchdog_timer; int audio_watchdog_triggered; bool block_touchscreen; + bool test_runner_mode; } gui_state_t; void se_draw_image(uint8_t *data, int im_width, int im_height,int x, int y, int render_width, int render_height, bool has_alpha); @@ -805,7 +806,7 @@ static void se_reset_core(){ void se_load_rom(const char *filename){ se_reset_rewind_buffer(&rewind_buffer); se_reset_save_states(); - char save_file[4096]; + char save_file[SB_FILE_PATH_SIZE]; save_file[0] = '\0'; const char* base, *c, *ext; sb_breakup_path(filename,&base, &c, &ext); @@ -816,9 +817,9 @@ void se_load_rom(const char *filename){ } return; } - snprintf(save_file,4096,"/offline/%s.sav",c); + snprintf(save_file,SB_FILE_PATH_SIZE,"/offline/%s.sav",c); #else - snprintf(save_file,4096,"%s/%s.sav",base, c); + se_join_path(save_file, SB_FILE_PATH_SIZE, base, c, ".sav"); #endif if(emu_state.rom_loaded){ @@ -897,7 +898,16 @@ static double se_get_sim_fps(){ return sim_fps; } static void se_emulate_single_frame(){ - if(emu_state.system == SYSTEM_GB)sb_tick(&emu_state,&core.gb); + if(emu_state.system == SYSTEM_GB){ + if(gui_state.test_runner_mode){ + uint8_t palette[4*3] = { 0xff,0xff,0xff,0xAA,0xAA,0xAA,0x55,0x55,0x55,0x00,0x00,0x00 }; + for(int i=0;i<12;++i)core.gb.dmg_palette[i]=palette[i]; + }else{ + uint8_t palette[4*3] = { 0x81,0x8F,0x38,0x64,0x7D,0x43,0x56,0x6D,0x3F,0x31,0x4A,0x2D }; + for(int i=0;i<12;++i)core.gb.dmg_palette[i]=palette[i]; + } + sb_tick(&emu_state,&core.gb); + } else if(emu_state.system == SYSTEM_GBA)gba_tick(&emu_state, &core.gba); else if(emu_state.system == SYSTEM_NDS)nds_tick(&emu_state, &core.nds); @@ -2272,7 +2282,7 @@ static void frame(void) { igPushStyleVarVec2(ImGuiStyleVar_FramePadding,(ImVec2){5,5}); igPushStyleVarVec2(ImGuiStyleVar_WindowPadding,(ImVec2){0,5}); ImGuiStyle* style = igGetStyle(); - if (igBeginMainMenuBar()) + if (gui_state.test_runner_mode==false&&igBeginMainMenuBar()) { int orig_x = igGetCursorPosX(); igSetCursorPosX((width/se_dpi_scale())-100); @@ -2626,6 +2636,22 @@ static void event(const sapp_event* ev) { sapp_desc sokol_main(int argc, char* argv[]) { emu_state.cmd_line_arg_count =argc; emu_state.cmd_line_args =argv; + int width = 1280; + int height = 800; + if(argc>2&&strcmp("run_gb_test",argv[1])==0){ + gui_state.test_runner_mode=true; + emu_state.cmd_line_arg_count =argc-1; + emu_state.cmd_line_args =argv+1; + width = SB_LCD_W; + height= SB_LCD_H; + } + if(argc>2&&strcmp("run_gba_test",argv[1])==0){ + gui_state.test_runner_mode=true; + emu_state.cmd_line_arg_count =argc-1; + emu_state.cmd_line_args =argv+1; + width = GBA_LCD_W; + height= GBA_LCD_H; + } #if defined(EMSCRIPTEN) em_init_fs(); #endif @@ -2635,8 +2661,8 @@ sapp_desc sokol_main(int argc, char* argv[]) { .cleanup_cb = cleanup, .event_cb = event, .window_title = "SkyEmu", - .width = 1280, - .height = 800, + .width = width, + .height = height, .enable_dragndrop = true, .enable_clipboard =true, .high_dpi = true, diff --git a/src/sb_instr_impl.h b/src/sb_instr_impl.h index a8a446760..edb570450 100644 --- a/src/sb_instr_impl.h +++ b/src/sb_instr_impl.h @@ -228,7 +228,7 @@ static void sb_call_impl(sb_gb_t* gb, int op1, int op2, int op1_enum, int op2_en } static void sb_callc_impl(sb_gb_t* gb, int op1, int op2, int op1_enum, int op2_enum, const uint8_t * flag_mask){ - if(op1)sb_call_impl(gb, op2, 0, 0, 0, flag_mask); + if(op1){sb_call_impl(gb, op2, 0, 0, 0, flag_mask);gb->cpu.branch_taken=true;} } static void sb_ccf_impl(sb_gb_t* gb, int op1, int op2, int op1_enum, int op2_enum, const uint8_t * flag_mask){ @@ -299,7 +299,7 @@ static void sb_jp_impl(sb_gb_t* gb, int op1, int op2, int op1_enum, int op2_enum } static void sb_jpc_impl(sb_gb_t* gb, int op1, int op2, int op1_enum, int op2_enum, const uint8_t * flag_mask){ - if(op1)sb_jp_impl(gb, op2, 0, 0, 0, flag_mask); + if(op1){sb_jp_impl(gb, op2, 0, 0, 0, flag_mask);gb->cpu.branch_taken=true;} } static void sb_jr_impl(sb_gb_t* gb, int op1, int op2, int op1_enum, int op2_enum, const uint8_t * flag_mask){ @@ -307,7 +307,7 @@ static void sb_jr_impl(sb_gb_t* gb, int op1, int op2, int op1_enum, int op2_enum } static void sb_jrc_impl(sb_gb_t* gb, int op1, int op2, int op1_enum, int op2_enum, const uint8_t * flag_mask){ - if(op1) sb_jr_impl(gb, op2, 0, 0, 0, flag_mask); + if(op1){ sb_jr_impl(gb, op2, 0, 0, 0, flag_mask);gb->cpu.branch_taken=true;} } static void sb_ld_impl(sb_gb_t* gb, int op1, int op2, int op1_enum, int op2_enum, const uint8_t * flag_mask){ @@ -365,7 +365,7 @@ static void sb_ret_impl(sb_gb_t* gb, int op1, int op2, int op1_enum, int op2_enu } static void sb_retc_impl(sb_gb_t* gb, int op1, int op2, int op1_enum, int op2_enum, const uint8_t * flag_mask){ - if(op1)sb_ret_impl(gb, op2, 0, 0, 0,flag_mask); + if(op1){sb_ret_impl(gb, op2, 0, 0, 0,flag_mask);gb->cpu.branch_taken=true;} } static void sb_reti_impl(sb_gb_t* gb, int op1, int op2, int op1_enum, int op2_enum, const uint8_t * flag_mask){ @@ -506,7 +506,7 @@ static void sb_stop_impl(sb_gb_t* gb, int op1, int op2, int op1_enum, int op2_en gb->cpu.wait_for_interrupt=true; gb->cpu.interrupt_enable = true; // Div is reset on stop - gb->timers.clocks_till_div_inc = 0; + gb->timers.total_clock_ticks = 0; sb_store8(gb,0xff04,0); } diff --git a/src/sb_types.h b/src/sb_types.h index 7846765a2..53bdffc3c 100644 --- a/src/sb_types.h +++ b/src/sb_types.h @@ -152,6 +152,7 @@ typedef struct { bool prefix_op; bool trigger_breakpoint; int last_inter_f; + bool branch_taken; } sb_gb_cpu_t; typedef struct { @@ -175,6 +176,7 @@ typedef struct { int ram_size; bool rumble; bool has_rumble; + bool bank_mode; //MBC1 } sb_gb_cartridge_t; typedef struct{ @@ -192,15 +194,15 @@ typedef struct{ bool in_hblank; bool active; int bytes_transferred; - bool oam_dma_active; int oam_bytes_transferred; bool hdma; } sb_dma_t; typedef struct{ - int clocks_till_div_inc; - int clocks_till_tima_inc; + uint32_t total_clock_ticks; + bool tima_written; + bool last_tick_tima; } sb_timer_t; static FORCE_INLINE uint32_t sb_ring_buffer_size(sb_ring_buffer_t* buff){ @@ -212,6 +214,13 @@ static FORCE_INLINE uint32_t sb_ring_buffer_size(sb_ring_buffer_t* buff){ v= v%SB_AUDIO_RING_BUFFER_SIZE; return v; } +typedef struct{ + bool regs_written; +}sb_audio_t; +typedef struct{ + uint32_t ticks_to_complete; + bool last_active; +}sb_serial_t; typedef struct { sb_gb_cartridge_t cart; sb_gb_cpu_t cpu; @@ -219,7 +228,10 @@ typedef struct { sb_lcd_ppu_t lcd; sb_timer_t timers; sb_dma_t dma; + sb_audio_t audio; + sb_serial_t serial; int model; + uint8_t dmg_palette[4*3]; } sb_gb_t; typedef void (*sb_opcode_impl_t)(sb_gb_t*,int op1,int op2, int op1_enum, int op2_enum, const uint8_t * flag_mask); @@ -302,9 +314,9 @@ static void sb_free_file_data(uint8_t* data){ if(data)free(data); } static void sb_breakup_path(const char* path, const char** base_path, const char** file_name, const char** ext){ - static char tmp_path[4096]; - strncpy(tmp_path,path,4096-1); - tmp_path[4095]='\0'; + static char tmp_path[SB_FILE_PATH_SIZE]; + strncpy(tmp_path,path,SB_FILE_PATH_SIZE-1); + tmp_path[SB_FILE_PATH_SIZE-1]='\0'; size_t sz = strlen(tmp_path); *base_path = ""; *file_name = tmp_path; @@ -326,13 +338,20 @@ static void sb_breakup_path(const char* path, const char** base_path, const char } } } +static void se_join_path(char * dest_path, int dest_size, const char * base_path, const char* file_name, const char* add_extension){ + char * seperator = base_path[0]==0? "" : "/"; + if(add_extension){ + const char * ext_sep = add_extension[0]=='.' ? "": "."; + snprintf(dest_path,dest_size,"%s%s%s%s%s",base_path, seperator, file_name,ext_sep,add_extension); + }else snprintf(dest_path,dest_size,"%s%s%s",base_path, seperator, file_name); + dest_path[dest_size-1]=0; +} static bool se_load_bios_file(const char* name, const char* base_path, const char* file_name, uint8_t * data, size_t data_size){ bool loaded_bios=false; const char* base, *file, *ext; sb_breakup_path(base_path, &base,&file, &ext); static char bios_path[SB_FILE_PATH_SIZE]; - snprintf(bios_path,SB_FILE_PATH_SIZE,"%s/%s",base, file_name); - bios_path[SB_FILE_PATH_SIZE-1]=0; + se_join_path(bios_path,SB_FILE_PATH_SIZE,base,file_name,NULL); size_t bios_bytes=0; uint8_t *bios_data = sb_load_file_data(bios_path, &bios_bytes); if(bios_data){ @@ -352,7 +371,7 @@ static FILE * se_load_log_file(const char* rom_path, const char* log_name){ const char* base, *file, *ext; sb_breakup_path(rom_path, &base,&file, &ext); static char log_path[SB_FILE_PATH_SIZE]; - snprintf(log_path,SB_FILE_PATH_SIZE,"%s/%s.%s",base, file,log_name); + se_join_path(log_path,SB_FILE_PATH_SIZE,base,file,log_name); log_path[SB_FILE_PATH_SIZE-1]=0; FILE * f = fopen(log_path, "rb"); if(f)printf("Loaded log file:%s\n",log_path);