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 }