Initial commit.

This commit is contained in:
Scott Duensing 2024-04-09 19:40:47 -05:00
commit d9db602f3d
11 changed files with 1412 additions and 0 deletions

2
.gitattributes vendored Normal file
View file

@ -0,0 +1,2 @@
*.png filter=lfs diff=lfs merge=lfs -text
a2-3d/A2-3D2\#066000 filter=lfs diff=lfs merge=lfs -text

6
.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
.builddir
backup/
*.iso
*~
*.pgz
apple2/

BIN
a2-3d/A2-3D2#066000 (Stored with Git LFS) Normal file

Binary file not shown.

73
build.sh Executable file
View file

@ -0,0 +1,73 @@
#!/bin/bash -ex
#
# 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.
#
PROJECT=shotel
F256=$(readlink -f $(pwd)/../f256)
LLVM=${F256}/llvm-mos
PATH=${LLVM}/bin:${PATH}
CLANG="mos-f256k-clang -I${F256} -I$(pwd)/src -Os"
# Update f256lib and tools.
pushd ${F256}
./build-f256lib.sh
./build-tools.sh
popd
[[ -d .builddir ]] && rm -rf .builddir
mkdir -p .builddir
# Do not use relative paths.
${F256}/overlay 5 $(pwd)/.builddir $(pwd)/src
pushd .builddir
cp ../a2-3d/A2-3D2#066000 a2-3d2.bin
truncate -s 8k a2-3d2.bin
cc -I../src -I${F256} ../tools/scene.c -o scene
./scene
cat a2-3d2.bin scene.3d > embedded.bin
${CLANG} -c main.c
${CLANG} -c a23d2.c
${CLANG} -T ../f256.ld \
-Wl,-Map=${PROJECT}.map \
-o ${PROJECT} \
main.o a23d2.o
mv ${PROJECT} ${PROJECT}.pgz
llvm-nm ${PROJECT}.elf > ${PROJECT}.sym
llvm-objdump --syms -d --print-imm-hex ${PROJECT}.elf > ${PROJECT}.lst
hexdump -C ${PROJECT}.pgz > ${PROJECT}.hex
python ${F256}/pgz-thunk.py ${PROJECT}.pgz
mv ${PROJECT}.pgz ../.
popd

116
f256.ld Normal file
View file

@ -0,0 +1,116 @@
/* fake C Stack */
PROVIDE(__stack = 0xA000);
/* entry point to my program */
PROVIDE(__f256_start = 0x300);
/* page size of a block of memory */
PROVIDE(__BLOCK_SIZE = 0x2000);
/* swappable block address */
PROVIDE(__SLOT_ADDR = 0xA000);
/* f256k uses first 16 bytes of ZP for mmu control? */
__rc0 = 0x10;
/* __rc0 = 0xc3; */
INCLUDE imag-regs.ld
/*
ASSERT(__rc0 == 0x10, "Inconsistent zero page map.")
ASSERT(__rc31 == 0x2f, "Inconsistent zero page map.")
*/
MEMORY {
/* kernel uses 0xf0-0xff for parameter passing */
/* A2-3D2 uses 0x60-0xc2 */
zp : ORIGIN = __rc31 + 1, LENGTH = 0xF0 - (__rc31 + 1)
/* zp : ORIGIN = 0x10, LENGTH = 80 */
ram (rw) : ORIGIN = __f256_start, LENGTH = __stack-__f256_start
}
/* LMAs */
__block8_lma = ( 8<<24)|__SLOT_ADDR;
__block9_lma = ( 9<<24)|__SLOT_ADDR;
__block10_lma = (10<<24)|__SLOT_ADDR;
__block11_lma = (11<<24)|__SLOT_ADDR;
__block12_lma = (12<<24)|__SLOT_ADDR;
__block13_lma = (13<<24)|__SLOT_ADDR;
__block14_lma = (14<<24)|__SLOT_ADDR;
__block15_lma = (15<<24)|__SLOT_ADDR;
__block16_lma = (16<<24)|__SLOT_ADDR;
__block17_lma = (17<<24)|__SLOT_ADDR;
__block18_lma = (18<<24)|__SLOT_ADDR;
__block19_lma = (19<<24)|__SLOT_ADDR;
__block20_lma = (20<<24)|__SLOT_ADDR;
__block21_lma = (21<<24)|__SLOT_ADDR;
__block22_lma = (22<<24)|__SLOT_ADDR;
__block23_lma = (23<<24)|__SLOT_ADDR;
/* Stash preloaded binary data */
__binarydata_lma = 0x54000; /* Block 42 */
__BINARYDATA_SIZE = 0x2C000; /* Size of A2-3D2, 3D/2D data area, and 2 bitmaps. */
MEMORY {
block8 : ORIGIN = __block8_lma, LENGTH = __BLOCK_SIZE
block9 : ORIGIN = __block9_lma, LENGTH = __BLOCK_SIZE
block10 : ORIGIN = __block10_lma, LENGTH = __BLOCK_SIZE
block11 : ORIGIN = __block11_lma, LENGTH = __BLOCK_SIZE
block12 : ORIGIN = __block12_lma, LENGTH = __BLOCK_SIZE
block13 : ORIGIN = __block13_lma, LENGTH = __BLOCK_SIZE
block14 : ORIGIN = __block14_lma, LENGTH = __BLOCK_SIZE
block15 : ORIGIN = __block15_lma, LENGTH = __BLOCK_SIZE
block16 : ORIGIN = __block16_lma, LENGTH = __BLOCK_SIZE
block17 : ORIGIN = __block17_lma, LENGTH = __BLOCK_SIZE
block18 : ORIGIN = __block18_lma, LENGTH = __BLOCK_SIZE
block19 : ORIGIN = __block19_lma, LENGTH = __BLOCK_SIZE
block20 : ORIGIN = __block20_lma, LENGTH = __BLOCK_SIZE
block21 : ORIGIN = __block21_lma, LENGTH = __BLOCK_SIZE
block22 : ORIGIN = __block22_lma, LENGTH = __BLOCK_SIZE
block23 : ORIGIN = __block23_lma, LENGTH = __BLOCK_SIZE
binarydata : ORIGIN = __binarydata_lma, LENGTH = __BINARYDATA_SIZE
}
REGION_ALIAS("c_writeable", ram)
REGION_ALIAS("c_readonly", ram)
SECTIONS {
INCLUDE c.ld
.block8 : { *(.block8 .block8.*) } >block8 end_block8 = .;
.block9 : { *(.block9 .block9.*) } >block9 end_block9 = .;
.block10 : { *(.block10 .block10.*) } >block10 end_block10 = .;
.block11 : { *(.block11 .block11.*) } >block11 end_block11 = .;
.block12 : { *(.block12 .block12.*) } >block12 end_block12 = .;
.block13 : { *(.block13 .block13.*) } >block13 end_block13 = .;
.block14 : { *(.block14 .block14.*) } >block14 end_block14 = .;
.block15 : { *(.block15 .block15.*) } >block15 end_block15 = .;
.block16 : { *(.block16 .block16.*) } >block16 end_block16 = .;
.block17 : { *(.block17 .block17.*) } >block17 end_block17 = .;
.block18 : { *(.block18 .block18.*) } >block18 end_block18 = .;
.block19 : { *(.block19 .block19.*) } >block19 end_block19 = .;
.block20 : { *(.block20 .block20.*) } >block20 end_block20 = .;
.block21 : { *(.block21 .block21.*) } >block21 end_block21 = .;
.block22 : { *(.block22 .block22.*) } >block22 end_block22 = .;
.block23 : { *(.block23 .block23.*) } >block23 end_block23 = .;
.binarydata : { *(.binarydata .binarydata.*) } >binarydata end_binarydata = .;
}
OUTPUT_FORMAT {
BYTE(0x5A) /* pgZ */
/* RAM Segment */
SHORT(ORIGIN(ram)) /* where to load it, 24 bits */
BYTE(0x00)
SHORT(__bss_start-ORIGIN(ram)) /* size to load */
BYTE(0x00)
TRIM(ram)
/* Overlay Segments */
INCLUDE output.ld
/* Binary Data */
SHORT(ORIGIN(binarydata))
BYTE(ORIGIN(binarydata)>>16)
SHORT(end_binarydata - __binarydata_lma)
BYTE((end_binarydata - __binarydata_lma)>>16)
TRIM(binarydata)
/* Launch the program, at _start */
SHORT(_start)
LONG(0)
}

