<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: loach</title>
    <description>The latest articles on DEV Community by loach (@loach2009).</description>
    <link>https://dev.to/loach2009</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4119133%2F9a7d0107-ed42-4ca8-a57a-d5b67333a4f5.png</url>
      <title>DEV Community: loach</title>
      <link>https://dev.to/loach2009</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/loach2009"/>
    <language>en</language>
    <item>
      <title>PC Engine CD BIOS Devlog: Reaching BIOS API Entry Points in Local Retail Disc Traces</title>
      <dc:creator>loach</dc:creator>
      <pubDate>Tue, 10 Nov 2026 03:25:12 +0000</pubDate>
      <link>https://dev.to/loach2009/pc-engine-cd-bios-devlog-reaching-bios-api-entry-points-in-local-retail-disc-traces-2l53</link>
      <guid>https://dev.to/loach2009/pc-engine-cd-bios-devlog-reaching-bios-api-entry-points-in-local-retail-disc-traces-2l53</guid>
      <description>&lt;p&gt;I'm developing &lt;a href="https://github.com/mao2009/oss-pce-cd-bios" rel="noopener noreferrer"&gt;oss-pce-cd-bios&lt;/a&gt;, an open-source project aiming to replace the PC Engine / TurboGrafx-CD System Card firmware.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mao2009/zenn-articles/blob/main/articles-en/oss-pce-cd-bios-progress-2026-11-06.md" rel="noopener noreferrer"&gt;The previous update&lt;/a&gt; covered a multi-sector synthetic IPL loader running through a separate 256 KiB development image on a pinned Geargrafx core.&lt;/p&gt;

&lt;p&gt;Since then, the most interesting result isn't a larger test count. &lt;strong&gt;Local traces of two commercial CDs now reach BIOS API entry points after the IPL&lt;/strong&gt;, rather than stopping at the earlier media-layout mismatch.&lt;/p&gt;

&lt;p&gt;To be precise: &lt;strong&gt;neither disc reached a title screen, and no retail-game compatibility is claimed.&lt;/strong&gt; The observed calls still lead to unimplemented development stubs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting beyond the pre-API blocker
&lt;/h2&gt;

&lt;p&gt;The original diagnostic loader used a synthetic disc-layout assumption. On the locally held commercial CUE/BIN media, that assumption could place the IPL information-sector selection incorrectly, producing a &lt;code&gt;PRE_API_BLOCKER_OBSERVED&lt;/code&gt; result before the BIOS interface could even be studied.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/728" rel="noopener noreferrer"&gt;PR #728&lt;/a&gt; introduced explicit opt-in local CUE discovery. The &lt;code&gt;--commercial&lt;/code&gt; mode only considers local media under &lt;code&gt;./rom/&lt;/code&gt;; the public CI suite does not search for or consume private commercial images.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/730" rel="noopener noreferrer"&gt;PR #730&lt;/a&gt; then added a CUE-aware automatic loader configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parse the selected CUE text and derive the data track's &lt;code&gt;INDEX 01&lt;/code&gt; position using file-size metadata without reading the BIN payload.&lt;/li&gt;
&lt;li&gt;Apply that position to a purpose-built PUBLIC diagnostic loader, with separate DEV behavior preserved.&lt;/li&gt;
&lt;li&gt;Reject unsupported or ambiguous layouts and unsafe destination-overflow cases.&lt;/li&gt;
&lt;li&gt;Keep names, absolute paths, media content, hashes, and private sector coordinates out of the public manifest and evidence.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The resulting local measurements, made with a pinned Geargrafx core in cold and warm configurations, were:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Loader&lt;/th&gt;
&lt;th&gt;Two locally tested commercial discs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Previous synthetic-layout selection&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PRE_API_BLOCKER_OBSERVED&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Automatic DEV loader control&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PRE_API_BLOCKER_OBSERVED&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Automatic PUBLIC diagnostic loader&lt;/td&gt;
&lt;td&gt;&lt;code&gt;API_ENTRY_CONFIRMED&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One disc reached a JSR into candidate slot &lt;code&gt;$02&lt;/code&gt;; another reached a sequence of API calls followed by a stub halt. The observer checks the call/return sequence before classifying a result as confirmed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is an instrumentation milestone, not a functioning replacement BIOS.&lt;/strong&gt; The commercial bytes stay local, and no private traces or game-derived content are published to the repository or CI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Investigating CD_BASE without pretending the ABI is known
&lt;/h2&gt;

&lt;p&gt;The candidate at slot &lt;code&gt;$02&lt;/code&gt; is being investigated as &lt;code&gt;CD_BASE&lt;/code&gt;, but that identification and its precise ABI are &lt;strong&gt;not verified&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/731" rel="noopener noreferrer"&gt;PR #731&lt;/a&gt; builds a host-side discriminator for competing address-coordinate models: absolute LBA, track-relative, &lt;code&gt;INDEX 00&lt;/code&gt;-relative, and other caller-adjusted interpretations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/733" rel="noopener noreferrer"&gt;PR #733&lt;/a&gt; adds a sanitized observation catalogue and fail-closed schema, while &lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/736" rel="noopener noreferrer"&gt;PR #736&lt;/a&gt; rejects unknown or unsupported host-side &lt;code&gt;CD_BASE&lt;/code&gt; conditions.&lt;/p&gt;

&lt;p&gt;None of these PRs execute the proprietary reference entry point. The candidate's register behavior, side effects, error contract, and LBA origin remain unresolved. A model that can distinguish hypotheses is not a measurement that proves one hypothesis correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  Better refusal boundaries and stronger evidence gates
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/734" rel="noopener noreferrer"&gt;PR #734&lt;/a&gt; improves the synthetic multi-sector loader by probing its final target sector &lt;em&gt;before&lt;/em&gt; performing the first load-area write. Some end-of-media failures that previously occurred after partial copying are now rejected with zero copied bytes.&lt;/p&gt;

&lt;p&gt;This is still a reachability probe, &lt;strong&gt;not&lt;/strong&gt; a guarantee that all sectors belong to the intended data track. Other failure modes and the media track-length contract remain open.&lt;/p&gt;

&lt;p&gt;Separately, the pinned-core &lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/732" rel="noopener noreferrer"&gt;ST/IRQ/MMIO diagnostic in PR #732&lt;/a&gt; was merged after additional no-resume, instruction-fetch, and side-effect validation.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/738" rel="noopener noreferrer"&gt;E1–E3&lt;/a&gt;, &lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/739" rel="noopener noreferrer"&gt;E4–E5&lt;/a&gt;, and &lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/741" rel="noopener noreferrer"&gt;unified host judge&lt;/a&gt; work is also merged. These components exercise synthetic pre-block and device-access decisions, but their fixture-level PASS is &lt;strong&gt;not permission to execute reference firmware&lt;/strong&gt;. The production gate remains blocked.&lt;/p&gt;

&lt;h2&gt;
  
  
  The development ROM now has 15 entries
&lt;/h2&gt;

&lt;p&gt;Finally, &lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/748" rel="noopener noreferrer"&gt;PR #748&lt;/a&gt; added a project-defined &lt;code&gt;EX_JOYREP&lt;/code&gt; implementation at candidate slot &lt;code&gt;$22&lt;/code&gt;. The DEV contract returns while preserving the tested CPU state, stack, and page registers; pinned Geargrafx regression tests and an intentionally broken mutant check the behavior.&lt;/p&gt;

&lt;p&gt;As of the November 10 main snapshot (&lt;code&gt;890c149&lt;/code&gt;):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Evidence axis&lt;/th&gt;
&lt;th&gt;Current status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Project-owned DEV entry implementations&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;15/81&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remaining nonreturning stubs&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;66/81&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Independently verified reference BIOS ABIs&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0/81&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Permitted reference BIOS JSR entries&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0/81&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Demonstrated retail-game title screens&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Release eligibility&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;false&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Upcoming work includes safely characterizing the first required CD-related API contracts, replacing one stub at a time, and refining IPL address-range handling. Work in open draft PRs, including wider LBA support, is not counted as a merged capability.&lt;/p&gt;

&lt;p&gt;The project is still far from a distributable, compatible System Card. But reaching an &lt;strong&gt;observable BIOS call boundary on real local disc layouts&lt;/strong&gt; provides a more concrete target for the next implementation step than merely improving synthetic boot tests.&lt;/p&gt;

&lt;p&gt;Repository: &lt;a href="https://github.com/mao2009/oss-pce-cd-bios" rel="noopener noreferrer"&gt;https://github.com/mao2009/oss-pce-cd-bios&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Implementation evidence: &lt;a href="https://github.com/mao2009/oss-pce-cd-bios/blob/main/docs/status.md" rel="noopener noreferrer"&gt;https://github.com/mao2009/oss-pce-cd-bios/blob/main/docs/status.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is an independent project, unaffiliated with NEC, Hudson Soft, Konami, or other rights holders. It does not distribute proprietary BIOS images, retail game data, or extracted commercial code.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>gamedev</category>
      <category>opensource</category>
      <category>programming</category>
    </item>
    <item>
      <title>PC Engine CD BIOS Devlog: A Multi-Sector IPL Loader Now Runs from a 256 KiB DEV Image</title>
      <dc:creator>loach</dc:creator>
      <pubDate>Fri, 06 Nov 2026 03:16:38 +0000</pubDate>
      <link>https://dev.to/loach2009/pc-engine-cd-bios-devlog-a-multi-sector-ipl-loader-now-runs-from-a-256-kib-dev-image-3obh</link>
      <guid>https://dev.to/loach2009/pc-engine-cd-bios-devlog-a-multi-sector-ipl-loader-now-runs-from-a-256-kib-dev-image-3obh</guid>
      <description>&lt;p&gt;I'm building &lt;a href="https://github.com/mao2009/oss-pce-cd-bios" rel="noopener noreferrer"&gt;oss-pce-cd-bios&lt;/a&gt;, an original, open-source replacement project for the PC Engine / TurboGrafx-CD System Card BIOS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mao2009/zenn-articles/blob/main/articles-en/oss-pce-cd-bios-progress-2026-10-29.md" rel="noopener noreferrer"&gt;My previous update&lt;/a&gt; covered 14 development-only routines out of 81 candidate BIOS entry points, plus a synthetic CUE/BIN generator and an independent host-side oracle.&lt;/p&gt;

