comparison ym2612.c @ 288:a8ee7934a1f8

Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
author Mike Pavone <pavone@retrodev.com>
date Sun, 05 May 2013 22:56:42 -0700
parents
children cc39629e8d06
comparison
equal deleted inserted replaced
287:fb840e0a48cd 288:a8ee7934a1f8
1 #include <string.h>
2 #include "ym2612.h"
3
4 #define BUSY_CYCLES 17
5 #define TIMERA_UPDATE_PERIOD 144
6
7 #define REG_TIMERA_HIGH 0x3 // 0x24
8 #define REG_TIMERA_LOW 0x4 // 0x25
9 #define REG_TIMERB 0x5 // 0x26
10 #define REG_TIME_CTRL 0x6 // 0x27
11
12 #define BIT_TIMERA_ENABLE 0x1
13 #define BIT_TIMERB_ENABLE 0x2
14 #define BIT_TIMERA_OVEREN 0x4
15 #define BIT_TIMERB_OVEREN 0x8
16 #define BIT_TIMERA_RESET 0x10
17 #define BIT_TIMERB_RESET 0x20
18
19 #define BIT_STATUS_TIMERA 0x1
20 #define BIT_STATUS_TIMERB 0x2
21
22 void ym_init(ym2612_context * context)
23 {
24 memset(context, 0, sizeof(*context));
25 }
26
27 void ym_run(ym2612_context * context, uint32_t to_cycle)
28 {
29 uint32_t delta = to_cycle - context->current_cycle;
30 //Timers won't be perfect with this, but it's good enough for now
31 //once actual FM emulation is in place the timers should just be
32 //decremented/reloaded on the appropriate ticks
33 uint32_t timer_delta = to_cycle / TIMERA_UPDATE_PERIOD;
34 if (context->part1_regs[REG_TIME_CTRL] & BIT_TIMERA_ENABLE) {
35 if (timer_delta > context->timer_a) {
36 if (context->part1_regs[REG_TIME_CTRL] & BIT_TIMERA_OVEREN) {
37 context->status |= BIT_STATUS_TIMERA;
38 }
39 uint32_t rem_delta = timer_delta - (context->timer_a+1);
40 uint16_t timer_val = (context->part1_regs[REG_TIMERA_HIGH] << 2) | (context->part1_regs[REG_TIMERA_LOW] & 0x3);
41 context->timer_a = timer_val - (rem_delta % (timer_val + 1));
42 } else {
43 context->timer_a -= timer_delta;
44 }
45 }
46 timer_delta /= 16; //Timer B runs at 1/16th the speed of Timer A
47 if (context->part1_regs[REG_TIME_CTRL] & BIT_TIMERB_ENABLE) {
48 if (timer_delta > context->timer_b) {
49 if (context->part1_regs[REG_TIME_CTRL] & BIT_TIMERB_OVEREN) {
50 context->status |= BIT_STATUS_TIMERB;
51 }
52 uint32_t rem_delta = timer_delta - (context->timer_b+1);
53 uint8_t timer_val = context->part1_regs[REG_TIMERB];
54 context->timer_b = timer_val - (rem_delta % (timer_val + 1));
55 } else {
56 context->timer_a -= timer_delta;
57 }
58 }
59 context->current_cycle = to_cycle;
60 if (to_cycle >= context->write_cycle + BUSY_CYCLES) {
61 context->status &= 0x7F;
62 }
63 }
64
65 void ym_address_write_part1(ym2612_context * context, uint8_t address)
66 {
67 if (address >= 0x21 && address < 0xB7) {
68 context->selected_reg = context->part1_regs + address - 0x21;
69 } else {
70 context->selected_reg = NULL;
71 }
72 }
73
74 void ym_address_write_part2(ym2612_context * context, uint8_t address)
75 {
76 if (address >= 0x30 && address < 0xB7) {
77 context->selected_reg = context->part1_regs + address - 0x30;
78 } else {
79 context->selected_reg = NULL;
80 }
81 }
82
83 void ym_data_write(ym2612_context * context, uint8_t value)
84 {
85 if (context->selected_reg && !(context->status & 0x80)) {
86 *context->selected_reg = value;
87 context->write_cycle = context->current_cycle;
88 context->selected_reg = NULL;//TODO: Verify this
89 }
90 }
91
92 uint8_t ym_read_status(ym2612_context * context)
93 {
94 return context->status;
95 }
96