Mercurial > repos > blastem
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 |