From a0db379ed727bf32570a878da1ab2ed66a4dbb06 Mon Sep 17 00:00:00 2001 From: sarah-walker-pcem <67199234+sarah-walker-pcem@users.noreply.github.com> Date: Mon, 25 Nov 2024 07:28:19 +0000 Subject: [PATCH] Add debug viewers (#277) * Fix up global variable definitions in ibm.h This fixes multiple definition errors when including ibm.h in C++ code. * viewers: Add viewer infrastructure Viewers are windows showing the status of a particular component of the emulated machine; primarily for amusement, though may be useful for debugging in some situations. This commit adds the basic infrastructure for viewers. * viewers: Add (S)VGA palette viewers This adds two basic viewers for the (S)VGA palette; one for the 16-entry attribute controller palette, and one for the 256-entry RAMDAC palette. * viewers: Add (S)VGA font viewer Add a simple viewer to display the current (S)VGA font. * viewers: Add (S)VGA video memory viewer Add a viewer for (S)VGA video memory. This allows viewing of on and off screen video memory in the various supported bitmap formats. * viewers: Add 3DFX viewer Add a viewer for 3DFX state. This shows all triangles and textures involved in the most recent frame, and allows showing of framebuffer, depth buffer, and wireframes. It currently does not work properly in SLI configurations (only the first card will be viewed) and trilinear textures will not display correctly. --- CMakeLists.txt | 2 +- includes/private/cpu/x86.h | 12 +- includes/private/ibm.h | 48 +- includes/private/memory/mem.h | 4 +- includes/private/video/vid_voodoo_common.h | 3 + includes/private/wx-ui/viewer.h | 64 ++ includes/private/wx-ui/viewer_voodoo.h | 18 + src/CMakeLists.txt | 1 + src/cpu/386_common.c | 3 + src/cpu/cpu.c | 2 + src/floppy/fdd.c | 2 + src/hdd/hdd.c | 2 + src/memory/mem.c | 11 +- src/models/dma.c | 2 + src/models/pic.c | 2 + src/models/pit.c | 3 + src/pc.c | 9 + src/ppi.c | 2 + src/sound/sound_speaker.c | 3 + src/video/vid_svga.c | 20 +- src/video/vid_voodoo.c | 5 + src/video/vid_voodoo_reg.c | 19 + src/video/vid_voodoo_render.c | 5 + src/video/vid_voodoo_texture.c | 8 + src/wx-ui/pc.xrc | 251 ++++- src/wx-ui/viewers/viewer.cc | 120 +++ src/wx-ui/viewers/viewer_font.cc | 99 ++ src/wx-ui/viewers/viewer_palette.cc | 159 +++ src/wx-ui/viewers/viewer_voodoo.cc | 1088 ++++++++++++++++++++ src/wx-ui/viewers/viewer_vram.cc | 698 +++++++++++++ src/wx-ui/viewers/viewers.cmake | 13 + src/wx-ui/wx-sdl2.c | 8 + 32 files changed, 2636 insertions(+), 50 deletions(-) create mode 100644 includes/private/wx-ui/viewer.h create mode 100644 includes/private/wx-ui/viewer_voodoo.h create mode 100644 src/wx-ui/viewers/viewer.cc create mode 100644 src/wx-ui/viewers/viewer_font.cc create mode 100644 src/wx-ui/viewers/viewer_palette.cc create mode 100644 src/wx-ui/viewers/viewer_voodoo.cc create mode 100644 src/wx-ui/viewers/viewer_vram.cc create mode 100644 src/wx-ui/viewers/viewers.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index e73870d1..0a562524 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -134,7 +134,7 @@ find_package(OpenAL REQUIRED) include_directories(${OPENAL_INCLUDE_DIR}) if(${PCEM_DISPLAY_ENGINE} STREQUAL "wxWidgets") - find_package(wxWidgets REQUIRED COMPONENTS core base xrc) + find_package(wxWidgets REQUIRED COMPONENTS core base xrc adv) include(${wxWidgets_USE_FILE}) set(DISPLAY_ENGINE_LIBRARIES ${wxWidgets_LIBRARIES}) endif() diff --git a/includes/private/cpu/x86.h b/includes/private/cpu/x86.h index 37671dd9..7b054975 100644 --- a/includes/private/cpu/x86.h +++ b/includes/private/cpu/x86.h @@ -55,7 +55,7 @@ typedef union MMX_REG { float f[2]; } MMX_REG; -struct { +typedef struct cpu_state_t { x86reg regs[8]; uint8_t tag[8]; @@ -113,7 +113,9 @@ struct { uint16_t flags, eflags; uint32_t smbase; -} cpu_state; +} cpu_state_t; + +extern cpu_state_t cpu_state; #define cpu_state_offset(MEMBER) ((uintptr_t)&cpu_state.MEMBER - (uintptr_t)&cpu_state - 128) @@ -318,12 +320,14 @@ void cyrix_write_seg_descriptor(uint32_t addr, x86seg *seg); #define SMHR_VALID (1 << 0) #define SMHR_ADDR_MASK (0xfffffffc) -struct { +typedef struct cyrix_t { struct { uint32_t base; uint64_t size; } arr[8]; uint32_t smhr; -} cyrix; +} cyrix_t; + +extern cyrix_t cyrix; #endif /* _X86_H_ */ diff --git a/includes/private/ibm.h b/includes/private/ibm.h index e18fd7bf..0652cfe2 100644 --- a/includes/private/ibm.h +++ b/includes/private/ibm.h @@ -23,16 +23,16 @@ #define readflash_get(offset, drive) ((readflash & (1 << ((offset) + (drive)))) != 0) /*Memory*/ -uint8_t *ram; +extern uint8_t *ram; -uint32_t rammask; +extern uint32_t rammask; -int readlookup[256], readlookupp[256]; -uintptr_t *readlookup2; -int readlnext; -int writelookup[256], writelookupp[256]; -uintptr_t *writelookup2; -int writelnext; +extern int readlookup[256], readlookupp[256]; +extern uintptr_t *readlookup2; +extern int readlnext; +extern int writelookup[256], writelookupp[256]; +extern uintptr_t *writelookup2; +extern int writelnext; extern int mmu_perm; @@ -104,7 +104,7 @@ typedef struct PIT { void (*set_out_funcs[3])(int new_out, int old_out); } PIT; -PIT pit, pit2; +extern PIT pit, pit2; void setpitclock(float clock); float pit_timer0_freq(); @@ -130,7 +130,7 @@ typedef struct dma_t { uint16_t io_addr; } dma_t; -dma_t dma[8]; +extern dma_t dma[8]; /*PPI*/ typedef struct PPI { @@ -138,7 +138,7 @@ typedef struct PPI { uint8_t pa, pb; } PPI; -PPI ppi; +extern PPI ppi; /*PIC*/ typedef struct PIC { @@ -149,15 +149,15 @@ typedef struct PIC { uint8_t level_sensitive; } PIC; -PIC pic, pic2; +extern PIC pic, pic2; extern int pic_intpending; -char discfns[2][256]; -int driveempty[2]; +extern char discfns[2][256]; +extern int driveempty[2]; #define PCJR (romset == ROM_IBMPCJR) -int GAMEBLASTER, GUS, SSI2001, voodoo_enabled; +extern int GAMEBLASTER, GUS, SSI2001, voodoo_enabled; extern int AMSTRAD, AT, is386, PCI, TANDY, MCA; enum { @@ -268,8 +268,8 @@ enum { extern int romspresent[ROM_MAX]; -int hasfpu; -int romset; +extern int hasfpu; +extern int romset; enum { GFX_BUILTIN = -1, @@ -327,12 +327,12 @@ enum { extern int gfx_present[GFX_MAX]; -int gfxcard; +extern int gfxcard; -int cpuspeed; +extern int cpuspeed; /*Video*/ -int readflash; +extern int readflash; extern int egareads, egawrites; extern int vid_resize; extern int vid_api; @@ -341,12 +341,12 @@ extern int winsizex, winsizey; extern int changeframecount; /*Sound*/ -int ppispeakon; +extern int ppispeakon; extern uint64_t CGACONST; extern uint64_t MDACONST; extern uint64_t VGACONST1, VGACONST2; extern uint64_t RTCCONST; -int gated, speakval, speakon; +extern int gated, speakval, speakon; /*Sound Blaster*/ #define SADLIB 1 /*No DSP*/ @@ -368,10 +368,10 @@ typedef struct { int tracks; } PcemHDC; -PcemHDC hdc[7]; +extern PcemHDC hdc[7]; /*Keyboard*/ -int keybsenddelay; +extern int keybsenddelay; /*CD-ROM*/ extern int cdrom_drive; diff --git a/includes/private/memory/mem.h b/includes/private/memory/mem.h index ec6f5137..19136ee4 100644 --- a/includes/private/memory/mem.h +++ b/includes/private/memory/mem.h @@ -96,8 +96,8 @@ void mem_write_nulll(uint32_t addr, uint32_t val, void *p); FILE *romfopen(char *fn, char *mode); -mem_mapping_t bios_mapping[8]; -mem_mapping_t bios_high_mapping[9]; +extern mem_mapping_t bios_mapping[8]; +extern mem_mapping_t bios_high_mapping[9]; extern mem_mapping_t ram_high_mapping; extern mem_mapping_t ram_remapped_mapping; diff --git a/includes/private/video/vid_voodoo_common.h b/includes/private/video/vid_voodoo_common.h index dcf896aa..6045a095 100644 --- a/includes/private/video/vid_voodoo_common.h +++ b/includes/private/video/vid_voodoo_common.h @@ -294,6 +294,7 @@ typedef struct voodoo_t { unsigned int vertex_next_age; int num_verticies; int cull_pingpong; + int in_strip; int flush; @@ -463,6 +464,8 @@ typedef struct voodoo_t { uint8_t *vram, *changedvram; void *p; + + int viewer_active; } voodoo_t; typedef struct voodoo_set_t { diff --git a/includes/private/wx-ui/viewer.h b/includes/private/wx-ui/viewer.h new file mode 100644 index 00000000..0414a8e3 --- /dev/null +++ b/includes/private/wx-ui/viewer.h @@ -0,0 +1,64 @@ +#ifndef _VIEWER_H_ +#define _VIEWER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct viewer_t +{ + void *(*open)(void *parent, void *p, const char *title); +} viewer_t; + +void viewer_reset(); +void viewer_add(char *title, viewer_t *viewer, void *p); +void viewer_open(void *hwnd, int id); +void viewer_remove(void *viewer); +void viewer_update(viewer_t *viewer, void *p); +void viewer_call(viewer_t *viewer, void *p, void (*func)(void *v, void *param), void *param); +void viewer_close_all(); +void viewer_notify_pause(); +void viewer_notify_resume(); +void update_viewers_menu(void *menu); + +extern viewer_t viewer_font; +extern viewer_t viewer_palette; +extern viewer_t viewer_palette_16; +extern viewer_t viewer_voodoo; +extern viewer_t viewer_vram; + +#define IDM_VIEWER 1600 +#define IDM_VIEWER_MAX 1700 + +#ifdef __cplusplus +} + + +class Viewer: public wxFrame +{ +public: + void *p; + + Viewer(wxWindow *parent, wxString title, wxSize size, void *p) + : wxFrame(parent, wxID_ANY, title, wxPoint(50, 50), size, wxDEFAULT_FRAME_STYLE), + p(p) + { + SetClientSize(size); + } + + virtual ~Viewer() + { + } + + virtual void NotifyPause() + { + } + + virtual void NotifyResume() + { + } +}; + +#endif + +#endif \ No newline at end of file diff --git a/includes/private/wx-ui/viewer_voodoo.h b/includes/private/wx-ui/viewer_voodoo.h new file mode 100644 index 00000000..0cdeaf05 --- /dev/null +++ b/includes/private/wx-ui/viewer_voodoo.h @@ -0,0 +1,18 @@ +#ifndef _VIEWER_VOODOO_H_ +#define _VIEWER_VOODOO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +void voodoo_viewer_swap_buffer(void *v, void *param); +void voodoo_viewer_queue_triangle(void *v, void *param); +void voodoo_viewer_begin_strip(void *v, void *param); +void voodoo_viewer_end_strip(void *v, void *param); +void voodoo_viewer_use_texture(void *v, void *param); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fa40f826..a692a92c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -51,6 +51,7 @@ include(${CMAKE_CURRENT_SOURCE_DIR}/video/video.cmake) if(${PCEM_DISPLAY_ENGINE} STREQUAL "wxWidgets") include(${CMAKE_CURRENT_SOURCE_DIR}/wx-ui/wx-ui.cmake) + include(${CMAKE_CURRENT_SOURCE_DIR}/wx-ui/viewers/viewers.cmake) endif() if(${PCEM_DISPLAY_ENGINE} STREQUAL "Qt") message(FATAL_ERROR "Qt Mode is not yet implemented.") diff --git a/src/cpu/386_common.c b/src/cpu/386_common.c index 27111683..1e08c82b 100644 --- a/src/cpu/386_common.c +++ b/src/cpu/386_common.c @@ -4,6 +4,9 @@ #include "x86_flags.h" #include "codegen.h" +cpu_state_t cpu_state; +cyrix_t cyrix; + x86seg gdt, ldt, idt, tr; uint32_t cr2, cr3, cr4; diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c index 9d6b2cb2..92f09905 100644 --- a/src/cpu/cpu.c +++ b/src/cpu/cpu.c @@ -17,6 +17,8 @@ static int cpu_turbo = 1; int isa_cycles; int has_vlb; static uint8_t ccr0, ccr1, ccr2, ccr3, ccr4, ccr5, ccr6; +int hasfpu; +int cpuspeed; OpFn *x86_dynarec_opcodes; OpFn *x86_dynarec_opcodes_0f; diff --git a/src/floppy/fdd.c b/src/floppy/fdd.c index ddffef39..510bb243 100644 --- a/src/floppy/fdd.c +++ b/src/floppy/fdd.c @@ -3,6 +3,8 @@ #include "fdc.h" #include "fdd.h" +char discfns[2][256]; + static struct { int type; diff --git a/src/hdd/hdd.c b/src/hdd/hdd.c index a00bc576..d7509a18 100644 --- a/src/hdd/hdd.c +++ b/src/hdd/hdd.c @@ -15,6 +15,8 @@ #include #include +PcemHDC hdc[7]; + extern HDD_CONTROLLER *hdd_controllers[HDDCONTROLLERS_MAX]; char hdd_controller_name[16]; diff --git a/src/memory/mem.c b/src/memory/mem.c index 9d5e4f51..41f6f5dc 100644 --- a/src/memory/mem.c +++ b/src/memory/mem.c @@ -58,6 +58,15 @@ void (*smram_disable)(void); int mmuflush = 0; int mmu_perm = 4; +int readlookup[256], readlookupp[256]; +uintptr_t *readlookup2; +int readlnext; +int writelookup[256], writelookupp[256]; +uintptr_t *writelookup2; +int writelnext; + +uint32_t rammask; + int mem_addr_is_ram(uint32_t addr) { mem_mapping_t *mapping = read_mapping[addr >> 14]; @@ -1161,7 +1170,7 @@ void mem_mapping_remove(mem_mapping_t *mapping) dest = dest->next; } prev->next = mapping->next; - + mem_mapping_recalc(mapping->base, mapping->size); } diff --git a/src/models/dma.c b/src/models/dma.c index ccc81459..4b17e47b 100644 --- a/src/models/dma.c +++ b/src/models/dma.c @@ -8,6 +8,8 @@ #include "video.h" #include "x86.h" +dma_t dma[8]; + static uint8_t dmaregs[16]; static uint8_t dma16regs[16]; static uint8_t dmapages[16]; diff --git a/src/models/pic.c b/src/models/pic.c index da1d1ff9..905681c9 100644 --- a/src/models/pic.c +++ b/src/models/pic.c @@ -4,6 +4,8 @@ #include "pit.h" #include "video.h" +PIC pic, pic2; + int intclear; int keywaiting = 0; int pic_intpending; diff --git a/src/models/pit.c b/src/models/pit.c index eb3b1608..fc295544 100644 --- a/src/models/pit.c +++ b/src/models/pit.c @@ -16,6 +16,9 @@ #include "timer.h" #include "video.h" #include "model.h" + +PIT pit, pit2; + /*B0 to 40, two writes to 43, then two reads - value does not change!*/ /*B4 to 40, two writes to 43, then two reads - value _does_ change!*/ // Tyrian writes 4300 or 17512 diff --git a/src/pc.c b/src/pc.c index 4f30a527..2eb6def2 100644 --- a/src/pc.c +++ b/src/pc.c @@ -61,6 +61,7 @@ #include "x86.h" #include "paths.h" #include "plugin.h" +#include "viewer.h" #ifdef USE_NETWORKING #include "nethandler.h" @@ -70,6 +71,11 @@ uint8_t ethif; int inum; #endif +int GAMEBLASTER, GUS, SSI2001, voodoo_enabled; +int gfxcard; +int readflash; +int romset; + int window_w, window_h, window_x, window_y, window_remember; int start_in_fullscreen = 0; @@ -248,6 +254,7 @@ void initpc(int argc, char *argv[]) { atfullspeed = 0; device_init(); + viewer_reset(); initvideo(); mem_init(); @@ -344,7 +351,9 @@ void resetpc_cad() { void resetpchard() { device_close_all(); mouse_emu_close(); + viewer_close_all(); device_init(); + viewer_reset(); timer_reset(); sound_reset(); diff --git a/src/ppi.c b/src/ppi.c index 9f8d30bc..9482fe0b 100644 --- a/src/ppi.c +++ b/src/ppi.c @@ -11,6 +11,8 @@ #include "plat-keyboard.h" #include "plat-mouse.h" +PPI ppi; + void ppi_reset() { ppi.pa = 0x0; // 0x1D; ppi.pb = 0x40; diff --git a/src/sound/sound_speaker.c b/src/sound/sound_speaker.c index 57aa32dc..d149ba51 100644 --- a/src/sound/sound_speaker.c +++ b/src/sound/sound_speaker.c @@ -2,6 +2,9 @@ #include "sound.h" #include "sound_speaker.h" +int gated, speakval, speakon; +int ppispeakon; + int speaker_mute = 0; static int16_t speaker_buffer[MAXSOUNDBUFLEN]; diff --git a/src/video/vid_svga.c b/src/video/vid_svga.c index a592c6f4..6f36ccf1 100644 --- a/src/video/vid_svga.c +++ b/src/video/vid_svga.c @@ -8,6 +8,7 @@ #include "vid_svga_render.h" #include "io.h" #include "timer.h" +#include "viewer.h" #define svga_output 0 @@ -600,6 +601,8 @@ void svga_poll(void *p) { } } if (svga->vc == svga->dispend) { + int changed = 0; + if (svga->vblank_start) svga->vblank_start(svga); // pclog("VC dispend\n"); @@ -614,11 +617,21 @@ void svga_poll(void *p) { for (x = 0; x < ((svga->vram_mask + 1) >> 12); x++) { if (svga->changedvram[x]) + { svga->changedvram[x]--; + changed = 1; + } } // memset(changedvram,0,2048); - if (svga->fullchange) + if (svga->fullchange) { svga->fullchange--; + viewer_update(&viewer_palette, svga); + viewer_update(&viewer_palette_16, svga); + } + if (changed) { + viewer_update(&viewer_font, svga); + viewer_update(&viewer_vram, svga); + } } if (svga->vc == svga->vsyncstart) { int wx, wy; @@ -780,6 +793,11 @@ int svga_init(svga_t *svga, void *p, int memsize, void (*recalctimings_ex)(struc svga->ramdac_type = RAMDAC_6BIT; + viewer_add("256-colour palette", &viewer_palette, svga); + viewer_add("16-colour palette", &viewer_palette_16, svga); + viewer_add("Font", &viewer_font, svga); + viewer_add("Video memory", &viewer_vram, svga); + return 0; } diff --git a/src/video/vid_voodoo.c b/src/video/vid_voodoo.c index 43e67e25..f1322abe 100644 --- a/src/video/vid_voodoo.c +++ b/src/video/vid_voodoo.c @@ -20,6 +20,7 @@ #include "vid_voodoo_regs.h" #include "vid_voodoo_render.h" #include "vid_voodoo_texture.h" +#include "viewer.h" rgba8_t rgb332[0x100], ai44[0x100], rgb565[0x10000], argb1555[0x10000], argb4444[0x10000], ai88[0x10000]; @@ -1156,6 +1157,8 @@ void *voodoo_2d3d_card_init(int type) { voodoo->disp_buffer = 0; voodoo->draw_buffer = 1; + viewer_add("3DFX Voodoo render", &viewer_voodoo, voodoo); + return voodoo; } @@ -1209,6 +1212,8 @@ void *voodoo_init() { mem_mapping_add(&voodoo_set->snoop_mapping, 0, 0, NULL, voodoo_snoop_readw, voodoo_snoop_readl, NULL, voodoo_snoop_writew, voodoo_snoop_writel, NULL, MEM_MAPPING_EXTERNAL, voodoo_set); + viewer_add("3DFX Voodoo render", &viewer_voodoo, voodoo_set->voodoos[0]); + return voodoo_set; } diff --git a/src/video/vid_voodoo_reg.c b/src/video/vid_voodoo_reg.c index c6b540e0..8e5cbbf1 100644 --- a/src/video/vid_voodoo_reg.c +++ b/src/video/vid_voodoo_reg.c @@ -17,6 +17,8 @@ #include "vid_voodoo_render.h" #include "vid_voodoo_setup.h" #include "vid_voodoo_texture.h" +#include "viewer.h" +#include "viewer_voodoo.h" enum { CHIP_FBI = 0x1, CHIP_TREX0 = 0x2, CHIP_TREX1 = 0x4, CHIP_TREX2 = 0x8 }; @@ -43,6 +45,8 @@ void voodoo_reg_writel(uint32_t addr, uint32_t val, void *p) { // pclog("swapbufferCMD %08x %08x\n", val, voodoo->leftOverlayBuf); voodoo_wait_for_render_thread_idle(voodoo); + if (voodoo->viewer_active) + viewer_call(&viewer_voodoo, voodoo, voodoo_viewer_swap_buffer, NULL); if (!(val & 1)) { banshee_set_overlay_addr(voodoo->p, voodoo->leftOverlayBuf); thread_lock_mutex(voodoo->swap_mutex); @@ -82,6 +86,8 @@ void voodoo_reg_writel(uint32_t addr, uint32_t val, void *p) { // pclog("Swap buffer %08x %d %p %i\n", val, voodoo->swap_count, &voodoo->swap_count, (voodoo == // voodoo->set->voodoos[1]) ? 1 : 0); voodoo->front_offset = params->front_offset; voodoo_wait_for_render_thread_idle(voodoo); + if (voodoo->viewer_active) + viewer_call(&viewer_voodoo, voodoo, voodoo_viewer_swap_buffer, NULL); if (!(val & 1)) { memset(voodoo->dirty_line, 1, sizeof(voodoo->dirty_line)); voodoo->front_offset = voodoo->params.front_offset; @@ -265,6 +271,11 @@ void voodoo_reg_writel(uint32_t addr, uint32_t val, void *p) { case SST_triangleCMD: case SST_remap_triangleCMD: + if (voodoo->in_strip) { + voodoo->in_strip = 0; + if (voodoo->viewer_active) + viewer_call(&viewer_voodoo, voodoo, voodoo_viewer_end_strip, NULL); + } voodoo->params.sign = val & (1 << 31); if (voodoo->ncc_dirty[0]) @@ -466,6 +477,11 @@ void voodoo_reg_writel(uint32_t addr, uint32_t val, void *p) { break; case SST_ftriangleCMD: + if (voodoo->in_strip) { + voodoo->in_strip = 0; + if (voodoo->viewer_active) + viewer_call(&viewer_voodoo, voodoo, voodoo_viewer_end_strip, NULL); + } voodoo->params.sign = val & (1 << 31); if (voodoo->ncc_dirty[0]) @@ -745,6 +761,9 @@ void voodoo_reg_writel(uint32_t addr, uint32_t val, void *p) { voodoo->num_verticies = 1; voodoo->cull_pingpong = 0; + if (voodoo->viewer_active) + viewer_call(&viewer_voodoo, voodoo, voodoo_viewer_begin_strip, NULL); + voodoo->in_strip = 1; break; case SST_sDrawTriCMD: // pclog("sDrawTriCMD %i %i\n", voodoo->num_verticies, voodoo->sSetupMode & SETUPMODE_STRIP_MODE); diff --git a/src/video/vid_voodoo_render.c b/src/video/vid_voodoo_render.c index 8be4afa0..e66c788e 100644 --- a/src/video/vid_voodoo_render.c +++ b/src/video/vid_voodoo_render.c @@ -12,6 +12,8 @@ #include "vid_voodoo_regs.h" #include "vid_voodoo_render.h" #include "vid_voodoo_texture.h" +#include "viewer.h" +#include "viewer_voodoo.h" typedef struct voodoo_state_t { int xstart, xend, xdir; @@ -1504,6 +1506,9 @@ void voodoo_queue_triangle(voodoo_t *voodoo, voodoo_params_t *params) { if (voodoo->dual_tmus) voodoo_use_texture(voodoo, params, 1); + if (voodoo->viewer_active) + viewer_call(&viewer_voodoo, voodoo, voodoo_viewer_queue_triangle, NULL); + memcpy(params_new, params, sizeof(voodoo_params_t)); voodoo->params_write_idx++; diff --git a/src/video/vid_voodoo_texture.c b/src/video/vid_voodoo_texture.c index 6bf6f590..7a8289f9 100644 --- a/src/video/vid_voodoo_texture.c +++ b/src/video/vid_voodoo_texture.c @@ -12,6 +12,8 @@ #include "vid_voodoo_regs.h" #include "vid_voodoo_render.h" #include "vid_voodoo_texture.h" +#include "viewer.h" +#include "viewer_voodoo.h" void voodoo_recalc_tex(voodoo_t *voodoo, int tmu) { int aspect = (voodoo->params.tLOD[tmu] >> 21) & 3; @@ -133,6 +135,7 @@ void voodoo_use_texture(voodoo_t *voodoo, voodoo_params_t *params, int tmu) { else addr = params->texBaseAddr[tmu]; + /*Try to find texture in cache*/ for (c = 0; c < TEX_CACHE_MAX; c++) { if (voodoo->texture_cache[tmu][c].base == addr && @@ -140,6 +143,8 @@ void voodoo_use_texture(voodoo_t *voodoo, voodoo_params_t *params, int tmu) { voodoo->texture_cache[tmu][c].palette_checksum == palette_checksum) { params->tex_entry[tmu] = c; voodoo->texture_cache[tmu][c].refcount++; + if (voodoo->viewer_active) + viewer_call(&viewer_voodoo, voodoo, voodoo_viewer_use_texture, (void *)(uintptr_t)tmu); return; } } @@ -428,6 +433,9 @@ void voodoo_use_texture(voodoo_t *voodoo, voodoo_params_t *params, int tmu) { params->tex_entry[tmu] = c; voodoo->texture_cache[tmu][c].refcount++; + + if (voodoo->viewer_active) + viewer_call(&viewer_voodoo, voodoo, voodoo_viewer_use_texture, (void *)(uintptr_t)tmu); } void flush_texture_cache(voodoo_t *voodoo, uint32_t dirty_addr, int tmu) { diff --git a/src/wx-ui/pc.xrc b/src/wx-ui/pc.xrc index 4853ac4f..405f857f 100644 --- a/src/wx-ui/pc.xrc +++ b/src/wx-ui/pc.xrc @@ -490,6 +490,9 @@ + + + @@ -1600,7 +1603,7 @@ wxEXPAND 5 - wxHORIZONTAL + wxHORIZONTAL wxALL @@ -1788,7 +1791,7 @@ wxEXPAND 5 - wxHORIZONTAL + wxHORIZONTAL wxALL @@ -1976,7 +1979,7 @@ wxEXPAND 5 - wxHORIZONTAL + wxHORIZONTAL wxALL @@ -2164,7 +2167,7 @@ wxEXPAND 5 - wxHORIZONTAL + wxHORIZONTAL wxALL @@ -2352,7 +2355,7 @@ wxEXPAND 5 - wxHORIZONTAL + wxHORIZONTAL wxALL @@ -2540,7 +2543,7 @@ wxEXPAND 5 - wxHORIZONTAL + wxHORIZONTAL wxALL @@ -2728,7 +2731,7 @@ wxEXPAND 5 - wxHORIZONTAL + wxHORIZONTAL wxALL @@ -2893,7 +2896,7 @@ - + New Hard Disc 1 @@ -3521,7 +3524,7 @@ 0 wxVERTICAL - + wxALL 0 @@ -3529,7 +3532,7 @@ - + wxALL 0 @@ -3537,12 +3540,12 @@ - + wxEXPAND - + wxALL 0 @@ -3550,7 +3553,7 @@ - + wxALL 0 @@ -3558,7 +3561,7 @@ - + wxALL 0 @@ -3566,7 +3569,7 @@ - + wxEXPAND @@ -3668,7 +3671,7 @@ - + Custom resolution @@ -3748,7 +3751,221 @@ - + + + + + + wxVERTICAL + + + wxALL + 5 + + + + + + + wxALL + + + + + + + + wxALL + + wxHORIZONTAL + + + wxALL + + + 1 + + + + + wxALL + + 0 + + + + + + + + wxALL + 5 + + + + + + + wxALL + + + + + + + + wxALL + + wxHORIZONTAL + + + wxALL + + + + + + + wxALL + + 80 + + + + + + + + wxALL + 5 + + + + + + + wxALL + + + + + + + + wxALL + + + + + + + wxALL + + + + + + + wxALL + + + + + + + wxALL + + + + + + + wxALL + + + + + + + wxALL + + + + + + + wxALL + + + + + + + wxALL + + + + + + + wxALL + 5 + + + + + + + wxALL + + + + + + + + wxALL + + + + + + + wxALL + + + + + + + wxALL + + + + + + + + wxALL + 5 + + + + + + + wxALL + + + 1 + 1 + 16 + + + + + + diff --git a/src/wx-ui/viewers/viewer.cc b/src/wx-ui/viewers/viewer.cc new file mode 100644 index 00000000..b8b94a18 --- /dev/null +++ b/src/wx-ui/viewers/viewer.cc @@ -0,0 +1,120 @@ +#include +#ifndef WX_PRECOMP +#include +#endif +#include + +#include "viewer.h" +#include +#include +#include + +extern "C" void pclog(const char *format, ...); + +class ViewerRout +{ +public: + const std::string title; + viewer_t *viewer; + void *p; + + ViewerRout() + { + } + ViewerRout(char *title, viewer_t *viewer, void *p) : title(title), viewer(viewer), p(p) + { + } +}; + +std::vector viewer_routs; +std::list viewer_windows; + +void viewer_reset() +{ + viewer_routs.clear(); + viewer_windows.clear(); +} + +void viewer_close_all() +{ + std::list viewer_windows_2 = viewer_windows; + + for (std::list::iterator it = viewer_windows_2.begin(); it != viewer_windows_2.end(); it++) + (*it)->Close(true); +} + +void viewer_add(char *title, viewer_t *viewer, void *p) +{ + viewer_routs.push_back(ViewerRout(title, viewer, p)); +} + +void viewer_open(void *parent, int id) +{ + Viewer *i = (Viewer *)viewer_routs[id].viewer->open(parent, viewer_routs[id].p, viewer_routs[id].title.c_str()); + if (i) + viewer_windows.push_back(i); +} + +void viewer_remove(void *viewer) +{ + viewer_windows.remove((Viewer *)viewer); +} + +void viewer_update(viewer_t *viewer, void *p) +{ + for (std::list::iterator it = viewer_windows.begin(); it != viewer_windows.end(); it++) + { + if ((*it)->p == p) + { + (*it)->Refresh(); + } + } +} + +void viewer_notify_pause() +{ + for (std::list::iterator it = viewer_windows.begin(); it != viewer_windows.end(); it++) + { + (*it)->NotifyPause(); + } +} + +void viewer_notify_resume() +{ + for (std::list::iterator it = viewer_windows.begin(); it != viewer_windows.end(); it++) + { + (*it)->NotifyResume(); + } +} + +void viewer_call(viewer_t *viewer, void *p, void (*func)(void *v, void *param), void *param) +{ + for (std::list::iterator it = viewer_windows.begin(); it != viewer_windows.end(); it++) + { + if ((*it)->p == p) + { + func(*it, param); + } + } +} + +void update_viewers_menu(void *menu) +{ + wxMenuItem *m = ((wxMenu *)menu)->FindItem(XRCID("IDM_VIEW")); + wxMenu *wm = m->GetSubMenu(); + + for (int i = IDM_VIEWER; i < IDM_VIEWER_MAX; i++) + { + wxMenuItem *menu_item = wm->FindChildItem(i); + + if (menu_item) + wm->Delete(i); + } + + int id = IDM_VIEWER; + + for (std::vector::iterator it = viewer_routs.begin(); it != viewer_routs.end(); it++) + { + wm->Append(id++, (*it).title, wxEmptyString, wxITEM_NORMAL); + } +} \ No newline at end of file diff --git a/src/wx-ui/viewers/viewer_font.cc b/src/wx-ui/viewers/viewer_font.cc new file mode 100644 index 00000000..933e0173 --- /dev/null +++ b/src/wx-ui/viewers/viewer_font.cc @@ -0,0 +1,99 @@ +#include +#ifndef WX_PRECOMP +#include +#endif +#include +#include "viewer.h" +extern "C" +{ +#include "ibm.h" +#include "mem.h" +#include "video.h" +#include "vid_svga.h" +} + +class ViewerFont: public Viewer +{ +private: + svga_t *svga; + wxImage buffer; + + void OnPaint(wxPaintEvent &event) + { + wxPaintDC dc(this); + wxCoord w, h; + + dc.GetSize(&w, &h); + + dc.SetBackground(*wxBLACK_BRUSH); + dc.Clear(); + + unsigned char *buffer_data = buffer.GetData(); + int buffer_width = buffer.GetWidth(); + + for (int y = 0; y < 16; y++) + { + int font_base = ((y & 8) ? svga->charsetb : svga->charseta) + ((y & 7) * 32 * 128); + + for (int x = 0; x < 32; x++) + { + int font_addr = font_base + x * 128; + + for (int yy = 0; yy < 16; yy++) + { + unsigned char *data = buffer_data + (y * 16 + yy) * 3 * buffer_width + x * 8 * 3; + + for (int xx = 0; xx < 8; xx++) + { + *data++ = (svga->vram[font_addr] & (0x80 >> xx)) ? 0xff : 0; + *data++ = (svga->vram[font_addr] & (0x80 >> xx)) ? 0xff : 0; + *data++ = (svga->vram[font_addr] & (0x80 >> xx)) ? 0xff : 0; + } + + font_addr += 4; + } + } + } + + wxBitmap bitmap(buffer); + + wxMemoryDC mdc(bitmap); + + dc.StretchBlit(0, 0, w, h, &mdc, 0, 0, 256, 256); + + } + + void OnClose(wxCloseEvent &event) + { + viewer_remove(this); + event.Skip(); + } + +public: + ViewerFont(wxWindow *parent, wxString title, wxSize size, void *p) + : Viewer(parent, title, size, p), + svga((svga_t *)p), + buffer(256, 256) + { + Bind(wxEVT_CLOSE_WINDOW, &ViewerFont::OnClose, this); + Bind(wxEVT_PAINT, &ViewerFont::OnPaint, this); + } + + virtual ~ViewerFont() + { + } +}; + +static void *viewer_font_open(void *parent, void *p, const char *title) +{ + wxFrame *w = new ViewerFont((wxWindow *)parent, title, wxSize(256, 256), p); + + w->Show(true); + + return w; +} + +viewer_t viewer_font = +{ + .open = viewer_font_open +}; diff --git a/src/wx-ui/viewers/viewer_palette.cc b/src/wx-ui/viewers/viewer_palette.cc new file mode 100644 index 00000000..17c76459 --- /dev/null +++ b/src/wx-ui/viewers/viewer_palette.cc @@ -0,0 +1,159 @@ +#include +#ifndef WX_PRECOMP +#include +#endif +#include +#include "viewer.h" +extern "C" +{ +#include "ibm.h" +#include "mem.h" +#include "video.h" +#include "vid_svga.h" +} + +class ViewerPalette: public Viewer +{ +private: + svga_t *svga; + wxBitmap buffer; + + void OnPaint(wxPaintEvent &event) + { + wxPaintDC dc(this); + wxCoord w, h; + + dc.GetSize(&w, &h); + + dc.SetBackground(*wxBLACK_BRUSH); + dc.Clear(); + + { + wxNativePixelData data(buffer); + wxNativePixelData::Iterator p(data); + + for (int y = 0; y < 16; y++) + { + wxNativePixelData::Iterator rowStart = p; + + for (int x = 0; x < 16; x++) + { + p.Red() = svga->vgapal[x + y*16].r * 4; + p.Green() = svga->vgapal[x + y*16].g * 4; + p.Blue() = svga->vgapal[x + y*16].b * 4; + p++; + } + + p = rowStart; + p.OffsetY(data, 1); + } + } + + wxMemoryDC mdc(buffer); + + dc.StretchBlit(0, 0, w, h, &mdc, 0, 0, 16, 16); + } + + void OnClose(wxCloseEvent &event) + { + viewer_remove(this); + event.Skip(); + } + +public: + ViewerPalette(wxWindow *parent, wxString title, wxSize size, void *p) + : Viewer(parent, title, size, p), + svga((svga_t *)p), + buffer(16, 16, 24) + { + Bind(wxEVT_CLOSE_WINDOW, &ViewerPalette::OnClose, this); + Bind(wxEVT_PAINT, &ViewerPalette::OnPaint, this); + } + + virtual ~ViewerPalette() + { + } +}; + +static void *viewer_palette_open(void *parent, void *p, const char *title) +{ + wxFrame *w = new ViewerPalette((wxWindow *)parent, title, wxSize(256, 256), p); + + w->Show(true); + + return w; +} + +viewer_t viewer_palette = +{ + .open = viewer_palette_open +}; + +class ViewerPalette16: public Viewer +{ +private: + svga_t *svga; + wxBitmap buffer; + + void OnPaint(wxPaintEvent &event) + { + wxPaintDC dc(this); + wxCoord w, h; + + dc.GetSize(&w, &h); + + dc.SetBackground(*wxBLACK_BRUSH); + dc.Clear(); + + { + wxNativePixelData data(buffer); + wxNativePixelData::Iterator p(data); + + for (int x = 0; x < 16; x++) + { + p.Red() = svga->vgapal[svga->egapal[x]].r * 4; + p.Green() = svga->vgapal[svga->egapal[x]].g * 4; + p.Blue() = svga->vgapal[svga->egapal[x]].b * 4; + p++; + } + } + + wxMemoryDC mdc(buffer); + + dc.StretchBlit(0, 0, w, h, &mdc, 0, 0, 16, 1); + } + + void OnClose(wxCloseEvent &event) + { + viewer_remove(this); + event.Skip(); + } + +public: + ViewerPalette16(wxWindow *parent, wxString title, wxSize size, void *p) + : Viewer(parent, title, size, p), + svga((svga_t *)p), + buffer(16, 1, 24) + { + Bind(wxEVT_CLOSE_WINDOW, &ViewerPalette16::OnClose, this); + Bind(wxEVT_PAINT, &ViewerPalette16::OnPaint, this); + } + + virtual ~ViewerPalette16() + { + } +}; + +static void *viewer_palette_16_open(void *parent, void *p, const char *title) +{ + wxFrame *w = new ViewerPalette16((wxWindow *)parent, title, wxSize(256, 20), p); + + w->Show(true); + + return w; +} + +viewer_t viewer_palette_16 = +{ + .open = viewer_palette_16_open +}; diff --git a/src/wx-ui/viewers/viewer_voodoo.cc b/src/wx-ui/viewers/viewer_voodoo.cc new file mode 100644 index 00000000..b2a187a0 --- /dev/null +++ b/src/wx-ui/viewers/viewer_voodoo.cc @@ -0,0 +1,1088 @@ +#include +#ifndef WX_PRECOMP +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "viewer.h" +#include "viewer_voodoo.h" +extern "C" +{ +#include "ibm.h" +#include "mem.h" +#include "thread.h" +#include "video.h" +#include "vid_svga.h" +#include "vid_voodoo_common.h" +#include "vid_voodoo_regs.h" +#include "vid_voodoo_texture.h" +} + +static std::mutex voodoo_viewer_refcount_mutex; + +static const char *texture_format_names[16] = { + "8-bit RGB (3-3-2)", + "8-bit YIQ (4-4-2)", + "8-bit Alpha", + "8-bit Intensity", + "8-bit Alpha, Intensity (4-4)", + "8-bit Palette (RGB)", + "8-bit Palette (RGBA)", + "Reserved", + "16-bit ARGB (8-3-3-2)", + "16-bit AYIQ (8-4-2-2)", + "16-bit RGB (5-6-5)", + "16-bit ARGB (1-5-5-5)", + "16-bit ARGB (4-4-4-4)", + "16-bit Alpha, Intensity (8-8)", + "16-bit Alpha, Palette (8-8)", + "Reserved" +}; + +class ViewerVoodoo; + +enum VoodooDisplayMode +{ + DM_FRAMEBUFFER, + DM_FRAMEBUFFER_WIREFRAME, + DM_DEPTHBUFFER, + DM_DEPTHBUFFER_WIREFRAME, + DM_WIREFRAME +}; + +enum VoodooCommand +{ + CMD_TRIANGLE, + CMD_START_STRIP, /*Covers fans as well as strips - some games will switch between the two in the middle of a run*/ + CMD_END_STRIP +}; + +typedef struct VoodooTriangle +{ + VoodooCommand cmd; + int id; + int strip_id; + float x[3], y[3], z[3], w[3]; + float r[3], g[3], b[3], a[3]; + float s0[3], t0[3], w0[3]; + float s1[3], t1[3], w1[3]; + int texture[2]; + uint32_t textureMode[2]; + uint32_t chromaKey; + uint32_t color0, color1; + rgb_t fogColor; + uint32_t zaColor; + uint32_t fbzMode; + uint32_t fbzColorPath; + uint32_t alphaMode; + uint32_t fogMode; +} VoodooTriangle; + +class ViewerVoodooCanvas: public wxWindow +{ +private: + class ViewerVoodoo *voodoo_parent; + voodoo_t *voodoo; + wxImage buffer; + wxImage depth_buffer; + wxSize window_size; + std::mutex buffer_mutex; + int width, height; + bool is_paused; + std::list *triangle_list_display; + int selected_tri_id; + int selected_strip_id; + int selected_texture_id; + VoodooDisplayMode display_mode; + + void OnPaint(wxPaintEvent &event) + { + std::lock_guard guard(buffer_mutex); + + if (!width || !height) + return; + + wxPaintDC dc(this); + wxCoord w, h; + + dc.GetSize(&w, &h); + + dc.SetBackground(*wxWHITE_BRUSH); + dc.Clear(); + + if (!is_paused) { + wxCoord text_w, text_h; + dc.GetTextExtent("Pause to update", &text_w, &text_h); + dc.DrawText("Pause to update", (w / 2) - (text_w / 2), (h / 2) - (text_h / 2)); + } else { + wxBitmap bitmap(width, height); + wxMemoryDC mdc(bitmap); + + if (display_mode == DM_FRAMEBUFFER || display_mode == DM_FRAMEBUFFER_WIREFRAME) { + wxBitmap framebuffer_bitmap(buffer); + wxMemoryDC framebuffer_mdc(framebuffer_bitmap); + + mdc.Blit(0, 0, width, height, &framebuffer_mdc, 0, 0); + } else if (display_mode == DM_DEPTHBUFFER || display_mode == DM_DEPTHBUFFER_WIREFRAME) { + wxBitmap depth_bitmap(depth_buffer); + wxMemoryDC depth_mdc(depth_bitmap); + + mdc.Blit(0, 0, width, height, &depth_mdc, 0, 0); + } else { + mdc.SetBackground(*wxBLACK_BRUSH); + mdc.Clear(); + } + + mdc.SetPen(*wxWHITE_PEN); + mdc.SetBrush(*wxRED_BRUSH); + + for (std::list::iterator it = triangle_list_display->begin(); it != triangle_list_display->end(); it++) { + VoodooTriangle *tri = &*it; + + if (tri->id == selected_tri_id || tri->strip_id == selected_strip_id || + ((tri->fbzColorPath & (1 << 27)) && (tri->texture[0] == selected_texture_id || (voodoo->dual_tmus && tri->texture[1] == selected_texture_id)))) { + wxPoint points[3] = { + wxPoint(tri->x[0], tri->y[0]), + wxPoint(tri->x[1], tri->y[1]), + wxPoint(tri->x[2], tri->y[2]), + }; + mdc.DrawPolygon(3, points); + } else if (display_mode == DM_WIREFRAME || display_mode == DM_DEPTHBUFFER_WIREFRAME || display_mode == DM_FRAMEBUFFER_WIREFRAME) { + mdc.DrawLine(tri->x[0], tri->y[0], tri->x[1], tri->y[1]); + mdc.DrawLine(tri->x[1], tri->y[1], tri->x[2], tri->y[2]); + mdc.DrawLine(tri->x[2], tri->y[2], tri->x[0], tri->y[0]); + } + } + + dc.Blit(0, 0, width, height, &mdc, 0, 0); + } + } + + void OnSize(wxSizeEvent &event) + { + window_size = event.GetSize(); + } + +public: + ViewerVoodooCanvas(wxWindow *parent, const wxString& title, const wxPoint& pos, const wxSize& size, voodoo_t *voodoo, class ViewerVoodoo *voodoo_parent, std::list *triangle_list_display) + : wxWindow(parent, wxID_ANY, pos, size, wxDEFAULT_FRAME_STYLE | wxVSCROLL | wxHSCROLL | wxALWAYS_SHOW_SB), + voodoo(voodoo), + buffer(4096, 4096), + depth_buffer(4096, 4096), + window_size(wxSize(1, 1)), + is_paused(false), + triangle_list_display(triangle_list_display), + selected_tri_id(-1), + selected_strip_id(-2), + selected_texture_id(-1), + display_mode(DM_FRAMEBUFFER_WIREFRAME) + { + Bind(wxEVT_PAINT, &ViewerVoodooCanvas::OnPaint, this); + Bind(wxEVT_SIZE, &ViewerVoodooCanvas::OnSize, this); + } + + void SwapBuffer() + { + { + std::lock_guard guard(buffer_mutex); + + width = voodoo->h_disp; + height = voodoo->v_disp; + + unsigned char *buffer_data = buffer.GetData(); + for (int y = 0; y < voodoo->v_disp; y++) { + unsigned char *data = buffer_data + y*3*buffer.GetWidth(); + + if (voodoo->params.col_tiled) { + uint16_t *src = (uint16_t *)&voodoo->fb_mem[voodoo->params.draw_offset + (y >> 5) * voodoo->params.row_width + + (y & 31) * 128]; + + for (int x = 0; x < voodoo->h_disp; x++) { + int x_tiled = (x & 63) | ((x >> 6) * 128 * 32 / 2); + uint32_t val = video_16to32[src[x_tiled]]; + + *data++ = (val >> 16) & 0xff; + *data++ = (val >> 8) & 0xff; + *data++ = val & 0xff; + } + } else { + uint16_t *src = (uint16_t *)&voodoo->fb_mem[voodoo->params.front_offset + y * voodoo->row_width]; + + for (int x = 0; x < voodoo->h_disp; x++) { + uint32_t val = video_16to32[*src++]; + + *data++ = (val >> 16) & 0xff; + *data++ = (val >> 8) & 0xff; + *data++ = val & 0xff; + } + } + } + + buffer_data = depth_buffer.GetData(); + for (int y = 0; y < voodoo->v_disp; y++) { + unsigned char *data = buffer_data + y*3*depth_buffer.GetWidth(); + + if (voodoo->params.aux_tiled) { + uint16_t *src = (uint16_t *)&voodoo->fb_mem[voodoo->params.aux_offset + (y >> 5) * voodoo->params.row_width + + (y & 31) * 128]; + + for (int x = 0; x < voodoo->h_disp; x++) { + int x_tiled = (x & 63) | ((x >> 6) * 128 * 32 / 2); + uint16_t val = src[x_tiled]; + + *data++ = (val >> 8) & 0xff; + *data++ = (val >> 8) & 0xff; + *data++ = (val >> 8) & 0xff; + } + } else { + uint16_t *src = (uint16_t *)&voodoo->fb_mem[voodoo->params.aux_offset + y * voodoo->row_width]; + + for (int x = 0; x < voodoo->h_disp; x++) { + uint16_t val = *src++; + + *data++ = (val >> 8) & 0xff; + *data++ = (val >> 8) & 0xff; + *data++ = (val >> 8) & 0xff; + } + } + } + } + + Refresh(); + } + + void set_paused(bool new_is_paused) + { + is_paused = new_is_paused; + if (!is_paused) { + selected_tri_id = -1; + selected_strip_id = -2; + selected_texture_id = -1; + } + SetMinSize(wxSize(width, height)); + Refresh(); + } + + void set_selected_triangle(int id) + { + selected_tri_id = id; + selected_strip_id = -2; + selected_texture_id = -1; + Refresh(); + } + + void set_selected_strip(int id) + { + selected_tri_id = -1; + selected_strip_id = id; + selected_texture_id = -1; + Refresh(); + } + + void set_selected_texture(int id) + { + selected_tri_id = -1; + selected_strip_id = -2; + selected_texture_id = id; + Refresh(); + } + + void set_display_mode(VoodooDisplayMode dm) + { + display_mode = dm; + Refresh(); + } +}; + +#define TEXTURE_DATA_SIZE ((256 * 256 + 256 * 256 + 128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8 + 4 * 4 + 2 * 2) * 4) + +class ViewerVoodoo; + +class ViewerVoodooTextureBitmap: public wxStaticBitmap +{ +private: + ViewerVoodoo *viewer; + int texture_id; + + void OnLeftDown(wxMouseEvent &event); + +public: + ViewerVoodooTextureBitmap(wxWindow *parent, const wxBitmap &bmp, const wxSize& size, ViewerVoodoo *viewer, int id) + : wxStaticBitmap(parent, wxID_ANY, bmp, wxDefaultPosition, size), + viewer(viewer), + texture_id(id) + { + Bind(wxEVT_LEFT_DOWN, &ViewerVoodooTextureBitmap::OnLeftDown, this); + } +}; + +class ViewerVoodooTexture: public wxBoxSizer +{ +public: + ViewerVoodooTexture(wxWindow *parent, const wxString &label, const wxBitmap &bmp, ViewerVoodoo *viewer, int id) : + wxBoxSizer(wxVERTICAL) + { + ViewerVoodooTextureBitmap *sbmp = new ViewerVoodooTextureBitmap(parent, bmp, wxSize(128, 128), viewer, id); + wxStaticText *text = new wxStaticText(parent, wxID_ANY, label); + Add(sbmp); + Add(text); + } +}; + +typedef struct viewer_texture_t +{ + texture_t t; + + int w[10]; + int h[10]; + uint32_t offset[10]; + + uint32_t textureMode; + int tmu; +} viewer_texture_t; + +class ViewerVoodoo: public Viewer +{ +private: + voodoo_t *voodoo; + ViewerVoodooCanvas *canvas; + wxComboBox *display_box; + wxBoxSizer *display_sz; + //wxPanel *display_panel; + + std::vector textures; + std::vector texture_list_display; + std::vector texture_list_active; + std::vector texture_data_display; + std::vector texture_data_active; + bool is_paused; + std::list triangle_list_display; + std::list triangle_list_active; + int triangle_next_id; + int strip_next_id; + int current_strip_id; + int current_texture[2]; + + wxTextCtrl *text_ctrl; + wxDataViewTreeCtrl *dv_ctrl; + wxTextCtrl *texture_text_ctrl; + wxStaticBitmap *texture_bitmap_ctrl; + wxGridSizer *texture_sz; + wxScrolledWindow *texture_panel; + + class TriangleItemData: public wxTreeItemData + { + private: + VoodooTriangle *tri; + + public: + TriangleItemData(VoodooTriangle *tri) : tri(tri) + { + } + + VoodooTriangle *GetTri() + { + return tri; + } + }; + + void OnClose(wxCloseEvent &event) + { + viewer_remove(this); + event.Skip(); + } + + void OnComboBox(wxCommandEvent &event) + { + switch (event.GetSelection()) { + case 0: + canvas->set_display_mode(DM_FRAMEBUFFER); + break; + case 1: + canvas->set_display_mode(DM_FRAMEBUFFER_WIREFRAME); + break; + case 2: + canvas->set_display_mode(DM_DEPTHBUFFER); + break; + case 3: + canvas->set_display_mode(DM_DEPTHBUFFER_WIREFRAME); + break; + case 4: + canvas->set_display_mode(DM_WIREFRAME); + break; + } + } + + void UpdateDVCtrl() + { + dv_ctrl->DeleteAllItems(); + + wxDataViewItem root = wxDataViewItem(0); + + for (std::list::iterator it = triangle_list_display.begin(); it != triangle_list_display.end(); it++) { + VoodooTriangle tri = *it; + wxString s; + + if (tri.cmd == CMD_TRIANGLE) { + s.Printf("Triangle %u", tri.id); + wxDataViewItem newitem = dv_ctrl->AppendItem(root, s); + dv_ctrl->SetItemData(newitem, new TriangleItemData(&*it)); + } else if (tri.cmd == CMD_START_STRIP) { + s.Printf("Strip %u", tri.id); + root = dv_ctrl->AppendContainer(wxDataViewItem(0), s); + dv_ctrl->SetItemData(root, new TriangleItemData(&*it)); + } else if (tri.cmd == CMD_END_STRIP) { + root = wxDataViewItem(0); + } + } + } + + void UpdateTextures() + { + int id = 0; + + for (std::vector::iterator it = texture_list_display.begin(); it != texture_list_display.end(); it++) { + viewer_texture_t *tex = &*it; + wxString label; + + label.Printf("Texture %u", id); + + int lod_min = (tex->t.tLOD >> 2) & 15; + lod_min = MIN(lod_min, 8); + + wxImage image(tex->w[lod_min], tex->h[lod_min]); + unsigned char *buffer_data = image.GetData(); + + int addr = texture_offset[lod_min]; + + for (int y = 0; y < tex->h[lod_min]; y++) { + unsigned char *data = buffer_data + y*3*image.GetWidth(); + + for (int x = 0; x < tex->w[lod_min]; x++) { + uint32_t val = texture_data_display[id][addr + x]; + + *data++ = (val >> 16) & 0xff; + *data++ = (val >> 8) & 0xff; + *data++ = val & 0xff; + } + + addr += (1 << (8 - lod_min)); + } + + if (tex->w[lod_min] > tex->h[lod_min]) { + int h = (tex->h[lod_min] * 128) / tex->w[lod_min]; + image.Rescale(128, h, wxIMAGE_QUALITY_HIGH); + } else { + int w = (tex->w[lod_min] * 128) / tex->h[lod_min]; + image.Rescale(w, 128, wxIMAGE_QUALITY_HIGH); + } + + ViewerVoodooTexture *vvt = new ViewerVoodooTexture(texture_panel, label, wxBitmap(image), this, id); + + texture_sz->Add(vvt); + textures.push_back(vvt); + + id++; + } + + texture_sz->Fit(texture_panel); + texture_panel->Layout(); + + wxSize size = texture_panel->GetBestVirtualSize(); + texture_panel->SetVirtualSize( size ); + + texture_panel->SetScrollRate(15, 15); + texture_panel->Refresh(); + } + + void ClearTextures() + { + texture_sz->Clear(true); + texture_sz->Fit(texture_panel); + + texture_panel->Layout(); + texture_panel->SetScrollRate(15, 15); + texture_panel->Refresh(); + + textures.clear(); + } + + void OnSelectionChanged(wxDataViewEvent &event) + { + wxDataViewItem item = event.GetItem(); + TriangleItemData *tid = static_cast(dv_ctrl->GetItemData(item)); + + canvas->set_selected_texture(-1); + if (tid) { + VoodooTriangle *tri = tid->GetTri(); + + if (tri->cmd == CMD_TRIANGLE) { + canvas->set_selected_triangle(tri->id); + + wxString s, s2; + + if (voodoo->dual_tmus) { + s.Printf("Vertex 0:\n\tX=%f Y=%f Z=%f W=%f\n\t\tRed=%f Green=%f Blue=%f Alpha=%f S=[%f,%f] T=[%f,%f]\n", + tri->x[0], tri->y[0], tri->z[0], tri->w[0], tri->r[0], tri->g[0], tri->b[0], tri->a[0], tri->s0[0], tri->s1[0], tri->t0[0], tri->t1[0]); + + s2.Printf("Vertex 1:\n\tX=%f Y=%f Z=%f W=%f\n\t\tRed=%f Green=%f Blue=%f Alpha=%f S=[%f,%f] T=[%f,%f]\n", + tri->x[1], tri->y[1], tri->z[1], tri->w[1], tri->r[1], tri->g[1], tri->b[1], tri->a[1], tri->s0[1], tri->s1[1], tri->t0[1], tri->t1[1]); + s.Append(s2); + + s2.Printf("Vertex 2:\n\tX=%f Y=%f Z=%f W=%f\n\t\tRed=%f Green=%f Blue=%f Alpha=%f S=[%f,%f] T=[%f,%f]\n", + tri->x[2], tri->y[2], tri->z[2], tri->w[2], tri->r[2], tri->g[2], tri->b[2], tri->a[2], tri->s0[2], tri->s1[2], tri->t0[2], tri->t1[2]); + s.Append(s2); + } else { + s.Printf("Vertex 0:\n\tX=%f Y=%f Z=%f W=%f\n\t\tRed=%f Green=%f Blue=%f Alpha=%f S=%f T=%f\n", + tri->x[0], tri->y[0], tri->z[0], tri->w[0], tri->r[0], tri->g[0], tri->b[0], tri->a[0], tri->s0[0], tri->t0[0]); + + s2.Printf("Vertex 1:\n\tX=%f Y=%f Z=%f W=%f\n\t\tRed=%f Green=%f Blue=%f Alpha=%f S=%f T=%f\n", + tri->x[1], tri->y[1], tri->z[1], tri->w[1], tri->r[1], tri->g[1], tri->b[1], tri->a[1], tri->s0[1], tri->t0[1]); + s.Append(s2); + + s2.Printf("Vertex 2:\n\tX=%f Y=%f Z=%f W=%f\n\t\tRed=%f Green=%f Blue=%f Alpha=%f S=%f T=%f\n", + tri->x[2], tri->y[2], tri->z[2], tri->w[2], tri->r[2], tri->g[2], tri->b[2], tri->a[2], tri->s0[2], tri->t0[2]); + s.Append(s2); + } + + if (!(tri->fbzColorPath & (1 << 27))) { + s2.Printf("Untextured\n"); + s.Append(s2); + } else if (voodoo->dual_tmus) { + s2.Printf("TMU0: Texture ID=%u textureMode=%08x\n", tri->texture[0], tri->textureMode[0]); + s.Append(s2); + s2.Printf("TMU1: Texture ID=%u textureMode=%08x\n", tri->texture[1], tri->textureMode[1]); + s.Append(s2); + } else { + s2.Printf("Texture ID=%u textureMode=%08x\n", tri->texture[0], tri->textureMode[0]); + s.Append(s2); + } + + s2.Printf("Chroma-key: Red=%u Green=%u Blue=%u\n", (tri->chromaKey >> 16) & 0xff, (tri->chromaKey >> 8) & 0xff, tri->chromaKey & 0xff); + s.Append(s2); + + s2.Printf("Color0: Red=%u Green=%u Blue=%u\n", (tri->color0 >> 16) & 0xff, (tri->color0 >> 8) & 0xff, tri->color0 & 0xff); + s.Append(s2); + + s2.Printf("Color1: Red=%u Green=%u Blue=%u\n", (tri->color1 >> 16) & 0xff, (tri->color1 >> 8) & 0xff, tri->color1 & 0xff); + s.Append(s2); + + s2.Printf("fogColor: Red=%u Green=%u Blue=%u\n", tri->fogColor.r, tri->fogColor.g, tri->fogColor.b); + s.Append(s2); + + s2.Printf("zaColor: Depth=%u Alpha=%u\n", tri->zaColor & 0xffff, (tri->zaColor >> 24) & 0xff); + s.Append(s2); + + s2.Printf("fbzMode=%08x fbzColorPath=%08x alphaMode=%08x fogMode=%08x", tri->fbzMode, tri->fbzColorPath, tri->alphaMode, tri->fogMode); + s.Append(s2); + + text_ctrl->SetValue(s); + } else if (tri->cmd == CMD_START_STRIP) { + canvas->set_selected_strip(tri->id); + text_ctrl->SetValue(wxEmptyString); + } else { + text_ctrl->SetValue(wxEmptyString); + } + } else { + text_ctrl->SetValue(wxEmptyString); + } + } + +public: + ViewerVoodoo(wxWindow *parent, wxString title, wxSize size, void *p) + : Viewer(parent, title, size, p), + voodoo((voodoo_t *)p), + is_paused(false), + triangle_next_id(0), + strip_next_id(0), + current_strip_id(-2) + { + { + std::lock_guard guard(voodoo_viewer_refcount_mutex); + + voodoo->viewer_active++; + } + + wxSplitterWindow *splitter = new wxSplitterWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_3D | wxSP_LIVE_UPDATE); + splitter->SetSashGravity(0.5); + splitter->SetMinimumPaneSize(1); + + wxPanel *display_panel = new wxPanel(splitter, wxID_ANY); + display_sz = new wxBoxSizer(wxVERTICAL); + display_panel->SetSizer(display_sz); + + canvas = new ViewerVoodooCanvas(display_panel, "canvas", wxDefaultPosition, wxSize(512, 384), (voodoo_t *)p, this, &triangle_list_display); + display_sz->Add(canvas); + display_box = new wxComboBox(display_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + display_box->Append("Frame buffer"); + display_box->Append("Frame buffer + wireframe"); + display_box->Append("Depth buffer"); + display_box->Append("Depth buffer + wireframe"); + display_box->Append("Wireframe"); + display_box->SetValue("Frame buffer + wireframe"); + display_box->Disable(); + display_sz->Add(display_box); + + wxNotebook *notebook = new wxNotebook(static_cast(splitter), wxID_ANY); + + wxSplitterWindow *splitter2 = new wxSplitterWindow(notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_3D | wxSP_LIVE_UPDATE); + splitter2->SetSashGravity(0.75); + splitter2->SetMinimumPaneSize(1); + + dv_ctrl = new wxDataViewTreeCtrl(splitter2, wxID_ANY, wxDefaultPosition, wxDefaultSize); + + text_ctrl = new wxTextCtrl(splitter2, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY | wxTE_DONTWRAP); + + splitter2->SplitHorizontally(dv_ctrl, text_ctrl); + + notebook->AddPage(splitter2, "Commands"); + + + wxSplitterWindow *texture_splitter = new wxSplitterWindow(notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_3D | wxSP_LIVE_UPDATE); + texture_splitter->SetSashGravity(0.75); + texture_splitter->SetMinimumPaneSize(1); + + wxSplitterWindow *texture_splitter2 = new wxSplitterWindow(texture_splitter, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_3D | wxSP_LIVE_UPDATE); + texture_splitter2->SetSashGravity(0.75); + texture_splitter2->SetMinimumPaneSize(1); + + texture_panel = new wxScrolledWindow(texture_splitter2, wxID_ANY); + texture_panel->SetScrollRate(15, 15); + texture_sz = new wxGridSizer(4, 16, 16); + texture_panel->SetSizer(texture_sz); + + texture_text_ctrl = new wxTextCtrl(texture_splitter2, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY | wxTE_DONTWRAP); + + texture_splitter2->SplitHorizontally(texture_panel, texture_text_ctrl); + + wxBitmap *texture_bitmap = new wxBitmap(256, 256+128+64+32+16+8+4+2+1); + texture_bitmap_ctrl = new wxStaticBitmap(static_cast(texture_splitter), wxID_ANY, *texture_bitmap); + + texture_splitter->SplitVertically(texture_splitter2, texture_bitmap_ctrl); + + notebook->AddPage(texture_splitter, "Textures"); + + splitter->SplitVertically(display_panel, notebook); + + + Bind(wxEVT_CLOSE_WINDOW, &ViewerVoodoo::OnClose, this); + Bind(wxEVT_COMBOBOX, &ViewerVoodoo::OnComboBox, this); + Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, &ViewerVoodoo::OnSelectionChanged, this); + } + + virtual ~ViewerVoodoo() + { + { + std::lock_guard guard(voodoo_viewer_refcount_mutex); + + voodoo->viewer_active--; + } + + delete canvas; + } + + void SwapBuffer() + { + canvas->SwapBuffer(); + triangle_list_display = triangle_list_active; + triangle_list_active.clear(); + triangle_next_id = 0; + strip_next_id = 0; + current_strip_id = -1; + + texture_list_display = texture_list_active; + texture_list_active.clear(); + texture_data_display = texture_data_active; + } + + void QueueTriangle() + { + float dxB = (float)(voodoo->params.vertexBx - voodoo->params.vertexAx) / 16.0f; + float dyB = (float)(voodoo->params.vertexBy - voodoo->params.vertexAy) / 16.0f; + float dxC = (float)(voodoo->params.vertexCx - voodoo->params.vertexCx) / 16.0f; + float dyC = (float)(voodoo->params.vertexCy - voodoo->params.vertexCy) / 16.0f; + + VoodooTriangle tri = { + .cmd = CMD_TRIANGLE, + .id = triangle_next_id, + .strip_id = current_strip_id, + .x = { + (float)voodoo->params.vertexAx / 16.0f, + (float)voodoo->params.vertexBx / 16.0f, + (float)voodoo->params.vertexCx / 16.0f + }, + .y = { + (float)voodoo->params.vertexAy / 16.0f, + (float)voodoo->params.vertexBy / 16.0f, + (float)voodoo->params.vertexCy / 16.0f + }, + .z = { + (float)voodoo->params.startZ / 4096.0f, + ((float)voodoo->params.startZ + (float)voodoo->params.dZdX * dxB + (float)voodoo->params.dZdY * dyB) / 4096.0f, + ((float)voodoo->params.startZ + (float)voodoo->params.dZdX * dxC + (float)voodoo->params.dZdY * dyC) / 4096.0f, + }, + .w = { + (float)voodoo->params.startW / 4294967296.0f, + ((float)voodoo->params.startW + (float)voodoo->params.dWdX * dxB + (float)voodoo->params.dWdY * dyB) / 4294967296.0f, + ((float)voodoo->params.startW + (float)voodoo->params.dWdX * dxC + (float)voodoo->params.dWdY * dyC) / 4294967296.0f, + }, + .r = { + (float)voodoo->params.startR / 4096.0f, + ((float)voodoo->params.startR + (float)voodoo->params.dRdX * dxB + (float)voodoo->params.dRdY * dyB) / 4096.0f, + ((float)voodoo->params.startR + (float)voodoo->params.dRdX * dxC + (float)voodoo->params.dRdY * dyC) / 4096.0f, + }, + .g = { + (float)voodoo->params.startG / 4096.0f, + ((float)voodoo->params.startG + (float)voodoo->params.dGdX * dxB + (float)voodoo->params.dGdY * dyB) / 4096.0f, + ((float)voodoo->params.startG + (float)voodoo->params.dGdX * dxC + (float)voodoo->params.dGdY * dyC) / 4096.0f, + }, + .b = { + (float)voodoo->params.startB / 4096.0f, + ((float)voodoo->params.startB + (float)voodoo->params.dBdX * dxB + (float)voodoo->params.dBdY * dyB) / 4096.0f, + ((float)voodoo->params.startB + (float)voodoo->params.dBdX * dxC + (float)voodoo->params.dBdY * dyC) / 4096.0f, + }, + .a = { + (float)voodoo->params.startA / 4096.0f, + ((float)voodoo->params.startA + (float)voodoo->params.dAdX * dxB + (float)voodoo->params.dAdY * dyB) / 4096.0f, + ((float)voodoo->params.startA + (float)voodoo->params.dAdX * dxC + (float)voodoo->params.dAdY * dyC) / 4096.0f, + }, + .s0 = { + (float)voodoo->params.tmu[0].startS / 4294967296.0f, + ((float)voodoo->params.tmu[0].startS + (float)voodoo->params.tmu[0].dSdX * dxB + (float)voodoo->params.tmu[0].dSdY * dyB) / 4294967296.0f, + ((float)voodoo->params.tmu[0].startS + (float)voodoo->params.tmu[0].dSdX * dxC + (float)voodoo->params.tmu[0].dSdY * dyC) / 4294967296.0f, + }, + .t0 = { + (float)voodoo->params.tmu[0].startT / 4294967296.0f, + ((float)voodoo->params.tmu[0].startT + (float)voodoo->params.tmu[0].dTdX * dxB + (float)voodoo->params.tmu[0].dTdY * dyB) / 4294967296.0f, + ((float)voodoo->params.tmu[0].startT + (float)voodoo->params.tmu[0].dTdX * dxC + (float)voodoo->params.tmu[0].dTdY * dyC) / 4294967296.0f, + }, + .w0 = { + (float)voodoo->params.tmu[0].startW / 4294967296.0f, + ((float)voodoo->params.tmu[0].startW + (float)voodoo->params.tmu[0].dWdX * dxB + (float)voodoo->params.tmu[0].dWdY * dyB) / 4294967296.0f, + ((float)voodoo->params.tmu[0].startW + (float)voodoo->params.tmu[0].dWdX * dxC + (float)voodoo->params.tmu[0].dWdY * dyC) / 4294967296.0f, + }, + .s1 = { + (float)voodoo->params.tmu[1].startS / 4294967296.0f, + ((float)voodoo->params.tmu[1].startS + (float)voodoo->params.tmu[1].dSdX * dxB + (float)voodoo->params.tmu[1].dSdY * dyB) / 4294967296.0f, + ((float)voodoo->params.tmu[1].startS + (float)voodoo->params.tmu[1].dSdX * dxC + (float)voodoo->params.tmu[1].dSdY * dyC) / 4294967296.0f, + }, + .t1 = { + (float)voodoo->params.tmu[1].startT / 4294967296.0f, + ((float)voodoo->params.tmu[1].startT + (float)voodoo->params.tmu[1].dTdX * dxB + (float)voodoo->params.tmu[1].dTdY * dyB) / 4294967296.0f, + ((float)voodoo->params.tmu[1].startT + (float)voodoo->params.tmu[1].dTdX * dxC + (float)voodoo->params.tmu[1].dTdY * dyC) / 4294967296.0f, + }, + .w1 = { + (float)voodoo->params.tmu[1].startW / 4294967296.0f, + ((float)voodoo->params.tmu[1].startW + (float)voodoo->params.tmu[1].dWdX * dxB + (float)voodoo->params.tmu[1].dWdY * dyB) / 4294967296.0f, + ((float)voodoo->params.tmu[1].startW + (float)voodoo->params.tmu[1].dWdX * dxC + (float)voodoo->params.tmu[1].dWdY * dyC) / 4294967296.0f, + }, + .texture = { + current_texture[0], + current_texture[1] + }, + .textureMode = { + voodoo->params.textureMode[0], + voodoo->params.textureMode[1] + }, + .chromaKey = voodoo->params.chromaKey, + .color0 = voodoo->params.color0, + .color1 = voodoo->params.color1, + .fogColor = voodoo->params.fogColor, + .zaColor = voodoo->params.zaColor, + .fbzMode = voodoo->params.fbzMode, + .fbzColorPath = voodoo->params.fbzColorPath, + .alphaMode = voodoo->params.alphaMode, + .fogMode = voodoo->params.fogMode + }; + + if (voodoo->params.fbzMode & (1 << 17)) { + int y_origin = (voodoo->type >= VOODOO_BANSHEE) ? voodoo->y_origin_swap : (voodoo->v_disp - 1); + + for (int i = 0; i < 3; i++) + tri.y[i] = y_origin - tri.y[i]; + } + + if (voodoo->params.textureMode[0] & 1) { + /*Apply perspective correction*/ + for (int i = 0; i < 3; i++) { + float oow = 1.0 / tri.w0[i]; + tri.s0[i] *= oow; + tri.t0[i] *= oow; + } + } + if (voodoo->params.textureMode[1] & 1) { + /*Apply perspective correction*/ + for (int i = 0; i < 3; i++) { + float oow = 1.0 / tri.w1[i]; + tri.s1[i] *= oow; + tri.t1[i] *= oow; + } + } + + triangle_next_id++; + + triangle_list_active.push_back(tri); + } + + void BeginStrip() + { + VoodooTriangle tri = { + .cmd = CMD_START_STRIP, + .id = strip_next_id + }; + + current_strip_id = strip_next_id; + strip_next_id++; + + triangle_list_active.push_back(tri); + } + + void EndStrip() + { + VoodooTriangle tri = { + .cmd = CMD_END_STRIP + }; + + current_strip_id = -1; + + triangle_list_active.push_back(tri); + } + + void UseTexture(int tmu) + { + voodoo_params_t *params = &voodoo->params; + uint32_t addr = 0; + uint32_t palette_checksum; + + if (params->tformat[tmu] == TEX_PAL8 || params->tformat[tmu] == TEX_APAL8 || params->tformat[tmu] == TEX_APAL88) { + if (voodoo->palette_dirty[tmu]) { + palette_checksum = 0; + + for (int i = 0; i < 256; i++) + palette_checksum ^= voodoo->palette[tmu][i].u; + + voodoo->palette_checksum[tmu] = palette_checksum; + voodoo->palette_dirty[tmu] = 0; + } else + palette_checksum = voodoo->palette_checksum[tmu]; + } else + palette_checksum = 0; + + if ((voodoo->params.tLOD[tmu] & LOD_SPLIT) && (voodoo->params.tLOD[tmu] & LOD_ODD) && + (voodoo->params.tLOD[tmu] & LOD_TMULTIBASEADDR)) + addr = params->texBaseAddr1[tmu]; + else + addr = params->texBaseAddr[tmu]; + + int id = 0; + + for (std::vector::iterator it = texture_list_active.begin(); it != texture_list_active.end(); it++) { + viewer_texture_t *tex = &*it; + + if (tex->t.base == addr && + tex->t.tLOD == (params->tLOD[tmu] & 0xf00fff) && + tex->t.palette_checksum == palette_checksum) { + /*Found in cache*/ + current_texture[tmu] = id; + return; + } + id++; + } + + /*Add to cache*/ + int cache_entry = params->tex_entry[tmu]; + id = texture_list_active.size(); + viewer_texture_t tex; + + tex.t = voodoo->texture_cache[tmu][cache_entry]; + uint32_t base = params->tex_base[tmu][0]; + + for (int lod = 0; lod < 10; lod++) { + tex.w[lod] = params->tex_w_mask[tmu][lod] + 1; + tex.h[lod] = params->tex_h_mask[tmu][lod] + 1; + tex.offset[lod] = params->tex_base[tmu][lod] - base; + } + tex.textureMode = params->textureMode[tmu]; + tex.tmu = tmu; + + texture_list_active.push_back(tex); + + while (id >= texture_data_active.size()) + texture_data_active.push_back((uint32_t *)malloc(TEXTURE_DATA_SIZE)); + + memcpy(texture_data_active[id], voodoo->texture_cache[tmu][cache_entry].data, TEXTURE_DATA_SIZE); + current_texture[tmu] = id; + } + + void SelectTexture(int id) + { + viewer_texture_t *tex = &texture_list_display[id]; + wxString s, s2; + + s.Printf("Texture #%u", id); + + int lod_min = (tex->t.tLOD >> 2) & 15; + int lod_max = (tex->t.tLOD >> 8) & 15; + lod_min = MIN(lod_min, 8); + lod_max = MIN(lod_max, 8); + + s2.Printf("\n\tLOD: min=%u (%ux%u) max=%u (%ux%u)", + lod_min, tex->w[lod_min], tex->h[lod_min], + lod_max, tex->w[lod_max], tex->h[lod_max]); + s.Append(s2); + + s2.Printf("\n\tFormat: %s", texture_format_names[(tex->textureMode >> 8) & 0xf]); + s.Append(s2); + + s2.Printf("\n\tTMU #%u", tex->tmu); + s.Append(s2); + + texture_text_ctrl->SetValue(s); + + wxImage image(256, 256+128+64+32+16+8+4+2+1); + image.InitAlpha(); + + unsigned char *buffer_data = image.GetData(); + unsigned char *alpha_data = image.GetAlpha(); + int yy = 0; + + memset(alpha_data, 0, image.GetWidth() * image.GetHeight()); + + for (int lod = lod_min; lod <= lod_max; lod++) { + int addr = texture_offset[lod]; + + for (int y = 0; y < tex->h[lod]; y++) { + unsigned char *data = buffer_data + yy*3*image.GetWidth(); + unsigned char *alpha = alpha_data + yy*image.GetWidth(); + int x; + + for (x = 0; x < tex->w[lod]; x++) { + uint32_t val = texture_data_display[id][addr + x]; + + *data++ = (val >> 16) & 0xff; + *data++ = (val >> 8) & 0xff; + *data++ = val & 0xff; + *alpha++ = 0xff; + } + for (; x < 256; x++) { + *data++ = 0; + *data++ = 0; + *data++ = 0; + *alpha++ = 0; + } + + addr += (1 << (8 - lod)); + yy++; + } + } + + wxBitmap bitmap(image); + texture_bitmap_ctrl->SetBitmap(bitmap); + + canvas->set_selected_texture(id); + } + + void NotifyPause() + { + if (!is_paused) + { + is_paused = true; + canvas->set_paused(true); + + UpdateDVCtrl(); + UpdateTextures(); + display_box->Enable(); + display_sz->Layout(); + + Layout(); + Refresh(); + Update(); + } + } + + void NotifyResume() + { + if (is_paused) + { + is_paused = false; + canvas->set_paused(false); + + dv_ctrl->DeleteAllItems(); + text_ctrl->SetValue(wxEmptyString); + texture_text_ctrl->SetValue(wxEmptyString); + + wxImage image(256, 256+128+64+32+16+8+4+2+1); + wxBitmap bitmap(image); + texture_bitmap_ctrl->SetBitmap(bitmap); + + ClearTextures(); + + display_box->Disable(); + + Refresh(); + } + } +}; + +void ViewerVoodooTextureBitmap::OnLeftDown(wxMouseEvent &event) +{ + viewer->SelectTexture(texture_id); +} + +void voodoo_viewer_swap_buffer(void *v, void *param) +{ + ViewerVoodoo *viewer = static_cast(v); + + viewer->SwapBuffer(); +} + +void voodoo_viewer_queue_triangle(void *v, void *param) +{ + ViewerVoodoo *viewer = static_cast(v); + + viewer->QueueTriangle(); +} + +void voodoo_viewer_begin_strip(void *v, void *param) +{ + ViewerVoodoo *viewer = static_cast(v); + + viewer->BeginStrip(); +} + +void voodoo_viewer_end_strip(void *v, void *param) +{ + ViewerVoodoo *viewer = static_cast(v); + + viewer->EndStrip(); +} + +void voodoo_viewer_use_texture(void *v, void *param) +{ + ViewerVoodoo *viewer = static_cast(v); + + viewer->UseTexture((int)(uintptr_t)param); +} + +static void *viewer_voodoo_open(void *parent, void *p, const char *title) +{ + wxFrame *w = new ViewerVoodoo((wxWindow *)parent, title, wxSize(800, 600), p); + + w->Show(true); + + return w; +} + +viewer_t viewer_voodoo = +{ + .open = viewer_voodoo_open +}; diff --git a/src/wx-ui/viewers/viewer_vram.cc b/src/wx-ui/viewers/viewer_vram.cc new file mode 100644 index 00000000..38c2eff6 --- /dev/null +++ b/src/wx-ui/viewers/viewer_vram.cc @@ -0,0 +1,698 @@ +#include +#ifndef WX_PRECOMP +#include +#endif +#include +#include +#include +#include +#include "viewer.h" +extern "C" +{ +#include "ibm.h" +#include "mem.h" +#include "video.h" +#include "vid_svga.h" +} + +enum ColourDepth +{ + CD_1BPP, + CD_2BPP, + CD_4BPP, + CD_8BPP, + CD_1555, + CD_565, + CD_888, + CD_8888 +}; + +enum AddrMode +{ + AM_NORMAL, + AM_ODDEVEN, + AM_CHAIN4 +}; + +class ViewerVRAM; + +class ViewerVRAMCanvas: public wxWindow +{ +private: + class ViewerVRAM *vram_parent; + svga_t *svga; + wxImage buffer; + wxSize window_size; + bool size_changed; + uint32_t custom_start_addr; + uint32_t custom_pitch; + ColourDepth custom_colour_depth; + AddrMode custom_addr_mode; + bool use_custom_start_addr; + bool use_custom_pitch; + bool use_custom_colour_depth; + bool use_custom_addr_mode; + + int scroll_x_size, scroll_x_range; + int scroll_y_size, scroll_y_range; + + uint32_t current_start_addr; + uint32_t current_pitch; + int current_bpp; + + int scale_factor; + + void update_scroll(int new_x_size, int new_x_range, int new_y_size, int new_y_range) + { + scroll_x_size = new_x_size; + scroll_x_range = new_x_range; + scroll_y_size = new_y_size; + scroll_y_range = new_y_range; + + SetScrollbar(wxHORIZONTAL, GetScrollPos(wxHORIZONTAL), new_x_size, new_x_range); + SetScrollbar(wxVERTICAL, GetScrollPos(wxVERTICAL), new_y_size, new_y_range); + } + + void UpdateLabels(); + + void OnPaint(wxPaintEvent &event) + { + wxPaintDC dc(this); + wxCoord w, h, disp_w, disp_h; + + dc.GetSize(&disp_w, &disp_h); + w = disp_w / scale_factor; + h = disp_h / scale_factor; + + dc.SetBackground(*wxWHITE_BRUSH); + dc.Clear(); + + uint32_t addr = use_custom_start_addr ? custom_start_addr : svga->ma_latch * 4; + uint32_t mask = (svga->vram_mask << 2) | 3; + + ColourDepth depth = custom_colour_depth; + + if (!use_custom_colour_depth) { + switch (svga->video_bpp) { + case 2: + depth = CD_2BPP; + break; + case 4: + depth = CD_4BPP; + break; + case 8: + depth = CD_8BPP; + break; + case 15: + depth = CD_1555; + break; + case 16: + depth = CD_565; + break; + case 24: + depth = CD_888; + break; + case 32: + depth = CD_8888; + break; + default: + depth = CD_8BPP; + break; + } + } + + uint32_t offset = use_custom_pitch ? custom_pitch * 8 : svga->rowoffset * 8; + int plot_width = 0; + int line_width = 0; + + switch (depth) + { + case CD_1BPP: + plot_width = std::min((int)(w & ~3), (int)offset*8); + line_width = offset * 8; + break; + + case CD_2BPP: + plot_width = std::min((int)(w & ~3), (int)offset*4); + line_width = offset * 4; + break; + + case CD_4BPP: + plot_width = std::min((int)(w & ~3), (int)offset*2); + line_width = offset * 2; + break; + + case CD_8BPP: + plot_width = std::min((int)(w & ~3), (int)offset); + line_width = offset; + break; + + case CD_1555: + case CD_565: + plot_width = std::min((int)(w & ~3), (int)offset / 2); + line_width = offset / 2; + break; + + case CD_888: + plot_width = std::min((int)(w & ~3), (int)offset / 3); + line_width = offset / 3; + break; + + case CD_8888: + plot_width = std::min((int)(w & ~3), (int)offset / 4); + line_width = offset / 4; + break; + } + + AddrMode addr_mode = custom_addr_mode; + + if (!use_custom_addr_mode) + { + if (svga->fb_only) + addr_mode = AM_NORMAL; + else if (svga->chain4 && !svga->packed_chain4) + addr_mode = AM_CHAIN4; + else if (svga->chain2_write || svga->chain2_read) + addr_mode = AM_ODDEVEN; + else + addr_mode = AM_NORMAL; + } + + int vram_height = offset ? (svga->vram_max / offset) : 1; + + if (scroll_x_size != w || scroll_x_range != line_width || scroll_y_size != h || scroll_y_range != vram_height) + update_scroll(w, line_width, h, vram_height); + + int x_offset = GetScrollPos(wxHORIZONTAL); + int y_offset = GetScrollPos(wxVERTICAL); + unsigned char *buffer_data = buffer.GetData(); + + for (int y = 0; y < h; y++) + { + unsigned char *data = buffer_data + y*3*buffer.GetWidth(); + uint old_addr = addr; + + addr += y_offset * offset; + + switch (depth) + { + case CD_1BPP: + addr += (x_offset / 2) & ~3; + for (int x = 0; x < plot_width; x += 8) + { + uint32_t use_addr = addr; + + if (addr_mode == AM_CHAIN4) + use_addr = ((addr & 0xfffc) << 2) | ((addr & 0x30000) >> 14) | (addr & ~0x3ffff); + else if (addr_mode == AM_ODDEVEN) + use_addr = ((addr << 1) & 0x1fff8) | ((addr >> 15) & 0x4) | (addr & ~0x1ffff); + + for (int xx = 0; xx < 8; xx++) + { + uint8_t pixel = 0; + + if (svga->vram[use_addr & svga->vram_mask] & (0x80 >> xx)) + pixel |= 1; + + *data++ = svga->vgapal[svga->egapal[pixel]].r * 4; + *data++ = svga->vgapal[svga->egapal[pixel]].g * 4; + *data++ = svga->vgapal[svga->egapal[pixel]].b * 4; + } + + addr += 4; + } + break; + + case CD_2BPP: + for (int x = 0; x < plot_width; x+= 4) + { + uint32_t use_addr = addr; + + if (addr_mode == AM_CHAIN4) + use_addr = ((addr & 0xfffc) << 2) | ((addr & 0x30000) >> 14) | (addr & ~0x3ffff); + else if (addr_mode == AM_ODDEVEN) + use_addr = ((addr << 1) & 0x1fff8) | ((addr >> 15) & 0x4) | (addr & ~0x1ffff); + + uint16_t pix_data = *(uint16_t *)&svga->vram[use_addr & svga->vram_mask]; + + for (int xx = 0; xx < 8; xx++) + { + uint8_t pixel = pix_data >> 14; + + *data++ = svga->vgapal[pixel].r * 4; + *data++ = svga->vgapal[pixel].g * 4; + *data++ = svga->vgapal[pixel].b * 4; + + pix_data <<= 2; + } + + addr += 4; + } + break; + + case CD_4BPP: + addr += (x_offset / 2) & ~3; + for (int x = 0; x < plot_width; x += 8) + { + uint32_t use_addr = addr; + + if (addr_mode == AM_CHAIN4) + use_addr = ((addr & 0xfffc) << 2) | ((addr & 0x30000) >> 14) | (addr & ~0x3ffff); + else if (addr_mode == AM_ODDEVEN) + use_addr = ((addr << 1) & 0x1fff8) | ((addr >> 15) & 0x4) | (addr & ~0x1ffff); + + for (int xx = 0; xx < 8; xx++) + { + uint8_t pixel = 0; + + if (svga->vram[use_addr & svga->vram_mask] & (0x80 >> xx)) + pixel |= 1; + if (svga->vram[(use_addr + 1) & svga->vram_mask] & (0x80 >> xx)) + pixel |= 2; + if (svga->vram[(use_addr + 2) & svga->vram_mask] & (0x80 >> xx)) + pixel |= 4; + if (svga->vram[(use_addr + 3) & svga->vram_mask] & (0x80 >> xx)) + pixel |= 8; + + *data++ = svga->vgapal[svga->egapal[pixel]].r * 4; + *data++ = svga->vgapal[svga->egapal[pixel]].g * 4; + *data++ = svga->vgapal[svga->egapal[pixel]].b * 4; + } + + addr += 4; + } + break; + + case CD_8BPP: + addr += x_offset & ~3; + for (int x = 0; x < plot_width; x += 4) + { + uint32_t use_addr = addr; + + if (addr_mode == AM_CHAIN4) + use_addr = ((addr & 0xfffc) << 2) | ((addr & 0x30000) >> 14) | (addr & ~0x3ffff); + else if (addr_mode == AM_ODDEVEN) + use_addr = ((addr << 1) & 0x1fff8) | ((addr >> 15) & 0x4) | (addr & ~0x1ffff); + + for (int xx = 0; xx < 4; xx++) + { + uint8_t pixel = svga->vram[(use_addr + xx) & svga->vram_mask]; + + *data++ = svga->vgapal[pixel].r * 4; + *data++ = svga->vgapal[pixel].g * 4; + *data++ = svga->vgapal[pixel].b * 4; + } + + addr += 4; + } + break; + + case CD_1555: + addr += (x_offset * 2) & ~3; + for (int x = 0; x < plot_width; x += 2) + { + uint32_t use_addr = addr; + + if (addr_mode == AM_CHAIN4) + use_addr = ((addr & 0xfffc) << 2) | ((addr & 0x30000) >> 14) | (addr & ~0x3ffff); + else if (addr_mode == AM_ODDEVEN) + use_addr = ((addr << 1) & 0x1fff8) | ((addr >> 15) & 0x4) | (addr & ~0x1ffff); + + for (int xx = 0; xx < 2; xx++) + { + uint16_t pixel = *(uint16_t *)&svga->vram[(use_addr + xx*2) & svga->vram_mask]; + + *data++ = video_15to32[pixel] >> 16; + *data++ = video_15to32[pixel] >> 8; + *data++ = video_15to32[pixel] & 0xff; + } + + addr += 4; + } + break; + + case CD_565: + addr += (x_offset * 2) & ~3; + for (int x = 0; x < plot_width; x += 2) + { + uint32_t use_addr = addr; + + if (addr_mode == AM_CHAIN4) + use_addr = ((addr & 0xfffc) << 2) | ((addr & 0x30000) >> 14) | (addr & ~0x3ffff); + else if (addr_mode == AM_ODDEVEN) + use_addr = ((addr << 1) & 0x1fff8) | ((addr >> 15) & 0x4) | (addr & ~0x1ffff); + + for (int xx = 0; xx < 2; xx++) + { + uint16_t pixel = *(uint16_t *)&svga->vram[(use_addr + xx*2) & svga->vram_mask]; + + *data++ = video_16to32[pixel] >> 16; + *data++ = video_16to32[pixel] >> 8; + *data++ = video_16to32[pixel] & 0xff; + } + + addr += 4; + } + break; + + case CD_888: + addr += (x_offset & ~3) * 3; + for (int x = 0; x < plot_width; x += 4) + { + uint32_t use_addr = addr; + + if (addr_mode == AM_CHAIN4) + use_addr = ((addr & 0xfffc) << 2) | ((addr & 0x30000) >> 14) | (addr & ~0x3ffff); + else if (addr_mode == AM_ODDEVEN) + use_addr = ((addr << 1) & 0x1fff8) | ((addr >> 15) & 0x4) | (addr & ~0x1ffff); + + for (int xx = 0; xx < 4; xx++) + { + *data++ = svga->vram[(use_addr + xx*3 + 2) & svga->vram_mask]; + *data++ = svga->vram[(use_addr + xx*3 + 1) & svga->vram_mask]; + *data++ = svga->vram[(use_addr + xx*3) & svga->vram_mask]; + } + + addr += 12; + } + break; + + case CD_8888: + addr += x_offset * 4; + for (int x = 0; x < plot_width; x++) + { + uint32_t use_addr = addr; + + if (addr_mode == AM_CHAIN4) + use_addr = ((addr & 0xfffc) << 2) | ((addr & 0x30000) >> 14) | (addr & ~0x3ffff); + else if (addr_mode == AM_ODDEVEN) + use_addr = ((addr << 1) & 0x1fff8) | ((addr >> 15) & 0x4) | (addr & ~0x1ffff); + + uint32_t pixel = *(uint32_t *)&svga->vram[use_addr & svga->vram_mask]; + + *data++ = pixel >> 16; + *data++ = pixel >> 8; + *data++ = pixel & 0xff; + + addr += 4; + } + break; + } + + addr = old_addr + offset; + } + + wxBitmap bitmap(buffer); + + wxMemoryDC mdc(bitmap); + + if (scale_factor == 1) + dc.Blit(0, 0, plot_width, h, &mdc, 0, 0); + else + dc.StretchBlit(0, 0, plot_width * scale_factor, h * scale_factor, &mdc, 0, 0, plot_width, h); + + if (current_start_addr != svga->ma_latch * 4 || current_pitch != svga->rowoffset || current_bpp != svga->video_bpp) + UpdateLabels(); + } + + void OnSize(wxSizeEvent &event) + { + window_size = event.GetSize(); + } + + void OnScroll(wxScrollWinEvent &event) + { + Refresh(); + } + +public: + ViewerVRAMCanvas(wxWindow *parent, const wxString& title, const wxPoint& pos, const wxSize& size, svga_t *svga, class ViewerVRAM *vram_parent) + : wxWindow(parent, wxID_ANY, pos, size, wxDEFAULT_FRAME_STYLE | wxVSCROLL | wxHSCROLL | wxALWAYS_SHOW_SB), + svga(svga), + buffer(4096, 4096), + window_size(wxSize(1, 1)), + size_changed(false), + custom_pitch(10), + use_custom_start_addr(true), /*Start with VRAM address fixed to 0*/ + use_custom_pitch(false), + use_custom_colour_depth(false), + use_custom_addr_mode(false), + custom_start_addr(0), + vram_parent(vram_parent), + scale_factor(1) + { + Bind(wxEVT_PAINT, &ViewerVRAMCanvas::OnPaint, this); + Bind(wxEVT_SIZE, &ViewerVRAMCanvas::OnSize, this); + Bind(wxEVT_SCROLLWIN_TOP, &ViewerVRAMCanvas::OnScroll, this); + Bind(wxEVT_SCROLLWIN_BOTTOM, &ViewerVRAMCanvas::OnScroll, this); + Bind(wxEVT_SCROLLWIN_LINEUP, &ViewerVRAMCanvas::OnScroll, this); + Bind(wxEVT_SCROLLWIN_LINEDOWN, &ViewerVRAMCanvas::OnScroll, this); + Bind(wxEVT_SCROLLWIN_PAGEUP, &ViewerVRAMCanvas::OnScroll, this); + Bind(wxEVT_SCROLLWIN_PAGEDOWN, &ViewerVRAMCanvas::OnScroll, this); + Bind(wxEVT_SCROLLWIN_THUMBTRACK, &ViewerVRAMCanvas::OnScroll, this); + Bind(wxEVT_SCROLLWIN_THUMBRELEASE, &ViewerVRAMCanvas::OnScroll, this); + } + + virtual ~ViewerVRAMCanvas() + { + } + + void set_custom_start_addr(uint32_t addr) + { + custom_start_addr = addr; + Refresh(); + } + + void set_use_custom_start_addr(bool use) + { + use_custom_start_addr = use; + Refresh(); + } + + void set_custom_pitch(uint32_t pitch) + { + custom_pitch = pitch; + Refresh(); + } + + void set_use_custom_pitch(bool use) + { + use_custom_pitch = use; + Refresh(); + } + + void set_custom_colour_depth(ColourDepth depth) + { + custom_colour_depth = depth; + Refresh(); + } + + void set_use_custom_colour_depth(bool use) + { + use_custom_colour_depth = use; + Refresh(); + } + + void set_custom_addr_mode(AddrMode mode) + { + custom_addr_mode = mode; + Refresh(); + } + + void set_use_custom_addr_mode(bool use) + { + use_custom_addr_mode = use; + Refresh(); + } + + void set_scale_factor(int new_scale_factor) + { + scale_factor = new_scale_factor; + Refresh(); + } +}; + +class ViewerVRAM: public Viewer +{ +private: + ViewerVRAMCanvas *canvas; + wxBoxSizer *sz; + wxPanel *panel; + wxFrame *temp_frame; + + void OnClose(wxCloseEvent &event) + { + viewer_remove(this); + event.Skip(); + } + + void OnRadioButton(wxCommandEvent &event) + { + wxWindowID id = event.GetId(); + + if (id == XRCID("IDC_STARTADDR_CURRENT")) { + canvas->set_use_custom_start_addr(false); + } else if (id == XRCID("IDC_STARTADDR_CUSTOM")) { + canvas->set_use_custom_start_addr(true); + } else if (id == XRCID("IDC_PITCH_CURRENT")) { + canvas->set_use_custom_pitch(false); + } else if (id == XRCID("IDC_PITCH_CUSTOM")) { + canvas->set_use_custom_pitch(true); + } else if (id == XRCID("IDC_DEPTH_CURRENT")) { + canvas->set_use_custom_colour_depth(false); + } else if (id == XRCID("IDC_DEPTH_1BPP")) { + canvas->set_use_custom_colour_depth(true); + canvas->set_custom_colour_depth(CD_1BPP); + } else if (id == XRCID("IDC_DEPTH_2BPP")) { + canvas->set_use_custom_colour_depth(true); + canvas->set_custom_colour_depth(CD_2BPP); + } else if (id == XRCID("IDC_DEPTH_4BPP")) { + canvas->set_use_custom_colour_depth(true); + canvas->set_custom_colour_depth(CD_4BPP); + } else if (id == XRCID("IDC_DEPTH_8BPP")) { + canvas->set_use_custom_colour_depth(true); + canvas->set_custom_colour_depth(CD_8BPP); + } else if (id == XRCID("IDC_DEPTH_1555")) { + canvas->set_use_custom_colour_depth(true); + canvas->set_custom_colour_depth(CD_1555); + } else if (id == XRCID("IDC_DEPTH_565")) { + canvas->set_use_custom_colour_depth(true); + canvas->set_custom_colour_depth(CD_565); + } else if (id == XRCID("IDC_DEPTH_888")) { + canvas->set_use_custom_colour_depth(true); + canvas->set_custom_colour_depth(CD_888); + } else if (id == XRCID("IDC_DEPTH_8888")) { + canvas->set_use_custom_colour_depth(true); + canvas->set_custom_colour_depth(CD_8888); + } else if (id == XRCID("IDC_ADDRMODE_CURRENT")) { + canvas->set_use_custom_addr_mode(false); + } else if (id == XRCID("IDC_ADDRMODE_NORMAL")) { + canvas->set_use_custom_addr_mode(true); + canvas->set_custom_addr_mode(AM_NORMAL); + } else if (id == XRCID("IDC_ADDRMODE_ODDEVEN")) { + canvas->set_use_custom_addr_mode(false); + canvas->set_custom_addr_mode(AM_ODDEVEN); + } else if (id == XRCID("IDC_ADDRMODE_CHAIN4")) { + canvas->set_use_custom_addr_mode(false); + canvas->set_custom_addr_mode(AM_CHAIN4); + } + } + + void OnText(wxCommandEvent &event) + { + wxWindowID id = event.GetId(); + + if (id == XRCID("IDC_STARTADDR_TEXTCTRL")) { + unsigned long addr = 0; + wxTextCtrl *text_ctrl = static_cast(event.GetEventObject()); + + text_ctrl->GetValue().ToULong(&addr, 16); + canvas->set_custom_start_addr(addr); + } else if (id == XRCID("IDC_PITCH_TEXTCTRL")) { + unsigned long pitch = 0; + wxTextCtrl *text_ctrl = static_cast(event.GetEventObject()); + + text_ctrl->GetValue().ToULong(&pitch, 10); + canvas->set_custom_pitch(pitch); + } + } + + void OnSpinCtrl(wxSpinEvent &event) + { + wxWindowID id = event.GetId(); + + if (id == XRCID("IDC_SCALE")) { + int scale_factor = event.GetPosition(); + + canvas->set_scale_factor(scale_factor); + } + } + +public: + ViewerVRAM(wxWindow *parent, wxString title, wxSize size, void *p) + : Viewer(parent, title, size, p) + { + panel = wxXmlResource::Get()->LoadPanel(this, "ViewerVRAMPanel"); + + sz = new wxBoxSizer(wxHORIZONTAL); + sz->Add(panel, 0, wxEXPAND | wxALL, 0); + + canvas = new ViewerVRAMCanvas(this, "vram", wxDefaultPosition, wxDefaultSize, (svga_t *)p, this); + sz->Add(canvas, 1, wxEXPAND | wxALL, 0); + SetSizer(sz); + + Bind(wxEVT_CLOSE_WINDOW, &ViewerVRAM::OnClose, this); + + panel->Bind(wxEVT_RADIOBUTTON, &ViewerVRAM::OnRadioButton, this); + panel->Bind(wxEVT_TEXT_ENTER, &ViewerVRAM::OnText, this); + panel->Bind(wxEVT_SPINCTRL, &ViewerVRAM::OnSpinCtrl, this); + } + + virtual ~ViewerVRAM() + { + delete canvas; + } +}; + +void ViewerVRAMCanvas::UpdateLabels() +{ + current_start_addr = svga->ma_latch * 4; + current_pitch = svga->rowoffset; + current_bpp = svga->video_bpp; + + char s[128]; + + wxRadioButton *rb = static_cast(vram_parent->FindWindow("IDC_STARTADDR_CURRENT")); + sprintf(s, "Current (%x)", current_start_addr); + rb->SetLabel(wxString(s)); + + rb = static_cast(vram_parent->FindWindow("IDC_PITCH_CURRENT")); + sprintf(s, "Current (%u)", current_pitch); + rb->SetLabel(wxString(s)); + + rb = static_cast(vram_parent->FindWindow("IDC_DEPTH_CURRENT")); + switch (current_bpp) + { + case 1: + rb->SetLabel("Current (2 cols)"); + break; + case 2: + rb->SetLabel("Current (4 cols)"); + break; + case 4: + rb->SetLabel("Current (16 cols)"); + break; + case 8: + rb->SetLabel("Current (256 cols)"); + break; + case 15: + rb->SetLabel("Current (1555)"); + break; + case 16: + rb->SetLabel("Current (555)"); + break; + case 24: + rb->SetLabel("Current (888)"); + break; + case 32: + rb->SetLabel("Current (8888)"); + break; + default: + rb->SetLabel("Current"); + break; + } +} + +static void *viewer_vram_open(void *parent, void *p, const char *title) +{ + wxFrame *w = new ViewerVRAM((wxWindow *)parent, title, wxDefaultSize, p); + + w->Show(true); + + return w; +} + +viewer_t viewer_vram = +{ + .open = viewer_vram_open +}; diff --git a/src/wx-ui/viewers/viewers.cmake b/src/wx-ui/viewers/viewers.cmake new file mode 100644 index 00000000..1ccc182e --- /dev/null +++ b/src/wx-ui/viewers/viewers.cmake @@ -0,0 +1,13 @@ +set(PCEM_PRIVATE_API ${PCEM_PRIVATE_API} + ${CMAKE_SOURCE_DIR}/includes/private/wx-ui/viewer.h + ) + +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/wx-ui/viewers) + +set(PCEM_SRC ${PCEM_SRC} + wx-ui/viewers/viewer.cc + wx-ui/viewers/viewer_font.cc + wx-ui/viewers/viewer_palette.cc + wx-ui/viewers/viewer_voodoo.cc + wx-ui/viewers/viewer_vram.cc + ) diff --git a/src/wx-ui/wx-sdl2.c b/src/wx-ui/wx-sdl2.c index 84c56be4..6c9da24a 100644 --- a/src/wx-ui/wx-sdl2.c +++ b/src/wx-ui/wx-sdl2.c @@ -49,6 +49,8 @@ #include "plugin.h" #include "pic.h" +#include "viewer.h" + #if __APPLE__ #define pause __pause #include @@ -355,6 +357,7 @@ void wx_initmenu() { int wx_setupmenu(void *data) { int c; update_cdrom_menu(menu); + update_viewers_menu(menu); sprintf(menuitem, "IDM_VID_RESOLUTION[%d]", vid_resize); wx_checkmenuitem(menu, WX_ID(menuitem), WX_MB_CHECKED); wx_enablemenuitem(menu, wx_xrcid("IDM_VID_SCALE_MENU"), !vid_resize); @@ -521,6 +524,7 @@ int resume_emulation() { if (emulation_state == EMULATION_PAUSED) { emulation_state = EMULATION_RUNNING; pause = 0; + viewer_notify_resume(); return TRUE; } return FALSE; @@ -588,6 +592,7 @@ int pause_emulation() { pclog("Emulation paused.\n"); emulation_state = EMULATION_PAUSED; pause = 1; + viewer_notify_pause(); return TRUE; } @@ -621,6 +626,7 @@ int stop_emulation() { pclog("Emulation stopped.\n"); + viewer_close_all(); wx_close_status(ghwnd); return TRUE; @@ -948,6 +954,8 @@ int wx_handle_command(void *hwnd, int wParam, int checked) { cdrom_drive = new_cdrom_drive; saveconfig(NULL); update_cdrom_menu(hmenu); + } else if (wParam >= IDM_VIEWER && wParam < IDM_VIEWER_MAX) { + viewer_open(hwnd, wParam - IDM_VIEWER); } return 0; }