├── .gitignore ├── LICENSE ├── README.md ├── lou ├── 00_turns │ ├── README.md │ ├── imgs │ │ ├── centered.png │ │ ├── dx-015.png │ │ ├── dx_005.png │ │ └── off_center.png │ ├── res │ │ ├── background │ │ │ └── road.png │ │ └── resources.res │ └── src │ │ └── main.c ├── 01_basic_road │ ├── README.md │ ├── img │ │ ├── rom_208.bmp │ │ └── rom_722.bmp │ ├── res │ │ ├── background │ │ │ ├── background.png │ │ │ └── road.png │ │ └── resources.res │ └── src │ │ └── main.c ├── 02_hills │ ├── res │ │ ├── background │ │ │ ├── background.png │ │ │ └── road.png │ │ └── resources.res │ └── src │ │ └── main.c ├── 03_hilly_road │ ├── res │ │ ├── background │ │ │ ├── background.png │ │ │ └── road.png │ │ └── resources.res │ └── src │ │ └── main.c ├── 04_colors │ ├── res │ │ ├── background │ │ │ ├── background.png │ │ │ └── road.png │ │ └── resources.res │ └── src │ │ ├── HInter.s │ │ ├── boot │ │ └── sega.s │ │ └── main.c ├── 05_sprites │ ├── res │ │ ├── background │ │ │ ├── background.png │ │ │ └── road.png │ │ ├── resources.res │ │ └── sprites │ │ │ ├── car.png │ │ │ ├── pseudobiker.png │ │ │ └── tree.png │ └── src │ │ ├── HInter.s │ │ ├── boot │ │ └── sega.s │ │ └── main.c └── 06_steering │ ├── res │ ├── background │ │ ├── background.png │ │ └── road.png │ ├── resources.res │ └── sprites │ │ ├── bush.png │ │ ├── bush0.png │ │ ├── car.png │ │ ├── green_car.png │ │ ├── pine.png │ │ ├── pinetree.png │ │ ├── red_car.png │ │ ├── rock.png │ │ ├── sign.png │ │ ├── thing.png │ │ └── tree.png │ └── src │ ├── HInter.s │ ├── boot │ └── sega.s │ └── main.c ├── sega └── background.png └── spacer ├── SpacerHarry ├── makeBackground.py ├── res │ ├── background │ │ └── ground.png │ ├── resources.res │ └── sprites │ │ ├── boss.png │ │ ├── player.bmp │ │ ├── player.png │ │ └── shadow.png ├── src │ ├── HInter.s │ ├── boot │ │ └── sega.s │ └── main.c └── starter.png ├── SpacerHarry2 ├── res │ ├── background │ │ ├── background.png │ │ └── ground.png │ ├── resources.res │ └── sprites │ │ ├── boss.png │ │ ├── player.bmp │ │ ├── player.png │ │ └── shadow.png ├── src │ ├── HInter.s │ ├── boot │ │ └── sega.s │ └── main.c └── starter.png └── SpacerHarryCC ├── makeBackgroundCC.py ├── res ├── bg │ └── bg_b.png └── resources.res └── src └── main.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | *.bin 10 | 11 | # Linker output 12 | *.ilk 13 | *.map 14 | *.exp 15 | 16 | # Precompiled Headers 17 | *.gch 18 | *.pch 19 | 20 | # Libraries 21 | *.lib 22 | *.a 23 | *.la 24 | *.lo 25 | 26 | # Shared objects (inc. Windows DLLs) 27 | *.dll 28 | *.so 29 | *.so.* 30 | *.dylib 31 | 32 | # Executables 33 | *.exe 34 | *.out 35 | *.app 36 | *.i*86 37 | *.x86_64 38 | *.hex 39 | 40 | # Debug files 41 | *.dSYM/ 42 | *.su 43 | *.idb 44 | *.pdb 45 | 46 | # Kernel Module Compile Results 47 | *.mod* 48 | *.cmd 49 | .tmp_versions/ 50 | modules.order 51 | Module.symvers 52 | Mkfile.old 53 | dkms.conf 54 | 55 | # SGDK output 56 | out/ 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Greg Gallardo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mega Driving 2 | The code has been updated to compile with SGDK 1.80. Earlier versions of SGDK will 3 | likely not work. 4 | 5 | I've been teaching myself SGDK for fun. This repository contains *experimental* code I've 6 | written for fake 3D roads and other Pseudo3D effects. 7 | 8 | ## Lou's Pseudo 3D Roads 9 | The code in the "lou" folder was 10 | written based on [Lou's Pseudo 3d Page](http://www.extentofthejam.com/pseudo/). It's 11 | provided here as an example of one possible way to implement pseudo 3d with SGDK. I am in 12 | no way saying that this is the best way to implement pseudo-3d roads. There are likely 13 | bugs and I haven't tried to optimize the code yet. 14 | 15 | 16 | I had hoped to write this entirely in C. Unfortunately, I wasn't able to pull off the color 17 | cycling used on the grass and road in C. So the code is a mix of C and some assembly code. 18 | 19 | The code has been tested on [BlastEM and Real Hardware](https://youtu.be/p99XATFhSpo) 20 | 21 | ### TODO 22 | * Improve steering code. 23 | * Look into smoothing road side object and car motion in the Y direction. 24 | * Add readme.md files to explain the code in each example. 25 | * Look at making my own fixed-point implementation for more decimal 26 | 27 | 28 | ## Spacer 29 | I decided to try adapting my road code to the pseudo 3d effect seen in Space Harrier. 30 | -------------------------------------------------------------------------------- /lou/00_turns/README.md: -------------------------------------------------------------------------------- 1 | # Simple Curves 2 | This code is based on the [Curves and Steering](http://www.extentofthejam.com/pseudo/#curves) section of [Lou's Pseudo 3d Page](http://www.extentofthejam.com/pseudo) 3 | 4 | For simplicity I'm not bothering with multiple segments in this example. 5 | 6 | 7 | ## The Road Image 8 | I'm using a flat image of a road instead of drawing lines to denote the edge of the road and centerline. 9 | ![Background Image A](./res/background/road.png) 10 | 11 | This image is 512 by 224 pixels. This is much wider than the display (320x224). The larger width lets me scroll the entire image left or right without wrapping around. 12 | 13 | Displaying the image is easy. You define it in the `resource.res` file. 14 | ~~~res 15 | IMAGE road "background/road.png" BEST 16 | ~~~ 17 | 18 | Then load it into scroll plane A with `VDP_drawImageEx()` 19 | ~~~c 20 | ////////////////////////////////////////////////////////////// 21 | // Setup background A 22 | VDP_setPalette(PAL1, road.palette->data); 23 | int ind = TILE_USERINDEX; 24 | VDP_drawImageEx(BG_A, &road, TILE_ATTR_FULL(PAL1, FALSE, FALSE, FALSE, ind), 0, 0, FALSE, TRUE); 25 | ~~~ 26 | 27 | ### Scrolling 28 | The only problem with this is the image is much wider (512 pixels) than the screen (320 Pixels), so it'll be off-center. 29 | 30 | ![Background Image A](./imgs/off_center.png) 31 | 32 | To correct for this, we need to scroll the image to the left. 33 | In this case the amount to scroll is 34 | ~~~c 35 | (512 - 320) /2 36 | ~~~ 37 | or 96 pixels. 38 | 39 | 40 | In my code I define `SCROLL_CENTER` as -96 pixels. -96 moves the image to the left. 41 | ~~~c 42 | #define SCROLL_CENTER -96 43 | ~~~ 44 | 45 | I'm setting the scrolling mode to `HSCROLL_LINE`. This is important for bending the road image to simulate curves. `HSCROLL_LINE` lets us shift each line independently of each other. `HSCROLL_PLANE` will scroll the entire plane and `HSCROLL_TILE` will scroll 8-pixel chunks. 46 | 47 | `HscrollA[]` is an 224 element array of a signed 16-bit ints. Each element corresponds to a line on screen and holds the amount of pixels we want to scroll each line by. To center the screen I'm initially setting them all to -96 48 | ~~~c 49 | VDP_setScrollingMode(HSCROLL_LINE, VSCROLL_PLANE); 50 | for (int i = 0; i < 224; i++) 51 | { 52 | HscrollA[i] = SCROLL_CENTER; 53 | } 54 | ~~~ 55 | 56 | The actual scrolling is performed by `VDP_setHorizontalScrollLine()` in the main loop. 57 | ~~~c 58 | VDP_setHorizontalScrollLine(BG_A, 0, HscrollA, 224, DMA_QUEUE); 59 | ~~~ 60 | ![Background Image A](./imgs/centered.png) 61 | 62 | 63 | ## Curves 64 | 65 | Lou's pseudo code in [Curves and Steering](http://www.extentofthejam.com/pseudo/#curves) bends road segments using two values 66 | * dx : Curve amount, constant per segment 67 | * ddx : (*Cumulative*) Curve amount, changes per line 68 | 69 | `dx` tells the program how far to shift each line as we move from the bottom of the screen to the top of the road. `ddx` accumulates the amount shifted so that each line up is shifted further than the previous line. 70 | 71 | Since I'm not using multiple segments in this example, the curve can be created with a simple loop. I'm moving from the bottom of the screen (224) to the top of the road in the background image (116). The `current_x` value is added to the `SCROLL_CENTER` and stored in `HscrollA[]`. 72 | ~~~c// Create example curves. 73 | // dx: curve amount. Constant over the image 74 | void CreateCurve(fix32 dx) 75 | { 76 | fix32 current_x = FIX32(0); // current x shift 77 | fix32 ddx = FIX32(0); // Cumulative Curve amount. Changes every line 78 | 79 | // start from the bottom of the screen and move up to the top of the road. 80 | for (u16 bgY = 223; bgY >= 116; bgY--) 81 | { 82 | ddx = fix32Add(dx, ddx); // shift ddx by dx 83 | current_x = fix32Add(current_x, ddx); 84 | HscrollA[bgY] = SCROLL_CENTER + fix32ToInt(current_x); 85 | } 86 | } 87 | ~~~ 88 | 89 | The sign and magnitude of `dx` determines the direction and sharpness of a given curve. 90 | 91 | `dx = 0.005` 92 | ![Background Image A](./imgs/dx_005.png) 93 | 94 | `dx = -0.015` 95 | ![Background Image A](./imgs/dx-015.png) 96 | -------------------------------------------------------------------------------- /lou/00_turns/imgs/centered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/00_turns/imgs/centered.png -------------------------------------------------------------------------------- /lou/00_turns/imgs/dx-015.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/00_turns/imgs/dx-015.png -------------------------------------------------------------------------------- /lou/00_turns/imgs/dx_005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/00_turns/imgs/dx_005.png -------------------------------------------------------------------------------- /lou/00_turns/imgs/off_center.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/00_turns/imgs/off_center.png -------------------------------------------------------------------------------- /lou/00_turns/res/background/road.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/00_turns/res/background/road.png -------------------------------------------------------------------------------- /lou/00_turns/res/resources.res: -------------------------------------------------------------------------------- 1 | IMAGE road "background/road.png" BEST 2 | PALETTE road_pal "background/road.png" 3 | -------------------------------------------------------------------------------- /lou/00_turns/src/main.c: -------------------------------------------------------------------------------- 1 | 2 | // SGDK 3 | #include 4 | #include "resources.h" 5 | 6 | // image is 512x224. Screen is 320, we want to move halfway 7 | // (512-320)/2 8 | #define SCROLL_CENTER -96 9 | 10 | // Horizontal Scrolling values 11 | s16 HscrollA[224]; 12 | 13 | // Create example curves. 14 | // dx: curve amount. Constant over the image 15 | void CreateCurve(fix32 dx) 16 | { 17 | fix32 current_x = FIX32(0); // current x shift 18 | fix32 ddx = FIX32(0); // Cumulative Curve amount. Changes every line 19 | 20 | // start from the bottom of the screen and move up. 21 | for (u16 bgY = 223; bgY >= 116; bgY--) 22 | { 23 | ddx = fix32Add(dx, ddx); 24 | current_x = fix32Add(current_x, ddx); 25 | 26 | // store the current x in HscrollA to shift the road in the main loop 27 | HscrollA[bgY] = SCROLL_CENTER + fix32ToInt(current_x); 28 | } 29 | } 30 | 31 | void handleJoypad() 32 | { 33 | u16 value = JOY_readJoypad(JOY_1); 34 | if (value & BUTTON_A) 35 | { 36 | // curve 37 | VDP_drawText("dx = 0.02", 15, 3); 38 | CreateCurve(FIX32(0.02)); 39 | } 40 | if (value & BUTTON_B) 41 | { 42 | VDP_drawText("dx = -0.015", 15, 3); 43 | CreateCurve(FIX32(-0.015)); 44 | } 45 | if (value & BUTTON_C) 46 | { 47 | VDP_drawText("dx = 0.005", 15, 3); 48 | CreateCurve(FIX32(0.005)); 49 | } 50 | } 51 | 52 | int main(bool arg) 53 | { 54 | 55 | ////////////////////////////////////////////////////////////// 56 | // VDP basic setup 57 | VDP_setBackgroundColor(16); 58 | VDP_setScreenWidth320(); 59 | 60 | ////////////////////////////////////////////////////////////// 61 | // initialize scrolling values to the center of the image. 62 | VDP_setScrollingMode(HSCROLL_LINE, VSCROLL_PLANE); 63 | for (int i = 0; i < 224; i++) 64 | { 65 | HscrollA[i] = SCROLL_CENTER; 66 | } 67 | 68 | ////////////////////////////////////////////////////////////// 69 | // Setup background A 70 | PAL_setPalette(PAL1, road_pal.data, CPU); 71 | int ind = TILE_USER_INDEX; 72 | VDP_drawImageEx(BG_A, &road, TILE_ATTR_FULL(PAL1, FALSE, FALSE, FALSE, ind), 0, 0, FALSE, TRUE); 73 | 74 | VDP_drawText("Press A, B, or C to Curve", 15, 1); 75 | // Main loop 76 | while (TRUE) 77 | { 78 | // update curve based on user input (A,B,C) 79 | handleJoypad(); 80 | 81 | // curve the road with horizontal scrolling 82 | VDP_setHorizontalScrollLine(BG_A, 0, HscrollA, 224, DMA_QUEUE); 83 | 84 | SYS_doVBlankProcess(); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /lou/01_basic_road/README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | This code is based on the [Curves and Steering](http://www.extentofthejam.com/pseudo/#curves) section of [Lou's Pseudo 3d Page](http://www.extentofthejam.com/pseudo). Unlike my [previous example](https://github.com/radioation/MegaDriving/tree/main/00_turns) I'm now using two road segments for curves and adding a second scroll plane. 3 | 4 | 5 | I'm assuming you've looked over the previous example, which covers using horizontal line scrolling to curve a flat image of a road. 6 | 7 | 8 | # Road Segments 9 | As seen in [Curves and Steering](http://www.extentofthejam.com/pseudo/#curves), Lou allows two segments on screen at a time. Each segment has its own rate of change for the road (`dx`). I'm adding a background image that scrolls when the player is on a curved segment. Each segment will have its on rate of change for the background. A simple struct can be used to hold both of these values: 10 | 11 | ~~~c 12 | typedef struct 13 | { 14 | fix16 dx; // rate of change for the road. 15 | fix16 bgdx; // rate of change for background. 16 | } ROAD_SEGMENT; 17 | ~~~ 18 | 19 | A track can be created with a `ROAD_SEGMENT` array. In my example I'm defining a track composed of 13 road segments. The first value sets `dx` and the second sets `bgdx` 20 | ~~~c 21 | #define ROAD_SEGMENTS_LENGTH 13 22 | const ROAD_SEGMENT segments[ROAD_SEGMENTS_LENGTH] = { 23 | {FIX16(0), FIX16(0)}, 24 | {FIX16(-0.02), FIX16(0.120)}, 25 | {FIX16(-0.04), FIX16(0.32)}, 26 | {FIX16(-0.02), FIX16(0.120)}, 27 | {FIX16(0), FIX16(0)}, 28 | {FIX16(0), FIX16(0)}, 29 | {FIX16(0.06), FIX16(-0.36)}, 30 | {FIX16(-0.06), FIX16(0.36)}, 31 | {FIX16(0), FIX16(0)}, 32 | {FIX16(0.02), FIX16(-0.12)}, 33 | {FIX16(0), FIX16(0)}, 34 | {FIX16(-0.03), FIX16(0.18)}, 35 | {FIX16(0.03), FIX16(-0.18)}}; 36 | ~~~ 37 | When `dx` is FIX16(0) it means the segment is straight. Negative `dx` values curve the road to the left. Positive values curve the road to the right. Similarly, when `bdgx` is FIX16(0) it means the background will not be scrolling. Positive `bgdx` values will scroll the background to the right. Negative values will scroll the background to the left. 38 | 39 | 40 | # Curving Road Segments. 41 | 42 | ## Finding `dx` 43 | Lou's pseudo code for getting the current dx value looks like this: 44 | ~~~c 45 | for each line of the screen from the bottom to the top: 46 | if line of screen's Z Map position is below segment.position: 47 | dx = bottom_segment.dx 48 | else if line of screen's Z Map position is above segment.position: 49 | dx = segment.dx 50 | end if 51 | ddx += dx 52 | current_x += ddx 53 | this_line.x = current_x 54 | end for 55 | ~~~ 56 | He suggests keeping track of the segment position in terms of where on the Z Map it is. To do this I've defined a `fix16` `segment_position` and added a `zmap[]` array to store the z for each Y position from the bottom of the screen to the top of the road in the background image. 57 | ~~~c 58 | // Zmap for tracking segment position 59 | #define ZMAP_LENGTH 110 60 | fix16 zmap[ZMAP_LENGTH]; 61 | ~~~ 62 | 63 | This gets initialized in `main()`. 64 | ~~~c 65 | ////////////////////////////////////////////////////////////// 66 | // http://www.extentofthejam.com/pseudo/ 67 | // Z = Y_world / (Y_screen - (height_screen / 2)) 68 | for (u16 i = 0; i < ZMAP_LENGTH; ++i) 69 | { 70 | zmap[i] = fix16Div(FIX16(-75), fix16Sub(FIX16(i), FIX16(112))); 71 | KLog_f1("FIX16(", zmap[i]); 72 | } 73 | ~~~ 74 | Note that: 75 | * I've defined the ZMAP to have the bottom of the screen 76 | (nearest position) start at zmap[0] 77 | * I tried several different values for Y_world. I settled on `FIX(-75)` because the results looked good to me. 78 | 79 | I initialize the `segment_position` at the highest zmap value. 80 | ~~~c 81 | segment_position = zmap[ZMAP_LENGTH - 1]; 82 | ~~~ 83 | 84 | I've defined the track segments as an array of `ROAD_SEGMENTS`. This lets me use a pair of index variables to keep track of `bottom_segment.dx` and `segment.dx`. So Lou's `dx` lookup pseudo code becomes: 85 | ~~~c 86 | fixed z = zmap[y]; 87 | //if line of screen's Z Map position is below segment.position: 88 | if (z < segment_position) 89 | { 90 | // dx = bottom_segment.dx 91 | dx = segments[bottom_segments_index].dx; 92 | } 93 | else // if line of Screen's Z map position is above segment position. 94 | { 95 | // dx = segment.dx 96 | dx = segments[segments_index].dx; 97 | } 98 | ~~~ 99 | 100 | Once we know the `dx`, we can increase `ddx` and find the `current_x` value for the current line. This gets stored in `HScrollA[]` for scrolling in the main loop 101 | ~~~c 102 | // ddx += dx 103 | ddx = fix16Add(ddx, dx); 104 | // current_x += ddx 105 | current_x = fix16Add(current_x, ddx); 106 | 107 | // this_line.x = current_x 108 | // we'll use horizontal scrolling of BG_A to fake curves. 109 | HscrollA[223 - y] = SCROLL_CENTER + fix16ToInt(current_x); 110 | ~~~ 111 | *Note:* 223 - y is used to set `HscrollA` because of how I defined the Z map. `zmap[0]` is the bottom of the screen. OTOH, HscrollA[223] is the bottom of the screen for the scroll function. 112 | 113 | 114 | ## Moving Through the Segments 115 | As we move through the track, the segment position moves from the top of the road to the bottom of the screen. I've defined a `fix16` variable named `speed` to move the segment position relative to the Z map. 116 | ~~~c 117 | speed = FIX16(-0.1); 118 | ~~~ 119 | 120 | The negative speed value moves the segment position down from the farthest Z value to the closest. I update `segment_position` with each call to the `update()` function. 121 | ~~~c 122 | // Move segments 123 | segment_position = fix16Add(segment_position, speed); 124 | ~~~ 125 | Once the segment position goes below zero, I move the segment indexes to their next positions 126 | ~~~c 127 | if (fix16ToInt(segment_position) < 0) // 0 is nearest 128 | { 129 | // bottom_segment = segment 130 | bottom_segments_index = segments_index; 131 | 132 | // segment.position = zmap.length - 1 133 | segment_position = zmap[ZMAP_LENGTH - 1]; // Send segment to farthest visible distance 134 | // fetch next segment from road 135 | segments_index++; // segment_index is used to get segment.dx 136 | if (segments_index == ROAD_SEGMENTS_LENGTH) 137 | { 138 | segments_index -= ROAD_SEGMENTS_LENGTH; // go back to the start 139 | } 140 | } 141 | ~~~ 142 | 143 | 144 | Here's the update() code for curving the road. (*Some of the comments were taken from Lou's pseudo code to make it easier to map my code to his*) 145 | ~~~c 146 | void update() 147 | { 148 | fix16 current_x = FIX16(0); // Lou's pseudo 3d page says to use Half of the screen width, 149 | // but I've defined SCROLL_CENTER to handle this 150 | 151 | fix16 dx = FIX16(0); // Curve amount, constant per segment. 152 | fix16 ddx = FIX16(0); // Curve amount, changes per line 153 | 154 | 155 | // for each line of the screen from the bottom to the top: 156 | for (u16 y = 0; y < ZMAP_LENGTH; ++y) 157 | { 158 | // I've defined the ZMAP to have the bottom of the screen 159 | // (nearest position) start at zmap[0] 160 | fix16 z = zmap[y]; 161 | // if line of screen's Z Map position is below segment position 162 | if (z < segment_position) 163 | { 164 | // dx = bottom_segment.dx 165 | dx = segments[bottom_segments_index].dx; 166 | } 167 | else // if line of Screen's Z map position is above segment position. 168 | { 169 | // dx = segment.dx 170 | dx = segments[segments_index].dx; 171 | } 172 | 173 | // ddx += dx 174 | ddx = fix16Add(ddx, dx); 175 | // current_x += ddx 176 | current_x = fix16Add(current_x, ddx); 177 | 178 | // this_line.x = current_x 179 | // we'll use horizontal scrolling of BG_A to fake curves. 180 | HscrollA[223 - y] = SCROLL_CENTER + fix16ToInt(current_x); 181 | } 182 | 183 | 184 | 185 | // Move segments 186 | segment_position = fix16Add(segment_position, speed); 187 | if (fix16ToInt(segment_position) < 0) // 0 is nearest 188 | { 189 | // bottom_segment = segment 190 | bottom_segments_index = segments_index; 191 | 192 | // segment.position = zmap.length - 1 193 | segment_position = zmap[ZMAP_LENGTH - 1]; // Send segment to farthest visible distance 194 | // fetch next segment from road 195 | segments_index++; // segment_index is used to get segment.dx 196 | if (segments_index == ROAD_SEGMENTS_LENGTH) 197 | { 198 | segments_index -= ROAD_SEGMENTS_LENGTH; // go back to the start 199 | } 200 | } 201 | } 202 | 203 | 204 | 205 | ~~~ 206 | ### Screenshots 207 | Curved segment followed by a straight segment 208 | 209 | ![curved and straight segments](./img/rom_722.bmp) 210 | 211 | Two curved segments 212 | 213 | ![twocurved segments](./img/rom_208.bmp) 214 | 215 | 216 | # Background Image Scrolling 217 | Scrolling the background image is pretty simple. 218 | 219 | I initialize `HScrollB` in `main()` to the `SCROLL_CENTER` 220 | ~~~c 221 | for (int i = 0; i < VERTICAL_REZ; i++) 222 | { 223 | HscrollA[i] = SCROLL_CENTER; 224 | HscrollB[i] = SCROLL_CENTER; 225 | } 226 | ~~~ 227 | 228 | During each update, I add the current bottom segment's `bgdx` to each line of `HscrollB[]` 229 | 230 | ~~~c 231 | // scroll the background 232 | background_position = fix16Add(background_position, segments[bottom_segments_index].bgdx); 233 | for (u16 y = 0; y < 120; ++y) 234 | { 235 | HscrollB[y] = fix16ToInt(background_position); 236 | } 237 | ~~~ 238 | 239 | And the main loop scrolls background B 240 | ~~~c 241 | // move the background 242 | VDP_setHorizontalScrollLine(BG_B, 0, HscrollB, 120, DMA_QUEUE); 243 | ~~~ -------------------------------------------------------------------------------- /lou/01_basic_road/img/rom_208.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/01_basic_road/img/rom_208.bmp -------------------------------------------------------------------------------- /lou/01_basic_road/img/rom_722.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/01_basic_road/img/rom_722.bmp -------------------------------------------------------------------------------- /lou/01_basic_road/res/background/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/01_basic_road/res/background/background.png -------------------------------------------------------------------------------- /lou/01_basic_road/res/background/road.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/01_basic_road/res/background/road.png -------------------------------------------------------------------------------- /lou/01_basic_road/res/resources.res: -------------------------------------------------------------------------------- 1 | IMAGE road "background/road.png" BEST 2 | IMAGE background "background/background.png" BEST 3 | PALETTE road_pal "background/road.png" 4 | PALETTE background_pal "background/background.png" 5 | -------------------------------------------------------------------------------- /lou/01_basic_road/src/main.c: -------------------------------------------------------------------------------- 1 | 2 | // SGDK 3 | #include 4 | #include "resources.h" 5 | 6 | #define VERTICAL_REZ 224 // number of line sin the screen. 7 | 8 | // image is 512x224. Screen is 320, we want to move halfway 9 | // (512-320)/2 10 | #define SCROLL_CENTER -96 11 | 12 | // Zmap for tracking segment position 13 | #define ZMAP_LENGTH 110 14 | fix16 zmap[ZMAP_LENGTH]; 15 | 16 | 17 | // Road data 18 | #define ROAD_SEGMENTS_LENGTH 13 19 | typedef struct 20 | { 21 | fix16 dx; // rate of change for the road. 22 | fix16 bgdx; // rate of change for background. ( ignore for now ) 23 | } ROAD_SEGMENT; 24 | const ROAD_SEGMENT segments[ROAD_SEGMENTS_LENGTH] = { 25 | {FIX16(0), FIX16(0)}, 26 | {FIX16(-0.02), FIX16(0.120)}, 27 | {FIX16(-0.04), FIX16(0.32)}, 28 | {FIX16(-0.02), FIX16(0.120)}, 29 | {FIX16(0), FIX16(0)}, 30 | {FIX16(0), FIX16(0)}, 31 | {FIX16(0.06), FIX16(-0.36)}, 32 | {FIX16(-0.06), FIX16(0.36)}, 33 | {FIX16(0), FIX16(0)}, 34 | {FIX16(0.02), FIX16(-0.12)}, 35 | {FIX16(0), FIX16(0)}, 36 | {FIX16(-0.03), FIX16(0.18)}, 37 | {FIX16(0.03), FIX16(-0.18)}}; 38 | 39 | u16 bottom_segments_index = 0; 40 | u16 segments_index = 0; 41 | 42 | // speed the 'vehicle' moves through the road 43 | fix16 speed = FIX16(0.00); 44 | 45 | // Horizontal scrolling values 46 | s16 HscrollA[VERTICAL_REZ]; 47 | s16 HscrollB[VERTICAL_REZ]; 48 | 49 | // position variables. 50 | fix16 segment_position = FIX16(0); // keep track fo the segment position onscreen 51 | fix16 background_position = FIX16(SCROLL_CENTER); // handle background X position 52 | 53 | // My interpretation of the pseudo-code in 54 | // http://www.extentofthejam.com/pseudo/#curves 55 | void update() 56 | { 57 | fix16 current_x = FIX16(0); // Lou's pseudo 3d page says to use Half of the screen width, 58 | // but I've defined SCROLL_CENTER to handle this 59 | 60 | fix16 dx = FIX16(0); // Curve amount, constant per segment. 61 | fix16 ddx = FIX16(0); // Curve amount, changes per line 62 | 63 | 64 | // for each line of the screen from the bottom to the top 65 | for (u16 y = 0; y < ZMAP_LENGTH; ++y) 66 | { 67 | // I've defined the ZMAP to have the bottom of the screen 68 | // (nearest position) start at zmap[0] 69 | fix16 z = zmap[y]; 70 | // if line of screen's Z Map position is below segment position 71 | if (z < segment_position) 72 | { 73 | // dx = bottom_segment.dx 74 | dx = segments[bottom_segments_index].dx; 75 | } 76 | else // if line of Screen's Z map position is above segment position. 77 | { 78 | // dx = segment.dx 79 | dx = segments[segments_index].dx; 80 | } 81 | 82 | // ddx += dx 83 | ddx = fix16Add(ddx, dx); 84 | // current_x += ddx 85 | current_x = fix16Add(current_x, ddx); 86 | 87 | // this_line.x = current_x 88 | // we'll use horizontal scrolling of BG_A to fake curves. 89 | HscrollA[223 - y] = SCROLL_CENTER + fix16ToInt(current_x); 90 | } 91 | 92 | // scroll the background 93 | background_position = fix16Add(background_position, segments[bottom_segments_index].bgdx); 94 | for (u16 y = 0; y < 120; ++y) 95 | { 96 | HscrollB[y] = fix16ToInt(background_position); 97 | } 98 | 99 | // Move segments 100 | segment_position = fix16Add(segment_position, speed); 101 | if (fix16ToInt(segment_position) < 0) // 0 is nearest 102 | { 103 | // bottom_segment = segment 104 | bottom_segments_index = segments_index; 105 | 106 | // segment.position = zmap.length - 1 107 | segment_position = zmap[ZMAP_LENGTH - 1]; // Send segment to farthest visible distance 108 | // fetch next segment from road 109 | segments_index++; // segment_index is used to get segment.dx 110 | if (segments_index == ROAD_SEGMENTS_LENGTH) 111 | { 112 | segments_index -= ROAD_SEGMENTS_LENGTH; // go back to the start 113 | } 114 | } 115 | } 116 | 117 | int main(bool hard) 118 | { 119 | ////////////////////////////////////////////////////////////// 120 | // http://www.extentofthejam.com/pseudo/ 121 | // Z = Y_world / (Y_screen - (height_screen / 2)) 122 | for (u16 i = 0; i < ZMAP_LENGTH; ++i) 123 | { 124 | zmap[i] = fix16Div(FIX16(-75), fix16Sub(FIX16(i), FIX16(112))); 125 | KLog_f1("FIX16(", zmap[i]); 126 | } 127 | 128 | ////////////////////////////////////////////////////////////// 129 | // VDP basic setup 130 | VDP_setBackgroundColor(16); 131 | VDP_setScreenWidth320(); 132 | 133 | ////////////////////////////////////////////////////////////// 134 | // initialize scrolling values to the center of the image. 135 | VDP_setScrollingMode(HSCROLL_LINE, VSCROLL_PLANE); 136 | for (int i = 0; i < VERTICAL_REZ; i++) 137 | { 138 | HscrollA[i] = SCROLL_CENTER; 139 | HscrollB[i] = SCROLL_CENTER; 140 | } 141 | 142 | ////////////////////////////////////////////////////////////// 143 | // Setup background A 144 | PAL_setPalette(PAL1, road_pal.data, CPU); 145 | int ind = TILE_USER_INDEX; 146 | VDP_drawImageEx(BG_A, &road, TILE_ATTR_FULL(PAL1, FALSE, FALSE, FALSE, ind), 0, 0, FALSE, TRUE); 147 | ind += road.tileset->numTile; 148 | PAL_setPalette(PAL2, background_pal.data, CPU); 149 | VDP_drawImageEx(BG_B, &background, TILE_ATTR_FULL(PAL2, FALSE, FALSE, FALSE, ind), 0, 0, FALSE, TRUE); 150 | ind += background.tileset->numTile; 151 | 152 | ////////////////////////////////////////////////////////////// 153 | // init segments 154 | bottom_segments_index = 0; 155 | segments_index = 1; 156 | segment_position = zmap[ZMAP_LENGTH - 1]; // put it at the farthest away point 157 | 158 | // set speed through z 159 | speed = FIX16(-0.1); 160 | 161 | // Main loop 162 | while (TRUE) 163 | { 164 | // update each frame 165 | update(); 166 | 167 | // curve the road with horizontal scrolling. 168 | VDP_setHorizontalScrollLine(BG_A, 0, HscrollA, VERTICAL_REZ, DMA_QUEUE); 169 | // move the background 170 | VDP_setHorizontalScrollLine(BG_B, 0, HscrollB, 120, DMA_QUEUE); 171 | 172 | SYS_doVBlankProcess(); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /lou/02_hills/res/background/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/02_hills/res/background/background.png -------------------------------------------------------------------------------- /lou/02_hills/res/background/road.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/02_hills/res/background/road.png -------------------------------------------------------------------------------- /lou/02_hills/res/resources.res: -------------------------------------------------------------------------------- 1 | IMAGE road "background/road.png" BEST 2 | IMAGE background "background/background.png" BEST 3 | PALETTE road_pal "background/road.png" 4 | PALETTE background_pal "background/background.png" 5 | -------------------------------------------------------------------------------- /lou/02_hills/src/main.c: -------------------------------------------------------------------------------- 1 | 2 | // SGDK 3 | #include 4 | #include "resources.h" 5 | 6 | #define VERTICAL_REZ 224 7 | 8 | // image is 512x224. Screen is 320, we want to move halfway 9 | // (512-320)/2 10 | #define SCROLL_CENTER -96 11 | 12 | // keep track of the current line during Horizontal Interrupts 13 | u16 lineDisplay = 0; 14 | 15 | // Horizontal Scrolling values 16 | s16 HscrollA[VERTICAL_REZ]; 17 | // Vertical Scrolling values ( to simulate hills ) 18 | s16 VscrollA[VERTICAL_REZ]; 19 | 20 | static void VIntHandler() 21 | { 22 | // Make sure HIntHander starts with line 0 23 | lineDisplay = 0; 24 | } 25 | 26 | HINTERRUPT_CALLBACK HIntHandler() 27 | { 28 | // Set vertical scrolling based on hill calculations 29 | VDP_setVerticalScroll(BG_A, VscrollA[lineDisplay]); 30 | 31 | // Move to next line for the next horizontal interrupt. 32 | lineDisplay++; 33 | } 34 | 35 | // My interpretation of the hills described in 36 | // http://www.extentofthejam.com/pseudo/#hills 37 | 38 | // dy1 : slope amount, constant for segment 1 39 | // dy2 : slope amount, constant for segment 2 40 | // segment line : transition point between dy1 and dy2 41 | void CreateHills(fix32 dy1, fix32 dy2, u16 segmentLine) 42 | { 43 | fix32 current_drawing_pos = FIX32(223); // The drawing loop would start at the beginning of the Z-map (nearest). Basically the bottom of the screen 44 | s16 horizon_line = 223; // keep track of where the horizon is. I"m starting at the bottom and will update as the rode gets computed 45 | 46 | fix32 dy = dy1; // y delta determines if we're sloping up or down 47 | 48 | fix32 ddy = FIX32(0); // slope amount, changes per line 49 | 50 | // iterate over every line in the road, closest to farthest Z 51 | for (u16 bgY = 223; bgY > 115; bgY--) 52 | { 53 | s16 cdp = fix32ToInt(current_drawing_pos); // get current drawing position as an int 54 | 55 | if (bgY == segmentLine) // simulate two segments 56 | { 57 | dy = dy2; // hill value for segment 2 58 | } 59 | 60 | if (cdp <= horizon_line) // if current drawing position is above the horizon 61 | { 62 | VscrollA[cdp] = bgY - cdp; // set vertical scroll amount for current drawing position 63 | horizon_line = cdp; // update horizon line 64 | } 65 | 66 | ddy = fix32Add(dy, ddy); 67 | fix32 delta_drawing_pos = fix32Add(FIX32(1), ddy); // increment drawing position 68 | 69 | fix32 next_drawing_pos = fix32Sub(current_drawing_pos, delta_drawing_pos); // figure out next drawing position 70 | s16 ndp = fix32ToInt(next_drawing_pos); 71 | KLog_S2(" cdp: ", cdp, " ndp: ", ndp); 72 | if (cdp - ndp > 1) // need to set Vertical scrolling value if the next drawing position is farther than one line. 73 | { 74 | // repeat line 75 | // cdp + 1; 76 | for (; cdp > ndp; --cdp) 77 | { 78 | KLog_S2(" bgY: ", bgY, " bgY- cdp: ", (bgY - cdp)); 79 | if (cdp <= horizon_line) 80 | { 81 | VscrollA[cdp] = bgY - cdp; 82 | horizon_line = cdp; 83 | } 84 | } 85 | } 86 | current_drawing_pos = next_drawing_pos; // move to next drawing position 87 | } 88 | 89 | // hide anything above the horizon line. 90 | for (s16 h = horizon_line - 1; h >= 16; --h) 91 | { 92 | VscrollA[h] = -h; 93 | } 94 | } 95 | 96 | void handleJoypad() 97 | { 98 | u16 value = JOY_readJoypad(JOY_1); 99 | 100 | if (value & BUTTON_A) 101 | { 102 | VDP_drawText("dy1: 0.015, dy2: 0, seg-line: 0 ", 13, 1); 103 | CreateHills(FIX32(0.015), FIX32(0), 0); 104 | } 105 | if (value & BUTTON_B) 106 | { 107 | VDP_drawText("dy1: -0.01, dy2: 0, seg-line: 0 ", 13, 1); 108 | CreateHills(FIX32(-0.01), FIX32(0), 0); 109 | } 110 | if (value & BUTTON_C) 111 | { 112 | VDP_drawText("dy1: 0.015, dy2: -0.01, seg-line: 160", 13, 1); 113 | CreateHills(FIX32(0.015), FIX32(-0.01), 160); 114 | } 115 | if (value & BUTTON_X) 116 | { 117 | VDP_drawText("dy1: -0.007, dy2: 0.06, seg-line: 160", 13, 1); 118 | CreateHills(FIX32(-0.007), FIX32(0.06), 160); 119 | } 120 | } 121 | 122 | int main(bool hard) 123 | { 124 | 125 | ////////////////////////////////////////////////////////////// 126 | // VDP basic setup 127 | VDP_setBackgroundColor(16); 128 | VDP_setScreenWidth320(); 129 | 130 | ////////////////////////////////////////////////////////////// 131 | // Initialize horizontal scrolling values to the center of the image 132 | VDP_setScrollingMode(HSCROLL_LINE, VSCROLL_PLANE); 133 | for (int i = 0; i < VERTICAL_REZ; i++) 134 | { 135 | HscrollA[i] = SCROLL_CENTER; 136 | VscrollA[i] = 0; 137 | } 138 | 139 | // setup initial hill (no slope) 140 | CreateHills(FIX32(0), FIX32(0.04), 145); 141 | 142 | ////////////////////////////////////////////////////////////// 143 | // Setup background A 144 | PAL_setPalette(PAL1, road_pal.data, CPU); 145 | int ind = TILE_USER_INDEX; 146 | VDP_drawImageEx(BG_A, &road, TILE_ATTR_FULL(PAL1, FALSE, FALSE, FALSE, ind), 0, 0, FALSE, TRUE); 147 | 148 | ////////////////////////////////////////////////////////////// 149 | // Setup interrupt handlers 150 | SYS_disableInts(); 151 | { 152 | VDP_setHIntCounter(0); 153 | VDP_setHInterrupt(1); 154 | 155 | SYS_setHIntCallback(HIntHandler); 156 | SYS_setVIntCallback(VIntHandler); 157 | } 158 | 159 | 160 | SYS_enableInts(); 161 | // Main loop 162 | while (TRUE) 163 | { 164 | VDP_drawText("Press A, B, C or X", 15, 0); 165 | // update 166 | handleJoypad(); 167 | 168 | // do horizontal scrolling to center the background 169 | VDP_setHorizontalScrollLine(BG_A, 0, HscrollA, VERTICAL_REZ, DMA_QUEUE); 170 | SYS_doVBlankProcess(); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /lou/03_hilly_road/res/background/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/03_hilly_road/res/background/background.png -------------------------------------------------------------------------------- /lou/03_hilly_road/res/background/road.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/03_hilly_road/res/background/road.png -------------------------------------------------------------------------------- /lou/03_hilly_road/res/resources.res: -------------------------------------------------------------------------------- 1 | IMAGE road "background/road.png" BEST 2 | IMAGE background "background/background.png" BEST 3 | PALETTE road_pal "background/road.png" 4 | PALETTE background_pal "background/background.png" 5 | -------------------------------------------------------------------------------- /lou/03_hilly_road/src/main.c: -------------------------------------------------------------------------------- 1 | 2 | // SGDK 3 | #include 4 | #include "resources.h" 5 | 6 | #define VERTICAL_REZ 224 7 | 8 | // image is 512x224. Screen is 320, we want to move halfway 9 | // 512/2 - 320 /2 10 | #define SCROLL_CENTER -96 11 | 12 | // Keep track of the current line during Horizontal Interrupts 13 | u16 lineDisplay = 0; 14 | 15 | // Horizontal Scrolling values 16 | s16 HscrollA[VERTICAL_REZ]; 17 | s16 HscrollB[VERTICAL_REZ]; 18 | // Vertical Scrolling values ( to simulate hills ) 19 | s8 VscrollA[VERTICAL_REZ]; 20 | 21 | // Zmap for tracking segment position 22 | #define ZMAP_LENGTH 110 23 | fix16 zmap[ZMAP_LENGTH]; 24 | 25 | // Road data 26 | #define ROAD_SEGMENTS_LENGTH 15 27 | typedef struct 28 | { 29 | fix16 dx; // rate of change for the road. 30 | fix16 bgdx; // rate of change for background. ( ignore for now ) 31 | fix32 dy; // rate of change for drawing road in y dir? 32 | } ROAD_SEGMENT; 33 | 34 | const ROAD_SEGMENT segments[ROAD_SEGMENTS_LENGTH] = { 35 | {FIX16(0), FIX16(0), FIX32(-0.001)}, 36 | {FIX16(-0.02), FIX16(-0.48), FIX32(0.002)}, 37 | {FIX16(-0.04), FIX16(-1.28), FIX32(-0.001)}, 38 | {FIX16(-0.02), FIX16(-0.48), FIX32(0.0)}, 39 | {FIX16(0), FIX16(0), FIX32(0.001)}, 40 | {FIX16(0), FIX16(0), FIX32(0.0025)}, 41 | {FIX16(0.03), FIX16(0.64), FIX32(-0.002)}, 42 | {FIX16(-0.03), FIX16(-0.64), FIX32(0)}, 43 | {FIX16(0), FIX16(0), FIX32(0.001)}, 44 | {FIX16(0), FIX16(0), FIX32(-0.0025)}, 45 | {FIX16(0), FIX16(0), FIX32(0.002)}, 46 | {FIX16(0.0), FIX16(0.0), FIX32(0)}, 47 | {FIX16(0), FIX16(0), FIX32(0.0)}, 48 | {FIX16(-0.015), FIX16(-0.32), FIX32(0)}, 49 | {FIX16(0.015), FIX16(0.32), FIX32(0)}}; 50 | 51 | u16 bottom_segments_index = 0; 52 | u16 segments_index = 0; 53 | 54 | // Speed the 'vehicle' moves through teh road 55 | fix16 speed = FIX16(0.00); 56 | 57 | // position variables. 58 | fix16 segment_position = FIX16(0); // keep track of the segment position on screen 59 | fix16 background_position = FIX16(SCROLL_CENTER); // handle background X position 60 | s16 horizon_line = 223; // keep track of where the horizon is 61 | 62 | HINTERRUPT_CALLBACK HIntHandler() 63 | { 64 | // set vertical scroll based on hill calculations 65 | VDP_setVerticalScroll(BG_A, VscrollA[lineDisplay]); 66 | 67 | // Move to the next line for the next horizontal interrupt. 68 | lineDisplay++; 69 | } 70 | 71 | void VIntHandler() 72 | { 73 | // Make sure HInt always starts with line 0 74 | lineDisplay = 0; 75 | } 76 | 77 | 78 | // My interpretation of the pseudo-code in 79 | // http://www.extentofthejam.com/pseudo/#curves 80 | // and the hills described in 81 | // http://www.extentofthejam.com/pseudo/#hills 82 | void update() 83 | { 84 | fix16 current_x = FIX16(0); // Lou's pseudo 3d page says to use Half of the screen width, 85 | // but I've defined SCROLL_CENTER to handle this 86 | 87 | fix16 dx = FIX16(0); // Curve Amount, constant per segment. 88 | fix16 ddx = FIX16(0); // Curve Amount, changes per line 89 | 90 | fix32 dy = FIX32(0); // Slope Amount 91 | fix32 ddy = FIX32(0); // Slope Amount, changes per line 92 | 93 | fix32 current_drawing_pos = FIX32(223); // The drawing loop would start at the beginning of the Z-map (nearest). Basically the bottom of the screen 94 | s16 horizon_line = 223; // keep track of where the horizon is. I"m starting at the bottom and will update as the rode gets computed 95 | 96 | 97 | // for each line of the screen from the bottom to the top 98 | //for (y = 0; y < ZMAP_LENGTH; ++y) // no longer works because up-hill/down-hill won't be exaclty 1. ++y isn't valid 99 | 100 | // HILL: draw loop starts at the beginning of the Z map ( nearest = 0 ) and stops at teh end (farthest = ZMAP_LENGTH ) 101 | // * flat roads decrements the drawing position each line by 1 102 | // * if we decrement the drawing position by 2 (doubling lines) the road gets drawn twice as high. 103 | // * by varying the amount we decrement the drawing position we can draw a hill that starts flat and curves upwards 104 | // * if the next drawing position is more than one line from the current drawing position, the currnet ZMap line is repeated until 105 | // we get there, producing a scalien effect. 106 | for (u16 bgY = 223; bgY > 113; bgY--) 107 | { 108 | 109 | ////////////////////////////////////////////////////////////////////// 110 | // Road Bending 111 | fix16 z = zmap[223 - bgY]; // zmap[0] is closest 112 | // if line of screen's Z map position is below segment position 113 | if (z < segment_position) 114 | { 115 | // dx = bottom_segment.dx 116 | dx = segments[bottom_segments_index].dx; 117 | dy = segments[bottom_segments_index].dy; 118 | } 119 | else // if line of Screen's Z map position is above segment position. 120 | { 121 | // dx = segment.dx 122 | dx = segments[segments_index].dx; 123 | dy = segments[segments_index].dy; 124 | } 125 | 126 | // ddx += dx 127 | ddx = fix16Add(ddx, dx); 128 | // current_x += ddx 129 | current_x = fix16Add(current_x, ddx); 130 | 131 | ddy = fix32Add(dy, ddy); 132 | s16 cdp = fix32ToInt(current_drawing_pos); // current vertical drawing position 133 | fix32 delta_drawing_pos = fix32Add(FIX32(1), ddy); // increment drawing position 134 | fix32 next_drawing_pos = fix32Sub(current_drawing_pos, delta_drawing_pos); 135 | s16 ndp = fix32ToInt(next_drawing_pos); // figure out next drawing position 136 | // repeat line if theres a gap greater than 1 137 | for (; cdp > ndp; --cdp) // 138 | { 139 | if (cdp <= horizon_line) // if current drawing position is above the horizon 140 | { 141 | HscrollA[cdp] = SCROLL_CENTER + fix16ToInt(current_x); // this_line.x = current x | using horizontal scrolling to fake curves 142 | VscrollA[cdp] = bgY - cdp; // set the vertical scroll amount for the current drawing position 143 | horizon_line = cdp; // update horizon line 144 | } 145 | } 146 | 147 | current_drawing_pos = next_drawing_pos; // move to next drawing position 148 | } 149 | 150 | // hide anything above the horizon line 151 | for (s16 h = horizon_line; h >= 0; --h) 152 | { 153 | VscrollA[h] = -h; 154 | } 155 | 156 | // scroll the background 157 | background_position = fix16Sub(background_position, segments[bottom_segments_index].bgdx); 158 | for (u16 y = 0; y < 160; ++y) 159 | { 160 | HscrollB[y] = fix16ToInt(background_position); 161 | } 162 | 163 | // Move segments 164 | segment_position = fix16Add(segment_position, speed); 165 | if (fix16ToInt(segment_position) < 0) 166 | { 167 | // bottom_segment = segment 168 | bottom_segments_index = segments_index; 169 | // segment.position = zmap.length - 1 170 | segment_position = zmap[ZMAP_LENGTH - 1]; // Send segment to farthest visible distance 171 | 172 | // get next segment from road 173 | segments_index++; // segment_index is used to get segment.dx 174 | if (segments_index == ROAD_SEGMENTS_LENGTH) 175 | { 176 | segments_index -= ROAD_SEGMENTS_LENGTH; // go back to the start 177 | } 178 | } 179 | } 180 | 181 | 182 | int main(bool hard) 183 | { 184 | ////////////////////////////////////////////////////////////// 185 | // http://www.extentofthejam.com/pseudo/ 186 | // Z = Y_world / (Y_screen - (height_screen / 2)) 187 | for (u16 i = 0; i < ZMAP_LENGTH; ++i) 188 | { 189 | zmap[i] = fix16Div(FIX16(-75), fix16Sub(FIX16(i), FIX16(112))); 190 | KLog_f1("FIX16(", zmap[i]); 191 | } 192 | 193 | 194 | ////////////////////////////////////////////////////////////// 195 | // VDP basic setup 196 | VDP_setBackgroundColor(16); 197 | VDP_setScreenWidth320(); 198 | VDP_setScrollingMode(HSCROLL_LINE, VSCROLL_PLANE); 199 | for (int i = 0; i < VERTICAL_REZ; i++) 200 | { 201 | HscrollA[i] = SCROLL_CENTER; 202 | HscrollB[i] = SCROLL_CENTER; 203 | VscrollA[i] = 0; 204 | } 205 | 206 | ////////////////////////////////////////////////////////////// 207 | // Setup scroll panes 208 | PAL_setPalette(PAL0, road_pal.data, CPU); 209 | int ind = TILE_USER_INDEX; 210 | VDP_drawImageEx(BG_A, &road, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, ind), 0, 0, FALSE, TRUE); 211 | ind += road.tileset->numTile; 212 | PAL_setPalette(PAL2, background_pal.data, CPU); 213 | VDP_drawImageEx(BG_B, &background, TILE_ATTR_FULL(PAL2, FALSE, FALSE, FALSE, ind), 0, 0, FALSE, TRUE); 214 | ind += background.tileset->numTile; 215 | 216 | ////////////////////////////////////////////////////////////// 217 | // init segments 218 | bottom_segments_index = 0; 219 | segments_index = 1; 220 | segment_position = zmap[ZMAP_LENGTH - 1 ]; // put it at the farthest away point 221 | 222 | // set spped through z 223 | speed = FIX16(-0.2); 224 | 225 | ////////////////////////////////////////////////////////////// 226 | // Setup interrupt handlers 227 | SYS_disableInts(); 228 | { 229 | VDP_setHIntCounter(0); 230 | VDP_setHInterrupt(1); 231 | 232 | SYS_setHIntCallback(HIntHandler); 233 | SYS_setVIntCallback(VIntHandler); 234 | } 235 | SYS_enableInts(); 236 | 237 | // Main loop 238 | while (TRUE) 239 | { 240 | // update 241 | update(); 242 | 243 | // curve the road with horizontal scrolling 244 | VDP_setHorizontalScrollLine(BG_A, 0, HscrollA, VERTICAL_REZ, DMA_QUEUE); 245 | // move the background 246 | VDP_setHorizontalScrollLine(BG_B, 0, HscrollB, 160, DMA_QUEUE); 247 | 248 | SYS_doVBlankProcess(); 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /lou/04_colors/res/background/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/04_colors/res/background/background.png -------------------------------------------------------------------------------- /lou/04_colors/res/background/road.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/04_colors/res/background/road.png -------------------------------------------------------------------------------- /lou/04_colors/res/resources.res: -------------------------------------------------------------------------------- 1 | IMAGE road "background/road.png" BEST 2 | IMAGE background "background/background.png" BEST 3 | PALETTE road_pal "background/road.png" 4 | PALETTE background_pal "background/background.png" 5 | -------------------------------------------------------------------------------- /lou/04_colors/src/HInter.s: -------------------------------------------------------------------------------- 1 | 2 | 3 | *------------------------------------------------ 4 | * LABELS 5 | *------------------------------------------------ 6 | .set VDP_CTRL, 0xC00004 7 | .set VDP_DATA, 0xC00000 8 | 9 | .set PAL0_COLOR0, 0xC0000000 10 | .set PAL0_COLOR1, 0xC0020000 11 | .set PAL0_COLOR2, 0xC0040000 12 | .set PAL0_COLOR3, 0xC0060000 13 | .set PAL0_COLOR4, 0xC0080000 14 | 15 | .set VSCROLL_A, 0x40000010 16 | .set VSCROLL_B, 0x40020010 17 | .set HSCROLL_A, 0x7C000002 18 | .set HSCROLL_B, 0x7C020002 19 | 20 | .set LINE_DARK_COLOR, 0x666 21 | .set LINE_LIGHT_COLOR, 0xFFF 22 | 23 | .set GRASS_DARK_COLOR, 0x060 24 | .set GRASS_LIGHT_COLOR, 0x0C0 25 | 26 | *------------------------------------------------ 27 | * Functions 28 | *------------------------------------------------ 29 | .globl HInter 30 | 31 | HInter: 32 | move.l %d0, -(%sp) /* push device register 0 onto the stack */ 33 | 34 | clr.l %d0 /* Clear D0 and read the current scanline to it */ 35 | move.b (0xC00008), %d0 36 | 37 | cmp.w #100, %d0 38 | jgt WORK /* do if current line is below horizon */ 39 | 40 | move.l (%sp)+, %d0 /* restore data register 0 */ 41 | rte 42 | 43 | WORK: 44 | move.l %a0, -(%sp) /* push address register 0 onto the stack */ 45 | 46 | lea VscrollA, %a0 /* get 'vscrollA' array effective address to A0 */ 47 | move.l #VSCROLL_A, 0xC00004 /* Vertical scrolling */ 48 | move.b (%a0, %d0.w), 0xC00000 49 | 50 | lea colors, %a0 /* get 'colors' array effective address to A0 */ 51 | move.b (%a0,%d0.w),%d0 /* check shading for current scanline */ 52 | move.l (%sp)+, %a0 /* restore address register 0 */ 53 | tst.b %d0 /* check shading value */ 54 | jeq LIGHT /* jump to light coloring */ 55 | 56 | DARK: 57 | LINE_DARK: 58 | move.b #2, %d0 59 | cmp.b line_color, %d0 60 | jne SET_LINE_DARK /* if not dark, set the color */ 61 | jmp GRASS_DARK 62 | move.l (%sp)+, %d0 /* restore data register 0 */ 63 | rte 64 | 65 | SET_LINE_DARK: 66 | 67 | move.b %d0, line_color 68 | move.l #PAL0_COLOR1, VDP_CTRL /* Tell VDP we want to change color 1 */ 69 | clr.w %d0 70 | 71 | LINE_DELAY1: 72 | move.w (VDP_CTRL), %d0 /* wait before we set color (to minimize dots) */ 73 | btst.b #0x02, %d0 74 | beq LINE_DELAY1 75 | 76 | move.w #LINE_DARK_COLOR, VDP_DATA /* set the color */ 77 | 78 | move.l (%sp)+, %d0 /* restore data register 0 */ 79 | rte 80 | 81 | GRASS_DARK: 82 | move.b #2, %d0 83 | cmp.b grass_color, %d0 84 | jne SET_GRASS_DARK /* if not dark, set the color */ 85 | move.l (%sp)+, %d0 /* restore data register 0 */ 86 | rte 87 | 88 | SET_GRASS_DARK: 89 | 90 | move.b %d0, grass_color 91 | move.l #PAL0_COLOR3, VDP_CTRL /* Tell VDP we want to change color 1 */ 92 | clr.w %d0 93 | 94 | GRASS_DELAY1: 95 | move.w (VDP_CTRL), %d0 /* wait before we set color (to minimize dots) */ 96 | btst.b #0x02, %d0 97 | beq GRASS_DELAY1 98 | 99 | move.w #GRASS_DARK_COLOR, VDP_DATA /* set the color */ 100 | 101 | move.l (%sp)+, %d0 /* restore data register 0 */ 102 | rte 103 | 104 | 105 | LIGHT: 106 | 107 | LINE_LIGHT: 108 | move.b #1, %d0 109 | cmp.b line_color, %d0 110 | jne SET_LINE_LIGHT /* if not dark, set the color */ 111 | JMP GRASS_LIGHT 112 | move.l (%sp)+, %d0 /* restore data register 0 */ 113 | rte 114 | 115 | SET_LINE_LIGHT: 116 | move.b %d0, line_color 117 | move.l #PAL0_COLOR1, VDP_CTRL /* Tell VDP we want to change color 1 */ 118 | clr.w %d0 119 | 120 | LINE_DELAY2: 121 | move.w (VDP_CTRL), %d0 /* wait before setting color */ 122 | btst.b #0x02, %d0 123 | beq LINE_DELAY2 124 | 125 | move.w #LINE_LIGHT_COLOR, VDP_DATA /* set the color */ 126 | 127 | move.l (%sp)+, %d0 /* restore data register 0 */ 128 | rte 129 | 130 | GRASS_LIGHT: 131 | move.b #1, %d0 132 | cmp.b grass_color, %d0 133 | jne SET_GRASS_LIGHT /* if not dark, set the color */ 134 | move.l (%sp)+, %d0 /* restore data register 0 */ 135 | rte 136 | 137 | SET_GRASS_LIGHT: 138 | 139 | move.b %d0, grass_color 140 | move.l #PAL0_COLOR3, VDP_CTRL /* Tell VDP we want to change color 1 */ 141 | clr.w %d0 142 | 143 | GRASS_DELAY2: 144 | move.w (VDP_CTRL), %d0 /* wait before we set color (to minimize dots) */ 145 | btst.b #0x02, %d0 146 | beq GRASS_DELAY2 147 | 148 | move.w #GRASS_LIGHT_COLOR, VDP_DATA /* set the color */ 149 | 150 | move.l (%sp)+, %d0 /* restore data register 0 */ 151 | rte 152 | 153 | -------------------------------------------------------------------------------- /lou/04_colors/src/boot/sega.s: -------------------------------------------------------------------------------- 1 | #include "task_cst.h" 2 | 3 | .section .text.keepboot 4 | 5 | *------------------------------------------------------- 6 | * 7 | * Sega startup code for the GNU Assembler 8 | * Translated from: 9 | * Sega startup code for the Sozobon C compiler 10 | * Written by Paul W. Lee 11 | * Modified by Charles Coty 12 | * Modified by Stephane Dallongeville 13 | * 14 | *------------------------------------------------------- 15 | 16 | .globl rom_header 17 | 18 | .org 0x00000000 19 | 20 | _Start_Of_Rom: 21 | _Vecteurs_68K: 22 | dc.l __stack /* Stack address */ 23 | dc.l _Entry_Point /* Program start address */ 24 | dc.l _Bus_Error 25 | dc.l _Address_Error 26 | dc.l _Illegal_Instruction 27 | dc.l _Zero_Divide 28 | dc.l _Chk_Instruction 29 | dc.l _Trapv_Instruction 30 | dc.l _Privilege_Violation 31 | dc.l _Trace 32 | dc.l _Line_1010_Emulation 33 | dc.l _Line_1111_Emulation 34 | dc.l _Error_Exception, _Error_Exception, _Error_Exception, _Error_Exception 35 | dc.l _Error_Exception, _Error_Exception, _Error_Exception, _Error_Exception 36 | dc.l _Error_Exception, _Error_Exception, _Error_Exception, _Error_Exception 37 | dc.l _Error_Exception 38 | dc.l _INT 39 | dc.l _EXTINT 40 | dc.l _INT 41 | dc.l HInter 42 | dc.l _INT 43 | dc.l _VINT 44 | dc.l _INT 45 | dc.l _trap_0 /* Resume supervisor task */ 46 | dc.l _INT,_INT,_INT,_INT,_INT,_INT,_INT 47 | dc.l _INT,_INT,_INT,_INT,_INT,_INT,_INT,_INT 48 | dc.l _INT,_INT,_INT,_INT,_INT,_INT,_INT,_INT 49 | dc.l _INT,_INT,_INT,_INT,_INT,_INT,_INT,_INT 50 | 51 | rom_header: 52 | .incbin "out/rom_head.bin", 0, 0x100 53 | 54 | _Entry_Point: 55 | move #0x2700,%sr 56 | tst.l 0xa10008 57 | bne.s SkipJoyDetect 58 | 59 | tst.w 0xa1000c 60 | 61 | SkipJoyDetect: 62 | bne.s SkipSetup 63 | 64 | lea Table,%a5 65 | movem.w (%a5)+,%d5-%d7 66 | movem.l (%a5)+,%a0-%a4 67 | * Check Version Number 68 | move.b -0x10ff(%a1),%d0 69 | andi.b #0x0f,%d0 70 | beq.s WrongVersion 71 | 72 | * Sega Security Code (SEGA) 73 | move.l #0x53454741,0x2f00(%a1) 74 | WrongVersion: 75 | * Read from the control port to cancel any pending read/write command 76 | move.w (%a4),%d0 77 | 78 | * Configure a USER_STACK_LENGTH bytes user stack at bottom, and system stack on top of it 79 | move %sp, %usp 80 | sub #USER_STACK_LENGTH, %sp 81 | 82 | move.w %d7,(%a1) 83 | move.w %d7,(%a2) 84 | 85 | * Jump to initialisation process now... 86 | 87 | jmp _start_entry 88 | 89 | SkipSetup: 90 | jmp _reset_entry 91 | 92 | 93 | Table: 94 | dc.w 0x8000,0x3fff,0x0100 95 | dc.l 0xA00000,0xA11100,0xA11200,0xC00000,0xC00004 96 | 97 | 98 | *------------------------------------------------ 99 | * 100 | * interrupt functions 101 | * 102 | *------------------------------------------------ 103 | 104 | registersDump: 105 | move.l %d0,registerState+0 106 | move.l %d1,registerState+4 107 | move.l %d2,registerState+8 108 | move.l %d3,registerState+12 109 | move.l %d4,registerState+16 110 | move.l %d5,registerState+20 111 | move.l %d6,registerState+24 112 | move.l %d7,registerState+28 113 | move.l %a0,registerState+32 114 | move.l %a1,registerState+36 115 | move.l %a2,registerState+40 116 | move.l %a3,registerState+44 117 | move.l %a4,registerState+48 118 | move.l %a5,registerState+52 119 | move.l %a6,registerState+56 120 | move.l %a7,registerState+60 121 | rts 122 | 123 | busAddressErrorDump: 124 | move.w 4(%sp),ext1State 125 | move.l 6(%sp),addrState 126 | move.w 10(%sp),ext2State 127 | move.w 12(%sp),srState 128 | move.l 14(%sp),pcState 129 | jmp registersDump 130 | 131 | exception4WDump: 132 | move.w 4(%sp),srState 133 | move.l 6(%sp),pcState 134 | move.w 10(%sp),ext1State 135 | jmp registersDump 136 | 137 | exceptionDump: 138 | move.w 4(%sp),srState 139 | move.l 6(%sp),pcState 140 | jmp registersDump 141 | 142 | 143 | _Bus_Error: 144 | jsr busAddressErrorDump 145 | movem.l %d0-%d1/%a0-%a1,-(%sp) 146 | move.l busErrorCB, %a0 147 | jsr (%a0) 148 | movem.l (%sp)+,%d0-%d1/%a0-%a1 149 | rte 150 | 151 | _Address_Error: 152 | jsr busAddressErrorDump 153 | movem.l %d0-%d1/%a0-%a1,-(%sp) 154 | move.l addressErrorCB, %a0 155 | jsr (%a0) 156 | movem.l (%sp)+,%d0-%d1/%a0-%a1 157 | rte 158 | 159 | _Illegal_Instruction: 160 | jsr exception4WDump 161 | movem.l %d0-%d1/%a0-%a1,-(%sp) 162 | move.l illegalInstCB, %a0 163 | jsr (%a0) 164 | movem.l (%sp)+,%d0-%d1/%a0-%a1 165 | rte 166 | 167 | _Zero_Divide: 168 | jsr exceptionDump 169 | movem.l %d0-%d1/%a0-%a1,-(%sp) 170 | move.l zeroDivideCB, %a0 171 | jsr (%a0) 172 | movem.l (%sp)+,%d0-%d1/%a0-%a1 173 | rte 174 | 175 | _Chk_Instruction: 176 | jsr exception4WDump 177 | movem.l %d0-%d1/%a0-%a1,-(%sp) 178 | move.l chkInstCB, %a0 179 | jsr (%a0) 180 | movem.l (%sp)+,%d0-%d1/%a0-%a1 181 | rte 182 | 183 | _Trapv_Instruction: 184 | jsr exception4WDump 185 | movem.l %d0-%d1/%a0-%a1,-(%sp) 186 | move.l trapvInstCB, %a0 187 | jsr (%a0) 188 | movem.l (%sp)+,%d0-%d1/%a0-%a1 189 | rte 190 | 191 | _Privilege_Violation: 192 | jsr exceptionDump 193 | movem.l %d0-%d1/%a0-%a1,-(%sp) 194 | move.l privilegeViolationCB, %a0 195 | jsr (%a0) 196 | movem.l (%sp)+,%d0-%d1/%a0-%a1 197 | rte 198 | 199 | _Trace: 200 | jsr exceptionDump 201 | movem.l %d0-%d1/%a0-%a1,-(%sp) 202 | move.l traceCB, %a0 203 | jsr (%a0) 204 | movem.l (%sp)+,%d0-%d1/%a0-%a1 205 | rte 206 | 207 | _Line_1010_Emulation: 208 | _Line_1111_Emulation: 209 | jsr exceptionDump 210 | movem.l %d0-%d1/%a0-%a1,-(%sp) 211 | move.l line1x1xCB, %a0 212 | jsr (%a0) 213 | movem.l (%sp)+,%d0-%d1/%a0-%a1 214 | rte 215 | 216 | _Error_Exception: 217 | jsr exceptionDump 218 | movem.l %d0-%d1/%a0-%a1,-(%sp) 219 | move.l errorExceptionCB, %a0 220 | jsr (%a0) 221 | movem.l (%sp)+,%d0-%d1/%a0-%a1 222 | rte 223 | 224 | _INT: 225 | movem.l %d0-%d1/%a0-%a1,-(%sp) 226 | move.l intCB, %a0 227 | jsr (%a0) 228 | movem.l (%sp)+,%d0-%d1/%a0-%a1 229 | rte 230 | 231 | _EXTINT: 232 | movem.l %d0-%d1/%a0-%a1,-(%sp) 233 | move.l eintCB, %a0 234 | jsr (%a0) 235 | movem.l (%sp)+,%d0-%d1/%a0-%a1 236 | rte 237 | 238 | _VINT: 239 | btst #5, (%sp) /* Skip context switch if not in user task */ 240 | bne.s no_user_task 241 | 242 | tst.w task_lock 243 | bne.s 1f 244 | move.w #0, -(%sp) /* TSK_superPend() will return 0 */ 245 | bra.s unlock /* If lock == 0, supervisor task is not locked */ 246 | 247 | 1: 248 | bcs.s no_user_task /* If lock < 0, super is locked with infinite wait */ 249 | subq.w #1, task_lock /* Locked with wait, subtract 1 to the frame count */ 250 | bne.s no_user_task /* And do not unlock if we did not reach 0 */ 251 | move.w #1, -(%sp) /* TSK_superPend() will return 1 */ 252 | 253 | unlock: 254 | /* Save bg task registers (excepting a7, that is stored in usp) */ 255 | move.l %a0, task_regs 256 | lea (task_regs + UTSK_REGS_LEN), %a0 257 | movem.l %d0-%d7/%a1-%a6, -(%a0) 258 | 259 | move.w (%sp)+, %d0 /* Load return value previously pushed to stack */ 260 | 261 | move.w (%sp)+, task_sr /* Pop user task sr and pc, and save them, */ 262 | move.l (%sp)+, task_pc /* so they can be restored later. */ 263 | movem.l (%sp)+, %d2-%d7/%a2-%a6 /* Restore non clobberable registers */ 264 | 265 | no_user_task: 266 | /* At this point, we always have in the stack the SR and PC of the task */ 267 | /* we want to jump after processing the interrupt, that might be the */ 268 | /* point where we came from (if there is no context switch) or the */ 269 | /* supervisor task (if we unlocked it). */ 270 | 271 | movem.l %d0-%d1/%a0-%a1,-(%sp) 272 | ori.w #0x0001, intTrace /* in V-Int */ 273 | addq.l #1, vtimer /* increment frame counter (more a vint counter) */ 274 | btst #3, VBlankProcess+1 /* PROCESS_XGM_TASK ? (use VBlankProcess+1 as btst is a byte operation) */ 275 | beq.s no_xgm_task 276 | 277 | jsr XGM_doVBlankProcess /* do XGM vblank task */ 278 | 279 | no_xgm_task: 280 | btst #1, VBlankProcess+1 /* PROCESS_BITMAP_TASK ? (use VBlankProcess+1 as btst is a byte operation) */ 281 | beq.s no_bmp_task 282 | 283 | jsr BMP_doVBlankProcess /* do BMP vblank task */ 284 | 285 | no_bmp_task: 286 | move.l vintCB, %a0 /* load user callback */ 287 | jsr (%a0) /* call user callback */ 288 | andi.w #0xFFFE, intTrace /* out V-Int */ 289 | movem.l (%sp)+,%d0-%d1/%a0-%a1 290 | rte 291 | 292 | *------------------------------------------------ 293 | * 294 | * Copyright (c) 1988 by Sozobon, Limited. Author: Johann Ruegg 295 | * 296 | * Permission is granted to anyone to use this software for any purpose 297 | * on any computer system, and to redistribute it freely, with the 298 | * following restrictions: 299 | * 1) No charge may be made other than reasonable charges for reproduction. 300 | * 2) Modified versions must be clearly marked as such. 301 | * 3) The authors are not responsible for any harmful consequences 302 | * of using this software, even if they result from defects in it. 303 | * 304 | *------------------------------------------------ 305 | 306 | ldiv: 307 | move.l 4(%a7),%d0 308 | bpl ld1 309 | neg.l %d0 310 | ld1: 311 | move.l 8(%a7),%d1 312 | bpl ld2 313 | neg.l %d1 314 | eor.b #0x80,4(%a7) 315 | ld2: 316 | bsr i_ldiv /* d0 = d0/d1 */ 317 | tst.b 4(%a7) 318 | bpl ld3 319 | neg.l %d0 320 | ld3: 321 | rts 322 | 323 | lmul: 324 | move.l 4(%a7),%d0 325 | bpl lm1 326 | neg.l %d0 327 | lm1: 328 | move.l 8(%a7),%d1 329 | bpl lm2 330 | neg.l %d1 331 | eor.b #0x80,4(%a7) 332 | lm2: 333 | bsr i_lmul /* d0 = d0*d1 */ 334 | tst.b 4(%a7) 335 | bpl lm3 336 | neg.l %d0 337 | lm3: 338 | rts 339 | 340 | lrem: 341 | move.l 4(%a7),%d0 342 | bpl lr1 343 | neg.l %d0 344 | lr1: 345 | move.l 8(%a7),%d1 346 | bpl lr2 347 | neg.l %d1 348 | lr2: 349 | bsr i_ldiv /* d1 = d0%d1 */ 350 | move.l %d1,%d0 351 | tst.b 4(%a7) 352 | bpl lr3 353 | neg.l %d0 354 | lr3: 355 | rts 356 | 357 | ldivu: 358 | move.l 4(%a7),%d0 359 | move.l 8(%a7),%d1 360 | bsr i_ldiv 361 | rts 362 | 363 | lmulu: 364 | move.l 4(%a7),%d0 365 | move.l 8(%a7),%d1 366 | bsr i_lmul 367 | rts 368 | 369 | lremu: 370 | move.l 4(%a7),%d0 371 | move.l 8(%a7),%d1 372 | bsr i_ldiv 373 | move.l %d1,%d0 374 | rts 375 | * 376 | * A in d0, B in d1, return A*B in d0 377 | * 378 | i_lmul: 379 | move.l %d3,%a2 /* save d3 */ 380 | move.w %d1,%d2 381 | mulu %d0,%d2 /* d2 = Al * Bl */ 382 | 383 | move.l %d1,%d3 384 | swap %d3 385 | mulu %d0,%d3 /* d3 = Al * Bh */ 386 | 387 | swap %d0 388 | mulu %d1,%d0 /* d0 = Ah * Bl */ 389 | 390 | add.l %d3,%d0 /* d0 = (Ah*Bl + Al*Bh) */ 391 | swap %d0 392 | clr.w %d0 /* d0 = (Ah*Bl + Al*Bh) << 16 */ 393 | 394 | add.l %d2,%d0 /* d0 = A*B */ 395 | move.l %a2,%d3 /* restore d3 */ 396 | rts 397 | * 398 | *A in d0, B in d1, return A/B in d0, A%B in d1 399 | * 400 | i_ldiv: 401 | tst.l %d1 402 | bne nz1 403 | 404 | * divide by zero 405 | * divu #0,%d0 /* cause trap */ 406 | move.l #0x80000000,%d0 407 | move.l %d0,%d1 408 | rts 409 | nz1: 410 | move.l %d3,%a2 /* save d3 */ 411 | cmp.l %d1,%d0 412 | bhi norm 413 | beq is1 414 | * AB and B is not 0 426 | norm: 427 | cmp.l #1,%d1 428 | bne not1 429 | * B==1, so ret A, rem 0 430 | clr.l %d1 431 | move.l %a2,%d3 /* restore d3 */ 432 | rts 433 | * check for A short (implies B short also) 434 | not1: 435 | cmp.l #0xffff,%d0 436 | bhi slow 437 | * A short and B short -- use 'divu' 438 | divu %d1,%d0 /* d0 = REM:ANS */ 439 | swap %d0 /* d0 = ANS:REM */ 440 | clr.l %d1 441 | move.w %d0,%d1 /* d1 = REM */ 442 | clr.w %d0 443 | swap %d0 444 | move.l %a2,%d3 /* restore d3 */ 445 | rts 446 | * check for B short 447 | slow: 448 | cmp.l #0xffff,%d1 449 | bhi slower 450 | * A long and B short -- use special stuff from gnu 451 | move.l %d0,%d2 452 | clr.w %d2 453 | swap %d2 454 | divu %d1,%d2 /* d2 = REM:ANS of Ahi/B */ 455 | clr.l %d3 456 | move.w %d2,%d3 /* d3 = Ahi/B */ 457 | swap %d3 458 | 459 | move.w %d0,%d2 /* d2 = REM << 16 + Alo */ 460 | divu %d1,%d2 /* d2 = REM:ANS of stuff/B */ 461 | 462 | move.l %d2,%d1 463 | clr.w %d1 464 | swap %d1 /* d1 = REM */ 465 | 466 | clr.l %d0 467 | move.w %d2,%d0 468 | add.l %d3,%d0 /* d0 = ANS */ 469 | move.l %a2,%d3 /* restore d3 */ 470 | rts 471 | * A>B, B > 1 472 | slower: 473 | move.l #1,%d2 474 | clr.l %d3 475 | moreadj: 476 | cmp.l %d0,%d1 477 | bhs adj 478 | add.l %d2,%d2 479 | add.l %d1,%d1 480 | bpl moreadj 481 | * we shifted B until its >A or sign bit set 482 | * we shifted #1 (d2) along with it 483 | adj: 484 | cmp.l %d0,%d1 485 | bhi ltuns 486 | or.l %d2,%d3 487 | sub.l %d1,%d0 488 | ltuns: 489 | lsr.l #1,%d1 490 | lsr.l #1,%d2 491 | bne adj 492 | * d3=answer, d0=rem 493 | move.l %d0,%d1 494 | move.l %d3,%d0 495 | move.l %a2,%d3 /* restore d3 */ 496 | rts 497 | -------------------------------------------------------------------------------- /lou/04_colors/src/main.c: -------------------------------------------------------------------------------- 1 | 2 | // SGDK 3 | #include 4 | #include "resources.h" 5 | 6 | #define VERTICAL_REZ 224 7 | void LineDark(); 8 | void LineLight(); 9 | // image is 512x224. Screen is 320, we want to move halfway 10 | // 512/2 - 320 /2 11 | #define SCROLL_CENTER -96 12 | 13 | // Keep track of the current line during Horizontal Interrupts 14 | u16 lineDisplay = 0; 15 | 16 | // Horizontal Scrolling values 17 | s16 HscrollA[VERTICAL_REZ]; 18 | s16 HscrollB[VERTICAL_REZ]; 19 | // Vertical Scrolling values ( to simulate hills ) 20 | s8 VscrollA[VERTICAL_REZ]; 21 | 22 | // color banding array 23 | u8 colors[VERTICAL_REZ]; 24 | u8 line_color = 0; // 0 -uninit, 1-light, 2-dark 25 | u8 side_color = 0; 26 | u8 grass_color = 0; // 0 -uninit, 1-light, 2-dark 27 | 28 | // Zmap for tracking segment position 29 | #define ZMAP_LENGTH 110 30 | fix16 zmap[ZMAP_LENGTH]; 31 | 32 | // Road data 33 | #define ROAD_SEGMENTS_LENGTH 15 34 | typedef struct 35 | { 36 | fix16 dx; // rate of change for the road. 37 | fix16 bgdx; // rate of change for background. ( ignore for now ) 38 | fix32 dy; // rate of change for drawing road in y dir? 39 | } ROAD_SEGMENT; 40 | 41 | const ROAD_SEGMENT segments[ROAD_SEGMENTS_LENGTH] = { 42 | {FIX16(0), FIX16(0), FIX32(-0.001)}, 43 | {FIX16(-0.02), FIX16(-0.48), FIX32(0.002)}, 44 | {FIX16(-0.04), FIX16(-1.28), FIX32(-0.001)}, 45 | {FIX16(-0.02), FIX16(-0.48), FIX32(0.0)}, 46 | {FIX16(0), FIX16(0), FIX32(0.001)}, 47 | {FIX16(0), FIX16(0), FIX32(0.0025)}, 48 | {FIX16(0.03), FIX16(0.64), FIX32(-0.002)}, 49 | {FIX16(-0.03), FIX16(-0.64), FIX32(0)}, 50 | {FIX16(0), FIX16(0), FIX32(0.001)}, 51 | {FIX16(0), FIX16(0), FIX32(-0.0025)}, 52 | {FIX16(0), FIX16(0), FIX32(0.002)}, 53 | {FIX16(0.0), FIX16(0.0), FIX32(0)}, 54 | {FIX16(0), FIX16(0), FIX32(0.0)}, 55 | {FIX16(-0.02), FIX16(-0.48), FIX32(0)}, 56 | {FIX16(0.02), FIX16(0.48), FIX32(0)}}; 57 | 58 | u16 bottom_segments_index = 0; 59 | u16 segments_index = 0; 60 | 61 | // Speed the 'vehicle' moves through teh road 62 | fix16 speed = FIX16(0.00); 63 | 64 | // position variables. 65 | fix16 segment_position = FIX16(0); // keep track of the segment position on screen 66 | fix16 background_position = FIX16(SCROLL_CENTER); // handle background X position 67 | s16 horizon_line = 223; // keep track of where the horizon is 68 | 69 | void HIntHandler() 70 | { 71 | // set vertical scroll based on hill calculations 72 | VDP_setVerticalScroll(BG_A, VscrollA[lineDisplay]); 73 | 74 | /* 75 | // set color. No Worky! 76 | //asm("move.l #0xC0020000, 0xC00004"); 77 | if (lineDisplay % 2) 78 | { 79 | asm volatile ( "move.l #0xC0020000, 0xC00004\n\t"); 80 | if (colors[lineDisplay] & 1) 81 | { 82 | asm volatile ( 83 | "\tmove.w #0x666, 0xC00000.l\n" 84 | ); 85 | } 86 | else 87 | { 88 | asm volatile ( 89 | "move.w #0xFFF, 0xC00000 \n\t" 90 | ); 91 | } 92 | } 93 | else 94 | { 95 | asm("move.l #0xC0060000, 0xC00004"); 96 | if (colors[lineDisplay] & 1) 97 | { 98 | asm("move.w #0x060, 0xC00000"); 99 | } 100 | else 101 | { 102 | asm("move.w #0x0C0, 0xC00000"); 103 | } 104 | } 105 | */ 106 | 107 | // Move to the next line for the next horizontal interrupt. 108 | lineDisplay++; 109 | } 110 | 111 | void VIntHandler() 112 | { 113 | // Make sure HInt always starts with line 0 114 | lineDisplay = 0; 115 | } 116 | 117 | // My interpretation of the pseudo-code in 118 | // http://www.extentofthejam.com/pseudo/#curves 119 | // and the hills described in 120 | // http://www.extentofthejam.com/pseudo/#hills 121 | void update() 122 | { 123 | fix16 current_x = FIX16(0); // Lou's pseudo 3d page says to use Half of the screen width, 124 | // but I've defined SCROLL_CENTER to handle this 125 | 126 | fix16 dx = FIX16(0); // Curve Amount, constant per segment. 127 | fix16 ddx = FIX16(0); // Curve Amount, changes per line 128 | 129 | fix32 dy = FIX32(0); // Slope Amount 130 | fix32 ddy = FIX32(0); // Slope Amount, changes per line 131 | 132 | fix32 current_drawing_pos = FIX32(223); // The drawing loop would start at the beginning of the Z-map (nearest). Basically the bottom of the screen 133 | horizon_line = 223; // keep track of where the horizon is. I"m starting at the bottom and will update as the rode gets computed 134 | 135 | // for each line of the screen from the bottom to the top 136 | //for (y = 0; y < ZMAP_LENGTH; ++y) // no longer works because up-hill/down-hill won't be exaclty 1. ++y isn't valid 137 | 138 | // HILL: draw loop starts at the beginning of the Z map ( nearest = 0 ) and stops at teh end (farthest = ZMAP_LENGTH ) 139 | // * flat roads decrements the drawing position each line by 1 140 | // * if we decrement the drawing position by 2 (doubling lines) the road gets drawn twice as high. 141 | // * by varying the amount we decrement the drawing position we can draw a hill that starts flat and curves upwards 142 | // * if the next drawing position is more than one line from the current drawing position, the currnet ZMap line is repeated until 143 | // we get there, producing a scalien effect. 144 | for (u16 bgY = 223; bgY > 113; bgY--) 145 | { 146 | 147 | ////////////////////////////////////////////////////////////////////// 148 | // Road Bending 149 | fix16 z = zmap[223 - bgY]; // zmap[0] is closest 150 | // if line of screen's Z map position is below segment position 151 | if (z < segment_position) 152 | { 153 | // dx = bottom_segment.dx 154 | dx = segments[bottom_segments_index].dx; 155 | dy = segments[bottom_segments_index].dy; 156 | } 157 | else // if line of Screen's Z map position is above segment position. 158 | { 159 | // dx = segment.dx 160 | dx = segments[segments_index].dx; 161 | dy = segments[segments_index].dy; 162 | } 163 | 164 | // ddx += dx 165 | ddx = fix16Add(ddx, dx); 166 | // current_x += ddx 167 | current_x = fix16Add(current_x, ddx); 168 | 169 | 170 | ////////////////////////////////////////////////////////////////////// 171 | // Coloring 172 | // For each Z, make one of the bits represent the shade 173 | // of the road (dark or light). Then, just draw the 174 | // appropriate road pattern or colors for that bit 175 | u8 zmapval = (u8)fix16ToInt(fix16Sub(segment_position, z)); 176 | 177 | ddy = fix32Add(dy, ddy); 178 | s16 cdp = fix32ToInt(current_drawing_pos); // current vertical drawing position 179 | fix32 delta_drawing_pos = fix32Add(FIX32(1), ddy); // increment drawing position 180 | fix32 next_drawing_pos = fix32Sub(current_drawing_pos, delta_drawing_pos); 181 | s16 ndp = fix32ToInt(next_drawing_pos); // figure out next drawing position 182 | // repeat line if theres a gap greater than 1 183 | for (; cdp > ndp; --cdp) // 184 | { 185 | if (cdp <= horizon_line) // if current drawing position is above the horizon 186 | { 187 | HscrollA[cdp] = SCROLL_CENTER + fix16ToInt(current_x); // this_line.x = current x | using horizontal scrolling to fake curves 188 | VscrollA[cdp] = bgY - cdp; // set the vertical scroll amount for the current drawing position 189 | horizon_line = cdp; // update horizon line 190 | 191 | // coloring 192 | colors[cdp] = zmapval & 1; 193 | } 194 | } 195 | 196 | current_drawing_pos = next_drawing_pos; // move to next drawing position 197 | } 198 | 199 | // hide anything above the horizon line 200 | for (s16 h = horizon_line; h >= 0; --h) 201 | { 202 | VscrollA[h] = -h; 203 | } 204 | 205 | 206 | // scroll the background 207 | background_position = fix16Sub(background_position, segments[bottom_segments_index].bgdx); 208 | for (u16 y = 0; y < 160; ++y) 209 | { 210 | HscrollB[y] = fix16ToInt(background_position); 211 | } 212 | 213 | 214 | // Move segments 215 | segment_position = fix16Add(segment_position, speed); 216 | if (fix16ToInt(segment_position) < 0) 217 | { 218 | // bottom_segment = segment 219 | bottom_segments_index = segments_index; 220 | // segment.position = zmap.length - 1 221 | segment_position = zmap[ZMAP_LENGTH - 1]; // Send segment to farthest visible distance 222 | 223 | // get next segment from road 224 | segments_index++; // segment_index is used to get segment.dx 225 | if (segments_index == ROAD_SEGMENTS_LENGTH) 226 | { 227 | segments_index -= ROAD_SEGMENTS_LENGTH; // go back to the start 228 | } 229 | } 230 | } 231 | 232 | int main(bool hard) 233 | { 234 | ////////////////////////////////////////////////////////////// 235 | // http://www.extentofthejam.com/pseudo/ 236 | // Z = Y_world / (Y_screen - (height_screen / 2)) 237 | for (u16 i = 0; i < ZMAP_LENGTH; ++i) 238 | { 239 | zmap[i] = fix16Div(FIX16(-75), fix16Sub(FIX16(i), FIX16(112))); 240 | KLog_f1("FIX16(", zmap[i]); 241 | } 242 | 243 | ////////////////////////////////////////////////////////////// 244 | // VDP basic setup 245 | VDP_setBackgroundColor(16); 246 | VDP_setScreenWidth320(); 247 | VDP_setScrollingMode(HSCROLL_LINE, VSCROLL_PLANE); 248 | for (int i = 0; i < VERTICAL_REZ; i++) 249 | { 250 | HscrollA[i] = SCROLL_CENTER; 251 | HscrollB[i] = SCROLL_CENTER; 252 | VscrollA[i] = 0; 253 | } 254 | 255 | ////////////////////////////////////////////////////////////// 256 | // Setup scroll panes 257 | PAL_setPalette(PAL0, road_pal.data, CPU); 258 | int ind = TILE_USER_INDEX; 259 | VDP_drawImageEx(BG_A, &road, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, ind), 0, 0, FALSE, TRUE); 260 | ind += road.tileset->numTile; 261 | PAL_setPalette(PAL2, background_pal.data, CPU); 262 | VDP_drawImageEx(BG_B, &background, TILE_ATTR_FULL(PAL2, FALSE, FALSE, FALSE, ind), 0, 0, FALSE, TRUE); 263 | ind += background.tileset->numTile; 264 | 265 | VDP_setVerticalScroll(BG_B, 0); 266 | 267 | ////////////////////////////////////////////////////////////// 268 | // init segments 269 | bottom_segments_index = 0; 270 | segments_index = 1; 271 | segment_position = zmap[ZMAP_LENGTH - 1]; // put it at the farthest away point 272 | 273 | // set spped through z 274 | speed = FIX16(-0.20); 275 | 276 | ////////////////////////////////////////////////////////////// 277 | // Setup interrupt handlers 278 | SYS_disableInts(); 279 | { 280 | VDP_setHIntCounter(0); 281 | VDP_setHInterrupt(1); 282 | 283 | //SYS_setHIntCallback(HIntHandler); 284 | //SYS_setVIntCallback(VIntHandler); 285 | } 286 | SYS_enableInts(); 287 | 288 | // Main loop 289 | //fix16 colorCycle = FIX16(0.0); 290 | u16 lastSet = -1; 291 | while (TRUE) 292 | { 293 | // update 294 | update(); 295 | 296 | /* sort of works 297 | // cycle colors 298 | colorCycle = fix16Add( colorCycle, FIX16(0.5)); 299 | u16 temp = fix16ToInt(colorCycle); 300 | if (temp != lastSet) 301 | { 302 | lastSet = temp; 303 | if (temp == 0) 304 | { 305 | PAL_setColor(1, RGB24_TO_VDPCOLOR(0x666666)); 306 | PAL_setColor(2, RGB24_TO_VDPCOLOR(0xFFFFFF)); 307 | PAL_setColor(3, RGB24_TO_VDPCOLOR(0x666666)); 308 | 309 | PAL_setColor(5, RGB24_TO_VDPCOLOR(0x008800)); 310 | PAL_setColor(6, RGB24_TO_VDPCOLOR(0x00CC00)); 311 | PAL_setColor(7, RGB24_TO_VDPCOLOR(0x008800)); 312 | } 313 | else if (temp == 1) 314 | { 315 | PAL_setColor(1, RGB24_TO_VDPCOLOR(0xFFFFFF)); 316 | PAL_setColor(2, RGB24_TO_VDPCOLOR(0x666666)); 317 | PAL_setColor(3, RGB24_TO_VDPCOLOR(0x666666)); 318 | 319 | PAL_setColor(5, RGB24_TO_VDPCOLOR(0x00CC00)); 320 | PAL_setColor(6, RGB24_TO_VDPCOLOR(0x008800)); 321 | PAL_setColor(7, RGB24_TO_VDPCOLOR(0x008800)); 322 | } 323 | else 324 | { 325 | PAL_setColor(1, RGB24_TO_VDPCOLOR(0x666666)); 326 | PAL_setColor(2, RGB24_TO_VDPCOLOR(0x666666)); 327 | PAL_setColor(3, RGB24_TO_VDPCOLOR(0xFFFFFF)); 328 | 329 | PAL_setColor(5, RGB24_TO_VDPCOLOR(0x008800)); 330 | PAL_setColor(6, RGB24_TO_VDPCOLOR(0x008800)); 331 | PAL_setColor(7, RGB24_TO_VDPCOLOR(0x00CC00)); 332 | } 333 | if (temp > 3) 334 | { 335 | colorCycle = FIX16(0); 336 | } 337 | } 338 | */ 339 | 340 | // curve the road with horizontal scrolling 341 | VDP_setHorizontalScrollLine(BG_A, 0, HscrollA, VERTICAL_REZ, DMA_QUEUE); 342 | // move the background 343 | VDP_setHorizontalScrollLine(BG_B, 0, HscrollB, 160, DMA_QUEUE); 344 | 345 | SYS_doVBlankProcess(); 346 | } 347 | } 348 | -------------------------------------------------------------------------------- /lou/05_sprites/res/background/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/05_sprites/res/background/background.png -------------------------------------------------------------------------------- /lou/05_sprites/res/background/road.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/05_sprites/res/background/road.png -------------------------------------------------------------------------------- /lou/05_sprites/res/resources.res: -------------------------------------------------------------------------------- 1 | IMAGE road "background/road.png" BEST 2 | IMAGE background "background/background.png" BEST 3 | 4 | SPRITE biker "sprites/pseudobiker.png" 6 9 NONE 2 5 | SPRITE car "sprites/car.png" 11 7 NONE 0 6 | SPRITE tree "sprites/tree.png" 7 10 NONE 0 7 | 8 | PALETTE road_pal "background/road.png" 9 | PALETTE background_pal "background/background.png" 10 | PALETTE car_pal "sprites/car.png" 11 | PALETTE tree_pal "sprites/tree.png" -------------------------------------------------------------------------------- /lou/05_sprites/res/sprites/car.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/05_sprites/res/sprites/car.png -------------------------------------------------------------------------------- /lou/05_sprites/res/sprites/pseudobiker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/05_sprites/res/sprites/pseudobiker.png -------------------------------------------------------------------------------- /lou/05_sprites/res/sprites/tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/05_sprites/res/sprites/tree.png -------------------------------------------------------------------------------- /lou/05_sprites/src/HInter.s: -------------------------------------------------------------------------------- 1 | 2 | 3 | *------------------------------------------------ 4 | * LABELS 5 | *------------------------------------------------ 6 | .set VDP_CTRL, 0xC00004 7 | .set VDP_DATA, 0xC00000 8 | 9 | .set PAL0_COLOR0, 0xC0000000 10 | .set PAL0_COLOR1, 0xC0020000 11 | .set PAL0_COLOR2, 0xC0040000 12 | .set PAL0_COLOR3, 0xC0060000 13 | .set PAL0_COLOR4, 0xC0080000 14 | 15 | .set VSCROLL_A, 0x40000010 16 | .set VSCROLL_B, 0x40020010 17 | .set HSCROLL_A, 0x7C000002 18 | .set HSCROLL_B, 0x7C020002 19 | 20 | .set LINE_DARK_COLOR, 0x666 21 | .set LINE_LIGHT_COLOR, 0xFFF 22 | 23 | .set GRASS_DARK_COLOR, 0x086 24 | .set GRASS_LIGHT_COLOR, 0x0A0 25 | 26 | *------------------------------------------------ 27 | * Functions 28 | *------------------------------------------------ 29 | .globl HInter 30 | 31 | HInter: 32 | move.l %d0, -(%sp) /* push device register 0 onto the stack */ 33 | 34 | clr.l %d0 /* Clear D0 and read the current scanline to it */ 35 | move.b (0xC00008), %d0 36 | 37 | cmp.w #100, %d0 38 | jgt WORK /* do if current line is below horizon */ 39 | 40 | move.l (%sp)+, %d0 /* restore data register 0 */ 41 | rte 42 | 43 | WORK: 44 | move.l %a0, -(%sp) /* push address register 0 onto the stack */ 45 | 46 | lea VscrollA, %a0 /* get 'vscrollA' array effective address to A0 */ 47 | move.l #VSCROLL_A, 0xC00004 /* Vertical scrolling */ 48 | move.b (%a0, %d0.w), 0xC00000 49 | 50 | lea colors, %a0 /* get 'colors' array effective address to A0 */ 51 | move.b (%a0,%d0.w),%d0 /* check shading for current scanline */ 52 | move.l (%sp)+, %a0 /* restore address register 0 */ 53 | tst.b %d0 /* check shading value */ 54 | jeq LIGHT /* jump to light coloring */ 55 | 56 | DARK: 57 | LINE_DARK: 58 | move.b #2, %d0 59 | cmp.b line_color, %d0 60 | jne SET_LINE_DARK /* if not dark, set the color */ 61 | jmp GRASS_DARK 62 | move.l (%sp)+, %d0 /* restore data register 0 */ 63 | rte 64 | 65 | SET_LINE_DARK: 66 | 67 | move.b %d0, line_color 68 | move.l #PAL0_COLOR1, VDP_CTRL /* Tell VDP we want to change color 1 */ 69 | clr.w %d0 70 | 71 | LINE_DELAY1: 72 | move.w (VDP_CTRL), %d0 /* wait before we set color (to minimize dots) */ 73 | btst.b #0x02, %d0 74 | beq LINE_DELAY1 75 | 76 | move.w #LINE_DARK_COLOR, VDP_DATA /* set the color */ 77 | 78 | move.l (%sp)+, %d0 /* restore data register 0 */ 79 | rte 80 | 81 | GRASS_DARK: 82 | move.b #2, %d0 83 | cmp.b grass_color, %d0 84 | jne SET_GRASS_DARK /* if not dark, set the color */ 85 | move.l (%sp)+, %d0 /* restore data register 0 */ 86 | rte 87 | 88 | SET_GRASS_DARK: 89 | 90 | move.b %d0, grass_color 91 | move.l #PAL0_COLOR2, VDP_CTRL /* Tell VDP we want to change color 1 */ 92 | clr.w %d0 93 | 94 | GRASS_DELAY1: 95 | move.w (VDP_CTRL), %d0 /* wait before we set color (to minimize dots) */ 96 | btst.b #0x02, %d0 97 | beq GRASS_DELAY1 98 | 99 | move.w #GRASS_DARK_COLOR, VDP_DATA /* set the color */ 100 | 101 | move.l (%sp)+, %d0 /* restore data register 0 */ 102 | rte 103 | 104 | 105 | LIGHT: 106 | 107 | LINE_LIGHT: 108 | move.b #1, %d0 109 | cmp.b line_color, %d0 110 | jne SET_LINE_LIGHT /* if not dark, set the color */ 111 | JMP GRASS_LIGHT 112 | move.l (%sp)+, %d0 /* restore data register 0 */ 113 | rte 114 | 115 | SET_LINE_LIGHT: 116 | move.b %d0, line_color 117 | move.l #PAL0_COLOR1, VDP_CTRL /* Tell VDP we want to change color 1 */ 118 | clr.w %d0 119 | 120 | LINE_DELAY2: 121 | move.w (VDP_CTRL), %d0 /* wait before setting color */ 122 | btst.b #0x02, %d0 123 | beq LINE_DELAY2 124 | 125 | move.w #LINE_LIGHT_COLOR, VDP_DATA /* set the color */ 126 | 127 | move.l (%sp)+, %d0 /* restore data register 0 */ 128 | rte 129 | 130 | GRASS_LIGHT: 131 | move.b #1, %d0 132 | cmp.b grass_color, %d0 133 | jne SET_GRASS_LIGHT /* if not dark, set the color */ 134 | move.l (%sp)+, %d0 /* restore data register 0 */ 135 | rte 136 | 137 | SET_GRASS_LIGHT: 138 | 139 | move.b %d0, grass_color 140 | move.l #PAL0_COLOR2, VDP_CTRL /* Tell VDP we want to change color 1 */ 141 | clr.w %d0 142 | 143 | GRASS_DELAY2: 144 | move.w (VDP_CTRL), %d0 /* wait before we set color (to minimize dots) */ 145 | btst.b #0x02, %d0 146 | beq GRASS_DELAY2 147 | 148 | move.w #GRASS_LIGHT_COLOR, VDP_DATA /* set the color */ 149 | 150 | move.l (%sp)+, %d0 /* restore data register 0 */ 151 | rte 152 | 153 | -------------------------------------------------------------------------------- /lou/05_sprites/src/boot/sega.s: -------------------------------------------------------------------------------- 1 | #include "task_cst.h" 2 | 3 | .section .text.keepboot 4 | 5 | *------------------------------------------------------- 6 | * 7 | * Sega startup code for the GNU Assembler 8 | * Translated from: 9 | * Sega startup code for the Sozobon C compiler 10 | * Written by Paul W. Lee 11 | * Modified by Charles Coty 12 | * Modified by Stephane Dallongeville 13 | * 14 | *------------------------------------------------------- 15 | 16 | .globl rom_header 17 | 18 | .org 0x00000000 19 | 20 | _Start_Of_Rom: 21 | _Vecteurs_68K: 22 | dc.l __stack /* Stack address */ 23 | dc.l _Entry_Point /* Program start address */ 24 | dc.l _Bus_Error 25 | dc.l _Address_Error 26 | dc.l _Illegal_Instruction 27 | dc.l _Zero_Divide 28 | dc.l _Chk_Instruction 29 | dc.l _Trapv_Instruction 30 | dc.l _Privilege_Violation 31 | dc.l _Trace 32 | dc.l _Line_1010_Emulation 33 | dc.l _Line_1111_Emulation 34 | dc.l _Error_Exception, _Error_Exception, _Error_Exception, _Error_Exception 35 | dc.l _Error_Exception, _Error_Exception, _Error_Exception, _Error_Exception 36 | dc.l _Error_Exception, _Error_Exception, _Error_Exception, _Error_Exception 37 | dc.l _Error_Exception 38 | dc.l _INT 39 | dc.l _EXTINT 40 | dc.l _INT 41 | dc.l HInter 42 | dc.l _INT 43 | dc.l _VINT 44 | dc.l _INT 45 | dc.l _trap_0 /* Resume supervisor task */ 46 | dc.l _INT,_INT,_INT,_INT,_INT,_INT,_INT 47 | dc.l _INT,_INT,_INT,_INT,_INT,_INT,_INT,_INT 48 | dc.l _INT,_INT,_INT,_INT,_INT,_INT,_INT,_INT 49 | dc.l _INT,_INT,_INT,_INT,_INT,_INT,_INT,_INT 50 | 51 | rom_header: 52 | .incbin "out/rom_head.bin", 0, 0x100 53 | 54 | _Entry_Point: 55 | move #0x2700,%sr 56 | tst.l 0xa10008 57 | bne.s SkipJoyDetect 58 | 59 | tst.w 0xa1000c 60 | 61 | SkipJoyDetect: 62 | bne.s SkipSetup 63 | 64 | lea Table,%a5 65 | movem.w (%a5)+,%d5-%d7 66 | movem.l (%a5)+,%a0-%a4 67 | * Check Version Number 68 | move.b -0x10ff(%a1),%d0 69 | andi.b #0x0f,%d0 70 | beq.s WrongVersion 71 | 72 | * Sega Security Code (SEGA) 73 | move.l #0x53454741,0x2f00(%a1) 74 | WrongVersion: 75 | * Read from the control port to cancel any pending read/write command 76 | move.w (%a4),%d0 77 | 78 | * Configure a USER_STACK_LENGTH bytes user stack at bottom, and system stack on top of it 79 | move %sp, %usp 80 | sub #USER_STACK_LENGTH, %sp 81 | 82 | move.w %d7,(%a1) 83 | move.w %d7,(%a2) 84 | 85 | * Jump to initialisation process now... 86 | 87 | jmp _start_entry 88 | 89 | SkipSetup: 90 | jmp _reset_entry 91 | 92 | 93 | Table: 94 | dc.w 0x8000,0x3fff,0x0100 95 | dc.l 0xA00000,0xA11100,0xA11200,0xC00000,0xC00004 96 | 97 | 98 | *------------------------------------------------ 99 | * 100 | * interrupt functions 101 | * 102 | *------------------------------------------------ 103 | 104 | registersDump: 105 | move.l %d0,registerState+0 106 | move.l %d1,registerState+4 107 | move.l %d2,registerState+8 108 | move.l %d3,registerState+12 109 | move.l %d4,registerState+16 110 | move.l %d5,registerState+20 111 | move.l %d6,registerState+24 112 | move.l %d7,registerState+28 113 | move.l %a0,registerState+32 114 | move.l %a1,registerState+36 115 | move.l %a2,registerState+40 116 | move.l %a3,registerState+44 117 | move.l %a4,registerState+48 118 | move.l %a5,registerState+52 119 | move.l %a6,registerState+56 120 | move.l %a7,registerState+60 121 | rts 122 | 123 | busAddressErrorDump: 124 | move.w 4(%sp),ext1State 125 | move.l 6(%sp),addrState 126 | move.w 10(%sp),ext2State 127 | move.w 12(%sp),srState 128 | move.l 14(%sp),pcState 129 | jmp registersDump 130 | 131 | exception4WDump: 132 | move.w 4(%sp),srState 133 | move.l 6(%sp),pcState 134 | move.w 10(%sp),ext1State 135 | jmp registersDump 136 | 137 | exceptionDump: 138 | move.w 4(%sp),srState 139 | move.l 6(%sp),pcState 140 | jmp registersDump 141 | 142 | 143 | _Bus_Error: 144 | jsr busAddressErrorDump 145 | movem.l %d0-%d1/%a0-%a1,-(%sp) 146 | move.l busErrorCB, %a0 147 | jsr (%a0) 148 | movem.l (%sp)+,%d0-%d1/%a0-%a1 149 | rte 150 | 151 | _Address_Error: 152 | jsr busAddressErrorDump 153 | movem.l %d0-%d1/%a0-%a1,-(%sp) 154 | move.l addressErrorCB, %a0 155 | jsr (%a0) 156 | movem.l (%sp)+,%d0-%d1/%a0-%a1 157 | rte 158 | 159 | _Illegal_Instruction: 160 | jsr exception4WDump 161 | movem.l %d0-%d1/%a0-%a1,-(%sp) 162 | move.l illegalInstCB, %a0 163 | jsr (%a0) 164 | movem.l (%sp)+,%d0-%d1/%a0-%a1 165 | rte 166 | 167 | _Zero_Divide: 168 | jsr exceptionDump 169 | movem.l %d0-%d1/%a0-%a1,-(%sp) 170 | move.l zeroDivideCB, %a0 171 | jsr (%a0) 172 | movem.l (%sp)+,%d0-%d1/%a0-%a1 173 | rte 174 | 175 | _Chk_Instruction: 176 | jsr exception4WDump 177 | movem.l %d0-%d1/%a0-%a1,-(%sp) 178 | move.l chkInstCB, %a0 179 | jsr (%a0) 180 | movem.l (%sp)+,%d0-%d1/%a0-%a1 181 | rte 182 | 183 | _Trapv_Instruction: 184 | jsr exception4WDump 185 | movem.l %d0-%d1/%a0-%a1,-(%sp) 186 | move.l trapvInstCB, %a0 187 | jsr (%a0) 188 | movem.l (%sp)+,%d0-%d1/%a0-%a1 189 | rte 190 | 191 | _Privilege_Violation: 192 | jsr exceptionDump 193 | movem.l %d0-%d1/%a0-%a1,-(%sp) 194 | move.l privilegeViolationCB, %a0 195 | jsr (%a0) 196 | movem.l (%sp)+,%d0-%d1/%a0-%a1 197 | rte 198 | 199 | _Trace: 200 | jsr exceptionDump 201 | movem.l %d0-%d1/%a0-%a1,-(%sp) 202 | move.l traceCB, %a0 203 | jsr (%a0) 204 | movem.l (%sp)+,%d0-%d1/%a0-%a1 205 | rte 206 | 207 | _Line_1010_Emulation: 208 | _Line_1111_Emulation: 209 | jsr exceptionDump 210 | movem.l %d0-%d1/%a0-%a1,-(%sp) 211 | move.l line1x1xCB, %a0 212 | jsr (%a0) 213 | movem.l (%sp)+,%d0-%d1/%a0-%a1 214 | rte 215 | 216 | _Error_Exception: 217 | jsr exceptionDump 218 | movem.l %d0-%d1/%a0-%a1,-(%sp) 219 | move.l errorExceptionCB, %a0 220 | jsr (%a0) 221 | movem.l (%sp)+,%d0-%d1/%a0-%a1 222 | rte 223 | 224 | _INT: 225 | movem.l %d0-%d1/%a0-%a1,-(%sp) 226 | move.l intCB, %a0 227 | jsr (%a0) 228 | movem.l (%sp)+,%d0-%d1/%a0-%a1 229 | rte 230 | 231 | _EXTINT: 232 | movem.l %d0-%d1/%a0-%a1,-(%sp) 233 | move.l eintCB, %a0 234 | jsr (%a0) 235 | movem.l (%sp)+,%d0-%d1/%a0-%a1 236 | rte 237 | 238 | _VINT: 239 | btst #5, (%sp) /* Skip context switch if not in user task */ 240 | bne.s no_user_task 241 | 242 | tst.w task_lock 243 | bne.s 1f 244 | move.w #0, -(%sp) /* TSK_superPend() will return 0 */ 245 | bra.s unlock /* If lock == 0, supervisor task is not locked */ 246 | 247 | 1: 248 | bcs.s no_user_task /* If lock < 0, super is locked with infinite wait */ 249 | subq.w #1, task_lock /* Locked with wait, subtract 1 to the frame count */ 250 | bne.s no_user_task /* And do not unlock if we did not reach 0 */ 251 | move.w #1, -(%sp) /* TSK_superPend() will return 1 */ 252 | 253 | unlock: 254 | /* Save bg task registers (excepting a7, that is stored in usp) */ 255 | move.l %a0, task_regs 256 | lea (task_regs + UTSK_REGS_LEN), %a0 257 | movem.l %d0-%d7/%a1-%a6, -(%a0) 258 | 259 | move.w (%sp)+, %d0 /* Load return value previously pushed to stack */ 260 | 261 | move.w (%sp)+, task_sr /* Pop user task sr and pc, and save them, */ 262 | move.l (%sp)+, task_pc /* so they can be restored later. */ 263 | movem.l (%sp)+, %d2-%d7/%a2-%a6 /* Restore non clobberable registers */ 264 | 265 | no_user_task: 266 | /* At this point, we always have in the stack the SR and PC of the task */ 267 | /* we want to jump after processing the interrupt, that might be the */ 268 | /* point where we came from (if there is no context switch) or the */ 269 | /* supervisor task (if we unlocked it). */ 270 | 271 | movem.l %d0-%d1/%a0-%a1,-(%sp) 272 | ori.w #0x0001, intTrace /* in V-Int */ 273 | addq.l #1, vtimer /* increment frame counter (more a vint counter) */ 274 | btst #3, VBlankProcess+1 /* PROCESS_XGM_TASK ? (use VBlankProcess+1 as btst is a byte operation) */ 275 | beq.s no_xgm_task 276 | 277 | jsr XGM_doVBlankProcess /* do XGM vblank task */ 278 | 279 | no_xgm_task: 280 | btst #1, VBlankProcess+1 /* PROCESS_BITMAP_TASK ? (use VBlankProcess+1 as btst is a byte operation) */ 281 | beq.s no_bmp_task 282 | 283 | jsr BMP_doVBlankProcess /* do BMP vblank task */ 284 | 285 | no_bmp_task: 286 | move.l vintCB, %a0 /* load user callback */ 287 | jsr (%a0) /* call user callback */ 288 | andi.w #0xFFFE, intTrace /* out V-Int */ 289 | movem.l (%sp)+,%d0-%d1/%a0-%a1 290 | rte 291 | 292 | *------------------------------------------------ 293 | * 294 | * Copyright (c) 1988 by Sozobon, Limited. Author: Johann Ruegg 295 | * 296 | * Permission is granted to anyone to use this software for any purpose 297 | * on any computer system, and to redistribute it freely, with the 298 | * following restrictions: 299 | * 1) No charge may be made other than reasonable charges for reproduction. 300 | * 2) Modified versions must be clearly marked as such. 301 | * 3) The authors are not responsible for any harmful consequences 302 | * of using this software, even if they result from defects in it. 303 | * 304 | *------------------------------------------------ 305 | 306 | ldiv: 307 | move.l 4(%a7),%d0 308 | bpl ld1 309 | neg.l %d0 310 | ld1: 311 | move.l 8(%a7),%d1 312 | bpl ld2 313 | neg.l %d1 314 | eor.b #0x80,4(%a7) 315 | ld2: 316 | bsr i_ldiv /* d0 = d0/d1 */ 317 | tst.b 4(%a7) 318 | bpl ld3 319 | neg.l %d0 320 | ld3: 321 | rts 322 | 323 | lmul: 324 | move.l 4(%a7),%d0 325 | bpl lm1 326 | neg.l %d0 327 | lm1: 328 | move.l 8(%a7),%d1 329 | bpl lm2 330 | neg.l %d1 331 | eor.b #0x80,4(%a7) 332 | lm2: 333 | bsr i_lmul /* d0 = d0*d1 */ 334 | tst.b 4(%a7) 335 | bpl lm3 336 | neg.l %d0 337 | lm3: 338 | rts 339 | 340 | lrem: 341 | move.l 4(%a7),%d0 342 | bpl lr1 343 | neg.l %d0 344 | lr1: 345 | move.l 8(%a7),%d1 346 | bpl lr2 347 | neg.l %d1 348 | lr2: 349 | bsr i_ldiv /* d1 = d0%d1 */ 350 | move.l %d1,%d0 351 | tst.b 4(%a7) 352 | bpl lr3 353 | neg.l %d0 354 | lr3: 355 | rts 356 | 357 | ldivu: 358 | move.l 4(%a7),%d0 359 | move.l 8(%a7),%d1 360 | bsr i_ldiv 361 | rts 362 | 363 | lmulu: 364 | move.l 4(%a7),%d0 365 | move.l 8(%a7),%d1 366 | bsr i_lmul 367 | rts 368 | 369 | lremu: 370 | move.l 4(%a7),%d0 371 | move.l 8(%a7),%d1 372 | bsr i_ldiv 373 | move.l %d1,%d0 374 | rts 375 | * 376 | * A in d0, B in d1, return A*B in d0 377 | * 378 | i_lmul: 379 | move.l %d3,%a2 /* save d3 */ 380 | move.w %d1,%d2 381 | mulu %d0,%d2 /* d2 = Al * Bl */ 382 | 383 | move.l %d1,%d3 384 | swap %d3 385 | mulu %d0,%d3 /* d3 = Al * Bh */ 386 | 387 | swap %d0 388 | mulu %d1,%d0 /* d0 = Ah * Bl */ 389 | 390 | add.l %d3,%d0 /* d0 = (Ah*Bl + Al*Bh) */ 391 | swap %d0 392 | clr.w %d0 /* d0 = (Ah*Bl + Al*Bh) << 16 */ 393 | 394 | add.l %d2,%d0 /* d0 = A*B */ 395 | move.l %a2,%d3 /* restore d3 */ 396 | rts 397 | * 398 | *A in d0, B in d1, return A/B in d0, A%B in d1 399 | * 400 | i_ldiv: 401 | tst.l %d1 402 | bne nz1 403 | 404 | * divide by zero 405 | * divu #0,%d0 /* cause trap */ 406 | move.l #0x80000000,%d0 407 | move.l %d0,%d1 408 | rts 409 | nz1: 410 | move.l %d3,%a2 /* save d3 */ 411 | cmp.l %d1,%d0 412 | bhi norm 413 | beq is1 414 | * AB and B is not 0 426 | norm: 427 | cmp.l #1,%d1 428 | bne not1 429 | * B==1, so ret A, rem 0 430 | clr.l %d1 431 | move.l %a2,%d3 /* restore d3 */ 432 | rts 433 | * check for A short (implies B short also) 434 | not1: 435 | cmp.l #0xffff,%d0 436 | bhi slow 437 | * A short and B short -- use 'divu' 438 | divu %d1,%d0 /* d0 = REM:ANS */ 439 | swap %d0 /* d0 = ANS:REM */ 440 | clr.l %d1 441 | move.w %d0,%d1 /* d1 = REM */ 442 | clr.w %d0 443 | swap %d0 444 | move.l %a2,%d3 /* restore d3 */ 445 | rts 446 | * check for B short 447 | slow: 448 | cmp.l #0xffff,%d1 449 | bhi slower 450 | * A long and B short -- use special stuff from gnu 451 | move.l %d0,%d2 452 | clr.w %d2 453 | swap %d2 454 | divu %d1,%d2 /* d2 = REM:ANS of Ahi/B */ 455 | clr.l %d3 456 | move.w %d2,%d3 /* d3 = Ahi/B */ 457 | swap %d3 458 | 459 | move.w %d0,%d2 /* d2 = REM << 16 + Alo */ 460 | divu %d1,%d2 /* d2 = REM:ANS of stuff/B */ 461 | 462 | move.l %d2,%d1 463 | clr.w %d1 464 | swap %d1 /* d1 = REM */ 465 | 466 | clr.l %d0 467 | move.w %d2,%d0 468 | add.l %d3,%d0 /* d0 = ANS */ 469 | move.l %a2,%d3 /* restore d3 */ 470 | rts 471 | * A>B, B > 1 472 | slower: 473 | move.l #1,%d2 474 | clr.l %d3 475 | moreadj: 476 | cmp.l %d0,%d1 477 | bhs adj 478 | add.l %d2,%d2 479 | add.l %d1,%d1 480 | bpl moreadj 481 | * we shifted B until its >A or sign bit set 482 | * we shifted #1 (d2) along with it 483 | adj: 484 | cmp.l %d0,%d1 485 | bhi ltuns 486 | or.l %d2,%d3 487 | sub.l %d1,%d0 488 | ltuns: 489 | lsr.l #1,%d1 490 | lsr.l #1,%d2 491 | bne adj 492 | * d3=answer, d0=rem 493 | move.l %d0,%d1 494 | move.l %d3,%d0 495 | move.l %a2,%d3 /* restore d3 */ 496 | rts 497 | -------------------------------------------------------------------------------- /lou/05_sprites/src/main.c: -------------------------------------------------------------------------------- 1 | 2 | // SGDK 3 | #include 4 | #include "resources.h" 5 | 6 | #define VERTICAL_REZ 224 7 | void LineDark(); 8 | void LineLight(); 9 | // image is 512x224. Screen is 320, we want to move halfway 10 | // 512/2 - 320 /2 11 | #define SCROLL_CENTER -96 12 | 13 | // Keep track of the current line during Horizontal Interrupts 14 | u16 lineDisplay = 0; 15 | 16 | // Horizontal Scrolling values 17 | s16 HscrollA[VERTICAL_REZ]; 18 | s16 HscrollB[VERTICAL_REZ]; 19 | // Vertical Scrolling values ( to simulate hills ) 20 | s8 VscrollA[VERTICAL_REZ]; 21 | fix32 roadOffsetRight[224]; // X offset from side of road ( for positioning the sprites 22 | fix32 roadOffsetLeft[224]; // X offset from side of road ( for positioning the sprites 23 | 24 | // color banding array 25 | u8 colors[VERTICAL_REZ]; 26 | u8 line_color = 0; // 0 -uninit, 1-light, 2-dark 27 | u8 side_color = 0; 28 | u8 grass_color = 0; // 0 -uninit, 1-light, 2-dark 29 | 30 | // Zmap for tracking segment position 31 | #define ZMAP_LENGTH 110 // slighty more than the bg horizon 32 | fix32 zmap[ZMAP_LENGTH]; 33 | fix32 scale[ZMAP_LENGTH]; 34 | 35 | // Road data 36 | #define ROAD_SEGMENTS_LENGTH 15 37 | typedef struct 38 | { 39 | fix32 dx; // rate of change for the road. 40 | fix32 bgdx; // rate of change for background. ( ignore for now ) 41 | fix32 dy; // rate of change for drawing road in y dir? 42 | } ROAD_SEGMENT; 43 | 44 | const ROAD_SEGMENT segments[ROAD_SEGMENTS_LENGTH] = { 45 | {FIX32(0), FIX32(0), FIX32(-0.005)}, 46 | {FIX32(0.0), FIX32(0.0), FIX32(0.003)}, 47 | {FIX32(-0.04), FIX32(-1.28), FIX32(-0.001)}, 48 | {FIX32(-0.02), FIX32(-0.48), FIX32(0.0)}, 49 | {FIX32(0), FIX32(0), FIX32(0.001)}, 50 | {FIX32(0), FIX32(0), FIX32(0.0025)}, 51 | {FIX32(0.03), FIX32(0.64), FIX32(-0.002)}, 52 | {FIX32(-0.03), FIX32(-0.64), FIX32(0)}, 53 | {FIX32(0), FIX32(0), FIX32(0.001)}, 54 | {FIX32(0), FIX32(0), FIX32(-0.0025)}, 55 | {FIX32(0), FIX32(0), FIX32(0.002)}, 56 | {FIX32(0.0), FIX32(0.0), FIX32(0)}, 57 | {FIX32(0), FIX32(0), FIX32(0.0)}, 58 | {FIX32(-0.02), FIX32(-0.48), FIX32(0)}, 59 | {FIX32(0.02), FIX32(0.48), FIX32(0)}}; 60 | 61 | u16 bottom_segments_index = 0; 62 | u16 segments_index = 0; 63 | 64 | // Speed the 'vehicle' moves through teh road 65 | fix32 speed = FIX32(0.00); 66 | s16 accelerate = 0; 67 | s16 deccelerate = 0; 68 | 69 | // position variables. 70 | fix32 segment_position = FIX32(0); // keep track of the segment position on screen 71 | fix32 background_position = FIX32(SCROLL_CENTER); // handle background X position 72 | s16 horizon_line = 223; // keep track of where the horizon is 73 | 74 | // Sprites 75 | struct CP_SPRITE 76 | { 77 | Sprite *sprite; 78 | fix32 pos_x; 79 | fix32 pos_y; 80 | fix32 zpos; // track position along the road. start it at the farthest on background - 12.5 81 | u8 update_y; 82 | }; 83 | struct CP_SPRITE carSprite; 84 | #define NUMBER_OF_TREES 6 85 | struct CP_SPRITE trees[NUMBER_OF_TREES]; 86 | 87 | void createTrees() 88 | { 89 | for (u16 i = 0; i < NUMBER_OF_TREES; ++i) 90 | { 91 | //trees[i] = malloc( sizeof(struct CP_SPRITE)); 92 | if (i < 2) 93 | { 94 | trees[i].zpos = FIX32(12.5); 95 | } 96 | else if (i < 4) 97 | { 98 | trees[i].zpos = FIX32(8); 99 | } 100 | else 101 | { 102 | trees[i].zpos = FIX32(4); 103 | } 104 | trees[i].update_y = 1; 105 | trees[i].sprite = SPR_addSprite(&tree, 106 | fix32ToInt(trees[i].pos_x), 107 | fix32ToInt(trees[i].pos_y), 108 | TILE_ATTR(PAL3, 0, FALSE, FALSE)); 109 | SPR_setFrame(trees[i].sprite, 4); 110 | SPR_setDepth(trees[i].sprite, 3); 111 | } 112 | } 113 | 114 | void updateTrees() 115 | { 116 | for (u16 i = 0; i < NUMBER_OF_TREES; ++i) 117 | { 118 | // figure out z position 119 | trees[i].zpos = fix32Add(trees[i].zpos, speed); 120 | if (trees[i].zpos < FIX32(-0.0)) 121 | { 122 | trees[i].zpos = FIX32(12.5); 123 | } 124 | // figure out scale 125 | if (trees[i].zpos < FIX32(1.01)) 126 | { 127 | SPR_setFrame(trees[i].sprite, 0); 128 | } 129 | else if (trees[i].zpos < FIX32(1.32)) 130 | { 131 | SPR_setFrame(trees[i].sprite, 1); 132 | } 133 | else if (trees[i].zpos < FIX32(2.01)) 134 | { 135 | SPR_setFrame(trees[i].sprite, 2); 136 | } 137 | else if (trees[i].zpos < FIX32(4.15)) 138 | { 139 | SPR_setFrame(trees[i].sprite, 3); 140 | } 141 | else 142 | { 143 | SPR_setFrame(trees[i].sprite, 4); 144 | } 145 | // actual Y will have to come from background update 146 | trees[i].update_y = 1; 147 | } 148 | } 149 | 150 | // joypad event handler. This gets called automatically by SGDK when the joypad 151 | // state changes 152 | static void joypadHandler(u16 joypadId, u16 changed, u16 state) 153 | { 154 | if ( joypadId == JOY_1 ) { 155 | if( state & BUTTON_A ) { 156 | accelerate = 1; 157 | } else if ( changed & BUTTON_A ) { 158 | accelerate = 0; 159 | } 160 | if( state & BUTTON_B ) { 161 | deccelerate = 1; 162 | } else if ( changed & BUTTON_B ) { 163 | deccelerate = 0; 164 | } 165 | } 166 | } 167 | 168 | void updateCar() 169 | { 170 | if( accelerate == 1 ) { 171 | speed = fix32Sub( speed, FIX32(0.005)); 172 | } 173 | 174 | if( deccelerate == 1 ) { 175 | speed = fix32Add( speed, FIX32(0.005)); 176 | } 177 | 178 | if( accelerate == 0 && deccelerate == 0 ) { 179 | speed = fix32Add( speed, FIX32(0.003)); 180 | if( speed > FIX32(0.0)){ 181 | speed = FIX32(0); 182 | } 183 | } 184 | if( speed < FIX32(-1.0)){ 185 | speed = FIX32(-1.0); 186 | } 187 | 188 | fix32 dx = segments[bottom_segments_index].dx; 189 | if (dx < FIX32(-0.02)) 190 | { 191 | SPR_setFrame(carSprite.sprite, 0); 192 | } 193 | else if (dx < FIX32(0.0)) 194 | { 195 | SPR_setFrame(carSprite.sprite, 1); 196 | } 197 | else if (dx >= FIX32(0.02)) 198 | { 199 | SPR_setFrame(carSprite.sprite, 4); 200 | } 201 | else if (dx > FIX32(0.0)) 202 | { 203 | SPR_setFrame(carSprite.sprite, 3); 204 | } 205 | else 206 | { 207 | SPR_setFrame(carSprite.sprite, 2); 208 | } 209 | } 210 | 211 | // My interpretation of the pseudo-code in 212 | // http://www.extentofthejam.com/pseudo/#curves 213 | // and the hills described in 214 | // http://www.extentofthejam.com/pseudo/#hills 215 | void update() 216 | { 217 | fix32 current_x = FIX32(0); // Lou's pseudo 3d page says to use Half of the screen width, 218 | // but I've defined SCROLL_CENTER to handle this 219 | 220 | fix32 dx = FIX32(0); // Curve Amount, constant per segment. 221 | fix32 ddx = FIX32(0); // Curve Amount, changes per line 222 | 223 | fix32 dy = FIX32(0); // Slope Amount 224 | fix32 ddy = FIX32(0); // Slope Amount, changes per line 225 | 226 | fix32 current_drawing_pos = FIX32(223); // The drawing loop would start at the beginning of the Z-map (nearest). Basically the bottom of the screen 227 | horizon_line = 223; // keep track of where the horizon is. I"m starting at the bottom and will update as the rode gets computed 228 | 229 | // for each line of the screen from the bottom to the top 230 | //for (y = 0; y < ZMAP_LENGTH; ++y) // no longer works because up-hill/down-hill won't be exaclty 1. ++y isn't valid 231 | 232 | // HILL: draw loop starts at the beginning of the Z map ( nearest = 0 ) and stops at teh end (farthest = ZMAP_LENGTH ) 233 | // * flat roads decrements the drawing position each line by 1 234 | // * if we decrement the drawing position by 2 (doubling lines) the road gets drawn twice as high. 235 | // * by varying the amount we decrement the drawing position we can draw a hill that starts flat and curves upwards 236 | // * if the next drawing position is more than one line from the current drawing position, the currnet ZMap line is repeated until 237 | // we get there, producing a scalien effect. 238 | for (u16 bgY = 223; bgY > 113; bgY--) 239 | { 240 | 241 | ////////////////////////////////////////////////////////////////////// 242 | // Road Bending 243 | fix32 z = zmap[223 - bgY]; // zmap[0] is closest 244 | // if line of screen's Z map position is below segment position 245 | if (z < segment_position) 246 | { 247 | // dx = bottom_segment.dx 248 | dx = segments[bottom_segments_index].dx; 249 | dy = segments[bottom_segments_index].dy; 250 | } 251 | else // if line of Screen's Z map position is above segment position. 252 | { 253 | // dx = segment.dx 254 | dx = segments[segments_index].dx; 255 | dy = segments[segments_index].dy; 256 | } 257 | 258 | // ddx += dx 259 | ddx = fix32Add(ddx, dx); 260 | // current_x += ddx 261 | current_x = fix32Add(current_x, ddx); 262 | 263 | ////////////////////////////////////////////////////////////////////// 264 | // Coloring 265 | // For each Z, make one of the bits represent the shade 266 | // of the road (dark or light). Then, just draw the 267 | // appropriate road pattern or colors for that bit 268 | u8 zmapval = (u8)fix32ToInt(fix32Sub(segment_position, z)); 269 | 270 | ddy = fix32Add(dy, ddy); 271 | s16 cdp = fix32ToInt(current_drawing_pos); // current vertical drawing position 272 | fix32 delta_drawing_pos = fix32Add(FIX32(1), ddy); // increment drawing position 273 | fix32 next_drawing_pos = fix32Sub(current_drawing_pos, delta_drawing_pos); 274 | s16 ndp = fix32ToInt(next_drawing_pos); // figure out next drawing position 275 | // repeat line if theres a gap greater than 1 276 | for (; cdp > ndp; --cdp) // 277 | { 278 | if (cdp <= horizon_line) // if current drawing position is above the horizon 279 | { 280 | HscrollA[cdp] = SCROLL_CENTER + fix32ToInt(current_x); // this_line.x = current x | using horizontal scrolling to fake curves 281 | VscrollA[cdp] = bgY - cdp; // set the vertical scroll amount for the current drawing position 282 | horizon_line = cdp; // update horizon line 283 | 284 | // coloring 285 | colors[cdp] = zmapval & 1; 286 | } 287 | } 288 | 289 | // sprite update 290 | for (u16 i = 0; i < NUMBER_OF_TREES; ++i) 291 | { 292 | if (trees[i].update_y == 1) 293 | { 294 | // check if z is close. 295 | if (z > trees[i].zpos) 296 | { 297 | trees[i].pos_y = fix32Sub(current_drawing_pos, FIX32(75)); 298 | if (i % 2 == 0) 299 | { 300 | trees[i].pos_x = fix32Add(fix32Add(FIX32(160), current_x), roadOffsetLeft[bgY]); 301 | } 302 | else 303 | { 304 | trees[i].pos_x = fix32Add(fix32Add(FIX32(160), current_x), roadOffsetRight[bgY]); 305 | } 306 | trees[i].update_y = 0; 307 | } 308 | } 309 | } 310 | 311 | current_drawing_pos = next_drawing_pos; // move to next drawing position 312 | } 313 | 314 | // hide anything above the horizon line 315 | for (s16 h = horizon_line; h >= 0; --h) 316 | { 317 | VscrollA[h] = -h; 318 | } 319 | 320 | // scroll the background 321 | if (speed != FIX32(0.0)) 322 | { 323 | 324 | background_position = fix32Sub(background_position, segments[bottom_segments_index].bgdx); 325 | for (u16 y = 0; y < 160; ++y) 326 | { 327 | HscrollB[y] = fix32ToInt(background_position); 328 | } 329 | } 330 | 331 | // Move segments 332 | segment_position = fix32Add(segment_position, speed); 333 | if (fix32ToInt(segment_position) < 0) 334 | { 335 | // bottom_segment = segment 336 | bottom_segments_index = segments_index; 337 | // segment.position = zmap.length - 1 338 | segment_position = zmap[ZMAP_LENGTH - 1]; // Send segment to farthest visible distance 339 | 340 | // get next segment from road 341 | segments_index++; // segment_index is used to get segment.dx 342 | if (segments_index == ROAD_SEGMENTS_LENGTH) 343 | { 344 | segments_index -= ROAD_SEGMENTS_LENGTH; // go back to the start 345 | } 346 | } 347 | } 348 | 349 | int main(bool hard) 350 | { 351 | ////////////////////////////////////////////////////////////// 352 | // http://www.extentofthejam.com/pseudo/ 353 | // Z = Y_world / (Y_screen - (height_screen / 2)) 354 | // this gets me 0.65 nearest and 25.0 farthest 355 | for (int i = 0; i < ZMAP_LENGTH; ++i) 356 | { 357 | zmap[i] = fix32Div(FIX32(-75), fix32Sub(FIX32(i), FIX32(112))); 358 | scale[i] = fix32Div(FIX32(1), zmap[i]); 359 | KLog_F3("i: ", FIX32(i), " z: ", zmap[i], " s: ", scale[i]); 360 | } 361 | 362 | ////////////////////////////////////////////////////////////// 363 | // Precompute road offsets for roadside sprites 364 | // 116 | 262-256 = 6 365 | // 223 | 415-256 = 159 366 | // 159 - 6 = 153 367 | // 223 - 116 = 107 368 | // step size 153/107 1.43 << step size per line 369 | // 370 | // Looks better w/ more padding 371 | // (159 + 42) - (6+3) = 192 372 | // step size 192/107 1.794 << step size per line 373 | fix32 rightFromCenter = FIX32(-22); // tree width is 56 .. half of 56 is 28 374 | fix32 leftFromCenter = FIX32(-34); // tree width is 56 .. half of 56 is 28 375 | fix32 step = FIX32(1.794); 376 | for (int i = 224 - ZMAP_LENGTH; i < 224; i++) 377 | { 378 | roadOffsetRight[i] = rightFromCenter; 379 | rightFromCenter = fix32Add(rightFromCenter, step); 380 | roadOffsetLeft[i] = leftFromCenter; 381 | leftFromCenter = fix32Sub(leftFromCenter, step); 382 | KLog_F2(" i: ", FIX32(i), " road offset: ", roadOffsetRight[i]); 383 | } 384 | 385 | ////////////////////////////////////////////////////////////// 386 | // VDP basic setup 387 | VDP_setBackgroundColor(16); 388 | VDP_setScreenWidth320(); 389 | VDP_setScrollingMode(HSCROLL_LINE, VSCROLL_PLANE); 390 | for (int i = 0; i < VERTICAL_REZ; i++) 391 | { 392 | HscrollA[i] = SCROLL_CENTER; 393 | HscrollB[i] = SCROLL_CENTER; 394 | VscrollA[i] = 0; 395 | } 396 | 397 | ////////////////////////////////////////////////////////////// 398 | // Setup scroll panes 399 | PAL_setPalette(PAL0, road_pal.data, CPU); 400 | int ind = TILE_USER_INDEX; 401 | VDP_drawImageEx(BG_A, &road, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, ind), 0, 0, FALSE, TRUE); 402 | ind += road.tileset->numTile; 403 | PAL_setPalette(PAL1, background_pal.data, CPU); 404 | VDP_drawImageEx(BG_B, &background, TILE_ATTR_FULL(PAL1, FALSE, FALSE, FALSE, ind), 0, 0, FALSE, TRUE); 405 | ind += background.tileset->numTile; 406 | 407 | VDP_setVerticalScroll(BG_B, 0); 408 | 409 | ////////////////////////////////////////////////////////////// 410 | // Setup Sprites 411 | SPR_init(); 412 | PAL_setPalette(PAL2, car_pal.data, CPU); 413 | carSprite.sprite = NULL; 414 | carSprite.pos_x = FIX32(116.0); 415 | carSprite.pos_y = FIX32(160.0); 416 | carSprite.sprite = SPR_addSprite(&car, // Sprite defined in resources 417 | fix32ToInt(carSprite.pos_x), // starting X position 418 | fix32ToInt(carSprite.pos_y), // starting Y position 419 | TILE_ATTR(PAL2, // specify palette 420 | 1, // Tile priority ( with background) 421 | FALSE, // flip the sprite vertically? 422 | FALSE // flip the sprite horizontally 423 | )); 424 | SPR_setFrame(carSprite.sprite, 2); 425 | 426 | PAL_setPalette(PAL3, tree_pal.data, CPU); 427 | createTrees(); 428 | 429 | ////////////////////////////////////////////////////////////// 430 | // init segments 431 | bottom_segments_index = 0; 432 | segments_index = 1; 433 | segment_position = zmap[ZMAP_LENGTH - 1]; // put it at the farthest away point 434 | 435 | // set spped through z 436 | speed = FIX32(0.0); 437 | //speed = FIX32(-0.1); 438 | 439 | ////////////////////////////////////////////////////////////// 440 | // Setup interrupt handlers 441 | SYS_disableInts(); 442 | { 443 | VDP_setHIntCounter(0); 444 | VDP_setHInterrupt(1); 445 | } 446 | SYS_enableInts(); 447 | 448 | // Asynchronous joystick handler. 449 | JOY_setEventHandler(joypadHandler); 450 | 451 | // Main loop 452 | while (TRUE) 453 | { 454 | // update 455 | updateTrees(); 456 | update(); 457 | updateCar(); 458 | 459 | for (u16 i = 0; i < NUMBER_OF_TREES; ++i) 460 | { 461 | // update z-order for trees 462 | SPR_setDepth(trees[i].sprite, 224 - fix32ToInt(trees[i].pos_y)); 463 | // Draw tree at new position 464 | SPR_setPosition(trees[i].sprite, fix32ToInt(trees[i].pos_x), fix32ToInt(trees[i].pos_y)); 465 | } 466 | 467 | // Draw car at now position 468 | SPR_setPosition(carSprite.sprite, fix32ToInt(carSprite.pos_x), fix32ToInt(carSprite.pos_y)); 469 | 470 | SPR_update(); 471 | 472 | // curve the road with horizontal scrolling 473 | VDP_setHorizontalScrollLine(BG_A, 0, HscrollA, VERTICAL_REZ, DMA_QUEUE); 474 | // move the background 475 | VDP_setHorizontalScrollLine(BG_B, 0, HscrollB, 160, DMA_QUEUE); 476 | 477 | fix32 h = FIX32(horizon_line - 113); 478 | h = fix32Div(h, FIX32(6)); 479 | VDP_setVerticalScroll(BG_B, fix32ToInt(h)); 480 | 481 | SYS_doVBlankProcess(); 482 | } 483 | } 484 | -------------------------------------------------------------------------------- /lou/06_steering/res/background/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/06_steering/res/background/background.png -------------------------------------------------------------------------------- /lou/06_steering/res/background/road.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/06_steering/res/background/road.png -------------------------------------------------------------------------------- /lou/06_steering/res/resources.res: -------------------------------------------------------------------------------- 1 | IMAGE road "background/road.png" BEST 2 | IMAGE background "background/background.png" BEST 3 | 4 | 5 | SPRITE car "sprites/car.png" 11 7 NONE 0 6 | SPRITE green_car "sprites/green_car.png" 11 7 BEST 0 7 | SPRITE red_car "sprites/red_car.png" 11 7 BEST 0 8 | 9 | SPRITE bush "sprites/bush0.png" 6 6 FAST 0 NONE TILE 10 | 11 | SPRITE rock "sprites/rock.png" 4 3 FAST 0 NONE TILE 12 | SPRITE pine "sprites/pine.png" 5 11 FAST 0 NONE TILE 13 | SPRITE sign "sprites/sign.png" 6 6 FAST 0 NONE TILE 14 | 15 | PALETTE background_pal "background/background.png" 16 | PALETTE car_pal "sprites/car.png" 17 | PALETTE sign_pal "sprites/sign.png" 18 | PALETTE bush_pal "sprites/bush0.png" 19 | -------------------------------------------------------------------------------- /lou/06_steering/res/sprites/bush.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/06_steering/res/sprites/bush.png -------------------------------------------------------------------------------- /lou/06_steering/res/sprites/bush0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/06_steering/res/sprites/bush0.png -------------------------------------------------------------------------------- /lou/06_steering/res/sprites/car.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/06_steering/res/sprites/car.png -------------------------------------------------------------------------------- /lou/06_steering/res/sprites/green_car.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/06_steering/res/sprites/green_car.png -------------------------------------------------------------------------------- /lou/06_steering/res/sprites/pine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/06_steering/res/sprites/pine.png -------------------------------------------------------------------------------- /lou/06_steering/res/sprites/pinetree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/06_steering/res/sprites/pinetree.png -------------------------------------------------------------------------------- /lou/06_steering/res/sprites/red_car.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/06_steering/res/sprites/red_car.png -------------------------------------------------------------------------------- /lou/06_steering/res/sprites/rock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/06_steering/res/sprites/rock.png -------------------------------------------------------------------------------- /lou/06_steering/res/sprites/sign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/06_steering/res/sprites/sign.png -------------------------------------------------------------------------------- /lou/06_steering/res/sprites/thing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/06_steering/res/sprites/thing.png -------------------------------------------------------------------------------- /lou/06_steering/res/sprites/tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/lou/06_steering/res/sprites/tree.png -------------------------------------------------------------------------------- /lou/06_steering/src/HInter.s: -------------------------------------------------------------------------------- 1 | 2 | 3 | *------------------------------------------------ 4 | * LABELS 5 | *------------------------------------------------ 6 | .set VDP_CTRL, 0xC00004 7 | .set VDP_DATA, 0xC00000 8 | 9 | .set PAL0_COLOR0, 0xC0000000 10 | .set PAL0_COLOR1, 0xC0020000 11 | .set PAL0_COLOR2, 0xC0040000 12 | .set PAL0_COLOR3, 0xC0060000 13 | .set PAL0_COLOR4, 0xC0080000 14 | 15 | .set VSCROLL_A, 0x40000010 16 | .set VSCROLL_B, 0x40020010 17 | .set HSCROLL_A, 0x7C000002 18 | .set HSCROLL_B, 0x7C020002 19 | 20 | .set LINE_DARK_COLOR, 0x666 21 | .set LINE_LIGHT_COLOR, 0xFFF 22 | 23 | .set GRASS_DARK_COLOR, 0x063 24 | .set GRASS_LIGHT_COLOR, 0x0A0 25 | 26 | *------------------------------------------------ 27 | * Functions 28 | *------------------------------------------------ 29 | .text 30 | .even 31 | .align 2 32 | .globl HInter 33 | .type HInter, @function 34 | 35 | HInter: 36 | move.l %d0, -(%sp) /* push device register 0 onto the stack */ 37 | 38 | clr.l %d0 /* Clear D0 and read the current scanline to it */ 39 | move.b (0xC00008), %d0 40 | 41 | 42 | cmp.w #20, %d0 43 | jgt WORK /* do if current line is below horizon */ 44 | 45 | move.l (%sp)+, %d0 /* restore data register 0 */ 46 | rte 47 | 48 | WORK: 49 | lsl.w #1, %d0 50 | move.l %a0, -(%sp) /* push address register 0 onto the stack */ 51 | 52 | 53 | lea VscrollA, %a0 /* get 'vscrollA' array effective address to A0 */ 54 | move.l #VSCROLL_A, 0xC00004 /* Vertical scrolling */ 55 | move.w (%a0, %d0.w), 0xC00000 56 | 57 | 58 | lea colors, %a0 /* get 'colors' array effective address to A0 */ 59 | move.w (%a0,%d0.w),%d0 /* check shading for current scanline */ 60 | move.l (%sp)+, %a0 /* restore address register 0 */ 61 | 62 | tst.w %d0 /* check shading value */ 63 | jeq LIGHT /* jump to light coloring */ 64 | 65 | 66 | DARK: 67 | LINE_DARK: 68 | move.b #2, %d0 69 | cmp.b lineColor, %d0 70 | jne SET_LINE_DARK /* if not dark, set the color */ 71 | jmp GRASS_DARK 72 | move.l (%sp)+, %d0 /* restore data register 0 */ 73 | rte 74 | 75 | SET_LINE_DARK: 76 | 77 | move.b %d0, lineColor 78 | move.l #PAL0_COLOR1, VDP_CTRL /* Tell VDP we want to change color 1 */ 79 | clr.w %d0 80 | 81 | LINE_DELAY1: 82 | move.w (VDP_CTRL), %d0 /* wait before we set color (to minimize dots) */ 83 | btst.b #0x02, %d0 84 | beq LINE_DELAY1 85 | 86 | move.w #LINE_DARK_COLOR, VDP_DATA /* set the color */ 87 | 88 | move.l (%sp)+, %d0 /* restore data register 0 */ 89 | rte 90 | 91 | GRASS_DARK: 92 | move.b #2, %d0 93 | cmp.b grassColor, %d0 94 | jne SET_GRASS_DARK /* if not dark, set the color */ 95 | move.l (%sp)+, %d0 /* restore data register 0 */ 96 | rte 97 | 98 | SET_GRASS_DARK: 99 | 100 | move.b %d0, grassColor 101 | move.l #PAL0_COLOR2, VDP_CTRL /* Tell VDP we want to change color 1 */ 102 | clr.w %d0 103 | 104 | GRASS_DELAY1: 105 | move.w (VDP_CTRL), %d0 /* wait before we set color (to minimize dots) */ 106 | btst.b #0x02, %d0 107 | beq GRASS_DELAY1 108 | 109 | move.w #GRASS_DARK_COLOR, VDP_DATA /* set the color */ 110 | 111 | move.l (%sp)+, %d0 /* restore data register 0 */ 112 | rte 113 | 114 | 115 | LIGHT: 116 | 117 | LINE_LIGHT: 118 | move.b #1, %d0 119 | cmp.b lineColor, %d0 120 | jne SET_LINE_LIGHT /* if not dark, set the color */ 121 | JMP GRASS_LIGHT 122 | move.l (%sp)+, %d0 /* restore data register 0 */ 123 | rte 124 | 125 | SET_LINE_LIGHT: 126 | move.b %d0, lineColor 127 | move.l #PAL0_COLOR1, VDP_CTRL /* Tell VDP we want to change color 1 */ 128 | clr.w %d0 129 | 130 | LINE_DELAY2: 131 | move.w (VDP_CTRL), %d0 /* wait before setting color */ 132 | btst.b #0x02, %d0 133 | beq LINE_DELAY2 134 | 135 | move.w #LINE_LIGHT_COLOR, VDP_DATA /* set the color */ 136 | 137 | move.l (%sp)+, %d0 /* restore data register 0 */ 138 | rte 139 | 140 | GRASS_LIGHT: 141 | move.b #1, %d0 142 | cmp.b grassColor, %d0 143 | jne SET_GRASS_LIGHT /* if not dark, set the color */ 144 | move.l (%sp)+, %d0 /* restore data register 0 */ 145 | rte 146 | 147 | SET_GRASS_LIGHT: 148 | 149 | move.b %d0, grassColor 150 | move.l #PAL0_COLOR2, VDP_CTRL /* Tell VDP we want to change color 1 */ 151 | clr.w %d0 152 | 153 | GRASS_DELAY2: 154 | move.w (VDP_CTRL), %d0 /* wait before we set color (to minimize dots) */ 155 | btst.b #0x02, %d0 156 | beq GRASS_DELAY2 157 | 158 | move.w #GRASS_LIGHT_COLOR, VDP_DATA /* set the color */ 159 | 160 | move.l (%sp)+, %d0 /* restore data register 0 */ 161 | rte 162 | 163 | 164 | 165 | 166 | 167 | *------------------------------------------------ 168 | * DATA 169 | *------------------------------------------------ 170 | .data 171 | .even 172 | .globl colors 173 | .section .bss 174 | .align 2 175 | .type colors, @object 176 | .size colors, 448 177 | colors: 178 | .zero 448 179 | 180 | # 0 unlit, 1 light, 2 dark 181 | .comm lineColor,2,2 182 | .comm grassColor,2,2 183 | 184 | 185 | .comm horizonLine,2,2 186 | 187 | .even 188 | .globl VscrollA 189 | .section .bss 190 | .align 2 191 | .type VscrollA, @object 192 | .size VscrollA, 448 193 | VscrollA: 194 | .zero 448 195 | 196 | -------------------------------------------------------------------------------- /lou/06_steering/src/boot/sega.s: -------------------------------------------------------------------------------- 1 | #include "task_cst.h" 2 | 3 | .section .text.keepboot 4 | 5 | *------------------------------------------------------- 6 | * 7 | * Sega startup code for the GNU Assembler 8 | * Translated from: 9 | * Sega startup code for the Sozobon C compiler 10 | * Written by Paul W. Lee 11 | * Modified by Charles Coty 12 | * Modified by Stephane Dallongeville 13 | * 14 | *------------------------------------------------------- 15 | 16 | .globl rom_header 17 | 18 | .org 0x00000000 19 | 20 | _Start_Of_Rom: 21 | _Vecteurs_68K: 22 | dc.l __stack /* Stack address */ 23 | dc.l _Entry_Point /* Program start address */ 24 | dc.l _Bus_Error 25 | dc.l _Address_Error 26 | dc.l _Illegal_Instruction 27 | dc.l _Zero_Divide 28 | dc.l _Chk_Instruction 29 | dc.l _Trapv_Instruction 30 | dc.l _Privilege_Violation 31 | dc.l _Trace 32 | dc.l _Line_1010_Emulation 33 | dc.l _Line_1111_Emulation 34 | dc.l _Error_Exception, _Error_Exception, _Error_Exception, _Error_Exception 35 | dc.l _Error_Exception, _Error_Exception, _Error_Exception, _Error_Exception 36 | dc.l _Error_Exception, _Error_Exception, _Error_Exception, _Error_Exception 37 | dc.l _Error_Exception 38 | dc.l _INT 39 | dc.l _EXTINT 40 | dc.l _INT 41 | dc.l HInter 42 | dc.l _INT 43 | dc.l _VINT 44 | dc.l _INT 45 | dc.l _trap_0 /* Resume supervisor task */ 46 | dc.l _INT,_INT,_INT,_INT,_INT,_INT,_INT 47 | dc.l _INT,_INT,_INT,_INT,_INT,_INT,_INT,_INT 48 | dc.l _INT,_INT,_INT,_INT,_INT,_INT,_INT,_INT 49 | dc.l _INT,_INT,_INT,_INT,_INT,_INT,_INT,_INT 50 | 51 | rom_header: 52 | .incbin "out/rom_head.bin", 0, 0x100 53 | 54 | _Entry_Point: 55 | move #0x2700,%sr 56 | tst.l 0xa10008 57 | bne.s SkipJoyDetect 58 | 59 | tst.w 0xa1000c 60 | 61 | SkipJoyDetect: 62 | bne.s SkipSetup 63 | 64 | lea Table,%a5 65 | movem.w (%a5)+,%d5-%d7 66 | movem.l (%a5)+,%a0-%a4 67 | * Check Version Number 68 | move.b -0x10ff(%a1),%d0 69 | andi.b #0x0f,%d0 70 | beq.s WrongVersion 71 | 72 | * Sega Security Code (SEGA) 73 | move.l #0x53454741,0x2f00(%a1) 74 | WrongVersion: 75 | * Read from the control port to cancel any pending read/write command 76 | move.w (%a4),%d0 77 | 78 | * Configure a USER_STACK_LENGTH bytes user stack at bottom, and system stack on top of it 79 | move %sp, %usp 80 | sub #USER_STACK_LENGTH, %sp 81 | 82 | move.w %d7,(%a1) 83 | move.w %d7,(%a2) 84 | 85 | * Jump to initialisation process now... 86 | 87 | jmp _start_entry 88 | 89 | SkipSetup: 90 | jmp _reset_entry 91 | 92 | 93 | Table: 94 | dc.w 0x8000,0x3fff,0x0100 95 | dc.l 0xA00000,0xA11100,0xA11200,0xC00000,0xC00004 96 | 97 | 98 | *------------------------------------------------ 99 | * 100 | * interrupt functions 101 | * 102 | *------------------------------------------------ 103 | 104 | registersDump: 105 | move.l %d0,registerState+0 106 | move.l %d1,registerState+4 107 | move.l %d2,registerState+8 108 | move.l %d3,registerState+12 109 | move.l %d4,registerState+16 110 | move.l %d5,registerState+20 111 | move.l %d6,registerState+24 112 | move.l %d7,registerState+28 113 | move.l %a0,registerState+32 114 | move.l %a1,registerState+36 115 | move.l %a2,registerState+40 116 | move.l %a3,registerState+44 117 | move.l %a4,registerState+48 118 | move.l %a5,registerState+52 119 | move.l %a6,registerState+56 120 | move.l %a7,registerState+60 121 | rts 122 | 123 | busAddressErrorDump: 124 | move.w 4(%sp),ext1State 125 | move.l 6(%sp),addrState 126 | move.w 10(%sp),ext2State 127 | move.w 12(%sp),srState 128 | move.l 14(%sp),pcState 129 | jmp registersDump 130 | 131 | exception4WDump: 132 | move.w 4(%sp),srState 133 | move.l 6(%sp),pcState 134 | move.w 10(%sp),ext1State 135 | jmp registersDump 136 | 137 | exceptionDump: 138 | move.w 4(%sp),srState 139 | move.l 6(%sp),pcState 140 | jmp registersDump 141 | 142 | 143 | _Bus_Error: 144 | jsr busAddressErrorDump 145 | movem.l %d0-%d1/%a0-%a1,-(%sp) 146 | move.l busErrorCB, %a0 147 | jsr (%a0) 148 | movem.l (%sp)+,%d0-%d1/%a0-%a1 149 | rte 150 | 151 | _Address_Error: 152 | jsr busAddressErrorDump 153 | movem.l %d0-%d1/%a0-%a1,-(%sp) 154 | move.l addressErrorCB, %a0 155 | jsr (%a0) 156 | movem.l (%sp)+,%d0-%d1/%a0-%a1 157 | rte 158 | 159 | _Illegal_Instruction: 160 | jsr exception4WDump 161 | movem.l %d0-%d1/%a0-%a1,-(%sp) 162 | move.l illegalInstCB, %a0 163 | jsr (%a0) 164 | movem.l (%sp)+,%d0-%d1/%a0-%a1 165 | rte 166 | 167 | _Zero_Divide: 168 | jsr exceptionDump 169 | movem.l %d0-%d1/%a0-%a1,-(%sp) 170 | move.l zeroDivideCB, %a0 171 | jsr (%a0) 172 | movem.l (%sp)+,%d0-%d1/%a0-%a1 173 | rte 174 | 175 | _Chk_Instruction: 176 | jsr exception4WDump 177 | movem.l %d0-%d1/%a0-%a1,-(%sp) 178 | move.l chkInstCB, %a0 179 | jsr (%a0) 180 | movem.l (%sp)+,%d0-%d1/%a0-%a1 181 | rte 182 | 183 | _Trapv_Instruction: 184 | jsr exception4WDump 185 | movem.l %d0-%d1/%a0-%a1,-(%sp) 186 | move.l trapvInstCB, %a0 187 | jsr (%a0) 188 | movem.l (%sp)+,%d0-%d1/%a0-%a1 189 | rte 190 | 191 | _Privilege_Violation: 192 | jsr exceptionDump 193 | movem.l %d0-%d1/%a0-%a1,-(%sp) 194 | move.l privilegeViolationCB, %a0 195 | jsr (%a0) 196 | movem.l (%sp)+,%d0-%d1/%a0-%a1 197 | rte 198 | 199 | _Trace: 200 | jsr exceptionDump 201 | movem.l %d0-%d1/%a0-%a1,-(%sp) 202 | move.l traceCB, %a0 203 | jsr (%a0) 204 | movem.l (%sp)+,%d0-%d1/%a0-%a1 205 | rte 206 | 207 | _Line_1010_Emulation: 208 | _Line_1111_Emulation: 209 | jsr exceptionDump 210 | movem.l %d0-%d1/%a0-%a1,-(%sp) 211 | move.l line1x1xCB, %a0 212 | jsr (%a0) 213 | movem.l (%sp)+,%d0-%d1/%a0-%a1 214 | rte 215 | 216 | _Error_Exception: 217 | jsr exceptionDump 218 | movem.l %d0-%d1/%a0-%a1,-(%sp) 219 | move.l errorExceptionCB, %a0 220 | jsr (%a0) 221 | movem.l (%sp)+,%d0-%d1/%a0-%a1 222 | rte 223 | 224 | _INT: 225 | movem.l %d0-%d1/%a0-%a1,-(%sp) 226 | move.l intCB, %a0 227 | jsr (%a0) 228 | movem.l (%sp)+,%d0-%d1/%a0-%a1 229 | rte 230 | 231 | _EXTINT: 232 | movem.l %d0-%d1/%a0-%a1,-(%sp) 233 | move.l eintCB, %a0 234 | jsr (%a0) 235 | movem.l (%sp)+,%d0-%d1/%a0-%a1 236 | rte 237 | 238 | _VINT: 239 | btst #5, (%sp) /* Skip context switch if not in user task */ 240 | bne.s no_user_task 241 | 242 | tst.w task_lock 243 | bne.s 1f 244 | move.w #0, -(%sp) /* TSK_superPend() will return 0 */ 245 | bra.s unlock /* If lock == 0, supervisor task is not locked */ 246 | 247 | 1: 248 | bcs.s no_user_task /* If lock < 0, super is locked with infinite wait */ 249 | subq.w #1, task_lock /* Locked with wait, subtract 1 to the frame count */ 250 | bne.s no_user_task /* And do not unlock if we did not reach 0 */ 251 | move.w #1, -(%sp) /* TSK_superPend() will return 1 */ 252 | 253 | unlock: 254 | /* Save bg task registers (excepting a7, that is stored in usp) */ 255 | move.l %a0, task_regs 256 | lea (task_regs + UTSK_REGS_LEN), %a0 257 | movem.l %d0-%d7/%a1-%a6, -(%a0) 258 | 259 | move.w (%sp)+, %d0 /* Load return value previously pushed to stack */ 260 | 261 | move.w (%sp)+, task_sr /* Pop user task sr and pc, and save them, */ 262 | move.l (%sp)+, task_pc /* so they can be restored later. */ 263 | movem.l (%sp)+, %d2-%d7/%a2-%a6 /* Restore non clobberable registers */ 264 | 265 | no_user_task: 266 | /* At this point, we always have in the stack the SR and PC of the task */ 267 | /* we want to jump after processing the interrupt, that might be the */ 268 | /* point where we came from (if there is no context switch) or the */ 269 | /* supervisor task (if we unlocked it). */ 270 | 271 | movem.l %d0-%d1/%a0-%a1,-(%sp) 272 | ori.w #0x0001, intTrace /* in V-Int */ 273 | addq.l #1, vtimer /* increment frame counter (more a vint counter) */ 274 | btst #3, VBlankProcess+1 /* PROCESS_XGM_TASK ? (use VBlankProcess+1 as btst is a byte operation) */ 275 | beq.s no_xgm_task 276 | 277 | jsr XGM_doVBlankProcess /* do XGM vblank task */ 278 | 279 | no_xgm_task: 280 | btst #1, VBlankProcess+1 /* PROCESS_BITMAP_TASK ? (use VBlankProcess+1 as btst is a byte operation) */ 281 | beq.s no_bmp_task 282 | 283 | jsr BMP_doVBlankProcess /* do BMP vblank task */ 284 | 285 | no_bmp_task: 286 | move.l vintCB, %a0 /* load user callback */ 287 | jsr (%a0) /* call user callback */ 288 | andi.w #0xFFFE, intTrace /* out V-Int */ 289 | movem.l (%sp)+,%d0-%d1/%a0-%a1 290 | rte 291 | 292 | *------------------------------------------------ 293 | * 294 | * Copyright (c) 1988 by Sozobon, Limited. Author: Johann Ruegg 295 | * 296 | * Permission is granted to anyone to use this software for any purpose 297 | * on any computer system, and to redistribute it freely, with the 298 | * following restrictions: 299 | * 1) No charge may be made other than reasonable charges for reproduction. 300 | * 2) Modified versions must be clearly marked as such. 301 | * 3) The authors are not responsible for any harmful consequences 302 | * of using this software, even if they result from defects in it. 303 | * 304 | *------------------------------------------------ 305 | 306 | ldiv: 307 | move.l 4(%a7),%d0 308 | bpl ld1 309 | neg.l %d0 310 | ld1: 311 | move.l 8(%a7),%d1 312 | bpl ld2 313 | neg.l %d1 314 | eor.b #0x80,4(%a7) 315 | ld2: 316 | bsr i_ldiv /* d0 = d0/d1 */ 317 | tst.b 4(%a7) 318 | bpl ld3 319 | neg.l %d0 320 | ld3: 321 | rts 322 | 323 | lmul: 324 | move.l 4(%a7),%d0 325 | bpl lm1 326 | neg.l %d0 327 | lm1: 328 | move.l 8(%a7),%d1 329 | bpl lm2 330 | neg.l %d1 331 | eor.b #0x80,4(%a7) 332 | lm2: 333 | bsr i_lmul /* d0 = d0*d1 */ 334 | tst.b 4(%a7) 335 | bpl lm3 336 | neg.l %d0 337 | lm3: 338 | rts 339 | 340 | lrem: 341 | move.l 4(%a7),%d0 342 | bpl lr1 343 | neg.l %d0 344 | lr1: 345 | move.l 8(%a7),%d1 346 | bpl lr2 347 | neg.l %d1 348 | lr2: 349 | bsr i_ldiv /* d1 = d0%d1 */ 350 | move.l %d1,%d0 351 | tst.b 4(%a7) 352 | bpl lr3 353 | neg.l %d0 354 | lr3: 355 | rts 356 | 357 | ldivu: 358 | move.l 4(%a7),%d0 359 | move.l 8(%a7),%d1 360 | bsr i_ldiv 361 | rts 362 | 363 | lmulu: 364 | move.l 4(%a7),%d0 365 | move.l 8(%a7),%d1 366 | bsr i_lmul 367 | rts 368 | 369 | lremu: 370 | move.l 4(%a7),%d0 371 | move.l 8(%a7),%d1 372 | bsr i_ldiv 373 | move.l %d1,%d0 374 | rts 375 | * 376 | * A in d0, B in d1, return A*B in d0 377 | * 378 | i_lmul: 379 | move.l %d3,%a2 /* save d3 */ 380 | move.w %d1,%d2 381 | mulu %d0,%d2 /* d2 = Al * Bl */ 382 | 383 | move.l %d1,%d3 384 | swap %d3 385 | mulu %d0,%d3 /* d3 = Al * Bh */ 386 | 387 | swap %d0 388 | mulu %d1,%d0 /* d0 = Ah * Bl */ 389 | 390 | add.l %d3,%d0 /* d0 = (Ah*Bl + Al*Bh) */ 391 | swap %d0 392 | clr.w %d0 /* d0 = (Ah*Bl + Al*Bh) << 16 */ 393 | 394 | add.l %d2,%d0 /* d0 = A*B */ 395 | move.l %a2,%d3 /* restore d3 */ 396 | rts 397 | * 398 | *A in d0, B in d1, return A/B in d0, A%B in d1 399 | * 400 | i_ldiv: 401 | tst.l %d1 402 | bne nz1 403 | 404 | * divide by zero 405 | * divu #0,%d0 /* cause trap */ 406 | move.l #0x80000000,%d0 407 | move.l %d0,%d1 408 | rts 409 | nz1: 410 | move.l %d3,%a2 /* save d3 */ 411 | cmp.l %d1,%d0 412 | bhi norm 413 | beq is1 414 | * AB and B is not 0 426 | norm: 427 | cmp.l #1,%d1 428 | bne not1 429 | * B==1, so ret A, rem 0 430 | clr.l %d1 431 | move.l %a2,%d3 /* restore d3 */ 432 | rts 433 | * check for A short (implies B short also) 434 | not1: 435 | cmp.l #0xffff,%d0 436 | bhi slow 437 | * A short and B short -- use 'divu' 438 | divu %d1,%d0 /* d0 = REM:ANS */ 439 | swap %d0 /* d0 = ANS:REM */ 440 | clr.l %d1 441 | move.w %d0,%d1 /* d1 = REM */ 442 | clr.w %d0 443 | swap %d0 444 | move.l %a2,%d3 /* restore d3 */ 445 | rts 446 | * check for B short 447 | slow: 448 | cmp.l #0xffff,%d1 449 | bhi slower 450 | * A long and B short -- use special stuff from gnu 451 | move.l %d0,%d2 452 | clr.w %d2 453 | swap %d2 454 | divu %d1,%d2 /* d2 = REM:ANS of Ahi/B */ 455 | clr.l %d3 456 | move.w %d2,%d3 /* d3 = Ahi/B */ 457 | swap %d3 458 | 459 | move.w %d0,%d2 /* d2 = REM << 16 + Alo */ 460 | divu %d1,%d2 /* d2 = REM:ANS of stuff/B */ 461 | 462 | move.l %d2,%d1 463 | clr.w %d1 464 | swap %d1 /* d1 = REM */ 465 | 466 | clr.l %d0 467 | move.w %d2,%d0 468 | add.l %d3,%d0 /* d0 = ANS */ 469 | move.l %a2,%d3 /* restore d3 */ 470 | rts 471 | * A>B, B > 1 472 | slower: 473 | move.l #1,%d2 474 | clr.l %d3 475 | moreadj: 476 | cmp.l %d0,%d1 477 | bhs adj 478 | add.l %d2,%d2 479 | add.l %d1,%d1 480 | bpl moreadj 481 | * we shifted B until its >A or sign bit set 482 | * we shifted #1 (d2) along with it 483 | adj: 484 | cmp.l %d0,%d1 485 | bhi ltuns 486 | or.l %d2,%d3 487 | sub.l %d1,%d0 488 | ltuns: 489 | lsr.l #1,%d1 490 | lsr.l #1,%d2 491 | bne adj 492 | * d3=answer, d0=rem 493 | move.l %d0,%d1 494 | move.l %d3,%d0 495 | move.l %a2,%d3 /* restore d3 */ 496 | rts 497 | -------------------------------------------------------------------------------- /sega/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/sega/background.png -------------------------------------------------------------------------------- /spacer/SpacerHarry/makeBackground.py: -------------------------------------------------------------------------------- 1 | from PIL import Image, ImageDraw 2 | 3 | 4 | width = 512 5 | midpint = width / 2 6 | bottomStripWidth = 32 7 | topRow = 113 8 | bottomLeftStart = ( width - bottomStripWidth * width ) / 2; 9 | height = 224 10 | 11 | img = Image.new( mode="RGB", size = (width,height), color = (85,146,42) ) 12 | dImage = ImageDraw.Draw( img ) 13 | 14 | shape = [ (0, 0), ( 0, topRow - 1 ), (width-1, topRow - 1), (width-1,0), (0,0) ] 15 | dImage.polygon( shape, fill = (255,0,255) , outline=(255,0,255)) 16 | 17 | 18 | for x in range( 0, width,2 ): 19 | shape = [ (x, topRow), ( bottomLeftStart + x*bottomStripWidth, height - 1 ), (bottomLeftStart + x*bottomStripWidth + bottomStripWidth, height - 1), (x,topRow) ] 20 | dImage.polygon( shape, fill = (183,182,80) , outline=(183,182,80)) 21 | 22 | 23 | 24 | img.save("starter.png") 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /spacer/SpacerHarry/res/background/ground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/spacer/SpacerHarry/res/background/ground.png -------------------------------------------------------------------------------- /spacer/SpacerHarry/res/resources.res: -------------------------------------------------------------------------------- 1 | IMAGE ground "background/ground.png" BEST 2 | 3 | SPRITE player "sprites/player.png" 7 10 NONE 1 4 | SPRITE shadow "sprites/shadow.png" 7 2 NONE 0 5 | 6 | SPRITE boss "sprites/boss.png" 12 14 NONE 0 7 | 8 | PALETTE ground_pal "background/ground.png" 9 | PALETTE player_pal "sprites/player.png" 10 | PALETTE boss_pal "sprites/boss.png" 11 | -------------------------------------------------------------------------------- /spacer/SpacerHarry/res/sprites/boss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/spacer/SpacerHarry/res/sprites/boss.png -------------------------------------------------------------------------------- /spacer/SpacerHarry/res/sprites/player.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/spacer/SpacerHarry/res/sprites/player.bmp -------------------------------------------------------------------------------- /spacer/SpacerHarry/res/sprites/player.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/spacer/SpacerHarry/res/sprites/player.png -------------------------------------------------------------------------------- /spacer/SpacerHarry/res/sprites/shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/spacer/SpacerHarry/res/sprites/shadow.png -------------------------------------------------------------------------------- /spacer/SpacerHarry/src/HInter.s: -------------------------------------------------------------------------------- 1 | 2 | 3 | *------------------------------------------------ 4 | * LABELS 5 | *------------------------------------------------ 6 | .set VDP_CTRL, 0xC00004 7 | .set VDP_DATA, 0xC00000 8 | 9 | .set PAL0_COLOR0, 0xC0000000 10 | .set PAL0_COLOR1, 0xC0020000 11 | .set PAL0_COLOR2, 0xC0040000 12 | .set PAL0_COLOR3, 0xC0060000 13 | .set PAL0_COLOR4, 0xC0080000 14 | 15 | .set VSCROLL_A, 0x40000010 16 | .set VSCROLL_B, 0x40020010 17 | .set HSCROLL_A, 0x7C000002 18 | .set HSCROLL_B, 0x7C020002 19 | 20 | .set LINE_DARK_COLOR, 0x4AA 21 | .set LINE_LIGHT_COLOR, 0xAFF 22 | 23 | .set GRASS_DARK_COLOR, 0x084 24 | .set GRASS_LIGHT_COLOR, 0x0B0 25 | 26 | *------------------------------------------------ 27 | * Functions 28 | *------------------------------------------------ 29 | .text 30 | .even 31 | .align 2 32 | .globl HInter 33 | .type HInter, @function 34 | 35 | HInter: 36 | move.l %d0, -(%sp) /* push device register 0 onto the stack */ 37 | 38 | clr.l %d0 /* Clear D0 and read the current scanline to it */ 39 | move.b (0xC00008), %d0 40 | 41 | 42 | cmp.w #20, %d0 43 | jgt WORK /* do if current line is below horizon */ 44 | 45 | move.l (%sp)+, %d0 /* restore data register 0 */ 46 | rte 47 | 48 | WORK: 49 | lsl.w #1, %d0 50 | move.l %a0, -(%sp) /* push address register 0 onto the stack */ 51 | 52 | 53 | lea VscrollA, %a0 /* get 'vscrollA' array effective address to A0 */ 54 | move.l #VSCROLL_A, 0xC00004 /* Vertical scrolling */ 55 | move.w (%a0, %d0.w), 0xC00000 56 | 57 | 58 | lea colors, %a0 /* get 'colors' array effective address to A0 */ 59 | move.w (%a0,%d0.w),%d0 /* check shading for current scanline */ 60 | move.l (%sp)+, %a0 /* restore address register 0 */ 61 | 62 | tst.w %d0 /* check shading value */ 63 | jeq LIGHT /* jump to light coloring */ 64 | 65 | 66 | DARK: 67 | LINE_DARK: 68 | move.b #2, %d0 69 | cmp.b lineColor, %d0 70 | jne SET_LINE_DARK /* if not dark, set the color */ 71 | jmp GRASS_DARK 72 | move.l (%sp)+, %d0 /* restore data register 0 */ 73 | rte 74 | 75 | SET_LINE_DARK: 76 | 77 | move.b %d0, lineColor 78 | move.l #PAL0_COLOR1, VDP_CTRL /* Tell VDP we want to change color 1 */ 79 | clr.w %d0 80 | 81 | LINE_DELAY1: 82 | move.w (VDP_CTRL), %d0 /* wait before we set color (to minimize dots) */ 83 | btst.b #0x02, %d0 84 | beq LINE_DELAY1 85 | 86 | move.w #LINE_DARK_COLOR, VDP_DATA /* set the color */ 87 | 88 | move.l (%sp)+, %d0 /* restore data register 0 */ 89 | rte 90 | 91 | GRASS_DARK: 92 | move.b #2, %d0 93 | cmp.b grassColor, %d0 94 | jne SET_GRASS_DARK /* if not dark, set the color */ 95 | move.l (%sp)+, %d0 /* restore data register 0 */ 96 | rte 97 | 98 | SET_GRASS_DARK: 99 | 100 | move.b %d0, grassColor 101 | move.l #PAL0_COLOR2, VDP_CTRL /* Tell VDP we want to change color 1 */ 102 | clr.w %d0 103 | 104 | GRASS_DELAY1: 105 | move.w (VDP_CTRL), %d0 /* wait before we set color (to minimize dots) */ 106 | btst.b #0x02, %d0 107 | beq GRASS_DELAY1 108 | 109 | move.w #GRASS_DARK_COLOR, VDP_DATA /* set the color */ 110 | 111 | move.l (%sp)+, %d0 /* restore data register 0 */ 112 | rte 113 | 114 | 115 | LIGHT: 116 | 117 | LINE_LIGHT: 118 | move.b #1, %d0 119 | cmp.b lineColor, %d0 120 | jne SET_LINE_LIGHT /* if not dark, set the color */ 121 | JMP GRASS_LIGHT 122 | move.l (%sp)+, %d0 /* restore data register 0 */ 123 | rte 124 | 125 | SET_LINE_LIGHT: 126 | move.b %d0, lineColor 127 | move.l #PAL0_COLOR1, VDP_CTRL /* Tell VDP we want to change color 1 */ 128 | clr.w %d0 129 | 130 | LINE_DELAY2: 131 | move.w (VDP_CTRL), %d0 /* wait before setting color */ 132 | btst.b #0x02, %d0 133 | beq LINE_DELAY2 134 | 135 | move.w #LINE_LIGHT_COLOR, VDP_DATA /* set the color */ 136 | 137 | move.l (%sp)+, %d0 /* restore data register 0 */ 138 | rte 139 | 140 | GRASS_LIGHT: 141 | move.b #1, %d0 142 | cmp.b grassColor, %d0 143 | jne SET_GRASS_LIGHT /* if not dark, set the color */ 144 | move.l (%sp)+, %d0 /* restore data register 0 */ 145 | rte 146 | 147 | SET_GRASS_LIGHT: 148 | 149 | move.b %d0, grassColor 150 | move.l #PAL0_COLOR2, VDP_CTRL /* Tell VDP we want to change color 1 */ 151 | clr.w %d0 152 | 153 | GRASS_DELAY2: 154 | move.w (VDP_CTRL), %d0 /* wait before we set color (to minimize dots) */ 155 | btst.b #0x02, %d0 156 | beq GRASS_DELAY2 157 | 158 | move.w #GRASS_LIGHT_COLOR, VDP_DATA /* set the color */ 159 | 160 | move.l (%sp)+, %d0 /* restore data register 0 */ 161 | rte 162 | 163 | 164 | 165 | 166 | 167 | *------------------------------------------------ 168 | * DATA 169 | *------------------------------------------------ 170 | .data 171 | .even 172 | .globl colors 173 | .section .bss 174 | .align 2 175 | .type colors, @object 176 | .size colors, 448 177 | colors: 178 | .zero 448 179 | 180 | # 0 unlit, 1 light, 2 dark 181 | .comm lineColor,2,2 182 | .comm grassColor,2,2 183 | 184 | 185 | .comm horizonLine,2,2 186 | 187 | .even 188 | .globl VscrollA 189 | .section .bss 190 | .align 2 191 | .type VscrollA, @object 192 | .size VscrollA, 448 193 | VscrollA: 194 | .zero 448 195 | 196 | -------------------------------------------------------------------------------- /spacer/SpacerHarry/src/boot/sega.s: -------------------------------------------------------------------------------- 1 | #include "task_cst.h" 2 | 3 | .section .text.keepboot 4 | 5 | *------------------------------------------------------- 6 | * 7 | * Sega startup code for the GNU Assembler 8 | * Translated from: 9 | * Sega startup code for the Sozobon C compiler 10 | * Written by Paul W. Lee 11 | * Modified by Charles Coty 12 | * Modified by Stephane Dallongeville 13 | * 14 | *------------------------------------------------------- 15 | 16 | .globl rom_header 17 | 18 | .org 0x00000000 19 | 20 | _Start_Of_Rom: 21 | _Vecteurs_68K: 22 | dc.l __stack /* Stack address */ 23 | dc.l _Entry_Point /* Program start address */ 24 | dc.l _Bus_Error 25 | dc.l _Address_Error 26 | dc.l _Illegal_Instruction 27 | dc.l _Zero_Divide 28 | dc.l _Chk_Instruction 29 | dc.l _Trapv_Instruction 30 | dc.l _Privilege_Violation 31 | dc.l _Trace 32 | dc.l _Line_1010_Emulation 33 | dc.l _Line_1111_Emulation 34 | dc.l _Error_Exception, _Error_Exception, _Error_Exception, _Error_Exception 35 | dc.l _Error_Exception, _Error_Exception, _Error_Exception, _Error_Exception 36 | dc.l _Error_Exception, _Error_Exception, _Error_Exception, _Error_Exception 37 | dc.l _Error_Exception 38 | dc.l _INT 39 | dc.l _EXTINT 40 | dc.l _INT 41 | dc.l HInter 42 | dc.l _INT 43 | dc.l _VINT 44 | dc.l _INT 45 | dc.l _trap_0 /* Resume supervisor task */ 46 | dc.l _INT,_INT,_INT,_INT,_INT,_INT,_INT 47 | dc.l _INT,_INT,_INT,_INT,_INT,_INT,_INT,_INT 48 | dc.l _INT,_INT,_INT,_INT,_INT,_INT,_INT,_INT 49 | dc.l _INT,_INT,_INT,_INT,_INT,_INT,_INT,_INT 50 | 51 | rom_header: 52 | .incbin "out/rom_head.bin", 0, 0x100 53 | 54 | _Entry_Point: 55 | move #0x2700,%sr 56 | tst.l 0xa10008 57 | bne.s SkipJoyDetect 58 | 59 | tst.w 0xa1000c 60 | 61 | SkipJoyDetect: 62 | bne.s SkipSetup 63 | 64 | lea Table,%a5 65 | movem.w (%a5)+,%d5-%d7 66 | movem.l (%a5)+,%a0-%a4 67 | * Check Version Number 68 | move.b -0x10ff(%a1),%d0 69 | andi.b #0x0f,%d0 70 | beq.s WrongVersion 71 | 72 | * Sega Security Code (SEGA) 73 | move.l #0x53454741,0x2f00(%a1) 74 | WrongVersion: 75 | * Read from the control port to cancel any pending read/write command 76 | move.w (%a4),%d0 77 | 78 | * Configure a USER_STACK_LENGTH bytes user stack at bottom, and system stack on top of it 79 | move %sp, %usp 80 | sub #USER_STACK_LENGTH, %sp 81 | 82 | move.w %d7,(%a1) 83 | move.w %d7,(%a2) 84 | 85 | * Jump to initialisation process now... 86 | 87 | jmp _start_entry 88 | 89 | SkipSetup: 90 | jmp _reset_entry 91 | 92 | 93 | Table: 94 | dc.w 0x8000,0x3fff,0x0100 95 | dc.l 0xA00000,0xA11100,0xA11200,0xC00000,0xC00004 96 | 97 | 98 | *------------------------------------------------ 99 | * 100 | * interrupt functions 101 | * 102 | *------------------------------------------------ 103 | 104 | registersDump: 105 | move.l %d0,registerState+0 106 | move.l %d1,registerState+4 107 | move.l %d2,registerState+8 108 | move.l %d3,registerState+12 109 | move.l %d4,registerState+16 110 | move.l %d5,registerState+20 111 | move.l %d6,registerState+24 112 | move.l %d7,registerState+28 113 | move.l %a0,registerState+32 114 | move.l %a1,registerState+36 115 | move.l %a2,registerState+40 116 | move.l %a3,registerState+44 117 | move.l %a4,registerState+48 118 | move.l %a5,registerState+52 119 | move.l %a6,registerState+56 120 | move.l %a7,registerState+60 121 | rts 122 | 123 | busAddressErrorDump: 124 | move.w 4(%sp),ext1State 125 | move.l 6(%sp),addrState 126 | move.w 10(%sp),ext2State 127 | move.w 12(%sp),srState 128 | move.l 14(%sp),pcState 129 | jmp registersDump 130 | 131 | exception4WDump: 132 | move.w 4(%sp),srState 133 | move.l 6(%sp),pcState 134 | move.w 10(%sp),ext1State 135 | jmp registersDump 136 | 137 | exceptionDump: 138 | move.w 4(%sp),srState 139 | move.l 6(%sp),pcState 140 | jmp registersDump 141 | 142 | 143 | _Bus_Error: 144 | jsr busAddressErrorDump 145 | movem.l %d0-%d1/%a0-%a1,-(%sp) 146 | move.l busErrorCB, %a0 147 | jsr (%a0) 148 | movem.l (%sp)+,%d0-%d1/%a0-%a1 149 | rte 150 | 151 | _Address_Error: 152 | jsr busAddressErrorDump 153 | movem.l %d0-%d1/%a0-%a1,-(%sp) 154 | move.l addressErrorCB, %a0 155 | jsr (%a0) 156 | movem.l (%sp)+,%d0-%d1/%a0-%a1 157 | rte 158 | 159 | _Illegal_Instruction: 160 | jsr exception4WDump 161 | movem.l %d0-%d1/%a0-%a1,-(%sp) 162 | move.l illegalInstCB, %a0 163 | jsr (%a0) 164 | movem.l (%sp)+,%d0-%d1/%a0-%a1 165 | rte 166 | 167 | _Zero_Divide: 168 | jsr exceptionDump 169 | movem.l %d0-%d1/%a0-%a1,-(%sp) 170 | move.l zeroDivideCB, %a0 171 | jsr (%a0) 172 | movem.l (%sp)+,%d0-%d1/%a0-%a1 173 | rte 174 | 175 | _Chk_Instruction: 176 | jsr exception4WDump 177 | movem.l %d0-%d1/%a0-%a1,-(%sp) 178 | move.l chkInstCB, %a0 179 | jsr (%a0) 180 | movem.l (%sp)+,%d0-%d1/%a0-%a1 181 | rte 182 | 183 | _Trapv_Instruction: 184 | jsr exception4WDump 185 | movem.l %d0-%d1/%a0-%a1,-(%sp) 186 | move.l trapvInstCB, %a0 187 | jsr (%a0) 188 | movem.l (%sp)+,%d0-%d1/%a0-%a1 189 | rte 190 | 191 | _Privilege_Violation: 192 | jsr exceptionDump 193 | movem.l %d0-%d1/%a0-%a1,-(%sp) 194 | move.l privilegeViolationCB, %a0 195 | jsr (%a0) 196 | movem.l (%sp)+,%d0-%d1/%a0-%a1 197 | rte 198 | 199 | _Trace: 200 | jsr exceptionDump 201 | movem.l %d0-%d1/%a0-%a1,-(%sp) 202 | move.l traceCB, %a0 203 | jsr (%a0) 204 | movem.l (%sp)+,%d0-%d1/%a0-%a1 205 | rte 206 | 207 | _Line_1010_Emulation: 208 | _Line_1111_Emulation: 209 | jsr exceptionDump 210 | movem.l %d0-%d1/%a0-%a1,-(%sp) 211 | move.l line1x1xCB, %a0 212 | jsr (%a0) 213 | movem.l (%sp)+,%d0-%d1/%a0-%a1 214 | rte 215 | 216 | _Error_Exception: 217 | jsr exceptionDump 218 | movem.l %d0-%d1/%a0-%a1,-(%sp) 219 | move.l errorExceptionCB, %a0 220 | jsr (%a0) 221 | movem.l (%sp)+,%d0-%d1/%a0-%a1 222 | rte 223 | 224 | _INT: 225 | movem.l %d0-%d1/%a0-%a1,-(%sp) 226 | move.l intCB, %a0 227 | jsr (%a0) 228 | movem.l (%sp)+,%d0-%d1/%a0-%a1 229 | rte 230 | 231 | _EXTINT: 232 | movem.l %d0-%d1/%a0-%a1,-(%sp) 233 | move.l eintCB, %a0 234 | jsr (%a0) 235 | movem.l (%sp)+,%d0-%d1/%a0-%a1 236 | rte 237 | 238 | _VINT: 239 | btst #5, (%sp) /* Skip context switch if not in user task */ 240 | bne.s no_user_task 241 | 242 | tst.w task_lock 243 | bne.s 1f 244 | move.w #0, -(%sp) /* TSK_superPend() will return 0 */ 245 | bra.s unlock /* If lock == 0, supervisor task is not locked */ 246 | 247 | 1: 248 | bcs.s no_user_task /* If lock < 0, super is locked with infinite wait */ 249 | subq.w #1, task_lock /* Locked with wait, subtract 1 to the frame count */ 250 | bne.s no_user_task /* And do not unlock if we did not reach 0 */ 251 | move.w #1, -(%sp) /* TSK_superPend() will return 1 */ 252 | 253 | unlock: 254 | /* Save bg task registers (excepting a7, that is stored in usp) */ 255 | move.l %a0, task_regs 256 | lea (task_regs + UTSK_REGS_LEN), %a0 257 | movem.l %d0-%d7/%a1-%a6, -(%a0) 258 | 259 | move.w (%sp)+, %d0 /* Load return value previously pushed to stack */ 260 | 261 | move.w (%sp)+, task_sr /* Pop user task sr and pc, and save them, */ 262 | move.l (%sp)+, task_pc /* so they can be restored later. */ 263 | movem.l (%sp)+, %d2-%d7/%a2-%a6 /* Restore non clobberable registers */ 264 | 265 | no_user_task: 266 | /* At this point, we always have in the stack the SR and PC of the task */ 267 | /* we want to jump after processing the interrupt, that might be the */ 268 | /* point where we came from (if there is no context switch) or the */ 269 | /* supervisor task (if we unlocked it). */ 270 | 271 | movem.l %d0-%d1/%a0-%a1,-(%sp) 272 | ori.w #0x0001, intTrace /* in V-Int */ 273 | addq.l #1, vtimer /* increment frame counter (more a vint counter) */ 274 | btst #3, VBlankProcess+1 /* PROCESS_XGM_TASK ? (use VBlankProcess+1 as btst is a byte operation) */ 275 | beq.s no_xgm_task 276 | 277 | jsr XGM_doVBlankProcess /* do XGM vblank task */ 278 | 279 | no_xgm_task: 280 | btst #1, VBlankProcess+1 /* PROCESS_BITMAP_TASK ? (use VBlankProcess+1 as btst is a byte operation) */ 281 | beq.s no_bmp_task 282 | 283 | jsr BMP_doVBlankProcess /* do BMP vblank task */ 284 | 285 | no_bmp_task: 286 | move.l vintCB, %a0 /* load user callback */ 287 | jsr (%a0) /* call user callback */ 288 | andi.w #0xFFFE, intTrace /* out V-Int */ 289 | movem.l (%sp)+,%d0-%d1/%a0-%a1 290 | rte 291 | 292 | *------------------------------------------------ 293 | * 294 | * Copyright (c) 1988 by Sozobon, Limited. Author: Johann Ruegg 295 | * 296 | * Permission is granted to anyone to use this software for any purpose 297 | * on any computer system, and to redistribute it freely, with the 298 | * following restrictions: 299 | * 1) No charge may be made other than reasonable charges for reproduction. 300 | * 2) Modified versions must be clearly marked as such. 301 | * 3) The authors are not responsible for any harmful consequences 302 | * of using this software, even if they result from defects in it. 303 | * 304 | *------------------------------------------------ 305 | 306 | ldiv: 307 | move.l 4(%a7),%d0 308 | bpl ld1 309 | neg.l %d0 310 | ld1: 311 | move.l 8(%a7),%d1 312 | bpl ld2 313 | neg.l %d1 314 | eor.b #0x80,4(%a7) 315 | ld2: 316 | bsr i_ldiv /* d0 = d0/d1 */ 317 | tst.b 4(%a7) 318 | bpl ld3 319 | neg.l %d0 320 | ld3: 321 | rts 322 | 323 | lmul: 324 | move.l 4(%a7),%d0 325 | bpl lm1 326 | neg.l %d0 327 | lm1: 328 | move.l 8(%a7),%d1 329 | bpl lm2 330 | neg.l %d1 331 | eor.b #0x80,4(%a7) 332 | lm2: 333 | bsr i_lmul /* d0 = d0*d1 */ 334 | tst.b 4(%a7) 335 | bpl lm3 336 | neg.l %d0 337 | lm3: 338 | rts 339 | 340 | lrem: 341 | move.l 4(%a7),%d0 342 | bpl lr1 343 | neg.l %d0 344 | lr1: 345 | move.l 8(%a7),%d1 346 | bpl lr2 347 | neg.l %d1 348 | lr2: 349 | bsr i_ldiv /* d1 = d0%d1 */ 350 | move.l %d1,%d0 351 | tst.b 4(%a7) 352 | bpl lr3 353 | neg.l %d0 354 | lr3: 355 | rts 356 | 357 | ldivu: 358 | move.l 4(%a7),%d0 359 | move.l 8(%a7),%d1 360 | bsr i_ldiv 361 | rts 362 | 363 | lmulu: 364 | move.l 4(%a7),%d0 365 | move.l 8(%a7),%d1 366 | bsr i_lmul 367 | rts 368 | 369 | lremu: 370 | move.l 4(%a7),%d0 371 | move.l 8(%a7),%d1 372 | bsr i_ldiv 373 | move.l %d1,%d0 374 | rts 375 | * 376 | * A in d0, B in d1, return A*B in d0 377 | * 378 | i_lmul: 379 | move.l %d3,%a2 /* save d3 */ 380 | move.w %d1,%d2 381 | mulu %d0,%d2 /* d2 = Al * Bl */ 382 | 383 | move.l %d1,%d3 384 | swap %d3 385 | mulu %d0,%d3 /* d3 = Al * Bh */ 386 | 387 | swap %d0 388 | mulu %d1,%d0 /* d0 = Ah * Bl */ 389 | 390 | add.l %d3,%d0 /* d0 = (Ah*Bl + Al*Bh) */ 391 | swap %d0 392 | clr.w %d0 /* d0 = (Ah*Bl + Al*Bh) << 16 */ 393 | 394 | add.l %d2,%d0 /* d0 = A*B */ 395 | move.l %a2,%d3 /* restore d3 */ 396 | rts 397 | * 398 | *A in d0, B in d1, return A/B in d0, A%B in d1 399 | * 400 | i_ldiv: 401 | tst.l %d1 402 | bne nz1 403 | 404 | * divide by zero 405 | * divu #0,%d0 /* cause trap */ 406 | move.l #0x80000000,%d0 407 | move.l %d0,%d1 408 | rts 409 | nz1: 410 | move.l %d3,%a2 /* save d3 */ 411 | cmp.l %d1,%d0 412 | bhi norm 413 | beq is1 414 | * AB and B is not 0 426 | norm: 427 | cmp.l #1,%d1 428 | bne not1 429 | * B==1, so ret A, rem 0 430 | clr.l %d1 431 | move.l %a2,%d3 /* restore d3 */ 432 | rts 433 | * check for A short (implies B short also) 434 | not1: 435 | cmp.l #0xffff,%d0 436 | bhi slow 437 | * A short and B short -- use 'divu' 438 | divu %d1,%d0 /* d0 = REM:ANS */ 439 | swap %d0 /* d0 = ANS:REM */ 440 | clr.l %d1 441 | move.w %d0,%d1 /* d1 = REM */ 442 | clr.w %d0 443 | swap %d0 444 | move.l %a2,%d3 /* restore d3 */ 445 | rts 446 | * check for B short 447 | slow: 448 | cmp.l #0xffff,%d1 449 | bhi slower 450 | * A long and B short -- use special stuff from gnu 451 | move.l %d0,%d2 452 | clr.w %d2 453 | swap %d2 454 | divu %d1,%d2 /* d2 = REM:ANS of Ahi/B */ 455 | clr.l %d3 456 | move.w %d2,%d3 /* d3 = Ahi/B */ 457 | swap %d3 458 | 459 | move.w %d0,%d2 /* d2 = REM << 16 + Alo */ 460 | divu %d1,%d2 /* d2 = REM:ANS of stuff/B */ 461 | 462 | move.l %d2,%d1 463 | clr.w %d1 464 | swap %d1 /* d1 = REM */ 465 | 466 | clr.l %d0 467 | move.w %d2,%d0 468 | add.l %d3,%d0 /* d0 = ANS */ 469 | move.l %a2,%d3 /* restore d3 */ 470 | rts 471 | * A>B, B > 1 472 | slower: 473 | move.l #1,%d2 474 | clr.l %d3 475 | moreadj: 476 | cmp.l %d0,%d1 477 | bhs adj 478 | add.l %d2,%d2 479 | add.l %d1,%d1 480 | bpl moreadj 481 | * we shifted B until its >A or sign bit set 482 | * we shifted #1 (d2) along with it 483 | adj: 484 | cmp.l %d0,%d1 485 | bhi ltuns 486 | or.l %d2,%d3 487 | sub.l %d1,%d0 488 | ltuns: 489 | lsr.l #1,%d1 490 | lsr.l #1,%d2 491 | bne adj 492 | * d3=answer, d0=rem 493 | move.l %d0,%d1 494 | move.l %d3,%d0 495 | move.l %a2,%d3 /* restore d3 */ 496 | rts 497 | -------------------------------------------------------------------------------- /spacer/SpacerHarry/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "resources.h" 3 | 4 | #define VERTICAL_REZ 224 5 | // image is 512x224. Screen is 320, we want to move halfway 6 | // 512/2 - 320 /2 7 | #define SCROLL_CENTER -96 8 | 9 | // Keep track of the current line during Horizontal Interrupts 10 | u16 lineDisplay = 0; 11 | 12 | // Horizontal Scrolling values 13 | s16 HscrollA[VERTICAL_REZ]; 14 | // Vertical Scrolling values ( to simulate hills ) 15 | extern u16 VscrollA[VERTICAL_REZ]; 16 | 17 | // color array for grass and road stripe palette changes. 18 | extern u16 colors[VERTICAL_REZ]; 19 | 20 | // Zmap for tracking segment position - 21 | #define ZMAP_LENGTH 111 22 | fix32 zmap[ZMAP_LENGTH]; 23 | fix32 hScrollIncrement1[ZMAP_LENGTH]; 24 | fix32 hScrollIncrement2[ZMAP_LENGTH]; 25 | fix32 hScrollIncrement3[ZMAP_LENGTH]; 26 | fix32 workScrollA[VERTICAL_REZ]; // working buffer to calculate actual scroll value 27 | s16 scrollSteps = 0; 28 | 29 | 30 | fix32 colorCyclePosition = FIX32(0); // keep track of the color cycling position 31 | fix16 backgroundPosition = FIX16(SCROLL_CENTER); // handle background X position 32 | 33 | extern s16 horizonLine; // keep track of where the horizon is 34 | 35 | fix32 centerLine = FIX32(160); // center line at the front (bottom) of the screen 36 | 37 | // Sprites 38 | struct CP_SPRITE 39 | { 40 | Sprite *sprite; 41 | fix32 posX; // horizontal screen position 42 | fix32 posY; // vertical screen position 43 | fix32 posZ; // z position along the visible segments. 44 | fix32 position; // track position along the *entire* road. start it at the farthest on background - 12.5 45 | fix32 speed; // sped the car travles through the road 46 | u16 segment_index; // current road segment of the sprite 47 | u16 offsetY; // amount to move the sprite up (since these aren't centered) 48 | u16 offsetX; // amount to move the sprite left (since these aren't centered) 49 | u16 updateY; // refresh flag for update() 50 | }; 51 | s16 playerXDir = 0; 52 | s16 playerYDir = 0; 53 | 54 | struct CP_SPRITE *playerSprite; 55 | struct CP_SPRITE *playerShadowSprite; 56 | struct CP_SPRITE *bossSprite; 57 | struct CP_SPRITE *bossShadowSprite; 58 | 59 | // joypad event handler. This gets called automatically by SGDK when the joypad 60 | // state changes 61 | static void joypadHandler(u16 joypadId, u16 changed, u16 state) 62 | { 63 | if (joypadId == JOY_1) 64 | { 65 | if (state & BUTTON_RIGHT) 66 | { 67 | playerXDir = 1; // Speed up 68 | } 69 | else if (changed & BUTTON_RIGHT) 70 | { 71 | playerXDir = 0; // state change A-up. no longer accelerating. 72 | } 73 | 74 | if (state & BUTTON_LEFT) 75 | { 76 | playerXDir = -1; // Slow down 77 | } 78 | else if (changed & BUTTON_LEFT) 79 | { 80 | playerXDir = 0; 81 | } 82 | } 83 | else if (state & BUTTON_DOWN) 84 | { 85 | } 86 | } 87 | 88 | void updatePlayer() 89 | { 90 | // player position affects H/Y 91 | if (playerXDir < 0) 92 | { 93 | playerSprite->posX = fix32Sub(playerSprite->posX, FIX32(2)); 94 | if (playerSprite->posX < FIX32(playerSprite->offsetX)) 95 | { 96 | playerSprite->posX = FIX32(playerSprite->offsetX); 97 | } 98 | } 99 | else if (playerXDir > 0) 100 | { 101 | playerSprite->posX = fix32Add(playerSprite->posX, FIX32(2)); 102 | if (playerSprite->posX > FIX32(292)) 103 | { 104 | playerSprite->posX = FIX32(292); 105 | } 106 | } 107 | playerShadowSprite->posX = playerSprite->posX; 108 | 109 | // determine frame 110 | SPR_setFrame(playerSprite->sprite, 0); 111 | } 112 | 113 | // My interpretation of the pseudo-code in 114 | // http://www.extentofthejam.com/pseudo/#curves 115 | void update() 116 | { 117 | // COLORS /////////////////////////////////////////////////////// 118 | colorCyclePosition = fix32Sub(colorCyclePosition, playerSprite->speed); 119 | if (fix32ToInt(colorCyclePosition) < 0) 120 | { 121 | colorCyclePosition = zmap[ZMAP_LENGTH - 1]; // Send segment to farthest visible distance 122 | } 123 | 124 | horizonLine = 223 - ZMAP_LENGTH; 125 | for (u16 i = 223, j = horizonLine; i > 223 - ZMAP_LENGTH; --i, ++j) 126 | { 127 | fix32 tmpz = fix32Sub(colorCyclePosition, zmap[i-ZMAP_LENGTH]); 128 | u16 zcolor = (u16)fix32ToInt(tmpz << 2); // >> 1); 129 | colors[j] = zcolor & 1; 130 | } 131 | 132 | // HORIZONTAL SCROLLING //////////////////////////////////////// 133 | if (fix32ToInt(playerSprite->posX) < 68) 134 | { 135 | for (int i = 223, j = ZMAP_LENGTH - 1; i > 223 - ZMAP_LENGTH; --i, --j) 136 | { 137 | workScrollA[i] = fix32Add(workScrollA[i], hScrollIncrement3[j]); 138 | HscrollA[i] = fix32ToInt(workScrollA[i]) + SCROLL_CENTER; 139 | } 140 | scrollSteps += 8; 141 | } 142 | else if (fix32ToInt(playerSprite->posX) < 138) 143 | { 144 | 145 | for (int i = 223, j = ZMAP_LENGTH - 1; i > 223 - ZMAP_LENGTH; --i, --j) 146 | { 147 | workScrollA[i] = fix32Add(workScrollA[i], hScrollIncrement2[j]); 148 | HscrollA[i] = fix32ToInt(workScrollA[i]) + SCROLL_CENTER; 149 | } 150 | scrollSteps += 4; 151 | } 152 | else if (fix32ToInt(playerSprite->posX) < 150) 153 | { 154 | 155 | for (int i = 223, j = ZMAP_LENGTH - 1; i > 223 - ZMAP_LENGTH; --i, --j) 156 | { 157 | workScrollA[i] = fix32Add(workScrollA[i], hScrollIncrement1[j]); 158 | HscrollA[i] = fix32ToInt(workScrollA[i]) + SCROLL_CENTER; 159 | } 160 | scrollSteps += 2; 161 | } 162 | else if (fix32ToInt(playerSprite->posX) > 252) 163 | { 164 | for (int i = 223, j = ZMAP_LENGTH - 1; i > 223 - ZMAP_LENGTH; --i, --j) 165 | { 166 | workScrollA[i] = fix32Sub(workScrollA[i], hScrollIncrement3[j]); 167 | HscrollA[i] = fix32ToInt(workScrollA[i]) + SCROLL_CENTER; 168 | } 169 | scrollSteps -= 8; 170 | } 171 | else if (fix32ToInt(playerSprite->posX) > 190) 172 | { 173 | for (int i = 223, j = ZMAP_LENGTH - 1; i > 223 - ZMAP_LENGTH; --i, --j) 174 | { 175 | workScrollA[i] = fix32Sub(workScrollA[i], hScrollIncrement2[j]); 176 | HscrollA[i] = fix32ToInt(workScrollA[i]) + SCROLL_CENTER; 177 | } 178 | scrollSteps -= 4; 179 | } 180 | else if (fix32ToInt(playerSprite->posX) > 170) 181 | { 182 | for (int i = 223, j = ZMAP_LENGTH - 1; i > 223 - ZMAP_LENGTH; --i, --j) 183 | { 184 | workScrollA[i] = fix32Sub(workScrollA[i], hScrollIncrement1[j]); 185 | HscrollA[i] = fix32ToInt(workScrollA[i]) + SCROLL_CENTER; 186 | } 187 | scrollSteps -= 2; 188 | } 189 | 190 | if (scrollSteps >= 64 || scrollSteps <= -64) 191 | { 192 | scrollSteps = 0; 193 | memset(workScrollA, 0, sizeof(workScrollA)); 194 | } 195 | } 196 | 197 | int main(bool hard) 198 | { 199 | 200 | ////////////////////////////////////////////////////////////// 201 | // precalculate some stuff 202 | fix32 step1 = fix32Div(FIX32(2), FIX32(ZMAP_LENGTH)); // divide bottom scroll increment by the height of the ground graphic. 203 | fix32 step2 = fix32Div(FIX32(4), FIX32(ZMAP_LENGTH)); 204 | fix32 step3 = fix32Div(FIX32(8), FIX32(ZMAP_LENGTH)); 205 | fix32 currentXDelta1 = FIX32(0); 206 | fix32 currentXDelta2 = FIX32(0); 207 | fix32 currentXDelta3 = FIX32(0); 208 | for (int i = 0; i < ZMAP_LENGTH; ++i) 209 | { 210 | // http://www.extentofthejam.com/pseudo/ 211 | // Z = Y_world / (Y_screen - (height_screen / 2)) 212 | zmap[i] = fix32Div(FIX32(-75), fix32Sub(FIX32(i), FIX32(113))); 213 | hScrollIncrement1[i] = currentXDelta1; 214 | currentXDelta1 = fix32Add(currentXDelta1, step1); 215 | hScrollIncrement2[i] = currentXDelta2; 216 | currentXDelta2 = fix32Add(currentXDelta2, step2); 217 | hScrollIncrement3[i] = currentXDelta3; 218 | currentXDelta3 = fix32Add(currentXDelta3, step3); 219 | KLog_F4("i: ", FIX32(i), " z: ", zmap[i], " d4: ", hScrollIncrement2[i], " d8: ", hScrollIncrement3[i]); 220 | } 221 | 222 | ////////////////////////////////////////////////////////////// 223 | // VDP basic setup 224 | VDP_setScreenWidth320(); 225 | VDP_setScrollingMode(HSCROLL_LINE, VSCROLL_PLANE); 226 | 227 | // initialize scrolling arrays 228 | for (int i = 0; i < VERTICAL_REZ; i++) 229 | { 230 | HscrollA[i] = SCROLL_CENTER; 231 | VscrollA[i] = 0; 232 | workScrollA[i] = FIX32(0); 233 | } 234 | 235 | ////////////////////////////////////////////////////////////// 236 | // Setup scroll panes 237 | PAL_setPalette(PAL0, ground_pal.data, CPU); 238 | int ind = TILE_USER_INDEX; 239 | VDP_drawImageEx(BG_A, &ground, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, ind), 0, 0, FALSE, TRUE); 240 | 241 | ////////////////////////////////////////////////////////////// 242 | // Setup Car Sprites 243 | SPR_init(); 244 | // SPR_initEx(900); 245 | PAL_setPalette(PAL1, player_pal.data, CPU); 246 | playerSprite = malloc(sizeof(struct CP_SPRITE)); 247 | playerSprite->position = FIX32(0); 248 | playerSprite->segment_index = 0; 249 | playerSprite->offsetY = 40; // 80~ish tall 250 | playerSprite->offsetX = 28; // 56~ish wide 251 | playerSprite->posX = FIX32(160.0); 252 | playerSprite->posY = FIX32(170.0); // 253 | playerSprite->sprite = SPR_addSprite(&player, // Sprite defined in resources 254 | fix32ToInt(playerSprite->posX) - playerSprite->offsetX, // starting X position 255 | fix32ToInt(playerSprite->posY) - playerSprite->offsetY, // starting Y position 256 | TILE_ATTR(PAL1, // specify palette 257 | 1, // Tile priority ( with background) 258 | FALSE, // flip the sprite vertically? 259 | FALSE // flip the sprite horizontally 260 | )); 261 | playerSprite->speed = FIX32(0.03); 262 | 263 | SPR_setAnim(playerSprite->sprite, 1); 264 | SPR_setFrame(playerSprite->sprite, 0); 265 | SPR_setDepth(playerSprite->sprite, 0); 266 | 267 | playerShadowSprite = malloc(sizeof(struct CP_SPRITE)); 268 | playerShadowSprite->position = FIX32(0); 269 | playerShadowSprite->segment_index = 0; 270 | playerShadowSprite->offsetY = 8; 271 | playerShadowSprite->offsetX = 28; 272 | playerShadowSprite->posX = FIX32(160.0); 273 | playerShadowSprite->posY = FIX32(210.0); // 274 | playerShadowSprite->sprite = SPR_addSprite(&shadow, // Sprite defined in resources 275 | fix32ToInt(playerShadowSprite->posX) - playerShadowSprite->offsetX, // starting X position 276 | fix32ToInt(playerShadowSprite->posY) - playerShadowSprite->offsetY, // starting Y position 277 | TILE_ATTR(PAL1, // specify palette 278 | 1, // Tile priority ( with background) 279 | FALSE, // flip the sprite vertically? 280 | FALSE // flip the sprite horizontally 281 | )); 282 | 283 | PAL_setPalette(PAL2, boss_pal.data, CPU); 284 | bossSprite = malloc(sizeof(struct CP_SPRITE)); 285 | bossSprite->position = FIX32(0); 286 | bossSprite->segment_index = 0; 287 | bossSprite->offsetY = 0; // not using offsets for this demo 288 | bossSprite->offsetX = 0; 289 | bossSprite->posX = FIX32(112.0); 290 | bossSprite->posY = FIX32(60.0); // 291 | bossSprite->sprite = SPR_addSprite(&boss, // Sprite defined in resources 292 | fix32ToInt(bossSprite->posX), // starting X position 293 | fix32ToInt(bossSprite->posY), // starting Y position 294 | TILE_ATTR(PAL2, // specify palette 295 | 1, // Tile priority ( with background) 296 | FALSE, // flip the sprite vertically? 297 | FALSE // flip the sprite horizontally 298 | )); 299 | 300 | bossShadowSprite = malloc(sizeof(struct CP_SPRITE)); 301 | bossShadowSprite->position = FIX32(0); 302 | bossShadowSprite->segment_index = 0; 303 | bossShadowSprite->offsetY = 0; // not using offsets for this demo 304 | bossShadowSprite->offsetX = 0; // 305 | bossShadowSprite->posX = FIX32(132.0); 306 | bossShadowSprite->posY = FIX32(180.0); // 307 | bossShadowSprite->sprite = SPR_addSprite(&shadow, // Sprite defined in resources 308 | fix32ToInt(bossShadowSprite->posX), // starting X position 309 | fix32ToInt(bossShadowSprite->posY), // starting Y position 310 | TILE_ATTR(PAL1, // specify palette 311 | 1, // Tile priority ( with background) 312 | FALSE, // flip the sprite vertically? 313 | FALSE // flip the sprite horizontally 314 | )); 315 | ////////////////////////////////////////////////////////////// 316 | // init segments 317 | colorCyclePosition = zmap[ZMAP_LENGTH - 1]; // put it at the farthest away point 318 | 319 | ////////////////////////////////////////////////////////////// 320 | // Setup interrupt handlers 321 | SYS_disableInts(); 322 | { 323 | VDP_setHIntCounter(0); 324 | VDP_setHInterrupt(1); 325 | } 326 | SYS_enableInts(); 327 | 328 | // Asynchronous joystick handler. 329 | JOY_init(); 330 | JOY_setEventHandler(joypadHandler); 331 | 332 | // Main loop 333 | scrollSteps = 0; 334 | while (TRUE) 335 | { 336 | // update background 337 | update(); 338 | 339 | // Set player position 340 | updatePlayer(); 341 | SPR_setPosition(playerSprite->sprite, fix32ToInt(playerSprite->posX) - playerSprite->offsetX, fix32ToInt(playerSprite->posY) - playerSprite->offsetY); 342 | SPR_setPosition(playerShadowSprite->sprite, fix32ToInt(playerShadowSprite->posX) - playerShadowSprite->offsetX, fix32ToInt(playerShadowSprite->posY) - playerShadowSprite->offsetY); 343 | 344 | // update tsprites 345 | SPR_update(); 346 | 347 | VDP_setHorizontalScrollLine(BG_A, 0, HscrollA, VERTICAL_REZ, DMA_QUEUE); 348 | 349 | SYS_doVBlankProcess(); 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /spacer/SpacerHarry/starter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/spacer/SpacerHarry/starter.png -------------------------------------------------------------------------------- /spacer/SpacerHarry2/res/background/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/spacer/SpacerHarry2/res/background/background.png -------------------------------------------------------------------------------- /spacer/SpacerHarry2/res/background/ground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/spacer/SpacerHarry2/res/background/ground.png -------------------------------------------------------------------------------- /spacer/SpacerHarry2/res/resources.res: -------------------------------------------------------------------------------- 1 | IMAGE ground "background/ground.png" BEST 2 | IMAGE background "background/background.png" BEST 3 | 4 | SPRITE player "sprites/player.png" 7 10 NONE 1 5 | SPRITE shadow "sprites/shadow.png" 7 2 NONE 0 6 | 7 | SPRITE boss "sprites/boss.png" 12 14 NONE 5 8 | 9 | 10 | PALETTE ground_pal "background/ground.png" 11 | PALETTE background_pal "background/background.png" 12 | PALETTE player_pal "sprites/player.png" 13 | PALETTE boss_pal "sprites/boss.png" 14 | -------------------------------------------------------------------------------- /spacer/SpacerHarry2/res/sprites/boss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/spacer/SpacerHarry2/res/sprites/boss.png -------------------------------------------------------------------------------- /spacer/SpacerHarry2/res/sprites/player.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/spacer/SpacerHarry2/res/sprites/player.bmp -------------------------------------------------------------------------------- /spacer/SpacerHarry2/res/sprites/player.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/spacer/SpacerHarry2/res/sprites/player.png -------------------------------------------------------------------------------- /spacer/SpacerHarry2/res/sprites/shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/spacer/SpacerHarry2/res/sprites/shadow.png -------------------------------------------------------------------------------- /spacer/SpacerHarry2/src/HInter.s: -------------------------------------------------------------------------------- 1 | 2 | 3 | *------------------------------------------------ 4 | * LABELS 5 | *------------------------------------------------ 6 | .set VDP_CTRL, 0xC00004 7 | .set VDP_DATA, 0xC00000 8 | 9 | .set PAL0_COLOR0, 0xC0000000 10 | .set PAL0_COLOR1, 0xC0020000 11 | .set PAL0_COLOR2, 0xC0040000 12 | .set PAL0_COLOR3, 0xC0060000 13 | .set PAL0_COLOR4, 0xC0080000 14 | 15 | .set VSCROLL_A, 0x40000010 16 | .set VSCROLL_B, 0x40020010 17 | .set HSCROLL_A, 0x7C000002 18 | .set HSCROLL_B, 0x7C020002 19 | 20 | .set LINE_DARK_COLOR, 0x4AA 21 | .set LINE_LIGHT_COLOR, 0xAFF 22 | 23 | .set GRASS_DARK_COLOR, 0x084 24 | .set GRASS_LIGHT_COLOR, 0x0B0 25 | 26 | *------------------------------------------------ 27 | * Functions 28 | *------------------------------------------------ 29 | .text 30 | .even 31 | .align 2 32 | .globl HInter 33 | .type HInter, @function 34 | 35 | HInter: 36 | move.l %d0, -(%sp) /* push device register 0 onto the stack */ 37 | 38 | clr.l %d0 /* Clear D0 and read the current scanline to it */ 39 | move.b (0xC00008), %d0 40 | 41 | 42 | cmp.w #20, %d0 43 | jgt WORK /* do if current line is below horizon */ 44 | 45 | move.l (%sp)+, %d0 /* restore data register 0 */ 46 | rte 47 | 48 | WORK: 49 | lsl.w #1, %d0 50 | move.l %a0, -(%sp) /* push address register 0 onto the stack */ 51 | 52 | 53 | lea VscrollA, %a0 /* get 'vscrollA' array effective address to A0 */ 54 | move.l #VSCROLL_A, 0xC00004 /* Vertical scrolling */ 55 | move.w (%a0, %d0.w), 0xC00000 56 | 57 | 58 | lea colors, %a0 /* get 'colors' array effective address to A0 */ 59 | move.w (%a0,%d0.w),%d0 /* check shading for current scanline */ 60 | move.l (%sp)+, %a0 /* restore address register 0 */ 61 | 62 | tst.w %d0 /* check shading value */ 63 | jeq LIGHT /* jump to light coloring */ 64 | 65 | 66 | DARK: 67 | LINE_DARK: 68 | move.b #2, %d0 69 | cmp.b lineColor, %d0 70 | jne SET_LINE_DARK /* if not dark, set the color */ 71 | jmp GRASS_DARK 72 | move.l (%sp)+, %d0 /* restore data register 0 */ 73 | rte 74 | 75 | SET_LINE_DARK: 76 | 77 | move.b %d0, lineColor 78 | move.l #PAL0_COLOR1, VDP_CTRL /* Tell VDP we want to change color 1 */ 79 | clr.w %d0 80 | 81 | LINE_DELAY1: 82 | move.w (VDP_CTRL), %d0 /* wait before we set color (to minimize dots) */ 83 | btst.b #0x02, %d0 84 | beq LINE_DELAY1 85 | 86 | move.w #LINE_DARK_COLOR, VDP_DATA /* set the color */ 87 | 88 | move.l (%sp)+, %d0 /* restore data register 0 */ 89 | rte 90 | 91 | GRASS_DARK: 92 | move.b #2, %d0 93 | cmp.b grassColor, %d0 94 | jne SET_GRASS_DARK /* if not dark, set the color */ 95 | move.l (%sp)+, %d0 /* restore data register 0 */ 96 | rte 97 | 98 | SET_GRASS_DARK: 99 | 100 | move.b %d0, grassColor 101 | move.l #PAL0_COLOR2, VDP_CTRL /* Tell VDP we want to change color 1 */ 102 | clr.w %d0 103 | 104 | GRASS_DELAY1: 105 | move.w (VDP_CTRL), %d0 /* wait before we set color (to minimize dots) */ 106 | btst.b #0x02, %d0 107 | beq GRASS_DELAY1 108 | 109 | move.w #GRASS_DARK_COLOR, VDP_DATA /* set the color */ 110 | 111 | move.l (%sp)+, %d0 /* restore data register 0 */ 112 | rte 113 | 114 | 115 | LIGHT: 116 | 117 | LINE_LIGHT: 118 | move.b #1, %d0 119 | cmp.b lineColor, %d0 120 | jne SET_LINE_LIGHT /* if not dark, set the color */ 121 | JMP GRASS_LIGHT 122 | move.l (%sp)+, %d0 /* restore data register 0 */ 123 | rte 124 | 125 | SET_LINE_LIGHT: 126 | move.b %d0, lineColor 127 | move.l #PAL0_COLOR1, VDP_CTRL /* Tell VDP we want to change color 1 */ 128 | clr.w %d0 129 | 130 | LINE_DELAY2: 131 | move.w (VDP_CTRL), %d0 /* wait before setting color */ 132 | btst.b #0x02, %d0 133 | beq LINE_DELAY2 134 | 135 | move.w #LINE_LIGHT_COLOR, VDP_DATA /* set the color */ 136 | 137 | move.l (%sp)+, %d0 /* restore data register 0 */ 138 | rte 139 | 140 | GRASS_LIGHT: 141 | move.b #1, %d0 142 | cmp.b grassColor, %d0 143 | jne SET_GRASS_LIGHT /* if not dark, set the color */ 144 | move.l (%sp)+, %d0 /* restore data register 0 */ 145 | rte 146 | 147 | SET_GRASS_LIGHT: 148 | 149 | move.b %d0, grassColor 150 | move.l #PAL0_COLOR2, VDP_CTRL /* Tell VDP we want to change color 1 */ 151 | clr.w %d0 152 | 153 | GRASS_DELAY2: 154 | move.w (VDP_CTRL), %d0 /* wait before we set color (to minimize dots) */ 155 | btst.b #0x02, %d0 156 | beq GRASS_DELAY2 157 | 158 | move.w #GRASS_LIGHT_COLOR, VDP_DATA /* set the color */ 159 | 160 | move.l (%sp)+, %d0 /* restore data register 0 */ 161 | rte 162 | 163 | 164 | 165 | 166 | 167 | *------------------------------------------------ 168 | * DATA 169 | *------------------------------------------------ 170 | .data 171 | .even 172 | .globl colors 173 | .section .bss 174 | .align 2 175 | .type colors, @object 176 | .size colors, 448 177 | colors: 178 | .zero 448 179 | 180 | # 0 unlit, 1 light, 2 dark 181 | .comm lineColor,2,2 182 | .comm grassColor,2,2 183 | 184 | 185 | .comm horizonLine,2,2 186 | 187 | .even 188 | .globl VscrollA 189 | .section .bss 190 | .align 2 191 | .type VscrollA, @object 192 | .size VscrollA, 448 193 | VscrollA: 194 | .zero 448 195 | 196 | -------------------------------------------------------------------------------- /spacer/SpacerHarry2/src/boot/sega.s: -------------------------------------------------------------------------------- 1 | #include "task_cst.h" 2 | 3 | .section .text.keepboot 4 | 5 | *------------------------------------------------------- 6 | * 7 | * Sega startup code for the GNU Assembler 8 | * Translated from: 9 | * Sega startup code for the Sozobon C compiler 10 | * Written by Paul W. Lee 11 | * Modified by Charles Coty 12 | * Modified by Stephane Dallongeville 13 | * 14 | *------------------------------------------------------- 15 | 16 | .globl rom_header 17 | 18 | .org 0x00000000 19 | 20 | _Start_Of_Rom: 21 | _Vecteurs_68K: 22 | dc.l __stack /* Stack address */ 23 | dc.l _Entry_Point /* Program start address */ 24 | dc.l _Bus_Error 25 | dc.l _Address_Error 26 | dc.l _Illegal_Instruction 27 | dc.l _Zero_Divide 28 | dc.l _Chk_Instruction 29 | dc.l _Trapv_Instruction 30 | dc.l _Privilege_Violation 31 | dc.l _Trace 32 | dc.l _Line_1010_Emulation 33 | dc.l _Line_1111_Emulation 34 | dc.l _Error_Exception, _Error_Exception, _Error_Exception, _Error_Exception 35 | dc.l _Error_Exception, _Error_Exception, _Error_Exception, _Error_Exception 36 | dc.l _Error_Exception, _Error_Exception, _Error_Exception, _Error_Exception 37 | dc.l _Error_Exception 38 | dc.l _INT 39 | dc.l _EXTINT 40 | dc.l _INT 41 | dc.l HInter 42 | dc.l _INT 43 | dc.l _VINT 44 | dc.l _INT 45 | dc.l _trap_0 /* Resume supervisor task */ 46 | dc.l _INT,_INT,_INT,_INT,_INT,_INT,_INT 47 | dc.l _INT,_INT,_INT,_INT,_INT,_INT,_INT,_INT 48 | dc.l _INT,_INT,_INT,_INT,_INT,_INT,_INT,_INT 49 | dc.l _INT,_INT,_INT,_INT,_INT,_INT,_INT,_INT 50 | 51 | rom_header: 52 | .incbin "out/rom_head.bin", 0, 0x100 53 | 54 | _Entry_Point: 55 | move #0x2700,%sr 56 | tst.l 0xa10008 57 | bne.s SkipJoyDetect 58 | 59 | tst.w 0xa1000c 60 | 61 | SkipJoyDetect: 62 | bne.s SkipSetup 63 | 64 | lea Table,%a5 65 | movem.w (%a5)+,%d5-%d7 66 | movem.l (%a5)+,%a0-%a4 67 | * Check Version Number 68 | move.b -0x10ff(%a1),%d0 69 | andi.b #0x0f,%d0 70 | beq.s WrongVersion 71 | 72 | * Sega Security Code (SEGA) 73 | move.l #0x53454741,0x2f00(%a1) 74 | WrongVersion: 75 | * Read from the control port to cancel any pending read/write command 76 | move.w (%a4),%d0 77 | 78 | * Configure a USER_STACK_LENGTH bytes user stack at bottom, and system stack on top of it 79 | move %sp, %usp 80 | sub #USER_STACK_LENGTH, %sp 81 | 82 | move.w %d7,(%a1) 83 | move.w %d7,(%a2) 84 | 85 | * Jump to initialisation process now... 86 | 87 | jmp _start_entry 88 | 89 | SkipSetup: 90 | jmp _reset_entry 91 | 92 | 93 | Table: 94 | dc.w 0x8000,0x3fff,0x0100 95 | dc.l 0xA00000,0xA11100,0xA11200,0xC00000,0xC00004 96 | 97 | 98 | *------------------------------------------------ 99 | * 100 | * interrupt functions 101 | * 102 | *------------------------------------------------ 103 | 104 | registersDump: 105 | move.l %d0,registerState+0 106 | move.l %d1,registerState+4 107 | move.l %d2,registerState+8 108 | move.l %d3,registerState+12 109 | move.l %d4,registerState+16 110 | move.l %d5,registerState+20 111 | move.l %d6,registerState+24 112 | move.l %d7,registerState+28 113 | move.l %a0,registerState+32 114 | move.l %a1,registerState+36 115 | move.l %a2,registerState+40 116 | move.l %a3,registerState+44 117 | move.l %a4,registerState+48 118 | move.l %a5,registerState+52 119 | move.l %a6,registerState+56 120 | move.l %a7,registerState+60 121 | rts 122 | 123 | busAddressErrorDump: 124 | move.w 4(%sp),ext1State 125 | move.l 6(%sp),addrState 126 | move.w 10(%sp),ext2State 127 | move.w 12(%sp),srState 128 | move.l 14(%sp),pcState 129 | jmp registersDump 130 | 131 | exception4WDump: 132 | move.w 4(%sp),srState 133 | move.l 6(%sp),pcState 134 | move.w 10(%sp),ext1State 135 | jmp registersDump 136 | 137 | exceptionDump: 138 | move.w 4(%sp),srState 139 | move.l 6(%sp),pcState 140 | jmp registersDump 141 | 142 | 143 | _Bus_Error: 144 | jsr busAddressErrorDump 145 | movem.l %d0-%d1/%a0-%a1,-(%sp) 146 | move.l busErrorCB, %a0 147 | jsr (%a0) 148 | movem.l (%sp)+,%d0-%d1/%a0-%a1 149 | rte 150 | 151 | _Address_Error: 152 | jsr busAddressErrorDump 153 | movem.l %d0-%d1/%a0-%a1,-(%sp) 154 | move.l addressErrorCB, %a0 155 | jsr (%a0) 156 | movem.l (%sp)+,%d0-%d1/%a0-%a1 157 | rte 158 | 159 | _Illegal_Instruction: 160 | jsr exception4WDump 161 | movem.l %d0-%d1/%a0-%a1,-(%sp) 162 | move.l illegalInstCB, %a0 163 | jsr (%a0) 164 | movem.l (%sp)+,%d0-%d1/%a0-%a1 165 | rte 166 | 167 | _Zero_Divide: 168 | jsr exceptionDump 169 | movem.l %d0-%d1/%a0-%a1,-(%sp) 170 | move.l zeroDivideCB, %a0 171 | jsr (%a0) 172 | movem.l (%sp)+,%d0-%d1/%a0-%a1 173 | rte 174 | 175 | _Chk_Instruction: 176 | jsr exception4WDump 177 | movem.l %d0-%d1/%a0-%a1,-(%sp) 178 | move.l chkInstCB, %a0 179 | jsr (%a0) 180 | movem.l (%sp)+,%d0-%d1/%a0-%a1 181 | rte 182 | 183 | _Trapv_Instruction: 184 | jsr exception4WDump 185 | movem.l %d0-%d1/%a0-%a1,-(%sp) 186 | move.l trapvInstCB, %a0 187 | jsr (%a0) 188 | movem.l (%sp)+,%d0-%d1/%a0-%a1 189 | rte 190 | 191 | _Privilege_Violation: 192 | jsr exceptionDump 193 | movem.l %d0-%d1/%a0-%a1,-(%sp) 194 | move.l privilegeViolationCB, %a0 195 | jsr (%a0) 196 | movem.l (%sp)+,%d0-%d1/%a0-%a1 197 | rte 198 | 199 | _Trace: 200 | jsr exceptionDump 201 | movem.l %d0-%d1/%a0-%a1,-(%sp) 202 | move.l traceCB, %a0 203 | jsr (%a0) 204 | movem.l (%sp)+,%d0-%d1/%a0-%a1 205 | rte 206 | 207 | _Line_1010_Emulation: 208 | _Line_1111_Emulation: 209 | jsr exceptionDump 210 | movem.l %d0-%d1/%a0-%a1,-(%sp) 211 | move.l line1x1xCB, %a0 212 | jsr (%a0) 213 | movem.l (%sp)+,%d0-%d1/%a0-%a1 214 | rte 215 | 216 | _Error_Exception: 217 | jsr exceptionDump 218 | movem.l %d0-%d1/%a0-%a1,-(%sp) 219 | move.l errorExceptionCB, %a0 220 | jsr (%a0) 221 | movem.l (%sp)+,%d0-%d1/%a0-%a1 222 | rte 223 | 224 | _INT: 225 | movem.l %d0-%d1/%a0-%a1,-(%sp) 226 | move.l intCB, %a0 227 | jsr (%a0) 228 | movem.l (%sp)+,%d0-%d1/%a0-%a1 229 | rte 230 | 231 | _EXTINT: 232 | movem.l %d0-%d1/%a0-%a1,-(%sp) 233 | move.l eintCB, %a0 234 | jsr (%a0) 235 | movem.l (%sp)+,%d0-%d1/%a0-%a1 236 | rte 237 | 238 | _VINT: 239 | btst #5, (%sp) /* Skip context switch if not in user task */ 240 | bne.s no_user_task 241 | 242 | tst.w task_lock 243 | bne.s 1f 244 | move.w #0, -(%sp) /* TSK_superPend() will return 0 */ 245 | bra.s unlock /* If lock == 0, supervisor task is not locked */ 246 | 247 | 1: 248 | bcs.s no_user_task /* If lock < 0, super is locked with infinite wait */ 249 | subq.w #1, task_lock /* Locked with wait, subtract 1 to the frame count */ 250 | bne.s no_user_task /* And do not unlock if we did not reach 0 */ 251 | move.w #1, -(%sp) /* TSK_superPend() will return 1 */ 252 | 253 | unlock: 254 | /* Save bg task registers (excepting a7, that is stored in usp) */ 255 | move.l %a0, task_regs 256 | lea (task_regs + UTSK_REGS_LEN), %a0 257 | movem.l %d0-%d7/%a1-%a6, -(%a0) 258 | 259 | move.w (%sp)+, %d0 /* Load return value previously pushed to stack */ 260 | 261 | move.w (%sp)+, task_sr /* Pop user task sr and pc, and save them, */ 262 | move.l (%sp)+, task_pc /* so they can be restored later. */ 263 | movem.l (%sp)+, %d2-%d7/%a2-%a6 /* Restore non clobberable registers */ 264 | 265 | no_user_task: 266 | /* At this point, we always have in the stack the SR and PC of the task */ 267 | /* we want to jump after processing the interrupt, that might be the */ 268 | /* point where we came from (if there is no context switch) or the */ 269 | /* supervisor task (if we unlocked it). */ 270 | 271 | movem.l %d0-%d1/%a0-%a1,-(%sp) 272 | ori.w #0x0001, intTrace /* in V-Int */ 273 | addq.l #1, vtimer /* increment frame counter (more a vint counter) */ 274 | btst #3, VBlankProcess+1 /* PROCESS_XGM_TASK ? (use VBlankProcess+1 as btst is a byte operation) */ 275 | beq.s no_xgm_task 276 | 277 | jsr XGM_doVBlankProcess /* do XGM vblank task */ 278 | 279 | no_xgm_task: 280 | btst #1, VBlankProcess+1 /* PROCESS_BITMAP_TASK ? (use VBlankProcess+1 as btst is a byte operation) */ 281 | beq.s no_bmp_task 282 | 283 | jsr BMP_doVBlankProcess /* do BMP vblank task */ 284 | 285 | no_bmp_task: 286 | move.l vintCB, %a0 /* load user callback */ 287 | jsr (%a0) /* call user callback */ 288 | andi.w #0xFFFE, intTrace /* out V-Int */ 289 | movem.l (%sp)+,%d0-%d1/%a0-%a1 290 | rte 291 | 292 | *------------------------------------------------ 293 | * 294 | * Copyright (c) 1988 by Sozobon, Limited. Author: Johann Ruegg 295 | * 296 | * Permission is granted to anyone to use this software for any purpose 297 | * on any computer system, and to redistribute it freely, with the 298 | * following restrictions: 299 | * 1) No charge may be made other than reasonable charges for reproduction. 300 | * 2) Modified versions must be clearly marked as such. 301 | * 3) The authors are not responsible for any harmful consequences 302 | * of using this software, even if they result from defects in it. 303 | * 304 | *------------------------------------------------ 305 | 306 | ldiv: 307 | move.l 4(%a7),%d0 308 | bpl ld1 309 | neg.l %d0 310 | ld1: 311 | move.l 8(%a7),%d1 312 | bpl ld2 313 | neg.l %d1 314 | eor.b #0x80,4(%a7) 315 | ld2: 316 | bsr i_ldiv /* d0 = d0/d1 */ 317 | tst.b 4(%a7) 318 | bpl ld3 319 | neg.l %d0 320 | ld3: 321 | rts 322 | 323 | lmul: 324 | move.l 4(%a7),%d0 325 | bpl lm1 326 | neg.l %d0 327 | lm1: 328 | move.l 8(%a7),%d1 329 | bpl lm2 330 | neg.l %d1 331 | eor.b #0x80,4(%a7) 332 | lm2: 333 | bsr i_lmul /* d0 = d0*d1 */ 334 | tst.b 4(%a7) 335 | bpl lm3 336 | neg.l %d0 337 | lm3: 338 | rts 339 | 340 | lrem: 341 | move.l 4(%a7),%d0 342 | bpl lr1 343 | neg.l %d0 344 | lr1: 345 | move.l 8(%a7),%d1 346 | bpl lr2 347 | neg.l %d1 348 | lr2: 349 | bsr i_ldiv /* d1 = d0%d1 */ 350 | move.l %d1,%d0 351 | tst.b 4(%a7) 352 | bpl lr3 353 | neg.l %d0 354 | lr3: 355 | rts 356 | 357 | ldivu: 358 | move.l 4(%a7),%d0 359 | move.l 8(%a7),%d1 360 | bsr i_ldiv 361 | rts 362 | 363 | lmulu: 364 | move.l 4(%a7),%d0 365 | move.l 8(%a7),%d1 366 | bsr i_lmul 367 | rts 368 | 369 | lremu: 370 | move.l 4(%a7),%d0 371 | move.l 8(%a7),%d1 372 | bsr i_ldiv 373 | move.l %d1,%d0 374 | rts 375 | * 376 | * A in d0, B in d1, return A*B in d0 377 | * 378 | i_lmul: 379 | move.l %d3,%a2 /* save d3 */ 380 | move.w %d1,%d2 381 | mulu %d0,%d2 /* d2 = Al * Bl */ 382 | 383 | move.l %d1,%d3 384 | swap %d3 385 | mulu %d0,%d3 /* d3 = Al * Bh */ 386 | 387 | swap %d0 388 | mulu %d1,%d0 /* d0 = Ah * Bl */ 389 | 390 | add.l %d3,%d0 /* d0 = (Ah*Bl + Al*Bh) */ 391 | swap %d0 392 | clr.w %d0 /* d0 = (Ah*Bl + Al*Bh) << 16 */ 393 | 394 | add.l %d2,%d0 /* d0 = A*B */ 395 | move.l %a2,%d3 /* restore d3 */ 396 | rts 397 | * 398 | *A in d0, B in d1, return A/B in d0, A%B in d1 399 | * 400 | i_ldiv: 401 | tst.l %d1 402 | bne nz1 403 | 404 | * divide by zero 405 | * divu #0,%d0 /* cause trap */ 406 | move.l #0x80000000,%d0 407 | move.l %d0,%d1 408 | rts 409 | nz1: 410 | move.l %d3,%a2 /* save d3 */ 411 | cmp.l %d1,%d0 412 | bhi norm 413 | beq is1 414 | * AB and B is not 0 426 | norm: 427 | cmp.l #1,%d1 428 | bne not1 429 | * B==1, so ret A, rem 0 430 | clr.l %d1 431 | move.l %a2,%d3 /* restore d3 */ 432 | rts 433 | * check for A short (implies B short also) 434 | not1: 435 | cmp.l #0xffff,%d0 436 | bhi slow 437 | * A short and B short -- use 'divu' 438 | divu %d1,%d0 /* d0 = REM:ANS */ 439 | swap %d0 /* d0 = ANS:REM */ 440 | clr.l %d1 441 | move.w %d0,%d1 /* d1 = REM */ 442 | clr.w %d0 443 | swap %d0 444 | move.l %a2,%d3 /* restore d3 */ 445 | rts 446 | * check for B short 447 | slow: 448 | cmp.l #0xffff,%d1 449 | bhi slower 450 | * A long and B short -- use special stuff from gnu 451 | move.l %d0,%d2 452 | clr.w %d2 453 | swap %d2 454 | divu %d1,%d2 /* d2 = REM:ANS of Ahi/B */ 455 | clr.l %d3 456 | move.w %d2,%d3 /* d3 = Ahi/B */ 457 | swap %d3 458 | 459 | move.w %d0,%d2 /* d2 = REM << 16 + Alo */ 460 | divu %d1,%d2 /* d2 = REM:ANS of stuff/B */ 461 | 462 | move.l %d2,%d1 463 | clr.w %d1 464 | swap %d1 /* d1 = REM */ 465 | 466 | clr.l %d0 467 | move.w %d2,%d0 468 | add.l %d3,%d0 /* d0 = ANS */ 469 | move.l %a2,%d3 /* restore d3 */ 470 | rts 471 | * A>B, B > 1 472 | slower: 473 | move.l #1,%d2 474 | clr.l %d3 475 | moreadj: 476 | cmp.l %d0,%d1 477 | bhs adj 478 | add.l %d2,%d2 479 | add.l %d1,%d1 480 | bpl moreadj 481 | * we shifted B until its >A or sign bit set 482 | * we shifted #1 (d2) along with it 483 | adj: 484 | cmp.l %d0,%d1 485 | bhi ltuns 486 | or.l %d2,%d3 487 | sub.l %d1,%d0 488 | ltuns: 489 | lsr.l #1,%d1 490 | lsr.l #1,%d2 491 | bne adj 492 | * d3=answer, d0=rem 493 | move.l %d0,%d1 494 | move.l %d3,%d0 495 | move.l %a2,%d3 /* restore d3 */ 496 | rts 497 | -------------------------------------------------------------------------------- /spacer/SpacerHarry2/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "resources.h" 3 | 4 | #define VERTICAL_REZ 224 5 | // image is 512x224. Screen is 320, we want to move halfway 6 | // 512/2 - 320 /2 7 | #define SCROLL_CENTER -96 8 | 9 | // Horizontal Scrolling values 10 | s16 HscrollA[VERTICAL_REZ]; 11 | s16 HscrollB[VERTICAL_REZ]; 12 | 13 | // Vertical Scrolling values ( to simulate hills ) 14 | extern u16 VscrollA[VERTICAL_REZ]; 15 | 16 | // color array for grass and road stripe palette changes. 17 | extern u16 colors[VERTICAL_REZ]; 18 | 19 | // Zmap for tracking segment position - 20 | #define ZMAP_LENGTH 111 21 | fix32 zmap[ZMAP_LENGTH]; 22 | fix32 hScrollIncrement1[ZMAP_LENGTH]; 23 | fix32 hScrollIncrement2[ZMAP_LENGTH]; 24 | fix32 hScrollIncrement3[ZMAP_LENGTH]; 25 | fix32 workScrollA[VERTICAL_REZ]; // working buffer to calculate actual scroll value 26 | s16 scrollSteps = 0; 27 | 28 | 29 | fix32 colorCyclePosition = FIX32(0); // keep track of the color cycling position 30 | fix16 backgroundPosition = FIX16(SCROLL_CENTER); // handle background X position 31 | 32 | extern s16 horizonLine; 33 | const fix32 fullGroundLineCount = FIX32(ZMAP_LENGTH); 34 | fix32 b = FIX32( 169.333); 35 | fix32 groundLineCount; 36 | fix32 groundLineStep; 37 | 38 | 39 | fix32 centerLine = FIX32(160); // center line at the front (bottom) of the screen 40 | 41 | // Sprites 42 | struct CP_SPRITE 43 | { 44 | Sprite *sprite; 45 | fix32 posX; // horizontal screen position 46 | fix32 posY; // vertical screen position 47 | fix32 posZ; // z position along the visible segments. 48 | fix32 position; // track position along the *entire* road. start it at the farthest on background - 12.5 49 | fix32 speed; // sped the car travles through the road 50 | u16 segment_index; // current road segment of the sprite 51 | u16 offsetY; // amount to move the sprite up (since these aren't centered) 52 | u16 offsetX; // amount to move the sprite left (since these aren't centered) 53 | u16 updateY; // refresh flag for update() 54 | }; 55 | s16 playerXDir = 0; 56 | s16 playerYDir = 0; 57 | 58 | struct CP_SPRITE *playerSprite; 59 | struct CP_SPRITE *playerShadowSprite; 60 | struct CP_SPRITE *bossSprite; 61 | struct CP_SPRITE *bossShadowSprite; 62 | 63 | // joypad event handler. This gets called automatically by SGDK when the joypad 64 | // state changes 65 | static void joypadHandler(u16 joypadId, u16 changed, u16 state) 66 | { 67 | if (joypadId == JOY_1) 68 | { 69 | if (state & BUTTON_RIGHT) 70 | { 71 | playerXDir = 1; 72 | } 73 | else if (changed & BUTTON_RIGHT) 74 | { 75 | playerXDir = 0; 76 | } 77 | 78 | if (state & BUTTON_LEFT) 79 | { 80 | playerXDir = -1; 81 | } 82 | else if (changed & BUTTON_LEFT) 83 | { 84 | playerXDir = 0; 85 | } 86 | 87 | if (state & BUTTON_UP) 88 | { 89 | playerYDir = -1; 90 | } 91 | else if (changed & BUTTON_UP) 92 | { 93 | playerYDir = 0; 94 | } 95 | 96 | if (state & BUTTON_DOWN) 97 | { 98 | playerYDir = +1; 99 | } 100 | else if (changed & BUTTON_DOWN) 101 | { 102 | playerYDir = 0; 103 | } 104 | 105 | } 106 | } 107 | 108 | void updatePlayer() 109 | { 110 | // player position affects H/Y 111 | if (playerXDir < 0) 112 | { 113 | playerSprite->posX = fix32Sub(playerSprite->posX, FIX32(2.5)); 114 | if (playerSprite->posX < FIX32(playerSprite->offsetX)) 115 | { 116 | playerSprite->posX = FIX32(playerSprite->offsetX); 117 | } 118 | } 119 | else if (playerXDir > 0) 120 | { 121 | playerSprite->posX = fix32Add(playerSprite->posX, FIX32(2.5)); 122 | if (playerSprite->posX > FIX32(292)) 123 | { 124 | playerSprite->posX = FIX32(292); 125 | } 126 | } 127 | playerShadowSprite->posX = playerSprite->posX; 128 | 129 | 130 | if (playerYDir < 0) 131 | { 132 | playerSprite->posY = fix32Sub(playerSprite->posY, FIX32(2.5)); 133 | if (playerSprite->posY < FIX32(52)) 134 | { 135 | playerSprite->posY = FIX32(52); 136 | } 137 | SPR_setAnim(playerSprite->sprite, 1); 138 | 139 | } 140 | else if (playerYDir > 0) 141 | { 142 | playerSprite->posY = fix32Add(playerSprite->posY, FIX32(2.5)); 143 | if (playerSprite->posY > FIX32(172)) 144 | { 145 | playerSprite->posY = FIX32(172); 146 | SPR_setAnim(playerSprite->sprite, 0); 147 | } 148 | } 149 | if( playerYDir != 0 ) { 150 | // get the horizon line 151 | // find step h = mx + b : m happens to be (-1/2) 152 | fix32 h = b - fix32Div( playerSprite->posY, FIX32(3.0) ); 153 | horizonLine = fix32ToInt( h ); 154 | // ground line steps 155 | groundLineCount = fix32Sub( FIX32(223), h ); 156 | groundLineStep = fix32Div( fullGroundLineCount, groundLineCount); 157 | } 158 | } 159 | 160 | 161 | 162 | void update() 163 | { 164 | // VERTICAL PROCESSING ////////////////////////////////////////// 165 | colorCyclePosition = fix32Sub(colorCyclePosition, playerSprite->speed); 166 | if (fix32ToInt(colorCyclePosition) < 0) 167 | { 168 | colorCyclePosition = zmap[ZMAP_LENGTH - 1]; // Send segment to farthest visible distance 169 | } 170 | 171 | fix32 i = FIX32(223); 172 | u16 c = 223; 173 | u16 j = horizonLine; 174 | 175 | while( c > horizonLine ) { 176 | fix32 tmpz = fix32Sub(colorCyclePosition, zmap[fix32ToInt(i)-ZMAP_LENGTH]); 177 | u16 zcolor = (u16)fix32ToInt(tmpz << 2); // >> 1); 178 | colors[j] = zcolor & 1; 179 | VscrollA[c] = fix32ToInt(i) - c; 180 | 181 | i = fix32Sub( i, groundLineStep); 182 | --c; 183 | ++j; 184 | } 185 | 186 | 187 | for (s16 h = horizonLine; h >= 0; --h) 188 | { 189 | VscrollA[h] = -h; 190 | } 191 | 192 | 193 | 194 | // HORIZONTAL SCROLLING //////////////////////////////////////// 195 | fix16 bgDelta = FIX16(0); 196 | if (fix32ToInt(playerSprite->posX) < 68) 197 | { 198 | fix32 i = FIX32(223); 199 | u16 c = 223; 200 | while (c > horizonLine) 201 | { 202 | workScrollA[c] = fix32Add(workScrollA[c], hScrollIncrement3[fix32ToInt(i) - ZMAP_LENGTH]); 203 | HscrollA[c] = fix32ToInt(workScrollA[c]) + SCROLL_CENTER; 204 | i = fix32Sub(i, groundLineStep); 205 | --c; 206 | } 207 | bgDelta = FIX16(-0.8); 208 | scrollSteps += 8; 209 | } 210 | else if (fix32ToInt(playerSprite->posX) < 138) 211 | { 212 | fix32 i = FIX32(223); 213 | u16 c = 223; 214 | while (c > horizonLine) 215 | { 216 | workScrollA[c] = fix32Add(workScrollA[c], hScrollIncrement2[fix32ToInt(i) - ZMAP_LENGTH]); 217 | HscrollA[c] = fix32ToInt(workScrollA[c]) + SCROLL_CENTER; 218 | i = fix32Sub(i, groundLineStep); 219 | --c; 220 | } 221 | bgDelta = FIX16(-0.4); 222 | scrollSteps += 4; 223 | } 224 | else if (fix32ToInt(playerSprite->posX) < 150) 225 | { 226 | fix32 i = FIX32(223); 227 | u16 c = 223; 228 | while (c > horizonLine) 229 | { 230 | workScrollA[c] = fix32Add(workScrollA[c], hScrollIncrement1[fix32ToInt(i) - ZMAP_LENGTH]); 231 | HscrollA[c] = fix32ToInt(workScrollA[c]) + SCROLL_CENTER; 232 | i = fix32Sub(i, groundLineStep); 233 | --c; 234 | } 235 | bgDelta = FIX16(-0.2); 236 | scrollSteps += 2; 237 | } 238 | else if (fix32ToInt(playerSprite->posX) > 252) 239 | { 240 | fix32 i = FIX32(223); 241 | u16 c = 223; 242 | while (c > horizonLine) 243 | { 244 | workScrollA[c] = fix32Sub(workScrollA[c], hScrollIncrement3[fix32ToInt(i) - ZMAP_LENGTH]); 245 | HscrollA[c] = fix32ToInt(workScrollA[c]) + SCROLL_CENTER; 246 | i = fix32Sub(i, groundLineStep); 247 | --c; 248 | } 249 | bgDelta = FIX16(0.8); 250 | scrollSteps -= 8; 251 | } 252 | else if (fix32ToInt(playerSprite->posX) > 190) 253 | { 254 | fix32 i = FIX32(223); 255 | u16 c = 223; 256 | while (c > horizonLine) 257 | { 258 | workScrollA[c] = fix32Sub(workScrollA[c], hScrollIncrement2[fix32ToInt(i) - ZMAP_LENGTH]); 259 | HscrollA[c] = fix32ToInt(workScrollA[c]) + SCROLL_CENTER; 260 | i = fix32Sub(i, groundLineStep); 261 | --c; 262 | } 263 | bgDelta = FIX16(0.4); 264 | scrollSteps -= 4; 265 | } 266 | else if (fix32ToInt(playerSprite->posX) > 170) 267 | { 268 | fix32 i = FIX32(223); 269 | u16 c = 223; 270 | while (c > horizonLine) 271 | { 272 | workScrollA[c] = fix32Sub(workScrollA[c], hScrollIncrement1[fix32ToInt(i) - ZMAP_LENGTH]); 273 | HscrollA[c] = fix32ToInt(workScrollA[c]) + SCROLL_CENTER; 274 | i = fix32Sub(i, groundLineStep); 275 | --c; 276 | } 277 | bgDelta = FIX16(0.2); 278 | scrollSteps -= 2; 279 | } 280 | 281 | if (scrollSteps >= 64 || scrollSteps <= -64) 282 | { 283 | scrollSteps = 0; 284 | memset(workScrollA, 0, sizeof(workScrollA)); 285 | } 286 | 287 | 288 | if (playerSprite->speed != FIX32(0.0)) 289 | { 290 | backgroundPosition = fix16Sub(backgroundPosition, bgDelta ); 291 | for (u16 y = 12; y < 160; ++y) 292 | { 293 | HscrollB[y] = fix16ToInt(backgroundPosition); 294 | } 295 | } 296 | 297 | 298 | } 299 | 300 | int main(bool hard) 301 | { 302 | horizonLine = 223 - ZMAP_LENGTH; 303 | groundLineCount = fix32Sub(FIX32(223), horizonLine); 304 | groundLineStep = fix32Div(fullGroundLineCount, fullGroundLineCount); 305 | ////////////////////////////////////////////////////////////// 306 | // precalculate some stuff 307 | fix32 step1 = fix32Div(FIX32(2), FIX32(ZMAP_LENGTH)); // divide bottom scroll increment by the height of the ground graphic. 308 | fix32 step2 = fix32Div(FIX32(4), FIX32(ZMAP_LENGTH)); 309 | fix32 step3 = fix32Div(FIX32(8), FIX32(ZMAP_LENGTH)); 310 | fix32 currentXDelta1 = FIX32(0); 311 | fix32 currentXDelta2 = FIX32(0); 312 | fix32 currentXDelta3 = FIX32(0); 313 | for (int i = 0; i < ZMAP_LENGTH; ++i) 314 | { 315 | // http://www.extentofthejam.com/pseudo/ 316 | // Z = Y_world / (Y_screen - (height_screen / 2)) 317 | zmap[i] = fix32Div(FIX32(-75), fix32Sub(FIX32(i), FIX32(113))); 318 | hScrollIncrement1[i] = currentXDelta1; 319 | currentXDelta1 = fix32Add(currentXDelta1, step1); 320 | hScrollIncrement2[i] = currentXDelta2; 321 | currentXDelta2 = fix32Add(currentXDelta2, step2); 322 | hScrollIncrement3[i] = currentXDelta3; 323 | currentXDelta3 = fix32Add(currentXDelta3, step3); 324 | // KLog_F4("i: ", FIX32(i), " z: ", zmap[i], " d4: ", hScrollIncrement2[i], " d8: ", hScrollIncrement3[i]); 325 | } 326 | 327 | ////////////////////////////////////////////////////////////// 328 | // VDP basic setup 329 | VDP_setScreenWidth320(); 330 | VDP_setScrollingMode(HSCROLL_LINE, VSCROLL_PLANE); 331 | 332 | // initialize scrolling arrays 333 | for (int i = 0; i < VERTICAL_REZ; i++) 334 | { 335 | HscrollA[i] = SCROLL_CENTER; 336 | VscrollA[i] = 0; 337 | workScrollA[i] = FIX32(0); 338 | } 339 | 340 | ////////////////////////////////////////////////////////////// 341 | // Setup scroll panes 342 | PAL_setPalette(PAL0, ground_pal.data, CPU); 343 | PAL_setPalette(PAL3, background_pal.data, CPU); 344 | int ind = TILE_USER_INDEX; 345 | VDP_drawImageEx(BG_A, &ground, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, ind), 0, 0, FALSE, TRUE); 346 | ind += ground.tileset->numTile; 347 | VDP_drawImageEx(BG_B, &background, TILE_ATTR_FULL(PAL3, FALSE, FALSE, FALSE, ind), 0, 0, FALSE, TRUE); 348 | ind += background.tileset->numTile; 349 | 350 | VDP_setVerticalScroll(BG_B, 122 - horizonLine); 351 | ////////////////////////////////////////////////////////////// 352 | // Setup Car Sprites 353 | SPR_init(); 354 | // SPR_initEx(900); 355 | PAL_setPalette(PAL1, player_pal.data, CPU); 356 | playerSprite = malloc(sizeof(struct CP_SPRITE)); 357 | playerSprite->position = FIX32(0); 358 | playerSprite->segment_index = 0; 359 | playerSprite->offsetY = 40; // 80~ish tall 360 | playerSprite->offsetX = 28; // 56~ish wide 361 | playerSprite->posX = FIX32(160.0); 362 | playerSprite->posY = FIX32(160.0); // 363 | playerSprite->sprite = SPR_addSprite(&player, // Sprite defined in resources 364 | fix32ToInt(playerSprite->posX) - playerSprite->offsetX, // starting X position 365 | fix32ToInt(playerSprite->posY) - playerSprite->offsetY, // starting Y position 366 | TILE_ATTR(PAL1, // specify palette 367 | 1, // Tile priority ( with background) 368 | FALSE, // flip the sprite vertically? 369 | FALSE // flip the sprite horizontally 370 | )); 371 | playerSprite->speed = FIX32(0.05); 372 | 373 | SPR_setAnim(playerSprite->sprite, 1); 374 | SPR_setDepth(playerSprite->sprite, 0); 375 | 376 | playerShadowSprite = malloc(sizeof(struct CP_SPRITE)); 377 | playerShadowSprite->position = FIX32(0); 378 | playerShadowSprite->segment_index = 0; 379 | playerShadowSprite->offsetY = 8; 380 | playerShadowSprite->offsetX = 28; 381 | playerShadowSprite->posX = FIX32(160.0); 382 | playerShadowSprite->posY = FIX32(210.0); // 383 | playerShadowSprite->sprite = SPR_addSprite(&shadow, // Sprite defined in resources 384 | fix32ToInt(playerShadowSprite->posX) - playerShadowSprite->offsetX, // starting X position 385 | fix32ToInt(playerShadowSprite->posY) - playerShadowSprite->offsetY, // starting Y position 386 | TILE_ATTR(PAL1, // specify palette 387 | 1, // Tile priority ( with background) 388 | FALSE, // flip the sprite vertically? 389 | FALSE // flip the sprite horizontally 390 | )); 391 | 392 | PAL_setPalette(PAL2, boss_pal.data, CPU); 393 | bossSprite = malloc(sizeof(struct CP_SPRITE)); 394 | bossSprite->position = FIX32(0); 395 | bossSprite->segment_index = 0; 396 | bossSprite->offsetY = 0; // not using offsets for this demo 397 | bossSprite->offsetX = 0; 398 | bossSprite->posX = FIX32(112.0); 399 | bossSprite->posY = FIX32(60.0); // 400 | bossSprite->sprite = SPR_addSprite(&boss, // Sprite defined in resources 401 | fix32ToInt(bossSprite->posX), // starting X position 402 | fix32ToInt(bossSprite->posY), // starting Y position 403 | TILE_ATTR(PAL2, // specify palette 404 | 1, // Tile priority ( with background) 405 | FALSE, // flip the sprite vertically? 406 | FALSE // flip the sprite horizontally 407 | )); 408 | 409 | bossShadowSprite = malloc(sizeof(struct CP_SPRITE)); 410 | bossShadowSprite->position = FIX32(0); 411 | bossShadowSprite->segment_index = 0; 412 | bossShadowSprite->offsetY = 0; // not using offsets for this demo 413 | bossShadowSprite->offsetX = 0; // 414 | bossShadowSprite->posX = FIX32(132.0); 415 | bossShadowSprite->posY = FIX32(180.0); // 416 | bossShadowSprite->sprite = SPR_addSprite(&shadow, // Sprite defined in resources 417 | fix32ToInt(bossShadowSprite->posX), // starting X position 418 | fix32ToInt(bossShadowSprite->posY), // starting Y position 419 | TILE_ATTR(PAL1, // specify palette 420 | 1, // Tile priority ( with background) 421 | FALSE, // flip the sprite vertically? 422 | FALSE // flip the sprite horizontally 423 | )); 424 | ////////////////////////////////////////////////////////////// 425 | // init segments 426 | colorCyclePosition = zmap[ZMAP_LENGTH - 1]; // put it at the farthest away point 427 | 428 | ////////////////////////////////////////////////////////////// 429 | // Setup interrupt handlers 430 | SYS_disableInts(); 431 | { 432 | VDP_setHIntCounter(0); 433 | VDP_setHInterrupt(1); 434 | } 435 | SYS_enableInts(); 436 | 437 | // Asynchronous joystick handler. 438 | JOY_init(); 439 | JOY_setEventHandler(joypadHandler); 440 | 441 | // Main loop 442 | scrollSteps = 0; 443 | while (TRUE) 444 | { 445 | // update background 446 | update(); 447 | 448 | // Set player position 449 | updatePlayer(); 450 | SPR_setPosition(playerSprite->sprite, fix32ToInt(playerSprite->posX) - playerSprite->offsetX, fix32ToInt(playerSprite->posY) - playerSprite->offsetY); 451 | SPR_setPosition(playerShadowSprite->sprite, fix32ToInt(playerShadowSprite->posX) - playerShadowSprite->offsetX, fix32ToInt(playerShadowSprite->posY) - playerShadowSprite->offsetY); 452 | 453 | // update tsprites 454 | SPR_update(); 455 | 456 | // move the ground 457 | VDP_setHorizontalScrollLine(BG_A, 0, HscrollA, VERTICAL_REZ, DMA_QUEUE); 458 | 459 | // move the background 460 | VDP_setHorizontalScrollLine(BG_B, 0, HscrollB, 160, DMA_QUEUE); 461 | VDP_setVerticalScroll(BG_B, 122 - horizonLine); 462 | 463 | SYS_doVBlankProcess(); 464 | } 465 | } 466 | -------------------------------------------------------------------------------- /spacer/SpacerHarry2/starter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/spacer/SpacerHarry2/starter.png -------------------------------------------------------------------------------- /spacer/SpacerHarryCC/makeBackgroundCC.py: -------------------------------------------------------------------------------- 1 | from PIL import Image, ImageDraw 2 | import math 3 | 4 | # 28 squares? 112 squares really, but 5 | 6 | width = 512 7 | midpint = width / 2 8 | buttomTileWidth = 48 9 | height = 400 # try differnt values to get slightly different tile results. I like 400~ish the best so far. 10 | 11 | bottomLeftStart = ( width - buttomTileWidth * width ) / 2; 12 | 13 | img = Image.new( mode="P", size = (width,height)) 14 | img.putpalette([ 15 | 0,0,0, # 0 16 | 63,0,0, # red 1 17 | 127,0,0, 18 | 190,0,0, 19 | 255,0,0, 20 | 0,0,63, # blue 5 21 | 0,0,127, 22 | 0,0,190, 23 | 0,0,255, 24 | 0,63,0, # green 9 25 | 0,127,0, 26 | 0,190,0, 27 | 0,255,0, 28 | 0,63,63, # 13 29 | 0,127,127, 30 | 0,190,190 31 | 32 | ]) 33 | dImage = ImageDraw.Draw( img ) 34 | 35 | worldY = -500 36 | 37 | 38 | start = 0 # bottom based on Lou's page. 39 | end = 159 # up 40 | lowZ = worldY/ (start - height/2) 41 | highZ = worldY/ (end - height/2) 42 | 43 | tiles = 25 44 | tileZ = ( highZ - lowZ ) / tiles 45 | 46 | lastZ = lowZ 47 | tileYs = [] 48 | for y in range( start, end, 1 ): 49 | #z = int( (1+(y-159)/15.9) * 47/ (y - 160)) 50 | z = worldY/ (y - height/2) 51 | msg = "" 52 | if z - lastZ > tileZ: 53 | # use which ever was closer. 54 | stepBackZ = worldY/ (y-1 - height/2) 55 | if stepBackZ - lastZ >= z - lastZ: 56 | tileYs.append( height - y ) # subtract from height to get back into pixel coords 57 | else: 58 | tileYs.append(height - y-1 ) 59 | lastZ += tileZ 60 | if z - lastZ == tileZ: 61 | tileYs.append( y ) 62 | 63 | 64 | 65 | currStart = start 66 | for y in tileYs: 67 | print( y ) 68 | 69 | 70 | # draw from bottom up 71 | currY = height - 1 72 | # Tile width variables 73 | buttomTileWidth = 48 74 | topStripWidth = 1 75 | tileWidthDelta = (buttomTileWidth - topStripWidth) / (end - start ) 76 | print(tileWidthDelta) 77 | currTileWidth = buttomTileWidth 78 | 79 | # tile depth variables 80 | tiles2 = tiles * 4 81 | tileZ2 = ( highZ - lowZ ) / tiles2 82 | lastZ = lowZ 83 | # tile color starting indicies 84 | color1 = 1 85 | color2 = 5 86 | for endY in tileYs: 87 | # how many X iterations? 88 | xCount = math.ceil(width/(2* currTileWidth)) +1 89 | colorOffset = 0 90 | for y in range ( currY, endY, -1 ) : 91 | # color choice now depends on a different distance 92 | if currY - endY > 4 : 93 | # use all 4 possible color offset 94 | z = worldY/ ( (height -1 )- y - height/2) 95 | if z - lastZ > tileZ2: 96 | z2 = worldY/ ( (height )- y - height/2) 97 | if z2 >= z : 98 | colorOffset+=1 99 | lastZ += tileZ2 100 | if ( colorOffset > 3 ) : 101 | colorOffset = 3; 102 | elif currY - endY > 2 : 103 | # use 3 of 4 possible color offset 104 | if y < currY-1: 105 | colorOffset += 1 106 | elif currY - endY > 1 : 107 | # use 2 of 4 possible color offset 108 | colorOffset = 0 109 | 110 | # spread out from center. 111 | p1 = round(width/2 -1) 112 | p2 = round(p1 - currTileWidth) 113 | for x in range( 0, xCount ) : 114 | dImage.line( [(p1,y) , (p2,y)], fill = color1 + colorOffset if x%2 == 0 else color2 + colorOffset ) 115 | # and opposites 116 | dImage.line( [(511-p1,y) , (512-p2,y)], fill = color2 + colorOffset if x%2 == 0 else color1 + colorOffset ) 117 | p1 = p2 -1 118 | p2 = p1 - currTileWidth 119 | 120 | currTileWidth -= tileWidthDelta 121 | 122 | # handle next tile 123 | currY = endY 124 | lastZ += tileZ2 # next tile start 125 | if color1 == 1: 126 | color1 = 5 127 | color2 = 1 128 | else: 129 | color1 = 1 130 | color2 = 5 131 | 132 | # save a starter image for the project 133 | img.save("starter.png") 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /spacer/SpacerHarryCC/res/bg/bg_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radioation/MegaDriving/3e20302f929415ae78e3e38ba9737f89951e3b24/spacer/SpacerHarryCC/res/bg/bg_b.png -------------------------------------------------------------------------------- /spacer/SpacerHarryCC/res/resources.res: -------------------------------------------------------------------------------- 1 | IMAGE bg_b "bg/bg_b.png" BEST 2 | PALETTE bg_b_pal "bg/bg_b.png" 3 | -------------------------------------------------------------------------------- /spacer/SpacerHarryCC/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "resources.h" 3 | 4 | #define VERTICAL_REZ 224 5 | 6 | // image is 512x214. Screen is 320, meet halfway -> 512/2 - 320/2 7 | #define SCROLL_CENTER -96 8 | s16 hScrollB[ VERTICAL_REZ ]; 9 | 10 | const u16 palette[16] = 11 | { 12 | 0x0E00, 13 | 0x0E00, 14 | 0x0E00, 15 | 0x0E00, 16 | 17 | 0x000E, 18 | 0x000E, 19 | 0x000E, 20 | 0x000E, 21 | 22 | 0x0E00, 23 | 0x0E00, 24 | 0x0E00, 25 | 0x0E00, 26 | 27 | 0x000E, 28 | 0x000E, 29 | 0x000E, 30 | 0x000E 31 | 32 | }; 33 | 34 | 35 | 36 | 37 | 38 | int main(bool in) 39 | { 40 | ////////////////////////////////////////////////////////////// 41 | // VDP basic setup 42 | VDP_setScreenWidth320(); 43 | VDP_setScrollingMode(HSCROLL_LINE, VSCROLL_PLANE); 44 | 45 | // initialize scrolling arrays 46 | for (int i = 0; i < VERTICAL_REZ; i++) 47 | { 48 | hScrollB[i] = SCROLL_CENTER; 49 | } 50 | 51 | 52 | 53 | 54 | ////////////////////////////////////////////////////////////// 55 | // Setup scroll panes 56 | PAL_setPalette(PAL0, bg_b_pal.data, CPU); 57 | PAL_setColors(1, palette, 8, CPU); 58 | int ind = TILE_USER_INDEX; 59 | VDP_drawImageEx(BG_B, &bg_b, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, ind), 0, 0, FALSE, TRUE); 60 | u16 paletteOffset = 0; 61 | while(TRUE) 62 | { 63 | PAL_setColors(1, palette + paletteOffset, 8, DMA_QUEUE); 64 | ++paletteOffset; 65 | if( paletteOffset > 7 ) { 66 | paletteOffset = 0; 67 | } 68 | VDP_setHorizontalScrollLine(BG_B, 0, hScrollB, VERTICAL_REZ, DMA_QUEUE); 69 | 70 | SYS_doVBlankProcess(); 71 | } 72 | 73 | } 74 | --------------------------------------------------------------------------------