Main Restorations Software Audio/Jukebox/MP3 Everything Else Buy/Sell/Trade
Project Announcements Monitor/Video GroovyMAME Merit/JVL Touchscreen Meet Up Retail Vendors
Driving & Racing Woodworking Software Support Forums Consoles Project Arcade Reviews
Automated Projects Artwork Frontend Support Forums Pinball Forum Discussion Old Boards
Raspberry Pi & Dev Board controls.dat Linux Miscellaneous Arcade Wiki Discussion Old Archives
Lightguns Arcade1Up Try the site in https mode Site News

Unread posts | New Replies | Recent posts | Rules | Chatroom | Wiki | File Repository | RSS | Submit news

  

Author Topic: Mame hacks that make driving games play better with mouse/spinner/360wheel  (Read 26994 times)

0 Members and 1 Guest are viewing this topic.

isamu

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 808
  • Last login:Yesterday at 08:21:14 am
  • I'm a llama!
Re: Mame hacks that make driving games play better with mouse/spinner/360wheel
« Reply #40 on: December 29, 2017, 07:03:05 pm »
This is cool but what about those of us with 270° wheels that want to play the 360° games?

Marcoqwerty

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 127
  • Last login:Yesterday at 12:00:20 am
Re: Mame hacks that make driving games play better with mouse/spinner/360wheel
« Reply #41 on: December 29, 2017, 08:31:03 pm »
This is cool but what about those of us with 270° wheels that want to play the 360° games?

You should play with mame sensibility and find the right setting "in game test mode"....but if you are on beginning to a new project, better to move on a 360 wheel imho.

Martin0037

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 21
  • Last login:May 11, 2018, 05:47:15 pm
  • I want to build my own arcade controls!
Re: Mame hacks that make driving games play better with mouse/spinner/360wheel
« Reply #42 on: February 27, 2018, 10:49:08 am »
Hi guys.  Great thread this is. Helped me loads so far.  Hope someone can help me here...
Has anyone any idea if a newer version of mame has been released with this sorted or has anyone compiled a newer version of mame with geecabs hack applied?

I’ve tried your mame hack version geecab and it works brilliant,  can’t seem to run newer games on it such as ridge racer or rave racer,  think ridge racer was 0.159 but I might be wrong.

I’m a total noob when it comes to compiling... 


geecab

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 250
  • Last login:May 13, 2024, 06:19:08 am
Hi there! I think mame's steering wheel code has changed a bit since I last did my hack. I can no longer compile you a new version of mame on windows as I only have a windows XP machine (and it is not possible to build mame on XP anymore). I use Linux Ubuntu now, so I can compile/test the latest version of mame on that. If I get my hack working with the latest sources of mame on Ubuntu, I could send you my Git "diff" file (I'm not sure if you've heard of the Git source control program? but a Git diff file is just a text file generated by Git that clearly details lines of code have been changed/removed/added). If you are able to compile the latest mame sources 'as is' on your windows machine, then you should simply be able to apply my "diff" file to your sources and you should be able to compile will all my code changes (without any manual editing).

Does this sound like a good way forward? I guess first off, are you able to compile the latest mame source 'as is'?


Yolo_Swaggins

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 36
  • Last login:May 24, 2024, 06:54:37 am
  • I want to build my own arcade controls!
Hi, sorry for replying to such a old thread but i have been trying to make a steering wheel hack for hard drivin's airborne i managed to stop the bug in the new version of mame that made the car do donuts and the same for street drivin' and i managed to get the brake input working on it so it's now playable but hard Drivin's Airborne does this weird thing where the steering goes out of alignment when you crash the car and hold the wheel left or right untill the car respawns. When you crash the car the wheel edge value gets reset to =0 and the game seems to use the position the wheel was in at that moment to set a new center point.

 I was able to make a code mod that applied a offset to recenter it when this happens but once the offset is greater than 127 the wheel starts pulling to one side badly again. I tried forcing the center value of the wheels port_minmax to be sent as the wheel position at the exact moment that wheel edge =0  and then watches for wheel movement going past the center point to set it to 1 again as usual but that code doesnt seem to work maybe because the game reads the wheel positions value too fast before the mame driver can intercept it? I've been told that Hard Drivin's Airborne uses a spinner wheel in the original prototype hardware and that this is the reason why only this game has this behaviour. All the other hard drivin's/race drivin's are working fine.

Street drivin would make the car go in circles and i modded the code for that and i fixed the brake input being mapped to the wrong 8 bit ADC channel and the steering fix works on airborne to stop the car going in circles too and it's been merged into the latest version of the mame source code on github but i've been trying now for a couple of weeks to fix this bug in airborne and cannot get my code to work it's close but the maths behind it just doesn't work. I googled some keywords to see if i could find more info and found this post from 2013 where you seem to have basically made a fix for the old version of mame that im sure could fix the wheel alignment problem in Hard Drivin's Airborne. I cannot work out how to get your older code to be modified correctly to work in the latest version of mame and was wondering if you could maybe have a look at it again?

I'll post my almost working code below. It's replacing the wheel latch code in the harddriv.cpp file. I'm using a Logitech G923 wheel.

Code: [Select]
uint16_t harddriv_state::hdc68k_wheel_r()
{
    static const uint16_t g_latchpoint = 0x800;  // Central point for wheel detection.
    static int16_t wheel_offset = 0;             // Cumulative offset to adjust the wheel position based on game events.
    static bool last_wheel_edge = false;         // To track the last state of crossing the center.

    // Read the current wheel position from the 12-bit ADC port.
    uint16_t new_wheel_raw = m_12badc[0].read_safe(0xffff);

    // Apply the cumulative offset to align with the game's perceived center.
    int16_t new_wheel = static_cast<int16_t>(new_wheel_raw) + wheel_offset;

    // Clamping the wheel position to avoid overflow and keep it within expected range. I found out that this helped stop the steering drifting off center when you hit the 2nd last checkpoint on the mountain track.
    const uint16_t min_clamp_value = 0x063A; // Observed minimum value when wheel is turned all the way to the right
    const uint16_t max_clamp_value = 0x09C6; // Observed maximum value when wheel is turned all the way to the left
    if (new_wheel < min_clamp_value) new_wheel = min_clamp_value;
    if (new_wheel > max_clamp_value) new_wheel = max_clamp_value;

    // Edge detection logic to detect being at or crossing the center point using raw ADC data.
    if ((m_hdc68k_last_wheel < g_latchpoint && new_wheel_raw >= g_latchpoint) ||
        (m_hdc68k_last_wheel > g_latchpoint && new_wheel_raw <= g_latchpoint) ||
        new_wheel_raw == g_latchpoint) {
        m_hdc68k_wheel_edge = 1;  // Set edge flag when at or crossing the center based on raw input.
    }

    // Check if the wheel edge flag has changed from 1 to 0, as reset by the game.
    if (last_wheel_edge && !m_hdc68k_wheel_edge) {
        int16_t new_offset_adjustment = g_latchpoint - new_wheel_raw; // Calculate the offset adjustment
        new_offset_adjustment /= 1; // Significantly reduce the impact of each adjustment
        wheel_offset += (new_wheel_raw > g_latchpoint) ? -new_offset_adjustment : new_offset_adjustment;
        wheel_offset = std::clamp(wheel_offset, static_cast<int16_t>(-0xfff), static_cast<int16_t>(0xfff));  // Cap the offset to 3-digit hex range
        new_wheel = static_cast<int16_t>(new_wheel_raw) + wheel_offset; // Reapply the updated offset.

        // Re-clamp the wheel after offset adjustment
        if (new_wheel < min_clamp_value) new_wheel = min_clamp_value;
        if (new_wheel > max_clamp_value) new_wheel = max_clamp_value;
    }

    last_wheel_edge = m_hdc68k_wheel_edge; // Store the last known state of the wheel edge flag.

    // Display current wheel information for debugging.
    popmessage("Wheel Raw: %04X, Wheel Adjusted: %04X, Edge: %d, Offset: %04X (Hex), %d (Dec)",
           new_wheel_raw, new_wheel, m_hdc68k_wheel_edge, static_cast<uint16_t>(wheel_offset & 0xFFFF), wheel_offset);

    // Store the current wheel value for the next comparison.
    m_hdc68k_last_wheel = new_wheel_raw;

    // Return the processed wheel value, formatted as required.
    return (static_cast<uint16_t>(new_wheel) << 8) | 0xff;
}
« Last Edit: April 14, 2024, 11:04:35 pm by Yolo_Swaggins »

geecab

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 250
  • Last login:May 13, 2024, 06:19:08 am
Hi there!

 Unfortunately, my mind is a bit hazy regarding my hard drivin' fix/hack but will try and help as best I can.

A few things off the top of my head:

1. My fix was to make hard drivin’ play ok with mouse/spinner, rather than a SteeringWheel/Joystick (that has a fixed centre). So I’m a bit concerned that porting whatever I did back in 2013 might not help much.

2. You’re probably already aware of this, but I’ll mention it anyway - My fix only worked for the Compact British version of hard drivin’ roms (That expects optical encoders for steering, and did the weird centre point latching thing). The full cockpit versions of hard drivin’ & race drivin’ used potentiometers for steering. I am guessing when you say hard drivin’ & race drivin’ are working fine for you, that you must be using the cockpit roms? I don’t suppose there are any street drivin’ or airborne roms available for cockpit cabinets are there?

I have to say, I am fascinated to know what’s going on with this steering/latching stuff again, so at some point soon I’ll get building mame again and try to recreate what you are seeing (I’d also like to have go at street drivin’ and airborne too 😊)!

Yolo_Swaggins

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 36
  • Last login:May 24, 2024, 06:54:37 am
  • I want to build my own arcade controls!
Made some progress. Car seems to not go wonky after crashing in Hard Drivin's Airborne with this code and i can drive around untill the timer runs out and the wheels seem to stay straight  :dunno I've not tested it too much as in crashing with the wheel held all the way around to the left or the right because thats not how i play or anyone else i think? Been keeping my regular play style and if i forget to let go of the wheel when i crash theres a offset applied to the new_wheel output that keeps the centre point lined up with what the game is doing.

Original wheel edge bit latch code from harddriv_m.cpp here

Code: [Select]
uint16_t harddriv_state::hdc68k_wheel_r()
{
/* grab the new wheel value */
uint16_t new_wheel = m_12badc[0].read_safe(0xffff);

/* hack to display the wheel position */
if (machine().input().code_pressed(KEYCODE_LSHIFT))
popmessage("%04X", new_wheel);

/* if we crossed the center line, latch the edge bit */
if ((m_hdc68k_last_wheel / 0xf00) != (new_wheel / 0xf00))
m_hdc68k_wheel_edge = 1;

/* remember the last value and return the low 8 bits */
m_hdc68k_last_wheel = new_wheel;
return (new_wheel << 8) | 0xff;
}

And this is the modification i made for Hard Drivin's Airborne to work better and stop going out of alignment so much.

Code: [Select]
uint16_t harddriv_state::hdc68k_wheel_r()
{
    static const uint16_t g_latchpoint = 0x8000;  // Central point for wheel detection.
    static int16_t wheel_offset = 0;             // Cumulative offset to adjust the wheel position based on game events.
    static bool last_wheel_edge = false;         // To track the last state of crossing the center.

    // Read the current wheel position from the 12-bit ADC port.
    uint16_t new_wheel_raw = m_12badc[0].read_safe(0xffff) << 4;


    // Apply the cumulative offset to align with the game's perceived center.
    uint16_t new_wheel = new_wheel_raw + wheel_offset;

    // Edge detection logic to detect being at or crossing the center point using raw ADC data.
    if ((m_hdc68k_last_wheel < g_latchpoint && new_wheel_raw >= g_latchpoint) ||
        (m_hdc68k_last_wheel > g_latchpoint && new_wheel_raw <= g_latchpoint) ||
        new_wheel_raw == g_latchpoint) {
        m_hdc68k_wheel_edge = 1;  // Set edge flag when at or crossing the center based on raw input.
    }

    // Check if the wheel edge flag has changed from 1 to 0, as reset by the game.
    if (last_wheel_edge && !m_hdc68k_wheel_edge) {
        // Calculate the offset adjustment when the wheel is recalibrated.
        int16_t new_offset_adjustment = g_latchpoint - new_wheel_raw;

        // Apply the adjustment based on the current wheel position relative to the center
        if (new_wheel_raw > g_latchpoint) {
            // Wheel is to the left, need to decrease raw value towards the center
            wheel_offset += abs(new_offset_adjustment); // Use addition to adjust back towards the center
        } else {
            // Wheel is to the right, need to increase raw value towards the center
            wheel_offset -= abs(new_offset_adjustment); // Use subtraction if below the center
        }

        new_wheel = new_wheel_raw + wheel_offset; // Reapply the updated offset.
    }

    last_wheel_edge = m_hdc68k_wheel_edge; // Store the last known state of the wheel edge flag.

    // Display current wheel information for debugging.
    popmessage("Wheel Raw: %04X, Wheel Adjusted: %04X, Edge: %d, Offset: %X",
               new_wheel_raw, new_wheel, m_hdc68k_wheel_edge, wheel_offset);

    // Store the current wheel value for the next comparison.
    m_hdc68k_last_wheel = new_wheel_raw;  // Update last_wheel to the raw value to ensure proper comparison next cycle.

    // Return the processed wheel value, formatted as required.
    return (new_wheel << 4) | 0xff;

}

