<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://sgeos.github.io/feed/gamedev.xml" rel="self" type="application/atom+xml" /><link href="https://sgeos.github.io/" rel="alternate" type="text/html" /><updated>2026-07-12T01:04:06+00:00</updated><id>https://sgeos.github.io/feed/gamedev.xml</id><title type="html">Brendan A R Sechter’s Development Blog | Gamedev</title><subtitle>A personal technical notebook covering systems programming, systems philosophy, tooling, mathematics, and emerging software paradigms.
</subtitle><author><name>Brendan Sechter</name></author><entry><title type="html">Magic Cards as a Model of Virtual Goods</title><link href="https://sgeos.github.io/mtg/gamedev/economics/2026/02/13/magic_cards_as_a_model_of_virtual_goods.html" rel="alternate" type="text/html" title="Magic Cards as a Model of Virtual Goods" /><published>2026-02-13T07:51:26+00:00</published><updated>2026-02-13T07:51:26+00:00</updated><id>https://sgeos.github.io/mtg/gamedev/economics/2026/02/13/magic_cards_as_a_model_of_virtual_goods</id><content type="html" xml:base="https://sgeos.github.io/mtg/gamedev/economics/2026/02/13/magic_cards_as_a_model_of_virtual_goods.html"><![CDATA[<!-- A81 -->
<script>console.log("A81");</script>

<p>Magic: The Gathering has been called
“the best game ever created” by players and commentators
across three decades of competitive play.
Whether or not one agrees with that assessment,
the commercial success of Magic is difficult to dispute.
Richard Garfield’s 1993 creation launched
the collectible card game industry,
and Magic remains commercially viable
more than thirty years later.
Over 200 sets have been released.
Tens of thousands of unique cards have been printed.
The game has more players today
than at any previous point in its history.</p>

<p>What makes Magic interesting as a subject of analysis
is not just the game itself
but the physical artifact at its center: the card.
A Magic card is an extraordinarily dense physical data structure.
It encodes identity, cost, type, rules, metadata,
and collectibility information
on a single piece of printed cardstock.
This density makes Magic cards
a useful model for understanding virtual goods,
digital items that share many of the same structural properties
but exist without the physical substrate.</p>

<p>This article examines the anatomy of a Magic card,
explains why the card structure maps cleanly
to the economics of virtual goods,
and explores what that mapping reveals
about the production, distribution, and valuation
of digital items.
A companion article,
<a href="/games/strategy/game-theory/war-gaming/2026/01/14/metagaming_framework_for_life_strategy.html">Metagaming as a Framework for Real-Life Strategy</a>,
covers the strategic dynamics of Magic
including chase cards, bulk cards, and meta-defining game tokens.</p>

<h2 id="software-versions">Software Versions</h2>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Date (UTC)</span>
<span class="nv">$ </span><span class="nb">date</span> <span class="nt">-u</span> <span class="s2">"+%Y-%m-%d %H:%M:%S +0000"</span>
2026-02-13 07:51:26 +0000

<span class="c"># OS and Version</span>
<span class="nv">$ </span><span class="nb">uname</span> <span class="nt">-vm</span>
Darwin Kernel Version 23.6.0: Mon Jul 29 21:14:30 PDT 2024<span class="p">;</span> root:xnu-10063.141.2~1/RELEASE_ARM64_T6000 arm64

<span class="nv">$ </span>sw_vers
ProductName:		macOS
ProductVersion:		14.6.1
BuildVersion:		23G93

<span class="c"># Hardware Information</span>
<span class="nv">$ </span>system_profiler SPHardwareDataType | <span class="nb">sed</span> <span class="nt">-n</span> <span class="s1">'8,10p'</span>
      Chip: Apple M1 Max
      Total Number of Cores: 10 <span class="o">(</span>8 performance and 2 efficiency<span class="o">)</span>
      Memory: 32 GB

<span class="c"># Shell and Version</span>
<span class="nv">$ </span><span class="nb">echo</span> <span class="s2">"</span><span class="k">${</span><span class="nv">SHELL</span><span class="k">}</span><span class="s2">"</span>
/bin/bash

<span class="nv">$ </span><span class="s2">"</span><span class="k">${</span><span class="nv">SHELL</span><span class="k">}</span><span class="s2">"</span> <span class="nt">--version</span> | <span class="nb">head</span> <span class="nt">-n</span> 1
GNU bash, version 3.2.57<span class="o">(</span>1<span class="o">)</span><span class="nt">-release</span> <span class="o">(</span>arm64-apple-darwin23<span class="o">)</span>

<span class="c"># Claude Code Installation Versions</span>
<span class="nv">$ </span>claude <span class="nt">--version</span>
2.1.37 <span class="o">(</span>Claude Code<span class="o">)</span>
</code></pre></div></div>

<h2 id="anatomy-of-a-magic-card">Anatomy of a Magic Card</h2>

<p>A Magic card is a standardized 63mm by 88mm piece of printed cardstock.
Every card in the game shares the same physical dimensions
and the same card back,
a design choice that ensures face-down cards
are nominally indistinguishable from one another.
This uniformity is structurally important.
It means that a player’s hand of cards
and the top card of their deck
carry no visible information to the opponent,
enabling hidden information as a core game mechanic.</p>

<p>The face of a Magic card is divided
into several distinct information regions.
Each region serves a specific function
in the game’s rules engine.
The following breakdown uses the standard card frame
introduced in the 2003 revision
and refined in subsequent updates.</p>

<h3 id="card-name">Card Name</h3>

<p>The card name appears at the top left of the card face.
It is the primary identifier for the card.
Game rules, deck construction constraints,
and card interactions all reference cards by name.
A standard deck may contain at most four copies
of any card with a given name,
with the exception of basic land cards.</p>

<h3 id="mana-cost">Mana Cost</h3>

<p>The mana cost appears at the top right of the card face.
It specifies the resources required to play the card.
Mana costs use a symbolic notation
that encodes both the total amount of mana required
and the specific colors of mana that must be included.
For example, a cost of 2R means
two mana of any color plus one red mana.
The five colors of mana in Magic
are white (W), blue (U), black (B), red (R), and green (G).
Colorless mana and hybrid mana costs
extend this system further.</p>

<p>The mana cost is a critical game design lever.
It determines when a card can be played
and what strategic trade-offs a player must make
to include it in a deck.
Powerful effects with low mana costs
tend to become chase cards
because they offer disproportionate value.</p>

<h3 id="card-art">Card Art</h3>

<p>The illustration occupies the largest single region of the card face.
Magic has commissioned original artwork for every card
from thousands of artists over the game’s history.
The art serves no mechanical function
but contributes significantly to the card’s identity,
collectibility, and market value.
Cards with art by sought-after artists
or with particularly striking compositions
can command premiums independent of gameplay value.</p>

<h3 id="type-line">Type Line</h3>

<p>The type line appears below the card art.
It specifies the card’s type, and optionally,
its subtypes and supertypes.</p>

<p>The major card types include Creature, Instant, Sorcery,
Enchantment, Artifact, Planeswalker, and Land.
Each type has distinct rules for how and when the card can be played
and how it behaves on the battlefield.
Subtypes provide further classification.
A creature might be a “Human Wizard” or a “Goblin Warrior.”
An instant might be a “Lesson.”
Supertypes like “Legendary” or “Basic”
add additional rules constraints.</p>

<p>The type line functions as a classification system
analogous to a type hierarchy in a programming language.
Cards interact with each other based on type.
A spell that says “destroy target creature”
operates on the Creature type.
A spell that says “search your library for a Goblin”
operates on the Goblin subtype.
This system enables combinatorial interactions
without requiring each interaction to be individually specified.</p>

<h3 id="set-symbol">Set Symbol</h3>

<p>The set symbol appears on the right side of the type line.
It identifies which set the card was printed in
and indicates the card’s rarity through color.</p>

<p>The four standard rarity levels are
common (black or white symbol),
uncommon (silver),
rare (gold),
and mythic rare (red-orange).
Rarity determines how frequently a card appears
in randomized booster packs.
A mythic rare appears roughly once in every eight packs,
while commons fill the majority of each pack.</p>

<p>Rarity is the primary supply-side mechanism
that creates scarcity and drives secondary market prices.
A powerful mythic rare is scarce by design,
while a powerful common is abundant.</p>

<h3 id="rules-text">Rules Text</h3>

<p>The rules text box occupies the center-lower portion of the card.
It contains the card’s mechanical instructions.</p>

<p>Magic’s rules system operates on a principle
that Richard Garfield has described as
making the game “bigger than the box.”
A base set of comprehensive rules governs the game,
but individual cards can create exceptions to those rules.
A card might say “this creature cannot be blocked,”
overriding the default blocking rules.
Another might say “you may play an additional land this turn,”
overriding the one-land-per-turn rule.</p>

<p>This exception-based design is what gives Magic
its combinatorial depth.
The base rules are learnable.
The card-specific exceptions are printed on the cards themselves.
New cards can introduce entirely new mechanics
without requiring changes to the core rules engine.
In software engineering terms,
the cards are plugins to a stable runtime.</p>

<h3 id="power-and-toughness">Power and Toughness</h3>

<p>Creature cards display two numbers
in the bottom right corner of the card,
separated by a slash.
The first number is the creature’s power,
which determines how much damage it deals in combat.
The second is its toughness,
which determines how much damage it can sustain
before being destroyed.</p>

<p>A 2/3 creature deals 2 damage and survives 3 damage.
These values interact with the mana cost
to define the creature’s efficiency.
A 2/3 creature for two mana
is more efficient than a 2/3 creature for five mana.
This efficiency calculation is central to competitive deck construction.</p>

<h3 id="collector-information">Collector Information</h3>

<p>The bottom edge of the card contains
a line of collector information
that serves no gameplay function
but encodes metadata about the card’s production.</p>

<p>A modern collector information line contains
the collector number (the card’s position within the set,
such as “123/250”),
the set code (a three-letter identifier like “MKM” for Murders at Karlov Manor),
the rarity indicator (C, U, R, or M),
the card’s language code,
and the artist credit.
Below this line,
a copyright notice identifies the card
as a product of Wizards of the Coast
and Hasbro.</p>

<p>Since 2014, rare and mythic rare cards
also carry a holographic security stamp
at the bottom center of the card.
This stamp serves as an anti-counterfeiting measure,
reflecting the fact that some individual cards
have secondary market values exceeding hundreds of dollars.</p>

<h3 id="flavor-text">Flavor Text</h3>

<p>When space permits, the rules text box
may include flavor text printed in italics
below the mechanical rules.
Flavor text has no game function.
It provides narrative context,
world-building details, or character quotes
that connect the card to Magic’s fictional multiverse.</p>

<h2 id="why-magic-cards-model-virtual-goods">Why Magic Cards Model Virtual Goods</h2>

<p>The structural analysis above reveals
why Magic cards are a useful physical model
for understanding virtual goods.
The key insight is that a Magic card
is essentially a physical data structure
with a near-zero marginal cost of production.</p>

<h3 id="near-zero-marginal-cost-of-production">Near-Zero Marginal Cost of Production</h3>

<p>The cost of printing a single Magic card
is trivially small when amortized across a production run.
The cardstock, ink, and packaging
cost fractions of a cent per card.
A chase mythic rare that sells for fifty dollars
on the secondary market
costs the same to print
as a bulk common that sells for a fraction of a cent.
If premium treatments like foil stamping
and alternate art variants are set aside,
the marginal cost of producing any individual card
is effectively identical regardless of its gameplay value
or secondary market price.</p>

<p>This is the defining characteristic of virtual goods as well.
A digital item in a video game,
whether it is a cosmetic skin, a weapon, or a currency unit,
has a near-zero marginal cost of distribution.
Once the asset has been created,
delivering one additional copy to one additional user
costs effectively nothing.
The physical card and the virtual item
share the same economic structure:
high fixed costs of creation,
near-zero marginal costs of distribution.</p>

<h3 id="the-rd-cost-structure">The R&amp;D Cost Structure</h3>

<p>The non-trivial costs in Magic card production
are research and development costs.
Designing a Magic set requires
game designers, developers, playtesters,
artists, editors, and production staff
working over a multi-year development cycle.
Each set must be balanced internally,
balanced against the existing card pool,
and tested across multiple competitive formats.
This R&amp;D effort is expensive,
but it is a fixed cost
that is amortized across millions of printed cards.</p>

<p>Virtual goods share this cost structure precisely.
The development cost of a new character class in an online game,
a new weapon skin, or a new expansion pack
is real and often substantial.
The distribution cost per unit is negligible.
The economic challenge in both cases
is identical: recover fixed R&amp;D costs
through volume distribution of a product
whose marginal cost approaches zero.</p>

<h3 id="scarcity-by-design">Scarcity by Design</h3>

<p>Magic uses rarity to create artificial scarcity.
A mythic rare is not more expensive to print than a common.
It is scarce because the production process
distributes mythic rares at a lower frequency
than commons in randomized booster packs.
This designed scarcity drives secondary market prices.</p>

<p>Virtual goods replicate this pattern explicitly.
Drop rates in video games function identically to rarity slots in booster packs.
A legendary weapon that drops once per thousand encounters
is not more expensive to generate than a common weapon.
It is scarce because the game’s random number generator
distributes it infrequently.
The scarcity is artificial, intentional,
and economically productive.</p>

<h3 id="chase-cards-and-bulk-junk">Chase Cards and Bulk Junk</h3>

<p>The secondary market for Magic cards
produces a stark value distribution.
A small number of cards become chase cards,
sought after by competitive players
and commanding prices of tens or hundreds of dollars.
The vast majority of cards,
including most rares,
become bulk junk with negligible secondary market value.</p>

<p>As discussed in
<a href="/games/strategy/game-theory/war-gaming/2026/01/14/metagaming_framework_for_life_strategy.html">Metagaming as a Framework for Real-Life Strategy</a>,
card value is driven by the competitive meta.
Cards that enable dominant strategies become expensive.
Cards that the meta does not favor
become bulk regardless of their design creativity
or mechanical interest.
The Screaming Nemesis example from that article
illustrates how a single card
can rise to chase status through meta dominance
and crash to near-bulk prices after a ban.</p>

<p>This distribution is not unique to Magic.
Virtual goods in online games
exhibit the same pattern.
A small number of items,
those that confer competitive advantage
or social status,
command high prices on secondary markets.
The remainder has negligible value.
The production cost is the same for both categories.</p>

<h3 id="subjective-value-and-real-money-trading">Subjective Value and Real Money Trading</h3>

<p>Magic cards have well-established secondary market prices
despite Wizards of the Coast’s official position
that booster packs are sold as sealed products
at a uniform retail price.
The secondary market exists
because players assign subjective value to individual cards
based on competitive utility, collectibility, and aesthetics.</p>

<p>Virtual goods demonstrate this same phenomenon.
Despite End User License Agreements (EULAs)
that typically prohibit Real Money Trading (RMT),
secondary markets for virtual items
are widespread and economically significant.
Players assign real monetary value to digital items
based on the same factors that drive Magic card prices:
competitive utility, rarity, and social signaling.</p>

<p>The economic lesson is that
prohibiting secondary markets through legal agreements
does not eliminate the underlying demand.
When items have subjective value,
whether physical or virtual,
markets emerge to facilitate exchange.
Magic’s established and sanctioned secondary market
is simply the transparent version
of what virtual goods economies
produce through unauthorized channels.</p>

<h2 id="analysis">Analysis</h2>

<p>Several observations flow from this comparison.</p>

<p><strong>Cards as physical APIs.</strong>
A Magic card is a self-documenting interface
to the game’s rules engine.
The card name is the identifier.
The mana cost is the calling convention.
The type line is the type signature.
The rules text is the implementation.
The power and toughness are the return values.
This is not a metaphor.
The Comprehensive Rules document that governs Magic
is a formal specification,
and each card is a concrete implementation
of an interface defined by that specification.
Virtual goods in well-designed games
follow the same pattern:
each item is an instance of a type
defined by the game’s rules engine.</p>

<p><strong>The printing press as a distribution platform.</strong>
Magic’s printing infrastructure
is functionally equivalent to a digital distribution platform.
Both take a designed artifact,
replicate it at near-zero marginal cost,
and distribute it through a supply chain
that introduces artificial scarcity
to support tiered pricing.
The booster pack is the loot box.
The card shop is the marketplace.
The difference is substrate, not structure.</p>

<p><strong>Rarity as a game design tool and an economic tool simultaneously.</strong>
Rarity in Magic serves two purposes that are often conflated.
As a game design tool,
rarity controls the complexity of the draft environment
by limiting how often players encounter
mechanically complex cards.
As an economic tool,
rarity creates scarcity that drives revenue.
Virtual goods designers face the same tension.
Drop rates affect both gameplay balance
and monetization.
Optimizing for one objective
can undermine the other.</p>

<p><strong>The bulk junk problem reveals value asymmetry.</strong>
Most Magic cards have negligible monetary value
despite representing genuine R&amp;D investment.
A bulk common may feature innovative mechanics,
beautiful artwork, and careful flavor text.
Its low value reflects not a failure of design
but a structural consequence of meta dynamics
and supply abundance.
Virtual goods face the same problem.
Most items in a game’s catalog have negligible player interest.
The R&amp;D cost is real.
The perceived value is not.
This asymmetry is inherent
to any system that produces many items
but concentrates demand on a few.</p>

<p><strong>Physical cards prove that scarcity alone does not create value.</strong>
Many rare Magic cards are worth less than a dollar.
Rarity is necessary but not sufficient for high market value.
The card must also be competitively relevant,
aesthetically desirable,
or culturally significant.
Virtual goods designers who assume
that low drop rates automatically create valuable items
are making the same mistake
as a Magic set designer
who assumes every mythic rare will be a chase card.
The meta, not the rarity, determines value.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Magic cards are physical data structures
with near-zero marginal production costs,
designed scarcity,
and subjective value driven by competitive utility and collectibility.
Virtual goods share every one of these properties.
The only structural difference is the substrate:
cardstock versus software.</p>

<p>This is not a coincidence.
Magic: The Gathering was designed in 1993,
before the modern virtual goods economy existed.
Richard Garfield solved many of the same design problems
that virtual goods designers would encounter a decade later:
how to distribute items at scale,
how to create and maintain scarcity,
how to balance gameplay value against economic value,
and how to sustain a market
where most individual items have negligible worth
but the system as a whole generates substantial revenue.</p>

<p>The lesson for virtual goods designers
is that physical card games
have thirty years of empirical evidence
about what works and what fails.
Rarity without competitive relevance does not create value.
Artificial scarcity without balanced gameplay
drives player frustration.
Secondary markets emerge whether sanctioned or not.
And the meta, the emergent strategic equilibrium
among the player population,
is the ultimate arbiter of which items matter
and which become bulk junk.</p>

<h2 id="future-reading">Future Reading</h2>

<ul>
  <li>
    <p><a href="https://www.gamedeveloper.com/design/what-magic-the-gathering-can-teach-us">What Magic: The Gathering Can Teach Us</a>
by Raph Koster on Game Developer,
examining how Magic’s design principles
apply to broader game design challenges.</p>
  </li>
  <li>
    <p><a href="/games/strategy/game-theory/war-gaming/2026/01/14/metagaming_framework_for_life_strategy.html">Metagaming as a Framework for Real-Life Strategy</a>,
the companion article covering competitive dynamics,
chase cards, bulk cards, and how the meta determines value.</p>
  </li>
  <li>
    <p><a href="https://scryfall.com/">Scryfall</a>,
a comprehensive Magic card database and search engine
with free API access to card data, images, and pricing information.</p>
  </li>
  <li>
    <p><a href="https://us.macmillan.com/books/9781137280114/thezeromarginalcostsociety/">The Zero Marginal Cost Society</a>
by Jeremy Rifkin,
examining how near-zero marginal costs
are reshaping economic structures across industries.</p>
  </li>
  <li>
    <p><a href="https://mitpress.mit.edu/9780262027250/virtual-economies/">Virtual Economies: Design and Analysis</a>
by Vili Lehdonvirta and Edward Castronova,
a scholarly treatment of virtual goods economics,
including RMT, artificial scarcity, and platform design.</p>
  </li>
</ul>

<h2 id="references">References</h2>

<ul>
  <li><a href="/games/strategy/game-theory/war-gaming/2026/01/14/metagaming_framework_for_life_strategy.html">Blog, Metagaming as a Framework for Real-Life Strategy</a></li>
  <li><a href="https://us.macmillan.com/books/9781137280114/thezeromarginalcostsociety/">Book, The Zero Marginal Cost Society</a></li>
  <li><a href="https://mitpress.mit.edu/9780262027250/virtual-economies/">Book, Virtual Economies: Design and Analysis</a></li>
  <li><a href="https://www.gamesradar.com/tabletop-gaming/magic-the-gathering-creator-richard-garfield-talks-game-design-players-look-back-now-and-see-a-bunch-of-broken-cards/">Industry, Magic: The Gathering Creator Richard Garfield Talks Game Design</a></li>
  <li><a href="https://www.gamedeveloper.com/design/what-magic-the-gathering-can-teach-us">Industry, What Magic: The Gathering Can Teach Us</a></li>
  <li><a href="https://mtg.fandom.com/wiki/Information_below_the_text_box">Reference, Information Below the Text Box</a></li>
  <li><a href="https://en.wikipedia.org/wiki/Magic:_The_Gathering">Reference, Magic: The Gathering</a></li>
  <li><a href="https://mtg.fandom.com/wiki/Parts_of_a_card">Reference, Parts of a Magic Card</a></li>
  <li><a href="https://scryfall.com/">Tool, Scryfall Card Database and API</a></li>
