comparison vdp.c @ 54:3b79cbcf6846

Get Flavio's color bar demo kind of sort of working
author Mike Pavone <pavone@retrodev.com>
date Tue, 18 Dec 2012 02:16:42 -0800
parents 3fc57e1a2c56
children a28b1dfe1af2
comparison
equal deleted inserted replaced
53:44e661913a51 54:3b79cbcf6846
1 #include "vdp.h" 1 #include "vdp.h"
2 #include <stdlib.h> 2 #include <stdlib.h>
3 #include <string.h> 3 #include <string.h>
4 4
5 #define MCLKS_LINE 3420
6 #define NTSC_ACTIVE 225 5 #define NTSC_ACTIVE 225
7 #define PAL_ACTIVE 241 6 #define PAL_ACTIVE 241
8 #define BUF_BIT_PRIORITY 0x40 7 #define BUF_BIT_PRIORITY 0x40
9 #define MAP_BIT_PRIORITY 0x8000 8 #define MAP_BIT_PRIORITY 0x8000
10 #define MAP_BIT_H_FLIP 0x800 9 #define MAP_BIT_H_FLIP 0x800
18 17
19 #define FLAG_DOT_OFLOW 0x1 18 #define FLAG_DOT_OFLOW 0x1
20 #define FLAG_CAN_MASK 0x2 19 #define FLAG_CAN_MASK 0x2
21 #define FLAG_MASKED 0x4 20 #define FLAG_MASKED 0x4
22 #define FLAG_WINDOW 0x8 21 #define FLAG_WINDOW 0x8
22 #define FLAG_PENDING 0x10
23 #define FLAG_UNUSED_SLOT 0x20
24
25 #define FIFO_SIZE 4
23 26
24 void init_vdp_context(vdp_context * context) 27 void init_vdp_context(vdp_context * context)
25 { 28 {
26 memset(context, 0, sizeof(context)); 29 memset(context, 0, sizeof(context));
27 context->vdpmem = malloc(VRAM_SIZE); 30 context->vdpmem = malloc(VRAM_SIZE);
28 context->framebuf = malloc(FRAMEBUF_SIZE); 31 context->framebuf = malloc(FRAMEBUF_SIZE);
32 memset(context->framebuf, 0, FRAMEBUF_SIZE);
29 context->linebuf = malloc(LINEBUF_SIZE + SCROLL_BUFFER_SIZE*2); 33 context->linebuf = malloc(LINEBUF_SIZE + SCROLL_BUFFER_SIZE*2);
34 memset(context->linebuf, 0, LINEBUF_SIZE + SCROLL_BUFFER_SIZE*2);
30 context->tmp_buf_a = context->linebuf + LINEBUF_SIZE; 35 context->tmp_buf_a = context->linebuf + LINEBUF_SIZE;
31 context->tmp_buf_b = context->tmp_buf_a + SCROLL_BUFFER_SIZE; 36 context->tmp_buf_b = context->tmp_buf_a + SCROLL_BUFFER_SIZE;
32 context->sprite_draws = MAX_DRAWS; 37 context->sprite_draws = MAX_DRAWS;
38 context->fifo_cur = malloc(sizeof(fifo_entry) * FIFO_SIZE);
39 context->fifo_end = context->fifo_cur + FIFO_SIZE;
33 } 40 }
34 41
35 void render_sprite_cells(vdp_context * context) 42 void render_sprite_cells(vdp_context * context)
36 { 43 {
37 if (context->cur_slot >= context->sprite_draws) { 44 if (context->cur_slot >= context->sprite_draws) {
166 context->flags |= FLAG_DOT_OFLOW; 173 context->flags |= FLAG_DOT_OFLOW;
167 } 174 }
168 } 175 }
169 } 176 }
170 177
178 #define VRAM_READ 0
179 #define VRAM_WRITE 1
180 #define CRAM_READ 8
181 #define CRAM_WRITE 3
182 #define VSRAM_READ 4
183 #define VSRAM_WRITE 5
184
171 void external_slot(vdp_context * context) 185 void external_slot(vdp_context * context)
172 { 186 {
173 //TODO: Implement me 187 fifo_entry * start = (context->fifo_end - FIFO_SIZE);
188 //TODO: Implement DMA
189 if (context->fifo_cur != start && start->cycle <= context->cycles) {
190 switch (context->cd & 0x7)
191 {
192 case VRAM_WRITE:
193 if (start->partial) {
194 printf("VRAM Write: %X to %X\n", start->value, context->address ^ 1);
195 context->vdpmem[context->address ^ 1] = start->value;
196 } else {
197 printf("VRAM Write: %X to %X\n", start->value >> 8, context->address);
198 context->vdpmem[context->address] = start->value >> 8;
199 start->partial = 1;
200 //skip auto-increment and removal of entry from fifo
201 return;
202 }
203 break;
204 case CRAM_WRITE:
205 printf("CRAM Write: %X to %X\n", start->value, context->address);
206 context->cram[context->address & (CRAM_SIZE-1)] = start->value;
207 break;
208 case VSRAM_WRITE:
209 if ((context->address & 63) < VSRAM_SIZE) {
210 printf("VSRAM Write: %X to %X\n", start->value, context->address);
211 context->vsram[context->address & 63] = start->value;
212 }
213 break;
214 }
215 context->address += context->regs[REG_AUTOINC];
216 fifo_entry * cur = start+1;
217 if (cur < context->fifo_cur) {
218 memmove(start, cur, sizeof(fifo_entry) * (context->fifo_cur - cur));
219 }
220 context->fifo_cur -= 1;
221 } else {
222 context->flags |= FLAG_UNUSED_SLOT;
223 }
174 } 224 }
175 225
176 #define WINDOW_RIGHT 0x80 226 #define WINDOW_RIGHT 0x80
177 #define WINDOW_DOWN 0x80 227 #define WINDOW_DOWN 0x80
178 228
211 line_offset = (((line) / 8) * 32 * 2) & 0xFFF; 261 line_offset = (((line) / 8) * 32 * 2) & 0xFFF;
212 mask = 0x3F; 262 mask = 0x3F;
213 } 263 }
214 offset = address + line_offset + (((column - 2) * 2) & mask); 264 offset = address + line_offset + (((column - 2) * 2) & mask);
215 context->col_1 = (context->vdpmem[offset] << 8) | context->vdpmem[offset+1]; 265 context->col_1 = (context->vdpmem[offset] << 8) | context->vdpmem[offset+1];
216 printf("Window | top: %d, bot: %d, left: %d, right: %d, base: %X, line: %X offset: %X, tile: %X, reg: %X\n", top_line, bottom_line, left_col, right_col, address, line_offset, offset, ((context->col_1 & 0x3FF) << 5), context->regs[REG_WINDOW]); 266 //printf("Window | top: %d, bot: %d, left: %d, right: %d, base: %X, line: %X offset: %X, tile: %X, reg: %X\n", top_line, bottom_line, left_col, right_col, address, line_offset, offset, ((context->col_1 & 0x3FF) << 5), context->regs[REG_WINDOW]);
217 offset = address + line_offset + (((column - 1) * 2) & mask); 267 offset = address + line_offset + (((column - 1) * 2) & mask);
218 context->col_2 = (context->vdpmem[offset] << 8) | context->vdpmem[offset+1]; 268 context->col_2 = (context->vdpmem[offset] << 8) | context->vdpmem[offset+1];
219 context->v_offset = (line) & 0x7; 269 context->v_offset = (line) & 0x7;
220 context->flags |= FLAG_WINDOW; 270 context->flags |= FLAG_WINDOW;
221 return; 271 return;
522 } 572 }
523 line &= mask; 573 line &= mask;
524 address += line * 4; 574 address += line * 4;
525 context->hscroll_a = context->vdpmem[address] << 8 | context->vdpmem[address+1]; 575 context->hscroll_a = context->vdpmem[address] << 8 | context->vdpmem[address+1];
526 context->hscroll_b = context->vdpmem[address+2] << 8 | context->vdpmem[address+3]; 576 context->hscroll_b = context->vdpmem[address+2] << 8 | context->vdpmem[address+3];
527 printf("%d: HScroll A: %d, HScroll B: %d\n", line, context->hscroll_a, context->hscroll_b); 577 //printf("%d: HScroll A: %d, HScroll B: %d\n", line, context->hscroll_a, context->hscroll_b);
528 break; 578 break;
529 case 36: 579 case 36:
530 //!HSYNC high 580 //!HSYNC high
531 case 37: 581 case 37:
532 case 38: 582 case 38:
741 void latch_mode(vdp_context * context) 791 void latch_mode(vdp_context * context)
742 { 792 {
743 context->latched_mode = (context->regs[REG_MODE_4] & 0x81) | (context->regs[REG_MODE_2] & BIT_PAL); 793 context->latched_mode = (context->regs[REG_MODE_4] & 0x81) | (context->regs[REG_MODE_2] & BIT_PAL);
744 } 794 }
745 795
796 #define DISPLAY_ENABLE 0x40
797
798 int is_refresh(vdp_context * context)
799 {
800 uint32_t linecyc = context->cycles % MCLKS_LINE;
801 if (context->latched_mode & BIT_H40) {
802 linecyc = linecyc/16;
803 return (linecyc == 73 || linecyc == 105 || linecyc == 137 || linecyc == 169 || linecyc == 201);
804 } else {
805 linecyc = linecyc/20;
806 return (linecyc == 66 || linecyc == 98 || linecyc == 130 || linecyc == 162);
807 }
808 }
809
810 void check_render_bg(vdp_context * context, int32_t line)
811 {
812 if (line > 0) {
813 line -= 1;
814 uint16_t * start = NULL, *end = NULL;
815 uint32_t linecyc = (context->cycles % MCLKS_LINE);
816 if (context->latched_mode & BIT_H40) {
817 linecyc /= 16;
818 if (linecyc >= 55 && linecyc <= 207 && !((linecyc-55) % 8)) {
819 uint32_t x = ((linecyc-55)&(~0xF))*2;
820 start = context->framebuf + line * 320 + x;
821 end = start + 16;
822 }
823 } else {
824 linecyc /= 20;
825 if (linecyc >= 48 && linecyc <= 168 && !((linecyc-48) % 8)) {
826 uint32_t x = ((linecyc-48)&(~0xF))*2;
827 start = context->framebuf + line * 256 + x;
828 end = start + 16;
829 }
830 }
831 while (start != end) {
832 *start = context->regs[REG_BG_COLOR] & 0x3F;
833 ++start;
834 }
835 }
836 }
837
746 void vdp_run_context(vdp_context * context, uint32_t target_cycles) 838 void vdp_run_context(vdp_context * context, uint32_t target_cycles)
747 { 839 {
748 while(context->cycles < target_cycles) 840 while(context->cycles < target_cycles)
749 { 841 {
750 uint32_t line = context->cycles / MCLKS_LINE; 842 uint32_t line = context->cycles / MCLKS_LINE;
751 uint32_t active_lines = context->latched_mode & BIT_PAL ? PAL_ACTIVE : NTSC_ACTIVE; 843 uint32_t active_lines = context->latched_mode & BIT_PAL ? PAL_ACTIVE : NTSC_ACTIVE;
752 if (line < active_lines) { 844 if (!line) {
753 if (!line) { 845 latch_mode(context);
754 latch_mode(context); 846 }
755 } 847 if (line < active_lines && context->regs[REG_MODE_2] & DISPLAY_ENABLE) {
756 //first sort-of active line is treated as 255 internally 848 //first sort-of active line is treated as 255 internally
757 //it's used for gathering sprite info for line 849 //it's used for gathering sprite info for line
758 line = (line - 1) & 0xFF; 850 line = (line - 1) & 0xFF;
759 uint32_t linecyc = context->cycles % MCLKS_LINE; 851 uint32_t linecyc = context->cycles % MCLKS_LINE;
760 852
761 //Convert to slot number 853 //Convert to slot number
762 if (context->latched_mode & BIT_H40){ 854 if (context->latched_mode & BIT_H40){
763 //TODO: Deal with nasty clock switching during HBLANK 855 //TODO: Deal with nasty clock switching during HBLANK
764 linecyc = linecyc/16; 856 linecyc = linecyc/16;
857 vdp_h40(line, linecyc, context);
765 context->cycles += 16; 858 context->cycles += 16;
766 vdp_h40(line, linecyc, context);
767 } else { 859 } else {
768 linecyc = linecyc/20; 860 linecyc = linecyc/20;
861 vdp_h32(line, linecyc, context);
769 context->cycles += 20; 862 context->cycles += 20;
770 vdp_h32(line, linecyc, context);
771 } 863 }
772 } else { 864 } else {
773 //TODO: Empty FIFO 865 if (!is_refresh(context)) {
866 external_slot(context);
867 }
868 if (line < active_lines) {
869 check_render_bg(context, line);
870 }
871 if (context->latched_mode & BIT_H40){
872 //TODO: Deal with nasty clock switching during HBLANK
873 context->cycles += 16;
874 } else {
875 context->cycles += 20;
876 }
774 } 877 }
775 } 878 }
776 } 879 }
777 880
778 uint32_t vdp_run_to_vblank(vdp_context * context) 881 uint32_t vdp_run_to_vblank(vdp_context * context)
779 { 882 {
780 uint32_t target_cycles = ((context->latched_mode & BIT_PAL) ? PAL_ACTIVE : NTSC_ACTIVE) * MCLKS_LINE; 883 uint32_t target_cycles = ((context->latched_mode & BIT_PAL) ? PAL_ACTIVE : NTSC_ACTIVE) * MCLKS_LINE;
781 vdp_run_context(context, target_cycles); 884 vdp_run_context(context, target_cycles);
782 return context->cycles; 885 return context->cycles;
886 }
887
888 void vdp_control_port_write(vdp_context * context, uint16_t value)
889 {
890 printf("control port write: %X\n", value);
891 if (context->flags & FLAG_PENDING) {
892 context->address = (context->address & 0x3FFF) | (value << 14);
893 context->cd = (context->cd & 0x3) | ((value >> 2) & 0x3C);
894 context->flags &= ~FLAG_PENDING;
895 } else {
896 if ((value & 0xC000) == 0x8000) {
897 //Register write
898 uint8_t reg = (value >> 8) & 0x1F;
899 if (reg < VDP_REGS) {
900 printf("register %d set to %X\n", reg, value);
901 context->regs[reg] = value;
902 }
903 } else {
904 context->flags |= FLAG_PENDING;
905 context->address = (context->address &0xC000) | (value & 0x3FFF);
906 context->cd = (context->cd &0x3C) | (value >> 14);
907 }
908 }
909 }
910
911 void vdp_data_port_write(vdp_context * context, uint16_t value)
912 {
913 printf("data port write: %X\n", value);
914 context->flags &= ~FLAG_PENDING;
915 if (context->fifo_cur == context->fifo_end) {
916 printf("FIFO full, waiting for space before next write at cycle %X\n", context->cycles);
917 }
918 while (context->fifo_cur == context->fifo_end) {
919 vdp_run_context(context, context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20));
920 }
921 context->fifo_cur->cycle = context->cycles;
922 context->fifo_cur->value = value;
923 context->fifo_cur->partial = 0;
924 context->fifo_cur++;
925 }
926
927 uint16_t vdp_control_port_read(vdp_context * context)
928 {
929 context->flags &= ~FLAG_PENDING;
930 uint16_t value = 0x3400;
931 if (context->fifo_cur == (context->fifo_end - FIFO_SIZE)) {
932 value |= 0x200;
933 }
934 if (context->fifo_cur == context->fifo_end) {
935 value |= 0x100;
936 }
937 //TODO: Lots of other bits in status port
938 return value;
939 }
940
941 uint16_t vdp_data_port_read(vdp_context * context)
942 {
943 context->flags &= ~FLAG_PENDING;
944 if (!(context->cd & 1)) {
945 return 0;
946 }
947 //Not sure if the FIFO should be drained before processing a read or not, but it would make sense
948 context->flags &= ~FLAG_UNUSED_SLOT;
949 while (!(context->flags & FLAG_UNUSED_SLOT)) {
950 vdp_run_context(context, context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20));
951 }
952 uint16_t value = 0;
953 switch (context->cd & 0x7)
954 {
955 case VRAM_READ:
956 value = context->vdpmem[context->address] << 8;
957 context->flags &= ~FLAG_UNUSED_SLOT;
958 while (!(context->flags & FLAG_UNUSED_SLOT)) {
959 vdp_run_context(context, context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20));
960 }
961 value |= context->vdpmem[context->address ^ 1];
962 break;
963 case CRAM_READ:
964 value = context->cram[(context->address/2) & (CRAM_SIZE-1)];
965 break;
966 case VSRAM_READ:
967 if (((context->address / 2) & 63) < VSRAM_SIZE) {
968 value = context->vsram[context->address & 63];
969 }
970 break;
971 }
972 context->address += context->regs[REG_AUTOINC];
973 return value;
783 } 974 }
784 975
785 #define GST_VDP_REGS 0xFA 976 #define GST_VDP_REGS 0xFA
786 #define GST_VDP_MEM 0x12478 977 #define GST_VDP_MEM 0x12478
787 978
800 context->vsram[i] = (tmp_buf[i*2+1] << 8) | tmp_buf[i*2]; 991 context->vsram[i] = (tmp_buf[i*2+1] << 8) | tmp_buf[i*2];
801 } 992 }
802 fseek(state_file, GST_VDP_MEM, SEEK_SET); 993 fseek(state_file, GST_VDP_MEM, SEEK_SET);
803 fread(context->vdpmem, 1, VRAM_SIZE, state_file); 994 fread(context->vdpmem, 1, VRAM_SIZE, state_file);
804 } 995 }
996