commit d9db602f3de0ef572fdbb1fcacaba9931337e075 Author: Scott Duensing Date: Tue Apr 9 19:40:47 2024 -0500 Initial commit. diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..2e568dd --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +*.png filter=lfs diff=lfs merge=lfs -text +a2-3d/A2-3D2\#066000 filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9c2c119 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.builddir +backup/ +*.iso +*~ +*.pgz +apple2/ diff --git a/a2-3d/A2-3D2#066000 b/a2-3d/A2-3D2#066000 new file mode 100644 index 0000000..a3b2be1 --- /dev/null +++ b/a2-3d/A2-3D2#066000 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6086ef15bc228f0818ee834666916c25d3d18a8b3221754996bd76502702a92f +size 8443 diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..5b4065c --- /dev/null +++ b/build.sh @@ -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 diff --git a/f256.ld b/f256.ld new file mode 100644 index 0000000..af64ebd --- /dev/null +++ b/f256.ld @@ -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) +} diff --git a/icon.png b/icon.png new file mode 100644 index 0000000..77641d3 --- /dev/null +++ b/icon.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:391c3f7b3a40bc65d829079678ba906b8ccab942739555cf71261625af2cc0cb +size 19045 diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..3dc7ed0 --- /dev/null +++ b/run.sh @@ -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 diff --git a/src/a23d2.c b/src/a23d2.c new file mode 100644 index 0000000..e3359dd --- /dev/null +++ b/src/a23d2.c @@ -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 // 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; +} diff --git a/src/a23d2.h b/src/a23d2.h new file mode 100644 index 0000000..79d6d9f --- /dev/null +++ b/src/a23d2.h @@ -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 +#include +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. diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..fe94b0b --- /dev/null +++ b/src/main.c @@ -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 + +#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; +} diff --git a/tools/scene.c b/tools/scene.c new file mode 100644 index 0000000..1f00b3c --- /dev/null +++ b/tools/scene.c @@ -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 +#include +#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; +}