365 lines
9.1 KiB
C
365 lines
9.1 KiB
C
/*
|
|
* 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 <stdlib.h>
|
|
|
|
#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<z+l-skip; i+=(skip*2)) {
|
|
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(i); // Z
|
|
db[bytes++] = HIGH_BYTE(i);
|
|
|
|
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(i+skip); // Z
|
|
db[bytes++] = HIGH_BYTE(i+skip);
|
|
}
|
|
db[bytes++] = END;
|
|
}
|
|
|
|
|
|
byte memoryRead(uint16_t addr, bool isDebug) {
|
|
(void)isDebug;
|
|
return _RAM[addr];
|
|
}
|
|
|
|
|
|
void memoryWrite(uint16_t addr, byte value) {
|
|
_RAM[addr] = value;
|
|
}
|