/* one-pole highpass filter y[n] = alpha * (y[n-1] + x[n] - x[n-1]) fcut_hz corresponds to 3 dB point */ void dc_filter_init(dc_filter_t *pstate, float fcut_hz, float sample_rate_hz) { float temp; memset(pstate, 0, sizeof(dc_filter_t)); // // alpha = 32767 - fcut * 26 * 8 / samplerate_khz // // warning: if alpha is too large, then lack of precision in // q15 * i16 multiply will // result in sticky bits // temp = 26.0f * 8.0f / (sample_rate_hz/1000.0f); temp = fcut_hz * temp; pstate->alpha_16 = (int16_t)32767 - (int16_t)temp; utils_log("/ndc_filter: alpha_i16 = %d\n",pstate->alpha_16); if (pstate->alpha_16 <= 0) { utils_log("fcut_hz = "); utils_print_float(fcut_hz,5); utils_log("\n"); utils_log("alpha must be > 0"); UTILS_ASSERT(0); } }
char gameboy_restore_stat(int idx) { char path[256]; char buf[6]; /* ensure i'm in pause */ gameboy_set_pause(1); /* build output file name */ snprintf(path, sizeof(path), "%s/%s.%d.stat", global_save_folder, global_rom_name, idx); FILE *fp = fopen(path, "r+"); if (fp == NULL) { utils_log("Cannot open stat file\n"); return 1; } /* read version */ fread(buf, 1, 6, fp); if (memcmp(buf, "000001", 6)) { utils_log("Version of stat file doesnt match\n"); return 1; } /* restore CPU status */ fread(&state, 1, sizeof(z80_state_t), fp); state.f = (uint8_t *) &state.flags; state.bc = (uint16_t *) &state.c; state.de = (uint16_t *) &state.e; state.hl = (uint16_t *) &state.l; /* dump every module */ cycles_restore_stat(fp); sound_restore_stat(fp); gpu_restore_stat(fp); serial_restore_stat(fp); mmu_restore_stat(fp); fclose(fp); return 0; }
char cartridge_load(char *file_gb) { FILE *fp; int i,z = 0; /* open ROM file */ if ((fp = fopen(file_gb, "r")) == NULL) return 1; /* read all the content into rom buffer */ size_t sz = fread(rom, 1, (2 << 24), fp); /* check for errors */ if (sz < 1) return 1; /* close */ fclose(fp); /* gameboy color? */ if (rom[0x143] == 0xC0 || rom[0x143] == 0x80) { utils_log("Gameboy Color cartridge\n"); global_cgb = 1; } else { utils_log("Gameboy Classic cartridge\n"); global_cgb = 0; } /* get cartridge infos */ uint8_t mbc = rom[0x147]; utils_log("Cartridge code: %02x\n", mbc); switch (mbc) { case 0x00: utils_log("ROM ONLY\n"); break; case 0x01: utils_log("MBC1\n"); break; case 0x02: utils_log("MBC1 + RAM\n"); break; case 0x03: utils_log("MBC1 + RAM + BATTERY\n"); break; case 0x05: utils_log("MBC2\n"); break; case 0x06: mmu_init_ram(512); utils_log("MBC2 + BATTERY\n"); break; case 0x10: utils_log("MBC3 + TIMER + RAM + BATTERY\n"); break; case 0x11: utils_log("MBC3\n"); break; case 0x12: utils_log("MBC3 + RAM\n"); break; case 0x13: utils_log("MBC3 + RAM + BATTERY\n"); break; case 0x19: utils_log("MBC5\n"); break; case 0x1A: utils_log("MBC5 + RAM\n"); break; case 0x1B: utils_log("MBC5 + RAM + BATTERY\n"); break; case 0x1C: utils_log("MBC5 + RUMBLE\n"); break; case 0x1D: utils_log("MBC5 + RUMBLE + RAM\n"); break; case 0x1E: utils_log("MBC5 + RUMBLE + RAM + BATTERY\n"); break; default: utils_log("Unknown cartridge type: %02x\n", mbc); return 2; } /* title */ for (i=0x134; i<0x143; i++) if (rom[i] > 0x40 && rom[i] < 0x5B) global_cart_name[z++] = rom[i]; global_cart_name[z] = '\0'; utils_log("%s\n", global_cart_name); /* get ROM banks */ uint8_t byte = rom[0x148]; utils_log("ROM: "); switch (byte) { case 0x00: utils_log("0 banks\n"); break; case 0x01: utils_log("4 banks\n"); break; case 0x02: utils_log("8 banks\n"); break; case 0x03: utils_log("16 banks\n"); break; case 0x04: utils_log("32 banks\n"); break; case 0x05: utils_log("64 banks\n"); break; case 0x06: utils_log("128 banks\n"); break; case 0x07: utils_log("256 banks\n"); break; case 0x52: utils_log("72 banks\n"); break; case 0x53: utils_log("80 banks\n"); break; case 0x54: utils_log("96 banks\n"); break; } /* init MMU */ mmu_init(mbc, byte); /* get RAM banks */ byte = rom[0x149]; utils_log("RAM: "); switch (byte) { case 0x00: utils_log("NO RAM\n"); break; case 0x01: mmu_init_ram(1 << 11); utils_log("2 kB\n"); break; case 0x02: /* MBC5 got bigger values */ if (mbc >= 0x19 && mbc <= 0x1E) { mmu_init_ram(1 << 16); utils_log("64 kB\n"); } else { mmu_init_ram(1 << 13); utils_log("8 kB\n"); } break; case 0x03: mmu_init_ram(1 << 15); utils_log("32 kB\n"); break; case 0x04: mmu_init_ram(1 << 17); utils_log("128 kB\n"); break; case 0x05: mmu_init_ram(1 << 16); utils_log("64 kB\n"); break; } /* save base name of the rom */ strncpy(global_rom_name, basename(file_gb), 256); /* build file.sav */ snprintf(file_sav, sizeof(file_sav), "%s/%s.sav", global_save_folder, global_rom_name); /* build file.rtc */ snprintf(file_rtc, sizeof(file_rtc), "%s/%s.rtc", global_save_folder, global_rom_name); /* restore saved RAM if it's the case */ mmu_restore_ram(file_sav); /* restore saved RTC if it's the case */ mmu_restore_rtc(file_rtc); /* load FULL ROM at 0x0000 address of system memory */ mmu_load_cartridge(rom, sz); return 0; }
void *network_start_thread(void *args) { utils_log("Starting network thread\n"); /* open socket sending broadcast messages */ network_sock_broad = socket(AF_INET, SOCK_DGRAM, 0); /* exit on error */ if (network_sock_broad < 1) { utils_log("Error opening broadcast socket"); return NULL; } /* open socket sending/receiving serial cable data */ network_sock_bound = socket(AF_INET, SOCK_DGRAM, 0); /* exit on error */ if (network_sock_bound < 1) { utils_log("Error opening serial-link socket"); close (network_sock_broad); return NULL; } /* enable to broadcast */ int enable=1; setsockopt(network_sock_broad, SOL_SOCKET, SO_BROADCAST, &enable, sizeof(enable)); /* prepare dest stuff */ struct sockaddr_in broadcast_addr; struct sockaddr_in bound_addr; struct sockaddr_in addr_from; socklen_t addr_from_len = sizeof(addr_from); memset(&broadcast_addr, 0, sizeof(broadcast_addr)); broadcast_addr.sin_family = AF_INET; broadcast_addr.sin_addr.s_addr = INADDR_BROADCAST; // inet_aton("239.255.0.37", // (struct in_addr *) &broadcast_addr.sin_addr.s_addr); //inet_aton("192.168.10.168", // (struct in_addr *) &broadcast_addr.sin_addr.s_addr); broadcast_addr.sin_port = htons(64333); /* setup listening socket */ memset(&bound_addr, 0, sizeof(bound_addr)); bound_addr.sin_family = AF_INET; bound_addr.sin_addr.s_addr = INADDR_ANY; bound_addr.sin_port = htons(64333); /* bind to selected port */ if (bind(network_sock_bound, (struct sockaddr *) &bound_addr, sizeof(bound_addr))) { utils_log("Error binding to port 64333"); /* close sockets and exit */ close(network_sock_broad); close(network_sock_bound); return NULL; } /* assign it to our multicast group */ /* struct ip_mreq mreq; mreq.imr_multiaddr.s_addr=inet_addr("239.255.0.37"); mreq.imr_interface.s_addr=htonl(INADDR_ANY); if (setsockopt(network_sock_bound, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { utils_log("Error joining multicast network"); close(network_sock_broad); close(network_sock_bound); return NULL; }*/ fd_set rfds; char buf[64]; int ret; ssize_t recv_ret; struct timeval tv; int timeouts = 4; unsigned int v, clock, prog; // uint16_t prog; /* message parts */ char msg_type; unsigned int msg_uuid; char msg_content[64]; /* generate a random uuid */ srand(time(NULL)); network_uuid = rand() & 0xFFFFFFFF; /* set callback in case of data to send */ serial_set_send_cb(&network_send_data); /* declare network is running */ network_running = 1; utils_log("Network thread started\n"); /* loop forever */ while (network_running) { FD_ZERO(&rfds); FD_SET(network_sock_bound, &rfds); /* wait one second */ tv.tv_sec = 1; tv.tv_usec = 0; /* one second timeout OR something received */ ret = select(network_sock_bound + 1, &rfds, NULL, NULL, &tv); // utils_log_urgent("SELECT RET: %d\n", ret); /* error! */ if (ret == -1) break; /* ret 0 = timeout */ if (ret == 0) { if (++timeouts == 5) { /* build output message */ sprintf(buf, "B%08x%s", network_uuid, global_cart_name); /* send broadcast message */ sendto(network_sock_broad, buf, strlen(buf), 0, (struct sockaddr *) &broadcast_addr, sizeof(broadcast_addr)); timeouts = 0; } } else { /* reset message content */ bzero(buf, sizeof(buf)); bzero(msg_content, sizeof(msg_content)); /* exit if an error occour */ if ((recv_ret = recvfrom(network_sock_bound, buf, 64, 0, (struct sockaddr *) &addr_from, (socklen_t *) &addr_from_len)) < 1) break; /* dissect message */ if (sscanf(buf, "%c%08x%s", &msg_type, &msg_uuid, msg_content) == 3) { /* was it send by myself? */ if (msg_uuid != network_uuid) { /* is it a serial message? */ if (msg_type == 'M') { /* extract value from hex string */ sscanf(msg_content, "%02x%02x%02x", &v, &clock, &prog); /* tell serial module something has arrived */ serial_recv_byte((uint8_t) v, (uint8_t) clock); /*if (clock == 0) { utils_ts_log("SEM POST\n"); network_sem_post(&network_sem); } */ } else if (msg_type == 'B') { /* someone is claiming is playing with the same game? */ if (strcmp(msg_content, global_cart_name) == 0 && serial.peer_connected == 0) { /* save sender */ memcpy(&network_peer_addr, &addr_from, sizeof(struct sockaddr_in)); /* just change dst port */ network_peer_addr.sin_port = htons(64333); /* notify the other peer by sending a b message */ sprintf(buf, "B%08x%s", network_uuid, global_cart_name); /* send broadcast message */ sendto(network_sock_broad, buf, strlen(buf), 0, (struct sockaddr *) &network_peer_addr, sizeof(network_peer_addr)); /* log that peer is connected */ utils_log("Peer connected: %s\n", inet_ntoa(network_peer_addr.sin_addr)); /* YEAH */ serial.peer_connected = 1; } } } } } } /* free serial */ serial.peer_connected = 0; /* close sockets */ close(network_sock_broad); close(network_sock_bound); return NULL; }
void gameboy_run() { uint8_t op; /* reset counter */ cycles.cnt = 0; /* get interrupt flags and interrupt enables */ uint8_t *int_e; uint8_t *int_f; /* pointers to memory location of interrupt enables/flags */ int_e = mmu_addr(0xFFFF); int_f = mmu_addr(0xFF0F); /* start at normal speed */ global_cpu_double_speed = 0; /* run stuff! */ /* mechanism is simple. */ /* 1) execute instruction 2) update cycles counter 3) check interrupts */ /* and repeat forever */ while (!global_quit) { /*if (global_slow_down) { usleep(100000); global_slow_down = 0; }*/ /* pause? */ while (global_pause) sem_wait(&gameboy_sem); /* get op */ op = mmu_read(state.pc); /* print out CPU state if enabled by debug flag */ if (global_debug) { utils_log("OP: %02x F: %02x PC: %04x:%02x:%02x SP: %04x:%02x:%02x ", op, *state.f & 0xd0, state.pc, mmu_read_no_cyc(state.pc + 1), mmu_read_no_cyc(state.pc + 2), state.sp, mmu_read_no_cyc(state.sp), mmu_read_no_cyc(state.sp + 1)); utils_log("A: %02x BC: %04x DE: %04x HL: %04x FF41: %02x " "FF44: %02x ENAB: %02x INTE: %02x INTF: %02x\n", state.a, *state.bc, *state.de, *state.hl, mmu_read_no_cyc(0xFF41), mmu_read_no_cyc(0xFF44), state.int_enable, *int_e, *int_f); } /* execute instruction by the GB Z80 version */ z80_execute(op); /* if last op was Interrupt Enable (0xFB) */ /* we need to check for INTR on next cycle */ if (op == 0xFB) continue; /* interrupts filtered by enable flags */ uint8_t int_r = (*int_f & *int_e); /* check for interrupts */ if ((state.int_enable || op == 0x76) && (int_r != 0)) { /* discard useless bits */ if ((int_r & 0x1F) == 0x00) continue; /* beware of instruction that doesn't move PC! */ /* like HALT (0x76) */ if (op == 0x76) { state.pc++; if (state.int_enable == 0) continue; } /* reset int-enable flag, it will be restored after a RETI op */ state.int_enable = 0; if ((int_r & 0x01) == 0x01) { /* vblank interrupt triggers RST 5 */ /* reset flag */ *int_f &= 0xFE; /* handle the interrupt */ z80_intr(0x0040); } else if ((int_r & 0x02) == 0x02) { /* LCD Stat interrupt */ /* reset flag */ *int_f &= 0xFD; /* handle the interrupt! */ z80_intr(0x0048); } else if ((int_r & 0x04) == 0x04) { /* timer interrupt */ /* reset flag */ *int_f &= 0xFB; /* handle the interrupt! */ z80_intr(0x0050); } else if ((int_r & 0x08) == 0x08) { /* serial interrupt */ /* reset flag */ *int_f &= 0xF7; /* handle the interrupt! */ z80_intr(0x0058); } } } /* terminate all the stuff */ cartridge_term(); sound_term(); mmu_term(); return; }