Seems to work fine in Street Drivin' too but that didn't have a wheel alignment issue after the last patch i did on it anyway and it doesn't set wheel edge to 0 when you crash so not a issue there or in any of the other ones really. This code might not play well with the compact versions because of how they do the wheel alignment I think you can just skip the wheel calibration setup and it should work anyway I applied a overclock in the source code for my own version of mame I'm testing and got the frame rate way higher in every one of them because i exceed the 400% limit on the GSP.

I made a dip switch that can toggle between 6 or 7 different types of wheel/sensitivity. I have my Logitech G923 wheel dialled in on it but not tidied the code up or labelled them appropriately yet i'll finish doing that later today or tomorrow after some sleep.

« Last Edit: April 17, 2024, 05:54:38 am by Yolo_Swaggins »

geecab

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 250
  • Last login:May 13, 2024, 06:19:08 am
Sounds good to me, great stuff :)

I'm still catching up. I've managed to get mame built from github and I can run hard drivin' (compact) and airborne. I've just not hacked the code yet (But I think that's what I need to do to get a feel for what is happening). I'll start experimenting with my wheel at the weekend.

Something that has always bugged me with the compact roms, is that it is not possible (yet, I think) to successfully calibrate the wheel using the service menu. I went and did some reading/investigation, trying to work out what values to expect (When viewing the "Control Inputs" service menu) from a successfully calibrated wheel. I didn't get very far. Thought I'd post my notes as they might be of interest/help:-

Code: [Select]
Compact harddrivin service manual (HDC_TM-329_2nd.pdf):
041787-02 Steering Encoder Disk.
043807-02 Centering Encoder Disk.

HDC_TM-329_2nd.pdf, From the "Control Inputs" service menu:
As you turn the steering wheel clockwise, the hexadecimal number should increase and change to zero once every turn.
As you turn the wheel counterclockwise, the number should decrease. Everytime the steering wheel passes the center position,
the words center edge should change from blue to green.

HDC_TM-329_2nd.pdf, from a section near the end about installing a new encoder wheel:
Install the steering wheel with the center spoke down. Make sure the single hole on the centering disk is between the opitcal reader on the centering
PCB so the steering wheel will be correctly centered.

From the Race Drivin' Compact Manual, for the Main Board Memory Map, it says:
OPTO: Optical Steering Wheel Reader
400000 (R) OPTORD Read the Optical Counter
404000 (W) OPTORES Reset the Optical Counter
408000 (W) CENRES Reset the Optical Centre Flag

I found a picture of a 041787-02 Steering Encoder Disk, and counted 72 holes. Based on this, I don't think we should see a steering value (When viewing the "Control Inputs" service menu) reported that exceeds 72 (Or maybe 144 if we are counting teeth passing as well has holes). Thus, when turning clockwise you'll see 0 increase to 72 then back to 0 etc... When turning anticlockwise you'll see 72 decrease 0 then back to 72 etc... In mame, I think we see values much greater than 72, this might cause strangeness. I think I'll try and hack things so that I force these 'ideal' values to occur at calibration.

Something odd I noticed, is that in mame when viewing the "Control Inputs" service menu and turning the wheel clockwise, I see the hexadecimal number decrease, not increase...

:)

PL1

  • Global Moderator
  • Trade Count: (+1)
  • Full Member
  • *****
  • Offline Offline
  • Posts: 9413
  • Last login:Today at 01:02:13 am
  • Designated spam hunter
Something odd I noticed, is that in mame when viewing the "Control Inputs" service menu and turning the wheel clockwise, I see the hexadecimal number decrease, not increase...
Sounds like the optical data lines (A and B) are swapped, which reverses the axis.
- It could be your setup (not likely), settings, the connections in MAME (also not likely), or the axis reversal setting in MAME's Analog menu.   :dunno




Scott
« Last Edit: April 17, 2024, 11:04:36 am by PL1 »

Yolo_Swaggins

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 36
  • Last login:May 24, 2024, 06:54:37 am
  • I want to build my own arcade controls!
Sounds good to me, great stuff :)

I'm still catching up. I've managed to get mame built from github and I can run hard drivin' (compact) and airborne. I've just not hacked the code yet (But I think that's what I need to do to get a feel for what is happening). I'll start experimenting with my wheel at the weekend.

Something that has always bugged me with the compact roms, is that it is not possible (yet, I think) to successfully calibrate the wheel using the service menu. I went and did some reading/investigation, trying to work out what values to expect (When viewing the "Control Inputs" service menu) from a successfully calibrated wheel. I didn't get very far. Thought I'd post my notes as they might be of interest/help:-

Code: [Select]
Compact harddrivin service manual (HDC_TM-329_2nd.pdf):
041787-02 Steering Encoder Disk.
043807-02 Centering Encoder Disk.

HDC_TM-329_2nd.pdf, From the "Control Inputs" service menu:
As you turn the steering wheel clockwise, the hexadecimal number should increase and change to zero once every turn.
As you turn the wheel counterclockwise, the number should decrease. Everytime the steering wheel passes the center position,
the words center edge should change from blue to green.

HDC_TM-329_2nd.pdf, from a section near the end about installing a new encoder wheel:
Install the steering wheel with the center spoke down. Make sure the single hole on the centering disk is between the opitcal reader on the centering
PCB so the steering wheel will be correctly centered.

From the Race Drivin' Compact Manual, for the Main Board Memory Map, it says:
OPTO: Optical Steering Wheel Reader
400000 (R) OPTORD Read the Optical Counter
404000 (W) OPTORES Reset the Optical Counter
408000 (W) CENRES Reset the Optical Centre Flag

I found a picture of a 041787-02 Steering Encoder Disk, and counted 72 holes. Based on this, I don't think we should see a steering value (When viewing the "Control Inputs" service menu) reported that exceeds 72 (Or maybe 144 if we are counting teeth passing as well has holes). Thus, when turning clockwise you'll see 0 increase to 72 then back to 0 etc... When turning anticlockwise you'll see 72 decrease 0 then back to 72 etc... In mame, I think we see values much greater than 72, this might cause strangeness. I think I'll try and hack things so that I force these 'ideal' values to occur at calibration.

Something odd I noticed, is that in mame when viewing the "Control Inputs" service menu and turning the wheel clockwise, I see the hexadecimal number decrease, not increase...

:)

Wow that is the info we have been looking for!
 :notworthy:


From the Race Drivin' Compact Manual, for the Main Board Memory Map, it says:
OPTO: Optical Steering Wheel Reader
400000    (R)   OPTORD      Read the Optical Counter
404000   (W)   OPTORES      Reset the Optical Counter
408000   (W)   CENRES      Reset the Optical Centre Flag

Does this part have any relation to this code in the harddriv.cpp file?

Code: [Select]
PORT_START("mainpcb:a80000")
PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_CUSTOM )  /* center edge on steering wheel */

I hacked the code about a month ago so that when i press a button i assign in the input assignment menu in mame it activates this function. When i press the button you see the center point indicator light up and the wheel reset back to 0000...............

There is no wheel calibration in street drivin or in hard drivin's airborne like the other games do. It only has the menu that shows the output value of it when your moving it around. I was thinking if i set the port_minmax from 0 to 71 or 143 ( if the games code counts 0 as a value) then trigger the code above to activate each time this reaches the centerpoint the same way it does when i push the button with this code i "hacked"

Code: [Select]
PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_CUSTOM ) PORT_TOGGLE PORT_NAME("wheel centre1")
Maybe then it will function correctly........then all we would need to do is implement the proper scaling for a 900/720/270 degree wheel etc so that it only goes up to the max value when you turn it whatever direction it should go like 71/72 or 143/144 for clockwise/counter clockwise max then the opposite for the other direction. I probobly explained that in a confusing way lol.

I'm having another mess around with it today. I made some other code that was implementing the stuff i was doing in the other code i sent you plus it buffers/smooths the wheel output and truncates the last digit from the hex value you so it doesnt fluctuate as quickly when you turn the wheel around and it seems to have fixed the wheel going out of alignment when you turn it fast, that was another issue you could kinda stop from happening if you made smoother wheel turns physicly but sometimes just not possible if you want or need a sharp turn. The recentring logic i did before doesnt work as well in the new code though i dont know if it's because im messing with the values before i apply the offset. Now that i have the info you provided i don't think any of that is even going to be needed lol

Code: [Select]
#include <numeric>  // For std::accumulate

// Function to calculate the Exponential Moving Average (EMA)
uint16_t harddriv_state::calculate_ema(uint16_t new_value) {
    static float ema_wheel = 0.0;  // Persistent storage of EMA across calls
    static const float alpha = 0.05;  // Smoothing constant

    ema_wheel = alpha * new_value + (1 - alpha) * ema_wheel;

    return static_cast<uint16_t>(ema_wheel);
}

// Main function to handle wheel input and apply adjustments
uint16_t harddriv_state::hdc68k_wheel_r() {
    static const uint16_t g_latchpoint = 0x8000;
    static int16_t wheel_offset = 0;
    static bool last_wheel_edge = false;

    uint16_t new_wheel_raw = m_12badc[0].read_safe(0xffff) << 4;
    uint16_t ema_wheel = calculate_ema(new_wheel_raw);
    uint16_t new_wheel = ema_wheel + wheel_offset;

    // Set wheel edge to 1 if at or crossing the center and it is not already set
    if (!m_hdc68k_wheel_edge &&
        ((m_hdc68k_last_wheel < g_latchpoint && new_wheel_raw >= g_latchpoint) ||
         (m_hdc68k_last_wheel > g_latchpoint && new_wheel_raw <= g_latchpoint))) {
        m_hdc68k_wheel_edge = 1;
    }

    // Handle wheel edge reset and offset adjustment
    if (last_wheel_edge && !m_hdc68k_wheel_edge) {
        int16_t adjustment = g_latchpoint - ema_wheel;
        adjustment = (adjustment / 0x10) * 0x10;  // Normalize adjustment to remove last digit
        wheel_offset += (new_wheel_raw > g_latchpoint) ? -abs(adjustment) : abs(adjustment);
        wheel_offset = (wheel_offset / 0x10) * 0x10; // Normalize wheel_offset to remove last digit
        new_wheel = ema_wheel + wheel_offset;
        new_wheel = (new_wheel / 0x10) * 0x10;  // Normalize new_wheel to remove last digit
    }

    last_wheel_edge = m_hdc68k_wheel_edge; // Store the last state of the wheel edge
    m_hdc68k_last_wheel = new_wheel_raw;   // Update the last wheel value for comparison in the next cycle

    // Continuously show the debugging message
    popmessage("Wheel Raw: %04X, EMA Wheel: %04X, Adjusted: %04X, Edge: %d, Offset: %d",
               new_wheel_raw, ema_wheel, new_wheel, m_hdc68k_wheel_edge, wheel_offset);

    return (new_wheel << 4) | 0xff;
}


Thats where i was last night before i woke up and saw your reply today, i actualy had a look yesterday but none of it was sinking in as i had no sleep for a couple of days lol. I'll get back to you if it's been improved any. Cheers! :applaud:





Yolo_Swaggins

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 36
  • Last login:May 24, 2024, 06:54:37 am
  • I want to build my own arcade controls!
My wheel center point button can be used to turn the wheel all the way to the left then i hit the button twice and in the input menu you now see the value of the wheel going from 0000 to 0702 when you turn it to the right. I'm just going to change my port_minmax to see if i can get this thing to tally up and alter the code if i need to.

Ok i just tried editing the port_minmax and now when i turn the wheel all the way to the left and hit the wheel edge toggle button i made it starts reading the wheel values properly from 0000 all the way on the left all the way up to 0058 on the right which in decimal = 72. if it needs flipped the other way it can be easily done. Just going to check the behaviour of the wheel in game.  I think that button for wheel center has to be triggered to activate every time that the wheel reaches the right(0058 in hex 72 dec)? Or left if it's the wrong way around? Either way it sounds closer to the original game. I would need to see what the values were on a real machine to make it accurate because we don't know how the machine was scaling these numbers. It was using a 12 bit ADC port so thats why it's a 4 digit hex value. Seems like a waste of hardware for a wheel that only had to read 72 notches? Maybe the wheel edge detection in the other harddriv_m.cpp file just needs the value for the edge detection to trigger to be tweaked to match the real hardware in there they have it set to 0xf00.
« Last Edit: April 20, 2024, 08:26:27 am by Yolo_Swaggins »

Yolo_Swaggins

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 36
  • Last login:May 24, 2024, 06:54:37 am
  • I want to build my own arcade controls!