</ul>]]></content><author><name>Brendan Sechter</name></author><category term="mtg" /><category term="gamedev" /><category term="economics" /></entry><entry><title type="html">Analyzing MMO User Management With The Chess Phase Model</title><link href="https://sgeos.github.io/gamedev/mmo/2023/05/19/analyzing_mmo_user_management_with_the_chess_phase_model.html" rel="alternate" type="text/html" title="Analyzing MMO User Management With The Chess Phase Model" /><published>2023-05-19T08:06:22+00:00</published><updated>2023-05-19T08:06:22+00:00</updated><id>https://sgeos.github.io/gamedev/mmo/2023/05/19/analyzing_mmo_user_management_with_the_chess_phase_model</id><content type="html" xml:base="https://sgeos.github.io/gamedev/mmo/2023/05/19/analyzing_mmo_user_management_with_the_chess_phase_model.html"><![CDATA[<!-- A64 -->
<script>console.log("A64");</script>

<h2 id="analyzing-mmo-user-management-with-the-chess-phase-model">Analyzing MMO User Management With The Chess Phase Model</h2>

<p>The phases of chess are broadly applicable to many, if not most, games.
Massively multiplayer online games (MMOs) are a little different because
they are designed to be played forever.  Therefore, it may seem odd to
analyze them with a model that ends with checkmating for a win.  The only
way to “beat” an MMO is to quit playing, and this has profound implications
for the MMO operation.</p>

<h2 id="chess-phase-model">Chess Phase Model</h2>

<p>A game of chess moves through the following phases.</p>

<ul>
  <li>Opening: Initial actions to start strong. Openings are documented and analyzed.</li>
  <li>Board Development: Getting pieces into useful places to facilitate the rest of the game.</li>
  <li>Midgame: The bulk of the game. Strategy and tactics come into play here.</li>
  <li>Check: Threaten to end the game. Requires an immediate reaction.</li>
  <li>Checkmate: Thoroughly end the game. No reaction can prolong the session.</li>
</ul>

<p>There are also a few alternate end states.</p>

<ul>
  <li>Resignation: A player is free to quit at any time.</li>
  <li>Draw: Players may agree to end the game without declaring a winner.</li>
  <li>Stalemate: The game ends in a draw because one player has no valid moves.</li>
</ul>

<h2 id="mmo-user-phases">MMO User Phases</h2>

<p>Users of an MMO progress through phases that parallel those of a chess game.</p>

<ul>
  <li>Opening: Onboarding and starting the game.</li>
  <li>Board Development: Learning the MMO and collecting core gear and abilities.</li>
  <li>Midgame- The bulk of the game. Ideally, players would stick with the game forever.</li>
  <li>Check: Threats that cause players to consider quitting.</li>
  <li>Checkmate: Situations where the player actually quits.</li>
</ul>

<p>There are also alternate end states.</p>

<ul>
  <li>Resignation: Circumstances may force a player to quit even if the MMO is enjoyable.</li>
  <li>Draw: Some users get banned. They are a bad fit for the service.</li>
  <li>Stalemate: Largely inactive players who have not quit, but who are not really playing.</li>
</ul>

<h2 id="opening-phase">Opening Phase</h2>

<p>During the opening phase, a player jumps through all the hoops required to
start the game.  This includes things like the following.</p>

<ul>
  <li>downloading the game client</li>
  <li>signing up for any required accounts</li>
  <li>buying a starter pack</li>
  <li>entering a referral code for a bonus</li>
  <li>select a starting character class or faction</li>
  <li>playing through the tutorial</li>
  <li>taking initial actions when able to freely act</li>
</ul>

<p>People write articles and make videos about the “best” way to start
popular games, and many prospective players use them to make an educated
start.  If anything goes wrong during onboarding, players are likely to
walk away forever.  Instant checkmate.</p>

<h2 id="board-development-phase">Board Development Phase</h2>

<p>Three main things happen during the board development phase.  The player
character fills out core gear and learns the fundamental abilities that
will carry it through the rest of the game.  The player learns how to play
the game, and the character or faction they decided to start as.  The
player becomes habituated to having an alter ego in the MMO’s particular
fantasy world.  Players are still sensitive to bad experiences when they
are warming up to a new MMO.</p>

<h2 id="midgame-phase">Midgame Phase</h2>

<p>At this point, the player knows the basics of the game and what they
decided to play.  They have also consistently made time in their life to
commit to the MMO.  Active midgame players are the most likely to put up
with problems because they are invested in the game and there are still
things they want to do.  Starting a new MMO is work and they are already
set up here.  The MMO operator’s goal is to keep players paying for the
privilege to happily play midgame content for as long as possible, ideally
forever.</p>

<h2 id="check-phase">Check Phase</h2>

<p>A check is any situation that causes a player to consider quitting the
game.  Most players stick through most checks, but these are the situations
where players are most likely to quit.  Checks are unavoidable, but paying
attention to them is important for user retention.  Checks include things
like the following.</p>

<ul>
  <li>Completing major milestones in game.</li>
  <li>Playing through all content and running out of new things to do.</li>
  <li>Poor management decisions and actions that upset the player base.</li>
  <li>Changing the game to attract new users at the expense of existing players.</li>
  <li>Changes to monetization strategy or price increases.</li>
  <li>The latest hot MMO that is competing for users.</li>
</ul>

<p>Like a check in chess, these kinds of situations require immediate reaction
to retain users.</p>

<ul>
  <li>Milestone completion opens new and exciting opportunities.</li>
  <li>Regular content releases and game systems that can be played endlessly.</li>
  <li>Positively engaging with an unhappy player base.</li>
  <li>Potentially roll back changes that land poorly and remain sensitive to existing players.</li>
  <li>Timing in game campaigns to coincide with competitor’s releases to keep players engaged in your game.</li>
</ul>

<h2 id="checkmate-phase">Checkmate Phase</h2>

<p>All players eventually quit the game for one reason or another.  As an
extreme example, nobody is immortal.  When a player dies, that is an
instant checkmate. Especially toxic community interaction? Checkmate.
Change in life circumstances? Checkmate. Bored and want to do something
else? Checkmate.</p>

<h2 id="conclusion">Conclusion</h2>

<p>The chess phases are a simple model for reasoning about MMO user
progression- opening, board development, midgame, check, and checkmate.
Therefore, it can be used to easily reason about user and mental energy
can be expended on hard problems and complex solutions.</p>

<p>No singles phase is more important than another.  Players need to be
onboarded.  They need to learn the game and become habituated to playing
it.  They need to have content to play to remain invested and engaged in
the experience.  Situations will occur that make players unhappy and cause
them to consider quitting.  Finally, players will leave despite the
developer’s best efforts to keep them engaged.</p>]]></content><author><name>Brendan Sechter</name></author><category term="gamedev" /><category term="mmo" /></entry><entry><title type="html">Go as a Prototypical Strategy Game</title><link href="https://sgeos.github.io/gamedev/2023/05/18/go_prototypical_strategy_game.html" rel="alternate" type="text/html" title="Go as a Prototypical Strategy Game" /><published>2023-05-18T05:45:53+00:00</published><updated>2023-05-18T05:45:53+00:00</updated><id>https://sgeos.github.io/gamedev/2023/05/18/go_prototypical_strategy_game</id><content type="html" xml:base="https://sgeos.github.io/gamedev/2023/05/18/go_prototypical_strategy_game.html"><![CDATA[<!-- A63 -->
<script>console.log("A63");</script>

<p>Go and chess are two classic strategy games with simple rules that are
hard to master.
Go arguably has simpler rules than chess, but the game is arguably
the harder of the two to master.
Go is essentially what you get if a strategy is reduced to the minimum
number of possible components.
Go gets a lot of things right.
This post will discuss the scope of Go’s rules, and discuss why it is an
important study for modern game design.</p>

<h2 id="go-game-scope">Go Game Scope</h2>

<p>This section will cover the scope of Go without going into details.
Readers are expected to know the rules, or know how to find them.
A typical go set includes the following.</p>

<ul>
  <li>one 19x19 board</li>
  <li>181 black stones</li>
  <li>180 white stones</li>
</ul>

<p>Different sized Go boards are played on, and a non-standard  board
is typically going to be square with the length of the sides being an odd
number.
Note that 19x19 is 361.
This implies that a set has enough stones to fill the whole board,
with black getting the extra odd stone because it moves first.
The following board sizes are also used.</p>

<ul>
  <li>9x9 for very short games to teach beginners</li>
  <li>13x13 for short games to teach beginners</li>
  <li>17x17 is the historical board size</li>
</ul>

<p>Note that Go only has one kind of game token- stones.
Black and white stones are mechanically identical and differ only in who
controls them.
Go is a battle for territorial control, and stones could abstractly be
considered soldiers on a battlefield.</p>

<p>Players take turns placing stones, starting with black.
If black receives a handicap, the handicap stones are placed and then white
moves (black is the weaker player).
Groups of stones that are completely surrounded are removed from the board.
Passing is legal, but it is only beneficial at the end of the game.</p>

<p>The basic concepts of Go include the following.</p>

<ul>
  <li>liberties - the number of open spaces adjacent to a group of stones</li>
  <li>eyes - the number of open areas inside a group of stones</li>
  <li>live string - any group with two or more eyes cannot be captured</li>
  <li>dead string - isolated groups that cannot form two eyes</li>
</ul>

<p>Go has a few additional rules.</p>

<ul>
  <li>ko - reverting the game state to the one on your previous turn is not allowed to prevent endless loops</li>
  <li>seki - a local stalemate where opposing groups do not have two eyes, but they cannot capture each other either</li>
</ul>

<p>All else being equal, Go is a game where a player who understands the game
better will consistently beat weaker opponents.
Go has a handicap system where the game starts off unequally, and this
allows players of different skill levels to enjoy playing against one
another.
The difference in player grade levels indicates the number of hanicap
stones, and the weaker player starts with anywhere from two to nine
extra stones.</p>

<p>Black has an advantage because it moves first.
If players are of equal skill, white starts with bonus points called komi.
Over millennia of play, people have determined that playing first is worth
about sevent points.
Tournaments typically give white seven and a half points to eliminate
the possibility of a draw.</p>

<h2 id="game-complexity-implications">Game Complexity Implications</h2>

<p>The complete game state for a game of Go can be represented in under one
hundred bytes if it is optimized for size.</p>

<ul>
  <li>361 bits (46 bytes) for white stone locations</li>
  <li>361 bits (46 bytes) for black stone locations</li>
  <li>1 or 2 bytes for captured black stones (and 7-point komi)</li>
  <li>1 or 2 bytes for captured white stones</li>
  <li>0 bits komi (white implicity get an extra half point)</li>
  <li>1 bit to indicate the active player</li>
  <li>1 bit to indicate if the game is over</li>
  <li>1 bit to indicate if the current player conceded</li>
  <li>1 bit to indicate if the previous player passed</li>
  <li>10 bits (2 bytes) for the coordinates of the previous black move (ko)</li>
  <li>10 bits (2 bytes) for the coordinates of the previous white move (ko)</li>
</ul>

<p>46 bytes contains 368 bits.
92 bytes for all stone locations works out to 14 unused bits.
Four of these can be used for game state flags.
Captured stones can then be stored in one byte and any remaining bits.
Add four more bytes to store information for determining ko, and the
total state is 98 bytes.</p>

<p>Alternatively, one byte can be used for each board location and flag, four
bytes can be used for each player’s score for a total of 373 bytes.
Add another 16 bytes for ko and the grand total is 389 bytes, or about 400.</p>

<p>The complexity of Go is not in the static game state, but in the possible
number of game states.
Each board location can have a black stone, a white stone, or it can be
empty.
This works out to 3 to the power of 361 possible board states,
or a number so large it is a one followed by 172 zeros.
Despite many of those states being illegal, the magnitude of possibilities
is way over one googol.
This is far too many for static analysis without accounting for captured
stones, passing, or ko.</p>

<p>For comparison, a chess board has 64 squares and each square has
13 possible states- empty, six white pieces, and six black pieces.
This works out to a one followed by only 71 zeroes, and the majority
of the states are impossible in practice.
Go has a magnitude of a googol more possible game states than chess.</p>

<h2 id="analyzing-go-using-chess-phases">Analyzing Go Using Chess Phases</h2>

<p>In a previous post, I wrote about using chess as a model for other games.
Go has less clearly defined phases, but the same analysis can be used.</p>

<p>Standard openings typically involve claiming corners or contesting a
handicap.
The board is arguably developed when rudimentary battle lines
are established at all corners and the sides of the board-
essentially all handicap locations.
The midgame begins after all major board zones have been claimed or
contested.
Therefore, a nine-stone handicap is strong because the weaker player
literally starts with a board presence everywhere.</p>

<p>A check could be threatening a major stone group or securing a zone of the
board.
A checkmate is a situation where the game is hopeless for the other player.
Unlike many other games, Go often ends when all locations on the board
have been played to their tactical conclusion.
The two patterns are playing to the bitter end, or playing until one player
simply concedes.</p>

<h2 id="the-brilliance-of-go">The Brilliance of Go</h2>

<p>Go is brilliant because deep gameplay emerges from only a single type of
game token and a board to place the tokens on.
This makes it the ultimate reduction in unnecessary complexity.
Every single rules has a very good reason for existing.</p>

<ul>
  <li>hanicap stones allow players of different skill levels to interact</li>
  <li>komi balances the advantage of moving first for equal skill players</li>
  <li>the half point added to komi eliminates the possibility of a draw</li>
  <li>ko prevents endless loops that are effectively a draw</li>
  <li>seki and liberty counting are basic situational awareness</li>
</ul>

<p>Many games fail to solve the above problems.</p>

<ul>
  <li>weak players get steamrolled for poor to non-existant handicapping</li>
  <li>first players often have an inherent advantage that is unaccounted for</li>
  <li>draws are not balanced out of game scoring</li>
  <li>endless loops are not accounted for, and this breaks something</li>
  <li>many games fail to understand the implications of their own mechanics</li>
</ul>

<p>In conclusion, Go’s level of minimal design is likely inappropriate for
modern products, but its solutions to fundamental game design problems 
are absolutely worth being aware of.</p>]]></content><author><name>Brendan Sechter</name></author><category term="gamedev" /></entry><entry><title type="html">Chess as a Prototypical Model for Game Progression</title><link href="https://sgeos.github.io/gamedev/2023/05/17/chess_as_prototypical_model_for_game_progression.html" rel="alternate" type="text/html" title="Chess as a Prototypical Model for Game Progression" /><published>2023-05-17T21:05:40+00:00</published><updated>2023-05-17T21:05:40+00:00</updated><id>https://sgeos.github.io/gamedev/2023/05/17/chess_as_prototypical_model_for_game_progression</id><content type="html" xml:base="https://sgeos.github.io/gamedev/2023/05/17/chess_as_prototypical_model_for_game_progression.html"><![CDATA[<!-- A62 -->
<script>console.log("A62");</script>

<p>Models are useful to help reason about complex problems.
This post will describe how chess can be used as a model to reason about
more complex games.
Chess will specifically be compared to Civilization 5, Magic the Gathering,
and Final Fantasy 1.</p>

<h2 id="chess-game-scope">Chess Game Scope</h2>

<p>This session is going to cover the scope of the game of chess,
without actually going into detail on any of the rules.
Readers are expected to know the rules or know how to find them.</p>

<p>A chess set includes one board and 32 pieces- 16 black and 16 white.
The board is an eight by eight grid.
Squares have a rank and file.</p>

<ul>
  <li>rank - eight horizontal rows from 1 to 8</li>
  <li>file - eight vertical columns from A to H</li>
</ul>

<p>Each player gets the following six kinds of pieces.
Point values have no mechanical role, but they help players reason
about the game.</p>

<ul>
  <li>8 pawns (1 point each)</li>
  <li>2 knights (3 points each)</li>
  <li>2 bishops (3 points each)</li>
  <li>2 rooks (5 points each)</li>
  <li>1 queen (9 points)</li>
  <li>1 king (no point value)</li>
</ul>

<p>The rules for moving and capturing pieces are fairly simple.
In addition to learning how the different kinds of pieces move,
chess has a few special rules.</p>

<ul>
  <li>castling</li>
  <li>pawn promotion</li>
  <li>en passant capture</li>
</ul>

<p>There are also rules that relate to ending the game.</p>

<ul>
  <li>check (threatened victory)</li>
  <li>checkmate (victory)</li>
  <li>stalemate (draw)</li>
  <li>threefold repetition rule (draw)</li>
  <li>resignation</li>
  <li>optional clock rules for competitive play</li>
</ul>

<p>Every game starts with all pieces in predefined locations,
and white moves first.
Finally, note that chess has sufficiently few components that people
have been known to draw paper chess sets for lack of dedicated pieces.</p>

<h2 id="chess-game-phases">Chess Game Phases</h2>

<p>If a game of chess is played from start to finish, it moves through the
following phases.</p>

<ul>
  <li>opening</li>
  <li>board development</li>
  <li>midgame</li>
  <li>check</li>
  <li>checkmate</li>
</ul>

<p>On the very first move,
each of the eight pawns can be moved one or two spaces,
and each of the two knights can also be moved into one of two locations.
Therefore, white can make one of twenty first moves,
and black can make one of twenty first responses.
The first move is the most important because it opens up options for
the second and subsequent moves.
Some of the first moves make more sense than others.</p>

<p>The opening is a set of predefined moves to start the game.
Openings can be memorized, and some are stronger than others.
Different openings lend themselves to different styles of play
and set the start of the game.
An extreme example is the two move checkmate, where
white opens F and G files and black responds with the queen for a checkmate.</p>

<p>Next, players focus on developing the board.
A piece is developed once it has been moved from its starting position.
The board development phase generally involves castling,
developing backrow pieces, and getting all the
non-castle side pawns into a useful formation.
Where board development starts and ends can be fuzzy, but the goal is
to get the pieces somewhere useful.
Many of the board development phase moves and responses can be memorized.</p>

<p>Once the board has been developed, the midgame begins.
At this point, the board is set and players can no longer rely on
having memorized exact board states and positions.
They need to use strategy and tactics to play chess.
The better player will do more damage and wind up in
a more favorable position.</p>

<p>A check occurs when one player threatens the other player’s king.
The rules of chess dictate that a player must get their king out of
check if it is threatened.
When in check, a player’s options are limited.
Sometimes this is not a problem, other times it can be used to set
up situations where the player in check loses material because
they need to focus on the king.</p>

<p>Simply playing better in midgame and putting the opponent’s king in check
is not enough to win a game of chess.
The opponent’s king must be put in a checkmate to win.
Checkmate occurs when it is impossible for the opponent to get their king
to safety.
Learning endgame checkmates is a skill that can be developed.
Depending on the circumstances, this knowledge can be used to win,
recover from a losing position,
or play for a draw.</p>

<h2 id="chess-as-a-model-for-other-games">Chess as a Model for Other Games</h2>

<p>Although the details depend on the title, many games move through the
same phases as chess.</p>

<ul>
  <li>opening</li>
  <li>board development</li>
  <li>midgame</li>
  <li>check</li>
  <li>checkmate</li>
</ul>

<p>Openings are a set of predefined moves that propel the game in one of many
directions.
They can be memorized, and people often put a lot of thought into the
analysis of openings.</p>

<p>The board development phase consists of collecting and deploying a
complete set of game tokens usefully.
Campaign-based games may have per-stage board development and
campaign level board development.
Note that board development often needs to occur while being
actively resisted.</p>

<p>The timing can be fuzzy, but at some point the board is developed, and
the player just needs to play the game.
This is the midgame.
Some games have an extremely long midgame.</p>

<p>A check occurs when a player threatens a victory,
or signals progress towards a victory.
Unlike chess, it may not require an immediate response.
Sometimes, however, a check does indicate a situation where one
or more players need to intervene so as not to lose the game.</p>

<p>A checkmate occurs when a player meets all the criteria
for a win condition.
Like chess, a checkmate often requires knowing the criteria to secure a
victory.
Midgame tactics and endgame criteria are often different,
but not unrelated.</p>

<h2 id="civilization-5-analysis">Civilization 5 Analysis</h2>

<p>In Civilization 5, the opening is the initial build queue.
Many beginners tend to first build a monument.
An example of a more complex opening is scout, scout, shrine, granary,
monument, settler.
Openings can be memorized and analyzed.
Tactically, deviating from an opening rarely makes sense.</p>

<p>Civ 5 is far more complex than chess, but the board development phase
likely concludes around the time all early game cities have been founded
and luxury resources have been improved.
Hypothetically, cities can be founded or conquered at any time,
and tile improvements are optional, but there does tend to be a point
when cities are settled and focus shifts to playing with what is
on the board.
Note that city expansions need to be planted despite barbarians,
hostile players, and competing priorities.</p>

<p>The midgame consists of development and conquest that happens after the
core cities are in place.
This is the bulk of the game.
A bad initial board state can lose games, but running with a developed
board is required to win games.</p>

<p>Civ 5 has multiple victory conditions- score, domination, diplomacy,
science, and culture.
Score is the default win condition if the game goes on for too long without
another more specific victory condition having been met.
The other victory types have conditions that could be considered a check.</p>

<ul>
  <li>domination - conquering the capital of another civilization</li>
  <li>diplomacy - having the most delegates when voting on the world leader</li>
  <li>science - completing the Apollo Program or attaching a spaceship part</li>
  <li>culture - becoming influential with another civilization</li>
</ul>

<p>Likewise, a checkmate occurs when all the conditions for a specific
victory type have been met.</p>

<ul>
  <li>domination - conquering the capital cities of all other civilizations</li>
  <li>diplomacy - having enough delegates to be voted world leader</li>
  <li>science - attaching all spaceship parts</li>
  <li>culture - becoming influential with all other civilizations</li>
</ul>

<p>Midgame development and warfare are not enough to win the game.
Especially poor play will result in a loss, but a checkmate requires
very specific actions.</p>

<ul>
  <li>score - playing for a high game score while not otherwise winning</li>
  <li>domination - systematically conquering capital cities</li>
  <li>diplomacy - systematically acquiring and retaining delegates</li>
  <li>science - researching, building and attaching spaceship parts</li>
  <li>culture - developing high tourism potential</li>
