#include #include #include #include #include #include #include #include #include #include #include #include #include SDL_Window *sdlWindow; SDL_Renderer *sdlRenderer; SDL_Texture *sdlTexture; SDL_Surface *screen; SDL_Window *sdlVramWindow; SDL_Renderer *sdlVramRenderer; SDL_Texture *sdlVramTexture; SDL_Surface *vramScreen; SDL_Window *sdlAudioWindow; SDL_Renderer *sdlAudioRenderer; SDL_Texture *sdlAudioTexture; SDL_Surface *audioScreen; TTF_Font *sans; static int scale = 4; static int screenWidth; static int screenHeight; void ui_init(){ screenWidth = XRES*scale; screenHeight = YRES*scale; SDL_Init(SDL_INIT_VIDEO); printf("SDL INIT\n"); TTF_Init(); printf("TTF INIT\n"); char buffer[1024]; strcpy(buffer, emu_get_context()->app_path); *strrchr(buffer, '\\') = 0; strcat(buffer,"/Sans.ttf"); sans = TTF_OpenFont(buffer, 24); if ( !sans ) { printf("Failed to load font: %s\n", TTF_GetError()); } SDL_CreateWindowAndRenderer(screenWidth, screenHeight, 0, &sdlWindow, &sdlRenderer); screen = SDL_CreateRGBSurface(0, screenWidth, screenHeight, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000); sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, screenWidth, screenHeight); SDL_CreateWindowAndRenderer(16 * 9 * scale, 24 * 9 * scale, SDL_WINDOW_HIDDEN, &sdlVramWindow, &sdlVramRenderer); vramScreen = SDL_CreateRGBSurface(0, (16 * 9 * scale), (24 * 9 * scale), 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000); sdlVramTexture = SDL_CreateTexture(sdlVramRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, (16 * 9 * scale), (24 * 9 * scale)); SDL_CreateWindowAndRenderer(384, 730, SDL_WINDOW_HIDDEN, &sdlAudioWindow, &sdlAudioRenderer); audioScreen = SDL_CreateRGBSurface(0, 384, 730, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000); sdlAudioTexture = SDL_CreateTexture(sdlAudioRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 384, 730); } static unsigned long tile_colors[4] = {0xFFFFFFFF, 0xFFAAAAAA, 0xFF555555, 0xFF000000}; void display_tile(SDL_Surface *surface, u16 startLocation, u16 tileNum, int x, int y) { SDL_Rect rc; for(int tileY = 0; tileY < 16; tileY += 2) { u8 b1 = bus_read(startLocation+(tileNum * 16) + tileY); u8 b2 = bus_read(startLocation+(tileNum * 16) + tileY + 1); for (int bit=7; bit >= 0; bit--) { u8 hi = !!(b1 & (1 << bit)) << 1; u8 lo = !!(b2 & (1 << bit)); u8 color = hi | lo; rc.x = x + ((7 - bit) * scale); rc.y = y + (tileY/2 * scale); rc.w = scale; rc.h = scale; SDL_FillRect(surface, &rc, tile_colors[color]); } } } void update_vram_window() { int xDraw = 0; int yDraw = 0; int tileNum = 0; SDL_Rect rc; rc.x = 0; rc.y = 0; rc.w = vramScreen->w; rc.h = vramScreen->h; SDL_FillRect(vramScreen, &rc, 0xFF111111); u16 addr = 0x8000; //384 tiles, 24 x 16 for(int y = 0; y < 24; y++) { for(int x = 0; x < 16; x++) { display_tile(vramScreen, addr, tileNum, xDraw + (x * scale), yDraw + (y * scale)); xDraw += (8 * scale); tileNum++; } yDraw += (8 * scale); xDraw = 0; } SDL_UpdateTexture(sdlVramTexture, NULL, vramScreen->pixels, vramScreen->pitch); SDL_RenderClear(sdlVramRenderer); SDL_RenderCopy(sdlVramRenderer, sdlVramTexture, NULL, NULL); SDL_RenderPresent(sdlVramRenderer); } void update_audio_window() { int xDraw = 0; int yDraw = 0; int tileNum = 0; SDL_Rect rc; rc.x = 0; rc.y = 0; rc.w = audioScreen->w; rc.h = audioScreen->h; SDL_FillRect(audioScreen, &rc, 0xFF111111); SDL_UpdateTexture(sdlAudioTexture, NULL, audioScreen->pixels, audioScreen->pitch); SDL_RenderClear(sdlAudioRenderer); SDL_RenderCopy(sdlAudioRenderer, sdlAudioTexture, NULL, NULL); SDL_Color text_color = {255,255,255}; int yOffset = 70; int lastY = (yOffset) - (audio_get_context()->sq1_history[audio_get_context()->sq1_index] * 50); int xOffset = 0; SDL_Surface* message = TTF_RenderText_Solid(sans, "Square 1", text_color); SDL_Texture* messageTexture = SDL_CreateTextureFromSurface(sdlAudioRenderer, message); SDL_Rect messageRect; messageRect.x = xOffset; messageRect.y = yOffset-70; messageRect.w = 50; messageRect.h = 20; SDL_RenderCopy(sdlAudioRenderer, messageTexture, NULL, &messageRect); SDL_SetRenderDrawColor(sdlAudioRenderer, 0, 255, 0, 255); SDL_FreeSurface(message); SDL_DestroyTexture(messageTexture); for(int x = 1; x < 384; x++){ int y = (yOffset) - (audio_get_context()->sq1_history[(x + audio_get_context()->sq1_index) % 384] * 50); SDL_RenderDrawLine(sdlAudioRenderer, x-1+xOffset, lastY, x+xOffset, y); lastY = y; if(x+xOffset == screenWidth) { break; } } yOffset = 190; lastY = (yOffset) - (audio_get_context()->sq2_history[audio_get_context()->sq2_index] * 50); message = TTF_RenderText_Solid(sans, "Square 2", text_color); messageTexture = SDL_CreateTextureFromSurface(sdlAudioRenderer, message); messageRect.x = xOffset; messageRect.y = yOffset-70; messageRect.w = 50; messageRect.h = 20; SDL_RenderCopy(sdlAudioRenderer, messageTexture, NULL, &messageRect); SDL_SetRenderDrawColor(sdlAudioRenderer, 0, 255, 0, 255); SDL_FreeSurface(message); SDL_DestroyTexture(messageTexture); SDL_SetRenderDrawColor(sdlAudioRenderer, 0, 255, 0, 255); for(int x = 1; x < 384; x++){ int y = (yOffset) - (audio_get_context()->sq2_history[(x + audio_get_context()->sq2_index) % 384] * 50); SDL_RenderDrawLine(sdlAudioRenderer, x-1+xOffset, lastY, x+xOffset, y); lastY = y; if(x+xOffset == screenWidth) { break; } } yOffset = 310; lastY = (yOffset) - (audio_get_context()->ch3_history[audio_get_context()->ch3_index] * 50); message = TTF_RenderText_Solid(sans, "Wave", text_color); messageTexture = SDL_CreateTextureFromSurface(sdlAudioRenderer, message); messageRect.x = xOffset; messageRect.y = yOffset-70; messageRect.w = 50; messageRect.h = 20; SDL_RenderCopy(sdlAudioRenderer, messageTexture, NULL, &messageRect); SDL_SetRenderDrawColor(sdlAudioRenderer, 0, 255, 0, 255); SDL_FreeSurface(message); SDL_DestroyTexture(messageTexture); SDL_SetRenderDrawColor(sdlAudioRenderer, 0, 255, 0, 255); SDL_SetRenderDrawColor(sdlAudioRenderer, 0, 255, 0, 255); for(int x = 1; x < 384; x++){ int y = (yOffset) - (audio_get_context()->ch3_history[(x + audio_get_context()->ch3_index) % 384] * 50); SDL_RenderDrawLine(sdlAudioRenderer, x-1+xOffset, lastY, x+xOffset, y); lastY = y; if(x+xOffset == screenWidth) { break; } } yOffset = 430; lastY = (yOffset) - (audio_get_context()->ch4_history[audio_get_context()->ch4_index] * 50); message = TTF_RenderText_Solid(sans, "Noise", text_color); messageTexture = SDL_CreateTextureFromSurface(sdlAudioRenderer, message); messageRect.x = xOffset; messageRect.y = yOffset-70; messageRect.w = 50; messageRect.h = 20; SDL_RenderCopy(sdlAudioRenderer, messageTexture, NULL, &messageRect); SDL_SetRenderDrawColor(sdlAudioRenderer, 0, 255, 0, 255); SDL_FreeSurface(message); SDL_DestroyTexture(messageTexture); SDL_SetRenderDrawColor(sdlAudioRenderer, 0, 255, 0, 255); SDL_SetRenderDrawColor(sdlAudioRenderer, 0, 255, 0, 255); for(int x = 1; x < 384; x++){ int y = (yOffset) - (audio_get_context()->ch4_history[(x + audio_get_context()->ch4_index) % 384] * 50); SDL_RenderDrawLine(sdlAudioRenderer, x-1+xOffset, lastY, x+xOffset, y); lastY = y; if(x+xOffset == screenWidth) { break; } } yOffset = 550; lastY = (yOffset) - (audio_get_context()->left_history[audio_get_context()->left_index] * 50); message = TTF_RenderText_Solid(sans, "Left Out", text_color); messageTexture = SDL_CreateTextureFromSurface(sdlAudioRenderer, message); messageRect.x = xOffset; messageRect.y = yOffset-70; messageRect.w = 50; messageRect.h = 20; SDL_RenderCopy(sdlAudioRenderer, messageTexture, NULL, &messageRect); SDL_SetRenderDrawColor(sdlAudioRenderer, 0, 255, 0, 255); SDL_FreeSurface(message); SDL_DestroyTexture(messageTexture); SDL_SetRenderDrawColor(sdlAudioRenderer, 0, 255, 0, 255); SDL_SetRenderDrawColor(sdlAudioRenderer, 0, 0, 255, 255); for(int x = 1; x < 384; x++){ int y = (yOffset) - (audio_get_context()->left_history[(x + audio_get_context()->left_index) % 384] * 50); SDL_RenderDrawLine(sdlAudioRenderer, x-1+xOffset, lastY, x+xOffset, y); lastY = y; if(x+xOffset == screenWidth) { break; } } yOffset = 680; lastY = (yOffset) - (audio_get_context()->right_history[audio_get_context()->right_index] * 50); message = TTF_RenderText_Solid(sans, "Right Out", text_color); messageTexture = SDL_CreateTextureFromSurface(sdlAudioRenderer, message); messageRect.x = xOffset; messageRect.y = yOffset-70; messageRect.w = 50; messageRect.h = 20; SDL_RenderCopy(sdlAudioRenderer, messageTexture, NULL, &messageRect); SDL_SetRenderDrawColor(sdlAudioRenderer, 0, 255, 0, 255); SDL_FreeSurface(message); SDL_DestroyTexture(messageTexture); SDL_SetRenderDrawColor(sdlAudioRenderer, 0, 255, 0, 255); SDL_SetRenderDrawColor(sdlAudioRenderer, 0, 0, 255, 255); for(int x = 1; x < 384; x++){ int y = (yOffset) - (audio_get_context()->right_history[(x + audio_get_context()->right_index) % 384] * 50); SDL_RenderDrawLine(sdlAudioRenderer, x-1+xOffset, lastY, x+xOffset, y); lastY = y; if(x+xOffset == screenWidth) { break; } } SDL_RenderPresent(sdlAudioRenderer); } void ui_update() { SDL_Rect rc; rc.x = rc.y = 0; rc.w = rc.h = 2048; u32 *video_buffer = ppu_get_context()->video_buffer; for(int line_num = 0; line_num < YRES; line_num++) { for(int x = 0; x < XRES; x++) { rc.x = x * scale; rc.y = line_num * scale; rc.w = scale; rc.h = scale; SDL_FillRect(screen, &rc, video_buffer[x + (line_num * XRES)]); } } SDL_UpdateTexture(sdlTexture, NULL, screen->pixels, screen->pitch); SDL_RenderClear(sdlRenderer); SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL); SDL_RenderPresent(sdlRenderer); update_vram_window(); update_audio_window(); } void ui_on_key(bool down, u32 key_code) { if(key_code == SDLK_SPACE && down == true) { emu_get_context()->fast_forward = !emu_get_context()->fast_forward; if(emu_get_context()->fast_forward){ ppu_get_context()->target_frame_time = 1000/(60 * FAST_FORWARD_SPEED); } else { ppu_get_context()->target_frame_time = 1000/60; } printf("target frame time: %d\n", ppu_get_context()->target_frame_time); } if(ppu_get_context()->paused) { if(key_code == SDLK_PERIOD && down == true){ ppu_get_context()->frame = true; } } if(key_code == SDLK_p && down == true) { ppu_get_context()->paused = !ppu_get_context()->paused; if(ppu_get_context()->paused) { printf("Paused Emulation\n"); } else { printf("Resumed Emulation\n"); } } if(key_code == SDLK_d && down == true) { ppu_get_context()->debug = !ppu_get_context()->debug; if(ppu_get_context()->debug) { printf("PPU Debug Enabled\n"); } else { printf("PPU Debug Disabled\n"); } } if(key_code == SDLK_r && down == true) { emu_stop(); emu_reset(); emu_start(); } if(key_code == SDLK_s && down == true) { emu_stop(); state_save_file(0); emu_start(); } if(key_code == SDLK_l && down == true) { emu_stop(); emu_reset(); state_load_file(0); emu_start(); } if(key_code == SDLK_v && down == true) { if(SDL_GetWindowFlags(sdlVramWindow) & SDL_WINDOW_HIDDEN) { SDL_ShowWindow(sdlVramWindow); } else { SDL_HideWindow(sdlVramWindow); } } if(key_code == SDLK_a && down == true) { if(SDL_GetWindowFlags(sdlAudioWindow) & SDL_WINDOW_HIDDEN) { SDL_ShowWindow(sdlAudioWindow); } else { SDL_HideWindow(sdlAudioWindow); } } switch(key_code){ case SDLK_z: gamepad_get_state()->b = down; break; case SDLK_x: gamepad_get_state()->a = down; break; case SDLK_RETURN: gamepad_get_state()->start = down; break; case SDLK_TAB: gamepad_get_state()->select = down; break; case SDLK_UP: gamepad_get_state()->up = down; break; case SDLK_DOWN: gamepad_get_state()->down = down; break; case SDLK_LEFT: gamepad_get_state()->left = down; break; case SDLK_RIGHT: gamepad_get_state()->right = down; break; } gamepad_int_update(); } void ui_handle_events(){ SDL_Event e; while(SDL_PollEvent(&e) > 0) { if(e.type == SDL_KEYDOWN) { ui_on_key(true, e.key.keysym.sym); } if(e.type == SDL_KEYUP) { ui_on_key(false, e.key.keysym.sym); } if(e.type == SDL_WINDOWEVENT && e.window.event == SDL_WINDOWEVENT_CLOSE) { if(e.window.windowID == SDL_GetWindowID(sdlWindow)){ emu_get_context()->die = true; } else { SDL_Window *w = SDL_GetWindowFromID(e.window.windowID); SDL_HideWindow(w); } } } } void delay(u32 ms) { SDL_Delay(ms); } u32 get_ticks() { return SDL_GetTicks(); }