atari8: updated presets, added 5th player

This commit is contained in:
Steven Hugg committed 2026-09-19 18:04:17 +02:00
1 parent e89e339dcd
commit 087759549d
6 files changed
+358 -16

No files matched your search

+57 -3
View File
@@ -378,6 +378,60 @@ void a8_pmg_clear_collisions(void) {
GTIA_WRITE.hitclr = 0;
}
static byte pmg_sizem;
void a8_pmg_5th_enable(byte on) {
if (on) {
GTIA_WRITE.prior |= PRIOR_5TH_PLAYER;
GTIA_WRITE.gractl |= GRACTL_MISSLES;
GTIA_WRITE.sizem = 0; /* every missile 2 px wide, so they tile */
} else {
GTIA_WRITE.prior &= (byte)~PRIOR_5TH_PLAYER;
}
pmg_sizem = 0;
}
/*
0: 8 color clocks (1 per pixel)
1: 16 color clocks (2 per pixel)
3: 32 color clocks (4 per pixel)
*/
void a8_pmg_5th_size(byte size) {
size &= 3;
pmg_sizem = size;
size |= size << 2;
size |= size << 4;
GTIA_WRITE.sizem = size;
}
static const byte pmg_size_tab[4] = { 2, 4, 2, 8 };
void a8_pmg_5th_x(byte x) {
byte* m = (byte*)&GTIA_WRITE.hposm0;
byte inc = pmg_size_tab[pmg_sizem];
m[0] = x;
x += inc;
m[1] = x;
x += inc;
m[2] = x;
x += inc;
m[3] = x;
}
void a8_pmg_5th_shape(const byte* shape, byte len, byte y) {
byte* p = a8_pmg_missile(0) + y;
while (len--) {
/* GRAFM packs two bits per missile, M0 in bits 1:0, and each missile
shifts its pair out MSB first, so the leftmost pixel is bit 1. Swap
the bit pairs to accept the usual player order (bit 7 = leftmost). */
byte s = *shape++;
*p++ = (byte)(((s >> 6) & 0x03)
| ((s >> 2) & 0x0c)
| ((s << 2) & 0x30)
| ((s << 6) & 0xc0));
}
}
/*==========================================================================*/
/* POKEY */
@@ -466,16 +520,16 @@ void a8_set_colpm(byte i, byte c) {
/* Input and utility */
/*==========================================================================*/
byte a8_console(void) {
byte a8_get_console(void) {
return GTIA_READ.consol;
}
byte a8_trigger(byte n) {
byte a8_get_trigger(byte n) {
return (byte)(((byte*)&GTIA_READ.trig0)[n] & 1 ? 0 : 1);
}
#if !defined(__ATARI5200__)
byte a8_stick(byte n) {
byte a8_get_stick(byte n) {
byte v = n ? PIA.portb : PIA.porta;
return (byte)((~v) & 0x1f);
}
+20 -3
View File
@@ -232,6 +232,23 @@ byte a8_pmg_hit_pf(byte i); /* player i hit playfield? */
byte a8_pmg_hit_pl(void); /* any player-player hit mask */
void a8_pmg_clear_collisions(void);
/* The four missiles can be combined into one extra, 8-pixel-wide "5th
player", colored with COLPF3 (PRIOR bit 4). It shares the missile
hardware, so the per-missile helpers above don't work while it is on.
The four missiles sit two pixels apart, M0 leftmost. Like players,
the shape lives in the P/M area (ANTIC reloads GRAFM from memory every
scanline), so write it with a8_pmg_5th_shape() rather than GRAFM. */
#ifndef PRIOR_5TH_PLAYER
#define PRIOR_5TH_PLAYER 0x10
#endif
void a8_pmg_5th_enable(byte on);
/* x is the left edge of the 8-pixel player. */
void a8_pmg_5th_x(byte x);
/* Copy a shape into the missile area at vertical byte offset y. len and
orientation are just like a8_pmg_set_shape(): bit 7 is the left pixel. */
void a8_pmg_5th_shape(const byte* shape, byte len, byte y);
void a8_pmg_5th_size(byte size);
/*==========================================================================*/
/* POKEY sound */
@@ -272,13 +289,13 @@ void a8_set_colpm(byte i, byte c);
/*==========================================================================*/
/* Console keys: bit0 start, bit1 select, bit2 option (0 = pressed). */
byte a8_console(void);
byte a8_get_console(void);
/* Trigger n (0..3): nonzero when pressed. */
byte a8_trigger(byte n);
byte a8_get_trigger(byte n);
#if !defined(__ATARI5200__)
/* Joystick 0/1 direction+fire bits (JOY_xxx_MASK order). 0 if none. */
byte a8_stick(byte n);
byte a8_get_stick(byte n);
#endif
/* Wait for the start of the next frame. (cc65 provides waitvsync(),
+30 -8
View File
@@ -58,6 +58,7 @@ static void build_display(void) {
void main(void) {
byte x = 128;
byte dx = 2;
byte i;
fill_screen();
build_display();
@@ -73,10 +74,18 @@ void main(void) {
a8_dli_install();
a8_pmg_init(A8_PMG_DOUBLE);
a8_pmg_set_x(0, x);
a8_pmg_set_color(0, A8_COLOR(HUE_GREY, 0x0f));
a8_pmg_set_size(0, A8_PMG_NORMAL);
a8_pmg_set_shape(0, ball, sizeof(ball), 0x38);
// set up all 4 sprites
for (i=0; i<4; i++) {
a8_pmg_set_x(i, x);
a8_pmg_set_color(i, A8_COLOR(HUE_GREY, 0x0f-i));
a8_pmg_set_size(i, A8_PMG_NORMAL);
a8_pmg_set_shape(i, ball, sizeof(ball), i*16 + 8);
}
// combine missiles into 5th sprite
// colors are taken from the 4 sprites
a8_pmg_5th_enable(1);
a8_pmg_5th_x(100);
a8_pmg_5th_shape(ball, sizeof(ball), 0x48);
// set interrupt and start music
a8_pokey_init();
@@ -89,12 +98,25 @@ void main(void) {
x += dx;
if (x < 48) { x = 48; dx = 2; }
if (x > 208) { x = 208; dx = (byte)-2; }
a8_pmg_set_x(0, x);
if (a8_trigger(0)) {
a8_pmg_set_size(0, A8_PMG_QUADW);
// set sprite sizes if joystick moved or trigger pushed
if (a8_get_trigger(0)) {
a8_pmg_5th_size(A8_PMG_QUADW);
} else {
a8_pmg_5th_size(A8_PMG_NORMAL);
}
if (a8_get_stick(0)) {
a8_pmg_set_size(a8_get_stick(0) & 3, A8_PMG_QUADW);
} else {
a8_pmg_set_size(0, A8_PMG_NORMAL);
a8_pmg_set_size(1, A8_PMG_NORMAL);
a8_pmg_set_size(2, A8_PMG_NORMAL);
a8_pmg_set_size(3, A8_PMG_NORMAL);
}
// set X positions
a8_pmg_set_x(0, x);
a8_pmg_set_x(1, x);
a8_pmg_set_x(2, x);
a8_pmg_set_x(3, x);
a8_pmg_5th_x(255-x);
}
}
+2 -2
View File
@@ -18,8 +18,8 @@ typedef signed char sbyte;
typedef unsigned short word;
// pointer to screen memory
// TODO: use OS.savmsc
static byte** SAVMSC = (byte**) 0x58;
// pointer to screen memory
static byte** SAVMSC = &OS.savmsc;
byte getchar(byte x, byte y) {
return (*SAVMSC)[x+y*COLS]; // lookup value @ cursor address
+248
View File
@@ -0,0 +1,248 @@
#include <stdio.h>
#include <stdlib.h>
#include <cc65.h>
#include <conio.h>
#include <ctype.h>
#include <modload.h>
#include <tgi.h>
#include <atari.h>
//#tooldef ld cfgfile=atari.cfg
//#symbol ld __RESERVED_MEMORY__=8192
/* Color values passed to TGI functions are indices into the default palette. */
#define COLOR_BACK 0
#define COLOR_FORE 1
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* Driver stuff */
static unsigned MaxX;
static unsigned MaxY;
static unsigned AspectRatio;
/*****************************************************************************/
/* Code */
/*****************************************************************************/
static void CheckError (const char* S)
{
unsigned char Error = tgi_geterror ();
if (Error != TGI_ERR_OK) {
cputs("Error ");
cputs(S);
if (doesclrscrafterexit ()) {
cgetc ();
}
exit (EXIT_FAILURE);
}
}
/*
* Note that everywhere else in the TGI API, colors are referred to via an index
* to the current palette.
*
* TGI_COLOR_ values can be used (ONLY!) for setting the palette, using them
* with other TGI functions only works by chance, on some targets.
*/
static void DoPalette (int n)
{
static const unsigned char Palette[4][2] = {
{ TGI_COLOR_BLACK, TGI_COLOR_BLUE },
{ TGI_COLOR_WHITE, TGI_COLOR_BLACK },
{ TGI_COLOR_RED, TGI_COLOR_BLACK },
};
tgi_setpalette (Palette[n]);
}
static void DoCircles (void)
{
unsigned char I;
unsigned char Color = COLOR_BACK;
const unsigned X = MaxX / 2;
const unsigned Y = MaxY / 2;
const unsigned Limit = (X < Y) ? Y : X;
tgi_setcolor (COLOR_FORE);
tgi_clear ();
tgi_line (0, 0, MaxX, MaxY);
tgi_line (0, MaxY, MaxX, 0);
while (!kbhit ()) {
Color = (Color == COLOR_FORE) ? COLOR_BACK : COLOR_FORE;
tgi_setcolor (Color);
for (I = 10; I <= Limit; I += 10) {
tgi_ellipse (X, Y, I, tgi_imulround (I, AspectRatio));
}
}
while (kbhit ()) {
cgetc ();
}
cgetc ();
}
static void DoCheckerboard (void)
{
unsigned X, Y;
unsigned char Color = COLOR_BACK;
tgi_clear ();
while (1) {
for (Y = 0; Y <= MaxY; Y += 10) {
for (X = 0; X <= MaxX; X += 10) {
Color = (Color == COLOR_FORE) ? COLOR_BACK : COLOR_FORE;
tgi_setcolor (Color);
tgi_bar (X, Y, X+9, Y+9);
if (kbhit ()) {
cgetc ();
return;
}
}
Color = Color == COLOR_FORE ? COLOR_BACK : COLOR_FORE;
}
Color = Color == COLOR_FORE ? COLOR_BACK : COLOR_FORE;
}
}
static void DoDiagram (void)
{
int XOrigin, YOrigin;
int Amp;
int X, Y;
unsigned I;
tgi_setcolor (COLOR_FORE);
tgi_clear ();
/* Determine zero and amplitude */
YOrigin = MaxY / 2;
XOrigin = 10;
Amp = (MaxY - 19) / 2;
/* Y axis */
tgi_line (XOrigin, 10, XOrigin, MaxY-10);
tgi_line (XOrigin-2, 12, XOrigin, 10);
tgi_lineto (XOrigin+2, 12);
/* X axis */
tgi_line (XOrigin, YOrigin, MaxX-10, YOrigin);
tgi_line (MaxX-12, YOrigin-2, MaxX-10, YOrigin);
tgi_lineto (MaxX-12, YOrigin+2);
/* Sine */
tgi_gotoxy (XOrigin, YOrigin);
for (I = 0; I <= 360; I += 5) {
/* Calculate the next points */
X = (int) (((long) (MaxX - 19) * I) / 360);
Y = (int) (((long) Amp * -_sin (I)) / 256);
/* Draw the line */
tgi_lineto (XOrigin + X, YOrigin + Y);
}
while (kbhit ()) {
cgetc ();
}
cgetc ();
}
static void DoLines (void)
{
unsigned X;
const unsigned Min = (MaxX < MaxY) ? MaxX : MaxY;
tgi_setcolor (COLOR_FORE);
tgi_clear ();
for (X = 0; X <= Min; X += 10) {
tgi_line (0, 0, Min, X);
tgi_line (0, 0, X, Min);
tgi_line (Min, Min, 0, Min-X);
tgi_line (Min, Min, Min-X, 0);
}
while (kbhit ()) {
cgetc ();
}
cgetc ();
}
#define DRIVER_COUNT 11
const void* drivers[DRIVER_COUNT] = {
atr3_tgi,
atr4_tgi,
atr5_tgi,
atr6_tgi,
atr7_tgi,
atr8_tgi,
atr9_tgi,
atr10_tgi,
atr11_tgi,
atr14_tgi,
atr15_tgi,
};
int main (void)
{
unsigned char Border;
unsigned int driverIndex;
/* Set the palette, set the border color */
Border = bordercolor (COLOR_BLACK);
for (driverIndex = 0; driverIndex < DRIVER_COUNT; driverIndex++) {
/* Install the driver */
tgi_install (drivers[driverIndex]);
CheckError ("tgi_install");
// __RESERVED_MEMORY__ can't override on atari-cart.cfg
//OS.appmhi = (unsigned char*)0x2000;
tgi_init ();
CheckError ("tgi_init");
/* Get stuff from the driver */
MaxX = tgi_getmaxx ();
MaxY = tgi_getmaxy ();
AspectRatio = tgi_getaspectratio ();
/* Do graphics stuff */
/* use default palette */
DoCircles ();
DoPalette (0);
DoCheckerboard ();
DoPalette (1);
DoDiagram ();
DoPalette (2);
DoLines ();
/* Uninstall the driver */
tgi_uninstall ();
}
/* Reset the border */
(void) bordercolor (Border);
return EXIT_SUCCESS;
}
+1
View File
@@ -9,6 +9,7 @@ var Atari8_PRESETS : Preset[] = [
{id:'hellopm.dasm', name:'Hello Sprites'},
{id:'helloconio.c', name:'Text Mode', category:'C'},
{id:'siegegame.c', name:'Siege Game'},
{id:'tgidemo.c', name:'TGI Demo'},
{id:'hellodlist.c', name:'Display List'},
{id:'libdemo.c', name:'Library Demo'},
];