{"id":50588156,"url":"https://github.com/iolo/a2han","last_synced_at":"2026-06-05T08:01:25.969Z","repository":{"id":345869488,"uuid":"1186823499","full_name":"iolo/a2han","owner":"iolo","description":null,"archived":false,"fork":false,"pushed_at":"2026-03-21T10:04:04.000Z","size":252,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-21T19:07:36.074Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/iolo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-20T02:56:32.000Z","updated_at":"2026-03-21T10:04:08.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/iolo/a2han","commit_stats":null,"previous_names":["iolo/a2han"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/iolo/a2han","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iolo%2Fa2han","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iolo%2Fa2han/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iolo%2Fa2han/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iolo%2Fa2han/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/iolo","download_url":"https://codeload.github.com/iolo/a2han/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iolo%2Fa2han/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33935514,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-05T02:00:06.157Z","response_time":120,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-05T08:01:24.733Z","updated_at":"2026-06-05T08:01:25.960Z","avatar_url":"https://github.com/iolo.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# a2han\n\nHangul input/output support for Apple II systems using an\n[AppleII-VGA card](https://github.com/markadev/AppleII-VGA) with\n[custom firmware](https://github.com/iolo/AppleII-VGA).\n\nThis repository is a low-level Apple II project. The intended reader is someone\nalready comfortable with 6502-era constraints, Applesoft BASIC quirks, disk\nimages, and patched firmware behavior.\n\n## Prerequisites\n\n- [cc65/ca65](https://cc65.github.io/)\n- [a2kit](https://github.com/dfgordon/a2kit): manipulate Apple II disk images (`.dsk`, `.po`)\n- [AppleII-VGA](https://github.com/markadev/AppleII-VGA): VGA output for Apple II\n- [AppleII-VGA Custom Firmware](https://github.com/iolo/AppleII-VGA): firmware support for the `modified` character mapping\n\n## Build and Run\n\n### Build the program\n\n```\nmake\n```\n\n### Create Apple II DOS disk image\n\n```\nmake dsk\n```\n\nStatus: active target. `make dsk` now packages the DOS-specific `A2HAN`\nbinary built from the shared `a2han.s` source.\n\n### Create Apple II ProDOS disk image\n\n```\nmake po\n```\n\nStatus: active target. `make po` packages the ProDOS-specific `A2HAN` binary\nbuilt from the shared `a2han.s` source.\n\n### Run the program\n\nBoot the Apple II from the generated disk image, then load `A2HAN`.\nThe current manual load/install path is:\n\n```\n] BLOAD A2HAN\n] CALL 24576\n```\n\nThis applies to both the ProDOS and DOS 3.3 disk images. `BRUN A2HAN` should\nnot yet be treated as the primary development workflow.\n\n### Repeatable `apple2ts` workflow\n\nFor emulator-driven development, the preferred repeatable cycle is:\n\n```sh\nmake dsk\ntools/run_apple2ts_a2han.sh\n```\n\nThat helper script:\n\n- mounts `dos-3.3.dsk` on `fd1`\n- mounts `build/a2han.dsk` on `fd2`\n- boots the emulator\n- sends `CATALOG,D2`\n- sends `BRUN A2HAN`\n- prints a short machine and screen summary from `GET /api/machine`\n\nNotes:\n\n- This is the current default workflow for `apple2ts` testing after `A2HAN`\n  code changes.\n- `apple2ts` may report mounted floppy images back as `.woz` even when the\n  source image mounted was `.dsk`.\n- For quick screen inspection, `GET /api/machine` and its `textPage` field are\n  usually more convenient than raw debugger memory reads.\n- For focused span/output repros after install, use\n  `tools/repro_apple2ts_print_span.sh`.\n\nRun the Applesoft BASIC demo:\n\n```\n] RUN DEMO\n```\n\nStatus: TODO/TBD.\n\nRun `A2HVIEW` to display a Hangul text file:\n\n```\n] BRUN A2HVIEW\nFILENAME: ...\nENCODING: (U)nicode, (M)odified, (N)bytes:\n...\n```\n\nStatus: basic implementation is in place. `A2HVIEW` can prompt for a filename and\nsource encoding, then display:\n\n- `utf8`: ASCII plus modern Hangul syllables/jamo transcoded to `modified`\n- `modified`: raw framebuffer-oriented Hangul bytes streamed directly\n- `nbytes`: mixed text plus delimited Hangul spans decoded and rendered\n\nCurrent limits:\n\n- UTF-8 support is intentionally narrow; unsupported non-Hangul Unicode code\n  points stop the display with an error.\n- `nbytes` spans are validated and must terminate cleanly.\n- `A2HAN` now uses a resident streaming automaton for delimited `nbytes`\n  output, but real Apple II and AppleWin runs still show corruption when the\n  hook emits `modified` byte pairs through the normal character-output path.\n- The immediate `A2HAN` blocker is observability: we need better tracing of\n  bytes entering the hook, bytes emitted by the hook, and the bytes that\n  actually survive the output vector path.\n- Recent debugging isolated a few separate behaviors that should not be mixed:\n  line-editor echo while typing, `INPUT` echo, stored Applesoft program text,\n  and ordinary `PRINT` output.\n- The current `apple2ts` repro for `PRINT \"\u003cCtrl-K\u003eRK\u003cCtrl-A\u003e\"` shows final\n  text-page bytes `4C 46`, `PRINT \"\u003cCtrl-K\u003eSK\u003cCtrl-A\u003e\"` shows `50 DE`, and\n  `PRINT \"\u003cCtrl-K\u003eGKS\u003cCtrl-A\u003e\"` shows `75 5C 41 59`.\n- A control program, `CSWTEST`, is now packaged on the DOS disk image to send\n  known raw byte pairs through the ordinary output vector without installing\n  `A2HAN`.\n- `A2HVIEW` now renders file content by writing Apple II text-page memory\n  directly rather than using stdio/conio character output for the content\n  stream.\n- `A2HVIEW` no longer scrolls; when it reaches the bottom line it waits for a key\n  press, clears the screen, and resumes from the top.\n- Real Apple II testing found that `A2HVIEW` now displays content correctly, but\n  it can still crash to the monitor after some amount of output. This remains\n  under investigation.\n\nThe generated disk images currently bundle matching pangram samples for each\nmode:\n\n- `PANGUTF8` for `ENCODING: U`\n- `PANGMOD` for `ENCODING: M`\n- `PANGNBYTES` for `ENCODING: N`\n\nThese samples are the current recommended smoke-test inputs for `A2HVIEW` on real\nhardware.\n\nPlanned next utility: `A2HEDIT`, a simple line editor in the style of `ed`.\n\nIntended shape for the first version:\n\n- command prompt loop rather than full-screen editing\n- current-line oriented commands such as `a`, `i`, `p`, `n`, `d`, `w`, `e`, `q`\n- `.` on its own line ends append/insert mode\n- small in-memory line buffer with explicit limits\n- reuse of `hconv` rules for file import/export rather than per-keystroke conversion\n\nThis is intentionally narrower than a visual editor. The Apple II target has\nstrict memory and display-path constraints, and the project already has one\nknown renderer-stability issue in `A2HVIEW`. A line editor is the preferred\nnext step because it is much cheaper to implement and verify.\n\n## Directory Structure\n\n```\na2han.s         ; main program source code\na2hview.c       ; interactive Hangul file viewer built with cc65\na2hedit.c       ; planned simple line editor built with cc65\nMakefile        ; build script\nhconv.py        ; utility to convert between different encodings\nbuild/          ; build output directory\n    A2HAN.PRO   ; ProDOS-specific build from a2han.s\n    A2HAN.DOS   ; DOS 3.3-specific build from a2han.s\n    A2HVIEW     ; Apple II file viewer binary\n    a2han.po    ; Apple II ProDOS disk image\n    a2han.dsk   ; Apple II DOS disk image\n```\n\n## Technical Details\n\n- `a2han` is written in 6502 assembly language and assembled using the `cc65`\n  toolchain.\n- `a2han` is built from a single shared source file, `a2han.s`, with\n  build-time conditionals selecting either the ProDOS BASIC vectors or the DOS\n  3.3 `KSW`/`CSW` hook path.\n- `a2hview` now provides a minimal interactive file-view utility for `utf8`,\n  `modified`, and `nbytes` input files.\n- `a2hedit` is planned as a small `ed`-style line editor rather than a\n  full-screen text editor.\n- the first `a2hedit` target should prefer command-loop simplicity, bounded\n  buffers, and predictable file I/O over screen-oriented UX.\n- The resident install path now supports both ProDOS and DOS 3.3 builds.\n- The resident output parser is now structured as a streaming `S0..S8`\n  automaton matching `nbytes-automata.md`.\n- The program hooks keyboard input and console output to provide Hangul input\n  and output.\n- Plain text is the default mode.\n- Public `nbytes` is a mixed-text encoding: plain text passes through unchanged, and bytes between `Ctrl-K` and `Ctrl-A` are treated as Hangul payloads.\n- The bytes inside a delimited `nbytes` span use the internal Hangul composition grammar and are transcoded into **modified Unicode** before reaching the text framebuffer.\n- Current debugging status: the parser/composer passes host-side checks, but\n  live runs still show that the normal Apple II output-vector path is not yet\n  behaving like a transparent transport for emitted `modified` bytes.\n- More specifically, current evidence suggests the resident line-edit/input echo\n  path and normal program output path behave differently and should be debugged\n  as separate transport layers.\n\nDesign model:\n\n- On a modern machine, UTF-8 is decoded into Unicode before rendering.\n- In this project, `nbytes` is decoded/transcoded into `modified` before rendering.\n- The analogy is useful, but not exact: public `nbytes` is a mixed-text transport, and its delimited payload is closer to an input grammar over Hangul components than a strict byte serialization of `modified`.\n\n### Runtime Model\n\n- Keyboard path: intercept raw input, preserve `Ctrl-K ... Ctrl-A` text as\n  `nbytes` for Applesoft/BASIC storage, and optionally provide user feedback\n  such as delimiter bells. The keyboard hook does not render Hangul directly.\n- Output path: intercept console output, detect encoded Hangul sequences, and write framebuffer-compatible bytes.\n- Rendering path: the AppleII-VGA custom firmware interprets the remapped code points and draws glyphs.\n- Standalone fallback should be understood as neutral `ja-eum` / `mo-eum`\n  tokens. Positional roles like `choseong` and `jongseong` exist only inside a\n  composed syllable.\n- Real-world caveat: this output model is still the design target, but current\n  resident tests on both AppleWin and real hardware show byte corruption after\n  the hook emits `modified` code points through the saved output vector.\n\n### Hook Rules\n\n- ProDOS and DOS 3.3 do not use equivalent hook chaining semantics even though\n  both expose input/output vectors.\n- In the ProDOS build, `a2han` installs into the BASIC vectors and chains\n  through the original saved vectors for pass-through behavior.\n- In the DOS 3.3 build, `a2han` installs into `CSW`/`KSW` and must call\n  `DOSFET` (`JSR $03EA`) immediately after patching those vectors.\n- In the DOS 3.3 build, pass-through must not chain through the pre-install\n  `CSW`/`KSW` targets. That path can loop back into the hook machinery and\n  recurse.\n- Instead, DOS 3.3 pass-through should use stable ROM entry points:\n  `COUT1` for output and `KEYIN` for input.\n- Practical rule: ProDOS chains through saved vectors; DOS 3.3 bypasses saved\n  hook targets and uses ROM routines after installing `CSW`/`KSW`.\n\n\u003e Note: The custom AppleII-VGA firmware renders the `modified` encoding from the Apple II text framebuffer (`0x400-0x7FF`). That rendering logic is out of scope for this project; see the firmware source for details.\n\n### N-byte Hangul Encoding\n\n- Applesoft BASIC uses the MSB to distinguish ASCII characters from tokens, so the MSB cannot be reused for Hangul encoding.\n- Instead, this project encodes Hangul characters as one or more bytes using the following mapping:\n\n```\nA: ㅁ, B: ㅠ, C: ㅊ, D: ㅇ, E: ㄷ, F: ㄹ, G: ㅎ, H: ㅗ, I: ㅑ, J: ㅓ,\nK: ㅏ, L: ㅣ, M: ㅡ, N: ㅜ, O: ㅐ, P: ㅔ, Q: ㅂ, R: ㄱ, S: ㄴ, T: ㅅ,\nU: ㅕ, V: ㅍ, W: ㅈ, X: ㅌ, Y: ㅛ, Z: ㅋ,\ne: ㄸ, o: ㅒ, p: ㅖ, q: ㅃ, r: ㄲ, t: ㅆ, w: ㅉ,\n...\n```\n\nFor compatibility with historical files, doubled consonants also accept the\nlegacy one-byte spellings from [call3327.txt](/home/iolo/workspace/retro/a2han/call3327.txt):\n\n```text\n-: ㄲ, =: ㄸ, *: ㅃ, \u003c: ㅆ, \u003e: ㅉ\n```\n\nLegacy `nbytes` remains intentionally lossy for standalone compound vowels and\ncompound final clusters. In particular, compound vowel sequences such as `HK`\nare recognized as medial forms only after an initial consonant; a plain\nstandalone `HK` decodes as `ㅗㅏ`, not as a distinct standalone `ㅘ`.\n\n### Modified Unicode Encoding\n\nStandard Hangul code points cannot be written directly into the\n[Apple II character set](https://en.wikipedia.org/wiki/Apple_II_character_set)\nspace without conflicts:\n\n- ASCII characters `0x00-0x7F` are displayed as `0x80-0xFF` in the Apple II text framebuffer.\n- `0x00-0x4F` maps to `INVERSE` characters.\n- `0x40-0x7F` maps to `FLASH` characters and MouseText.\n\nThis project reuses the `FLASH` range for Hangul characters:\n\n- Hangul syllables `0xAC00-0xD7A3` are mapped to `0x4C00-0x77A3`\n- Hangul jamo `0x1100-0x11FF` are mapped to `0x4100-0x41FF`\n\n### Examples\n\n- `PRINT \"ABC\";CHR$(11);\"GKS\";CHR$(5)` in Applesoft BASIC displays `ABC한`.\n  The corresponding text buffer bytes are `41 42 43 c1 c2 c3 75 5c`.\n- Typing `ABC\u003cCtrl-K\u003eGKS\u003cCtrl-A\u003e` from the keyboard also displays `ABC한`.\n  The corresponding text framebuffer bytes are `c1 c2 c3 75 5c`.\n- `RHK` composes `과`.\n- `RHOS` composes `괜`.\n\nInterpretation:\n\n- `CHR$(11)` is `Ctrl-K`, the start delimiter for N-byte Hangul input.\n- `CHR$(1)` is `Ctrl-A`, the end delimiter.\n- `GKS` is the internal Hangul payload for `한`.\n- `75 5c` is the `modified` code point written for that syllable.\n\n## Host Utilities\n\n### Convert Between `UTF-8`, `Modified Unicode`, and `N-byte Hangul`\n\n```\npython3 hconv.py \u003coptions\u003e\n  -f, --from-code=\u003cencoding\u003e   `utf8`, `modified`, or `nbytes`\n  -t, --to-code=\u003cencoding\u003e     `utf8`, `modified`, or `nbytes`\n  -i, --input \u003cfile\u003e           input file; reads from stdin if omitted\n  -o, --output \u003cfile\u003e          output file; writes to stdout if omitted\n  -h, --help                   show this help message and exit\n```\n\n## Notes for Humans and Agents\n\n- Treat the AppleII-VGA firmware mapping as part of the ABI. Do not change the remap ranges casually.\n- Distinguish clearly between three layers: public `nbytes` transport, internal Hangul payload grammar, and `modified` framebuffer encoding.\n- When documenting behavior, describe which path is being discussed: keyboard hook, console hook, file conversion, or firmware rendering.\n- The README describes encoding intent and externally visible behavior. Assembly source remains the authority for exact hook and memory semantics.\n- Keep unfinished pieces like `demo.bas` visible in the docs, but do not label\n  shipped build targets as TODO when they are already implemented.\n\n---\nMay the **SOURCE** be with you!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiolo%2Fa2han","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fiolo%2Fa2han","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiolo%2Fa2han/lists"}