BIN
icon.png (Stored with Git LFS) Normal file

Binary file not shown.

26
run.sh Executable file
View file

@ -0,0 +1,26 @@
#!/bin/bash
#
# 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.
#
python ../f256/FoenixMgr/FoenixMgr/fnxmgr.py --run-pgz shotel.pgz

201
src/a23d2.c Normal file
View file

@ -0,0 +1,201 @@
/*
* 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 "a23d2.h"
#include <string.h> // For memcopy
cameraT *_camera = (cameraT *)0x200; // Simulation copy of camera.
cameraT *_cameraInDatabase;
uint16_t _drawlist;
uint16_t _drawlistInDatabase;
byte *_pointer;
uint16_t _bytes;
uint8_t _x1;
uint8_t _y1;
uint8_t _x2;
uint8_t _y2;
bool _useColor;
byte _mmu;
byte _ram;
float _trig;
#define SEGMENT_A23D2
// There's a lot of global use in here. We can't use the virtual stack.
void a23d2Cos(void) {
// Map 0-359 into 0-255.
_tdata = _tdata % 359;
_y1 = (360/256) * _tdata;
POKE(A23D2_TDATA, _y1);
asm("jsr %[addy]":: [addy] "i"(A23D2_COSEX));
_trig = (float)PEEKW(A23D2_TDATA) / 32768;
}
void a23d2Draw(void) {
_pointer = (byte *)_drawlist;
// Move our 3D/2D data buffer at 0x56000 into slot 4.
POKE(MMU_MEM_BANK_4, DATABASE_FAR_BLOCK);
while (true) {
switch (*_pointer++) {
// Line.
case (byte)LIN2D:
_x1 = (uint8_t)((int8_t)(*_pointer++) + 128);
_y1 = 255 - (uint8_t)((int8_t)(*_pointer++) + 128);
_x2 = (uint8_t)((int8_t)(*_pointer++) + 128);
_y2 = 255 - (uint8_t)((int8_t)(*_pointer++) + 128);
bitmapLine(_x1, _y1, _x2, _y2);
continue;
// Point.
case (byte)PNT2D:
_x1 = (uint8_t)((int8_t)(*_pointer++) + 128);
_y1 = 255 - (uint8_t)((int8_t)(*_pointer++) + 128);
bitmapPutPixel(_x1, _y1);
continue;
// Set Color.
case (byte)STCOL:
_x1 = (int8_t)(*_pointer++);
if (_useColor) bitmapSetColor(_x1);
continue;
// Set Resolution.
case (byte)SRES:
// Eat this byte. We don't need it.
_pointer++;
continue;
// End.
case (byte)END:
break;
}
break;
}
// Restore memory map.
POKE(MMU_MEM_BANK_4, 4);
}
void a23d2Init(void) {
// We need to manually page 0x54000 into 0x6000.
// This isn't actually large enough for A2-3D2 but what we should only
// lose Apple ][ stuff we're not using anyway.
POKE(MMU_MEM_BANK_3, A23D2_FAR_BLOCK);
// We're going to clobber from 0x80fb to 0x8101. Back it up.
memcpy((byte *)0x2f7, (byte *)A23D2_TEST_DATABASE, 0x06);
// Initialize A2-3D2 so we can use the "fast entry point" (NXTPT) when rendering.
_bytes = 0;
_pointer = (byte *)A23D2_TEST_DATABASE; // Standard location for test database in A2-3D2.
_pointer[_bytes++] = SCRSZ; // Screen size. 256x240. Center is 0,0.
_pointer[_bytes++] = 255;
_pointer[_bytes++] = 239;
_pointer[_bytes++] = 0;
_pointer[_bytes++] = 0;
_pointer[_bytes++] = END; // Setup complete!
asm("jsr %[addy]":: [addy] "i"(A23D2_ENTRYS)); // Call ENTRYS. This preserves the ZP for us.
// asm("jsr 0x606c"); // Call ENTRYS. This preserves the ZP for us.
// Put back the RAM we clobbered.
memcpy((byte *)A23D2_TEST_DATABASE, (byte *)0x2f7, 0x06);
// Move our 3D/2D data buffer at 0x56000 into slot 4.
POKE(MMU_MEM_BANK_4, DATABASE_FAR_BLOCK);
// Set up drawlists.
_drawlistInDatabase = DATABASE + 1; // First value after ARRAY in near memory.
POKE(DRAWLIST_P0, END);
POKE(DRAWLIST_P1, END);
// Set up camera.
_cameraInDatabase = (cameraT *)(DATABASE + 4); // First value after EYE in near memory.
_camera->x = _cameraInDatabase->x;
_camera->y = _cameraInDatabase->y;
_camera->z = _cameraInDatabase->z;
_camera->p = _cameraInDatabase->p;
_camera->b = _cameraInDatabase->b;
_camera->h = _cameraInDatabase->h;
// Restore memory map.
POKE(MMU_MEM_BANK_4, 4);
POKE(MMU_MEM_BANK_3, 3);
}
void a23d2Render(void) {
// We need to manually page 0x54000 into 0x6000.
// This isn't actually large enough for A2-3D2 but what we should only
// lose Apple ][ stuff we're not using anyway.
POKE(MMU_MEM_BANK_3, A23D2_FAR_BLOCK);
// Move our 3D/2D data buffer at 0x56000 into slot 4.
POKE(MMU_MEM_BANK_4, DATABASE_FAR_BLOCK);
// Update drawlist.
POKEW(_drawlistInDatabase, _drawlist);
// Update camera position.
_cameraInDatabase->x = _camera->x;
_cameraInDatabase->y = _camera->y;
_cameraInDatabase->z = _camera->z;
_cameraInDatabase->p = _camera->p;
_cameraInDatabase->b = _camera->b;
_cameraInDatabase->h = _camera->h;
memcpy((byte *)0x29d, (byte *)A23D2_ZP_START, A23D2_ZP_LENGTH); // Save the ZP we're going to clobber.
POKEW(A23D2_IBP, DATABASE); // Set IBP.
__attribute__((leaf)) asm("jsr %[addy]":: [addy] "i"(A23D2_NXTPT) : "a","x","y","c","v"); // Call NXTPT.
// __attribute__((leaf)) asm("jsr 0x6118"::: "a","x","y","c","v"); // Call NXTPT.
memcpy((byte *)A23D2_ZP_START, (byte *)0x29d, A23D2_ZP_LENGTH); // Put the ZP back.
// Restore memory map.
POKE(MMU_MEM_BANK_4, 4);
POKE(MMU_MEM_BANK_3, 3);
}
void a23d2Sin(void) {
// Map 0-359 into 0-255.
_tdata = _tdata % 359;
_y1 = (360/256) * _tdata;
POKE(A23D2_TDATA, _y1);
asm("jsr %[addy]":: [addy] "i"(A23D2_SINEX));
_trig = (float)PEEKW(A23D2_TDATA) / 32768;
}

127
src/a23d2.h Normal file
View file

@ -0,0 +1,127 @@
/*
* 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.
*/
#ifdef __F256K__
#include "f256lib.h"
#else
#include <stdint.h>
#include <stdbool.h>
typedef uint8_t byte;
#endif
#define A23D2_FAR_BLOCK 42
#define DATABASE_FAR_BLOCK 43
#define DATABASE_FAR 0x56000
#define DRAWLIST_P0_FAR 0x57000
#define DRAWLIST_P1_FAR 0x57800
#define DATABASE 0x8000
#define DRAWLIST_P0 0x9000
#define DRAWLIST_P1 0x9800
// A2-3D2 Function Addresses.
#define A23D2_ENTRYS 0x606c
#define A23D2_NXTPT 0x6118
#define A23D2_SINEX 0x61f6
#define A23D2_COSEX 0x620f
// A2-3D2 Data Addresses.
#define A23D2_TEST_DATABASE 0x80fb
#define A23D2_TDATA 0x613e
#define A23D2_IBP 0x9b
#define A23D2_ZP_START 0x60
#define A23D2_ZP_LENGTH 0x62
// A2-3D1 Commands. Commented out items are Apple ][ only.
#define PNT 0x00 // xLSB, xMSB, yLSB, yMSB, zLSB, zMSB - Define 3D Point
#define SPNT 0x01 // xLSB, xMSB, yLSB, yMSB, zLSB, zMSB - Define 3D Start Point
#define CPNT 0x02 // xLSB, xMSB, yLSB, yMSB, zLSB, zMSB - Define 3D Continue Point
#define RAY 0x03 // xLSB, xMSB, yLSB, yMSB, zLSB, zMSB - Define 3D Ray
#define CLPSW 0x04 // N - Define Clipper (0=off, 1=on)
#define EYE 0x05 // xLSB, xMSB, yLSB, yMSB, zLSB, zMSB, P, B, H - Define 3D Eye/Camera
#define LIN2D 0x06 // X1, Y1, X2, Y2 - Draw 2D Line
//#define DISP 0x07 // N - Display Select (50=set graphics, 51=set text, 52=clear mixed, 53=set mixed, 54=set page 1, 55=set page 2, 56=clear hi-res, 57=set hi-res)
//#define ERAS 0x08 // N - Erase Screen (0=erase page 1, 1=erase page 2, 2=fill page 1, 3=fill page 2)
//#define DRAW 0x09 // N - Select Draw Page (0=page 1, 1=page 2)
#define PNT2D 0x0a // X1, Y1 - Draw 2D Point
#define JMP 0x0b // LSB, MSB - Interpretive Jump
//#define LMODE 0x0c // N - Line Drawing Mode (0=solid, 1=xor)
#define ARRAY 0x0d // LSB, MSB - Enable Output Array
#define SCRSZ 0x0e // WIDTH, HEIGHT, xCENTER, yCENTER - Define Screen Size
#define FIELD 0x0f // xLSB, xMSB, yLSB, yMSB, zLSB, zMSB - Field of View Selection
#define INIT 0x10 // Easy Init
#define NOP 0x11 // No Operation
// A2-3D2 Commands. Commented out items are Apple ][ only.
#define STCOL 0x12 // COL - Set Color
#define ICALL 0x13 // STAT, LOC, ADDR - Independent Object Call
#define SRES 0x14 // RES - Set Resolution (0=140x192, 1=280x192)
//#define HLIN 0x15 // x1L, x1H, y1, x2L, x2H, y2 - Hi-Res (280x192) Line 2D
//#define SHRB 0x16 // xL, xH, y - Set Hi-Res Bias
//#define HLIN2 0x17 // x1, y1, x2, y2 - Hi-Res (x limited) Line 2D
//#define HPNT 0x18 // xL, xH, y - Hi-Res (280x192) Point 2D
//#define HPNT2 0x19 // x, y - Hi-Res (x limited) Point 2D
#define SKIP 0x1a // SIZE, STATUS - Skip Segment
//#define PAUS 0x1b // TIME - Pause for TIME/5ths of a Second
#define SET323 0x1c // LSB, MSB - Set 3D to 3D Array Address
#define GN323 0x1d // STATUS - Set 3D to 3D Status
#define END 0x79 // End of Database
typedef struct cameraS {
int16_t x;
int16_t y;
int16_t z;
byte p;
byte b;
byte h;
} cameraT;
extern cameraT *_camera;
extern cameraT *_cameraInDatabase;
extern uint16_t _drawlist;
extern uint16_t _drawlistInDatabase;
extern byte *_pointer;
extern uint16_t _bytes;
extern uint8_t _x1;
#define _tdata _x1 // Alias.
extern uint8_t _y1;
extern uint8_t _x2;
extern uint8_t _y2;
extern bool _useColor;
extern byte _mmu;
extern byte _ram;
extern float _trig;
// NOTE:
// There are no function prototypes in this header.
// This library lives in it's own overlay segment and the overlay tool
// will generate prototypes as well as trampoline macros for us.

