1 /***************************************************************************
2 Process Outputs.
3
4 - Cabinet Vibration & Hydraulic Movement
5 - Brake & Start Lamps
6 - Coin Chute Outputs
7
8 The Deluxe Motor code is also used by the force-feedback haptic system.
9
10 One thing to note is that this code was originally intended to drive
11 a moving hydraulic cabinet, not to be mapped to a haptic device.
12
13 Therefore, it's not perfect when used in this way, but the results
14 aren't bad :)
15
16 Copyright Chris White.
17 See license.txt for more details.
18 ***************************************************************************/
19
20 #include <iostream>
21 #include <cstdlib> // abs
22
23 #include "utils.hpp"
24
25 #include "engine/outrun.hpp"
26 #include "engine/ocrash.hpp"
27 #include "engine/oferrari.hpp"
28 #include "engine/ohud.hpp"
29 #include "engine/oinputs.hpp"
30 #include "engine/ooutputs.hpp"
31 #include "directx/ffeedback.hpp"
32
OOutputs(void)33 OOutputs::OOutputs(void)
34 {
35 mode = MODE_DISABLED;
36
37 chute1.output_bit = D_COIN1_SUCC;
38 chute2.output_bit = D_COIN2_SUCC;
39
40 col1 = 0;
41 col2 = 0;
42 limit_left = 0;
43 limit_right = 0;
44 motor_enabled = true;
45 }
46
~OOutputs(void)47 OOutputs::~OOutputs(void)
48 {
49 }
50
51
52 // Initalize Moving Cabinet Motor
53 // Source: 0xECE8
init()54 void OOutputs::init()
55 {
56 motor_state = STATE_INIT;
57 hw_motor_control = MOTOR_OFF;
58 dig_out = 0;
59 dig_out_old = -1;
60 motor_control = 0;
61 motor_movement = 0;
62 is_centered = false;
63 motor_change_latch = 0;
64 speed = 0;
65 curve = 0;
66 counter = 0;
67 vibrate_counter = 0;
68 was_small_change = false;
69 movement_adjust1 = 0;
70 movement_adjust2 = 0;
71 movement_adjust3 = 0;
72 chute1.counter[0] = 0;
73 chute1.counter[1] = 0;
74 chute1.counter[2] = 0;
75 chute2.counter[0] = 0;
76 chute2.counter[1] = 0;
77 chute2.counter[2] = 0;
78 }
79
set_mode(int m)80 void OOutputs::set_mode(int m)
81 {
82 mode = m;
83 }
84
tick(int16_t input_motor)85 void OOutputs::tick(int16_t input_motor)
86 {
87 switch (mode)
88 {
89 case MODE_DISABLED:
90 break;
91
92 // Force Feedback Steering Wheels
93 case MODE_FFEEDBACK:
94 do_motors(mode, input_motor); // Use X-Position of wheel instead of motor position
95 motor_output(hw_motor_control); // Force Feedback Handling
96 break;
97
98 // SMARTYPI: Real Cabinet
99 case MODE_CABINET:
100 if (config.smartypi.cabinet == Config::CABINET_MOVING)
101 {
102 do_motors(mode, input_motor);
103 }
104 else
105 {
106 if (config.smartypi.cabinet == Config::CABINET_UPRIGHT)
107 do_vibrate_upright();
108 else if (config.smartypi.cabinet == Config::CABINET_MINI)
109 do_vibrate_mini();
110 }
111 break;
112
113 // GamePad: Basic Rumble
114 case MODE_RUMBLE:
115 do_vibrate_upright();
116 break;
117 }
118 }
119
writeDigitalToConsole()120 void OOutputs::writeDigitalToConsole()
121 {
122 if (config.smartypi.enabled && config.smartypi.ouputs)
123 {
124 if ((dig_out & D_BRAKE_LAMP) != (dig_out_old & D_BRAKE_LAMP))
125 std::cout << "brake_lamp = " << is_set(D_BRAKE_LAMP) << std::endl;
126 if ((dig_out & D_START_LAMP) != (dig_out_old & D_START_LAMP))
127 std::cout << "start_lamp = " << is_set(D_START_LAMP) << std::endl;
128 if ((dig_out & D_MOTOR) != (dig_out_old & D_MOTOR))
129 std::cout << "wheel_motor = " << is_set(D_MOTOR) << std::endl;
130
131 dig_out_old = dig_out;
132 }
133 }
134
135 // ------------------------------------------------------------------------------------------------
136 // Digital Outputs
137 // ------------------------------------------------------------------------------------------------
138
set_digital(uint8_t output)139 void OOutputs::set_digital(uint8_t output)
140 {
141 dig_out |= output;
142 }
143
clear_digital(uint8_t output)144 void OOutputs::clear_digital(uint8_t output)
145 {
146 dig_out &= ~output;
147 }
148
is_set(uint8_t output)149 int OOutputs::is_set(uint8_t output)
150 {
151 return (dig_out & output) ? 1 : 0;
152 }
153
154 // ------------------------------------------------------------------------------------------------
155 // Motor Diagnostics
156 // Source: 0x1885E
157 // ------------------------------------------------------------------------------------------------
158
diag_motor(int16_t input_motor,uint8_t hw_motor_limit,uint32_t packets)159 bool OOutputs::diag_motor(int16_t input_motor, uint8_t hw_motor_limit, uint32_t packets)
160 {
161 switch (motor_state)
162 {
163 // Initalize
164 case STATE_INIT:
165 col1 = 10;
166 col2 = 27;
167 ohud.blit_text_new(col1, 9, "LEFT LIMIT");
168 ohud.blit_text_new(col1, 11, "RIGHT LIMIT");
169 ohud.blit_text_new(col1, 13, "CENTRE");
170 ohud.blit_text_new(col1, 16, "MOTOR POSITION");
171 ohud.blit_text_new(col1, 18, "LIMIT B3 LEFT");
172 ohud.blit_text_new(col1, 19, "LIMIT B4 CENTRE");
173 ohud.blit_text_new(col1, 20, "LIMIT B5 RIGHT");
174 counter = COUNTER_RESET;
175 motor_centre_pos = 0;
176 motor_enabled = true;
177 motor_state = STATE_LEFT;
178 break;
179
180 case STATE_LEFT:
181 diag_left(input_motor, hw_motor_limit);
182 break;
183
184 case STATE_RIGHT:
185 diag_right(input_motor, hw_motor_limit);
186 break;
187
188 case STATE_CENTRE:
189 diag_centre(input_motor, hw_motor_limit);
190 break;
191
192 case STATE_DONE:
193 diag_done();
194 break;
195 }
196
197 // Print Motor Position & Limit Switch
198 ohud.blit_text_new(col2, 16, " H", 0x80);
199 ohud.blit_text_new(col2, 16, Utils::to_string(input_motor).c_str(), 0x80);
200 ohud.blit_text_new(col2, 18, (hw_motor_limit & BIT_3) ? "ON " : "OFF ", 0x80);
201 ohud.blit_text_new(col2, 19, (hw_motor_limit & BIT_4) ? "ON " : "OFF ", 0x80);
202 ohud.blit_text_new(col2, 20, (hw_motor_limit & BIT_5) ? "ON " : "OFF ", 0x80);
203 return motor_state == STATE_DONE;
204 }
205
diag_left(int16_t input_motor,uint8_t hw_motor_limit)206 void OOutputs::diag_left(int16_t input_motor, uint8_t hw_motor_limit)
207 {
208 // If Right Limit Set, Move Left
209 if (hw_motor_limit & BIT_5)
210 {
211 if (--counter >= 0)
212 {
213 hw_motor_control = MOTOR_LEFT;
214 return;
215 }
216 // Counter Expired, Left Limit Still Not Reached
217 else
218 ohud.blit_text_new(col2, 9, "FAIL 1", 0x80);
219 }
220 // Left Limit Reached
221 else if (hw_motor_limit & BIT_3)
222 {
223 ohud.blit_text_new(col2, 9, " H", 0x80);
224 ohud.blit_text_new(col2, 9, Utils::to_string(input_motor).c_str(), 0x80);
225 }
226 else
227 ohud.blit_text_new(col2, 9, "FAIL 2", 0x80);
228
229 counter = COUNTER_RESET;
230 motor_state = STATE_RIGHT;
231 }
232
233
diag_right(int16_t input_motor,uint8_t hw_motor_limit)234 void OOutputs::diag_right(int16_t input_motor, uint8_t hw_motor_limit)
235 {
236 if (motor_centre_pos == 0 && ((hw_motor_limit & BIT_4) == 0))
237 motor_centre_pos = input_motor;
238
239 // If Left Limit Set, Move Right
240 if (hw_motor_limit & BIT_3)
241 {
242 if (--counter >= 0)
243 {
244 hw_motor_control = MOTOR_RIGHT; // Move Right
245 return;
246 }
247 // Counter Expired, Right Limit Still Not Reached
248 else
249 ohud.blit_text_new(col2, 11, "FAIL 1", 0x80);
250 }
251 // Right Limit Reached
252 else if (hw_motor_limit & BIT_5)
253 {
254 ohud.blit_text_new(col2, 11, " H", 0x80);
255 ohud.blit_text_new(col2, 11, Utils::to_string(input_motor).c_str(), 0x80);
256 }
257 else
258 {
259 ohud.blit_text_new(col2, 11, "FAIL 2", 0x80);
260 motor_enabled = false;
261 motor_state = STATE_DONE;
262 return;
263 }
264
265 motor_state = STATE_CENTRE;
266 counter = COUNTER_RESET;
267 }
268
269
diag_centre(int16_t input_motor,uint8_t hw_motor_limit)270 void OOutputs::diag_centre(int16_t input_motor, uint8_t hw_motor_limit)
271 {
272 if (hw_motor_limit & BIT_4)
273 {
274 if (--counter >= 0)
275 {
276 hw_motor_control = (counter <= COUNTER_RESET/2) ? MOTOR_RIGHT : MOTOR_LEFT; // Move Right
277 return;
278 }
279 else
280 {
281 ohud.blit_text_new(col2, 13, "FAIL", 0x80);
282 }
283 }
284 else
285 {
286 ohud.blit_text_new(col2, 13, " H", 0x80);
287 ohud.blit_text_new(col2, 13, Utils::to_string((input_motor + motor_centre_pos) >> 1).c_str(), 0x86);
288 hw_motor_control = MOTOR_OFF; // switch off
289 counter = 32;
290 motor_state = STATE_DONE;
291 }
292 }
293
diag_done()294 void OOutputs::diag_done()
295 {
296 if (counter > 0)
297 counter--;
298
299 if (counter == 0)
300 hw_motor_control = MOTOR_CENTRE;
301 }
302
303 // ------------------------------------------------------------------------------------------------
304 // Calibrate Motors
305 // ------------------------------------------------------------------------------------------------
306
calibrate_motor(int16_t input_motor,uint8_t hw_motor_limit,uint32_t packets)307 bool OOutputs::calibrate_motor(int16_t input_motor, uint8_t hw_motor_limit, uint32_t packets)
308 {
309 switch (motor_state)
310 {
311 // Initalize
312 case STATE_INIT:
313 col1 = 11;
314 col2 = 25;
315 ohud.blit_text_big( 2, "MOTOR CALIBRATION");
316 ohud.blit_text_new(col1, 10, "MOVE LEFT -");
317 ohud.blit_text_new(col1, 12, "MOVE RIGHT -");
318 ohud.blit_text_new(col1, 14, "MOVE CENTRE -");
319 counter = 25;
320 motor_centre_pos = 0;
321 motor_enabled = true;
322 motor_state++;
323 break;
324
325 // Just a delay to wait for the serial for safety
326 case STATE_DELAY:
327 if (--counter == 0 || packets > 60)
328 {
329 counter = COUNTER_RESET;
330 motor_state++;
331 }
332 break;
333
334 // Calibrate Left Limit
335 case STATE_LEFT:
336 calibrate_left(input_motor, hw_motor_limit);
337 break;
338
339 // Calibrate Right Limit
340 case STATE_RIGHT:
341 calibrate_right(input_motor, hw_motor_limit);
342 break;
343
344 // Return to Centre
345 case STATE_CENTRE:
346 calibrate_centre(input_motor, hw_motor_limit);
347 break;
348
349 // Clear Screen & Exit Calibration
350 case STATE_DONE:
351 calibrate_done();
352 break;
353
354 case STATE_EXIT:
355 return true;
356 }
357
358 return false;
359 }
360
calibrate_left(int16_t input_motor,uint8_t hw_motor_limit)361 void OOutputs::calibrate_left(int16_t input_motor, uint8_t hw_motor_limit)
362 {
363 // If Right Limit Set, Move Left
364 if (hw_motor_limit & BIT_5)
365 {
366 if (--counter >= 0)
367 {
368 hw_motor_control = MOTOR_LEFT;
369 return;
370 }
371 // Counter Expired, Left Limit Still Not Reached
372 else
373 {
374 ohud.blit_text_new(col2, 10, "FAIL 1");
375 motor_centre_pos = 0;
376 limit_left = input_motor; // Set Left Limit
377 hw_motor_control = MOTOR_LEFT; // Move Left
378 counter = COUNTER_RESET;
379 motor_state = STATE_RIGHT;
380 }
381 }
382 // Left Limit Reached
383 else if (hw_motor_limit & BIT_3)
384 {
385 ohud.blit_text_new(col2, 10, Utils::to_hex_string(input_motor).c_str(), 0x80);
386 motor_centre_pos = 0;
387 limit_left = input_motor; // Set Left Limit
388 hw_motor_control = MOTOR_LEFT; // Move Left
389 counter = COUNTER_RESET;
390 motor_state = STATE_RIGHT;
391 }
392 else
393 {
394 ohud.blit_text_new(col2, 10, "FAIL 2");
395 ohud.blit_text_new(col2, 12, "FAIL 2");
396 motor_enabled = false;
397 counter = COUNTER_RESET;
398 motor_state = STATE_CENTRE;
399 }
400 }
401
calibrate_right(int16_t input_motor,uint8_t hw_motor_limit)402 void OOutputs::calibrate_right(int16_t input_motor, uint8_t hw_motor_limit)
403 {
404 if (motor_centre_pos == 0 && ((hw_motor_limit & BIT_4) == 0))
405 {
406 motor_centre_pos = input_motor;
407 }
408
409 // If Left Limit Set, Move Right
410 if (hw_motor_limit & BIT_3)
411 {
412 if (--counter >= 0)
413 {
414 hw_motor_control = MOTOR_RIGHT; // Move Right
415 return;
416 }
417 // Counter Expired, Right Limit Still Not Reached
418 else
419 {
420 ohud.blit_text_new(col2, 12, "FAIL 1");
421 limit_right = input_motor;
422 motor_state = STATE_CENTRE;
423 counter = COUNTER_RESET;
424 }
425 }
426 // Right Limit Reached
427 else if (hw_motor_limit & BIT_5)
428 {
429 ohud.blit_text_new(col2, 12, Utils::to_hex_string(input_motor).c_str(), 0x80);
430 limit_right = input_motor; // Set Right Limit
431 motor_state = STATE_CENTRE;
432 counter = COUNTER_RESET;
433 }
434 else
435 {
436 ohud.blit_text_new(col2, 12, "FAIL 2");
437 motor_enabled = false;
438 motor_state = STATE_CENTRE;
439 counter = COUNTER_RESET;
440 }
441 }
442
calibrate_centre(int16_t input_motor,uint8_t hw_motor_limit)443 void OOutputs::calibrate_centre(int16_t input_motor, uint8_t hw_motor_limit)
444 {
445 bool fail = false;
446
447 if (hw_motor_limit & BIT_4)
448 {
449 if (--counter >= 0)
450 {
451 hw_motor_control = (counter <= COUNTER_RESET/2) ? MOTOR_RIGHT : MOTOR_LEFT; // Move Right
452 return;
453 }
454 else
455 {
456 ohud.blit_text_new(col2, 14, "FAIL SW");
457 fail = true;
458 // Fall through to EEB6
459 }
460 }
461
462 // 0xEEB6:
463 motor_centre_pos = (input_motor + motor_centre_pos) >> 1;
464
465 int16_t d0 = limit_right - motor_centre_pos;
466 int16_t d1 = motor_centre_pos - limit_left;
467
468 // set both to left limit
469 if (d0 > d1)
470 d1 = d0;
471
472 d0 = d1;
473
474 limit_left = d0 - 6;
475 limit_right = -d1 + 6;
476
477 if (std::abs(motor_centre_pos - 0x80) > 0x20)
478 {
479 ohud.blit_text_new(col2, 14, "FAIL DIST");
480 motor_enabled = false;
481 }
482 else if (!fail)
483 {
484 ohud.blit_text_new(col2, 14, Utils::to_hex_string(motor_centre_pos).c_str(), 0x80);
485 }
486
487 ohud.blit_text_new(13, 17, "TESTS COMPLETE!", 0x82);
488
489 hw_motor_control = MOTOR_OFF; // switch off
490 counter = 90;
491 motor_state = STATE_DONE;
492 }
493
calibrate_done()494 void OOutputs::calibrate_done()
495 {
496 if (counter > 0)
497 counter--;
498 else
499 motor_state = STATE_EXIT;
500 }
501
502 // ------------------------------------------------------------------------------------------------
503 // Moving Cabinet Code
504 // ------------------------------------------------------------------------------------------------
505
506 const static uint8_t MOTOR_VALUES[] =
507 {
508 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3,
509 2, 2, 3, 3, 4, 4, 5, 5, 2, 3, 4, 5, 6, 7, 7, 7
510 };
511
512 const static uint8_t MOTOR_VALUES_STATIONARY[] =
513 {
514 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4
515 };
516
517 const static uint8_t MOTOR_VALUES_OFFROAD1[] =
518 {
519 0x8, 0x8, 0x5, 0x5, 0x8, 0x8, 0xB, 0xB, 0x8, 0x8, 0x4, 0x4, 0x8, 0x8, 0xC, 0xC,
520 0x8, 0x8, 0x3, 0x3, 0x8, 0x8, 0xD, 0xD, 0x8, 0x8, 0x2, 0x2, 0x8, 0x8, 0xE, 0xE,
521 };
522
523 const static uint8_t MOTOR_VALUES_OFFROAD2[] =
524 {
525 0x8, 0x5, 0x8, 0xB, 0x8, 0x5, 0x8, 0xB, 0x8, 0x4, 0x8, 0xC, 0x8, 0x4, 0x8, 0xC,
526 0x8, 0x3, 0x8, 0xD, 0x8, 0x3, 0x8, 0xD, 0x8, 0x2, 0x8, 0xE, 0x8, 0x2, 0x8, 0xE,
527 };
528
529 const static uint8_t MOTOR_VALUES_OFFROAD3[] =
530 {
531 0x8, 0x5, 0x5, 0x8, 0xB, 0x8, 0x0, 0x8, 0x8, 0x4, 0x4, 0x8, 0xC, 0x8, 0x0, 0x8,
532 0x8, 0x3, 0x3, 0x8, 0xD, 0x8, 0x0, 0x8, 0x8, 0x2, 0x2, 0x8, 0xE, 0x8, 0x0, 0x8,
533 };
534
535 const static uint8_t MOTOR_VALUES_OFFROAD4[] =
536 {
537 0x8, 0xB, 0xB, 0x8, 0x5, 0x8, 0x0, 0x8, 0x8, 0xC, 0xC, 0x8, 0x4, 0x8, 0x0, 0x8,
538 0x8, 0xD, 0xD, 0x8, 0x3, 0x8, 0x0, 0x8, 0x8, 0xE, 0xE, 0x8, 0x2, 0x8, 0x0, 0x8,
539 };
540
541
542 // Process Motor Code.
543 // Note, that only the Deluxe Moving Motor Code is ported for now.
544 // Source: 0xE644
do_motors(int MODE,int16_t input_motor)545 void OOutputs::do_motors(int MODE, int16_t input_motor)
546 {
547 motor_x_change = -(input_motor - (MODE == MODE_FFEEDBACK ? CENTRE_POS : motor_centre_pos));
548
549 if (!motor_enabled)
550 {
551 done();
552 return;
553 }
554
555 // In-Game: Test for crash, skidding, whether car is moving
556 if (outrun.game_state == GS_INGAME)
557 {
558 if (ocrash.crash_counter)
559 {
560 if ((oinitengine.car_increment >> 16) <= 0x14)
561 car_stationary();
562 else
563 do_motor_crash();
564 }
565 else if (ocrash.skid_counter)
566 {
567 do_motor_crash();
568 }
569 else
570 {
571 if ((oinitengine.car_increment >> 16) <= 0x14)
572 {
573 if (!was_small_change)
574 done();
575 else
576 car_stationary();
577 }
578 else
579 car_moving(MODE);
580 }
581 }
582 // Not In-Game: Act as though car is stationary / moving slow
583 else
584 {
585 car_stationary();
586 }
587 }
588
589 // Source: 0xE6DA
car_moving(const int MODE)590 void OOutputs::car_moving(const int MODE)
591 {
592 // Motor is currently moving
593 if (motor_movement)
594 {
595 hw_motor_control = motor_control;
596 adjust_motor();
597 return;
598 }
599
600 // Motor is not currently moving. Setup new movement as necessary.
601 if (oferrari.wheel_state != OFerrari::WHEELS_ON)
602 {
603 do_motor_offroad();
604 return;
605 }
606
607 const uint16_t car_inc = oinitengine.car_increment >> 16;
608 if (car_inc <= 0x64) speed = 0;
609 else if (car_inc <= 0xA0) speed = 1 << 3;
610 else if (car_inc <= 0xDC) speed = 2 << 3;
611 else speed = 3 << 3;
612
613 if (oinitengine.road_curve == 0) curve = 0;
614 else if (oinitengine.road_curve <= 0x3C) curve = 2; // sharp curve
615 else if (oinitengine.road_curve <= 0x5A) curve = 1; // gentle curve
616 else curve = 0;
617
618 int16_t steering = oinputs.steering_adjust;
619 steering += (movement_adjust1 + movement_adjust2 + movement_adjust3);
620 steering >>= 2;
621 movement_adjust3 = movement_adjust2; // Trickle down values
622 movement_adjust2 = movement_adjust1;
623 movement_adjust1 = oinputs.steering_adjust;
624
625 // Veer Left
626 if (steering >= 0)
627 {
628 steering = (steering >> 4) - 1;
629 if (steering < 0)
630 {
631 car_stationary();
632 return;
633 }
634
635 if (steering > 0)
636 steering--;
637
638 uint8_t motor_value = MOTOR_VALUES[speed + curve];
639
640 if (MODE == MODE_FFEEDBACK)
641 {
642 hw_motor_control = motor_value + 8;
643 }
644 else
645 {
646 int16_t change = motor_x_change + (motor_value << 1);
647 // Latch left movement
648 if (change >= limit_left)
649 {
650 hw_motor_control = MOTOR_CENTRE;
651 motor_movement = 1;
652 motor_control = 7;
653 motor_change_latch = motor_x_change;
654 }
655 else
656 {
657 hw_motor_control = motor_value + 8;
658 }
659 }
660
661 done();
662 }
663 // Veer Right
664 else
665 {
666 steering = -steering;
667 steering = (steering >> 4) - 1;
668 if (steering < 0)
669 {
670 car_stationary();
671 return;
672 }
673
674 if (steering > 0)
675 steering--;
676
677 uint8_t motor_value = MOTOR_VALUES[speed + curve];
678
679 if (MODE == MODE_FFEEDBACK)
680 {
681 hw_motor_control = -motor_value + 8;
682 }
683 else
684 {
685 int16_t change = motor_x_change - (motor_value << 1);
686 // Latch right movement
687 if (change <= limit_right)
688 {
689 hw_motor_control = MOTOR_CENTRE;
690 motor_movement = -1;
691 motor_control = 9;
692 motor_change_latch = motor_x_change;
693 }
694 else
695 {
696 hw_motor_control = -motor_value + 8;
697 }
698 }
699
700 done();
701 }
702 }
703
704 // Source: 0xE822
car_stationary()705 void OOutputs::car_stationary()
706 {
707 int16_t change = std::abs(motor_x_change);
708
709 if (change <= 8)
710 {
711 if (!is_centered)
712 {
713 hw_motor_control = MOTOR_CENTRE;
714 is_centered = true;
715 }
716 else
717 {
718 hw_motor_control = MOTOR_OFF;
719 is_centered = false;
720 done();
721 }
722 }
723 else
724 {
725 int8_t motor_value = MOTOR_VALUES_STATIONARY[change >> 3];
726
727 if (motor_x_change >= 0)
728 motor_value = -motor_value;
729
730 hw_motor_control = motor_value + 8;
731
732 done();
733 }
734 }
735
736 // Source: 0xE8DA
adjust_motor()737 void OOutputs::adjust_motor()
738 {
739 int16_t change = motor_change_latch; // d1
740 motor_change_latch = motor_x_change;
741 change -= motor_x_change;
742 if (change < 0)
743 change = -change;
744
745 // no movement
746 if (change <= 2)
747 {
748 motor_movement = 0;
749 is_centered = true;
750 }
751 // moving right
752 else if (motor_movement < 0)
753 {
754 if (++motor_control > 10)
755 motor_control = 10;
756 }
757 // moving left
758 else
759 {
760 if (--motor_control < 6)
761 motor_control = 6;
762 }
763
764 done();
765 }
766
767 // Adjust motor during crash/skid state
768 // Source: 0xE994
do_motor_crash()769 void OOutputs::do_motor_crash()
770 {
771 if (oferrari.car_x_diff == 0)
772 set_value(MOTOR_VALUES_OFFROAD1, 3);
773 else if (oferrari.car_x_diff < 0)
774 set_value(MOTOR_VALUES_OFFROAD4, 3);
775 else
776 set_value(MOTOR_VALUES_OFFROAD3, 3);
777 }
778
779 // Adjust motor when wheels are off-road
780 // Source: 0xE9BE
do_motor_offroad()781 void OOutputs::do_motor_offroad()
782 {
783 const uint8_t* table = (oferrari.wheel_state != OFerrari::WHEELS_OFF) ? MOTOR_VALUES_OFFROAD2 : MOTOR_VALUES_OFFROAD1;
784
785 const uint16_t car_inc = oinitengine.car_increment >> 16;
786 uint8_t index;
787 if (car_inc <= 0x32) index = 0;
788 else if (car_inc <= 0x50) index = 1;
789 else if (car_inc <= 0x6E) index = 2;
790 else index = 3;
791
792 set_value(table, index);
793 }
794
set_value(const uint8_t * table,uint8_t index)795 void OOutputs::set_value(const uint8_t* table, uint8_t index)
796 {
797 hw_motor_control = table[(index << 3) + (counter & 7)];
798 counter++;
799 done();
800 }
801
802 // Source: 0xE94E
done()803 void OOutputs::done()
804 {
805 if (std::abs(motor_x_change) <= 8)
806 {
807 was_small_change = true;
808 motor_control = MOTOR_CENTRE;
809 }
810 else
811 {
812 was_small_change = false;
813 }
814 }
815
816 // Send output commands to motor hardware
817 // This is the equivalent to writing to register 0x140003
motor_output(uint8_t cmd)818 void OOutputs::motor_output(uint8_t cmd)
819 {
820 if (cmd == MOTOR_OFF || cmd == MOTOR_CENTRE)
821 return;
822
823 int8_t force = 0;
824
825 if (cmd < MOTOR_CENTRE) // left
826 force = cmd - 1;
827 else if (cmd > MOTOR_CENTRE) // right
828 force = 15 - cmd;
829
830 forcefeedback::set(cmd, force);
831 }
832
833 // ------------------------------------------------------------------------------------------------
834 // Deluxe Upright: Steering Wheel Movement
835 // ------------------------------------------------------------------------------------------------
836
837 // Deluxe Upright: Vibration Enable Table. 4 Groups of vibration values.
838 const static uint8_t VIBRATE_LOOKUP[] =
839 {
840 // SLOW SPEED -------- // MEDIUM SPEED ------
841 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0,
842 // FAST SPEED -------- // VERY FAST SPEED ---
843 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
844 };
845
846 // Source: 0xEAAA
do_vibrate_upright()847 void OOutputs::do_vibrate_upright()
848 {
849 if (outrun.game_state != GS_INGAME)
850 {
851 clear_digital(D_MOTOR);
852 return;
853 }
854
855 const uint16_t speed = oinitengine.car_increment >> 16;
856 uint16_t index = 0;
857
858 // Car Crashing: Diable Motor once speed below 10
859 if (ocrash.crash_counter)
860 {
861 if (speed <= 10)
862 {
863 clear_digital(D_MOTOR);
864 return;
865 }
866 }
867 // Car Normal
868 else if (!ocrash.skid_counter)
869 {
870 // 0xEAE2: Disable Vibration once speed below 30 or wheels on-road
871 if (speed < 30 || oferrari.wheel_state == OFerrari::WHEELS_ON)
872 {
873 clear_digital(D_MOTOR);
874 return;
875 }
876
877 // 0xEAFC: Both wheels off-road. Faster the car speed, greater the chance of vibrating
878 if (oferrari.wheel_state == OFerrari::WHEELS_OFF)
879 {
880 if (speed > 220) index = 3;
881 else if (speed > 170) index = 2;
882 else if (speed > 120) index = 1;
883 }
884 // 0xEB38: One wheel off-road. Faster the car speed, greater the chance of vibrating
885 else
886 {
887 if (speed > 270) index = 3;
888 else if (speed > 210) index = 2;
889 else if (speed > 150) index = 1;
890 }
891
892 if (VIBRATE_LOOKUP[ (vibrate_counter & 7) + (index << 3) ])
893 set_digital(D_MOTOR);
894 else
895 clear_digital(D_MOTOR);
896
897 vibrate_counter++;
898 return;
899 }
900 // 0xEB68: Car Crashing or Skidding
901 if (speed > 140) index = 3;
902 else if (speed > 100) index = 2;
903 else if (speed > 60) index = 1;
904
905 if (VIBRATE_LOOKUP[ (vibrate_counter & 7) + (index << 3) ])
906 set_digital(D_MOTOR);
907 else
908 clear_digital(D_MOTOR);
909
910 vibrate_counter++;
911 }
912
913 // ------------------------------------------------------------------------------------------------
914 // Mini Upright: Steering Wheel Movement
915 // ------------------------------------------------------------------------------------------------
916
do_vibrate_mini()917 void OOutputs::do_vibrate_mini()
918 {
919 if (outrun.game_state != GS_INGAME)
920 {
921 clear_digital(D_MOTOR);
922 return;
923 }
924
925 const uint16_t speed = oinitengine.car_increment >> 16;
926 uint16_t index = 0;
927
928 // Car Crashing: Disable Motor once speed below 10
929 if (ocrash.crash_counter)
930 {
931 if (speed <= 10)
932 {
933 clear_digital(D_MOTOR);
934 return;
935 }
936 }
937 // Car Normal
938 else if (!ocrash.skid_counter)
939 {
940 if (speed < 10 || oferrari.wheel_state == OFerrari::WHEELS_ON)
941 {
942 clear_digital(D_MOTOR);
943 return;
944 }
945
946 if (speed > 140) index = 5;
947 else if (speed > 100) index = 4;
948 else if (speed > 60) index = 3;
949 else if (speed > 20) index = 2;
950 else index = 1;
951
952 if (index > vibrate_counter)
953 {
954 vibrate_counter = 0;
955 clear_digital(D_MOTOR);
956 }
957 else
958 {
959 vibrate_counter++;
960 set_digital(D_MOTOR);
961 }
962 return;
963 }
964
965 // 0xEC7A calc_crash_skid:
966 if (speed > 90) index = 4;
967 else if (speed > 70) index = 3;
968 else if (speed > 50) index = 2;
969 else if (speed > 30) index = 1;
970 if (index > vibrate_counter)
971 {
972 vibrate_counter = 0;
973 clear_digital(D_MOTOR);
974 }
975 else
976 {
977 vibrate_counter++;
978 set_digital(D_MOTOR);
979 }
980 }
981
982 // ------------------------------------------------------------------------------------------------
983 // Coin Chute Output
984 // Source: 0x6F8C
985 // ------------------------------------------------------------------------------------------------
986
coin_chute_out(CoinChute * chute,bool insert)987 void OOutputs::coin_chute_out(CoinChute* chute, bool insert)
988 {
989 // Initalize counter if coin inserted
990 chute->counter[2] = insert ? 1 : 0;
991
992 if (chute->counter[0])
993 {
994 if (--chute->counter[0] != 0)
995 return;
996 chute->counter[1] = 6;
997 clear_digital(chute->output_bit);
998 }
999 else if (chute->counter[1])
1000 {
1001 chute->counter[1]--;
1002 }
1003 // Coin first inserted. Called Once.
1004 else if (chute->counter[2])
1005 {
1006 chute->counter[2]--;
1007 chute->counter[0] = 6;
1008 set_digital(chute->output_bit);
1009 }
1010 }