</ul>

<p>Note that telegraphing a check may elicit a response from one or more
other players.
War may be declared, or an opponent may double down on their victory push.</p>

<h2 id="magic-the-gathering-analysis">Magic the Gathering Analysis</h2>

<p>Magic the Gathering (MTG) is a game where players build decks with
collectable cards.
Unlike other games, different players can show up with completely different
decks of game tokens, but the game can still be modeled using the same
chess phases.</p>

<p>Players draw 7 random cards at the beginning of the game, so openings
are not as reliable as chess.
Some hands are just bad.
Building a reliable deck is a skill, and certain cards will likely be
available in the first few turns if included in sufficient quantity.
Therefore, the opening consists of cards that are likely available and
playable during the first few turns of the game.</p>

<p>Board development largely consists of summoning creatures, but enchantments
and artifacts can also be played.
During the first few turns in a game, players will typically develop
a board to face down their opponent.</p>

<p>A check is any problem that threatens to kill the opponent
if left unanswered.
Even a weak 1/1 creature can be a check if the opponent has no board state.
A checkmate occurs when one player is reduced to zero life points, or an
alternate win condition is met.</p>

<p>The phases of chess can also be used as an analogy for collecting cards
and building decks.
Most people open with some sort of starter deck, but there are different
ways to start playing a collectible trading card game.
Board development consists of getting enough cards together to build a
competitive deck.
Check is a player’s first meta deck, and checkmate is the point where
the player has the knowledge and tools to play against all the common
meta decks.</p>

<h2 id="final-fantasy-1-analysis">Final Fantasy 1 Analysis</h2>

<p>An RPG is a campaign-based game.
Each dungeon can be modelled with the chess phases, as can the
party in the context of the greater adventure.</p>

<p>At the campaign level, the opening is simply the player’s initial party
of four heroes.
Board development consists of equipping the party, learning useful early
game spells, and maybe gaining a few levels.
The midgame consists of playing through the game after the party has a
full set of equipment.</p>

<p>Defeating each of the four fiends is arguably a check, and defeating the
last boss is a checkmate.
The endgame content is largely the same as the midgame content.</p>

<p>At the dungeon level, the opening consists of picking a direction.
Strategically approaching dungeons is possible for people who are familiar
with the game.</p>

<p>Board development consists of collecting key pieces of treasure.
The party keeps the gear even if a tactical retreat is necessary.
A check is any milestone that threatens to make the dungeon easy to beat,
and a checkmate is actually beating the dungeon.
Conversely, anything that threatens a party wipe could be considered a
check.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Compared to modern commercial games, chess is simple and easy to reason
about.
It only has 32 pieces that are placed on a 64-square grid.
More complex games can often be broken into phases that parallel the
progression of a chess match.</p>

<ul>
  <li>opening - actions taken immediately after starting a game</li>
  <li>board development - early game actions that set the stage</li>
  <li>midgame - the bulk of the game, tactics and strategy are used here</li>
  <li>check - partial victory crateria that telegraphs a win</li>
  <li>checkmate - complete victory criteria that scores a win</li>
  <li>draw - sometimes players neither win nor lose, but the game still ends</li>
  <li>resignation - a player can always resign if the situation is hopeless</li>
</ul>]]></content><author><name>Brendan Sechter</name></author><category term="gamedev" /></entry><entry><title type="html">Fortran Playdate Development</title><link href="https://sgeos.github.io/gamedev/playdate/fortran/2022/10/10/fortran_playdate_development.html" rel="alternate" type="text/html" title="Fortran Playdate Development" /><published>2022-10-10T21:55:03+00:00</published><updated>2022-10-10T21:55:03+00:00</updated><id>https://sgeos.github.io/gamedev/playdate/fortran/2022/10/10/fortran_playdate_development</id><content type="html" xml:base="https://sgeos.github.io/gamedev/playdate/fortran/2022/10/10/fortran_playdate_development.html"><![CDATA[<!-- A60 -->
<script>console.log("A60");</script>

<p><a href="https://fortran-lang.org/">Fortran</a> is used for heavy number crunching in mathematical and
scientific computing.
This post will start by covering calling Fortran from C.
It will then discuss using Fortran on the <a href="https://play.date/">Panic Playdate</a>,
both the simulator and hardware.</p>

<h2 id="software-versions">Software Versions</h2>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">$ </span><span class="nb">date</span> <span class="nt">-u</span> <span class="s2">"+%Y-%m-%d %H:%M:%S +0000"</span>
2022-10-10 21:55:03 +0000
<span class="nv">$ </span><span class="nb">uname</span> <span class="nt">-vm</span>
Darwin Kernel Version 21.6.0: Mon Aug 22 20:19:52 PDT 2022<span class="p">;</span> root:xnu-8020.140.49~2/RELEASE_ARM64_T6000 arm64
<span class="nv">$ </span>ex <span class="nt">-s</span> +<span class="s1">'%s/&lt;[^&gt;].\{-}&gt;//ge'</span> +<span class="s1">'%s/\s\+//e'</span> +<span class="s1">'%norm J'</span> +<span class="s1">'g/^$/d'</span> +%p +q! /System/Library/CoreServices/SystemVersion.plist | <span class="nb">grep</span> <span class="nt">-E</span> <span class="s1">'ProductName|ProductVersion'</span> | <span class="nb">sed</span> <span class="s1">'s/^[^ ]* //g'</span> | <span class="nb">sed</span> <span class="s1">'N; s/\n/ /g'</span>
macOS 12.6
<span class="nv">$ </span><span class="nb">echo</span> <span class="s2">"</span><span class="k">${</span><span class="nv">SHELL</span><span class="k">}</span><span class="s2">"</span>
/bin/bash
<span class="nv">$ </span><span class="s2">"</span><span class="k">${</span><span class="nv">SHELL</span><span class="k">}</span><span class="s2">"</span> <span class="nt">--version</span>  | <span class="nb">head</span> <span class="nt">-n</span> 1
GNU bash, version 3.2.57<span class="o">(</span>1<span class="o">)</span><span class="nt">-release</span> <span class="o">(</span>arm64-apple-darwin21<span class="o">)</span>
<span class="nv">$ </span><span class="nb">cat</span> <span class="s2">"</span><span class="k">${</span><span class="nv">HOME</span><span class="k">}</span><span class="s2">/Developer/PlaydateSDK/VERSION.txt"</span>
1.12.3
<span class="nv">$ </span>gfortran <span class="nt">-v</span> 2&gt; <span class="o">&gt;(</span><span class="nb">tail</span> <span class="nt">-1</span><span class="o">)</span>
gcc version 12.2.0 <span class="o">(</span>MacPorts gcc12 12.2.0_0+stdlib_flag<span class="o">)</span>
<span class="nv">$ </span>arm-none-eabi-gfortran <span class="nt">-v</span> 2&gt; <span class="o">&gt;(</span><span class="nb">tail</span> <span class="nt">-1</span><span class="o">)</span>
gcc version 11.3.1 20220712 <span class="o">(</span>Arm GNU Toolchain 11.3.Rel1<span class="o">)</span></code></pre></figure>

<h2 id="instructions">Instructions</h2>

<h4 id="calling-fortran-from-c">Calling Fortran from C</h4>

<p>This section assumes that <strong>gcc</strong> and <strong>gfortran</strong> are installed.
First, create a new project.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">PROJECT</span><span class="o">=</span><span class="s2">"c_fortran_interop_example"</span>
<span class="nb">mkdir</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PROJECT</span><span class="k">}</span><span class="s2">"</span>
<span class="nb">cd</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PROJECT</span><span class="k">}</span><span class="s2">"</span></code></pre></figure>

<p>Add <strong>main.c</strong>.</p>

<p><strong>main.c</strong></p>

<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">"fast_sqrt.h"</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="n">argv</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="n">argc</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">double</span> <span class="n">input</span> <span class="o">=</span> <span class="n">atof</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
    <span class="kt">double</span> <span class="n">output</span> <span class="o">=</span> <span class="n">fast_sqrt</span><span class="p">(</span><span class="n">input</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"The square root of %.3f is %.3f.</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">input</span><span class="p">,</span> <span class="n">output</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>

<p>Add <strong>fast_sqrt.h</strong> for interoperation with C.</p>

<p><strong>fast_sqrt.h</strong></p>

<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="cp">#ifndef FAST_SQRT_H
#define FAST_SQRT_H
</span>
<span class="k">extern</span> <span class="kt">double</span> <span class="nf">fast_sqrt</span><span class="p">(</span><span class="kt">double</span><span class="p">);</span>

<span class="cp">#endif  // FAST_SQRT_H</span></code></pre></figure>

<p>Add the Fortran implementation of <strong>fast_sqrt()</strong>.</p>

<p><strong>fast_sqrt.f90</strong></p>

<figure class="highlight"><pre><code class="language-fortran" data-lang="fortran"><span class="k">function</span><span class="w"> </span><span class="n">fast_sqrt</span><span class="p">(</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="k">result</span><span class="p">(</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="k">bind</span><span class="p">(</span><span class="w"> </span><span class="n">C</span><span class="p">,</span><span class="w"> </span><span class="n">name</span><span class="o">=</span><span class="s2">"fast_sqrt"</span><span class="w"> </span><span class="p">)</span><span class="w">
  </span><span class="k">use</span><span class="w"> </span><span class="n">iso_c_binding</span><span class="p">,</span><span class="w"> </span><span class="k">only</span><span class="p">:</span><span class="w"> </span><span class="n">c_double</span><span class="w">
  </span><span class="k">implicit</span><span class="w"> </span><span class="k">none</span><span class="w">

  </span><span class="kt">real</span><span class="p">(</span><span class="n">c_double</span><span class="p">),</span><span class="w"> </span><span class="k">VALUE</span><span class="w"> </span><span class="p">::</span><span class="w"> </span><span class="n">x</span><span class="w">
  </span><span class="kt">real</span><span class="p">(</span><span class="n">c_double</span><span class="p">)</span><span class="w"> </span><span class="p">::</span><span class="w"> </span><span class="n">y</span><span class="w">

  </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">sqrt</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w">
</span><span class="k">end</span><span class="w"> </span><span class="k">function</span></code></pre></figure>

<p>Create a <strong>Makefile</strong> to capture simple logic for building and testing
the program.
The C and Fortran files are compiled into object files to combine into a binary.
The testing code calls the program with the numbers from zero to ten as
test parameters.</p>

<p><strong>Makefile</strong></p>

<figure class="highlight"><pre><code class="language-make" data-lang="make"><span class="nl">.PHONEY</span><span class="o">:</span> <span class="nf">all clean force run test</span>

<span class="nv">CFLAGS</span><span class="o">=</span><span class="nt">-Wall</span>
<span class="nv">FC</span><span class="o">=</span>gfortran
<span class="nv">FCFLAGS</span><span class="o">=</span><span class="nt">-Wall</span>

<span class="nv">TARGET</span><span class="o">=</span>fast_sqrt
<span class="nv">OBJS</span><span class="o">=</span>main.o fast_sqrt.o

<span class="nl">all</span><span class="o">:</span> <span class="nf">$(TARGET)</span>

<span class="nl">force</span><span class="o">:</span> <span class="nf">clean all</span>

<span class="nl">$(TARGET)</span><span class="o">:</span> <span class="nf">$(OBJS)</span>
	<span class="nv">$(FC)</span> <span class="nv">$(CFLAGS)</span> <span class="nv">$^</span> <span class="nt">-o</span> <span class="nv">$@</span>

<span class="nl">%.o</span><span class="o">:</span> <span class="nf">%.c</span>
	<span class="nv">$(CC)</span> <span class="nv">$(CFLAGS)</span> <span class="nt">-c</span> <span class="nv">$&lt;</span> <span class="nt">-o</span> <span class="nv">$@</span>

<span class="nl">%.o</span><span class="o">:</span> <span class="nf">%.f90</span>
	<span class="nv">$(FC)</span> <span class="nv">$(FCFLAGS)</span> <span class="nt">-c</span> <span class="nv">$&lt;</span> <span class="nt">-o</span> <span class="nv">$@</span>

<span class="nl">test</span><span class="o">:</span> <span class="nf">$(TARGET) run</span>

<span class="nl">run</span><span class="o">:</span>
	<span class="k">for </span>i <span class="k">in</span> <span class="o">{</span>0..10<span class="o">}</span><span class="p">;</span> <span class="k">do</span> ./<span class="nv">$(TARGET)</span> <span class="nv">$$</span>i<span class="p">;</span> <span class="k">done</span>

<span class="nl">clean</span><span class="o">:</span>
	<span class="nb">rm</span> <span class="nt">-rf</span> <span class="k">*</span>.o <span class="nv">$(TARGET)</span></code></pre></figure>

<p>Build and test the program.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">$ </span>make <span class="nb">test
</span>cc <span class="nt">-Wall</span> <span class="nt">-c</span> main.c <span class="nt">-o</span> main.o
The square root of 0.000 is 0.000.
The square root of 1.000 is 1.000.
The square root of 2.000 is 1.414.
The square root of 3.000 is 1.732.
The square root of 4.000 is 2.000.
The square root of 5.000 is 2.236.
The square root of 6.000 is 2.449.
The square root of 7.000 is 2.646.
The square root of 8.000 is 2.828.
The square root of 9.000 is 3.000.
The square root of 10.000 is 3.162.</code></pre></figure>

<h4 id="running-fortran-on-the-playdate-simulator">Running Fortran on the Playdate Simulator</h4>

<p>Calling Fortran from C on the Playdate simulator is much the same as in
the previous section.
The Playdate simulator runs on the development machine and uses the host
architecture.
Therefore, cross-compilation is not necessary.
The Hello World C API example is a reasonable project template, so make a copy.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">PROJECT</span><span class="o">=</span><span class="s2">"fortran_test"</span>
<span class="nv">PROJECT_PATH</span><span class="o">=</span><span class="s2">"my_playdate_projects"</span>
<span class="nb">cd</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PROJECT_PATH</span><span class="k">}</span><span class="s2">"</span>
<span class="nb">cp</span> <span class="nt">-r</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PLAYDATE_SDK_PATH</span><span class="k">}</span><span class="s2">/C_API/Examples/Hello World/"</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PROJECT</span><span class="k">}</span><span class="s2">"</span>
<span class="nb">cd</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PROJECT</span><span class="k">}</span><span class="s2">"</span></code></pre></figure>

<p>Next, update <strong>pdxinfo</strong>.</p>

<p><strong>Source/pdxinfo</strong></p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">name</span><span class="o">=</span>FortranTest
<span class="nv">author</span><span class="o">=</span>Brendan Sechter
<span class="nv">description</span><span class="o">=</span>Fortran on Playdate proof of concept.
<span class="nv">bundleID</span><span class="o">=</span>com.sennue.poc_fortrantest
<span class="nv">imagePath</span><span class="o">=</span></code></pre></figure>

<p>The <strong>main.c</strong> and <strong>main.h</strong> files from an earlier post,
<a href="/gamedev/playdate/asm/arm/x86/2022/10/05/asm_playdate_development.html">ASM Playdate Development</a>, can be used for this project.
Modify <strong>main.c</strong>.</p>

<p><strong>src/main.c</strong></p>

<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="cp">#include</span> <span class="cpf">"fast_sqrt.h"</span><span class="cp">
#include</span> <span class="cpf">"main.h"</span><span class="cp">
#include</span> <span class="cpf">"pd_api.h"</span><span class="cp">
</span>
<span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">fontpath</span> <span class="o">=</span> <span class="s">"/System/Fonts/Asheville-Sans-14-Bold.pft"</span><span class="p">;</span>
<span class="k">const</span> <span class="n">LCDPattern</span> <span class="n">gray50</span> <span class="o">=</span> <span class="p">{</span>
  <span class="c1">// Bitmap</span>
  <span class="mi">0</span><span class="n">b10101010</span><span class="p">,</span>
  <span class="mi">0</span><span class="n">b01010101</span><span class="p">,</span>
  <span class="mi">0</span><span class="n">b10101010</span><span class="p">,</span>
  <span class="mi">0</span><span class="n">b01010101</span><span class="p">,</span>
  <span class="mi">0</span><span class="n">b10101010</span><span class="p">,</span>
  <span class="mi">0</span><span class="n">b01010101</span><span class="p">,</span>
  <span class="mi">0</span><span class="n">b10101010</span><span class="p">,</span>
  <span class="mi">0</span><span class="n">b01010101</span><span class="p">,</span>

  <span class="c1">// Mask</span>
  <span class="mi">0</span><span class="n">b11111111</span><span class="p">,</span>
  <span class="mi">0</span><span class="n">b11111111</span><span class="p">,</span>
  <span class="mi">0</span><span class="n">b11111111</span><span class="p">,</span>
  <span class="mi">0</span><span class="n">b11111111</span><span class="p">,</span>
  <span class="mi">0</span><span class="n">b11111111</span><span class="p">,</span>
  <span class="mi">0</span><span class="n">b11111111</span><span class="p">,</span>
  <span class="mi">0</span><span class="n">b11111111</span><span class="p">,</span>
  <span class="mi">0</span><span class="n">b11111111</span><span class="p">,</span>
<span class="p">};</span>

