ultimecia

A ps1 emulator in c
Log | Files | Refs

gpu.c (12798B)


      1 #include <stdlib.h>
      2 #include <stdio.h>
      3 #include <string.h>
      4 #include "gpu.h"
      5 #include "types.h"
      6 #include "sr.h"
      7 #include "log.h"
      8 
      9 GPU*
     10 GPU_new(void)
     11 {
     12     GPU* gpu;
     13 
     14     gpu = (GPU*)malloc(sizeof(GPU));
     15     memset(gpu, 0, sizeof(GPU));
     16     gpu->display_disabled = 1;
     17     gpu->ren = REN_new();
     18 
     19     return gpu;
     20 }
     21 
     22 u32
     23 GPU_read(GPU* gpu)
     24 {
     25     (void)gpu;
     26     return 0;
     27 }
     28 
     29 u32
     30 GPU_status(GPU* gpu)
     31 {
     32     u32 r;
     33     u32 dma_req;
     34 
     35     dma_req = 0;
     36     r = 0;
     37 
     38     r |= ((u32)gpu->page_base_x) << 0;
     39     r |= ((u32)gpu->page_base_y) << 4;
     40     r |= ((u32)gpu->semi_transparency) << 5;
     41     r |= ((u32)gpu->texture_depth) << 7;
     42     r |= ((u32)gpu->dithering) << 9;
     43     r |= ((u32)gpu->draw_to_display) << 10;
     44     r |= ((u32)gpu->force_set_mask_bit) << 11;
     45     r |= ((u32)gpu->preserve_masked_pixels) << 12;
     46     r |= ((u32)gpu->field) << 13;
     47     /* Bit 14 not supported */
     48     r |= ((u32)gpu->texture_disable) << 15;
     49     r |= HOR_RES_into_status(gpu->hres);
     50     /* If we don't emulate bit 31 correctly setting vres to 1
     51        locks the BIOS. 4.17 on the guide.
     52        */
     53     /*r |= ((u32)gpu->vres) << 19;*/
     54     r |= ((u32)gpu->vmode) << 20;
     55     r |= ((u32)gpu->display_depth) << 21;
     56     r |= ((u32)gpu->interlaced) << 22;
     57     r |= ((u32)gpu->display_disabled) << 23;
     58     r |= ((u32)gpu->interrupt) << 24;
     59 
     60     /* Always GPU is ready */
     61     r |= 1 << 26;
     62     r |= 1 << 27;
     63     r |= 1 << 28;
     64 
     65     r |= ((u32) gpu->dma_direction) << 29;
     66 
     67     /* Ignore odd lines for now */
     68     r |= 0 << 31;
     69 
     70     switch(gpu->dma_direction)
     71     {
     72         case DMA_DIR_OFF: dma_req = 0; break;
     73         case DMA_DIR_FIFO: dma_req = 1; break;
     74         case DMA_DIR_CPUTOGP0: dma_req = (r >> 28) & 1; break;
     75         case DMA_DIR_VRAMTOCPU: dma_req = (r >> 27) & 1; break;
     76         default: log_fatal("UNREACHABLE"); exit(1);
     77     }
     78 
     79     r |= dma_req << 25;
     80 
     81     return r;
     82 }
     83 
     84 void
     85 GPU_gp1(GPU* gpu, u32 val)
     86 {
     87 
     88     u32 opcode;
     89 
     90     opcode = (val >> 24) & 0xff;
     91 
     92     switch (opcode)
     93     {
     94         case 0x00: GPU_gp1_reset(gpu, val); break;
     95         case 0x01: GPU_gp1_reset_command_buffer(gpu, val); break;
     96         case 0x02: GPU_gp1_acknowledge_irq(gpu, val); break;
     97         case 0x03: GPU_gp1_display_enable(gpu, val); break;
     98         case 0x04: GPU_gp1_dma_direction(gpu, val); break;
     99         case 0x05: GPU_gp1_display_vram_start(gpu, val); break;
    100         case 0x06: GPU_gp1_display_horizontal_range(gpu, val); break;
    101         case 0x07: GPU_gp1_display_vertical_range(gpu, val); break;
    102         case 0x08: GPU_gp1_display_mode(gpu, val); break;
    103         default: log_fatal("OP: %08X: Unhandled GP1 command %08X", opcode, val); exit(1);
    104     }
    105 
    106 }
    107 
    108 void
    109 GPU_gp1_reset(GPU* gpu, u32 val)
    110 {
    111     (void)val;
    112     REN* tmp = gpu->ren;
    113     memset(gpu, 0, sizeof(GPU));
    114     /* Fill out the rest of the fields with no$cash spec reset values*/
    115     gpu->display_disabled = 1;
    116     gpu->interlaced = 1;
    117     gpu->display_horiz_start = 0x200;
    118     gpu->display_horiz_end = 0xc00;
    119     gpu->display_line_start = 0x10;
    120     gpu->display_line_end = 0x100;
    121     gpu->ren = tmp;
    122 }
    123 
    124 void
    125 GPU_gp1_display_enable(GPU* gpu, u32 val)
    126 {
    127     gpu->display_disabled = (val & 1) != 0;
    128 }
    129 
    130 void
    131 GPU_gp1_acknowledge_irq(GPU* gpu, u32 val)
    132 {
    133     (void)val;
    134     gpu->interrupt = 0;
    135 }
    136 
    137 void
    138 GPU_gp1_reset_command_buffer(GPU* gpu, u32 val)
    139 {
    140     (void)val;
    141     GPU_CMD_BUFFER_clear(&gpu->gp0_command);
    142     gpu->gp0_words_remaining = 0;
    143     gpu->gp0_mode = GP0_MODE_COMMAND;
    144 
    145     /* XXX Should also clear the command FIFO when we implement it*/
    146 }
    147 
    148     void
    149 GPU_gp1_display_horizontal_range(GPU* gpu, u32 val)
    150 {
    151     gpu->display_horiz_start = (u16)(val & 0xfff);
    152     gpu->display_horiz_end = (u16)((val >> 12) & 0xfff);
    153 }
    154 
    155     void
    156 GPU_gp1_display_vertical_range(GPU* gpu, u32 val)
    157 {
    158     gpu->display_line_start = (u16)(val & 0x3ff);
    159     gpu->display_line_end = (u16)((val >> 10) & 0x3ff);
    160 }
    161 
    162     void
    163 GPU_gp1_display_vram_start(GPU* gpu, u32 val)
    164 {
    165     gpu->display_vram_x_start = (u16)(val & 0x3fe);
    166     gpu->display_vram_y_start = (u16)((val >> 10) & 0x1ff);
    167 }
    168 
    169     void
    170 GPU_gp1_dma_direction(GPU* gpu, u32 val)
    171 {
    172     switch (val & 3) {
    173         case 0: gpu->dma_direction = DMA_DIR_OFF; break;
    174         case 1: gpu->dma_direction = DMA_DIR_FIFO; break;
    175         case 2: gpu->dma_direction = DMA_DIR_CPUTOGP0; break;
    176         case 3: gpu->dma_direction = DMA_DIR_VRAMTOCPU; break;
    177         default: log_fatal("UNREACHABLE"); exit(EXIT_FAILURE);
    178     }
    179 }
    180 
    181     void
    182 GPU_gp1_display_mode(GPU* gpu, u32 val)
    183 {
    184     u8 hr1, hr2;
    185 
    186     hr1 = (u8)(val & 3);
    187     hr2 = (u8)((val >> 6) & 1);
    188 
    189     gpu->hres = HOR_RES_from_fields(hr1, hr2);
    190     gpu->vres = (val & 0x4) != 0;
    191     gpu->vmode = (val & 0x8) != 0 ? VMODE_NTSC : VMODE_PAL;
    192     gpu->display_depth = (val & 0x10) != 0 ? DISP_DEPTH_D24BITS : DISP_DEPTH_D15BITS;
    193     gpu->interlaced = (val & 0x20) != 0;
    194 
    195     if (val & 0x80)
    196         log_fatal("Unsupported display mode %08X", val), exit(EXIT_FAILURE);
    197 }
    198 
    199     void
    200 GPU_gp0(GPU* gpu, u32 val)
    201 {
    202     if (gpu->gp0_words_remaining == 0) {
    203         u32 opcode, len;
    204         void (*method)(GPU*);
    205 
    206         opcode = (val >> 24) & 0xff;
    207 
    208         switch (opcode) {
    209             case 0x00: len = 1; method = GPU_gp0_nop;  break;
    210             case 0x01: len = 1; method = GPU_gp0_clear_cache;  break;
    211             case 0xa0: len = 3; method = GPU_gp0_image_load;  break;
    212             case 0xe1: len = 1; method = GPU_gp0_draw_mode; break;
    213             case 0xe2: len = 1; method = GPU_gp0_texture_window; break;
    214             case 0xe3: len = 1; method = GPU_gp0_drawing_area_top_left; break;
    215             case 0xe4: len = 1; method = GPU_gp0_drawing_area_bottom_right; break;
    216             case 0xe5: len = 1; method = GPU_gp0_drawing_offset; break;
    217             case 0xe6: len = 1; method = GPU_gp0_mask_bit_setting; break;
    218             case 0x28: len = 5; method = GPU_gp0_quad_mono_opaque; break;
    219             case 0x2c: len = 9; method = GPU_gp0_quad_texture_blend_opaque; break;
    220             case 0x30: len = 6; method = GPU_gp0_triangle_shaded_opaque; break;
    221             case 0x38: len = 8; method = GPU_gp0_quad_shaded_opaque; break;
    222             case 0xc0: len = 3; method = GPU_gp0_image_store; break;
    223             default: log_fatal("OP: %08X: Unhandled GP0 command %08X", opcode, val); exit(1);
    224         }
    225 
    226         gpu->gp0_words_remaining = len;
    227         gpu->gp0_command_method = method;
    228         GPU_CMD_BUFFER_clear(&gpu->gp0_command);
    229     }
    230 
    231     gpu->gp0_words_remaining -= 1;
    232 
    233     switch (gpu->gp0_mode) {
    234         case GP0_MODE_COMMAND:
    235             GPU_CMD_BUFFER_push_word(&gpu->gp0_command, val);
    236             if (gpu->gp0_words_remaining == 0)
    237                 gpu->gp0_command_method(gpu);
    238             break;
    239         case GP0_MODE_IMAGE_LOAD:
    240             if (gpu->gp0_words_remaining == 0)
    241                 gpu->gp0_mode = GP0_MODE_COMMAND;
    242             break;
    243     }
    244 }
    245 
    246 void GPU_gp0_nop(GPU* gpu) {(void)gpu;}
    247 
    248 void GPU_gp0_clear_cache(GPU* gpu) {(void)gpu;}
    249 
    250     void
    251 GPU_gp0_image_load(GPU* gpu)
    252 {
    253     u32 res, width, height, imgsize;
    254 
    255     res = gpu->gp0_command.buffer[2];
    256     width = res & 0xffff;
    257     height = res >> 16;
    258     imgsize = width * height;
    259     imgsize = (imgsize + 1) & ~1;
    260     gpu->gp0_words_remaining = imgsize / 2;
    261     gpu->gp0_mode = GP0_MODE_IMAGE_LOAD;
    262 }
    263 
    264     void
    265 GPU_gp0_draw_mode(GPU* gpu)
    266 {
    267     u32 val;
    268     val = gpu->gp0_command.buffer[0];
    269 
    270     gpu->page_base_x = (u8)(val & 0xf);
    271     gpu->page_base_y = (u8)((val >> 4) & 1);
    272     gpu->semi_transparency = (u8)((val >> 5) & 3);
    273 
    274     switch ((val >> 7) & 3)
    275     {
    276         case 0: gpu->texture_depth = TD_T4BIT; break;
    277         case 1: gpu->texture_depth = TD_T8BIT; break;
    278         case 2: gpu->texture_depth = TD_T15BIT; break;
    279         default: log_fatal("Unhandled texture depth %d", (val >> 7) & 3); exit(1);
    280     }
    281 
    282     gpu->dithering = ((val >> 9) & 1);
    283     gpu->draw_to_display = ((val >> 10) & 1);
    284     gpu->texture_disable = ((val >> 11) & 1);
    285     gpu->rectangle_texture_x_flip = ((val >> 12) & 1);
    286     gpu->rectangle_texture_y_flip = ((val >> 13) & 1);
    287 }
    288 
    289     void
    290 GPU_gp0_drawing_area_top_left(GPU* gpu)
    291 {
    292     u32 val;
    293     val = gpu->gp0_command.buffer[0];
    294     gpu->drawing_area_top = (u16)((val >> 10) & 0x3ff);
    295     gpu->drawing_area_left = (u16)(val & 0x3ff);
    296 }
    297 
    298     void
    299 GPU_gp0_drawing_area_bottom_right(GPU* gpu)
    300 {
    301     u32 val;
    302     val = gpu->gp0_command.buffer[0];
    303 
    304     gpu->drawing_area_bottom = (u16)((val >> 10) & 0x3ff);
    305     gpu->drawing_area_right = (u16)(val & 0x3ff);
    306 }
    307 
    308     void
    309 GPU_gp0_drawing_offset(GPU* gpu)
    310 {
    311     u16 x, y;
    312     u32 val;
    313 
    314     val = gpu->gp0_command.buffer[0];
    315     x = (u16)(val & 0x7ff);
    316     y = (u16)((val >> 11) & 0x7ff);
    317 
    318     /* Values here are 11bit 2s complement signed values, we need to force sign extension */
    319     gpu->drawing_x_offset = ((i16)(x << 5)) >> 5;
    320     gpu->drawing_y_offset = ((i16)(y << 5)) >> 5;
    321 
    322     /*
    323        This is a hack because I haven't implemented gpu timings yet.
    324        But gp0_drawing_offset is called every frame apparently.
    325        */
    326     REN_display(gpu->ren);
    327 
    328 }
    329 
    330     void
    331 GPU_gp0_texture_window(GPU* gpu)
    332 {
    333     u32 val;
    334     val = gpu->gp0_command.buffer[0];
    335 
    336     gpu->texture_window_x_mask   = (u8)(val & 0x1f);
    337     gpu->texture_window_y_mask   = (u8)((val >> 5) & 0x1f);
    338     gpu->texture_window_x_offset = (u8)((val >> 10) & 0x1f);
    339     gpu->texture_window_y_offset = (u8)((val >> 15) & 0x1f);
    340 }
    341 
    342     void
    343 GPU_gp0_mask_bit_setting(GPU* gpu)
    344 {
    345     u32 val;
    346     val = gpu->gp0_command.buffer[0];
    347 
    348     gpu->force_set_mask_bit = (val & 1) != 0;
    349     gpu->preserve_masked_pixels = (val & 2) != 0;
    350 }
    351 
    352     void
    353 GPU_gp0_quad_mono_opaque(GPU* gpu)
    354 {
    355     ivec2 positions[4];
    356     C colors[4];
    357 
    358     positions[0] = POSITION_from_gp0(gpu->gp0_command.buffer[1]);
    359     positions[1] = POSITION_from_gp0(gpu->gp0_command.buffer[2]);
    360     positions[2] = POSITION_from_gp0(gpu->gp0_command.buffer[3]);
    361     positions[3] = POSITION_from_gp0(gpu->gp0_command.buffer[4]);
    362 
    363     colors[0] = colors[1] = colors[2] = colors[3] = COLOR_from_gp0(gpu->gp0_command.buffer[0]);
    364 
    365     /*GPU_LOG("Draw quad mono opaque at x: %d, y: %d\n", positions[0].x, positions[0].y, NULL);*/
    366     /*GPU_LOG("Draw quad mono opaque with R: %d, G: %d, B: %d\n", colors[0].r, colors[0].g, colors[0].b, NULL);*/
    367 
    368     REN_push_quad(gpu->ren, positions, colors);
    369 }
    370 
    371     void
    372 GPU_gp0_triangle_shaded_opaque(GPU* gpu)
    373 {
    374     ivec2 positions[3];
    375     C colors[3];
    376 
    377     positions[0] = POSITION_from_gp0(gpu->gp0_command.buffer[1]);
    378     positions[1] = POSITION_from_gp0(gpu->gp0_command.buffer[3]);
    379     positions[2] = POSITION_from_gp0(gpu->gp0_command.buffer[5]);
    380 
    381     colors[0] = COLOR_from_gp0(gpu->gp0_command.buffer[0]);
    382     colors[1] = COLOR_from_gp0(gpu->gp0_command.buffer[2]);
    383     colors[2] = COLOR_from_gp0(gpu->gp0_command.buffer[4]);
    384 
    385     REN_push_triangle(gpu->ren, positions, colors);
    386 
    387     /*GPU_LOG("Draw triangle shaded at x: %d, y: %d\n", positions[0].x, positions[0].y, NULL);*/
    388 }
    389 
    390     void
    391 GPU_gp0_quad_texture_blend_opaque(GPU* gpu)
    392 {
    393     ivec2 positions[4];
    394     C colors[4];
    395     C tmp = {0x80, 0x00, 0x00};
    396 
    397 
    398     positions[0] = POSITION_from_gp0(gpu->gp0_command.buffer[1]);
    399     positions[1] = POSITION_from_gp0(gpu->gp0_command.buffer[3]);
    400     positions[2] = POSITION_from_gp0(gpu->gp0_command.buffer[5]);
    401     positions[3] = POSITION_from_gp0(gpu->gp0_command.buffer[7]);
    402 
    403     colors[0] = colors[1] = colors[2] = colors[3] = tmp;
    404 
    405     REN_push_quad(gpu->ren, positions, colors);
    406 }
    407 
    408     void
    409 GPU_gp0_quad_shaded_opaque(GPU* gpu)
    410 {
    411     ivec2 positions[4];
    412     C colors[4];
    413 
    414     positions[0] = POSITION_from_gp0(gpu->gp0_command.buffer[1]);
    415     positions[1] = POSITION_from_gp0(gpu->gp0_command.buffer[3]);
    416     positions[2] = POSITION_from_gp0(gpu->gp0_command.buffer[5]);
    417     positions[3] = POSITION_from_gp0(gpu->gp0_command.buffer[7]);
    418 
    419     colors[0] = COLOR_from_gp0(gpu->gp0_command.buffer[0]);
    420     colors[1] = COLOR_from_gp0(gpu->gp0_command.buffer[2]);
    421     colors[2] = COLOR_from_gp0(gpu->gp0_command.buffer[4]);
    422     colors[3] = COLOR_from_gp0(gpu->gp0_command.buffer[6]);
    423 
    424     /*GPU_LOG("I GOT CALLED\n", NULL);*/
    425     REN_push_quad(gpu->ren, positions, colors);
    426 }
    427 
    428     void
    429 GPU_gp0_image_store(GPU* gpu)
    430 {
    431     u32 res, width, height;
    432 
    433     res = gpu->gp0_command.buffer[2];
    434     width = res & 0xffff;
    435     height = res >> 16;
    436 
    437     log_debug("Unhandled image store: %dx%d", width, height);
    438 }
    439 
    440     GPU_CMD_BUFFER*
    441 GPU_CMD_BUFFER_new(void)
    442 {
    443     GPU_CMD_BUFFER* buffer;
    444 
    445     buffer = (GPU_CMD_BUFFER*)malloc(sizeof(GPU_CMD_BUFFER));
    446     memset(buffer, 0, sizeof(GPU_CMD_BUFFER));
    447 
    448     return buffer;
    449 }
    450 
    451     void
    452 GPU_CMD_BUFFER_clear(GPU_CMD_BUFFER* cmd_buffer)
    453 {
    454     cmd_buffer->len = 0;
    455     memset(cmd_buffer->buffer, 413, 12 * sizeof(u32));
    456 }
    457 
    458     void
    459 GPU_CMD_BUFFER_push_word(GPU_CMD_BUFFER* cmd_buffer, u32 word)
    460 {
    461     if (cmd_buffer->len > 12)
    462         log_fatal("OUT OF BOUNDS GPU_CMD_BUFFER"), exit(EXIT_FAILURE);
    463 
    464     cmd_buffer->buffer[cmd_buffer->len] = word;
    465     cmd_buffer->len++;
    466 }