djhackersdev / bemanitools

Runs recent Konami arcade games and emulates various arcade hardware.
The Unlicense
84 stars 16 forks source link

aciodrv: add PANB support (+ aciotest handler) - [merged] #197

Closed icex2 closed 1 year ago

icex2 commented 3 years ago

In GitLab by @shtokopep on May 2, 2021, 15:16

Merges nostalgia -> master

Summary

add PANB support (nostalgia piano) to aciodrv as well as an aciotest handler for it

Description

this allows to use a nostalgia piano through aciodrv

Related Issue

https://dev.s-ul.net/djhackers/bemanitools/-/issues/69

How Has This Been Tested?

The aciotest handler :

Checklist

icex2 commented 3 years ago

In GitLab by @shtokopep on May 2, 2021, 16:01

marked the checklist item Updated existing doc of or add new doc to README file(s). aciotest doc didn't provide such level of detail as completed

icex2 commented 3 years ago

In GitLab by @shtokopep on May 2, 2021, 16:01

marked the checklist item Updated existing doc of or add new doc to README file(s). aciotest doc didn't provide such level of detail as incomplete

icex2 commented 3 years ago

In GitLab by @shtokopep on May 2, 2021, 16:29

marked the checklist item Updated development documentation. didn't find aciodrv details in the docs as completed

icex2 commented 3 years ago

In GitLab by @shtokopep on May 2, 2021, 16:29

marked the checklist item Updated existing doc of or add new doc to README file(s). aciotest doc didn't provide such level of detail as completed

icex2 commented 3 years ago

Nit: Naming-wise, I would stick to an out prefix for out data like the names of the other structs in this union

                struct ac_io_panb_poll_out panb_poll_out;
icex2 commented 3 years ago

Minor improvement: You could use a struct to actually put this comment into code, e.g.

struct keypair {
    uint8_t key1_velocity : 4;
    uint8_t key2_velocity : 4;
};
icex2 commented 3 years ago

Actually, maybe you can even align this nicely with the total number of keys avoid this key pair construct:

struct ac_io_panb_key_velocity {
    uint8_t value : 4;
};

#pragma pack(push, 1)
struct ac_io_panb_poll_in {
    // ... other stuff

    struct ac_io_panb_key_velocity key_velocity[AC_IO_PANB_MAX_KEYS];
};
#pragma pack(pop)

You need to verify if this turns out properly packed. Otherwise, the compiler might fill-up each velocity item to a full byte which might break the whole thing.

icex2 commented 3 years ago

Nit magic number 28: I assume that's the total number of keys on the input device? Suggestion: #define AC_IO_PANB_MAX_KEYS 28

Same for the magic number 14 above that: #define AC_IO_PANB_MAX_KEY_PAIRS 14 Not needed anymore if my above suggestion is do-able.

icex2 commented 3 years ago

Do you mind elaborating the reason for this change?

icex2 commented 3 years ago

Same here. This is in a module shared with other acio devices. I am worried this change breaks something else. Please let me know your thoughts behind this.

icex2 commented 3 years ago

Nice refactoring. Do we need to expose the split send and receive methods in the module or could these even be static?

icex2 commented 3 years ago

Unfortunately, this breaks the entire architecture of aciodrv modules. Each module should work independent of any thread management which a user of the module should take care of on their level of abstraction. I understand the reasoning for that but I am wondering if there is something else wrong for this root cause. Why is the serial buffer overlapping itself? If you don't drive the hardware, I would expect nothing to happen like that. Furthermore, sends and receives are typically executed in sync, i.e. receive input data, do stuff with that, set output data derived from inputs and send it.

It would be great if you can elaborate on that particaular issue a bit because I would like to understand the need for this implementation.

icex2 commented 3 years ago

Nice contribution. There are a bunch of open points that I would like to clarify. Please let me know if you got any questions.

icex2 commented 3 years ago

In GitLab by @shtokopep on May 2, 2021, 20:54

Commented on src/main/aciodrv/device.c line 205

oh! as I was typing the explanation I realized we are not reading one byte at a time here. I'm lucky this works with panb, you are entirely right it might break something else, will revert this.

icex2 commented 3 years ago

In GitLab by @shtokopep on May 2, 2021, 20:54

Commented on src/main/aciodrv/device.c line 91

I think it was a leftover oversight from when the device parameter has been added.

I kept getting compilation warnings about it because the calls to this function in the file are with the parameters in this order, so I switched them back and now it's happy... (note that this only happens when AC_IO_MSG_LOG is on so that might explain why it wasn't caught before)

icex2 commented 3 years ago

In GitLab by @shtokopep on May 2, 2021, 20:55

Commented on src/main/aciodrv/device.c line 405

they have to be exposed due to the way panb works (more on that in the comment about spawning a thread)

icex2 commented 3 years ago

In GitLab by @shtokopep on May 2, 2021, 21:05