<span class="kt">void</span> <span class="nf">initProgramState</span><span class="p">(</span><span class="k">struct</span> <span class="n">ProgramState</span> <span class="o">*</span><span class="n">ps</span><span class="p">,</span> <span class="n">PlaydateAPI</span> <span class="o">*</span><span class="n">pd</span><span class="p">)</span>
<span class="p">{</span>
  <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">errorMessage</span><span class="p">;</span>

  <span class="n">ps</span><span class="o">-&gt;</span><span class="n">pd</span> <span class="o">=</span> <span class="n">pd</span><span class="p">;</span>
  <span class="n">ps</span><span class="o">-&gt;</span><span class="n">font</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
  <span class="n">ps</span><span class="o">-&gt;</span><span class="n">font</span> <span class="o">=</span> <span class="n">pd</span><span class="o">-&gt;</span><span class="n">graphics</span><span class="o">-&gt;</span><span class="n">loadFont</span><span class="p">(</span><span class="n">fontpath</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">errorMessage</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="nb">NULL</span> <span class="o">==</span> <span class="n">ps</span><span class="o">-&gt;</span><span class="n">font</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">pd</span><span class="o">-&gt;</span><span class="n">system</span><span class="o">-&gt;</span><span class="n">error</span><span class="p">(</span><span class="s">"%s:%i Couldn't load font %s: %s"</span><span class="p">,</span>
      <span class="n">__FILE__</span><span class="p">,</span> <span class="n">__LINE__</span><span class="p">,</span> <span class="n">fontpath</span><span class="p">,</span> <span class="n">errorMessage</span>
    <span class="p">);</span>
  <span class="p">}</span>
  <span class="n">ps</span><span class="o">-&gt;</span><span class="n">previousInput</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="n">ps</span><span class="o">-&gt;</span><span class="n">strokeWidth</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
  <span class="n">ps</span><span class="o">-&gt;</span><span class="n">x</span> <span class="o">=</span> <span class="p">(</span><span class="n">LCD_COLUMNS</span> <span class="o">-</span> <span class="n">TEXT_WIDTH</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
  <span class="n">ps</span><span class="o">-&gt;</span><span class="n">y</span> <span class="o">=</span> <span class="p">(</span><span class="n">LCD_ROWS</span> <span class="o">-</span> <span class="n">TEXT_HEIGHT</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
  <span class="n">ps</span><span class="o">-&gt;</span><span class="n">dx</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
  <span class="n">ps</span><span class="o">-&gt;</span><span class="n">dy</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>

<span class="cp">#ifdef _WINDLL
</span><span class="kr">__declspec</span><span class="p">(</span><span class="n">dllexport</span><span class="p">)</span>
<span class="cp">#endif
</span><span class="kt">int</span> <span class="nf">eventHandler</span><span class="p">(</span><span class="n">PlaydateAPI</span><span class="o">*</span> <span class="n">pd</span><span class="p">,</span> <span class="n">PDSystemEvent</span> <span class="n">event</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">arg</span><span class="p">)</span>
<span class="p">{</span>
  <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">arg</span><span class="p">;</span> <span class="c1">// only used for kEventKeyPressed == event</span>
  <span class="k">static</span> <span class="k">struct</span> <span class="n">ProgramState</span> <span class="o">*</span><span class="n">ps</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>

  <span class="k">switch</span> <span class="p">(</span><span class="n">event</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">case</span> <span class="n">kEventInit</span><span class="p">:</span>
      <span class="n">ps</span> <span class="o">=</span> <span class="n">pd</span><span class="o">-&gt;</span><span class="n">system</span><span class="o">-&gt;</span><span class="n">realloc</span><span class="p">(</span><span class="n">ps</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="k">struct</span> <span class="n">ProgramState</span><span class="p">));</span>
      <span class="n">initProgramState</span><span class="p">(</span><span class="n">ps</span><span class="p">,</span> <span class="n">pd</span><span class="p">);</span>
      <span class="n">pd</span><span class="o">-&gt;</span><span class="n">system</span><span class="o">-&gt;</span><span class="n">setUpdateCallback</span><span class="p">(</span><span class="n">update</span><span class="p">,</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">ps</span><span class="p">);</span>
      <span class="k">break</span><span class="p">;</span>
    <span class="k">case</span> <span class="n">kEventTerminate</span><span class="p">:</span>
      <span class="n">pd</span><span class="o">-&gt;</span><span class="n">system</span><span class="o">-&gt;</span><span class="n">realloc</span><span class="p">(</span><span class="n">ps</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
      <span class="n">ps</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
      <span class="k">break</span><span class="p">;</span>
    <span class="nl">default:</span>
      <span class="c1">// do nothing</span>
    <span class="k">break</span><span class="p">;</span>
  <span class="p">};</span>

  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">int</span> <span class="nf">update</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">userdata</span><span class="p">)</span>
<span class="p">{</span>
  <span class="k">struct</span> <span class="n">ProgramState</span> <span class="o">*</span><span class="n">ps</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">ProgramState</span> <span class="o">*</span><span class="p">)</span><span class="n">userdata</span><span class="p">;</span>
  <span class="n">handleInput</span><span class="p">(</span><span class="n">ps</span><span class="p">);</span>
  <span class="n">draw</span><span class="p">(</span><span class="n">ps</span><span class="p">);</span>
  <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">handleInput</span><span class="p">(</span><span class="k">struct</span> <span class="n">ProgramState</span> <span class="o">*</span><span class="n">ps</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">PlaydateAPI</span><span class="o">*</span> <span class="n">pd</span> <span class="o">=</span> <span class="n">ps</span><span class="o">-&gt;</span><span class="n">pd</span><span class="p">;</span>

  <span class="kt">int</span> <span class="n">x_direction</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">y_direction</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

  <span class="c1">// dpad input</span>
  <span class="n">PDButtons</span> <span class="n">currentInput</span><span class="p">;</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">system</span><span class="o">-&gt;</span><span class="n">getButtonState</span><span class="p">(</span><span class="o">&amp;</span><span class="n">currentInput</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span> <span class="n">currentInput</span> <span class="o">&amp;</span> <span class="n">kButtonUp</span> <span class="p">)</span> <span class="p">{</span>
    <span class="n">ps</span><span class="o">-&gt;</span><span class="n">y</span><span class="o">--</span><span class="p">;</span>
  <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span> <span class="n">currentInput</span> <span class="o">&amp;</span> <span class="n">kButtonDown</span> <span class="p">)</span> <span class="p">{</span>
    <span class="n">ps</span><span class="o">-&gt;</span><span class="n">y</span><span class="o">++</span><span class="p">;</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="n">y_direction</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">if</span> <span class="p">(</span> <span class="n">currentInput</span> <span class="o">&amp;</span> <span class="n">kButtonLeft</span> <span class="p">)</span> <span class="p">{</span>
    <span class="n">ps</span><span class="o">-&gt;</span><span class="n">x</span><span class="o">--</span><span class="p">;</span>
  <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span> <span class="n">currentInput</span> <span class="o">&amp;</span> <span class="n">kButtonRight</span> <span class="p">)</span> <span class="p">{</span>
    <span class="n">ps</span><span class="o">-&gt;</span><span class="n">x</span><span class="o">++</span><span class="p">;</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="n">x_direction</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">if</span> <span class="p">((</span> <span class="n">currentInput</span> <span class="o">&amp;</span> <span class="n">kButtonA</span> <span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="p">(</span> <span class="n">ps</span><span class="o">-&gt;</span><span class="n">previousInput</span> <span class="o">&amp;</span> <span class="n">kButtonA</span> <span class="p">))</span> <span class="p">{</span>
    <span class="n">ps</span><span class="o">-&gt;</span><span class="n">dx</span> <span class="o">*=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">if</span> <span class="p">((</span> <span class="n">currentInput</span> <span class="o">&amp;</span> <span class="n">kButtonB</span> <span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="p">(</span> <span class="n">ps</span><span class="o">-&gt;</span><span class="n">previousInput</span> <span class="o">&amp;</span> <span class="n">kButtonB</span> <span class="p">))</span> <span class="p">{</span>
    <span class="n">ps</span><span class="o">-&gt;</span><span class="n">dy</span> <span class="o">*=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="n">ps</span><span class="o">-&gt;</span><span class="n">previousInput</span> <span class="o">=</span> <span class="n">currentInput</span><span class="p">;</span>

  <span class="kt">int</span> <span class="n">steps</span><span class="p">;</span>

  <span class="k">if</span> <span class="p">(</span><span class="n">pd</span><span class="o">-&gt;</span><span class="n">system</span><span class="o">-&gt;</span><span class="n">isCrankDocked</span><span class="p">())</span> <span class="p">{</span>
    <span class="n">steps</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="n">steps</span> <span class="o">=</span> <span class="n">pd</span><span class="o">-&gt;</span><span class="n">system</span><span class="o">-&gt;</span><span class="n">getCrankChange</span><span class="p">();</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">steps</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">steps</span> <span class="o">=</span> <span class="o">-</span><span class="n">steps</span><span class="p">;</span>
      <span class="n">x_direction</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
      <span class="n">y_direction</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>

  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">steps</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">ps</span><span class="o">-&gt;</span><span class="n">x</span> <span class="o">+=</span> <span class="n">ps</span><span class="o">-&gt;</span><span class="n">dx</span> <span class="o">*</span> <span class="n">x_direction</span><span class="p">;</span>
    <span class="n">ps</span><span class="o">-&gt;</span><span class="n">y</span> <span class="o">+=</span> <span class="n">ps</span><span class="o">-&gt;</span><span class="n">dy</span> <span class="o">*</span> <span class="n">y_direction</span><span class="p">;</span>

    <span class="c1">// bounce</span>
    <span class="k">if</span> <span class="p">(</span> <span class="n">ps</span><span class="o">-&gt;</span><span class="n">x</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">LCD_COLUMNS</span> <span class="o">&lt;</span> <span class="n">ps</span><span class="o">-&gt;</span><span class="n">x</span> <span class="p">)</span> <span class="p">{</span>
      <span class="n">ps</span><span class="o">-&gt;</span><span class="n">dx</span> <span class="o">*=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">if</span> <span class="p">(</span> <span class="n">ps</span><span class="o">-&gt;</span><span class="n">y</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">LCD_ROWS</span> <span class="o">&lt;</span> <span class="n">ps</span><span class="o">-&gt;</span><span class="n">y</span> <span class="p">)</span> <span class="p">{</span>
      <span class="n">ps</span><span class="o">-&gt;</span><span class="n">dy</span> <span class="o">*=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">adjustTextPosition</span><span class="p">(</span><span class="kt">int</span> <span class="n">x</span><span class="p">,</span> <span class="kt">int</span> <span class="n">w</span><span class="p">,</span> <span class="kt">int</span> <span class="n">min</span><span class="p">,</span> <span class="kt">int</span> <span class="n">max</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">&lt;</span> <span class="n">min</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">min</span><span class="p">;</span>
  <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">max</span> <span class="o">&lt;</span> <span class="n">x</span> <span class="o">+</span> <span class="n">w</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">max</span> <span class="o">-</span> <span class="n">w</span><span class="p">;</span>
  <span class="p">}</span> <span class="c1">// else</span>
  <span class="k">return</span> <span class="n">x</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">keepTextOnScreen</span><span class="p">(</span><span class="kt">int</span> <span class="o">*</span><span class="n">x_ptr</span><span class="p">,</span> <span class="kt">int</span> <span class="o">*</span><span class="n">y_ptr</span><span class="p">,</span> <span class="kt">int</span> <span class="n">x</span><span class="p">,</span> <span class="kt">int</span> <span class="n">y</span><span class="p">)</span> <span class="p">{</span>
  <span class="o">*</span><span class="n">x_ptr</span> <span class="o">=</span> <span class="n">adjustTextPosition</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">TEXT_WIDTH</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">LCD_COLUMNS</span><span class="p">);</span>
  <span class="o">*</span><span class="n">y_ptr</span> <span class="o">=</span> <span class="n">adjustTextPosition</span><span class="p">(</span><span class="n">y</span><span class="p">,</span> <span class="n">TEXT_HEIGHT</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">LCD_ROWS</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">drawOutlinedText</span><span class="p">(</span>
  <span class="n">PlaydateAPI</span><span class="o">*</span> <span class="n">pd</span><span class="p">,</span>
  <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">message</span><span class="p">,</span>
  <span class="kt">int</span> <span class="n">x</span><span class="p">,</span>
  <span class="kt">int</span> <span class="n">y</span><span class="p">,</span>
  <span class="kt">int</span> <span class="n">outlineWidth</span><span class="p">,</span>
  <span class="n">LCDColor</span> <span class="n">textColor</span><span class="p">,</span>
  <span class="n">LCDColor</span> <span class="n">outlineColor</span>
<span class="p">)</span> <span class="p">{</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">graphics</span><span class="o">-&gt;</span><span class="n">setDrawMode</span><span class="p">(</span><span class="n">outlineColor</span><span class="p">);</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">graphics</span><span class="o">-&gt;</span><span class="n">drawText</span><span class="p">(</span>
    <span class="n">message</span><span class="p">,</span> <span class="n">strlen</span><span class="p">(</span><span class="n">message</span><span class="p">),</span> <span class="n">kASCIIEncoding</span><span class="p">,</span> <span class="n">x</span> <span class="o">-</span> <span class="n">outlineWidth</span><span class="p">,</span> <span class="n">y</span>
  <span class="p">);</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">graphics</span><span class="o">-&gt;</span><span class="n">drawText</span><span class="p">(</span>
    <span class="n">message</span><span class="p">,</span> <span class="n">strlen</span><span class="p">(</span><span class="n">message</span><span class="p">),</span> <span class="n">kASCIIEncoding</span><span class="p">,</span> <span class="n">x</span> <span class="o">+</span> <span class="n">outlineWidth</span><span class="p">,</span> <span class="n">y</span>
  <span class="p">);</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">graphics</span><span class="o">-&gt;</span><span class="n">drawText</span><span class="p">(</span>
    <span class="n">message</span><span class="p">,</span> <span class="n">strlen</span><span class="p">(</span><span class="n">message</span><span class="p">),</span> <span class="n">kASCIIEncoding</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">-</span> <span class="n">outlineWidth</span>
  <span class="p">);</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">graphics</span><span class="o">-&gt;</span><span class="n">drawText</span><span class="p">(</span>
    <span class="n">message</span><span class="p">,</span> <span class="n">strlen</span><span class="p">(</span><span class="n">message</span><span class="p">),</span> <span class="n">kASCIIEncoding</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">+</span> <span class="n">outlineWidth</span>
  <span class="p">);</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">graphics</span><span class="o">-&gt;</span><span class="n">setDrawMode</span><span class="p">(</span><span class="n">textColor</span><span class="p">);</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">graphics</span><span class="o">-&gt;</span><span class="n">drawText</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">strlen</span><span class="p">(</span><span class="n">message</span><span class="p">),</span> <span class="n">kASCIIEncoding</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">draw</span><span class="p">(</span><span class="k">struct</span> <span class="n">ProgramState</span> <span class="o">*</span><span class="n">ps</span><span class="p">)</span>
<span class="p">{</span>
  <span class="n">PlaydateAPI</span><span class="o">*</span> <span class="n">pd</span> <span class="o">=</span> <span class="n">ps</span><span class="o">-&gt;</span><span class="n">pd</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">stroke</span> <span class="o">=</span> <span class="n">ps</span><span class="o">-&gt;</span><span class="n">strokeWidth</span><span class="p">;;</span>
  <span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="n">ps</span><span class="o">-&gt;</span><span class="n">x</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="n">ps</span><span class="o">-&gt;</span><span class="n">y</span><span class="p">;</span>

  <span class="kt">char</span> <span class="o">*</span><span class="n">message</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">text_x</span><span class="p">,</span> <span class="n">text_y</span><span class="p">;</span>

  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">graphics</span><span class="o">-&gt;</span><span class="n">clear</span><span class="p">((</span><span class="n">LCDColor</span><span class="p">)</span><span class="n">gray50</span><span class="p">);</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">graphics</span><span class="o">-&gt;</span><span class="n">setFont</span><span class="p">(</span><span class="n">ps</span><span class="o">-&gt;</span><span class="n">font</span><span class="p">);</span>

  <span class="c1">// distance visual</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">graphics</span><span class="o">-&gt;</span><span class="n">fillRect</span><span class="p">(</span><span class="n">stroke</span><span class="p">,</span> <span class="n">stroke</span><span class="p">,</span> <span class="n">x</span><span class="o">-</span><span class="mi">2</span><span class="o">*</span><span class="n">stroke</span><span class="p">,</span> <span class="n">y</span><span class="o">-</span><span class="mi">2</span><span class="o">*</span><span class="n">stroke</span><span class="p">,</span> <span class="n">kColorBlack</span><span class="p">);</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">graphics</span><span class="o">-&gt;</span><span class="n">drawLine</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">stroke</span><span class="p">,</span> <span class="n">kColorWhite</span><span class="p">);</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">graphics</span><span class="o">-&gt;</span><span class="n">drawLine</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">stroke</span><span class="p">,</span> <span class="n">kColorBlack</span><span class="p">);</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">graphics</span><span class="o">-&gt;</span><span class="n">drawLine</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">stroke</span><span class="p">,</span> <span class="n">kColorBlack</span><span class="p">);</span>

  <span class="c1">// distance message</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">system</span><span class="o">-&gt;</span><span class="n">formatString</span><span class="p">(</span><span class="o">&amp;</span><span class="n">message</span><span class="p">,</span> <span class="s">"d=%.3f"</span><span class="p">,</span> <span class="n">fast_sqrt</span><span class="p">(</span><span class="n">x</span><span class="o">*</span><span class="n">x</span> <span class="o">+</span> <span class="n">y</span><span class="o">*</span><span class="n">y</span><span class="p">));</span>
  <span class="n">keepTextOnScreen</span><span class="p">(</span>
    <span class="o">&amp;</span><span class="n">text_x</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">text_y</span><span class="p">,</span> <span class="p">(</span><span class="n">x</span> <span class="o">-</span> <span class="n">TEXT_WIDTH</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">,</span> <span class="p">(</span><span class="n">y</span> <span class="o">-</span> <span class="n">TEXT_HEIGHT</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span>
  <span class="p">);</span>
  <span class="n">drawOutlinedText</span><span class="p">(</span>
    <span class="n">pd</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="n">text_x</span><span class="p">,</span> <span class="n">text_y</span><span class="p">,</span> <span class="n">stroke</span><span class="p">,</span> <span class="n">kDrawModeInverted</span><span class="p">,</span> <span class="n">kDrawModeCopy</span>
  <span class="p">);</span>

  <span class="c1">// position message</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">system</span><span class="o">-&gt;</span><span class="n">formatString</span><span class="p">(</span><span class="o">&amp;</span><span class="n">message</span><span class="p">,</span> <span class="s">"(%d, %d)"</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">);</span>
  <span class="n">keepTextOnScreen</span><span class="p">(</span><span class="o">&amp;</span><span class="n">text_x</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">text_y</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">);</span>
  <span class="n">drawOutlinedText</span><span class="p">(</span>
    <span class="n">pd</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="n">text_x</span><span class="p">,</span> <span class="n">text_y</span><span class="p">,</span> <span class="n">stroke</span><span class="p">,</span> <span class="n">kDrawModeCopy</span><span class="p">,</span> <span class="n">kDrawModeInverted</span>
  <span class="p">);</span>

  <span class="c1">// FPS display</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">system</span><span class="o">-&gt;</span><span class="n">drawFPS</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>

  <span class="c1">// cleanup</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">system</span><span class="o">-&gt;</span><span class="n">realloc</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="p">}</span></code></pre></figure>

<p>Add <strong>main.h</strong>.</p>

<p><strong>src/main.h</strong></p>

<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="cp">#ifndef MAIN_H
#define MAIN_H
</span>
<span class="cp">#include</span> <span class="cpf">"pd_api.h"</span><span class="cp">
</span>
<span class="cp">#define TEXT_WIDTH 86
#define TEXT_HEIGHT 16
</span><span class="k">extern</span> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">fontpath</span><span class="p">;</span>
<span class="k">extern</span> <span class="k">const</span> <span class="n">LCDPattern</span> <span class="n">gray50</span><span class="p">;</span>

<span class="k">struct</span> <span class="n">ProgramState</span> <span class="p">{</span>
  <span class="n">PlaydateAPI</span> <span class="o">*</span><span class="n">pd</span><span class="p">;</span>
  <span class="n">LCDFont</span> <span class="o">*</span><span class="n">font</span><span class="p">;</span>
  <span class="n">PDButtons</span> <span class="n">previousInput</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">strokeWidth</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">x</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">y</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">dx</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">dy</span><span class="p">;</span>
<span class="p">};</span>

<span class="kt">void</span> <span class="nf">initProgramState</span><span class="p">(</span><span class="k">struct</span> <span class="n">ProgramState</span> <span class="o">*</span><span class="n">ps</span><span class="p">,</span> <span class="n">PlaydateAPI</span> <span class="o">*</span><span class="n">pd</span><span class="p">);</span>
<span class="cp">#ifdef _WINDLL
</span><span class="kr">__declspec</span><span class="p">(</span><span class="n">dllexport</span><span class="p">)</span>
<span class="cp">#endif
</span><span class="kt">int</span> <span class="nf">eventHandler</span><span class="p">(</span><span class="n">PlaydateAPI</span><span class="o">*</span> <span class="n">pd</span><span class="p">,</span> <span class="n">PDSystemEvent</span> <span class="n">event</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">arg</span><span class="p">);</span>
<span class="k">static</span> <span class="kt">int</span> <span class="nf">update</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">userdata</span><span class="p">);</span>
<span class="kt">void</span> <span class="nf">handleInput</span><span class="p">(</span><span class="k">struct</span> <span class="n">ProgramState</span> <span class="o">*</span><span class="n">ps</span><span class="p">);</span>
<span class="kt">void</span> <span class="nf">draw</span><span class="p">(</span><span class="k">struct</span> <span class="n">ProgramState</span> <span class="o">*</span><span class="n">ps</span><span class="p">);</span>

<span class="cp">#endif // MAIN_H</span></code></pre></figure>

<p>Take the <strong>fast_sqrt.f90</strong> and <strong>fast_sqrt.h</strong> files from the previous section.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">PREVIOUS_PROJECT_PATH</span><span class="o">=</span><span class="s2">"path_to_project_in_previous_section"</span>
<span class="nb">cp</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PREVIOUS_PROJECT_PATH</span><span class="k">}</span><span class="s2">/fast_sqrt.f90"</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PREVIOUS_PROJECT_PATH</span><span class="k">}</span><span class="s2">/fast_sqrt.h"</span> src/</code></pre></figure>

<p>Change the <strong>PRODUCT</strong> and <strong>SRC</strong> lines in the <strong>Makefile</strong>.</p>

<p><strong>Makefile</strong> Partial Listing</p>

<figure class="highlight"><pre><code class="language-make" data-lang="make"><span class="nv">PRODUCT</span> <span class="o">=</span> FortranTest.pdx

<span class="c"># List C source files here
</span><span class="nv">SRC</span> <span class="o">=</span> src/main.c

<span class="c"># List Fortran source files here
</span><span class="nv">FSRC</span> <span class="o">=</span> src/fast_sqrt.f90

<span class="c"># last line of Makefile
</span><span class="k">include</span><span class="sx"> common.mk</span></code></pre></figure>

<p>Playdate Makefiles rely on centralized machinery that does not support Fortran
out of the box.
Instead of directly modifying the original
<strong>${PLAYDATE_SDK_PATH}/C_API/buildsupport/common.mk</strong> file, 
make a copy of <strong>common.mk</strong> for this project.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nb">cp</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PLAYDATE_SDK_PATH</span><span class="k">}</span><span class="s2">/C_API/buildsupport/common.mk"</span> common.mk</code></pre></figure>

<p>Use the following <strong>diff</strong> as a guide to update <strong>common.mk</strong>.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">$ </span>diff <span class="s2">"</span><span class="k">${</span><span class="nv">PLAYDATE_SDK_PATH</span><span class="k">}</span><span class="s2">/C_API/buildsupport/common.mk"</span> common.mk
84c84
&lt; _OBJS	<span class="o">=</span> <span class="si">$(</span>SRC:.c<span class="o">=</span>.o<span class="si">)</span>
<span class="nt">---</span>
<span class="o">&gt;</span> _OBJS	<span class="o">=</span> <span class="si">$(</span>SRC:.c<span class="o">=</span>.o<span class="si">)</span> <span class="si">$(</span>FSRC:.f90<span class="o">=</span>.o<span class="si">)</span>
106a107,111
<span class="o">&gt;</span> <span class="nv">SIMFC</span><span class="o">=</span><span class="si">$(</span>shell which gfortran<span class="si">)</span>
<span class="o">&gt;</span> SIMFCFLAGS <span class="o">=</span> <span class="nt">-gdwarf-2</span> <span class="nt">-Wall</span>
<span class="o">&gt;</span> <span class="nv">FC</span><span class="o">=</span><span class="si">$(</span>shell which <span class="si">$(</span>TRGT<span class="si">)</span>gfortran<span class="si">)</span>
<span class="o">&gt;</span> FCFLAGS <span class="o">=</span> <span class="si">$(</span>MCFLAGS<span class="si">)</span> <span class="si">$(</span>OPT<span class="si">)</span> <span class="nt">-gdwarf-2</span> <span class="nt">-Wall</span>
<span class="o">&gt;</span>
131c136
&lt; pdc: simulator
<span class="nt">---</span>
<span class="o">&gt;</span> pdc: device simulator
140a146,153
<span class="o">&gt;</span> <span class="si">$(</span>OBJDIR<span class="si">)</span>/%.o : %.f90 | OBJDIR DEPDIR
<span class="o">&gt;</span> 	<span class="nb">mkdir</span> <span class="nt">-p</span> <span class="sb">`</span><span class="nb">dirname</span> <span class="nv">$@</span><span class="sb">`</span>
<span class="o">&gt;</span> 	<span class="si">$(</span>FC<span class="si">)</span> <span class="si">$(</span>FCFLAGS<span class="si">)</span> <span class="nt">-c</span> <span class="nv">$&lt;</span> <span class="nt">-o</span> <span class="nv">$@</span>
<span class="o">&gt;</span>
<span class="o">&gt;</span> <span class="si">$(</span>OBJDIR<span class="si">)</span>/%_simulator.o : %.f90 | OBJDIR DEPDIR
<span class="o">&gt;</span> 	<span class="nb">mkdir</span> <span class="nt">-p</span> <span class="si">$(</span><span class="nb">dir</span> <span class="nv">$@</span><span class="si">)</span>
<span class="o">&gt;</span> 	<span class="si">$(</span>SIMFC<span class="si">)</span> <span class="si">$(</span>SIMFCFLAGS<span class="si">)</span> <span class="nt">-c</span> <span class="nv">$&lt;</span> <span class="nt">-o</span> <span class="nv">$@</span>
<span class="o">&gt;</span>
153,154c166,167
&lt; <span class="si">$(</span>OBJDIR<span class="si">)</span>/pdex.<span class="k">${</span><span class="nv">DYLIB_EXT</span><span class="k">}</span>: OBJDIR
&lt; 	<span class="si">$(</span>SIMCOMPILER<span class="si">)</span> <span class="si">$(</span>DYLIB_FLAGS<span class="si">)</span> <span class="nt">-lm</span> <span class="nt">-DTARGET_SIMULATOR</span><span class="o">=</span>1 <span class="nt">-DTARGET_EXTENSION</span><span class="o">=</span>1 <span class="si">$(</span>INCDIR<span class="si">)</span> <span class="nt">-o</span> <span class="si">$(</span>OBJDIR<span class="si">)</span>/pdex.<span class="k">${</span><span class="nv">DYLIB_EXT</span><span class="k">}</span> <span class="si">$(</span>SRC<span class="si">)</span>
<span class="nt">---</span>
<span class="o">&gt;</span> <span class="si">$(</span>OBJDIR<span class="si">)</span>/pdex.<span class="k">${</span><span class="nv">DYLIB_EXT</span><span class="k">}</span>: OBJDIR <span class="si">$(</span>FSRC:%.f90<span class="o">=</span><span class="si">$(</span>OBJDIR<span class="si">)</span>/%_simulator.o<span class="si">)</span>
<span class="o">&gt;</span> 	<span class="si">$(</span>SIMCOMPILER<span class="si">)</span> <span class="si">$(</span>DYLIB_FLAGS<span class="si">)</span> <span class="nt">-lm</span> <span class="nt">-DTARGET_SIMULATOR</span><span class="o">=</span>1 <span class="nt">-DTARGET_EXTENSION</span><span class="o">=</span>1 <span class="si">$(</span>INCDIR<span class="si">)</span> <span class="nt">-o</span> <span class="si">$(</span>OBJDIR<span class="si">)</span>/pdex.<span class="k">${</span><span class="nv">DYLIB_EXT</span><span class="k">}</span> <span class="si">$(</span>SRC<span class="si">)</span> <span class="si">$(</span>FSRC:%.f90<span class="o">=</span><span class="si">$(</span>OBJDIR<span class="si">)</span>/%_simulator.o<span class="si">)</span></code></pre></figure>

<p>Finally, build and run the project to verify it works.
The square root demo should boot and run in the simulator.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">PRODUCT</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span><span class="nb">cat </span>Source/pdxinfo | <span class="nb">grep </span>name | <span class="nb">cut</span> <span class="nt">-d</span> <span class="s2">"="</span> <span class="nt">-f</span> 2-<span class="si">)</span><span class="s2">.pdx"</span>
make clean simulator
pdc Source <span class="s2">"</span><span class="k">${</span><span class="nv">PRODUCT</span><span class="k">}</span><span class="s2">"</span>
playdate_simulator <span class="s2">"</span><span class="k">${</span><span class="nv">PRODUCT</span><span class="k">}</span><span class="s2">"</span></code></pre></figure>

<h4 id="running-fortran-on-playdate-hardware">Running Fortran on Playdate Hardware</h4>

<p>The Playdate SDK does not ship with <strong>arm-none-eabi-gfortran</strong>, but the full
toolchain distributed by ARM includes it.
Download the latest copy of the ARM toolchain for your host platfrom from the
<a href="https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads">ARM GNU Toolchain Downloads Page</a>, and install it.
The author of this post downloaded the
<a href="arm-gnu-toolchain-11.3.rel1-darwin-x86_64-arm-none-eabi.pkg">macOS Hosted Bare-Metal Target (arm-none-eabi) 11.3.rel1 toolchain</a>.</p>

<p>The toolchain was installed in <strong>/Applications/ArmGNUToolchain/</strong> on macOS,
The following command can be used to find the installation directory from
the command line.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nb">dirname</span> <span class="si">$(</span>find / <span class="nt">-name</span> <span class="s2">"arm-none-eabi-gfortran"</span> 2&gt;/dev/null<span class="si">)</span></code></pre></figure>

<p>Add the directory to the <strong>PATH</strong> environment variable.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">ARM_TOOLCHAIN_PATH</span><span class="o">=</span><span class="s2">"/Applications/ArmGNUToolchain/11.3.rel1/arm-none-eabi/bin"</span>
<span class="nb">echo</span> <span class="s1">'export PATH="'</span><span class="s2">"</span><span class="k">${</span><span class="nv">ARM_TOOLCHAIN_PATH</span><span class="k">}</span><span class="s2">"</span><span class="s1">':${PATH}"'</span> <span class="o">&gt;&gt;</span> <span class="s2">"</span><span class="k">${</span><span class="nv">HOME</span><span class="k">}</span><span class="s2">/.profile"</span></code></pre></figure>

<p>Reload <strong>.profile</strong> if necessary.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nb">source</span> <span class="s2">"</span><span class="k">${</span><span class="nv">HOME</span><span class="k">}</span><span class="s2">/.profile"</span></code></pre></figure>

<p>The above <strong>diff</strong> contains all of the necessary changes to build a PDX file
that runs on hardware.
After updating the <strong>PATH</strong>, build and run the program to verify it works.
To upload a PDX file to hardware, first run it in the simulator.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">PRODUCT</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span><span class="nb">cat </span>Source/pdxinfo | <span class="nb">grep </span>name | <span class="nb">cut</span> <span class="nt">-d</span> <span class="s2">"="</span> <span class="nt">-f</span> 2-<span class="si">)</span><span class="s2">.pdx"</span>
make
playdate_simulator <span class="s2">"</span><span class="k">${</span><span class="nv">PRODUCT</span><span class="k">}</span><span class="s2">"</span></code></pre></figure>

<p>Then either “Upload Game to Device” from the “Device” menu or Playdate icon on
the lower lefthand corner of the simulator (with the crank controls collapsed).
Once the game is on the device, <strong>pdutil</strong> can launch it.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c"># after the game is on the device</span>
<span class="nv">PRODUCT</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span><span class="nb">cat </span>Source/pdxinfo | <span class="nb">grep </span>name | <span class="nb">cut</span> <span class="nt">-d</span> <span class="s2">"="</span> <span class="nt">-f</span> 2-<span class="si">)</span><span class="s2">.pdx"</span>
<span class="nv">PDUTIL_DEVICE</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span><span class="nb">ls</span> /dev/cu.usbmodemPD<span class="k">*</span> | <span class="nb">head</span> <span class="nt">-n</span> 1<span class="si">)</span><span class="s2">"</span>
pdutil <span class="s2">"</span><span class="k">${</span><span class="nv">PDUTIL_DEVICE</span><span class="k">}</span><span class="s2">"</span> run <span class="s2">"/Games/</span><span class="k">${</span><span class="nv">PRODUCT</span><span class="k">}</span><span class="s2">"</span></code></pre></figure>

<h4 id="verification">Verification</h4>

<p>Pull out the crank and use the D-pad to move the lower righthand point to
(300, 125).
The diagonal length should be 325.000.
Use a calculator to verify other sets of values.</p>

<figure class="highlight"><pre><code class="language-c" data-lang="c">  <span class="mi">300</span> <span class="o">*</span>   <span class="mi">300</span> <span class="o">=</span>  <span class="mi">90000</span>
  <span class="mi">125</span> <span class="o">*</span>   <span class="mi">125</span> <span class="o">=</span>  <span class="mi">15625</span>
  <span class="mi">325</span> <span class="o">*</span>   <span class="mi">325</span> <span class="o">=</span> <span class="mi">105625</span>
<span class="o">----------------------</span>
<span class="mi">90000</span> <span class="o">+</span> <span class="mi">15625</span> <span class="o">=</span> <span class="mi">105625</span></code></pre></figure>

<h4 id="more-information">More Information</h4>

<p>The Fortran quickstart tutorial has a section on
<a href="https://fortran-lang.org/en/learn/quickstart/derived_types/">Derived Types</a>.
Specifically, <strong>bind(c)</strong> offers interoperability with the
C programming language.
gcc also has documention on
<a href="https://gcc.gnu.org/onlinedocs/gfortran/Interoperability-with-C.html">Fortran interoperability with C</a>.</p>

<p>This post is based on the
“<a href="https://devforum.play.date/t/fortran-on-playdate/4238">Fortran on Playdate?</a>” thread on the
<a href="https://devforum.play.date/">Playdate Developer Forums</a>.</p>

<h2 id="references">References:</h2>

<ul>
  <li><a href="https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads">ARM GNU Toolchain Downloads Page</a></li>
  <li><a href="arm-gnu-toolchain-11.3.rel1-darwin-x86_64-arm-none-eabi.pkg">ARM GNU Toolchain, macOS Hosted Bare-Metal Target (arm-none-eabi) 11.3.rel1</a></li>
  <li><a href="https://fortran-lang.org/">Fortran Homepage</a></li>
  <li><a href="https://fortran-lang.org/en/learn/quickstart/derived_types/">Fortran Quickstart Tutorial, Derived Types</a></li>
  <li><a href="https://gcc.gnu.org/onlinedocs/gfortran/Interoperability-with-C.html">Fortran, gcc Interoperability with C</a></li>
  <li><a href="https://devforum.play.date/">Playdate Developer Forum</a></li>
  <li><a href="https://devforum.play.date/t/fortran-on-playdate/4238">Playdate Developer Forum, Fortran on Playdate</a></li>
  <li><a href="https://play.date/">Playdate Homepage</a></li>
  <li><a href="/gamedev/playdate/asm/arm/x86/2022/10/05/asm_playdate_development.html">Playdate, ASM Playdate Development</a></li>
</ul>]]></content><author><name>Brendan Sechter</name></author><category term="gamedev" /><category term="playdate" /><category term="fortran" /></entry><entry><title type="html">ASM Playdate Development</title><link href="https://sgeos.github.io/gamedev/playdate/asm/arm/x86/2022/10/05/asm_playdate_development.html" rel="alternate" type="text/html" title="ASM Playdate Development" /><published>2022-10-05T19:28:00+00:00</published><updated>2022-10-05T19:28:00+00:00</updated><id>https://sgeos.github.io/gamedev/playdate/asm/arm/x86/2022/10/05/asm_playdate_development</id><content type="html" xml:base="https://sgeos.github.io/gamedev/playdate/asm/arm/x86/2022/10/05/asm_playdate_development.html"><![CDATA[<!-- A57 -->
<script>console.log("A57");</script>

<p>The <a href="https://play.date/">Panic Playdate</a> is a tiny, just-for-fun indie game console.
This post will discuss getting started with ASM on the Panic Playdate.
The Playdate’s CPU is a Cortex M7 that <em>only</em> supports Thumb instructions.
The simulator uses the instruction set of the development machine.
The author has tested ASM in versions of the simulator that run on both
X86_64 and Apple Silicon.</p>

<h2 id="software-versions">Software Versions</h2>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">$ </span><span class="nb">date</span> <span class="nt">-u</span> <span class="s2">"+%Y-%m-%d %H:%M:%S +0000"</span>
2022-10-05 19:28:00 +0000
<span class="nv">$ </span><span class="nb">uname</span> <span class="nt">-vm</span>
Darwin Kernel Version 21.6.0: Mon Aug 22 20:19:52 PDT 2022<span class="p">;</span> root:xnu-8020.140.49~2/RELEASE_ARM64_T6000 arm64
<span class="nv">$ </span>ex <span class="nt">-s</span> +<span class="s1">'%s/&lt;[^&gt;].\{-}&gt;//ge'</span> +<span class="s1">'%s/\s\+//e'</span> +<span class="s1">'%norm J'</span> +<span class="s1">'g/^$/d'</span> +%p +q! /System/Library/CoreServices/SystemVersion.plist | <span class="nb">grep</span> <span class="nt">-E</span> <span class="s1">'ProductName|ProductVersion'</span> | <span class="nb">sed</span> <span class="s1">'s/^[^ ]* //g'</span> | <span class="nb">sed</span> <span class="s1">'N; s/\n/ /g'</span>
macOS 12.6
<span class="nv">$ </span>sysctl <span class="nt">-n</span> machdep.cpu.brand_string
Apple M1 Max
<span class="nv">$ </span><span class="nb">echo</span> <span class="s2">"</span><span class="k">${</span><span class="nv">SHELL</span><span class="k">}</span><span class="s2">"</span>
/bin/bash
<span class="nv">$ </span><span class="s2">"</span><span class="k">${</span><span class="nv">SHELL</span><span class="k">}</span><span class="s2">"</span> <span class="nt">--version</span>  | <span class="nb">head</span> <span class="nt">-n</span> 1
GNU bash, version 3.2.57<span class="o">(</span>1<span class="o">)</span><span class="nt">-release</span> <span class="o">(</span>arm64-apple-darwin21<span class="o">)</span>
<span class="nv">$ </span><span class="nb">cat</span> <span class="s2">"</span><span class="k">${</span><span class="nv">HOME</span><span class="k">}</span><span class="s2">/Developer/PlaydateSDK/VERSION.txt"</span>
1.12.3
<span class="nv">$ </span>cargo <span class="nt">--version</span>
cargo 1.66.0-nightly <span class="o">(</span>f5fed93ba 2022-09-27<span class="o">)</span></code></pre></figure>

<h2 id="instructions">Instructions</h2>

<h4 id="project-template">Project Template</h4>

<p>When ASM is handwritten at all, it is generally used for optimizing native
code.
Therefore, the Hello World C API example will make a reasonable
project template.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c"># copy in project template</span>
<span class="nv">PROJECT</span><span class="o">=</span><span class="s2">"asm_test"</span>
<span class="nv">PROJECT_PATH</span><span class="o">=</span><span class="s2">"my_playdate_projects"</span>
<span class="nb">cd</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PROJECT_PATH</span><span class="k">}</span><span class="s2">"</span>
<span class="nb">cp</span> <span class="nt">-r</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PLAYDATE_SDK_PATH</span><span class="k">}</span><span class="s2">/C_API/Examples/Hello World/"</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PROJECT</span><span class="k">}</span><span class="s2">"</span>
<span class="nb">cd</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PROJECT</span><span class="k">}</span><span class="s2">"</span></code></pre></figure>

