diff --git a/Millfork.sublime-syntax b/Millfork.sublime-syntax new file mode 100644 index 00000000..9470f858 --- /dev/null +++ b/Millfork.sublime-syntax @@ -0,0 +1,110 @@ +%YAML 1.2 +--- +name: Millfork +scope: source.mfk +file_extensions: [mfk] + +contexts: + main: + - include: comments + - include: preprocessor + - include: numbers + - include: strings + - include: keywords + - include: operators + - include: punctuation + + comments: + - match: '//' + scope: punctuation.definition.comment.mfk + push: + - meta_scope: comment.line.double-slash.mfk + - match: $\n? + pop: true + - match: ';' + scope: punctuation.definition.comment.mfk + push: + - meta_scope: comment.line.semicolon.mfk + - match: $\n? + pop: true + - match: '/\*' + scope: punctuation.definition.comment.begin.mfk + push: + - meta_scope: comment.block.mfk + - match: '\*/' + scope: punctuation.definition.comment.end.mfk + pop: true + + preprocessor: + # Poprawiona reguła obejmująca wszystkie warianty #elseif / #elsif / #else if + - match: '^\s*#\s*(if|else|else\s*if|elseif|elsif|endif|use|warn|error|info|fatal|pragma|infoeval|define)\b' + scope: keyword.control.preprocessor.mfk + + numbers: + - match: '\b0[bB][01]+\b|\%[01]+\b' + scope: constant.numeric.binary.mfk + - match: '\b0[oOqQ][0-7]+\b' + scope: constant.numeric.octal.mfk + - match: '\b0[xX][0-9A-Fa-f]+\b|\$[0-9A-Fa-f]+\b|\b[0-9A-Fa-f]+[hH]\b' + scope: constant.numeric.hex.mfk + - match: '\b[0-9]+\b' + scope: constant.numeric.decimal.mfk + - match: ':-+|:\++' + scope: constant.language.unnamed-label.mfk + + strings: + - match: '"' + scope: punctuation.definition.string.begin.mfk + push: + - meta_scope: string.quoted.double.mfk + - match: '\\.' + scope: constant.character.escape.mfk + - match: '"' + scope: punctuation.definition.string.end.mfk + pop: true + + keywords: + # Typy danych + - match: '\b(void|bool|byte|sbyte|ubyte|word|farword|pointer|farpointer|long|word_be|word_le|long_be|long_le|file|int[0-9]+|signed[0-9]+|unsigned[0-9]+|set_carry|set_zero|set_overflow|set_negative|clear_carry|clear_zero|clear_overflow|clear_negative)\b' + scope: storage.type.mfk + + # Modyfikatory + - match: '\b(const|volatile|register|static|stack|asm|inline|noinline|interrupt|kernal_interrupt|macro|reentrant|extern|segment|align|fast)\b' + scope: storage.modifier.mfk + + # Kontrola przepływu + - match: '\b(if|else|for|while|do|until|to|downto|parallelto|paralleluntil|return|break|continue|goto|default)\b' + scope: keyword.control.mfk + + # Inne słowa kluczowe + - match: '\b(import|array|struct|union|enum|alias|call|label)\b' + scope: keyword.other.mfk + + # Wbudowane funkcje i stałe + - match: '\b(not|hi|lo|nonet|sizeof|sin|cos|tan|defined|true|false|nullptr|nullchar)\b' + scope: support.function.mfk + + # Kodowania znaków + - match: '\b(default|defaultz|scr|scrz|petscii|ascii|petscr|pet|atascii|atari|bbc|sinclair|apple2|jis|jisx|iso_de|iso_yu|iso_no|iso_dk|iso_se|iso_fi|petsciiz|asciiz|petscrz|petz|atasciiz|atariz|bbcz|sinclairz|apple2z|jisz|jisxz|iso_dez|iso_yuz|iso_noz|iso_dkz|iso_sez|iso_fiz|utf8|utf16le|utf16be|latin0|latin9|iso8859_15|zx80|zx81|vectrex|koi7n2|short_koi|msx_intl|msx_us|msx_uk|msx_de|msx_fr|msx_es|msx_ru|msx_jp|msx_br|utf8z|utf16lez|utf16bez|latin0z|latin9z|iso8859_15z|zx80z|zx81z|vectrexz|koi7n2z|short_koiz|msx_intlz|msx_usz|msx_ukz|msx_dez|msx_frz|msx_esz|msx_ruz|msx_jpz|msx_brz)\b' + scope: constant.language.mfk + + operators: + # Operatory specyficzne Millforka + - match: '(\$\+|\$\-|\$\*|\$\<{2}|\$\>{2}|\$\+\=|\$\-\=|\$\*\=|\$\<{2}\=|\$\>{2}\=)' + scope: keyword.operator.millfork.special + - match: '(\+''|\-''|\*''|\<{2}''|\>{2}''|\+''\=|\-''\=|\*''\=|\<{2}''\=|\>{2}''\=)' + scope: keyword.operator.millfork.special + + # Operatory asemblera + - match: '#' + scope: keyword.operators.immediate.mfk + - match: '\.(?i:mod|bitnot|bitand|bitor|shl|shr|and|or|not|xor)\b' + scope: keyword.operators.word.mfk + + # Standardowe operatory + - match: '(\+|\-|\*|/|%|<{2}|>{2}|==|!=|<=|>=|<|>|=|\+=|-=|\*=|/=|&&|&|\|{2}|\||\^|\^=)' + scope: keyword.operator.mfk + + punctuation: + - match: '(\{|\}|\(|\)|\[|\]|@|#|;|,|\.)' + scope: punctuation.separator.mfk diff --git a/examples/x65/data/image.4bpp b/examples/x65/data/image.4bpp new file mode 100644 index 00000000..9ab2040a Binary files /dev/null and b/examples/x65/data/image.4bpp differ diff --git a/examples/x65/mode1_4bpp.mfk b/examples/x65/mode1_4bpp.mfk new file mode 100644 index 00000000..67a3bf46 --- /dev/null +++ b/examples/x65/mode1_4bpp.mfk @@ -0,0 +1,171 @@ +//----------------------------------------------------------------------------- +// MODE1 4bpp demo picture for X65 https://x65.zone/ +// +// Displays a full-screen 384×240 paletted bitmap using CGIA MODE1. +// Each pixel is 4 bits, packing 2 pixels per byte, giving 16 effective +// colours (8 palette registers × half-bright flag). +// +// The display list follows the ANTIC-style approach: one mode-row +// instruction per raster line, generated at compile time by a `for` loop. +//----------------------------------------------------------------------------- + +// Screen layout constants +const word BITMAP_ADDR = $2000 // where the bitmap lives in RAM +const byte BYTES_PER_LINE = 192 // 384 pixels / 2 pixels per byte (4bpp) +const byte ROWS = 240 // visible raster lines + +// Load the packed 4bpp image file (46080 bytes) +array BITMAP @ BITMAP_ADDR = file("data/image.4bpp", 0) + +//----------------------------------------------------------------------------- +// Global zero‑page variables +//----------------------------------------------------------------------------- + +volatile int24 vblclock @ $0 // 24‑bit frame counter, incremented each VBL + +byte b @ $3 // scratch byte +word w @ $4 // scratch word +pointer p @ $6 // scratch pointer + +//----------------------------------------------------------------------------- +// Display List +//----------------------------------------------------------------------------- +// The CGIA is a fetch master — once configured, it reads the display list +// from memory every frame and drives the raster output without CPU help. +// +// This list: +// 1. LOAD_MEMORY sets the LMS (Memory Scan) pointer to the bitmap. +// 2. 240 × MODE1 instructions each draw a single raster line from the +// current LMS address, then advance LMS by `stride` bytes. +// 3. JUMP + DLI loops the display list back to the start after each +// vertical blank, so the same picture is redrawn every frame. +//----------------------------------------------------------------------------- + +array(byte) dl align(fast) = [ + DL_INS_LOAD_MEMORY | DL_INS_LM_MEMORY_SCAN, // load LMS pointer + @word[BITMAP_ADDR], // 16‑bit address of bitmap + for x, 1, until, ROWS [ DL_MODE_PALETTE_BITMAP ], // one MODE1 row per raster line + DL_INS_JUMP | DL_INS_DL_INTERRUPT, @word[dl.addr] // jump to DL start at VBL +] + +//----------------------------------------------------------------------------- +// System initialisation +//----------------------------------------------------------------------------- +// Sets up the 65C816 CPU state, configures CGIA plane 0 for 4bpp bitmap +// mode, loads the palette, and enables vertical‑blank NMI interrupts so +// the `vblclock` counter runs. +//----------------------------------------------------------------------------- + +macro void x65_init() { + // --- CPU housekeeping --- + asm { + sei ; disable maskable IRQs during setup + cld ; ensure binary arithmetic (not BCD) + + ldx #$ff + txs ; initialise stack pointer to $01FF + } + + // --- Disable all planes while we configure --- + cgia.planes = 0 + + // --- Clear all 16 plane‑0 registers to known state --- + p = cgia.plane0.addr + for b, 9, downto, 0 { p[b] = 0 } + + // --- Configure plane 0 as 4bpp paletted bitmap --- + cgia.plane0.bckgnd.flags = PLANE_BITS_4BPP // 4 bits per pixel + half‑bright + cgia.plane0.bckgnd.row_height = 0 // each DL mode row = 1 raster line + + // --- Palette (8 base colours; upper 8 are half‑bright variants) --- + // The CGIA uses a 256‑colour palette organised as 32 hues, + // each with 8 brightness steps (0 = darkest, 7 = brightest). + // In 4 bpp mode the low 3 bits of a pixel select one of these + // 8 registers; the high bit (half‑bright) flips to the opposite + // brightness half of the same hue row, giving 16 effective colours. + // + // Register Index Palette entry RGB approximation + // ---------------------------------------------------------- + cgia.plane0.bckgnd.shared_color0 = 4 // hue 0, luma 4 → mid grey (146,146,146) + cgia.plane0.bckgnd.shared_color1 = 23 // hue 2, luma 7 → light orange (250,211,187) + cgia.plane0.bckgnd.color2 = 2 // hue 0, luma 2 → dark grey ( 73, 73, 73) + cgia.plane0.bckgnd.color3 = 3 // hue 0, luma 3 → grey (109,109,109) + cgia.plane0.bckgnd.color4 = 1 // hue 0, luma 1 → almost black ( 36, 36, 36) + cgia.plane0.bckgnd.color5 = 1 // hue 0, luma 1 → (duplicate) + cgia.plane0.bckgnd.color6 = 0 // hue 0, luma 0 → pure black ( 0, 0, 0) + cgia.plane0.bckgnd.color7 = 0 // hue 0, luma 0 → (duplicate) + // + // Half‑bright pairs for the above: + // index 4 ↔ index 196 (dark half of grey row) + // index 23 ↔ index 215 (dark half of orange row) + // index 2 ↔ index 194 (bright half of grey row) + // etc. + + // --- Point plane 0 to the display list --- + cgia.offset0 = dl.addr + + // --- Activate plane 0 as a background graphics plane (type = 0) --- + cgia.planes = 1 + + // --- Enable vertical‑blank NMI and reset frame counter --- + vblclock = 0 + cgia.int_enable = %10000000 // VBI flag set → NMI at each VBL +} + +//----------------------------------------------------------------------------- +// Vertical Blank Interrupt (NMI) +//----------------------------------------------------------------------------- +// Fires once per frame (60 Hz on NTSC‑like timing). Increments a 24‑bit +// software clock that can be used by the main loop for synchronising +// animations, delays, or timed events. +//----------------------------------------------------------------------------- + +interrupt asm void vbl() { +.vblclock: inc vblclock.b2 ; increment low byte + bne .tickend ; if it wrapped to zero… + inc vblclock.b1 ; carry into middle byte + bne .tickend + inc vblclock.b0 ; carry into high byte +.tickend: + stz cgia.int_status ; acknowledge the interrupt (write‑1‑to‑clear) + rti +} + +//----------------------------------------------------------------------------- +// Frame‑accurate delay helpers +//----------------------------------------------------------------------------- +// `pause()` waits exactly one frame (the VBL clock must advance). +// `wait(f)` waits `f` frames (0–255) and returns. +// Useful for simple synchronisation without a dedicated timer. +//----------------------------------------------------------------------------- + +asm void pause() { + lda vblclock.b2 ; remember low byte of frame counter +.rt_check: cmp vblclock.b2 ; spin until it changes (next VBL) + beq .rt_check + rts +} + +noinline asm void wait(byte register(a) f) { + clc + adc vblclock.b2 ; target = current low byte + f +.rt_check: cmp vblclock.b2 ; spin until low byte matches target + bne .rt_check + rts +} + +//----------------------------------------------------------------------------- +// Main program +//----------------------------------------------------------------------------- +// The CGIA runs autonomously from the display list; the CPU is free. +// The infinite loop here does nothing, but real programs would place +// game logic or animation updates inside it. +//----------------------------------------------------------------------------- + +void main() { + x65_init() + + while true { + // wait(20) would pause here for 20 frames, etc. + } +} diff --git a/examples/x65/plasma.mfk b/examples/x65/plasma.mfk new file mode 100644 index 00000000..9237c66c --- /dev/null +++ b/examples/x65/plasma.mfk @@ -0,0 +1,150 @@ +//----------------------------------------------------------------------------- +// Plasma effect for X65 https://x65.zone/ +//----------------------------------------------------------------------------- + +byte b @ 0 +word w @ 1 +pointer p @ 3 + +//----------------------------------------------------------------------------- + +const byte SCR_W = 48 +const byte SCR_H = 30 + +const word LMS = $2000 // Memory address for character data (first text row) +const word LFS = $2600 // Memory address for character color data (first text row) +const word LBS = $2c00 // Memory address for background color data (first text row) +const word LCG = $3800 // Character generator memory address (8x8 font) + +const array(byte) DL align(fast) = [ + $f3, // LMS + LFS + LBS + LCG + @word[LMS], + @word[LFS], + @word[LBS], + @word[LCG], + $a,$a,$a,$a,$a,$a,$a,$a,$a,$a, // MODE2 + $a,$a,$a,$a,$a,$a,$a,$a,$a,$a, // MODE2 + $a,$a,$a,$a,$a,$a,$a,$a,$a,$a, // MODE2 + $82,@word[DL.addr] // JMP to begin of DL and wait for Vertical BLank +] + +const array(byte) CHARSET @ LCG = [ + $00, $00, $00, $00, $00, $00, $00, $00, + $00, $00, $00, $10, $00, $00, $00, $00, + $00, $00, $18, $18, $00, $00, $00, $00, + $00, $00, $38, $38, $38, $00, $00, $00, + $00, $00, $3c, $3c, $3c, $3c, $00, $00, + $00, $7c, $7c, $7c, $7c, $7c, $00, $00, + $00, $7e, $7e, $7e, $7e, $7e, $7e, $00, + $fe, $fe, $fe, $fe, $fe, $fe, $fe, $00, + $00, $7f, $7f, $7f, $7f, $7f, $7f, $7f, + $00, $7e, $7e, $7e, $7e, $7e, $7e, $00, + $00, $7c, $7c, $7c, $7c, $7c, $00, $00, + $00, $00, $3c, $3c, $3c, $3c, $00, $00, + $00, $00, $38, $38, $38, $00, $00, $00, + $00, $00, $18, $18, $00, $00, $00, $00, + $00, $00, $00, $08, $00, $00, $00, $00, + $00, $00, $00, $00, $00, $00, $00, $00 +] + +const array SINUS_TABLE @ $4000 = [for i, 0, until, 256 [sin((i * 256) / 256, 100)]] + +//----------------------------------------------------------------------------- + +byte c1A = 1, c1B = 5 + +pointer screen @ $60 +pointer color @ $62 + +array(byte) lookupDiv16[256] @ $4100 +array(byte) xbuf[48] @ $4200 + +//----------------------------------------------------------------------------- + +macro asm void x65_init() { + sei ; disable IRQ + cld ; clear decimal mode + + ldx #$ff + txs ; initialize stack pointer + + lda #%10000000 + sta cgia.int_enable ; trigger NMI on VBL +} + +//----------------------------------------------------------------------------- + +macro void cgia_init() { + cgia.planes = 0 // disable all planes + + screen = LMS + // parallelto - risky thing + // paralleluntil - risky thing + for w, ($600 * 3) - 1, downto, 0 { screen[w] = 0 } // clear screen + + p = cgia.plane0.addr + for b, 9, downto, 0 { p[b] = 0 } // clear CGIA_PLANE0 registers + + cgia.plane0.bckgnd.row_height = 7 // 8 rows per character + cgia.offset0 = DL.addr // point plane0 to DL + + cgia.planes = 1 // activate plane0 +} + +//----------------------------------------------------------------------------- + +void plasma() { + byte _c1a, _c1b + byte i, ii, tmp + + screen = LMS + color = LFS + _c1a = c1A + _c1b = c1B + + for i, SCR_W - 1, downto, 0 { + xbuf[i] = SINUS_TABLE[_c1a] + SINUS_TABLE[_c1b] + _c1a += 3 + _c1b += 7 + } + + for ii, SCR_H - 1, downto, 0 { + tmp = SINUS_TABLE[_c1a] + SINUS_TABLE[_c1b] + + _c1a += 4 + _c1b += 9 + + for i, SCR_W - 1, downto, 0 { + screen[i] = lookupDiv16[xbuf[i] + tmp] + color[i] = screen[i] + } + + screen += SCR_W + color += SCR_W + } + + c1A += 3 + c1B -= 5 +} + +//----------------------------------------------------------------------------- + +interrupt asm void vbl() { + jsr plasma + stz cgia.int_status + rti +} + +//----------------------------------------------------------------------------- + +void main() { + x65_init() + cgia_init() + + // store values divided by 16 + for b:lookupDiv16 { lookupDiv16[b] = b >> 4 } + + while true {} +} + +//----------------------------------------------------------------------------- \ No newline at end of file diff --git a/examples/x65/template.mfk b/examples/x65/template.mfk new file mode 100644 index 00000000..d5aa0bc6 --- /dev/null +++ b/examples/x65/template.mfk @@ -0,0 +1,27 @@ +byte tmp0 @ 0 +byte tmp1 @ 1 + +macro asm void x65_init() { + sei ; disable IRQ + cld ; clear decimal mode + + ldx #$ff + txs ; initialize stack pointer + + lda #%10000000 + sta cgia.int_enable ; trigger NMI on VBL +} + +interrupt asm void vbl() { + inc tmp0 + stz cgia.int_status + rti +} + +void main() { + x65_init() + + while true { + tmp1 += 1 + } +} diff --git a/include/platform/x65.ini b/include/platform/x65.ini new file mode 100644 index 00000000..9397e93d --- /dev/null +++ b/include/platform/x65.ini @@ -0,0 +1,22 @@ +[compilation] +arch=w65c02 +encoding=petscii +screen_encoding=petscr +modules=x65_hardware,default_panic + +[allocation] +zp_bytes=$0-$ff +segment_default_start=$200 +segment_default_end=$ffff + +[define] +X65=1 +WIDESCREEN=1 +KEYBOARD=1 +JOYSTICKS=2 +HAS_BITMAP_MODE=1 + +[output] +style=single +format=$FF,$FF,startaddr,endaddr,allocated,$E0,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,addr:vbl,startaddr,$00,$00 +extension=xex diff --git a/include/x65_cgia.mfk b/include/x65_cgia.mfk new file mode 100644 index 00000000..07306239 --- /dev/null +++ b/include/x65_cgia.mfk @@ -0,0 +1,261 @@ +// X65 CGIA hardware +// https://docs.x65.zone/ +// https://docs.google.com/spreadsheets/d/1mADeuKo_zZCQmT42eyEhKunW5CIMZ9LZTW8EDv7rU7w/ +// https://github.com/X65/examples/blob/main/src/cgia.asm + +#if not(X65) +#warn x65_cgia module should be used only on X65 computer-compatible targets +#endif + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +const byte CGIA_MODE_HIRES_BIT = %00000001 +const byte CGIA_MODE_INTERLACE_BIT = %00000010 + +const byte CGIA_REG_INT_FLAG_VBI = %10000000 +const byte CGIA_REG_INT_FLAG_DLI = %01000000 +const byte CGIA_REG_INT_FLAG_RSI = %00100000 + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// Display List Values +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// bit 3 unset (0-7) - instructions: +// Bits 0-2 encode the instruction: +// 0 - empty lines filled with fill color +// bits 6-4 - how many +// bit 7 - DLI +// 1 - [TBD] +// 2 - JMP Display List (Load DL offset address) +// - DLI bit set - wait for Vertical Blank +// 3 - Load Memory - bits 4-7 flag which offsets will follow +// 4 - LMS - memory scan +// 5 - LFS - color scan +// 6 - LBS - background scan +// 7 - LCG - character generator address +// 4 - Load 8 bit value to Register Offset +// 5 - Load 16 bit value to Register Offset +// bits 6-4 - register index +// 6 - [TBD] +// 7 - [TBD] +// +// bit 3 set (8-F) - generate mode row: +// Bits 0-2 encode the mode: +// 0 - palette text/tile mode +// 1 - palette bitmap mode +// 2 - attribute text/tile mode +// 3 - attribute bitmap mode +// 4 - [TBD] +// 5 - [TBD] +// 6 - Hold-and-Modify (HAM) mode +// 7 - affine transform chunky pixel mode +// +// bit 7 - trigger DLI - Display List Interrupt +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +// --- DISPLAY LIST INSTRUCTIONS --- +const byte DL_INS_EMPTY_LINES = $00 +const byte DL_INS_RESERVED_1 = $01 +const byte DL_INS_JUMP = $02 +const byte DL_INS_DL_INTERRUPT = %10000000 +const byte DL_INS_LOAD_MEMORY = $03 +const byte DL_INS_LM_MEMORY_SCAN = %00010000 +const byte DL_INS_LM_FOREGROUND_SCAN = %00100000 +const byte DL_INS_LM_BACKGROUND_SCAN = %01000000 +const byte DL_INS_LM_CHARACTER_GENERATOR = %10000000 +const byte DL_INS_LOAD_REG8 = $04 +const byte DL_INS_LOAD_REG16 = $05 +const byte DL_INS_RESERVED_6 = $06 +const byte DL_INS_RESERVED_7 = $07 + +const byte DL_MODE_PALETTE_TEXT = $08 +const byte DL_MODE_PALETTE_BITMAP = $09 +const byte DL_MODE_ATTRIBUTE_TEXT = $0A +const byte DL_MODE_ATTRIBUTE_BITMAP = $0B +const byte DL_MODE_HOLD_AND_MODIFY = $0E +const byte DL_MODE_AFFINE_TRANSFORM = $0F + +const byte DL_MODE_BIT = %00001000 +const byte DL_DOUBLE_WIDTH_BIT = %00010000 +const byte DL_MULTICOLOR_BIT = %00100000 +const byte DL_RESERVED_BIT = %01000000 +const byte DL_DLI_BIT = %10000000 + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// Multicolor encoding +// https://csbruce.com/cbm/hacking/hacking12.txt +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// 00 background color +// 01 same as "off" color in hires mode +// 10 same as "on" color in hires mode +// 11 another "background" color +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// plane flags: +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// 0 - color 0 is transparent +// 1-2 - [RESERVED] +// 3 - border is transparent +// 4 - double-width pixel +// 5 - multicolor-pixel +// 6,7 - pixel bits: 00 - 1bit, 2 colors; 01 - 2bit, 4 colors; +// 10 - 3bit, 8 colors; 11 - 4bit, 8 colors + half-bright +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +const byte PLANE_MASK_TRANSPARENT = %00000001 +const byte PLANE_MASK_BORDER_TRANSPARENT = %00001000 +const byte PLANE_MASK_DOUBLE_WIDTH = %00010000 +const byte PLANE_MASK_MULTICOLOR = %00100000 +const byte PLANE_MASK_PIXEL_BITS = %11000000 + +const byte PLANE_MASK_FROM_DL = PLANE_MASK_DOUBLE_WIDTH | PLANE_MASK_MULTICOLOR + +const byte PLANE_BITS_1BPP = (%00 << 6) +const byte PLANE_BITS_2BPP = (%01 << 6) +const byte PLANE_BITS_3BPP = (%10 << 6) +const byte PLANE_BITS_4BPP = (%11 << 6) + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// sprite flags: +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// 0-2 - width in bytes +// 3 - [RESERVED] +// 4 - double-width +// 5 - multicolor +// 6 - mirror X +// 7 - mirror Y +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +const byte SPRITE_MASK_WIDTH = %00000111 +const byte SPRITE_MASK_RESERVED = %00001000 +const byte SPRITE_MASK_DOUBLE_WIDTH = %00010000 +const byte SPRITE_MASK_MULTICOLOR = %00100000 +const byte SPRITE_MASK_MIRROR_X = %01000000 +const byte SPRITE_MASK_MIRROR_Y = %10000000 + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +struct cgia_bckgnd_regs { + byte flags + byte border_columns + byte row_height + byte stride + byte scroll_x + byte offset_x + byte scroll_y + byte offset_y + byte shared_color0 + byte shared_color1 + byte color2 + byte color3 + byte color4 + byte color5 + byte color6 + byte color7 +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// Hold-And-Modify MODE6 +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// HAM commands are 6bit each, 4 screen pixels packed in 3 bytes. +// [CCCDDD] - C -command bit, D - data bit +// +// 000 - load base color index at DDD (one of 8 base colors) +// 001 - blend current color with color at DDD +// +// CCS - CC: +// 01 - Modify Red channel +// 10 - Modify Green channel +// 11 - Modify Blue channel +// +// S: sign, 0 +delta, 1 -delta +// DDD: delta (2's complement, with above sign bit) +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +struct cgia_ham_regs { + byte flags + byte border_columns + byte row_height + array(byte) reserved[5] + byte color0 + byte color1 + byte color2 + byte color3 + byte color4 + byte color5 + byte color6 + byte color7 +} + +struct cgia_affine_regs { + byte flags + byte border_columns + byte row_height + byte texture_bits // 2-0 texture_width_bits, 6-4 texture_height_bits + word u + word v + word du + word dv + word dx + word dy +} + +struct cgia_sprite_regs { + byte active // bitmask for active sprites + byte border_columns + byte start_y + byte stop_y + array(byte) reserved[12] +} + + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// 16 registers for plane use +// interpretation depends on plane type and active display list mode +// BACKGROUND | HAM | AFFINE | SPRITES +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +union plane { + cgia_bckgnd_regs bckgnd + cgia_ham_regs ham + cgia_affine_regs affine + cgia_sprite_regs sprite +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// CGIA registers +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +struct cgia_regs { + byte mode // Chip mode | currently reserved for future use - must be 0 + byte bckgnd_bank // Background Layers bank no | third byte (23-16 bits) of generated address + byte sprite_bank // Sprites bank no | third byte (23-16 bits) of generated address + array(byte) ctl_reserved[13] + word raster // Raster Counter + byte rst_status // Raster Status Bits | [EVEN/ODD x x x x x x x] + array(byte) rst_reserved1[5] + word int_raster // Interrupt register | Write to set raster line generating RSI + byte int_enable // Interrupt enabled flags | [VBI DLI RSI x x x x x] + byte int_status // Interrupt status | [VBI DLI RSI x x x x x] + array(byte) rst_reserved2[4] + array(byte) reserved[16] + byte planes // Plane enable and type | [TTTTEEEE] EEEE - enable bits, TTTT - type (0 bckgnd, 1 sprite) + byte order // Plane order | plane order permutation - SJT ordering + array(byte) pln_reserved1[2] + byte back_color // Backdrop/Border color | palette index + array(byte) pln_reserved2[3] + word offset0 // PLANE0 | DisplayList or SpriteDescriptor table start, interpretation depends on plane type in PLANES register + word offset1 // PLANE1 | DisplayList or SpriteDescriptor table start, interpretation depends on plane type in PLANES register + word offset2 // PLANE2 | DisplayList or SpriteDescriptor table start, interpretation depends on plane type in PLANES register + word offset3 // PLANE3 | DisplayList or SpriteDescriptor table start, interpretation depends on plane type in PLANES register + plane plane0 + plane plane1 + plane plane2 + plane plane3 +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +volatile cgia_regs cgia @ $FF00 + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ \ No newline at end of file diff --git a/include/x65_hardware.mfk b/include/x65_hardware.mfk new file mode 100644 index 00000000..fc1c7c8e --- /dev/null +++ b/include/x65_hardware.mfk @@ -0,0 +1,6 @@ +#if not(X65) +#warn x65_hardware module should be used only on X65 computer-compatible targets +#endif + +import x65_cgia +import x65_ria diff --git a/include/x65_ria.mfk b/include/x65_ria.mfk new file mode 100644 index 00000000..4c9ccb59 --- /dev/null +++ b/include/x65_ria.mfk @@ -0,0 +1,7 @@ +// X65 RIA hardware + +#if not(X65) +#warn x65_ria module should be used only on X65 computer-compatible targets +#endif + +volatile byte gpio_in0 @$FF80 \ No newline at end of file diff --git a/src/main/scala/millfork/output/OutputPackager.scala b/src/main/scala/millfork/output/OutputPackager.scala index dc113a3f..1b20a162 100644 --- a/src/main/scala/millfork/output/OutputPackager.scala +++ b/src/main/scala/millfork/output/OutputPackager.scala @@ -137,7 +137,7 @@ case class SymbolAddressOutput(symbol: String, bonus: Int) extends OutputPackage def packageOutput(flc: FileLayoutCollector, mem: CompiledMemory, bank: String): Array[Byte] = { val b = mem.banks(bank) val x = mem.getAddress(symbol) + bonus - Array(b.end.toByte, b.end.>>(8).toByte) + Array(x.toByte, x.>>(8).toByte) } }