From aef3898aa7290eb62504bb657d83a05b866546b0 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sat, 19 Sep 2026 18:45:25 +0200 Subject: [PATCH] atari8: renamed to a8lib* --- presets/atari8-5200/common.c | 1 - presets/atari8-5200/common.h | 1 - presets/atari8-5200/common_irq.s | 1 - presets/atari8-5200/libdemo.c | 1 - presets/atari8-5200/pokeymusic.ca65 | 1 - presets/atari8-800/a8lib.c | 163 ++++++ presets/atari8-800/{common.h => a8lib.h} | 59 +- presets/atari8-800/a8lib_dli.c | 58 ++ .../{common_irq.s => a8lib_dli_asm.ca65} | 26 +- presets/atari8-800/a8lib_pmg.c | 155 +++++ presets/atari8-800/a8lib_pokey.c | 67 +++ presets/atari8-800/a8lib_scroll.c | 70 +++ presets/atari8-800/common.c | 546 ------------------ presets/atari8-800/libdemo.c | 10 +- presets/atari8-800/libdemo.cpp | 57 +- presets/atari8-800/scrolldemo.c | 8 +- src/platform/atari8.ts | 4 +- 17 files changed, 628 insertions(+), 600 deletions(-) delete mode 120000 presets/atari8-5200/common.c delete mode 120000 presets/atari8-5200/common.h delete mode 120000 presets/atari8-5200/common_irq.s delete mode 120000 presets/atari8-5200/libdemo.c delete mode 120000 presets/atari8-5200/pokeymusic.ca65 create mode 100644 presets/atari8-800/a8lib.c rename presets/atari8-800/{common.h => a8lib.h} (89%) create mode 100644 presets/atari8-800/a8lib_dli.c rename presets/atari8-800/{common_irq.s => a8lib_dli_asm.ca65} (86%) create mode 100644 presets/atari8-800/a8lib_pmg.c create mode 100644 presets/atari8-800/a8lib_pokey.c create mode 100644 presets/atari8-800/a8lib_scroll.c delete mode 100644 presets/atari8-800/common.c diff --git a/presets/atari8-5200/common.c b/presets/atari8-5200/common.c deleted file mode 120000 index 49fe8867..00000000 --- a/presets/atari8-5200/common.c +++ /dev/null @@ -1 +0,0 @@ -../atari8-800/common.c \ No newline at end of file diff --git a/presets/atari8-5200/common.h b/presets/atari8-5200/common.h deleted file mode 120000 index c0980dac..00000000 --- a/presets/atari8-5200/common.h +++ /dev/null @@ -1 +0,0 @@ -../atari8-800/common.h \ No newline at end of file diff --git a/presets/atari8-5200/common_irq.s b/presets/atari8-5200/common_irq.s deleted file mode 120000 index c7ba99a9..00000000 --- a/presets/atari8-5200/common_irq.s +++ /dev/null @@ -1 +0,0 @@ -../atari8-800/common_irq.s \ No newline at end of file diff --git a/presets/atari8-5200/libdemo.c b/presets/atari8-5200/libdemo.c deleted file mode 120000 index 39cbef78..00000000 --- a/presets/atari8-5200/libdemo.c +++ /dev/null @@ -1 +0,0 @@ -../atari8-800/libdemo.c \ No newline at end of file diff --git a/presets/atari8-5200/pokeymusic.ca65 b/presets/atari8-5200/pokeymusic.ca65 deleted file mode 120000 index 22c4f6d7..00000000 --- a/presets/atari8-5200/pokeymusic.ca65 +++ /dev/null @@ -1 +0,0 @@ -../atari8-800/pokeymusic.ca65 \ No newline at end of file diff --git a/presets/atari8-800/a8lib.c b/presets/atari8-800/a8lib.c new file mode 100644 index 00000000..a7efd9f0 --- /dev/null +++ b/presets/atari8-800/a8lib.c @@ -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) ; +} diff --git a/presets/atari8-800/common.h b/presets/atari8-800/a8lib.h similarity index 89% rename from presets/atari8-800/common.h rename to presets/atari8-800/a8lib.h index 5d0c8c78..f156dd4d 100644 --- a/presets/atari8-800/common.h +++ b/presets/atari8-800/a8lib.h @@ -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 diff --git a/presets/atari8-800/a8lib_dli.c b/presets/atari8-800/a8lib_dli.c new file mode 100644 index 00000000..a883d64b --- /dev/null +++ b/presets/atari8-800/a8lib_dli.c @@ -0,0 +1,58 @@ + +#include +#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 + diff --git a/presets/atari8-800/common_irq.s b/presets/atari8-800/a8lib_dli_asm.ca65 similarity index 86% rename from presets/atari8-800/common_irq.s rename to presets/atari8-800/a8lib_dli_asm.ca65 index 80e67587..81433f79 100644 --- a/presets/atari8-800/common_irq.s +++ b/presets/atari8-800/a8lib_dli_asm.ca65 @@ -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 diff --git a/presets/atari8-800/a8lib_pmg.c b/presets/atari8-800/a8lib_pmg.c new file mode 100644 index 00000000..502b3ac4 --- /dev/null +++ b/presets/atari8-800/a8lib_pmg.c @@ -0,0 +1,155 @@ + +#include +#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)); + } +} + + diff --git a/presets/atari8-800/a8lib_pokey.c b/presets/atari8-800/a8lib_pokey.c new file mode 100644 index 00000000..3ba8e498 --- /dev/null +++ b/presets/atari8-800/a8lib_pokey.c @@ -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 + diff --git a/presets/atari8-800/a8lib_scroll.c b/presets/atari8-800/a8lib_scroll.c new file mode 100644 index 00000000..4e0074f2 --- /dev/null +++ b/presets/atari8-800/a8lib_scroll.c @@ -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; +} + + diff --git a/presets/atari8-800/common.c b/presets/atari8-800/common.c deleted file mode 100644 index 79dfbe41..00000000 --- a/presets/atari8-800/common.c +++ /dev/null @@ -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 -#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) ; -} diff --git a/presets/atari8-800/libdemo.c b/presets/atari8-800/libdemo.c index c48b7b86..f92818bd 100644 --- a/presets/atari8-800/libdemo.c +++ b/presets/atari8-800/libdemo.c @@ -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. */ diff --git a/presets/atari8-800/libdemo.cpp b/presets/atari8-800/libdemo.cpp index deefdbb5..eb710837 100644 --- a/presets/atari8-800/libdemo.cpp +++ b/presets/atari8-800/libdemo.cpp @@ -5,8 +5,11 @@ #include #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; -} +} \ No newline at end of file diff --git a/presets/atari8-800/scrolldemo.c b/presets/atari8-800/scrolldemo.c index 2ca7170e..e490e8a7 100644 --- a/presets/atari8-800/scrolldemo.c +++ b/presets/atari8-800/scrolldemo.c @@ -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 -#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); diff --git a/src/platform/atari8.ts b/src/platform/atari8.ts index 7ec01d53..37a6dda9 100644 --- a/src/platform/atari8.ts +++ b/src/platform/atari8.ts @@ -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'},