&lt;p&gt;As of &lt;strong&gt;November 6, 2026&lt;/strong&gt;, the number of DEV entry points is still &lt;strong&gt;14/81&lt;/strong&gt;. The meaningful progress this week is elsewhere: &lt;strong&gt;the project can load and execute a multi-sector, self-authored IPL through a separate 256 KiB development image on a pinned Geargrafx core&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This does &lt;strong&gt;not&lt;/strong&gt; mean the firmware can boot commercial PC Engine CD games. The loader implements a project-specific development contract, not a verified System Card ABI.&lt;/p&gt;

&lt;h2&gt;
  
  
  From a one-sector payload to a multi-sector IPL
&lt;/h2&gt;

&lt;p&gt;The first stage was &lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/669" rel="noopener noreferrer"&gt;PR #669&lt;/a&gt;. It added a DEV-only reset-to-execution path:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start from RESET.&lt;/li&gt;
&lt;li&gt;Read a selected synthetic CD sector with guest SCSI &lt;code&gt;READ(6)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Validate a project-defined &lt;code&gt;PCEX&lt;/code&gt; header on the guest.&lt;/li&gt;
&lt;li&gt;Copy the payload to RAM at &lt;code&gt;$3000&lt;/code&gt; and execute it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The test can keep the ROM byte-identical while changing the payload on disc, and observe a different result. That helps distinguish actual CD-sourced execution from a payload accidentally embedded in the ROM.&lt;/p&gt;

&lt;p&gt;But this path only handled a single-sector payload.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/681" rel="noopener noreferrer"&gt;PR #681&lt;/a&gt; moves to a &lt;strong&gt;multi-sector synthetic IPL&lt;/strong&gt;. The loader reads an information sector, validates metadata &lt;em&gt;before&lt;/em&gt; writing to the destination, and then streams sectors through a staging area into RAM.&lt;/p&gt;

&lt;p&gt;It handles memory page register (MPR) changes across 8 KiB windows, including test cases that cross &lt;code&gt;$3FFF/$4000&lt;/code&gt; and &lt;code&gt;$5FFF/$6000&lt;/code&gt;. After copying, it reads the destination back and checks a project-defined 16-bit additive checksum before transferring control to the loaded entry point.&lt;/p&gt;

&lt;p&gt;Malformed metadata and read failures must result in refusal rather than accidental execution. The checksum is explicitly a &lt;strong&gt;DEV extension&lt;/strong&gt;, not an established commercial IPL standard, and it cannot catch every possible corruption.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving the same loader into the BIOS loading path
&lt;/h2&gt;

&lt;p&gt;An important distinction: PR #681 originally ran on an &lt;strong&gt;8 KiB HuCARD + synthetic CUE&lt;/strong&gt; test path. That proved the guest-side multi-sector loader worked on a real pinned emulator core, but it did not prove that the image could be loaded as a System Card BIOS.&lt;/p&gt;

&lt;p&gt;The follow-up, &lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/683" rel="noopener noreferrer"&gt;PR #683&lt;/a&gt;, packages the same loader into a &lt;strong&gt;separate 262,144-byte DEV image&lt;/strong&gt; and runs it using Geargrafx's &lt;code&gt;LoadBiosFromBuffer&lt;/code&gt; plus &lt;code&gt;LoadMedia&lt;/code&gt; path.&lt;/p&gt;

&lt;p&gt;The image builder checks its ROM layout, reset vector, and expected regions. The integration test checks a loader-identification CAP record so that an unexpected fallback to the HuCARD path cannot silently pass.&lt;/p&gt;

