Mercurial > repos > blastem
comparison sms.c @ 1117:928a65750345
Initial support for Genesis/Megadrive PBC mode. VDP still needs Mode 4 to be useful.
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Thu, 22 Dec 2016 19:51:25 -0800 |
parents | |
children | 55ea7f9a4e92 |
comparison
equal
deleted
inserted
replaced
1116:fe8c79f82c22 | 1117:928a65750345 |
---|---|
1 #include <string.h> | |
2 #include <stdlib.h> | |
3 #include <stddef.h> | |
4 #include "sms.h" | |
5 #include "blastem.h" | |
6 #include "render.h" | |
7 #include "util.h" | |
8 | |
9 static void *memory_io_write(uint32_t location, void *vcontext, uint8_t value) | |
10 { | |
11 z80_context *z80 = vcontext; | |
12 sms_context *sms = z80->system; | |
13 if (location & 1) { | |
14 sms->io.ports[0].control = ~(value << 5 & 0x60); | |
15 sms->io.ports[1].control = ~(value << 3 & 0x60); | |
16 io_data_write(sms->io.ports, value << 1, z80->current_cycle); | |
17 io_data_write(sms->io.ports + 1, value >> 1, z80->current_cycle); | |
18 } else { | |
19 //TODO: memory control write | |
20 } | |
21 return vcontext; | |
22 } | |
23 | |
24 static uint8_t hv_read(uint32_t location, void *vcontext) | |
25 { | |
26 z80_context *z80 = vcontext; | |
27 sms_context *sms = z80->system; | |
28 vdp_run_context(sms->vdp, z80->current_cycle); | |
29 uint16_t hv = vdp_hv_counter_read(sms->vdp); | |
30 if (location & 1) { | |
31 return hv; | |
32 } else { | |
33 return hv >> 8; | |
34 } | |
35 } | |
36 | |
37 static void *sms_psg_write(uint32_t location, void *vcontext, uint8_t value) | |
38 { | |
39 z80_context *z80 = vcontext; | |
40 sms_context *sms = z80->system; | |
41 psg_run(sms->psg, z80->current_cycle); | |
42 psg_write(sms->psg, value); | |
43 return vcontext; | |
44 } | |
45 | |
46 static void update_interrupts(sms_context *sms) | |
47 { | |
48 uint32_t vint = vdp_next_vint(sms->vdp); | |
49 uint32_t hint = vdp_next_hint(sms->vdp); | |
50 sms->z80->int_pulse_start = vint < hint ? vint : hint; | |
51 } | |
52 | |
53 static uint8_t vdp_read(uint32_t location, void *vcontext) | |
54 { | |
55 z80_context *z80 = vcontext; | |
56 sms_context *sms = z80->system; | |
57 vdp_run_context(sms->vdp, z80->current_cycle); | |
58 if (location & 1) { | |
59 sms->vdp->flags &= ~(FLAG2_VINT_PENDING|FLAG2_HINT_PENDING); | |
60 update_interrupts(sms); | |
61 return vdp_control_port_read(sms->vdp); | |
62 } else { | |
63 return vdp_data_port_read(sms->vdp); | |
64 } | |
65 } | |
66 | |
67 static void *vdp_write(uint32_t location, void *vcontext, uint8_t value) | |
68 { | |
69 z80_context *z80 = vcontext; | |
70 sms_context *sms = z80->system; | |
71 vdp_run_context(sms->vdp, z80->current_cycle); | |
72 if (location & 1) { | |
73 vdp_control_port_write_pbc(sms->vdp, value); | |
74 update_interrupts(sms); | |
75 } else { | |
76 vdp_data_port_write_pbc(sms->vdp, value); | |
77 } | |
78 return vcontext; | |
79 } | |
80 | |
81 static uint8_t io_read(uint32_t location, void *vcontext) | |
82 { | |
83 z80_context *z80 = vcontext; | |
84 sms_context *sms = z80->system; | |
85 if (location == 0xC0 || location == 0xDC) { | |
86 uint8_t port_a = io_data_read(sms->io.ports, z80->current_cycle); | |
87 uint8_t port_b = io_data_read(sms->io.ports, z80->current_cycle); | |
88 return (port_a & 0x3F) | (port_b << 6); | |
89 } | |
90 if (location == 0xC1 || location == 0xDD) { | |
91 uint8_t port_a = io_data_read(sms->io.ports, z80->current_cycle); | |
92 uint8_t port_b = io_data_read(sms->io.ports, z80->current_cycle); | |
93 return (port_a & 0x40) | (port_b >> 2 & 0xF) | (port_b << 1 & 0x80) | 0x10; | |
94 } | |
95 return 0xFF; | |
96 } | |
97 | |
98 static memmap_chunk io_map[] = { | |
99 {0x00, 0x40, 0xFF, 0, 0, 0, NULL, NULL, NULL, NULL, memory_io_write}, | |
100 {0x40, 0x80, 0xFF, 0, 0, 0, NULL, NULL, NULL, hv_read, sms_psg_write}, | |
101 {0x80, 0xC0, 0xFF, 0, 0, 0, NULL, NULL, NULL, vdp_read, vdp_write}, | |
102 {0xC0, 0x100,0xFF, 0, 0, 0, NULL, NULL, NULL, io_read, NULL} | |
103 }; | |
104 | |
105 static void set_speed_percent(system_header * system, uint32_t percent) | |
106 { | |
107 sms_context *context = (sms_context *)system; | |
108 uint32_t old_clock = context->master_clock; | |
109 context->master_clock = ((uint64_t)context->normal_clock * (uint64_t)percent) / 100; | |
110 | |
111 psg_adjust_master_clock(context->psg, context->master_clock); | |
112 } | |
113 | |
114 static void run_sms(system_header *system) | |
115 { | |
116 render_disable_ym(); | |
117 sms_context *sms = (sms_context *)system; | |
118 uint32_t target_cycle = sms->z80->current_cycle + 3420*262; | |
119 while (!sms->should_return) | |
120 { | |
121 z80_run(sms->z80, target_cycle); | |
122 target_cycle = sms->z80->current_cycle; | |
123 vdp_run_context(sms->vdp, target_cycle); | |
124 psg_run(sms->psg, target_cycle); | |
125 target_cycle += 3420*262; | |
126 if (target_cycle > 0x10000000) { | |
127 uint32_t adjust = sms->z80->current_cycle - 3420*262*2; | |
128 io_adjust_cycles(sms->io.ports, sms->z80->current_cycle, adjust); | |
129 io_adjust_cycles(sms->io.ports+1, sms->z80->current_cycle, adjust); | |
130 z80_adjust_cycles(sms->z80, adjust); | |
131 vdp_adjust_cycles(sms->vdp, adjust); | |
132 sms->psg->cycles -= adjust; | |
133 target_cycle -= adjust; | |
134 } | |
135 } | |
136 sms->should_return = 0; | |
137 render_enable_ym(); | |
138 } | |
139 | |
140 static void start_sms(system_header *system, char *statefile) | |
141 { | |
142 sms_context *sms = (sms_context *)system; | |
143 set_keybindings(&sms->io); | |
144 | |
145 z80_assert_reset(sms->z80, 0); | |
146 z80_clear_reset(sms->z80, 128*15); | |
147 | |
148 run_sms(system); | |
149 } | |
150 | |
151 static void free_sms(system_header *system) | |
152 { | |
153 sms_context *sms = (sms_context *)system; | |
154 vdp_free(sms->vdp); | |
155 z80_options_free(sms->z80->options); | |
156 free(sms->z80); | |
157 psg_free(sms->psg); | |
158 free(sms); | |
159 } | |
160 | |
161 static uint16_t get_open_bus_value(system_header *system) | |
162 { | |
163 return 0xFFFF; | |
164 } | |
165 | |
166 static void request_exit(system_header *system) | |
167 { | |
168 sms_context *sms = (sms_context *)system; | |
169 sms->should_return = 1; | |
170 } | |
171 | |
172 sms_context *alloc_configure_sms(void *rom, uint32_t rom_size, void *extra_rom, uint32_t extra_rom_size, uint32_t opts, uint8_t force_region, rom_info *info_out) | |
173 { | |
174 memset(info_out, 0, sizeof(*info_out)); | |
175 sms_context *sms = calloc(1, sizeof(sms_context)); | |
176 rom_size = nearest_pow2(rom_size); | |
177 uint32_t mask = rom_size >= 0xC000 ? 0xFFFF : rom_size-1; | |
178 memmap_chunk memory_map[] = { | |
179 {0x0000, 0xC000, rom_size-1, 0, 0, MMAP_READ, rom, NULL, NULL, NULL, NULL}, | |
180 {0xC000, 0x10000, sizeof(sms->ram)-1, 0, 0, MMAP_READ|MMAP_WRITE|MMAP_CODE, sms->ram, NULL, NULL, NULL, NULL} | |
181 }; | |
182 info_out->map = malloc(sizeof(memory_map)); | |
183 memcpy(info_out->map, memory_map, sizeof(memory_map)); | |
184 z80_options *zopts = malloc(sizeof(z80_options)); | |
185 init_z80_opts(zopts, info_out->map, 2, io_map, 4, 15, 0xFF); | |
186 sms->z80 = malloc(sizeof(z80_context)); | |
187 init_z80_context(sms->z80, zopts); | |
188 sms->z80->system = sms; | |
189 | |
190 char * lowpass_cutoff_str = tern_find_path(config, "audio\0lowpass_cutoff\0").ptrval; | |
191 uint32_t lowpass_cutoff = lowpass_cutoff_str ? atoi(lowpass_cutoff_str) : 3390; | |
192 | |
193 //TODO: Detect region and pick master clock based off of that | |
194 sms->normal_clock = sms->master_clock = 53693175; | |
195 | |
196 sms->psg = malloc(sizeof(psg_context)); | |
197 psg_init(sms->psg, render_sample_rate(), sms->master_clock, 15*16, render_audio_buffer(), lowpass_cutoff); | |
198 | |
199 sms->vdp = malloc(sizeof(vdp_context)); | |
200 init_vdp_context(sms->vdp, 0); | |
201 sms->vdp->system = &sms->header; | |
202 | |
203 info_out->save_type = SAVE_NONE; | |
204 info_out->name = strdup("Master System Game"); | |
205 | |
206 setup_io_devices(config, info_out, &sms->io); | |
207 | |
208 sms->header.set_speed_percent = set_speed_percent; | |
209 sms->header.start_context = start_sms; | |
210 sms->header.resume_context = run_sms; | |
211 //TODO: Fill in NULL values | |
212 sms->header.load_save = NULL; | |
213 sms->header.persist_save = NULL; | |
214 sms->header.free_context = free_sms; | |
215 sms->header.get_open_bus_value = get_open_bus_value; | |
216 sms->header.request_exit = request_exit; | |
217 sms->header.inc_debug_mode = NULL; | |
218 sms->header.inc_debug_pal = NULL; | |
219 | |
220 return sms; | |
221 } |