{"id":50958319,"url":"https://github.com/plummerssoftwarellc/tinyretropad","last_synced_at":"2026-06-18T10:03:22.994Z","repository":{"id":362547100,"uuid":"1259676981","full_name":"PlummersSoftwareLLC/TinyRetroPad","owner":"PlummersSoftwareLLC","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-04T18:54:10.000Z","size":31,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-04T21:20:01.719Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Assembly","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PlummersSoftwareLLC.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.TXT","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":"agents.md","dco":null,"cla":null}},"created_at":"2026-06-04T18:43:16.000Z","updated_at":"2026-06-04T21:10:34.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/PlummersSoftwareLLC/TinyRetroPad","commit_stats":null,"previous_names":["davepl/tinyretropad"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/PlummersSoftwareLLC/TinyRetroPad","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PlummersSoftwareLLC%2FTinyRetroPad","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PlummersSoftwareLLC%2FTinyRetroPad/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PlummersSoftwareLLC%2FTinyRetroPad/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PlummersSoftwareLLC%2FTinyRetroPad/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PlummersSoftwareLLC","download_url":"https://codeload.github.com/PlummersSoftwareLLC/TinyRetroPad/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PlummersSoftwareLLC%2FTinyRetroPad/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34485169,"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-18T02:00:06.871Z","response_time":128,"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-18T10:03:21.896Z","updated_at":"2026-06-18T10:03:22.975Z","avatar_url":"https://github.com/PlummersSoftwareLLC.png","language":"Assembly","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- markdownlint-disable MD041 --\u003e\n```ascii\n  _____      _             _____          _\n |  __ \\    | |           |  __ \\        | |\n | |__) |___| |_ _ __ ___ | |__) |_ _  __| |\n |  _  // _ \\ __| '__/ _ \\|  ___/ _` |/ _` |\n | | \\ \\  __/ |_| | | (_) | |  | (_| | (_| |\n |_|  \\_\\___|\\__|_|  \\___/|_|   \\__,_|\\__,_|\n T I N Y  X 86   D E S K T O P   E D I T O R\n```\n\n# TinyRetroPad\n\nA working, Notepad-style Windows text editor in roughly 2.5 KB.\n\nCompiles with: MASM and Crinkler.\n\nTinyRetroPad is a fork of **Dave's Tiny Editor (DTE)** by Matt Power, which is itself an extension of `tiny.asm` [HelloAssembly](https://github.com/PlummersSoftwareLLC/HelloAssembly) by [Dave Plummer](https://github.com/davepl). The original goal was a working windowed text editor in the sub-1KB category; TinyRetroPad keeps that minimalist, size-obsessed spirit while filling out a full Notepad-style menu set (File / Edit / Format / View / Help) on top of it. It uses [Crinkler](https://github.com/runestubbe/Crinkler) compression at build time.\n\nTinyRetroPad is basically a wrapper around the RICHEDIT50W control from the WinAPI. DTE versions 1.0+ used the EDIT control with Crinkler cranked and were built up from tiny.asm, then worked down to 890 bytes with Win Defender quite unhappy. Versions 2.0+ backed Crinkler off a bit and use RICHEDIT to gain cheaper access to Courier font and much larger files; 2.0+ was worked down from 995 to 981 bytes as a bare editor. TinyRetroPad then grows from that 981-byte base by adding real menus and dialogs — Open/Save/Save As, Print/Page Setup, Find/Replace/Go To, Font, Word Wrap, Time/Date, and a Ln/Col status bar — landing near 2,476 bytes. Each addition was kept as cheap as possible; the growth log at the top of [trpad.asm](trpad.asm) records what every feature cost in bytes.\n\n**Important:** Programs using Crinkler can be flagged as a false positive by antivirus, including Windows Defender. You may need to make an antivirus exception folder to build this (especially for 1.0+), or Windows may delete the EXE as soon as the build completes. Therefore, try this out AT YOUR OWN RISK - NO WARRANTIES / NO GUARANTEES. You can accomplish this with PowerShell, but I am not going to tell you how. Sorry. You're on your own when messing with antivirus.\n\n- MASM version used: Microsoft (R) Macro Assembler Version 14.44.35224.0\n\n- MASM can vary depending on version. If you experience:\n\n  ```console\n  C:\\masm32\\include\\winextra.inc(11052) : error A2026:constant expected\n  C:\\masm32\\include\\winextra.inc(11053) : error A2026:constant expected\n  ```\n\n  In masm32\\include\\winextra.inc change:\n\n  ```assembly\n      STD_ALERT struct\n          alrt_timestamp dd ?\n          alrt_eventname WCHAR  [EVLEN + 1] dup(?)\n          alrt_servicename WCHAR [SNLEN + 1] dup(?)\n      STD_ALERT ends\n  ```\n\n  to:\n\n  ```assembly\n      STD_ALERT struct\n          alrt_timestamp dd ?\n          alrt_eventname WCHAR  (EVLEN + 1) dup(?)\n          alrt_servicename WCHAR (SNLEN + 1) dup(?)\n      STD_ALERT ends\n  ```\n\n  The brackets on lines 13,14 were changed to parens.\n\n- `build.bat` contains: /LIBPATH:\"C:\\Program Files (x86)\\\\Windows Kits\\\\10\\\\Lib\\\\10.0.20348.0\\\\um\\\\x86\"\n  You may need to change to fit your system: /LIBPATH:\"...\\\\Windows Kits\\\\10\\\\Lib\\\\(your version)\\\\um\\\\x86\"\n\n- You need to have Crinkler installed in a directory that has been added to PATH.\n  Example: C:\\utils\\Crinkler.exe\n\n## Contents\n\n| Folder | Description |\n| - | - |\n| `1_0` | DTE Version 1.0 aggressive 890 bytes build. Needs AV exception to be usable. |\n| `2_0_BACKUPS` | DTE Version 2.0 bare editor, 981 bytes build from RICHEDIT to release. |\n\n| File | Description |\n| - | - |\n| `build.bat` | Builds TinyRetroPad from command line. |\n| `DRAG ME ONTO DTE.txt` | How to use the editor. |\n| `DTE ABOUT.txt` | Explains some design decisions. |\n| `trpad.asm` | The program. TinyRetroPad, forked from DTE 2.0.9 |\n| `LICENSE.TXT` | Usage permissions (Apache License 2.0). |\n\n## Building the menus and Notepad features\n\nEverything past the bare RICHEDIT wrapper is built up the same way: keep the control doing the heavy lifting, and let the WinAPI common dialogs and a few `SendMessage` calls supply the rest. Almost every \"feature\" is just a menu ID routed to a one- or two-instruction handler, so the byte cost stays tiny. The\ngrowth log at the top of [trpad.asm](trpad.asm) tracks what each addition cost.\n\n### The menu bar\n\nThe whole menu bar is assembled at startup in `CreateNotepadMenus`:\n\n- `CreateMenu` makes the top-level bar, then each drop-down is a\n  `CreatePopupMenu`.\n- Items are added with two thin wrappers, `AppendEnabled` and\n  `AppendDisabled`, that call `AppendMenuA`. `AppendDisabled` with a null\n  string draws the separator lines, which avoids carrying a separate separator\n  call.\n- Every menu label lives as a packed `db` string (`MFile`, `MNew`, `MOpen`, etc.) and every command is a constant ID (`IDM_FILE_OPEN`, `IDM_EDIT_FIND`, …). `SetMenu` attaches the finished bar to the main window.\n- The same IDs power the right-click context menu via `TrackPopupMenu`, so the context menu costs almost nothing extra.\n\nThe classic `Save` item is appended onto the window's **system menu** with\n`GetSystemMenu`/`AppendMenuA`, which is why it arrives as a `WM_SYSCOMMAND` instead of a `WM_COMMAND`.\n\n### Routing commands\n\nIn `WndProc`, `WM_COMMAND` extracts the low word of `wParam` and runs it down a flat list of `cmp`/`je` checks straight to each `Cmd…` handler. Most handlers just forward an EDIT/RICHEDIT message and return:\n\n| Menu | How it works |\n| - | - |\n| **Edit → Undo / Cut / Copy / Paste / Delete / Select All** | Single `SendMessageA` of the matching `WM_UNDO` / `WM_CUT` / `WM_COPY` / `WM_PASTE` / `WM_CLEAR` / `EM_SETSEL` message to the control. |\n| **Edit → Time/Date** | `InsertTimeDate` calls `GetLocalTime` + `GetTimeFormatA`/`GetDateFormatA`, then replaces the selection with `EM_REPLACESEL`. |\n\n### File: New / Open / Save / Save As\n\nThe file commands are driven by the common dialogs so we never hand-roll a\nfile picker:\n\n- **Open** (`PickOpenFile`) and **Save As** (`PickSaveFile`) call\n  `GetOpenFileNameA` / `GetSaveFileNameA` with a single all-files filter and store the chosen path in `CmdFile`. Open then reuses the existing `LoadStartupFile` path that the drag-and-drop launch already used.\n- Loading/saving the actual bytes goes through `CreateFileA`, `GetFileSize`, `GlobalAlloc`, `ReadFile`/`WriteFile`, and `CloseHandle` — the same plumbing the original drop-file editor used, now shared by the menus.\n- A `fDirty` flag (set on `EN_CHANGE`) drives the title-bar `*` and the `MaybeSaveChanges` \"Save changes?\" prompt that New / Open / Exit run before discarding the buffer.\n\n### File: Page Setup and Print\n\nPrinting leans entirely on RICHEDIT's own renderer:\n\n- **Page Setup** (`PageSetup`) just shows `PageSetupDlgA`.\n- **Print** (`PrintDoc`) calls `PrintDlgA` to get a printer DC, queries it with `GetDeviceCaps`, then asks the control to lay text onto that DC with `EM_FORMATRANGE`, looping `StartPage`/`EndPage` until the whole document is emitted, wrapped in `StartDocA`/`EndDoc` and released with `DeleteDC`. No GDI text drawing of our own is required.\n\n### Edit: Find / Find Next / Replace / Go To\n\n- **Find / Replace** use the modeless common dialogs `FindTextA` /\n  `ReplaceTextA`. Because they are modeless, the message loop calls\n  `IsDialogMessageA` for the dialog, and the actual searching happens when the registered `FINDMSGSTRING` message (`uFindMsg`, fetched via `RegisterWindowMessageA`) arrives at `OnFindReplaceMsg`. `DoFindNext`, `DoReplaceOne`, and `DoReplaceAll` then drive selection via `EM_FINDTEXTEX`\n  and `EM_REPLACESEL`.\n- **Go To** uses an in-memory dialog template (`GoToTmpl`) shown with `DialogBoxIndirectParamA`, reads the line number with `GetDlgItemInt`, and scrolls there with `EM_LINEINDEX` + `EM_SETSEL`. Building the template in memory avoids a `.rc` resource and its overhead.\n\n### Format: Word Wrap and Font\n\n- **Word Wrap** (`ToggleWrap`) flips `fWrap` and re-targets the control's wrap width with `EM_SETTARGETDEVICE`.\n- **Font** (`ChooseFontDlg`) shows `ChooseFontW` and applies the result through `EM_SETCHARFORMAT` — the same mechanism used to force Courier at startup, which is why no `gdi32` import is needed.\n\n### View: Status Bar\n\nThe status bar is a plain `STATIC` child window created with `CreateWindowExA`. `UpdateStatus` reads the caret with `EM_EXGETSEL`, converts it to a line with `EM_EXLINEFROMCHAR` and a column with `EM_LINEINDEX`, formats `Ln %d, Col %d` with `wsprintfA`, and pushes it in with `SetWindowTextA`. The **Status Bar** menu item toggles `fStatus` and calls `ShowWindow`, while `RelayoutClient` resizes the editor and bar together on `WM_SIZE`.\n\n### Help\n\n**About** is a one-line `MessageBoxA`, and **View Help** opens the project URL in the default browser via `ShellExecuteA` — both cheaper than bundling any help content.\n\n## Credits\n\n- **TinyRetroPad** — fork adding the full Notepad-style menu set and dialogs.\n- **Dave's Tiny Editor (DTE)** — Matt Power, the sub-1KB RICHEDIT editor this fork is built on.\n- **`tiny.asm` / [HelloAssembly](https://github.com/PlummersSoftwareLLC/HelloAssembly)** — [Dave Plummer](https://github.com/davepl), the original foundation.\n\n## TinyRetroPad in use\n\n![TinyRetroPad in action](images/dte-in-action.png)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplummerssoftwarellc%2Ftinyretropad","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fplummerssoftwarellc%2Ftinyretropad","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplummerssoftwarellc%2Ftinyretropad/lists"}