mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2026-10-25 00:02:16 +00:00
cc65: upgraded to V2.19-3867
apple2: added hires tgi driver, patches lores tgi driver in cc65
This commit is contained in:
1 parent
410b063c15
commit
5c813c2526
32 files changed
+1007
-35
No files matched your search
+7
-1
@@ -9,6 +9,7 @@ See [extension/CHANGELOG.md](extension/CHANGELOG.md).
|
||||
### Debugging and the `8bws` CLI
|
||||
|
||||
- New `8bws` command-line tool (`npm run cli`) to build and run programs headlessly: `build`, `run`, `verify-replay`, `profile`, `detect`, `list-platforms`, `list-tools`; scripted emulator control; `--png`, `--json`, `--symbols`, `--memdump`, `--info`; platform and main file detected from a project directory.
|
||||
- Platform detection no longer confuses the Atari 8-bit targets: `atari.h` means the 800 (it is guarded by `__ATARI__`), `atari5200.h` means the 5200, and a folder named for a platform breaks a near-tie.
|
||||
- Emulators now run on a timeline: step back, seek, reverse run, `rbreak`, and deterministic replay with a replay-verification test that found determinism bugs in several platforms.
|
||||
- Step Over for C and assembly sources, a Breakpoints pane, and more breakpoint conditions.
|
||||
- Call Stack view renamed Call Graph, with clocks and more detail per line.
|
||||
@@ -16,6 +17,7 @@ See [extension/CHANGELOG.md](extension/CHANGELOG.md).
|
||||
- Click the hex offset in the gutter to run to that line.
|
||||
- Waveform viewer for Verilog, with help and shortcuts.
|
||||
- Faster line lookups in listings.
|
||||
- Atari 8-bit: the XEX loader now calls each INIT vector before loading the next chunk (cc65's system-check chunk overlaps the main program), fixing a crash on startup of `tgidemo.c`.
|
||||
|
||||
### Editor and UI
|
||||
|
||||
@@ -41,7 +43,7 @@ See [extension/CHANGELOG.md](extension/CHANGELOG.md).
|
||||
- **Game Boy**: native emulator with GBC double speed, RTC and banking; C via a partial gbdk-2020 header set; presets ported from GBDK, plus Chase and Paku Paku; ghosting on the LCD.
|
||||
- **Atari 8-bit**: `.atr` disk images, 5th player, ANTIC and POKEY fixes, a8lib common library, scrolling and libdemo examples, oscar64 support.
|
||||
- **Atari 2600**: a Javatari-derived `vcs.jt4` machine that also runs headlessly; updated `vcs.h`/`macro.h`.
|
||||
- **Apple II**: paddle/joystick and SW0-2 support, dynamic origin, DOS 3.3 and AppleSingle samples.
|
||||
- **Apple II**: paddle/joystick and SW0-2 support, dynamic origin, DOS 3.3 and AppleSingle samples. New self-contained hi-res TGI driver (`a2hires.s`, 280x192, 8 colors, no ROM calls) used by the TGI demos.
|
||||
- **NES**: updated jsnes and a fix for a write bug.
|
||||
- **SMS/Game Gear/ColecoVision**: shared code, SMS Solarian port, more presets.
|
||||
- **Arcade**: Pac-Man hardware, Williams, Galaxian and Astrocade accuracy updates; early Exidy, MCR and Channel F work.
|
||||
@@ -57,6 +59,10 @@ See [extension/CHANGELOG.md](extension/CHANGELOG.md).
|
||||
- New build directives replace the legacy CFGFILE-style ones; build parameters no longer leak between builds.
|
||||
- `#pragma compile("...")` and oscar64 symbol/listing parsing.
|
||||
- Tool metadata centralized in `src/common/toolmeta.ts`; `npm run toolversions` checks tool versions.
|
||||
- cc65/ca65/ld65 warnings no longer fail the build; only errors are reported (the WASI tools print non-fatal `Warning:` lines such as duplicate `const` qualifiers).
|
||||
- cc65 toolchain (cc65/ca65/ld65 WASI builds and the per-platform include/lib zips) updated from the 2021 build (`6ac4aa4`) to cc65 `d8a486a` (V2.19-3867, 2026-09-26). The compiler runs with `--disable-opt OptLoadStore1` (the new step drops stores after loads of the same address, which breaks hardware registers and inline asm) and `-unreachable-code`.
|
||||
- cc65 `a2.lo.s` (Apple II lo-res TGI) patched in `8bitworkshop-compilers/patches/cc65-apple2-lo-tgi.patch`: a stray `bpl :+` in `INSTALL` made `tgi_install(a2_lo_tgi)` crash on a non-enhanced Apple II.
|
||||
- Presets adjusted for the newer cc65: NES `chase` closes its zero-page `#pragma` region, C64 `side_scroller` uses an unsigned fixed-point constant, and the Atari vector presets no longer recurse into `main()`. `neslib.h` no longer redefines `NULL` if `stddef.h` already did.
|
||||
- Build is now only `esbuild` through `scripts/build.mjs`; code split into a headless `src/common/projectcore.ts` / `toolselect.ts`.
|
||||
- New `buildpresets` script that builds and runs every preset.
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
/* Self-contained hi-res TGI driver (280x192, 8 colors); see a2hires.s */
|
||||
extern unsigned char a2hires_tgi[];
|
||||
@@ -0,0 +1,499 @@
|
||||
;
|
||||
; Self-contained TGI driver for the Apple ][ hi-res screen (280x192, 8 colors)
|
||||
;
|
||||
; The stock cc65 hi-res driver calls Applesoft ROM routines, which the
|
||||
; emulator's ROM doesn't have. This one draws everything itself.
|
||||
; Usage: //#link "a2hires.s" then tgi_install(a2hires_tgi);
|
||||
; (no text output, no palette changes)
|
||||
;
|
||||
; The picture lives on hi-res page 2 ($4000-$5FFF), so programs may use
|
||||
; everything below $4000 (the default config puts code at $0803 up).
|
||||
;
|
||||
.include "zeropage.inc"
|
||||
.include "tgi-kernel.inc"
|
||||
.include "tgi-error.inc"
|
||||
|
||||
.export _a2hires_tgi
|
||||
|
||||
; Soft switches
|
||||
TXTCLR := $C050
|
||||
TXTSET := $C051
|
||||
MIXCLR := $C052
|
||||
MIXSET := $C053 ; MIXCLR+1
|
||||
LOWSCR := $C054
|
||||
HISCR := $C055 ; LOWSCR+1
|
||||
LORES := $C056
|
||||
HIRES := $C057
|
||||
WNDTOP := $22
|
||||
|
||||
; Parameters passed by the TGI kernel
|
||||
X1 := ptr1
|
||||
Y1 := ptr2
|
||||
X2 := ptr3
|
||||
Y2 := ptr4
|
||||
|
||||
; Scratch (never live across a driver call)
|
||||
SCR := sreg ; pointer to screen byte
|
||||
MASK := tmp1 ; pixel mask within byte
|
||||
TMP := tmp2
|
||||
|
||||
; ------------------------------------------------------------------------
|
||||
|
||||
.rodata
|
||||
|
||||
; Driver header: signature, capabilities, then the jump table.
|
||||
; Entries are absolute since this driver is linked statically.
|
||||
_a2hires_tgi:
|
||||
.byte $74, $67, $69 ; "tgi"
|
||||
.byte TGI_API_VERSION
|
||||
.addr $0000 ; Library reference
|
||||
.word 280 ; X resolution
|
||||
.word 192 ; Y resolution
|
||||
.byte 8 ; Number of drawing colors
|
||||
.byte 1 ; Number of screens
|
||||
.byte 7 ; System font X size
|
||||
.byte 8 ; System font Y size
|
||||
.word $00EA ; Aspect ratio (based on 4/3 display)
|
||||
.byte 0 ; TGI driver flags
|
||||
|
||||
.addr INSTALL
|
||||
.addr UNINSTALL
|
||||
.addr INIT
|
||||
.addr DONE
|
||||
.addr GETERROR
|
||||
.addr CONTROL
|
||||
.addr CLEAR
|
||||
.addr SETVIEWPAGE
|
||||
.addr SETDRAWPAGE
|
||||
.addr SETCOLOR
|
||||
.addr SETPALETTE
|
||||
.addr GETPALETTE
|
||||
.addr GETDEFPALETTE
|
||||
.addr SETPIXEL
|
||||
.addr GETPIXEL
|
||||
.addr LINE
|
||||
.addr BAR
|
||||
.addr TEXTSTYLE
|
||||
.addr OUTTEXT
|
||||
|
||||
; Colors: 0 black, 1 green, 2 violet, 3 white, 4 black2, 5 orange, 6 blue, 7 white2
|
||||
DEFPALETTE:
|
||||
.byte 0, 1, 2, 3, 4, 5, 6, 7
|
||||
|
||||
; Per-color pixel behavior, indexed by color & 3
|
||||
EVENSET: .byte 0, 0, 1, 1 ; set the pixel in even columns?
|
||||
ODDSET: .byte 0, 1, 0, 1 ; set the pixel in odd columns?
|
||||
|
||||
ROWOFS: .byte 0, 40, 80 ; 40 bytes per row of 8 scanlines, 3 groups
|
||||
|
||||
; ------------------------------------------------------------------------
|
||||
|
||||
.bss
|
||||
|
||||
ERROR: .res 1
|
||||
SETEVEN: .res 1 ; current color: set pixel in even columns
|
||||
SETODD: .res 1 ; set pixel in odd columns
|
||||
PALBIT: .res 1 ; palette bit ($00 or $80)
|
||||
|
||||
XDIV: .res 256 ; x / 7
|
||||
XMASK: .res 256 ; 1 << (x % 7)
|
||||
ROWLO: .res 192 ; address of scanline y on page 1
|
||||
ROWHI: .res 192
|
||||
|
||||
; Pixel position, also used as the running point of LINE
|
||||
PX: .res 2
|
||||
PY: .res 1
|
||||
|
||||
; Line state
|
||||
SXDIR: .res 1 ; $00 = x increases, $FF = decreases
|
||||
SYDIR: .res 1
|
||||
MAJVEC: .res 2 ; routine that steps the major axis
|
||||
MINVEC: .res 2 ; ... and the minor axis
|
||||
LEN: .res 2 ; major axis length (steps remaining)
|
||||
TOT: .res 2 ; copy of LEN
|
||||
MINOR: .res 2 ; minor axis length
|
||||
ERR: .res 2
|
||||
|
||||
; ------------------------------------------------------------------------
|
||||
|
||||
.code
|
||||
|
||||
INSTALL:
|
||||
UNINSTALL:
|
||||
SETVIEWPAGE:
|
||||
SETDRAWPAGE:
|
||||
TEXTSTYLE:
|
||||
OUTTEXT:
|
||||
rts
|
||||
|
||||
; INIT: build lookup tables, then switch on page 1 of hi-res
|
||||
INIT:
|
||||
; x -> byte offset and bit mask, for x = 0..255
|
||||
ldx #0
|
||||
stx tmp3 ; div
|
||||
stx tmp4 ; mod
|
||||
lda #1
|
||||
sta MASK
|
||||
@xloop: lda tmp3
|
||||
sta XDIV,x
|
||||
lda MASK
|
||||
sta XMASK,x
|
||||
asl MASK
|
||||
inc tmp4
|
||||
lda tmp4
|
||||
cmp #7
|
||||
bcc @nx
|
||||
lda #0
|
||||
sta tmp4
|
||||
lda #1
|
||||
sta MASK
|
||||
inc tmp3
|
||||
@nx: inx
|
||||
bne @xloop
|
||||
|
||||
; y -> address of scanline: $2000 + (y&7)*$400 + ((y>>3)&7)*$80 + (y>>6)*40
|
||||
ldy #0
|
||||
@yloop: tya
|
||||
and #7
|
||||
asl
|
||||
asl
|
||||
ora #$20
|
||||
sta TMP
|
||||
tya
|
||||
lsr
|
||||
lsr
|
||||
lsr
|
||||
and #7
|
||||
lsr ; carry = bit 0 of the 8-line group
|
||||
ora TMP
|
||||
sta TMP
|
||||
lda #0
|
||||
ror ; $80 if carry
|
||||
sta MASK
|
||||
tya
|
||||
asl
|
||||
rol
|
||||
rol
|
||||
and #3
|
||||
tax
|
||||
lda ROWOFS,x
|
||||
clc
|
||||
adc MASK
|
||||
sta ROWLO,y
|
||||
lda TMP
|
||||
adc #0
|
||||
sta ROWHI,y
|
||||
iny
|
||||
cpy #192
|
||||
bne @yloop
|
||||
|
||||
bit MIXCLR
|
||||
bit HISCR
|
||||
bit HIRES
|
||||
bit TXTCLR
|
||||
lda #TGI_ERR_OK
|
||||
sta ERROR
|
||||
rts
|
||||
|
||||
DONE:
|
||||
bit TXTSET
|
||||
bit LOWSCR
|
||||
bit LORES
|
||||
lda #0
|
||||
sta WNDTOP
|
||||
rts
|
||||
|
||||
GETERROR:
|
||||
lda ERROR
|
||||
ldx #TGI_ERR_OK
|
||||
stx ERROR
|
||||
rts
|
||||
|
||||
; CONTROL: data = 0 for full screen, 1 for 4 lines of text
|
||||
CONTROL:
|
||||
ora ptr1+1
|
||||
bne @err
|
||||
lda ptr1
|
||||
cmp #2
|
||||
bcs @err
|
||||
tax
|
||||
beq @top
|
||||
lda #20
|
||||
@top: sta WNDTOP
|
||||
lda MIXCLR,x
|
||||
lda #TGI_ERR_OK
|
||||
.byte $2C ; BIT absolute
|
||||
@err: lda #TGI_ERR_INV_ARG
|
||||
sta ERROR
|
||||
rts
|
||||
|
||||
; CLEAR: fill the screen with zeros
|
||||
CLEAR:
|
||||
lda #0
|
||||
sta SCR
|
||||
lda #$40
|
||||
sta SCR+1
|
||||
ldx #32 ; 32 pages of 256 bytes
|
||||
ldy #0
|
||||
tya
|
||||
@loop: sta (SCR),y
|
||||
iny
|
||||
bne @loop
|
||||
inc SCR+1
|
||||
dex
|
||||
bne @loop
|
||||
rts
|
||||
|
||||
; SETCOLOR: A = 0..7
|
||||
SETCOLOR:
|
||||
tax
|
||||
and #3
|
||||
tay
|
||||
lda EVENSET,y
|
||||
sta SETEVEN
|
||||
lda ODDSET,y
|
||||
sta SETODD
|
||||
txa
|
||||
and #4
|
||||
beq @lo
|
||||
lda #$80
|
||||
@lo: sta PALBIT
|
||||
rts
|
||||
|
||||
SETPALETTE:
|
||||
lda #TGI_ERR_INV_FUNC
|
||||
sta ERROR
|
||||
rts
|
||||
|
||||
GETPALETTE:
|
||||
GETDEFPALETTE:
|
||||
lda #<DEFPALETTE
|
||||
ldx #>DEFPALETTE
|
||||
rts
|
||||
|
||||
; ADDR: locate the pixel at PX/PY. Returns the byte address in (SCR),y
|
||||
; and the pixel's bit in MASK.
|
||||
ADDR:
|
||||
ldx PY
|
||||
lda ROWLO,x
|
||||
sta SCR
|
||||
lda ROWHI,x
|
||||
clc
|
||||
adc #$20 ; page 2
|
||||
sta SCR+1
|
||||
ldy #0 ; byte offset of the 256 column page
|
||||
ldx PX
|
||||
lda PX+1
|
||||
beq @lo
|
||||
txa ; x >= 256: use x-252, which is 36 bytes on
|
||||
sec
|
||||
sbc #252
|
||||
tax
|
||||
ldy #36
|
||||
@lo: tya
|
||||
clc
|
||||
adc XDIV,x
|
||||
tay
|
||||
lda XMASK,x
|
||||
sta MASK
|
||||
rts
|
||||
|
||||
; PLOT: draw the pixel at PX/PY in the current color
|
||||
PLOT:
|
||||
jsr ADDR
|
||||
ldx SETEVEN
|
||||
lda PX
|
||||
lsr ; carry = odd column
|
||||
bcc @even
|
||||
ldx SETODD
|
||||
@even: lda MASK
|
||||
ora #$80 ; the palette bit is always rewritten
|
||||
eor #$FF
|
||||
and (SCR),y
|
||||
ora PALBIT
|
||||
cpx #0
|
||||
beq @store
|
||||
ora MASK
|
||||
@store: sta (SCR),y
|
||||
rts
|
||||
|
||||
SETPIXEL:
|
||||
lda X1
|
||||
sta PX
|
||||
lda X1+1
|
||||
sta PX+1
|
||||
lda Y1
|
||||
sta PY
|
||||
jmp PLOT
|
||||
|
||||
; GETPIXEL: returns black/white (+4 for the second palette)
|
||||
GETPIXEL:
|
||||
lda X1
|
||||
sta PX
|
||||
lda X1+1
|
||||
sta PX+1
|
||||
lda Y1
|
||||
sta PY
|
||||
jsr ADDR
|
||||
lda (SCR),y
|
||||
tax ; remember the palette bit
|
||||
and MASK
|
||||
beq @black
|
||||
lda #3
|
||||
@black: cpx #$80
|
||||
bcc @done
|
||||
adc #3 ; carry is set: +4
|
||||
@done: ldx #0
|
||||
rts
|
||||
|
||||
; LINE: DDA between X1/Y1 and X2/Y2. The major axis is stepped every pixel,
|
||||
; and the minor axis whenever the accumulated error passes the major length.
|
||||
LINE:
|
||||
lda X1
|
||||
sta PX
|
||||
lda X1+1
|
||||
sta PX+1
|
||||
lda Y1
|
||||
sta PY
|
||||
|
||||
; dx = |x2 - x1|
|
||||
lda #0
|
||||
sta SXDIR
|
||||
sec
|
||||
lda X2
|
||||
sbc X1
|
||||
sta LEN
|
||||
lda X2+1
|
||||
sbc X1+1
|
||||
sta LEN+1
|
||||
bcs @dxpos
|
||||
dec SXDIR ; $FF
|
||||
sec ; negate
|
||||
lda #0
|
||||
sbc LEN
|
||||
sta LEN
|
||||
lda #0
|
||||
sbc LEN+1
|
||||
sta LEN+1
|
||||
@dxpos:
|
||||
; dy = |y2 - y1|
|
||||
lda #0
|
||||
sta SYDIR
|
||||
sta MINOR+1
|
||||
sec
|
||||
lda Y2
|
||||
sbc Y1
|
||||
bcs @dypos
|
||||
dec SYDIR
|
||||
eor #$FF
|
||||
adc #1
|
||||
@dypos: sta MINOR
|
||||
|
||||
; the major axis is the longer one
|
||||
lda MINOR
|
||||
cmp LEN
|
||||
lda MINOR+1
|
||||
sbc LEN+1
|
||||
bcs @ymajor ; dy >= dx
|
||||
lda #<stepx
|
||||
sta MAJVEC
|
||||
lda #>stepx
|
||||
sta MAJVEC+1
|
||||
lda #<stepy
|
||||
sta MINVEC
|
||||
lda #>stepy
|
||||
sta MINVEC+1
|
||||
bne @go ; branch always
|
||||
@ymajor:
|
||||
lda LEN ; swap: LEN <-> MINOR
|
||||
ldx MINOR
|
||||
sta MINOR
|
||||
stx LEN
|
||||
lda LEN+1
|
||||
ldx MINOR+1
|
||||
sta MINOR+1
|
||||
stx LEN+1
|
||||
lda #<stepy
|
||||
sta MAJVEC
|
||||
lda #>stepy
|
||||
sta MAJVEC+1
|
||||
lda #<stepx
|
||||
sta MINVEC
|
||||
lda #>stepx
|
||||
sta MINVEC+1
|
||||
@go: lda LEN
|
||||
sta TOT
|
||||
lda LEN+1
|
||||
sta TOT+1
|
||||
lsr ; error starts at half the length
|
||||
sta ERR+1
|
||||
lda LEN
|
||||
ror
|
||||
sta ERR
|
||||
@loop: jsr PLOT
|
||||
lda LEN
|
||||
ora LEN+1
|
||||
beq @done
|
||||
lda LEN
|
||||
bne @nb
|
||||
dec LEN+1
|
||||
@nb: dec LEN
|
||||
jsr @major
|
||||
clc
|
||||
lda ERR
|
||||
adc MINOR
|
||||
sta ERR
|
||||
lda ERR+1
|
||||
adc MINOR+1
|
||||
sta ERR+1
|
||||
lda ERR ; error >= length?
|
||||
cmp TOT
|
||||
lda ERR+1
|
||||
sbc TOT+1
|
||||
bcc @loop
|
||||
sec ; yes: subtract it and step the minor axis
|
||||
lda ERR
|
||||
sbc TOT
|
||||
sta ERR
|
||||
lda ERR+1
|
||||
sbc TOT+1
|
||||
sta ERR+1
|
||||
jsr @minor
|
||||
jmp @loop
|
||||
@done: rts
|
||||
@major: jmp (MAJVEC)
|
||||
@minor: jmp (MINVEC)
|
||||
|
||||
stepx: lda SXDIR
|
||||
bne @dec
|
||||
inc PX
|
||||
bne @ret
|
||||
inc PX+1
|
||||
@ret: rts
|
||||
@dec: lda PX
|
||||
bne @nb
|
||||
dec PX+1
|
||||
@nb: dec PX
|
||||
rts
|
||||
|
||||
stepy: lda SYDIR
|
||||
bne @dec
|
||||
inc PY
|
||||
rts
|
||||
@dec: dec PY
|
||||
rts
|
||||
|
||||
; BAR: horizontal lines from Y1 to Y2 (the kernel sorts and clips the corners)
|
||||
BAR:
|
||||
inc Y2
|
||||
@row: lda Y2
|
||||
pha
|
||||
lda Y1
|
||||
sta Y2
|
||||
jsr LINE
|
||||
pla
|
||||
sta Y2
|
||||
inc Y1
|
||||
cmp Y1
|
||||
bne @row
|
||||
rts
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
Hi-res (280x192, 6 colors) TGI demo for the Apple ][.
|
||||
Four full-screen animations; press a key to switch to the next (ESC quits):
|
||||
a Lissajous line figure, a swirl of dots, random lines, random bars.
|
||||
Each shape is remembered in a ring buffer; when the buffer wraps, the
|
||||
oldest shape is erased, so the picture keeps moving.
|
||||
Uses our own hi-res TGI driver, a2hires.s (the stock one needs
|
||||
Applesoft ROM routines). apple2-hgr2.cfg keeps $2000-$5FFF free.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <cc65.h>
|
||||
#include <conio.h>
|
||||
#include <tgi.h>
|
||||
#include "a2hires.h"
|
||||
//#link "a2hires.s"
|
||||
//#tooldef ld cfgfile=apple2-hgr2.cfg
|
||||
//#resource "apple2-hgr2.cfg"
|
||||
|
||||
#define W 280 // screen size
|
||||
#define H 192
|
||||
#define R (H/2 - 4) // radius of the figures
|
||||
#define MAXSEG 240
|
||||
|
||||
// hi-res colors: 1 green, 2 violet, 5 orange, 6 blue
|
||||
static const unsigned char Colors[4] = { 1, 2, 5, 6 };
|
||||
|
||||
#define SIN(a) _sin((unsigned)(a) % 360)
|
||||
#define COS(a) _sin(((unsigned)(a) + 90) % 360)
|
||||
|
||||
typedef struct {
|
||||
int x1, y1, x2, y2;
|
||||
unsigned char color;
|
||||
} Seg;
|
||||
|
||||
static Seg ring[MAXSEG]; // the shapes currently on screen
|
||||
static unsigned head; // next slot to overwrite
|
||||
static unsigned count; // slots in use
|
||||
static unsigned T; // time, advances every step
|
||||
static unsigned char mode;
|
||||
static const unsigned ringsize[4] = { 40, 240, 70, 14 };
|
||||
|
||||
// scale a sine value (-256..256) to -R..R
|
||||
static int scale(int v)
|
||||
{
|
||||
return (int)(((long)v * R) / 256);
|
||||
}
|
||||
|
||||
static unsigned char randcolor(void)
|
||||
{
|
||||
return Colors[rand() & 3];
|
||||
}
|
||||
|
||||
// draw (or, with color 0, erase) one shape for the current mode
|
||||
static void draw(const Seg* s, unsigned char color)
|
||||
{
|
||||
int cx = W/2, cy = H/2;
|
||||
tgi_setcolor(color);
|
||||
switch (mode) {
|
||||
case 0: // line, mirrored four ways
|
||||
tgi_line(cx + s->x1, cy + s->y1, cx + s->x2, cy + s->y2);
|
||||
tgi_line(cx - s->x1, cy + s->y1, cx - s->x2, cy + s->y2);
|
||||
tgi_line(cx + s->x1, cy - s->y1, cx + s->x2, cy - s->y2);
|
||||
tgi_line(cx - s->x1, cy - s->y1, cx - s->x2, cy - s->y2);
|
||||
break;
|
||||
case 1: // dot
|
||||
tgi_setpixel(s->x1, s->y1);
|
||||
break;
|
||||
case 2: // line
|
||||
tgi_line(s->x1, s->y1, s->x2, s->y2);
|
||||
break;
|
||||
case 3: // bar
|
||||
tgi_bar(s->x1, s->y1, s->x2, s->y2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Lissajous lines
|
||||
static void make_lissajous(Seg* s)
|
||||
{
|
||||
unsigned a = T * 2;
|
||||
s->x1 = scale(SIN(a * 3)) * 3/2;
|
||||
s->y1 = scale(COS(a * 2));
|
||||
s->x2 = scale(COS(a * 5 + T)) * 3/2;
|
||||
s->y2 = scale(SIN(a * 4));
|
||||
s->color = Colors[(T >> 4) & 3];
|
||||
}
|
||||
|
||||
// dots on a wandering rose curve
|
||||
static void make_swirl(Seg* s)
|
||||
{
|
||||
unsigned a = T * 7;
|
||||
int r = scale(SIN(a * 5 + T / 2)) + scale(COS(a * 3)) / 2;
|
||||
s->x1 = W/2 + r * COS(a) / 128;
|
||||
s->y1 = H/2 + r * SIN(a) / 256;
|
||||
s->color = Colors[(a / 90) & 3];
|
||||
}
|
||||
|
||||
// random horizontal and vertical lines
|
||||
static void make_hatch(Seg* s)
|
||||
{
|
||||
int x = rand() % W;
|
||||
int y = rand() % H;
|
||||
int len = 4 + rand() % 60;
|
||||
s->x1 = s->x2 = x;
|
||||
s->y1 = s->y2 = y;
|
||||
if (rand() & 1) {
|
||||
s->x2 = x + len < W ? x + len : W - 1;
|
||||
} else {
|
||||
s->y2 = y + len < H ? y + len : H - 1;
|
||||
}
|
||||
s->color = randcolor();
|
||||
}
|
||||
|
||||
// random filled bars
|
||||
static void make_bar(Seg* s)
|
||||
{
|
||||
int x = rand() % (W - 8);
|
||||
int y = rand() % (H - 4);
|
||||
int w = 8 + rand() % (W/2);
|
||||
int h = 4 + rand() % 24;
|
||||
s->x1 = x;
|
||||
s->y1 = y;
|
||||
s->x2 = x + w < W ? x + w : W - 1;
|
||||
s->y2 = y + h < H ? y + h : H - 1;
|
||||
s->color = randcolor();
|
||||
}
|
||||
|
||||
// add one shape, erasing the oldest if the ring is full
|
||||
static void step(void)
|
||||
{
|
||||
Seg* s = &ring[head];
|
||||
unsigned size = ringsize[mode];
|
||||
if (count == size) {
|
||||
draw(s, TGI_COLOR_BLACK);
|
||||
} else {
|
||||
count++;
|
||||
}
|
||||
switch (mode) {
|
||||
case 0: make_lissajous(s); break;
|
||||
case 1: make_swirl(s); break;
|
||||
case 2: make_hatch(s); break;
|
||||
case 3: make_bar(s); break;
|
||||
}
|
||||
draw(s, s->color);
|
||||
if (++head == size) head = 0;
|
||||
T++;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
tgi_install(a2hires_tgi);
|
||||
tgi_init();
|
||||
for (;;) {
|
||||
tgi_clear();
|
||||
head = count = T = 0;
|
||||
while (!kbhit()) {
|
||||
step();
|
||||
if (mode == 1) { step(); step(); step(); }
|
||||
}
|
||||
if (cgetc() == 27) break;
|
||||
mode = (mode + 1) & 3;
|
||||
}
|
||||
tgi_uninstall();
|
||||
return 0;
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
/*
|
||||
NOTE: The alternate Apple ][ ROM used by 8bitworkshop
|
||||
is not compatible with the hi-res TGI driver.
|
||||
So we use the lo-res driver here.
|
||||
has no Applesoft routines, so the stock hi-res TGI driver
|
||||
doesn't work. We use our own hi-res driver, a2hires.s.
|
||||
apple2-hgr2.cfg keeps $2000-$5FFF free for the picture.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -10,6 +11,10 @@ NOTE: The alternate Apple ][ ROM used by 8bitworkshop
|
||||
#include <ctype.h>
|
||||
#include <modload.h>
|
||||
#include <tgi.h>
|
||||
#include "a2hires.h"
|
||||
//#link "a2hires.s"
|
||||
//#tooldef ld cfgfile=apple2-hgr2.cfg
|
||||
//#resource "apple2-hgr2.cfg"
|
||||
|
||||
#ifndef DYN_DRV
|
||||
# define DYN_DRV 0
|
||||
@@ -189,7 +194,7 @@ int main (void)
|
||||
CheckError ("tgi_load_driver");
|
||||
#else
|
||||
/* Install the driver */
|
||||
tgi_install (a2_lo_tgi);
|
||||
tgi_install (a2hires_tgi);
|
||||
CheckError ("tgi_install");
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
Hi-res (280x192, 6 colors) TGI demo for the Apple ][.
|
||||
Four full-screen animations; press a key to switch to the next (ESC quits):
|
||||
a Lissajous line figure, a swirl of dots, random lines, random bars.
|
||||
Each shape is remembered in a ring buffer; when the buffer wraps, the
|
||||
oldest shape is erased, so the picture keeps moving.
|
||||
Uses our own hi-res TGI driver, a2hires.s (the stock one needs
|
||||
Applesoft ROM routines). apple2-hgr2.cfg keeps $2000-$5FFF free.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <cc65.h>
|
||||
#include <conio.h>
|
||||
#include <tgi.h>
|
||||
|
||||
//#tooldef ld cfgfile=atari.cfg
|
||||
//#symbol ld __RESERVED_MEMORY__=8192
|
||||
|
||||
#define W 80 // screen size
|
||||
#define H 192
|
||||
#define R (H/2 - 4) // radius of the figures
|
||||
#define MAXSEG 240
|
||||
#define MAXCOL 7
|
||||
|
||||
#define SIN(a) _sin((unsigned)(a) % 360)
|
||||
#define COS(a) _sin(((unsigned)(a) + 90) % 360)
|
||||
|
||||
typedef struct {
|
||||
int x1, y1, x2, y2;
|
||||
unsigned char color;
|
||||
} Seg;
|
||||
|
||||
static Seg ring[MAXSEG]; // the shapes currently on screen
|
||||
static unsigned head; // next slot to overwrite
|
||||
static unsigned count; // slots in use
|
||||
static unsigned T; // time, advances every step
|
||||
static unsigned char mode;
|
||||
static const unsigned ringsize[4] = { 40, 240, 70, 14 };
|
||||
|
||||
// scale a sine value (-256..256) to -R..R
|
||||
static int scale(int v)
|
||||
{
|
||||
return (int)(((long)v * R) / 256);
|
||||
}
|
||||
|
||||
static unsigned char randcolor(void)
|
||||
{
|
||||
return rand() & MAXCOL;
|
||||
}
|
||||
|
||||
// draw (or, with color 0, erase) one shape for the current mode
|
||||
static void draw(const Seg* s, unsigned char color)
|
||||
{
|
||||
int cx = W/2, cy = H/2;
|
||||
tgi_setcolor(color);
|
||||
switch (mode) {
|
||||
case 0: // line, mirrored four ways
|
||||
tgi_line(cx + s->x1, cy + s->y1, cx + s->x2, cy + s->y2);
|
||||
tgi_line(cx - s->x1, cy + s->y1, cx - s->x2, cy + s->y2);
|
||||
tgi_line(cx + s->x1, cy - s->y1, cx + s->x2, cy - s->y2);
|
||||
tgi_line(cx - s->x1, cy - s->y1, cx - s->x2, cy - s->y2);
|
||||
break;
|
||||
case 1: // dot
|
||||
tgi_setpixel(s->x1, s->y1);
|
||||
break;
|
||||
case 2: // line
|
||||
tgi_line(s->x1, s->y1, s->x2, s->y2);
|
||||
break;
|
||||
case 3: // bar
|
||||
tgi_bar(s->x1, s->y1, s->x2, s->y2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Lissajous lines
|
||||
static void make_lissajous(Seg* s)
|
||||
{
|
||||
unsigned a = T * 2;
|
||||
s->x1 = scale(SIN(a * 3)) * 3/2;
|
||||
s->y1 = scale(COS(a * 2));
|
||||
s->x2 = scale(COS(a * 5 + T)) * 3/2;
|
||||
s->y2 = scale(SIN(a * 4));
|
||||
s->color = (T >> 4) & MAXCOL;
|
||||
}
|
||||
|
||||
// dots on a wandering rose curve
|
||||
static void make_swirl(Seg* s)
|
||||
{
|
||||
unsigned a = T * 7;
|
||||
int r = scale(SIN(a * 5 + T / 2)) + scale(COS(a * 3)) / 2;
|
||||
s->x1 = W/2 + r * COS(a) / 128;
|
||||
s->y1 = H/2 + r * SIN(a) / 256;
|
||||
s->color = (a / 90) & MAXCOL;
|
||||
}
|
||||
|
||||
// random horizontal and vertical lines
|
||||
static void make_hatch(Seg* s)
|
||||
{
|
||||
int x = rand() % W;
|
||||
int y = rand() % H;
|
||||
int len = 4 + rand() % 60;
|
||||
s->x1 = s->x2 = x;
|
||||
s->y1 = s->y2 = y;
|
||||
if (rand() & 1) {
|
||||
s->x2 = x + len < W ? x + len : W - 1;
|
||||
} else {
|
||||
s->y2 = y + len < H ? y + len : H - 1;
|
||||
}
|
||||
s->color = randcolor();
|
||||
}
|
||||
|
||||
// random filled bars
|
||||
static void make_bar(Seg* s)
|
||||
{
|
||||
int x = rand() % (W - 8);
|
||||
int y = rand() % (H - 4);
|
||||
int w = 8 + rand() % (W/2);
|
||||
int h = 4 + rand() % 24;
|
||||
s->x1 = x;
|
||||
s->y1 = y;
|
||||
s->x2 = x + w < W ? x + w : W - 1;
|
||||
s->y2 = y + h < H ? y + h : H - 1;
|
||||
s->color = randcolor();
|
||||
}
|
||||
|
||||
// add one shape, erasing the oldest if the ring is full
|
||||
static void step(void)
|
||||
{
|
||||
Seg* s = &ring[head];
|
||||
unsigned size = ringsize[mode];
|
||||
if (count == size) {
|
||||
draw(s, TGI_COLOR_BLACK);
|
||||
} else {
|
||||
count++;
|
||||
}
|
||||
switch (mode) {
|
||||
case 0: make_lissajous(s); break;
|
||||
case 1: make_swirl(s); break;
|
||||
case 2: make_hatch(s); break;
|
||||
case 3: make_bar(s); break;
|
||||
}
|
||||
draw(s, s->color);
|
||||
if (++head == size) head = 0;
|
||||
T++;
|
||||
}
|
||||
|
||||
void set_palette(void) {
|
||||
int i;
|
||||
for (i=0; i<=MAXCOL; i++) {
|
||||
_setcolor(i, i*2, 15);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
tgi_install(atr10_tgi);
|
||||
tgi_init();
|
||||
set_palette();
|
||||
for (;;) {
|
||||
tgi_clear();
|
||||
head = count = T = 0;
|
||||
while (!kbhit()) {
|
||||
step();
|
||||
if (mode == 1) { step(); step(); step(); }
|
||||
}
|
||||
if (cgetc() == 27) break;
|
||||
mode = (mode + 1) & 3;
|
||||
}
|
||||
tgi_uninstall();
|
||||
return 0;
|
||||
}
|
||||
@@ -71,7 +71,7 @@ const char SPRITE_DATA[64*NUM_SPRITE_PATTERNS] = {
|
||||
#define OBSTACLE_INDEX 2
|
||||
|
||||
#define CENTER_X 172 // sprite start X coord.
|
||||
#define FLOOR_Y (128 << 8) // sprite start Y (fixed 8.8)
|
||||
#define FLOOR_Y (128u << 8) // sprite start Y (fixed 8.8)
|
||||
#define JUMP_VELOCITY (-900) // jump velocity (fixed 8.8)
|
||||
#define GRAVITY 32 // gravity (fixed 8.8)
|
||||
|
||||
|
||||
@@ -287,6 +287,12 @@ static unsigned char bright;
|
||||
|
||||
static unsigned char update_list[7*3+1];
|
||||
|
||||
// close the zero-page region. (Without the matching pop, cc65 V2.19-3867 also
|
||||
// moves the map[] declared above into ZEROPAGE, overflowing the 256-byte area.)
|
||||
|
||||
#pragma bss-name(pop)
|
||||
#pragma data-name(pop)
|
||||
|
||||
|
||||
|
||||
//smoothly fade current bright to the given value
|
||||
|
||||
@@ -376,22 +376,23 @@ byte just_one_actor_left() {
|
||||
}
|
||||
|
||||
void main() {
|
||||
dvgclear();
|
||||
dvgwrofs = 0x800; //write into the middle of VEC ROM (TODO)
|
||||
make_cached_shapes();
|
||||
create_obstacles(5);
|
||||
new_player_ship();
|
||||
while (!just_one_actor_left()) {
|
||||
dvgreset();
|
||||
control_player();
|
||||
draw_and_update_actors();
|
||||
CNTR();
|
||||
HALT();
|
||||
dvgstart();
|
||||
remove_expired_actors();
|
||||
frame++;
|
||||
watchdog=0;
|
||||
// while (vidframe == (frame & 0x3)) {}
|
||||
for (;;) {
|
||||
dvgclear();
|
||||
dvgwrofs = 0x800; //write into the middle of VEC ROM (TODO)
|
||||
make_cached_shapes();
|
||||
create_obstacles(5);
|
||||
new_player_ship();
|
||||
while (!just_one_actor_left()) {
|
||||
dvgreset();
|
||||
control_player();
|
||||
draw_and_update_actors();
|
||||
CNTR();
|
||||
HALT();
|
||||
dvgstart();
|
||||
remove_expired_actors();
|
||||
frame++;
|
||||
watchdog=0;
|
||||
// while (vidframe == (frame & 0x3)) {}
|
||||
}
|
||||
}
|
||||
main();
|
||||
}
|
||||
@@ -17,5 +17,4 @@ void main() {
|
||||
HALT();
|
||||
dvgstart();
|
||||
}
|
||||
main();
|
||||
}
|
||||
@@ -65,7 +65,7 @@ const SYSTEM_HEADERS: { [header: string]: string[] } = {
|
||||
'cbm.h': ['c64', 'vic20'],
|
||||
'apple2.h': ['apple2'],
|
||||
'apple2enh.h': ['apple2'],
|
||||
'atari.h': ['atari8-800', 'atari8-5200'],
|
||||
'atari.h': ['atari8-800'],
|
||||
'atari5200.h': ['atari8-5200'],
|
||||
'atari7800.h': ['atari7800'],
|
||||
'pce.h': ['pce'],
|
||||
@@ -292,7 +292,9 @@ export async function detectProject(input: DetectInput): Promise<Detection[]> {
|
||||
export function isClearWinner(detections: Detection[]): boolean {
|
||||
if (!detections.length || detections[0].score < 0.5) return false;
|
||||
var w = (d: Detection) => d.weight ?? d.score;
|
||||
return detections.length == 1 || w(detections[0]) - w(detections[1]) >= 0.2;
|
||||
// weights are rounded for display, so allow for the rounding error when the
|
||||
// lead lands exactly on the threshold (e.g. a 0.2 folder-name hint)
|
||||
return detections.length == 1 || w(detections[0]) - w(detections[1]) >= 0.2 - 1e-9;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# cc65 Toolchain Upgrade
|
||||
|
||||
The cc65 family (`cc65`, `ca65`, `ld65`) used by the 6502 platforms was upgraded
|
||||
for 8bitworkshop 4.0.
|
||||
|
||||
| | Old build | Current build |
|
||||
| --- | --- | --- |
|
||||
| cc65 commit | `6ac4aa4` (2021-12-24) | `d8a486a` (V2.19-3867, 2026-09-26) |
|
||||
| Runtime | Emscripten | WASI (`WASIRunner`) |
|
||||
| Includes/libs | hand-mixed 2019–2023 objects | one set built from the same revision |
|
||||
| Files | `fs65-*.data` (~22 MB) | `cc65-fs-<platform>.zip` (~10 MB) |
|
||||
|
||||
## Pitfalls
|
||||
|
||||
There are a few potential incompatibilities in the new compiler:
|
||||
|
||||
- **Inline assembly.** cc65 now optimizes non-volatile inline assembly.
|
||||
8bitworkshop runs with `--disable-opt OptLoadStore1` to prevent unexpected changes.
|
||||
- **`NULL` is now `((void*)0)`** from `<stddef.h>`, so headers that
|
||||
defined their own `NULL` had to stop (see `neslib.h`).
|
||||
- **Signed constant overflow warning**. For example, `128 << 8`
|
||||
is now an error-adjacent warning and needs an unsigned constant (`128u << 8`).
|
||||
- **Recursing into `main()` is an error.** Programs that tail-called
|
||||
`main()` need an explicit loop instead.
|
||||
- **`#pragma bss-name`/`data-name` regions must be closed.** Without a
|
||||
matching `pop`, the newer compiler could place earlier globals into
|
||||
`ZEROPAGE` and overflow the 256-byte area. (See `nes/chase.c`.)
|
||||
- **`sp` was renamed to `c_sp`.** The old alias still works but emits an
|
||||
`ld65` deprecation warning. Assembly code may need to be modified.
|
||||
|
||||
## Warnings vs. errors
|
||||
|
||||
8bitworkshop treats only **errors** as build failures; `Warning:` lines are
|
||||
ignored so a clean build does not fail on style or version noise.
|
||||
|
||||
## Apple II graphics
|
||||
|
||||
The stock lo-res driver (`a2.lo.s`) required a small upstream
|
||||
patch (a stray branch in `INSTALL`).
|
||||
@@ -52,6 +52,8 @@ multi-file projects, compiler/linker directives, and file extensions.
|
||||
See [Toolchains & Platforms](toolchains.md) for the full list of tools and
|
||||
the complete platform × extension → tool reference.
|
||||
|
||||
CC65 was updated in 8bitworkshop 4.0 -- for more information see
|
||||
[cc65 Toolchain Upgrade](cc65-upgrade.md).
|
||||
|
||||
## Debugger
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import breakpointsMd from "../../docs/breakpoints.md";
|
||||
import asseteditorMd from "../../docs/asseteditor.md";
|
||||
import managingFilesMd from "../../docs/managing-files.md";
|
||||
import buildDirectivesMd from "../../docs/build-directives.md";
|
||||
import cc65upgrade from "../../docs/cc65-upgrade.md";
|
||||
import toolchainsMd from "../../docs/toolchains.md";
|
||||
import assetHeadersMD from "../../docs/asset-headers.md";
|
||||
import embeddingIdeMd from "../../docs/embedding-ide.md";
|
||||
@@ -57,6 +58,7 @@ export const HELP_TOPICS: HelpTopic[] = [
|
||||
{ id: "asseteditor", title: "Asset Editor", html: asseteditorMd },
|
||||
{ id: "managing-files", title: "Managing Files", html: managingFilesMd },
|
||||
{ id: "build-directives", title: "Build Directives", html: buildDirectivesMd },
|
||||
{ id: "cc65-upgrade", title: "CC65 Upgrade", html: cc65upgrade },
|
||||
{ id: "toolchains", title: "Toolchains & Platforms", html: toolchainsMd },
|
||||
{ id: "asset-headers", title: "Asset Headers", html: assetHeadersMD },
|
||||
{ id: "embedding-ide", title: "Embedding the IDE", html: embeddingIdeMd },
|
||||
|
||||
@@ -12,6 +12,7 @@ const APPLE2_PRESETS: Preset[] = [
|
||||
{ id: 'keyboardtest.c', name: 'Keyboard Test' },
|
||||
{ id: 'mandel.c', name: 'Mandelbrot' },
|
||||
{ id: 'tgidemo.c', name: 'TGI Graphics Demo' },
|
||||
{ id: 'icepiano.c', name: 'Ice Piano (Hires Demo)' },
|
||||
{ id: 'Eliza.c', name: 'Eliza' },
|
||||
{ id: 'siegegame.c', name: 'Siege Game' },
|
||||
{ id: 'cosmic.c', name: 'Cosmic Impalas' },
|
||||
|
||||
@@ -16,6 +16,7 @@ var Atari8_PRESETS : Preset[] = [
|
||||
var Atari800_PRESETS = Atari8_PRESETS.concat([
|
||||
//{id:'testmusic.c', name:'POKEY Music'},
|
||||
{id:'tgidemo.c', name:'TGI Demo'},
|
||||
{id:'icepiano.c', name:'Ice Piano (TGI Demo)'},
|
||||
{id:'libdemo.c', name:'Library Demo'},
|
||||
{id:'scrolldemo.c', name:'Scrolling'},
|
||||
{id:'hello.cpp', name:'Hello Oscar64', category:'Oscar64'},
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -297,7 +297,10 @@ void __fastcall__ nmi_set_callback(void (*callback)(void));
|
||||
#define NAMETABLE_C 0x2800
|
||||
#define NAMETABLE_D 0x2c00
|
||||
|
||||
#define NULL 0
|
||||
// newer cc65 stddef.h defines NULL as ((void*)0); don't clash with it
|
||||
#ifndef NULL
|
||||
#define NULL ((void*)0)
|
||||
#endif
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
|
||||
@@ -12,6 +12,11 @@ import { wasiFSAdapter } from "../wasiutils";
|
||||
// cc65-fs-<platform>.zip, preopened at '.'
|
||||
const CC65_SHARE = 'share/cc65';
|
||||
|
||||
// cc65/ca65/ld65 report both errors and non-fatal warnings as
|
||||
// "<file>:<line>: <Warning|Error>: <msg>" on stderr; skip the warnings so a
|
||||
// clean build with only style/deprecation warnings doesn't fail.
|
||||
const re_cc65_warning = /:\s*Warning:/;
|
||||
|
||||
const wasiModules: { [tool: string]: WebAssembly.Module } = {};
|
||||
|
||||
/**
|
||||
@@ -187,7 +192,7 @@ export async function assembleCA65(step: BuildStep): Promise<BuildStepResult> {
|
||||
.concat(extraArgsFor('ca65', step.params.buildArgs));
|
||||
args.splice(args.length - 1, 0, ...extra);
|
||||
const { wasi, errno, stderr } = await runCC65Tool(step, 'ca65', args, (fs) => populateFiles(step, fs));
|
||||
stderr.forEach(makeErrorMatcher(errors, /(.+?):(\d+): (.+)/, 2, 3, step.path, 1));
|
||||
stderr.filter(s => !re_cc65_warning.test(s)).forEach(makeErrorMatcher(errors, /(.+?):(\d+): (.+)/, 2, 3, step.path, 1));
|
||||
checkExitCode('ca65', errno, stderr, errors);
|
||||
if (errors.length) {
|
||||
let listings : CodeListingMap = {};
|
||||
@@ -231,8 +236,13 @@ export async function linkLD65(step: BuildStep): Promise<BuildStepResult> {
|
||||
populateEntry(fs, params.cfgfile, store.getFileEntry(params.cfgfile), null);
|
||||
}
|
||||
});
|
||||
// any ld65 message (even a warning) fails the build
|
||||
for (let s of stderr) errors.push({ msg: s, line: 0 });
|
||||
// any non-warning ld65 message fails the build; again, "Warning:"-severity
|
||||
// output (e.g. the sp->c_sp deprecation from our prebuilt crt0.o/neslib2.lib)
|
||||
// is non-fatal noise the user can't act on.
|
||||
for (let s of stderr) {
|
||||
if (re_cc65_warning.test(s)) continue;
|
||||
errors.push({ msg: s, line: 0 });
|
||||
}
|
||||
checkExitCode('ld65', errno, stderr, errors);
|
||||
if (errors.length)
|
||||
return { errors: errors };
|
||||
@@ -356,8 +366,13 @@ export async function compileCC65(step: BuildStep): Promise<BuildStepResult> {
|
||||
// //#symbol c / //#flag c
|
||||
args.push.apply(args, defineArgs('cc65', params.symbols && params.symbols.compiler));
|
||||
args.push.apply(args, extraArgsFor('cc65', params.buildArgs));
|
||||
var customArgs = params.extra_compiler_args || ['-T', '-g', '-Oirs', '-Cl', '-W', '-pointer-sign,-no-effect'];
|
||||
args = args.concat(customArgs, args);
|
||||
var customArgs = params.extra_compiler_args || ['-T', '-g', '-Oirs', '-Cl', '-W', '-pointer-sign,-no-effect,-unreachable-code'];
|
||||
// cc65 V2.19-3867 parses inline asm into code entries and a new
|
||||
// optimizer step (OptLoadStore1) removes a store that follows a load
|
||||
// from the same address -- wrong for hardware registers and
|
||||
// self-modifying code. The old cc65 never touched inline asm; keep
|
||||
// that behavior by disabling just this new step.
|
||||
args = args.concat(customArgs, ['--disable-opt', 'OptLoadStore1'], args);
|
||||
args.push(step.path);
|
||||
const { wasi, errno, stderr } = await runCC65Tool(step, 'cc65', args, (fs) => {
|
||||
populateFiles(step, fs, {
|
||||
@@ -371,7 +386,7 @@ export async function compileCC65(step: BuildStep): Promise<BuildStepResult> {
|
||||
});
|
||||
populateExtraFiles(step, fs, params.extra_compile_files);
|
||||
});
|
||||
stderr.forEach(makeErrorMatcher(errors, /(.*?):(\d+): (.+)/, 2, 3, step.path, 1));
|
||||
stderr.filter(s => !re_cc65_warning.test(s)).forEach(makeErrorMatcher(errors, /(.*?):(\d+): (.+)/, 2, 3, step.path, 1));
|
||||
checkExitCode('cc65', errno, stderr, errors);
|
||||
if (errors.length) return { errors };
|
||||
putWorkFile(destpath, readWASIOutputString(wasi, destpath));
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -284,6 +284,39 @@ describe('8bws CLI', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('apple2 TGI drivers', function () {
|
||||
// build a one-file program next to the preset driver, run it, return the script output
|
||||
function runTGI(body, headers, script) {
|
||||
var dir = fs.mkdtempSync(path.join(os.tmpdir(), '8bws-tgi-'));
|
||||
for (var f of ['a2hires.s', 'a2hires.h']) {
|
||||
fs.copyFileSync(path.join('presets/apple2', f), path.join(dir, f));
|
||||
}
|
||||
var src = path.join(dir, 'main.c');
|
||||
fs.writeFileSync(src, headers + '\nint main(void) {\n' + body + '\nfor(;;);\n}\n');
|
||||
return cli('run', '--platform', 'apple2', src, '-e', script);
|
||||
}
|
||||
it('hires driver should draw pixels and lines on page 2', function () {
|
||||
var out = runTGI(
|
||||
'tgi_install(a2hires_tgi); tgi_init(); tgi_clear();' +
|
||||
'tgi_setcolor(3); tgi_setpixel(0, 0); tgi_setpixel(279, 191);' +
|
||||
'tgi_setcolor(5); tgi_line(0, 8, 13, 8);',
|
||||
'#include <tgi.h>\n#include "a2hires.h"\n//#link "a2hires.s"',
|
||||
'run 20; mem $4000 2; mem $4080 2; mem $5FF0 8');
|
||||
assert.ok(/^4000: 01 00/m.test(out), out); // white pixel at 0,0
|
||||
assert.ok(/^4080: AA D5/m.test(out), out); // orange line: odd columns + palette bit
|
||||
assert.ok(/^5FF0: 00 00 00 00 00 00 00 40/m.test(out), out); // last pixel
|
||||
});
|
||||
it('lores driver should install and draw (stray branch in cc65 a2.lo.s)', function () {
|
||||
var out = runTGI(
|
||||
'tgi_install(a2_lo_tgi); tgi_init(); tgi_clear();' +
|
||||
'tgi_setcolor(15); tgi_bar(0, 0, 39, 9);',
|
||||
'#include <tgi.h>',
|
||||
'run 30; pc 1; mem $400 4');
|
||||
assert.ok(!/PC=\$0000/.test(out), out); // not crashed into zero page
|
||||
assert.ok(/^0400: (?!00 00 00 00)/m.test(out), out); // lo-res memory was written
|
||||
});
|
||||
});
|
||||
|
||||
describe('legacy aliases', function () {
|
||||
it('should accept compile, check and compilerun', function () {
|
||||
assert.equal(cliJSON('compile', '--platform', 'gb', 'presets/gb/hello.c', '--check').command, 'check');
|
||||
|
||||
@@ -162,6 +162,9 @@ describe('Worker', function() {
|
||||
it('should compile CC65', function(done) {
|
||||
compile('cc65', '#if defined(__8BITWORKSHOP__) && defined(__MAIN__)\nint main() {\nint x=1;\nreturn x+2;\n}\n#endif', 'nes.mame', done, 40976, 3);
|
||||
});
|
||||
it('should compile CC65 with a warning (warnings are not errors)', function(done) {
|
||||
compile('cc65', '#if defined(__8BITWORKSHOP__) && defined(__MAIN__)\nint main() {\nconst const int x=1;\nreturn x+2;\n}\n#endif', 'nes.mame', done, 40976, 3, 0);
|
||||
});
|
||||
it('should NOT compile CC65 (compile error)', function(done) {
|
||||
compile('cc65', 'int main() {\nint x=1;\nprintf("%d",x);\nreturn x+2;\n}', 'nes', done, 0, 0, 1);
|
||||
});
|
||||
@@ -388,7 +391,7 @@ describe('Worker', function() {
|
||||
});
|
||||
it('should compile apple2 skeleton with CC65', function(done) {
|
||||
var csource = ab2str(fs.readFileSync('presets/apple2/skeleton.cc65'));
|
||||
compile('cc65', csource, 'apple2', done, 3023+58, 4, 0);
|
||||
compile('cc65', csource, 'apple2', done, 3382, 4, 0);
|
||||
});
|
||||
// TODO: test if compile, errors, then compile same file
|
||||
it('should compile CC65 banked', function(done) {
|
||||
@@ -402,7 +405,7 @@ describe('Worker', function() {
|
||||
it('should compile C64 cc65 skeleton', function(done) {
|
||||
var csource = ab2str(fs.readFileSync('presets/c64/skeleton.cc65'));
|
||||
csource = csource.replace('#include "','//');
|
||||
compile('cc65', csource, 'c64.wasm', done, 3112, 3, 0);
|
||||
compile('cc65', csource, 'c64.wasm', done, 3026, 3, 0);
|
||||
});
|
||||
it('should compile zmachine inform6 skeleton', function(done) {
|
||||
var csource = ab2str(fs.readFileSync('presets/zmachine/skeleton.inform6'));
|
||||
@@ -420,7 +423,7 @@ describe('Worker', function() {
|
||||
doBuild(msgs, done2, 205, 0, 0);
|
||||
});
|
||||
it('should compile CC65 flags', function(done) {
|
||||
compile('cc65', '#define CC65_FLAGS -Or,-g,-j\nint main() {\nint x=1;\nreturn x+2;\n}', 'apple2', done, 416+58, 3);
|
||||
compile('cc65', '#define CC65_FLAGS -Or,-g,-j\nint main() {\nint x=1;\nreturn x+2;\n}', 'apple2', done, 489, 2);
|
||||
});
|
||||
/*
|
||||
it('should compile gameboy', function(done) {
|
||||
|
||||
@@ -45,6 +45,24 @@ describe('detect', () => {
|
||||
assert.strictEqual(d[0].platform, 'vcs');
|
||||
});
|
||||
|
||||
it('tells the Atari 8-bit family apart by header', async () => {
|
||||
// atari.h is guarded by __ATARI__ (800); atari5200.h by __ATARI5200__
|
||||
var d = await detectProject(files({ 'game.c': '#include <atari.h>\nvoid main() {}\n' }));
|
||||
assert.strictEqual(d[0].platform, 'atari8-800');
|
||||
d = await detectProject(files({ 'game.c': '#include <atari5200.h>\nvoid main() {}\n' }));
|
||||
assert.strictEqual(d[0].platform, 'atari8-5200');
|
||||
});
|
||||
|
||||
it('prefers the folder name over a shared Atari library header', async () => {
|
||||
var map: { [fn: string]: string } = {
|
||||
'game.c': '#include <atari.h>\nvoid main() {}\n',
|
||||
'a8lib.h': '#if defined(__ATARI5200__)\n#include <atari5200.h>\n#else\n#include <atari.h>\n#endif\n',
|
||||
};
|
||||
var d = await detectProject({ files: Object.keys(map), read: (fn) => map[fn], platforms: PLATFORMS, dirName: 'atari8-800' });
|
||||
assert.strictEqual(d[0].platform, 'atari8-800');
|
||||
assert.ok(isClearWinner(d));
|
||||
});
|
||||
|
||||
it('says nothing about plain C', async () => {
|
||||
var d = await detectProject(files({ 'util.c': 'int add(int a, int b) { return a + b; }\n', 'util.h': 'int add(int, int);\n' }));
|
||||
assert.ok(d.every(x => x.score < 0.5), JSON.stringify(d));
|
||||
|
||||
Reference in new issue
Block a user