After having a think about how the machine works, and this is just me thinking out loud having no actual experience with one of the machines in it's testmode and only working with it on the mame emulator is that we need to split the "PORT_MINMAX" of the wheels range in mame into 8 virtual segments meaning it would need to be a port_minmax range that can be divided into 8 segements with no remainder etc? Then what we would need to do in the code is maybe trigger 2 flags each time it hits or passes one of the 8th's.........like each time it passed one the flag for  "404000   (W)   OPTORES Reset the Optical Counter" would need to be triggered once to reset the wheel position to 0000 and at the same time trigger the "408000   (W)   CENRES Reset the Optical Centre Flag" so that it knows that was just 1 full rotation of the emulated wheel in mame based off the port_minmax range being given to it? Depending on how these actualy work you might only need to use the "408000   (W)   CENRES Reset the Optical Centre Flag" when it reaches the middle 8th of the wheels range in mame.......so it would be at 4/8ths...........to let the game know this was the middle we reached.

Hopefully you can understand what i mean by this! I think it would allow the machine being emulated in mame to do the correct maths or functions when the car crashes in airborne or you go offraod untill the timer hits 0. it would know in it's memory at what point the wheel was actually at more in line with the real hardware? I could be totally wrong but it's been annoying me for a while that i got the game to work but there couple of small bugs kinda ruin it if your not aware that you need to let go of the wheel as soon as you crash the car or just before your offroad timer reaches 0  :dunno I seem to have fixed or heavily mitigated the "wheel going out of alignment when turning the wheel fast" in my own version of the game but my guess is you would need to plug in the appropriate port_minmax values that work for your specific wheel or input device. if a calibration menu could be added to mame for this purpose it would be pretty cool, it could normalise the input of anyones wheel/controller to work with each game better.

geecab

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 250
  • Last login:May 13, 2024, 06:19:08 am
Hi Yolo!

Really good work. Unfortunately, I’ve not really made much/any progress. I've been focusing on hacking/understanding the harddrivcb (Compact British) rom.

I’ve been watching this youtube clip because at 24:32 the chap manages to successfully calibrate his wheel:

   

His old settings look like:
    OLD CNTSPTRN 1080 PATCEN -98
His new settings look like (after successfully calibrating):
    NEW CNTSPTRN 1110 PATCEN 14

I hacked mame to trigger the centre edge after 256 steering wheel values. After calibrating things looked like this for me:
    NEW CNTSPTRN 256 PATCEN 0

I concluded that CNTSPTRN is the amount of steering wheel values for 1 complete turn. I was clearly wrong about my 72 theory. I’m not sure where the actual arcade value of 1110 comes from. I guess I’ll just accept that the encoder processor gives you about 1024 values per turn. I can see now from PL1's previous post (Thanks for the post btw!) why you'd get more values out of an optical encoder than just teeth count. The PATCEN thing is to do with straightening up the wheel at the end of the calibration test, if its not dead straight (i.e. to one side of the centre edge) then you get a non-zero number.

Whilst the above is interesting-ish, I realise it doesn’t really help us…

I had observed the centre edge triggering working well in the service menu (You see the steering wheel value get reset to zero each time ‘Centre Edge’ turns green), and you also see the
harddriv_state::hdc68k_wheel_edge_reset_w(uint16_t data) function being called each time a reset happens. However, in-game, when attempting to trigger the centre edge, I had noticed the hdc68k_wheel_edge_reset_w callback never gets called…

You recently wrote about those encoder addresses you’d seen in harddriv.cpp (Good shout by the way! :)), and I got excited and added this:-
PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_CUSTOM )  PORT_NAME("Encoder Reset")

I removed my code that automatically triggers the Centre Edge reset (That stuff to do with processing the m_hdc68k_wheel_edge flag and setting the result ^= 0x4000 thing in hdc68k_port1_r) , and assigned a key to the new ‘Encoder Reset’ setting so I could just trigger everything manually.

In the service menu, everything worked well (The manual reset set the steering wheel value to zero, and  hdc68k_wheel_edge_reset_w() was called). In-game, the manual reset now did result in hdc68k_wheel_edge_reset_w() being called, but it seemed to have no effect on the actual steering. Its like the game just ignores the manual encoder reset whilst being played…. Strange… The game also (when you crash) resets the steering to some weird values (I guess to the nearest centre edge) and doesn’t tell you when its done it...

I also added...
PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_CUSTOM )  PORT_NAME("Centre Reset")
And assigned a key to that, but that seemed to have absolutely no effect on anything.

Time to sleep on this for a bit… Its interesting stuff :) but frustrating  :timebomb:

PL1

  • Global Moderator
  • Trade Count: (+1)
  • Full Member
  • *****
  • Offline Offline
  • Posts: 9413
  • Last login:Today at 01:02:13 am
  • Designated spam hunter
I can see now from PL1's previous post (Thanks for the post btw!) why you'd get more values out of an optical encoder than just teeth count.
Glad to assist.   ;D

For anyone wanting to learn more on optos and encoder wheels including how to calculate opto spacing and encoder wheel measurements, check out this thread.

Am I correct in thinking that as it will be quadrature each hole in the wheel will actually be 2 pulses?
Assuming you have a properly paired encoder wheel and opto spacing, quadrature waveforms have 4 phases per tooth/gap.
- The good spacing image shows the encoder wheel at the left edge of Phase 1.
- Data line A is transitioning from high (not blocked) to low (blocked) and data line B is in the middle of being blocked.
- As you rotate the encoder wheel clockwise, the blocking and un-blocking of the optos will produce the quadrature waveforms shown.




Scott

Yolo_Swaggins

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 36
  • Last login:May 24, 2024, 06:54:37 am
  • I want to build my own arcade controls!
Hi Yolo!

Really good work. Unfortunately, I’ve not really made much/any progress. I've been focusing on hacking/understanding the harddrivcb (Compact British) rom.

I’ve been watching this youtube clip because at 24:32 the chap manages to successfully calibrate his wheel:

   

His old settings look like:
    OLD CNTSPTRN 1080 PATCEN -98
His new settings look like (after successfully calibrating):
    NEW CNTSPTRN 1110 PATCEN 14

I hacked mame to trigger the centre edge after 256 steering wheel values. After calibrating things looked like this for me:
    NEW CNTSPTRN 256 PATCEN 0

I concluded that CNTSPTRN is the amount of steering wheel values for 1 complete turn. I was clearly wrong about my 72 theory. I’m not sure where the actual arcade value of 1110 comes from. I guess I’ll just accept that the encoder processor gives you about 1024 values per turn. I can see now from PL1's previous post (Thanks for the post btw!) why you'd get more values out of an optical encoder than just teeth count. The PATCEN thing is to do with straightening up the wheel at the end of the calibration test, if its not dead straight (i.e. to one side of the centre edge) then you get a non-zero number.

Whilst the above is interesting-ish, I realise it doesn’t really help us…

I had observed the centre edge triggering working well in the service menu (You see the steering wheel value get reset to zero each time ‘Centre Edge’ turns green), and you also see the
harddriv_state::hdc68k_wheel_edge_reset_w(uint16_t data) function being called each time a reset happens. However, in-game, when attempting to trigger the centre edge, I had noticed the hdc68k_wheel_edge_reset_w callback never gets called…

You recently wrote about those encoder addresses you’d seen in harddriv.cpp (Good shout by the way! :)), and I got excited and added this:-
PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_CUSTOM )  PORT_NAME("Encoder Reset")

I removed my code that automatically triggers the Centre Edge reset (That stuff to do with processing the m_hdc68k_wheel_edge flag and setting the result ^= 0x4000 thing in hdc68k_port1_r) , and assigned a key to the new ‘Encoder Reset’ setting so I could just trigger everything manually.

In the service menu, everything worked well (The manual reset set the steering wheel value to zero, and  hdc68k_wheel_edge_reset_w() was called). In-game, the manual reset now did result in hdc68k_wheel_edge_reset_w() being called, but it seemed to have no effect on the actual steering. Its like the game just ignores the manual encoder reset whilst being played…. Strange… The game also (when you crash) resets the steering to some weird values (I guess to the nearest centre edge) and doesn’t tell you when its done it...

I also added...
PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_CUSTOM )  PORT_NAME("Centre Reset")
And assigned a key to that, but that seemed to have absolutely no effect on anything.

Time to sleep on this for a bit… Its interesting stuff :) but frustrating  :timebomb:

I fixed the brake calibration using the same method i used to fix it on Street Drivin'.

Code: [Select]
PORT_START("mainpcb:8BADC.6")        /* b00000 - 8 bit ADC 6 - force brake */
PORT_BIT( 0xff, 0x20, IPT_PEDAL2 ) PORT_SENSITIVITY(25) PORT_MINMAX(0x20, 0xf0) PORT_KEYDELTA(40) PORT_REVERSE PORT_NAME("Force Brake")

Still working on the wheel , i got it to register the 4 turns to the left but in the game the center is off and wheel pulls slightly to the right hand side.

We'll fix this eventually lol.

I now reckon from checking out the compact versioin that airborne might use the same hardware for the wheel but just implements it differently if that makes sense. Might even be the same. That flag you notice that doesnt get called i think is key to it. maybe it has to have the IP_ACTIVE_HIGH instead of low?
« Last Edit: April 20, 2024, 02:58:16 pm by Yolo_Swaggins »

geecab

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 250
  • Last login:May 13, 2024, 06:19:08 am
Cool, I'll try the IP_ACTIVE_HIGH things and see what happens.

>>I now reckon from checking out the compact versioin that airborne might use the same hardware for the wheel but just implements it differently if that makes sense.

Agreed.

One thing I forgot to mention. I started looking at the void harddriv_state::hd68k_wr2_write(offs_t offset, uint16_t data) function. I thought it could be used to retrieve the steering wheel value (the one that you see in the service menu) during the actual game. I thought if we could do that, we'd be able to see exactly where the game thinks the steering centre is after crashing the car. Then maybe following a crash, when you're turning your key to start (and your physical wheel would be centred at that point), you'd be able to set a really accurate offset at that point. That probably doesn't make any sense... Anyways, I'm not really sure what calling hd68k_wr2_write gives you. Its values do change based on steering position though (but only during the game, It never gets called during the service menu).

I'm not sure if this is helpful or not, but here's some hacky code I added to see if I could work out what's going on...

Code: [Select]
void harddriv_state::hd68k_wr2_write(offs_t offset, uint16_t data)
{
    static unsigned int static_count = 0;
    static uint8_t static_data[4];

    static_data[static_count] = data >> 8;
    static_count++;
    if (static_count > 3) static_count = 0;

    if (offset == 0)
    {
        // logerror("Steering Wheel Latch = %02X\n", data);
        m_wheel = data >> 8;
    }
    else
        logerror("/WR2(%04X)=%02X\n", offset, data);


    // The 'data' repeats every 4 times this function is called.
    // You should see the data repeating in this debug output...
    printf("wr2 offset=%04X data=%04X\n", offset, data);

    // Try to put the data together (we must be writing 16bits somewhere?) to see what's going on...
    if (machine().input().code_pressed(KEYCODE_RSHIFT))
    {
        popmessage("wr2 [%02X] [%02X] [%02X] [%02X]", static_data[0], static_data[1], static_data[2], static_data[3]);
    }
}

Yolo_Swaggins

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 36
  • Last login:May 24, 2024, 06:54:37 am
  • I want to build my own arcade controls!
I concluded that CNTSPTRN is the amount of steering wheel values for 1 complete turn. I was clearly wrong about my 72 theory. I’m not sure where the actual arcade value of 1110 comes from

I'm sure i saw somebody on youtube or some other forum saying they had a 1100 degree wheel in the arcade. I don't know if it's true or not but it matches exactly what you said here?

geecab

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 250
  • Last login:May 13, 2024, 06:19:08 am
Still no joy my end...

I've just done a bit more investigation regarding the values I'm seeing in  hd68k_wr2_write().

The range of values it reports is different depending on if you are watching the attract mode, or are viewing the "choose manual or auto transmission screen", or actually driving the car. I have been focusing on the values it reports whilst driving the car during the game.

Here's my latest hack:-
Code: [Select]
void harddriv_state::hd68k_wr2_write(offs_t offset, uint16_t data)
{
    static unsigned int static_count = 0;
    static uint8_t static_data[4];
    static bool seeing_e000 = false;

    static_data[static_count] = data >> 8;
    static_count++;
    if (static_count > 3) static_count = 0;

    if (offset == 0)
    {
        // logerror("Steering Wheel Latch = %02X\n", data);
        m_wheel = data >> 8;
    }
    else
        logerror("/WR2(%04X)=%02X\n", offset, data);

    if ((static_data[2] == 0xE0) && (static_data[0] == 0x00))
    {
        if(!seeing_e000)
        {
            popmessage("wr2 MIDDLE");
            seeing_e000 = true;
        }
    }
    else
    {
        seeing_e000 = false;
    }


    // The 'data' repeats every 4 times this function is called.
    // You should see the data repeating in this debug output...
    //printf("wr2 offset=%04X data=%04X\n", offset, data);

    // Try to put the data together (we must be writing 16bits somewhere?) to see what's going on...
    if (machine().input().code_pressed(KEYCODE_RSHIFT))
    {
        popmessage("wr2 [%02X%02X]", static_data[2], static_data[0]);
    }
}

