-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
yamaha: new GEW7 and GEW12 machines, various GEW8/MultiPCM improvements #11105
Conversation
… fixes (envelope behavior, sample lengths, stream updates, save state members) New working machines ---------- Yamaha PSR-150 [Edward d-tech, Devin Acker] Yamaha PSR-180 [Edward d-tech, Devin Acker] Yamaha PSR-78 [Edward d-tech, Devin Acker] New working clones ---------- Yamaha PSR-110 [Edward d-tech, Devin Acker] Yamaha PSR-75 [Edward d-tech, Devin Acker] Yamaha PSR-76 [Edward d-tech, Devin Acker] Yamaha PSS-11 [Edward d-tech, Devin Acker] Yamaha PSS-21 [Edward d-tech, Devin Acker] Yamaha PSS-31 [Edward d-tech, Devin Acker] Yamaha PSS-6 [Devin Acker] Machines promoted to working ---------- Yamaha PSS-12 [Devin Acker] New NOT_WORKING machines ---------- Yamaha PSR-260 [Edward d-tech, Devin Acker] Yamaha PSR-79 [Edward d-tech, Devin Acker] New NOT_WORKING clones ---------- Yamaha PSR-160 [Edward d-tech, Devin Acker]
…itself and related machines are less barebones
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The internal timers shouldn’t be implemented using scheduler timers. You don’t want to be causing scheduler synchronisation for internal events like this, and you particularly don’t want to be causing scheduler syncrhonisation on every tick if possible, because that really hurts performance.
I already did the work to template the underlying CPU type of the 6502 microcontroller helper in 864bfe1. You can now use it with the 65C02 core, and use the 6500/1 timer as a guide for how to implement a lazily evaluated timer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking a lot closer to ready now.
…ts (mamedev#11105) * yamaha: implement GEW7, partially implement GEW12, some GEW8/MultiPCM fixes (envelope behavior, sample lengths, stream updates, save state members) New working machines ---------- Yamaha PSR-150 [Edward d-tech, Devin Acker] Yamaha PSR-180 [Edward d-tech, Devin Acker] Yamaha PSR-78 [Edward d-tech, Devin Acker] New working clones ---------- Yamaha PSR-110 [Edward d-tech, Devin Acker] Yamaha PSR-75 [Edward d-tech, Devin Acker] Yamaha PSR-76 [Edward d-tech, Devin Acker] Yamaha PSS-11 [Edward d-tech, Devin Acker] Yamaha PSS-21 [Edward d-tech, Devin Acker] Yamaha PSS-31 [Edward d-tech, Devin Acker] Yamaha PSS-6 [Devin Acker] Machines promoted to working ---------- Yamaha PSS-12 [Devin Acker] New NOT_WORKING machines ---------- Yamaha PSR-260 [Edward d-tech, Devin Acker] Yamaha PSR-79 [Edward d-tech, Devin Acker] New NOT_WORKING clones ---------- Yamaha PSR-160 [Edward d-tech, Devin Acker]
A bit out of topic, but do you have any clue how key and button inputs work on the GEW12 (specifically PSR-260)? Looking at the firmware, it appears as-if certain addresses in RAM (e.g. 0x0FC, 0x4C7-0x4FF) are somehow populated with key/button information externally, but I can't seem to find any code in the firmware that configures any external "device" to use these addresses. |
The psr260 schematics are available on electrotanya. https://elektrotanya.com/yamaha_psr260_262.pdf/download.html There we see that the button inputs are on ports A & B, and the keyboard on C, D & E. Dunno how the GEW12 maps the ports though. OG. |
Thanks! I would have assumed they would be mapped in |
So while I was reversing the firmware for the PSR-260, I wrote a quick-and-dirty emulator to allow me to debug it. While the code can't really be used in MAME, I did figure out a major chunk of the GEW12 hardware that is not emulated or documented elsewhere:
I'm not sure if GEW12 emulation is still being worked on or how much MAME contributors dug into the hardware by themselves already, but I could document what I know if it helps. I'm attaching a short video demonstrating my current state of emulation. The most notable thing missing is the lack of volume envelopes. Screen.Recording.2024-05-31.at.00.52.00.mp4 |
I don't know if Devin's put further work into the GEW12 or PSR260, but I'd certainly like to see more :-) |
So the key and button scanners operate this way: They have direct access to RAM, and I assume they halt the CPU while they access RAM (but I'm not entirely sure how and when they do so). Both key scanning the button scanning share a "control byte", at address Button scan codes are identical for both models, and follow this order:
Button scanning seems to be enabled by bit 4 in the control byte. The button scanner sets bit 7 to 1 when changes are available, and the firmware resets it when it's done processing the changes. Button state is stored differently across models. In PSR-160, the state is stored in 6 bytes, starting at Button state in PSR-260 is scattered all over RAM. Each group takes 3 bytes, and only bites 6-7 are used to hold the state. The group start addresses are:
So the Voice button, for example, is stored in bit 6 of address Key scanning seems to be enabled by bit 3 in the control byte. The key scanner sets bit 6 to 1 when events are available, and the firmware resets it when it's done processing the changes. Events are stored in a buffer starting at address The PSR-260 event format is:
Velocity ranges from 0 to Scancodes seem to differ between PSR-260 and PSR-160. The scan codes for PSR-260, from C1 to C6 are:
That's it for now, I'll document the rest later. |
Sound registers: 0x1004-0x1007: Trigger register. This 4-byte register is a 16-bit bitmap (only the even bits, 0, 2, 4 and 6, are used) where each bit maps to a channel. Writing 1 to a bit will start the corresponding channel. Writing 0s has no effect. This design allows triggering up to 4 channels with a single write so they always remain in sync. 0x1008-0x100B: Release register. This is the same as the trigger register, but does releases rather than triggers 0x1010-0x1011: Pitch register. BE, 16-bit. Determines the pitch of the currently selected (see 0x1001) channel. Bits 0-11 determine the pitch, while bits 12-15 are a signed octave modifier (i.e. multiples the pitch by 0x1013: Volume. Another banked register that controls the volume. 0x7F is completely silent, 0x00 is the maximum volume. I'm not sure how middle values are to be interpolated, but it's not linear interpolation. 0x1014 and 0x1015: Left and Right volume, respectively. These two are banked as well, and determine the panning. 0x3C is completely silent (for that output terminal) while 0x00 is maximum volume. Similarly, I'm not sure how the middle values are to be interpolated. 0x101C and 0x101D: I don't fully understand the meaning of these registers, but they appear to be related to the vibrato effect. They're banked. 0x1023: I don't fully understand the meaning of this registers, but they appear to be related to the release segment of the ADSR envelope. Unlike the other segments, the release envelop can be dynamically controlled after triggering a sound, which is how the firmware handles pedaling. 0x102D-0x102F: Sound definition pointer. Banked. Take this 24-bit, BE value and multiply it by two, and treat it as a ROM offset. The 16-byte structure at this offset determines the properties of the sound to be played on the channel. I'll describe this structure later, but it generally includes a pointer to the waveform, the waveform's format, looping information, and ADSR parameters. LCD segments are a bit annoying to work with because their hardware ordering is all over the place. So instead, I adapted code from the firmware to convert the physical ordering into the more logical order used by the firmware: bool PSR_260_get_lcd_segment(PSR_260_t *psr260, PSR_260_lcd_segment_t segment)
{
static const struct {
uint8_t offset;
uint8_t mask;
} mapping[256] = {
{0x2C, 4},{0x2A, 2},{0x29, 2},{0x2A, 4}, {0x29, 4}, {6, 0x10},{6, 8},{0x3E, 1},{0x3A, 1},{0x3B, 1},{0x3F, 1},{0x3C, 1},
{2, 8},{2, 4},{3, 4},{7, 8},{3, 0x10},{2, 0x10},{3, 8}, {2, 1},{0xA, 0x10},{0xB, 0x10},{7, 1},{3, 2},{2, 2},{3, 1},
{0xA, 4},{0xA, 2},{0xB, 2},{0xF, 4},{0xB, 8},{0xA, 8},{0xB, 4}, {0x32, 4},{0x2B, 0x10},{0x2C, 0x10},{0x34, 4},{0x34, 8},
{0x33, 8},{0x33, 4},{0x1A, 1},{0x23, 0x10},{0x24, 0x10},{0x1C, 1},{0x2C, 8},{0x2B, 8},{0x1B, 1},{0x3A, 8},{0x3B, 4},
{0x3C, 4},{0x3C, 8},{0x3C, 0x10},{0x3B, 0x10},{0x3B, 8},{0x37, 0x10},{0x26, 8},{0x2A, 8},{0x34, 2},{0, 0x10},{0x34, 1},
{0x33, 2},{7, 0x10},{7, 2},{0xF, 0x10},{0xC, 0x10},{4, 4},{7, 4},{4, 2},{0xF, 8},{0x1F, 8},{0xC, 2},{0xF, 2},{0xC, 8},
{0x27, 8},{0x1C, 8},{0x1C, 2},{0x1C, 4},{0x1F, 1},{0x27, 0x10},{6, 2},{0x20, 8},{8, 2},{8, 8},{8, 0x10},{0, 4},{0x16, 4},
{0, 1},{8, 4},{8, 1},{0x10, 0x10},{0x10, 8},{0x16, 2},{0x18, 8},{0x18, 4},{0x18, 2},{0x18, 1},{0x20, 0x10},{0x36, 2},
{0x28, 4},{0x28, 2},{0x28, 1},{0x30, 0x10},{0x30, 8},{0x36, 1},{0x38, 8},{0x38, 2},{0x38, 1},{0x20, 1},{0x20, 2},{0x34, 0x10},
{0x25, 4},{0x27, 4},{0x25, 8},{0x27, 2},{0xD, 2},{0xD, 8},{0x27, 1},{0xD, 0x10},{0x2F, 0x10},{5, 4},{0x2F, 8},{5, 0x10},
{5, 2},{0x2F, 4},{5, 1},{0x2F, 2},{0xD, 4},{0xD, 1},{0x2F, 1},{0x15, 0x10},{0x37, 8},{0x15, 8},{0x37, 4},{0x15, 4},
{0x1D, 0x10},{0x16, 1},{0x1D, 8},{0x3E, 4},{0x1D, 4},{0x1D, 2},{0x3E, 2},{0x1D, 1},{0x2E, 1},{0x25, 0x10},{0x36, 0x10},
{0x15, 2},{0x15, 1},{0x36, 8},{0x2D, 4},{0x2E, 0x10},{0x2D, 2},{0x2E, 2},{0x2E, 8},{0x35, 0x10},{0x26, 0x10},{0x35, 8},
{0x3E, 0x10},{0x35, 2},{0x35, 1},{0x2E, 4},{0x3D, 8},{0x2B, 4},{0x3D, 2},{0x3D, 1},{0x2B, 2},{0x25, 1},{0x2C, 2},{0x25, 2},
{0x2C, 1},{0x24, 2},{0x2D, 1},{0x1A, 8},{0x32, 8},{0x2A, 0x10},{0x28, 8},{0x1C, 0x10},{0x14, 1},{0x14, 2},{0x14, 4},{0x14, 8},
{0x14, 0x10},{0xC, 1},{0x31, 2},{0x31, 1},{0x39, 0x10},{0x39, 8},{0x11, 2},{0x11, 1},{0x39, 4},{0x39, 2},{0x29, 1},
{0x31, 0x10},{0x21, 8},{0x11, 4},{0x31, 4},{0x29, 0x10},{0x29, 8},{0x1A, 0x10},{0x12, 1},{0x12, 2},{0x12, 8},{0xA, 1},
{0x30, 2},{0x30, 1},{0x38, 0x10},{0x10, 2},{0x10, 1},{0x38, 4},{0x2A, 1},{0x32, 0x10},{0x10, 4},{0x30, 4},{0x28, 0x10}
};
return psr260->lcdc.cgram[mapping[segment].offset] & mapping[segment].mask;
} Then we have this logical mapping: VOICE_SCREEN = 0
STYLE_SCREEN = 1
SONG_SCREEN = 2
STYLE_MODE = 3
SONG_MODE = 4
OVERALL_1 = 5
OVERALL_2 = 6
OVERALL_3 = 7
OVERALL_4 = 8
OVERALL_5 = 9
OVERALL_6 = 10
OVERALL_7 = 11
// The large 3-digit display denoting voice number, etc
SEVEN_SEGEMENT_1_TOP = 12
SEVEN_SEGEMENT_1_TOP_RIGHT = 13
SEVEN_SEGEMENT_1_BOTTOM_RIGHT = 14
SEVEN_SEGEMENT_1_BOTTOM = 15
SEVEN_SEGEMENT_1_BOTTOM_LEFT = 16
SEVEN_SEGEMENT_1_BOTTOM_TOP = 17
SEVEN_SEGEMENT_1_CENTER = 18
SEVEN_SEGEMENT_2_TOP = 19
SEVEN_SEGEMENT_2_TOP_RIGHT = 20
SEVEN_SEGEMENT_2_BOTTOM_RIGHT = 21
SEVEN_SEGEMENT_2_BOTTOM = 22
SEVEN_SEGEMENT_2_BOTTOM_LEFT = 23
SEVEN_SEGEMENT_2_BOTTOM_TOP = 24
SEVEN_SEGEMENT_2_CENTER = 25
SEVEN_SEGEMENT_3_TOP = 26
SEVEN_SEGEMENT_3_TOP_RIGHT = 27
SEVEN_SEGEMENT_3_BOTTOM_RIGHT = 28
SEVEN_SEGEMENT_3_BOTTOM = 29
SEVEN_SEGEMENT_3_BOTTOM_LEFT = 30
SEVEN_SEGEMENT_3_BOTTOM_TOP = 31
SEVEN_SEGEMENT_3_CENTER = 32
// The small 3-digit display denoting tempo, etc
SEVEN_SEGEMENT_MINI_1_TOP = 33
SEVEN_SEGEMENT_MINI_1_TOP_RIGHT = 34
SEVEN_SEGEMENT_MINI_1_BOTTOM_RIGHT = 35
SEVEN_SEGEMENT_MINI_1_BOTTOM = 36
SEVEN_SEGEMENT_MINI_1_BOTTOM_LEFT = 37
SEVEN_SEGEMENT_MINI_1_BOTTOM_TOP = 38
SEVEN_SEGEMENT_MINI_1_CENTER = 39
SEVEN_SEGEMENT_MINI_2_TOP = 40
SEVEN_SEGEMENT_MINI_2_TOP_RIGHT = 41
SEVEN_SEGEMENT_MINI_2_BOTTOM_RIGHT = 42
SEVEN_SEGEMENT_MINI_2_BOTTOM = 43
SEVEN_SEGEMENT_MINI_2_BOTTOM_LEFT = 44
SEVEN_SEGEMENT_MINI_2_BOTTOM_TOP = 45
SEVEN_SEGEMENT_MINI_2_CENTER = 46
SEVEN_SEGEMENT_MINI_3_TOP = 47
SEVEN_SEGEMENT_MINI_3_TOP_RIGHT = 48
SEVEN_SEGEMENT_MINI_3_BOTTOM_RIGHT = 49
SEVEN_SEGEMENT_MINI_3_BOTTOM = 50
SEVEN_SEGEMENT_MINI_3_BOTTOM_LEFT = 51
SEVEN_SEGEMENT_MINI_3_BOTTOM_TOP = 52
SEVEN_SEGEMENT_MINI_3_CENTER = 53
ACCOMP = 54
MEASURE = 55
TEMPO = 56
HAND_OPEN = 57
UNKNOWN = 58 // Seems to be used, but doesn't map to any LCD segment?
HAND_CLOSED = 59
BEAT = 60
// Chord letter
CHORD_LETTER_TOP_LEFT_ARC = 61
CHORD_LETTER_TOP_RIGHT = 62
CHORD_LETTER_MIDDLE_NOTCH = 63
CHORD_LETTER_BOTTOM_RIGHT = 64
CHORD_LETTER_BOTTOM = 65
CHORD_LETTER_MIDDLE_LEFT = 66
CHORD_LETTER_MIDDLE_RIGHT = 67
// Chord modifiers, didn't map them all yet
CHORD_MODIFIER_MAJOR = 70
CHORD_MODIFIER_MINOR = 71
CHORD_MODIFIER_7 = 72
/* The static keyboard shape. Since this entire group is either
completely on (normal operation) or completely off (test mode),
I'm not entirely sure how it is split */
KEYBOARD_SHAPE = 79 - 103
// Keyboard buttons
KEYBOARD_BUTTON_C1 = 110
// ... Chromatically continues toward C6
KEYBOARD_BUTTON_C6 = 170
LOW_8VA = 171
HIGH_8VA = 172
// Notes
NOTE_C2 = 175
// ... Continues toward C5
NOTE_C5 = 196
/* Also missing: Flat and sharp signs */ |
Nice, thanks. That's very useful! @devinacker are you interested? I would like to see all this implemented but I'm in the middle of additional Mac untangling right now :-) |
Possibly, but I'm also focused on other stuff at the moment, myself. |
New working machines
Yamaha PSR-150 [Edward d-tech, Devin Acker]
Yamaha PSR-180 [Edward d-tech, Devin Acker]
Yamaha PSR-78 [Edward d-tech, Devin Acker]
New working clones
Yamaha PSR-110 [Edward d-tech, Devin Acker]
Yamaha PSR-75 [Edward d-tech, Devin Acker]
Yamaha PSR-76 [Edward d-tech, Devin Acker]
Yamaha PSS-11 [Edward d-tech, Devin Acker]
Yamaha PSS-21 [Edward d-tech, Devin Acker]
Yamaha PSS-31 [Edward d-tech, Devin Acker]
Yamaha PSS-6 [Devin Acker]
Machines promoted to working
Yamaha PSS-12 [Devin Acker]
New NOT_WORKING machines
Yamaha PSR-260 [Edward d-tech, Devin Acker]
Yamaha PSR-79 [Edward d-tech, Devin Acker]
New NOT_WORKING clones
Yamaha PSR-160 [Edward d-tech, Devin Acker]
GEW7 and MultiPCM now share a common base class that handles sample decoding, mixing, envelope generation, etc. In the process I fixed a handful of issues that affected both devices:
gew_pcm_device::get_rate
,gew_pcm_device::envelope_generator_calc
)gew_pcm_device::envelope_generator_update
for DECAY1)gew_pcm_device::envelope_generator_calc
)gew_pcm_device::sound_stream_update
when offset >= end)multipcm_device::init_sample
)sound_stream::update
for MultiPCM where appropriate (register reads/writes)The MU5 now sounds much better than before, though the demo still has some noticeable problems with at least one instrument having a much slower attack than it's supposed to, and probably other issues. I added the imperfect sound flag to
gew_pcm_device
as a result.