<rss version="2.0">
  <channel>
    <title>Arnau Camprubí</title>
    <link>https://arnauc.me</link>
    <description>Arnau Camprubí - Blog</description>
    <generator>Zine -- https://zine-ssg.io</generator>
    <language>en-US</language>
    <lastBuildDate>Sun, 19 Apr 2026 00:19:44 +0000</lastBuildDate>
    
      <item>
        <title>How web browsers work</title>
        <description>&lt;p&gt;A few years ago, I started going down the rabbithole of &lt;em&gt;browser engines&lt;/em&gt;. I wanted to write one, so I started looking for resources. None were as detailed as I had hoped. So, I decided to simply read the official specification (rather, the set of interleaved specifications). Understanably, the &lt;em&gt;spec&lt;/em&gt; didn’t explain how a browser engine works, just what its result should be. So I started designing a browser engine – just to find out some time later that my design was horrible from the beginning. Well… I restarted the entire project from scratch &lt;strong&gt;more than 10 times&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;The current iteration has lasted for half a year so far, and I’m still confident it is the right design. Finally, I feel perpared to write something I wish existed when I started: an explanation of &lt;strong&gt;how browsers actually work&lt;/strong&gt;.&lt;/p&gt;&lt;h1&gt;The browser and the browser engine&lt;/h1&gt;&lt;p&gt;A &lt;em&gt;browser&lt;/em&gt; and a &lt;em&gt;browser engine&lt;/em&gt; are two distinct things. The program you install on your computer is the &lt;em&gt;browser&lt;/em&gt;. It is composed of two parts: the &lt;em&gt;chrome&lt;/em&gt; (not to be confused with the &lt;em&gt;browser&lt;/em&gt; of the same name) and the &lt;em&gt;browser engine&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;The &lt;em&gt;browser engine&lt;/em&gt; is the part that parses HTML, does layout or runs JS, whereas you can think of the &lt;em&gt;chrome&lt;/em&gt; as “the UI”. The &lt;em&gt;browser engine&lt;/em&gt; doesn’t know anything about tabs, windows or downloads, those are all part of the &lt;em&gt;chrome&lt;/em&gt;. For example, Firefox, Chrome or Opera are &lt;em&gt;browsers&lt;/em&gt;. Blink, WebKit or Gecko are &lt;em&gt;browser engines&lt;/em&gt;. This post will be centered around the &lt;em&gt;browser engine&lt;/em&gt;, since it’s the core of the browser (and the interesting part).&lt;/p&gt;&lt;h1&gt;The main pipeline&lt;/h1&gt;&lt;p&gt;The main task for the browser engine is transforming an HTML source into a drawable page. The output of this whole pipeline will be a &lt;em&gt;draw list&lt;/em&gt; or &lt;em&gt;display list&lt;/em&gt;. It is a list of simple commands like “draw a rectangle of color &lt;em&gt;C&lt;/em&gt; at position (&lt;em&gt;X&lt;/em&gt;,&lt;em&gt;Y&lt;/em&gt;) and of size &lt;em&gt;W&lt;/em&gt; by &lt;em&gt;H&lt;/em&gt;”. But doing this directly would be absurdly hard. So we split it into simpler tasks, forming a pipeline.&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://arnauc.me/blog/how-browsers-work/pipeline.svg&quot;&gt;
