Mercurial > repos > blastem
comparison ymf262.c @ 2558:3f58fec775df
Initial work on YMF262 (aka OPL3) emulation
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sun, 19 Jan 2025 00:31:16 -0800 |
parents | |
children | eb588f22ec76 |
comparison
equal
deleted
inserted
replaced
2557:75dd7536c467 | 2558:3f58fec775df |
---|---|
1 #include <stdlib.h> | |
2 #include <string.h> | |
3 #include "ymf262.h" | |
4 #include "render_audio.h" | |
5 | |
6 void ymf262_init(ymf262_context *context, uint32_t master_clock, uint32_t clock_div, uint32_t options) | |
7 { | |
8 memset(context, 0, sizeof(*context)); | |
9 context->clock_inc = clock_div * 8; | |
10 context->audio = render_audio_source("YMF262", master_clock, context->clock_inc * OPL3_NUM_OPERATORS, 2); | |
11 ymf262_reset(context); | |
12 } | |
13 | |
14 void ymf262_reset(ymf262_context *context) | |
15 { | |
16 //TODO: implement me | |
17 } | |
18 | |
19 void ymf262_free(ymf262_context *context) | |
20 { | |
21 render_free_source(context->audio); | |
22 free(context); | |
23 } | |
24 | |
25 void ymf262_adjust_master_clock(ymf262_context *context, uint32_t master_clock) | |
26 { | |
27 render_audio_adjust_clock(context->audio, master_clock, context->clock_inc * OPL3_NUM_OPERATORS); | |
28 } | |
29 | |
30 void ymf262_adjust_cycles(ymf262_context *context, uint32_t deduction) | |
31 { | |
32 context->cycle -= deduction; | |
33 } | |
34 | |
35 void ymf262_run(ymf262_context *context, uint32_t to_cycle) | |
36 { | |
37 for (; context->cycle < to_cycle; context->cycle += context->clock_inc) | |
38 { | |
39 context->current_op++; | |
40 if (context->current_op == OPL3_NUM_OPERATORS) { | |
41 context->current_op = 0; | |
42 int16_t left = 0, right = 0; | |
43 render_put_stereo_sample(context->audio, left, right); | |
44 } | |
45 } | |
46 } | |
47 | |
48 void ymf262_address_write_part1(ymf262_context *context, uint8_t address) | |
49 { | |
50 context->selected_reg = address; | |
51 context->selected_part = 0; | |
52 } | |
53 void ymf262_address_write_part2(ymf262_context *context, uint8_t address) | |
54 { | |
55 context->selected_reg = address; | |
56 context->selected_part = 0; | |
57 } | |
58 | |
59 #define OPL3_NTS 0x08 | |
60 | |
61 void ymf262_data_write(ymf262_context *context, uint8_t value) | |
62 { | |
63 if (!context->selected_reg) { | |
64 return; | |
65 } | |
66 uint8_t old = 0; | |
67 if (context->selected_reg >= OPL3_PARAM_START && context->selected_reg < OPL3_PARAM_END) { | |
68 if (context->selected_part) { | |
69 old = context->part2_regs[context->selected_reg - OPL3_PARAM_START]; | |
70 context->part2_regs[context->selected_reg - OPL3_PARAM_START] = value; | |
71 } else { | |
72 old = context->part1_regs[context->selected_reg - OPL3_PARAM_START]; | |
73 context->part1_regs[context->selected_reg - OPL3_PARAM_START] = value; | |
74 } | |
75 } else if (context->selected_part) { | |
76 if (context->selected_reg <= sizeof(context->timer_test)) { | |
77 old = context->timer_test[context->selected_reg - 1]; | |
78 context->timer_test[context->selected_reg - 1] = value; | |
79 } else if (context->selected_reg == OPL3_NTS) { | |
80 old = context->nts; | |
81 context->nts = value; | |
82 } else { | |
83 return; | |
84 } | |
85 } else { | |
86 switch (context->selected_reg) | |
87 { | |
88 case 0x01: | |
89 old = context->part2_test; | |
90 context->part2_test = value; | |
91 break; | |
92 case 0x04: | |
93 old = context->connection_sel; | |
94 context->connection_sel = value; | |
95 break; | |
96 case 0x05: | |
97 old = context->opl3_mode; | |
98 context->opl3_mode = value; | |
99 break; | |
100 default: | |
101 return; | |
102 } | |
103 } | |
104 if (value != old) { | |
105 if (context->vgm) { | |
106 if (context->selected_reg) { | |
107 vgm_ymf262_part2_write(context->vgm, context->cycle, context->selected_reg, value); | |
108 } else { | |
109 vgm_ymf262_part1_write(context->vgm, context->cycle, context->selected_reg, value); | |
110 } | |
111 } | |
112 } | |
113 } | |
114 | |
115 void ymf262_vgm_log(ymf262_context *context, uint32_t master_clock, vgm_writer *vgm) | |
116 { | |
117 vgm_ymf262_init(vgm, 8 * master_clock / context->clock_inc); | |
118 context->vgm = vgm; | |
119 //TODO: write initial state | |
120 } | |
121 | |
122 uint8_t ymf262_read_status(ymf262_context *context, uint32_t cycle, uint32_t port) | |
123 { | |
124 if (port) { | |
125 //TODO: Investigate behavior of invalid status reads | |
126 return 0xFF; | |
127 } | |
128 return context->status; | |
129 } |