I noticed that combining the 1st and the 3rd byte showed me a range of values where the middle point is 0xE000.
From middle, if I turn the wheel fully left the value changes from 0xE000, 0xE001, 0xE002, ... up to  0xE112 (Thus 274 possible values)
From middle, if I turn the wheel fully right the value changes from 0xE000, 0xFF1F, 0xFF1E, ... down to 0xFE0D  (Thus, also 274 possible values)
Giving an overall range of 548 values.

No matter what I calibrated the the wheel to in the system settings, this range remained the same (Always 548 values, left limit 0xE112, mid point=0xE000, right limit=0xFE0D).

I also noticed that when you crash with the wheel substantially turned, the value is reset back to 0xE000 when the car re-spawns. If you crash during soft turn, the value remains the same as whatever the value was when you crashed.

I not sure this helps us but thought it was worth a mention :)

Yolo_Swaggins

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 36
  • Last login:May 24, 2024, 06:54:37 am
  • I want to build my own arcade controls!
Still no joy my end...

I've just done a bit more investigation regarding the values I'm seeing in  hd68k_wr2_write().

The range of values it reports is different depending on if you are watching the attract mode, or are viewing the "choose manual or auto transmission screen", or actually driving the car. I have been focusing on the values it reports whilst driving the car during the game.

Here's my latest hack:-
Code: [Select]
void harddriv_state::hd68k_wr2_write(offs_t offset, uint16_t data)
{
    static unsigned int static_count = 0;
    static uint8_t static_data[4];
    static bool seeing_e000 = false;

    static_data[static_count] = data >> 8;
    static_count++;
    if (static_count > 3) static_count = 0;

    if (offset == 0)
    {
        // logerror("Steering Wheel Latch = %02X\n", data);
        m_wheel = data >> 8;
    }
    else
        logerror("/WR2(%04X)=%02X\n", offset, data);

    if ((static_data[2] == 0xE0) && (static_data[0] == 0x00))
    {
        if(!seeing_e000)
        {
            popmessage("wr2 MIDDLE");
            seeing_e000 = true;
        }
    }
    else
    {
        seeing_e000 = false;
    }


    // The 'data' repeats every 4 times this function is called.
    // You should see the data repeating in this debug output...
    //printf("wr2 offset=%04X data=%04X\n", offset, data);

    // Try to put the data together (we must be writing 16bits somewhere?) to see what's going on...
    if (machine().input().code_pressed(KEYCODE_RSHIFT))
    {
        popmessage("wr2 [%02X%02X]", static_data[2], static_data[0]);
    }
}

I noticed that combining the 1st and the 3rd byte showed me a range of values where the middle point is 0xE000.
From middle, if I turn the wheel fully left the value changes from 0xE000, 0xE001, 0xE002, ... up to  0xE112 (Thus 274 possible values)
From middle, if I turn the wheel fully right the value changes from 0xE000, 0xFF1F, 0xFF1E, ... down to 0xFE0D  (Thus, also 274 possible values)
Giving an overall range of 548 values.

No matter what I calibrated the the wheel to in the system settings, this range remained the same (Always 548 values, left limit 0xE112, mid point=0xE000, right limit=0xFE0D).

I also noticed that when you crash with the wheel substantially turned, the value is reset back to 0xE000 when the car re-spawns. If you crash during soft turn, the value remains the same as whatever the value was when you crashed.

I not sure this helps us but thought it was worth a mention :)

I was experimenting with the << 4 lower 8 bit thing etc and found i can give myself a 4 digit port_minmax on the 12 bit ADC and the wheel will use the whole 4 digits of it. Surely if we just get the wheel input to match 1-1 with what the game expects we could use those values to apply and subtract offsets to the wheel to keep it in exact or almost exact alignment live as we are playing? I had it showing the same values in the test menu as it did from my raw wheel input. The code i had that worked the most for offset adjustment would work for small turns when you crashed.......as soon as the offset exceeded 127 or went below -128 (uint8_t?) the offset being applied wouldnt match what was happening behind the scenes with the game.......you could make several crashes with the wheel turned a bit and it would stop it pulling to the one side which was better than nothing but with the new code i made that stops the wheel going out of alignment whilst driving and moving the wheel very fast seems to gimp my offset alignment procedure so it doesn't even work at all but that code to stop it happening when your not even crashing and just moving the wheel quick is more valuable to the gameplay and lets me get a much higher score and a longer play session. it used to happen to me on the 2nd last checkpoint in the mountain track and i assumed it was a trigger set by the checkpoint to reset the wheel but i found out the other day it's just because the wheel value changes so fast when your making quick sharp turns on that part of the track it sends it out of synch with the wheel or something. I created a code that buffers the wheel input with EMA algorithm which introduces a tiny bit of latency but stops the wheel ever going out of alignment when you move it fast from left to right etc. I messed around with the values and the port_minmax and got a good wheel sensitivity and responsiveness but stil mitigated the glitch that happens when you move it too fast. My method is a way too complex solution the fix could be much more simply those values you have found must be the key to keeping everything in synch and alignment!  :dunno

I'm talking about airborne and i assume your still talking about the compact version of race drivin'? it's slightly different behaviour between the 2 games but i reckon the solution for one will fix the other.

« Last Edit: April 21, 2024, 03:15:15 pm by Yolo_Swaggins »

Yolo_Swaggins

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 36
  • Last login:May 24, 2024, 06:54:37 am
  • I want to build my own arcade controls!
I got the steering to work on race drivin' compact. I'm going to have a quick once over of the code and i'll stick it up here and put it on github as a commit to the mamedev/master.

I can get past the wheel calibration but it involves pressing a button when you reach the far right to register the turn and the values that show up arent accurate or inline with what should appear on the real machine but when you start the game it just behaves the same as the other Race Drivin' and Hard Drivin'.  ;D

geecab

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 250
  • Last login:May 13, 2024, 06:19:08 am
Wow! Well done Yolo, sounds like progress at last!! :) Can't wait to see the changes :)

Yolo_Swaggins

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 36
  • Last login:May 24, 2024, 06:54:37 am
  • I want to build my own arcade controls!
Wow! Well done Yolo, sounds like progress at last!! :) Can't wait to see the changes :)

I still haven't got round to using the info you gave me on Airborne im using my older hack to get that working and added differennt wheel sensitivities /port_minmax's through a dip switch menu that changes your wheel to a different calibration and goes through EMA filtering. The fix for the compact version of race drivin' was easier than i thought lol. The test menu calibration is still gimped but you can just skip it.

https://github.com/Madcadden/mame/tree/Madcadden-Hard-Drivin-Race-Drivin-MAME-Build

Code: [Select]
PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_CUSTOM )  PORT_TOGGLE PORT_NAME("Center Wheel Edge")  /* center edge on steering wheel, IPT_ACTIVE_HIGH fixes the steering alignment in game! */

Code: [Select]
#include <numeric>  // For std::accumulate

// Function to calculate the Exponential Moving Average (EMA)
uint16_t harddriv_state::calculate_ema(uint16_t new_value) {
    static float ema_wheel = 0.0;  // Persistent storage of EMA across calls
    static bool ema_initialized = false;  // To check if ema_wheel is initialized
    static const float alpha = 0.082;  // Smoothing constant

    if (!ema_initialized) {
        ema_wheel = new_value;  // Initialize EMA with the first raw value
        ema_initialized = true;
    } else {
        ema_wheel = alpha * new_value + (1 - alpha) * ema_wheel;
    }

    // Ensure the last digit of the EMA output is always zero
    return static_cast<uint16_t>(ema_wheel) & 0xFFF0;
}

// Main function to handle wheel input and apply adjustments
uint16_t harddriv_state::hdc68k_wheel_r() {
    static const uint16_t g_latchpoint = 0x8000;  // Center point for wheel edge detection
    static bool initialized = false;  // To ensure the initial setup is done only once
    static int16_t last_offset = 0;   // To store the last offset applied
    static int wheel_edge_zero_count = 0;  // Counter for how many times wheel edge has been set to 0
    static int resets_recorded = 0;   // Counter to delay offset recording

    // Initial setup
    if (!initialized) {
        m_hdc68k_wheel_edge = 1;  // Set the edge to 1 at the start of the game
        initialized = true;  // Prevent further initialization
    }

    uint16_t new_wheel_raw = m_12badc[0].read_safe(0xffff) << 4;
    uint16_t ema_wheel = calculate_ema(new_wheel_raw) << 3;

    // If the game has reset the wheel edge to 0, handle the deviation
    if (m_hdc68k_wheel_edge == 0) {
        wheel_edge_zero_count++;  // Increment the counter each time wheel edge is 0

        // Record adjustment only after the first reset is recorded
        if (resets_recorded > 0) {
            // Check for deviation from the center point
            if (ema_wheel != g_latchpoint) {
                int16_t adjustment = g_latchpoint - ema_wheel;  // Calculate the necessary adjustment
                ema_wheel += adjustment;  // Correct the EMA to the center point
                last_offset = adjustment;  // Store the last offset applied
            } else {
                last_offset = 0;  // No offset needed if already at center
            }
        }

        resets_recorded++;  // Increment resets recorded after processing
        m_hdc68k_wheel_edge = 1;  // Set the wheel edge back to 1 after handling the adjustment
    }

    // Debugging output
    popmessage("Wheel Raw: %04X, EMA Wheel: %04X, Last Offset: %d, Edge: %d, Edge 0 Count: %d, Resets Recorded: %d",
               new_wheel_raw, ema_wheel, last_offset, m_hdc68k_wheel_edge, wheel_edge_zero_count, resets_recorded);

    return ema_wheel;  // Use the adjusted EMA directly
}


Code: [Select]
    // Handlers for the wheel and port inputs
    m_maincpu->space(AS_PROGRAM).install_read_handler(0x400000, 0x400001, read16smo_delegate(*this, FUNC(harddriv_state::hdc68k_wheel_r)));
    m_maincpu->space(AS_PROGRAM).install_write_handler(0x408000, 0x408001, write16smo_delegate(*this, FUNC(harddriv_state::hdc68k_wheel_edge_reset_w)));
    m_maincpu->space(AS_PROGRAM).install_read_handler(0xa80000, 0xafffff, read16smo_delegate(*this, FUNC(harddriv_state::hda68k_port1_r)));


Code: [Select]
class harddriv_state : public device_t
{
public:

void reset_wheel_offset();  // Function to reset the wheel offset


harddriv_state(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);

void driver_msp(machine_config &config);
@@ -98,8 +102,21 @@ class harddriv_state : public device_t
void video_int_write_line(int state);
void sound_int_write_line(int state);

//New method declarations for steering logic
uint16_t hdc68k_wheel_r();  // Ensure this is declared only once
uint16_t custom_steering_logic();
uint16_t original_steering_logic();

// Add your EMA function declaration here
uint16_t calculate_ema(uint16_t new_value); // Corrected declaration


protected:

 int16_t wheel_offset = 0;  // Make wheel_offset a member variable

void init_video();

INTERRUPT_GEN_MEMBER(hd68k_irq_gen);
TIMER_CALLBACK_MEMBER(deferred_adsp_bank_switch);
TIMER_CALLBACK_MEMBER(rddsp32_sync_cb);
@@ -122,7 +139,7 @@ class harddriv_state : public device_t
uint16_t hd68k_adc12_r();
uint16_t hdc68k_port1_r();
uint16_t hda68k_port1_r();
// uint16_t hdc68k_wheel_r();
uint16_t hd68k_sound_reset_r();

void hd68k_adc_control_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);

geecab

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 250
  • Last login:May 13, 2024, 06:19:08 am
Code: [Select]
PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_CUSTOM )  PORT_TOGGLE PORT_NAME("Center Wheel Edge")  /* center edge on steering wheel, IPT_ACTIVE_HIGH fixes the steering alignment in game! */

OMG lol! I didn't try IP_ACTIVE_HIGH on 0x4000, only on 0x8000. I didn't change it on 0x4000 because it looked like it was doing the right thing in the service menu :p I honestly can't wait to try this later, nice one Yolo and well found! :)

Yolo_Swaggins

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 36
  • Last login:May 24, 2024, 06:54:37 am
  • I want to build my own arcade controls!
Code: [Select]
PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_CUSTOM )  PORT_TOGGLE PORT_NAME("Center Wheel Edge")  /* center edge on steering wheel, IPT_ACTIVE_HIGH fixes the steering alignment in game! */

OMG lol! I didn't try IP_ACTIVE_HIGH on 0x4000, only on 0x8000. I didn't change it on 0x4000 because it looked like it was doing the right thing in the service menu :p I honestly can't wait to try this later, nice one Yolo and well found! :)

I didn't try it either because i already tried it on Airborne and it didnt fix that or stop the behaviour that makes the wheel center get reset, but because i had made that button to activate it i noticed in the reacedrivc level select and car select screen that if i held the button down the wheel would follow my logitech g932 like it should so i just changed it to high to keep it on all the time lol

