diff --git a/css/ui.css b/css/ui.css index d39045d5..ba616400 100644 --- a/css/ui.css +++ b/css/ui.css @@ -479,6 +479,84 @@ div.markdown td { div.markdown th { padding:0.2em; } +/* help window (#help/): Markdown docs shipped with the IDE */ +.help-view { + background-color:#333; + color:#ddd; + padding:1.25em 1.5em; + font-size:1em; + line-height:1.5; + min-height:100%; + box-sizing:border-box; + font-family:'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif; +} +.help-view h1 { + color:#fff; + font-size:1.5em; + margin:0 0 0.6em 0; +} +.help-view h2 { + color:#7ec7ff; + font-size:1.25em; + margin:1.4em 0 0.5em 0; + padding-bottom:0.2em; + border-bottom:1px solid #444; +} +.help-view h3 { + color:#9fd0ff; + font-size:1.05em; + margin:1.1em 0 0.4em 0; +} +.help-view a { + color:#7ea6ff; +} +.help-view a:hover { + color:#a9c6ff; +} +.help-view code { + background:#1d1d1d; + color:#ffd479; + padding:0.1em 0.3em; + border-radius:3px; + font-size:0.9em; +} +.help-view pre { + background:#1d1d1d; + padding:0.75em 1em; + border-radius:4px; + overflow-x:auto; +} +.help-view pre code { + background:none; + padding:0; + color:#e8e8e8; +} +.help-view table { + border-collapse:collapse; + margin:1em 0; +} +.help-view th, .help-view td { + border:1px solid #444; + padding:0.3em 0.6em; + text-align:left; +} +.help-view th { + background:#383838; +} +.help-view blockquote { + border-left:3px solid #555; + margin:1em 0; + padding:0.2em 1em; + color:#bbb; +} +.help-view hr { + border:none; + border-top:1px solid #444; + margin:1.5em 0; +} +.help-view img { + max-width:100%; +} #sidebar { overflow:auto; } diff --git a/doc/asset-editor.md b/doc/asset-editor.md deleted file mode 100644 index 3717b2be..00000000 --- a/doc/asset-editor.md +++ /dev/null @@ -1,414 +0,0 @@ -# Asset Editor Graphics Format - -The 8bitworkshop IDE scans your source files for **asset headers** — small JSON -descriptors in comments — and turns the data block that follows into an editable -bitmap, tilemap, or palette. Edits are written straight back into the source text, -preserving the original number base and formatting of each literal. - -Source of truth: - -| What | Where | -| --- | --- | -| Header scanning, decode/encode, palettes | `src/ide/pixeleditor.ts` | -| Editor pipeline & view construction | `src/ide/views/asseteditor.ts` | -| In-app reference dialog (Help ▸ Asset Editor) | `openAssetEditorHelp()` in `src/ide/ui.ts` | - ---- - -## 1. Header syntax - -A header is a comment containing a single JSON object, immediately followed by a -data block. The data block ends at the next closing delimiter: - -| Language | Header | Data block ends at | -| --- | --- | --- | -| C / C++ | `/*{w:8,h:8}*/` | next `;` | -| Assembly | `;;{w:8,h:8};;` | next `;;` | -| Verilog | `/*{w:8,h:8}*/` | next `end` | - -The Verilog terminator is selected by platform (`platform_id.includes('verilog')`), -not by file extension. - -**Literals must carry a radix prefix.** Recognized: `0xNN`, `$NN`, `#$NN`, `%0101`, -`0b0101`, `8'hNN`, `8'b0101`, and assembler `hex 1f20…` statements. Each is -rewritten in its original notation when you edit, so a file of `$`-prefixed bytes -stays `$`-prefixed. **Plain decimal is not matched at all** — a block of -`{24,60,126,…}` parses as zero values and reports *"Expected 8 value(s), found 0"*. - -```c -/*{w:8,h:8,bpp:1,count:2,brev:1}*/ -const byte tiles[] = { - 0xff,0x81,0x81,0x81,0x81,0x81,0x81,0xff, - 0x18,0x3c,0x7e,0xff,0xff,0x7e,0x3c,0x18, -}; -``` - -```asm -;;{w:8,h:8,count:1,brev:1,flip:1};; -PlayerGfx: - .byte $18,$3c,$7e,$ff,$ff,$7e,$3c,$18 -;; -``` - -### JSON leniency - -Keys are unquoted — the scanner rewrites `([A-Za-z]+):` to `"$1":` before calling -`JSON.parse`. String *values* still need quotes: `pal:"nes"`, `comp:"rletag"`. - -### Gotchas - -* **The first `;` wins.** In C the block ends at the very next semicolon, so the - array declaration must be the only statement between the header and that `;`. -* Keys are case-sensitive and must be alphabetic. -* A malformed header is reported inline in the Asset Editor tab rather than - silently ignored. - -### External data with `#embed` - -If the data block contains a C23 `#embed` directive, the bytes are read from that -file instead of from the source text: - -```c -/*{w:24,h:21,bpp:1,brev:1,wpimg:64,count:1}*/ -const char sprite[] = { -#embed "sprite.bin" -}; -``` - -The path is resolved first as given, then relative to the including file's -directory (`sub/main.c` + `data.bin` → `sub/data.bin`). The byte length of the -file is validated against the format the same way inline data is. - ---- - -## 2. The address model - -Everything except the special modes in §6 is described by one addressing formula. -Understanding it is the difference between guessing at fields and deriving them. - -``` -bpw = bpw ?? 8 // bits per word -sl = sl ?? ceil(w * bpp / bpw) // words per scanline -wpimg = wpimg ?? sl * h // words per image -pofs = pofs ?? sl * h * count // distance between bitplanes -rowstride = sl // (see il: below) - -for each image n, for each row y: - yp = flip ? h-1-y : y - ofs0 = n*wpimg + yp*rowstride - shift = 0 - for each column x: - ofs = remap(ofs0) // bit permutation, §4 - for each plane p: - word = data[ofs + p*pofs + skip] - bits = brev ? word >> (bpw-shift-bpp) : word >> shift - color |= (bits & ((1<= bpw: // move to next word in the row - ofs0 += 1 - shift = 0 -``` - -Four consequences worth internalizing: - -1. **`remap` is applied to `ofs0` inside the `x` loop**, after the byte-advance. - It therefore sees the byte index *within* a row as well as the row and image - index — which is exactly what lets you describe multi-tile-wide sprites (§4). -2. **`skip` is added last**, after `remap` and after the plane offset. It offsets - the whole block, not each image. -3. **`pofs` defaults to "one plane block after another"** (`sl*h*count`). Set it - explicitly for anything interleaved. -4. Total colors per pixel = `1 << (bpp * np)`. - -### Bit order - -`brev:1` means **MSB is the leftmost pixel** — the usual arrangement for NES, -Game Boy, TMS9918, VCS, and most tile-based hardware. Without it, LSB is leftmost -(Apple II HGR, some bitmap formats). - -### Planes vs. bits-per-pixel - -Two ways to get more than two colors, and most formats use one or the other: - -* **`bpp:N`** — the bits of one pixel are adjacent within a word. `bpp:4` packs - two 16-color pixels per byte. -* **`np:N`** — the bits of one pixel live in N separate words, `pofs` apart. - Plane `p` supplies bits `p*bpp`. - -Game Boy 2bpp is `bpp:1,np:2,pofs:1,sl:2` — planes interleaved every other byte. -NES 2bpp is `bpp:1,np:2,pofs:8` — plane 0 in bytes 0–7, plane 1 in bytes 8–15. - -### `il` — row interleaving - -`il:1` stores all `count` images as one wide block, row by row: -`wpimg` becomes `sl`, and `rowstride` becomes `sl*count`. Useful for character -sets stored as a single wide strip. - ---- - -## 3. Field reference - -### Image fields - -| Field | Default | Description | -| --- | --- | --- | -| `w` | *required* | Width in pixels | -| `h` | *required* | Height in pixels | -| `count` | `1` | Number of images | -| `bpp` | `1` | Bits per pixel | -| `np` | `1` | Number of bitplanes (colors = 2bpp×np) | -| `bpw` | `8` | Bits per word (8, 16, 32) | -| `sl` | `ceil(w*bpp/bpw)` | Words per scanline (stride) | -| `wpimg` | `sl*h` | Words per image — use when hardware pads images | -| `pofs` | `sl*h*count` | Distance between bitplanes, in words | -| `skip` | `0` | Words to skip at the start of the whole block | -| `brev` | `false` | MSB is the leftmost pixel | -| `flip` | `false` | Data is stored bottom row first | -| `il` | `false` | Interleave images row by row | -| `remap` | — | Bit permutation applied to the word offset (§4) | -| `reindex` | — | Per-column word/bit table (§5) | -| `aspect` | `1` | Pixel aspect ratio for display only | -| `art` | `false` | Apple II HGR artifact-color mode (§6) | -| `comp` | — | `"rletag"` = RLE-compressed block (§6) | -| `map` | — | `"nesnt"` = NES nametable, not a bitmap (§6) | -| `pacstrip` | — | Pac-Man/Namco vertical-strip layout (§6) | -| `xform` | — | CSS transform. Parsed and stored, but currently inert: the editor overwrites it with `scale(2)` and the line that would apply it is commented out | - -### Palette fields - -A block with `pal` and no `w`/`h` becomes a palette editor. - -| Field | Default | Description | -| --- | --- | --- | -| `pal` | — | Palette decoding, see below | -| `n` | — | Advisory entry count — the real count comes from the data | -| `layout` | — | Grouped editor layout: `nes`, `astrocade`, `pacman` | - -`pal` is either: - -* **A number** like `332` or `444` — the three digits are channel bit widths, - applied **from the low bits upward**. A positive value puts red lowest - (`pal:332` = RGB332, red in bits 0–2); a **negative** value puts blue lowest - (`pal:-332` = blue in bits 0–2, red in the top 2 bits). Note the picker's color - space is `1 << (sum of digits)` entries, which is why `pal:444` is a 4096-color - chooser regardless of how many entries the block holds. -* **A name**: `"nes"`, `"vcs"`, `"c64"`, `"gb"`, `"ap2lores"`, `"astrocade"`, or - `"pacman"` (Namco color-PROM bytes, decoded with MAME's resistor weights). - These are the only recognized names — an unknown one throws - `No palette named X`, which the Asset Editor reports in place of *every* asset - in that file. (The `pal:"pce"` blocks in `presets/pce/*_gfx.h` currently hit - this; there is no `pce` entry in `PREDEF_PALETTES`.) - -Bitmaps pick up their colors from palette blocks **anywhere in the open project**, -matched by entry count — a 4-color bitmap offers every 4-entry palette (and every -4-entry slice named by a `layout`) in a dropdown. Game Boy projects fall back to -the DMG green scale when no palette block matches. - -```c -/*{pal:"nes",layout:"nes"}*/ -const char PALETTE[8] = { 0x0F, 0x11,0x24,0x3C, 0x00, 0x01,0x15,0x25 }; -``` - -`bpw` applies to palette blocks too — PC Engine palettes are 16-bit words, so -their blocks carry `bpw:16` for correct parsing and write-back. - ---- - -## 4. `remap` — describing hardware tile layouts - -`remap` permutes the **bits of the word offset**. Entry `i` of the array says -which destination bit source bit `i` becomes: - -``` -remap(ofs) = OR over i of: bit i of ofs → bit remap[i] of result -``` - -A **negative** entry `-n` maps source bit `i` to destination bit `n-1` **and -inverts it** — use it when the hardware stores the halves in the opposite order. - -This exists because the natural row-major offset (`n*wpimg + y*sl + xbyte`) rarely -matches how hardware stores multi-tile sprites. Writing the two offsets side by -side in binary tells you the array. - -### Worked example: NES 16×16 metasprite - -From `presets/nes/shoot2.c`: - -```c -/*{w:16,h:16,bpp:1,count:16,brev:1,np:2,pofs:8,remap:[5,0,1,2,4,6,7,8,9,10,11,12]}*/ -``` - -`sl` = 2, `wpimg` = 32, so the natural offset is `32n + 2y + xb`: - -| Source bit | Meaning | → | Dest bit | Contributes | -| --- | --- | --- | --- | --- | -| 0 | `xb` (left/right byte) | → | 5 | 32 | -| 1,2,3 | `y0,y1,y2` | → | 0,1,2 | `y & 7` | -| 4 | `y3` (top/bottom half) | → | 4 | 16 | -| 5+ | image index `n` | → | 6+ | 64·n | - -Physical offset = `64n + 32·xb + 16·y3 + (y&7)`, plus `p*8` for the plane. That is -exactly NES CHR: four 16-byte tiles per metasprite in 8×16 sprite order — -upper-left, lower-left, upper-right, lower-right. - -### Worked example: Game Boy 16×16 metasprite - -From `presets/gb/pakupaku.c`: - -```c -/*{w:16,h:16,bpp:1,count:19,brev:1,np:2,pofs:1,sl:2,wpimg:64,remap:[5,1,2,3,4,0,6,7,8,9,10,11,12]}*/ -``` - -Game Boy interleaves its planes (`pofs:1`), so `sl:2` is already consumed by the -two planes of a single 8-pixel row, and the natural offset `64n + 2y + xb` puts -`xb` in bit 0 and `y` in bits 1–4. Bit 5 is never set. The remap sends bit 0 (the -8×16 column) up to bit 5 and folds the unused bit 5 down to 0, producing -`64n + 32·xb + 16·y3 + 2·(y&7)` — the L-top, L-bot, R-top, R-bot tile order the -game's OAM code uses. - -### `remap` must be a bijection - -The size check computes the required data length as -`max over n,i of remap(n*wpimg + i)` for `i` in `[0, wpimg)`, plus one. If your -remap isn't a permutation of that range, the maximum comes out wrong and you get a -spurious *"Expected N values, found M"* error. This is why the Game Boy example -above bothers to map the unused bit 5 down to bit 0 instead of leaving it alone — -the mapping is never exercised during decoding, but it keeps the range exact. - -### Other patterns in the tree - -| Format | Meaning | -| --- | --- | -| `remap:[0,1,2,4,5,6,7,8,9,10,11,12]` | NES 8×8: doubles the image stride to 16 bytes so the two 8-byte planes fit | -| `remap:[4,0,1,2,3,5,6,7,8,9]` | TMS9918 16×16 sprite: UL, LL, UR, LR (ColecoVision, MSX) | -| `remap:[-5,0,1,2,3,5,6,7,8,9]` | Same, with the halves stored right-first | -| `remap:[3,0,1,2,4,5,6,7,8,9,10]` | Galaxian/Scramble 16×16 sprite | - ---- - -## 5. `reindex` - -`reindex` replaces the shift/advance logic entirely with a per-column table. For -column `x`, `reindex[x % len]` is a **bit index within the row**: `>> 3` gives the -word offset added to the row base, `& 7` gives the bit position (still subject to -`brev`). Entry `15` therefore means word 1, bit 7. Because it drives the offset -directly, the automatic `ofs0 += 1` advance is disabled. - -It exists for hardware where pixel order within a row is not linear — notably VCS -playfields, where the three playfield registers are not all in the same bit order: - -```asm -;;{w:20,h:10,flip:1,reindex:[4,5,6,7,15,14,13,12,11,10,9,8,16,17,18,19]};; -``` - ---- - -## 6. Special modes - -### `comp:"rletag"` - -The block is RLE-compressed: the first byte is the tag, a literal byte sets the -current value, and a tag byte is followed by a repeat count (count 0 = end). -Decompression happens before decoding; the editor shows the expanded image. - -### `map:"nesnt"` - -The block is an NES nametable, not a bitmap. `w`/`h` are in **tiles** (default -32×30) and the block is rendered as a tilemap using the project's CHR data, with a -map editor instead of a pixel editor. Usually paired with `comp:"rletag"`: - -```c -/*{w:32,h:30,bpp:8,comp:"rletag",map:"nesnt"}*/ -``` - -### `pacstrip:1` - -Pac-Man / Namco arcade layout: 8-byte vertical strips of 4 rows × 8 columns, -2bpp packed as bit `y` plus bit `y+4`, X/Y mirrored within each strip. Tiles (8×8) -use 2 strips; sprites (16×16) use 8 strips in hardware order. The mode implies -4 colors regardless of `bpp`. - -```c -/*{w:8,h:8,count:256,bpp:2,pacstrip:1}*/ -/*{w:16,h:16,count:64,bpp:2,pacstrip:1}*/ -``` - -The strip tables must stay in sync with `PacmanVideo` in `src/machine/pacman.ts`. - -### `art:1` - -Apple II HGR artifact color: bit 7 of each byte selects the color set, and the -editor shows a per-group palette toggle. The artifact bit is bit 0 when `brev` is -set, bit 7 otherwise. No preset currently uses it — the Apple II presets declare -plain `bpp:1` bitmaps. - ---- - -## 7. Validation - -Before creating an editor, the number of parsed words (= bytes when `bpw` is 8) is -checked against the format: - -``` -required = max(remap(n*wpimg + i)) + planeExtent + 1 + skip -planeExtent = (np-1)*pofs, or 0 if that is smaller than wpimg -``` - -The `planeExtent` rule distinguishes the two plane layouts: when planes are -interleaved (`pofs < wpimg`, Game Boy/SMS) the plane bytes are already inside each -image block; when planes are stored as separate blocks (`pofs >= wpimg`, NES) the -extra plane is counted at the end. - -A mismatch shows as **"Expected N value(s), found M"** in the Asset Editor tab. -When that happens, check in this order: - -1. **`found 0`** — the literals have no radix prefix (§1), or the data block ended - early at a stray `;`. -2. `count` — the most common culprit otherwise. -3. `wpimg` — hardware often pads (C64 sprites are 24×21 bits in 64 bytes). -4. `remap` — is it a bijection over `[0, wpimg)`? (§4) -5. `pofs` — interleaved planes need it set explicitly. - ---- - -## 8. Cookbook - -Real formats from the preset tree: - -| Platform | Header | -| --- | --- | -| NES 8×8 CHR | `/*{w:8,h:8,bpp:1,count:256,brev:1,np:2,pofs:8,remap:[0,1,2,4,5,6,7,8,9,10,11,12]}*/` | -| NES 16×16 metasprite | `/*{w:16,h:16,bpp:1,count:16,brev:1,np:2,pofs:8,remap:[5,0,1,2,4,6,7,8,9,10,11,12]}*/` | -| NES palette | `/*{pal:"nes",layout:"nes"}*/` | -| NES nametable | `/*{w:32,h:30,bpp:8,comp:"rletag",map:"nesnt"}*/` | -| Game Boy 8×8 tile | `/*{w:8,h:8,bpp:1,count:4,brev:1,np:2,pofs:1,sl:2}*/` | -| Game Boy 16×16 metasprite | `/*{w:16,h:16,bpp:1,count:19,brev:1,np:2,pofs:1,sl:2,wpimg:64,remap:[5,1,2,3,4,0,6,7,8,9,10,11,12]}*/` | -| ColecoVision / MSX sprite | `/*{w:16,h:16,brev:1,remap:[4,0,1,2,3,5,6,7,8,9],count:2}*/` | -| ColecoVision layered 2-plane sprite | `/*{w:16,h:16,remap:[-5,0,1,2,3,5,6,7,8,9],count:5,np:2}*/` | -| C64 hires sprite | `/*{w:24,h:21,bpp:1,brev:1,wpimg:64,aspect:1,count:3}*/` | -| C64 multicolor sprite | `/*{w:12,h:21,bpp:2,brev:1,wpimg:64,count:4,aspect:2}*/` | -| VCS sprite | `;;{w:8,h:16,brev:1,flip:1};;` | -| VCS palette | `;;{pal:"vcs"};;` | -| Galaxian/Scramble tile ROM | `/*{w:16,h:16,remap:[3,0,1,2,4,5,6,7,8,9,10],brev:1,np:2,pofs:2048,count:64}*/` | -| Pac-Man tiles / sprites | `/*{w:8,h:8,count:256,bpp:2,pacstrip:1}*/` · `/*{w:16,h:16,count:64,bpp:2,pacstrip:1}*/` | -| Pac-Man color PROM | `/*{pal:"pacman",n:32}*/` | -| Astrocade | `/*{w:16,h:16,bpp:2,brev:1}*/` · `/*{pal:"astrocade",layout:"astrocade"}*/` | -| Williams | `/*{w:16,h:16,bpp:4,brev:1}*/` | -| PC Engine 16×16 sprite | `/*{w:16,h:16,bpp:1,count:3,brev:1,np:4,pofs:16,sl:1,bpw:16,wpimg:64}*/` | -| Apple II HGR | `/*{w:8,h:8,bpp:1,count:96}*/` (LSB-first, no `brev`) | -| Verilog 16-bit words | `/*{w:16,h:16,bpw:16,count:5}*/` | - ---- - -## 9. Deriving a new format - -1. Write down how the hardware stores one image: bytes per row, where the second - plane lives, whether rows go top-down. -2. Set `w`, `h`, `bpp`, `np`, `brev`, `flip` from that. -3. Compute the natural offset `n*wpimg + y*sl + xbyte` and the physical offset the - hardware actually uses. If they differ, write both in binary and read off - `remap` — source bit position on the left, destination on the right. -4. Confirm `count * wpimg` equals the real byte count; adjust `wpimg` if the - hardware pads. -5. Open the Asset Editor tab. Wrong `brev` looks mirrored; wrong `remap` looks - like scrambled or swapped quadrants; wrong `pofs` shows one plane as noise. diff --git a/doc/compilers.txt b/doc/compilers.txt deleted file mode 100644 index 9a5b25a5..00000000 --- a/doc/compilers.txt +++ /dev/null @@ -1,91 +0,0 @@ - -C COMPILERS - -cc65 - A fairly mature C compiler + toolchain for the 6502 family with lots of target -platforms and an extensive library. The code generation is -not great, using the AX register pair and a parameter stack for local variables that has to be -indirect-indexed (you can make locals static with an option, but I found -this introduced compiler bugs.) - -LCC - A small retargetable C compiler released in 1994, most notoriously -used to compile bytecode for the QuakeC language. It doesn't generate 6502 -code, but it probably could -- although optimizations are weak. -There's an older port to the 65C816. - -ACK - This ancient compiler used for Minix has a lot of frontends and backends, and is designed -for small-register CISC CPUs. There's a 6500 backend but I couldn't get it -working. Looks like it could be resurrected though. There's an intermediate -file for each step of the pipeline. - -PCC - An early C compiler from the days of UNIX, now updated to C99. -There's a 16-bit target (x86) and the backends looks pretty simple. -http://pcc.ludd.ltu.se/internals/pass2/ - -SDCC - A mature compiler that supports lots of microcontrollers including -the Z80. This is probably the best candidate for porting to 6502, -but the backends are bespoke works of evil genius and there's precious little documentation. -(I wonder if you could post-process the HC08 output to 6502, since they're similar in some ways.) - -LLVM-6502 - Someone uploaded this to Github. I got it to build, but the code -generated wasn't comprehensible. - -osXdk - A fork of the Oric SDK. The C compiler has a weird design which -emits bytecode and then uses a C preprocessor to expand the macros -into 6502 assembler. - -gcc6809 - need to check this out - -SmallerC - https://github.com/alexfru/SmallerC - -vbcc - Targets 68K, patch for z-machine -http://sun.hasenbraten.de/vbcc/ - - -OTHER COMPILERS - -PLASMA - Language + VM, sort of like SWEET16 meets -Java. Simple, concise code base. Pretty fast too, in the same ballpark as -CC65. Focused on the Apple I/II/III family. - -Atalan - Rule-based language/assembler with support for multiple 8-bit -platforms. Looks promising, but I had problems with it crashing, and it -lacks documentation/test cases. Atari-focused. - -Macross/Slinky - A macro cross-assembler developed by Chip Morningstar at -Lucasfilm Ltd. for use in the game Habitat. Billed as "An assembler for -people who hate assembly language." - -NESHLA - A high-level assembler in the same spirit as Macross, but with -NES-specific features. - -WLA-DX - Some other weird cross-assembler with lots of CPU targets. - -z88dk - A Small C-derived cross-compiler for the Z80, supporting 20 target -machines. - -https://github.com/EtchedPixels/FUZIX/wiki - -Java Grinder - Compile Java bytecode to microcontroller assembly. Currently -supporting MSP430, dsPIC, 6502/6510, 68000, MIPS, TMS9900, and Z80 with -platforms that include Commodore 64, Sega Genesis, Atari 2600, Apple IIgs, -ChipKit (PIC32), TI99, TI84 and more. - -ZX Basic - This cross-compiler is focused on ZX-Spectrum, but with some -patchworking it can be used to compile to whatever machine running on z80 -architecture. (Python) - -IntyBASIC: a BASIC compiler for Intellivision. IntyBASIC is a cross -compiler that takes BASIC source code and translates it to CP1610 assembler -code. http://nanochess.org/intybasic.html - -SWEET16 - Woz's tiny bytecode interpreter on the Apple ][ integer BASIC ROM. -Still emcumbered by Apple's copyright for the foreseeable future. -http://6502.org/source/interpreters/sweet16.htm - -Quetzalcoatl - http://www.kdef.com/geek/vic/quetz.html - -UC65 - https://code.google.com/archive/p/uc65/ - -Cowgol - https://github.com/davidgiven/cowgol - -AcheronVM - https://github.com/AcheronVM/acheronvm diff --git a/doc/ecsroot/kernel.txt b/doc/ecsroot/kernel.txt deleted file mode 100644 index f5696d2a..00000000 --- a/doc/ecsroot/kernel.txt +++ /dev/null @@ -1,90 +0,0 @@ -# Kernel -# A Kernel draws a set of scanlines to the screen. - -component Kernel -lines: 0..255 "Height of region in scanlines" -bgcolor: 0..255 "Background color" -end - -system SimpleKernel -tempbytes 8 -on preframe do once [Kernel] --- - lda #192 ; TODO: numlines - sec - sbc ypos_ypos_b0+ent - sta {{$5}}+ofs - - ldy hasbitmap_bitmap_b0+ent - lda bitmap_bitmapdata_b0,y - sec - sbc {{$5}}+ofs - sta {{$0}}+ofs - lda bitmap_bitmapdata_b8,y - sbc #0 - sta {{$1}}+ofs - - ldy hascolormap_colormap_b0+ent - lda colormap_colormapdata_b0,y - sec - sbc {{$5}}+ofs - sta {{$2}}+ofs - lda colormap_colormapdata_b8,y - sbc #0 - sta {{$3}}+ofs - - lda sprite_height_b0+ent - sta {{$4}}+ofs - lda ypos_ypos_b0+ent - sta {{$5}}+ofs ---- -on preframe do once [Sprite,HasBitmap,HasColormap,HasYpos] -- - {{@KernelSetup}} 0,0 - {{@KernelSetup}} 1,6 --- -on kernel do once [Kernel]: - lda %{1 file with same name? (local/nonlocal/directory) - - what if .c and .s names collide? - - maybe put stuff in examples/ dir? - - error msg when #link doesn't work - - warning when ROM too big - - detect "libcv.h" and include library automagically? -- sdcc: - - can't link asm files before c files (e.g. acheader.s must be last) - - figure out area names ordering - - debug inline asm -- live coding URL -- resize memory browser, other windows when vertical div resize -- markdown, verilog: can't share -- https://www.crowdsupply.com/tinyfpga/tinyfpga-bx -- stego shareable images (http://pico-8.wikia.com/wiki/P8PNGFileFormat) -- https://makecode.com/language? -- open ROM from URL? -- game starts even if switched away before first load -- put globals into view/controller objects -- autostart audio - - chrome: https://github.com/processing/p5.js-sound/issues/249 - - firefox: https://support.mozilla.org/en-US/kb/block-autoplay -- touch support -- better undo/diff for mistakes? -- ide bug/feature visualizer for sponsors -- global undo/redo at checkpoints (when rom changes) -- landscape mode for arcade ports -- symmetric load/save state types (like in Machine) -- pixel editor - - persist palette/tilemap selections - - more tools for editing - - map editor - - arbitrary nametable editing - - metasprites - - throw errors when bad/no refs - - per-View keyboard shortcuts - - parse labels - - parse .incbin directives? - - can't replace in hex directives - - should maybe use same single-canvas editor for map + char editor - - undo doesn't refresh editor - - editing sometimes messes up cursor movement (CURBS) -- crt0.s compiled each time? -- debug highlight doesn't go away when debugging -> running - - show breakpoint of PC or highest address on stack - - can we highlight line instead of select? -- profiler - - profiler restarts emulator when paused - - running profiler while replaying? grand unified replay? - - click on profiler to step to position - - breakpoints stop profiler from running - - single-screen profiler - - hide labels that aren't available, like BIOS addrs - - show interrupts, other events - - sometimes interleaves two different PCs? like two profilers running simultaneously? - - ah, symbols persist across builds -- Verilog - - larger scope range, better scrolling - - make scope data wrap around range - - remove FPS and play controls when Verilog scope paused - - when paused scope doesn't work - - Safari: scope doesn't show while CRT in use (sometimes Firefox too) - - verilog debugging/reloading makes it slow - - why loadState() on verilog kill perf? - - quantify verilog "graph iterations" - - CPU debugging - - use $readmem for inline asm programs? - - can't add control instructions b/c of split - - bad error msg if >2 moduels and top module doesn't match filename - - separate Scope View - - Audio doesn't work if clock != default -- Verilog WASM not yet supported: - - wide numbers > 64 bits (and in scope view) - - multiple modules instances - - structs - - other $funcs - - multidim arrays - - optimize trace log w/ wasm buffer - - yosys compatibility - - randomize on reset? (https://www.xilinx.com/support/documentation/white_papers/wp272.pdf) - - XML should tell us which values are supposed to reset -- single-stepping vector games makes screen fade -- break on stack overflow, illegal op, bad access, BRK, etc - - show in scope instead? -- nes - - replay doesn't work for nes (force background tile redraw, sprites - messed up) - - nes debug view toolbar - - support NES_HEADER_16K? - - NES crt should mark raster pos when debugging - - OAMDMA in profiler? (haltCycles) - - typed arrays don't serialize? -- JSNES - - doesn't support hiding >8 sprites - - doesn't do sprite zero test right - - doesn't do clip right - - doesn't do b/w tint -- vcs - - sometimes still plays when paused on FF - - vcs: INPTx needs to be added to control state - - vcs: break on # of lines changed (maybe using getRasterPosition?) - - chrome looks blurry on vcs - - VCS asm game library - - VCS skips step on lsr/lsr after run to line - - better VCS single stepping, maybe also listings - - upgrade to 4.1 for Supercharger format -- upload multiple files/zip file to subdirectory - - allow "include graphics.asm" instead of "include project/graphics.asm" -- convert more stuff to Promises -- don't have to include firebase always? - - squelch error msgs? - - test offline? (if window.firebase) -- Github - - platform_id/repo.platform mismatch (can't leave repository) - - handle overwrite logic - - what to do about included files? - - can published files retain path? - - what if import interrupted and partial files? - - CORS for some blobs? - - don't import useless files - - support projects with subdirectories, file list? - - switching platform of a repo? - - make sure to flatten subdirs - - re-publish repo (bug: doesn't put all files in local repo after publishing) - - allow text/binary change - - importing from subtree commits to root anyway - - publishing Markdown file loads default file? - - better text/binary detection (e.g. 0xa9 copyright) - - going into repo chooses wrong file if republished with different main - - renaming main file doesn't change repo def. - - can't edit or push files until successful compile - - changing platform of repo makes one platform redirect to another platform - - should be able to change vcs -> vcs.mame for exmaple -- keyboard shortcuts - - ctrl+alt+l on ubuntu locks screen - - alt-D doesn't work anymore - - facade/kbd shortcuts for emulators, focus - - maybe eat key events? ("'" messes things up) -- cookie - - list of stuff for policy - - popup -- convert binary to hex stmts -- "suggestions" (vblank overrun, variable # scanlines, etc) -- SMS - - can't step back twice? - - compiler bug in chase -- "shared" in URL doesn't work, leave in URL? (also importURL) -- 6502 - - KIL stops debugger -- TypeError: null is not an object (evaluating 'n.destination') - https://8bitworkshop.com/v3.4.1/javatari.js/release/javatari/javatari.js - (32:443651) Safari 12.1.2 -- Safari: doesn't send good exception reasons ("undefined") - - probably XMLHttpRequest.timeout (https://github.com/getsentry/sentry-javascript/issues/2210) -- C64 - - disk image support - - https://github.com/cc65/cc65/issues/946 - - player 2 joystick and keyboard conflict - - problems with cgetc() - -- upgrade to ES2020? - - https://github.com/microsoft/TypeScript/issues/16577 - - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules - - need Edge 79+ - - no import in workers - - copy to gen/ directory (allowJs is weird) -- ca65 line numbers are not aligned with source code - - neither are DASM when you change comments - - segments not read properly -- Debug Browser - - more stuff like 7800 display lists - - don't include start/end line on platforms w/o raster timing -- BIOS symbols -- show current tool for file -- download non-text incbin source file -- show hidden header files that only exist in Emscripten FS -- WASI modules: https://docs.wasmer.io/integrations/js/wasi/browser -- FS Access: https://web.dev/file-system-access/ - - https://wicg.github.io/file-system-access/#api-showdirectorypicker - -Probing -- probe log doesn't start @ reset -- scroll to current probe log location -Debug, play then halt cpu doesn't highlight final line -- illegal instruction should show debug info + jump to line -- halt cpu should show video too -vcs: input not preserved when replaying frames? - -compile errors disappear when focus returns - -Stellerator -- window eats keystrokes (e.g. "i") -- more features, debugging - - -WEB WORKER FORMAT - -{code,platform,tool,dependencies} -{preload} - -new: - -{updates,buildsteps} -[{filepath,platform,tool}] - -implicit rules -- push/shift build steps -- preprocessor, compile, assemble, link -- access to intermediate files - -only build files that have changed - -build options - - -BUILD PROJECT - -local/foo.project/*.* -build all files in project -send update for dependencies at startup (single file version) -should be able to change file w/o reloading (Not platform tho) - -pulldown selects file in project, or goes back to parent -gotta preserve editor windows for each one - window list -make source/disasm window visible when debugging -gutter info/errors in all files - - - -UI REFACTOR - -editor -debugger -disasm view -memory -profile -errors - -separate into files - -window methods: -- set -- mode -- get -- changes -- cursorActivity (inspect) -- gutters (setCompileOutput) -- current line / gutter marker (setCurrentLine / clearCurrentLine) -- source map -- global symbols -- pixel editor / find comment (openBitmapEditorAtCursor) -- update debug window (200 ms) -- inspect variable - -file store mirrors that on worker - -worker result: -{ - output: - sources:{ - :{lines, asmlines, listing} - } -} - -add file to errors - -http://www.playvectrex.com/designit/chrissalo/vectordisplay.htm -http://oldies.malban.de/secondvectrex/index.htm - - -PIXEL EDITOR - -everything is an editor -back/forward propogation -encoded <-> raw + spec + palette(s) <-> full tilemap <-> selected tile/sprite/metasprite -need references -panes: source code, metadata tree, graphics panes -decoded info: -- CHR tiles -- palettes -- maps/rooms -- metasprites -- metatiles -- sprite data (williams etc) -store in intermediate single file or embed in C/ASM? -we'd then have to generate C/ASM probably automatically -intermediate file could still be edited -switch between hex/graphic editor? (palettes should edit via hex) -final display types: number, color, sprite, etc -CHR editor has palette selector, obj selector -individual objects in single file or multiple files? -RLE compression - -Types -- byte -- RGBA color -- structured object - - metasprite - - metatile - - level actor -- bitmap -- array of stuff, array can have a shape (width,height) like numpy -Mappers can refer to multiple objs -Slice (window) of array for selections - -AssetEditorView -- scan files for assets -- build hierarchical view of assets -- expand/collapse -- create editor upon expansion -- double-click to source file - -Assets come from: -- chr files -- bitmap data in files -- structured data (palette, sprites, metasprites, levels, etc) -- think about new comment format, platform-specific types - -Programmatic Asset Language -- load PNG, draw image, or generate bitmap font -- split into images -- paletteize -- fit into tileset/spriteset - - NES: 8x8 or 8x16 tiles - - 7800: 256xN slice - - VCS: 24xN playfield, 8xN sprite, 32xN big sprite -- generate data file (asm/c/bin) -- generate char mapping -- tile generator (makechr-like) -- metasprites too -- RLE compression -- check constraints - -https://github.com/endojs/endo/blob/master/packages/ses/README.md - -- CPU-independent IR -- structured assembler IR -- ECS/macros module -- HLL -- static analysis module -- linker/placement module - - inlining, data reuse - - nested tag for segments -- binary generation module -- remote fetch module -- asset module (bitmaps, sprites, tiles, palettes, compression...) -- asset editor UI -- debugging API (custom state, per-cycle watchpoints) - - ---- - -Github Support - -Import -Export -Login -Pull -Push - -Git metadata kept in local storage - -Converting from NESASM to DASM -- asl a -> asl -- subroutine keyword on labels -- [zp],y to (zp),y -- LOW(x) and HIGH(x) to <() and >() -- .db to .byte, .dw to .word -- use NES_HEADER macros -- no .bank - -Cross platform NES/SMS/GG library -- use CHR RAM - - create flipped tiles/sprites - - create alternate palette tiles -- metatiles -- cross platform music/sound -- scrolling - - row/column mask - - no nametable mirroring in SMS - - 256x240 vs 256x192 - -Emulator Lib -- move getPresets() (into presets/ dir?) -- CPU interface - - execCycle(), execInsn() - - fix/unfix PC - - interrupt -- generic raster scanline platform -- PlatformRunner - - handles rewind, intra-frame breakpoint, debugging - - profiling log, exec/read/write/intr (for each bus?) -- expose video, audio, controller interfaces -- new debugging info -- memory map interface -- "About" metadata -- auto load/save state? -- handle legacy - - VCS - - NES - - MAME -- MAME - - debugging? - - kbd instructions don't work - - https://emscripten.org/docs/porting/asyncify.html - -Markdown (showdown) interface -- add code patches to files -- cursor shows current state/errors (must map markdown->src lines) -- show preview emulator at each step (play button? run when viewed?) - - -DOCUMENTATION - -memory map -BIOS routines -cc65 standard libs -cc65 headers -example headers (presets/*/*.h) -libcv headers (src/worker/lib) -ASM includes - - -NETPLAY - -runs alongside of emulator, doesn't modify controls (just state) -when controls change: player sends control inputs + frame# -add latency so clients more often are in sync -if miss latency window, client syncs state (moves forward a few frames?) -state checksum? -socket.io - - -X86 - -https://github.com/freebasic/fbc -GW-BASIC -http://www.grifo.com/SOFT/Pacific/uk_pacific.html -http://www.cpm.z80.de/small_c.html -https://github.com/open-watcom -Free Pascal -https://bellard.org/tcc/ -https://wiki.osdev.org/Smaller_C -https://yasm.tortall.net/ -https://wiki.osdev.org/Tool_Comparison - - -COMPONENT EMULATION - -Emulate discrete chips -- verilog -- js -- wasm? (overhead?) -Block diagram level -Buses -Insn/cycle timing -Pos/neg clock edge -Timers -Polling vs. callbacks (https://github.com/floooh/chips) -- callbacks might only affect devices internally, until next cycle -CRT simulator -Move leaf nodes to workers (sound chips) -CPU opcode-step switch: https://floooh.github.io/2019/12/13/cycle-stepped-6502.html -- macros are cool! -Inspect bus edges and internal state - -type Bits = number; -interface Clocked { } -interface BusListener { - recv(value : Bits) : void; -} -class Bus { - listeners : BusListener; - send(value : Bits) : void { - } -} -function bus(bits : number) { - return function( - target: any, - propertyKey: string, - descriptor: PropertyDescriptor - ) { - //descriptor.configurable = value; - }; -} -class M6502_T { - IR : Bits<16>; /* internal instruction register */ - addr : Bus<16>; - data : Bus<8>; - - @bus(16) address : Bus<16>; -} - -Optimizations -- ordered/delayed assignments -- skip unused exprs -- opcode -> fn table -- bus protocols -> functions -- state -> lookup tables or switch statements -- vec[] -> bitmask -- multiclock execution? - - - -STATE REWIND - -in Platform: - - advance?(novideo? : boolean) : number; - advanceFrameClock?(trap:DebugCondition, step:number) : number; - -in devices: - - advanceFrame(trap:TrapCondition) : number; - advanceClock() : void; - advanceInsn() : number; - -Should call trap() every cycle or insn of frame, or exit when returns true? - - -BETTER DEBUGGING - -Know if we are actively debugging or trap occurred -isRunning() = stopped, running, waiting, debugging... -Showing running PC may be distracting, maybe lines visited? -Don't grab cursor focus when trap occurs (how do we know?) -Use tick() and refresh(), not callbacks -Show current datum when using READ -Use https://codemirror.net/doc/manual.html#markText -Reset doesn't break @ start unless debugging tools expanded -Single-step can un-sync frame/scanline timing - - -PORTING CC65 TO IDE - -flat directory for .c files -#include "file.h" vs -//#link "foo.c" for each C file -preprocessor differences (#define FOO 1 so #if works) -extern void do_func(void) -#pragma warnings -CFG files - diff --git a/doc/parsedoc.py b/doc/parsedoc.py deleted file mode 100644 index 2582edb4..00000000 --- a/doc/parsedoc.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/python -# -# C header file -> Markdown-ish converter - -import sys,re - -re_define = re.compile("^#define\s+(\w+)([(][^)]+[)])?") -re_func = re.compile("^(\w+\s+[A-Za-z_ ]+[(][^)]+[)]);") -re_comment1 = re.compile("//(.+)\\\\?") - -comment = '' - -def dump(s): - global comment - print('`'+ s.strip() +'`') - print(': ' + comment.strip()) - comment = '' - -files = sys.argv[1:] -for fn in files: - with open(fn,'r') as f: - print("## " + fn) - l = f.readline() - while l: - l = l.strip() - m1 = re_define.match(l) - m2 = re_func.match(l) - m3 = re_comment1.match(l) - if m3: - comment += m3.group(1) - if m1: - s = m1.group(1) - if m1.group(2): - s += m1.group(2) - dump(s) - elif m2: - s = m2.group(1) - s = s.replace('void ','').replace('(void)','()') - s = s.replace('__fastcall__',' ') - s = s.replace(' ',' ') - s = s.replace(' ',' ') - dump(s) - elif not m3: - if l != '': - print('') - else: - print('') - l = f.readline() - diff --git a/doc/platforms.md b/doc/platforms.md deleted file mode 100644 index ae465c89..00000000 --- a/doc/platforms.md +++ /dev/null @@ -1,265 +0,0 @@ - -class Platform --------------- - -Mandatory functions: -~~~ - start() : void; - reset() : void; - isRunning() : boolean; - pause() : void; - resume() : void; - loadROM(title:string, rom:any); -~~~ - -These are for the compiler/editor: -~~~ - getToolForFilename(s:string) : string; - getDefaultExtensions() : string[]; - getPresets() : Preset[]; -~~~ - -Most platforms have these: -~~~ - loadState?(state : EmuState) : void; - saveState?() : EmuState; -~~~ - -... etc - - -6502 ----- - -`advance()` advances one frame. -The basic idea: iterate through all the scanlines, run a bunch of CPU cycles per scanline. -If we hit a breakpoint, exit the loop. - -~~~ - var debugCond = this.getDebugCallback(); - for (var sl=0; sl<262; sl++) { - for (var i=0; i { - }, - onAudioSample: (left:number, right:number) => { - }, - onStatusUpdate: function(s) { - }, - }); -~~~ - -We monkey-patch the code to add a debugging hook: - -~~~ - // insert debug hook - this.nes.cpu._emulate = this.nes.cpu.emulate; - this.nes.cpu.emulate = () => { - var cycles = this.nes.cpu._emulate(); - this.evalDebugCondition(); - return cycles; - } -~~~ - -NES was the first platform with an "illegal opcode" hard stop, so we added a special `EmuHalt` exception which causes a breakpoint: - -~~~ - this.nes.stop = () => { - console.log(this.nes.cpu.toJSON()); - throw new EmuHalt("CPU STOPPED @ PC $" + hex(this.nes.cpu.REG_PC)); - }; -~~~ - - -MAME ----- - -The `BaseMAMEPlatform` class implements a MAME platform. -You just have to pass it various parameters when starting, and tell it how to load the ROM file: - -~~~ -class ColecoVisionMAMEPlatform extends BaseMAMEPlatform implements Platform { - - start() { - this.startModule(this.mainElement, { - jsfile: 'mamecoleco.js', - cfgfile: 'coleco.cfg', - biosfile: 'coleco/313 10031-4005 73108a.u2', - driver: 'coleco', - width: 280 * 2, - height: 216 * 2, - romfn: '/emulator/cart.rom', - romsize: 0x8000, - preInit: function(_self) { - }, - }); - } - - loadROM(title, data) { - this.loadROMFile(data); - this.loadRegion(":coleco_cart:rom", data); - } - - getPresets() { return ColecoVision_PRESETS; } - getToolForFilename = getToolForFilename_z80; - getDefaultExtensions() { return [".c", ".ns", ".s", ".scc", ".sgb", ".z", ".wiz"]; } -} -~~~ - -A lot of things are done via Lua scripting -- for example, loading a ROM requires we loop over the memory region and issue `rgn:write_u32` calls. -It kinda-sorta works, except debugging isn't reliable because MAME [doesn't return from the event loop](https://github.com/mamedev/mame/issues/3649) at breakpoints. - -MAME platforms don't have state load/save either. - - -Verilog --------- - -The Verilog platform is the odd one out, since it has no fixed CPU as such. -The `loadROM` function instead loads a JavaScript function. -Some platforms do have a ROM if using assembly, so we load that into a Verilog array. -It's quite the hack, and it could be better. - -Verilog has its own debugger, logging signals in a fixed-size buffer. - - - -Profiling ----------- - -`EmuProfilerImpl` runs the profiler. -When started, it calls `setBreakpoint` to add a profiler-specific breakpoint that never hits, just records the CPU state at each clock. -It uses `getRasterScanline` to associate IPs with scanlines. -Platforms can also log their own reads, writes, interrupts, etc. - - -Future Ideas ------------- - -There should be a standard CPU interface, buses, memory map. -More like MAME configuration. - -Platforms might have different ideas of "clock" (CPU clock, pixel clock, 1 clock per instruction, etc) - -The goal is to rewind and advance to any clock cycle within a frame, and get complete introspection of events, without hurting performance. - -Unify raster platforms, they should all allow the same debugging and CPU interfaces. - -Separate UI/sim parts of platform? -A lot of platforms write into a uint32 buffer. -We might want to buffer audio the same way. -Also some way to log events, and handle input. - -Figure out how to make platform-specific type for load/save state. -(generics?) - -Separate emulators from 8bitworkshop IDE. - -Can we use WASM emulators without JS interop penalty? -Maybe using [AssemblyScript](https://docs.assemblyscript.org/)? -Startup would be faster, probably runtime too. -Drawback is that dynamic stuff (custom breakpoint functions, profiling) might be slow, slower dev too maybe. -Need proof-of-concept. diff --git a/index.html b/index.html index 8d40c2bb..1e547c37 100644 --- a/index.html +++ b/index.html @@ -110,12 +110,6 @@ body {