sierrahotel/pc/src/a23d2.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;
}