Another thing i will mention because it might help out in fixing airborne etc is that my wheel EMA code didnt work on street drivin'............i had to start the game then hit f3 to reset the machine and suddenly it would work fine.....i made a change to the code so that it would not send the EMA wheel info when the game boots up, it sends the unprocessed wheel info to the game and then switches to the EMA wheel. When i made this change it worked fine again. I have to change the port_minmax for all of the games for it to work with my code so to me it seems like all these different version of the game have slightly different ways or reading and calibrating the wheel positions which makes finding a fix for airborne maybe harder but as i said before i haven't had a proper look at the info you sent earlier as in i haven't actualy tried to use it yet.......i should give that a go today too once i have a few coffees in me  ;D

The EMA wheel code can be manipulated to make the smoothing more or less aggressive and more aggressive will mean more lag between you moving the wheel IRL and it moving in game. The setting i have it set to in the code snippet below seems to be perfect and stops the wheel going out of alignment in airborne when you make erratic/fast movements. The lower the number the more EMA smoothing/latency is applied and the closer it gets to 1 the more responsive/less smoothing it will have. A EMA setting that works for a port_minmax range might not work if you adjust the port_minmax range to be more sensitive, you'll need to readjust the EMA to compensate for it so there is a balancing act in working out whats best.

Code: [Select]
static const float alpha = 0.082;  // Smoothing constant
I noticed that Hard Drivin' Compact has an other issue that it doesn't fix........the wheel seems to every so often try to go to another position for a split second and sems to make the car skid sometimes for no reason? I'll have another look at it today.
« Last Edit: April 24, 2024, 06:23:53 am by Yolo_Swaggins »

geecab

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 250
  • Last login:May 13, 2024, 06:19:08 am
Oh man... I'm not sure we are out of the woods yet.

Last night I played about with setting IP_ACTIVE_HIGH for the Compact Hard Drivin' roms yesterday. I have to say, I kind of think the correct thing to do is leave it at  IP_ACTIVE_LOW...

Setting it active high means the 'Centre Edge' thing (Seen in the Service Menu) is constantly on (Green), the 'Steering Wheel' value is held at 0x0000, and steering calibration is not possible (Which can't be correct). While you can skip the service menu to play the game, and observe the car re-spawns always with centred steering (which is very good to know and well found by yourself), it feels to me like we are building on a side effect of doing something that we shouldn't be doing. Without fully understanding what should be happening...

Another spanner in the works is that I also think for these compact roms, the steering control should probably be an IPT_DIAL (which has unlimited rotation) rather than an IPT_PADDLE (that has limited rotation), to reflect the actual arcade hardware.

 :dunno

Yolo_Swaggins

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 36
  • Last login:May 24, 2024, 06:54:37 am
  • I want to build my own arcade controls!
Oh man... I'm not sure we are out of the woods yet.

Last night I played about with setting IP_ACTIVE_HIGH for the Compact Hard Drivin' roms yesterday. I have to say, I kind of think the correct thing to do is leave it at  IP_ACTIVE_LOW...

Setting it active high means the 'Centre Edge' thing (Seen in the Service Menu) is constantly on (Green), the 'Steering Wheel' value is held at 0x0000, and steering calibration is not possible (Which can't be correct). While you can skip the service menu to play the game, and observe the car re-spawns always with centred steering (which is very good to know and well found by yourself), it feels to me like we are building on a side effect of doing something that we shouldn't be doing. Without fully understanding what should be happening...

Another spanner in the works is that I also think for these compact roms, the steering control should probably be an IPT_DIAL (which has unlimited rotation) rather than an IPT_PADDLE (that has limited rotation), to reflect the actual arcade hardware.

 :dunno

 
Thats why i only have it set high for racedrivc the others i left alone. It just makes the game playable. My feeling is that those functions you sent from the hard drivin' compact manual with memory addresses 404000,404000 and 408000 are not being used properly........i think that the devs have made the game only use one of them to do a job that the other one is meant to carry out when it reaches the centerpoint?

Code: [Select]
From the Race Drivin' Compact Manual, for the Main Board Memory Map, it says:
OPTO: Optical Steering Wheel Reader
400000 (R) OPTORD Read the Optical Counter
404000 (W) OPTORES Reset the Optical Counter
408000 (W) CENRES Reset the Optical Centre Flag

I have a hunch that only the middle one is being set or the last one? In mame when we press that wheel edge button to manually activate it the counter suddenly becomes 0000.........that sounds like it's activating the middle function which kinda makes it seem like it's doing it's job because suddenly the wheel will think thats the centerpoint?  Shouldn't it be activating the last one to be setting in stone what the actual centerpoint is? Where in the mame code is the other function? I only see the one wheel edge function mentioned in mame driver code? What if the wheel edge in mame is linked to the wrong operation in that manual? Wouldn't that be the cause of it all? I think theres meant to be 2 flags or operations working together and from what i see in the mame driver code is only one operation/flag being used. I might be completely wrong but it's all my brain can come up with.

Yolo_Swaggins

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 36
  • Last login:May 24, 2024, 06:54:37 am
  • I want to build my own arcade controls!
Hard Drivin'/Race Drivin' cockpit in the mame driver only has this.

Code: [Select]
PORT_START("mainpcb:a80000")
PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_START2 ) PORT_NAME("Abort")    /* abort */
PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_START1 ) PORT_NAME("Key")  /* key */
PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_COIN3 )    /* aux coin */
PORT_BIT( 0xfff8, IP_ACTIVE_LOW, IPT_UNUSED )

But all the rest have this?

Code: [Select]
PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_CUSTOM )  PORT_TOGGLE PORT_NAME("Center Wheel Edge")  /* center edge on steering wheel */
PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_UNUSED )

I notice the highest memory address for these operations is on the steel talons/stun runner board that has this

Code: [Select]
PORT_BIT( 0xfff0, IP_ACTIVE_LOW, IPT_UNUSED )

Maybe the other function that could potentially be missing is lurking around within the 0x8000 to 0xfff0 range?

PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_UNUSED )  actually is used in Airborne in the debug menu..........

Wonder what 0x9000, 0xa000,0xb000 etc would do? :dunno
« Last Edit: April 24, 2024, 09:15:12 am by Yolo_Swaggins »

geecab

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 250
  • Last login:May 13, 2024, 06:19:08 am
Hi Yolo,

I think I might now have something that shows the centre latching stuff working... I'm tentative saying this in case I'm talking rubbish and I've just been lucky running a few tests lol! But its looking reasonable so far :)

I'll explain how (I think) its working in the next post, but first here's the diff of my changes:-

Code: [Select]
diff --git a/src/mame/atari/harddriv.cpp b/src/mame/atari/harddriv.cpp
index e0b25440ad7..2ff020056b9 100644
--- a/src/mame/atari/harddriv.cpp
+++ b/src/mame/atari/harddriv.cpp
@@ -1041,8 +1041,8 @@ static INPUT_PORTS_START( racedrivc )
  PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_BUTTON4 )  PORT_NAME("3rd Gear")
  PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_BUTTON5 )  PORT_NAME("4th Gear")
  PORT_BIT( 0x3000, IP_ACTIVE_LOW, IPT_UNUSED )
- PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_CUSTOM )  /* center edge on steering wheel */
- PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_UNUSED )
+ PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_CUSTOM )  PORT_NAME("Encoder Reset") /* center edge on steering wheel */
+ PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_CUSTOM )  PORT_NAME("Center Reset") /* center edge on steering wheel */
 
  PORT_START("mainpcb:8BADC.0")        /* b00000 - 8 bit ADC 0 - gas pedal */
  PORT_BIT( 0xff, 0x00, IPT_PEDAL ) PORT_SENSITIVITY(25) PORT_KEYDELTA(20) PORT_NAME("Gas Pedal")
@@ -1069,7 +1069,7 @@ static INPUT_PORTS_START( racedrivc )
  PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )
 
  PORT_START("mainpcb:12BADC.0")       /* 400000 - steering wheel */
- PORT_BIT(0xfff, 0x800, IPT_PADDLE) PORT_MINMAX(0x010, 0xff0) PORT_SENSITIVITY(400) PORT_KEYDELTA(5) PORT_NAME("Steering Wheel")
+ PORT_BIT( 0xfff, 0x800, IPT_DIAL ) PORT_SENSITIVITY(400) PORT_KEYDELTA(5) PORT_NAME("Steering Wheel")
 
  /* dummy ADC ports to end up with the same number as the full version */
  PORT_START("mainpcb:12BADC.1")
diff --git a/src/mame/atari/harddriv_m.cpp b/src/mame/atari/harddriv_m.cpp
index 856783e2fb6..e17d54bc4d6 100644
--- a/src/mame/atari/harddriv_m.cpp
+++ b/src/mame/atari/harddriv_m.cpp
@@ -9,7 +9,6 @@
 #include "emu.h"
 #include "harddriv.h"
 
-
 /*************************************
  *
  *  Constants and macros
@@ -246,42 +245,68 @@ uint16_t harddriv_state::hdc68k_port1_r()
  result = (result | 0x0f00) ^ (m_hdc68k_shifter_state << 8);
 
  /* merge in the wheel edge latch bit */
- if (m_hdc68k_wheel_edge)
- result ^= 0x4000;
+    if (m_hdc68k_wheel_edge)
+    {
+        result ^= 0x4000;
+        printf("hdc68k_port1_r: merge latch result=%04X m_hdc68k_last_wheel=%04X\n", result, m_hdc68k_last_wheel);
+        m_hdc68k_wheel_edge = 0;
+    }
 
- m_hdc68k_last_port1 = result;
- return result;
+    m_hdc68k_last_port1 = result;
+    return result;
 }
 
 
 uint16_t harddriv_state::hda68k_port1_r()
 {
- uint16_t result = m_a80000->read();
+    uint16_t result = m_a80000->read();
 
- /* merge in the wheel edge latch bit */
- if (m_hdc68k_wheel_edge)
- result ^= 0x4000;
+    /* merge in the wheel edge latch bit */
+    if (m_hdc68k_wheel_edge)
+    {
+        result ^= 0x4000;
+        printf("hda68k_port1_r: merge latch result=%04X m_hdc68k_last_wheel=%04X\n", result, m_hdc68k_last_wheel);
+        m_hdc68k_wheel_edge = 0;
+    }
 
- return result;
+    return result;
 }
 
 
 uint16_t harddriv_state::hdc68k_wheel_r()
 {
- /* grab the new wheel value */
- uint16_t new_wheel = m_12badc[0].read_safe(0xffff);
-
- /* hack to display the wheel position */
- if (machine().input().code_pressed(KEYCODE_LSHIFT))
- popmessage("%04X", new_wheel);
-
- /* if we crossed the center line, latch the edge bit */
- if ((m_hdc68k_last_wheel / 0xf00) != (new_wheel / 0xf00))
- m_hdc68k_wheel_edge = 1;
-
- /* remember the last value and return the low 8 bits */
- m_hdc68k_last_wheel = new_wheel;
- return (new_wheel << 8) | 0xff;
+    // grab the new wheel value
+    uint16_t new_wheel = m_12badc[0].read_safe(0xffff);
+
+    // hack to display the wheel position
+    if (machine().input().code_pressed(KEYCODE_LSHIFT))
+    {
+        popmessage("wheel new=%04X", new_wheel);
+    }
+
+    if ((m_hdc68k_last_wheel & 0x0C00) != (new_wheel & 0x0C00))
+    {
+        /*
+        Why the 0x0C00 mask? It checks if the new wheel position has moved into a new range.
+        NNNN 00NN NNNN NNNN     Thus range 0x0000 to 0x03FF
+        NNNN 01NN NNNN NNNN     Thus range 0x0400 to 0x07FF
+        NNNN 10NN NNNN NNNN     Thus range 0x0800 to 0x0BFF
+        NNNN 11NN NNNN NNNN     Thus range 0x0C00 to 0x0FFF
+        */
+        if(m_hdc68k_wheel_edge == 1)
+        {
+            //Already pending a latch. There is no point in doing 2 really quick latches,
+            //do nothing for the same effect.
+            m_hdc68k_wheel_edge = 0;
+        }
+        else
+        {
+            m_hdc68k_wheel_edge = 1;
+        }
+    }
+
+    m_hdc68k_last_wheel = new_wheel;
+    return (new_wheel << 8) | 0xff;
 }
 
 
@@ -445,11 +470,10 @@ void harddriv_state::hd68k_nwr_w(offs_t offset, uint16_t data)
 void harddriv_state::hdc68k_wheel_edge_reset_w(uint16_t data)
 {
  /* reset the edge latch */
- m_hdc68k_wheel_edge = 0;
+ //m_hdc68k_wheel_edge = 1;
 }
 
 
