shithub: psxe

Download patch

ref: b043b943449c64f14c220d78fe731cfa47143031
parent: fe9be079e19b0c05b331b906e8f7a602d477f755
author: allkern <lisandroaalarcon@gmail.com>
date: Tue Jun 11 12:05:22 EDT 2024

Many fixes

Fixed multiple GPU bugs: poly seam, wrong rect colors, transparent overlap
Implemented Namco GunCon support
Implemented support from 4MB/8MB RAM

--- a/compat.txt
+++ b/compat.txt
@@ -39,6 +39,7 @@
 Tokimeki Memorial 2 (Japan) *1
 Tomb Raider (USA) *8
 Tony Hawk's Pro Skater (USA) *10
+Vib-Ribbon (Japan) *8
 WipeOut (USA) *7
 Yu-Gi-Oh! Forbidden Memories (USA) *1
 
--- a/frontend/main.c
+++ b/frontend/main.c
@@ -1,5 +1,6 @@
 #include "../psx/psx.h"
 #include "../psx/input/sda.h"
+#include "../psx/input/guncon.h"
 #include "../psx/disc/cue.h"
 
 #include "screen.h"
@@ -75,6 +76,9 @@
     psx_input_t* input = psx_input_create();
     psx_input_init(input);
 
+    // psxi_guncon_t* controller = psxi_guncon_create();
+    // psxi_guncon_init(controller);
+    // psxi_guncon_init_input(controller, input);
     psxi_sda_t* controller = psxi_sda_create();
     psxi_sda_init(controller, SDA_MODEL_DIGITAL);
     psxi_sda_init_input(controller, input);
--- a/frontend/screen.c
+++ b/frontend/screen.c
@@ -1,6 +1,7 @@
 #include "screen.h"
 
 #include "input/sda.h"
+#include "input/guncon.h"
 
 uint32_t screen_get_button(SDL_Keycode k) {
     if (k == SDLK_x     ) return PSXI_SW_SDA_CROSS;
@@ -355,6 +356,46 @@
                     }
                 }
             } break;
+
+            // To-do: GunCon
+            // case SDL_MOUSEMOTION: {
+            //     psx_pad_analog_change(screen->pad, 0, PSXI_AX_GUNCON_SX, psx_get_dmode_width(screen->psx));
+            //     psx_pad_analog_change(screen->pad, 0, PSXI_AX_GUNCON_SY, psx_get_dmode_height(screen->psx));
+            //     psx_pad_analog_change(screen->pad, 0, PSXI_AX_GUNCON_X, event.motion.x * (1.0f / (float)screen->scale));
+            //     psx_pad_analog_change(screen->pad, 0, PSXI_AX_GUNCON_Y, event.motion.y * (1.0f / (float)screen->scale));
+            // } break;
+
+            // case SDL_MOUSEBUTTONDOWN: {
+            //     switch (event.button.button) {
+            //         case SDL_BUTTON_LEFT: {
+            //             psx_pad_button_press(screen->pad, 0, PSXI_SW_GUNCON_A);
+            //         } break;
+
+            //         case SDL_BUTTON_RIGHT: {
+            //             psx_pad_button_press(screen->pad, 0, PSXI_SW_GUNCON_B);
+            //         } break;
+
+            //         case SDL_BUTTON_MIDDLE: {
+            //             psx_pad_button_press(screen->pad, 0, PSXI_SW_GUNCON_TRIGGER);
+            //         } break;
+            //     }
+            // } break;
+
+            // case SDL_MOUSEBUTTONUP: {
+            //     switch (event.button.button) {
+            //         case SDL_BUTTON_LEFT: {
+            //             psx_pad_button_release(screen->pad, 0, PSXI_SW_GUNCON_A);
+            //         } break;
+
+            //         case SDL_BUTTON_RIGHT: {
+            //             psx_pad_button_release(screen->pad, 0, PSXI_SW_GUNCON_B);
+            //         } break;
+
+            //         case SDL_BUTTON_MIDDLE: {
+            //             psx_pad_button_release(screen->pad, 0, PSXI_SW_GUNCON_TRIGGER);
+            //         } break;
+            //     }
+            // } break;
 
             case SDL_TEXTINPUT: {
                 psx_exp2_atcons_put(screen->psx->exp2, event.text.text[0]);
--- a/psx/bus.c
+++ b/psx/bus.c
@@ -104,6 +104,12 @@
     if (addr == 0x1f801054)
         return 0x05;
 
+    if (addr == 0x1f400004)
+        return 0xc0;
+
+    if (addr == 0x1f400006)
+        return 0x1fe0;
+
     printf("Unhandled 16-bit read from %08x:%08x\n", vaddr, addr);
 
     // exit(1);
--- a/psx/dev/cdrom.c
+++ b/psx/dev/cdrom.c
@@ -73,6 +73,7 @@
 
         msf_add_f(&cdrom->seek_msf, 1);
 
+        return;
         // Check RT and Video/Data bit
         // if (cdrom->dfifo[0x12] & 4)
         //     continue;
@@ -79,17 +80,17 @@
 
         // If we get here it means this is a real-time video sector.
         // If the XA filter is disabled, we're done
-        if (!(cdrom->mode & MODE_XA_FILTER))
-            return;
+        // if (!(cdrom->mode & MODE_XA_FILTER))
+        //     return;
 
-        // Else check XA file/channel
-        int file_eq = cdrom->dfifo[0x10] == cdrom->xa_file;
-        int channel_eq = cdrom->dfifo[0x11] == cdrom->xa_channel;
+        // // Else check XA file/channel
+        // int file_eq = cdrom->dfifo[0x10] == cdrom->xa_file;
+        // int channel_eq = cdrom->dfifo[0x11] == cdrom->xa_channel;
 
-        // If they are equal to our filter values, we're done
-        // else keep searching
-        if (file_eq && channel_eq)
-            return;
+        // // If they are equal to our filter values, we're done
+        // // else keep searching
+        // if (file_eq && channel_eq)
+        //     return;
     }
 }
 