Commented on src/main/aciodrv/panb.h line 7

in fact, the way PANB works, you send the start_auto_input command only once, and from there the piano keeps spamming auto_poll messages, it's not in sync, not even a "send one command get one reply" thing, unlike all other acio devices I came across.

(this is also why we had to split the "send" and "receive parts", and why I don't bother checking the command number in the reply because in panb case it's not even the same command number (you send 01 15 once, and you get a lot of 01 10 messages which keep piling...)

when you send a lamp update command, you don't get a reply at all either. You just see the command has been processed because the lamp do light up, and because in the 01 10 messages you'll get from that point, the first subsequence number will be incremented to reflect what was in that last send_lamp command you sent.

basically the piano has no time to send you anything else than 01 10 messages, and it does so indefinitely.

Now, in the case of aciotest, I tried not to use a thread and rather just read the incoming packet at each handler_update, but doing this gave me the following behavior :

Discussing the issue with @xyen we came up with this conclusion that it was probably due to too much data being sent on the serial line without us reading them to empty the buffer...

icex2 commented 3 years ago

In GitLab by @shtokopep on May 2, 2021, 21:07

Commented on src/main/acio/panb.h line 17

oh, interesting... I will try and see if this works, would indeed be useful

icex2 commented 3 years ago

thanks for the detailed explanation. well, that sucks that konmai is not sticking to their own "standards" they defined, again. I suggest to reflect that in the architecture using another layer. Make the panb module stick to how the other modules have done it so far and add another panb-proc module that takes care of threading and synchronisation. Documentation on the panb module should include stating the issue and advice to use the panb-proc module.

What do you think?

Also want to loop-in feedback by @xyen regarding that.

icex2 commented 3 years ago

In GitLab by @shtokopep on May 2, 2021, 21:18

Commented on src/main/aciodrv/panb.h line 7

I guess it's possible to move all the thread management into the aciotest handler (or through a panb-proc module indeed, then we get a place to document all this I agree) rather than keeping it inside the aciodrv module if I expose the recv_poll and the start_auto_input functions

icex2 commented 3 years ago

Just checked the code and you are correct. The code using that function is removed using ifdef-blocks. Therefore, this mistake did not have any effect so far on standard builds.

In that case, I would stick to the old signature because all other functions always have the context as the first parameter which is common to what I have seen if you pass around state. Instead, I suggest to fix the parameter order on the code calling this function.

icex2 commented 3 years ago

In GitLab by @shtokopep on May 2, 2021, 21:49

Commented on src/main/acio/panb.h line 17

I have a question about using bitfields... how am I supposed to go about splicing this to separate values for aciotest ?

src/main/aciodrv/panb.c:142:13: error: used struct type value where scalar is required
  142 |         if (key_velocity[i]) {
      |             ^~~~~~~~~~~~
src/main/aciodrv/panb.c:143:27: error: incompatible types when initializing type 'uint8_t' {aka 'unsigned char'} using type 'struct ac_io_panb_key_velocity'
  143 |             uint8_t but = (key_velocity[i]);
      |                           ^

should I recast the key_velocity into uint8_t and then perform the bitshift myself, or is there another way ?

icex2 commented 3 years ago

You forgot to access the actual value field since this is wrapped in a struct. I guess what you want is:

uint8_t but = poll_in.key_velocity[i].value;
icex2 commented 3 years ago

In GitLab by @shtokopep on May 2, 2021, 22:14

Commented on src/main/acio/panb.h line 17

oh, good catch... it indeeds looks like each velocity value gets packed into a full byte unfortunately (on the aciotest thing first button value is updated by 2nd physical key, second button by 4th physical key, and the latter half of the buttons contain garbage..)

icex2 commented 3 years ago

I think you have to also wrap the other struct to pack it and avoid that the compilers adds padding, try:

#pragma pack(push, 1)
struct ac_io_panb_key_velocity {
    uint8_t value : 4;
};
#pragma pack(pop)
icex2 commented 3 years ago

In GitLab by @shtokopep on May 2, 2021, 22:26

Commented on src/main/acio/panb.h line 17

didn't work either... for reference here's the relevant bit of code :

    /* splice the keypairs into separate button values */
    for (int i=0; i<AC_IO_PANB_MAX_KEYS; i++) {
        button_state[i] = key_velocity[i].value;
    }

and instead of having this, it behaves as if velocity[2*i+1] gets mapped to button_state[i]

icex2 commented 3 years ago

I just realized that the parameter 1 in #pragma pack(push, 1) is for defining the alignment to a single byte. Hence, my idea isn't possible. You could however at least split the higher and lower values into:

#pragma pack(push, 1)
struct ac_io_panb_key_pair_velocity {
    uint8_t key_1_value : 4;
    uint8_t key_2_value : 4;
};
#pragma pack(pop)

Not ideal, but at least removes the bit-shifting

icex2 commented 3 years ago

In GitLab by @xyen on May 2, 2021, 22:32

Commented on src/main/acio/panb.h line 17

Yeah, bit fields don't compose like that correctly, you'll need to make a struct that just contains the high / low nibble, and access them in pairs if you wanna make use of this for readability

icex2 commented 3 years ago

In GitLab by @xyen on May 2, 2021, 22:32

Commented on src/main/aciodrv/device.c line 405

@icex2 the internal send / recv functions remain static, it's just these outer ones have been split, for the reasons they mentioned.

icex2 commented 3 years ago

In GitLab by @xyen on May 2, 2021, 22:32

Commented on src/main/aciodrv/panb.h line 7

I'd move kicking off the auto polling to its own function, but arguably the thread logic should just stay here. I don't think splitting it off to another library makes much sense.

icex2 commented 3 years ago

In GitLab by @shtokopep on May 2, 2021, 22:34

Commented on src/main/acio/panb.h line 17

using struct ac_io_panb_keypair keypair[AC_IO_PANB_MAX_KEYPAIRS]; works but for some reason each pair of key is reversed? (key1 is actually key2..)

icex2 commented 3 years ago

In GitLab by @xyen on May 2, 2021, 22:35

Commented on src/main/acio/panb.h line 17

Yeah, big endian, just swap the struct around

icex2 commented 3 years ago

In GitLab by @xyen on May 2, 2021, 22:38

Commented on src/main/aciodrv/panb.h line 7

Actually on second thought, as all the init function does is start the auto polling, and the thread, I think it makes sense without splitting it off to another function.

icex2 commented 3 years ago

In GitLab by @shtokopep on May 2, 2021, 22:38

Commented on src/main/acio/panb.h line 17

ok done, thanks :) working now

icex2 commented 3 years ago

In GitLab by @xyen on May 2, 2021, 22:43

Commented on src/main/aciodrv/panb.h line 7

Hmmmm, on third thought, I think adding a new module is the right choice, this way the parsing code can live in aciodrv still, and the only thing the panb-proc handles is the threading / last state management.

This would provide a clear distinction / note to anyone reading the code, that there is something different going on here.

icex2 commented 3 years ago

In GitLab by @shtokopep on May 2, 2021, 22:44

Commented on src/main/acio/acio.h line 84

changed this line in version 2 of the diff

icex2 commented 3 years ago

In GitLab by @shtokopep on May 2, 2021, 22:44

Commented on src/main/acio/panb.h line 17

changed this line in version 2 of the diff

icex2 commented 3 years ago

In GitLab by @shtokopep on May 2, 2021, 22:44

Commented on src/main/acio/panb.h line 28

changed this line in version 2 of the diff

icex2 commented 3 years ago

In GitLab by @shtokopep on May 2, 2021, 22:44

Commented on src/main/aciodrv/device.c line 91

changed this line in version 2 of the diff

icex2 commented 3 years ago

In GitLab by @shtokopep on May 2, 2021, 22:44

Commented on src/main/aciodrv/device.c line 205

changed this line in version 2 of the diff

icex2 commented 3 years ago

In GitLab by @shtokopep on May 2, 2021, 22:44

added 1 commit

Compare with previous version

icex2 commented 3 years ago

Clear. Thanks for clarifying.

icex2 commented 3 years ago

This would provide a clear distinction / note to anyone reading the code, that there is something different going on here.

That is my primary intention with this approach.

icex2 commented 3 years ago

In GitLab by @shtokopep on May 3, 2021, 01:18

Commented on src/main/aciodrv/panb.h line 7

changed this line in version 3 of the diff

icex2 commented 3 years ago

In GitLab by @shtokopep on May 3, 2021, 01:18

added 2 commits

Compare with previous version

icex2 commented 3 years ago

In GitLab by @shtokopep on May 3, 2021, 01:26

Commented on src/main/aciodrv/panb.h line 7

A new aciodrv-proc module has been added (even though @xyen confirmed that PANB was the only device from libacio that would require threading as of now, and there's little chance that konami adds new ones in the future, I feel panb-proc might become ambiguous when the acioemu part of panb will come into play)

icex2 commented 3 years ago

I guess that is fine. If things can be iterated/refactored easily, that's better than leaving things in a difficult to manage/static state.

icex2 commented 3 years ago

Nit: I don't see that one exposed, so I guess you can make it a static function.

icex2 commented 3 years ago

I think right now it is fine to use btools "built-in" thread abstraction. I would suggest to rather have this provided as a parameter like on all the btools APIs to give flexibility to the user regarding which threading implementation to use. I just don't see the use-case needing this right now.

So good as is, but maybe something to keep in mind for a small refactoring step in the future.

icex2 commented 3 years ago

lgtm. I suggest to do a full sqash to a single commit and provide an elaborate message of what you did and some reasoning for some key aspects, e.g. the threading part.