1 /***************************************************************************
2 Road Rendering & Control
3
4 This is a complete port of the 68000 SUB CPU Program ROM.
5
6 The original code consists of a shared Sega library and some routines
7 which are OutRun specific.
8
9 Some of the original code is not used and is therefore not ported.
10
11 This is the most complex area of the game code, and an area of the code
12 in need of refactoring.
13
14 Useful background reading on road rendering:
15 http://www.extentofthejam.com/pseudo/
16
17 Copyright Chris White.
18 See license.txt for more details.
19 ***************************************************************************/
20
21 #include "stdint.hpp"
22 #include "globals.hpp"
23 #include "roms.hpp"
24 #include "trackloader.hpp"
25
26 #include "engine/oaddresses.hpp"
27 #include "engine/outils.hpp"
28 #include "engine/oinitengine.hpp"
29
30 #include "engine/oroad.hpp"
31 #include "engine/ostats.hpp"
32
33 ORoad oroad;
34
ORoad(void)35 ORoad::ORoad(void)
36 {
37
38 }
39
~ORoad(void)40 ORoad::~ORoad(void)
41 {
42 }
43
tick()44 void ORoad::tick()
45 {
46 // Enhancement: Adjust View
47 if (horizon_target != horizon_offset)
48 {
49 if (horizon_offset > horizon_target)
50 {
51 horizon_offset -= 16;
52 if (horizon_offset < horizon_target)
53 horizon_offset = horizon_target;
54 }
55 else if (horizon_offset < horizon_target)
56 {
57 horizon_offset += 16;
58 if (horizon_offset > horizon_target)
59 horizon_offset = horizon_target;
60 }
61 }
62
63 do_road();
64 }
65
66 // Helper function
get_road_y(uint16_t index)67 int16_t ORoad::get_road_y(uint16_t index)
68 {
69 return -(road_y[road_p0 + index] >> 4) + 223;
70 }
71
72 // Initialize Road Values
73 //
74 // Source Address: 0x10B8
75 // Input: None
76 // Output: None
77
init()78 void ORoad::init()
79 {
80 // Extra initalization code here
81 set_view_mode(VIEW_ORIGINAL, true);
82 road_pos = 0;
83 tilemap_h_target = 0;
84 stage_lookup_off = 0;
85 road_width_bak = 0;
86 car_x_bak = 0;
87 height_lookup = 0;
88 road_pos_change = 0;
89 road_load_end = 0;
90 road_ctrl = ROAD_OFF;
91 road_load_split = 0;
92 road_width = 0;
93 horizon_y2 = 0;
94 horizon_y_bak = 0;
95 pos_fine = 0;
96 horizon_base = 0;
97
98 for (int i = 0; i < ARRAY_LENGTH; i++)
99 {
100 road_x[i] = 0;
101 road0_h[i] = 0;
102 road1_h[i] = 0;
103 road_unk[i] = 0;
104 }
105
106 for (int i = 0; i < 0x1000; i++)
107 road_y[i] = 0;
108
109 road_pos_old = 0;
110 road_data_offset = 0;
111 height_start = 0;
112 height_ctrl = 0;
113 pos_fine_old = 0;
114 pos_fine_diff = 0;
115 counter = 0;
116 height_index = 0;
117 height_final = 0;
118 height_inc = 0;
119 height_step = 0;
120 height_ctrl2 = 0;
121 height_addr = 0;
122 elevation = 0;
123 height_lookup_wrk = 0;
124 height_delay = 0;
125 step_adjust = 0;
126 do_height_inc = 0;
127 height_end = 0;
128 up_mult = 0;
129 down_mult = 0;
130 horizon_mod = 0;
131 length_offset = 0;
132 a1_lookup = 0;
133 change_per_entry = 0;
134 d5_o = 0;
135 a3_o = 0;
136 y_addr = 0;
137 scanline = 0;
138 total_height = 0;
139 // End of extra initialization code
140
141 stage_loaded = -1;
142 horizon_set = 0;
143
144 road_p0 = 0;
145 road_p1 = road_p0 + 0x400;
146 road_p2 = road_p1 + 0x400;
147 road_p3 = road_p2 + 0x400;
148
149 set_default_hscroll();
150 clear_road_ram();
151 init_stage1();
152 }
153
154 // ----------------------------------------------------------------------------
155 // View Mode Enhancements
156 //
157 // Original: Same as Arcade
158 // Elevated: Camera positioned further above car
159 // In Car : Lower viewpoint
160 // ----------------------------------------------------------------------------
161
get_view_mode()162 uint8_t ORoad::get_view_mode()
163 {
164 return view_mode;
165 }
166
set_view_mode(uint8_t mode,bool snap)167 void ORoad::set_view_mode(uint8_t mode, bool snap)
168 {
169 view_mode = mode;
170
171 if (mode == VIEW_ORIGINAL)
172 {
173 horizon_target = 0;
174 }
175 else if (mode == VIEW_ELEVATED)
176 {
177 horizon_target = 0x280;
178 }
179 else if (mode == VIEW_INCAR)
180 {
181 horizon_target = -0x70;
182 }
183
184 if (snap)
185 horizon_offset = horizon_target;
186 }
187
188 // Main Loop
189 //
190 // Source Address: 0x1044
191 // Input: None
192 // Output: None
do_road()193 void ORoad::do_road()
194 {
195 rotate_values();
196 setup_road_x();
197 setup_road_y();
198 set_road_y();
199 set_horizon_y();
200 do_road_data();
201 blit_roads();
202 output_hscroll(&road0_h[0], HW_HSCROLL_TABLE0);
203 output_hscroll(&road1_h[0], HW_HSCROLL_TABLE1);
204 copy_bg_color();
205
206 hwroad.read_road_control(); // swap halves of road ram
207 }
208
209 // Set Default Horizontal Scroll Values
210 //
211 // Source Address: 0x1106
212 // Input: None
213 // Output: None
214
set_default_hscroll()215 void ORoad::set_default_hscroll()
216 {
217 uint32_t adr = HW_HSCROLL_TABLE0;
218
219 for (uint16_t i = 0; i <= 0x1FF; i++)
220 hwroad.write32(&adr, 0x1000100);
221
222 hwroad.read_road_control();
223 }
224
225 // Clear Road RAM
226 //
227 // Source Address: 0x115A
228 // Input: None
229 // Output: None
230
231 // Initialises 0x80000 - 0x8001C0 as follows.
232 //
233 // 0x80000 : 00 00 [scanline 0]
234 // 0x80002 : 00 01 [scanline 1]
235 // 0x80004 : 00 02 [scanline 2]
236
clear_road_ram()237 void ORoad::clear_road_ram()
238 {
239 uint32_t adr = 0x80000; // start of road ram
240
241 for (uint8_t scanline = 0; scanline < S16_HEIGHT; scanline++)
242 hwroad.write16(&adr, scanline);
243
244 hwroad.read_road_control();
245 }
246
247 // Initalize Stage 1
248 //
249 // Source Address: 0x10DE
250 // Input: None
251 // Output: None
252
init_stage1()253 void ORoad::init_stage1()
254 {
255 trackloader.init_path(0);
256 road_pos = 0;
257 road_ctrl = ROAD_BOTH_P0;
258 // ignore init counter stuff (move.b #$A,$49(a5))
259 }
260
261 // Rotate Values & Load Next Level/Split/Bonus Road
262 //
263 // Source Address: 0x119E
264 // Input: None
265 // Output: None
266
rotate_values()267 void ORoad::rotate_values()
268 {
269 uint16_t p0_copy = road_p0;
270 road_p0 = road_p1;
271 road_p1 = road_p2;
272 road_p2 = road_p3;
273 road_p3 = p0_copy;
274
275 road_pos_change = (road_pos >> 16) - road_pos_old;
276 road_pos_old = road_pos >> 16;
277
278 check_load_road();
279 }
280
281 // Check whether we need to load the next stage / road split / bonus data
282 //
283 // Source Address: 0x11D0
284 // Input: None
285 // Output: None
286
check_load_road()287 void ORoad::check_load_road()
288 {
289 // If stage is not loaded
290 if (ostats.cur_stage != stage_loaded)
291 {
292 stage_loaded = ostats.cur_stage; // Denote loaded
293 trackloader.init_path(stage_lookup_off);
294 return;
295 }
296
297 // Check Split
298 if (road_load_split)
299 {
300 trackloader.init_path_split();
301 road_load_split = 0;
302 }
303
304 // Check End Section
305 else if (road_load_end)
306 {
307 trackloader.init_path_end();
308 road_load_end = 0;
309 }
310 }
311
312 // 1/ Setup Road X Data from ROM
313 // 2/ Apply H-Scroll To Data
314 //
315 // Source Address: 0x1590
316 // Input: None
317 // Output: None
318
setup_road_x()319 void ORoad::setup_road_x()
320 {
321 // If moved to next chunk of road data, setup x positions
322 if (road_pos_change != 0)
323 {
324 road_data_offset = (road_pos >> 16) << 2;
325 //uint32_t addr = stage_addr + road_data_offset;
326 uint32_t addr = road_data_offset; // temporary hack for custom data
327 set_tilemap_x(addr);
328 setup_x_data(addr);
329 }
330 setup_hscroll();
331 }
332
333 // Setup Road Path
334 //
335 // Road Path is stored as a series of words representing x,y change
336 //
337 // Source Address: 0x15B0
338
setup_x_data(uint32_t addr)339 void ORoad::setup_x_data(uint32_t addr)
340 {
341 const int16_t x = trackloader.readPath(addr) + trackloader.readPath(addr + 4); // Length 1
342 const int16_t y = trackloader.readPath(addr + 2) + trackloader.readPath(addr + 6); // Length 2
343
344 // Use Pythagorus' theorem to find the distance/length between x & y
345 const uint16_t distance = outils::isqrt((x * x) + (y * y));
346
347 const int16_t curve_x_dist = (x << 14) / distance; // Scale up is for fixed point division in create_curve
348 const int16_t curve_y_dist = (y << 14) / distance;
349
350 // Setup Default Straight Positions Of Road at 60800 - 608FF
351 for (uint8_t i = 0; i < 0x80;)
352 {
353 // Set marker to ignore current road position, and use last good value
354 road_x[i++] = 0x3210;
355 road_x[i++] = 0x3210;
356 }
357
358 // Now We Setup The Real Road Data at 60800
359 int32_t curve_x_total = 0;
360 int32_t curve_y_total = 0;
361 int16_t curve_start = 0;
362 int16_t curve_end = 0;
363 int16_t curve_inc = 0;
364 int16_t curve_inc_old = 0;
365
366 int16_t scanline = 0x37E / 2; // Index into road_x
367
368 // Note this works its way from closest to the camera, further into the horizon
369 // So the amount to offset x is going to increase over time
370 // We sample 20 Road Positions to generate the road.
371 for (uint8_t i = 0; i <= 0x20; i++)
372 {
373 const int32_t x_next = trackloader.readPath(addr) + trackloader.readPath(addr + 4); // Length 1
374 const int32_t y_next = trackloader.readPath(addr + 2) + trackloader.readPath(addr + 6); // Length 2
375 addr += 8;
376
377 curve_x_total += x_next;
378 curve_y_total += y_next;
379
380 // Calculate curve increment and end position
381 create_curve(curve_inc, curve_end, curve_x_total, curve_y_total, curve_x_dist, curve_y_dist);
382 int16_t curve_steps = curve_end - curve_start;
383 if (curve_steps < 0) return;
384 if (curve_steps == 0) continue; // skip_pos
385
386 // X Amount to increment curve by each iteration
387 int32_t xinc = (curve_inc - curve_inc_old) / curve_steps;
388 int16_t x = curve_inc_old;
389
390 for (int16_t pos = curve_start; pos <= curve_end; pos++)
391 {
392 x += xinc;
393 if (x < -0x3200 || x > 0x3200) return;
394 road_x[scanline] = x;
395 if (--scanline < 0) return;
396 }
397
398 curve_inc_old = curve_inc;
399 curve_start = curve_end;
400 }
401 }
402
403 // Interpolates road data into a smooth curve.
404 //
405 // Source Address: 0x16B6
406
create_curve(int16_t & curve_inc,int16_t & curve_end,int32_t curve_x_total,int32_t curve_y_total,int16_t curve_x_dist,int16_t curve_y_dist)407 void ORoad::create_curve(
408 int16_t &curve_inc, int16_t &curve_end,
409 int32_t curve_x_total, int32_t curve_y_total, int16_t curve_x_dist, int16_t curve_y_dist)
410 {
411 // Note multiplication result should be 32bit, inputs 16 bit
412 int32_t d0 = ((curve_x_total >> 5) * curve_y_dist) - ((curve_y_total >> 5) * curve_x_dist);
413 int32_t d2 = ((curve_x_total >> 5) * curve_x_dist) + ((curve_y_total >> 5) * curve_y_dist);
414
415 d2 >>= 7;
416 int32_t d1 = (d2 >> 7) + 0x410;
417
418 curve_inc = d0 / d1; // Total amount to increment x by
419 curve_end = (d2 / d1) * 4;
420 }
421
422 // Set The Correct Tilemap X *target* Position From The Road Data
423 //
424 // Source Address: 0x16F6
425 //
426 // Use Euclidean distance between a series of points.
427 // This is essentially the standard distance that you'd measure with a ruler.
428
set_tilemap_x(uint32_t addr)429 void ORoad::set_tilemap_x(uint32_t addr)
430 {
431 // d0 = Word 0 + Word 2 + Word 4 + Word 6 [Next 4 x positions]
432 // d1 = Word 1 + Word 3 + Word 5 + Word 7 [Next 4 y positions]
433
434 int16_t x = trackloader.readPath(&addr);
435 int16_t y = trackloader.readPath(&addr);
436 x += trackloader.readPath(&addr);
437 y += trackloader.readPath(&addr);
438 x += trackloader.readPath(&addr);
439 y += trackloader.readPath(&addr);
440 x += trackloader.readPath(&addr);
441 y += trackloader.readPath(&addr);
442
443 int16_t x_abs = x;
444 int16_t y_abs = y;
445 if (x_abs < 0) x_abs = -x_abs;
446 if (y_abs < 0) y_abs = -y_abs;
447
448 int16_t scroll_x;
449
450 // scroll right
451 if (y_abs > x_abs)
452 {
453 scroll_x = (0x100 * x) / y;
454 }
455 // scroll left
456 else
457 {
458 scroll_x = (-0x100 * y) / x;
459 }
460
461 // turn right
462 if (x > 0) scroll_x += 0x200;
463 else if (x < 0) scroll_x += 0x600;
464 else if (y >= 0) scroll_x += 0x200;
465 else scroll_x += 0x600;
466
467 if (y_abs > x_abs)
468 {
469 int32_t d0 = x * y;
470
471 if (d0 >= 0)
472 scroll_x -= 0x200;
473 else
474 scroll_x += 0x200;
475 }
476
477 tilemap_h_target = scroll_x;
478 }
479
480 // Creates the bend in the next section of road.
481 //
482 // Source Address: 0x180C
483
setup_hscroll()484 void ORoad::setup_hscroll()
485 {
486 switch (road_ctrl)
487 {
488 case ROAD_OFF:
489 return;
490
491 case ROAD_R0:
492 case ROAD_R0_SPLIT:
493 do_road_offset(road0_h, -road_width_bak, false);
494 break;
495
496 case ROAD_R1:
497 do_road_offset(road1_h, +road_width_bak, false);
498 break;
499
500 case ROAD_BOTH_P0:
501 case ROAD_BOTH_P1:
502 do_road_offset(road0_h, -road_width_bak, false);
503 do_road_offset(road1_h, +road_width_bak, false);
504 break;
505
506 case ROAD_BOTH_P0_INV:
507 case ROAD_BOTH_P1_INV:
508 do_road_offset(road0_h, -road_width_bak, false);
509 do_road_offset(road1_h, +road_width_bak, true);
510 break;
511
512 case ROAD_R1_SPLIT:
513 do_road_offset(road1_h, +road_width_bak, true);
514 break;
515 }
516 }
517
518 // Adjust the offset for Road 0 or Road 1
519 //
520 // Source Address: 0x1864
521 //
522 // Setup the H-Scroll Values in RAM (not Road RAM) for Road 0.
523 // Take into account the position of the player's car.
524 //
525 // The road data is adjusted per scanline.
526 // It is adjusted a different amount depending on how far into the horizon it is.
527 // So lines closer to the player's car are adjusted more.
528
do_road_offset(int16_t * dst_x,int16_t width,bool invert)529 void ORoad::do_road_offset(int16_t* dst_x, int16_t width, bool invert)
530 {
531 int32_t car_offset = car_x_bak + width + oinitengine.camera_x_off; // note extra debug of camera x
532 int32_t scanline_inc = 0; // Total amount to increment scanline (increased every line)
533 int16_t* src_x = road_x; // a0
534
535 // ---------------------------------------------------------------
536 // Process H-Scroll: Car not central on road 0
537 // The following loop ensures that the road offset
538 // becomes before significant the closer it is to the player's car
539 // ---------------------------------------------------------------
540 if (car_offset != 0)
541 {
542 car_offset <<= 7;
543 for (uint16_t i = 0; i <= 0x1FF; i++)
544 {
545 int16_t h_scroll = scanline_inc >> 16;
546
547 // If ignore car position
548 if (*src_x == 0x3210)
549 h_scroll = 0;
550
551 // Get next line of road x data
552 int16_t x_off = (*src_x++) >> 6;
553
554 if (!invert)
555 h_scroll += x_off;
556 else
557 h_scroll -= x_off;
558
559 // Write final h-scroll value
560 (*dst_x++) = h_scroll;
561
562 scanline_inc += car_offset;
563 }
564 return;
565 }
566 // ------------------------------
567 // Ignore Car Position / H-Scroll
568 // ------------------------------
569
570 if (!invert)
571 {
572 for (uint16_t i = 0; i <= 0x3F; i++)
573 {
574 (*dst_x++) = (*src_x++) >> 6;
575 (*dst_x++) = (*src_x++) >> 6;
576 (*dst_x++) = (*src_x++) >> 6;
577 (*dst_x++) = (*src_x++) >> 6;
578
579 (*dst_x++) = (*src_x++) >> 6;
580 (*dst_x++) = (*src_x++) >> 6;
581 (*dst_x++) = (*src_x++) >> 6;
582 (*dst_x++) = (*src_x++) >> 6;
583 }
584 }
585 else
586 {
587 for (uint16_t i = 0; i <= 0x3F; i++)
588 {
589 (*dst_x++) = -(*src_x++) >> 6;
590 (*dst_x++) = -(*src_x++) >> 6;
591 (*dst_x++) = -(*src_x++) >> 6;
592 (*dst_x++) = -(*src_x++) >> 6;
593
594 (*dst_x++) = -(*src_x++) >> 6;
595 (*dst_x++) = -(*src_x++) >> 6;
596 (*dst_x++) = -(*src_x++) >> 6;
597 (*dst_x++) = -(*src_x++) >> 6;
598 }
599 }
600 }
601
602 // Main Loop
603 //
604 // Source Address: 0x1B12
605 // Input: None
606 // Output: None
607
608 // Road height information is stored in blocks in this ROM.
609 // Each block is interchangeable and can be used with any segment of road.
610 // Each block can vary in length.
611 //
612 // The format of each height block is as follows:
613 //
614 // +0 [Byte] Jump Table Control
615 // 0 = Elevated section
616 // 4 = Flat section
617 // +1 [Byte] Length of each section
618 // +2 [Byte] Length of ascents (multiplied with +1)
619 // +3 [Byte] Length of descents (multiplied with +1)
620 // +4 [Word] 1st Signed Height Value For Section Of Road
621 // +6 [Word] 2st Signed Height Value For Section Of Road
622 //
623 // etc.
624 //
625 // +xx [Word] 0xFFFF / -1 Signifies end of height data
626
setup_road_y()627 void ORoad::setup_road_y()
628 {
629 pos_fine_diff = pos_fine - pos_fine_old;
630 pos_fine_old = pos_fine;
631
632 // Setup horizon base value if necessary
633 if (!horizon_set)
634 {
635 horizon_base = 0x240;
636 horizon_set = 1;
637 }
638
639 // Code omitted that doesn't seem to be used
640
641 switch (height_ctrl)
642 {
643 // Clear Road Height Segment & Init Segment 0
644 case 0:
645 height_lookup = 0;
646 init_height_seg();
647 break;
648
649 // Init Next Road Height Segment
650 case 1:
651 init_height_seg();
652 break;
653
654 // Segment Initialized: Use Elevation Flag
655 case 2:
656 do_elevation();
657 break;
658
659 // Segment Initalized: Delayed Elevation Section
660 case 3:
661 do_elevation_delay();
662 break;
663
664 // Needed for stage 0x1b
665 case 4:
666 do_elevation_mixed();
667 break;
668
669 // Segment Initalized: Ignore Elevation Flag
670 case 5:
671 do_horizon_adjust();
672 break;
673 }
674 }
675
676 // Source Address: 0x1BCE
init_height_seg()677 void ORoad::init_height_seg()
678 {
679 height_index = 0; // Set to first entry in height segment
680 height_inc = 0; // Do not increment to next segment
681 elevation = NO_CHANGE; // No Change to begin with
682 height_step = 1; // Set to start of height segment
683
684 // Get Address of actual road height data
685 height_lookup_wrk = height_lookup;
686 uint32_t h_addr = trackloader.read_heightmap_table(height_lookup_wrk);
687
688 height_ctrl2 = trackloader.read8(trackloader.heightmap_data, &h_addr);
689 step_adjust = trackloader.read8(trackloader.heightmap_data, &h_addr); // Speed at which to move through height segment
690
691 switch (height_ctrl2)
692 {
693 case 0:
694 init_elevation(h_addr);
695 break;
696
697 case 1:
698 case 2:
699 init_elevation_delay(h_addr);
700 break;
701
702 case 3:
703 init_elevation_mixed(h_addr);
704 break;
705
706 case 4:
707 init_horizon_adjust(h_addr);
708 break;
709 }
710 }
711
712
713
714 // Standard Elevation Height Section.
715 // A series of sequential values are provided to adjust the horizon position.
716 //
717 // The duration of each value can be adjusted using the 'step' value and the multipliers.
718 //
719 // To map a road position to a height segment length:
720 //
721 // 1 Position
722 // = 10 Pos Fine Difference (* 10)
723 // = 120 Height Step (* 12)
724 // = 120 / 3 Step Adjust
725 // = 40
726 // = 255 / 40 = 6.3 positions per height segment
727 //
728 // Source Address: 0x1C2C
init_elevation(uint32_t & addr)729 void ORoad::init_elevation(uint32_t& addr)
730 {
731 down_mult = trackloader.read8(trackloader.heightmap_data, &addr);
732 up_mult = trackloader.read8(trackloader.heightmap_data, &addr);
733 height_addr = addr;
734 height_ctrl = 2; // Use do_elevation()
735 do_elevation();
736 }
737
do_elevation()738 void ORoad::do_elevation()
739 {
740 // By Default: One Height Entry Per Two Road Positions
741 // Potential advance stage of height map we're on
742 height_step += pos_fine_diff * 12;
743
744 // Adjust the speed at which we transverse elevation sections of the height map
745 uint16_t d3 = step_adjust;
746 if (elevation == UP)
747 d3 *= up_mult;
748 else if (elevation == DOWN)
749 d3 *= down_mult;
750
751 uint16_t d1 = (height_step / d3);
752 if (d1 > 0xFF) d1 = 0xFF;
753 d1 += 0x100;
754
755 height_start = d1;
756 height_end = d1;
757
758 // Advance to next height entry if appropriate.
759 height_index += height_inc;
760 height_inc = 0;
761
762 // Increment to next height entry in table.
763 if (height_start == 0x1FF)
764 {
765 height_start = 0x1FF;
766 height_end = 0x1FF;
767 height_step = 1; // Start Of Height Segment
768 height_inc = 1;
769 elevation = NO_CHANGE;
770 return;
771 }
772 if (height_lookup == 0 || height_lookup_wrk != 0)
773 return;
774 // If working road height index is 0, then init next section
775 height_ctrl = 1;
776 }
777
778 // Hold Elevation For Specified Delay
779 //
780 // Two values are provided by the data referenced by Height Index and split over three parts.
781 //
782 // Part 1: [Height Index = 0] Height Start Increments from 256 to 511. Change horizon to relevant height over this period
783 // Part 2: [Height Index = 1] Decrement Delay from provided value to 0. Hold horizon at this height
784 // Part 3: [Height Index = 1] Height Start Decrements from 511 to 256. Revert horizon to original height over this period
785 //
786 // <-delay->
787 // _________
788 // [1] / [2] \ [3]
789 // _______/ \______
790 //
791 // Height End remains constant during this period at 512
792 //
793 // Note: There is an intriguing bug in this routine, where the actual length of part 2 differs dependent on the speed you
794 // are driving at. It's actually possible to elongate the length of hills by driving slower!
795 //
796 // Example Data: 0x29AA
797 // Source : 0x1CE4 (first used at the chicane at stage 1)
798
init_elevation_delay(uint32_t & addr)799 void ORoad::init_elevation_delay(uint32_t& addr)
800 {
801 height_delay = trackloader.read16(trackloader.heightmap_data, &addr);
802 height_addr = addr;
803 do_height_inc = 1; // Set to Part 1
804 height_inc = 0;
805 height_end = 0x100; // Remains constant
806 height_ctrl = 3; // Use do_elevation_delay()
807 do_elevation_delay();
808 }
809
810 // Source: 1D04
do_elevation_delay()811 void ORoad::do_elevation_delay()
812 {
813 int16_t d1 = pos_fine_diff * 12;
814 height_index += height_inc; // Next height entry
815 height_inc = 0;
816
817 // Part 1 || Part 3
818 if (height_index == 0 || do_height_inc == 0)
819 {
820 // 1D50 inc_pos_in_seg:
821 height_step += d1;
822 //d1 = 0;
823 d1 = height_step / step_adjust;
824 if (d1 > 0xFF) d1 = 0xFF;
825
826 height_start = d1;
827
828 // Advance to Part 2.
829 if (height_start > 0xFE)
830 {
831 height_start = 0xFF;
832 height_step = 1;
833 height_inc = 1;
834 elevation = NO_CHANGE; // Keep horizon at level for delayed section
835 }
836
837 // Part 3: Invert counter
838 if (height_index != 0)
839 {
840 d1 = 0xFF - height_start;
841 }
842
843 height_start = d1 + 0x100;
844 }
845 // Part 2
846 // Source: 0x1D2E
847 else
848 {
849 height_delay -= (d1 / step_adjust);
850 height_start = 0x1FF;
851
852 if (height_delay < 0) // Set flag so we inc next time
853 do_height_inc = 0;
854 }
855 }
856
857 // Mixed Elevation Section
858 //
859 // This is a combination of looking ahead at the height map entries, but then holding on entry 6 when reached.
860 // It's like a combination of the previous two, although the way in which it's used seems to offer
861 // no real advantage over the previous hold section code.
862 //
863 // Part 1: [Height Index = 0 - 5] Change horizon to relevant height over this period by looking at upcoming height entries.
864 // Height Start & End Increments from 256 to 511.
865 // Part 2: [Height Index = 6] Decrement Delay from provided value to 0. Hold horizon at this height.
866 // Height Start constant at 511 and end at 256
867 // Part 3: [Height Index = 1] Height Start Decrements from 511 to 256. Revert horizon to original height over this period.
868 //
869 // Source : 0x1DAC
870 // Example Data: 0x2A8E
init_elevation_mixed(uint32_t & addr)871 void ORoad::init_elevation_mixed(uint32_t& addr)
872 {
873 height_delay = trackloader.read16(trackloader.heightmap_data, &addr);
874 height_addr = addr;
875 do_height_inc = 1;
876 height_inc = 0;
877 height_ctrl = 4; // Use do_elevation_mixed() function
878 do_elevation_mixed();
879 }
880
881 // Source: 0x1DC6
do_elevation_mixed()882 void ORoad::do_elevation_mixed()
883 {
884 uint16_t d1 = pos_fine_diff * 12;
885 height_index += height_inc;
886 height_inc = 0;
887
888 // Parts 2 & 3 - Delayed Section. Source: 0x1E1C
889 if (height_index >= 6)
890 {
891 uint16_t d3 = step_adjust;
892
893 // Part 2: Create a delay on the sixth entry
894 if (do_height_inc != 0)
895 {
896 height_delay -= (d1 / d3);
897 height_start = 0x1FF;
898 height_end = 0x100;
899 // Delay has expired. Advance to last entry. Source: 0x1E46
900 if (height_delay < 0)
901 {
902 height_addr += 12; // Advance 6 words.
903 do_height_inc = 0;
904 }
905 }
906 // Part 3. Source: 0x1E58
907 else
908 {
909 height_step += d1;
910 d1 = height_step / d3;
911 if (d1 > 0xFF) d1 = 0xFF;
912 height_start = 0x1FF - d1;
913 if (height_start != 0x100) return;
914
915 height_step = 1; // Set position on road segment to start
916 height_inc = 1;
917 elevation = NO_CHANGE;
918 }
919 }
920 // Part 1. Source: 0x1DEA
921 // Look ahead at next 6 entries in data as normal.
922 else
923 {
924 height_step += d1;
925 d1 = height_step / 4;
926 if (d1 > 0xFF) d1 = 0xFF;
927 d1 += 0x100;
928 height_start = d1;
929 height_end = d1;
930
931 if (height_start < 0x1FF) return;
932
933 // set_end2:
934 height_start = 0x1FF;
935 height_end = 0x1FF;
936 height_step = 1; // Set position on road segment to start
937 height_inc = 1;
938 elevation = NO_CHANGE;
939 }
940 }
941
942 // Adjust Horizon To New Position
943 //
944 // Part 1: Height Start Increments from 256 to 512. Change horizon to relevant height over this period.
945 // Alter the speed at which this takes place with the step value.
946 // Part 2: Horizon is set and remains at this position until changed with another horizon data block.
947 //
948 // Example Data : 0x3672
949 // Source : 0x1EB6
init_horizon_adjust(uint32_t & addr)950 void ORoad::init_horizon_adjust(uint32_t& addr)
951 {
952 height_addr = addr;
953
954 // Set Horizon Modifier
955 horizon_mod = trackloader.read16(trackloader.heightmap_data, addr) - horizon_base;
956
957 // Use do_horizon_adjust() function
958 height_ctrl = 5;
959 do_horizon_adjust();
960 }
961
do_horizon_adjust()962 void ORoad::do_horizon_adjust()
963 {
964 height_step += pos_fine_diff * 12;
965 uint16_t d1 = (height_step / step_adjust);
966
967 // Scale height between 100 and 1FF
968 if (d1 > 0xFF) d1 = 0xFF;
969 d1 += 0x100;
970
971 height_start = d1;
972 height_end = d1;
973 }
974
975 // ----------------------------------------------------------------------------
976 // END OF SETUP SECTION
977 // ----------------------------------------------------------------------------
978
979 // Source Address: 0x1F00
set_road_y()980 void ORoad::set_road_y()
981 {
982 switch (height_ctrl2)
983 {
984 // Set Normal Y Coordinate
985 case 0:
986 case 1:
987 case 2:
988 case 3:
989 set_y_interpolate();
990 break;
991 // Set Y with Horizon Adjustment (Stage 2 - Gateway)
992 case 4:
993 set_y_horizon();
994 break;
995 }
996 }
997
998 // 1/ Takes distance into track section
999 // 2/ Interpolates this value into a series of counters, stored between 60700 - 6070D
1000 // 3/ Each of these counters define how many times to write and adjust the height value
1001 //
1002 // section_lengths stores the distance from the player to the horizon.
1003 // This essentially represents 7 track segments.
1004
1005 // Source Address: 0x1F22
set_y_interpolate()1006 void ORoad::set_y_interpolate()
1007 {
1008 // ------------------------------------------------------------------------
1009 // Convert desired elevation of track into a series of section lengths
1010 // Each stored in 7 counters which will be used to output the track
1011 // ------------------------------------------------------------------------
1012
1013 uint16_t d2 = height_end;
1014 uint16_t d1 = 0x1FF - d2;
1015
1016 section_lengths[0] = d1;
1017 d2 >>= 1;
1018 section_lengths[1] = d2;
1019
1020 uint16_t d3 = 0x200 - d1 - d2;
1021 section_lengths[3] = d3;
1022 d3 >>= 1;
1023 section_lengths[2] = d3;
1024 section_lengths[3] -= d3;
1025
1026 d3 = section_lengths[3];
1027 section_lengths[4] = d3;
1028 d3 >>= 1;
1029 section_lengths[3] = d3;
1030 section_lengths[4] -= d3;
1031
1032 d3 = section_lengths[4];
1033 section_lengths[5] = d3;
1034 d3 >>= 1;
1035 section_lengths[4] = d3;
1036 section_lengths[5] -= d3;
1037
1038 d3 = section_lengths[5];
1039 section_lengths[6] = d3;
1040 d3 >>= 1;
1041 section_lengths[5] = d3;
1042 section_lengths[6] -= d3;
1043
1044 length_offset = 0;
1045
1046 // Address of next entry in road height table data [rom so no need to scale]
1047 a1_lookup = (height_index * 2) + height_addr;
1048
1049 // Road Y Positions (references to this decrement) [Destination]
1050 y_addr = 0x200 + road_p1;
1051
1052 a3_o = 0;
1053 road_unk[a3_o++] = 0x1FF;
1054 road_unk[a3_o++] = 0;
1055 counter = 0; // Reset interpolated counter index to 0
1056
1057 const int16_t next_height_value = trackloader.read16(trackloader.heightmap_data, a1_lookup);
1058 height_final = (next_height_value * (height_start - 0x100)) >> 4;
1059
1060 // 1faa
1061 int32_t horizon_copy = (horizon_base + horizon_offset) << 4;
1062 if (height_ctrl2 == 2) // hold state
1063 horizon_copy += height_final;
1064
1065 //set_y_2044(0x200, horizon_copy, 0, y_addr, 2);
1066
1067 change_per_entry = horizon_copy;
1068 scanline = 0x200; // 200 (512 pixels)
1069 total_height = 0;
1070 set_y_2044();
1071 }
1072
1073 // Source: 0x2044
set_y_2044()1074 void ORoad::set_y_2044()
1075 {
1076 if (change_per_entry > 0x10000)
1077 change_per_entry = 0x10000;
1078
1079 d5_o = change_per_entry;
1080
1081 // ------------------------------------------------------------------------
1082 // Get length of this road section in scanlines
1083 // ------------------------------------------------------------------------
1084 int16_t section_length = section_lengths[length_offset++] - 1;
1085 scanline -= section_length;
1086
1087 // ------------------------------------------------------------------------
1088 // Write this section of road number of times dictated by length
1089 // Source: 0x2080: write_y
1090 // ------------------------------------------------------------------------
1091 if (section_length >= 0)
1092 {
1093 for (uint16_t i = 0; i <= section_length; i++)
1094 {
1095 total_height += change_per_entry;
1096 road_y[--y_addr] = (total_height << 4) >> 16;
1097 }
1098 }
1099
1100 // ------------------------------------------------------------------------
1101 // Whilst there are still sections of track to write, read the height for
1102 // the upcoming section
1103 // Source: 0x216C
1104 // ------------------------------------------------------------------------
1105 if (++counter != 7)
1106 {
1107 read_next_height();
1108 return;
1109 }
1110
1111 // Writing last part of interpolated data
1112 road_unk[a3_o] = 0;
1113
1114 int16_t y = trackloader.read16(trackloader.heightmap_data, a1_lookup);
1115
1116 // Return if not end at end of height section data
1117 if (y != -1) return;
1118
1119 // At end of height section data, initalize next segmnet
1120 if (height_lookup == height_lookup_wrk)
1121 height_lookup = 0;
1122
1123 height_ctrl = 1; // init next road segment
1124 }
1125
1126 // Source Address: 0x1FC6
read_next_height()1127 void ORoad::read_next_height()
1128 {
1129 switch (height_ctrl2)
1130 {
1131 case 0:
1132 // set_elevation_flag
1133 break;
1134
1135 case 1:
1136 change_per_entry += height_final;
1137 case 2:
1138 set_elevation();
1139 return; // Note we return
1140 case 3:
1141 if (height_index >= 6)
1142 {
1143 change_per_entry += height_final;
1144 set_elevation();
1145 return;
1146 }
1147 // otherwise set_elevation_flag
1148 break;
1149 }
1150
1151 // 1ff2: set_elevation_flag
1152 // Note the way this bug was fixed
1153 // Needed to read the signed value into an int16_t before assigning to a 32 bit value
1154 change_per_entry = trackloader.read16(trackloader.heightmap_data, &a1_lookup) << 4;
1155 change_per_entry += d5_o;
1156 if (counter != 1)
1157 {
1158 set_elevation();
1159 return;
1160 }
1161
1162 // Writing first part of interpolated data
1163 change_per_entry -= height_final;
1164
1165 int32_t horizon_shift = (horizon_base + horizon_offset) << 4;
1166
1167 if (horizon_shift == change_per_entry)
1168 {
1169 set_elevation();
1170 }
1171 else if (change_per_entry > horizon_shift)
1172 {
1173 elevation = UP;
1174 set_elevation();
1175 }
1176 else
1177 {
1178 elevation = DOWN;
1179 set_elevation();
1180 }
1181 }
1182
1183 // Source Address: 0x2028
set_elevation()1184 void ORoad::set_elevation()
1185 {
1186 // d2: h
1187 // d3: total_height
1188 // d4: scanline
1189 // d5: h_copy (global)
1190 d5_o -= change_per_entry;
1191
1192 if (d5_o == 0)
1193 {
1194 set_y_2044();
1195 return;
1196 }
1197
1198 // should be 2400, 2400, 2400, ffffe764, ffffdc00
1199 if (d5_o < 0)
1200 d5_o = -d5_o;
1201
1202 road_unk[a3_o++] = scanline;
1203 int32_t total_height_wrk = total_height;
1204 if (total_height_wrk < 0)
1205 total_height_wrk = -total_height_wrk;
1206 total_height_wrk <<= 4;
1207
1208 road_unk[a3_o++] = total_height_wrk >> 16;
1209
1210 set_y_2044();
1211 }
1212
1213 // Set Horizon Base Position. Used on Gateway.
1214 //
1215 // Source Address: 0x219E
set_y_horizon()1216 void ORoad::set_y_horizon()
1217 {
1218 uint32_t a0 = 0x200 + road_p1; // a0 = Road Height Positions + Relevant Offset
1219
1220 int32_t d1 = (horizon_mod * (height_start - 0x100)) >> 4;
1221 int32_t d2 = ((horizon_base + horizon_offset) << 4) + d1;
1222
1223 uint32_t total_height = 0;
1224
1225 // write_next_y:
1226 for (uint16_t i = 0; i <= 0x1FF; i++) // (1FF height positions)
1227 {
1228 total_height += d2;
1229 uint32_t d0 = (total_height << 4) >> 16;
1230 road_y[--a0] = d0;
1231 }
1232
1233 road_unk[0] = 0;
1234
1235 // If not at end of section return. Otherwise, setup new horizon y-value
1236 if (height_start != 0x1FF) return;
1237
1238 // Read Up/Down Multiplier Word From Lookup Table and set new horizon base value
1239 horizon_base = (int32_t) (trackloader.read16(trackloader.heightmap_data, height_addr));
1240
1241 if (height_lookup == height_lookup_wrk)
1242 height_lookup = 0;
1243
1244 // Set master switch control to init next road segment
1245 height_ctrl = 1;
1246 }
1247
1248 // Aspect correct road inclines.
1249 // Set Horizon Tilemap Y Position
1250 //
1251 // Source Address: 0x13B8
set_horizon_y()1252 void ORoad::set_horizon_y()
1253 {
1254 // ------------------------------------------------------------------------
1255 // Aspect correct road inclines
1256 // ------------------------------------------------------------------------
1257
1258 uint32_t road_int = 0; // a0
1259 uint32_t road_y_addr = (0 + road_p1); // a1
1260
1261 // read_next
1262 while (true)
1263 {
1264 int16_t d0 = road_unk[road_int];
1265 if (d0 == 0) break;
1266 int16_t d7 = d0 >> 3;
1267 int16_t d6 = (d7 + d0) - 0x1FF;
1268
1269 if (d6 > 0)
1270 {
1271 d7 += d7;
1272 d7 -= d6;
1273 d7 >>= 1;
1274 d0 = 0x1FF - d7;
1275 }
1276 // 13e6
1277 d6 = d7 >> 1;
1278 if (d6 <= 2) break;
1279 int16_t d1 = d0 - d6;
1280 int16_t d2 = d0;
1281 int16_t d3 = d0 + d6;
1282 int16_t d4 = d0 + d7;
1283
1284 // 1402: Chris - made some edits below here, seems to go rather wrong from here onwards
1285 d0 -= d7;
1286 int16_t d5 = d0;
1287 uint16_t a2 = road_y_addr + d5;
1288
1289 d0 = road_y[road_y_addr + d0];
1290 d1 = road_y[road_y_addr + d1];
1291 d2 = road_y[road_y_addr + d2];
1292 d3 = road_y[road_y_addr + d3];
1293 d4 = road_y[road_y_addr + d4];
1294
1295 // 1426:
1296 d5 = d0;
1297
1298 int32_t d2l = (d2 + d0 + d4) * 0x5555; // turn d2 into a long
1299 d2l >>= 16;
1300 int32_t d1l = (d1 + d0 + (int16_t) d2l) * 0x5555; // turn d2 into a long
1301 d1l >>= 16;
1302 int32_t d3l = (d3 + d4 + (int16_t) d2l) * 0x5555;
1303 d3l >>= 16;
1304
1305 d0 -= (int16_t) d1l;
1306 d1l -= (int16_t) d2l;
1307 d2l -= (int16_t) d3l;
1308 d3l -= d4;
1309
1310 d0 = (d0 << 2) / d6;
1311 d1 = (d1l << 2) / d6;
1312 d2 = (d2l << 2) / d6;
1313 d3 = (d3l << 2) / d6;
1314
1315 d5 <<= 2;
1316 d6--;
1317
1318 // loop 1:
1319 for (int16_t i = 0; i <= d6; i++)
1320 {
1321 road_y[a2++] = d5 >> 2;
1322 d5 -= d0;
1323 }
1324 // loop 2:
1325 for (int16_t i = 0; i <= d6; i++)
1326 {
1327 road_y[a2++] = d5 >> 2;
1328 d5 -= d1;
1329 }
1330 // loop 3:
1331 for (int16_t i = 0; i <= d6; i++)
1332 {
1333 road_y[a2++] = d5 >> 2;
1334 d5 -= d2;
1335 }
1336 // loop 4:
1337 for (int16_t i = 0; i <= d6; i++)
1338 {
1339 road_y[a2++] = d5 >> 2;
1340 d5 -= d3;
1341 }
1342
1343 road_int += 2;
1344 } // end loop
1345
1346 // ------------------------------------------------------------------------
1347 // Set Horizon Y Position
1348 // ------------------------------------------------------------------------
1349
1350 int16_t y_pos = road_y[road_p3] >> 4;
1351 horizon_y2 = -y_pos + 224;
1352 }
1353
1354 // Parse Road Scanline Data.
1355 //
1356 // Output hardware ready format.
1357 // Also output priority information for undulations in road for sprite hardware.
1358 // This is so that sprites can be hidden behind crests of hills.
1359 //
1360 // Destination Output Format:
1361 //
1362 // ----s--- -------- Solid fill (1) or ROM fill
1363 // -------- -ccccccc Solid color (if solid fill)
1364 // -------i iiiiiiii Index for other tables
1365 //
1366 // Source: 0x1318
1367
do_road_data()1368 void ORoad::do_road_data()
1369 {
1370 // Road data in RAM #1 [Destination] (Solid fill/index fill etc)
1371 // This is the final block of data to be output to road hardware
1372 uint32_t addr_dst = 0x400 + road_p1; // [a0]
1373
1374 // Road data in RAM [Source]
1375 uint32_t addr_src = 0x200 + road_p1; // [a1]
1376
1377 // Road Priority Elevation Data [Destination]
1378 uint32_t addr_priority = 0x280 + road_p1;
1379
1380 int16_t rom_line_select = 0x1FF; // This is the triangular road shape from ROM. Lines 0 - 512.
1381 int16_t src_this = road_y[--addr_src] >> 4; // source entry #1 / 16 [d0]
1382 int16_t src_next = 0; // [d4]
1383 int16_t write_priority = 0; // [d5]
1384
1385 road_y[--addr_dst] = rom_line_select; // Write source entry counter to a0 / destination
1386 int16_t scanline = 254; // Loop counter #2 (Scanlines) - Start at bottom of screen
1387
1388 // ------------------------------------------------------------------------
1389 // Draw normal road
1390 // ------------------------------------------------------------------------
1391 while (--rom_line_select > 0) // Read next line of road source data
1392 {
1393 src_next = road_y[--addr_src] >> 4;
1394
1395 // Plot next position of road using rom_line_select source
1396 // The speed at which we advance through the source data is controlled by the values in road_y,
1397 // which are a sequential list of numbers when the road is flat
1398 if (src_this < src_next)
1399 {
1400 // Only draw if within screen area
1401 if (--scanline <= 0)
1402 {
1403 road_y[addr_priority] = 0; road_y[addr_priority + 1] = 0; // Denote end of priority list
1404 return;
1405 }
1406 src_this = src_next; // d0 = source entry #1 / 16
1407
1408 road_y[--addr_dst] = rom_line_select; // Set next line of road appearance info, from ROM
1409 write_priority = -1; // Denote that we need to write priority data
1410 }
1411 else if (src_this == src_next)
1412 continue;
1413 // Write priority data for elevation. Only called when there is a hill.
1414 // This is denoted by the next number in the sequence being smaller than the current
1415 // Note: This isn't needed to directly render the road.
1416 else if (write_priority == -1)
1417 {
1418 road_y[addr_priority++] = rom_line_select; // Write rom line select value
1419 road_y[addr_priority++] = src_this; // Write source entry value
1420 write_priority = 0; // Denote that values have been written
1421 }
1422 } // end loop
1423
1424 // ------------------------------------------------------------------------
1425 // Fill Horizon above road at top of screen
1426 //
1427 // Note that this is the area above the road, and is detected because
1428 // the rom line select is negative, and therefore there is no more to
1429 // read.
1430 // ------------------------------------------------------------------------
1431 if (rom_line_select <= 0)
1432 {
1433 const uint16_t SOLID_FILL = 0x800;
1434
1435 // d3 = (0x3F | 0x800) = 0x83F [1000 00111111] Sets Solid Fill, Transparent Colour
1436 const uint16_t TRANSPARENT = 0x3F | SOLID_FILL;
1437 int16_t d7 = 255 - src_next - scanline;
1438
1439 if (d7 < 0)
1440 d7 = SOLID_FILL; // use default solid fill
1441
1442 // loc 1388
1443 else if (d7 > 0x3F)
1444 {
1445 while (scanline-- > 0)
1446 road_y[--addr_dst] = TRANSPARENT; // Copy transparent line to ram
1447
1448 // end
1449 road_y[addr_priority] = 0; road_y[addr_priority + 1] = 0;
1450 return;
1451 }
1452 else
1453 d7 |= SOLID_FILL; // Solid Fill Bits | Colour Info
1454
1455 // 1392
1456 do // Output solid colours while looping (compare with default solid fill d3)
1457 {
1458 road_y[--addr_dst] = d7; // Output solid colour
1459 if (--scanline < 0) // Check whether scanloop counter expired
1460 {
1461 road_y[addr_priority] = 0; road_y[addr_priority + 1] = 0;
1462 return;
1463 }
1464 road_y[--addr_dst] = d7; // Output solid colour
1465 if (--scanline < 0) // Check whether scanloop counter expired
1466 {
1467 road_y[addr_priority] = 0; road_y[addr_priority + 1] = 0;
1468 return;
1469 }
1470 d7++; // Increment solid colour
1471 }
1472 while (d7 <= TRANSPARENT);
1473
1474 while (scanline-- > 0)
1475 road_y[--addr_dst] = TRANSPARENT; // Copy transparent line to ram
1476 // 1346
1477 }
1478
1479 // end:
1480 road_y[addr_priority] = 0; road_y[addr_priority + 1] = 0; // Denote end of priority list
1481 }
1482
1483 // ------------------------------------------------------------------------------------------------
1484 // ROUTINES FOLLOW TO BLIT RAM TO ROAD HARDWARE
1485 // ------------------------------------------------------------------------------------------------
1486
1487 // Source Address: 0x14C8
blit_roads()1488 void ORoad::blit_roads()
1489 {
1490 const uint32_t road0_adr = 0x801C0;
1491 const uint32_t road1_adr = 0x803C0;
1492
1493 switch (road_ctrl)
1494 {
1495 case ROAD_OFF: // Both Roads Off
1496 return;
1497
1498 case ROAD_R0: // Road 0
1499 case ROAD_R0_SPLIT: // Road 0 (Road Split.)
1500 blit_road(road0_adr);
1501 hwroad.write_road_control(0);
1502 break;
1503
1504 case ROAD_R1: // Road 1
1505 case ROAD_R1_SPLIT: // Road 1 (Road Split. Invert Road 1)
1506 blit_road(road1_adr);
1507 hwroad.write_road_control(3);
1508 break;
1509
1510 case ROAD_BOTH_P0: // Both Roads (Road 0 Priority) [DEFAULT]
1511 case ROAD_BOTH_P0_INV: // Both Roads (Road 0 Priority) (Road Split. Invert Road 1)
1512 blit_road(road0_adr);
1513 blit_road(road1_adr);
1514 hwroad.write_road_control(1);
1515 break;
1516
1517 case ROAD_BOTH_P1: // Both Roads (Road 1 Priority)
1518 case ROAD_BOTH_P1_INV: // Both Roads (Road 1 Priority) (Road Split. Invert Road 1)
1519 blit_road(road0_adr);
1520 blit_road(road1_adr);
1521 hwroad.write_road_control(2);
1522 break;
1523 }
1524 }
1525
blit_road(uint32_t a0)1526 void ORoad::blit_road(uint32_t a0)
1527 {
1528 uint32_t a2 = 0x400 + road_p2;
1529
1530 // Write 0x1A0 bytes total Src: (0x260 - 0x400) Dst: 0x1C0
1531 for (int16_t i = 0; i <= 13; i++)
1532 {
1533 a0 -= 2; hwroad.write16(a0, road_y[--a2]);
1534 a0 -= 2; hwroad.write16(a0, road_y[--a2]);
1535 a0 -= 2; hwroad.write16(a0, road_y[--a2]);
1536 a0 -= 2; hwroad.write16(a0, road_y[--a2]);
1537 a0 -= 2; hwroad.write16(a0, road_y[--a2]);
1538 a0 -= 2; hwroad.write16(a0, road_y[--a2]);
1539 a0 -= 2; hwroad.write16(a0, road_y[--a2]);
1540 a0 -= 2; hwroad.write16(a0, road_y[--a2]);
1541
1542 a0 -= 2; hwroad.write16(a0, road_y[--a2]);
1543 a0 -= 2; hwroad.write16(a0, road_y[--a2]);
1544 a0 -= 2; hwroad.write16(a0, road_y[--a2]);
1545 a0 -= 2; hwroad.write16(a0, road_y[--a2]);
1546 a0 -= 2; hwroad.write16(a0, road_y[--a2]);
1547 a0 -= 2; hwroad.write16(a0, road_y[--a2]);
1548 a0 -= 2; hwroad.write16(a0, road_y[--a2]);
1549 a0 -= 2; hwroad.write16(a0, road_y[--a2]);
1550 }
1551 }
1552
output_hscroll(int16_t * src,uint32_t dst)1553 void ORoad::output_hscroll(int16_t* src, uint32_t dst)
1554 {
1555 const int32_t d6 = 0x654;
1556 uint32_t src_addr = 0;
1557
1558 // Copy 16 bytes
1559 for (int32_t i = 0; i <= 0x3F; i++)
1560 {
1561 int16_t d0h = -src[src_addr++] + d6;
1562 int16_t d0l = -src[src_addr++] + d6;
1563 int16_t d1h = -src[src_addr++] + d6;
1564 int16_t d1l = -src[src_addr++] + d6;
1565 int16_t d2h = -src[src_addr++] + d6;
1566 int16_t d2l = -src[src_addr++] + d6;
1567 int16_t d3h = -src[src_addr++] + d6;
1568 int16_t d3l = -src[src_addr++] + d6;
1569 hwroad.write16(&dst, d0h);
1570 hwroad.write16(&dst, d0l);
1571 hwroad.write16(&dst, d1h);
1572 hwroad.write16(&dst, d1l);
1573 hwroad.write16(&dst, d2h);
1574 hwroad.write16(&dst, d2l);
1575 hwroad.write16(&dst, d3h);
1576 hwroad.write16(&dst, d3l);
1577 }
1578 }
1579
1580 // Copy Background Colour To Road.
1581 // + Scroll stripe data over road based on fine position
1582 // + pos_fine is used as an offset into a table in rom.
1583 //
1584 // Source Address: 0x1ABC
1585 //
1586 // Notes:
1587 //
1588 // C00-FFF ----bbbb -------- Background color index
1589 // -------- s------- Road 1: stripe color index
1590 // -------- -a------ Road 1: pixel value 2 color index
1591 // -------- --b----- Road 1: pixel value 1 color index
1592 // -------- ---c---- Road 1: pixel value 0 color index
1593 // -------- ----s--- Road 0: stripe color index
1594 // -------- -----a-- Road 0: pixel value 2 color index
1595 // -------- ------b- Road 0: pixel value 1 color index
1596 // -------- -------c Road 0: pixel value 0 color index
1597
copy_bg_color()1598 void ORoad::copy_bg_color()
1599 {
1600 // Scroll stripe data over road based on fine position
1601 uint32_t pos_fine_copy = (pos_fine & 0x1F) << 10;
1602 uint32_t src = ROAD_BGCOLOR + pos_fine_copy;
1603 uint32_t dst = HW_BGCOLOR;
1604
1605 // Copy 1K
1606 for (int i = 0; i < 32; i++)
1607 {
1608 hwroad.write32(&dst, roms.rom1p->read32(&src));
1609 hwroad.write32(&dst, roms.rom1p->read32(&src));
1610 hwroad.write32(&dst, roms.rom1p->read32(&src));
1611 hwroad.write32(&dst, roms.rom1p->read32(&src));
1612 hwroad.write32(&dst, roms.rom1p->read32(&src));
1613 hwroad.write32(&dst, roms.rom1p->read32(&src));
1614 hwroad.write32(&dst, roms.rom1p->read32(&src));
1615 hwroad.write32(&dst, roms.rom1p->read32(&src));
1616 }
1617 }