<p>Replace the contents of <strong>main.c</strong> with the following.
This program calculates the distance of a diagonal line according to
the Pythagorean theorem.
The bulk of the code is C, but the square root will be calculated in ASM.
Note that the D-pad, buttons, and crank can be used to control the
distance in different ways.</p>

<p><strong>src/main.c</strong></p>

<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="cp">#include</span> <span class="cpf">"fast_sqrt.h"</span><span class="cp">
#include</span> <span class="cpf">"main.h"</span><span class="cp">
#include</span> <span class="cpf">"pd_api.h"</span><span class="cp">
</span>
<span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">fontpath</span> <span class="o">=</span> <span class="s">"/System/Fonts/Asheville-Sans-14-Bold.pft"</span><span class="p">;</span>
<span class="k">const</span> <span class="n">LCDPattern</span> <span class="n">gray50</span> <span class="o">=</span> <span class="p">{</span>
  <span class="c1">// Bitmap</span>
  <span class="mi">0</span><span class="n">b10101010</span><span class="p">,</span>
  <span class="mi">0</span><span class="n">b01010101</span><span class="p">,</span>
  <span class="mi">0</span><span class="n">b10101010</span><span class="p">,</span>
  <span class="mi">0</span><span class="n">b01010101</span><span class="p">,</span>
  <span class="mi">0</span><span class="n">b10101010</span><span class="p">,</span>
  <span class="mi">0</span><span class="n">b01010101</span><span class="p">,</span>
  <span class="mi">0</span><span class="n">b10101010</span><span class="p">,</span>
  <span class="mi">0</span><span class="n">b01010101</span><span class="p">,</span>

  <span class="c1">// Mask</span>
  <span class="mi">0</span><span class="n">b11111111</span><span class="p">,</span>
  <span class="mi">0</span><span class="n">b11111111</span><span class="p">,</span>
  <span class="mi">0</span><span class="n">b11111111</span><span class="p">,</span>
  <span class="mi">0</span><span class="n">b11111111</span><span class="p">,</span>
  <span class="mi">0</span><span class="n">b11111111</span><span class="p">,</span>
  <span class="mi">0</span><span class="n">b11111111</span><span class="p">,</span>
  <span class="mi">0</span><span class="n">b11111111</span><span class="p">,</span>
  <span class="mi">0</span><span class="n">b11111111</span><span class="p">,</span>
<span class="p">};</span>

