{"id":516,"date":"2019-04-07T13:46:10","date_gmt":"2019-04-07T11:46:10","guid":{"rendered":"https:\/\/next-hack.com\/?p=516"},"modified":"2020-06-05T21:16:40","modified_gmt":"2020-06-05T19:16:40","slug":"lets-build-an-handheld-platform-game-with-a-cortex-m0-microcontroller","status":"publish","type":"post","link":"https:\/\/next-hack.com\/index.php\/2019\/04\/07\/lets-build-an-handheld-platform-game-with-a-cortex-m0-microcontroller\/","title":{"rendered":"Let&#8217;s build a handheld 40-fps platform game with a Cortex M0+ microcontroller!"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">Introduction<\/h2>\n\n\n\n<p><em>(tl;dr. source code and KiCAD project at the end of this page!)<\/em><\/p>\n\n\n\n<p>Although we were born in the 8 bit era, our first computer was an Amiga 500. It was a glorious 16-bit machine, and it featured stunning graphics and sound, making it well suited for gaming.<br>On this computer, a very popular genre of games was the platform. Many of them were very colorful, featuring a very fluid parallax scrolling. This was achieved by talented programmers that ingeniously used the Amiga&#8217;s coprocessors to increase the number of on-screen colors. Take a look for instance at LionHeart!<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"560\" height=\"416\" src=\"https:\/\/next-hack.com\/wp-content\/uploads\/2019\/04\/lionheart.png\" alt=\"\" class=\"wp-image-527\"\/><figcaption>Lionheart on Amiga. This static image does not give honor to the beauty of the graphics.<\/figcaption><\/figure>\n\n\n\n<p>Electronics has changed a\nlot since the 90s, and now there are plenty of small microcontrollers that\nallow you to create very spectacular things.<\/p>\n\n\n\n<p>We always loved platform\ngames, and now with few bucks one can buy a Raspberry Zero, install Linux, and\nprogram a colorful platformer \u201cquite easily\u201d. <\/p>\n\n\n\n<p>That&#8217;s not a job for us,\nit&#8217;s like using a nuke to kill a mosquito!<\/p>\n\n\n\n<p> We want to use microcontrollers, with limited memory, not powerful system on a chip with built-in GPU! In other words: we want some challenge!<\/p>\n\n\n\n<p>Speaking about video capabilities, some people managed to squeeze every drop out of an AVR microcontroller with some projects (like the Uzebox project or Craft by lft). However, to achieve this, AVR microcontrollers force you to write in assembly, and even if there are some examples of very nice games, you\u2019ll face some heavy constraints, that won\u2019t allow to get a 16-bit style game. <\/p>\n\n\n\n<p>Instead, we would like to use\nsome more balanced microcontroller\/board, that allowed us to code entirely in\nC. <\/p>\n\n\n\n<p>Not as powerful as an Arduino\nDue but not as limited as an Arduino Uno. Interestingly enough, \u201cDue\u201d means 2,\nand \u201cUno\u201d means 1. And just like Microsoft taught us how to count (1, 2, 3, 95,\n98, ME, 2000, XP, Vista, 7, 8, 10), Arduino cannot be any different! We will\nuse Arduino Zero as intermediate between 1 and 2! <\/p>\n\n\n\n<p>Yes, 1 &lt; 0 &lt; 2,\naccording to Arduino \ud83d\ude42<\/p>\n\n\n\n<p>In particular, what we are\ninterested in, is not the board itself. Instead, we are considering its processor\nseries. The Arduino Zero mounts an ATSAMD21 series microcontroller, with a\n48MHz Cortex M0+, 256kB of flash, and 32kB of RAM.<\/p>\n\n\n\n<p>While a 48MHz Cortex M0+ could largely outperform an old 7 MHz MC68000, the Amiga 500 had 512kB of RAM, hardware sprites, built in dual playfield, the Blitter (a DMA based block image transfer engine, with built in pixel exact collision detection system and transparency) and the Copper (a raster based coprocessor, which allowed to perform register operations based on the raster position, to create a lot of very nice effects).<br> In the SAMD21 all this hardware is missing (except a rather simple DMA, at least with respect to Amiga\u2019s Blitter), therefore a lot of drawing stuff will be performed in software.<br><br>We would like to achieve these features:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>160 x 128 pixel using an SPI 1.8\u201d display.<\/li><li> 16 bits per pixel graphics;<\/li><li> As many fps as possible. At least 25 fps with a 12 MHz SPI clock, or 40 fps at 24 MHz;<\/li><li> dual playfield with parallax scrolling;<\/li><li> all in C. No asm code;<\/li><li> pixel exact collision detection;<\/li><li> on screen overlay.<\/li><\/ul>\n\n\n\n<p>These sounds like rather hard to achieve goals. <br>And they are, especially if asm code is ruled out!<\/p>\n\n\n\n<p>For instance&nbsp;a 160&#215;128 pixel screen takes 40kB for frame buffer at 16 bit, but we only have 32kB of RAM! And we also want dual playfield parallax scrolling, and a lot of other stuffs, at 25\/40 fps at least! <\/p>\n\n\n\n<p>But we are next-hack here,\naren&#8217;t we?<\/p>\n\n\n\n<p>We will use some tricks and embedded ATSAMD21 features!<br> <br>As for the hardware we will use <a rel=\"noreferrer noopener\" aria-label=\" (opens in a new tab)\" href=\"https:\/\/shop.itaca-innovation.com\" target=\"_blank\">uChip<\/a>, which is available on the<a rel=\"noreferrer noopener\" aria-label=\" Itaca Store (opens in a new tab)\" href=\"https:\/\/shop.itaca-innovation.com\" target=\"_blank\"> Itaca Store<\/a> at <a href=\"https:\/\/shop.itaca-innovation.com\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"https:\/\/shop.itaca-innovation.com (opens in a new tab)\">https:\/\/shop.itaca-innovation.com<\/a>. <\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"952\" height=\"537\" src=\"https:\/\/next-hack.com\/wp-content\/uploads\/2019\/03\/bestUchipReduced.jpg\" alt=\"\" class=\"wp-image-476\"\/><figcaption>uChip: the heart of our project!<\/figcaption><\/figure>\n\n\n\n<p>It has the same specs of the Arduino Zero, but it is much smaller, not considering it is also cheaper than an original Arduino Zero (yes, you can buy counterfeit Arduino Zero for 10$ in AliExpress\u2026 but we like to stick to original one) . This will allow us to make a small hand held console. You can adapt this project with almost no effort to Arduino Zero. You\u2019ll just have a bulky result though.<\/p>\n\n\n\n<p>We also created a small test board, which implements a poor man&#8217;s hand held console. Details are shown below!<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1076\" height=\"625\" src=\"https:\/\/next-hack.com\/wp-content\/uploads\/2019\/04\/uChipPlayNotMounted.jpg\" alt=\"\" class=\"wp-image-545\"\/><\/figure>\n\n\n\n<p>We won&#8217;t be using the Arduino framework though. It\u2019s not well suited when it comes to optimization and hardware control. (And let&#8217;s not talk about the IDE!)<\/p>\n\n\n\n<p>In this series, we will see\nhow we arrived to the final version of the current game, and we will describe\nall the optimization and the criteria we used. The game itself is not finished,\nand it lacks sound, levels, etc. However this can be still used as a starting\npoint for many types of game!<\/p>\n\n\n\n<p>Furthermore, there is room for\na lot of optimization, even without assembly!<\/p>\n\n\n\n<p>So, let&#8217;s start our journey!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Challenges<\/strong><\/h2>\n\n\n\n<p>There are essentially two\nchallenges on this project. Timing and memory (both RAM and storage).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Memory<\/strong><\/h3>\n\n\n\n<p> Let&#8217;s consider the memory first.<\/p>\n\n\n\n<p>First of all, instead of storing a huge picture of the level, we will use tiles. In fact, if you closely analyze most of the platform games, you&#8217;ll notice that these are made of a small quantity of graphics element (tiles) repeated many times.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"772\" height=\"574\" src=\"https:\/\/next-hack.com\/wp-content\/uploads\/2019\/04\/Turrican.png\" alt=\"\" class=\"wp-image-521\"\/><figcaption>Turrican 2 on Amiga. One of the best platform games. Ever. You can easily see the tiles here!<\/figcaption><\/figure>\n\n\n\n<p>The world\/level appears as\nvaried because of the different tile combinations. This saves a lot of storage\nmemory, but it does not solve the problem of the huge frame buffer.<\/p>\n\n\n\n<p>The second trick we will be\nusing is that our uC has both a quite amount of processing power and it has DMA\ntoo! Therefore, instead of storing all the frame data in RAM (why should we,\nanyway?), we will be create the scene from scratch each frame. In particular we\nwill still use buffers, but just large enough to contain a single 16-pixel tall\nhorizontal block of graphics data.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Timing &#8211; CPU<\/strong><\/h3>\n\n\n\n<p>When an engineer has to\nmake something, the first step is to check if something is feasible. Of course,\nwe did this check at the beginning! <\/p>\n\n\n\n<p>Well, we want at least 25\nfps on a 160&#215;128 pixel display. This means 512000 pixels\/s. Since our\nmicrocontroller runs at 48 MHz, we have at least 93 clock cycles per pixel. This\namount of cycles reduces to 58 if we want to go at 40 fps.<\/p>\n\n\n\n<p>Actually, our\nmicrocontroller can handle up to 2 pixels per time, because each pixel is 16\nbit, but the ATSAMD21 has a 32 bit internal bus, so the figures could be\nbetter!<\/p>\n\n\n\n<p>93 clock cycles per pixel\nsuggests us that actually the task is quite feasible! In fact, we might also\nconclude that the CPU alone might be enough to perform all the drawing tasks,\nwithout DMA. This is probably true, especially with assembly. However, the code\nwould be extremely difficult to handle. And in C it should be very optimized! A\nCortex M0+, in fact, is not as C-friendly as a Cortex M3, and it lacks many\ninstructions (even load\/store with post\/pre increment\/decrement are missing!),\nwhich have to be implemented with two or more simpler ones.<\/p>\n\n\n\n<p>Let\u2019s check what we need to do, to draw the two playfields (assuming we already know the x and y coordinates, etc).<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Compute the location of the foreground pixel in the flash memory.<\/li><li>Get the pixel value.<\/li><li>If it is transparent, then compute the position of the background pixel in the flash memory.<\/li><li>Get the pixel value.<\/li><li>Compute the destination location.<\/li><li>Store the pixel in the buffer.<\/li><\/ul>\n\n\n\n<p>Furthermore, these\noperations must be performed for each sprite that might appear in the buffer:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Compute the position of the sprite pixel in the flash memory.<\/li><li>Get the pixel value.<\/li><li>If it is not transparent, then compute the location of the destination buffer.<\/li><li>Store the pixel in the buffer.<\/li><\/ul>\n\n\n\n<p>Not only all these\noperations are not implemented as a single ASM instruction, but also, each ASM instruction\ntakes two cycles when accessing RAM\/Flash memory.<\/p>\n\n\n\n<p>Moreover, we are also missing game logic (which, luckily takes a negligible amount of time as they are calculated once per frame), collision detection, buffer handling, and the instructions needed to send the data via SPI.<\/p>\n\n\n\n<p>For instance, here is what\nwe would have to do in pseudo code (assuming no scrolling and a fixed color\nbackground playfield for now!) just for the foreground.<\/p>\n\n\n\n<p>Let cameraY and cameraX be\nthe coordinates of the top left corner of the display in the game world.<\/p>\n\n\n\n<p>Let xTilepos and yTilepos be the position of the current tile in the map.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">xTilepos = cameraX \/ 16; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \/\/ this is  a rightward shift of 4 bits.<br>yTilepos = cameraY \/ 16;<br>destBufferAddress = &amp;buffer[0][0];<br>for tile = 0...9 <br>    nTile = gameMap[yTilepos][xTilepos];<br>    tileDataAddress = &amp;tileData[nTile];<br>    xTilepos = xTilepos + 1;<br>    for y = 0\u202615<br>        for x = 0\u202615<br>            pixel = *tileDataAddress;<br>            tileDataAddress&nbsp; = tileDataAddress&nbsp; + 1;<br>            *destBufferAddress = pixel;<br>            destBufferAddress = destBufferAddress + 1; <br>        next<br>        destBufferAddress = destBufferAddress + 144;&nbsp;&nbsp;&nbsp;&nbsp; \/\/ point to next row <br>    next<br>    destBufferAddress = destBufferAddress \u2013 ( 160 * 16&nbsp; - 16);&nbsp; \/\/ now point to the position where the next tile will be saved.<br>next<\/pre>\n\n\n\n<p>The number of instructions for 2560 pixels (160 x 16) is about 16k, i.e. 6 per pixel. Actually one could draw two pixels at once. This halves the effective number of instructions per pixel, so the number of high-level instructions per pixel is about 3. However, some of these high-level instructions will either be split in two or more assembly instructions, or they will require at least two cycles to complete, because they access to the memory. We are also not considering CPU pipeline flush due to jump, and wait states for the flash memory. Yes, we might be far from the 58-93 available cycles, but we must count the background playfield and the sprites.<\/p>\n\n\n\n<p>Although this shows that it\nis feasible even with only the CPU, we will see that DMA is much faster. This\nleaves more room for more on-screen sprites or better graphics effects (we\ncould implement alpha blending for instance).<\/p>\n\n\n\n<p>We will see that the number\nof C instructions required to set-up the DMA for each tile is less than 100,\ni.e., we need less than 0.5 instructions per pixel! Of course, DMA still has to\nperform the same number of memory transfer, but the address increment and the\ntransfer is done without any CPU intervention, which is therefore free to do\nsomething else (e.g. computing and drawing sprites).<\/p>\n\n\n\n<p>Using the SysTick timer, we found that the time required to setup the DMA for an entire block, and then waiting for the DMA to finish, is about 12k clock cycles. Please note: clock Cycles! Not high level instructions! The number of cycles is rather high for only 2560 pixels, i.e. 1280 32-bit words. In fact we get about 10 cycles per 32-bit word. However, one should take into account the time required to set up the DMA, and also the time required by the DMA to load from RAM the transfer descriptors (which essentially contain the pointers and number of bytes to transfer). Furthermore, there is always some sort of memory bus arbitration (to avoid starving the CPU), and the Flash memory requires at least one wait state. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Timing &#8211; SPI<\/strong><\/h3>\n\n\n\n<p>The other bottleneck is the\nSPI. Is 12 MHz fast enough for 25 fps ? The answer is yes: 12 MHz corresponds\nto about 36 frames per second. If we use 24 MHz, then the limit would be two\ntimes as large!<\/p>\n\n\n\n<p>By the way, the datasheets\nof the display and the microcontroller show a maximum SPI speed of 15 and 12\nMHz, respectively. We actually tested that these can be pushed to 24 MHz with\nno problem, at least in the \u201cdirection\u201d (the microcontroller writes to the\ndisplay) we want.<\/p>\n\n\n\n<p>We will use a popular 1.8\ninch SPI display. We verified that both ILI9163 and ST7735 work fine (at least\nat 12 MHz. The ST7735 is confirmed to work at up to 24 MHz). If you want to use\nthe same display of the tutorial &#8220;how to play a video on Arduino Uno&#8221;,\nwe suggest to modify it, if later you want to add SD support.<br>\nWe use the version with SD card so that we will have plenty of space for other\nthings such as audio or additional levels.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>The graphics\ufeff<\/strong><\/h2>\n\n\n\n<p> As we said we will use tiles. Each level will consists of tiles repeated according to a table, which we will call \u201cgameMap\u201d.<br> How big will be each tile?<br> The size of each tile has strong impact on the memory consumption, details, and flexibility (and later we will see speed too).<br> Too large tiles will require to make a new tile for each small variation we would like to have. This would eat up storage memory.<\/p>\n\n\n\n<figure class=\"wp-block-image is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/next-hack.com\/wp-content\/uploads\/2019\/04\/twoTiles.png\" alt=\"\" class=\"wp-image-529\" width=\"580\" height=\"165\"\/><figcaption>Two 32&#215;32-pixel tiles (left and center), which differ only by a small fraction (the top-right 16&#215;16 pixel part). This requires us to store two different 32&#215;32-pixel tiles. Instead, using a 16&#215;16-pixel tile (right), we just need to store only two 16&#215;16-pixel tiles (a completely white tile, plus the tile on the right). However, using 16&#215;16 tiles we need 4 map entries. <\/figcaption><\/figure>\n\n\n\n<p>However fewer tiles per screen would be required, increasing speed (see later) and reducing the map size (i.e. number of rows and columns in the table) for each level.<br> Too small tiles would create the opposite problem. Bigger map tables, and lower speed.<br> Of course, we won&#8217;t be using silly choices like using tiles of 17&#215;31 pixels. Powers of two are always your friend!<br> 16&#215;16 pixels is almost a golden rule, it is used in many games, and this is what we will be using!<\/p>\n\n\n\n<p>Our screen is 160&#215;128. In\nother words, we need 10&#215;8 tiles per screen, i.e. 80 entries in our table. A\nhuge level, with 10&#215;10 screens (or 100&#215;1 screens), will require only 8000\nentries (16 kB if we use 16 bit per entry. We will show you later why we are\ngoing to use 16 bit per entry).<\/p>\n\n\n\n<p>Let&#8217;s compare this with the\namount of memory we would likely use if we stored a big picture of the entire\nscreen: 40k*100= 4MB! That&#8217;s insane!<\/p>\n\n\n\n<p>Let&#8217;s talk about the\ndrawing system!<\/p>\n\n\n\n<p>Each frame will need to contain (in order of drawing):<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>background graphics (back playfield)<\/li><li>the actual level graphics (foreground).<\/li><li>the sprites<\/li><li>text\/top overlay. <\/li><\/ul>\n\n\n\n<p> In particular, we will perform sequentially these operations: <\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>drawing background + foreground\n(tiles)<\/li><li>drawing semitransparent tiles +\nsprites+ top overlay<\/li><li>sending the data via SPI.<\/li><\/ol>\n\n\n\n<p>Background and completely opaque tiles will be drawn by DMA. A completely opaque tile is a tile in which there are no transparent pixels. <\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"368\" height=\"163\" src=\"https:\/\/next-hack.com\/wp-content\/uploads\/2019\/04\/transparentTiles.png\" alt=\"\" class=\"wp-image-528\"\/><figcaption>Partially transparent tile (left) and completely opaque (right). The partially transparent tile has some pixels (on the bottom left part) which are transparent, therefore the background will be visible in that region. <\/figcaption><\/figure>\n\n\n\n<p>Partially transparent tiles, sprites and overlay cannot be efficiently drawn by DMA. In fact, the DMA system of the ATSAMD21 merely copies the data, and unlike Amiga&#8217;s Blitter, it does not check for transparency (which is determined by the color value). <br>All this partially transparent stuff will be drawn by the CPU.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1090\" height=\"354\" src=\"https:\/\/next-hack.com\/wp-content\/uploads\/2019\/04\/drawing.png\" alt=\"\" class=\"wp-image-524\"\/><\/figure>\n\n\n\n<p> Finally, data is sent to the display using DMA. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Pipelining<\/h3>\n\n\n\n<p>As you might see, if we\nperformed these operation in sequence on the same buffer, we would waste a lot\nof time. In fact, when the DMA is working, the CPU would not be doing anything,\nexcept for waiting that DMA has finished doing its job! This is not a good way\nto implement a graphic engine! Furthermore, when the DMA sends data to the SPI\nunit, it does not exploit its full bandwidth. In fact, even when the SPI works\nat 24 MHz, data is sent only with a frequency of 3 MHz, whch is a rather low\nfigure. In other words, the DMA is under used: the DMA could perform other\ntasks without losing too much in terms of performance.<\/p>\n\n\n\n<p>That\u2019s why we implement a\npipeline, which is an extension of the double buffering (actually we use three\nbuffers!). Of course, at the end the operations are always performed in\nsequence. But at the same time the CPU and DMA perform different tasks, without\ninterfering (too much) each other. <\/p>\n\n\n\n<p>We use in fact three\nbuffers, we will call block. <\/p>\n\n\n\n<p>At the same time:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>a buffer is used to draw background data using the DMA channel 1;<\/li><li>in another buffer (which was previously filled with the background data) the CPU draws sprites and partially transparent tiles;<\/li><li> finally, another buffer (which now contains a complete horizontal block of data) is used to send the data to the display via SPI, using the DMA channel 0.  Of course, the buffer used to send data to the SPI was previously filled with the sprites, while the SPI was sending the previous block, and while the other buffer was being filled with tiles.  <\/li><\/ul>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"2059\" height=\"781\" src=\"https:\/\/next-hack.com\/wp-content\/uploads\/2019\/04\/graphisPipeline.png\" alt=\"\" class=\"wp-image-525\"\/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>The DMA<\/strong><\/h3>\n\n\n\n<p>The ATSAMD21 DMA system\ncannot be compared to a blitter, but it still has some useful features. Thanks\nto the DMA, we can have a very high refresh rate, despite the presence of a\ndual playfield.<\/p>\n\n\n\n<p>The DMA transfer\nconfiguration is stored in RAM, in \u201cDMA descriptors\u201d, which tell the DMA how\nand from where to where it should perform the current transfer. These\ndescriptors can be linked together: if the link is present (i.e. no null\npointer), the DMA will automatically fetch a new descriptor, once the transfer\nis complete. By using many descriptors, the DMA can perform \u201ccomplex transfers\u201d,\nwhich is useful when, for instance, the source buffer is a sequence of non\ncontiguous segments of contiguous bytes. The fetch and write back of the\ndescriptors, however, takes time, as it must save\/load 16 descriptor bytes from\nthe RAM.<\/p>\n\n\n\n<p>The DMA can handle different data widths: bytes, half-words (16 bits) and words (32 bits). The datasheet calls this width as \u201cbeat size\u201d. For the SPI, we are forced to use byte transfers (even if the current REVD datasheet states that ATSAMD21&#8217;s SERCOMs have FIFOs &#8211; which Microchip claims they accept also 32-bit data -, it seems that actually it has no FIFO. The REVD datasheet also refers to a SERCOM CTRLC register, which is then missing both on the header files, and on the register description section. Luckily, unlike AVR, ATSAMD21 has at least a buffered transmit data register, so there won&#8217;t be transfer gap during send!). To draw the tiles we surely use 32 bits. This allows to copy two pixels per beat. The ATSAMD21\u2019s DMA also permits to increase the source or destination address each beat, by a fixed number of beat sizes.&nbsp; <\/p>\n\n\n\n<p>These two aspects are very\nimportant, and they will determine how we draw the tiles.<\/p>\n\n\n\n<p>First of all, if we drew only one pixel per beat (16 bit), we would halve the bandwidth of our system. We cannot renounce to the full bandwidth! <\/p>\n\n\n\n<p>However, if we draw two\npixels per beat, the playfield could be only scrolled by an even number of\npixels, and this would not appear as a fluid motion. To overcome this, we could\nuse a buffer which is two pixels or more larger. When we send the data to the\ndisplay, we would use the correct offset (0 or 1 pixel) depending if we need to\nmove the \u201ccamera\u201d of an even or odd number of pixels. <\/p>\n\n\n\n<p>However, for sake of simplicity we will reserve room for 11 entire tiles (160 + 16 pixel) instead of only 160+2 pixels. This has one big advantage: we will not need to calculate and update the destination address of each DMA descriptor (this would require several instructions, which might add too much overhead per tile). Of course, we will only draw the minimum number of pixels, i.e. at most 162. Yes, at the end this will waste some memory (about 1500 bytes, considering three buffers), for sake of speed and simplicity. Further optimization can be still performed.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"91\" src=\"https:\/\/next-hack.com\/wp-content\/uploads\/2019\/04\/AnimazioneGip.gif\" alt=\"\" class=\"wp-image-517\"\/><\/figure>\n\n\n\n<p>In the animated GIF above,\nyou see all the 16 line buffers (without descriptors) of a block. On the right\nthere is what will actually appear on the display. The GIF shows the first 32\nframes, when you move rightward by 1 pixel each frame. The black portion of the\nbuffer is the part that it is not updated, and its content is just leftover\nfrom previous operations. When the screen is scrolled by an odd amount of\nframes, a 162-pixel wide is drawn on the buffer. However, of these, the first\nand the last column (which are highlighted in the animation) are discarded.\nWhen the scroll amount is a multiple of 16 pixels, the drawing operations on\nthe buffer start back at the first column (x = 0).<\/p>\n\n\n\n<p>What about vertical\nscrolling?<\/p>\n\n\n\n<p>This must be dealt after we\nshow you how tiles are stored in the flash memory.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>How to store tiles<\/strong><\/h3>\n\n\n\n<p>A na\u00efve way (which would be\nOK if you used CPU-only drawing) to store tiles in the flash, is like a\nsequence of pixel colors. The first pixel of the first row, the second, etc\u2026\nuntil the 16<sup>th<\/sup>. Then you store the first pixel of the second row,\nthe second, and so on.<\/p>\n\n\n\n<p>Why is this na\u00efve? Because\nin this way DMA could draw only 16 pixels for each DMA descriptor! Therefore\nyou would need 16 descriptors, which would require each one to make 4+4 memory\naccesses (that is to transfer 32 bytes \u2013 8 memory read + 8 memory write, the\nDMA has to perform other 4 reads + 4 writes). This is rather inefficient! <\/p>\n\n\n\n<p>In fact, for each\ndescriptor, the DMA can increment only by a fixed amount of words the source\naddress and destination address. After the first row of the tile has been\ncopied to the buffer, the destination address should be incremented not by 1\nword, but by an amount so that it points to the next buffer row. This is not\npossible, because each transfer descriptor only specifies the beat-transfer\nincrement, which cannot be changed.<\/p>\n\n\n\n<p>A much more smart way is to send the first two pixels of each row of the tile, in sequence, that is, pixel 0 and 1 of row 0, pixels 0 and 1 of row 1, etc, until pixels 0 and 1 of row 15. Then we send pixel 2 and 3 of row 0, and so on.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"841\" height=\"837\" src=\"https:\/\/next-hack.com\/wp-content\/uploads\/2019\/04\/TileOrdering.png\" alt=\"\" class=\"wp-image-520\"\/><figcaption>How a tile is stored.<\/figcaption><\/figure>\n\n\n\n<p>In the figure above, each\nnumber indicates the order on which the 16-bit pixel is saved on the tile\narray.<\/p>\n\n\n\n<p>This can be done using one\ndescriptor, but we need two things:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>The\ntiles must be stored so that, by incrementing the source by one word, we point\nalways to the correct pixel positions. In other words, if (r,c) is the pixel at\nrow r and column c, we need to store, consecutively pixels:\n(0,0)(0,1)(1,0)(1,1)(2,0)(2,1)\u2026 (15,0)(15,1)(0,2)(0,3)(1,2)(1,3)\u2026<\/li><li>The\nbuffer should be 256 pixels wide (and not only 160)<\/li><\/ul>\n\n\n\n<p>The first is very simple to achieve: it\u2019s just a matter of reordering the data, it can be done when exporting the graphics to a c file (see previous picture).<\/p>\n\n\n\n<p>The second one can be achieved because the DMA allows to increase the destination address, after each beat, of 512 bytes. This has two consequences.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>We cannot send using a single\ndescriptor, the entire block via SPI. This is not a huge problem, because after\nall we just read one descriptor after 160 pixels. The impact will be minimal.<\/li><li>A block will be 256 * 2 * 16 bytes =\n8kB large, and there will be a lot of \u201cunused space\u201d.<\/li><\/ul>\n\n\n\n<p>The unused space, however,\ncan be still used, for examples, for the descriptors.<\/p>\n\n\n\n<p>In fact each descriptor is\n16 bytes large. We need at least 10 * 8 (actually we need 11 * 8!) descriptors\nfor the tiles, and 16 descriptors for the SPI.<\/p>\n\n\n\n<p>Here\u2019s why larger tiles\nwould lead to faster speed. In fact, if we used, for instance, a 32 x 32 tile,\nwe would need less descriptors per screen (320 instead of 640). This would\nreduce overhead. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Display data block<\/strong><\/h3>\n\n\n\n<p>The block buffer, the\ndescriptors and other data are stored in a structure type we called displayBlock_t.<\/p>\n\n\n\n<p>A displayBlock is an array of 16 displayLineData_t. The displayLine data contains 176 pixels, plus 80 words. In these 80 words we store display descriptors, or other useful display data (using a union).<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1250\" height=\"226\" src=\"https:\/\/next-hack.com\/wp-content\/uploads\/2019\/04\/displayLneData.png\" alt=\"\" class=\"wp-image-523\"\/><\/figure>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1868\" height=\"720\" src=\"https:\/\/next-hack.com\/wp-content\/uploads\/2019\/04\/displayBlock.png\" alt=\"\" class=\"wp-image-522\"\/><\/figure>\n\n\n\n<p>Since there are 16 lines,\neach tile in position X uses the first 8 DMA descriptors (0 to 7) of lines X.\nSince we have at most 11 tiles (hence the 176 pixel wide display line), the\ntiles only uses the DMA descriptors of the first 11 data lines. Descriptors 8-9\nof all the lines, and descriptors 0 to 9 of lines 11-15 are free.<\/p>\n\n\n\n<p>Of these, descriptors 8 and\n9 of lines 0..7 will be used for the SPI. <\/p>\n\n\n\n<p>The descriptors 0..9 of\nlines 11-15 (up to 50 descriptors, even though we will use only 48 of them)\nwill be used for the background playfield.<\/p>\n\n\n\n<p>The picture below shows its organization.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1986\" height=\"1155\" src=\"https:\/\/next-hack.com\/wp-content\/uploads\/2019\/04\/DisplayDescriptorUSage.png\" alt=\"\" class=\"wp-image-518\"\/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>The background playfield<\/strong><\/h3>\n\n\n\n<p>The background playfield is treated differently. First, we must renounce to the two-pixel alignment, if we want smooth scrolling. This is because the foreground and background scrolls at different speeds. Therefore beats will be half-word large. Even if this has some drawback in terms of speed, this allows an easier integration. Since there is quite a small number of descriptors left, we can\u2019t use small tiles. Furthermore, to simplify everything and to quickly add parallax, we will use long \u201cslices&#8221;.<\/p>\n\n\n\n<p>Background is actually\ndrawn only if there is at least one partially transparent pixel. This means\nthat if there is just a single transparent tile, the background will be drawn.\nThis is of course a waste of bandwidth, but it simplifies everything.<\/p>\n\n\n\n<p>If we compare the\nbackground to foreground playfield:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>The\nbackground uses slices, which are long tiles, stored in the \u201cna\u00efve\u201d way.<\/li><li>The\nbackground has its own map, which however is repeated horizontally. In this\nway, less memory is used.<\/li><li>The\nbackground has per-slice parallax.<\/li><\/ul>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>The foreground playfield<\/strong><\/h3>\n\n\n\n<p>As we said, we have on each\nblock, up to 11 tiles (10 full tiles, or 9 full tiles and 2 partial tiles). Each\nof these tiles is drawn by the DMA only if it is not marked as transparent.\nInstead, if they are not completely opaque, they are added to a list, which\nwill be analyzed later, when sprites are drawn.&nbsp;\n<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Putting together the two playfields<\/strong><\/h3>\n\n\n\n<p>The descriptors of the\nbackground playlist (which are always calculated) and those of the foreground\nplayfield form a very long linked list. The first part draws the background\nplayfield. The second part draws the tiles, over the background. The length of\nthe latter part might be variable, as the DMA descriptors of partially\ntransparent tiles are excluded from the list. If a block contains only opaque\ntiles, then the DMA is set up so that it starts directly from the first\ndescriptor of the first tile.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Sprites and transparent tiles<\/strong><\/h3>\n\n\n\n<p>Transparent tiles and\nsprites are treated almost at the same way. The pixel of the tile\/sprite is\nanalyzed. If it is black, then it is transparent, therefore the background tile\nis not changed. Instead, if it is not black, the background pixel is replaced\nwith that one of the sprite\/tile.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Vertical scrolling<\/strong><\/h3>\n\n\n\n<p>When dealing with\nhorizontal scrolling, we draw up to 11 tiles, even if, when drawing 11 tiles,\nthe first and the last will be only drawn partially. This partial drawing is possible\nbecause each descriptor draws two columns of a tile, therefore we can easily\nset where to start and stop our linked list.<\/p>\n\n\n\n<p>Instead, when dealing with vertical scrolling, we should compute both the destination register and the transfer count. This should set multiple times per frame. To avoid this hassle, we will simply draw up to 9 complete blocks per frame (just 8 when the scroll is a multiple of 16). <\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Hardware<\/strong><\/h2>\n\n\n\n<p>As we said, uChip is the\nheart. What about the other things?<\/p>\n\n\n\n<p>Here is the schematics! There are some aspects about it that are worthy of discussion.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/next-hack.com\/wp-content\/uploads\/2019\/04\/Schematics.png\" alt=\"\" class=\"wp-image-519\"\/><figcaption>(save the image to see it at a decent size!)<\/figcaption><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Keys<\/strong><\/h3>\n\n\n\n<p>To optimize I\/O usage, we\nuse a small trick. There will be 4 sensing lines, L1-L4, and one common wire LC.\nThe common wire will be alternatively set to 1 and 0. The sensing lines will be\nalternatively pulled low or high, respectively, using the internal pull-up\/down\nresistors. Two keys are connected between each key line and the common line. In\nseries to these two keys, a diode is inserted. Each of these diode has the\nopposite polarity, so that only one key can be effectively \u201csensed\u201d each time.<\/p>\n\n\n\n<p>Since there is no built in\nkeyboard controller (and since no embedded keyboard controller use this\npeculiar method), the eight keys are quickly sensed at the beginning of each\nframe. Since the inputs must be both pulled up and down, we cannot use (and we don\u2019t\neven want to) external resistors, so we must use the integrated resistors,\nwhich might be quite high valued (60 kOhm). This means that, when the common\nline changes state, and the sensing lines change their pull up\/down state, one\nshould insert some delay, to allow the integrated pull up\/down resistor\ncharging the pin and track parasitic capacitance to the desired level. <br>\nHowever we do not want to wait! Therefore we set the common line to a high\nimpedance state (so that no contentions occur), and we precharge the sensing\nlines to a logic value 1 or 0, by temporarily configuring them as output.\nLater, they are configured as input with pull-up or down respectively. Since\nthe output resistance is in the order of few tens of ohm, the state is forced\nin few ns, therefore, when the sensing line is configured back to input, it\nwill be already at the right state. After that, the common line is set to\noutput with the opposite polarity.<\/p>\n\n\n\n<p>This drastically increases the scanning speed and no delays\/nop are requested.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>SPI connection<\/strong><\/h3>\n\n\n\n<p>We connected the SD and the\ndisplay so that they can communicate each other without the data actually going\nto the ATSAMD21. This might be useful if you want to play a video. <\/p>\n\n\n\n<p>The resistors that connects\nMISO and MOSI should be low valued. In fact if it is too high, the SPI will not\nwork, as the signal will be too much degraded.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Optimizations and further developments<\/strong><\/h2>\n\n\n\n<p>One of the biggest issue is\nthe RAM usage. The three blocks take 8 kB each, leaving only 8 kB for stack,\nand the other variables. As for now, we have only 1.3 kB of free RAM + 4 kB of\nStack (4 kB of stack is very large, we might reduce it). <\/p>\n\n\n\n<p>However, we could consider 8-pixel\ntall blocks, and not 16. This would increase the overhead for the DMA\ndescriptors, but it would cut almost by half the memory usage for the block buffer\n(note that the number of descriptors would not change, if we still used 16&#215;16-pixel\ntiles, therefore we would need to change the block organization). This could\nfree about 7.5kB of RAM, which could be very useful, to easily implement some\nfeatures, like variable map with secrets, or add audio (though Audio could be\nstill easily added even with as low as 1kB of RAM).<\/p>\n\n\n\n<p>Another issue is the\nsprite, but this modification is much easier, and it only involves the function\ncreateNextFrameScene(). In fact, we create a huge array in RAM with the state\nof all the sprites. For each sprite, we then calculate if their position is\nwithin the display area, and then we animate and add them to the draw list.<\/p>\n\n\n\n<p>We could instead perform some kind of optimization. For instance, in the gameMap we not only store the tile value, but also a flag indicating the transparency of the tile, which is determined by the Editor. This allows us to quickly check if the tile must be drawn by DMA or CPU. &nbsp;That\u2019s why we use 16-bit entries for the tile map. Assuming a 256 tile-set (currently we have less than 128 tiles, but there is enough room in the flash to add some more), we have still 7 bits available, for other purposes. Three of these 7 bits could be used to indicate if there is stored any sprite\/object. For instance: <\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">0b000 = no stored object<br>0b001 = opossum<br>0b010 = eagle<br>0b011 = frog<br>0b100 = gem<br>0b101 = cherry<br>0b110 = switch<br>0b111 = other future things, like secret walls that disappear.<\/pre>\n\n\n\n<p>Then, we could create a\nbitmap table in RAM, in which each bit indicates if the item has not been\ndiscovered (for instance an enemy) or if it not has been picked up (e.g.\nbonus), or not activated (switch). In a 10&#215;10-screen level, we would need 8000 bits,\ni.e. 1kB of RAM. The bit is cleared when the enemy has been discovered, or the\nbonus has been picked up.<\/p>\n\n\n\n<p>In the\ncreateNextFrameScene() we would check the bits corresponding to the tiles in\nthe current view area. If these are at 1:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>If\nthese are bonus, simply add them to the sprite list to be drawn.<\/li><li>If\nthese are enemies, create a dynamic sprite, and clear the flag. Next frame\nscene will still contain this dynamic sprite, until the enemy goes off-screen,\nor it has been killed.<\/li><\/ul>\n\n\n\n<p>This has some drawbacks. <\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>The first one is that the sprites must be aligned at the tile boundaries, when it is created (then it is free to move with sub pixel precision). This is not a big issue, though.<\/li><li>The second one, is that we always need to check all the 80 screen tiles, to check which sprite we need to draw. A much clever solution would be to check the bitmap table, because with one access we get 32 tiles at once. Still this would be an issue for switches\/objects that may have an on\/off state (they should be drawn even in off position, i.e. when their flag in the bitmap table is 0!). The solution would be to draw always the tile in off-state and then have a sprite (which has the exact size of the tile and it is completely opaque) drawn over that tile, in on state.<\/li><li>The third one is that we need to create a dynamic list of sprites. If one manages to get followed by many enemies (without killing them) the list could become huge. Furthermore dynamic lists are always somewhat difficult to handle.<\/li><li>The fourth one is that one could cheat, by first discovering an enemy, then having it becoming off-screen. A partial workaround would be to store in the dynamic sprite data the tile position where it was born. If it goes offscreen, and it has not been killed, we could set again the bit corresponding to the tile it belong, so that if the player goes back, the enemy is back again!<\/li><li>This technique cannot be efficiently used when all the characters must be \u201clive\u201d even if off-screen (e.g. like a top-view version of Unreal Tournaments, where the bots must continuously fight each other).<\/li><\/ol>\n\n\n\n<p>However, in this way we\ncould much more efficiently store and handle a lot of sprites in the level.<\/p>\n\n\n\n<p>Still, this technique is more\nrelated to the \u201cgame logic\u201d part of the game, rather than the graphics engine!<\/p>\n\n\n\n<p>We might implement later this feature.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"mce_1\"><strong>Hey,&nbsp;you&nbsp;are&nbsp;saying&nbsp;that&nbsp;you&nbsp;are using 16&nbsp;bits&nbsp;per&nbsp;pixel!&nbsp;<\/strong><br><strong>But&nbsp;I&nbsp;do&nbsp;not&nbsp;see&nbsp;65536&nbsp;colors&nbsp;onscreen!<\/strong><\/h2>\n\n\n\n<p>Yes, for two reasons. <br>The first one is that the original tile set did not use 65536 colors. Yes, the original color space was 24 bits, but not every RGB combination was used! Probably the original author (<a rel=\"noreferrer noopener\" href=\"https:\/\/ansimuz.itch.io\/sunny-land-pixel-game-art\" target=\"_blank\">ansimuz<\/a>) optimized the tileset for a 256 on-screen palette (actually we did not perform a color count!). Had the tileset been in black and white, you woud have seen just 2 colors, despite the 16-bit graphics operations! You are free (and encouraged too!) to change the graphics, and achieve more onscreen colors: this article is not about the particular pixel art graphics, but rather about the <em>capability<\/em> of handling and showing 16bpp graphics at 40 fps, with dual playfield, parallax scrolling and onscreen sprites on a Cortex M0+ microcontroller.<br>The second reason is even simpler: the display has only 20480 pixels so at any time you&#8217;ll at most see 20480 different colors onscreen. Unless bizarre color combinations are choosen, you&#8217;ll never see that many different onscreen colors from a 16 bit palette&#8230; Still, this does not mean that, in the whole level, you won&#8217;t be able to see all the 65536 colors (that would be still very difficult to achieve, but only from an artistic viewpoint). <\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Summary<\/strong><\/h2>\n\n\n\n<p>We hoped you enjoyed this introduction.\nThere are still a lot of things we should and would explain. These will be the\nsubject of future articles.<\/p>\n\n\n\n<p>In the meantime, you can download the full source code of the game! Please if you enjoyed it, consider of making a donation to <a href=\"https:\/\/ansimuz.itch.io\/sunny-land-pixel-game-art\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"ansimuz (opens in a new tab)\">ansimuz<\/a>, who is the artist that drew all the graphics and donated it to the worlds, for free! <a href=\"https:\/\/www.paypal.me\/nexthack\">We won\u2019t reject donations either \ud83d\ude42<\/a> <\/p>\n\n\n\n<p>The game is not complete\nyet. We still want to add audio, multiple levels, objects to interact with,\netc. Feel free to make your own modifications! We hope to see new games with\nnew graphics and levels!<\/p>\n\n\n\n<p>The map editor will be released soon. It\u2019s too rudimental for now to be released to the public! &nbsp;&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Video<\/h2>\n\n\n\n<p>(Note! The video is taken at a much lower frame rate, due to poor ambient illumination! We will update the video very soon, to make you appreciate the full 40 fps speed!)<\/p>\n\n\n\n<iframe loading=\"lazy\" src=\"https:\/\/www.youtube.com\/embed\/1ekTeCCWKNI\" allow=\"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen=\"\" height=\"270\" frameborder=\"0\" width=\"480\"><\/iframe>\n\n\n\n<h2 class=\"wp-block-heading\">Credits<\/h2>\n\n\n\n<p>The graphics of the game (and of the tiles shown in some of the images above) comes from the <a rel=\"noreferrer noopener\" aria-label=\"&quot;Sunny Land free asset&quot; by ansimuz. (opens in a new tab)\" href=\"https:\/\/ansimuz.itch.io\/sunny-land-pixel-game-art\" target=\"_blank\">&#8220;Sunny Land free asset&#8221; by ansimuz.<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Downloads<\/strong><\/h2>\n\n\n\n<p>Please note that this project is open source. As such, it is provided for free (even though we will not be angry if you wanted to make us a <a href=\"https:\/\/next-hack.com\/index.php\/donate\/\">donation to show your appreciation<\/a>!). It is shared in the hope you will find it useful. We also make no guarantee your house\/city\/country\/planet won&#8217;t explode due to some bug\/mistake, which might be present in the code!<\/p>\n\n\n\n<p><a href=\"https:\/\/next-hack.com\/wp-content\/uploads\/2019\/04\/Schematics.png\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"Schematics (opens in a new tab)\">Schematics<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/next-hack.com\/wp-content\/uploads\/2019\/04\/uChipGameKiCAD.zip\">KiCad project<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/next-hack.com\/wp-content\/uploads\/2019\/09\/uChipGameFullSource.zip\">Atmel Studio 7 project (sources)<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introduction (tl;dr. source code and KiCAD project at the end of this page!) Although we were born in the 8 bit era, our first computer was an Amiga 500. It was a glorious 16-bit machine, and it featured stunning graphics&#8230; <a class=\"read-more-button\" href=\"https:\/\/next-hack.com\/index.php\/2019\/04\/07\/lets-build-an-handheld-platform-game-with-a-cortex-m0-microcontroller\/\">(READ MORE)<\/a><\/p>\n","protected":false},"author":2,"featured_media":541,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[32,7,1],"tags":[],"class_list":["post-516","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-all-projects","category-electronics-tips","category-general-hacks"],"_links":{"self":[{"href":"https:\/\/next-hack.com\/index.php\/wp-json\/wp\/v2\/posts\/516"}],"collection":[{"href":"https:\/\/next-hack.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/next-hack.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/next-hack.com\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/next-hack.com\/index.php\/wp-json\/wp\/v2\/comments?post=516"}],"version-history":[{"count":42,"href":"https:\/\/next-hack.com\/index.php\/wp-json\/wp\/v2\/posts\/516\/revisions"}],"predecessor-version":[{"id":632,"href":"https:\/\/next-hack.com\/index.php\/wp-json\/wp\/v2\/posts\/516\/revisions\/632"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/next-hack.com\/index.php\/wp-json\/wp\/v2\/media\/541"}],"wp:attachment":[{"href":"https:\/\/next-hack.com\/index.php\/wp-json\/wp\/v2\/media?parent=516"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/next-hack.com\/index.php\/wp-json\/wp\/v2\/categories?post=516"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/next-hack.com\/index.php\/wp-json\/wp\/v2\/tags?post=516"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}