&lt;figcaption&gt;A diagram of the browser engine pipeline&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;I will explain, one by one, each of the components of the pipeline.&lt;/p&gt;&lt;h2&gt;HTML tokenizer&lt;/h2&gt;&lt;p&gt;The HTML tokenizer takes HTML source (text) as input and returns a list of tokens. Tokens are the most basic syntactic units. A comment is a token, a tag is a token, text is a token, etc. As an example, the following HTML source would be tokenized as follows:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;html&quot;&gt;&lt;span class=&quot;constant&quot;&gt;&amp;lt;!DOCTYPE html&lt;/span&gt;&lt;span class=&quot;punctuation_bracket constant&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;id&lt;/span&gt;=&amp;quot;&lt;span class=&quot;string&quot;&gt;foo&lt;/span&gt;&amp;quot;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;
      Hello world
    &lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;&lt;li&gt;Doctype token&lt;/li&gt;&lt;li&gt;Start tag token for &lt;code&gt;html&lt;/code&gt; (no attributes)&lt;/li&gt;&lt;li&gt;Start tag token for &lt;code&gt;body&lt;/code&gt; (no atrributes)&lt;/li&gt;&lt;li&gt;Start tag token for &lt;code&gt;div&lt;/code&gt; (with &lt;code&gt;id=&amp;quot;foo&amp;quot;&lt;/code&gt;)&lt;/li&gt;&lt;li&gt;Text token &lt;code&gt;&amp;quot;Hello world&amp;quot;&lt;/code&gt;&lt;/li&gt;&lt;li&gt;End tag token for &lt;code&gt;div&lt;/code&gt;&lt;/li&gt;&lt;li&gt;End tag token for &lt;code&gt;body&lt;/code&gt;&lt;/li&gt;&lt;li&gt;End tag token for &lt;code&gt;html&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;It is usually a simple state machine, &lt;a href=&quot;https://html.spec.whatwg.org/#tokenization&quot; target=&quot;_blank&quot;&gt;following the spec&lt;/a&gt;.&lt;/p&gt;&lt;h2&gt;HTML parser&lt;/h2&gt;&lt;p&gt;The HTML parser (sometimes called the &lt;em&gt;tree constructor&lt;/em&gt;) takes tokens as input and generates a DOM (Document Object Model). It does this by first creating an empty DOM and, for each token, modifying it incrementally. Again, this is pretty well-defined &lt;a href=&quot;https://html.spec.whatwg.org/#tree-construction&quot; target=&quot;_blank&quot;&gt;in the spec&lt;/a&gt;. The code from before would generate a DOM similar to:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Document&lt;ul&gt;&lt;li&gt;&lt;code&gt;html&lt;/code&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;body&lt;/code&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;div&lt;/code&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;#text &amp;quot;Hello world&amp;quot;&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Notice that it is in this step that &lt;strong&gt;structure&lt;/strong&gt; is introduced. The DOM is where &lt;em&gt;elements&lt;/em&gt; live, and where modifications from javascript happen (causing a cascading effect onwards).&lt;/p&gt;&lt;h2&gt;CSS tokenizer and parser&lt;/h2&gt;&lt;p&gt;With the CSS sources we found in the DOM (&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;) and the ones included in the browser (the so-called &lt;em&gt;user agent CSS&lt;/em&gt;) we do a process similar to the HTML tokenizer and parser. We obtain a CSSOM (CSS Object Model).&lt;/p&gt;&lt;h2&gt;Style engine&lt;/h2&gt;&lt;p&gt;This stage takes the DOM and CSSOM and &lt;em&gt;combines&lt;/em&gt; them, by computing the style properties &lt;em&gt;for every element&lt;/em&gt;, producing the &lt;em&gt;style tree&lt;/em&gt;. Think of the &lt;em&gt;style tree&lt;/em&gt; as a DOM with extra information for each element.&lt;/p&gt;&lt;p&gt;This is where CSS selector matching is checked. If we have a rule like &lt;code&gt;.foo { color: red }&lt;/code&gt;, it will modify the &lt;code&gt;color&lt;/code&gt; property of each element with the class &lt;code&gt;foo&lt;/code&gt;. Other elements will have the default value for that property.&lt;/p&gt;&lt;h2&gt;Box generation&lt;/h2&gt;&lt;p&gt;Elements are an abstract concept of the DOM. &lt;em&gt;Boxes&lt;/em&gt;, on the other hand, are “physical” boxes. You can think of them as rectangles with size, position, etc. But before we can give them size and position values, we need to generate the boxes. From the &lt;em&gt;style tree&lt;/em&gt;, we create a &lt;em&gt;layout tree&lt;/em&gt;, where nodes are &lt;em&gt;boxes&lt;/em&gt;. Generally, an element translates into a single box. But it’s not always the case. For example, an element with &lt;code&gt;display: none&lt;/code&gt; generates no box at all (and neither do their descendants). In some cases, an element can even generate multiple boxes (think of a &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt;, it has a “marker” box and a “content” box).&lt;/p&gt;&lt;p&gt;Boxes also have padding, margin, border, color, border color, display type (e.g. &lt;code&gt;block&lt;/code&gt; or &lt;code&gt;inline&lt;/code&gt;), etc. All these properties are derived from the associated style in the &lt;em&gt;style tree&lt;/em&gt;. Only the position and size properties are left undefined (for now).&lt;/p&gt;&lt;h2&gt;Reflow&lt;/h2&gt;&lt;p&gt;We now have a tree of boxes (the &lt;em&gt;layout tree&lt;/em&gt;). Reflow modifies the tree, giving it position and size information, according to a layout scheme. This is probably the most complex stage of the entire main pipeline, but I’ll do my best to explain it properly.&lt;/p&gt;&lt;p&gt;There are many types of flow, each with its own reflow algorithm. Most elements follow &lt;em&gt;normal flow&lt;/em&gt; (&lt;code&gt;position: static&lt;/code&gt;). In normal flow, boxes are laid out depending on many parameters, but mainly:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Block-level or inline-level&lt;/li&gt;&lt;li&gt;Block container or inline container&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;In case you’re curious, this is where &lt;code&gt;display: inline-block&lt;/code&gt; does its thing. Take a look at the mappings:&lt;/p&gt;&lt;table&gt;&lt;tr&gt;&lt;th&gt;Value of &lt;code&gt;display&lt;/code&gt;&lt;/th&gt;&lt;th&gt;Box level&lt;/th&gt;&lt;th&gt;Container type&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;block&lt;/code&gt;&lt;/td&gt;&lt;td&gt;block-level&lt;/td&gt;&lt;td&gt;block container&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;inline&lt;/code&gt;&lt;/td&gt;&lt;td&gt;inline-level&lt;/td&gt;&lt;td&gt;inline container&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;inline-block&lt;/code&gt;&lt;/td&gt;&lt;td&gt;inline-level&lt;/td&gt;&lt;td&gt;block container&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;p&gt;Let’s assume, for the sake of simplicity, that everything is a &lt;em&gt;block box&lt;/em&gt; (a &lt;em&gt;block box&lt;/em&gt; is a box which is both &lt;em&gt;block-level&lt;/em&gt; and a &lt;em&gt;block container&lt;/em&gt;).&lt;/p&gt;&lt;p&gt;The way the reflow algorithm would work would be:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Determine my width from the containing block&lt;/li&gt;&lt;li&gt;Reflow children&lt;/li&gt;&lt;li&gt;Determine my height from the sum of heights of my children (or properties like &lt;code&gt;height&lt;/code&gt;)&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;So, in this layout:&lt;/p&gt;&lt;div style=&quot;width:200px&quot;&gt;
  &lt;div style=&quot;background-color:#c00000;padding:10px;min-height:30px&quot;&gt;
    &lt;div style=&quot;background-color:#00c000;padding:10px;min-height:30px&quot;&gt;&lt;/div&gt;
    &lt;div style=&quot;background-color:#0000c0;padding:10px;min-height:30px&quot;&gt;
      &lt;div style=&quot;background-color:#c0c000;padding:10px;min-height:30px&quot;&gt;&lt;/div&gt;
      &lt;div style=&quot;background-color:#c000c0;padding:10px;min-height:30px&quot;&gt;&lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;style&gt;