<span class="kt">void</span> <span class="nf">initProgramState</span><span class="p">(</span><span class="k">struct</span> <span class="n">ProgramState</span> <span class="o">*</span><span class="n">ps</span><span class="p">,</span> <span class="n">PlaydateAPI</span> <span class="o">*</span><span class="n">pd</span><span class="p">)</span>
<span class="p">{</span>
  <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">errorMessage</span><span class="p">;</span>

  <span class="n">ps</span><span class="o">-&gt;</span><span class="n">pd</span> <span class="o">=</span> <span class="n">pd</span><span class="p">;</span>
  <span class="n">ps</span><span class="o">-&gt;</span><span class="n">font</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
  <span class="n">ps</span><span class="o">-&gt;</span><span class="n">font</span> <span class="o">=</span> <span class="n">pd</span><span class="o">-&gt;</span><span class="n">graphics</span><span class="o">-&gt;</span><span class="n">loadFont</span><span class="p">(</span><span class="n">fontpath</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">errorMessage</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="nb">NULL</span> <span class="o">==</span> <span class="n">ps</span><span class="o">-&gt;</span><span class="n">font</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">pd</span><span class="o">-&gt;</span><span class="n">system</span><span class="o">-&gt;</span><span class="n">error</span><span class="p">(</span><span class="s">"%s:%i Couldn't load font %s: %s"</span><span class="p">,</span>
      <span class="n">__FILE__</span><span class="p">,</span> <span class="n">__LINE__</span><span class="p">,</span> <span class="n">fontpath</span><span class="p">,</span> <span class="n">errorMessage</span>
    <span class="p">);</span>
  <span class="p">}</span>
  <span class="n">ps</span><span class="o">-&gt;</span><span class="n">previousInput</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="n">ps</span><span class="o">-&gt;</span><span class="n">strokeWidth</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
  <span class="n">ps</span><span class="o">-&gt;</span><span class="n">x</span> <span class="o">=</span> <span class="p">(</span><span class="n">LCD_COLUMNS</span> <span class="o">-</span> <span class="n">TEXT_WIDTH</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
  <span class="n">ps</span><span class="o">-&gt;</span><span class="n">y</span> <span class="o">=</span> <span class="p">(</span><span class="n">LCD_ROWS</span> <span class="o">-</span> <span class="n">TEXT_HEIGHT</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
  <span class="n">ps</span><span class="o">-&gt;</span><span class="n">dx</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
  <span class="n">ps</span><span class="o">-&gt;</span><span class="n">dy</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>

<span class="cp">#ifdef _WINDLL
</span><span class="kr">__declspec</span><span class="p">(</span><span class="n">dllexport</span><span class="p">)</span>
<span class="cp">#endif
</span><span class="kt">int</span> <span class="nf">eventHandler</span><span class="p">(</span><span class="n">PlaydateAPI</span><span class="o">*</span> <span class="n">pd</span><span class="p">,</span> <span class="n">PDSystemEvent</span> <span class="n">event</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">arg</span><span class="p">)</span>
<span class="p">{</span>
  <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">arg</span><span class="p">;</span> <span class="c1">// only used for kEventKeyPressed == event</span>
  <span class="k">static</span> <span class="k">struct</span> <span class="n">ProgramState</span> <span class="o">*</span><span class="n">ps</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>

  <span class="k">switch</span> <span class="p">(</span><span class="n">event</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">case</span> <span class="n">kEventInit</span><span class="p">:</span>
      <span class="n">ps</span> <span class="o">=</span> <span class="n">pd</span><span class="o">-&gt;</span><span class="n">system</span><span class="o">-&gt;</span><span class="n">realloc</span><span class="p">(</span><span class="n">ps</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="k">struct</span> <span class="n">ProgramState</span><span class="p">));</span>
      <span class="n">initProgramState</span><span class="p">(</span><span class="n">ps</span><span class="p">,</span> <span class="n">pd</span><span class="p">);</span>
      <span class="n">pd</span><span class="o">-&gt;</span><span class="n">system</span><span class="o">-&gt;</span><span class="n">setUpdateCallback</span><span class="p">(</span><span class="n">update</span><span class="p">,</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">ps</span><span class="p">);</span>
      <span class="k">break</span><span class="p">;</span>
    <span class="k">case</span> <span class="n">kEventTerminate</span><span class="p">:</span>
      <span class="n">pd</span><span class="o">-&gt;</span><span class="n">system</span><span class="o">-&gt;</span><span class="n">realloc</span><span class="p">(</span><span class="n">ps</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
      <span class="n">ps</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
      <span class="k">break</span><span class="p">;</span>
    <span class="nl">default:</span>
      <span class="c1">// do nothing</span>
    <span class="k">break</span><span class="p">;</span>
  <span class="p">};</span>

  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">int</span> <span class="nf">update</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">userdata</span><span class="p">)</span>
<span class="p">{</span>
  <span class="k">struct</span> <span class="n">ProgramState</span> <span class="o">*</span><span class="n">ps</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">ProgramState</span> <span class="o">*</span><span class="p">)</span><span class="n">userdata</span><span class="p">;</span>
  <span class="n">handleInput</span><span class="p">(</span><span class="n">ps</span><span class="p">);</span>
  <span class="n">draw</span><span class="p">(</span><span class="n">ps</span><span class="p">);</span>
  <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">handleInput</span><span class="p">(</span><span class="k">struct</span> <span class="n">ProgramState</span> <span class="o">*</span><span class="n">ps</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">PlaydateAPI</span><span class="o">*</span> <span class="n">pd</span> <span class="o">=</span> <span class="n">ps</span><span class="o">-&gt;</span><span class="n">pd</span><span class="p">;</span>

  <span class="kt">int</span> <span class="n">x_direction</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">y_direction</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

  <span class="c1">// dpad input</span>
  <span class="n">PDButtons</span> <span class="n">currentInput</span><span class="p">;</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">system</span><span class="o">-&gt;</span><span class="n">getButtonState</span><span class="p">(</span><span class="o">&amp;</span><span class="n">currentInput</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span> <span class="n">currentInput</span> <span class="o">&amp;</span> <span class="n">kButtonUp</span> <span class="p">)</span> <span class="p">{</span>
    <span class="n">ps</span><span class="o">-&gt;</span><span class="n">y</span><span class="o">--</span><span class="p">;</span>
  <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span> <span class="n">currentInput</span> <span class="o">&amp;</span> <span class="n">kButtonDown</span> <span class="p">)</span> <span class="p">{</span>
    <span class="n">ps</span><span class="o">-&gt;</span><span class="n">y</span><span class="o">++</span><span class="p">;</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="n">y_direction</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">if</span> <span class="p">(</span> <span class="n">currentInput</span> <span class="o">&amp;</span> <span class="n">kButtonLeft</span> <span class="p">)</span> <span class="p">{</span>
    <span class="n">ps</span><span class="o">-&gt;</span><span class="n">x</span><span class="o">--</span><span class="p">;</span>
  <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span> <span class="n">currentInput</span> <span class="o">&amp;</span> <span class="n">kButtonRight</span> <span class="p">)</span> <span class="p">{</span>
    <span class="n">ps</span><span class="o">-&gt;</span><span class="n">x</span><span class="o">++</span><span class="p">;</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="n">x_direction</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">if</span> <span class="p">((</span> <span class="n">currentInput</span> <span class="o">&amp;</span> <span class="n">kButtonA</span> <span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="p">(</span> <span class="n">ps</span><span class="o">-&gt;</span><span class="n">previousInput</span> <span class="o">&amp;</span> <span class="n">kButtonA</span> <span class="p">))</span> <span class="p">{</span>
    <span class="n">ps</span><span class="o">-&gt;</span><span class="n">dx</span> <span class="o">*=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">if</span> <span class="p">((</span> <span class="n">currentInput</span> <span class="o">&amp;</span> <span class="n">kButtonB</span> <span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="p">(</span> <span class="n">ps</span><span class="o">-&gt;</span><span class="n">previousInput</span> <span class="o">&amp;</span> <span class="n">kButtonB</span> <span class="p">))</span> <span class="p">{</span>
    <span class="n">ps</span><span class="o">-&gt;</span><span class="n">dy</span> <span class="o">*=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="n">ps</span><span class="o">-&gt;</span><span class="n">previousInput</span> <span class="o">=</span> <span class="n">currentInput</span><span class="p">;</span>

  <span class="kt">int</span> <span class="n">steps</span><span class="p">;</span>

  <span class="k">if</span> <span class="p">(</span><span class="n">pd</span><span class="o">-&gt;</span><span class="n">system</span><span class="o">-&gt;</span><span class="n">isCrankDocked</span><span class="p">())</span> <span class="p">{</span>
    <span class="n">steps</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="n">steps</span> <span class="o">=</span> <span class="n">pd</span><span class="o">-&gt;</span><span class="n">system</span><span class="o">-&gt;</span><span class="n">getCrankChange</span><span class="p">();</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">steps</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">steps</span> <span class="o">=</span> <span class="o">-</span><span class="n">steps</span><span class="p">;</span>
      <span class="n">x_direction</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
      <span class="n">y_direction</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>

  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">steps</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">ps</span><span class="o">-&gt;</span><span class="n">x</span> <span class="o">+=</span> <span class="n">ps</span><span class="o">-&gt;</span><span class="n">dx</span> <span class="o">*</span> <span class="n">x_direction</span><span class="p">;</span>
    <span class="n">ps</span><span class="o">-&gt;</span><span class="n">y</span> <span class="o">+=</span> <span class="n">ps</span><span class="o">-&gt;</span><span class="n">dy</span> <span class="o">*</span> <span class="n">y_direction</span><span class="p">;</span>

    <span class="c1">// bounce</span>
    <span class="k">if</span> <span class="p">(</span> <span class="n">ps</span><span class="o">-&gt;</span><span class="n">x</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">LCD_COLUMNS</span> <span class="o">&lt;</span> <span class="n">ps</span><span class="o">-&gt;</span><span class="n">x</span> <span class="p">)</span> <span class="p">{</span>
      <span class="n">ps</span><span class="o">-&gt;</span><span class="n">dx</span> <span class="o">*=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">if</span> <span class="p">(</span> <span class="n">ps</span><span class="o">-&gt;</span><span class="n">y</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">LCD_ROWS</span> <span class="o">&lt;</span> <span class="n">ps</span><span class="o">-&gt;</span><span class="n">y</span> <span class="p">)</span> <span class="p">{</span>
      <span class="n">ps</span><span class="o">-&gt;</span><span class="n">dy</span> <span class="o">*=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">adjustTextPosition</span><span class="p">(</span><span class="kt">int</span> <span class="n">x</span><span class="p">,</span> <span class="kt">int</span> <span class="n">w</span><span class="p">,</span> <span class="kt">int</span> <span class="n">min</span><span class="p">,</span> <span class="kt">int</span> <span class="n">max</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">&lt;</span> <span class="n">min</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">min</span><span class="p">;</span>
  <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">max</span> <span class="o">&lt;</span> <span class="n">x</span> <span class="o">+</span> <span class="n">w</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">max</span> <span class="o">-</span> <span class="n">w</span><span class="p">;</span>
  <span class="p">}</span> <span class="c1">// else</span>
  <span class="k">return</span> <span class="n">x</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">keepTextOnScreen</span><span class="p">(</span><span class="kt">int</span> <span class="o">*</span><span class="n">x_ptr</span><span class="p">,</span> <span class="kt">int</span> <span class="o">*</span><span class="n">y_ptr</span><span class="p">,</span> <span class="kt">int</span> <span class="n">x</span><span class="p">,</span> <span class="kt">int</span> <span class="n">y</span><span class="p">)</span> <span class="p">{</span>
  <span class="o">*</span><span class="n">x_ptr</span> <span class="o">=</span> <span class="n">adjustTextPosition</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">TEXT_WIDTH</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">LCD_COLUMNS</span><span class="p">);</span>
  <span class="o">*</span><span class="n">y_ptr</span> <span class="o">=</span> <span class="n">adjustTextPosition</span><span class="p">(</span><span class="n">y</span><span class="p">,</span> <span class="n">TEXT_HEIGHT</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">LCD_ROWS</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">drawOutlinedText</span><span class="p">(</span>
  <span class="n">PlaydateAPI</span><span class="o">*</span> <span class="n">pd</span><span class="p">,</span>
  <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">message</span><span class="p">,</span>
  <span class="kt">int</span> <span class="n">x</span><span class="p">,</span>
  <span class="kt">int</span> <span class="n">y</span><span class="p">,</span>
  <span class="kt">int</span> <span class="n">outlineWidth</span><span class="p">,</span>
  <span class="n">LCDColor</span> <span class="n">textColor</span><span class="p">,</span>
  <span class="n">LCDColor</span> <span class="n">outlineColor</span>
<span class="p">)</span> <span class="p">{</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">graphics</span><span class="o">-&gt;</span><span class="n">setDrawMode</span><span class="p">(</span><span class="n">outlineColor</span><span class="p">);</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">graphics</span><span class="o">-&gt;</span><span class="n">drawText</span><span class="p">(</span>
    <span class="n">message</span><span class="p">,</span> <span class="n">strlen</span><span class="p">(</span><span class="n">message</span><span class="p">),</span> <span class="n">kASCIIEncoding</span><span class="p">,</span> <span class="n">x</span> <span class="o">-</span> <span class="n">outlineWidth</span><span class="p">,</span> <span class="n">y</span>
  <span class="p">);</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">graphics</span><span class="o">-&gt;</span><span class="n">drawText</span><span class="p">(</span>
    <span class="n">message</span><span class="p">,</span> <span class="n">strlen</span><span class="p">(</span><span class="n">message</span><span class="p">),</span> <span class="n">kASCIIEncoding</span><span class="p">,</span> <span class="n">x</span> <span class="o">+</span> <span class="n">outlineWidth</span><span class="p">,</span> <span class="n">y</span>
  <span class="p">);</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">graphics</span><span class="o">-&gt;</span><span class="n">drawText</span><span class="p">(</span>
    <span class="n">message</span><span class="p">,</span> <span class="n">strlen</span><span class="p">(</span><span class="n">message</span><span class="p">),</span> <span class="n">kASCIIEncoding</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">-</span> <span class="n">outlineWidth</span>
  <span class="p">);</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">graphics</span><span class="o">-&gt;</span><span class="n">drawText</span><span class="p">(</span>
    <span class="n">message</span><span class="p">,</span> <span class="n">strlen</span><span class="p">(</span><span class="n">message</span><span class="p">),</span> <span class="n">kASCIIEncoding</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">+</span> <span class="n">outlineWidth</span>
  <span class="p">);</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">graphics</span><span class="o">-&gt;</span><span class="n">setDrawMode</span><span class="p">(</span><span class="n">textColor</span><span class="p">);</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">graphics</span><span class="o">-&gt;</span><span class="n">drawText</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="n">strlen</span><span class="p">(</span><span class="n">message</span><span class="p">),</span> <span class="n">kASCIIEncoding</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">draw</span><span class="p">(</span><span class="k">struct</span> <span class="n">ProgramState</span> <span class="o">*</span><span class="n">ps</span><span class="p">)</span>
<span class="p">{</span>
  <span class="n">PlaydateAPI</span><span class="o">*</span> <span class="n">pd</span> <span class="o">=</span> <span class="n">ps</span><span class="o">-&gt;</span><span class="n">pd</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">stroke</span> <span class="o">=</span> <span class="n">ps</span><span class="o">-&gt;</span><span class="n">strokeWidth</span><span class="p">;;</span>
  <span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="n">ps</span><span class="o">-&gt;</span><span class="n">x</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="n">ps</span><span class="o">-&gt;</span><span class="n">y</span><span class="p">;</span>

  <span class="kt">char</span> <span class="o">*</span><span class="n">message</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">text_x</span><span class="p">,</span> <span class="n">text_y</span><span class="p">;</span>

  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">graphics</span><span class="o">-&gt;</span><span class="n">clear</span><span class="p">((</span><span class="n">LCDColor</span><span class="p">)</span><span class="n">gray50</span><span class="p">);</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">graphics</span><span class="o">-&gt;</span><span class="n">setFont</span><span class="p">(</span><span class="n">ps</span><span class="o">-&gt;</span><span class="n">font</span><span class="p">);</span>

  <span class="c1">// distance visual</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">graphics</span><span class="o">-&gt;</span><span class="n">fillRect</span><span class="p">(</span><span class="n">stroke</span><span class="p">,</span> <span class="n">stroke</span><span class="p">,</span> <span class="n">x</span><span class="o">-</span><span class="mi">2</span><span class="o">*</span><span class="n">stroke</span><span class="p">,</span> <span class="n">y</span><span class="o">-</span><span class="mi">2</span><span class="o">*</span><span class="n">stroke</span><span class="p">,</span> <span class="n">kColorBlack</span><span class="p">);</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">graphics</span><span class="o">-&gt;</span><span class="n">drawLine</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">stroke</span><span class="p">,</span> <span class="n">kColorWhite</span><span class="p">);</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">graphics</span><span class="o">-&gt;</span><span class="n">drawLine</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">stroke</span><span class="p">,</span> <span class="n">kColorBlack</span><span class="p">);</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">graphics</span><span class="o">-&gt;</span><span class="n">drawLine</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">stroke</span><span class="p">,</span> <span class="n">kColorBlack</span><span class="p">);</span>

  <span class="c1">// distance message</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">system</span><span class="o">-&gt;</span><span class="n">formatString</span><span class="p">(</span><span class="o">&amp;</span><span class="n">message</span><span class="p">,</span> <span class="s">"d=%.3f"</span><span class="p">,</span> <span class="n">fast_sqrt</span><span class="p">(</span><span class="n">x</span><span class="o">*</span><span class="n">x</span> <span class="o">+</span> <span class="n">y</span><span class="o">*</span><span class="n">y</span><span class="p">));</span>
  <span class="n">keepTextOnScreen</span><span class="p">(</span>
    <span class="o">&amp;</span><span class="n">text_x</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">text_y</span><span class="p">,</span> <span class="p">(</span><span class="n">x</span> <span class="o">-</span> <span class="n">TEXT_WIDTH</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">,</span> <span class="p">(</span><span class="n">y</span> <span class="o">-</span> <span class="n">TEXT_HEIGHT</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span>
  <span class="p">);</span>
  <span class="n">drawOutlinedText</span><span class="p">(</span>
    <span class="n">pd</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="n">text_x</span><span class="p">,</span> <span class="n">text_y</span><span class="p">,</span> <span class="n">stroke</span><span class="p">,</span> <span class="n">kDrawModeInverted</span><span class="p">,</span> <span class="n">kDrawModeCopy</span>
  <span class="p">);</span>

  <span class="c1">// position message</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">system</span><span class="o">-&gt;</span><span class="n">formatString</span><span class="p">(</span><span class="o">&amp;</span><span class="n">message</span><span class="p">,</span> <span class="s">"(%d, %d)"</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">);</span>
  <span class="n">keepTextOnScreen</span><span class="p">(</span><span class="o">&amp;</span><span class="n">text_x</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">text_y</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">);</span>
  <span class="n">drawOutlinedText</span><span class="p">(</span>
    <span class="n">pd</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="n">text_x</span><span class="p">,</span> <span class="n">text_y</span><span class="p">,</span> <span class="n">stroke</span><span class="p">,</span> <span class="n">kDrawModeCopy</span><span class="p">,</span> <span class="n">kDrawModeInverted</span>
  <span class="p">);</span>

  <span class="c1">// FPS display</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">system</span><span class="o">-&gt;</span><span class="n">drawFPS</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>

  <span class="c1">// cleanup</span>
  <span class="n">pd</span><span class="o">-&gt;</span><span class="n">system</span><span class="o">-&gt;</span><span class="n">realloc</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="p">}</span></code></pre></figure>

<p>Add <strong>main.h</strong>.</p>

<p><strong>src/main.h</strong></p>

<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="cp">#ifndef MAIN_H
#define MAIN_H
</span>
<span class="cp">#include</span> <span class="cpf">"pd_api.h"</span><span class="cp">
</span>
<span class="cp">#define TEXT_WIDTH 86
#define TEXT_HEIGHT 16
</span><span class="k">extern</span> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">fontpath</span><span class="p">;</span>
<span class="k">extern</span> <span class="k">const</span> <span class="n">LCDPattern</span> <span class="n">gray50</span><span class="p">;</span>

<span class="k">struct</span> <span class="n">ProgramState</span> <span class="p">{</span>
  <span class="n">PlaydateAPI</span> <span class="o">*</span><span class="n">pd</span><span class="p">;</span>
  <span class="n">LCDFont</span> <span class="o">*</span><span class="n">font</span><span class="p">;</span>
  <span class="n">PDButtons</span> <span class="n">previousInput</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">strokeWidth</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">x</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">y</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">dx</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">dy</span><span class="p">;</span>
<span class="p">};</span>

<span class="kt">void</span> <span class="nf">initProgramState</span><span class="p">(</span><span class="k">struct</span> <span class="n">ProgramState</span> <span class="o">*</span><span class="n">ps</span><span class="p">,</span> <span class="n">PlaydateAPI</span> <span class="o">*</span><span class="n">pd</span><span class="p">);</span>
<span class="cp">#ifdef _WINDLL
</span><span class="kr">__declspec</span><span class="p">(</span><span class="n">dllexport</span><span class="p">)</span>
<span class="cp">#endif
</span><span class="kt">int</span> <span class="nf">eventHandler</span><span class="p">(</span><span class="n">PlaydateAPI</span><span class="o">*</span> <span class="n">pd</span><span class="p">,</span> <span class="n">PDSystemEvent</span> <span class="n">event</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">arg</span><span class="p">);</span>
<span class="k">static</span> <span class="kt">int</span> <span class="nf">update</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">userdata</span><span class="p">);</span>
<span class="kt">void</span> <span class="nf">handleInput</span><span class="p">(</span><span class="k">struct</span> <span class="n">ProgramState</span> <span class="o">*</span><span class="n">ps</span><span class="p">);</span>
<span class="kt">void</span> <span class="nf">draw</span><span class="p">(</span><span class="k">struct</span> <span class="n">ProgramState</span> <span class="o">*</span><span class="n">ps</span><span class="p">);</span>

<span class="cp">#endif // MAIN_H</span></code></pre></figure>

<p>Add the <strong>fast_sqrt.h</strong> header file for the ASM code.</p>

<p><strong>src/fast_sqrt.h</strong></p>

<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="cp">#ifndef FAST_SQRT_H
#define FAST_SQRT_H
</span>
<span class="k">extern</span> <span class="kt">float</span> <span class="nf">fast_sqrt</span><span class="p">(</span><span class="kt">float</span><span class="p">);</span>

<span class="cp">#endif  // FAST_SQRT_H</span></code></pre></figure>

<p>Add a stub <strong>fast_sqrt.c</strong> implementation.</p>

<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="cp">#include</span> <span class="cpf">"fast_sqrt.h"</span><span class="cp">
</span>
<span class="kt">float</span> <span class="nf">fast_sqrt</span><span class="p">(</span><span class="kt">float</span> <span class="n">x</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// non-functional stub</span>
  <span class="k">return</span> <span class="mi">0</span><span class="p">.</span><span class="mi">0</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>

<p>Change the <strong>PRODUCT</strong> and <strong>SRC</strong> lines in the <strong>Makefile</strong>.</p>

<p><strong>Makefile</strong> Partial Listing</p>

<figure class="highlight"><pre><code class="language-make" data-lang="make"><span class="nv">PRODUCT</span> <span class="o">=</span> ASMTest.pdx
<span class="nv">SRC</span> <span class="o">=</span> src/main.c src/fast_sqrt.c</code></pre></figure>

<p>Update <strong>pdxinfo</strong>.</p>

<p><strong>Source/pdxinfo</strong></p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">name</span><span class="o">=</span>ASMTest
<span class="nv">author</span><span class="o">=</span>Brendan Sechter
<span class="nv">description</span><span class="o">=</span>ASM on Playdate proof of concept.
<span class="nv">bundleID</span><span class="o">=</span>com.sennue.poc_asmtest
<span class="nv">imagePath</span><span class="o">=</span></code></pre></figure>

<p>Finally, build and run the project to make sure it works.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">PRODUCT</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span><span class="nb">cat </span>Source/pdxinfo | <span class="nb">grep </span>name | <span class="nb">cut</span> <span class="nt">-d</span> <span class="s2">"="</span> <span class="nt">-f</span> 2-<span class="si">)</span><span class="s2">.pdx"</span>
make
playdate_simulator <span class="s2">"</span><span class="k">${</span><span class="nv">PRODUCT</span><span class="k">}</span><span class="s2">"</span></code></pre></figure>

<p>The distance of the diagonal line should be a fixed value of 0.0.
The goal is to calculate that value with ASM.</p>

<h4 id="asm-square-root-implementations">ASM Square Root Implementations</h4>

<p>On modern CPUs, calculating the square root of a number can be done with
a single instruction.
The Cortex M7 uses the ARMv7E-M microarchitecture.
It only supports Thumb instructions.
ARM-Thumb interworking is not possible on the Playdate.</p>

<p>The Thumb ASM for a square root subroutine that will run on hardware follows.</p>

<p><strong>src/fast_sqrt_armv7.s</strong></p>

<figure class="highlight"><pre><code class="language-nasm" data-lang="nasm">        <span class="nf">.syntax</span> <span class="nv">unified</span>
        <span class="nf">.set</span> <span class="nb">AL</span><span class="nv">IGNMENT</span><span class="p">,</span> <span class="mi">4</span>

<span class="nf">.text</span>
        <span class="nf">.align</span> <span class="nb">AL</span><span class="nv">IGNMENT</span>
        <span class="nf">.global</span> <span class="nv">fast_sqrt</span>
<span class="nl">fast_sqrt:</span>
        <span class="nf">vsqrt.f32</span>  <span class="nv">s0</span><span class="p">,</span> <span class="nv">s0</span>
        <span class="nf">bx</span>         <span class="nv">lr</span></code></pre></figure>

<p>The MacBook Pro this post is being typed up on runs on an Apple M1 Max.
It uses the ARMv8.5-A instruction set.
The Apple Silicon cores only support 64-bit ARM instructions.
Thumb is a thing of the past, and 32-bit ARM instructions are not supported
either.</p>

<p>The ARMv8 ASM for a square root subroutine that will run on the simulator
follows.</p>

<p><strong>src/fast_sqrt_armv8.s</strong></p>

<figure class="highlight"><pre><code class="language-nasm" data-lang="nasm">        <span class="nf">.set</span> <span class="nb">AL</span><span class="nv">IGNMENT</span><span class="p">,</span> <span class="mi">8</span>

<span class="nf">.text</span>
        <span class="nf">.align</span> <span class="nb">AL</span><span class="nv">IGNMENT</span>
        <span class="nf">.global</span> <span class="nv">_fast_sqrt</span>
<span class="nl">_fast_sqrt:</span>
        <span class="nf">fsqrt</span>   <span class="nv">s0</span><span class="p">,</span> <span class="nv">s0</span>
        <span class="nf">ret</span></code></pre></figure>

<p>The author first experimented with ASM on Playdate using a machine that runs
x86-64 instructions.
This is the ASM for a simulator subroutine that runs on a 64-bit Intel
processor.</p>

<p><strong>src/fast_sqrt_x86_64.s</strong></p>

<figure class="highlight"><pre><code class="language-nasm" data-lang="nasm">        <span class="nf">.intel_syntax</span>
        <span class="nf">.set</span> <span class="nb">AL</span><span class="nv">IGNMENT</span><span class="p">,</span> <span class="mi">16</span>

<span class="nf">.text</span>
        <span class="nf">.global</span> <span class="nv">_fast_sqrt</span>
<span class="nl">_fast_sqrt:</span>
        <span class="nf">sqrtss</span> <span class="nv">xmm0</span><span class="p">,</span> <span class="nv">xmm0</span>
        <span class="nf">ret</span></code></pre></figure>

<h4 id="makefile-modifications">Makefile Modifications</h4>

<p>The Playdate Makefiles look like they were written with ASM, but a few
modifications need to be made to properly build the project.
First, the ASM for the device and simulator need to be listed separately.
Update <strong>SRC</strong> and add <strong>ASRC</strong> definitions in the <strong>Makefile</strong>.
Uncomment the <strong>ASRC_SIMULATOR</strong> definition for your development platform.
Also, update the last line of the <strong>Makefile</strong> for the next step.</p>

<p><strong>Makefile</strong></p>

<figure class="highlight"><pre><code class="language-make" data-lang="make"><span class="c"># List C source files here
</span><span class="nv">SRC</span> <span class="o">=</span> src/main.c

<span class="c"># List ASM source files here
# Uncomment the ASRC_SIMULATOR definition for your development platform.
</span><span class="nv">ASRC_DEVICE</span> <span class="o">=</span> src/fast_sqrt_armv7.s
<span class="c">#ASRC_SIMULATOR = src/fast_sqrt_armv8.s
#ASRC_SIMULATOR = src/fast_sqrt_x86_64.s
</span>
<span class="c"># last line of Makefile
</span><span class="k">include</span><span class="sx"> common.mk</span></code></pre></figure>

<p>Playdate Makefiles rely on some common machinery that does not entirely
support ASM code.
The <strong>${PLAYDATE_SDK_PATH}/C_API/buildsupport/common.mk</strong> file could be
modified directly, but it is better to keep the modified copy in the project.
Copy <strong>common.mk</strong> into the project.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nb">cp</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PLAYDATE_SDK_PATH</span><span class="k">}</span><span class="s2">/C_API/buildsupport/common.mk"</span> common.mk</code></pre></figure>

<p>Three modifications need to be made to the local <strong>common.mk</strong>.
<strong>ASRC_DEVICE</strong> needs to be appended to <strong>_OBJS</strong>, and <strong>ASRC_SIMULATOR</strong>
needs to be appended to the <strong>$(OBJDIR)/pdex.${DYLIB_EXT}</strong> target.
Finally, the <strong>pdc</strong> target needs to rely on the <strong>device</strong> so that a
functional copy of <strong>pdex.bin</strong> is always included in the “fat” PDX file.
(Hardware runs <strong>pdex.bin</strong> while the simulator uses a dynamic library,
<strong>pdex.dylib</strong> on macOS.)</p>

<p>The following <strong>diff</strong> gives a guide to the changes that need to be made to
<strong>common.mk</strong>.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">$ </span>diff <span class="s2">"</span><span class="k">${</span><span class="nv">PLAYDATE_SDK_PATH</span><span class="k">}</span><span class="s2">/C_API/buildsupport/common.mk"</span> common.mk
84c84
&lt; _OBJS	<span class="o">=</span> <span class="si">$(</span>SRC:.c<span class="o">=</span>.o<span class="si">)</span>
<span class="nt">---</span>
<span class="o">&gt;</span> _OBJS	<span class="o">=</span> <span class="si">$(</span>SRC:.c<span class="o">=</span>.o<span class="si">)</span> <span class="si">$(</span>ASRC_DEVICE:.s<span class="o">=</span>.o<span class="si">)</span>
131c131
&lt; pdc: simulator
<span class="nt">---</span>
<span class="o">&gt;</span> pdc: device simulator
154c154
&lt; 	<span class="si">$(</span>SIMCOMPILER<span class="si">)</span> <span class="si">$(</span>DYLIB_FLAGS<span class="si">)</span> <span class="nt">-lm</span> <span class="nt">-DTARGET_SIMULATOR</span><span class="o">=</span>1 <span class="nt">-DTARGET_EXTENSION</span><span class="o">=</span>1 <span class="si">$(</span>INCDIR<span class="si">)</span> <span class="nt">-o</span> <span class="si">$(</span>OBJDIR<span class="si">)</span>/pdex.<span class="k">${</span><span class="nv">DYLIB_EXT</span><span class="k">}</span> <span class="si">$(</span>SRC<span class="si">)</span>
<span class="nt">---</span>
<span class="o">&gt;</span> 	<span class="si">$(</span>SIMCOMPILER<span class="si">)</span> <span class="si">$(</span>DYLIB_FLAGS<span class="si">)</span> <span class="nt">-lm</span> <span class="nt">-DTARGET_SIMULATOR</span><span class="o">=</span>1 <span class="nt">-DTARGET_EXTENSION</span><span class="o">=</span>1 <span class="si">$(</span>INCDIR<span class="si">)</span> <span class="nt">-o</span> <span class="si">$(</span>OBJDIR<span class="si">)</span>/pdex.<span class="k">${</span><span class="nv">DYLIB_EXT</span><span class="k">}</span> <span class="si">$(</span>SRC<span class="si">)</span> <span class="si">$(</span>ASRC_SIMULATOR<span class="si">)</span></code></pre></figure>

<p>Rebuild the project.
If all goes well, the diagonal distance should be calculated and updated in
realtime.
The program will automatically resize the rectangle if the crank is docked.
Undock the crank for manual control and verification.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">PRODUCT</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span><span class="nb">cat </span>Source/pdxinfo | <span class="nb">grep </span>name | <span class="nb">cut</span> <span class="nt">-d</span> <span class="s2">"="</span> <span class="nt">-f</span> 2-<span class="si">)</span><span class="s2">.pdx"</span>
make
playdate_simulator <span class="s2">"</span><span class="k">${</span><span class="nv">PRODUCT</span><span class="k">}</span><span class="s2">"</span></code></pre></figure>

<h4 id="running-on-hardware">Running on Hardware</h4>

<p>To upload a PDX file to hardware, first, run it in the simulator.
Then either “Upload Game to Device” from the “Device” menu or Playdate icon on
the lower lefthand corner of the simulator (with the crank controls collapsed).
Once the game is on the device, <strong>pdutil</strong> can be used to launch it.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c"># after the game is on the device</span>
<span class="nv">PRODUCT</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span><span class="nb">cat </span>Source/pdxinfo | <span class="nb">grep </span>name | <span class="nb">cut</span> <span class="nt">-d</span> <span class="s2">"="</span> <span class="nt">-f</span> 2-<span class="si">)</span><span class="s2">.pdx"</span>
<span class="nv">PDUTIL_DEVICE</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span><span class="nb">ls</span> /dev/cu.usbmodemPD<span class="k">*</span> | <span class="nb">head</span> <span class="nt">-n</span> 1<span class="si">)</span><span class="s2">"</span>
pdutil <span class="s2">"</span><span class="k">${</span><span class="nv">PDUTIL_DEVICE</span><span class="k">}</span><span class="s2">"</span> run <span class="s2">"/Games/</span><span class="k">${</span><span class="nv">PRODUCT</span><span class="k">}</span><span class="s2">"</span></code></pre></figure>

<p>Alternatively, <strong>pdutil</strong> can be used to directly upload the game to hardware
from the command line.
Note that the author could not get the following code to reliably work in
a script because it takes time for the Playdate to become available after
it is mounted in datadisk mode.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">PDUTIL_DEVICE</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span><span class="nb">ls</span> /dev/cu.usbmodemPD<span class="k">*</span> | <span class="nb">head</span> <span class="nt">-n</span> 1<span class="si">)</span><span class="s2">"</span>
<span class="nv">PRODUCT</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span><span class="nb">cat </span>Source/pdxinfo | <span class="nb">grep </span>name | <span class="nb">cut</span> <span class="nt">-d</span> <span class="s2">"="</span> <span class="nt">-f</span> 2-<span class="si">)</span><span class="s2">.pdx"</span>
<span class="nv">DEVICE_PRODUCT_PATH</span><span class="o">=</span><span class="s2">"/Volumes/PLAYDATE/Games/</span><span class="k">${</span><span class="nv">PRODUCT</span><span class="k">}</span><span class="s2">"</span>
make device
pdc Source <span class="s2">"</span><span class="k">${</span><span class="nv">PRODUCT</span><span class="k">}</span><span class="s2">"</span>
pdutil <span class="s2">"</span><span class="k">${</span><span class="nv">PDUTIL_DEVICE</span><span class="k">}</span><span class="s2">"</span> datadisk
<span class="c"># it may take time for the device to become available</span>
<span class="nb">cp</span> <span class="nt">-r</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PRODUCT</span><span class="k">}</span><span class="s2">"</span> <span class="s2">"</span><span class="k">${</span><span class="nv">DEVICE_PRODUCT_PATH</span><span class="k">}</span><span class="s2">"</span>
<span class="nv">MOUNT_DEVICE</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span>diskutil list | <span class="nb">grep </span>PLAYDATE | <span class="nb">grep</span> <span class="nt">-oE</span> <span class="s1">'[^ ]+$'</span><span class="si">)</span><span class="s2">"</span>
diskutil unmount <span class="s2">"</span><span class="k">${</span><span class="nv">MOUNT_DEVICE</span><span class="k">}</span><span class="s2">"</span>
<span class="c"># press "A" to get the Playdate out of datadisk mode</span></code></pre></figure>

<h4 id="verification">Verification</h4>

<p>Pull out the crank and use the D-pad to move the lower righthand point to
(100, 75).  The diagonal length should be 125.000.  Use a calculator to
verify other sets of values.</p>

<figure class="highlight"><pre><code class="language-c" data-lang="c">  <span class="mi">100</span> <span class="o">*</span>  <span class="mi">100</span> <span class="o">=</span> <span class="mi">10000</span>
   <span class="mi">75</span> <span class="o">*</span>   <span class="mi">75</span> <span class="o">=</span>  <span class="mi">5625</span>
  <span class="mi">125</span> <span class="o">*</span>  <span class="mi">125</span> <span class="o">=</span> <span class="mi">15625</span>
<span class="o">--------------------</span>
<span class="mi">10000</span> <span class="o">+</span> <span class="mi">5625</span> <span class="o">=</span> <span class="mi">15625</span></code></pre></figure>

<h4 id="more-information">More Information</h4>

<p>The <a href="https://developer.arm.com/documentation/ddi0489/f/introduction/about-the-cortex-m7-processor/features">ARM Cortext-M7 Processor Technical Reference Manual</a> gives
detailed information on the processor used in the Playdate.  Instruction
references for <a href="https://users.ece.utexas.edu/~valvano/Volume1/QuickReferenceCard.pdf">ARM+Thumb ASM</a>,
<a href="https://courses.cs.washington.edu/courses/cse469/19wi/arm64.pdf">64-bit ARM ASM</a>, and <a href="https://www.felixcloutier.com/x86/">X86+AMD64 ASM</a> may be
useful.</p>

<p>This post is based on the
“<a href="https://devforum.play.date/t/adding-asm-s-file-to-a-project/3804/2">Adding ASM .s file to a project</a>” thread on the
<a href="https://devforum.play.date/">Playdate Developer Forums</a>.</p>

<h2 id="references">References:</h2>

<ul>
  <li><a href="https://courses.cs.washington.edu/courses/cse469/19wi/arm64.pdf">ARMv8 A64 Quick Reference</a></li>
  <li><a href="https://developer.arm.com/documentation/ddi0489/f/introduction/about-the-cortex-m7-processor/features">ARM Cortext-M7 Processor Technical Reference Manual</a></li>
  <li><a href="https://users.ece.utexas.edu/~valvano/Volume1/QuickReferenceCard.pdf">ARM and Thumb-2 Instruction Set Quick Reference Card</a></li>
  <li><a href="https://devforum.play.date/">Playdate Developer Forum</a></li>
  <li><a href="https://devforum.play.date/t/adding-asm-s-file-to-a-project/3804/2">Playdate Developer Forum, Adding ASM .s file to a project</a></li>
  <li><a href="https://play.date/">Playdate Homepage</a></li>
  <li><a href="https://www.felixcloutier.com/x86/">X86 and AMD64 Instruction Reference</a></li>
</ul>]]></content><author><name>Brendan Sechter</name></author><category term="gamedev" /><category term="playdate" /><category term="asm" /><category term="arm" /><category term="x86" /></entry><entry><title type="html">Getting Started with Rust on Playdate</title><link href="https://sgeos.github.io/gamedev/playdate/rust/2022/10/04/getting-started-with-rust-on-playdate.html" rel="alternate" type="text/html" title="Getting Started with Rust on Playdate" /><published>2022-10-04T18:23:42+00:00</published><updated>2022-10-04T18:23:42+00:00</updated><id>https://sgeos.github.io/gamedev/playdate/rust/2022/10/04/getting-started-with-rust-on-playdate</id><content type="html" xml:base="https://sgeos.github.io/gamedev/playdate/rust/2022/10/04/getting-started-with-rust-on-playdate.html"><![CDATA[<!-- A56 -->
<script>console.log("A56");</script>

<p>The <a href="https://play.date/">Panic Playdate</a> is a tiny, just-for-fun indie game console.
<a href="https://www.rust-lang.org">Rust</a> is a drop-in replacement for C that is fast and memory safe.
This post will discuss getting started with the Panic Playdate using Rust.
It is a follow-up to my post on
<a href="/gamedev/playdate/c/c++/lua/2022/10/03/getting-started-with-playdate.html">getting started with Playdate</a>.</p>

<p><a href="https://github.com/pd-rs/crank">crank</a> and <a href="https://github.com/pd-rs/crankstart">crankstart</a> will be used because they are
working, existing solutions for using Rust to develop applications for the
Playdate.
The post will cover installation, running examples, as well as building and
running a couple of existing crankstart projects:
<a href="https://github.com/pd-rs/crankstart-klondike">Klondike solitaire</a>,
and <a href="https://github.com/bravely/nine_lives">Nine Lives</a>.</p>

<p>Familiarity with Rust and the command line are assumed.
It is also assumed that the <a href="https://download.panic.com/playdate_sdk/PlaydateSDK-latest.zip">Playdate SDK</a> has already
been installed from the <a href="https://play.date/dev/">Playdate Developer Page</a> and that Rust
has been installed with <a href="https://rustup.rs">rustup</a>.</p>

<h2 id="software-versions">Software Versions</h2>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">$ </span><span class="nb">date</span> <span class="nt">-u</span> <span class="s2">"+%Y-%m-%d %H:%M:%S +0000"</span>
2022-10-04 18:23:42 +0000
<span class="nv">$ </span><span class="nb">uname</span> <span class="nt">-vm</span>
Darwin Kernel Version 21.6.0: Mon Aug 22 20:19:52 PDT 2022<span class="p">;</span> root:xnu-8020.140.49~2/RELEASE_ARM64_T6000 arm64
<span class="nv">$ </span>ex <span class="nt">-s</span> +<span class="s1">'%s/&lt;[^&gt;].\{-}&gt;//ge'</span> +<span class="s1">'%s/\s\+//e'</span> +<span class="s1">'%norm J'</span> +<span class="s1">'g/^$/d'</span> +%p +q! /System/Library/CoreServices/SystemVersion.plist | <span class="nb">grep</span> <span class="nt">-E</span> <span class="s1">'ProductName|ProductVersion'</span> | <span class="nb">sed</span> <span class="s1">'s/^[^ ]* //g'</span> | <span class="nb">sed</span> <span class="s1">'N; s/\n/ /g'</span>
macOS 12.6
<span class="nv">$ </span><span class="nb">echo</span> <span class="s2">"</span><span class="k">${</span><span class="nv">SHELL</span><span class="k">}</span><span class="s2">"</span>
/bin/bash
<span class="nv">$ </span><span class="s2">"</span><span class="k">${</span><span class="nv">SHELL</span><span class="k">}</span><span class="s2">"</span> <span class="nt">--version</span>
GNU bash, version 3.2.57<span class="o">(</span>1<span class="o">)</span><span class="nt">-release</span> <span class="o">(</span>arm64-apple-darwin21<span class="o">)</span>
Copyright <span class="o">(</span>C<span class="o">)</span> 2007 Free Software Foundation, Inc.
<span class="nv">$ </span><span class="nb">cat</span> <span class="s2">"</span><span class="k">${</span><span class="nv">HOME</span><span class="k">}</span><span class="s2">/Developer/PlaydateSDK/VERSION.txt"</span>
1.12.3
<span class="nv">$ </span>cargo <span class="nt">--version</span>
cargo 1.66.0-nightly <span class="o">(</span>f5fed93ba 2022-09-27<span class="o">)</span></code></pre></figure>

<h2 id="instructions">Instructions</h2>

<h4 id="installation">Installation</h4>

<p>First, install <a href="https://github.com/pd-rs/crank">crank</a> according to the README.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">PROJECT_PATH</span><span class="o">=</span><span class="s2">"my_playdate_projects"</span>
<span class="nb">cd</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PROJECT_PATH</span><span class="k">}</span><span class="s2">"</span>
git clone git@github.com:pd-rs/crank.git
<span class="nb">cd </span>crank
cargo <span class="nb">install</span> <span class="nt">--path</span> <span class="nb">.</span> <span class="nt">--force</span></code></pre></figure>

