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 }