// remove asintab until it is needed? See how small the code can go...
#define GCC6809 1 // ONLY ON VIDE SYSTEM. Is there a predefined symbol I can test instead?
#ifdef NOTGCC6809
#undef GCC6809 // (sorry, couldn't find an easier way yet. Makefile fixes the problem...)
#endif
#ifdef VECTREX /* predefined by cmoc - don't define this with GCC for Vectrex */
#undef GCC6809
#include <vectrex/bios.h>
#define debugstr(s) /* ignore */
#define signed16 short
#define unsigned16 unsigned short
#define signed8 char
#define unsigned8 unsigned char
#define const /* const not supported in cmoc - const arrays require a #pragma */
#define inline /* also not supported by cmoc */
#pragma vx_copyright "2017"
#pragma vx_title_pos 0,-80
#pragma vx_title_size -8, 80
#pragma vx_title "CMOC VSN"
#pragma vx_music vx_music_1
/*
F3AD Mov_Draw_VLc_a
F3B1 Mov_Draw_VL_b
F3B5 Mov_Draw_VLcs
F3B7 Mov_Draw_VL_ab
F3B9 Mov_Draw_VL_a
F3BC Mov_Draw_VL
F3BE Mov_Draw_VL_d
F3CE Draw_VLc
F3D2 Draw_VL_b
F3D6 Draw_VLcs
F3D8 Draw_VL_ab
F3DA Draw_VL_a
F3DD Draw_VL
F3DF Draw_Line_d
F404 Draw_VLp_FF
F408 Draw_VLp_7F
F40C Draw_VLp_scale
F40E Draw_VLp_b
F410 Draw_VLp
F434 Draw_Pat_VL_a
F437 Draw_Pat_VL
F439 Draw_Pat_VL_d
F46E Draw_VL_mode
'PACKET' Style Drawing Routines
RUM Entry Label Used By
Subroutine Point Mine Storm Function
DASHY3 $F46E DASHPK Draw dashed lines from 'PACKET' list
PAC1X $F408 PACK1X Draw from 'PACKET' style list
PAC2X $F404 PACK2X Draw from 'PACKET' style list
PACB $F40E TPACK Draw from 'PACKET' list
PACKET $F410 --- Draw from 'PACKET' list
PACXX $F40C LPACK Draw from 'PACKET' style list
POTATA $F61F PROT 'PACKET' style rotate
POTATE $F622 APROT 'PACKET' style rotate
Bios source code:
PACXX LDB 0,X+ ;PICK UP TIMER INDEXED <---- aka Draw_VLp_scale
PACB STB T1LOLC ;ENTRY IF B HAS TIMER VALUE <---- aka Draw_VLp_b
PACKET LDD 1,X ;ENTRY POINT HERE IF T1LOLC VALID <---- aka Draw_VLp
STA DAC ;FORMAT=STATUS,X,Y
CLR PORT
LDA 0,X ;PICK UP VIDEO ENABLE
LEAX 3,X
INC PORT ;Y S/H
STB DAC ;SETUP X
STA SR ;VID CONTROL
CLR T1HOC ;START RAMP- FOR CRT PUT THIS BEFORE 'STB SR'
LDD #$40
EXIT BITB IFR
BEQ EXIT
NOP ;MOUSE DELAY
STA SR
LDA 0,X ;ANOTHER ONE?
BLE PACKET
JMP ZEGO
*/
void displaylist(int8_t* list) {
// scale is already set up.
asm {
PSHS X,Y,U,D,CC ; err on the side of safety until I get this working...
JSR DP_to_D0
LDX list
JSR Draw_VLp
PULS CC,D,U,Y,X
}
}
#else /* not cmoc */
#ifdef GCC6809
#include <vectrex.h>
#define debugstr(s) /* ignore */
#define signed16 long
#define unsigned16 unsigned long
#define signed8 signed char
#define unsigned8 unsigned char
static inline void intensity(unsigned8 brightness) {Intensity_a(brightness);}
static inline void dot(signed8 y, signed8 x) {Dot_d((int)y, (int)x);}
static inline void move(signed8 y, signed8 x) {Moveto_d((int)y, (int)x);}
static inline void reset_beam(void) {Reset0Ref();}
static inline void set_scale(unsigned8 scale) {/*VIA_t1_cnt_lo*/ *(volatile unsigned char *)0xD004 = scale;}
static inline void line(signed8 y, signed8 x) {*(volatile unsigned char *)0xC823 = 0; Draw_Line_d((int)y, (int)x);}
static inline void lines(unsigned8 size, const signed8 *array) { (void)size; (void)array; }
static inline void displaylist(const signed8 *list) {
// some assembly required
#define jsrx(v, func) asm("ldx %0\n\t" \
"jsr " #func "\n\t" : : "g" (v) : "d", "x")
#define Draw_VLp(ra) jsrx(ra, 0xF410) // (pattern y x)* 0x01
Draw_VLp(list);
}
static inline void wait_retrace(void) {Wait_Recal();}
#define signed16 long
#define unsigned16 unsigned long
#define signed8 signed char
#define unsigned8 unsigned char
#else /* anything except gcc for the 6809 */
#include <stdio.h>
#ifndef __GNUC__
#define inline /* GCC extension not available elsewhere - https://gcc.gnu.org/onlinedocs/gcc/Inline.html#Inline */
#endif
// Portable version - generate a debug trace only!
#define debugstr(s) printf("// %s\n", s)
inline void wait_retrace(void) {fflush(stdout); printf(" wait_retrace();\n");}
inline void move(char y, char x) {printf(" move(%d, %d);\n", (int)y, (int)x);}
inline void line(char y, char x) {printf(" line(%d, %d);\n", (int)y, (int)x);}
#define lines(size, array) printf(" lines(%d, %s);\n", (int)((unsigned char)(size)), #array)
#define displaylist(array) printf(" displaylist(%s);\n", #array)
inline void dot(char y, char x) {printf(" dot(%d, %d);\n", (int)y, (int)x);}
inline void reset_beam(void) {printf(" reset_beam();\n");}
inline void set_scale(unsigned char scale) {printf(" set_scale(%d);\n", (int)((unsigned char)scale));}
inline void intensity(char brightness) {printf(" intensity(%d);\n", (int)brightness);}
#define signed16 short
#define unsigned16 unsigned short
#define signed8 char
#define unsigned8 unsigned char
#endif /* not GCC6809 */
#endif /* not cmoc */
#define line_rel_XY(x,y) line(y,x)
#define move_rel_XY(x,y) move(y,x)
#define MAX_BRIGHTNESS (0x7f)
#define fp2_14 /* SIGNED */ signed16 /* we use a restricted range (2.14) fixed point for trig support */
#define ONE_POINT_ZERO ((fp2_14)(1<<14))
static signed16 Debug;
static signed16 vectors_dumped;
// All const arrays in cmoc must come in this section.
// Also, static scalars cannot be explicitly initialised
// - do so with assignments at program start instead.
// (Need to check if they're all set to 0 on startup)
#ifdef VECTREX
#pragma const_data start
#endif
/* Shields get a special proc because they're one continuous line */
static const signed8 shieldvec[] = {
24,24, 96,-96, 24,24, -72,72, -72,-72, 24,-24, 96,96, 24,-24, -72,-72, -72,72
};
static const signed8 crosshair_points[41] = {
0x03, 0x20, 0x3e, 0x20,
0x34, 0x16, 0x34, 0x2a,
0x16, 0x34, 0x2a, 0x34,
0x20, 0x02, 0x20, 0x3e,
0x16, 0x0c, 0x2a, 0x0c,
0x0c, 0x16, 0x0c, 0x2a,
0x1b, 0x2a, 0x25, 0x2a,
0x2a, 0x1b, 0x2a, 0x25,
0x1b, 0x16, 0x25, 0x16,
0x16, 0x1b, 0x16, 0x25,
0
};
static const signed8 tg_intro_placard[] = {
163/2 - 64,64/2 - 64, 93/2 - 64,64/2 - 64, 93/2 - 64,64/2 - 64, 93/2 - 64,192/2 - 64,
93/2 - 64,192/2 - 64, 163/2 - 64,192/2 - 64, 163/2 - 64,192/2 - 64, 163/2 - 64,64/2 - 64,
155/2 - 64,71/2 - 64, 101/2 - 64,71/2 - 64, 101/2 - 64,71/2 - 64, 101/2 - 64,185/2 - 64,
101/2 - 64,185/2 - 64, 155/2 - 64,185/2 - 64, 155/2 - 64,185/2 - 64, 155/2 - 64,71/2 - 64,
149/2 - 64,109/2 - 64, 149/2 - 64,93/2 - 64, 131/2 - 64,101/2 - 64, 149/2 - 64,101/2 - 64,
149/2 - 64,119/2 - 64, 131/2 - 64,113/2 - 64, 131/2 - 64,125/2 - 64, 149/2 - 64,119/2 - 64,
140/2 - 64,122/2 - 64, 140/2 - 64,116/2 - 64, 149/2 - 64,137/2 - 64, 131/2 - 64,137/2 - 64,
131/2 - 64,149/2 - 64, 149/2 - 64,149/2 - 64, 131/2 - 64,161/2 - 64, 131/2 - 64,149/2 - 64,
107/2 - 64,167/2 - 64, 125/2 - 64,167/2 - 64, 125/2 - 64,179/2 - 64, 125/2 - 64,167/2 - 64,
116/2 - 64,179/2 - 64, 125/2 - 64,179/2 - 64, 107/2 - 64,179/2 - 64, 116/2 - 64,176/2 - 64,
116/2 - 64,167/2 - 64, 116/2 - 64,179/2 - 64, 107/2 - 64,149/2 - 64, 107/2 - 64,161/2 - 64,
125/2 - 64,149/2 - 64, 107/2 - 64,149/2 - 64, 125/2 - 64,161/2 - 64, 125/2 - 64,149/2 - 64,
116/2 - 64,149/2 - 64, 116/2 - 64,155/2 - 64, 107/2 - 64,143/2 - 64, 125/2 - 64,143/2 - 64,
125/2 - 64,131/2 - 64, 107/2 - 64,143/2 - 64, 107/2 - 64,131/2 - 64, 125/2 - 64,131/2 - 64,
107/2 - 64,125/2 - 64, 125/2 - 64,125/2 - 64, 125/2 - 64,113/2 - 64, 107/2 - 64,125/2 - 64,
107/2 - 64,113/2 - 64, 125/2 - 64,113/2 - 64, 107/2 - 64,107/2 - 64, 125/2 - 64,107/2 - 64,
107/2 - 64,95/2 - 64, 107/2 - 64,107/2 - 64, 125/2 - 64,95/2 - 64, 107/2 - 64,95/2 - 64,
114/2 - 64,86/2 - 64, 114/2 - 64,92/2 - 64, 107/2 - 64,89/2 - 64, 114/2 - 64,89/2 - 64,
107/2 - 64,77/2 - 64, 107/2 - 64,89/2 - 64, 125/2 - 64,77/2 - 64, 107/2 - 64,77/2 - 64,
125/2 - 64,89/2 - 64, 125/2 - 64,77/2 - 64,
};
static const fp2_14 sine[65] = {
0, 402, 803, 1205, 1605, 2005, 2404, 2801, 3196, 3589, 3980, 4369, 4756, 5139, 5519, 5896,
6269, 6639, 7005, 7366, 7723, 8075, 8423, 8765, 9102, 9434, 9759, 10079, 10393, 10701, 11002, 11297,
11585, 11866, 12139, 12406, 12665, 12916, 13159, 13395, 13622, 13842, 14053, 14255, 14449, 14634, 14810, 14978,
15136, 15286, 15426, 15557, 15678, 15790, 15892, 15985, 16069, 16142, 16206, 16260, 16305, 16339, 16364, 16379,
16384 // => 1.0
};
// I've checked the results (see asin_test.c) and they're close enough for game work...
// Can halve the size of this table easily by using rotational symmetry around 0.
// (same as first part of sin table optimisation)
static const fp2_14 asintab[129] = { // multiply by 360 for degrees, by 256 for angles
// (float)asintab[x]*(360.0) / (1<<14) would be directly equivalent to scratch's asin
// but we want asintab[x]>>6 for 256 angles
-4096, -3770, -3634, -3530, -3442, -3365, -3294, -3230, -3169, -3112, -3058, -3007, -2958, -2911, -2865, -2821,
-2778, -2737, -2696, -2657, -2619, -2581, -2544, -2508, -2473, -2438, -2404, -2371, -2338, -2306, -2274, -2242,
-2211, -2181, -2151, -2121, -2091, -2062, -2033, -2005, -1977, -1949, -1921, -1894, -1867, -1840, -1813, -1787,
-1760, -1734, -1709, -1683, -1658, -1632, -1607, -1583, -1558, -1533, -1509, -1485, -1460, -1436, -1413, -1389,
-1365, -1342, -1319, -1295, -1272, -1249, -1226, -1203, -1181, -1158, -1136, -1113, -1091, -1069, -1046, -1024,
-1002, -980, -959, -937, -915, -893, -872, -850, -829, -807, -786, -765, -743, -722, -701, -680,
-659, -638, -617, -596, -575, -554, -533, -513, -492, -471, -450, -430, -409, -389, -368, -347,
-327, -306, -286, -265, -245, -224, -204, -183, -163, -143, -122, -102, -82, -61, -41, -20,
0, // X=-X: antisymmetric
};
// I think the first byte of three is really a pattern byte,
// so 0 looks like a move and 255 looks like a solid line,
// but it could equally be 0F for a dashed line etc...
#define DEFINE(name) static const signed8 name[] = {
#define move_rel_xy(x,y) 0 /*MOVE*/, (y), (x)
#define line_rel_xy(x,y) (signed8)-1 /*LINE*/, (y), (x)
#define ENDDEF(name) 1 /*STOP*/ }; static void SHOW_##name(void) { displaylist(name); }
// faster drawing routines
DEFINE(SCORE)
line_rel_xy(16,0), // to 16,0
line_rel_xy(0,12), // to 16,12
line_rel_xy(-16,0), // to 0,12
line_rel_xy(0,12), // to 0,24
line_rel_xy(16,0), // to 16,24
move_rel_xy(8,0), // to 24,24
line_rel_xy(16,0), // to 40,24
move_rel_xy(0,-24), // to 40,0
line_rel_xy(-16,0), // to 24,0
line_rel_xy(0,24), // to 24,24
move_rel_xy(24,0), // to 48,24
line_rel_xy(16,0), // to 64,24
line_rel_xy(0,-24), // to 64,0
line_rel_xy(-16,0), // to 48,0
line_rel_xy(0,24), // to 48,24
move_rel_xy(40,-24), // to 88,0
line_rel_xy(-4,12), // to 84,12
move_rel_xy(-12,0), // to 72,12
line_rel_xy(16,0), // to 88,12
line_rel_xy(0,12), // to 88,24
line_rel_xy(-16,0), // to 72,24
line_rel_xy(0,-24), // to 72,0
move_rel_xy(40,0), // to 112,0
line_rel_xy(-16,0), // to 96,0
line_rel_xy(0,24), // to 96,24
move_rel_xy(0,-12), // to 96,12
line_rel_xy(8,0), // to 104,12
move_rel_xy(-8,12), // to 96,24
line_rel_xy(16,0), // to 112,24
ENDDEF(SCORE)
DEFINE(HIGH_SCORE)
line_rel_xy(0,24), // to 0,24
move_rel_xy(0,-12), // to 0,12
line_rel_xy(16,0), // to 16,12
move_rel_xy(0,-12), // to 16,0
line_rel_xy(0,24), // to 16,24
move_rel_xy(16,-24), // to 32,0
line_rel_xy(0,24), // to 32,24
move_rel_xy(24,-12), // to 56,12
line_rel_xy(8,0), // to 64,12
line_rel_xy(0,-12), // to 64,0
line_rel_xy(-16,0), // to 48,0
line_rel_xy(0,24), // to 48,24
line_rel_xy(16,0), // to 64,24
move_rel_xy(8,-24), // to 72,0
line_rel_xy(0,24), // to 72,24
move_rel_xy(0,-12), // to 72,12
line_rel_xy(16,0), // to 88,12
move_rel_xy(0,-12), // to 88,0
line_rel_xy(0,24), // to 88,24
move_rel_xy(32,-24), // to 120,0
line_rel_xy(16,0), // to 136,0
line_rel_xy(0,12), // to 136,12
line_rel_xy(-16,0), // to 120,12
line_rel_xy(0,12), // to 120,24
line_rel_xy(16,0), // to 136,24
move_rel_xy(8,0), // to 144,24
line_rel_xy(16,0), // to 160,24
move_rel_xy(0,-24), // to 160,0
line_rel_xy(-16,0), // to 144,0
line_rel_xy(0,24), // to 144,24
move_rel_xy(24,0), // to 168,24
line_rel_xy(16,0), // to 184,24
line_rel_xy(0,-24), // to 184,0
line_rel_xy(-16,0), // to 168,0
line_rel_xy(0,24), // to 168,24
move_rel_xy(40,-24), // to 208,0
line_rel_xy(-4,12), // to 204,12
move_rel_xy(-12,0), // to 192,12
line_rel_xy(16,0), // to 208,12
line_rel_xy(0,12), // to 208,24
line_rel_xy(-16,0), // to 192,24
line_rel_xy(0,-24), // to 192,0
move_rel_xy(40,0), // to 232,0
line_rel_xy(-16,0), // to 216,0
line_rel_xy(0,24), // to 216,24
move_rel_xy(0,-12), // to 216,12
line_rel_xy(8,0), // to 224,12
move_rel_xy(-8,12), // to 216,24
line_rel_xy(16,0), // to 232,24
ENDDEF(HIGH_SCORE)
DEFINE(CREDITS)
move_rel_xy(0,24), // to 0,24
line_rel_xy(16,0), // to 16,24
move_rel_xy(0,-24), // to 16,0
line_rel_xy(-16,0), // to 0,0
line_rel_xy(0,24), // to 0,24
move_rel_xy(40,-24), // to 40,0
line_rel_xy(-4,12), // to 36,12
move_rel_xy(-12,0), // to 24,12
line_rel_xy(16,0), // to 40,12
line_rel_xy(0,12), // to 40,24
line_rel_xy(-16,0), // to 24,24
line_rel_xy(0,-24), // to 24,0
move_rel_xy(40,0), // to 64,0
line_rel_xy(-16,0), // to 48,0
line_rel_xy(0,24), // to 48,24
move_rel_xy(0,-12), // to 48,12
line_rel_xy(8,0), // to 56,12
move_rel_xy(-8,12), // to 48,24
line_rel_xy(16,0), // to 64,24
move_rel_xy(8,0), // to 72,24
line_rel_xy(12,0), // to 84,24
line_rel_xy(4,-12), // to 88,12
line_rel_xy(-4,-12), // to 84,0
line_rel_xy(-12,0), // to 72,0
line_rel_xy(0,24), // to 72,24
move_rel_xy(32,-24), // to 104,0
line_rel_xy(0,24), // to 104,24
move_rel_xy(16,0), // to 120,24
line_rel_xy(16,0), // to 136,24
move_rel_xy(-8,0), // to 128,24
line_rel_xy(0,-24), // to 128,0
move_rel_xy(16,0), // to 144,0
line_rel_xy(16,0), // to 160,0
line_rel_xy(0,12), // to 160,12
line_rel_xy(-16,0), // to 144,12
line_rel_xy(0,12), // to 144,24
line_rel_xy(16,0), // to 160,24
move_rel_xy(30,-24), // to 190,0
ENDDEF(CREDITS)
DEFINE(INSERT_COIN)
move_rel_xy(8,0), // to 8,0
line_rel_xy(0,24), // to 8,24
move_rel_xy(16,-24), // to 24,0
line_rel_xy(0,24), // to 24,24
line_rel_xy(16,-24), // to 40,0
line_rel_xy(0,24), // to 40,24
move_rel_xy(8,-24), // to 48,0
line_rel_xy(16,0), // to 64,0
line_rel_xy(0,12), // to 64,12
line_rel_xy(-16,0), // to 48,12
line_rel_xy(0,12), // to 48,24
line_rel_xy(16,0), // to 64,24
move_rel_xy(24,-24), // to 88,0
line_rel_xy(-16,0), // to 72,0
line_rel_xy(0,24), // to 72,24
move_rel_xy(0,-12), // to 72,12
line_rel_xy(8,0), // to 80,12
move_rel_xy(-8,12), // to 72,24
line_rel_xy(16,0), // to 88,24
move_rel_xy(24,-24), // to 112,0
line_rel_xy(-4,12), // to 108,12
move_rel_xy(-12,0), // to 96,12
line_rel_xy(16,0), // to 112,12
line_rel_xy(0,12), // to 112,24
line_rel_xy(-16,0), // to 96,24
line_rel_xy(0,-24), // to 96,0
move_rel_xy(24,24), // to 120,24
line_rel_xy(16,0), // to 136,24
move_rel_xy(-8,0), // to 128,24
line_rel_xy(0,-24), // to 128,0
move_rel_xy(40,24), // to 168,24
line_rel_xy(16,0), // to 184,24
move_rel_xy(0,-24), // to 184,0
line_rel_xy(-16,0), // to 168,0
line_rel_xy(0,24), // to 168,24
move_rel_xy(24,0), // to 192,24
line_rel_xy(16,0), // to 208,24
line_rel_xy(0,-24), // to 208,0
line_rel_xy(-16,0), // to 192,0
line_rel_xy(0,24), // to 192,24
move_rel_xy(32,-24), // to 224,0
line_rel_xy(0,24), // to 224,24
move_rel_xy(16,-24), // to 240,0
line_rel_xy(0,24), // to 240,24
line_rel_xy(16,-24), // to 256,0
line_rel_xy(0,24), // to 256,24
ENDDEF(INSERT_COIN)
DEFINE(PRESS_START)
line_rel_xy(0,24), // to 0,24
line_rel_xy(16,0), // to 16,24
line_rel_xy(0,-12), // to 16,12
line_rel_xy(-16,0), // to 0,12
move_rel_xy(40,-12), // to 40,0
line_rel_xy(-4,12), // to 36,12
move_rel_xy(-12,0), // to 24,12
line_rel_xy(16,0), // to 40,12
line_rel_xy(0,12), // to 40,24
line_rel_xy(-16,0), // to 24,24
line_rel_xy(0,-24), // to 24,0
move_rel_xy(40,0), // to 64,0
line_rel_xy(-16,0), // to 48,0
line_rel_xy(0,24), // to 48,24
move_rel_xy(0,-12), // to 48,12
line_rel_xy(8,0), // to 56,12
move_rel_xy(-8,12), // to 48,24
line_rel_xy(16,0), // to 64,24
move_rel_xy(8,-24), // to 72,0
line_rel_xy(16,0), // to 88,0
line_rel_xy(0,12), // to 88,12
line_rel_xy(-16,0), // to 72,12
line_rel_xy(0,12), // to 72,24
line_rel_xy(16,0), // to 88,24
move_rel_xy(8,-24), // to 96,0
line_rel_xy(16,0), // to 112,0
line_rel_xy(0,12), // to 112,12
line_rel_xy(-16,0), // to 96,12
line_rel_xy(0,12), // to 96,24
line_rel_xy(16,0), // to 112,24
move_rel_xy(32,-24), // to 144,0
line_rel_xy(16,0), // to 160,0
line_rel_xy(0,12), // to 160,12
line_rel_xy(-16,0), // to 144,12
line_rel_xy(0,12), // to 144,24
line_rel_xy(16,0), // to 160,24
move_rel_xy(8,0), // to 168,24
line_rel_xy(16,0), // to 184,24
move_rel_xy(-8,0), // to 176,24
line_rel_xy(0,-24), // to 176,0
move_rel_xy(32,0), // to 208,0
line_rel_xy(0,24), // to 208,24
move_rel_xy(-16,0), // to 192,24
line_rel_xy(16,0), // to 208,24
move_rel_xy(-16,-12), // to 192,12
line_rel_xy(16,0), // to 208,12
move_rel_xy(-16,12), // to 192,24
line_rel_xy(0,-24), // to 192,0
move_rel_xy(40,0), // to 232,0
line_rel_xy(-4,12), // to 228,12
move_rel_xy(-12,0), // to 216,12
line_rel_xy(16,0), // to 232,12
line_rel_xy(0,12), // to 232,24
line_rel_xy(-16,0), // to 216,24
line_rel_xy(0,-24), // to 216,0
move_rel_xy(24,24), // to 240,24
line_rel_xy(16,0), // to 256,24
move_rel_xy(-8,0), // to 248,24
line_rel_xy(0,-24), // to 248,0
ENDDEF(PRESS_START)
DEFINE(N0)
move_rel_xy(0,24), // to 0,24
line_rel_xy(16,0), // to 16,24
line_rel_xy(0,-24), // to 16,0
line_rel_xy(-16,0), // to 0,0
line_rel_xy(0,24), // to 0,24
move_rel_xy(24,-24), // to 24,0
ENDDEF(N0)
DEFINE(N1)
move_rel_xy(8,0), // to 8,0
line_rel_xy(0,24), // to 8,24
move_rel_xy(16,-24), // to 24,0
ENDDEF(N1)
DEFINE(N2)
move_rel_xy(0,24), // to 0,24
line_rel_xy(16,0), // to 16,24
line_rel_xy(0,-12), // to 16,12
line_rel_xy(-16,0), // to 0,12
line_rel_xy(0,-12), // to 0,0
line_rel_xy(16,0), // to 16,0
move_rel_xy(8,0), // to 24,0
ENDDEF(N2)
DEFINE(N3)
line_rel_xy(16,0), // to 16,0
move_rel_xy(-8,12), // to 8,12
line_rel_xy(8,0), // to 16,12
move_rel_xy(0,-12), // to 16,0
line_rel_xy(0,24), // to 16,24
line_rel_xy(-16,0), // to 0,24
move_rel_xy(24,-24), // to 24,0
ENDDEF(N3)
DEFINE(N4)
move_rel_xy(0,24), // to 0,24
line_rel_xy(0,-12), // to 0,12
line_rel_xy(16,0), // to 16,12
move_rel_xy(-8,0), // to 8,12
line_rel_xy(0,-12), // to 8,0
move_rel_xy(16,0), // to 24,0
ENDDEF(N4)
DEFINE(N5)
line_rel_xy(16,0), // to 16,0
line_rel_xy(0,12), // to 16,12
line_rel_xy(-16,0), // to 0,12
line_rel_xy(0,12), // to 0,24
line_rel_xy(16,0), // to 16,24
move_rel_xy(8,-24), // to 24,0
ENDDEF(N5)
DEFINE(N6)
move_rel_xy(0,12), // to 0,12
line_rel_xy(16,0), // to 16,12
line_rel_xy(0,-12), // to 16,0
line_rel_xy(-16,0), // to 0,0
line_rel_xy(0,24), // to 0,24
move_rel_xy(24,-24), // to 24,0
ENDDEF(N6)
DEFINE(N7)
move_rel_xy(8,0), // to 8,0
line_rel_xy(8,24), // to 16,24
line_rel_xy(-16,0), // to 0,24
move_rel_xy(24,-24), // to 24,0
ENDDEF(N7)
DEFINE(N8)
line_rel_xy(16,0), // to 16,0
move_rel_xy(-16,24), // to 0,24
line_rel_xy(16,0), // to 16,24
move_rel_xy(-16,-12), // to 0,12
line_rel_xy(16,0), // to 16,12
move_rel_xy(0,12), // to 16,24
line_rel_xy(0,-24), // to 16,0
move_rel_xy(-16,24), // to 0,24
line_rel_xy(0,-24), // to 0,0
move_rel_xy(24,0), // to 24,0
ENDDEF(N8)
DEFINE(N9)
move_rel_xy(16,24), // to 16,24
line_rel_xy(-16,0), // to 0,24
line_rel_xy(0,-12), // to 0,12
line_rel_xy(16,0), // to 16,12
move_rel_xy(0,12), // to 16,24
line_rel_xy(0,-24), // to 16,0
move_rel_xy(4,0), // to 24,0
ENDDEF(N9)
#ifdef VECTREX
#pragma const_data end
#endif
// 16 bit maths, no floats.
static signed16 rseed;
static signed16 urandom16(void)
{
// VECTREX HAS A BUILT-IN RANDOM THAT WE CAN USE!
return (rseed = (rseed * 2053) + 13849);
}
static unsigned16 irand(signed16 max) {
// returns int 0..max-1
return (unsigned16)(urandom16() % max); // probably not very random, but this is a video game, not crypto
}
static signed16 pickrandom(signed16 low, signed16 high)
{
// We'll just assume that high > low...
return (signed16)irand(high-low+1)+low; // pick a random integer between low and high inclusive.
}
#ifdef NOTYET
static unsigned8 numdigits(signed16 num)
{
if (num < 0) return 1+numdigits(-num);
if (num < 10) return 1U;
if (num < 100) return 2U;
if (num < 1000) return 3U;
if (num < 10000) return 4U;
return 5U;
}
#endif
static int /* boolean */ clipoff(signed16 x, signed16 y)
{
return ( -0x20 <= x && x < 0x20 && -0x12 <= y && y < 0x12);
}
static signed16 pxa[16], pya[16];
static signed16 star_x1(unsigned8 idx) {
unsigned8 xtype = (idx>>2)&3, ytype = idx&3;
idx = xtype ^ ytype; // four types of star trail ...
if (idx == 0) { // left edge
return -1016; // -127<<3 // COMPILER BUG in folded constant expression
} else if (idx == 2) { // right edge
return 1016; // 127<<3
} else { // top or bottom edges
return pickrandom(-127, 127)<<3; // could use Scratch's random byte directly...
}
}
static signed16 star_y1(unsigned8 idx) {
unsigned8 xtype = (idx>>2)&3, ytype = idx&3;
idx = xtype ^ ytype; // four types of star trail ...
if (idx == 1) { // top edge
return 1016; // 127<<3; // right edge
} else if (idx == 3) { // bottom edge
return -1016; // -127<<3;
} else {
return pickrandom(-127, 127)<<3;
}
}
static void move_star(unsigned8 starno)
{
signed16 x = pxa[starno], y=pya[starno];
// coordinate space is 0,0 center!
x = ((x<<5)-x)>>5;
y = ((y<<5)-y)>>5;
// move stars closer to the center, ie towards 0
if (clipoff(x>>3,y>>3)) {
x = star_x1(starno); y = star_y1(starno); // reset when they disappear
}
pxa[starno] = x; pya[starno] = y;
}
static void plot_star(unsigned8 starno)
{
signed16 brightx, brighty;
if (pxa[starno] < 0) brightx = -pxa[starno]; else brightx = pxa[starno];
brightx = (brightx>>3)&127;
if (pya[starno] < 0) brighty = -pya[starno]; else brighty = pya[starno];
brighty = (brighty>>3)&127;
// take this out if we don't want fading over distance...
// If we don't adjust individual star intensities, we can use
// the 'dots()' procedure to draw them all at once...
intensity((unsigned8)((brightx+brighty)>>1)); // good enough approximation for hypotenuse :-)
dot((signed8)(pxa[starno]>>4),(signed8)(pya[starno]>>4));
move((signed8)(-(pxa[starno]>>4)),(signed8)(-(pya[starno]>>4))); // correct back to 0,0 for next dot.
}
static void init_stars(void)
{
unsigned8 i, star;
for (star = 0U; star < 16U; star++) {
pxa[star] = star_x1(star); pya[star] = star_y1(star);
for (i=0; i < star*13; i++) move_star(star); // skew the starting points (QUICK HACK)
}
}
static void stars(unsigned8 speed) // will always be centered on 0,0
{
unsigned8 i, star;
debugstr("stars");
for (star = 0U; star < 16U; star++) {
for (i = 0U; i < speed; i++) move_star(star);
}
for (star = 0U; star < 16U; star++) {
if ((star & 15) == 0) {
// control the frequency that the beam is reset!
// Use a smaller mask if there is jitter if only
// doing this once every 16 stars
reset_beam();
set_scale(0xF0);
move(0, 0);
}
plot_star(star);
}
intensity(MAX_BRIGHTNESS); // restore default
}
/* Shields on the vectrex are sideways wrt the 'real' tailgunner */
static void draw_shields(unsigned8 scale)
{
const signed8 *ptr = shieldvec;
signed16 i;
signed8 x1, y1;
debugstr("draw_shields");
reset_beam();
set_scale(0x7f);
move(0, 0);
set_scale(scale);
move(-72, 24); /* Should move the starting point into the array... */
for (i = 0; i < 10; i++) {
/* DRAW THE VECTOR */
y1 = (*ptr++);
x1 = (*ptr++);
line(y1, x1); // Swapped x for y because of portrait mode Vectrex display
}
}
static void Draw_crosshair(signed8 absx, signed8 absy, unsigned8 scale)
{
signed8 x1, y1, x2, y2, dx, dy;
const signed8 *ptr = crosshair_points;
debugstr("Draw_crosshair");
reset_beam();
set_scale(0x7f);
move(absx, absy);
set_scale(scale);
move(-0x20, -0x20);
for (;;) {
/* DRAW THE VECTOR */
x1 = *ptr++; if (x1 == 0) break;
y1 = *ptr++;
x2 = *ptr++;
y2 = *ptr++;
dx = x2-x1;
dy = y2-y1;
move(y1, x1);
line(dy, dx );
move(-dy-y1,-dx-x1);
//move(-y1, -x1);
/* back at relative 0,0 */
}
}
/* 3d rendering */
// Remember that changing FOCAL_LENGTH changes the scale it is drawn at as well!
// Maybe not the proper name for this parameter?
//#define FOCAL_LENGTH_SHIFT 7 /* 128 */
//#define FOCAL_LENGTH_SHIFT 8 /* 256 */
#define FOCAL_LENGTH_SHIFT 9 /* 512 */
//#define FOCAL_LENGTH_SHIFT 10 /* 1024 */
#define FOCAL_LENGTH ((signed16)1<<(signed16)FOCAL_LENGTH_SHIFT)
static unsigned8 Credits;
static unsigned8 intro_rot;
static signed16 frame_number;
static unsigned8 global_flashing_intensity;
static signed16 HighScore;
static signed16 LastScore;
static signed16 Score;
#ifdef NOTYET
static signed16 UsingShields;
static signed16 Shields;
static signed16 shieldsegment;
static signed16 shieldsubticksleft;
#endif
static fp2_14 sinsymmetry(unsigned8 angle128)
{
// '>', not '>=', is a special case for sin(x)=1.0
if (angle128 > 64U) return sine[128-angle128]; else return sine[angle128];
}
static fp2_14 fp2_14_sin(signed16 angle256)
{
if ((unsigned8)angle256 >= 128U) return -sinsymmetry((unsigned8)(angle256-128)&127); else return sinsymmetry((unsigned8)angle256&127);
}
static fp2_14 fp2_14_cos(signed16 angle256)
{
return fp2_14_sin(angle256+64); // implicit &255 in these unsigned8 parameters
}
// The revolving intro placard...
static void drawtgintro(unsigned8 scale, unsigned8 bright) {
signed16 i, from_x, from_y, to_x, to_y, objects, delta_fx, delta_tx;
const signed8 *ptr;
fp2_14 sin_rot, cos_rot;
debugstr("drawtgintro");
reset_beam();
set_scale(scale);
move(0,0); vectors_dumped = 0;
intensity(bright);
if ((intro_rot&128) == 0) { // are we looking at the front of the tumbling intro placard?
objects = ((sizeof (tg_intro_placard) / sizeof (tg_intro_placard[0])) / 4); // yes - draw rectangles and text together
} else {
objects = 8; // no - just draw the rectangles
}
sin_rot = fp2_14_sin (intro_rot); cos_rot = fp2_14_cos (intro_rot);
ptr = tg_intro_placard;
for (i = 0; i < objects; i++) {
from_y = *ptr++; // make coords center on 0,0 - this can be moved into the const array
from_x = *ptr++;
to_y = *ptr++;
to_x = *ptr++;
if (intro_rot != 64) { // optimise case where the placard is face-on.
// y coords signed, in range -64:63 => 0..127
// These all need some tweaking to reduce inaccuracy due to lack of precision
// - it is visibly wrong, but for now we will just live with it...
delta_fx = (from_x * (from_y)) >> 8;
delta_tx = (to_x * (to_y)) >> 8;
from_x += (delta_fx * (cos_rot>>4)) >> 10;
to_x += (delta_tx * (cos_rot>>4)) >> 10;
from_y = (from_y * (sin_rot>>4)) >> 10;
to_y = (to_y * (sin_rot>>4)) >> 10;
}
// I did implement a layer on top of move/line which optimised consecutive moves
// and eliminated redundant ones where lines were adjacent, but for reasons that
// are too long-winded to be worth going into, I've temporarily removed that code;
// it can be found in the rcs logs if/when I add it back in.
move((signed8)(from_y), (signed8)(from_x));
line((signed8)(to_y-from_y), (signed8)(to_x-from_x));
move((signed8)-(to_y), (signed8)-(to_x));
vectors_dumped += 3;
if (vectors_dumped > 12) {
reset_beam(); // we are already at 0,0
// not sure if these are needed?:
set_scale(scale);
move(0,0); vectors_dumped = 0;
intensity(bright);
}
}
}
static void position_and_scale(signed8 absx, signed8 absy, signed16 scale)
{
reset_beam();
set_scale(0x7f);
move_rel_XY(absx, absy);
set_scale((unsigned8)scale);
}
static void show_digit(signed8 digit)
{
// a binary chop or an array of procedures might be
// faster depending on how switch is implemented, however
// cmoc does not support arrays of procedure pointers.
switch (digit) {
case 0: SHOW_N0(); break;
case 1: SHOW_N1(); break;
case 2: SHOW_N2(); break;
case 3: SHOW_N3(); break;
case 4: SHOW_N4(); break;
case 5: SHOW_N5(); break;
case 6: SHOW_N6(); break;
case 7: SHOW_N7(); break;
case 8: SHOW_N8(); break;
case 9: SHOW_N9(); break;
}
}
static void SHOW_NUM(signed16 num, signed8 absx, signed8 absy, unsigned16 scale) { // left-aligned
signed8 digit, zeroes;
if (scale) position_and_scale(absx, absy, 0x20);
// This replaces code that used divide by 10 and modulo 10. Much faster.
#ifdef PREV_VERSION
// doesn't handle -32768
if (num < 0) num = -num; // TO DO... font character for '-'...
zeroes = 0;
// max 33 add/subtracts... could be less if use 8000, 4000, 2000, 1000 etc to chop instead
digit = 0; while (num >= 10000) { num -= 10000; digit += 1; zeroes = 1; } if (zeroes) show_digit(digit);
digit = 0; while (num >= 1000) { num -= 1000; digit += 1; zeroes = 1; } if (zeroes) show_digit(digit);
digit = 0; while (num >= 100) { num -= 100; digit += 1; zeroes = 1; } if (zeroes) show_digit(digit);
digit = 0; while (num >= 10) { num -= 10; digit += 1; zeroes = 1; } if (zeroes) show_digit(digit);
show_digit((signed8)num);
#endif
// handles -32768:32767
if (num >= 0) num = -num; /* else MINUS(); */ // TO DO: add a font character for '-'.
digit = 0;
zeroes = 1; // CLRing is shorter
// max 11 add/subtracts...
if (num <= -20000) { num += 20000; digit += 2; zeroes = 0; }
if (num <= -10000) { num += 10000; digit += 1; zeroes = 0; }
if (!zeroes) show_digit(digit);
digit = 0;
if (num <= -8000) { num += 8000; digit += 8; zeroes = 0; } else if (num <= -4000) { num += 4000; digit += 4; zeroes = 0; }
if (num <= -2000) { num += 2000; digit += 2; zeroes = 0; }
if (num <= -1000) { num += 1000; digit += 1; zeroes = 0; }
if (!zeroes) show_digit(digit);
digit = 0;
if (num <= -800) { num += 800; digit += 8; zeroes = 0; } else if (num <= -400) { num += 400; digit += 4; zeroes = 0; }
if (num <= -200) { num += 200; digit += 2; zeroes = 0; }
if (num <= -100) { num += 100; digit += 1; zeroes = 0; }
if (!zeroes) show_digit(digit);
digit = 0;
if (num <= -80) { num += 80; digit += 8; zeroes = 0; } else if (num <= -40) { num += 40; digit += 4; zeroes = 0; }
if (num <= -20) { num += 20; digit += 2; zeroes = 0; }
if (num <= -10) { num += 10; digit += 1; zeroes = 0; }
if (!zeroes) show_digit(digit);
show_digit((signed8)-num);
}
static void Draw_lastscore(void)
{
debugstr("Draw_lastscore");
position_and_scale(-100,127,32);
SHOW_SCORE();
SHOW_NUM(LastScore, -98,113, 256);
}
static void Draw_highscore(void)
{
debugstr("Draw_highscore");
position_and_scale(40,127,32);
SHOW_HIGH_SCORE();
SHOW_NUM(HighScore, 52,113, 256);
}
static void Draw_insertcoin(void) // Not yet used
{
debugstr("Draw_insertcoin");
intensity(global_flashing_intensity<<1);
position_and_scale(-40,-80,32);
SHOW_INSERT_COIN();
}
static void Draw_start(void)
{
debugstr("Draw_start");
intensity(global_flashing_intensity<<1);
position_and_scale(-39,-80,32);
SHOW_PRESS_START();
}
static void Draw_credits(void)
{
debugstr("Draw_credits");
position_and_scale(-46,-30,48);
SHOW_CREDITS();
if (Credits > 9U) {
SHOW_NUM(9, 0,0,0); // display any more than 9 as 9. (This is what many arcade machines did in real life)
} else {
SHOW_NUM(Credits, 0,0,0);
}
}
static void Draw_ships_remaining(void)
{
SHOW_NUM(10, 90,120, 64);
}
static void Draw_shields_remaining(void)
{
SHOW_NUM(80, -5,127, 128);
}
static void Draw_score(void)
{
SHOW_NUM(Score, -100,120, 64);
}
static void next_frame(void)
{
debugstr("next_frame");
wait_retrace();
if ((frame_number & 16) == 0) {
global_flashing_intensity += 4;
} else {
global_flashing_intensity -= 4;
}
frame_number++;
//SHOW_NUM(Debug, -100,-100, 128);
}
int main(void) { // cmoc doesn't allow argc, argv
signed16 frames;
unsigned8 placard_scale, placard_brightness;
vectors_dumped = 0;
global_flashing_intensity = 0; frame_number = 0; // start flashing from off position
frame_number = 0;
init_stars();
reset_beam();
set_scale(0x7f);
move(0,0);
intensity(MAX_BRIGHTNESS);
Debug = 0; Score = 72; HighScore = 2047; LastScore = 132; Credits = 0;
for (;;) {
debugstr("main loop");
// approach banner:
placard_scale = (unsigned8)((signed16)(0xFF-160));
placard_brightness = (unsigned8)((signed16)(((signed16)MAX_BRIGHTNESS+(signed16)MAX_BRIGHTNESS-160)));
intro_rot = 128;
// start at 128 step by -2 end at 64 after one full revolution (128 + 32 steps)
for (frames = 0; frames < 160; frames++) {
next_frame();
drawtgintro(placard_scale, placard_brightness>>1);
if ((intro_rot&128) != 0) {
next_frame();
drawtgintro(placard_scale, placard_brightness>>1);
next_frame();
drawtgintro(placard_scale, placard_brightness>>1);
}
placard_scale++; placard_brightness++;
//stars(1);
intro_rot -= 2;
}
{signed16 i;
for (i = 0; i < 2; i++) {
placard_brightness = MAX_BRIGHTNESS&0xF8;
while (placard_brightness != 0) {
next_frame();
drawtgintro(placard_scale, placard_brightness);
//stars(1);
placard_brightness -= 8;
}
while (placard_brightness != (MAX_BRIGHTNESS&0xF8)) {
placard_brightness += 8;
next_frame();
drawtgintro(placard_scale, placard_brightness);
//stars(1);
}
}
}
placard_brightness = MAX_BRIGHTNESS&0xF8;
while (placard_brightness != 0) {
next_frame();
drawtgintro(placard_scale, placard_brightness);
// unfortunately this does not affect stars: intensity(MAX_BRIGHTNESS-placard_brightness);
stars(1);
placard_brightness -= 8;
}
intensity(MAX_BRIGHTNESS);
//stars:
debugstr("wait for coins...");
global_flashing_intensity = 0; frame_number = 0; // start flashing from off position
for (frames = 0; frames < 255; frames++) {
next_frame();
stars(1);
if (frames < 80) {
Draw_insertcoin();
intensity(MAX_BRIGHTNESS);
Draw_lastscore();
Draw_highscore();
} else if (frames < 160) {
if (frames == 80) Credits += 1;
if (frames == 120) Credits += 1;
Draw_start();
intensity(MAX_BRIGHTNESS);
Draw_credits();
Draw_lastscore();
Draw_highscore();
} else {
Draw_ships_remaining();
Draw_shields_remaining();
Draw_score();
Draw_crosshair((signed8)(frames>>1), (signed8)(-(frames>>2)), 0x38);
}
}
Credits = 0;
// shields:
debugstr("shields...");
for (frames = 0; frames < 200; frames++) {
next_frame();
if ((frames > 120) && (frames < 200)) {
intensity((unsigned8)pickrandom(64,127));
if (pickrandom(0,31)) draw_shields(0xF0);
} else {
Draw_crosshair((signed8)(frames>>1), (signed8)(-(frames>>2)), 0x38); // don't allow shooting while shields are up...
}
stars(1);
Draw_ships_remaining();
Draw_shields_remaining();
Draw_score();
//Only in attract mode:
//Draw_lastscore();
//Draw_highscore();
}
// whoosh:
debugstr("whoosh...");
for (frames = 0; frames < 64; frames++) {
next_frame();
stars(3);
intensity((unsigned8)(64-frames)*2);
Draw_ships_remaining();
Draw_shields_remaining();
Draw_score();
//Only in attract mode:
//Draw_lastscore();
//Draw_highscore();
}
}
return 0;
}