mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2026-10-25 00:02:16 +00:00
atari8: renamed to a8lib*
This commit is contained in:
1 parent
087759549d
commit
aef3898aa7
17 files changed
+628
-600
No files matched your search
@@ -1 +0,0 @@
|
||||
../atari8-800/common.c
|
||||
@@ -1 +0,0 @@
|
||||
../atari8-800/common.h
|
||||
@@ -1 +0,0 @@
|
||||
../atari8-800/common_irq.s
|
||||
@@ -1 +0,0 @@
|
||||
../atari8-800/libdemo.c
|
||||
@@ -1 +0,0 @@
|
||||
../atari8-800/pokeymusic.ca65
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* a8lib.c - helper library for cc65 Atari 8-bit programming.
|
||||
* See a8lib.h for the interface and libdemo.c for an example.
|
||||
*/
|
||||
|
||||
#include "a8lib.h"
|
||||
|
||||
/*==========================================================================*/
|
||||
/* Display list builder */
|
||||
/*==========================================================================*/
|
||||
|
||||
/* Make sure this array doesn't cross a 1K boundary */
|
||||
#ifdef __CC65__
|
||||
/* we really should use a custom .cfg file with the align attribute */
|
||||
/* but let's just put it here and maybe no one will notice */
|
||||
#if defined(__ATARI5200__)
|
||||
byte* a8_dlist = (byte*)0x1700;
|
||||
#else
|
||||
byte* a8_dlist = (byte*)0x9700;
|
||||
#endif
|
||||
#else
|
||||
byte a8_dlist[256];
|
||||
#pragma align(a8_dlist, 0x100)
|
||||
#endif
|
||||
|
||||
byte a8_dlist_len;
|
||||
byte a8_dli_count;
|
||||
byte a8_dli_lines = 1; /* DLI bits in the installed list */
|
||||
byte a8_dmactl_val = 0x22; /* playfield normal + DMA fetch */
|
||||
|
||||
void a8_set_dmactl(byte v) {
|
||||
a8_dmactl_val = v;
|
||||
A8_SDMCTL = v;
|
||||
ANTIC.dmactl = v;
|
||||
}
|
||||
|
||||
void a8_dlist_reset(void) {
|
||||
a8_dlist_len = 0;
|
||||
a8_dli_count = 0;
|
||||
a8_dli_lines = 1;
|
||||
// TODO: a8_dli_clear();
|
||||
// TODO: a8_scroll_reset();
|
||||
}
|
||||
|
||||
void a8_dlist_byte(byte b) {
|
||||
a8_dlist[a8_dlist_len++] = b;
|
||||
}
|
||||
|
||||
void a8_dlist_blank(byte n) {
|
||||
if (n < 1) n = 1;
|
||||
while (n > 8) {
|
||||
a8_dlist_byte(0x70); /* DL_BLK8 */
|
||||
n -= 8;
|
||||
}
|
||||
a8_dlist_byte((byte)((n - 1) << 4));
|
||||
}
|
||||
|
||||
byte a8_dlist_line(byte mode, const void* data, byte dli) {
|
||||
byte idx = 0xff;
|
||||
if (dli) {
|
||||
mode = DL_DLI(mode);
|
||||
idx = a8_dli_count;
|
||||
if (a8_dli_count < 0xff) a8_dli_count++;
|
||||
}
|
||||
if (data) mode = DL_LMS(mode);
|
||||
a8_dlist_byte(mode);
|
||||
if (data) {
|
||||
word a = (word)data;
|
||||
a8_dlist_byte((byte)(a & 0xff));
|
||||
a8_dlist_byte((byte)(a >> 8));
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
void a8_dlist_finish(void) {
|
||||
word a = (word)a8_dlist;
|
||||
a8_dlist_byte(DL_JVB);
|
||||
a8_dlist_byte((byte)(a & 0xff));
|
||||
a8_dlist_byte((byte)(a >> 8));
|
||||
/* One DLI fires per frame for each flagged line, so wrapping the
|
||||
dispatch index at this count keeps it in phase without a VBI hook. */
|
||||
a8_dli_lines = a8_dli_count ? a8_dli_count : 1;
|
||||
}
|
||||
|
||||
void a8_dlist_install(void) {
|
||||
word a = (word)a8_dlist;
|
||||
#if defined(__ATARI5200__)
|
||||
*(byte*)A8_SDLSTL_ADDR = (byte)(a & 0xff);
|
||||
*(byte*)A8_SDLSTH_ADDR = (byte)(a >> 8);
|
||||
#else
|
||||
#ifdef __CC65__
|
||||
OS.sdlstl = (byte)(a & 0xff);
|
||||
OS.sdlsth = (byte)(a >> 8);
|
||||
#else
|
||||
OS._sdl._st.stl = (byte)(a & 0xff);
|
||||
OS._sdl._st.sth = (byte)(a >> 8);
|
||||
#endif
|
||||
#endif
|
||||
ANTIC.dlistl = (byte)(a & 0xff);
|
||||
ANTIC.dlisth = (byte)(a >> 8);
|
||||
a8_set_dmactl(0x22);
|
||||
a8_waitvsync();
|
||||
}
|
||||
|
||||
void a8_dlist_off(void) {
|
||||
a8_set_dmactl(0x00);
|
||||
}
|
||||
|
||||
void a8_set_playfield_width(byte w) {
|
||||
a8_set_dmactl((byte)((a8_dmactl_val & ~0x03) | (w & 0x03)));
|
||||
}
|
||||
|
||||
|
||||
/*==========================================================================*/
|
||||
/* Colors */
|
||||
/*==========================================================================*/
|
||||
|
||||
void a8_set_colpf(byte i, byte c) {
|
||||
if (i > 3) i = 3;
|
||||
((byte*)>IA_WRITE.colpf0)[i] = c;
|
||||
A8_SHDW_PF[i] = c;
|
||||
}
|
||||
|
||||
void a8_set_colbk(byte c) {
|
||||
GTIA_WRITE.colbk = c;
|
||||
A8_SHDW_BK = c;
|
||||
}
|
||||
|
||||
void a8_set_colpm(byte i, byte c) {
|
||||
if (i > 3) i = 3;
|
||||
((byte*)>IA_WRITE.colpm0)[i] = c;
|
||||
A8_SHDW_PM[i] = c;
|
||||
}
|
||||
|
||||
|
||||
/*==========================================================================*/
|
||||
/* Input and utility */
|
||||
/*==========================================================================*/
|
||||
|
||||
byte a8_get_console(void) {
|
||||
return GTIA_READ.consol;
|
||||
}
|
||||
|
||||
byte a8_get_trigger(byte n) {
|
||||
return (byte)(((byte*)>IA_READ.trig0)[n] & 1 ? 0 : 1);
|
||||
}
|
||||
|
||||
#if !defined(__ATARI5200__)
|
||||
byte a8_get_stick(byte n) {
|
||||
byte v = n ? PIA.portb : PIA.porta;
|
||||
return (byte)((~v) & 0x1f);
|
||||
}
|
||||
#endif
|
||||
|
||||
void a8_waitvsync(void) {
|
||||
#if defined(__ATARI5200__)
|
||||
volatile byte* rtc = (volatile byte*)0x0002; /* RTCLOK+1 */
|
||||
#else
|
||||
volatile byte* rtc = &OS.rtclok[2]; /* frame counter */
|
||||
#endif
|
||||
byte t = *rtc;
|
||||
while (*rtc == t) ;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef _ATARI8_COMMON_H
|
||||
#define _ATARI8_COMMON_H
|
||||
#ifndef _ATARI8_A8LIB_H
|
||||
#define _ATARI8_A8LIB_H
|
||||
|
||||
/*
|
||||
* common.h - small helper library for cc65 Atari 8-bit programming.
|
||||
@@ -46,6 +46,36 @@ typedef unsigned int word;
|
||||
#define A8_AUDC_POLYS_NONE 0xA0 /* pure square wave */
|
||||
#define A8_AUDC_POLYS_4 0xC0 /* rumble */
|
||||
|
||||
/* Target-specific display-list / NMI vector locations. The 5200 BIOS
|
||||
keeps its page-2 vectors in a more compact layout than the 800 OS. */
|
||||
#if defined(__ATARI5200__)
|
||||
#define A8_VDSLST_ADDR 0x0206
|
||||
#define A8_SDLSTL_ADDR 0x0005
|
||||
#define A8_SDLSTH_ADDR 0x0006
|
||||
#define A8_SDMCTL_ADDR 0x0007
|
||||
#define A8_CHBASE_VAL 0xf8
|
||||
#else
|
||||
#define A8_VDSLST_ADDR 0x0200
|
||||
#define A8_CHBASE_VAL 0xe0
|
||||
#endif
|
||||
|
||||
#define A8_VDSLST (*(void(**)(void))A8_VDSLST_ADDR)
|
||||
|
||||
/* OS shadow registers. The 800's vertical-blank routine copies these to
|
||||
the hardware every frame, so anything we change mid-frame has to be
|
||||
written here too or it gets stomped. The 5200's BIOS uses the same
|
||||
zero-page slots (cc65's 5200 conio uses them as well). */
|
||||
#if defined(__ATARI5200__)
|
||||
#define A8_SHDW_PM ((byte*)0x0008)
|
||||
#define A8_SHDW_PF ((byte*)0x000c)
|
||||
#define A8_SHDW_BK (*(byte*)0x0010)
|
||||
#define A8_SDMCTL (*(byte*)0x0007)
|
||||
#else
|
||||
#define A8_SHDW_PM (&OS.pcolr0)
|
||||
#define A8_SHDW_PF (&OS.color0)
|
||||
#define A8_SHDW_BK (OS.color4)
|
||||
#define A8_SDMCTL (OS.sdmctl)
|
||||
#endif
|
||||
|
||||
/*==========================================================================*/
|
||||
/* Display list builder (ANTIC) */
|
||||
@@ -56,7 +86,9 @@ typedef unsigned int word;
|
||||
extern byte a8_dlist_len; /* current length in bytes */
|
||||
extern byte a8_dli_count; /* number of DLI bits emitted so far */
|
||||
|
||||
/* reset: call a8_dli_clear() and a8_scroll_reset() first, if linked */
|
||||
void a8_dlist_reset(void);
|
||||
/* add a byte to the dlist */
|
||||
void a8_dlist_byte(byte b);
|
||||
/* Emit n blank scanlines (any count; splits into DL_BLK8 chunks). */
|
||||
void a8_dlist_blank(byte n);
|
||||
@@ -83,20 +115,25 @@ void a8_set_playfield_width(byte w);
|
||||
/* Registers that a8_dli_set() understands. Anything else can be written
|
||||
from a user callback with a8_dli_hook. */
|
||||
enum {
|
||||
A8_REG_COLBK = 0,
|
||||
A8_REG_COLPF0, A8_REG_COLPF1, A8_REG_COLPF2, A8_REG_COLPF3,
|
||||
A8_REG_COLPM0, A8_REG_COLPM1, A8_REG_COLPM2, A8_REG_COLPM3,
|
||||
A8_REG_PRIOR,
|
||||
A8_REG_HPOSP0, A8_REG_HPOSP1, A8_REG_HPOSP2, A8_REG_HPOSP3,
|
||||
A8_REG_HPOSP0 = 0, A8_REG_HPOSP1, A8_REG_HPOSP2, A8_REG_HPOSP3,
|
||||
A8_REG_HPOSM0, A8_REG_HPOSM1, A8_REG_HPOSM2, A8_REG_HPOSM3,
|
||||
A8_REG_SIZEP0, A8_REG_SIZEP1, A8_REG_SIZEP2, A8_REG_SIZEP3,
|
||||
A8_REG_SIZEM,
|
||||
A8_REG_GRAFP0, A8_REG_GRAFP1, A8_REG_GRAFP2, A8_REG_GRAFP3,
|
||||
A8_REG_GRAFM,
|
||||
A8_REG_VDELAY, A8_REG_GRACTL, A8_REG_HITCLR,
|
||||
A8_REG_CHBASE, A8_REG_CHACTL,
|
||||
A8_REG_HSCROL, A8_REG_VSCROL, A8_REG_DMACTL,
|
||||
A8_REG_COUNT
|
||||
A8_REG_COLPM0, A8_REG_COLPM1, A8_REG_COLPM2, A8_REG_COLPM3,
|
||||
A8_REG_COLPF0, A8_REG_COLPF1, A8_REG_COLPF2, A8_REG_COLPF3,
|
||||
A8_REG_COLBK,
|
||||
A8_REG_PRIOR,
|
||||
A8_REG_VDELAY,
|
||||
A8_REG_GRACTL,
|
||||
A8_REG_HITCLR,
|
||||
/* If high bit set, ANTIC, otherwise GTIA. */
|
||||
A8_REG_DMACTL = 0x80,
|
||||
A8_REG_CHACTL = 0x81,
|
||||
A8_REG_HSCROL = 0x84,
|
||||
A8_REG_VSCROL = 0x85,
|
||||
A8_REG_CHBASE = 0x89,
|
||||
};
|
||||
|
||||
/* How many display-list DLIs we can service, and how many register
|
||||
@@ -0,0 +1,58 @@
|
||||
|
||||
#include <string.h>
|
||||
#include "a8lib.h"
|
||||
|
||||
/*==========================================================================*/
|
||||
/* DLI */
|
||||
/*==========================================================================*/
|
||||
|
||||
/* reg,val pairs terminated by A8_DLI_NONE in the reg slot */
|
||||
byte a8_dli_tab[A8_DLI_LINES][A8_DLI_WRITES * 2];
|
||||
byte a8_dli_line;
|
||||
void (*a8_dli_hook)(void);
|
||||
|
||||
void a8_dli_clear(void) {
|
||||
memset(a8_dli_tab, 0xff, sizeof(a8_dli_tab));
|
||||
a8_dli_line = 0;
|
||||
}
|
||||
|
||||
void a8_dli_set(byte line, byte reg, byte val) {
|
||||
byte j;
|
||||
byte* dli_line = a8_dli_tab[line];
|
||||
if (line >= A8_DLI_LINES) return;
|
||||
for (j = 0; j < A8_DLI_WRITES; j++) {
|
||||
if (*dli_line == 0xff) {
|
||||
dli_line[0] = reg;
|
||||
dli_line[1] = val;
|
||||
return;
|
||||
}
|
||||
dli_line += 2;
|
||||
}
|
||||
}
|
||||
|
||||
void a8_dli_install(void) {
|
||||
a8_dli_line = 0;
|
||||
A8_VDSLST = a8_dli_stub;
|
||||
ANTIC.nmien = NMIEN_VBI | NMIEN_DLI;
|
||||
}
|
||||
|
||||
void a8_dli_remove(void) {
|
||||
ANTIC.nmien = NMIEN_VBI;
|
||||
}
|
||||
|
||||
#ifdef __OSCAR64C__
|
||||
__hwinterrupt void a8_dli_stub() {
|
||||
if (a8_dli_line >= a8_dli_lines) a8_dli_line = 0;
|
||||
byte* w = &a8_dli_tab[a8_dli_line][0];
|
||||
for (byte i = 0; i < A8_DLI_WRITES; i++) {
|
||||
byte r = w[0];
|
||||
if (r == 0xff) break;
|
||||
void* base = r & 0x80 ? (void*)&ANTIC : (void*)>IA_WRITE;
|
||||
((byte*)base)[r & 0x1f] = w[1];
|
||||
w += 2;
|
||||
}
|
||||
music_duty();
|
||||
a8_dli_line++;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
;
|
||||
; common_irq.s - interrupt trampoline for presets/atari8-800/common.c
|
||||
;
|
||||
|
||||
; _a8_dli_stub
|
||||
; The Atari DLI is delivered as an NMI: the OS (or the 5200 BIOS) jumps
|
||||
; through the VDSLST vector, and the handler has to finish with RTI.
|
||||
; cc65 has no way to express that in C, so this tiny stub saves the C
|
||||
; runtime zero page, calls the C dispatcher, and returns with RTI.
|
||||
;
|
||||
; It also restores A/X/Y, so the interrupted program sees no side effects.
|
||||
;
|
||||
|
||||
.export _a8_dli_stub
|
||||
.import _a8_dli_dispatch
|
||||
@@ -61,12 +55,18 @@ _a8_dli_stub:
|
||||
lda _a8_dli_tab,y
|
||||
; if (r == 0xff) break;
|
||||
bmi @doneline
|
||||
; *a8_dli_regs[r] = w[1];
|
||||
asl
|
||||
tax
|
||||
lda _a8_dli_regs,x
|
||||
; lo byte
|
||||
pha
|
||||
and #$1f
|
||||
sta tmp
|
||||
lda _a8_dli_regs+1,x
|
||||
pla
|
||||
; hi byte calculated ($d4 or $d0)
|
||||
; TODO: Atari 5200
|
||||
asl
|
||||
lda #$d0
|
||||
bcc @gtia
|
||||
lda #$d4
|
||||
@gtia:
|
||||
sta tmp+1
|
||||
iny
|
||||
lda _a8_dli_tab,y
|
||||
@@ -0,0 +1,155 @@
|
||||
|
||||
#include <string.h>
|
||||
#include "a8lib.h"
|
||||
|
||||
extern byte a8_dmactl_val;
|
||||
extern void a8_set_dmactl(byte val);
|
||||
|
||||
/*==========================================================================*/
|
||||
/* Player/Missile graphics */
|
||||
/*==========================================================================*/
|
||||
|
||||
/* Need 2K for a single-line P/M area, plus up to 2K of alignment slack. */
|
||||
#ifdef __CC65__
|
||||
#if defined(__ATARI5200__)
|
||||
static byte* a8_pmg_base = (byte*)0x1800;
|
||||
#else
|
||||
static byte* a8_pmg_base = (byte*)0x9800;
|
||||
#endif
|
||||
#else
|
||||
byte a8_pmg_base[0x800];
|
||||
#pragma align(a8_pmg_base, 0x800)
|
||||
#endif
|
||||
static byte a8_pmg_mode;
|
||||
|
||||
byte* a8_pmg_player(byte i) {
|
||||
if (a8_pmg_mode == A8_PMG_SINGLE)
|
||||
return a8_pmg_base + 0x400 + ((word)i << 8);
|
||||
return a8_pmg_base + 0x200 + ((word)i << 7);
|
||||
}
|
||||
|
||||
byte* a8_pmg_missile(byte i) {
|
||||
(void)i; /* the four missiles share one 128-byte region */
|
||||
return a8_pmg_base + ((a8_pmg_mode == A8_PMG_SINGLE) ? 0x300 : 0x180);
|
||||
}
|
||||
|
||||
void a8_pmg_clear(void) {
|
||||
memset(a8_pmg_base, 0, 0x800);
|
||||
}
|
||||
|
||||
void a8_pmg_init(byte mode) {
|
||||
word i;
|
||||
a8_pmg_mode = mode;
|
||||
a8_pmg_clear();
|
||||
GTIA_WRITE.gractl = 0; /* disable while we set up */
|
||||
ANTIC.pmbase = (byte)((word)a8_pmg_base >> 8);
|
||||
a8_dmactl_val = (a8_dmactl_val & ~0x1c) | 0x0c | (mode & 0x10);
|
||||
a8_set_dmactl(a8_dmactl_val);
|
||||
GTIA_WRITE.gractl = GRACTL_PLAYERS | GRACTL_MISSLES;
|
||||
GTIA_WRITE.prior = PRIOR_P03_PF03;
|
||||
for (i = 0; i < 4; i++)
|
||||
a8_pmg_set_color(i, (byte)(0x10 + i * 0x20));
|
||||
}
|
||||
|
||||
void a8_pmg_off(void) {
|
||||
GTIA_WRITE.gractl = 0;
|
||||
a8_dmactl_val &= ~0x1c;
|
||||
a8_set_dmactl(a8_dmactl_val);
|
||||
}
|
||||
|
||||
void a8_pmg_set_x(byte i, byte x) {
|
||||
((byte*)>IA_WRITE.hposp0)[i] = x;
|
||||
}
|
||||
|
||||
void a8_pmg_set_color(byte i, byte c) {
|
||||
((byte*)>IA_WRITE.colpm0)[i] = c;
|
||||
A8_SHDW_PM[i] = c;
|
||||
}
|
||||
|
||||
void a8_pmg_set_size(byte i, byte size) {
|
||||
((byte*)>IA_WRITE.sizep0)[i] = size;
|
||||
}
|
||||
|
||||
void a8_pmg_set_shape(byte i, const byte* shape, byte len, byte y) {
|
||||
byte* p = a8_pmg_player(i) + y;
|
||||
while (len--) *p++ = *shape++;
|
||||
}
|
||||
|
||||
void a8_pmg_set_missile_x(byte i, byte x) {
|
||||
((byte*)>IA_WRITE.hposm0)[i] = x;
|
||||
}
|
||||
|
||||
void a8_pmg_set_missile_width(byte i, byte size) {
|
||||
GTIA_WRITE.sizem = (byte)((GTIA_WRITE.sizem & ~(3 << (i * 2)))
|
||||
| ((size & 3) << (i * 2)));
|
||||
}
|
||||
|
||||
byte a8_pmg_hit_pf(byte i) {
|
||||
return ((byte*)>IA_READ.p0pf)[i];
|
||||
}
|
||||
|
||||
byte a8_pmg_hit_pl(void) {
|
||||
byte i, m = 0;
|
||||
for (i = 0; i < 4; i++) m |= ((byte*)>IA_READ.p0pl)[i];
|
||||
return m;
|
||||
}
|
||||
|
||||
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*)>IA_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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
|
||||
#ifdef __CC65__
|
||||
#include <6502.h>
|
||||
#endif
|
||||
#include "a8lib.h"
|
||||
|
||||
/*==========================================================================*/
|
||||
/* POKEY */
|
||||
/*==========================================================================*/
|
||||
|
||||
static byte a8_irq_stack[128];
|
||||
|
||||
void a8_pokey_init(void) {
|
||||
POKEY_WRITE.skctl = SKCTL_KEYBOARD_DEBOUNCE | SKCTL_KEYBOARD_SCANNING;
|
||||
POKEY_WRITE.audctl = AUDCTL_CLOCKBASE_15HZ;
|
||||
a8_pokey_stop_all();
|
||||
}
|
||||
|
||||
void a8_pokey_sound(byte chan, byte freq, byte ctrl, byte vol) {
|
||||
byte* p = (byte*)&POKEY_WRITE.audf1 + (word)(chan << 1);
|
||||
p[0] = freq;
|
||||
p[1] = (byte)(ctrl | (vol & 0x0f));
|
||||
}
|
||||
|
||||
void a8_pokey_stop(byte chan) {
|
||||
((byte*)&POKEY_WRITE.audc1)[chan << 1] = 0;
|
||||
}
|
||||
|
||||
void a8_pokey_stop_all(void) {
|
||||
byte i;
|
||||
for (i = 0; i < 4; i++) a8_pokey_stop(i);
|
||||
}
|
||||
|
||||
#ifdef __CC65__
|
||||
/* called from set_irq() */
|
||||
unsigned char a8_pokey_music_update() {
|
||||
music_tick();
|
||||
music_duty();
|
||||
return IRQ_NOT_HANDLED;
|
||||
}
|
||||
|
||||
void a8_pokey_music_init(void) {
|
||||
set_irq(a8_pokey_music_update, a8_irq_stack, sizeof(a8_irq_stack));
|
||||
}
|
||||
|
||||
void a8_pokey_music_done(void) {
|
||||
reset_irq();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __OSCAR64C__
|
||||
void a8_pokey_music_update() {
|
||||
__asm { pha; txa; pha; tya; pha; } // save regs
|
||||
music_tick();
|
||||
music_duty();
|
||||
__asm { pla; tay; pla; tax; pla; } // restore regs
|
||||
}
|
||||
|
||||
void a8_pokey_music_init(void) {
|
||||
OS.vvblki = a8_pokey_music_update;
|
||||
}
|
||||
|
||||
void a8_pokey_music_done(void) {
|
||||
//reset_irq();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
|
||||
#include "a8lib.h"
|
||||
|
||||
/*==========================================================================*/
|
||||
/* Scrolling */
|
||||
/*==========================================================================*/
|
||||
|
||||
extern byte* a8_dlist;
|
||||
|
||||
static byte* a8_scroll_lmsp[A8_SCROLL_MAX];
|
||||
static const byte* a8_scroll_base[A8_SCROLL_MAX];
|
||||
static byte a8_scroll_count;
|
||||
byte a8_scroll_x_shift = 4;
|
||||
byte a8_scroll_x_mask = 15;
|
||||
byte a8_scroll_y_shift = 3;
|
||||
byte a8_scroll_y_mask = 7;
|
||||
|
||||
/* Indexed by ANTIC mode: scanlines per line, bytes per HSCROL line, and
|
||||
log2 of color clocks per byte. */
|
||||
const byte a8_mode_height[16] = { 0,0, 8,10, 8,16, 8,16, 8, 4, 4, 2, 1, 2, 1, 1 };
|
||||
static const byte a8_mode_stride[16] = { 0,0, 48,48,48,48,24,24,12,12,24,24,24,48,48,48 };
|
||||
static const byte a8_mode_xshift[16] = { 0,0, 2, 2, 2, 2, 3, 3, 4, 4, 3, 3, 3, 2, 2, 2 };
|
||||
|
||||
byte a8_scroll_mode(byte mode) {
|
||||
byte h;
|
||||
mode &= 15;
|
||||
a8_scroll_x_shift = a8_mode_xshift[mode];
|
||||
a8_scroll_x_mask = (byte)((1 << a8_scroll_x_shift) - 1);
|
||||
h = a8_mode_height[mode];
|
||||
a8_scroll_y_shift = 0;
|
||||
while (h > 1) { h >>= 1; a8_scroll_y_shift++; }
|
||||
a8_scroll_y_mask = (byte)((1 << a8_scroll_y_shift) - 1);
|
||||
return a8_mode_stride[mode];
|
||||
}
|
||||
|
||||
void a8_scroll_reset(void) {
|
||||
a8_scroll_count = 0;
|
||||
}
|
||||
|
||||
byte a8_scroll_line(byte mode, const void* base, byte dli) {
|
||||
byte idx = a8_dlist_line(mode, base, dli);
|
||||
a8_scroll_add(base, &a8_dlist[a8_dlist_len - 2]);
|
||||
return idx;
|
||||
}
|
||||
|
||||
void a8_scroll_add(const void* base, byte* lms) {
|
||||
if (a8_scroll_count >= A8_SCROLL_MAX) return;
|
||||
a8_scroll_lmsp[a8_scroll_count] = lms;
|
||||
a8_scroll_base[a8_scroll_count] = (const byte*)base;
|
||||
a8_scroll_count++;
|
||||
}
|
||||
|
||||
void a8_scroll_move(word byteoff, byte xfine) {
|
||||
byte i;
|
||||
for (i = 0; i < a8_scroll_count; i++) {
|
||||
word a = (word)a8_scroll_base[i] + byteoff;
|
||||
a8_scroll_lmsp[i][0] = (byte)a;
|
||||
a8_scroll_lmsp[i][1] = (byte)(a >> 8);
|
||||
}
|
||||
/* HSCROL delays the picture, so count down as the LMS counts up */
|
||||
ANTIC.hscrol = xfine ^ a8_scroll_x_mask;
|
||||
}
|
||||
|
||||
void a8_scroll_set(byte x, byte y, byte stride) {
|
||||
word off = (word)(y >> a8_scroll_y_shift) * stride + (x >> a8_scroll_x_shift);
|
||||
a8_scroll_move(off, (byte)(x & a8_scroll_x_mask));
|
||||
ANTIC.vscrol = y & a8_scroll_y_mask;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,546 +0,0 @@
|
||||
/*
|
||||
* common.c - helper library for cc65 Atari 8-bit programming.
|
||||
* See common.h for the interface and libdemo.c for an example.
|
||||
*
|
||||
* Link it with:
|
||||
* //#link "common.c"
|
||||
* //#link "common_irq.s"
|
||||
*/
|
||||
|
||||
#ifdef __CC65__
|
||||
#include <6502.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include "common.h"
|
||||
|
||||
/* Target-specific display-list / NMI vector locations. The 5200 BIOS
|
||||
keeps its page-2 vectors in a more compact layout than the 800 OS. */
|
||||
#if defined(__ATARI5200__)
|
||||
#define A8_VDSLST_ADDR 0x0206
|
||||
#define A8_SDLSTL_ADDR 0x0005
|
||||
#define A8_SDLSTH_ADDR 0x0006
|
||||
#define A8_SDMCTL_ADDR 0x0007
|
||||
#define A8_CHBASE_VAL 0xf8
|
||||
#else
|
||||
#define A8_VDSLST_ADDR 0x0200
|
||||
#define A8_CHBASE_VAL 0xe0
|
||||
#endif
|
||||
|
||||
#define A8_VDSLST (*(void(**)(void))A8_VDSLST_ADDR)
|
||||
|
||||
/* OS shadow registers. The 800's vertical-blank routine copies these to
|
||||
the hardware every frame, so anything we change mid-frame has to be
|
||||
written here too or it gets stomped. The 5200's BIOS uses the same
|
||||
zero-page slots (cc65's 5200 conio uses them as well). */
|
||||
#if defined(__ATARI5200__)
|
||||
#define A8_SHDW_PM ((byte*)0x0008)
|
||||
#define A8_SHDW_PF ((byte*)0x000c)
|
||||
#define A8_SHDW_BK (*(byte*)0x0010)
|
||||
#define A8_SDMCTL (*(byte*)0x0007)
|
||||
#else
|
||||
#define A8_SHDW_PM (&OS.pcolr0)
|
||||
#define A8_SHDW_PF (&OS.color0)
|
||||
#define A8_SHDW_BK (OS.color4)
|
||||
#define A8_SDMCTL (OS.sdmctl)
|
||||
#endif
|
||||
|
||||
|
||||
/*==========================================================================*/
|
||||
/* Display list builder */
|
||||
/*==========================================================================*/
|
||||
|
||||
/* Make sure this array doesn't cross a 1K boundary */
|
||||
#ifdef __CC65__
|
||||
/* we really should use a custom .cfg file with the align attribute */
|
||||
/* but let's just put it here and maybe no one will notice */
|
||||
#if defined(__ATARI5200__)
|
||||
static byte* a8_dlist = (byte*)0x1700;
|
||||
#else
|
||||
static byte* a8_dlist = (byte*)0x9700;
|
||||
#endif
|
||||
#else
|
||||
byte a8_dlist[256];
|
||||
#pragma align(a8_dlist, 0x100)
|
||||
#endif
|
||||
|
||||
byte a8_dlist_len;
|
||||
byte a8_dli_count;
|
||||
byte a8_dli_lines = 1; /* DLI bits in the installed list */
|
||||
byte a8_dmactl_val = 0x22; /* playfield normal + DMA fetch */
|
||||
|
||||
static void a8_set_dmactl(byte v) {
|
||||
a8_dmactl_val = v;
|
||||
A8_SDMCTL = v;
|
||||
ANTIC.dmactl = v;
|
||||
}
|
||||
|
||||
void a8_dlist_reset(void) {
|
||||
a8_dlist_len = 0;
|
||||
a8_dli_count = 0;
|
||||
a8_dli_lines = 1;
|
||||
a8_dli_clear();
|
||||
a8_scroll_reset();
|
||||
}
|
||||
|
||||
void a8_dlist_byte(byte b) {
|
||||
a8_dlist[a8_dlist_len++] = b;
|
||||
}
|
||||
|
||||
void a8_dlist_blank(byte n) {
|
||||
if (n < 1) n = 1;
|
||||
while (n > 8) {
|
||||
a8_dlist_byte(0x70); /* DL_BLK8 */
|
||||
n -= 8;
|
||||
}
|
||||
a8_dlist_byte((byte)((n - 1) << 4));
|
||||
}
|
||||
|
||||
byte a8_dlist_line(byte mode, const void* data, byte dli) {
|
||||
byte idx = 0xff;
|
||||
if (dli) {
|
||||
mode = DL_DLI(mode);
|
||||
idx = a8_dli_count;
|
||||
if (a8_dli_count < 0xff) a8_dli_count++;
|
||||
}
|
||||
if (data) mode = DL_LMS(mode);
|
||||
a8_dlist_byte(mode);
|
||||
if (data) {
|
||||
word a = (word)data;
|
||||
a8_dlist_byte((byte)(a & 0xff));
|
||||
a8_dlist_byte((byte)(a >> 8));
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
void a8_dlist_finish(void) {
|
||||
word a = (word)a8_dlist;
|
||||
a8_dlist_byte(DL_JVB);
|
||||
a8_dlist_byte((byte)(a & 0xff));
|
||||
a8_dlist_byte((byte)(a >> 8));
|
||||
/* One DLI fires per frame for each flagged line, so wrapping the
|
||||
dispatch index at this count keeps it in phase without a VBI hook. */
|
||||
a8_dli_lines = a8_dli_count ? a8_dli_count : 1;
|
||||
}
|
||||
|
||||
void a8_dlist_install(void) {
|
||||
word a = (word)a8_dlist;
|
||||
#if defined(__ATARI5200__)
|
||||
*(byte*)A8_SDLSTL_ADDR = (byte)(a & 0xff);
|
||||
*(byte*)A8_SDLSTH_ADDR = (byte)(a >> 8);
|
||||
#else
|
||||
#ifdef __CC65__
|
||||
OS.sdlstl = (byte)(a & 0xff);
|
||||
OS.sdlsth = (byte)(a >> 8);
|
||||
#else
|
||||
OS._sdl._st.stl = (byte)(a & 0xff);
|
||||
OS._sdl._st.sth = (byte)(a >> 8);
|
||||
#endif
|
||||
#endif
|
||||
ANTIC.dlistl = (byte)(a & 0xff);
|
||||
ANTIC.dlisth = (byte)(a >> 8);
|
||||
a8_set_dmactl(0x22);
|
||||
a8_waitvsync();
|
||||
}
|
||||
|
||||
void a8_dlist_off(void) {
|
||||
a8_set_dmactl(0x00);
|
||||
}
|
||||
|
||||
void a8_set_playfield_width(byte w) {
|
||||
a8_set_dmactl((byte)((a8_dmactl_val & ~0x03) | (w & 0x03)));
|
||||
}
|
||||
|
||||
|
||||
/*==========================================================================*/
|
||||
/* DLI */
|
||||
/*==========================================================================*/
|
||||
|
||||
/* reg,val pairs terminated by A8_DLI_NONE in the reg slot */
|
||||
byte a8_dli_tab[A8_DLI_LINES][A8_DLI_WRITES * 2];
|
||||
byte a8_dli_line;
|
||||
void (*a8_dli_hook)(void);
|
||||
|
||||
volatile byte* const a8_dli_regs[A8_REG_COUNT] = {
|
||||
>IA_WRITE.colbk,
|
||||
>IA_WRITE.colpf0, >IA_WRITE.colpf1, >IA_WRITE.colpf2, >IA_WRITE.colpf3,
|
||||
>IA_WRITE.colpm0, >IA_WRITE.colpm1, >IA_WRITE.colpm2, >IA_WRITE.colpm3,
|
||||
>IA_WRITE.prior,
|
||||
>IA_WRITE.hposp0, >IA_WRITE.hposp1, >IA_WRITE.hposp2, >IA_WRITE.hposp3,
|
||||
>IA_WRITE.hposm0, >IA_WRITE.hposm1, >IA_WRITE.hposm2, >IA_WRITE.hposm3,
|
||||
>IA_WRITE.sizep0, >IA_WRITE.sizep1, >IA_WRITE.sizep2, >IA_WRITE.sizep3,
|
||||
>IA_WRITE.sizem,
|
||||
>IA_WRITE.grafp0, >IA_WRITE.grafp1, >IA_WRITE.grafp2, >IA_WRITE.grafp3,
|
||||
>IA_WRITE.grafm,
|
||||
>IA_WRITE.vdelay, >IA_WRITE.gractl, >IA_WRITE.hitclr,
|
||||
&ANTIC.chbase, &ANTIC.chactl,
|
||||
&ANTIC.hscrol, &ANTIC.vscrol, &ANTIC.dmactl
|
||||
};
|
||||
|
||||
void a8_dli_clear(void) {
|
||||
memset(a8_dli_tab, 0xff, sizeof(a8_dli_tab));
|
||||
a8_dli_line = 0;
|
||||
}
|
||||
|
||||
void a8_dli_set(byte line, byte reg, byte val) {
|
||||
byte j;
|
||||
byte* dli_line = a8_dli_tab[line];
|
||||
if (line >= A8_DLI_LINES || reg >= A8_REG_COUNT) return;
|
||||
for (j = 0; j < A8_DLI_WRITES; j++) {
|
||||
if (*dli_line == 0xff) {
|
||||
dli_line[0] = reg;
|
||||
dli_line[1] = val;
|
||||
return;
|
||||
}
|
||||
dli_line += 2;
|
||||
}
|
||||
}
|
||||
|
||||
void a8_dli_install(void) {
|
||||
a8_dli_line = 0;
|
||||
A8_VDSLST = a8_dli_stub;
|
||||
ANTIC.nmien = NMIEN_VBI | NMIEN_DLI;
|
||||
}
|
||||
|
||||
void a8_dli_remove(void) {
|
||||
ANTIC.nmien = NMIEN_VBI;
|
||||
}
|
||||
|
||||
#ifdef __OSCAR64C__
|
||||
__hwinterrupt void a8_dli_stub() {
|
||||
if (a8_dli_line >= a8_dli_lines) a8_dli_line = 0;
|
||||
byte* w = &a8_dli_tab[a8_dli_line][0];
|
||||
for (byte i = 0; i < A8_DLI_WRITES; i++) {
|
||||
byte r = w[0];
|
||||
if (r == 0xff) break;
|
||||
*a8_dli_regs[r] = w[1];
|
||||
w += 2;
|
||||
}
|
||||
music_duty();
|
||||
a8_dli_line++;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*==========================================================================*/
|
||||
/* Scrolling */
|
||||
/*==========================================================================*/
|
||||
|
||||
static byte* a8_scroll_lmsp[A8_SCROLL_MAX];
|
||||
static const byte* a8_scroll_base[A8_SCROLL_MAX];
|
||||
static byte a8_scroll_count;
|
||||
byte a8_scroll_x_shift = 4;
|
||||
byte a8_scroll_x_mask = 15;
|
||||
byte a8_scroll_y_shift = 3;
|
||||
byte a8_scroll_y_mask = 7;
|
||||
|
||||
/* Indexed by ANTIC mode: scanlines per line, bytes per HSCROL line, and
|
||||
log2 of color clocks per byte. */
|
||||
const byte a8_mode_height[16] = { 0,0, 8,10, 8,16, 8,16, 8, 4, 4, 2, 1, 2, 1, 1 };
|
||||
static const byte a8_mode_stride[16] = { 0,0, 48,48,48,48,24,24,12,12,24,24,24,48,48,48 };
|
||||
static const byte a8_mode_xshift[16] = { 0,0, 2, 2, 2, 2, 3, 3, 4, 4, 3, 3, 3, 2, 2, 2 };
|
||||
|
||||
byte a8_scroll_mode(byte mode) {
|
||||
byte h;
|
||||
mode &= 15;
|
||||
a8_scroll_x_shift = a8_mode_xshift[mode];
|
||||
a8_scroll_x_mask = (byte)((1 << a8_scroll_x_shift) - 1);
|
||||
h = a8_mode_height[mode];
|
||||
a8_scroll_y_shift = 0;
|
||||
while (h > 1) { h >>= 1; a8_scroll_y_shift++; }
|
||||
a8_scroll_y_mask = (byte)((1 << a8_scroll_y_shift) - 1);
|
||||
return a8_mode_stride[mode];
|
||||
}
|
||||
|
||||
void a8_scroll_reset(void) {
|
||||
a8_scroll_count = 0;
|
||||
}
|
||||
|
||||
byte a8_scroll_line(byte mode, const void* base, byte dli) {
|
||||
byte idx = a8_dlist_line(mode, base, dli);
|
||||
a8_scroll_add(base, &a8_dlist[a8_dlist_len - 2]);
|
||||
return idx;
|
||||
}
|
||||
|
||||
void a8_scroll_add(const void* base, byte* lms) {
|
||||
if (a8_scroll_count >= A8_SCROLL_MAX) return;
|
||||
a8_scroll_lmsp[a8_scroll_count] = lms;
|
||||
a8_scroll_base[a8_scroll_count] = (const byte*)base;
|
||||
a8_scroll_count++;
|
||||
}
|
||||
|
||||
void a8_scroll_move(word byteoff, byte xfine) {
|
||||
byte i;
|
||||
for (i = 0; i < a8_scroll_count; i++) {
|
||||
word a = (word)a8_scroll_base[i] + byteoff;
|
||||
a8_scroll_lmsp[i][0] = (byte)a;
|
||||
a8_scroll_lmsp[i][1] = (byte)(a >> 8);
|
||||
}
|
||||
/* HSCROL delays the picture, so count down as the LMS counts up */
|
||||
ANTIC.hscrol = xfine ^ a8_scroll_x_mask;
|
||||
}
|
||||
|
||||
void a8_scroll_set(byte x, byte y, byte stride) {
|
||||
word off = (word)(y >> a8_scroll_y_shift) * stride + (x >> a8_scroll_x_shift);
|
||||
a8_scroll_move(off, (byte)(x & a8_scroll_x_mask));
|
||||
ANTIC.vscrol = y & a8_scroll_y_mask;
|
||||
}
|
||||
|
||||
|
||||
/*==========================================================================*/
|
||||
/* Player/Missile graphics */
|
||||
/*==========================================================================*/
|
||||
|
||||
/* Need 2K for a single-line P/M area, plus up to 2K of alignment slack. */
|
||||
#ifdef __CC65__
|
||||
#if defined(__ATARI5200__)
|
||||
static byte* a8_pmg_base = (byte*)0x1800;
|
||||
#else
|
||||
static byte* a8_pmg_base = (byte*)0x9800;
|
||||
#endif
|
||||
#else
|
||||
byte a8_pmg_base[0x800];
|
||||
#pragma align(a8_pmg_base, 0x800)
|
||||
#endif
|
||||
static byte a8_pmg_mode;
|
||||
|
||||
byte* a8_pmg_player(byte i) {
|
||||
if (a8_pmg_mode == A8_PMG_SINGLE)
|
||||
return a8_pmg_base + 0x400 + ((word)i << 8);
|
||||
return a8_pmg_base + 0x200 + ((word)i << 7);
|
||||
}
|
||||
|
||||
byte* a8_pmg_missile(byte i) {
|
||||
(void)i; /* the four missiles share one 128-byte region */
|
||||
return a8_pmg_base + ((a8_pmg_mode == A8_PMG_SINGLE) ? 0x300 : 0x180);
|
||||
}
|
||||
|
||||
void a8_pmg_clear(void) {
|
||||
memset(a8_pmg_base, 0, 0x800);
|
||||
}
|
||||
|
||||
void a8_pmg_init(byte mode) {
|
||||
word i;
|
||||
a8_pmg_mode = mode;
|
||||
a8_pmg_clear();
|
||||
GTIA_WRITE.gractl = 0; /* disable while we set up */
|
||||
ANTIC.pmbase = (byte)((word)a8_pmg_base >> 8);
|
||||
a8_dmactl_val = (a8_dmactl_val & ~0x1c) | 0x0c | (mode & 0x10);
|
||||
a8_set_dmactl(a8_dmactl_val);
|
||||
GTIA_WRITE.gractl = GRACTL_PLAYERS | GRACTL_MISSLES;
|
||||
GTIA_WRITE.prior = PRIOR_P03_PF03;
|
||||
for (i = 0; i < 4; i++)
|
||||
a8_pmg_set_color(i, (byte)(0x10 + i * 0x20));
|
||||
}
|
||||
|
||||
void a8_pmg_off(void) {
|
||||
GTIA_WRITE.gractl = 0;
|
||||
a8_dmactl_val &= ~0x1c;
|
||||
a8_set_dmactl(a8_dmactl_val);
|
||||
}
|
||||
|
||||
void a8_pmg_set_x(byte i, byte x) {
|
||||
((byte*)>IA_WRITE.hposp0)[i] = x;
|
||||
}
|
||||
|
||||
void a8_pmg_set_color(byte i, byte c) {
|
||||
((byte*)>IA_WRITE.colpm0)[i] = c;
|
||||
A8_SHDW_PM[i] = c;
|
||||
}
|
||||
|
||||
void a8_pmg_set_size(byte i, byte size) {
|
||||
((byte*)>IA_WRITE.sizep0)[i] = size;
|
||||
}
|
||||
|
||||
void a8_pmg_set_shape(byte i, const byte* shape, byte len, byte y) {
|
||||
byte* p = a8_pmg_player(i) + y;
|
||||
while (len--) *p++ = *shape++;
|
||||
}
|
||||
|
||||
void a8_pmg_set_missile_x(byte i, byte x) {
|
||||
((byte*)>IA_WRITE.hposm0)[i] = x;
|
||||
}
|
||||
|
||||
void a8_pmg_set_missile_width(byte i, byte size) {
|
||||
GTIA_WRITE.sizem = (byte)((GTIA_WRITE.sizem & ~(3 << (i * 2)))
|
||||
| ((size & 3) << (i * 2)));
|
||||
}
|
||||
|
||||
byte a8_pmg_hit_pf(byte i) {
|
||||
return ((byte*)>IA_READ.p0pf)[i];
|
||||
}
|
||||
|
||||
byte a8_pmg_hit_pl(void) {
|
||||
byte i, m = 0;
|
||||
for (i = 0; i < 4; i++) m |= ((byte*)>IA_READ.p0pl)[i];
|
||||
return m;
|
||||
}
|
||||
|
||||
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*)>IA_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 */
|
||||
/*==========================================================================*/
|
||||
|
||||
static byte a8_irq_stack[128];
|
||||
|
||||
void a8_pokey_init(void) {
|
||||
POKEY_WRITE.skctl = SKCTL_KEYBOARD_DEBOUNCE | SKCTL_KEYBOARD_SCANNING;
|
||||
POKEY_WRITE.audctl = AUDCTL_CLOCKBASE_15HZ;
|
||||
a8_pokey_stop_all();
|
||||
}
|
||||
|
||||
void a8_pokey_sound(byte chan, byte freq, byte ctrl, byte vol) {
|
||||
byte* p = (byte*)&POKEY_WRITE.audf1 + (word)(chan << 1);
|
||||
p[0] = freq;
|
||||
p[1] = (byte)(ctrl | (vol & 0x0f));
|
||||
}
|
||||
|
||||
void a8_pokey_stop(byte chan) {
|
||||
((byte*)&POKEY_WRITE.audc1)[chan << 1] = 0;
|
||||
}
|
||||
|
||||
void a8_pokey_stop_all(void) {
|
||||
byte i;
|
||||
for (i = 0; i < 4; i++) a8_pokey_stop(i);
|
||||
}
|
||||
|
||||
#ifdef __CC65__
|
||||
/* called from set_irq() */
|
||||
unsigned char a8_pokey_music_update() {
|
||||
music_tick();
|
||||
music_duty();
|
||||
return IRQ_NOT_HANDLED;
|
||||
}
|
||||
|
||||
void a8_pokey_music_init(void) {
|
||||
set_irq(a8_pokey_music_update, a8_irq_stack, sizeof(a8_irq_stack));
|
||||
}
|
||||
|
||||
void a8_pokey_music_done(void) {
|
||||
reset_irq();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __OSCAR64C__
|
||||
void a8_pokey_music_update() {
|
||||
__asm { pha; txa; pha; tya; pha; } // save regs
|
||||
music_tick();
|
||||
music_duty();
|
||||
__asm { pla; tay; pla; tax; pla; } // restore regs
|
||||
}
|
||||
|
||||
void a8_pokey_music_init(void) {
|
||||
OS.vvblki = a8_pokey_music_update;
|
||||
}
|
||||
|
||||
void a8_pokey_music_done(void) {
|
||||
//reset_irq();
|
||||
}
|
||||
#endif
|
||||
|
||||
/*==========================================================================*/
|
||||
/* Colors */
|
||||
/*==========================================================================*/
|
||||
|
||||
void a8_set_colpf(byte i, byte c) {
|
||||
if (i > 3) i = 3;
|
||||
((byte*)>IA_WRITE.colpf0)[i] = c;
|
||||
A8_SHDW_PF[i] = c;
|
||||
}
|
||||
|
||||
void a8_set_colbk(byte c) {
|
||||
GTIA_WRITE.colbk = c;
|
||||
A8_SHDW_BK = c;
|
||||
}
|
||||
|
||||
void a8_set_colpm(byte i, byte c) {
|
||||
if (i > 3) i = 3;
|
||||
((byte*)>IA_WRITE.colpm0)[i] = c;
|
||||
A8_SHDW_PM[i] = c;
|
||||
}
|
||||
|
||||
|
||||
/*==========================================================================*/
|
||||
/* Input and utility */
|
||||
/*==========================================================================*/
|
||||
|
||||
byte a8_get_console(void) {
|
||||
return GTIA_READ.consol;
|
||||
}
|
||||
|
||||
byte a8_get_trigger(byte n) {
|
||||
return (byte)(((byte*)>IA_READ.trig0)[n] & 1 ? 0 : 1);
|
||||
}
|
||||
|
||||
#if !defined(__ATARI5200__)
|
||||
byte a8_get_stick(byte n) {
|
||||
byte v = n ? PIA.portb : PIA.porta;
|
||||
return (byte)((~v) & 0x1f);
|
||||
}
|
||||
#endif
|
||||
|
||||
void a8_waitvsync(void) {
|
||||
#if defined(__ATARI5200__)
|
||||
volatile byte* rtc = (volatile byte*)0x0002; /* RTCLOK+1 */
|
||||
#else
|
||||
volatile byte* rtc = &OS.rtclok[2]; /* frame counter */
|
||||
#endif
|
||||
byte t = *rtc;
|
||||
while (*rtc == t) ;
|
||||
}
|
||||
@@ -7,12 +7,15 @@
|
||||
* - a POKEY melody driven from the vblank interrupt
|
||||
*/
|
||||
|
||||
//#link "common.c"
|
||||
//#link "common_irq.s"
|
||||
//#link "a8lib.c"
|
||||
//#link "a8lib_dli.c"
|
||||
//#link "a8lib_pmg.c"
|
||||
//#link "a8lib_pokey.c"
|
||||
//#link "a8lib_dli_asm.ca65"
|
||||
//#link "pokeymusic.ca65"
|
||||
|
||||
#include <6502.h>
|
||||
#include "common.h"
|
||||
#include "a8lib.h"
|
||||
|
||||
/* 24 rows x 40 columns of text screen codes. */
|
||||
static byte screen[24][40];
|
||||
@@ -36,6 +39,7 @@ static void fill_screen(void) {
|
||||
|
||||
static void build_display(void) {
|
||||
byte i, d;
|
||||
a8_dli_clear();
|
||||
a8_dlist_reset();
|
||||
a8_dlist_blank(24);
|
||||
/* First text line carries the LMS pointer and the first DLI band. */
|
||||
|
||||
@@ -5,8 +5,11 @@
|
||||
#include <conio.h>
|
||||
|
||||
#include "atari.h"
|
||||
#include "common.h"
|
||||
//#link "common.c"
|
||||
#include "a8lib.h"
|
||||
//#link "a8lib.c"
|
||||
//#link "a8lib_dli.c"
|
||||
//#link "a8lib_pmg.c"
|
||||
//#link "a8lib_pokey.c"
|
||||
//#link "pokeymusic.cpp"
|
||||
|
||||
/* 24 rows x 40 columns of text screen codes. */
|
||||
@@ -33,6 +36,7 @@ static void fill_screen(void) {
|
||||
|
||||
static void build_display(void) {
|
||||
byte i, d;
|
||||
a8_dli_clear();
|
||||
a8_dlist_reset();
|
||||
a8_dlist_blank(24);
|
||||
/* First text line carries the LMS pointer and the first DLI band. */
|
||||
@@ -52,9 +56,10 @@ static void build_display(void) {
|
||||
a8_dlist_finish();
|
||||
}
|
||||
|
||||
int main() {
|
||||
int main(void) {
|
||||
static byte x = 128;
|
||||
static char dx = 2;
|
||||
static byte dx = 2;
|
||||
byte i;
|
||||
|
||||
fill_screen();
|
||||
build_display();
|
||||
@@ -70,29 +75,49 @@ int main() {
|
||||
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();
|
||||
a8_pokey_music_init();
|
||||
music_start(music1);
|
||||
|
||||
|
||||
while (1) {
|
||||
a8_waitvsync();
|
||||
|
||||
x += dx;
|
||||
if (x < 48) { x = 48; dx = 2; }
|
||||
if (x > 208) { x = 208; dx = -2; }
|
||||
a8_pmg_set_x(0, x);
|
||||
|
||||
if (a8_trigger(0)) {
|
||||
a8_pmg_set_size(0, A8_PMG_QUADW);
|
||||
if (x > 208) { x = 208; dx = (byte)-2; }
|
||||
// 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);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,11 @@
|
||||
* PAD scanlines of overlap below, so it can scroll without leaving the page.
|
||||
*/
|
||||
|
||||
//#link "common.c"
|
||||
//#link "common_irq.s"
|
||||
//#link "pokeymusic.ca65"
|
||||
//#link "a8lib.c"
|
||||
//#link "a8lib_scroll.c"
|
||||
|
||||
#include <string.h>
|
||||
#include "common.h"
|
||||
#include "a8lib.h"
|
||||
|
||||
#define MODE DL_MAP160x2x4 /* try DL_CHR40x8x4, DL_MAP320x1x1, ... */
|
||||
#define SCANLINES 192 /* visible scanlines */
|
||||
@@ -78,6 +77,7 @@ static void build_display(void) {
|
||||
/* too many bands? shrink the window */
|
||||
if (lines > band_lines * MAX_BANDS) lines = band_lines * MAX_BANDS;
|
||||
|
||||
a8_scroll_reset();
|
||||
a8_dlist_reset();
|
||||
top = 24 + (SCANLINES - lines * h) / 2;
|
||||
a8_dlist_blank(top);
|
||||
|
||||
@@ -9,13 +9,13 @@ 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'},
|
||||
];
|
||||
|
||||
var Atari800_PRESETS = Atari8_PRESETS.concat([
|
||||
//{id:'testmusic.c', name:'POKEY Music'},
|
||||
{id:'tgidemo.c', name:'TGI Demo'},
|
||||
{id:'libdemo.c', name:'Library Demo'},
|
||||
{id:'scrolldemo.c', name:'Scrolling'},
|
||||
{id:'hello.cpp', name:'Hello Oscar64', category:'Oscar64'},
|
||||
{id:'libdemo.cpp', name:'Library Demo Oscar64'},
|
||||
|
||||
Reference in new issue
Block a user