-
 /*************************************
  *
  *  68000 ZRAM access

geecab

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 250
  • Last login:May 13, 2024, 06:19:08 am
So after implementing my code in the previous post, if you run the hard drivin compact roms, you should be able to successfully calibrate the wheel, but you must use a mouse. Moving the mouse for ages to the right will simulate 4 wheel rotations and after completing the calibration you want to see:
    NEW CNTSPTRN 1024 PATCEN 0

Which indicates one complete wheel rotation (CNTSPTRN) amounts to 1024 (0x400) encoder values and the wheel is currently in its centre position (PATCEN 0).

Note. You will want to hold the left shift key to get the encoder value spot on at the end of the calibration (When its working out the PATCEN thing).

In the game, the centre edge bit (That "result ^= 0x4000" stuff in hdc68k_port1_r()) will 'latch' each time the wheel does a full rotation (Hitting the wheel's centre point). The encoder wheel value at these centre points will be 0x000, 0x400, 0x800 or 0xC00. So say the centre edge bit is 0 between range 0x000 to 0x3FF, this means in the 0x400 to 0x7FF range the centre edge bit will be 1, in the 0x800 to 0xBFF range the centre edge bit will be 0, and in the 0xC00 to 0xFFF range the centre edge bit will be 1.

Note. I believe incorrect calibration and incorrect centre edit bit latching were the main reasons why the old code didn't work. The old code would set m_hdc68k_wheel_edge to 1 when it wanted to latch the centre edge bit. In hdc68k_port1_r(), because m_hdc68k_wheel_edge was set it would toggle the centre edge bit. The problem is, during the game, you don't always get a hdc68k_wheel_edge_reset_w() acknowledgement for changing the centre edge bit (You do in service mode, but not  always during the game), and the code relied on that to set m_hdc68k_wheel_edge back to 0. The result is, during the game you can get into a state where the centre edit bit toggles repeatedly because  m_hdc68k_wheel_edge it left set to 1 for ages (Until such a time when hdc68k_wheel_edge_reset_w() is called (After the car re-spawns is one of those times)).

I think that just as your car re-spawns is the moment when the game works out which one of the centre points is the most appropriate (nearest). Let say you started the game around the 0x800 position, then you crash after turning hard right (and say the encoder at that moment is 0xD50). Then once you re-spawn, the game doesn't expect you to wind back the steering all the way to the 0x800 centre point (0xD50-0x550 encoder values), you'll only be expected to wind back the steering to the 0xC00 centre point (0xD50-0x150 encoder values).

So now after you crash, when you get the car to go in a straight forwards direction and observe the encoder value (left shift), you should notice it'll always be around one of those those centre points (0x000, 0x400, 0x800, 0xC00).

:)
« Last Edit: April 25, 2024, 05:28:29 am by geecab »

Yolo_Swaggins

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 36
  • Last login:May 24, 2024, 06:54:37 am
  • I want to build my own arcade controls!
Hi geecab i got the code you posted working but the mouse thing was annoying me so i added a dip switch that lets you select a "Calibration Wheel" so that you can 100% accurately set it up using the Keyboard then you switch it back to the "Game Wheel 1" or "Game Wheel 2" setting and the game works fine. I added "Game Wheel 2" because it's a custom PORT_MINMAX that makes the steering less twitchy on a Logitech G932 wheel but still gives you the full range of motion in the car.

To get it 100% accurate all you do is set "Calibration Wheel" in the dip switch menu then in the input assignment menu you just map "Calibration Wheel Analogue Inc" to the Kbd Right (right key on keyboard) and hold it down untill it reaches 4 turns (it takes about 10 seconds), let go of the button and turn the Key and you get CNTSPTRN 1024, PATCEN 0.

Virtual DIP Switches for Wheel selection.



Code: [Select]

PORT_START("mainpcb:SW1")       /* 60c002 */
// Virtual DIP switches here
PORT_DIPNAME( 0x700, 0x100, "Toggle Calibration Wheel or Game Wheel 1-2" )
PORT_DIPSETTING(    0x100, "Game Wheel 1" )
PORT_DIPSETTING(    0x300, "Game Wheel 2" )
PORT_DIPSETTING(    0x400, "Calibration Wheel" )

//Hardware DIP switches continue here
PORT_DIPNAME( 0x01, 0x01, "SW1:8" )
PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
PORT_DIPSETTING(    0x00, DEF_STR( On ) )
PORT_DIPNAME( 0x02, 0x02, "SW1:7" )

Message in the Input Assignment menu to remind users how to calibrate wheel.



Code: [Select]
PORT_START("mainpcb:a80000")
PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_START2 ) PORT_NAME("Abort")
PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_START1 ) PORT_NAME("Key")
PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_COIN3 )    /* aux coin */
PORT_BIT( 0x00f8, IP_ACTIVE_LOW, IPT_UNUSED )
PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON2 )  PORT_NAME("1st Gear")
PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_BUTTON3 )  PORT_NAME("2nd Gear")
PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_BUTTON4 )  PORT_NAME("3rd Gear")
PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_BUTTON5 )  PORT_NAME("4th Gear")
PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_CUSTOM ) /* center edge on steering wheel */
PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_UNUSED )
PORT_BIT( 0x3000, IP_ACTIVE_LOW, IPT_UNUSED ) PORT_NAME("1: Use DIP Switch & choose calibration wheel to calibrate wheel in test menu!")
PORT_BIT(0x10000, IP_ACTIVE_LOW, IPT_UNUSED ) PORT_NAME("2: Map Calibration Wheel Analogue Inc to Kbd Right!")
PORT_BIT(0x20000, IP_ACTIVE_LOW, IPT_UNUSED ) PORT_NAME("3: Hold down Kbd Right until you reach 4 turns!")
PORT_BIT(0x40000, IP_ACTIVE_LOW, IPT_UNUSED ) PORT_NAME("4: Let go of Kbd Right and turn key!")
PORT_BIT(0x80000, IP_ACTIVE_LOW, IPT_UNUSED ) PORT_NAME("5: Switch back to Game Wheel 1 or 2 after Test Menu calibration complete!")

The different Wheel setups selectable in DIP Switch menu.

Code: [Select]
PORT_START("mainpcb:12BADC.0")       /* b80000 - 12 bit ADC 0 - steering wheel */
/* Game Steering Wheel 1 */
PORT_BIT(0xfff, 0x800, IPT_PADDLE) PORT_CONDITION("mainpcb:SW1", 0x700, EQUALS, 0x100) PORT_MINMAX(0x010, 0xff0) PORT_SENSITIVITY(1) PORT_KEYDELTA(0) PORT_NAME("Game Wheel 1")

/* Game Steering Wheel 2 */
PORT_BIT(0xffff, 0x0800, IPT_PADDLE) PORT_CONDITION("mainpcb:SW1", 0x700, EQUALS, 0x300) PORT_MINMAX(0x0440, 0x0bc0) PORT_SENSITIVITY(1) PORT_KEYDELTA(0) PORT_NAME("Game Wheel 2")

/* Calibration Wheel */
PORT_BIT(0xffff, 0x0000, IPT_PADDLE) PORT_CONDITION("mainpcb:SW1", 0x700, EQUALS, 0x400) PORT_MINMAX(0x0000, 0x2800) PORT_SENSITIVITY(1) PORT_KEYDELTA(0) PORT_NAME("Calibration Wheel")



Game wheel 1 is the original mame PORT_MINMAX of 0x010, 0xff0 and Game Wheel 2 is the one i tweaked for my Logitech G932 with a PORT_MINMAX of  0x0440, 0x0bc0. Calibration Wheel has a PORT_MINMAX of 0x0000, 0x2800 and thats why it calibrates the machine perfectly but it's way too high to actually use it as a wheel because of how sensitive it is. It's only useful for calibrating the game.



« Last Edit: April 25, 2024, 07:29:14 am by Yolo_Swaggins »

Yolo_Swaggins

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 36
  • Last login:May 24, 2024, 06:54:37 am
  • I want to build my own arcade controls!
This is working great now for Hard Drivin' compact and Race Drivin' compact but that damn Hard Drivin's Airborne is being a pain in the ass! :timebomb: :laugh:

offset

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 48
  • Last login:May 12, 2024, 07:04:08 am
Any of these code changes being tracked somehwere (ie. github repo), or is everyone just doing diffs to their code from snippets here?

Also, curious what changes were made for RacerMAME and if those have been incorporated over time already
http://www.racermame.altervista.org/index.html

geecab

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 250
  • Last login:May 13, 2024, 06:19:08 am
Um, this is a tricky one...

I would like to see an IPT_DIAL option in the configuration (with no PORT MIN/MAX limitation). I'd encourage it to be the default option too. The reason is because it would be most authentic to the arcade Compact cabinets, and the in-game steering re-positioning latching behaviour can now be observed working. I think that's what the arcade purists and the mame team are all about.

I do think there should be an IPT_PADDLE option there too (That benefits from your changes, because having just an IPT_DIAL option means the game is pretty much unplayable using a self centring wheel like many people have). Hard/race drivin compact cabinets were unusual in that their dual encoder (free spinning) steering idea was trying to act like the steering potentiometer seen in the hard/race drivin cockpit cabinets. Its almost like mame needs an IPT_DIAL_PRENTENDING_TO_BE_A_PADDLE option). Have to say, I'd be a bit concerned on the amount of options you've added (and the calibration help stuff in the input assignments), might be a bit much for the mame team to swallow.

Hey, just read the above back and I sound like I'm preaching a bit. Really, don't listen to me too much, I can't speak on behalf of the mame team and am just giving you my thoughts. I'll be cool with what you choose to submit :)
 
Regarding Airborne, looking at the control inputs service menu, the steering range is fixed from 0x000 to 0x3FF, and there is no steering calibration menu section... Perhaps steering for this was just a potentiometer, not the free spinning dual encoder idea after all...  If that is the case, I reckon removing the code that toggles the latch bit completely might help. Or having it toggle only at 0x200 maybe? Just guessing.

@offset - Hi there, I've looked at the racerMame v160 diff (which I think is the latest). I couldn't find any hard/race drivin changes in there. I think generally most people with self centring wheels would use the Cockpit roms with racerMame. The only reasons I can think of using compact roms instead of cockpit roms is because of 1. Performance (The compact roms are less CPU intensive than the cockpit roms) and 2. You have a mame cabinet which uses a spinner/mouse for steering (Thus steering repositioning will work as it should).

:)

Yolo_Swaggins

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 36
  • Last login:May 24, 2024, 06:54:37 am
  • I want to build my own arcade controls!
Have to say, I'd be a bit concerned on the amount of options you've added (and the calibration help stuff in the input assignments), might be a bit much for the mame team to swallow.

Regarding Airborne, looking at the control inputs service menu, the steering range is fixed from 0x000 to 0x3FF, and there is no steering calibration menu section... Perhaps steering for this was just a potentiometer, not the free spinning dual encoder idea after all...  If that is the case, I reckon removing the code that toggles the latch bit completely might help. Or having it toggle only at 0x200 maybe? Just guessing.

Hi geecab, you don't sound preachy at all mate i actually did submit options like that before for Airborne and street drivin' on mame's github and one of the devs just said he didn't think it was worth adding extra controller configs :dunno.....so it was never merged. I do agree there should be a IPT_DIAL option and that is one of the things i was originally testing out and a guy who's been following what i was doing was willing to do beta testing since he has a spinner wheel and i think he's talking about making his own custom flight yoke for it using a cheap fixed position wheel and modifying it so that it can free spin but will have centring springs and a mouse wheel in the back that detects the motion of the wheel and going to make the wheel able to move back and forwards to control up and down motion in Airborne.

Now i know the differences with the machines i agree with you that IPT_DIAL should be the default option for the compact cabinets for use on a spinner wheel and then there should be a 2nd option for a normal wheel. I don't know what the most common wheel ranges would be but i have a 900 degree wheel and the steering was just a bit too sensitive so i lowered it and used a youtube video online that shows a guy doing a hard left at the start of Hard Drivin' to go onto the "secret" bit of road that takes you to that big post in the middle of a circle. I kept lowering it until the car didn't make the turn as sharp as i saw him do it in that video. It's probobly not 100% accurate but my guess would be it's got to be above 95% accurate at least.

That info in the options i know would never be accepted into the official branch i just put it here in case somebody wanted to incorporate it into their own version or something. I don't know how to make or change messages/warnings that pop up before the game starts otherwise i would have just added the info in there for those specific games. :laugh:

The Airborne steering range used to be like all the others by default, 0x010 to 0xff0 , i made my own commit that was merged into the mamedev/master that changed it to 0x000 to 0x3FE to fix the steering for Street Drivin' and Airborne then one of the devs suggested it should be 0x000, 0x3FF and edited it and merged it into the master branch along with the brake calibration fix for street drivin'. Just waiting for the brake fix for race drivin' and hard drivin' compact to hopefully be merged into the master now.