<p>Next, clone the <a href="https://github.com/pd-rs/crankstart">crankstart</a> repository to run the examples.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">PROJECT_PATH</span><span class="o">=</span><span class="s2">"my_playdate_projects"</span>
<span class="nb">cd</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PROJECT_PATH</span><span class="k">}</span><span class="s2">"</span>
git clone git@github.com:pd-rs/crankstart.git</code></pre></figure>

<h4 id="running-examples">Running Examples</h4>

<p>The hello_world and life examples can simply be run.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">PROJECT</span><span class="o">=</span><span class="s2">"crankstart"</span>
<span class="nv">PROJECT_PATH</span><span class="o">=</span><span class="s2">"my_playdate_projects"</span>
<span class="nb">cd</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PROJECT_PATH</span><span class="k">}</span><span class="s2">/</span><span class="k">${</span><span class="nv">PROJECT</span><span class="k">}</span><span class="s2">"</span>
<span class="nv">EXAMPLE</span><span class="o">=</span><span class="s2">"hello_world"</span>
crank run <span class="nt">--release</span> <span class="nt">--example</span> <span class="s2">"</span><span class="k">${</span><span class="nv">EXAMPLE</span><span class="k">}</span><span class="s2">"</span>
<span class="nv">EXAMPLE</span><span class="o">=</span><span class="s2">"life"</span>
crank run <span class="nt">--release</span> <span class="nt">--example</span> <span class="s2">"</span><span class="k">${</span><span class="nv">EXAMPLE</span><span class="k">}</span><span class="s2">"</span></code></pre></figure>

<p>Assets from the C_API need to be copied in before running the sprite_game
example.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nb">cp</span> <span class="nt">-a</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PLAYDATE_SDK_PATH</span><span class="k">}</span><span class="s2">/C_API/Examples/Sprite Game/Source/images/."</span> sprite_game_images
<span class="nv">EXAMPLE</span><span class="o">=</span><span class="s2">"sprite_game"</span>
crank run <span class="nt">--release</span> <span class="nt">--example</span> <span class="s2">"</span><span class="k">${</span><span class="nv">EXAMPLE</span><span class="k">}</span><span class="s2">"</span></code></pre></figure>

<h4 id="klondike-solitaire">Klondike Solitaire</h4>

<p>The author of crank and crankstart wrote a
<a href="https://github.com/pd-rs/crankstart-klondike">Klondike solitaire</a> game in Rust.
Use the following commands to build and run it.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">PROJECT_PATH</span><span class="o">=</span><span class="s2">"my_playdate_projects"</span>
<span class="nb">cd</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PROJECT_PATH</span><span class="k">}</span><span class="s2">"</span>
git clone git@github.com:pd-rs/crankstart-klondike.git
<span class="nb">cd </span>crankstart-klondike
crank run <span class="nt">--release</span></code></pre></figure>

<h4 id="nine-lives">Nine Lives</h4>

<p>A third party, bravely, wrote the
<a href="https://github.com/bravely/nine_lives">Nine Lives</a> game in Rust.
It can be built and run as follows.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">PROJECT_PATH</span><span class="o">=</span><span class="s2">"my_playdate_projects"</span>
<span class="nb">cd</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PROJECT_PATH</span><span class="k">}</span><span class="s2">"</span>
git clone git@github.com:bravely/nine_lives.git
<span class="nb">cd </span>nine_lives
crank run <span class="nt">--release</span></code></pre></figure>

<h4 id="running-on-hardware">Running on Hardware</h4>

<p><a href="https://docs.rs/crate/cargo-xbuild/latest">cargo-xbuild</a> is required to build for hardware.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">cargo <span class="nb">install </span>cargo-xbuild</code></pre></figure>

<p>xbuild requires source to be installed, so it needs to be installed with
<a href="https://rustup.rs">rustup</a>.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh">rustup component add rust-src</code></pre></figure>