&lt;p&gt;According to the merged PR evidence:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test path&lt;/th&gt;
&lt;th&gt;Synthetic scenarios&lt;/th&gt;
&lt;th&gt;Cold/warm executions&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;8 KiB HuCARD + CUE (#681)&lt;/td&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;44&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;256 KiB DEV image via &lt;code&gt;LoadBiosFromBuffer&lt;/code&gt; (#683)&lt;/td&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;44&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each path reports passing results for four positive scenarios, 15 rejection scenarios, two copy/read-failure scenarios, and one deliberately mutated-ROM readback-failure scenario.&lt;/p&gt;

&lt;p&gt;Those are &lt;strong&gt;scenario executions reported for the respective changes&lt;/strong&gt;, not 88 distinct commercial games or an independent hardware compatibility test. The readback fault is injected by changing test ROM behavior, not by simulating a real failing CD bus.&lt;/p&gt;

&lt;p&gt;Also, the 256 KiB loader image is &lt;strong&gt;not&lt;/strong&gt; the ordinary &lt;code&gt;make dev-rom&lt;/code&gt; output promoted to a completed BIOS. It is a purpose-built development artifact used to exercise a specific boot path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the edges of HuC6280 behavior
&lt;/h2&gt;

&lt;p&gt;There has also been less visible work on the surrounding verification infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/610" rel="noopener noreferrer"&gt;PR #610&lt;/a&gt; rechecks instruction boundaries after MPR remapping. &lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/614" rel="noopener noreferrer"&gt;PR #614&lt;/a&gt; adds a &lt;strong&gt;51-case&lt;/strong&gt; pinned-core block-transfer matrix, including lengths, page boundaries, aliases, prohibited destinations, and checks that preflight inspection doesn't mutate the MPR state.&lt;/p&gt;

&lt;p&gt;On the observability side, &lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/620" rel="noopener noreferrer"&gt;PR #620&lt;/a&gt; separates measured output-ring loss from capacity merely reported by the core. A value that cannot be measured reliably remains labelled &lt;code&gt;NOT_MEASURED&lt;/code&gt;, rather than being turned into a precise-looking number.&lt;/p&gt;

&lt;p&gt;These tests improve the evidence available to the project. They do not constitute a production-safe generic interception system or establish proprietary firmware ABI parity.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is actually verified?
&lt;/h2&gt;

&lt;p&gt;The current public status remains deliberately conservative:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;November 6 status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Project-owned DEV entry implementations&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;14/81&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nonreturning stubs&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;67/81&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Independently verified reference ABI contracts&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0/81&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Owner-approved &lt;code&gt;DEV_CALLABLE&lt;/code&gt; entries&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Permitted reference BIOS JSR entries&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0/81&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Demonstrated commercial BIOS-mediated boots&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Firmware release eligibility&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;false&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;No callable System Card-compatible &lt;code&gt;CD_RESET&lt;/code&gt;, &lt;code&gt;CD_BASE&lt;/code&gt;, &lt;code&gt;CD_READ&lt;/code&gt;, or &lt;code&gt;CD_BOOT&lt;/code&gt; API has been independently verified. No retail game boot, second-emulator equivalence, or physical-hardware compatibility is established.&lt;/p&gt;

&lt;p&gt;The next steps are to establish safe, independently checked reference calling contracts and build real caller-visible CD services on top of the now-testable loading path.&lt;/p&gt;

&lt;p&gt;It's easy to make firmware progress sound impressive by counting routines or test cases. For this project, the more useful milestone is narrower: &lt;strong&gt;we can observe a multi-sector synthetic payload being read, mapped, checked, and executed along a specific development boot path—and describe precisely what that evidence does not prove.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Repository: &lt;a href="https://github.com/mao2009/oss-pce-cd-bios" rel="noopener noreferrer"&gt;https://github.com/mao2009/oss-pce-cd-bios&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dated implementation status: &lt;a href="https://github.com/mao2009/oss-pce-cd-bios/blob/main/docs/status.md" rel="noopener noreferrer"&gt;https://github.com/mao2009/oss-pce-cd-bios/blob/main/docs/status.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;DEV IPL boot notes: &lt;a href="https://github.com/mao2009/oss-pce-cd-bios/blob/main/docs/dev-ipl-boot.md" rel="noopener noreferrer"&gt;https://github.com/mao2009/oss-pce-cd-bios/blob/main/docs/dev-ipl-boot.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This project is independent of NEC, Hudson Soft, Konami, and other rights holders. It does not distribute proprietary BIOS images, commercial game data, or extracted copyrighted code.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>opensource</category>
      <category>programming</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>PC Engine CD BIOS Progress: 14 DEV Entries and a Synthetic Boot-Media Testbed</title>
      <dc:creator>loach</dc:creator>
      <pubDate>Thu, 29 Oct 2026 03:15:03 +0000</pubDate>
      <link>https://dev.to/loach2009/pc-engine-cd-bios-progress-14-dev-entries-and-a-synthetic-boot-media-testbed-dhe</link>
      <guid>https://dev.to/loach2009/pc-engine-cd-bios-progress-14-dev-entries-and-a-synthetic-boot-media-testbed-dhe</guid>
      <description>&lt;p&gt;I'm working on &lt;a href="https://github.com/mao2009/oss-pce-cd-bios" rel="noopener noreferrer"&gt;oss-pce-cd-bios&lt;/a&gt;, an open-source effort to create a redistributable replacement for the PC Engine / TurboGrafx-CD System Card BIOS.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/loach2009/pc-engine-cd-bios-progress-8-dev-entries-0-verified-compatible-apis-3cl8"&gt;my October 22 update&lt;/a&gt;, the project had eight project-defined implementations connected to its 81 candidate entry points.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;As of October 29, the main branch (&lt;code&gt;94ef337&lt;/code&gt;) has 14 DEV implementations and 67 nonreturning stubs.&lt;/strong&gt; That's real progress in original firmware code, but it is not 14 verified System Card APIs.&lt;/p&gt;

&lt;p&gt;There is also a new synthetic disc testbed, with an important distinction: the new media checks run on the host; they do not demonstrate successful BIOS-mediated CD boot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Six more development ROM entries
&lt;/h2&gt;

&lt;p&gt;The 256 KiB development-only ROM gained six entries since the previous article:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Entry&lt;/th&gt;
&lt;th&gt;DEV implementation&lt;/th&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EX_RND&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Random-number generation&lt;/td&gt;
&lt;td&gt;Project-defined behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MA_MUL16S&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Signed 16-bit multiplication&lt;/td&gt;
&lt;td&gt;Original arithmetic routine and contract tests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MA_SQRT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Integer square root&lt;/td&gt;
&lt;td&gt;Original integer routine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MA_SIN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sine&lt;/td&gt;
&lt;td&gt;Project-defined signed 16-bit Q15 representation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MA_COS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cosine&lt;/td&gt;
&lt;td&gt;Project-defined Q15 representation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EX_SETWRT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set VDC MAWR&lt;/td&gt;
&lt;td&gt;Development-only VRAM write-address setting&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are entries in a project-owned implementation manifest. All 81 readiness states remain &lt;code&gt;NOT_READY&lt;/code&gt;, regardless of whether the entry has implementation code.&lt;/p&gt;

&lt;p&gt;The recent &lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/527" rel="noopener noreferrer"&gt;MA_SIN / MA_COS work in PR #527&lt;/a&gt; is an example of why the details matter. The two functions use a development-specific numeric representation, with tests for input coverage, sign and rounding rules, register preservation, and unwanted memory effects. A correct sine value by itself would not establish the caller-visible reference ABI.&lt;/p&gt;

&lt;p&gt;The latest &lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/524" rel="noopener noreferrer"&gt;EX_SETWRT work in PR #524&lt;/a&gt; reaches beyond arithmetic. It connects slot &lt;code&gt;$3A&lt;/code&gt; to original code that sets the Video Display Controller's MAWR (the VRAM write-address register). Its verification includes host checks, observations with a pinned Geargrafx core, and negative/mutation cases.&lt;/p&gt;

&lt;p&gt;Those checks provide evidence about the &lt;strong&gt;DEV contract&lt;/strong&gt; and the measured emulator behavior. They are not evidence that the proprietary System Card's &lt;code&gt;EX_SETWRT&lt;/code&gt; calling convention and side effects have been reproduced in full, much less that physical hardware has been validated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building synthetic boot media without borrowing game data
&lt;/h2&gt;

&lt;p&gt;Another merged change, &lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/556" rel="noopener noreferrer"&gt;PR #556&lt;/a&gt;, adds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;tools/synthetic_boot_media.py&lt;/code&gt;: a generator for self-authored CUE/BIN media.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tools/synthetic_boot_oracle.py&lt;/code&gt;: an independent host-side parser and checker.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tests/host/test_synthetic_boot_media.py&lt;/code&gt;: regression cases for supported layouts and malformed inputs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The supported V1/V2 layouts exercise an audio track followed by a MODE1/2352 data track, including a V2 pregap. The host oracle parses the generated media independently instead of importing the generator's answers.&lt;/p&gt;

&lt;p&gt;That separation matters. Testing only a generator against its own assumptions can miss a shared mistake; a separately implemented checker gives us a better chance of rejecting an invalid layout.&lt;/p&gt;

&lt;p&gt;The multi-file V2M format is still unsupported. As clarified in the follow-up &lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/559" rel="noopener noreferrer"&gt;specification update in PR #559&lt;/a&gt;, &lt;strong&gt;these are host-side media validation results, not successful CD boot observations on the actual Geargrafx CPU core&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The project previously demonstrated guest-side SCSI &lt;code&gt;READ(6)&lt;/code&gt; and execution of a self-authored payload copied from CD to RAM. That was a DEV diagnostic, not a callable System Card &lt;code&gt;CD_READ&lt;/code&gt;, &lt;code&gt;CD_EXEC&lt;/code&gt;, or full BIOS boot implementation. The new synthetic media work does not change that boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measuring firmware progress without a misleading percentage
&lt;/h2&gt;

&lt;p&gt;Here is the current evidence split:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Measurement&lt;/th&gt;
&lt;th&gt;October 29 main&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DEV ROM implementations&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;14/81&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remaining nonreturning stubs&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;67/81&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Independently VERIFIED reference BIOS ABIs&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0/81&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Owner-approved &lt;code&gt;DEV_CALLABLE&lt;/code&gt; entries&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verified BIOS-mediated CD boots&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Release eligibility&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;false&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The project explicitly keeps a reference-code execution gate closed. A development entry that runs under an original contract is not automatically compatible with an existing game, nor does static analysis authorize executing a proprietary reference ROM entry point.&lt;/p&gt;

&lt;p&gt;The next work is to establish safely verified caller contracts, implement callable CD services such as &lt;code&gt;CD_RESET&lt;/code&gt; and &lt;code&gt;CD_READ&lt;/code&gt;, and boot a self-authored disc through actual BIOS services. Independent emulator comparison and physical-hardware verification remain future tasks.&lt;/p&gt;

&lt;p&gt;There is no releasable replacement BIOS or demonstrated retail-game compatibility yet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Going from eight to fourteen original entry points is useful. Keeping that progress separate from the zero verified reference ABIs is essential.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Repository: &lt;a href="https://github.com/mao2009/oss-pce-cd-bios" rel="noopener noreferrer"&gt;https://github.com/mao2009/oss-pce-cd-bios&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Implementation status and evidence boundaries: &lt;a href="https://github.com/mao2009/oss-pce-cd-bios/blob/main/docs/status.md" rel="noopener noreferrer"&gt;https://github.com/mao2009/oss-pce-cd-bios/blob/main/docs/status.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This project is independent of NEC, Hudson Soft, Konami, and other rights holders. It does not distribute proprietary BIOS images, commercial game images, or game-derived data.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>github</category>
      <category>gamedev</category>
      <category>opensource</category>
    </item>
    <item>
      <title>PC Engine CD BIOS Progress: 8 DEV Entries, 0 Verified Compatible APIs</title>
      <dc:creator>loach</dc:creator>
      <pubDate>Thu, 22 Oct 2026 03:26:53 +0000</pubDate>
      <link>https://dev.to/loach2009/pc-engine-cd-bios-progress-8-dev-entries-0-verified-compatible-apis-3cl8</link>
      <guid>https://dev.to/loach2009/pc-engine-cd-bios-progress-8-dev-entries-0-verified-compatible-apis-3cl8</guid>
      <description>&lt;p&gt;I'm working on &lt;a href="https://github.com/mao2009/oss-pce-cd-bios" rel="noopener noreferrer"&gt;oss-pce-cd-bios&lt;/a&gt;, an effort to build an open-source replacement for the PC Engine / TurboGrafx-CD System Card BIOS.&lt;/p&gt;

&lt;p&gt;My previous update focused on original HuC6280 routines and a synthetic CD-to-ADPCM DMA diagnostic. This update has a more visible milestone:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The development ROM now has eight implemented entry points out of 81 candidates, up from just one in the previous post.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's genuine implementation progress. But it is &lt;strong&gt;not&lt;/strong&gt; the same as eight compatible System Card services, and it doesn't mean any commercial CD game boots.&lt;/p&gt;

&lt;h2&gt;
  
  
  From standalone routines to actual DEV ROM entries
&lt;/h2&gt;

&lt;p&gt;The current 256 KiB development-only image has project-owned implementations installed at these candidate jump-table slots:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Slot&lt;/th&gt;
&lt;th&gt;Routine&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$1E&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;EX_GETVER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Development version response&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$3C&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;EX_BINBCD&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Numeric conversion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$3D&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;EX_BCDBIN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Numeric conversion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$3F&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;MA_MUL8U&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unsigned 8-bit multiplication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$40&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;MA_MUL8S&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Signed 8-bit multiplication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$41&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;MA_MUL16U&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unsigned 16-bit multiplication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$42&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;MA_DIV16S&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Signed 16-bit division&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$43&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;MA_DIV16U&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unsigned 16-bit division&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The remaining &lt;strong&gt;73 slots are nonreturning stubs&lt;/strong&gt;, not functional API implementations.&lt;/p&gt;

&lt;p&gt;Previously, several math and conversion routines were tested only as independent components. They have now been wired into the development ROM's actual entry points and exercised under a project-defined calling convention with a pinned &lt;a href="https://github.com/drhelius/Geargrafx" rel="noopener noreferrer"&gt;Geargrafx&lt;/a&gt; core.&lt;/p&gt;

&lt;p&gt;The newest addition, &lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/480" rel="noopener noreferrer"&gt;&lt;code&gt;MA_DIV16U&lt;/code&gt; in PR #480&lt;/a&gt;, checks quotient/remainder behavior, divide-by-zero handling, stack boundaries, caller interrupt/decimal flag combinations, and changes across the 8 KiB working RAM. Deliberately modified variants must be rejected for the &lt;em&gt;expected&lt;/em&gt; reason rather than merely producing any failure.&lt;/p&gt;

&lt;p&gt;The signed/unsigned arithmetic routines are original implementations, and their DEV-side contracts are being tested. They &lt;strong&gt;are not claims of compatibility with the proprietary reference System Card ABI&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why 8/81 isn't a compatibility percentage
&lt;/h2&gt;

&lt;p&gt;The project now tracks separate measurements instead of merging different kinds of evidence into one progress bar:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Measurement&lt;/th&gt;
&lt;th&gt;Current main&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Project-defined DEV ROM entries implemented&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8/81&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remaining nonreturning stubs&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;73/81&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reference BIOS ABIs independently VERIFIED&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0/81&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Owner-approved DEV_CALLABLE entries&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reference BIOS JSR executions permitted by the current gate&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0/81&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verified BIOS-mediated CD program boots&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This distinction is important. An entry can execute and return according to &lt;strong&gt;our&lt;/strong&gt; development contract without proving that a game can call it using the &lt;strong&gt;original&lt;/strong&gt; System Card's parameters, registers, flags, memory layout, or error behavior.&lt;/p&gt;

&lt;p&gt;In other words, the source code has advanced much faster than compatibility certification. The latter remains intentionally conservative.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/mao2009/oss-pce-cd-bios/blob/main/docs/status.md" rel="noopener noreferrer"&gt;current status ledger&lt;/a&gt; documents these independent axes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learning about the reference BIOS without executing it
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/472" rel="noopener noreferrer"&gt;static ABI investigation in PR #472&lt;/a&gt; added a local-only auditor and evidence ledger for seven reference BIOS entry points.&lt;/p&gt;

&lt;p&gt;Instead of jumping directly into proprietary firmware code, the audit records narrowly scoped static properties: control-flow shape, memory writes, bank-register operations, and hazards that would make an execution experiment unsafe.&lt;/p&gt;

&lt;p&gt;One important observation is that some reference math entry points are dispatchers that change memory page registers. An apparently simple multiplication API can therefore carry hardware-visible side effects that an independent arithmetic routine does not reproduce.&lt;/p&gt;

&lt;p&gt;A static safety classification is &lt;strong&gt;not&lt;/strong&gt; permission to run the reference code. The project still blocks all 81 reference JSR entry points. No proprietary BIOS bytes, extracted code, or commercial game content were published with this work.&lt;/p&gt;

&lt;h2&gt;
  
  
  A guard can detect trouble too late
&lt;/h2&gt;

&lt;p&gt;Another revealing experiment is &lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/481" rel="noopener noreferrer"&gt;PR #481&lt;/a&gt;, which tested a synthetic HuC6280 caller/stack contract on a pinned, unmodified Geargrafx core.&lt;/p&gt;

&lt;p&gt;With the expected WRAM stack mapping, the test caller returned and its measured CPU state was preserved. With the stack mapped to I/O instead, the CPU's JSR and subsequent stack pushes changed VDC state &lt;strong&gt;before the guard detected the invalid mapping&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This demonstrates a subtle failure of the “check, then refuse” approach: checking after a side-effecting instruction cannot undo the side effect that already happened.&lt;/p&gt;

&lt;p&gt;The result doesn't prove a production-safe guard exists. Nor is the experiment a complete I/O bus trace or a real-hardware result. It provides a reproducible counterexample that the eventual safety design must address.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;As of &lt;strong&gt;October 22, 2026&lt;/strong&gt;, the goal is still a genuinely compatible and redistributable System Card implementation—not merely a development ROM with increasingly many working routines.&lt;/p&gt;

&lt;p&gt;The immediate work is to verify caller-visible ABI contracts safely, establish testable CD services such as &lt;code&gt;CD_RESET&lt;/code&gt; and &lt;code&gt;CD_READ&lt;/code&gt;, and eventually boot a &lt;strong&gt;self-authored disc through actual BIOS services&lt;/strong&gt;. Comparison against a second emulator and physical hardware will be needed before broader compatibility claims.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No retail game boot has been demonstrated. The reference ABI has zero VERIFIED entries. The release gate remains blocked (&lt;code&gt;release_eligible: false&lt;/code&gt;).&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Going from one to eight implemented DEV entries is meaningful progress. Keeping that separate from “eight compatible BIOS APIs” is just as important.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/mao2009/oss-pce-cd-bios" rel="noopener noreferrer"&gt;https://github.com/mao2009/oss-pce-cd-bios&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is an independent project, unaffiliated with NEC, Hudson Soft, Konami, or other rights holders. It does not distribute proprietary BIOS images, commercial game images, or game-derived content.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>opensource</category>
      <category>programming</category>
    </item>
    <item>
      <title>PC Engine CD BIOS Progress: Tested HuC6280 Routines and ADPCM DMA</title>
      <dc:creator>loach</dc:creator>
      <pubDate>Wed, 21 Oct 2026 03:48:30 +0000</pubDate>
      <link>https://dev.to/loach2009/pc-engine-cd-bios-progress-tested-huc6280-routines-and-adpcm-dma-2c64</link>
      <guid>https://dev.to/loach2009/pc-engine-cd-bios-progress-tested-huc6280-routines-and-adpcm-dma-2c64</guid>
      <description>&lt;p&gt;I'm building &lt;a href="https://github.com/mao2009/oss-pce-cd-bios" rel="noopener noreferrer"&gt;oss-pce-cd-bios&lt;/a&gt;, an open-source project aiming for a redistributable replacement for the PC Engine / TurboGrafx-CD System Card BIOS.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://dev.to/loach2009/building-an-open-source-pc-engine-cd-bios-from-reading-sectors-to-running-code-from-ram-41ij"&gt;previous update&lt;/a&gt;, I described reading sectors from a self-authored CD image and executing a small synthetic HuC6280 payload from RAM. Those were isolated diagnostic experiments—not CD game boot.&lt;/p&gt;

&lt;p&gt;Since then, the focus has moved toward &lt;strong&gt;implementing original low-level routines and testing their actual effects in a pinned emulator core&lt;/strong&gt;. The results are useful, but it's important to keep the boundary between a tested routine and a compatible BIOS service explicit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Seven original routines exercised in the actual CPU core
&lt;/h2&gt;

&lt;p&gt;One tangible change is a set of independent HuC6280 assembly routines. The current opt-in real-core test target covers seven:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;EX_BINBCD&lt;/code&gt; and &lt;code&gt;EX_BCDBIN&lt;/code&gt; (numeric conversions)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MA_MUL8S&lt;/code&gt; and &lt;code&gt;MA_MUL8U&lt;/code&gt; (8-bit multiplication)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MA_MUL16U&lt;/code&gt; (16-by-16-bit unsigned multiplication)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MA_DIV16U&lt;/code&gt; and &lt;code&gt;MA_DIV16S&lt;/code&gt; (16-bit division)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These aren't just Python models: the test harnesses execute original guest instructions with a pinned &lt;a href="https://github.com/drhelius/Geargrafx" rel="noopener noreferrer"&gt;Geargrafx&lt;/a&gt; core and check outcomes and relevant CPU state. For example, the unsigned 16-bit division work exercises edge cases and 65,536 generated operand pairs while checking quotient/remainder invariants, register/stack behavior, and deliberately broken variants.&lt;/p&gt;

&lt;p&gt;An additional &lt;code&gt;EX_RND&lt;/code&gt; implementation provides an original 16-bit LFSR-based random-number routine with its own tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;None of these standalone routines has been promoted to a caller-compatible System Card API.&lt;/strong&gt; Their local contracts are not automatically the reference BIOS calling conventions. In particular, they are not connected to the development ROM's API jump table.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/391" rel="noopener noreferrer"&gt;real-core integration change&lt;/a&gt; records the seven-routine target; the &lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/386" rel="noopener noreferrer"&gt;division implementation&lt;/a&gt; is a concrete example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving a synthetic CD sector through ADPCM DMA
&lt;/h2&gt;

&lt;p&gt;The earlier CD read experiment transferred sector data into ordinary guest RAM. A newer experiment exercises a different hardware path.&lt;/p&gt;

&lt;p&gt;A self-authored HuC6280 guest issues SCSI &lt;code&gt;READ(6)&lt;/code&gt; through the emulated CD interface and arms ADPCM DMA via register &lt;code&gt;$180B&lt;/code&gt;, moving data into ADPCM RAM starting at &lt;code&gt;$0100&lt;/code&gt;. The host probe observes the resulting device state and memory; it does &lt;strong&gt;not&lt;/strong&gt; perform the transfer by directly calling a host-side ADPCM write method.&lt;/p&gt;

&lt;p&gt;The test includes both a successful transfer and a negative case where DMA is not armed. It also stores the raw observations, rechecks them against the source/build hashes and known toolchain/core versions, and verifies that twelve selected manipulations of the evidence are rejected.&lt;/p&gt;

&lt;p&gt;That extra evidence work matters: if a result can't be reproduced or its recorded inputs can't be bound to what actually ran, a green status is much less informative.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/406" rel="noopener noreferrer"&gt;ADPCM DMA experiment&lt;/a&gt; was merged on October 21.&lt;/p&gt;

&lt;p&gt;But the boundary remains clear: this is a &lt;strong&gt;device-layer transfer diagnostic&lt;/strong&gt;. It does not implement the System Card &lt;code&gt;AD_TRANS&lt;/code&gt; service, establish its ABI, or make the BIOS bootable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Observing CD status and subcode-Q without claiming BIOS compatibility
&lt;/h2&gt;

&lt;p&gt;Two more lines of investigation now have real-core observations.&lt;/p&gt;

&lt;p&gt;For the CD status work, a test distinguishes a synthetic disc from no media and checks how cases such as an unrecognised disc and selection timeout are classified. For subcode-Q, the project decodes an observed &lt;code&gt;$DD&lt;/code&gt; response and checks track/time/status information at a particular synthetic-disc location.&lt;/p&gt;

&lt;p&gt;The real-core subcode-Q result is deliberately narrow: &lt;strong&gt;track 2 at LBA 66&lt;/strong&gt;, with playing, paused, and stopped states. Other track boundaries have host-side synthetic tests, but those cases have not all been measured in the actual emulator core.&lt;/p&gt;

&lt;p&gt;These investigations provide building blocks for future &lt;code&gt;CD_STAT&lt;/code&gt; and &lt;code&gt;CD_SUBQ&lt;/code&gt; work. They do &lt;strong&gt;not&lt;/strong&gt; mean their reference System Card entry points have been executed or reproduced.&lt;/p&gt;

&lt;p&gt;See the &lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/409" rel="noopener noreferrer"&gt;CD status diagnostic&lt;/a&gt; and &lt;a href="https://github.com/mao2009/oss-pce-cd-bios/pull/412" rel="noopener noreferrer"&gt;subcode-Q diagnostic&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making the test boundary harder to misunderstand
&lt;/h2&gt;

&lt;p&gt;The default test workflow has also been clarified:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;make &lt;span class="nb"&gt;test&lt;/span&gt;            &lt;span class="c"&gt;# build and host-side checks&lt;/span&gt;
make test-real-core  &lt;span class="c"&gt;# required Geargrafx integration checks&lt;/span&gt;
make routine-real-core  &lt;span class="c"&gt;# opt-in standalone routine checks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The required CI path still runs the real-core checks; they weren't removed to make the host suite greener. The separate standalone-routine target remains opt-in rather than a required CI gate. Missing prerequisites and tests that haven't run must not be reported as passing observations.&lt;/p&gt;

&lt;p&gt;Another diagnostic now checks all 8 KiB of working RAM around a &lt;strong&gt;synthetic ROM-to-RAM handoff&lt;/strong&gt;, rejecting unexpected modifications. It does not read code from a CD or demonstrate the actual &lt;code&gt;CD_EXEC&lt;/code&gt; API.&lt;/p&gt;

&lt;h2&gt;
  
  
  So how many BIOS APIs actually work?
&lt;/h2&gt;

&lt;p&gt;The honest answer is still small.&lt;/p&gt;

&lt;p&gt;The development ROM has &lt;strong&gt;81 candidate API slots, and exactly one is callable as a development-only service: &lt;code&gt;EX_GETVER&lt;/code&gt;&lt;/strong&gt;. The other 80 slots remain nonreturning stubs. Adding independently tested arithmetic routines does &lt;em&gt;not&lt;/em&gt; change that number.&lt;/p&gt;

&lt;p&gt;The reference System Card ABI remains unverified. Callable &lt;code&gt;CD_RESET&lt;/code&gt;, &lt;code&gt;CD_READ&lt;/code&gt;, and &lt;code&gt;CD_EXEC&lt;/code&gt; services, BIOS-mediated homebrew CD boot, commercial-game compatibility, and a releasable firmware image are still missing. The release gate remains blocked.&lt;/p&gt;

&lt;p&gt;The distinction is the central point of this update:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A tested HuC6280 routine isn't necessarily a compatible BIOS API. A demonstrated DMA path isn't &lt;code&gt;AD_TRANS&lt;/code&gt;. An observed CD status response isn't a finished &lt;code&gt;CD_STAT&lt;/code&gt; service.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Still, these experiments move the project from preparing diagnostic scaffolding toward original, measured building blocks. That's the progress I want to report—without claiming a usable replacement BIOS before the evidence supports one.&lt;/p&gt;

&lt;p&gt;If you're interested in HuC6280 assembly, firmware replacement, CD-ROM² devices, or emulator-driven verification, technical feedback is welcome.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/mao2009/oss-pce-cd-bios" rel="noopener noreferrer"&gt;https://github.com/mao2009/oss-pce-cd-bios&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is an independent project, unaffiliated with NEC, Hudson Soft, Konami, or other rights holders. It does not distribute proprietary BIOS images, commercial game images, or game-derived data.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>assembly</category>
      <category>gamedev</category>
      <category>opensource</category>
      <category>programming</category>
    </item>
    <item>
      <title>Building an Open-Source PC Engine CD BIOS: From Reading Sectors to Running Code from RAM</title>
      <dc:creator>loach</dc:creator>
      <pubDate>Fri, 16 Oct 2026 04:21:00 +0000</pubDate>
      <link>https://dev.to/loach2009/building-an-open-source-pc-engine-cd-bios-from-reading-sectors-to-running-code-from-ram-41ij</link>
      <guid>https://dev.to/loach2009/building-an-open-source-pc-engine-cd-bios-from-reading-sectors-to-running-code-from-ram-41ij</guid>
      <description>&lt;p&gt;I'm working on &lt;a href="https://github.com/mao2009/oss-pce-cd-bios" rel="noopener noreferrer"&gt;oss-pce-cd-bios&lt;/a&gt;, an open-source effort to create a redistributable replacement for the &lt;strong&gt;PC Engine / TurboGrafx-CD System Card BIOS&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The initial long-term target is behavior compatible with Super System Card 3.0, using original code and third-party OSS only where its licensing and suitability have been reviewed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The project does not have a usable replacement BIOS yet.&lt;/strong&gt; No commercial CD title has been shown booting with it. But the development experiments have progressed from building diagnostic ROMs to reading a synthetic CD sector &lt;em&gt;and executing code loaded from it&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Here's what has been established so far—and what hasn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting useful evidence from the real emulator core
&lt;/h2&gt;

&lt;p&gt;The PC Engine uses a HuC6280 CPU, bank-switched memory, and a separate CD-ROM interface. A replacement System Card needs more than a ROM file with the expected size: it needs correct startup behavior, bank mapping, calling conventions, and device interactions.&lt;/p&gt;

&lt;p&gt;I'm using a pinned build of &lt;a href="https://github.com/drhelius/Geargrafx" rel="noopener noreferrer"&gt;Geargrafx&lt;/a&gt; as the first instrumented execution environment.&lt;/p&gt;

&lt;p&gt;The repository now builds original diagnostic ROMs and checks actual CPU execution, including program-counter transitions, MPR (memory page register) state, stack behavior, and working RAM. There are also deliberately broken ROM variants that must &lt;em&gt;fail&lt;/em&gt; the same checks.&lt;/p&gt;

&lt;p&gt;The 256 KiB development image includes a scaffold for &lt;strong&gt;81 candidate System Card API slots&lt;/strong&gt;. Those entries are placeholders that stop rather than return success. &lt;strong&gt;None of those 81 services is implemented as a compatible BIOS API yet.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mao2009/oss-pce-cd-bios/blob/main/docs/api-stub-scaffold.md" rel="noopener noreferrer"&gt;Diagnostic API scaffold and its limits&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading 2,048 bytes from a synthetic CD
&lt;/h2&gt;

&lt;p&gt;The next step was an independent, development-only HuC6280 program that drives the emulated CD-ROM² controller.&lt;/p&gt;

&lt;p&gt;It sends a SCSI &lt;code&gt;READ(6)&lt;/code&gt; command through the CD interface registers and transfers a MODE1 data sector to guest RAM. The verifier checks all &lt;strong&gt;2,048 user-data bytes&lt;/strong&gt; against a separately generated reference. A three-sector, &lt;strong&gt;6,144-byte&lt;/strong&gt; case is covered too.&lt;/p&gt;

&lt;p&gt;This runs through guest HuC6280 instructions in the actual Geargrafx core, rather than copying the expected data into RAM from the host side. Cold- and warm-reset cases are checked, along with negative scenarios such as invalid addresses, damaged disc data, and interrupted transfers.&lt;/p&gt;

&lt;p&gt;This is an important low-level milestone, but it is &lt;strong&gt;not&lt;/strong&gt; the System Card &lt;code&gt;CD_READ&lt;/code&gt; API. Its calling convention, return behavior, interrupts, retries, and compatibility with real hardware remain separate work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mao2009/oss-pce-cd-bios/blob/main/docs/cd-read-diagnostic.md" rel="noopener noreferrer"&gt;CD sector-read diagnostic&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A new milestone: load code from CD and execute it in RAM
&lt;/h2&gt;

&lt;p&gt;The most recent experiment connects a few of these pieces into one traceable sequence.&lt;/p&gt;

&lt;p&gt;A small, independently assembled &lt;strong&gt;28-byte HuC6280 program&lt;/strong&gt; is embedded in sector 5 of an entirely synthetic CUE/BIN image. The development ROM then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Synthetic CD sector (LBA 5)
             |
       SCSI READ(6)
             |
   2,048 bytes into RAM
             |
   Validate payload header
             |
     Copy code to $3000
             |
      Jump to RAM code
             |
   Write observable markers
             |
 Return to diagnostic ROM
             |
       Confirm halt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The test checks the order of the actual execution checkpoints, the bytes copied into RAM, and the marker values written by the downloaded code. It also confirms the return to the diagnostic ROM and the final halt over 64 instructions.&lt;/p&gt;

&lt;p&gt;Corrupting the payload checksum, header, or entry offset must prevent execution. Other negative tests ensure that simply reaching an expected-looking result cannot substitute for the correct execution path.&lt;/p&gt;

&lt;p&gt;This is &lt;strong&gt;a custom load-and-execute experiment&lt;/strong&gt;, not CD game boot. It does not implement the System Card boot process or prove that any existing game can run.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mao2009/oss-pce-cd-bios/blob/main/docs/cd-load-exec-probe.md" rel="noopener noreferrer"&gt;Load-and-execute implementation and evidence&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The less visible work matters too
&lt;/h2&gt;

&lt;p&gt;A few supporting pieces have landed alongside the ROM experiments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Disc inspection:&lt;/strong&gt; CUE/BIN and TOC diagnostic tools check track boundaries, &lt;code&gt;INDEX 00/01&lt;/code&gt;, pregaps, LBA/MSF calculations, and malformed inputs using synthetic fixtures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bus-reset observation:&lt;/strong&gt; a separate Geargrafx experiment measures two reset pulses through &lt;code&gt;$1804&lt;/code&gt; and verifies the bus is free afterward. This is not proof of the System Card &lt;code&gt;CD_RESET&lt;/code&gt; service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evidence and isolation:&lt;/strong&gt; an isolated trace harness distinguishes &lt;code&gt;PASS&lt;/code&gt;, &lt;code&gt;FAIL&lt;/code&gt;, &lt;code&gt;BLOCKED&lt;/code&gt;, and &lt;code&gt;SKIP&lt;/code&gt; instead of treating missing prerequisites as success.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source provenance:&lt;/strong&gt; an automated guard checks for suspicious binary or proprietary material in changes. It reduces risk but is not a legal guarantee; adopting third-party code still requires human review.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The current main-branch diagnostic CI has passed for the latest load-and-execute work. Reproducing and checking the scope of those results is part of the project, not an afterthought.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this goes next
&lt;/h2&gt;

&lt;p&gt;A useful homebrew-CD boot path is still ahead, followed by validated System Card entry points, error handling, interrupts, and comparison against an independent emulator and eventually real hardware.&lt;/p&gt;

&lt;p&gt;I want to keep the evidence labels precise:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A ROM that loads is not a BIOS that boots. A sector that reads is not an implemented &lt;code&gt;CD_READ&lt;/code&gt; service. A 28-byte test program running from RAM is not commercial game compatibility.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Still, being able to follow bytes from a self-authored disc, through the guest CD interface, into RAM, and then watch the guest CPU execute those bytes is genuine progress.&lt;/p&gt;

&lt;p&gt;If you work on HuC6280 assembly, CD-ROM² emulation, firmware replacement, or reproducible compatibility tests, I'd welcome technical feedback.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/mao2009/oss-pce-cd-bios" rel="noopener noreferrer"&gt;https://github.com/mao2009/oss-pce-cd-bios&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This project is independent of NEC, Hudson Soft, Konami, and other rights holders. It does not distribute proprietary BIOS images, commercial games, or game-derived disc data.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>programming</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>PSXRecompStudio Progress: Persona's Opening, a Recompiler Demo, and a Runtime Mystery</title>
      <dc:creator>loach</dc:creator>
      <pubDate>Fri, 16 Oct 2026 04:01:23 +0000</pubDate>
      <link>https://dev.to/loach2009/psxrecompstudio-progress-personas-opening-a-recompiler-demo-and-a-runtime-mystery-3p92</link>
      <guid>https://dev.to/loach2009/psxrecompstudio-progress-personas-opening-a-recompiler-demo-and-a-runtime-mystery-3p92</guid>
      <description>&lt;p&gt;I'm building &lt;a href="https://github.com/mao2009/PSXRecompStudio" rel="noopener noreferrer"&gt;PSXRecompStudio&lt;/a&gt;, an open-source research project exploring static recompilation of PlayStation 1 software.&lt;/p&gt;

&lt;p&gt;The long-term goal is to help authorized rights holders turn existing PS1 games into maintainable, rebuildable ports, even when their original source code and build tools are no longer available.&lt;/p&gt;

&lt;p&gt;This is still research-stage work, not a way to recompile and play a complete commercial game today. But over the past few weeks, several parts of the project have become much easier to demonstrate and measure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Seeing Persona's opening for the first time
&lt;/h2&gt;

&lt;p&gt;One of the most encouraging milestones was getting visible opening imagery from &lt;em&gt;Megami Ibunroku Persona&lt;/em&gt; (&lt;em&gt;Revelations: Persona&lt;/em&gt;) through the &lt;strong&gt;OpenBIOS interpreter execution path&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I added screenshots captured at specific VBlank intervals rather than trying to reconstruct an earlier frame from the final state of video memory. The resulting captures show the ATLUS logo and opening text referencing Zhuangzi's butterfly dream.&lt;/p&gt;

&lt;p&gt;That matters because the project had spent a long time making progress that was mostly visible in CPU traces, BIOS calls, and test output. Seeing recognizable frames is a different kind of feedback.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fri0qbngotren4my8droy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fri0qbngotren4my8droy.png" alt="Persona opening: ATLUS logo" width="320" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The qualification is important: &lt;strong&gt;this is interpreter execution, not a successfully recompiled version of Persona&lt;/strong&gt;. Reaching the title screen, playing the game, and matching this behavior in the generated native host remain unproven.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mao2009/PSXRecompStudio/pull/754" rel="noopener noreferrer"&gt;How the VBlank capture was implemented (PR #754)&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A tiny PS1 executable can go all the way through recompilation
&lt;/h2&gt;

&lt;p&gt;There is also now a reproducible showcase for the actual recompilation pipeline.&lt;/p&gt;

&lt;p&gt;Using the released &lt;code&gt;v0.0.0-alpha.2&lt;/code&gt; CLI, a script generates an original five-instruction PS-X EXE, then follows this path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Synthetic PS-X EXE
       |
       v
MIPS analysis and lowering
       |
       v
Generated host code
       |
       v
Native artifact build
       |
       v
Execute and inspect output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The demo prints &lt;code&gt;P&lt;/code&gt; to the terminal. Real console captures document the result on Windows, and an equivalent released-binary run was verified on Linux.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffqodd4e9r73g9f0ctz29.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffqodd4e9r73g9f0ctz29.png" alt="Recompiler demo in a real console" width="800" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Five instructions do not demonstrate compatibility with an entire commercial game. They do demonstrate that analysis, code generation, building, and execution can work together end to end for a controlled input.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mao2009/PSXRecompStudio/pull/781" rel="noopener noreferrer"&gt;Demo verification and screenshots (PR #781)&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The hard part: two execution paths, different results
&lt;/h2&gt;

&lt;p&gt;The biggest open problem is less photogenic.&lt;/p&gt;

&lt;p&gt;The interpreter can advance through Persona's opening, but the &lt;strong&gt;standalone AOT + interpreter-fallback runtime still produces a black screen&lt;/strong&gt; in the investigated scenario.&lt;/p&gt;

&lt;p&gt;AOT means &lt;em&gt;ahead-of-time compilation&lt;/em&gt;: known PS1 instructions are translated into host code before execution. When the generated host encounters a path it cannot handle, an interpreter can take over.&lt;/p&gt;

&lt;p&gt;I am comparing the two execution paths instead of assuming the GPU renderer is responsible.&lt;/p&gt;

&lt;p&gt;At one shared checkpoint, the recorded CPU state, RAM, VRAM, GPU state, captured device registers, and guest-cycle count match. &lt;strong&gt;That is equality at one checkpoint, not a claim that all preceding execution was identical.&lt;/strong&gt; Their subsequent instruction paths diverge, and the root cause has not yet been identified.&lt;/p&gt;

&lt;p&gt;One useful outcome of this investigation was finding a misleading diagnostic: a run stopped by an explicit request could be reported as if it had exhausted its instruction budget. The diagnostic was corrected, but the underlying rendering divergence is still open.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mao2009/PSXRecompStudio/pull/787" rel="noopener noreferrer"&gt;Investigation and measured limits (draft PR #787)&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A concrete runtime optimization
&lt;/h2&gt;

&lt;p&gt;On October 16, I merged a smaller, measurable change to the generated-host runtime.&lt;/p&gt;

&lt;p&gt;The runtime was spending a lot of time exchanging synchronous messages between generated code and the host. In particular, a guest-time report was often sent immediately before an MMIO access.&lt;/p&gt;

&lt;p&gt;Combining that time report with the MMIO request into one message reduced &lt;strong&gt;IPC round trips per guest instruction from 0.106 to 0.071&lt;/strong&gt;, approximately &lt;strong&gt;33% fewer&lt;/strong&gt; in the measured setup.&lt;/p&gt;

&lt;p&gt;A separate throughput measurement had a higher median, but considerable host noise, so I'm treating the round-trip reduction as the more reliable result.&lt;/p&gt;

&lt;p&gt;This improves communication overhead; it &lt;strong&gt;does not&lt;/strong&gt; establish that Persona now boots further or that the black-screen divergence is fixed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mao2009/PSXRecompStudio/pull/796" rel="noopener noreferrer"&gt;IPC optimization and measurements (PR #796)&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tests should say what they actually tested
&lt;/h2&gt;

&lt;p&gt;Another recent change was deliberately unglamorous: integration tests requiring real CHD files now &lt;strong&gt;skip explicitly&lt;/strong&gt; when their input is unavailable, instead of silently returning and appearing to pass.&lt;/p&gt;

&lt;p&gt;With a lawful local fixture present, the relevant tests can run normally. Without it, the test report distinguishes missing evidence from successful execution.&lt;/p&gt;

&lt;p&gt;I want the project's status reports to keep three things separate: what a synthetic input proves, what a locally supplied real disc proves, and what has not been verified.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mao2009/PSXRecompStudio/pull/791" rel="noopener noreferrer"&gt;Real-ROM test reporting fix (PR #791)&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;The immediate priority is to identify the first meaningful behavioral difference between interpreter and generated-host execution, then extend the runtime only with evidence for the missing behavior.&lt;/p&gt;

&lt;p&gt;There is still substantial work ahead in BIOS and device behavior, recompiler coverage, runtime integration, and independent validation. A working demo and recognizable screenshots are milestones, not compatibility guarantees.&lt;/p&gt;

&lt;p&gt;But I'm happy to have moved from &lt;em&gt;“the traces are changing”&lt;/em&gt; to a project with visible frames, a reproducible native-code demo, a precise unsolved divergence, and measurements that can guide performance work.&lt;/p&gt;

&lt;p&gt;If you work on emulators, recompilers, MIPS, or retro-game preservation, I'd be interested in technical feedback—especially on differential execution testing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/mao2009/PSXRecompStudio" rel="noopener noreferrer"&gt;https://github.com/mao2009/PSXRecompStudio&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The project does not distribute commercial game images, BIOS files, or copyrighted game data. Persona screenshots document interoperability research; the game and its imagery belong to their respective rights holders. PSXRecompStudio is unaffiliated with ATLUS.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>computerscience</category>
      <category>gamedev</category>
      <category>opensource</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Implementing 'You Can't' for AI Coding Agents</title>
      <dc:creator>loach</dc:creator>
      <pubDate>Thu, 08 Oct 2026 03:28:21 +0000</pubDate>
      <link>https://dev.to/loach2009/implementing-you-cant-for-ai-coding-agents-2d1a</link>
      <guid>https://dev.to/loach2009/implementing-you-cant-for-ai-coding-agents-2d1a</guid>
      <description>&lt;p&gt;AI coding agents are getting better at writing software. I use them in my own open-source projects, too.&lt;/p&gt;

&lt;p&gt;But there's a distinction I keep coming back to:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Teaching an AI what it shouldn't do is not the same as enforcing what its code is allowed to do.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We ask agents not to break architectural boundaries, introduce side effects, or add unapproved dependencies. Those instructions are useful, but instructions are not guarantees.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prompts aren't enforcement
&lt;/h2&gt;

&lt;p&gt;Imagine an agent generates this code inside a Domain layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;MyApp.Domain&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Infrastructure&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrderRepository&lt;/span&gt; &lt;span class="n"&gt;_repository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the referenced type exists, the C# compiler will not normally know that the dependency violates your architecture.&lt;/p&gt;

&lt;p&gt;The code may work, compile, and still be unacceptable.&lt;/p&gt;

&lt;p&gt;A README or &lt;code&gt;AGENTS.md&lt;/code&gt; communicates a rule. It does not independently enforce it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Move the decision outside the agent
&lt;/h2&gt;

&lt;p&gt;The workflow I want looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI agent or human writes code
             |
             v
      Compiler + Analyzers
             |
      Policy satisfied?
         /       \
       Yes        No
        |          |
   Candidate     Diagnostic
   for merge       |
                   v
                Revise code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent can propose a solution. An independent tool decides whether the proposal satisfies the project's checkable constraints.&lt;/p&gt;

&lt;p&gt;For C# projects, Roslyn Analyzers let us report diagnostics during compilation. Configured as errors, those diagnostics can make a violating build fail.&lt;/p&gt;

&lt;p&gt;This gives AI agents immediate, deterministic feedback rather than another natural-language reminder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three open-source projects exploring the approach
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ArchitectureAnalyzer: Compile the architecture contract
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/mao2009/ArchitectureAnalyzer" rel="noopener noreferrer"&gt;ArchitectureAnalyzer&lt;/a&gt; uses a JSON architecture contract and Roslyn diagnostics to check rules such as layer dependencies, forbidden APIs, and dependency graph constraints.&lt;/p&gt;

&lt;p&gt;An architecture document records intent. A machine-checkable contract helps detect drift between that intent and the codebase.&lt;/p&gt;

&lt;h3&gt;
  
  
  PureSharp: Enforce specific code-level contracts
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/mao2009/PureSharp" rel="noopener noreferrer"&gt;PureSharp&lt;/a&gt; introduces functional-programming-oriented checks for C#.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;PureMethod&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It detects selected side effects within its documented purity contract. It also checks conventions for immutable local variables and FluentIf termination.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;_value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Calculate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// _value = 100; // Diagnosed reassignment&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is not a whole-program mathematical proof of purity. It's enforcement of explicitly defined, mechanically checkable properties.&lt;/p&gt;

&lt;h3&gt;
  
  
  PolicySharp: Allow by policy, deny by default
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/mao2009/PolicySharp" rel="noopener noreferrer"&gt;PolicySharp&lt;/a&gt; takes an allowlist-oriented approach.&lt;/p&gt;

&lt;p&gt;A denylist catches violations someone remembered to enumerate. A new API or dependency may not appear on that list.&lt;/p&gt;

&lt;p&gt;A default-deny policy instead follows this model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Explicitly allowed    -&amp;gt; ALLOW
Explicitly denied     -&amp;gt; DENY
Unknown or ambiguous  -&amp;gt; DENY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, a policy can describe which namespaces a Domain scope is allowed to depend on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"default-deny"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scopes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"domain"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"match"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"namespace"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MyApp.Domain.**"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"allow"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"namespaces"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"System"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"MyApp.Domain.**"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This illustrates the policy model; precise enforcement coverage depends on the implemented version.&lt;/p&gt;

&lt;h2&gt;
  
  
  What if the agent changes the rules?
&lt;/h2&gt;

&lt;p&gt;There's an obvious bypass:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The agent introduces a prohibited dependency.&lt;/li&gt;
&lt;li&gt;An analyzer reports an error.&lt;/li&gt;
&lt;li&gt;The agent edits the policy or project configuration.&lt;/li&gt;
&lt;li&gt;The build passes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's why an analyzer alone isn't enough.&lt;/p&gt;

&lt;p&gt;The authority to modify application code should be separate from the authority to weaken the constraints. Policy files, project files, and analyzer references may need independent approvals and CI checks.&lt;/p&gt;

&lt;p&gt;Static analysis also has limits. It cannot enforce properties it does not analyze, and a build gate only matters when the project actually runs it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Freedom to propose is not permission to merge
&lt;/h2&gt;

&lt;p&gt;I don't want to constrain every implementation choice an AI makes.&lt;/p&gt;

&lt;p&gt;I want to distinguish &lt;strong&gt;freedom to propose a solution&lt;/strong&gt; from &lt;strong&gt;permission to accept the resulting change&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Within the approved boundaries, the agent can explore different approaches. If a boundary needs to change, that should become an explicit design decision instead of a workaround silently introduced during implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Prompts communicate intent.&lt;/p&gt;

&lt;p&gt;Analyzers enforce checkable rules.&lt;/p&gt;

&lt;p&gt;CI validates changes.&lt;/p&gt;

&lt;p&gt;Approval processes protect the rules themselves.&lt;/p&gt;

&lt;p&gt;Instead of expecting AI to remember everything it must never do, we can create development environments that reliably detect prohibited changes before accepting them.&lt;/p&gt;

&lt;p&gt;That is the direction I'm exploring with ArchitectureAnalyzer, PureSharp, and PolicySharp.&lt;/p&gt;

&lt;p&gt;The goal isn't less capable AI. It's more predictable, maintainable AI-assisted software development.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>csharp</category>
      <category>programming</category>
    </item>
    <item>
      <title>Publishing .NET Packages to NuGet.org with an API Key</title>
      <dc:creator>loach</dc:creator>
      <pubDate>Thu, 10 Sep 2026 12:00:55 +0000</pubDate>
      <link>https://dev.to/loach2009/publishing-net-packages-to-nugetorg-with-an-api-key-37n5</link>
      <guid>https://dev.to/loach2009/publishing-net-packages-to-nugetorg-with-an-api-key-37n5</guid>
      <description>&lt;p&gt;Publishing a .NET library manually is fine once or twice, but it becomes tedious when releases are frequent or automated through CI/CD.&lt;/p&gt;

&lt;p&gt;The standard solution is to push packages to NuGet.org with &lt;code&gt;dotnet nuget push&lt;/code&gt; and an API key.&lt;/p&gt;

&lt;p&gt;This article covers the basic flow and the most important security consideration: &lt;strong&gt;never commit the API key into the repository&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a .NET project&lt;/li&gt;
&lt;li&gt;a generated &lt;code&gt;.nupkg&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;a NuGet.org account&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, your package might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./bin/Release/YourPackage.1.0.0.nupkg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create a NuGet API key
&lt;/h2&gt;

&lt;p&gt;Sign in to NuGet.org and open the API Keys page.&lt;/p&gt;

&lt;p&gt;When creating a new key, configure at least:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a recognizable key name&lt;/li&gt;
&lt;li&gt;an expiration period&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;Push&lt;/code&gt; scope&lt;/li&gt;
&lt;li&gt;an optional package pattern if you want to restrict which packages the key can publish&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The generated key should be treated like a password.&lt;/p&gt;

&lt;p&gt;Do not put it in source code, scripts committed to Git, or Markdown examples containing a real value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Push a package
&lt;/h2&gt;

&lt;p&gt;The basic command is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet nuget push &amp;lt;PACKAGE_FILE&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--api-key&lt;/span&gt; &amp;lt;YOUR_API_KEY&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--source&lt;/span&gt; https://api.nuget.org/v3/index.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet nuget push ./bin/Release/YourPackage.1.0.0.nupkg &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--api-key&lt;/span&gt; YOUR_API_KEY_HERE &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--source&lt;/span&gt; https://api.nuget.org/v3/index.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A useful option for CI is &lt;code&gt;--skip-duplicate&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet nuget push ./bin/Release/&lt;span class="k"&gt;*&lt;/span&gt;.nupkg &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--api-key&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NUGET_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--source&lt;/span&gt; https://api.nuget.org/v3/index.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--skip-duplicate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This avoids failing a rerun just because the same package version was already published.&lt;/p&gt;

&lt;h2&gt;
  
  
  Store the key in GitHub Actions Secrets
&lt;/h2&gt;

&lt;p&gt;For GitHub Actions, store the key as a repository secret, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NUGET_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then reference it from the workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Publish package&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;NUGET_API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.NUGET_API_KEY }}&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;dotnet nuget push ./bin/Release/*.nupkg \&lt;/span&gt;
      &lt;span class="s"&gt;--api-key "$NUGET_API_KEY" \&lt;/span&gt;
      &lt;span class="s"&gt;--source https://api.nuget.org/v3/index.json \&lt;/span&gt;
      &lt;span class="s"&gt;--skip-duplicate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API key stays outside the repository while still being available to the CI job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prefer narrow credentials
&lt;/h2&gt;

&lt;p&gt;If the publishing key only needs to release one package family, restrict it to that package pattern where possible.&lt;/p&gt;

&lt;p&gt;A narrow key reduces the impact if it is ever exposed.&lt;/p&gt;

&lt;p&gt;The same principle applies to other CI credentials: give automation only the permissions it actually needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing thoughts
&lt;/h2&gt;

&lt;p&gt;The mechanics of publishing a NuGet package are simple. The part worth designing carefully is credential handling.&lt;/p&gt;

&lt;p&gt;A solid release flow usually looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;build
  ↓
pack
  ↓
secret injected by CI
  ↓
dotnet nuget push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keeping the credential out of the repository makes the process both automatable and reasonably safe.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>dotnet</category>
      <category>security</category>
    </item>
    <item>
      <title>PureSharp: Bringing Functional Safety to C# with Roslyn Analyzers</title>
      <dc:creator>loach</dc:creator>
      <pubDate>Thu, 10 Sep 2026 11:55:44 +0000</pubDate>
      <link>https://dev.to/loach2009/puresharp-bringing-functional-safety-to-c-with-roslyn-analyzers-4293</link>
      <guid>https://dev.to/loach2009/puresharp-bringing-functional-safety-to-c-with-roslyn-analyzers-4293</guid>
      <description>&lt;p&gt;C# is a very flexible language, but that flexibility also makes it easy for side effects and mutable state to spread through a codebase.&lt;/p&gt;

&lt;p&gt;I built &lt;strong&gt;PureSharp&lt;/strong&gt; to bring a few functional-programming ideas into everyday C# development and make them enforceable at compile time.&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/mao2009/PureSharp" rel="noopener noreferrer"&gt;https://github.com/mao2009/PureSharp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;NuGet: &lt;a href="https://www.nuget.org/packages/loach.PureSharp" rel="noopener noreferrer"&gt;https://www.nuget.org/packages/loach.PureSharp&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What PureSharp tries to enforce
&lt;/h2&gt;

&lt;p&gt;PureSharp focuses on three ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purity&lt;/strong&gt;: methods explicitly marked as pure should not perform side effects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Immutability&lt;/strong&gt;: local variables can opt into a "do not reassign" convention.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safer control flow&lt;/strong&gt;: fluent conditional expressions should terminate explicitly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key point is that these rules are not just documentation. Roslyn analyzers report violations while you write and build the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Declaring a pure method
&lt;/h2&gt;

&lt;p&gt;A method can be marked with &lt;code&gt;[PureMethod]&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;PureSharp.Core&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Calculator&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;PureMethod&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside such a method, PureSharp can report operations that violate the declared purity contract, such as accessing mutable static state, calling methods that are not known to be pure, or performing I/O.&lt;/p&gt;

&lt;p&gt;The goal is not to claim that every C# method can be proven mathematically pure. The goal is more practical: if a developer explicitly says "this method should be pure," the analyzer should help keep it that way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Opt-in immutability for local variables
&lt;/h2&gt;

&lt;p&gt;C# does not have a general-purpose &lt;code&gt;const&lt;/code&gt;-style feature for local variables initialized at runtime.&lt;/p&gt;

&lt;p&gt;PureSharp uses a naming convention: local variables beginning with &lt;code&gt;_&lt;/code&gt; are treated as non-reassignable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ProcessData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;_result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;CalculateValue&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Reported by the analyzer:&lt;/span&gt;
    &lt;span class="c1"&gt;// _result = 100;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This turns a simple convention into something the compiler can enforce.&lt;/p&gt;

&lt;p&gt;The analyzer can also require immediate initialization and can suggest the immutable convention for locals that are never reassigned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fluent conditional expressions
&lt;/h2&gt;

&lt;p&gt;PureSharp also provides &lt;code&gt;Fluent.If&lt;/code&gt;, which makes it possible to use conditional branching as an expression:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Fluent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;If&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ElseIf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Else&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The analyzer checks that a fluent chain is properly terminated with &lt;code&gt;.Else()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The motivation is simple: if the construct is intended to produce a value, all paths should be explicit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Roslyn analyzers?
&lt;/h2&gt;

&lt;p&gt;These rules could be described in a style guide, but a style guide is only useful when someone remembers to enforce it.&lt;/p&gt;

&lt;p&gt;Roslyn analyzers make the feedback immediate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;violations appear in the IDE&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dotnet build&lt;/code&gt; can fail on violations&lt;/li&gt;
&lt;li&gt;CI does not need a separate custom validator&lt;/li&gt;
&lt;li&gt;rules stay close to the normal C# development workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That makes them especially useful for conventions that are easy to understand but tedious to police in code review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;Install the package from NuGet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package loach.PureSharp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, the analyzer participates in normal builds.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned from building it
&lt;/h2&gt;

&lt;p&gt;The interesting part of a tool like PureSharp is not the syntax of a single diagnostic. It is deciding where static analysis can give useful guarantees without pretending to understand more than it actually can.&lt;/p&gt;

&lt;p&gt;C# remains an imperative, object-oriented language with many escape hatches. PureSharp is therefore intentionally opt-in: developers declare the rules they want, and the analyzer checks those declarations as far as static analysis reasonably allows.&lt;/p&gt;

&lt;p&gt;That balance is what makes Roslyn analyzers attractive for this kind of developer tooling.&lt;/p&gt;

&lt;p&gt;PureSharp is open source here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mao2009/PureSharp" rel="noopener noreferrer"&gt;https://github.com/mao2009/PureSharp&lt;/a&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>opensource</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Using Ctrl+Enter to Submit in OpenCode and Enter for New Lines</title>
      <dc:creator>loach</dc:creator>
      <pubDate>Thu, 10 Sep 2026 11:55:33 +0000</pubDate>
      <link>https://dev.to/loach2009/using-ctrlenter-to-submit-in-opencode-and-enter-for-new-lines-2065</link>
      <guid>https://dev.to/loach2009/using-ctrlenter-to-submit-in-opencode-and-enter-for-new-lines-2065</guid>
      <description>&lt;p&gt;Another OpenCode behavior I wanted to change was message submission.&lt;/p&gt;

&lt;p&gt;By default, pressing &lt;code&gt;Enter&lt;/code&gt; submits the current prompt. If you are used to chat tools where &lt;code&gt;Enter&lt;/code&gt; inserts a new line and &lt;code&gt;Ctrl + Enter&lt;/code&gt; sends, this makes accidental submissions easy.&lt;/p&gt;

&lt;p&gt;My preferred setup is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Enter&lt;/code&gt;: insert a new line&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ctrl + Enter&lt;/code&gt;: submit the prompt&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  OpenCode configuration
&lt;/h2&gt;

&lt;p&gt;Edit your OpenCode configuration file and update the keybindings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"keybinds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"app_exit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ctrl+d,&amp;lt;leader&amp;gt;q"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"input_submit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ctrl+return"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"input_newline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shift+return,return,alt+return,ctrl+j"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells OpenCode to treat normal Enter-style keys as new lines and reserve &lt;code&gt;Ctrl + Enter&lt;/code&gt; for submission.&lt;/p&gt;

&lt;h2&gt;
  
  
  The terminal may need configuration too
&lt;/h2&gt;

&lt;p&gt;There is one important detail: some terminals do not send a distinct key sequence for &lt;code&gt;Ctrl + Enter&lt;/code&gt; by default.&lt;/p&gt;

&lt;p&gt;In that case, OpenCode cannot distinguish it from plain Enter.&lt;/p&gt;

&lt;p&gt;For Windows Terminal, you can define a custom sequence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"actions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sendInput"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;001b[13;5u"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"User.sendInput.CtrlEnterCustom"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then bind &lt;code&gt;Ctrl + Enter&lt;/code&gt; to that action in the terminal settings.&lt;/p&gt;

&lt;p&gt;The exact terminal configuration can vary, but the principle is the same: the terminal must send a distinguishable sequence before OpenCode can bind it separately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this improves the workflow
&lt;/h2&gt;

&lt;p&gt;Long prompts often contain code blocks, lists, and multiple paragraphs.&lt;/p&gt;

&lt;p&gt;Making &lt;code&gt;Enter&lt;/code&gt; safe for editing removes the pressure to finish the whole prompt on one line and reduces accidental sends.&lt;/p&gt;

&lt;p&gt;It also makes OpenCode behave more like many chat and editor-oriented interfaces.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing thoughts
&lt;/h2&gt;

&lt;p&gt;Keybindings are a small detail, but AI coding tools are used interactively for long periods. Small interaction problems become surprisingly expensive when repeated all day.&lt;/p&gt;

&lt;p&gt;For me, separating newline and submit made OpenCode much more comfortable to use.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>software</category>
      <category>tools</category>
    </item>
    <item>
      <title>Preventing Accidental OpenCode Exits with Ctrl+C</title>
      <dc:creator>loach</dc:creator>
      <pubDate>Thu, 10 Sep 2026 11:50:23 +0000</pubDate>
      <link>https://dev.to/loach2009/preventing-accidental-opencode-exits-with-ctrlc-39dh</link>
      <guid>https://dev.to/loach2009/preventing-accidental-opencode-exits-with-ctrlc-39dh</guid>
      <description>&lt;p&gt;OpenCode is powerful, but one default behavior kept getting in my way: pressing &lt;code&gt;Ctrl + C&lt;/code&gt; could close the application itself.&lt;/p&gt;

&lt;p&gt;If you spend a lot of time in terminals, &lt;code&gt;Ctrl + C&lt;/code&gt; is muscle memory. You press it to interrupt a command, stop a long-running process, or simply because your hands move before you think.&lt;/p&gt;

&lt;p&gt;Losing the whole OpenCode session because of that is frustrating.&lt;/p&gt;

&lt;p&gt;The fix is simple: move the application exit action to a different keybinding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Change the exit keybinding
&lt;/h2&gt;

&lt;p&gt;Open your OpenCode configuration file and customize the keybindings.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"keybinds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"app_exit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ctrl+d,&amp;lt;leader&amp;gt;q"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this configuration, &lt;code&gt;Ctrl + C&lt;/code&gt; is no longer the application-exit shortcut.&lt;/p&gt;

&lt;p&gt;You can then use &lt;code&gt;Ctrl + D&lt;/code&gt; or the leader-based shortcut to exit intentionally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this small change matters
&lt;/h2&gt;

&lt;p&gt;AI coding agents often stay open for long sessions with valuable context.&lt;/p&gt;

&lt;p&gt;A keybinding that is common for interruption in terminal workflows should not unexpectedly destroy that session.&lt;/p&gt;

&lt;p&gt;Separating "interrupt what is happening" from "close the entire application" makes the interface much harder to use incorrectly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing thoughts
&lt;/h2&gt;

&lt;p&gt;This is a tiny configuration change, but it removed one of the most annoying sources of accidental session loss for me.&lt;/p&gt;

&lt;p&gt;If you use OpenCode inside a terminal and frequently hit &lt;code&gt;Ctrl + C&lt;/code&gt; out of habit, remapping &lt;code&gt;app_exit&lt;/code&gt; is worth doing early.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>software</category>
      <category>tools</category>
    </item>
  </channel>
</rss>