The code i have made for the Airborne issues that utilizes EMA smoothing/buffering although it's not in anyway accurate or true to the arcade machine it does stop the steering from going out of alignment in almost all scenarios as long as you don't crash the car and have the wheel held to left or right lol. The only way I've been able to throw it off center is by doing really stupid fast movement with the wheel that would just never happen IRL. I can make that code run in mame on the condition that it detects the Airborne game being loaded up but in all other versions it would just use your modified code. It could be a kinda halfway fix/patch untill somebody like yourself who's way more qualified than me can look at what is going on. I remember when you posted the code to pull the wheel values from the game itself i was just looking at the popmessage box on the screen wondering wtf is going on :lol

I had put my hacky steering fix for the compact version into a pull request on mame's gitbub but i think i should remove it now that you have actually fixed it properly. You know what's funny is i never realised there was a problem with those games because the only couple of times i tried the compact roms i had toggled that wheel centre edge button to be on all the time by accident and made it seem like it worked  :laugh: I only realised that it had that problem when you mentioned it and i went back and had a look and realised it was messed up badly. You should put your fix for the steering into a pull request for the mame master branch i have been having a lot of fun playing the compact version because i don't have a analogue gear shifter so i was always playing with a automatic car in the cockpit version before you made that fix and the game does run way smoother than the other versions. :cheers:

I was wondering why the compact versions use less CPU? Another thing i was told by the guy who's talking about making his own flight yoke for Airborne is that there are a few cabinets known in existence and a couple of them are cockpit versions and the others are compact.........i was wondering if the problem is maybe the rom dump available on the internet is maybe from the compact version? Maybe it had a different set of values for the steering so the 0xC00 thing might need tweaked to another value for that to work properly? I really don't know it's just a thought. I have thought about taking the roms from the earlier prototype and trying to mix and match them with the one that is working to maybe see if the steering code could be different in the earlier version.

When you mention disabling the latching code could i just "//" out that section and compile the game without it? I've not tried anything yet i was having too much fun on compact hard drivin' yesterday to be bothered about it and just woke up :lol


geecab

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 250
  • Last login:May 13, 2024, 06:19:08 am
Ah that's good stuff then Yolo! I think we are bang on the same page. I just didn't want to come over all preachy and put you off doing what you are doing, experimenting with the code because that's the key to understanding and finding things out. Also, if it wasn't for your efforts and recent interest in this, I doubt the mystery of the latching bit would have ever been solved!!

Quote
Now i know the differences with the machines i agree with you that IPT_DIAL should be the default option for the compact cabinets for use on a spinner wheel and then there should be a 2nd option for a normal wheel. I don't know what the most common wheel ranges would be but i have a 900 degree wheel and the steering was just a bit too sensitive so i lowered it and used a youtube video online that shows a guy doing a hard left at the start of Hard Drivin' to go onto the "secret" bit of road that takes you to that big post in the middle of a circle. I kept lowering it until the car didn't make the turn as sharp as i saw him do it in that video. It's probobly not 100% accurate but my guess would be it's got to be above 95% accurate at least.

Cool, lets get IPT_DIAL and IPT_PADDLE options working then. I know what you mean about the secret road to the left. I'm not sure if its possible to do doughnuts around the pole at the end of the track (I remember trying this once on my mame cab but couldn't keep the backend out consistently). Its cool these hidden features are there though!

Quote
That info in the options i know would never be accepted into the official branch i just put it here in case somebody wanted to incorporate it into their own version or something. I don't know how to make or change messages/warnings that pop up before the game starts otherwise i would have just added the info in there for those specific games. :laugh:

I know what you mean about placing it the messages/warnings things that pop up at the start. I'm not sure how you change that either. I agree though, a description of how to calibrate would be helpful.

Quote
The Airborne steering range used to be like all the others by default, 0x010 to 0xff0 , i made my own commit that was merged into the mamedev/master that changed it to 0x000 to 0x3FE to fix the steering for Street Drivin' and Airborne then one of the devs suggested it should be 0x000, 0x3FF and edited it and merged it into the master branch along with the brake calibration fix for street drivin'. Just waiting for the brake fix for race drivin' and hard drivin' compact to hopefully be merged into the master now.

Ok I understand now thanks. It does seem like 0x000 to 0x3FF is the correct range.

Quote
The code i have made for the Airborne issues that utilizes EMA smoothing/buffering although it's not in anyway accurate or true to the arcade machine it does stop the steering from going out of alignment in almost all scenarios as long as you don't crash the car and have the wheel held to left or right lol. The only way I've been able to throw it off center is by doing really stupid fast movement with the wheel that would just never happen IRL. I can make that code run in mame on the condition that it detects the Airborne game being loaded up but in all other versions it would just use your modified code.

The "as long as you don't crash the car and have the wheel held to left or right" does really make it sound like an encoder latching issue like we've been seeing doesn't it. I'm now back to think its dual encoders in airborne and not potentiometers lol!

Quote
It could be a kinda halfway fix/patch untill somebody like yourself who's way more qualified than me can look at what is going on. I remember when you posted the code to pull the wheel values from the game itself i was just looking at the popmessage box on the screen wondering wtf is going on :lol

lol! It might appear like I know what I'm talking about, but trust me I'm completely winging / guessing at everything all the time!!

Quote
I was wondering why the compact versions use less CPU? Another thing i was told by the guy who's talking about making his own flight yoke for Airborne is that there are a few cabinets known in existence and a couple of them are cockpit versions and the others are compact.........i was wondering if the problem is maybe the rom dump available on the internet is maybe from the compact version? Maybe it had a different set of values for the steering so the 0xC00 thing might need tweaked to another value for that to work properly? I really don't know it's just a thought. I have thought about taking the roms from the earlier prototype and trying to mix and match them with the one that is working to maybe see if the steering code could be different in the earlier version.

Yes I know what you mean. Its interesting the compact versions use less CPU, atari must have simplified things, made things more efficient with boards used in the compact cabs (and cheaper probably). I had a thought, I might try and read those nvram binary files that get saved after you finish calibrating controls in hard drivin. If we know where to fish out the CNTSPTRN and PATCEN stuff that gets saved, maybe it'll be the same for the airborne nvrams... Knowing what airborne's steering wheels settings are we can modify the code to suit (Or even edit the nvrams and force them to our favoured CNTSPTRN 1024 PATCEN 0 settings). Bit of a long short but you never know...

Quote
When you mention disabling the latching code could i just "//" out that section and compile the game without it? I've not tried anything yet i was having too much fun on compact hard drivin' yesterday to be bothered about it and just woke up :lol

Yes that's it, just comment out the lines that do the "result ^= 0x4000" thing (In hdc68k_port1_r() and hda68k_port1_r()) I reckon.

I've honestly not messed around with airborne much at all yet, but I hopefully will do over the weekend.

:)
« Last Edit: April 26, 2024, 12:19:07 pm by geecab »

Yolo_Swaggins

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 36
  • Last login:May 24, 2024, 06:54:37 am
  • I want to build my own arcade controls!
Hi geecab i managed to combine your code with my code so that my mods only get applied if airborne is loaded up in mame but all the others will use your code. I'm wondering if i should maybe add street drivin' to it because it kinda had a similar issue but not as bad. I notice if i only shift the output left by 7 bits instead of 8 the steering in airborne becomes more like the original games and you can get away with using a port_minmax of 0x000, 0x7ff with a center point of 0x400 or 0x400, 0xbff with a center point of 0x800. It applied EMA smoothing to the wheel output so that it doesn't fluctuate too wildly when turning the wheel and seems to make the wheel stay in alignment when driving for long periods but crashing and holding the wheel left or right  or staying offroad until the timer runs out with the wheel held left or right will knock the centerpoint out of place even if you have the wheel only turned a little bit.

Code: [Select]
    // Merge in the wheel edge latch bit
    if (m_hdc68k_wheel_edge)
    {
        result ^= 0x4000;
        printf("hdc68k_port1_r: merge latch result=%04X m_hdc68k_last_wheel=%04X\n", result, m_hdc68k_last_wheel);
        m_hdc68k_wheel_edge = 0;
    }

    m_hdc68k_last_port1 = result;
    return result;
}
   

uint16_t harddriv_state::hda68k_port1_r()
{
    uint16_t result = m_a80000->read();

   if (m_hdc68k_wheel_edge)
    {
        result ^= 0x4000;
        printf("hda68k_port1_r: merge latch result=%04X m_hdc68k_last_wheel=%04X\n", result, m_hdc68k_last_wheel);
        m_hdc68k_wheel_edge = 0;
    }

    return result;
}


uint16_t harddriv_state::hdc68k_wheel_r()
{
    static float ema_wheel = 0.0f;  // Persistent storage of EMA across calls
    static const float alpha = 0.05f;  // Smoothing factor for EMA, adjustable
    static bool initialized = false;  // To check if ema_wheel is initialized

    uint16_t new_wheel = m_12badc[0].read_safe(0xffff);

    // Display the wheel position when left shift is pressed
    if (machine().input().code_pressed(KEYCODE_LSHIFT))
    {
        popmessage("wheel new=%04X", new_wheel);
    }

    // Determine if the current game is Hard Drivin's Airborne
    bool is_hdrivair = (machine().system().name == std::string("hdrivair"));

    // Separate edge detection logic for hdrivair and non-hdrivair games
    if (is_hdrivair)
    {
        // Use a specific mask for Hard Drivin's Airborne (placeholder for future changes)
        if ((m_hdc68k_last_wheel & 0x0c00) != (new_wheel & 0x0c00))
        {
            m_hdc68k_wheel_edge = m_hdc68k_wheel_edge == 0 ? 1 : 0;
        }
    }
    else
    {
        // Use the default mask for other games
        if ((m_hdc68k_last_wheel & 0x0c00) != (new_wheel & 0x0c00))
        {
            m_hdc68k_wheel_edge = m_hdc68k_wheel_edge == 0 ? 1 : 0;
        }
    }

    if (is_hdrivair)
    {
        // EMA calculation only for Hard Drivin's Airborne
        if (!initialized)
        {
            ema_wheel = new_wheel;  // Initialize EMA with the first reading
            initialized = true;
        }
        else
        {
            ema_wheel = alpha * new_wheel + (1 - alpha) * ema_wheel;  // Update EMA
        }
    }
    else
    {
        // For other games, use the raw wheel data without EMA
        ema_wheel = new_wheel;
    }

    m_hdc68k_last_wheel = new_wheel;

    uint16_t output;
    if (is_hdrivair)
    {
        // Hard Drivin's Airborne specific processing
        output = (static_cast<uint16_t>(ema_wheel) << 7) & 0xFFF0; // Shift and mask for hdrivair
    }
    else
    {
        // For other games, shift left by 8 and set the last hex digit to 0xFF
        output = (static_cast<uint16_t>(ema_wheel) << 8) | 0xFF;
    }

    // Return the processed wheel value
    return output;
}

« Last Edit: April 27, 2024, 05:12:27 am by Yolo_Swaggins »

geecab

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 250
  • Last login:May 13, 2024, 06:19:08 am
Hi Yolo!

I reckon you've discovered something that I've been all around the houses investigating today lol! I'll cut to the chase, Street Drivin's default steering values are CNTSPTRN: 512 PATCEN: -98. I think this is pretty much what you've discovered when bit shifting by 7 and not 8.

I made a python script that can read and compare the nvram files (That get created when you exit the game for the first time, and get modified when you perform wheel calibration) and reports interesting values (Currently only steering information is fished out, but it could be modified to gas pedal etc I guess). The script works fine for hard drivin, race drivin and street drivin. The airborne nvrams look like a different format and I don't think the CNTSPTRN stuff is saved at the same location. If it is the same location, then the result for airborne is CNTSPTRN: 255 PATCEN: 0.

I have added an option to edit/save the CNTSPTRN and  PATCEN values in the nvram files but unfortunately the game won't accept it. I think the file is protected in some way.

I'm not sure if you are familiar with python, but here's my script that does the read/diff/reset if you're interested...

Code: [Select]
import sys

BYTES_EXPECTED = 2048

def load_byte_array(filename):

    global BYTES_EXPECTED

    with open(filename, mode='rb') as file: # b is important -> binary
        fileContent = file.read().hex()

    data = []
    byte_count = 0
    this_byte = ""
    for c in fileContent:
        this_byte += c
        if len(this_byte) == 2:
            data.append(int(this_byte, 16))
            this_byte = ""
            byte_count += 1

    if byte_count != BYTES_EXPECTED:
        print("Error! Unexpected number of bytes in {}. Expecting={}, actual={} ".format(filename, BYTES_EXPECTED, byte_count))
        exit(1)

    #print("{}".format((fileContent)))
    return data


def save_byte_array(filename, byte_array):
    # make file
    newFile = open(filename, "wb")
    # write to file
    newFileByteArray = bytearray(byte_array)
    newFile.write(newFileByteArray)


def load_nvram(nvram_path):

    global BYTES_EXPECTED

    mainpcb_200e = load_byte_array(nvram_path + "mainpcb_200e")
    mainpcb_210e = load_byte_array(nvram_path + "mainpcb_210e")

    # Combine the data...
    nvram = []
    for i in range(0, BYTES_EXPECTED):
        nvram.append((mainpcb_200e[i] << 8) | mainpcb_210e[i])

    return nvram