681
src/main.c Normal file
View file

@ -0,0 +1,681 @@
/*
* 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.
*/
/*
* Memory Map:
*
* Far Heap (upper 448k):
*
* 0x7ffff - Top of far heap / End of bitmap page 1
* - Bitmap page is 0x14000 (81920 bytes)
* 0x6c000 - Start of bitmap page 1
* 0x6bfff - End of bitmap page 2
* - Bitmap page is 0x14000 (81920 bytes)
* 0x58000 - Start of bitmap page 2
* 0x57fff - End of A2-3D2 and data to be used in slots 3 and 2/4 below
* - ...
* 0x54000 - Start of A2-3D2 and data to be used in slots 3 and 2/4 below
* - 57344 bytes free
* 0x45fff - End of overlay code
* - ...
* 0x10000 - Bottom of far heap / Start of overlay code
*
*
* Near Heap (lower 64k):
*
* 0xffff - Top of near heap
* - ...
* 0xe000 - Microkernel
* - ...
* 0xc000 - I/O and microkernel
* - ...
* 0xa000 - Overlay code
* 0x9fff - Top of virtual stack
* - ...
* 0x8000 - Program code / 3D/2D data
* - ...
* 0x6000 - Program code / 3D library
* - ...
* 0x0300 - Start of program code
* 0x02ff - End of program arguments / data storage
* - ... (We're going to overwrite this!)
* 0x0200 - Start of program arguments / data storage
* 0x01ff - Top of CPU stack space
* - ...
* 0x0100 - Bottom of CPU stack space
* 0x00ff - End of microkernel use
* - ...
* 0x00f0 - Start of microkernel use
* - 46 bytes free
* 0x00c2 - End of A2-3D2 use
* - ...
* 0x0060 - Start of A2-3D2 use
* - 49 bytes free
* 0x002f - llvm virtual register 31
* - ...
* 0x0010 - llvm virtual register 0
* 0x000f - MMU_MEM_BANK_7
* - ...
* 0x0008 - MMU_MEM_BANK_0
* - 7 bytes free
* 0x0001 - MMU_IO_CTRL
* 0x0000 - MMU_MEM_CTRL
*
*
* Calling the 3D Renderer:
*
* The MMU splits the near heap into 8k "slots" that 8k "blocks" of far heap
* can be mapped into. f256lib, by default, sets the near heap up as
* follows:
*
* 0xe000 - Slot 7 - Microkernel
* 0xc000 - Slot 6 - I/O and Microkernel
* 0xa000 - Slot 5 - Code Overlay Region
* 0x8000 - Slot 4 - Program Code
* 0x6000 - Slot 3 - Program Code
* 0x4000 - Slot 2 - Program Code
* 0x2000 - Slot 1 - Program Code
* 0x0000 - Slot 0 - Zero Page & Program Code
*
* Our 3D library, subLOGIC's A2-3D2, was designed to be run on an Apple ][.
* The Apple ][ has two high-resolution video pages in the middle of the
* memory map and A2-3D2 loads at 0x6000 immediately after the second video
* page. This makes sense on the Apple. On the Foenix, it's in the way at
* slot 3. The library also needs a 3D scene database and outputs an array
* of 2D drawing instructions. Given the size of A2-3D2 this pushes our
* memory usage up into slot 4. This leaves slots 0 to 2 for the simulator.
* That's less than 24k. Not good.
*
* We're going to place the 3D library and related data in the far heap and
* manually swap them in using the MMU when we need to render the scene. To
* do this, we need to ensure we're not currently executing code from
* somewhere in the slot 2 to 4 range. To accomplish this, all the functions
* that deal with A2-3D2 are placed in an overlay. This ensures whatever
* code calls into slots 2 to 4 has a program counter between 0xa000 and
* 0xbfff - well out of the way of the 3D stuff.
*
* The last thing to worry about is data needed by both the simulator and
* A2-3D2. This is basically only the camera position and rotation. These
* 10 bytes will be stored directly into RAM from 0x200 to 0x20a overwriting
* any command line arguments that may have been in memory.
*
*/
#include <string.h>
#define WITHOUT_FILE
#define WITHOUT_SPRITE
#define WITHOUT_TILE
#define F256LIB_IMPLEMENTATION
#include "f256lib.h"
#include "a23d2.h"
typedef struct airplaneS {
int8_t aileron; // -15 to 15
int8_t elevator; // -15 to 15
int8_t rudder; // -15 to 15
int8_t throttle; // -15 to 15
bool ignition;
bool engine;
int16_t rpm;
float hSpeed;
float vSpeed;
float deltaZ;
float efAOF;
float climbRate;
bool airborne;
bool stall;
bool brake;
int16_t x;
int16_t y;
int16_t z;
double pitch; // 0 to 255
double yaw; // 0 to 255
double roll; // 0 to 255
} airplaneT;
airplaneT _plane;
byte _gamepad;
/*
* embedded.bin is loaded at 0x54000:
*
* 8k a2-3d2.bin @ 0x54000
* 8k scene.3d @ 0x56000
*
*/
EMBED(".binarydata.embedded", embedded, "embedded.bin");
#define SEGMENT_MATH
#define PI 3.1415926
//#define Rads(d) (((d) < 0 ? (d) + 360 : (d)) * (PI / 180))
#define Degs(r) ((r) * (180 / PI))
// https://www.reddit.com/r/programming/comments/3e7ghi/discrete_arctan_in_6502/
#define ATAN_SPLINE_C0 (double)(-0.14380550980765507115) /* 3pi/4 - 5/2 */
#define ATAN_SPLINE_C1 (double)(-0.07079632679489661923) /* 3/2 - pi/2 */
double atan(double x){
if (x >= 0) {
if ( x<= 1) {
return x + (ATAN_SPLINE_C0 + ATAN_SPLINE_C1 * x) * x * x;
} else {
x = 1 / x;
return PI / 2 - (x + (ATAN_SPLINE_C0 + ATAN_SPLINE_C1 * x) * x * x);
}
} else {
if (x >= -1) {
return x - (ATAN_SPLINE_C0 - ATAN_SPLINE_C1 * x) * x * x;
} else {
x = -1 / x;
return (x + (ATAN_SPLINE_C0 + ATAN_SPLINE_C1 * x) * x * x) - PI / 2;
}
}
}
float sin(float d) {
_tdata = d;
a23d2Sin();
return _trig;
}
float cos(float d) {
_tdata = d;
a23d2Cos();
return _trig;
}
#define SEGMENT_INPUT
void getInput(void) {
static byte oneJoy = 0;
static byte twoJoy = 0;
static byte keyJoy = 0;
do {
kernelCall(NextEvent);
// Read real joysticks.
if (kernelEventData.type == kernelEvent(GAME)) {
oneJoy = kernelEventData.game.game0;
twoJoy = kernelEventData.game.game1;
}
//***TODO*** Add SNES/NES once I get some.
// Use keyboard as virtual joystick.
if (kernelEventData.type == kernelEvent(key.PRESSED)) {
switch (kernelEventData.key.ascii) {
case 'w':
case 'W':
keyJoy |= JOY_UP;
break;
case 'a':
case 'A':
keyJoy |= JOY_LEFT;
break;
case 's':
case 'S':
keyJoy |= JOY_DOWN;
break;
case 'd':
case 'D':
keyJoy |= JOY_RIGHT;
break;
case 'j':
case 'J':
keyJoy |= JOY_BUTTON_1;
break;
case 'k':
case 'K':
keyJoy |= JOY_BUTTON_2;
break;
case 'l':
case 'L':
keyJoy |= JOY_BUTTON_3;
break;
}
}
if (kernelEventData.type == kernelEvent(key.RELEASED)) {
switch (kernelEventData.key.ascii) {
case 'w':
case 'W':
keyJoy &= ~JOY_UP;
break;
case 'a':
case 'A':
keyJoy &= ~JOY_LEFT;
break;
case 's':
case 'S':
keyJoy &= ~JOY_DOWN;
break;
case 'd':
case 'D':
keyJoy &= ~JOY_RIGHT;
break;
case 'j':
case 'J':
keyJoy &= ~JOY_BUTTON_1;
break;
case 'k':
case 'K':
keyJoy &= ~JOY_BUTTON_2;
break;
case 'l':
case 'L':
keyJoy &= ~JOY_BUTTON_3;
break;
}
}
} while (kernelGetPending() > 0);
// Merge inputs. Yes, this allows dumb things like LEFT and RIGHT at the same time.
_gamepad = oneJoy | twoJoy | keyJoy;
}
#define SEGMENT_FLIGHT_MODEL_1
// These didn't use to be global. Ugly.
float tmpX;
float tmpY;
float tmpZ;
float newX;
float newY;
float newZ;
float iSpeed;
float lSpeed;
float hAccel;
float lVeloc;
float gVeloc;
float deltaZ;
float AOA;
float torque;
float torque2;
uint16_t loopTime = 40;
static double dPitch = 0;
static double dYaw = 0;
static double dRoll = 0;
static float collectX = 0;
static float collectY = 0;
static float collectZ = 0;
// This flight model is basically the one from "Build Your Own Flight Sim
// in C++" by Michael Radtke and Chris Lampton.
void lightPlane(void) {
// Flight model. Power Dynamics.
if (_plane.ignition) {
// Start engine.
if (!_plane.engine) _plane.engine = true;
// Adjust RPM.
if (_plane.rpm < (375 + (_plane.throttle * 117))) _plane.rpm += loopTime * 0.5; //***TODO*** T
if (_plane.rpm > (375 + (_plane.throttle * 117))) _plane.rpm -= loopTime * 0.5;
} else {
// Stop engine.
if (_plane.engine) _plane.engine = false;
// Run down engine.
if (_plane.rpm > 0) _plane.rpm -= (int16_t)(loopTime / 2); //***TODO*** This will never work.
if (_plane.rpm < 0) _plane.rpm = 0;
}
// Flight model. Flight Dynamics.
// Calculate speed from RPM.
iSpeed = _plane.rpm / 17.5;
// Modify speed by pitch.
iSpeed += (_plane.pitch * 1.5);
// Horizontal acceleration - thrust.
hAccel = ((_plane.rpm * (iSpeed - _plane.hSpeed)) / 10000);
hAccel /= 1000;
hAccel *= loopTime;
if (_plane.brake && !_plane.airborne) {
// Handle brakes.
if (_plane.hSpeed > 0) {
_plane.hSpeed -= 1;
} else {
_plane.hSpeed = 0;
}
} else {
// Accelerate normally.
_plane.hSpeed += hAccel;
}
// Force speed to range -1..1.
lSpeed = (_plane.hSpeed / 65) - 1;
if (lSpeed > 1) lSpeed = 1;
// Lift curve.
lVeloc = Degs(atan(lSpeed));
// Force lift to range 0..90.
lVeloc += 45;
// Shift range to 0..-17.
lVeloc /= 5.29;
// Multiply by pitch modifier.
lVeloc *= (-(_plane.pitch * .157) + 1);
// Time slice.
lVeloc /= 1000;
lVeloc *= loopTime;
// Gravity.
gVeloc = loopTime * (-16.0 / 10000); // -16.0 is ft/sec for gravity.
// Sum vertical velocity.
_plane.vSpeed = gVeloc + lVeloc;
// No vertical speed if we're on the ground.
if (!_plane.airborne && (_plane.vSpeed < 0)) _plane.vSpeed = 0;
// Save climb rate in ft/min.
_plane.climbRate = _plane.vSpeed / loopTime;
_plane.climbRate *= 60000L;
// Expand to ft/hr.
deltaZ = _plane.hSpeed * 5280;
// Get ft/ms.
deltaZ /= 3600000L;
deltaZ *= loopTime;
// Find effective angle of flight.
if (deltaZ) {
_plane.efAOF = -(atan(_plane.vSpeed / deltaZ));
} else {
_plane.efAOF = -(atan(_plane.vSpeed));
}
// Convert to degrees.
AOA = Degs(_plane.efAOF);
// Handle stalling.
if (((_plane.pitch < AOA) && (AOA < 0)) && (_plane.hSpeed < 40)) {
if ((_plane.pitch - AOA) < -20) _plane.stall = true;
}
if (_plane.stall) {
if (_plane.pitch > 30) {
_plane.stall = false;
} else {
_plane.pitch++;
}
}
}
#define SEGMENT_FLIGHT_MODEL_2
void lightPlane2(void) {
// Flight model. Inertial Damping.
if (dPitch) {
dPitch -= dPitch / 10;
if (((dPitch > 0) && (dPitch < 0.01)) || ((dPitch < 0) && (dPitch > -0.01))) dPitch = 0;
}
if (dYaw) {
dYaw -= dYaw / 10;
if (((dYaw > 0) && (dYaw < 0.01)) || ((dYaw < 0) && (dYaw > -0.01))) dYaw = 0;
}
if (dRoll) {
dRoll -= dRoll / 10;
if (((dRoll > 0) && (dRoll < 0.01)) || ((dRoll < 0) && (dRoll > -0.01))) dRoll = 0;
}
// Flight model. Rate of Change.
if (_plane.airborne) {
if (_plane.aileron != 0) {
torque = ((_plane.hSpeed * _plane.aileron) / 10000);
if (dRoll != (torque * loopTime)) dRoll += torque * 6;
}
}
if (_plane.elevator != 0) {
torque = ((_plane.hSpeed * _plane.elevator) / 10000);
if ((!_plane.airborne) && (torque > 0)) torque = 0; //***FIX*** This is dumb.
if (dPitch != (torque * loopTime)) dPitch += torque * 1.5;
}
if (_plane.hSpeed) {
torque = 0.0;
if (_plane.rudder != 0) torque -= ((_plane.hSpeed * _plane.rudder) / 10000);
torque2 = 0.0;
if ((_plane.roll > 0) && (_plane.roll <= 90)) { //***FIX*** This is dumb.
torque2 = _plane.roll * 0.00050;
} else {
if ((_plane.roll < 0) && (_plane.roll >= -90)) {
torque2 = _plane.roll * 0.00050;
}
}
torque += torque2;
if (dYaw != (torque * loopTime)) dYaw += torque * 1.5;
}
// Flight model. Apply Rotations.
// Transform pitch into components of yaw and pitch based on roll.
_plane.roll += dRoll;
_plane.yaw += dYaw;
_plane.pitch += (dPitch * cos(_plane.roll));
_plane.yaw += -(dPitch * sin(_plane.roll));
if (_plane.roll > 180) {
_plane.roll = -180 + (_plane.roll - 180);
} else {
if (_plane.roll < -180) _plane.roll = 180 + (_plane.roll + 180);
}
if (_plane.yaw > 180) {
_plane.yaw = -180 + (_plane.yaw - 180);
} else {
if (_plane.yaw < -180) _plane.yaw = 180 + (_plane.yaw + 180);
}
// Handle special case when aircraft pitch passes vertical.
if ((_plane.pitch > 90) || (_plane.pitch < -90)) {
if (_plane.roll >= 0) {
_plane.roll -= 180;
} else {
if (_plane.roll < 0) _plane.roll += 180;
}
if (_plane.yaw >= 0) {
_plane.yaw -= 180;
} else {
if (_plane.yaw < 0) _plane.yaw += 180;
}
if (_plane.pitch > 0) {
_plane.pitch = (180 - _plane.pitch);
} else {
if (_plane.pitch < 0) _plane.pitch = (-180 - _plane.pitch);
}
}
// Dampen everything out to 0 if they get close enough.
if ((_plane.pitch > -0.5) && (_plane.pitch < 0.5)) _plane.pitch = 0;
if ((_plane.roll > -0.5) && (_plane.roll < 0.5)) _plane.roll = 0;
if ((_plane.yaw > -0.5) && (_plane.yaw < 0.5)) _plane.yaw = 0;
}
#define SEGMENT_FLIGHT_MODEL_3
void moveAircraft(void) {
// Calculate new aircraft position. Each coordinate is 1 foot in 3D space.
tmpX = 0;
tmpY = 0;
tmpZ = deltaZ;
// Order of these points is significant.
// Rotate in Z.
newX = (tmpX * cos(_plane.roll)) - (tmpY * sin(_plane.roll));
newY = (tmpX * sin(_plane.roll)) + (tmpY * cos(_plane.roll));
tmpX = newX;
tmpY = newY;
// Rotate in X;
_plane.efAOF = Degs(_plane.efAOF);
newY = (tmpY * cos(_plane.efAOF)) - (tmpZ * sin(_plane.efAOF));
newZ = (tmpY * sin(_plane.efAOF)) + (tmpZ * cos(_plane.efAOF));
tmpY = newY;
tmpZ = newZ;
// Rotate in X;
newX = (tmpZ * sin(_plane.yaw)) + (tmpX * cos(_plane.yaw));
newZ = (tmpZ * cos(_plane.yaw)) - (tmpX * sin(_plane.yaw));
tmpX = newX;
tmpZ = newZ;
// Translate rotated point back to where it should be
// relative to it's last position.
collectX += newX;
if ((collectX > 1) || (collectX < -1)) {
_plane.x -= collectX;
collectX = 0;
}
collectY += newY;
if ((collectY > 1) || (collectY < -1)) {
_plane.y -= collectY;
collectY = 0;
}
collectZ += newZ;
if ((collectY > 1) || (collectY < -1)) {
_plane.z += collectZ;
collectZ = 0;
}
// Are we flying?
if ((!_plane.airborne) && (-_plane.y)) _plane.airborne = true;
}
#define SEGMENT_MAIN
void runSimulation(void) {
bool pageZero;
// Initialize airplane.
memset(&_plane, 0, sizeof(airplaneT));
// Which page is visible?
pageZero = true;
// Draw it!
while (true) {
// Flip pages.
if (pageZero) {
// Looking at bitmap 0.
pageZero = false;
bitmapSetActive(1);
bitmapSetVisible(0, true);
bitmapSetVisible(1, false);
_drawlist = DRAWLIST_P1;
} else {
// Looking at bitmap 1.
pageZero = true;
bitmapSetActive(0);
bitmapSetVisible(1, true);
bitmapSetVisible(0, false);
_drawlist = DRAWLIST_P0;
}
// Erase old scene.
bitmapSetColor(0);
_useColor = false;
a23d2Draw();
// Draw new scene.
a23d2Render();
_useColor = true;
a23d2Draw();
// Update player input.
getInput();
// Update aircraft control state.
if ((_gamepad & JOY_BUTTON_1) || (_gamepad & JOY_BUTTON_2) || (_gamepad & JOY_BUTTON_3)) {
// Modified input with button down.
if ((_gamepad & JOY_UP) && (_plane.throttle > -15)) _plane.throttle++;
if ((_gamepad & JOY_DOWN) && (_plane.throttle < 15)) _plane.throttle--;
if (_gamepad & JOY_RIGHT) _plane.brake = !_plane.brake;
} else {
// No button pressed.
if ((_gamepad & JOY_UP) && (_plane.elevator > -15)) _plane.elevator--;
if ((_gamepad & JOY_DOWN) && (_plane.elevator < 15)) _plane.elevator++;
if ((_gamepad & JOY_LEFT) && (_plane.aileron > -15)) _plane.aileron--;
if ((_gamepad & JOY_RIGHT) && (_plane.aileron < 15)) _plane.aileron++;
}
// "Coordinated" flight.
_plane.rudder = _plane.aileron;
// Do the actual flying!
lightPlane();
lightPlane2();
moveAircraft();
// Move camera.
_camera->p += 1; // Change pitch angle.
_camera->b += 2; // Change bank angle.
_camera->h += 3; // Change heading angle.
}
}
int main(int argc, char *argv[]) {
byte c;
byte ega[16][3] = {
{ 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0xaa },
{ 0x00, 0xaa, 0x00 },
{ 0x00, 0xaa, 0xaa },
{ 0xaa, 0x00, 0x00 },
{ 0xaa, 0x00, 0xaa },
{ 0xaa, 0x55, 0x00 },
{ 0xaa, 0xaa, 0xaa },
{ 0x55, 0x55, 0x55 },
{ 0x55, 0x55, 0xff },
{ 0x55, 0xff, 0x55 },
{ 0x55, 0xff, 0xff },
{ 0xff, 0x55, 0x55 },
{ 0xff, 0x55, 0xff },
{ 0xff, 0xff, 0x55 },
{ 0xff, 0xff, 0xff }
};
// Load EGA palette into CLUT0.
for (c=0; c<16; c++) graphicsDefineColor(0, c, ega[c][0], ega[c][1], ega[c][2]);
a23d2Init();
// Set up bitmap planes.
for (c=0; c<2; c++) {
// Clear screen.
bitmapSetActive(c);
bitmapSetColor(0);
bitmapClear();
// 3D Viewport edge.
bitmapSetColor(14);
bitmapLine(256, 0, 256, 239);
}
bitmapSetVisible(0, true);
bitmapSetVisible(1, false);
runSimulation();
return 0;
}