.color-red { color: #c00000 }
.color-green { color: #00c000 }
.color-blue { color: #0000c0 }
.color-yellow { color: #c0c000 }
.color-pink { color: #c000c0 }
&lt;/style&gt;
&lt;p&gt;The steps, recursively, would be:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Reflow the &lt;strong&gt;&lt;span class=&quot;color-red &quot;&gt;red&lt;/span&gt;&lt;/strong&gt; box&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;Determine the &lt;strong&gt;width&lt;/strong&gt; of the &lt;strong&gt;&lt;span class=&quot;color-red &quot;&gt;red&lt;/span&gt;&lt;/strong&gt; box&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Reflow the &lt;strong&gt;&lt;span class=&quot;color-green &quot;&gt;green&lt;/span&gt;&lt;/strong&gt; box&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;Determine the &lt;strong&gt;width&lt;/strong&gt; of the &lt;strong&gt;&lt;span class=&quot;color-green &quot;&gt;green&lt;/span&gt;&lt;/strong&gt; box&lt;/li&gt;&lt;li&gt;Determine the &lt;strong&gt;height&lt;/strong&gt; of the &lt;strong&gt;&lt;span class=&quot;color-green &quot;&gt;green&lt;/span&gt;&lt;/strong&gt; box&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Reflow the &lt;strong&gt;&lt;span class=&quot;color-blue &quot;&gt;blue&lt;/span&gt;&lt;/strong&gt; box&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;Determine the &lt;strong&gt;width&lt;/strong&gt; of the &lt;strong&gt;&lt;span class=&quot;color-blue &quot;&gt;blue&lt;/span&gt;&lt;/strong&gt; box&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Reflow the &lt;strong&gt;&lt;span class=&quot;color-yellow &quot;&gt;yellow&lt;/span&gt;&lt;/strong&gt; box&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;Determine the &lt;strong&gt;width&lt;/strong&gt; of the &lt;strong&gt;&lt;span class=&quot;color-yellow &quot;&gt;yellow&lt;/span&gt;&lt;/strong&gt; box&lt;/li&gt;&lt;li&gt;Determine the &lt;strong&gt;height&lt;/strong&gt; of the &lt;strong&gt;&lt;span class=&quot;color-yellow &quot;&gt;yellow&lt;/span&gt;&lt;/strong&gt; box&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Reflow the &lt;strong&gt;&lt;span class=&quot;color-pink &quot;&gt;pink&lt;/span&gt;&lt;/strong&gt; box&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;Determine the &lt;strong&gt;width&lt;/strong&gt; of the &lt;strong&gt;&lt;span class=&quot;color-pink &quot;&gt;pink&lt;/span&gt;&lt;/strong&gt; box&lt;/li&gt;&lt;li&gt;Determine the &lt;strong&gt;height&lt;/strong&gt; of the &lt;strong&gt;&lt;span class=&quot;color-pink &quot;&gt;pink&lt;/span&gt;&lt;/strong&gt; box&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Determine the &lt;strong&gt;height&lt;/strong&gt; of the &lt;strong&gt;&lt;span class=&quot;color-blue &quot;&gt;blue&lt;/span&gt;&lt;/strong&gt; box&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Determine the &lt;strong&gt;height&lt;/strong&gt; of the &lt;strong&gt;&lt;span class=&quot;color-red &quot;&gt;red&lt;/span&gt;&lt;/strong&gt; box&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Of course this gets &lt;em&gt;much&lt;/em&gt; more complicated with more complex layouts, but the algorithms generally work in a pretty similar way to the one I described.&lt;/p&gt;&lt;h2&gt;Painting&lt;/h2&gt;&lt;p&gt;We now have a complete &lt;em&gt;layout tree&lt;/em&gt;. To paint it, we can walk the tree, emitting “commands” for each node. For example, if an element has a background color assigned, we can emit a command to draw a rectangle on screen on the correct coordinates.&lt;/p&gt;&lt;p&gt;When I say &lt;em&gt;emit&lt;/em&gt; what I really mean is append it to a sequence of commands which, as we discussed earlier, will be the output of the entire pipeline. The &lt;em&gt;browser&lt;/em&gt; (not &lt;em&gt;browser engine&lt;/em&gt;) will then take those commands and actually draw it on screen in a platform-specific way.&lt;/p&gt;&lt;p&gt;That was quite long, just to explain the main pipeline, huh?&lt;/p&gt;&lt;h1&gt;My engine&lt;/h1&gt;&lt;p&gt;This whole time I haven’t talked about my engine at all. And honestly, that’s because I don’t have much to show. I was planning on writing a post about it when it got more advanced, but I wanted to do this post explaining concepts in general. Since we’re at it, here’s a sneak peak of what (mostly) works:&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://arnauc.me/blog/how-browsers-work/axiom.png&quot;&gt;
&lt;figcaption&gt;A test page rendered on my own browser engine&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;I know, it’s not very amusing, and the text is barely readable&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, but it shows it can render something. Here’s the exact HTML in case you’re interested:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;html&quot;&gt;&lt;span class=&quot;constant&quot;&gt;&amp;lt;!DOCTYPE html&lt;/span&gt;&lt;span class=&quot;punctuation_bracket constant&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;Test page&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;Test page&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;This is a test page.&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;style&lt;/span&gt;=&amp;quot;&lt;span class=&quot;string&quot;&gt;color: red&lt;/span&gt;&amp;quot;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;The quick brown fox jumps over the lazy dog.&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;style&lt;/span&gt;=&amp;quot;&lt;span class=&quot;string&quot;&gt;color: green&lt;/span&gt;&amp;quot;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;The quick brown fox jumps over the lazy dog.&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;style&lt;/span&gt;=&amp;quot;&lt;span class=&quot;string&quot;&gt;color: blue&lt;/span&gt;&amp;quot;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;The quick brown fox jumps over the lazy dog.&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;style&lt;/span&gt;=&amp;quot;&lt;span class=&quot;string&quot;&gt;background-color: #c00000; padding: 4px; color: white&lt;/span&gt;&amp;quot;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;style&lt;/span&gt;=&amp;quot;&lt;span class=&quot;string&quot;&gt;background-color: #00c000; padding: 4px&lt;/span&gt;&amp;quot;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;attribute&quot;&gt;style&lt;/span&gt;=&amp;quot;&lt;span class=&quot;string&quot;&gt;background-color: #0000c0&lt;/span&gt;&amp;quot;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;Hello world&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;tag&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The engine source is &lt;a href=&quot;https://codeberg.org/arnauc/axiom&quot; target=&quot;_blank&quot;&gt;on codeberg&lt;/a&gt;&lt;/p&gt;</description>
        <link>https://arnauc.me/blog/how-browsers-work/</link>
        <pubDate>Mon, 26 Jan 2026 00:00:00 +0000</pubDate>
        <guid>https://arnauc.me/blog/how-browsers-work/</guid>
      </item>
    
      <item>
        <title>Finding vulnerabilities in a cleaning robot</title>
        <description>&lt;p&gt;I’ve had a &lt;em&gt;round cleaning robot of a certain brand&lt;/em&gt; for quite some time now. The other day I noticed a suspicious micro USB port on the bottom. So I had to do some poking around. This is the story of how I got root shell access to my cleaning robot.&lt;/p&gt;&lt;p&gt;After connecting it to my computer, the first thing I did was a &lt;code&gt;lsusb&lt;/code&gt;. It basically shows all the connected USB devices. When I saw the list, I immediately got excited.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Bus 001 Device 005: ID 18d1:d002 Google Inc. Nexus 4 (debug)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Wait, so it’s &lt;a href=&quot;https://en.wikipedia.org/wiki/Nexus_4&quot; target=&quot;_blank&quot;&gt;a phone&lt;/a&gt;? No. But the fact that it’s showing up as an android device means it probably has ADB (Android Debug Bridge) support.&lt;/p&gt;&lt;p&gt;I fired &lt;code&gt;adb shell&lt;/code&gt;, hoping to get access right away. But instead I was prompted for login. Weird. Apparently, &lt;code&gt;/bin/login&lt;/code&gt; was being executed, instead of a shell. I get why they would ask for a login, but it’s &lt;em&gt;not the right way&lt;/em&gt; to secure a device. &lt;code&gt;adb shell&lt;/code&gt; is not the only way to access it. &lt;code&gt;adb&lt;/code&gt; has commands like &lt;code&gt;push&lt;/code&gt; and &lt;code&gt;pull&lt;/code&gt; (which send and receive files, respectively). And, when using those commands, the shell (or in this case &lt;code&gt;/bin/login&lt;/code&gt;) is not executed.&lt;/p&gt;&lt;p&gt;So, a simple &lt;code&gt;adb pull /etc&lt;/code&gt; can get us &lt;code&gt;/etc/passwd&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;root:x:0:0:root:/root:/bin/ash
daemon:*:1:1:daemon:/var:/bin/false
ftp:*:55:55:ftp:/home/ftp:/bin/false
network:*:101:101:network:/var:/bin/false
nobody:*:65534:65534:nobody:/var:/bin/false
dnsmasq:x:453:453:dnsmasq:/var/run/dnsmasq:/bin/false
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can see the password field for &lt;code&gt;root&lt;/code&gt; is &lt;code&gt;x&lt;/code&gt;, which means the password is actually stored in &lt;code&gt;/etc/shadow&lt;/code&gt;, hashed. We read it the same way:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;root:$1$trVg0hig$L.xDOM91z4d/.8FZRnr.h1:17752:0:99999:7:::
daemon:*:0:0:99999:7:::
ftp:*:0:0:99999:7:::
network:*:0:0:99999:7:::
nobody:*:0:0:99999:7:::
dnsmasq:x:0:0:99999:7:::
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The password is stored in &lt;em&gt;MD5&lt;/em&gt;. So, that’s it, we can simply crack it.&lt;/p&gt;&lt;p&gt;Or rather, we can delete that &lt;code&gt;x&lt;/code&gt; in &lt;code&gt;/etc/passwd&lt;/code&gt; using &lt;code&gt;adb push&lt;/code&gt;, and the password will be completely diasbled (not like they use PAM or anything…).&lt;/p&gt;&lt;p&gt;Now, if we do &lt;code&gt;adb shell&lt;/code&gt;, we are prompted for the user, but we no longer need the password.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;TinaLinux login: root


BusyBox v1.24.1 () built-in shell (ash)

 _____  _              __     _
|_   _||_| ___  _ _   |  |   |_| ___  _ _  _ _
  | |   _ |   ||   |  |  |__ | ||   || | ||_&amp;apos;_|
  | |  | || | || _ |  |_____||_||_|_||___||_,_|
  |_|  |_||_|_||_|_|  Tina is Based on OpenWrt!
 ----------------------------------------------
 Tina Linux (Neptune, 57513AA3)
 ----------------------------------------------
root@TinaLinux:~#
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There you go, we have a shell!&lt;/p&gt;&lt;h1&gt;Network access&lt;/h1&gt;&lt;p&gt;Sure, someone can get root access to your cleaning robot, but they’ll need to physically connect to it. At that point they might as well steal it. So it’s not a big deal, right?&lt;/p&gt;&lt;p&gt;Right…?&lt;/p&gt;&lt;p&gt;Guess what, the cleaning robot is running an SSH server, with password login enabled. So you don’t even need to connect via ADB, you can connect via SSH.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Anyone on the local network&lt;/strong&gt; can deploy their code to your cleaning robot, even in an automated way (the password is the same for all the robots of a specific model). In some cases, a leftover open port in the home router firewall configuration&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; can allow that kind of attack from anywhere on the internet.&lt;/p&gt;&lt;h1&gt;Key takeaways&lt;/h1&gt;&lt;p&gt;Multiple things can be learned from this.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;If you want to add authentication to something, don’t implement your own hacky and quick solution – do it the intended way.&lt;/li&gt;&lt;li&gt;Don’t allow password login for SSH. Or, if you do so, don’t use MD5.&lt;/li&gt;&lt;li&gt;Never assume your device will be behind a NAT. Don’t make its security dependant on that.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Thanks for reading! &lt;code&gt;;)&lt;/code&gt;&lt;/p&gt;</description>
        <link>https://arnauc.me/blog/cleaning-robot/</link>
        <pubDate>Fri, 28 Nov 2025 00:00:00 +0000</pubDate>
        <guid>https://arnauc.me/blog/cleaning-robot/</guid>
      </item>
    
      <item>
        <title>Creating a game in assembly</title>
        <description>&lt;p&gt;Coding in assembly imposes some challenges and difficulties that make it &lt;strong&gt;fun&lt;/strong&gt;, the same way a puzzle is fun. I recently decided to create a game in assembly. Not for the final result, but for the fun of writing assembly. This is the first post about the project. I might not “finish” it, but again, it’s not the goal of the project anyway.&lt;/p&gt;&lt;p&gt;I wanted it to be x86 (as that’s the architecture family I know the most), but I didn’t know which specific architecture would be best. At the end, I decided to do it for 16-bit &lt;a href=&quot;https://en.wikipedia.org/wiki/Real_mode&quot; target=&quot;_blank&quot;&gt;real mode&lt;/a&gt;, as that’s the mode &lt;em&gt;every&lt;/em&gt; x86 CPU boots in. That way, I am as close to the hardware as possible. Also, writing assembly for a 16-bit CPU is more challenging than a 32 or 64-bit one – and therefore more fun.&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://arnauc.me/blog/assembly-game/fun-chart.svg&quot;&gt;
&lt;figcaption&gt;A “challenging” to “fun” chart&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;h1&gt;Booting&lt;/h1&gt;&lt;p&gt;When a computer powers on&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fn-1-ref-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, the BIOS looks for disks containing a magic byte sequence at the end of the first 512-byte sector. When it finds one, it loads the entire sector into memory and jumps to it.&lt;/p&gt;&lt;p&gt;The &lt;code&gt;boot.asm&lt;/code&gt; file looks like this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;asm&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;org &lt;/span&gt;&lt;span class=&quot;keyword constant_numeric_integer&quot;&gt;0x7c00&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;; BIOS loads us at 0x7c00, absolute references should have this offset&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;BITS 16&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;; Default to 16-bit mode instructions&lt;/span&gt;

&lt;span class=&quot;constant label constant_builtin variable&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter label&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;comment&quot;&gt;; All the code goes here...&lt;/span&gt;

&lt;span class=&quot;function_special&quot;&gt;times &lt;/span&gt;&lt;span class=&quot;constant_numeric_integer function_special&quot;&gt;510&lt;/span&gt;&lt;span class=&quot;operator_binary function_special&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;punctuation_bracket function_special&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable_builtin function_special&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;operator_binary function_special&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;variable_builtin function_special&quot;&gt;$$&lt;/span&gt;&lt;span class=&quot;punctuation_bracket function_special&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;function_special&quot;&gt; &lt;/span&gt;&lt;span class=&quot;function_special&quot;&gt;db &lt;/span&gt;&lt;span class=&quot;constant_numeric_integer function_special&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;; Pad with zeros to make it 510 bytes long&lt;/span&gt;
&lt;span class=&quot;function_special&quot;&gt;dw &lt;/span&gt;&lt;span class=&quot;constant_numeric_integer function_special&quot;&gt;0xAA55&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;; Add two more bytes (the magic bytes)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This does works, and the code under &lt;code&gt;start&lt;/code&gt; will run. The &lt;code&gt;start&lt;/code&gt; label is used here to make it easier to understand. In reality the BIOS just jumps to &lt;code&gt;0x7c00&lt;/code&gt;, so it will execute the first instruction in the boot sector.&lt;/p&gt;&lt;p&gt;Now we can write the game, and that’s it! Well, not quite. As our game grows larger and larger, it will eventually be &lt;strong&gt;too big&lt;/strong&gt; to fit in the 512-byte boot sector. The solution is to write a boot sector that &lt;em&gt;loads more sectors&lt;/em&gt;, and put the game on those extra sectors. In case you’re curious on how this works (&lt;em&gt;it’s pretty low-level, feel free to skip it&lt;/em&gt;), here you have the code:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;asm&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;org &lt;/span&gt;&lt;span class=&quot;keyword constant_numeric_integer&quot;&gt;0x7c00&lt;/span&gt;
&lt;span class=&quot;keyword&quot;&gt;BITS 16&lt;/span&gt;

&lt;span class=&quot;constant label constant_builtin variable&quot;&gt;SECTORS_TO_LOAD&lt;/span&gt; &lt;span class=&quot;function_special&quot;&gt;equ &lt;/span&gt;&lt;span class=&quot;constant_numeric_integer function_special&quot;&gt;64&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;; 32 KiB&lt;/span&gt;

&lt;span class=&quot;constant label constant_builtin variable&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter label&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;comment&quot;&gt;; Disable interrupts&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;cli&lt;/span&gt;

    &lt;span class=&quot;comment&quot;&gt;; Set the stack on segment 0 (ss=0) and address 0x7c00 (sp=0x7c00)&lt;/span&gt;
    &lt;span class=&quot;comment&quot;&gt;; Since the stack grows downwards, it won&amp;apos;t interfere with our code&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;xor&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;ax&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;ax&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;mov&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;ss&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;ax&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;mov&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;sp&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;0x7c00&lt;/span&gt;

    &lt;span class=&quot;comment&quot;&gt;; We will load the &amp;quot;stage 2&amp;quot; code at 0x1000:0x0000 (physical address 0x10000)&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;mov&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;ax&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;0x1000&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;mov&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;es&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;ax&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;xor&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;bx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;bx&lt;/span&gt;

    &lt;span class=&quot;comment&quot;&gt;; Start from cylinder 0, head 0, sector 2 (sector 1 is the boot sector)&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;xor&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;ch&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;ch&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;; cylinder&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;xor&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;dh&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;dh&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;; head&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;mov&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;cl&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;; sector&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;mov&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;si&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant constant_builtin variable&quot;&gt;SECTORS_TO_LOAD&lt;/span&gt;

&lt;span class=&quot;constant label constant_builtin variable&quot;&gt;load_loop&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter label&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;comment&quot;&gt;; Call the BIOS function to read the sector&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;mov&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;ah&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;0x02&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;; read sectors&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;mov&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;al&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;; 1 sector&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;0x13&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;jc&lt;/span&gt; &lt;span class=&quot;constant constant_builtin variable&quot;&gt;disk_error&lt;/span&gt;

    &lt;span class=&quot;comment&quot;&gt;; Increment the offset by 512&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;bx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;512&lt;/span&gt;

    &lt;span class=&quot;comment&quot;&gt;; CHS increment logic&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;inc&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;cl&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;cmp&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;cl&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;19&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;jl&lt;/span&gt; &lt;span class=&quot;constant constant_builtin variable&quot;&gt;cont_chs&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;mov&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;cl&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;inc&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;dh&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;cmp&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;dh&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;2&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;jl&lt;/span&gt; &lt;span class=&quot;constant constant_builtin variable&quot;&gt;cont_chs&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;mov&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;dh&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;inc&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;ch&lt;/span&gt;

&lt;span class=&quot;constant label constant_builtin variable&quot;&gt;cont_chs&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter label&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;comment&quot;&gt;; Loop if there are sectors left to load&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;dec&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;si&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;jnz&lt;/span&gt; &lt;span class=&quot;constant constant_builtin variable&quot;&gt;load_loop&lt;/span&gt;

    &lt;span class=&quot;comment&quot;&gt;; Set the data segment (ds) to 0x1000&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;mov&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;ax&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;0x1000&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;mov&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;ds&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;ax&lt;/span&gt;

    &lt;span class=&quot;comment&quot;&gt;; Far-jump to code segment 0x1000 at offset 0x0000&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;jmp&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;0x1000&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;:&lt;/span&gt;0x0000

&lt;span class=&quot;constant label constant_builtin variable&quot;&gt;disk_error&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter label&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;comment&quot;&gt;; Report the error...&lt;/span&gt;

    &lt;span class=&quot;comment&quot;&gt;; Halt forever&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;jmp&lt;/span&gt; &lt;span class=&quot;variable_builtin&quot;&gt;$&lt;/span&gt;

&lt;span class=&quot;function_special&quot;&gt;times &lt;/span&gt;&lt;span class=&quot;constant_numeric_integer function_special&quot;&gt;510&lt;/span&gt;&lt;span class=&quot;operator_binary function_special&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;punctuation_bracket function_special&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable_builtin function_special&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;operator_binary function_special&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;variable_builtin function_special&quot;&gt;$$&lt;/span&gt;&lt;span class=&quot;punctuation_bracket function_special&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;function_special&quot;&gt; &lt;/span&gt;&lt;span class=&quot;function_special&quot;&gt;db &lt;/span&gt;&lt;span class=&quot;constant_numeric_integer function_special&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;function_special&quot;&gt;dw &lt;/span&gt;&lt;span class=&quot;constant_numeric_integer function_special&quot;&gt;0xAA55&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’s it! Now, we can append a “stage 2” after the boot sector in our binary, and it will load and run it.&lt;/p&gt;&lt;h1&gt;VGA&lt;/h1&gt;&lt;p&gt;Most games have graphics. But right now, how do we draw to the screen? That’s where VGA modes come in. We can ask the BIOS (via &lt;a href=&quot;https://en.wikipedia.org/wiki/BIOS_interrupt_call&quot; target=&quot;_blank&quot;&gt;BIOS services&lt;/a&gt;) to set up a specific VGA mode. Each mode has a set resolution, color depth, etc. We’ll be using &lt;a href=&quot;https://en.wikipedia.org/wiki/Mode_13h&quot; target=&quot;_blank&quot;&gt;VGA mode &lt;code&gt;0x13&lt;/code&gt;&lt;/a&gt; (also called mode &lt;code&gt;13h&lt;/code&gt;). It has a resolution of 320 by 200 pixels and a 256-color palette.&lt;/p&gt;&lt;p&gt;So, before we draw anything, we must set up mode &lt;code&gt;0x13&lt;/code&gt;. We do so with the &lt;a href=&quot;https://en.wikipedia.org/wiki/INT_10H&quot; target=&quot;_blank&quot;&gt;BIOS interrupt &lt;code&gt;0x10&lt;/code&gt;&lt;/a&gt; with &lt;code&gt;ah=0&lt;/code&gt;. After the interrupt call, the video mode will be set to &lt;code&gt;al&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;asm&quot;&gt;    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;mov&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;ah&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;0x00&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;mov&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;al&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;0x13&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;0x10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, at physical address &lt;code&gt;0xa0000&lt;/code&gt; we have the framebuffer. Because of the way &lt;a href=&quot;https://en.wikipedia.org/wiki/X86_memory_segmentation#Real_mode&quot; target=&quot;_blank&quot;&gt;segmentation&lt;/a&gt; works, this corresponds to segment &lt;code&gt;0xa000&lt;/code&gt; at offset &lt;code&gt;0x0000&lt;/code&gt;. So, we could simply do something like:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;asm&quot;&gt;    &lt;span class=&quot;comment&quot;&gt;; Set es=0xa000&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;mov&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;ax&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;0xa000&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;mov&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;es&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;ax&lt;/span&gt;

    &lt;span class=&quot;comment&quot;&gt;; Set di=0 (top-left pixel)&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;xor&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;di&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;di&lt;/span&gt;

    &lt;span class=&quot;comment&quot;&gt;; Write at byte 0xa000:0x0000&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;mov&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;byte&lt;/span&gt; &lt;span class=&quot;punctuation_bracket&quot;&gt;[&lt;/span&gt;es&lt;span class=&quot;punctuation_delimiter constant_builtin&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;constant_builtin&quot;&gt;di&lt;/span&gt;&lt;span class=&quot;punctuation_bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;0x04&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;; dark red&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The problem is that the computer is repeatidly displaying what’s on the buffer. So, what happens if it displays the framebuffer while we’re still drawing a frame? The user will experience visual artifacts, such as &lt;a href=&quot;https://en.wikipedia.org/wiki/Screen_tearing&quot; target=&quot;_blank&quot;&gt;tearing&lt;/a&gt;. The solution is &lt;a href=&quot;https://en.wikipedia.org/wiki/Multiple_buffering&quot; target=&quot;_blank&quot;&gt;double buffering&lt;/a&gt;. We’ll draw to a side buffer and, when we’re done drawing the frame, we copy it to the actual framebuffer.&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img src=&quot;https://arnauc.me/blog/assembly-game/double-buffering.svg&quot;&gt;
&lt;figcaption&gt;A timeline of a program with double buffering&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;As you can see in the diagram, while the drawing buffer contains invalid (incomplete) frames most of the times, the actual framebuffer &lt;strong&gt;always contains a valid frame&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;To do this, we can write to our own framebuffer at address, say, &lt;code&gt;0x90000&lt;/code&gt;, and then copy it to &lt;code&gt;0xa0000&lt;/code&gt; once we’re done. To do this copying, we can write this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;asm&quot;&gt;&lt;span class=&quot;constant label constant_builtin variable&quot;&gt;present_framebuffer&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter label&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;pusha&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;; push all the general registers to the stack&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;push&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;ds&lt;/span&gt;
    &lt;span class=&quot;constant label constant_builtin variable&quot;&gt;push&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;es&lt;/span&gt;

    &lt;span class=&quot;comment&quot;&gt;; set es=0xa000&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;mov&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;ax&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;0xa000&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;mov&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;es&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;ax&lt;/span&gt;

    &lt;span class=&quot;comment&quot;&gt;; set ds=0x9000&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;mov&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;ax&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;0x9000&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;mov&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;ds&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;ax&lt;/span&gt;

    &lt;span class=&quot;comment&quot;&gt;; set di=0 and si=0&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;xor&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;di&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;di&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;xor&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;si&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;si&lt;/span&gt;

    &lt;span class=&quot;comment&quot;&gt;; set cx to the length of the framebuffer (in words)&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;mov&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;cx&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant constant_builtin variable&quot;&gt;SCREEN_WIDTH&lt;/span&gt;&lt;span class=&quot;operator_binary&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;constant constant_builtin variable&quot;&gt;SCREEN_HEIGHT&lt;/span&gt;&lt;span class=&quot;operator_binary&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;constant_numeric_integer&quot;&gt;2&lt;/span&gt;

    &lt;span class=&quot;comment&quot;&gt;; copy cx words from [(ds):si] to [es:di]&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;cld&lt;/span&gt;
    &lt;span class=&quot;keyword&quot;&gt;rep&lt;/span&gt; &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;movsw&lt;/span&gt;

    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;pop&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;es&lt;/span&gt;
    &lt;span class=&quot;constant label constant_builtin variable&quot;&gt;pop&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;ds&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;popa&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;; pop all the general registers from the stack&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;; jump to the popped address (to return from a `call` instruction)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, when we’re done rendering, we do &lt;code&gt;call present_framebuffer&lt;/code&gt;.&lt;/p&gt;&lt;h1&gt;Calling convention&lt;/h1&gt;&lt;p&gt;“Functions” and “arguments” are high-level concepts that mean nothing in assembly. That’s why, if we want to have similar constructs, we must establish a &lt;em&gt;calling convention&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;For this project, I used a calling convention similar to &lt;a href=&quot;https://en.wikipedia.org/wiki/X86_calling_conventions#cdecl&quot; target=&quot;_blank&quot;&gt;cdecl&lt;/a&gt;. The arguments are pushed to the stack before calling the function. Then, the function is called using the &lt;code&gt;call&lt;/code&gt; instruction, which pushes the return address and jumps. When it returns, it’s the caller responsibility to clean up the stack. So, a basic call might look like:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;asm&quot;&gt;    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;push&lt;/span&gt; &lt;span class=&quot;constant constant_builtin variable&quot;&gt;grass_texture&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;; texture address&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;push&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;32&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;; texture width&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;push&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;32&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;; texture height&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;push&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;; x position&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;push&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;; y position&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;call&lt;/span&gt; &lt;span class=&quot;constant constant_builtin variable&quot;&gt;draw_texture&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;sp&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Having the responsibility to clean the stack on the caller, combined with clever argument order, allows us to do something really interesting. Often, you want to draw the same texture multiple times, at different positions. Since only the last two arguments change, we can do the following:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;asm&quot;&gt;    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;push&lt;/span&gt; &lt;span class=&quot;constant constant_builtin variable&quot;&gt;grass_texture&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;; texture address&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;push&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;32&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;; texture width&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;push&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;32&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;; texture height&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;push&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;; x position&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;push&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;; y position&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;call&lt;/span&gt; &lt;span class=&quot;constant constant_builtin variable&quot;&gt;draw_texture&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;sp&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;4&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;push&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;; x position&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;push&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;50&lt;/span&gt; &lt;span class=&quot;comment&quot;&gt;; y position&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;call&lt;/span&gt; &lt;span class=&quot;constant constant_builtin variable&quot;&gt;draw_texture&lt;/span&gt;
    &lt;span class=&quot;variable constant constant_builtin function&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;constant_builtin&quot;&gt;sp&lt;/span&gt;&lt;span class=&quot;punctuation_delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;constant_numeric_integer&quot;&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;The actual game&lt;/h1&gt;&lt;p&gt;As for the game itself, I have little to show. I have a tile-based terrain renderer, a scrollable camera, and some basic UI elements. I haven’t even fully decided the gameplay style yet. I hope in the next post about the project I can show some gameplay, but for now I don’t have anything worth showing.&lt;/p&gt;&lt;p&gt;To give a sense of how low-level the development is, the project already has more than 600 lines of assembly and I barely have anything. So don’t expect much.&lt;/p&gt;</description>
        <link>https://arnauc.me/blog/assembly-game/</link>
        <pubDate>Tue, 07 Oct 2025 00:00:00 +0000</pubDate>
        <guid>https://arnauc.me/blog/assembly-game/</guid>
      </item>
    
  </channel>
</rss>