@@ -921,7 +922,7 @@
         } break;
 
         case CD_STATE_SEND_RESP1: {
-            msf_t absolute = cdrom->xa_playing ? cdrom->xa_msf : cdrom->seek_msf;
+            msf_t absolute = cdrom->seek_msf;
             msf_t relative = absolute;
             msf_t track_msf = cdrom_get_track_addr(cdrom, absolute);
 
@@ -963,7 +964,7 @@
             RESP_PUSH(relative.s);
             RESP_PUSH(relative.m);
             RESP_PUSH(0x01);
-            RESP_PUSH(0x01);
+            RESP_PUSH(0x15);
 
             if (cdrom->ongoing_read_command) {
                 printf("getlocp command=%02x\n", cdrom->ongoing_read_command);
@@ -1803,6 +1804,15 @@
     memset(cdrom->xa_left_resample_buf, 0, (XA_STEREO_RESAMPLE_SIZE * 2) * sizeof(int16_t));
     memset(cdrom->xa_right_resample_buf, 0, (XA_STEREO_RESAMPLE_SIZE * 2) * sizeof(int16_t));
     memset(cdrom->xa_mono_resample_buf, 0, (XA_MONO_RESAMPLE_SIZE * 2) * sizeof(int16_t));
+
+    cdrom->vol[0] = 0x80;
+    cdrom->vol[1] = 0x00;
+    cdrom->vol[2] = 0x80;
+    cdrom->vol[3] = 0x00;
+    cdrom->vapp[0] = 0x80;
+    cdrom->vapp[1] = 0x00;
+    cdrom->vapp[2] = 0x80;
+    cdrom->vapp[3] = 0x00;
 
     cdrom->seek_msf.m = 0;
     cdrom->seek_msf.s = 2;
--- a/psx/dev/gpu.c
+++ b/psx/dev/gpu.c
@@ -6,6 +6,8 @@
 #include "gpu.h"
 #include "../log.h"
 
+#define SE10(v) ((int16_t)((v) << 5) >> 5)
+
 int g_psx_gpu_dither_kernel[] = {
     -4, +0, -3, +1,
     +2, -2, +3, -1,
@@ -176,6 +178,9 @@
     }
 }
 
+#define TL(z, a, b) \
+    ((z < 0) || ((z == 0) && ((b.y > a.y) || ((b.y == a.y) && (b.x < a.x)))))
+
 void gpu_render_triangle(psx_gpu_t* gpu, vertex_t v0, vertex_t v1, vertex_t v2, poly_data_t data, int edge) {
     vertex_t a, b, c, p;
 
@@ -216,7 +221,7 @@
     int xmax = max3(a.x, b.x, c.x);
     int ymax = max3(a.y, b.y, c.y);
 
-    if (((xmax - xmin) > 2048) || ((ymax - ymin) > 1024)) 
+    if (((xmax - xmin) > 2048) || ((ymax - ymin) > 1024))
         return;
 
     float area = EDGE(a, b, c);
@@ -233,15 +238,18 @@
             p.y = y;
 
             float z0 = EDGE(b, c, p);
+
+            if (TL(z0, b, c))
+                continue;
+
             float z1 = EDGE(c, a, p);
-            float z2 = EDGE(a, b, p);
 
-            int e = ((z0 < 0) || (z1 < 0) || (z2 < 0));
+            if (TL(z1, c, a))
+                continue;
 
-            if (transp && edge)
-                e = ((z0 < 0) || (z1 <= 0) || (z2 < 0));
+            float z2 = EDGE(a, b, p);
 
-            if (e)
+            if (TL(z2, a, b))
                 continue;
 
             uint16_t color = 0;
@@ -248,9 +256,9 @@
             uint32_t mod   = 0;
 
             if (data.attrib & PA_SHADED) {
-                int cr = (z0 * ((a.c >>  0) & 0xff) + z1 * ((b.c >>  0) & 0xff) + z2 * ((c.c >>  0) & 0xff)) / area;
-                int cg = (z0 * ((a.c >>  8) & 0xff) + z1 * ((b.c >>  8) & 0xff) + z2 * ((c.c >>  8) & 0xff)) / area;
-                int cb = (z0 * ((a.c >> 16) & 0xff) + z1 * ((b.c >> 16) & 0xff) + z2 * ((c.c >> 16) & 0xff)) / area;
+                float cr = (z0 * ((a.c >>  0) & 0xff) + z1 * ((b.c >>  0) & 0xff) + z2 * ((c.c >>  0) & 0xff)) / area;
+                float cg = (z0 * ((a.c >>  8) & 0xff) + z1 * ((b.c >>  8) & 0xff) + z2 * ((c.c >>  8) & 0xff)) / area;
+                float cb = (z0 * ((a.c >> 16) & 0xff) + z1 * ((b.c >> 16) & 0xff) + z2 * ((c.c >> 16) & 0xff)) / area;
 
                 int dy = (y - ymin) & 3;
                 int dx = (x - xmin) & 3;
@@ -262,12 +270,16 @@
                 cb += dither;
 
                 // Saturate (clamp) to 00-ff
-                cr = (cr >= 0xff) ? 0xff : ((cr <= 0) ? 0 : cr);
-                cg = (cg >= 0xff) ? 0xff : ((cg <= 0) ? 0 : cg);
-                cb = (cb >= 0xff) ? 0xff : ((cb <= 0) ? 0 : cb);
+                cr = (cr >= 255.0f) ? 255.0f : ((cr <= 0.0f) ? 0.0f : cr);
+                cg = (cg >= 255.0f) ? 255.0f : ((cg <= 0.0f) ? 0.0f : cg);
+                cb = (cb >= 255.0f) ? 255.0f : ((cb <= 0.0f) ? 0.0f : cb);
 
-                uint32_t rgb = (cb << 16) | (cg << 8) | cr;
+                unsigned int ucr = roundf(cr);
+                unsigned int ucg = roundf(cg);
+                unsigned int ucb = roundf(cb);
 
+                uint32_t rgb = (ucb << 16) | (ucg << 8) | ucr;
+
                 mod = rgb;
             } else {
                 mod = data.v[0].c;
@@ -274,8 +286,8 @@
             }
 
             if (data.attrib & PA_TEXTURED) {
-                uint32_t tx = ((z0 * a.tx) + (z1 * b.tx) + (z2 * c.tx)) / area;
-                uint32_t ty = ((z0 * a.ty) + (z1 * b.ty) + (z2 * c.ty)) / area;
+                uint32_t tx = roundf(((z0 * a.tx) + (z1 * b.tx) + (z2 * c.tx)) / area);
+                uint32_t ty = roundf(((z0 * a.ty) + (z1 * b.ty) + (z2 * c.ty)) / area);
 
                 uint16_t texel = gpu_fetch_texel(gpu, tx, ty, tpx, tpy, clutx, cluty, depth);
 
@@ -282,7 +294,7 @@
                 if (!texel)
                     continue;
 
-                if (transp)
+                if ((data.attrib & PA_TRANSP) != 0)
                     transp = (texel & 0x8000) != 0;
 
                 if (data.attrib & PA_RAW) {
@@ -304,9 +316,9 @@
                     cg = (cg >= 255.0f) ? 255.0f : ((cg <= 0.0f) ? 0.0f : cg);
                     cb = (cb >= 255.0f) ? 255.0f : ((cb <= 0.0f) ? 0.0f : cb);
 
-                    unsigned int ucr = cr;
-                    unsigned int ucg = cg;
-                    unsigned int ucb = cb;
+                    unsigned int ucr = roundf(cr);
+                    unsigned int ucg = roundf(cg);
+                    unsigned int ucb = roundf(cb);
 
                     uint32_t rgb = ucr | (ucg << 8) | (ucb << 16);
 
@@ -355,9 +367,9 @@
                 cg = (cg >= 255.0f) ? 255.0f : ((cg <= 0.0f) ? 0.0f : cg);
                 cb = (cb >= 255.0f) ? 255.0f : ((cb <= 0.0f) ? 0.0f : cb);
 
-                unsigned int ucr = cr;
-                unsigned int ucg = cg;
-                unsigned int ucb = cb;
+                unsigned int ucr = roundf(cr);
+                unsigned int ucg = roundf(cg);
+                unsigned int ucb = roundf(cb);
 
                 uint32_t rgb = ucr | (ucg << 8) | (ucb << 16);
 
@@ -431,7 +443,7 @@
                 if (!texel)
                     goto skip;
 
-                if (transp)
+                if ((data.attrib & RA_TRANSP) != 0)
                     transp = (texel & 0x8000) != 0;
 
                 float tr = ((texel >> 0 ) & 0x1f) << 3;
@@ -446,10 +458,14 @@
                 float cg = (tg * mg) / 128.0f;
                 float cb = (tb * mb) / 128.0f;
 
-                unsigned int ucr = cr;
-                unsigned int ucg = cg;
-                unsigned int ucb = cb;
+                cr = (cr >= 255.0f) ? 255.0f : ((cr <= 0.0f) ? 0.0f : cr);
+                cg = (cg >= 255.0f) ? 255.0f : ((cg <= 0.0f) ? 0.0f : cg);
+                cb = (cb >= 255.0f) ? 255.0f : ((cb <= 0.0f) ? 0.0f : cb);
 
+                unsigned int ucr = roundf(cr);
+                unsigned int ucg = roundf(cg);
+                unsigned int ucb = roundf(cb);
+
                 uint32_t rgb = ucr | (ucg << 8) | (ucb << 16);
 
                 color = BGR555(rgb);
@@ -495,9 +511,9 @@
                 cg = (cg >= 255.0f) ? 255.0f : ((cg <= 0.0f) ? 0.0f : cg);
                 cb = (cb >= 255.0f) ? 255.0f : ((cb <= 0.0f) ? 0.0f : cb);
 
-                unsigned int ucr = cr;
-                unsigned int ucg = cg;
-                unsigned int ucb = cb;
+                unsigned int ucr = roundf(cr);
+                unsigned int ucg = roundf(cg);
+                unsigned int ucb = roundf(cb);
 
                 uint32_t rgb = ucr | (ucg << 8) | (ucb << 16);
 
@@ -851,8 +867,8 @@
                 int size_offset = 2 + textured;
 
                 rect.v0.c   = gpu->buf[0] & 0xffffff;
-                rect.v0.x   = gpu->buf[1] & 0xffff;
-                rect.v0.y   = gpu->buf[1] >> 16;
+                rect.v0.x   = SE10(gpu->buf[1] & 0xffff);
+                rect.v0.y   = SE10(gpu->buf[1] >> 16);
                 rect.v0.tx  = (gpu->buf[2] >> 0) & 0xff;
                 rect.v0.ty  = (gpu->buf[2] >> 8) & 0xff;
                 rect.clut   = gpu->buf[2] >> 16;
@@ -903,18 +919,28 @@
                 poly.clut = gpu->buf[2] >> 16;
                 poly.texp = gpu->buf[texp_offset] >> 16;
 
+                // Undocumented behavior?
+                // Fixes Mortal Kombat II, Bubble Bobble, Driver 1 & 2
+                if (textured) {
+                    gpu->texp_x = (poly.texp & 0xf) << 6;
+                    gpu->texp_y = (poly.texp & 0x10) << 4;
+                    gpu->texp_d = (poly.texp >> 7) & 0x3;
+                    gpu->gpustat &= 0xfffffe00;
+                    gpu->gpustat |= poly.texp & 0x1ff;
+                }
+
                 poly.v[0].c = gpu->buf[0+0*color_offset] & 0xffffff;
                 poly.v[1].c = gpu->buf[0+1*color_offset] & 0xffffff;
                 poly.v[2].c = gpu->buf[0+2*color_offset] & 0xffffff;
                 poly.v[3].c = gpu->buf[0+3*color_offset] & 0xffffff;
-                poly.v[0].x = gpu->buf[1+0*vert_offset] & 0xffff;
-                poly.v[1].x = gpu->buf[1+1*vert_offset] & 0xffff;
-                poly.v[2].x = gpu->buf[1+2*vert_offset] & 0xffff;
-                poly.v[3].x = gpu->buf[1+3*vert_offset] & 0xffff;
-                poly.v[0].y = gpu->buf[1+0*vert_offset] >> 16;
-                poly.v[1].y = gpu->buf[1+1*vert_offset] >> 16;
-                poly.v[2].y = gpu->buf[1+2*vert_offset] >> 16;
-                poly.v[3].y = gpu->buf[1+3*vert_offset] >> 16;
+                poly.v[0].x = SE10(gpu->buf[1+0*vert_offset] & 0xffff);
+                poly.v[1].x = SE10(gpu->buf[1+1*vert_offset] & 0xffff);
+                poly.v[2].x = SE10(gpu->buf[1+2*vert_offset] & 0xffff);
+                poly.v[3].x = SE10(gpu->buf[1+3*vert_offset] & 0xffff);
+                poly.v[0].y = SE10(gpu->buf[1+0*vert_offset] >> 16);
+                poly.v[1].y = SE10(gpu->buf[1+1*vert_offset] >> 16);
+                poly.v[2].y = SE10(gpu->buf[1+2*vert_offset] >> 16);
+                poly.v[3].y = SE10(gpu->buf[1+3*vert_offset] >> 16);
                 poly.v[0].tx = gpu->buf[2+0*texc_offset] & 0xff;
                 poly.v[1].tx = gpu->buf[2+1*texc_offset] & 0xff;
                 poly.v[2].tx = gpu->buf[2+2*texc_offset] & 0xff;
@@ -925,7 +951,7 @@
                 poly.v[3].ty = (gpu->buf[2+3*texc_offset] >> 8) & 0xff;
 
                 if (poly.attrib & PA_QUAD) {
-                    gpu_render_triangle(gpu, poly.v[0], poly.v[1], poly.v[2], poly, 0);
+                    gpu_render_triangle(gpu, poly.v[0], poly.v[1], poly.v[2], poly, 1);
                     gpu_render_triangle(gpu, poly.v[1], poly.v[2], poly.v[3], poly, 1);
                 } else {
                     gpu_render_triangle(gpu, poly.v[0], poly.v[1], poly.v[2], poly, 0);
@@ -1647,8 +1673,8 @@
             gpu->draw_y2 = (gpu->buf[0] >> 10) & 0x1ff;
         } break;
         case 0xe5: {
-            gpu->off_x = ((int32_t)((gpu->buf[0] >> 0 ) & 0x7ff) << 21) >> 21;
-            gpu->off_y = ((int32_t)((gpu->buf[0] >> 11) & 0x7ff) << 21) >> 21;
+            gpu->off_x = ((int32_t)(((gpu->buf[0] >> 0 ) & 0x7ff) << 21)) >> 21;
+            gpu->off_y = ((int32_t)(((gpu->buf[0] >> 11) & 0x7ff) << 21)) >> 21;
         } break;
         case 0xe6: {
             /* To-do: Implement mask bit thing */
--- a/psx/dev/input.h
+++ b/psx/dev/input.h
@@ -11,7 +11,7 @@
 typedef uint32_t (*psx_input_read_t)(void*);
 typedef void (*psx_input_on_button_press_t)(void*, uint32_t);
 typedef void (*psx_input_on_button_release_t)(void*, uint32_t);
-typedef void (*psx_input_on_analog_change_t)(void*, uint32_t, uint8_t);
+typedef void (*psx_input_on_analog_change_t)(void*, uint32_t, uint16_t);
 typedef int (*psx_input_query_fifo_t)(void*);
 
 struct psx_input_t {
--- a/psx/dev/pad.c
+++ b/psx/dev/pad.c
@@ -262,7 +262,7 @@
         selected_slot->on_button_release_func(selected_slot->udata, data);
 }
 
-void psx_pad_analog_change(psx_pad_t* pad, int slot, uint32_t stick, uint8_t data) {
+void psx_pad_analog_change(psx_pad_t* pad, int slot, uint32_t stick, uint16_t data) {
     psx_input_t* selected_slot = pad->joy_slot[slot];
 
     if (selected_slot)
--- a/psx/dev/pad.h
+++ b/psx/dev/pad.h
@@ -131,7 +131,7 @@
 void psx_pad_destroy(psx_pad_t*);
 void psx_pad_button_press(psx_pad_t*, int, uint32_t);
 void psx_pad_button_release(psx_pad_t*, int, uint32_t);
-void psx_pad_analog_change(psx_pad_t*, int, uint32_t, uint8_t);
+void psx_pad_analog_change(psx_pad_t*, int, uint32_t, uint16_t);
 void psx_pad_attach_joy(psx_pad_t*, int, psx_input_t*);
 void psx_pad_detach_joy(psx_pad_t*, int);
 void psx_pad_attach_mcd(psx_pad_t*, int, const char*);
--- a/psx/dev/ram.c
+++ b/psx/dev/ram.c
@@ -9,7 +9,7 @@
     return (psx_ram_t*)malloc(sizeof(psx_ram_t));
 }
 
-void psx_ram_init(psx_ram_t* ram, psx_mc2_t* mc2) {
+void psx_ram_init(psx_ram_t* ram, psx_mc2_t* mc2, int size) {
     memset(ram, 0, sizeof(psx_ram_t));
 
     ram->io_base = PSX_RAM_BEGIN;
@@ -16,43 +16,46 @@
     ram->io_size = PSX_RAM_SIZE;
 
     ram->mc2 = mc2;
-    ram->buf = (uint8_t*)malloc(PSX_RAM_SIZE);
+    ram->buf = (uint8_t*)malloc(RAM_SIZE);
 
-    memset(ram->buf, 0xee, PSX_RAM_SIZE);
+    if (size & 0x1ffff)
+        size = RAM_SIZE_2MB;
+
+    memset(ram->buf, RAM_INIT_FILL, size);
 }
 
 uint32_t psx_ram_read32(psx_ram_t* ram, uint32_t offset) {
-    offset &= 0x1fffff;
+    offset &= RAM_SIZE - 1;
 
     return *((uint32_t*)(ram->buf + offset));
 }
 
 uint16_t psx_ram_read16(psx_ram_t* ram, uint32_t offset) {
-    offset &= 0x1fffff;
+    offset &= RAM_SIZE - 1;
 
     return *((uint16_t*)(ram->buf + offset));
 }
 
 uint8_t psx_ram_read8(psx_ram_t* ram, uint32_t offset) {
-    offset &= 0x1fffff;
+    offset &= RAM_SIZE - 1;
 
     return ram->buf[offset];
 }
 
 void psx_ram_write32(psx_ram_t* ram, uint32_t offset, uint32_t value) {
-    offset &= 0x1fffff;
+    offset &= RAM_SIZE - 1;
 
     *((uint32_t*)(ram->buf + offset)) = value;
 }
 
 void psx_ram_write16(psx_ram_t* ram, uint32_t offset, uint16_t value) {
-    offset &= 0x1fffff;
+    offset &= RAM_SIZE - 1;
 
     *((uint16_t*)(ram->buf + offset)) = value;
 }
 
 void psx_ram_write8(psx_ram_t* ram, uint32_t offset, uint8_t value) {
-    offset &= 0x1fffff;
+    offset &= RAM_SIZE - 1;
 
     ram->buf[offset] = value;
 }
--- a/psx/dev/ram.h
+++ b/psx/dev/ram.h
@@ -6,12 +6,17 @@
 #include "../log.h"
 #include "mc2.h"
 
-//#define PSX_RAM_SIZE    0x200000
+#define RAM_SIZE        0x200000
 #define PSX_RAM_SIZE    0x1f000000
 #define PSX_RAM_BEGIN   0x00000000
 //#define PSX_RAM_END     0x001fffff
 #define PSX_RAM_END     0x1effffff
+#define RAM_INIT_FILL   0
 
+#define RAM_SIZE_2MB 0x200000
+#define RAM_SIZE_4MB 0x400000
+#define RAM_SIZE_8MB 0x800000
+
 typedef struct {
     uint32_t bus_delay;
     uint32_t io_base, io_size;
@@ -22,7 +27,7 @@
 } psx_ram_t;
 
 psx_ram_t* psx_ram_create(void);
-void psx_ram_init(psx_ram_t*, psx_mc2_t*);
+void psx_ram_init(psx_ram_t*, psx_mc2_t*, int size);
 uint32_t psx_ram_read32(psx_ram_t*, uint32_t);
 uint16_t psx_ram_read16(psx_ram_t*, uint32_t);
 uint8_t psx_ram_read8(psx_ram_t*, uint32_t);
--- /dev/null
+++ b/psx/input/guncon.c
@@ -1,0 +1,138 @@
+/*
+    This file is part of the PSXE Emulator Project
+
+    Sony PlayStation Standard Digital/Analog Controller Emulator
+*/
+
+#include "guncon.h"
+#include "../log.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+const char* states[] = {
+    "HIZ",
+    "IDL",
+    "IDH",
+    "SWL",
+    "SWH",
+    "XPL",
+    "XPH",
+    "YPL",
+    "YPH"
+};
+
+psxi_guncon_t* psxi_guncon_create(void) {
+    return (psxi_guncon_t*)malloc(sizeof(psxi_guncon_t));
+}
+
+void psxi_guncon_init(psxi_guncon_t* guncon) {
+    memset(guncon, 0, sizeof(psxi_guncon_t));
+
+    guncon->tx_data = 0xff;
+    guncon->tx_data_ready = 1;
+    guncon->state = GUNCON_STATE_TX_HIZ;
+    guncon->sw = 0xffff;
+    guncon->x = 0x4d;
+    guncon->y = 0x19;
+    guncon->sx = 384;
+    guncon->sy = 240;
+}
+
+uint32_t psxi_guncon_read(void* udata) {
+    psxi_guncon_t* guncon = (psxi_guncon_t*)udata;
+
+    switch (guncon->state) {
+        case GUNCON_STATE_TX_HIZ: guncon->tx_data = 0xff; break;
+        case GUNCON_STATE_TX_IDL: guncon->tx_data = GUNCON_IDL; break;
+        case GUNCON_STATE_TX_IDH: guncon->tx_data = GUNCON_IDH; break;
+        case GUNCON_STATE_TX_SWL: guncon->tx_data = guncon->sw & 0xff; break;
+        case GUNCON_STATE_TX_SWH: guncon->tx_data = guncon->sw >> 8; break;
+        case GUNCON_STATE_TX_XPL: guncon->tx_data = guncon->x & 0xff; break;
+        case GUNCON_STATE_TX_XPH: guncon->tx_data = guncon->x >> 8; break;
+        case GUNCON_STATE_TX_YPL: guncon->tx_data = guncon->y & 0xff; break;
+        case GUNCON_STATE_TX_YPH: {
+            // printf("guncon: read state %u (%s) -> %02x\n", guncon->state, states[guncon->state], guncon->y >> 8);
+
+            guncon->tx_data_ready = 0;
+            guncon->state = GUNCON_STATE_TX_HIZ;
+
+            return guncon->y >> 8;
+        } break;
+    }
+
+    // printf("guncon: read state %u (%s) -> %02x\n", guncon->state, states[guncon->state], guncon->tx_data);
+
+    guncon->tx_data_ready = 1;
+    guncon->state++;
+
+    return guncon->tx_data;
+}
+
+void psxi_guncon_write(void* udata, uint16_t data) {
+    psxi_guncon_t* guncon = (psxi_guncon_t*)udata;
+
+    // printf("guncon: write %02x\n", data);
+
+    (void)guncon;
+}
+
+void psxi_guncon_on_button_press(void* udata, uint32_t data) {
+    psxi_guncon_t* guncon = (psxi_guncon_t*)udata;
+
+    guncon->sw &= ~data;
+}
+
+void psxi_guncon_on_button_release(void* udata, uint32_t data) {
+    psxi_guncon_t* guncon = (psxi_guncon_t*)udata;
+
+    guncon->sw |= data;
+}
+
+void psxi_guncon_on_analog_change(void* udata, uint32_t axis, uint16_t data) {
+    psxi_guncon_t* guncon = (psxi_guncon_t*)udata;
+
+    switch (axis) {
+        case PSXI_AX_GUNCON_X: {
+            data *= (461.0f - 77.0f) / (float)guncon->sx;
+            data += 77;
+
+            guncon->x = data;
+        } break;
+
+        case PSXI_AX_GUNCON_Y: {
+            data *= (248.0f - 25.0f) / (float)guncon->sy;
+            data += 25;
+
+            guncon->y = data;
+        } break;
+
+        case PSXI_AX_GUNCON_SX: {
+            guncon->sx = data;
+        } break;
+
+        case PSXI_AX_GUNCON_SY: {
+            guncon->sy = data;
+        } break;
+    }
+}
+
+int psxi_guncon_query_fifo(void* udata) {
+    psxi_guncon_t* guncon = (psxi_guncon_t*)udata;
+
+    return guncon->tx_data_ready;
+}
+
+void psxi_guncon_init_input(psxi_guncon_t* guncon, psx_input_t* input) {
+    input->udata = guncon;
+    input->write_func = psxi_guncon_write;
+    input->read_func = psxi_guncon_read;
+    input->on_button_press_func = psxi_guncon_on_button_press;
+    input->on_button_release_func = psxi_guncon_on_button_release;
+    input->on_analog_change_func = psxi_guncon_on_analog_change;
+    input->query_fifo_func = psxi_guncon_query_fifo;
+}
+
+void psxi_guncon_destroy(psxi_guncon_t* guncon) {
+    free(guncon);
+}
\ No newline at end of file
--- /dev/null
+++ b/psx/input/guncon.h
@@ -1,0 +1,67 @@
+/*
+    This file is part of the PSXE Emulator Project
+
+    Namco GunCon emulation
+*/
+
+#ifndef GUNCON_H
+#define GUNCON_H
+
+#include "../dev/input.h"
+
+/*
+  __Halfword 0 (Controller Info)___________________
+  0-15  Controller Info  (5A63h=Namco Lightgun; GunCon/Cinch Type)
+  __Halfword 1 (Buttons)___________________________
+  0-2   Not used              (All bits always 1)
+  3     Button A (Left Side)  (0=Pressed, 1=Released) ;aka Joypad Start
+  4-12  Not used              (All bits always 1)
+  13    Trigger Button        (0=Pressed, 1=Released) ;aka Joypad O-Button
+  14    Button B (Right Side) (0=Pressed, 1=Released) ;aka Joypad X-Button
+  15    Not used              (All bits always 1)
+  __Halfword 2 (X)_________________________________
+  0-15  8MHz clks since HSYNC (01h=Error, or 04Dh..1CDh)
+  __Halfword 3 (Y)_________________________________
+  0-15  Scanlines since VSYNC (05h/0Ah=Error, PAL=20h..127h, NTSC=19h..F8h)
+*/
+
+#define GUNCON_IDL              0x63
+#define GUNCON_IDH              0x5a
+
+#define PSXI_SW_GUNCON_A        0x00000008
+#define PSXI_SW_GUNCON_TRIGGER  0x00002000
+#define PSXI_SW_GUNCON_B        0x00004000
+#define PSXI_AX_GUNCON_X        0x00010000
+#define PSXI_AX_GUNCON_Y        0x00020000
+#define PSXI_AX_GUNCON_SX       0x00030000
+#define PSXI_AX_GUNCON_SY       0x00040000
+
+enum {
+    GUNCON_STATE_TX_HIZ = 0,
+    GUNCON_STATE_TX_IDL,
+    GUNCON_STATE_TX_IDH,
+    GUNCON_STATE_TX_SWL,
+    GUNCON_STATE_TX_SWH,
+    GUNCON_STATE_TX_XPL,
+    GUNCON_STATE_TX_XPH,
+    GUNCON_STATE_TX_YPL,
+    GUNCON_STATE_TX_YPH
+};
+
+typedef struct {
+    int state;
+    uint16_t sw;
+    uint8_t tx_data;
+    int tx_data_ready;
+    uint16_t x;
+    uint16_t y;
+    uint16_t sx;
+    uint16_t sy;
+} psxi_guncon_t;
+
+psxi_guncon_t* psxi_guncon_create(void);
+void psxi_guncon_init(psxi_guncon_t*);
+void psxi_guncon_init_input(psxi_guncon_t*, psx_input_t*);
+void psxi_guncon_destroy(psxi_guncon_t*);
+
+#endif
\ No newline at end of file
--- a/psx/input/sda.c
+++ b/psx/input/sda.c
@@ -122,7 +122,7 @@
 }
 
 // To-do: Implement analog mode
-void psxi_sda_on_analog_change(void* udata, uint32_t axis, uint8_t data) {
+void psxi_sda_on_analog_change(void* udata, uint32_t axis, uint16_t data) {
     // Suppress warning until we implement analog mode
     psxi_sda_t* sda = (psxi_sda_t*)udata;
 
--- a/psx/psx.c
+++ b/psx/psx.c
@@ -167,7 +167,7 @@
     psx_mc1_init(psx->mc1);
     psx_mc2_init(psx->mc2);
     psx_mc3_init(psx->mc3);
-    psx_ram_init(psx->ram, psx->mc2);
+    psx_ram_init(psx->ram, psx->mc2, RAM_SIZE_2MB);
     psx_dma_init(psx->dma, psx->bus, psx->ic);
     psx_exp1_init(psx->exp1, psx->mc1, exp_path);
     psx_exp2_init(psx->exp2, atcons_tx, NULL);
--