/* * Copyright (c) 2024 Scott Duensing, scott@kangaroopunch.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include "vrEmu6502.h" #include "a23d2bin.h" #include "bitmap.h" #include "iflight.h" #include "fflight.h" #include "mem.h" #include "a23d2.h" // Emulator Stuff. byte _RAM[0x10000]; // 6502 memory. VrEmu6502 *_v6502 = NULL; // Externs. uint16_t _drawlistInDatabase; #define SCALE 1.25 // Scale factor for 256->320 #define SCREEN3DX 255 // Resolution of A2-3D2 renderer. #define SCREEN3DY 233 // These get scaled up to full-screen. #define SCREEN3DXH 128 // Half of SCREEN3DX. #define SCREEN3DYH 117 // Half of SCREEN3DY. byte _scaleX[SCREEN3DX + 1]; byte _scaleY[SCREEN3DY + 1]; void jsr(uint16_t addr); void landscape(void); byte memoryRead(uint16_t addr, bool isDebug); void memoryWrite(uint16_t addr, byte value); void a23d2Draw(void) { uint16_t pointer = DRAWLIST_P0; int16_t x1; int16_t y1; int16_t x2; int16_t y2; while (true) { switch (_RAM[pointer++]) { // Line. case LIN2D: x1 = (int8_t)_RAM[pointer++] + SCREEN3DXH; y1 = SCREEN3DYH - (int8_t)_RAM[pointer++]; x2 = (int8_t)_RAM[pointer++] + SCREEN3DXH; y2 = SCREEN3DYH - (int8_t)_RAM[pointer++]; if ((x1 < 0) || (y1 < 0) || (x2 < 0) || (y2 < 0) || (x1 > SCREEN3DX) || (y1 > SCREEN3DY) || (x2 > SCREEN3DX) || (y2 > SCREEN3DY)) { printf("Point out of bounds! %d %d %d %d\n", x1, y1, x2, y2); fflush(0); } else { bitmapLine(x1 + _scaleX[x1], _scaleY[y1], x2 + _scaleX[x2], _scaleY[y2]); } continue; // Point. case PNT2D: x1 = (int8_t)_RAM[pointer++] + SCREEN3DXH; y1 = SCREEN3DYH - (int8_t)_RAM[pointer++]; bitmapPutPixelIOSet(x1 + _scaleX[x1], _scaleY[y1]); continue; // Set Color. case STCOL: x1 = _RAM[pointer++]; colorSet(x1); continue; // Set Resolution. case SRES: // Eat this byte. We don't need it. pointer++; continue; // End. case END: break; } break; } } void a23d2Init(void) { uint16_t bytes; // Clear VM RAM. scdMemSet(_RAM, 0, 0x10000); // Create our VM. _v6502 = vrEmu6502New(CPU_W65C02, memoryRead, memoryWrite); // Copy A2-3D2 into 6502 RAM. scdMemCpy(&_RAM[0x6000], a23d2bin, a23d2bin_len); // Initialize A2-3D2 so we can use the "fast entry point" (NXTPT) when rendering. bytes = A23D2_TEST_DATABASE; _RAM[bytes++] = SCRSZ; // Screen size. 255x233. Center is 0,0. We're going to scale this up. _RAM[bytes++] = SCREEN3DX; // Scales up to 320. _RAM[bytes++] = SCREEN3DY; // Scales up to 193 (leaves 46 pixels for a control panel). _RAM[bytes++] = 0; _RAM[bytes++] = 0; _RAM[bytes++] = END; // Setup complete! jsr(A23D2_ENTRYN); // Generate scene database. landscape(); // Set up drawlist. _drawlistInDatabase = DATABASE + 1; _RAM[DRAWLIST_P0] = END; // Set up camera. _camera = (cameraT *)&_RAM[DATABASE + 4]; _camera->x = 10600; _camera->y = PLANE_HEIGHT; _camera->z = 10500; _camera->p = 0; _camera->h = 0; // Build scaling tables for stretching 3D scene to fill display. // X table is the difference needed. Allows us to store it in one byte. // Y table is the actual coordinate since it fits in a byte anyway. for (bytes=0; bytes<=SCREEN3DX; bytes++) _scaleX[bytes] = (bytes * SCALE) - bytes; for (bytes=0; bytes<=SCREEN3DY; bytes++) _scaleY[bytes] = (bytes / SCALE); } void a23d2Render(void) { // Update drawlist. _RAM[_drawlistInDatabase + 0] = LOW_BYTE(DRAWLIST_P0); _RAM[_drawlistInDatabase + 1] = HIGH_BYTE(DRAWLIST_P0); // Set IBP. _RAM[A23D2_IBP + 0] = LOW_BYTE(DATABASE); _RAM[A23D2_IBP + 1] = HIGH_BYTE(DATABASE); // Render. jsr(A23D2_NXTPT); } void a23d2Shutdown(void) { vrEmu6502Destroy(_v6502); _v6502 = NULL; } void jsr(uint16_t addr) { int32_t depth = 0; //int32_t instructions = 0; uint16_t PC = addr; vrEmu6502SetPC(_v6502, PC); while (true) { // Did we execute an instruction? if (PC != vrEmu6502GetPC(_v6502)) { PC = vrEmu6502GetPC(_v6502); //instructions++; // Track JSRs & RTSs. if (vrEmu6502GetCurrentOpcode(_v6502) == 0x20) depth++; if (vrEmu6502GetCurrentOpcode(_v6502) == 0x60) { depth--; if (depth < 0) break; } } // Step. vrEmu6502Tick(_v6502); } //printf("%x = %d instructions\n", addr, instructions); } void landscape(void) { int32_t i; int32_t x; int32_t z; int32_t w; int32_t l; int32_t min = 0; int32_t max = 30000; // A mile is 5280. int32_t lines = 10; int32_t skip = (abs(min) + abs(max)) / lines; byte *db = _RAM; uint16_t bytes = DATABASE; // All scene databases need to begin with the ARRAY and EYE records. // The CLPSW clipping setting is up to you. :-) db[bytes++] = ARRAY; // Will be filled in by the program. db[bytes++] = 0; db[bytes++] = 0; db[bytes++] = EYE; // Will be filled in by the program. db[bytes++] = 0x00; // X db[bytes++] = 0x00; db[bytes++] = 0x00; // Y db[bytes++] = 0x00; db[bytes++] = 0x00; // Z db[bytes++] = 0x00; db[bytes++] = 0x00; // P db[bytes++] = 0x00; // B db[bytes++] = 0x00; // H db[bytes++] = CLPSW; db[bytes++] = 0x00; // Landscape db[bytes++] = STCOL; db[bytes++] = 2; // Green for (i=min; i<=max; i+=skip) { db[bytes++] = SPNT; db[bytes++] = LOW_BYTE(i); // X db[bytes++] = HIGH_BYTE(i); db[bytes++] = 0; // Y db[bytes++] = 0; db[bytes++] = LOW_BYTE(min); // Z db[bytes++] = HIGH_BYTE(min); db[bytes++] = CPNT; db[bytes++] = LOW_BYTE(i); // X db[bytes++] = HIGH_BYTE(i); db[bytes++] = 0; // Y db[bytes++] = 0; db[bytes++] = LOW_BYTE(max); // Z db[bytes++] = HIGH_BYTE(max); db[bytes++] = SPNT; db[bytes++] = LOW_BYTE(min); // X db[bytes++] = HIGH_BYTE(min); db[bytes++] = 0; // Y db[bytes++] = 0; db[bytes++] = LOW_BYTE(i); // Z db[bytes++] = HIGH_BYTE(i); db[bytes++] = CPNT; db[bytes++] = LOW_BYTE(max); // X db[bytes++] = HIGH_BYTE(max); db[bytes++] = 0; // Y db[bytes++] = 0; db[bytes++] = LOW_BYTE(i); // Z db[bytes++] = HIGH_BYTE(i); } // Runway - 3000' x 200' w = 200; l = 3000; x = skip * 3.5; z = skip * 3.5; //printf("%dx%d\n", x, z); db[bytes++] = STCOL; db[bytes++] = 7; // Light Grey db[bytes++] = SPNT; db[bytes++] = LOW_BYTE(x); // X db[bytes++] = HIGH_BYTE(x); db[bytes++] = 0; // Y db[bytes++] = 0; db[bytes++] = LOW_BYTE(z); // Z db[bytes++] = HIGH_BYTE(z); db[bytes++] = CPNT; db[bytes++] = LOW_BYTE(x+w); // X db[bytes++] = HIGH_BYTE(x+w); db[bytes++] = 0; // Y db[bytes++] = 0; db[bytes++] = LOW_BYTE(z); // Z db[bytes++] = HIGH_BYTE(z); db[bytes++] = CPNT; db[bytes++] = LOW_BYTE(x+w); // X db[bytes++] = HIGH_BYTE(x+w); db[bytes++] = 0; // Y db[bytes++] = 0; db[bytes++] = LOW_BYTE(z+l); // Z db[bytes++] = HIGH_BYTE(z+l); db[bytes++] = CPNT; db[bytes++] = LOW_BYTE(x); // X db[bytes++] = HIGH_BYTE(x); db[bytes++] = 0; // Y db[bytes++] = 0; db[bytes++] = LOW_BYTE(z+l); // Z db[bytes++] = HIGH_BYTE(z+l); db[bytes++] = CPNT; db[bytes++] = LOW_BYTE(x); // X db[bytes++] = HIGH_BYTE(x); db[bytes++] = 0; // Y db[bytes++] = 0; db[bytes++] = LOW_BYTE(z); // Z db[bytes++] = HIGH_BYTE(z); db[bytes++] = STCOL; db[bytes++] = 15; // White skip = l / (lines * 2); x += (w / 2); for (i=z + skip; i