diff --git a/3rdparty/ymfm/README.md b/3rdparty/ymfm/README.md index 86b7dd446f9..c3cb61eb995 100644 --- a/3rdparty/ymfm/README.md +++ b/3rdparty/ymfm/README.md @@ -11,39 +11,42 @@ It has been tested on gcc, clang, and Microsoft Visual C++ 2019. Currently, support is present for the following chips (organized by header file): +* ymfm_misc.h: + * YM2149 (SSG) [1983: MSX; Atari ST] * ymfm_opm.h: - * YM2151 (OPM) - * YM2164 (OPP) + * YM2151 (OPM) [1983: Sharp X1, X68000; MSX; synths: DX21, DX27, DX100] + * YM2164 (OPP) [1985: FB-01 MIDI Expander; IBM Music Feature Card; MSX; synths: Korg DS-8, 707] * ymfm_opn.h: - * YM2149 (SSG) - * YM2203 (OPN) - * YM2608 (OPNA) - * YM2610 (OPNB) + * YM2203 (OPN) [1984: NEC PC-88, PC-98, NEC PC-6001mkII SR, PC-6601 SR] + * YM2608 (OPNA) [1985: NEC PC-88, PC-98] + * YM2610 (OPNB) [1987: Neo Geo] * YM2610B (OPNB2) - * YM2612 (OPN2) + * YM2612 (OPN2) [1988: Sega Mega Drive/Genesis; FM Towns] * YM3438 (OPN2C) * YMF276 (OPN2L) - * YMF288 (OPN3L) + * YMF288 (OPN3L) [1995: NEC PC-98] * ymfm_opl.h: - * YM3526 (OPL) - * Y8950 (MSX-Audio) - * YM3812 (OPL2) - * YMF262 (OPL3) + * YM3526 (OPL) [1984: C64 SFX Sound Expander] + * Y8950 (MSX-Audio) [1984: MSX] + * YM3812 (OPL2) [1985: AdLib, Sound Blaster; synths: some Portasound keyboards] + * YMF262 (OPL3) [1988: Sound Blaster Pro 2.0, SB16] * YMF289B (OPL3L) - * YMF278B (OPL4) - * YM2413 (OPLL) + * YMF278B (OPL4) [1993: MSX Moonsound cartridge] + * YM2413 (OPLL) [1986: Sega Master System, Mark III; MSX; synths: Portasound PSS-140, PSS-170, PSS-270] * YM2423 (OPLL-X) * YMF281 (OPLLP) - * DS1001 (Konami 053982) + * DS1001 (Konami 053982/VRC7) [1991: Famicom cartridge Lagrange Point] * ymfm_opq.h: - * YM3806 (OPQ) -- preliminary + * YM3806 (OPQ) [synths: PSR-60/70] * ymfm_opz.h: - * YM2414 (OPZ) -- preliminary + * YM2414 (OPZ) [1987: synths: TX81Z, DX11, YS200; Korg Z3 guitar synth] There are some obviously-related chips that also are on my horizon but have no implementation as yet: * YMW-258-F 'GEW8' (aka Sega 315-5560 aka Sega Multi-PCM) * YMF271 (OPX) +* YM21280 (OPS) / YM21290 (EGS) [synths: DX7, DX1, DX5, DX9, TX7, TX216, TX416, TX816] +* OPK? ## History diff --git a/3rdparty/ymfm/examples/vgmrender/vgmrender.cpp b/3rdparty/ymfm/examples/vgmrender/vgmrender.cpp index fd99d432ae4..cc1f3a13bae 100644 --- a/3rdparty/ymfm/examples/vgmrender/vgmrender.cpp +++ b/3rdparty/ymfm/examples/vgmrender/vgmrender.cpp @@ -329,7 +329,7 @@ void add_chips(uint32_t clock, chip_type type, char const *chipname) { char name[100]; sprintf(name, "%s #%d", chipname, index); - active_chips.push_back(new vgm_chip(clockval, type, chipname)); + active_chips.push_back(new vgm_chip(clockval, type, (numchips == 2) ? name : chipname)); } if (type == CHIP_YM2608) @@ -910,7 +910,6 @@ void generate_all(std::vector &buffer, uint32_t data_start, uint32_t ou break; uint8_t type = buffer[offset++]; uint32_t size = parse_uint32(buffer, offset); - uint32_t start, length; uint32_t localoffset = offset; switch (type) @@ -1114,15 +1113,22 @@ int write_wav(char const *filename, uint32_t output_rate, std::vector & { // determine normalization parameters int32_t max_scale = 0; - for (int index = 0; index < wav_buffer_src.size(); index++) + for (size_t index = 0; index < wav_buffer_src.size(); index++) { int32_t absval = std::abs(wav_buffer_src[index]); max_scale = std::max(max_scale, absval); } + // warn if only silence was detected (and also avoid divide by zero) + if (max_scale == 0) + { + fprintf(stderr, "The WAV file data will only contain silence.\n"); + max_scale = 1; + } + // now convert std::vector wav_buffer(wav_buffer_src.size()); - for (int index = 0; index < wav_buffer_src.size(); index++) + for (size_t index = 0; index < wav_buffer_src.size(); index++) wav_buffer[index] = wav_buffer_src[index] * 26000 / max_scale; // write the WAV file diff --git a/3rdparty/ymfm/src/ymfm.h b/3rdparty/ymfm/src/ymfm.h index cba731d9afa..906e3211a08 100644 --- a/3rdparty/ymfm/src/ymfm.h +++ b/3rdparty/ymfm/src/ymfm.h @@ -33,6 +33,10 @@ #pragma once +#ifdef _MSC_VER + #define _CRT_SECURE_NO_WARNINGS +#endif + #include #include #include @@ -136,7 +140,7 @@ inline uint8_t count_leading_zeros(uint32_t value) inline uint8_t count_leading_zeros(uint32_t value) { unsigned long index; - return _BitScanReverse(&index, value) ? (31U - index) : 32U; + return _BitScanReverse(&index, value) ? uint8_t(31U - index) : 32U; } #else @@ -355,8 +359,8 @@ public: void save(bool &data) { write(data ? 1 : 0); } void save(int8_t &data) { write(data); } void save(uint8_t &data) { write(data); } - void save(int16_t &data) { write(data).write(data >> 8); } - void save(uint16_t &data) { write(data).write(data >> 8); } + void save(int16_t &data) { write(uint8_t(data)).write(data >> 8); } + void save(uint16_t &data) { write(uint8_t(data)).write(data >> 8); } void save(int32_t &data) { write(data).write(data >> 8).write(data >> 16).write(data >> 24); } void save(uint32_t &data) { write(data).write(data >> 8).write(data >> 16).write(data >> 24); } void save(envelope_state &data) { write(uint8_t(data)); } diff --git a/3rdparty/ymfm/src/ymfm_fm.ipp b/3rdparty/ymfm/src/ymfm_fm.ipp index 2e04330a40e..ab5fc064e30 100644 --- a/3rdparty/ymfm/src/ymfm_fm.ipp +++ b/3rdparty/ymfm/src/ymfm_fm.ipp @@ -91,7 +91,7 @@ inline uint32_t attenuation_to_volume(uint32_t input) // as a nod to performance, the implicit 0x400 bit is pre-incorporated, and // the values are left-shifted by 2 so that a simple right shift is all that // is needed; also the order is reversed to save a NOT on the input -#define X(a) ((a | 0x400) << 2) +#define X(a) (((a) | 0x400) << 2) static uint16_t const s_power_table[256] = { X(0x3fa),X(0x3f5),X(0x3ef),X(0x3ea),X(0x3e4),X(0x3df),X(0x3da),X(0x3d4), @@ -500,7 +500,7 @@ int32_t fm_operator::compute_noise_volume(uint32_t am_offset) cons // application manual says the logarithmic transform is not applied here, so we // just use the raw envelope attenuation, inverted (since 0 attenuation should be // maximum), and shift it up from a 10-bit value to an 11-bit value - uint32_t result = (envelope_attenuation(am_offset) ^ 0x3ff) << 1; + int32_t result = (envelope_attenuation(am_offset) ^ 0x3ff) << 1; // QUESTION: is AM applied still? @@ -995,7 +995,7 @@ void fm_channel::output_4op(output_data &output, uint32_t rshift, // -x-------- include opout[2] in final sum // x--------- include opout[3] in final sum #define ALGORITHM(op2in, op3in, op4in, op1out, op2out, op3out) \ - (op2in | (op3in << 1) | (op4in << 4) | (op1out << 7) | (op2out << 8) | (op3out << 9)) + ((op2in) | ((op3in) << 1) | ((op4in) << 4) | ((op1out) << 7) | ((op2out) << 8) | ((op3out) << 9)) static uint16_t const s_algorithm_ops[8+4] = { ALGORITHM(1,2,3, 0,0,0), // 0: O1 -> O2 -> O3 -> O4 -> out (O4) diff --git a/3rdparty/ymfm/src/ymfm_opl.cpp b/3rdparty/ymfm/src/ymfm_opl.cpp index 5819a411620..b1b5db9efc4 100644 --- a/3rdparty/ymfm/src/ymfm_opl.cpp +++ b/3rdparty/ymfm/src/ymfm_opl.cpp @@ -383,7 +383,7 @@ std::string opl_registers_base::log_keyon(uint32_t choffs, uint32_t op char buffer[256]; char *end = &buffer[0]; - end += sprintf(end, "%2d.%02d freq=%04X fb=%d alg=%X mul=%X tl=%02X ksr=%d ns=%d ksl=%d adr=%X/%X/%X sl=%X sus=%d", + end += sprintf(end, "%2u.%02u freq=%04X fb=%u alg=%X mul=%X tl=%02X ksr=%u ns=%u ksl=%u adr=%X/%X/%X sl=%X sus=%u", chnum, opnum, ch_block_freq(choffs), ch_feedback(choffs), @@ -406,11 +406,11 @@ std::string opl_registers_base::log_keyon(uint32_t choffs, uint32_t op ch_output_2(choffs) ? '0' : '-', ch_output_3(choffs) ? '1' : '-'); if (op_lfo_am_enable(opoffs) != 0) - end += sprintf(end, " am=%d", lfo_am_depth()); + end += sprintf(end, " am=%u", lfo_am_depth()); if (op_lfo_pm_enable(opoffs) != 0) - end += sprintf(end, " pm=%d", lfo_pm_depth()); + end += sprintf(end, " pm=%u", lfo_pm_depth()); if (waveform_enable() && op_waveform(opoffs) != 0) - end += sprintf(end, " wf=%d", op_waveform(opoffs)); + end += sprintf(end, " wf=%u", op_waveform(opoffs)); if (is_rhythm(choffs)) end += sprintf(end, " rhy=1"); if (DYNAMIC_OPS) @@ -682,7 +682,7 @@ std::string opll_registers::log_keyon(uint32_t choffs, uint32_t opoffs) char buffer[256]; char *end = &buffer[0]; - end += sprintf(end, "%d.%02d freq=%04X inst=%X fb=%d mul=%X", + end += sprintf(end, "%u.%02u freq=%04X inst=%X fb=%u mul=%X", chnum, opnum, ch_block_freq(choffs), ch_instrument(choffs), @@ -694,7 +694,7 @@ std::string opll_registers::log_keyon(uint32_t choffs, uint32_t opoffs) else end += sprintf(end, " tl=%02X", ch_total_level(choffs)); - end += sprintf(end, " ksr=%d ksl=%d adr=%X/%X/%X sl=%X sus=%d/%d", + end += sprintf(end, " ksr=%u ksl=%u adr=%X/%X/%X sl=%X sus=%u/%u", op_ksr(opoffs), op_ksl(opoffs), op_attack_rate(opoffs), diff --git a/3rdparty/ymfm/src/ymfm_opm.cpp b/3rdparty/ymfm/src/ymfm_opm.cpp index 6a1e96613c3..9fd447e8c7d 100644 --- a/3rdparty/ymfm/src/ymfm_opm.cpp +++ b/3rdparty/ymfm/src/ymfm_opm.cpp @@ -152,10 +152,6 @@ bool opm_registers::write(uint16_t index, uint8_t data, uint32_t &channel, uint3 else if (index != 0x1a) m_regdata[index] = data; - // check test register writes for the LFO reset bit - if (index == 0x01 && bitfield(data, 1)) - m_lfo_counter = 0; - // handle writes to the key on index if (index == 0x08) { @@ -200,6 +196,13 @@ int32_t opm_registers::clock_noise_and_lfo() // manual, though it might not be implemented exactly this way on chip uint32_t rate = lfo_rate(); m_lfo_counter += (0x10 | bitfield(rate, 0, 4)) << bitfield(rate, 4, 4); + + // bit 1 of the test register is officially undocumented but has been + // discovered to hold the LFO in reset while active + if (lfo_reset()) + m_lfo_counter = 0; + + // now pull out the non-fractional LFO value uint32_t lfo = bitfield(m_lfo_counter, 22, 8); // fill in the noise entry 1 ahead of our current position; this @@ -352,7 +355,7 @@ std::string opm_registers::log_keyon(uint32_t choffs, uint32_t opoffs) char buffer[256]; char *end = &buffer[0]; - end += sprintf(end, "%d.%02d freq=%04X dt2=%d dt=%d fb=%d alg=%X mul=%X tl=%02X ksr=%d adsr=%02X/%02X/%02X/%X sl=%X out=%c%c", + end += sprintf(end, "%u.%02u freq=%04X dt2=%u dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X out=%c%c", chnum, opnum, ch_block_freq(choffs), op_detune2(opoffs), @@ -372,10 +375,10 @@ std::string opm_registers::log_keyon(uint32_t choffs, uint32_t opoffs) bool am = (lfo_am_depth() != 0 && ch_lfo_am_sens(choffs) != 0 && op_lfo_am_enable(opoffs) != 0); if (am) - end += sprintf(end, " am=%d/%02X", ch_lfo_am_sens(choffs), lfo_am_depth()); + end += sprintf(end, " am=%u/%02X", ch_lfo_am_sens(choffs), lfo_am_depth()); bool pm = (lfo_pm_depth() != 0 && ch_lfo_pm_sens(choffs) != 0); if (pm) - end += sprintf(end, " pm=%d/%02X", ch_lfo_pm_sens(choffs), lfo_pm_depth()); + end += sprintf(end, " pm=%u/%02X", ch_lfo_pm_sens(choffs), lfo_pm_depth()); if (am || pm) end += sprintf(end, " lfo=%02X/%c", lfo_rate(), "WQTN"[lfo_waveform()]); if (noise_enable() && opoffs == 31) diff --git a/3rdparty/ymfm/src/ymfm_opm.h b/3rdparty/ymfm/src/ymfm_opm.h index d35a0168e7c..ad657ac0159 100644 --- a/3rdparty/ymfm/src/ymfm_opm.h +++ b/3rdparty/ymfm/src/ymfm_opm.h @@ -49,7 +49,8 @@ namespace ymfm // OPM register map: // // System-wide registers: -// 01 xxxxxxxx Test register +// 01 xxxxxx-x Test register +// ------x- LFO reset // 08 -x------ Key on/off operator 4 // --x----- Key on/off operator 3 // ---x---- Key on/off operator 2 @@ -172,6 +173,7 @@ public: // system-wide registers uint32_t test() const { return byte(0x01, 0, 8); } + uint32_t lfo_reset() const { return byte(0x01, 1, 1); } uint32_t noise_frequency() const { return byte(0x0f, 0, 5); } uint32_t noise_enable() const { return byte(0x0f, 7, 1); } uint32_t timer_a_value() const { return word(0x10, 0, 8, 0x11, 0, 2); } diff --git a/3rdparty/ymfm/src/ymfm_opn.cpp b/3rdparty/ymfm/src/ymfm_opn.cpp index da7290dc384..4a334a63b43 100644 --- a/3rdparty/ymfm/src/ymfm_opn.cpp +++ b/3rdparty/ymfm/src/ymfm_opn.cpp @@ -396,7 +396,7 @@ std::string opn_registers_base::log_keyon(uint32_t choffs, uint32_t opof char buffer[256]; char *end = &buffer[0]; - end += sprintf(end, "%d.%02d freq=%04X dt=%d fb=%d alg=%X mul=%X tl=%02X ksr=%d adsr=%02X/%02X/%02X/%X sl=%X", + end += sprintf(end, "%u.%02u freq=%04X dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X", chnum, opnum, block_freq, op_detune(opoffs), @@ -419,10 +419,10 @@ std::string opn_registers_base::log_keyon(uint32_t choffs, uint32_t opof end += sprintf(end, " ssg=%X", op_ssg_eg_mode(opoffs)); bool am = (lfo_enable() && op_lfo_am_enable(opoffs) && ch_lfo_am_sens(choffs) != 0); if (am) - end += sprintf(end, " am=%d", ch_lfo_am_sens(choffs)); + end += sprintf(end, " am=%u", ch_lfo_am_sens(choffs)); bool pm = (lfo_enable() && ch_lfo_pm_sens(choffs) != 0); if (pm) - end += sprintf(end, " pm=%d", ch_lfo_pm_sens(choffs)); + end += sprintf(end, " pm=%u", ch_lfo_pm_sens(choffs)); if (am || pm) end += sprintf(end, " lfo=%02X", lfo_rate()); if (multi_freq() && choffs == 2) diff --git a/3rdparty/ymfm/src/ymfm_opq.cpp b/3rdparty/ymfm/src/ymfm_opq.cpp index 59e99db0a81..3467c0ddf95 100644 --- a/3rdparty/ymfm/src/ymfm_opq.cpp +++ b/3rdparty/ymfm/src/ymfm_opq.cpp @@ -341,7 +341,7 @@ std::string opq_registers::log_keyon(uint32_t choffs, uint32_t opoffs) char buffer[256]; char *end = &buffer[0]; - end += sprintf(end, "%d.%02d freq=%04X dt=%+2d fb=%d alg=%X mul=%X tl=%02X ksr=%d adsr=%02X/%02X/%02X/%X sl=%X out=%c%c", + end += sprintf(end, "%u.%02u freq=%04X dt=%+2d fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X out=%c%c", chnum, opnum, (opoffs & 1) ? ch_block_freq_24(choffs) : ch_block_freq_13(choffs), int32_t(op_detune(opoffs)) - 0x20, @@ -360,10 +360,10 @@ std::string opq_registers::log_keyon(uint32_t choffs, uint32_t opoffs) bool am = (lfo_enable() && op_lfo_am_enable(opoffs) && ch_lfo_am_sens(choffs) != 0); if (am) - end += sprintf(end, " am=%d", ch_lfo_am_sens(choffs)); + end += sprintf(end, " am=%u", ch_lfo_am_sens(choffs)); bool pm = (lfo_enable() && ch_lfo_pm_sens(choffs) != 0); if (pm) - end += sprintf(end, " pm=%d", ch_lfo_pm_sens(choffs)); + end += sprintf(end, " pm=%u", ch_lfo_pm_sens(choffs)); if (am || pm) end += sprintf(end, " lfo=%02X", lfo_rate()); if (ch_reverb(choffs)) diff --git a/3rdparty/ymfm/src/ymfm_opz.cpp b/3rdparty/ymfm/src/ymfm_opz.cpp index 8ee0cd9ab66..adeefd79f19 100644 --- a/3rdparty/ymfm/src/ymfm_opz.cpp +++ b/3rdparty/ymfm/src/ymfm_opz.cpp @@ -557,14 +557,14 @@ std::string opz_registers::log_keyon(uint32_t choffs, uint32_t opoffs) char buffer[256]; char *end = &buffer[0]; - end += sprintf(end, "%d.%02d", chnum, opnum); + end += sprintf(end, "%u.%02u", chnum, opnum); if (op_fix_mode(opoffs)) end += sprintf(end, " fixfreq=%X fine=%X shift=%X", op_fix_frequency(opoffs), op_fine(opoffs), op_fix_range(opoffs)); else - end += sprintf(end, " freq=%04X dt2=%d fine=%X", ch_block_freq(choffs), op_detune2(opoffs), op_fine(opoffs)); + end += sprintf(end, " freq=%04X dt2=%u fine=%X", ch_block_freq(choffs), op_detune2(opoffs), op_fine(opoffs)); - end += sprintf(end, " dt=%d fb=%d alg=%X mul=%X tl=%02X ksr=%d adsr=%02X/%02X/%02X/%X sl=%X out=%c%c", + end += sprintf(end, " dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X out=%c%c", op_detune(opoffs), ch_feedback(choffs), ch_algorithm(choffs), @@ -580,30 +580,30 @@ std::string opz_registers::log_keyon(uint32_t choffs, uint32_t opoffs) ch_output_1(choffs) ? 'R' : '-'); if (op_eg_shift(opoffs) != 0) - end += sprintf(end, " egshift=%d", op_eg_shift(opoffs)); + end += sprintf(end, " egshift=%u", op_eg_shift(opoffs)); bool am = (lfo_am_depth() != 0 && ch_lfo_am_sens(choffs) != 0 && op_lfo_am_enable(opoffs) != 0); if (am) - end += sprintf(end, " am=%d/%02X", ch_lfo_am_sens(choffs), lfo_am_depth()); + end += sprintf(end, " am=%u/%02X", ch_lfo_am_sens(choffs), lfo_am_depth()); bool pm = (lfo_pm_depth() != 0 && ch_lfo_pm_sens(choffs) != 0); if (pm) - end += sprintf(end, " pm=%d/%02X", ch_lfo_pm_sens(choffs), lfo_pm_depth()); + end += sprintf(end, " pm=%u/%02X", ch_lfo_pm_sens(choffs), lfo_pm_depth()); if (am || pm) end += sprintf(end, " lfo=%02X/%c", lfo_rate(), "WQTN"[lfo_waveform()]); bool am2 = (lfo2_am_depth() != 0 && ch_lfo2_am_sens(choffs) != 0 && op_lfo_am_enable(opoffs) != 0); if (am2) - end += sprintf(end, " am2=%d/%02X", ch_lfo2_am_sens(choffs), lfo2_am_depth()); + end += sprintf(end, " am2=%u/%02X", ch_lfo2_am_sens(choffs), lfo2_am_depth()); bool pm2 = (lfo2_pm_depth() != 0 && ch_lfo2_pm_sens(choffs) != 0); if (pm2) - end += sprintf(end, " pm2=%d/%02X", ch_lfo2_pm_sens(choffs), lfo2_pm_depth()); + end += sprintf(end, " pm2=%u/%02X", ch_lfo2_pm_sens(choffs), lfo2_pm_depth()); if (am2 || pm2) end += sprintf(end, " lfo2=%02X/%c", lfo2_rate(), "WQTN"[lfo2_waveform()]); if (op_reverb_rate(opoffs) != 0) - end += sprintf(end, " rev=%d", op_reverb_rate(opoffs)); + end += sprintf(end, " rev=%u", op_reverb_rate(opoffs)); if (op_waveform(opoffs) != 0) - end += sprintf(end, " wf=%d", op_waveform(opoffs)); + end += sprintf(end, " wf=%u", op_waveform(opoffs)); if (noise_enable() && opoffs == 31) end += sprintf(end, " noise=1"); diff --git a/3rdparty/ymfm/src/ymfm_pcm.cpp b/3rdparty/ymfm/src/ymfm_pcm.cpp index 3acd59e2a4d..cb3402e6ad7 100644 --- a/3rdparty/ymfm/src/ymfm_pcm.cpp +++ b/3rdparty/ymfm/src/ymfm_pcm.cpp @@ -425,7 +425,7 @@ void pcm_channel::load_wavetable() // for some reason that is unclear m_endpos = read_pcm(wavheader + 5) << 8; m_endpos |= read_pcm(wavheader + 6); - m_endpos = -m_endpos << 16; + m_endpos = -int32_t(m_endpos) << 16; // remaining data values set registers m_owner.write(0x80 + m_choffs, read_pcm(wavheader + 7)); @@ -560,9 +560,9 @@ int16_t pcm_channel::fetch_sample() const // 12-bit PCM: assemble out of half of 3 bytes addr += (pos / 2) * 3; if ((pos & 1) == 0) - return (read_pcm(addr + 0) << 8) | ((read_pcm(addr + 1) << 0) & 0xf0); + return (read_pcm(addr + 0) << 8) | ((read_pcm(addr + 1) << 4) & 0xf0); else - return (read_pcm(addr + 2) << 8) | ((read_pcm(addr + 1) << 4) & 0xf0); + return (read_pcm(addr + 2) << 8) | ((read_pcm(addr + 1) << 0) & 0xf0); }