def do_read(nvram_path):

    nvram = load_nvram(nvram_path)
    cntsptrn = "?"
    patcen = "?"

    for i in range(0, BYTES_EXPECTED):
        temp = "ADDR:0x{:04X} - 0x{:04X} {:<8}".format(i, nvram[i], f"({nvram[i]})")
        if i == 0x212:
            temp = temp + " CNTSPTRN"
            cntsptrn = nvram[i]
        elif i == 0x213:
            temp = temp + " PATCEN"
            patcen = nvram[i]

        print(temp)

    i_patcen = patcen
    if (patcen&0xf000) == 0xf000:
        i_patcen = -1 * (0x1000 - patcen&0x0fff)

    print(f"Read complete. CNTSPTRN:{hex(cntsptrn)} ({cntsptrn})     PATCEN:{hex(patcen)} ({i_patcen})")


def do_diff(nvram_path_a, nvram_path_b):

    global BYTES_EXPECTED

    a = load_nvram(nvram_path_a)
    b = load_nvram(nvram_path_b)

    diff_count = 0
    for i in range(0, BYTES_EXPECTED):
        if a[i] != b[i]:
            temp = "ADDR:0x{:04X} - A:0x{:04X} {:<8} DIFF B:0x{:04X} {:<8}".format(i, a[i], f"({a[i]})", b[i], f"({b[i]})")
            if i == 0x212:
                temp = temp + " CNTSPTRN"
            elif i == 0x213:
                temp = temp + " PATCEN"
            diff_count += 1
            print(temp)

    print(f"{diff_count} differences detected.")


def do_reset(nvram_path):

    mainpcb_200e = load_byte_array(nvram_path + "mainpcb_200e")
    mainpcb_210e = load_byte_array(nvram_path + "mainpcb_210e")

    mainpcb_200e[530] = 0x04
    mainpcb_210e[530] = 0x00
    mainpcb_200e[531] = 0x00
    mainpcb_210e[531] = 0x00

    save_byte_array(nvram_path + "mainpcb_200e", mainpcb_200e)
    save_byte_array(nvram_path + "mainpcb_210e", mainpcb_210e)

    print("CNTSPTRN reset to 1024, PATCEN reset to 0.")

def clean_path(my_path):
    if not my_path.endswith('/'):
        my_path = my_path + '/'
    return my_path

def show_usage():
    print("Nvram Helper")
    print("------------")
    print("")
    print("Usage:")
    print("    python3 nvram_helper.py read  <nvram_directory>")
    print("    python3 nvram_helper.py diff  <nvram_directory_a> <nvram_directory_b>")
    print("    python3 nvram_helper.py reset <nvram_directory>")

    print("Examples:")
    print("    python3 nvram_helper.py read nvram/harddrivcb")
    print("    python3 nvram_helper.py diff nvram/harddrivcb nvram/strtdriv")
    print("    python3 nvram_helper.py read nvram/strtdriv")
    exit(0)


#print("{}".format(sys.argv))

if len(sys.argv) < 2:
    show_usage()

cmd = sys.argv[1]
if cmd == 'read':
    if len(sys.argv) < 3:
        print(f"Not enough arguments.")
        exit(1)
    do_read(clean_path(sys.argv[2]))

elif cmd == 'diff':
    if len(sys.argv) < 4:
        print(f"Not enough arguments.")
        exit(1)
    do_diff(clean_path(sys.argv[2]), clean_path(sys.argv[3]))

elif cmd == 'reset':
    if len(sys.argv) < 3:
        print(f"Not enough arguments.")
        exit(1)
    do_reset(clean_path(sys.argv[2]))

elif cmd.lower() in ["h", "-h", "help", "/help", "/h", "?", "--h"]:
    show_usage()

else:
    print(f"Unknown command '{cmd}'.")
    show_usage()



With the help of this script, I could work out the memory locations for when the game reads and writes CNTSPTRN and PATCEN stuff during execution time. This is kind of interesting because I can now automatically work out when to trigger the centre edge without having to rely on the user having to calibrate either wheel to the magic CNTSPTRN:1024 and PATCEN:0 numbers.  I have actually got this working well, but my code is a bit complex/ugly at the moment. I'll post it soon.

BTW. I've had a few goes with airborne, sometimes (most of the time) I go backwards when I accelerate. I've had a look at previously posts, did you mention something about this to me? If not, have you seen this before?

:)
« Last Edit: April 28, 2024, 03:08:46 pm by geecab »

Yolo_Swaggins

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 36
  • Last login:May 24, 2024, 06:54:37 am
  • I want to build my own arcade controls!
Hi Yolo!

I reckon you've discovered something that I've been all around the houses investigating today lol! I'll cut to the chase, Street Drivin's default steering values are CNTSPTRN: 512 PATCEN: -98. I think this is pretty much what you've discovered when bit shifting by 7 and not 8.

I made a python script that can read and compare the nvram files (That get created when you exit the game for the first time, and get modified when you perform wheel calibration) and reports interesting values (Currently only steering information is fished out, but it could be modified to gas pedal etc I guess). The script works fine for hard drivin, race drivin and street drivin. The airborne nvrams look like a different format and I don't think the CNTSPTRN stuff is saved at the same location. If it is the same location, then the result for airborne is CNTSPTRN: 255 PATCEN: 0.

I have added an option to edit/save the CNTSPTRN and  PATCEN values in the nvram files but unfortunately the game won't accept it. I think the file is protected in some way.

I'm not sure if you are familiar with python, but here's my script that does the read/diff/reset if you're interested...

Code: [Select]
import sys

BYTES_EXPECTED = 2048

def load_byte_array(filename):

    global BYTES_EXPECTED

    with open(filename, mode='rb') as file: # b is important -> binary
        fileContent = file.read().hex()

    data = []
    byte_count = 0
    this_byte = ""
    for c in fileContent:
        this_byte += c
        if len(this_byte) == 2:
            data.append(int(this_byte, 16))
            this_byte = ""
            byte_count += 1

    if byte_count != BYTES_EXPECTED:
        print("Error! Unexpected number of bytes in {}. Expecting={}, actual={} ".format(filename, BYTES_EXPECTED, byte_count))
        exit(1)

    #print("{}".format((fileContent)))
    return data


def save_byte_array(filename, byte_array):
    # make file
    newFile = open(filename, "wb")
    # write to file
    newFileByteArray = bytearray(byte_array)
    newFile.write(newFileByteArray)


def load_nvram(nvram_path):

    global BYTES_EXPECTED

    mainpcb_200e = load_byte_array(nvram_path + "mainpcb_200e")
    mainpcb_210e = load_byte_array(nvram_path + "mainpcb_210e")

    # Combine the data...
    nvram = []
    for i in range(0, BYTES_EXPECTED):
        nvram.append((mainpcb_200e[i] << 8) | mainpcb_210e[i])

    return nvram


def do_read(nvram_path):

    nvram = load_nvram(nvram_path)
    cntsptrn = "?"
    patcen = "?"

    for i in range(0, BYTES_EXPECTED):
        temp = "ADDR:0x{:04X} - 0x{:04X} {:<8}".format(i, nvram[i], f"({nvram[i]})")
        if i == 0x212:
            temp = temp + " CNTSPTRN"
            cntsptrn = nvram[i]
        elif i == 0x213:
            temp = temp + " PATCEN"
            patcen = nvram[i]

        print(temp)

    i_patcen = patcen
    if (patcen&0xf000) == 0xf000:
        i_patcen = -1 * (0x1000 - patcen&0x0fff)

    print(f"Read complete. CNTSPTRN:{hex(cntsptrn)} ({cntsptrn})     PATCEN:{hex(patcen)} ({i_patcen})")


def do_diff(nvram_path_a, nvram_path_b):

    global BYTES_EXPECTED

    a = load_nvram(nvram_path_a)
    b = load_nvram(nvram_path_b)

    diff_count = 0
    for i in range(0, BYTES_EXPECTED):
        if a[i] != b[i]:
            temp = "ADDR:0x{:04X} - A:0x{:04X} {:<8} DIFF B:0x{:04X} {:<8}".format(i, a[i], f"({a[i]})", b[i], f"({b[i]})")
            if i == 0x212:
                temp = temp + " CNTSPTRN"
            elif i == 0x213:
                temp = temp + " PATCEN"
            diff_count += 1
            print(temp)

    print(f"{diff_count} differences detected.")


def do_reset(nvram_path):

    mainpcb_200e = load_byte_array(nvram_path + "mainpcb_200e")
    mainpcb_210e = load_byte_array(nvram_path + "mainpcb_210e")

    mainpcb_200e[530] = 0x04
    mainpcb_210e[530] = 0x00
    mainpcb_200e[531] = 0x00
    mainpcb_210e[531] = 0x00

    save_byte_array(nvram_path + "mainpcb_200e", mainpcb_200e)
    save_byte_array(nvram_path + "mainpcb_210e", mainpcb_210e)

    print("CNTSPTRN reset to 1024, PATCEN reset to 0.")

def clean_path(my_path):
    if not my_path.endswith('/'):
        my_path = my_path + '/'
    return my_path

def show_usage():
    print("Nvram Helper")
    print("------------")
    print("")
    print("Usage:")
    print("    python3 nvram_helper.py read  <nvram_directory>")
    print("    python3 nvram_helper.py diff  <nvram_directory_a> <nvram_directory_b>")
    print("    python3 nvram_helper.py reset <nvram_directory>")

    print("Examples:")
    print("    python3 nvram_helper.py read nvram/harddrivcb")
    print("    python3 nvram_helper.py diff nvram/harddrivcb nvram/strtdriv")
    print("    python3 nvram_helper.py read nvram/strtdriv")
    exit(0)


#print("{}".format(sys.argv))

if len(sys.argv) < 2:
    show_usage()

cmd = sys.argv[1]
if cmd == 'read':
    if len(sys.argv) < 3:
        print(f"Not enough arguments.")
        exit(1)
    do_read(clean_path(sys.argv[2]))

elif cmd == 'diff':
    if len(sys.argv) < 4:
        print(f"Not enough arguments.")
        exit(1)
    do_diff(clean_path(sys.argv[2]), clean_path(sys.argv[3]))

elif cmd == 'reset':
    if len(sys.argv) < 3:
        print(f"Not enough arguments.")
        exit(1)
    do_reset(clean_path(sys.argv[2]))

elif cmd.lower() in ["h", "-h", "help", "/help", "/h", "?", "--h"]:
    show_usage()

else:
    print(f"Unknown command '{cmd}'.")
    show_usage()



With the help of this script, I could work out the memory locations for when the game reads and writes CNTSPTRN and PATCEN stuff during execution time. This is kind of interesting because I can now automatically work out when to trigger the centre edge without having to rely on the user having to calibrate either wheel to the magic CNTSPTRN:1024 and PATCEN:0 numbers.  I have actually got this working well, but my code is a bit complex/ugly at the moment. I'll post it soon.

BTW. I've had a few goes with airborne, sometimes (most of the time) I go backwards when I accelerate. I've had a look at previously posts, did you mention something about this to me? If not, have you seen this before?

:)

Hi geecab just logged in and read this now, thats really good work fishing out that info from the nvram i'll check out the script.

About the reverse thing in Airborne, theres a reverse button thats a toggle

PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_TOGGLE  /* reverse */

You need to just set up a button for that and toggle it off or go into the input settings in game and go to the toggle section and turn it off there.

Had a long weird day but i'll have a mess around now and see if i can figure anything else out now.

Yolo_Swaggins

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 36
  • Last login:May 24, 2024, 06:54:37 am
  • I want to build my own arcade controls!
Hi geecab so i've had some sleep and made my own python script that combines the nvram files like this "MAME\nvram\harddrivcb\combined_high_200e_low_210e". So i calibrated the wheel correctly and combined it into the file i just mentioned and saved it in another location. I then deleted the nvram files and started the game and calibrated the wheel wrong. i opened both the correct and incorrect files in HxD and compared the difference. Copied the bytes from the correctly calibrated and combined nvram file into the incorrectly calibrated one and saved it. Then i used another script to split the file back into the 2 original nvram files and loaded up the game. Works fine and the wheel was calibrated. What i noticed is at the very bottom of the combined file there is a couple of bytes that were changed and i copied them in from the correctly calibrated file and maybe thats why it works? I might have misunderstood what you were saying before was it only airborne you couldn't get the game to accept the values? As of now i've not even really looked at it i just wanted to see what was going on in the ones we know we can get working first.



OK ignore everything i said i just copied the bytes that changed in the middle part of the file above the high score name "phantom Photon" and it works fine even without the changes on the very last line in HxD being copied over.

« Last Edit: April 29, 2024, 04:05:46 am by Yolo_Swaggins »

Yolo_Swaggins

  • Trade Count: (0)
  • Full Member
  • ***
  • Offline Offline
  • Posts: 36
  • Last login:May 24, 2024, 06:54:37 am
  • I want to build my own arcade controls!
That value you found before is the old brake max in Hard Drivin's Airborne so the wheels not at the same position.