174
tools/scene.c Normal file
View file

@ -0,0 +1,174 @@
/*
* 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 <stdio.h>
#include <stdlib.h>
#include "a23d2.h"
#define LOW_BYTE(x) ((uint8_t)(x))
#define HIGH_BYTE(x) ((uint8_t)(((uint16_t)(x)) >> 8))
uint8_t db[8192] = { 0 };
uint16_t bytes = 0;
void cube(void) {
int16_t i = 0;
uint8_t scene[] = {
STCOL, 0x04, // Red
SPNT, 0x00, 0xff, 0x00, 0xff, 0x00, 0x03, // Cube
CPNT, 0x00, 0x01, 0x00, 0xff, 0x00, 0x03,
CPNT, 0x00, 0x01, 0x00, 0x01, 0x00, 0x03,
CPNT, 0x00, 0xff, 0x00, 0x01, 0x00, 0x03,
CPNT, 0x00, 0xff, 0x00, 0xff, 0x00, 0x03,
CPNT, 0x00, 0xff, 0x00, 0xff, 0x00, 0x05,
CPNT, 0x00, 0x01, 0x00, 0xff, 0x00, 0x05,
RAY, 0x00, 0x01, 0x00, 0xff, 0x00, 0x03,
CPNT, 0x00, 0x01, 0x00, 0x01, 0x00, 0x05,
RAY, 0x00, 0x01, 0x00, 0x01, 0x00, 0x03,
CPNT, 0x00, 0xff, 0x00, 0x01, 0x00, 0x05,
RAY, 0x00, 0xff, 0x00, 0x01, 0x00, 0x03,
CPNT, 0x00, 0xff, 0x00, 0xff, 0x00, 0x05,
SPNT, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, // Edge Line
CPNT, 0x00, 0x00, 0x80, 0x00, 0x00, 0x03,
END
};
while (scene[i] != END) {
db[bytes++] = scene[i++];
}
}
void landscape(void) {
int16_t i;
int16_t x;
int16_t y;
int16_t min = 0;
int16_t max = 10000;
db[bytes++] = STCOL;
db[bytes++] = 2; // Green
for (i=min; i<=max; i+=1000) {
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 100'
min = 1500;
max = min + 300;
x = 1500;
db[bytes++] = STCOL;
db[bytes++] = 8; // Dark Grey
for (i=0; i<=10; i+=2) {
db[bytes++] = SPNT;
db[bytes++] = LOW_BYTE(x+i); // X
db[bytes++] = HIGH_BYTE(x+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(x+i); // X
db[bytes++] = HIGH_BYTE(x+i);
db[bytes++] = 0; // Y
db[bytes++] = 0;
db[bytes++] = LOW_BYTE(max); // Z
db[bytes++] = HIGH_BYTE(max);
}
}
int main(int argc, char *argv[]) {
FILE *out;
(void)argc;
(void)argv;
// 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;
//cube();
landscape();
db[bytes++] = END;
out = fopen("scene.3d", "wb");
if (out) {
fwrite(db, 1, 8192, out);
fclose(out);
}
return 0;
}