YMF262_devboard_Firmware/Lib/YMF262-HAL/Inc/YMF262-HAL.hpp

145 lines
4.9 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @file YMF262-HAL.hpp
* @brief Hardware Abstraction Layer for the Yamaha YMF262 (OPL3).
*/
#ifndef YMF262_HAL_HPP
#define YMF262_HAL_HPP
#include <cstdint>
#include "GPIO.hpp"
#include "YMF262-Types.hpp"
/**
* @brief Hardware Abstraction Layer for the Yamaha YMF262 (OPL3) integrated circuit.
*
* Choreographs the control protocol between the synthesizer IC and the
* microcontroller. The electrical handling of the pins is delegated to the
* GPIOPolicy (template parameter), allowing the logic to be tested with a
* mock without real hardware.
*
* @tparam GPIOPolicy Class type implementing the verbs for electrical pin control
* (set_a0, set_a1, set_cs, set_wr, set_ic, set_data_bus, delay_ticks).
*/
template <class GPIOPolicy>
class YMF262_HAL{
private:
GPIOPolicy& _gpio; ///< Reference (not a copy) so tests can inspect the same policy instance
uint32_t _t_icw_ticks; ///< Reset Pulse Width
uint32_t _t_as_ticks; ///< Address Setup Time
uint32_t _t_ah_ticks; ///< Address Hold Time
uint32_t _t_csw_ticks; ///< Chip Select Write Width
uint32_t _t_csr_ticks; ///< Chip Select Read Width
uint32_t _t_ww_ticks; ///< Write Pulse Width
uint32_t _t_wds_ticks; ///< Write Data Setup Time
uint32_t _t_wdh_ticks; ///< Write Data Hold Time
uint32_t _t_rw_ticks; ///< Read Pulse Width
uint32_t _t_acc_ticks; ///< Read Data Access Time
uint32_t _t_rdh_ticks; ///< Read Data Hold Time
uint32_t _t_recovery_ticks; ///< Recovery between writes (32 φM cycles of the chip)
/**
* @brief Performs a single bus write cycle (one of the two in a register write).
* @param bank Register bank (drives A1).
* @param port ADDRESS or DATA phase (drives A0).
* @param data Byte to place on the data bus.
*/
void write_bus(Bank bank, Port port, uint8_t data){
_gpio.set_a0(port);
_gpio.set_a1(bank);
_gpio.delay_ticks(_t_as_ticks);
_gpio.set_cs(State::ACTIVE);
_gpio.set_wr(State::ACTIVE);
_gpio.set_data_bus(data);
_gpio.delay_ticks(_t_wds_ticks);
_gpio.set_wr(State::INACTIVE);
_gpio.set_cs(State::INACTIVE);
_gpio.delay_ticks(_t_wdh_ticks);
_gpio.delay_ticks(_t_recovery_ticks);
};
public:
YMF262_HAL(GPIOPolicy& policy, uint32_t opl_clock, uint32_t cpu_clock):_gpio(policy){
_t_icw_ticks = 400 * (cpu_clock/ opl_clock);
_t_recovery_ticks = 32 * (cpu_clock / opl_clock);
double ticks_per_ns = (double)cpu_clock / 1.0e9;
_t_as_ticks = (uint32_t)(10 * ticks_per_ns);
_t_ah_ticks = (uint32_t)(10 * ticks_per_ns);
_t_csw_ticks = (uint32_t)(100 * ticks_per_ns);
_t_ww_ticks = (uint32_t)(100 * ticks_per_ns);
_t_wds_ticks = (uint32_t)(10 * ticks_per_ns);
_t_wdh_ticks = (uint32_t)(20 * ticks_per_ns);
_t_csr_ticks = (uint32_t)(150 * ticks_per_ns);
_t_rw_ticks = (uint32_t)(150 * ticks_per_ns);
_t_acc_ticks = (uint32_t)(150 * ticks_per_ns);
_t_rdh_ticks = (uint32_t)(10 * ticks_per_ns);
};
/**
* @brief Boots the chip: resets it, then selects the operating mode.
*
* Order matters: the chip must be reset first (known state), then the
* mode is set explicitly. After reset the chip defaults to OPL2, so the
* mode is always set deliberately rather than relying on the default.
*
* @param mode Operating mode to configure after reset.
*/
void initialize(OPLMode mode){
initial_clear();
set_OPL_Mode(mode);
}
/**
* @brief Writes a value to a YMF262 register.
*
* Performs the chip's two-cycle write protocol: first the register
* address is latched, then the data value.
*
* @param bank Register bank to target (BANK_0 or BANK_1).
* @param reg Register address (0x000xFF).
* @param data Value to write.
*/
void write(Bank bank, uint8_t reg, uint8_t data){
write_bus(bank,Port::ADDRESS,reg);
write_bus(bank,Port::DATA,data);
};
/**
* @brief Selects OPL2 (compatibility) or OPL3 mode.
*
* Writes the OPL3-enable bit in register 0x105 (bank ARRAY1).
* OPL3 mode unlocks the 4-operator channels and extra waveforms.
*
* @param mode OPLMode::OPL2 or OPLMode::OPL3.
*/
void set_OPL_Mode(OPLMode mode){
write(Bank::BANK_1, 0x105, (mode == OPLMode::OPL3) ? 0x01 : 0x00);
};
/**
* @brief Resets the chip via the /IC (Initial Clear) line.
*
* Holds /IC active for the required Initial Clear Width, then releases it.
* Leaves the chip in a known default state (OPL2 mode).
*/
void initial_clear(){
_gpio.set_ic(State::ACTIVE);
_gpio.delay_ticks(_t_icw_ticks);
_gpio.set_ic(State::INACTIVE);
_gpio.delay_ticks(_t_recovery_ticks);
};
};
#endif