<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Kernel Techniques on Programming the Atari VCS in Assembly</title><link>https://cdeever.github.io/atari-vcs/docs/kernel-techniques/</link><description>Recent content in Kernel Techniques on Programming the Atari VCS in Assembly</description><generator>Hugo</generator><language>en-us</language><atom:link href="https://cdeever.github.io/atari-vcs/docs/kernel-techniques/index.xml" rel="self" type="application/rss+xml"/><item><title>Counting Cycles</title><link>https://cdeever.github.io/atari-vcs/docs/kernel-techniques/counting-cycles/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://cdeever.github.io/atari-vcs/docs/kernel-techniques/counting-cycles/</guid><description>&lt;h1 id="counting-cycles"&gt;Counting Cycles&lt;a class="anchor" href="#counting-cycles"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Every other platform lets you write code and measure it later. On the VCS you must know the cost &lt;em&gt;before&lt;/em&gt; you run it, because a &lt;a href="https://cdeever.github.io/atari-vcs/docs/6502-basics/cycles-and-timing/"&gt;visible scanline is 76 CPU cycles&lt;/a&gt; and there is no &amp;ldquo;a little slow&amp;rdquo; — a kernel line that needs 77 doesn&amp;rsquo;t lag, it corrupts the picture. Counting cycles is the habit that makes everything else in this chapter possible.&lt;/p&gt;
&lt;h2 id="budget-from-wsync-to-wsync"&gt;Budget from WSYNC to WSYNC&lt;a class="anchor" href="#budget-from-wsync-to-wsync"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A kernel is a loop, and the unit you budget is &lt;strong&gt;one pass: the cycles between one &lt;code&gt;STA WSYNC&lt;/code&gt; and the next.&lt;/strong&gt; Add up the cost of every instruction in that span — using the &lt;a href="https://cdeever.github.io/atari-vcs/docs/6502-basics/cycles-and-timing/"&gt;per-instruction costs&lt;/a&gt; — and confirm the total fits under 76 (minus whatever the visible drawing already spends).&lt;/p&gt;</description></item><item><title>Waiting Precisely</title><link>https://cdeever.github.io/atari-vcs/docs/kernel-techniques/waiting-precisely/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://cdeever.github.io/atari-vcs/docs/kernel-techniques/waiting-precisely/</guid><description>&lt;h1 id="waiting-precisely"&gt;Waiting Precisely&lt;a class="anchor" href="#waiting-precisely"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Half of kernel work is making things happen; the other half is making them happen &lt;em&gt;at the right cycle&lt;/em&gt;. &lt;a href="https://cdeever.github.io/atari-vcs/docs/6502-basics/cycles-and-timing/"&gt;&lt;code&gt;WSYNC&lt;/code&gt;&lt;/a&gt; handles the coarse case — it parks you at the start of the next line — but it can only round &lt;em&gt;up&lt;/em&gt; to a line boundary. When you need to land on an exact cycle &lt;em&gt;within&lt;/em&gt; a line (positioning a sprite, &lt;a href="https://cdeever.github.io/atari-vcs/docs/playfield/asymmetric/"&gt;rewriting a playfield register mid-line&lt;/a&gt;), or to wait out a whole region of the frame, you need finer and coarser tools than &lt;code&gt;WSYNC&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Multi-Line Kernels</title><link>https://cdeever.github.io/atari-vcs/docs/kernel-techniques/multi-line-kernels/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://cdeever.github.io/atari-vcs/docs/kernel-techniques/multi-line-kernels/</guid><description>&lt;h1 id="multi-line-kernels"&gt;Multi-Line Kernels&lt;a class="anchor" href="#multi-line-kernels"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://cdeever.github.io/atari-vcs/docs/kernel-techniques/counting-cycles/"&gt;Counting cycles&lt;/a&gt; eventually delivers bad news: there are jobs that simply don&amp;rsquo;t fit in one &lt;a href="https://cdeever.github.io/atari-vcs/docs/6502-basics/cycles-and-timing/"&gt;76-cycle line&lt;/a&gt;. Updating &lt;em&gt;both&lt;/em&gt; &lt;a href="https://cdeever.github.io/atari-vcs/docs/sprites/drawing-a-player/"&gt;players&lt;/a&gt;, fetching their colors, advancing their counters, and still leaving room for the draw — added up, it overflows. When a single line can&amp;rsquo;t hold the work, you stop trying to do it in one.&lt;/p&gt;
&lt;h2 id="spread-the-work-across-two-lines"&gt;Spread the work across two lines&lt;a class="anchor" href="#spread-the-work-across-two-lines"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A &lt;strong&gt;two-line kernel&lt;/strong&gt; runs its loop body once for every &lt;em&gt;two&lt;/em&gt; scanlines. The TIA still draws every line — the beam doesn&amp;rsquo;t slow down — but your code now has roughly &lt;strong&gt;two lines&amp;rsquo; worth of cycles&lt;/strong&gt; to prepare what those lines show. You split the work: some on the first line, the rest on the second.&lt;/p&gt;</description></item><item><title>Front-Loading &amp; Tables</title><link>https://cdeever.github.io/atari-vcs/docs/kernel-techniques/front-loading-and-tables/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://cdeever.github.io/atari-vcs/docs/kernel-techniques/front-loading-and-tables/</guid><description>&lt;h1 id="front-loading--tables"&gt;Front-Loading &amp;amp; Tables&lt;a class="anchor" href="#front-loading--tables"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;The last way to fit work into a &lt;a href="https://cdeever.github.io/atari-vcs/docs/6502-basics/cycles-and-timing/"&gt;76-cycle line&lt;/a&gt; is to &lt;strong&gt;not do it on that line.&lt;/strong&gt; Two habits push work out of the hot path: &lt;em&gt;front-loading&lt;/em&gt; — computing a line&amp;rsquo;s data before the line needs it — and &lt;em&gt;tables&lt;/em&gt; — computing it before the program even runs. Both trade something plentiful (earlier time, ROM) for something scarce (cycles in the critical window).&lt;/p&gt;
&lt;h2 id="front-loading-be-early"&gt;Front-loading: be early&lt;a class="anchor" href="#front-loading-be-early"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In the moment the beam reaches a sprite, you have just a few cycles to put the right byte in &lt;code&gt;GRP0&lt;/code&gt;. If those cycles are also spent &lt;em&gt;deciding&lt;/em&gt; which byte — fetching a pointer, adding an offset, looking up a color — you&amp;rsquo;ll overflow. So you do that deciding &lt;strong&gt;earlier&lt;/strong&gt;, during the slack of the &lt;em&gt;previous&lt;/em&gt; line, and leave only a fast store for the critical instant.&lt;/p&gt;</description></item></channel></rss>