<p>The build subcommand takes a –device flag that can be used to target hardware
exclusively.
Alternatively, the package subcommand can be used to build binaries for both
the device and the simulator.
To upload a PDX file to hardware, first, run it in the simulator.
Then either “Upload Game to Device” from the “Device” menu or Playdate icon on
the lower lefthand corner of the simulator (with the crank controls collapsed).
Once the game is on the device, pdutil can be used to launch it.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c"># example (build)</span>
<span class="nv">EXAMPLE</span><span class="o">=</span><span class="s2">"life"</span>
<span class="nv">PDX_FILE</span><span class="o">=</span><span class="s2">"Life"</span>
<span class="nv">PROJECT</span><span class="o">=</span><span class="s2">"crankstart"</span>
<span class="nv">PROJECT_PATH</span><span class="o">=</span><span class="s2">"my_playdate_projects"</span>
<span class="nb">cd</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PROJECT_PATH</span><span class="k">}</span><span class="s2">/</span><span class="k">${</span><span class="nv">PROJECT</span><span class="k">}</span><span class="s2">"</span>
crank build <span class="nt">--device</span> <span class="nt">--release</span> <span class="nt">--example</span> <span class="s2">"</span><span class="k">${</span><span class="nv">EXAMPLE</span><span class="k">}</span><span class="s2">"</span>
playdate_simulator <span class="s2">"target/</span><span class="k">${</span><span class="nv">PDX_FILE</span><span class="k">}</span><span class="s2">.pdx"</span>
<span class="c"># from simulator: "Device &gt; Upload Game to Device</span>
<span class="c"># once game is on devce</span>
pdutil /dev/cu.usbmodemPD<span class="k">*</span> run <span class="s2">"/Games/</span><span class="k">${</span><span class="nv">PDX_FILE</span><span class="k">}</span><span class="s2">.pdx"</span>

<span class="c"># example (package)</span>
<span class="nv">EXAMPLE</span><span class="o">=</span><span class="s2">"hello_world"</span>
<span class="nv">PDX_FILE</span><span class="o">=</span><span class="s2">"Hello World"</span>
crank package <span class="nt">--example</span> <span class="s2">"</span><span class="k">${</span><span class="nv">EXAMPLE</span><span class="k">}</span><span class="s2">"</span>
playdate_simulator <span class="s2">"target/</span><span class="k">${</span><span class="nv">PDX_FILE</span><span class="k">}</span><span class="s2">.pdx"</span>
<span class="c"># from simulator: "Device &gt; Upload Game to Device</span>
<span class="c"># once game is on devce</span>
pdutil /dev/cu.usbmodemPD<span class="k">*</span> run <span class="s2">"/Games/</span><span class="k">${</span><span class="nv">PDX_FILE</span><span class="k">}</span><span class="s2">.pdx"</span>

<span class="c"># project (build)</span>
<span class="nv">PROJECT</span><span class="o">=</span><span class="s2">"crankstart-klondike"</span>
<span class="nv">PDX_FILE</span><span class="o">=</span><span class="s2">"Klondike"</span>
<span class="nv">PROJECT_PATH</span><span class="o">=</span><span class="s2">"my_playdate_projects"</span>
<span class="nb">cd</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PROJECT_PATH</span><span class="k">}</span><span class="s2">/</span><span class="k">${</span><span class="nv">PROJECT</span><span class="k">}</span><span class="s2">"</span>
crank build <span class="nt">--device</span> <span class="nt">--release</span>
playdate_simulator <span class="s2">"target/</span><span class="k">${</span><span class="nv">PDX_FILE</span><span class="k">}</span><span class="s2">.pdx"</span>
<span class="c"># from simulator: "Device" &gt; "Upload Game to Device"</span>
<span class="c"># once game is on devce</span>
pdutil /dev/cu.usbmodemPD<span class="k">*</span> run <span class="s2">"/Games/</span><span class="k">${</span><span class="nv">PDX_FILE</span><span class="k">}</span><span class="s2">.pdx"</span>

<span class="c"># project (package)</span>
<span class="nv">PROJECT</span><span class="o">=</span><span class="s2">"nine_lives"</span>
<span class="nv">PDX_FILE</span><span class="o">=</span><span class="s2">"Nine Lives"</span>
<span class="nv">PROJECT_PATH</span><span class="o">=</span><span class="s2">"my_playdate_projects"</span>
<span class="nb">cd</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PROJECT_PATH</span><span class="k">}</span><span class="s2">/</span><span class="k">${</span><span class="nv">PROJECT</span><span class="k">}</span><span class="s2">"</span>
crank package
playdate_simulator <span class="s2">"target/</span><span class="k">${</span><span class="nv">PDX_FILE</span><span class="k">}</span><span class="s2">.pdx"</span>
<span class="c"># from simulator: "Device" &gt; "Upload Game to Device"</span>
<span class="c"># once game is on devce</span>
pdutil /dev/cu.usbmodemPD<span class="k">*</span> run <span class="s2">"/Games/</span><span class="k">${</span><span class="nv">PDX_FILE</span><span class="k">}</span><span class="s2">.pdx"</span></code></pre></figure>

<p>The run subcommand should work like the build subcommand, but it is not working
on the author’s machice in the latest version of the SDK.  The latest version
of the SDK also seems to have trouble booting PDX files with spaces in the
filename using the simulator and pdutil, but the game can be launched on the
device after it has been uploaded.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c"># example</span>
<span class="nv">EXAMPLE</span><span class="o">=</span><span class="s2">"sprite_game"</span>
<span class="nv">PROJECT</span><span class="o">=</span><span class="s2">"crankstart"</span>
<span class="nv">PROJECT_PATH</span><span class="o">=</span><span class="s2">"my_playdate_projects"</span>
<span class="nb">cd</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PROJECT_PATH</span><span class="k">}</span><span class="s2">/</span><span class="k">${</span><span class="nv">PROJECT</span><span class="k">}</span><span class="s2">"</span>
crank run <span class="nt">--device</span> <span class="nt">--release</span> <span class="nt">--example</span> <span class="s2">"</span><span class="k">${</span><span class="nv">EXAMPLE</span><span class="k">}</span><span class="s2">"</span>

<span class="c"># project</span>
<span class="nv">PROJECT</span><span class="o">=</span><span class="s2">"nine_lives"</span>
<span class="nv">PROJECT_PATH</span><span class="o">=</span><span class="s2">"my_playdate_projects"</span>
<span class="nb">cd</span> <span class="s2">"</span><span class="k">${</span><span class="nv">PROJECT_PATH</span><span class="k">}</span><span class="s2">/</span><span class="k">${</span><span class="nv">PROJECT</span><span class="k">}</span><span class="s2">"</span>
crank run <span class="nt">--device</span> <span class="nt">--release</span></code></pre></figure>

<h4 id="more-information">More Information</h4>

<p>Note that the Playdate <a href="https://sdk.play.date/inside-playdate-with-c">C API documentation</a> (and to a
lesser extent, the <a href="https://sdk.play.date/Inside%20Playdate.html">Lua documentation</a>) are useful when
developing applications in Rust.
This post is based on the <a href="https://devforum.play.date/t/rust-development-thread/3999">Rust development thread</a>
from the <a href="https://devforum.play.date">Playdate Developer Forum</a>.</p>

<h2 id="references">References:</h2>

<ul>
  <li><a href="https://github.com/pd-rs/crank">crank, cargo wrapper for building and running Playdate software</a></li>
  <li><a href="https://github.com/pd-rs/crankstart-klondike">crankstart, Klondike Solitaire</a></li>
  <li><a href="https://github.com/pd-rs/crankstart">crankstart, Rust crate for Playdate application development</a></li>
  <li><a href="https://github.com/bravely/nine_lives">crankstart, Nine Lives</a></li>
  <li><a href="https://sdk.play.date/inside-playdate-with-c">Playdate C API Documentation</a></li>
  <li><a href="https://devforum.play.date">Playdate Developer Forum</a></li>
  <li><a href="https://devforum.play.date/t/rust-development-thread/3999">Playdate Developer Forum, Rust Development Thread</a></li>
  <li><a href="https://play.date/dev/">Playdate Developer Page</a></li>
  <li><a href="https://play.date/">Playdate Homepage</a></li>
  <li><a href="https://sdk.play.date/Inside%20Playdate.html">Playdate Lua Documentation</a></li>
  <li><a href="/gamedev/playdate/c/c++/lua/2022/10/03/getting-started-with-playdate.html">Playdate, Getting Started with</a></li>
  <li><a href="https://download.panic.com/playdate_sdk/PlaydateSDK-latest.zip">Playdate SDK, Latest</a></li>
  <li><a href="https://www.rust-lang.org">Rust Homepage</a></li>
  <li><a href="https://rustup.rs">Rust, rustup</a></li>
  <li><a href="https://docs.rs/crate/cargo-xbuild/latest">Rust, cargo-xbuild</a></li>
</ul>]]></content><author><name>Brendan Sechter</name></author><category term="gamedev" /><category term="playdate" /><category term="rust" /></entry><entry><title type="html">Getting Started with Playdate</title><link href="https://sgeos.github.io/gamedev/playdate/c/c++/lua/2022/10/03/getting-started-with-playdate.html" rel="alternate" type="text/html" title="Getting Started with Playdate" /><published>2022-10-03T20:21:04+00:00</published><updated>2022-10-03T20:21:04+00:00</updated><id>https://sgeos.github.io/gamedev/playdate/c/c++/lua/2022/10/03/getting-started-with-playdate</id><content type="html" xml:base="https://sgeos.github.io/gamedev/playdate/c/c++/lua/2022/10/03/getting-started-with-playdate.html"><![CDATA[<!-- A55 -->
<script>console.log("A55");</script>

<p>The <a href="https://play.date/">Panic Playdate</a> is a tiny, just-for-fun indie game console.
This post will discuss getting started with the Panic Playdate.
It will cover downloading the <a href="https://download.panic.com/playdate_sdk/PlaydateSDK-latest.zip">SDK</a>, installing it,
and running the examples.
macOS will be used, but the steps should be largely the same for other
platforms.
This post assumes familiarity with the command line.</p>

<p>Note that command line familiarity is not needed for Playdate development.
Downloading the SDK is enough to get started if Pulp is sufficient for your
needs.
For most developers, <a href="https://www.lua.org">Lua</a> knowledge and a Playdate-integrated editor,
like <a href="https://nova.app">Nova</a> (link to <a href="https://extensions.panic.com/extensions/com.panic/com.panic.Playdate/">Playdate Extensions</a>), will be
enough to make their Playdate development dreams come true.
(Dedication to see a project to completion is also required.)</p>

<h2 id="software-versions">Software Versions</h2>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">$ </span><span class="nb">date</span> <span class="nt">-u</span> <span class="s2">"+%Y-%m-%d %H:%M:%S +0000"</span>
2022-10-03 20:21:04 +0000
<span class="nv">$ </span><span class="nb">uname</span> <span class="nt">-vm</span>
Darwin Kernel Version 21.6.0: Mon Aug 22 20:19:52 PDT 2022<span class="p">;</span> root:xnu-8020.140.49~2/RELEASE_ARM64_T6000 arm64
<span class="nv">$ </span>ex <span class="nt">-s</span> +<span class="s1">'%s/&lt;[^&gt;].\{-}&gt;//ge'</span> +<span class="s1">'%s/\s\+//e'</span> +<span class="s1">'%norm J'</span> +<span class="s1">'g/^$/d'</span> +%p +q! /System/Library/CoreServices/SystemVersion.plist | <span class="nb">grep</span> <span class="nt">-E</span> <span class="s1">'ProductName|ProductVersion'</span> | <span class="nb">sed</span> <span class="s1">'s/^[^ ]* //g'</span> | <span class="nb">sed</span> <span class="s1">'N; s/\n/ /g'</span>
macOS 12.6
<span class="nv">$ </span><span class="nb">echo</span> <span class="s2">"</span><span class="k">${</span><span class="nv">SHELL</span><span class="k">}</span><span class="s2">"</span>
/bin/bash
<span class="nv">$ </span><span class="s2">"</span><span class="k">${</span><span class="nv">SHELL</span><span class="k">}</span><span class="s2">"</span> <span class="nt">--version</span>
GNU bash, version 3.2.57<span class="o">(</span>1<span class="o">)</span><span class="nt">-release</span> <span class="o">(</span>arm64-apple-darwin21<span class="o">)</span>
Copyright <span class="o">(</span>C<span class="o">)</span> 2007 Free Software Foundation, Inc.
<span class="nv">$ </span><span class="nb">cat</span> <span class="s2">"</span><span class="k">${</span><span class="nv">HOME</span><span class="k">}</span><span class="s2">/Developer/PlaydateSDK/VERSION.txt"</span>
1.12.3</code></pre></figure>

<h2 id="instructions">Instructions</h2>

<h4 id="installation">Installation</h4>

<p>First, download the <a href="https://download.panic.com/playdate_sdk/PlaydateSDK-latest.zip">latest Playdate SDK</a>
(link to <a href="https://download.panic.com/playdate_sdk/">all versions</a>) from the
<a href="https://play.date/dev/">Playdate Developer Page</a>.
Update the path, set the SDK path, and consider adding an alias for the
Playdate simulator.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c"># update path</span>
<span class="nb">echo</span> <span class="s1">'export PATH="${HOME}/Developer/PlaydateSDK/bin:${PATH}"'</span> <span class="o">&gt;&gt;</span> <span class="s2">"</span><span class="k">${</span><span class="nv">HOME</span><span class="k">}</span><span class="s2">/.profile"</span>

<span class="c"># set the Playdate SDK path</span>
<span class="nb">echo</span> <span class="s1">'export PLAYDATE_SDK_PATH="${HOME}/Developer/PlaydateSDK"'</span> <span class="o">&gt;&gt;</span> <span class="s2">"</span><span class="k">${</span><span class="nv">HOME</span><span class="k">}</span><span class="s2">/.bash_profile"</span>

<span class="c"># add alias for simulator</span>
<span class="nb">echo</span> <span class="s1">'alias playdate_simulator="open ${HOME}/Developer/PlaydateSDK/bin/Playdate\ Simulator.app"'</span> <span class="o">&gt;&gt;</span> <span class="s2">"</span><span class="k">${</span><span class="nv">HOME</span><span class="k">}</span><span class="s2">/.bash_profile"</span>

<span class="c"># reload profiles, if necessary</span>
<span class="nb">.</span> <span class="s2">"</span><span class="k">${</span><span class="nv">HOME</span><span class="k">}</span><span class="s2">/.profile"</span>
<span class="nb">.</span> <span class="s2">"</span><span class="k">${</span><span class="nv">HOME</span><span class="k">}</span><span class="s2">/.bash_profile"</span></code></pre></figure>

<h4 id="pulp">Pulp</h4>

<p><a href="https://play.date/pulp/">Pulp</a> is a web-based game editor for the Playdate.
The target audience is entry-level game developers and people who want to
rapidly prototype.
All development and testing can be done in the browser.
Pulp has a lot of limitations, but it is also
<a href="https://play.date/dev/links/#devLinksCategorySDKPulp">easy to learn</a>!
(Links to documentation for <a href="https://play.date/pulp/docs/">Pulp</a> and
<a href="https://play.date/pulp/docs/pulpscript/">PulpScript</a>.)
Pulp is a good choice for people who are not ready to learn to program.</p>

<p>Entire games can be exported and imported in JSON format.
To run a game developed with Pulp in the simulator, click the “DOWNLOAD PDX”
button, and then pass the PDX file to the simulator.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">GAME</span><span class="o">=</span><span class="s2">"my-game"</span>
<span class="nv">GAME_PATH</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">HOME</span><span class="k">}</span><span class="s2">/Downloads"</span>
playdate_simulator <span class="s2">"</span><span class="k">${</span><span class="nv">GAME_PATH</span><span class="k">}</span><span class="s2">/</span><span class="k">${</span><span class="nv">GAME</span><span class="k">}</span><span class="s2">.pdx"</span></code></pre></figure>

<h4 id="lua-examples">Lua Examples</h4>

<p>The <a href="https://www.lua.org">Lua</a> scripting language can also be used to develop Playdate games.
The target audience is moderately sophisticated developers.
Lua is a popular scripting language commonly used in game development,
and the Lua API is not as restricted as Pulp’s web-based development model.
(Link to <a href="https://sdk.play.date/Inside%20Playdate.html#developing-in-lua">Lua documentation</a>.)
Lua is generally the best choice for projects with a programmer.</p>

<p>Regular project-based Lua examples can be run as follows.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c"># cd to location of examples directory</span>
<span class="nb">cd</span> <span class="s2">"</span><span class="k">${</span><span class="nv">HOME</span><span class="k">}</span><span class="s2">/Developer/PlaydateSDK/Examples/"</span>

<span class="c"># run the "2020" exmaple</span>
<span class="nv">EXAMPLE</span><span class="o">=</span><span class="s2">"2020"</span>
pdc <span class="s2">"</span><span class="k">${</span><span class="nv">EXAMPLE</span><span class="k">}</span><span class="s2">/Source"</span> <span class="s2">"</span><span class="k">${</span><span class="nv">EXAMPLE</span><span class="k">}</span><span class="s2">.pdx"</span>
playdate_simulator <span class="s2">"</span><span class="k">${</span><span class="nv">EXAMPLE</span><span class="k">}</span><span class="s2">"</span>.pdx

<span class="c"># cleanup file when no longer needed</span>
<span class="nb">rm</span> <span class="nt">-rf</span> <span class="s2">"</span><span class="k">${</span><span class="nv">EXAMPLE</span><span class="k">}</span><span class="s2">.pdx"</span></code></pre></figure>

<p>Single file Lua examples can be run as follows.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c"># cd to location of examples directory</span>
<span class="nb">cd</span> <span class="s2">"</span><span class="k">${</span><span class="nv">HOME</span><span class="k">}</span><span class="s2">/Developer/PlaydateSDK/Examples/"</span>

<span class="c"># run the "animator" exmaple</span>
<span class="nv">EXAMPLE</span><span class="o">=</span><span class="s2">"animator"</span>
pdc <span class="nt">-m</span> <span class="s2">"Single File Examples/</span><span class="k">${</span><span class="nv">EXAMPLE</span><span class="k">}</span><span class="s2">.lua"</span> <span class="s2">"</span><span class="k">${</span><span class="nv">EXAMPLE</span><span class="k">}</span><span class="s2">.pdx"</span>
playdate_simulator <span class="s2">"</span><span class="k">${</span><span class="nv">EXAMPLE</span><span class="k">}</span><span class="s2">.pdx"</span>

<span class="c"># cleanup file when no longer needed</span>
<span class="nb">rm</span> <span class="nt">-rf</span> <span class="s2">"</span><span class="k">${</span><span class="nv">EXAMPLE</span><span class="k">}</span><span class="s2">.pdx"</span></code></pre></figure>

<h4 id="c-api-examples">C API Examples</h4>

<p>Sophisticated developers can use the C API to make software for the Playdate.
Many professional AAA game developers use C++, but C is the language they used
in the past.
(Link to <a href="https://sdk.play.date/inside-playdate-with-c">C API documentation</a>.)
Only use the C API if you know why you need to use it.</p>

<p>The C API examples can be run as follows.</p>

<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c"># cd to location of examples directory</span>
<span class="nb">cd</span> <span class="s2">"</span><span class="k">${</span><span class="nv">HOME</span><span class="k">}</span><span class="s2">/Developer/PlaydateSDK/C_API/Examples/"</span>

<span class="c"># run the "3D library" exmaple</span>
<span class="nv">EXAMPLE</span><span class="o">=</span><span class="s2">"3D library"</span>
<span class="o">(</span><span class="nb">cd</span> <span class="s2">"./</span><span class="k">${</span><span class="nv">EXAMPLE</span><span class="k">}</span><span class="s2">"</span><span class="p">;</span> make<span class="p">;</span> playdate_simulator <span class="k">*</span>.pdx<span class="o">)</span>

<span class="c"># cleanup file when no longer needed</span>
<span class="o">(</span><span class="nb">cd</span> <span class="s2">"./</span><span class="k">${</span><span class="nv">EXAMPLE</span><span class="k">}</span><span class="s2">"</span><span class="p">;</span> make clean<span class="o">)</span></code></pre></figure>

<h4 id="more-information">More Information</h4>

<p>Consider visiting the <a href="https://play.date/dev/">Playdate Developer Page</a> and
<a href="https://devforum.play.date">Playdate Developer Forums</a> for more information!</p>

<h2 id="references">References:</h2>

<ul>
  <li><a href="https://www.lua.org">Lua Home Page</a></li>
  <li><a href="https://nova.app">Nova Homepage</a></li>
  <li><a href="https://extensions.panic.com/extensions/com.panic/com.panic.Playdate/">Nova, Playdate Extensions</a></li>
  <li><a href="https://sdk.play.date/inside-playdate-with-c">Playdate C API Documentation</a></li>
  <li><a href="https://devforum.play.date">Playdate Developer Forum</a></li>
  <li><a href="https://play.date/dev/">Playdate Developer Page</a></li>
  <li><a href="https://play.date/">Playdate Homepage</a></li>
  <li><a href="https://sdk.play.date/Inside%20Playdate.html#developing-in-lua">Playdate Lua Documentation</a></li>
  <li><a href="https://play.date/pulp/">Playdate Pulp</a></li>
  <li><a href="https://play.date/pulp/docs/">Playdate Pulp Documentation</a></li>
  <li><a href="https://play.date/dev/links/#devLinksCategorySDKPulp">Playdate Pulp Tutorials</a></li>
  <li><a href="https://play.date/pulp/docs/pulpscript/">Playdate PulpScript Documentation</a></li>
  <li><a href="https://download.panic.com/playdate_sdk/">Playdate SDK, All Downloads</a></li>
  <li><a href="https://download.panic.com/playdate_sdk/PlaydateSDK-latest.zip">Playdate SDK, Latest</a></li>
</ul>]]></content><author><name>Brendan Sechter</name></author><category term="gamedev" /><category term="playdate" /><category term="c" /><category term="c++" /><category term="lua" /></entry></feed>